トップ 最新 追記

SikabaneWorksが関係するコンテンツ(主に*band系ローグライク)の開発近況・補足から全く個人的な雑記まで。

[WEB:屍の見える丘公園]| [RSS]

Angband | Badiashimshe | C# | CRAUZEL | D'angband/小説草稿 | D'angband/開発 | DarkSouls | Demon'sSouls | DungeonCrawl | ElvenUteruses | Haskell | Hengband | J9シリーズ | LEGO | LineDrawing | MISC | MTG | Mac | Math | Moria | R-18 | Roguelike | Rough | RoughSketch | Ruby | SDL | UNIX | VMware | WarHammer | Zangband | アタシラヂョウヲウ | イラスト | ガジェット | ゲーム | ゲーム紹介 | ゲーム製作技術 | ゲーム論 | スケッチ | ツクール | テクノロジー | ニコニコ動画 | ファルコム | ファンタジー | マリオ | ヴィーヤウトゥムノ | 別記事追加予定 | 変愚蛮怒 | 変愚蛮怒/スポイラー | 変愚蛮怒/元ネタ探訪 | 変愚蛮怒/攻略 | 変愚蛮怒/開発 | 宗教 | 情報 | 政治 | 文字コード | 日ペ昔話 | 東方ワンドロ | 東方外法漢女 | 歴史 | 漫画製作 | 版権絵 | 画像処理 | 翻訳 | 自然言語 | 艦隊これくしょん | 落書き | 言語解析 | 読書 | 超人ロック | 追記予定 | 通信 | 阿片窟 | 馬鹿馬鹿蛮怒/開発 | 魔法少女まどか☆マギカ | 魚類版深夜の真剣お絵描き60分一本勝負




2018/12/03

[変愚蛮怒/開発]変愚蛮怒用インターフェース拡張の可能性

これはRoguelike Advent Calendar 2018 3日目の記事です。

はじめに

今年もAdvent Calenderの季節がやってまいりました。 一昨年とか去年とかの進行に関する記事を見直しながらやはり溜息つくのが現状であります。

冒頭から私事の愚痴で恐縮なのですが、ここ数年は実の所リアルの事情が停滞でじり貧でした。逆に今年からは逆に良い方にも悪い方にも振れ幅が大きすぎて頭抱えることが多く、中々に続けるものを続けることが困難な日々が続いております。

どうにか他の人々のご厚意受けて立て直したいとまだ考えてはいるのでどうぞよろしく。

過去の関連記事

本ブログの記事を見直すに4年以上前には現状のWindowsAPIを拡張する形で、あれこれやろうとして(実際BGM機能は既存先人のものを改良できたりしましたが)頓挫しっぱなしになっていたりします。

また、マルチプラットホームに関する件については過去のAdvent Calenderの記事にもした

などがあります。

現状のインターフェースについて

しばしば某所で言われますし、それで当然とは思われますが、現状変愚が直接はZangbandから引き継いできた旧WindowsAPIによるCursesの延長上を意識したテキストインターフェースも昔から色々限界になっております。 何分往年の主力であったDirectXすら使わない、恐らくはWindows98時代基準のAPI採用です。

テキストだけで完結できるシステムというのが、恐らく今ですら全くメリットがないとは言えないとは思います。現に本家Angbandは現行でもGNU系Cursesやでのビルドも可能なようですし、ANSI準拠のCが走る環境をサポートする態勢も確保しているようです。

とは言え平成も終わろうとしているこのご時世にいい加減にテキストのままも虚しいですよね。その点も含み本家は64ドットタイルにも対応していますがZangbandなどを挟み、フォークとして遠く離れてしまった変愚はここから恩恵を受けることなく、独自路線で行くしかありません。

そのZangから受け取ったインターフェースの構造はどんなもんでしょうか。一重にz-form.hの構造体にまとまっています。

/*!
 * @brief term実装構造体 / An actual "term" structure
 */
typedef struct term term;
struct term
{
	vptr user; //!< Extra "user" info (used by application)
	vptr data; //!< Extra "data" info (used by implementation)

	bool user_flag; //!< Flag "user_flag" An extra "user" flag (used by application)
	bool data_flag; //!< Flag "data_flag" An extra "data" flag (used by implementation)

	bool active_flag; //!< Flag "active_flag" This "term" is "active"
	bool mapped_flag; //!< Flag "mapped_flag" This "term" is "mapped"
	bool total_erase; //!< Flag "total_erase" This "term" should be fully erased
	bool fixed_shape; //!< Flag "fixed_shape" This "term" is not allowed to resize
	bool icky_corner; //!< Flag "icky_corner" This "term" has an "icky" corner grid
	bool soft_cursor; //!< Flag "soft_cursor" This "term" uses a "software" cursor
	bool always_pict; //!< Flag "always_pict" Use the "Term_pict()" routine for all text
	bool higher_pict; //!< Flag "higher_pict" Use the "Term_pict()" routine for special text
	bool always_text; //!< Flag "always_text" Use the "Term_text()" routine for invisible text
	bool unused_flag; //!< Flag "unused_flag" Reserved for future use
	bool never_bored; //!< Flag "never_bored" Never call the "TERM_XTRA_BORED" action
	bool never_frosh; //!< Flag "never_frosh" Never call the "TERM_XTRA_FROSH" action

	byte attr_blank; //!< Value "attr_blank" Use this "attr" value for "blank" grids
	char char_blank; //!< Value "char_blank" Use this "char" value for "blank" grids

	char *key_queue; //!< Keypress Queue -- various data / Keypress Queue -- pending keys
	u16b key_head;
	u16b key_tail;
	u16b key_xtra;
	u16b key_size;

	TERM_LEN wid; //!< Window Width(max 255)
	TERM_LEN hgt; //!< Window Height(max 255)

	TERM_LEN y1; //!< Minimum modified row
	TERM_LEN y2; //!< Maximum modified row

	TERM_LEN *x1; //!< Minimum modified column(per row)
	TERM_LEN *x2; //!< Maximum modified column(per row)

	term_win *old; //!< Displayed screen image
	term_win *scr; //!< Requested screen image

	term_win *tmp; //!< Temporary screen image
	term_win *mem; //!< Memorized screen image

	void (*init_hook)(term *t); //!< Hook for init - ing the term
	void (*nuke_hook)(term *t); //!< Hook for nuke - ing the term

	errr (*user_hook)(int n); //!< ユーザ設定項目実装部 / Hook for user actions
	errr (*xtra_hook)(int n, int v); //!< 拡張機能実装部 / Hook for extra actions
	errr (*curs_hook)(TERM_LEN x, TERM_LEN y); //!< カーソル描画実装部 / Hook for placing the cursor
	errr (*bigcurs_hook)(TERM_LEN x, TERM_LEN y); //!< 大型タイル時カーソル描画実装部 / Hook for placing the cursor on bigtile mode
	errr (*wipe_hook)(TERM_LEN x, TERM_LEN y, int n); //!< 指定座標テキスト消去実装部 / Hook for drawing some blank spaces
	errr (*text_hook)(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, cptr s); //!< テキスト描画実装部 / Hook for drawing a string of chars using an attr
	void (*resize_hook)(void); //!< 画面リサイズ実装部
	errr (*pict_hook)(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR *ap, const char *cp, const TERM_COLOR *tap, const char *tcp); //!< タイル描画実装部 / Hook for drawing a sequence of special attr / char pairs
};

一応インターフェースの抽象化と言う奴はこれで一通りできてはいるんですね。ここの後半を占める関数ポインタでTerm(テキスト画面)の描画や消去を行い、前半で各インターフェースの性質に応じた処理の行い分けも実現は出来ている。

これを前の記事で述べたmain-???.c各自が実装することでWindows版、UNIX/Linuxのcurses版やx11版、古いMac版などが成り立っている次第です。

逆に言えば、この構造体の中身を新規にmain-???.cで埋め尽くせば、ひとまずは新しいインターフェースが作成できることになります。今回それの候補を考えるとどうなるかなのですが。

SDL(特に2.0系)

今やSteamでも鳴らして久しいToME先輩Angband大先輩もどうも対応しているらしい選択肢です。 私自身馴染みがある、といいますかそもそもD'angbandだなんて、残骸になりかけている方でらしいインターフェースは作ったりしています。

SDL2

こちらも大概変愚ソース改変から始まっていたり、かと思ったらフルスクラッチでやったり迷走している有様なのですが、じゃあここで作ったリソースを少しでも変愚の方に還元するのもありかなと。

いっそUnity使えや

もうSDLもレガシーにも程がある遺産な訳で今から手を付けるのならいっそ一足飛びに最近のスタンダードになりつつあるUnityに飛び込むのもありなのかもしれません。

Unity

個人的に、こちとら技術の進歩と流行りに取り残されて悶絶している最中ですが、将来の本業としてあるいはここでその一足飛びをかますのもありでしょう。さしあたってネイティブプラグインという選択肢はあるようですし。

最後に

どれでもいいからいい加減計画は立てるだけでなく軌道修正しながら実現しましょう。

カレンダー4日目はp31xxx氏のCAVES OF QUDの紹介となります。




2018/12/07

[変愚蛮怒/開発]変愚蛮怒開発日誌part99…特別編「変愚蛮怒の性格実装例 ~かわいそうなお友達~」

はじめに

変愚蛮怒がまさにソースコードの違法建築物であり、拡張に色々手を付けづらいのは事実なのですが、その中でも「性格」の実装はまだしも追加しないといけない処理が少なく、比較的手軽に実装が出来るのもまた確かです。

という訳で今回は全く趣味に走り、某超個性派レトロアニメの要素をモンスターだけでなくプレイヤー側に実現する手順を見てみましょう。

このネタは今や停滞して久しい拙バリアントD'angの旧ソースにも一応組み込んだことのあるものです。

ブランチ

という訳でOSDNのリポジトリにこんなブランチを作りました。今回は本当に即興なので、本流には調整やデバッグ済ませてからマージします。

基礎ステータス設定

まず特に最低限の部分だけ済ませましょう。性格のステータス修正を追加します。

defines.hにある性格の定義数を直しIDを定義します。

diff --git a/src/defines.h b/src/defines.h
index ec8e7db..4cf51fe 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -181,7 +181,7 @@
 #define MAX_OWNERS      32 /*!< 各店舗毎の店主定義最大数 / Total number of owners per store (see "store.c", etc) */
 #define MAX_SEXES        2 /*!< 性別の定義最大数 / Maximum number of player "sex" types (see "table.c", etc) */
 #define MAX_CLASS       28 /*!< 職業の最大定義数 Maximum number of player "class" types (see "table.c", etc) */
-#define MAX_SEIKAKU     12 /*!< 性格の最大定義数 */
+#define MAX_SEIKAKU     13 /*!< 性格の最大定義数 */
 #define MAX_PATRON      16 /*!< カオスパトロンの最大定義数 / The number of "patrons" available (for Chaos Warriors) */

 /* ELDRITCH_HORRORによるsanity blast処理に関するメッセージの最大数 / Number of entries in the sanity-blast descriptions */
@@ -825,6 +825,7 @@
 #define SEIKAKU_LUCKY	 9
 #define SEIKAKU_GAMAN	 10
 #define SEIKAKU_MUNCHKIN 11
+#define SEIKAKU_CHARGEMAN 12

tables.cにある性格ステータス修正テーブルにも追加します。seikaku_info構造体は以下の通りです。

typedef struct player_seikaku player_seikaku;
struct player_seikaku
{
	cptr title;			/* Type of seikaku */

#ifdef JP
	cptr E_title;		/* 英語性格 */
#endif

	s16b a_adj[6];		/* seikaku stat bonuses */

	s16b a_dis;			/* seikaku disarming */
	s16b a_dev;			/* seikaku magic devices */
	s16b a_sav;			/* seikaku saving throw */
	s16b a_stl;			/* seikaku stealth */
	s16b a_srh;			/* seikaku search ability */
	s16b a_fos;			/* seikaku search frequency */
	s16b a_thn;			/* seikaku combat (normal) */
	s16b a_thb;			/* seikaku combat (shooting) */

	s16b a_mhp;			/* Race hit-dice modifier */

	byte no;			/* の */
	byte sex;			/* seibetu seigen */
};

大体、ちからじまんといのちしらずのメリットデメリットを足して極端にしてみました。

diff --git a/src/tables.c b/src/tables.c
index cbb707d..71ec372 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -3240,6 +3240,17 @@ const player_seikaku seikaku_info[MAX_SEIKAKU] =
 		 20, 40, 30, 10, 40, 40,  80, 80,
 		 15, 1, 0
 	},
+
+	{
+#ifdef JP
+		"チャージマン",
+#endif
+		"Chargeman",
+		{ 2,  -2,  -2,  0,  1,  -2 },
+		-7, 7, -5, -1, -2, -4, 15, 20,
+		-1, 0, 0
+	},
+
 };


--

最後にキャラメイク時の解説追加です。ガバガバ英語 ゆ る し て

diff --git a/src/birth.c b/src/birth.c
index 8a92971..2cbd48e 100644
--- a/src/birth.c
+++ b/src/birth.c
@@ -347,6 +347,8 @@ static cptr seikaku_jouhou[MAX_SEIKAKU] =

 "いかさまは、初心者の練習用の性格です。あらゆる能力が高くなっています。この性格を使えば勝利者になることは容易ですが、勝利しても全く自慢になりません。",

+"チャージマンは「こんなところ」に連れて行かれても仕方のない可愛そうなお友達なんDA。腕っ節やタフさはマンモス並みに強いのだけれど知能面はまるで駄目なのが分かるだろう?この性格は最初から気が狂っているので、混乱したり幻覚を見る心配がないのです。",
+
 #else

 "\"Ordinary\" is a personality with no special skills or talents, with unmodified stats and skills.",
@@ -372,7 +374,10 @@ static cptr seikaku_jouhou[MAX_SEIKAKU] =

 "A \"Patient\" person does things carefully.  Patient people have high constitution, and high resilience, but poor abilities in most other skills.  Also it directly influences your hit-points.",

-"\"munchkin\" is a personality for beginners.  It raises all your stats and skills.  With this personality, you can win the game easily, but gain little honor in doing so."
+"\"munchkin\" is a personality for beginners.  It raises all your stats and skills.  With this personality, you can win the game easily, but gain little honor in doing so.",
+
+"\ChargeMan\" is crazy killer. It render you powerfull strength and constitution, but poor intelligence.you are not confused and seen the illusion because this you go mad from the beginning.",
+
 #endif
 };

以上でこうして性格がキャラメイク時に指定できるまでは可能になります。

Chargeman1

基礎特性修正

続けて上記の馴レーションで解説している通り、頭が元からお詳しいので、混乱耐性と幻覚耐性を持たせてみます。

耐性などの付加は種族、職業、性格などを問わずxtra1.cのcalc_bonuses()で追加できます。

diff --git a/src/xtra1.c b/src/xtra1.c
index fcb013a..e708b2c 100644
--- a/src/xtra1.c
+++ b/src/xtra1.c
@@ -3860,10 +3860,17 @@ void calc_bonuses(void)

 	/* Sexy Gal */
 	if (p_ptr->pseikaku == SEIKAKU_SEXY) p_ptr->cursed |= (TRC_AGGRAVATE);
+
 	if (p_ptr->pseikaku == SEIKAKU_NAMAKE) p_ptr->to_m_chance += 10;
 	if (p_ptr->pseikaku == SEIKAKU_KIREMONO) p_ptr->to_m_chance -= 3;
 	if ((p_ptr->pseikaku == SEIKAKU_GAMAN) || (p_ptr->pseikaku == SEIKAKU_CHIKARA)) p_ptr->to_m_chance++;

+	if (p_ptr->pseikaku == SEIKAKU_CHARGEMAN)
+	{
+		p_ptr->to_m_chance += 5;
+		p_ptr->resist_conf = TRUE;
+	}
+
 	/* Lucky man */
 	if (p_ptr->pseikaku == SEIKAKU_LUCKY) p_ptr->muta3 |= MUT3_GOOD_LUCK;

--

それとは別に files.cのplayer_flags()関数でCコマンドで表示される処理も追加しなければなりません。

diff --git a/src/files.c b/src/files.c
index f27b2da..4c016d9 100644
--- a/src/files.c
+++ b/src/files.c
@@ -2588,6 +2588,8 @@ static void player_flags(BIT_FLAGS flgs[TR_FLAG_SIZE])

 	if (p_ptr->pseikaku == SEIKAKU_SEXY)
 		add_flag(flgs, TR_AGGRAVATE);
+	if (p_ptr->pseikaku == SEIKAKU_CHARGEMAN)
+		add_flag(flgs, TR_RES_CONF);
 	if (p_ptr->pseikaku == SEIKAKU_MUNCHKIN)
 	{
 		add_flag(flgs, TR_RES_BLIND);

幻覚耐性については、ゴーレムやアンデッドが出血を抑止されるのと同様、set_image()関数で常に値を0にする処理で実装しましょう。

diff --git a/src/effects.c b/src/effects.c
index 5f391f3..ef781a0 100644
--- a/src/effects.c
+++ b/src/effects.c
@@ -697,6 +697,7 @@ bool set_image(TIME_EFFECT v)
 	v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;

 	if (p_ptr->is_dead) return FALSE;
+	if (p_ptr->pseikaku == SEIKAKU_CHARGEMAN) v = 0;


 	/* Open */

以下のように@さん自身の先天的体質として表示されます。

Chargeman3

台詞変化

あの「コンバット」に劣らぬ個性なのですから台詞の変化も欲しい所、大体、PSEIKAKU_COMBATで見つかる処理から、チャージマン向けに使えそうなメッセージをつうずるっこみます。

diff --git a/src/bldg.c b/src/bldg.c
index f8d9975..0b57b9d 100644
--- a/src/bldg.c
+++ b/src/bldg.c
@@ -4353,6 +4353,7 @@ void do_cmd_quest(void)
 		if (!get_check(_("クエストに入りますか?", "Do you enter? "))) return;
 		if ((p_ptr->pseikaku == SEIKAKU_COMBAT) || (inventory[INVEN_BOW].name1 == ART_CRIMSON))
 			msg_print(_("『とにかく入ってみようぜぇ。』", ""));
+		else if(p_ptr->pseikaku == SEIKAKU_CHARGEMAN) msg_print("『全滅してやるぞ!』");
diff --git a/src/mspells4.c b/src/mspells4.c
index a0a31c3..d7f267f 100644
--- a/src/mspells4.c
+++ b/src/mspells4.c
@@ -229,6 +229,11 @@ void spell_RF4_DISPEL(MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)

 		if ((p_ptr->pseikaku == SEIKAKU_COMBAT) || (inventory[INVEN_BOW].name1 == ART_CRIMSON))
 			msg_print(_("やりやがったな!", ""));
+		else if ((p_ptr->pseikaku == SEIKAKU_CHARGEMAN))
+		{
+			if (randint0(2) == 0) msg_print(_("ジュラル星人め!", ""));
+			else msg_print(_("弱い者いじめは止めるんだ!", ""));
+		}

 		learn_spell(MS_DISPEL);
 	}
@@ -2267,7 +2272,12 @@ void spell_RF6_TELE_AWAY(MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 	{
 		if ((p_ptr->pseikaku == SEIKAKU_COMBAT) || (inventory[INVEN_BOW].name1 == ART_CRIMSON))
 			msg_print(_("くっそ~", ""));
-
+		else if ((p_ptr->pseikaku == SEIKAKU_CHARGEMAN))
+		{
+			if (randint0(2) == 0) msg_print(_("ジュラル星人め!", ""));
+			else msg_print(_("弱い者いじめは止めるんだ!", ""));
+		}
+
 		learn_spell(MS_TELE_AWAY);
 		teleport_player_away(m_idx, 100);
 	}
diff --git a/src/player-damage.c b/src/player-damage.c
index 77d850d..05e2ac0 100644
--- a/src/player-damage.c
+++ b/src/player-damage.c
@@ -300,6 +300,11 @@ int inven_damage(inven_func typ, int perc)
 #ifdef JP
 				if ((p_ptr->pseikaku == SEIKAKU_COMBAT) || (inventory[INVEN_BOW].name1 == ART_CRIMSON))
 					msg_print("やりやがったな!");
+				else if ((p_ptr->pseikaku == SEIKAKU_CHARGEMAN))
+				{
+					if (randint0(2) == 0) msg_print(_("ジュラル星人め!", ""));
+					else msg_print(_("弱い者いじめは止めるんだ!", ""));
+				}
 #endif

 				/* Potions smash open */
diff --git a/src/trap.c b/src/trap.c
index 9bb329f..5c93fdf 100644
--- a/src/trap.c
+++ b/src/trap.c
@@ -321,6 +321,9 @@ void hit_trap(bool break_trap)
 			msg_print(_("落とし戸に落ちた!", "You have fallen through a trap door!"));
 			if ((p_ptr->pseikaku == SEIKAKU_COMBAT) || (inventory[INVEN_BOW].name1 == ART_CRIMSON))
 				msg_print(_("くっそ~!", ""));
+			else if((p_ptr->pseikaku == SEIKAKU_CHARGEMAN))
+				msg_print(_("ジュラル星人の仕業に違いない!", ""));
+

Chargeman2

まあこんな感じに。

モンスター誤認

個人的にキチガイアニメらしい着想を考えてみました。何でもかんでもとりあえずジュラル星人として襲い掛かるマジキチぶりをモンスターを誤認する能力で表してみようと思います。

具体的には地上では100%、地下でも20%の確率で相手をジュラル星人と思い込ませることにしましょう。この実装は、現状の「あやしい影」や「たぬき」で実装済のモンスターの実IDと外見IDの分化仕様から簡単にできました。

---
 src/defines.h  | 1 +
 src/monster2.c | 6 +++++-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/defines.h b/src/defines.h
index 4cf51fe..6955ce2 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -5119,6 +5119,7 @@ extern int PlayerUID;
 #define MON_WYRM_SPACE    1064
 #define MON_JIZOTAKO      1065
 #define MON_TANUKI        1067
+#define MON_ALIEN_JURAL   1082
 #define MON_HATOPOPPO     1083
 #define MON_KOGAN         1096

diff --git a/src/monster2.c b/src/monster2.c
index 38a07a6..a6d0059 100644
--- a/src/monster2.c
+++ b/src/monster2.c
@@ -2904,10 +2904,14 @@ static bool monster_hook_tanuki(MONRACE_IDX r_idx)
 static IDX initial_r_appearance(MONRACE_IDX r_idx)
 {
 	int attempts = 1000;
-
 	IDX ap_r_idx;
 	DEPTH min = MIN(base_level-5, 50);

+	if (p_ptr->pseikaku == SEIKAKU_CHARGEMAN)
+	{
+		if (base_level == 0 || one_in_(5)) return MON_ALIEN_JURAL;
+	}
+
 	if (!(r_info[r_idx].flags7 & RF7_TANUKI))
 		return r_idx;

Chargeman3

いやあ、絵になる頭のおかしさです。

最後に

いかがでしたでしょうか。これで参考になるならば是非色々いじってみてください。そんでもって、これを機に誰かソースコードのプルリクエストとかかましてくれませんかね……

ああ、ただ今後リファクタリングで修正を要する箇所が変化する可能性が……まあ分からんことがありましたらTwitterなどでご質問もどうぞ(震え声)




2018/12/10

[変愚蛮怒/開発]変愚蛮怒開発日誌part101…特別編「変愚蛮怒の種族実装例 ~MtGの主に青いヤクザ種族~(前編/遅刻した)」

はじめに

という訳でAdvent Calendar 10日目埋め、ついでに遅刻しました。ゆ る し て。

前回、新しい性格の実装(調整)をあっさり1日で済ませてしまいまして、そんじゃあ続けて次に簡単そうな種族でも行ってみようかという例を、前後編か前中後編くらいに分けてやっていこうと思います。

ファンタジーな種族が何だかんだ既にそろい踏みな中、足りない奴をD'ang旧ソースや、ニワカハマりしたMtGなどで探った結果、赤のゴブリン、緑のエルフに並ぶ、青の囲んで棒で叩くヤクザ種族、マーフォークで行ってみようと思います。

ちなみに私はレガシー2tで難題の予見者を出したり、現実を砕くもので暴力の限りを尽くしたり、終末を招くものでねっとりアド稼いだり、積み上げたマナ加速の果てにウラモグ兄貴を叩きつけると絶頂しそうになる変態糞エルドラージ使いなので、無色マナ塗れになりたい奴、至急メールくれや(自分語り)

性格より少々と面倒な種族実装

という訳で、まずさっさと性格の時同様、ハードコーディング上の基礎ステ設定から行ってみましょう。

まずdefines.hに新しくマーフォークのIDを定義し、種族最大数を増やします。

diff --git a/src/defines.h b/src/defines.h
index ec8e7db..b6457ab 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -774,11 +774,12 @@
 #define RACE_S_FAIRY            34
 #define RACE_KUTAR              35
 #define RACE_ANDROID            36
+#define RACE_MERFOLK            37

 /*
  * Maximum number of player "race" types (see "table.c", etc)
  */
-#define MAX_RACES               37
+#define MAX_RACES               38

続けてプレイヤー種族のステータステーブル player_race race_info[MAX_RACES] にマーフォークのステータスを書き込みましょう。

--- a/src/tables.c
+++ b/src/tables.c
@@ -2768,6 +2768,22 @@ const player_race race_info[MAX_RACES] =
 		0,
 		0x0800001,
 	},
+	{
+#ifdef JP
+		"マーフォーク",
+#endif
+		"Merfolk",
+
+		{ -1,  0,  2,  1,  -1,  1},
+			2,  3,  2,  1, 6,  11, -1,  5,
+			10,  130,
+			24, 16,
+			66,  6, 130, 15,
+			62,  6, 100, 10,
+			2,
+			0xE77E7FF,
+	},
+
 };

現状基本的ステータス以外はハーフエルフからのコピーです。追々調整します。player_raceの構造体は以下の通りです。

typedef struct player_race player_race;

struct player_race
{
	cptr title;			/* Type of race */

#ifdef JP
	cptr E_title;		/* 英語種族 */
#endif
	s16b r_adj[6];		/* Racial stat bonuses */

	s16b r_dis;			/* disarming */
	s16b r_dev;			/* magic devices */
	s16b r_sav;			/* saving throw */
	s16b r_stl;			/* stealth */
	s16b r_srh;			/* search ability */
	s16b r_fos;			/* search frequency */
	s16b r_thn;			/* combat (normal) */
	s16b r_thb;			/* combat (shooting) */

	byte r_mhp;			/* Race hit-dice modifier */
	byte r_exp;			/* Race experience factor */

	byte b_age;			/* base age */
	byte m_age;			/* mod age */

	byte m_b_ht;		/* base height (males) */
	byte m_m_ht;		/* mod height (males) */
	byte m_b_wt;		/* base weight (males) */
	byte m_m_wt;		/* mod weight (males) */

	byte f_b_ht;		/* base height (females) */
	byte f_m_ht;		/* mod height (females)	  */
	byte f_b_wt;		/* base weight (females) */
	byte f_m_wt;		/* mod weight (females) */

	byte infra;			/* Infra-vision	range */

	u32b choice;        /* Legal class choices */
/*    byte choice_xtra;   */
};

Legal class choicesだけ分かりづらいかもしれませんが、要は職業選択時に種族に向いている職業、を戦士から順に指定しているビット配列です。現状ステータス上には何の影響もないものですね。

んでもって、birth.cに種族の解説メッセージを追加します。とりあえず完全に仕様を確定するまでは仮メッセージを加えます。

+"アンドロイドは機(略)
+
+"マーフォーク実装中",

 #else

@@ -197,6 +199,8 @@ static cptr race_jouhou[MAX_RACES] =

 "An android is (略)

+"Merfolk implementing.",
+
 #endif
 };

ここまでは性格と全く同じ要領です。続けて、種族ごとに依存する項目として、生い立ちと、それに伴う社会的地位の変動設定などを加える必要がありますが、ひとまず今は一時人間と同じにしておきましょう。get_history()に以下のように加えます。

@@ -1277,6 +1281,7 @@ static void get_history(void)
 		case RACE_HUMAN:
 		case RACE_BARBARIAN:
 		case RACE_DUNADAN:
+		case RACE_MERFOLK:
 		{
 			chart = 1;
 			break;
diff --git a/src/defines.h b/src/defines.h

ここまでで、一度は走らせてみようとするとこうなります。

町情報がアレ

これは「辺境の地」の町データをまとめている/lib/edit/t0000001.txtの以下の部分の問題です。

B:$0:N:The White Horse Inn:Otick:Human
B:0:N:旅の宿『白馬亭』:オティック:人間
B:$0:A:0:Rest for the night:20:20:r:17:0
B:0:A:0:一泊する:20:20:r:17:0
B:$0:A:1:buy Food and drink:1:1:f:18:1
B:0:A:1:食事をする:1:1:f:18:1
B:$0:A:2:Listen for rumors:5:5:u:19:0
B:0:A:2:噂を聞く:5:5:u:19:0
B:$0:A:3:request Quest:0:0:q:6:0
B:0:A:3:クエスト:0:0:q:6:0
B:$0:A:4:Teleport to other town:500:500:m:42:0
B:0:A:4:他の町へ移動:500:500:m:42:0
B:$0:R:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:0:0:0:0:0:1:1:0:1:0:1:1:1:0
B:0:R:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:0:0:0:0:0:1:1:0:1:0:1:1:1:0

一番最後の行です。宿で、アンデッドやバルログなどが魔道具のエネルギーや血やら死体やらを売ってもらえない制限処理に該当する部分が、今回のマーフォークの追加に伴って、スロットが一つ増えたため、「引数不足」になってしまった次第です。このまま進めると恐ろしいバグで強制終了し、再起動しても町が消滅したりするというギャグみたいな状態になります。

ではこれを直していこうということで続きは次回。