2 * @brief ウルティマ4を参考にした徳のシステムの実装 / Enable an Ultima IV style "avatar" game where you try to achieve perfection in various virtues.
9 * Copyright (c) 1989 James E. Wilson, Christopher J. Stuart
10 * This software may be copied and distributed for educational, research, and
11 * not for profit purposes provided that this copyright and statement are
12 * included in all such copies.
15 #include "avatar/avatar.h"
16 #include "core/player-update-types.h"
17 #include "game-option/text-display-options.h"
18 #include "player-info/class-info.h"
19 #include "player-info/race-types.h"
20 #include "realm/realm-names-table.h"
21 #include "system/player-type-definition.h"
24 * 徳の名称 / The names of the virtues
26 concptr virtue[MAX_VIRTUE] = {
33 _("啓", "Enlightenment"),
44 _("個", "Individualism"),
48 * @brief 該当の徳がプレイヤーに指定されているか否かに応じつつ、大小を比較する。
49 * @details 徳がない場合は値0として比較する。
50 * @param type 比較したい徳のID
52 * @param tekitou VIRTUE_LARGE = 基準値より大きいか / VIRTUE_SMALL = 基準値より小さいか
56 bool compare_virtue(PlayerType *player_ptr, int type, int num, int tekitou)
58 int vir = virtue_number(player_ptr, type) ? player_ptr->virtues[virtue_number(player_ptr, type) - 1] : 0;
78 * @brief プレイヤーの指定の徳が何番目のスロットに登録されているかを返す。 / Aux function
79 * @param type 確認したい徳のID
80 * @return スロットがあるならばスロットのID(0~7)+1、ない場合は0を返す。
82 int virtue_number(PlayerType *player_ptr, int type)
84 for (int i = 0; i < 8; i++) {
85 if (player_ptr->vir_types[i] == type) {
94 * @brief プレイヤーの職業や種族に依存しないランダムな徳を取得する / Aux function
95 * @param which 確認したい徳のID
97 static void get_random_virtue(PlayerType *player_ptr, int which)
100 while (!(type) || virtue_number(player_ptr, type)) {
101 switch (randint1(29)) {
152 player_ptr->vir_types[which] = (int16_t)type;
156 * @brief プレイヤーの選んだ魔法領域に応じて対応する徳を返す。
157 * @param realm 魔法領域のID
160 static enum virtue_idx get_realm_virtues(PlayerType *player_ptr, int16_t realm)
164 if (virtue_number(player_ptr, V_VITALITY)) {
170 if (virtue_number(player_ptr, V_KNOWLEDGE)) {
176 if (virtue_number(player_ptr, V_NATURE)) {
182 if (virtue_number(player_ptr, V_CHANCE)) {
183 return V_INDIVIDUALISM;
194 if (virtue_number(player_ptr, V_ENCHANT)) {
195 return V_INDIVIDUALISM;
200 if (virtue_number(player_ptr, V_JUSTICE)) {
206 if (virtue_number(player_ptr, V_JUSTICE)) {
212 if (virtue_number(player_ptr, V_COMPASSION)) {
223 * @brief 作成中のプレイヤーキャラクターに徳8種類を与える。 / Select virtues & reset values for a new character
224 * @details 職業に応じて1~4種が固定、種族に応じて1種類が与えられ、後は重複なくランダムに選択される。
226 void initialize_virtues(PlayerType *player_ptr)
232 for (i = 0; i < 8; i++) {
233 player_ptr->virtues[i] = 0;
234 player_ptr->vir_types[i] = 0;
239 /* Get pre-defined types */
240 /* 1 or more virtues based on class */
241 switch (player_ptr->pclass) {
242 case PlayerClassType::WARRIOR:
243 case PlayerClassType::SAMURAI:
244 player_ptr->vir_types[i++] = V_VALOUR;
245 player_ptr->vir_types[i++] = V_HONOUR;
247 case PlayerClassType::MAGE:
248 player_ptr->vir_types[i++] = V_KNOWLEDGE;
249 player_ptr->vir_types[i++] = V_ENCHANT;
251 case PlayerClassType::PRIEST:
252 player_ptr->vir_types[i++] = V_FAITH;
253 player_ptr->vir_types[i++] = V_TEMPERANCE;
255 case PlayerClassType::ROGUE:
256 case PlayerClassType::SNIPER:
257 player_ptr->vir_types[i++] = V_HONOUR;
259 case PlayerClassType::RANGER:
260 case PlayerClassType::ARCHER:
261 player_ptr->vir_types[i++] = V_NATURE;
262 player_ptr->vir_types[i++] = V_TEMPERANCE;
264 case PlayerClassType::PALADIN:
265 player_ptr->vir_types[i++] = V_JUSTICE;
266 player_ptr->vir_types[i++] = V_VALOUR;
267 player_ptr->vir_types[i++] = V_HONOUR;
268 player_ptr->vir_types[i++] = V_FAITH;
270 case PlayerClassType::WARRIOR_MAGE:
271 case PlayerClassType::RED_MAGE:
272 player_ptr->vir_types[i++] = V_ENCHANT;
273 player_ptr->vir_types[i++] = V_VALOUR;
275 case PlayerClassType::CHAOS_WARRIOR:
276 player_ptr->vir_types[i++] = V_CHANCE;
277 player_ptr->vir_types[i++] = V_INDIVIDUALISM;
279 case PlayerClassType::MONK:
280 case PlayerClassType::FORCETRAINER:
281 player_ptr->vir_types[i++] = V_FAITH;
282 player_ptr->vir_types[i++] = V_HARMONY;
283 player_ptr->vir_types[i++] = V_TEMPERANCE;
284 player_ptr->vir_types[i++] = V_PATIENCE;
286 case PlayerClassType::MINDCRAFTER:
287 case PlayerClassType::MIRROR_MASTER:
288 player_ptr->vir_types[i++] = V_HARMONY;
289 player_ptr->vir_types[i++] = V_ENLIGHTEN;
290 player_ptr->vir_types[i++] = V_PATIENCE;
292 case PlayerClassType::HIGH_MAGE:
293 case PlayerClassType::SORCERER:
294 player_ptr->vir_types[i++] = V_ENLIGHTEN;
295 player_ptr->vir_types[i++] = V_ENCHANT;
296 player_ptr->vir_types[i++] = V_KNOWLEDGE;
298 case PlayerClassType::TOURIST:
299 player_ptr->vir_types[i++] = V_ENLIGHTEN;
300 player_ptr->vir_types[i++] = V_CHANCE;
302 case PlayerClassType::IMITATOR:
303 player_ptr->vir_types[i++] = V_CHANCE;
305 case PlayerClassType::BLUE_MAGE:
306 player_ptr->vir_types[i++] = V_CHANCE;
307 player_ptr->vir_types[i++] = V_KNOWLEDGE;
309 case PlayerClassType::BEASTMASTER:
310 player_ptr->vir_types[i++] = V_NATURE;
311 player_ptr->vir_types[i++] = V_CHANCE;
312 player_ptr->vir_types[i++] = V_VITALITY;
314 case PlayerClassType::MAGIC_EATER:
315 player_ptr->vir_types[i++] = V_ENCHANT;
316 player_ptr->vir_types[i++] = V_KNOWLEDGE;
318 case PlayerClassType::BARD:
319 player_ptr->vir_types[i++] = V_HARMONY;
320 player_ptr->vir_types[i++] = V_COMPASSION;
322 case PlayerClassType::CAVALRY:
323 player_ptr->vir_types[i++] = V_VALOUR;
324 player_ptr->vir_types[i++] = V_HARMONY;
326 case PlayerClassType::BERSERKER:
327 player_ptr->vir_types[i++] = V_VALOUR;
328 player_ptr->vir_types[i++] = V_INDIVIDUALISM;
330 case PlayerClassType::SMITH:
331 player_ptr->vir_types[i++] = V_HONOUR;
332 player_ptr->vir_types[i++] = V_KNOWLEDGE;
334 case PlayerClassType::NINJA:
335 player_ptr->vir_types[i++] = V_PATIENCE;
336 player_ptr->vir_types[i++] = V_KNOWLEDGE;
337 player_ptr->vir_types[i++] = V_FAITH;
338 player_ptr->vir_types[i++] = V_UNLIFE;
340 case PlayerClassType::ELEMENTALIST:
341 player_ptr->vir_types[i++] = V_NATURE;
343 case PlayerClassType::MAX:
347 /* Get one virtue based on race */
348 switch (player_ptr->prace) {
349 case PlayerRaceType::HUMAN:
350 case PlayerRaceType::HALF_ELF:
351 case PlayerRaceType::DUNADAN:
352 player_ptr->vir_types[i++] = V_INDIVIDUALISM;
354 case PlayerRaceType::ELF:
355 case PlayerRaceType::SPRITE:
356 case PlayerRaceType::ENT:
357 case PlayerRaceType::MERFOLK:
358 player_ptr->vir_types[i++] = V_NATURE;
360 case PlayerRaceType::HOBBIT:
361 case PlayerRaceType::HALF_OGRE:
362 player_ptr->vir_types[i++] = V_TEMPERANCE;
364 case PlayerRaceType::DWARF:
365 case PlayerRaceType::KLACKON:
366 case PlayerRaceType::ANDROID:
367 player_ptr->vir_types[i++] = V_DILIGENCE;
369 case PlayerRaceType::GNOME:
370 case PlayerRaceType::CYCLOPS:
371 player_ptr->vir_types[i++] = V_KNOWLEDGE;
373 case PlayerRaceType::HALF_ORC:
374 case PlayerRaceType::AMBERITE:
375 case PlayerRaceType::KOBOLD:
376 player_ptr->vir_types[i++] = V_HONOUR;
378 case PlayerRaceType::HALF_TROLL:
379 case PlayerRaceType::BARBARIAN:
380 player_ptr->vir_types[i++] = V_VALOUR;
382 case PlayerRaceType::HIGH_ELF:
383 case PlayerRaceType::KUTAR:
384 player_ptr->vir_types[i++] = V_VITALITY;
386 case PlayerRaceType::HALF_GIANT:
387 case PlayerRaceType::GOLEM:
388 case PlayerRaceType::ARCHON:
389 case PlayerRaceType::BALROG:
390 player_ptr->vir_types[i++] = V_JUSTICE;
392 case PlayerRaceType::HALF_TITAN:
393 player_ptr->vir_types[i++] = V_HARMONY;
395 case PlayerRaceType::YEEK:
396 player_ptr->vir_types[i++] = V_SACRIFICE;
398 case PlayerRaceType::MIND_FLAYER:
399 player_ptr->vir_types[i++] = V_ENLIGHTEN;
401 case PlayerRaceType::DARK_ELF:
402 case PlayerRaceType::DRACONIAN:
403 case PlayerRaceType::S_FAIRY:
404 player_ptr->vir_types[i++] = V_ENCHANT;
406 case PlayerRaceType::NIBELUNG:
407 player_ptr->vir_types[i++] = V_PATIENCE;
409 case PlayerRaceType::IMP:
410 player_ptr->vir_types[i++] = V_FAITH;
412 case PlayerRaceType::ZOMBIE:
413 case PlayerRaceType::SKELETON:
414 case PlayerRaceType::VAMPIRE:
415 case PlayerRaceType::SPECTRE:
416 player_ptr->vir_types[i++] = V_UNLIFE;
418 case PlayerRaceType::BEASTMAN:
419 player_ptr->vir_types[i++] = V_CHANCE;
421 case PlayerRaceType::MAX:
425 /* Get a virtue for realms */
426 if (player_ptr->realm1) {
427 tmp_vir = get_realm_virtues(player_ptr, player_ptr->realm1);
429 player_ptr->vir_types[i++] = tmp_vir;
433 if (player_ptr->realm2) {
434 tmp_vir = get_realm_virtues(player_ptr, player_ptr->realm2);
436 player_ptr->vir_types[i++] = tmp_vir;
440 /* Eliminate doubles */
441 for (i = 0; i < 8; i++) {
442 for (j = i + 1; j < 8; j++) {
443 if ((player_ptr->vir_types[j] != 0) && (player_ptr->vir_types[j] == player_ptr->vir_types[i])) {
444 player_ptr->vir_types[j] = 0;
449 /* Fill in the blanks */
450 for (i = 0; i < 8; i++) {
451 if (player_ptr->vir_types[i] == 0) {
452 get_random_virtue(player_ptr, i);
458 * @brief 対応する徳をプレイヤーがスロットに登録している場合に加減を行う。
459 * @details 範囲は-125~125、基本的に絶対値が大きいほど絶対値が上がり辛くなる。
463 void chg_virtue(PlayerType *player_ptr, int virtue_id, int amount)
465 for (int i = 0; i < 8; i++) {
466 if (player_ptr->vir_types[i] != virtue_id) {
471 if ((amount + player_ptr->virtues[i] > 50) && one_in_(2)) {
472 player_ptr->virtues[i] = std::max<short>(player_ptr->virtues[i], 50);
476 if ((amount + player_ptr->virtues[i] > 80) && one_in_(2)) {
477 player_ptr->virtues[i] = std::max<short>(player_ptr->virtues[i], 80);
481 if ((amount + player_ptr->virtues[i] > 100) && one_in_(2)) {
482 player_ptr->virtues[i] = std::max<short>(player_ptr->virtues[i], 100);
486 if (amount + player_ptr->virtues[i] > 125) {
487 player_ptr->virtues[i] = 125;
489 player_ptr->virtues[i] = player_ptr->virtues[i] + amount;
492 if ((amount + player_ptr->virtues[i] < -50) && one_in_(2)) {
493 player_ptr->virtues[i] = std::min<short>(player_ptr->virtues[i], -50);
497 if ((amount + player_ptr->virtues[i] < -80) && one_in_(2)) {
498 player_ptr->virtues[i] = std::min<short>(player_ptr->virtues[i], -80);
502 if ((amount + player_ptr->virtues[i] < -100) && one_in_(2)) {
503 player_ptr->virtues[i] = std::min<short>(player_ptr->virtues[i], -100);
507 if (amount + player_ptr->virtues[i] < -125) {
508 player_ptr->virtues[i] = -125;
510 player_ptr->virtues[i] = player_ptr->virtues[i] + amount;
514 player_ptr->update |= PU_BONUS;
520 * @brief 対応する徳をプレイヤーがスロットに登録している場合に固定値をセットする
522 * @param amount セットしたい値
524 void set_virtue(PlayerType *player_ptr, int virtue_id, int amount)
526 for (int i = 0; i < 8; i++) {
527 if (player_ptr->vir_types[i] == virtue_id) {
528 player_ptr->virtues[i] = (int16_t)amount;
536 * @param out_file ファイルポインタ
538 void dump_virtues(PlayerType *player_ptr, FILE *out_file)
544 for (int v_nr = 0; v_nr < 8; v_nr++) {
545 GAME_TEXT vir_name[20];
546 int tester = player_ptr->virtues[v_nr];
547 strcpy(vir_name, virtue[(player_ptr->vir_types[v_nr]) - 1]);
548 const std::string vir_val_str = format(" (%d)", tester);
549 const auto vir_val = show_actual_value ? vir_val_str.data() : "";
550 if (player_ptr->vir_types[v_nr] == 0 || player_ptr->vir_types[v_nr] > MAX_VIRTUE) {
551 fprintf(out_file, _("おっと。%sの情報なし。", "Oops. No info about %s."), vir_name);
554 else if (tester < -100) {
555 fprintf(out_file, _("[%s]の対極%s", "You are the polar opposite of %s.%s"), vir_name, vir_val);
556 } else if (tester < -80) {
557 fprintf(out_file, _("[%s]の大敵%s", "You are an arch-enemy of %s.%s"), vir_name, vir_val);
558 } else if (tester < -60) {
559 fprintf(out_file, _("[%s]の強敵%s", "You are a bitter enemy of %s.%s"), vir_name, vir_val);
560 } else if (tester < -40) {
561 fprintf(out_file, _("[%s]の敵%s", "You are an enemy of %s.%s"), vir_name, vir_val);
562 } else if (tester < -20) {
563 fprintf(out_file, _("[%s]の罪者%s", "You have sinned against %s.%s"), vir_name, vir_val);
564 } else if (tester < 0) {
565 fprintf(out_file, _("[%s]の迷道者%s", "You have strayed from the path of %s.%s"), vir_name, vir_val);
566 } else if (tester == 0) {
567 fprintf(out_file, _("[%s]の中立者%s", "You are neutral to %s.%s"), vir_name, vir_val);
568 } else if (tester < 20) {
569 fprintf(out_file, _("[%s]の小徳者%s", "You are somewhat virtuous in %s.%s"), vir_name, vir_val);
570 } else if (tester < 40) {
571 fprintf(out_file, _("[%s]の中徳者%s", "You are virtuous in %s.%s"), vir_name, vir_val);
572 } else if (tester < 60) {
573 fprintf(out_file, _("[%s]の高徳者%s", "You are very virtuous in %s.%s"), vir_name, vir_val);
574 } else if (tester < 80) {
575 fprintf(out_file, _("[%s]の覇者%s", "You are a champion of %s.%s"), vir_name, vir_val);
576 } else if (tester < 100) {
577 fprintf(out_file, _("[%s]の偉大な覇者%s", "You are a great champion of %s.%s"), vir_name, vir_val);
579 fprintf(out_file, _("[%s]の具現者%s", "You are the living embodiment of %s.%s"), vir_name, vir_val);
582 fprintf(out_file, "\n");