トップ 最新 追記

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/05

[ヴィーヤウトゥムノ]ハーフエルフ暗黒プリースト

13日に本日枠補完。女体補充。

ハーフエルフ暗黒プリースト



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

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

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


2018/12/11

[変愚蛮怒/開発]変愚蛮怒開発日誌part102…Visual Studio 2017ソリューション追加

ビルドで「warning C5045: /Qspectre スイッチが」の頻発を確認。

例のこれか。

全く個人的な話だが、Visual Studio 2015からカーソルが行端より先に行くと次の行でなく、一時的にスペースがつくやつ、不便な仕様かと思ったら、何かの拍子でエディタの「仮想空白文字を使用する」がONになっているだけだった。


2018/12/12

[変愚蛮怒/開発]変愚蛮怒開発日誌part103…相変わらずリファクタリング中

いい加減機械的なこと以外にも手は付け始めていますが。


2018/12/13

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

Advent Calenderのネタのためにも、続けて実装していきます。

種族/職業/魔法領域制限指定のフォーマットを緩くする

前回のような種族の追加の毎に、参照しづらい/lib/edit内のデータをあれこれいじらないといけないのは、これからの追加上にも優しくないので、そこを修正しましょう。行の項目数が足りない時にはとりあえず埋めます。

同じ症状が職業、魔法領域でも発生するようなのでついでに直します。

---
 src/init1.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/src/init1.c b/src/init1.c
index 525dc16..4a57777 100644
--- a/src/init1.c
+++ b/src/init1.c
@@ -3838,11 +3838,13 @@ static errr parse_line_building(char *buf)
 		/* Building Classes */
 		case 'C':
 		{
-			if (tokenize(s + 2, MAX_CLASS, zz, 0) == MAX_CLASS)
+			int n;
+			n = tokenize(s + 2, MAX_CLASS, zz, 0);
+			if (n <= MAX_CLASS)
 			{
 				for (i = 0; i < MAX_CLASS; i++)
-				{
-					building[index].member_class[i] = (CLASS_IDX)atoi(zz[i]);
+				{
+					building[index].member_class[i] = ((i > n) ? (CLASS_IDX)atoi(zz[i]) : 1);
 				}

 				break;
@@ -3854,11 +3856,13 @@ static errr parse_line_building(char *buf)
 		/* Building Races */
 		case 'R':
 		{
-			if (tokenize(s+2, MAX_RACES, zz, 0) == MAX_RACES)
+			int n;
+			n = tokenize(s + 2, MAX_RACES, zz, 0);
+			if (n <= MAX_RACES)
 			{
 				for (i = 0; i < MAX_RACES; i++)
 				{
-					building[index].member_race[i] = (RACE_IDX)atoi(zz[i]);
+					building[index].member_race[i] = ((i > n) ? (RACE_IDX)atoi(zz[i]) : 1);
 				}

 				break;
@@ -3870,11 +3874,13 @@ static errr parse_line_building(char *buf)
 		/* Building Realms */
 		case 'M':
 		{
-			if (tokenize(s+2, MAX_MAGIC, zz, 0) == MAX_MAGIC)
+			int n;
+			n = tokenize(s + 2, MAX_MAGIC, zz, 0);
+			if (n <= MAX_MAGIC)
 			{
 				for (i = 0; i < MAX_MAGIC; i++)
 				{
-					building[index].member_realm[i+1] = (REALM_IDX)atoi(zz[i]);
+					building[index].member_realm[i+1] = ((i > n) ? (REALM_IDX)atoi(zz[i]) : 1);
 				}

 				break;
--

2019/02/04追記・ここ不等号逆でした。長いこと気づかず大変申し訳ない。 今はこうなっています。

		/* Building Classes */
		case 'C':
		{
			int n;
			n = tokenize(s + 2, MAX_CLASS, zz, 0);
			for (i = 0; i < MAX_CLASS; i++)
			{
				building[index].member_class[i] = ((i < n) ? (CLASS_IDX)atoi(zz[i]) : 1);
			}
			break;
		}

		/* Building Races */
		case 'R':
		{
			int n;
			n = tokenize(s + 2, MAX_RACES, zz, 0);
			for (i = 0; i < MAX_RACES; i++)
			{
				building[index].member_race[i] = ((i < n) ? (RACE_IDX)atoi(zz[i]) : 1);
			}
			break;
		}

		/* Building Realms */
		case 'M':
		{
			int n;
			n = tokenize(s + 2, MAX_MAGIC, zz, 0);
			for (i = 0; i < MAX_MAGIC; i++)
			{
				building[index].member_realm[i+1] = ((i < n) ? (REALM_IDX)atoi(zz[i]) : 1);
			}
			break;
		}

その上で、マーフォークの指定を行う。

基本、人間とエルフあたりと同じ食糧やその他事情で済むでしょうから適当に。

diff --git a/lib/edit/t0000001.txt b/lib/edit/t0000001.txt
index f2082c9..85ce876 100644
--- a/lib/edit/t0000001.txt
+++ b/lib/edit/t0000001.txt
@@ -496,8 +496,8 @@ 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
+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:1
+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:1

 B:$1:N:Mayor:Uldrik:Human
 B:1:N:村長:ウルドリック:人間
diff --git a/lib/edit/t0000002.txt b/lib/edit/t0000002.txt
index da73aa1..8e50b02 100644
--- a/lib/edit/t0000002.txt
+++ b/lib/edit/t0000002.txt
@@ -321,8 +321,8 @@ B:$4:A:2:Listen for rumors:10:10:u:19:0
 B:4:A:2:噂を聞く:10:10:u:19:0
 B:$4:A:3:Teleport to other town:500:500:m:42:0
 B:4:A:3:他の町へ移動:500:500:m:42:0
-B:$4: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:4: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:$4: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:1
+B:4: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:1

 B:$5:N:Beastmaster:Lorien:Elf
 B:5:N:モンスター仙人:ロリエン:エルフ
diff --git a/lib/edit/t0000003.txt b/lib/edit/t0000003.txt
index b0609b0..7973022 100644
--- a/lib/edit/t0000003.txt
+++ b/lib/edit/t0000003.txt
@@ -264,8 +264,8 @@ B:$4:A:2:Listen for rumors:10:10:u:19:0
 B:4:A:2:噂を聞く:10:10:u:19:0
 B:$4:A:3:Teleport to other town:500:500:m:42:0
 B:4:A:3:他の町へ移動:500:500:m:42:0
-B:$4: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:4: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:$4: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:1
+B:4: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:1

 B:$5:N:Beastmaster:Draxle:Draconian
 B:5:N:モンスター仙人:ドラクスル:ドラコニアン
diff --git a/lib/edit/t0000004.txt b/lib/edit/t0000004.txt
index 7850a3f..30cd5c6 100644
--- a/lib/edit/t0000004.txt
+++ b/lib/edit/t0000004.txt
@@ -171,8 +171,8 @@ B:$4:A:2:Listen for rumors:10:10:u:19:0
 B:4:A:2:噂を聞く:10:10:u:19:0
 B:$4:A:3:Teleport to other town:500:500:m:42:0
 B:4:A:3:他の町へ移動:500:500:m:42:0
-B:$4: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:4: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:$4: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:1
+B:4: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:1

 B:$5:N:Beastmaster:Aradreth:Elf
 B:5:N:モンスター仙人:アラドレス:エルフ
@@ -252,8 +252,8 @@ B:$13:A:0:Recall to dungeon:0:150:r:33:0
 B:13:A:0:ダンジョンへ帰還:0:150:r:33:0
 B:$13:A:1:Teleport to dungeon-level:100000:1000000:t:34:0
 B:13:A:1:階を指定してテレポート:100000:1000000:t:34:0
-B:$13:R:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
-B:13:R:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
+B:$13:R:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
+B:13:R:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
 B:$13:M:0:0:0:0:0:2:0:0:0:0
 B:13:M:0:0:0:0:0:2:0:0:0:0

diff --git a/lib/edit/t0000005.txt b/lib/edit/t0000005.txt
index f64b4bb..6bf5f6f 100644
--- a/lib/edit/t0000005.txt
+++ b/lib/edit/t0000005.txt
@@ -128,8 +128,8 @@ B:$4:A:1:Buy food and drink:2:2:f:18:1
 B:4:A:1:食事をする:2:2:f:18:1
 B:$4:A:2:Listen for rumors:10:10:u:19:0
 B:4:A:2:噂を聞く:10:10:u:19:0
-B:$4:R:2: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:4:R:2: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:$4:R:2: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:1
+B:4:R:2: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:1
 B:$4:A:3:Identify item:100:100:i:44:0
 B:4:A:3:アイテム鑑定:100:100:i:44:0

@@ -152,8 +152,8 @@ B:$14:A:0:Request quest:0:0:q:6:1
 B:14:A:0:クエスト:0:0:q:6:1
 B:$14:A:1:Cure mutation:1000:5000:m:35:0
 B:14:A:1:突然変異を治療する:1000:5000:m:35:0
-B:$14:R:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0
-B:14:R:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0
+B:$14:R:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0
+B:14:R:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:1:0:0:0:0:0:0:0:0
 B:$14:M:0:0:0:2:0:0:0:0:0:0
 B:14:M:0:0:0:2:0:0:0:0:0:0

その際に配列オーバーのバグも修正しました。

--- a/src/init1.c
+++ b/src/init1.c
@@ -3752,7 +3752,7 @@ static errr parse_line_feature(char *buf)
 static errr parse_line_building(char *buf)
 {
 	int i;
-	char *zz[37];
+	char *zz[1000];
 	int index;
 	char *s;

水流耐性を新しく実装してみる

さて、マーフォークとしての独自の仕様として、やはり水棲種族なのですから、レアな水流耐性など持っていても良いかもしれません。現状として仕様がないので、ここで新しく水流耐性を追加してみます。

まず新しくplayer_type構造体にresist_waterを加えて、calc_nonuses()に初期化を追加、種族がマーフォークならばTRUEにします。

--- a/src/types.h
+++ b/src/types.h
@@ -1248,6 +1248,7 @@ struct player_type
 	bool resist_neth;	/* Resist nether */
 	bool resist_fear;	/* Resist fear */
 	bool resist_time;	/* Resist time */
+	bool resist_water;	/* Resist water */

 	bool reflect;       /* Reflect 'bolt' attacks */
 	bool sh_fire;       /* Fiery 'immolation' effect */
--- a/src/xtra1.c
+++ b/src/xtra1.c
@@ -3290,6 +3290,7 @@ void calc_bonuses(void)
 	p_ptr->resist_blind = FALSE;
 	p_ptr->resist_neth = FALSE;
 	p_ptr->resist_time = FALSE;
+	p_ptr->resist_water = FALSE;
 	p_ptr->resist_fear = FALSE;
 	p_ptr->reflect = FALSE;
 	p_ptr->sh_fire = FALSE;
@@ -3770,6 +3771,8 @@ void calc_bonuses(void)
 			p_ptr->resist_pois = TRUE;
 			p_ptr->hold_exp = TRUE;
 			break;
+		case RACE_MERFOLK:
+			p_ptr->resist_water = TRUE;
 		default:
 			/* Do nothing */
 			;

そしてこの水流耐性がある場合、GF_WATERのダメージを喰らった時、轟音や混乱耐性とは別に朦朧や混乱に陥らず、ダメージが1/4になるようにしましょう。

diff --git a/src/spells1.c b/src/spells1.c
index b0f8750..b10d18b 100644
--- a/src/spells1.c
+++ b/src/spells1.c
@@ -5412,19 +5412,21 @@ static bool project_p(MONSTER_IDX who, cptr who_name, int r, POSITION y, POSITIO
 			if (fuzzy) msg_print(_("何か湿ったもので攻撃された!", "You are hit by something wet!"));
 			if (!CHECK_MULTISHADOW())
 			{
-				if (!p_ptr->resist_sound)
+				if (!p_ptr->resist_sound && !p_ptr->resist_water)
 				{
 					set_stun(p_ptr->stun + randint1(40));
 				}
-				if (!p_ptr->resist_conf)
+				if (!p_ptr->resist_conf && !p_ptr->resist_water)
 				{
 					set_confused(p_ptr->confused + randint1(5) + 5);
 				}

-				if (one_in_(5))
+				if (one_in_(5) && !p_ptr->resist_water)
 				{
 					inven_damage(set_cold_destroy, 3);
 				}
+
+				if (p_ptr->resist_water) get_damage /= 4;
 			}

ついでに重量オーバーでも溺れなくなります。

--- a/src/dungeon.c
+++ b/src/dungeon.c
@@ -1505,7 +1505,7 @@ static void process_world_aux_hp_and_sp(void)
 	}

 	if (have_flag(f_ptr->flags, FF_WATER) && have_flag(f_ptr->flags, FF_DEEP) &&
-	    !p_ptr->levitation && !p_ptr->can_swim)
+	    !p_ptr->levitation && !p_ptr->can_swim && !p_ptr->res_water)
 	{
 		if (p_ptr->total_weight > weight_limit())
 		{

大分それらしくなってきたと思います。このまま間延びですが後編へ。



2018/12/15

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

前回から続けていきます。

水地形にいるか否か、浮遊しているかどうかで加速したり減速したりする。

MtGのマーフォークは各次元(ともすれば絵師)によって下半身が魚や水蛇っぽかったり、二本脚であったり、それを時々に応じて変化させたり様々なようですが、総じて水中での機動に長けていて、陸上でも活動はできるが限度があるようです。

それに即して以下のようなルールを考えます。

  • 水地形(FF_WATERフラグ付き)の地形に入っている間は2+(レベル)/10の加速がつく。
  • それ以外の地形で浮遊を持っていない場合、加速-2。

これを実装するために、move_player_effect()の地形参照処理を直した上で、移動前と移動後の水地形の是非が切り替わる毎にプレイヤーの修正更新処理を呼び出すよう追加します。丁度前後には忍者の超隠密や早駆けの切替処理があります。

diff --git a/src/cmd1.c b/src/cmd1.c
index 5efc9be..c78e626 100644
--- a/src/cmd1.c
+++ b/src/cmd1.c
@@ -826,16 +826,17 @@ bool player_can_enter(s16b feature, u16b mode)
  */
 bool move_player_effect(POSITION ny, POSITION nx, BIT_FLAGS mpe_mode)
 {
+	POSITION oy = p_ptr->y;
+	POSITION ox = p_ptr->x;
 	cave_type *c_ptr = &cave[ny][nx];
+	cave_type *oc_ptr = &cave[oy][ox];
 	feature_type *f_ptr = &f_info[c_ptr->feat];
+	feature_type *of_ptr = &f_info[oc_ptr->feat];

 	if (!(mpe_mode & MPE_STAYING))
 	{
-		POSITION oy = p_ptr->y;
-		POSITION ox = p_ptr->x;
-		cave_type *oc_ptr = &cave[oy][ox];
-		IDX om_idx = oc_ptr->m_idx;
-		IDX nm_idx = c_ptr->m_idx;
+		MONSTER_IDX om_idx = oc_ptr->m_idx;
+		MONSTER_IDX nm_idx = c_ptr->m_idx;

 		/* Move the player */
 		p_ptr->y = ny;
@@ -885,7 +886,6 @@ bool move_player_effect(POSITION ny, POSITION nx, BIT_FLAGS mpe_mode)
 		}

 		p_ptr->update |= (PU_VIEW | PU_LITE | PU_FLOW | PU_MON_LITE | PU_DISTANCE);
-
 		p_ptr->window |= (PW_OVERHEAD | PW_DUNGEON);

 		/* Remove "unsafe" flag */
@@ -908,6 +908,15 @@ bool move_player_effect(POSITION ny, POSITION nx, BIT_FLAGS mpe_mode)
 			msg_print(_("ここでは素早く動けない。", "You cannot run in here."));
 			set_action(ACTION_NONE);
 		}
+
+		if (p_ptr->prace == RACE_MERFOLK)
+		{
+			if(have_flag(f_ptr->flags, FF_WATER) ^ have_flag(of_ptr->flags, FF_WATER))
+			{
+				p_ptr->update |= PU_BONUS;
+				update_stuff();
+			}
+		}
 	}

 	if (mpe_mode & MPE_ENERGY_USE)

calc_bonuses()の種族特性switch文に以下のように条件を追加します。

@@ -3773,6 +3776,15 @@ void calc_bonuses(void)
 			break;
 		case RACE_MERFOLK:
 			p_ptr->resist_water = TRUE;
+			if(have_flag(f_ptr->flags, FF_WATER))
+			{
+				new_speed += (2 + p_ptr->lev / 10);
+			}
+			else if(!p_ptr->levitation)
+			{
+				new_speed -= 2;
+			}
+			break;
 		default:
 			/* Do nothing */
 			;

おまけに浮遊の指輪を装備させる。

この実装を済ませた上で、ちょっと戦士で盗賊クエに行ったのですがちょっと参りました。最序盤の減速-2はかなり大きく、追い剥ぎとやり合ってギリで負けています。実際-2は通常の0.8倍になってしまうのですから厳しいかも知れません。

減速を-1としてもいいですが、そもそも自力救済が求められるような世界で、従来水棲の種族が地上で長く滞在する以上は、魔法の装備で問題を軽減するのも当然かも知れません。

浮遊の指輪自体は最序盤のアイテムにして、早々に装備枠を逼迫させる要因になる程度のものですし、種族マーフォークを選んでいる場合は常に浮遊の指輪を与えることにしましょう。

birth.cにplayer_outfit()への追加を行います。

diff --git a/src/birth.c b/src/birth.c
index 142896f..ad99a51 100644
--- a/src/birth.c
+++ b/src/birth.c
@@ -2393,6 +2393,13 @@ void player_outfit(void)
 	}
 	q_ptr = &forge;

+	if (p_ptr->prace == RACE_MERFOLK)
+	{
+		object_prep(q_ptr, lookup_kind(TV_RING, SV_RING_LEVITATION_FALL));
+		q_ptr->number = 1;
+		add_outfit(q_ptr);
+	}
+
 	if ((p_ptr->pclass == CLASS_RANGER) || (p_ptr->pclass == CLASS_CAVALRY))
 	{
 		/* Hack -- Give the player some arrows */

calc_bonuses()のミスを修正

ここまでで浮遊の指輪を自動装備させて気づきました。calc_bonuses()内の処理で装備による浮遊がTRUEになる前から、マーフォークの減速判定を行っているため、適用がされていません。プレイヤー自身の耐性や能力などに関わる処理はcalc_bonuses()の後半に回しましょう。

---
 src/xtra1.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/xtra1.c b/src/xtra1.c
index 2065f39..40cd5bf 100644
--- a/src/xtra1.c
+++ b/src/xtra1.c
@@ -3776,14 +3776,6 @@ void calc_bonuses(void)
 			break;
 		case RACE_MERFOLK:
 			p_ptr->resist_water = TRUE;
-			if(have_flag(f_ptr->flags, FF_WATER))
-			{
-				new_speed += (2 + p_ptr->lev / 10);
-			}
-			else if(!p_ptr->levitation)
-			{
-				new_speed -= 2;
-			}
 			break;
 		default:
 			/* Do nothing */
@@ -4898,6 +4890,20 @@ void calc_bonuses(void)
 	/* Searching slows the player down */
 	if (p_ptr->action == ACTION_SEARCH) new_speed -= 10;

+	/* Feature bonus */
+	if(p_ptr->prace == RACE_MERFOLK)
+	{
+		if (have_flag(f_ptr->flags, FF_WATER))
+		{
+			new_speed += (2 + p_ptr->lev / 10);
+		}
+		else if (!p_ptr->levitation)
+		{
+			new_speed -= 2;
+		}
+	}
+
+
 	/* Actual Modifier Bonuses (Un-inflate stat bonuses) */
 	p_ptr->to_a += ((int)(adj_dex_ta[p_ptr->stat_ind[A_DEX]]) - 128);
 	p_ptr->to_d[0] += ((int)(adj_str_td[p_ptr->stat_ind[A_STR]]) - 128);

大体できました

この位まで実装すれば、種族としての特化は十分にできたと思います。まだ、生い立ち設定、援軍指定とか本当に正式に詰めることは、細々とありますが、一旦ここで完結とさせてもらおうと思います。

正式なアップは2.2.2として調整済ませて出します。お楽しみに。

マーフォーク

2019/02/14 追記 マーフォーク用の rgold_adj を追加

忘れていました(小声)rgold_adjは店の主との種族的相性補正を与える者です。ないと色々まずいのになぜ今まで動いていたのか。

ともかく面倒ですが必ず入れましょう。


2018/12/16

[魚類版深夜の真剣お絵描き60分一本勝負]ハコフグ

遅刻した上に雑、ついでにお題を前日と間違えた。やっちゃったぜ。

ハコフグ


2018/12/17

[変愚蛮怒/開発]変愚蛮怒開発日誌part107…特別編「変愚蛮怒の技能実装例 ~剣は捨てても盾は捨てるなという名ゼリフを知らないのかよ~」

Roguelike Advent Calendar 2018の17日目です。肩こりと眠気でまた遅刻しました。ゆ る し て。

今回もしつこく記事のためという不純な動機で新しく盾技能などを追加してみようと思います。例によってブランチを切って調整などは済ませてから正規実装予定です。

盾技能の枠を追加する。

現在実装されている技能枠はマーシャルアーツ、乗馬、二刀流の三種。実装当初から配列上10枠用意してくれているため面倒になるセーブデータ処理の追加は行わずに済みそうです。

ただ、マジックナンバーはやめろください。ということで直しながら以下のように。

diff --git a/src/cmd4.c b/src/cmd4.c
index c698567..63afa94 100644
--- a/src/cmd4.c
+++ b/src/cmd4.c
@@ -5787,9 +5787,13 @@ static void do_cmd_knowledge_skill_exp(void)
 	FILE *fff;

 	char file_name[1024];
-	char skill_name[3][20]={_("マーシャルアーツ", "Martial Arts    "),
-							_("二刀流          ", "Dual Wielding   "),
-							_("乗馬            ", "Riding          ")};
+	char skill_name[GINOU_TEMPMAX][20] =
+	{
+		_("マーシャルアーツ", "Martial Arts    "),
+		_("二刀流          ", "Dual Wielding   "),
+		_("乗馬            ", "Riding          "),
+		_("盾              ", "Shield          ")
+	};

 	/* Open a new file */
 	fff = my_fopen_temp(file_name, 1024);
@@ -5799,7 +5803,7 @@ static void do_cmd_knowledge_skill_exp(void)
 	    return;
 	}

-	for (i = 0; i < 3; i++)
+	for (i = 0; i < GINOU_TEMPMAX; i++)
 	{
 		skill_exp = p_ptr->skill_exp[i];
 		fprintf(fff, "%-20s ", skill_name[i]);
diff --git a/src/defines.h b/src/defines.h
index ec8e7db..06d5052 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -4661,6 +4661,8 @@ extern int PlayerUID;
 #define GINOU_SUDE      0
 #define GINOU_NITOURYU  1
 #define GINOU_RIDING    2
+#define GINOU_SHIELD    3
+#define GINOU_TEMPMAX   4
 #define GINOU_MAX      10

1

s_info.txtに各職業ごとの初期値、最大値を指定する

上記の通り今のままではどんな職業でも初期値最大値が0なので、s_info.txtに新しく追加してやる必要があります。

長いのでdiff張りつけは略します。各職業ごとに追加。全く無関係な話ですが武器経験値は似たような武器であれこれ付けすぎて冗長な気はします。

盾技能上昇処理追加

盾を装備している時に、相手の攻撃をかわすことに成功した際に追加。

現状は惰性で他の技能値とほぼ似たような上昇条件です。

 src/melee1.c | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

diff --git a/src/melee1.c b/src/melee1.c
index 1406cac..e10c385 100644
--- a/src/melee1.c
+++ b/src/melee1.c
@@ -1455,8 +1455,8 @@ bool py_attack(POSITION y, POSITION x, BIT_FLAGS mode)

 		if (cur < max)
 		{
-			int ridinglevel = r_info[m_list[p_ptr->riding].r_idx].level;
-			int targetlevel = r_ptr->level;
+			DEPTH ridinglevel = r_info[m_list[p_ptr->riding].r_idx].level;
+			DEPTH targetlevel = r_ptr->level;
 			int inc = 0;

 			if ((cur / 200 - 5) < targetlevel)
@@ -1472,7 +1472,6 @@ bool py_attack(POSITION y, POSITION x, BIT_FLAGS mode)
 			}

 			p_ptr->skill_exp[GINOU_RIDING] = MIN(max, cur + inc);
-
 			p_ptr->update |= (PU_BONUS);
 		}
 	}
@@ -3224,6 +3223,33 @@ bool make_attack_normal(MONSTER_IDX m_idx)
 #endif

 				}
+
+				/* Gain shield experience */
+				if (object_is_armour(&inventory[INVEN_RARM]) || object_is_armour(&inventory[INVEN_LARM]))
+				{
+					int cur = p_ptr->skill_exp[GINOU_SHIELD];
+					int max = s_info[p_ptr->pclass].s_max[GINOU_SHIELD];
+
+					if (cur < max)
+					{
+						DEPTH targetlevel = r_ptr->level;
+						int inc = 0;
+
+
+						/* Extra experience */
+						if ((cur / 100) < targetlevel)
+						{
+							if ((cur / 100 + 15) < targetlevel)
+								inc += 1 + (targetlevel - (cur / 100 + 15));
+							else
+								inc += 1;
+						}
+
+						p_ptr->skill_exp[GINOU_SHIELD] = MIN(max, cur + inc);
+						p_ptr->update |= (PU_BONUS);
+					}
+				}
+
 				damage = 0;

 				break;

盾技能によるAC修正追加

例によってデカいcalc_bonuses()内にぽつりと。追加ACが最大+12は小さいか大きいか、正式実装までには調整したいと思います。

diff --git a/src/xtra1.c b/src/xtra1.c
index 3795201..3a84445 100644
--- a/src/xtra1.c
+++ b/src/xtra1.c
@@ -4355,6 +4355,13 @@ void calc_bonuses(void)
 		}
 	}

+	/* Shield skill bonus */
+	if (object_is_armour(&inventory[INVEN_RARM]) || object_is_armour(&inventory[INVEN_LARM]))
+	{
+		p_ptr->ac += p_ptr->skill_exp[GINOU_SHIELD] * (1 + p_ptr->lev / 22) / 2000;
+		p_ptr->dis_ac += p_ptr->skill_exp[GINOU_SHIELD] * (1 + p_ptr->lev / 22) / 2000;
+	}
+
 	if (old_mighty_throw != p_ptr->mighty_throw)
 	{
 		/* Redraw average damege display of Shuriken */

2

技能値8000でレベル6ならば+4修正。2(耐久修正)+3(盾AC)+14(鎧AC)+4=23で計算通り。

こんなんなりました。以上!終わり!閉廷!


2018/12/18

[変愚蛮怒/開発]変愚蛮怒開発日誌part108…更新処理整理

どうせ、handle_stuff()でまとめているなら半端にするなよということで。


2018/12/19

[変愚蛮怒/開発]変愚蛮怒開発日誌part109…特別編「変愚蛮怒の地形実装例 ~毒沼やら酸の沼やら~(前編)」

今日の枠が埋まりそうになかったのでしつこくやっていきます。今回の地形追加は、以前から少しだけ手を付けてみた内容であったりします。

現状ダメージ地形は溶岩と、浮遊なし&重量オーバー時の溺れ効果しかないので、折角だからもっと属性ダメージフィールドを増やしてみようという魂胆です。

FF_*定義

まずがf_info.txtで用いる地形フラグであるFF_*を新しく定義してみましょう。

例によってdefines.hへの追加と

diff --git a/src/defines.h b/src/defines.h
index 1cf2681..21b2982 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -988,15 +988,15 @@
 #define FF_LAVA          40
 #define FF_SHALLOW       41
 #define FF_DEEP          42
-/* #define FF_FILLED        43 */
+#define FF_POISON_PUDDLE 43
 #define FF_HURT_ROCK     44
 /* #define FF_HURT_FIRE     45 */
 /* #define FF_HURT_COLD     46 */
 /* #define FF_HURT_ACID     47 */
-/* #define FF_ICE           48 */
-/* #define FF_ACID          49 */
-/* #define FF_OIL           50 */
-/* #define FF_XXX04      51 */
+#define FF_COLD_PUDDLE   48
+#define FF_ACID_PUDDLE   49
+/* #define FF_OIL           50
+#define FF_ELEC_PUDDLE   51
 /* #define FF_CAN_CLIMB     52 */
 #define FF_CAN_FLY       53
 #define FF_CAN_SWIM      54

f_info.txtで用いるフラグ文字列の追加です。こうして見てみると結構没になっていたらしい種々の地形フラグの形跡はあるようです。

diff --git a/src/init1.c b/src/init1.c
index 4e70c73..c98898f 100644
--- a/src/init1.c
+++ b/src/init1.c
@@ -189,15 +189,15 @@ static cptr f_info_flags[] =
 	"LAVA",
 	"SHALLOW",
 	"DEEP",
-	"FILLED",
+	"POISON_PUDDLE",
 	"HURT_ROCK",
 	"HURT_FIRE",
 	"HURT_COLD",
 	"HURT_ACID",
-	"ICE",
-	"ACID",
+	"COLD_PUDDLE",
+	"ACID_PUDDLE",
 	"OIL",
-	"XXX04",
+	"ELEC_PUDDLE",
 	"CAN_CLIMB",
 	"CAN_FLY",
 	"CAN_SWIM",

地形ダメージ処理実装

dungeon.cの溶岩の熱処理を実装している場所に、そのまま参考にしつつ冷気、電撃、酸、毒ダメージの実装をしてみます。表現や対応する英文はちと苦しいかもしれません。

--- a/src/dungeon.c
+++ b/src/dungeon.c
@@ -1502,6 +1502,160 @@ static void process_world_aux_hp_and_sp(void)
 		}
 	}

+	if (have_flag(f_ptr->flags, FF_COLD_PUDDLE) && !IS_INVULN() && !p_ptr->immune_cold)
+	{
+		int damage = 0;
+
+		if (have_flag(f_ptr->flags, FF_DEEP))
+		{
+			damage = 6000 + randint0(4000);
+		}
+		else if (!p_ptr->levitation)
+		{
+			damage = 3000 + randint0(2000);
+		}
+
+		if (damage)
+		{
+			if (p_ptr->resist_cold) damage = damage / 3;
+			if (IS_OPPOSE_COLD()) damage = damage / 3;
+			if (p_ptr->levitation) damage = damage / 5;
+
+			damage = damage / 100 + (randint0(100) < (damage % 100));
+
+			if (p_ptr->levitation)
+			{
+				msg_print(_("冷気に覆われた!", "The cold engulfs you!"));
+				take_hit(DAMAGE_NOESCAPE, damage, format(_("%sの上に浮遊したダメージ", "flying over %s"),
+					f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name), -1);
+			}
+			else
+			{
+				cptr name = f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name;
+				msg_format(_("%sに凍えた!", "The %s frostbites you!"), name);
+				take_hit(DAMAGE_NOESCAPE, damage, name, -1);
+			}
+
+			cave_no_regen = TRUE;
+		}
+	}
+
+	if (have_flag(f_ptr->flags, FF_ELEC_PUDDLE) && !IS_INVULN() && !p_ptr->immune_elec)
+	{
+		int damage = 0;
+
+		if (have_flag(f_ptr->flags, FF_DEEP))
+		{
+			damage = 6000 + randint0(4000);
+		}
+		else if (!p_ptr->levitation)
+		{
+			damage = 3000 + randint0(2000);
+		}
+
+		if (damage)
+		{
+			if (p_ptr->resist_elec) damage = damage / 3;
+			if (IS_OPPOSE_ELEC()) damage = damage / 3;
+			if (p_ptr->levitation) damage = damage / 5;
+
+			damage = damage / 100 + (randint0(100) < (damage % 100));
+
+			if (p_ptr->levitation)
+			{
+				msg_print(_("電撃を受けた!", "The electric shocks you!"));
+				take_hit(DAMAGE_NOESCAPE, damage, format(_("%sの上に浮遊したダメージ", "flying over %s"),
+					f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name), -1);
+			}
+			else
+			{
+				cptr name = f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name;
+				msg_format(_("%sに感電した!", "The %s shocks you!"), name);
+				take_hit(DAMAGE_NOESCAPE, damage, name, -1);
+			}
+
+			cave_no_regen = TRUE;
+		}
+	}
+
+	if (have_flag(f_ptr->flags, FF_ACID_PUDDLE) && !IS_INVULN() && !p_ptr->immune_acid)
+	{
+		int damage = 0;
+
+		if (have_flag(f_ptr->flags, FF_DEEP))
+		{
+			damage = 6000 + randint0(4000);
+		}
+		else if (!p_ptr->levitation)
+		{
+			damage = 3000 + randint0(2000);
+		}
+
+		if (damage)
+		{
+			if (p_ptr->resist_acid) damage = damage / 3;
+			if (IS_OPPOSE_ACID()) damage = damage / 3;
+			if (p_ptr->levitation) damage = damage / 5;
+
+			damage = damage / 100 + (randint0(100) < (damage % 100));
+
+			if (p_ptr->levitation)
+			{
+				msg_print(_("酸を受けた!", "The acid melt you!"));
+				take_hit(DAMAGE_NOESCAPE, damage, format(_("%sの上に浮遊したダメージ", "flying over %s"),
+					f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name), -1);
+			}
+			else
+			{
+				cptr name = f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name;
+				msg_format(_("%sに溶かされた!", "The %s melts you!"), name);
+				take_hit(DAMAGE_NOESCAPE, damage, name, -1);
+			}
+
+			cave_no_regen = TRUE;
+		}
+	}
+
+	if (have_flag(f_ptr->flags, FF_POISON_PUDDLE) && !IS_INVULN())
+	{
+		int damage = 0;
+
+		if (have_flag(f_ptr->flags, FF_DEEP))
+		{
+			damage = 6000 + randint0(4000);
+		}
+		else if (!p_ptr->levitation)
+		{
+			damage = 3000 + randint0(2000);
+		}
+
+		if (damage)
+		{
+			if (p_ptr->resist_pois) damage = damage / 3;
+			if (IS_OPPOSE_POIS()) damage = damage / 3;
+			if (p_ptr->levitation) damage = damage / 5;
+
+			damage = damage / 100 + (randint0(100) < (damage % 100));
+
+			if (p_ptr->levitation)
+			{
+				msg_print(_("毒気を吸い込んだ!", "The gas poisons you!"));
+				take_hit(DAMAGE_NOESCAPE, damage, format(_("%sの上に浮遊したダメージ", "flying over %s"),
+					f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name), -1);
+				if (p_ptr->resist_pois) (void)set_poisoned(p_ptr->poisoned + 1);
+			}
+			else
+			{
+				cptr name = f_name + f_info[get_feat_mimic(&cave[p_ptr->y][p_ptr->x])].name;
+				msg_format(_("%sに毒された!", "The %s poisons you!"), name);
+				take_hit(DAMAGE_NOESCAPE, damage, name, -1);
+				if (p_ptr->resist_pois) (void)set_poisoned(p_ptr->poisoned + 3);
+			}
+
+			cave_no_regen = TRUE;
+		}
+	}
+
 	if (have_flag(f_ptr->flags, FF_WATER) && have_flag(f_ptr->flags, FF_DEEP) &&
 	    !p_ptr->levitation && !p_ptr->can_swim)
 	{

f_info.txtに各FF_*を当てた新地形を追加。

同じく、これも地形名とかもう少しスマートな表現はあっていい気はします。多少前述のルールの変更も含めて。

--- a/lib/edit/f_info.txt
+++ b/lib/edit/f_info.txt
@@ -1796,3 +1796,67 @@ W:2
 K:DESTROYED:SHALLOW_WATER
 F:POWER_40 | LOS | REMEMBER | TUNNEL | WALL | HURT_ROCK | CAN_PASS |
 F:HURT_DISI | GLASS
+
+N:227:HEAVY_COLD_ZONE
+J:極低温帯
+E:heavy cold zone
+G:~:W
+W:2
+F:LOS | PROJECT | MOVE | PLACE | REMEMBER | GLOW | COLD_PUDDLE | DEEP | CAN_FLY |
+F:TELEPORTABLE
+
+N:228:COLD_ZONE
+J:低温帯
+E:cold zone
+G:~:w
+W:2
+F:LOS | PROJECT | MOVE | PLACE | DROP | REMEMBER | COLD_PUDDLE | SHALLOW | CAN_FLY |
+F:TELEPORTABLE
+
+N:229:HEAVY_ELECTRICAL_ZONE
+J:高圧帯電帯
+E:heavy electrical zone
+G:~:y
+W:2
+F:LOS | PROJECT | MOVE | PLACE | REMEMBER | GLOW | ELEC_PUDDLE | DEEP | CAN_FLY |
+F:TELEPORTABLE
+
+N:230:ELECTRICAL_ZONE
+J:帯電帯
+E:electrical zone
+G:~:o
+W:2
+F:LOS | PROJECT | MOVE | PLACE | DROP | REMEMBER | ELEC_PUDDLE | SHALLOW | CAN_FLY |
+F:TELEPORTABLE
+
+N:231:DEEP_ACID_PUDDLE
+J:深い酸の沼
+E:deep acid puddle
+G:~:u
+W:2
+F:LOS | PROJECT | MOVE | PLACE | REMEMBER | GLOW | ACID_PUDDLE | DEEP | CAN_FLY |
+F:TELEPORTABLE
+
+N:232:SHALLOW_ACID_PUDDLE
+J:浅い酸の沼
+E:shallow acid puddle
+G:~:U
+W:2
+F:LOS | PROJECT | MOVE | PLACE | DROP | REMEMBER | ACID_PUDDLE | SHALLOW | CAN_FLY |
+F:TELEPORTABLE
+
+N:233:DEEP_POISONOUS_PUDDLE
+J:深い毒の沼
+E:deep poisonous puddle
+G:~:g
+W:2
+F:LOS | PROJECT | MOVE | PLACE | REMEMBER | GLOW | POISON_PUDDLE | DEEP | CAN_FLY |
+F:TELEPORTABLE
+
+N:234:SHALLOW_ACID_PUDDLE
+J:浅い毒の沼
+E:shallow poisonous puddle
+G:~:G
+W:2
+F:LOS | PROJECT | MOVE | PLACE | DROP | REMEMBER | POISON_PUDDLE | SHALLOW | CAN_FLY |
+F:TELEPORTABLE

そんなこんなで以下の通り追加できました。次回で実際にダンジョンにこれらの地形を生成する手段を追加します。

0





2018/12/23

[変愚蛮怒/開発]変愚蛮怒開発日誌part111…さらにバグ2件修正

こんなん直しました


2018/12/24

[変愚蛮怒/開発]変愚蛮怒開発日誌part112…特別編「変愚蛮怒の地形実装例 ~毒沼やら酸の沼やら~(中編)」

前回はこちら。風邪でダウンしたりMtG Arenaにハマってしまったりしたのでこの件も全中後編で行きます。ゆ る し て。

グローバル変数上の定義も追加する

前回、f_info.txtで定義を追加しましたが、ダンジョンフロア作成処理向けには別途グローバル変数で各IDを加え込む処理が必要だったのでそれを追加しました。このコミットには、他にちょっとした修正も入っています。

diff --git a/src/externs.h b/src/externs.h
index c639cc2..34e84b8 100644
--- a/src/externs.h
+++ b/src/externs.h
@@ -428,6 +428,14 @@ extern FEAT_IDX feat_deep_water;
 extern FEAT_IDX feat_shallow_water;
 extern FEAT_IDX feat_deep_lava;
 extern FEAT_IDX feat_shallow_lava;
+extern FEAT_IDX feat_heavy_cold_zone;
+extern FEAT_IDX feat_cold_zone;
+extern FEAT_IDX feat_heavy_electrical_zone;
+extern FEAT_IDX feat_electrical_zone;
+extern FEAT_IDX feat_deep_acid_puddle;
+extern FEAT_IDX feat_shallow_acid_puddle;
+extern FEAT_IDX feat_deep_poisonous_puddle;
+extern FEAT_IDX feat_shallow_poisonous_puddle;
 extern FEAT_IDX feat_dirt;
 extern FEAT_IDX feat_grass;
 extern FEAT_IDX feat_flower;
diff --git a/src/init2.c b/src/init2.c
index c95785d..f64375e 100644
--- a/src/init2.c
+++ b/src/init2.c
@@ -1737,6 +1737,14 @@ static errr init_feat_variables(void)
 	feat_shallow_water = f_tag_to_index_in_init("SHALLOW_WATER");
 	feat_deep_lava = f_tag_to_index_in_init("DEEP_LAVA");
 	feat_shallow_lava = f_tag_to_index_in_init("SHALLOW_LAVA");
+	feat_heavy_cold_zone = f_tag_to_index_in_init("HEAVY_COLD_ZONE");
+	feat_cold_zone = f_tag_to_index_in_init("COLD_ZONE");
+	feat_heavy_electrical_zone = f_tag_to_index_in_init("HEAVY_ELECTRICAL_ZONE");
+	feat_electrical_zone = f_tag_to_index_in_init("ELECTRICAL_ZONE");
+	feat_deep_acid_puddle = f_tag_to_index_in_init("DEEP_ACID_PUDDLE");
+	feat_shallow_acid_puddle = f_tag_to_index_in_init("SHALLOW_ACID_PUDDLE");
+	feat_deep_poisonous_puddle = f_tag_to_index_in_init("DEEP_POISONOUS_PUDDLE");
+	feat_shallow_poisonous_puddle = f_tag_to_index_in_init("SHALLOW_POISONOUS_PUDDLE");
 	feat_dirt = f_tag_to_index_in_init("DIRT");
 	feat_grass = f_tag_to_index_in_init("GRASS");
 	feat_flower = f_tag_to_index_in_init("FLOWER");
diff --git a/src/variable.c b/src/variable.c
index fd8958e..f8cf1b9 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -935,6 +935,14 @@ FEAT_IDX feat_deep_water;
 FEAT_IDX feat_shallow_water;
 FEAT_IDX feat_deep_lava;
 FEAT_IDX feat_shallow_lava;
+FEAT_IDX feat_heavy_cold_zone;
+FEAT_IDX feat_cold_zone;
+FEAT_IDX feat_heavy_electrical_zone;
+FEAT_IDX feat_electrical_zone;
+FEAT_IDX feat_deep_acid_puddle;
+FEAT_IDX feat_shallow_acid_puddle;
+FEAT_IDX feat_deep_poisonous_puddle;
+FEAT_IDX feat_shallow_poisonous_puddle;
 FEAT_IDX feat_dirt;
 FEAT_IDX feat_grass;
 FEAT_IDX feat_flower;
--

d_info.txt向けのダンジョンフラグを追加する

これらの地形をさらにダンジョンフロアに生成するためのフラグを作成し、それを各ダンジョンの定義を行う、d_info.txtに追加します。

diff --git a/src/init1.c b/src/init1.c
index 8b4d290..4f3f150 100644
--- a/src/init1.c
+++ b/src/init1.c
@@ -963,8 +963,8 @@ static cptr d_info_flags1[] =
 	"CAVE",
 	"CAVERN",
 	"ARCADE",
-	"XXX",
-	"XXX",
+	"LAKE_ACID",
+	"LAKE_POISONOUS",
 	"XXX",
 	"FORGET",
 	"LAKE_WATER",
@@ -980,8 +980,8 @@ static cptr d_info_flags1[] =
 	"NO_MELEE",
 	"CHAMELEON",
 	"DARKNESS",
-	"XXX",
-	"XXX"
+	"ACID_RIVER",
+	"POISONOUS_RIVER"
 };

新たに適用されるダンジョンは、まあそれっぽいものに。

---
 lib/edit/d_info.txt | 6 ++++++
 src/init1.c         | 8 ++++----
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/lib/edit/d_info.txt b/lib/edit/d_info.txt
index cdb6f3f..5aa9142 100644
--- a/lib/edit/d_info.txt
+++ b/lib/edit/d_info.txt
@@ -62,6 +62,7 @@ L:FLOOR:100:FLOOR:0:FLOOR:0:50
 A:GRANITE:100:GRANITE:0:GRANITE:0:GRANITE:GRANITE:MAGMA_VEIN:QUARTZ_VEIN
 F:CAVERN | WATER_RIVER | LAVA_RIVER | ARENA | DESTROY | CURTAIN | GLASS_ROOM |
 F:LAKE_WATER | LAKE_LAVA | LAKE_RUBBLE | LAKE_TREE | ARCADE
+F:LAKE_ACID | ACID_RIVER | LAKE_POISONOUS | POISONOUS_RIVER
 F:MONSTER_DIV_64

 N:2:イークの洞穴
@@ -111,6 +112,7 @@ A:GRANITE:90:DARK_PIT:10:GRANITE:0:GRANITE:GRANITE:MAGMA_VEIN:DARK_PIT
 F:BIG | LAVA_RIVER | CAVERN | DESTROY
 F:MONSTER_DIV_10
 F:CAVE | LAKE_LAVA | LAKE_TREE | LAKE_RUBBLE
+F:LAKE_ACID | ACID_RIVER | LAKE_POISONOUS | POISONOUS_RIVER
 M:DRAGON | R_CHAR_dD

 # Vecna is there, guarding Soulsword
@@ -124,6 +126,7 @@ L:FLOOR:85:SHALLOW_WATER:15:FLOOR:0:30
 A:GRANITE:75:DARK_PIT:25:GRANITE:0:GRANITE:GRANITE:SHALLOW_WATER:DEEP_WATER
 F:MONSTER_DIV_4 | FINAL_GUARDIAN_804 | FINAL_ARTIFACT_89
 F:WATER_RIVER | DESTROY | ARENA | LAKE_WATER | LAKE_RUBBLE | DESTROY
+F:LAKE_POISONOUS | POISONOUS_RIVER
 M:UNDEAD | NONLIVING

 N:7:森
@@ -208,6 +211,7 @@ L:FLOOR:50:SHALLOW_WATER:30:DEEP_WATER:20:100
 A:GRANITE:100:GRANITE:0:GRANITE:0:GRANITE:GRANITE:SHALLOW_WATER:DEEP_WATER
 F:MONSTER_DIV_16 | ARENA | WATER_RIVER | LAKE_WATER |
 F:FINAL_GUARDIAN_857 | FINAL_ARTIFACT_129
+F:LAKE_ACID | ACID_RIVER | LAKE_POISONOUS | POISONOUS_RIVER
 M:DEMON | ELDRITCH_HORROR

 N:14:山
@@ -232,6 +236,7 @@ L:FLOOR:100:FLOOR:0:FLOOR:0:0
 A:GRANITE:60:MAGMA_TREASURE:20:QUARTZ_TREASURE:20:GRANITE:GRANITE:MAGMA_VEIN:QUARTZ_VEIN
 F:MONSTER_DIV_1 | WATER_RIVER | CAVE | CAVERN | SMALLEST
 F:LAVA_RIVER | DESTROY
+F:LAKE_ACID | ACID_RIVER
 M:R_CHAR_$

 N:16:反魔法の洞窟
@@ -281,6 +286,7 @@ L:FLOOR:100:FLOOR:0:FLOOR:0:50
 A:GRANITE:100:GRANITE:0:GRANITE:0:GRANITE:GRANITE:MAGMA_VEIN:QUARTZ_VEIN
 F:CAVERN | WATER_RIVER | LAVA_RIVER | ARENA | DESTROY | GLASS_ROOM |
 F:LAKE_WATER | LAKE_LAVA | LAKE_RUBBLE | LAKE_TREE
+F:LAKE_ACID | ACID_RIVER | LAKE_POISONOUS | POISONOUS_RIVER
 F:MONSTER_DIV_64 | DARKNESS | FINAL_GUARDIAN_803

 N:20:ガラスの城

ACID_RIVERとPOISONOUS_RIVERを実装する。

溶岩の川の実装をベースに。普通の川を先に優先しつつ、そちらが選ばれなければフラグのある限り、他の川を当確率に指定します。

diff --git a/src/floor-generate.c b/src/floor-generate.c
index 77b11d3..fb33ba0 100644
--- a/src/floor-generate.c
+++ b/src/floor-generate.c
@@ -711,20 +711,43 @@ static bool cave_gen(void)
 		/* Hack -- Add some rivers */
 		if (one_in_(3) && (randint1(dun_level) > 5))
 		{
-			IDX feat1 = 0, feat2 = 0;
+			FEAT_IDX feat1 = 0, feat2 = 0;

-			/* Choose water or lava */
+			/* Choose water mainly */
 			if ((randint1(MAX_DEPTH * 2) - 1 > dun_level) && (d_info[dungeon_type].flags1 & DF1_WATER_RIVER))
 			{
 				feat1 = feat_deep_water;
 				feat2 = feat_shallow_water;
 			}
-			else if  (d_info[dungeon_type].flags1 & DF1_LAVA_RIVER)
+			else /* others */
 			{
-				feat1 = feat_deep_lava;
-				feat2 = feat_shallow_lava;
+				FEAT_IDX select_deep_feat[10];
+				FEAT_IDX select_shallow_feat[10];
+				int select_id_max = 0, selected;
+
+				if (d_info[dungeon_type].flags1 & DF1_LAVA_RIVER)
+				{
+					select_deep_feat[select_id_max] = feat_deep_lava;
+					select_shallow_feat[select_id_max] = feat_shallow_lava;
+					select_id_max++;
+				}
+				if (d_info[dungeon_type].flags1 & DF1_POISONOUS_RIVER)
+				{
+					select_deep_feat[select_id_max] = feat_deep_poisonous_puddle;
+					select_shallow_feat[select_id_max] = feat_shallow_poisonous_puddle;
+					select_id_max++;
+				}
+				if (d_info[dungeon_type].flags1 & DF1_ACID_RIVER)
+				{
+					select_deep_feat[select_id_max] = feat_deep_acid_puddle;
+					select_shallow_feat[select_id_max] = feat_shallow_acid_puddle;
+					select_id_max++;
+				}
+
+				selected = randint1(select_id_max);
+				feat1 = select_deep_feat[selected];
+				feat2 = select_shallow_feat[selected];
 			}
-			else feat1 = 0;

 			if (feat1)
 			{
--

んでもって

で き ま し た。

冷気と電撃、他に池などは引き続き次回の明日に。

酸の川1

酸の川2

本日のツッコミ(全2件) [ツッコミを入れる]

sunawa [ぼくも仕事中にArenaのデッキレシピ考えてるせいで仕事が進みません(半ギレ) 酸の沼はくさそう、冷気属性の床はス..]

deskull [毒とかもう岡山の県北が-more- Arenaいいですよね、もう当分中毒です(マジキチスマイル)]


2018/12/25

[変愚蛮怒/攻略] 変愚蛮怒開発日誌part113…特別編「変愚蛮怒の地形実装例 ~毒沼やら酸の沼やら~(後編)」

相変わらずトリを欲しがる割には盛り上がらない最後ですが、前回の続きで最後です。

前提:新しい固定部屋生成処理を追加してみる

前回までで溶岩と同じ要領で、酸と毒の川を作成しました。このまま同じように冷気と電撃もなし崩しにやっていいのかもしれませんが、基本的に川として生成される要素ではない気もします。

そこで今回は、これまで大きく分けて16種類だった部屋生成に17番目の「特殊固定部屋」を設定し(実はこの件より前に実装したものだったりしますが)v_info.txtにこれまで実装された地形を追加してみましょう。 これまでの部屋については、

なども参照ください。

今回作成するType17は、このうち、Type7/8の固定Vaultと同じように、v_info.txtで予め作られた固定の地形を生成する形にします。固定Vaultとの違いは、単純にアイテムや大量の深層的が生成されず、比較的部屋の生成確率を高くする、それだけです。

Type17(特殊固定部屋)の処理実装実装

基本、Type7/8のコピペです。いやDRY精神に則ると随分とアレですが。いずれ直します(すぐ直すとは言ってない)

@@ -109,6 +110,7 @@ static byte room_build_order[ROOM_T_MAX] = {
 	ROOM_T_OVERLAP,
 	ROOM_T_CROSS,
 	ROOM_T_FRACAVE,
+	ROOM_T_FIXED,
 	ROOM_T_NORMAL,
 };

@@ -2072,6 +2074,7 @@ static bool room_build(int typ)
 	case ROOM_T_TRAP:          return build_type14();
 	case ROOM_T_GLASS:         return build_type15();
 	case ROOM_T_ARCADE:        return build_type16();
+	case ROOM_T_FIXED:        return build_type17();
 	}

 	/* Paranoia */
diff --git a/src/rooms.h b/src/rooms.h
index 51f8209..97d3b43 100644
--- a/src/rooms.h
+++ b/src/rooms.h
@@ -39,8 +39,9 @@
 #define ROOM_T_TRAP          13 /* Piranha/Armageddon trap room */
 #define ROOM_T_GLASS         14 /* Glass room */
 #define ROOM_T_ARCADE        15 /* Arcade */
+#define ROOM_T_FIXED         16 /* Fixed room */

-#define ROOM_T_MAX 16
+#define ROOM_T_MAX 17

素直に#define定義を追加し、

--- a/src/rooms.c
+++ b/src/rooms.c
@@ -89,6 +89,7 @@ static room_info_type room_info_normal[ROOM_T_MAX] =
 	{{  0,  0,  1,  1,  1,  2,  3,  4,  5,  6,  8}, 20}, /*TRAP     */
 	{{  0,  0,  0,  0,  1,  1,  1,  2,  2,  2,  2}, 40}, /*GLASS    */
 	{{  1,  1,  1,  1,  1,  1,  1,  2,  2,  3,  3},  1}, /*ARCADE   */
+	{{ 20, 40, 60, 80,100,100,100,100,100,100,100},  1}, /*FIX   */
 };

room_info_normalに生成確率テーブルも追加します。

v_info.txtにまず適当に足してみる

いくつか雑に追加したうちの一部を抜粋します。

N:129:Fixed room1
X:16:8:7:7
D:%%%%%%%
D:%.....%
D:%.'''.%
D:%.'.'.%
D:%.'''.%
D:%.....%
D:%%%%%%%

N:130:Fixed room2
X:16:8:14:14
D:%%%%%%%%%%%%%%
D:%............%
D:%...........%%
D:%..........%%
D:%.........%%
D:%........%%
D:%.......%%
D:%......%%
D:%.....%%
D:%....%%
D:%...%%
D:%..%%
D:%.%%
D:%%%

N:131:Fixed room3
X:16:8:7:7
D:%%%%%%%
D:%..#..%
D:%.'#'.%
D:%##.##%
D:%.'#'.%
D:%..#..%
D:%%%%%%%

N:132:Fixed room4
X:16:8:10:10
D:   %%%%
D:  %%##%%
D: %%.. .%%
D:%%.#. #.%%
D:%#..##..#%
D:%#..##..#%
D:%%.#. #.%%
D: %%.. .%%
D:  %%##%%
D:   %%%%

この時点では以下のような地形を指定できます。Xは順に部屋の型ID、レアリティ(多分働いていない)、縦の長さ、横の長さです。……17番目の部屋種別なのに16にしてしまったのはミスです、早めに直します。

地形定義はType7/8の記事でも書いてありますがv_info.txtで以下の通りです。

#    % - outside of the vault, where corridors may be connected
#    # - granite
#    $ - glass wall
#    X - impenetrable rock
#    Y - impenetrable glass wall
#    * - treasure or trap
#    + - secret door
#    - - secret glass door
#    ' - curtain
#    ^ - trap
#    & - monster up to 5 levels out of depth (OOD)
#    @ - monster up to 11 levels OOD
#    9 - monster up to 9 levels OOD and treasure up to 7 levels OOD
#    8 - monster up to 40 levels OOD and treasure up to 20 levels OOD
#    , - monster up to 3 levels OOD and/or treasure up to 7 levels OOD

v_info.txt用の新規定義追加と該当地形を持った固定部屋を作成。

雑にバグ修正も挟まっている辺りの更新です、ご了承ください。 build_vault()関数のswitchにこんな感じに追加します。

@@ -470,6 +470,38 @@ static void build_vault(POSITION yval, POSITION xval, POSITION ymax, POSITION xm
 				set_cave_feat(y, x, feat_deep_lava);
 				break;

+			case 'f':
+				set_cave_feat(y, x, feat_shallow_acid_puddle);
+				break;
+
+			case 'F':
+				set_cave_feat(y, x, feat_deep_acid_puddle);
+				break;
+
+			case 'g':
+				set_cave_feat(y, x, feat_shallow_poisonous_puddle);
+				break;
+
+			case 'G':
+				set_cave_feat(y, x, feat_deep_poisonous_puddle);
+				break;
+
+			case 'h':
+				set_cave_feat(y, x, feat_cold_zone);
+				break;
+
+			case 'H':
+				set_cave_feat(y, x, feat_heavy_cold_zone);
+				break;
+
+			case 'i':
+				set_cave_feat(y, x, feat_electrical_zone);
+				break;
+
+			case 'I':
+				set_cave_feat(y, x, feat_heavy_electrical_zone);
+				break;
+

これに合わせてv_info.txtも追加します。

N:136:Spring 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..^^^^..%%
D:%..^^==^^..%
D:%..^====^..%
D:%..^====^..%
D:%..^^==^^..%
D:%%..^^^^..%%
D: %%......%%
D:  %%%%%%%%

N:137:LavaSpring 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..vvvv..%%
D:%..vvwwvv..%
D:%..vwwwwv..%
D:%..vwwwwv..%
D:%..vvwwvv..%
D:%%..vvvv..%%
D: %%......%%
D:  %%%%%%%%

N:138:AcidSpring 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..ffff..%%
D:%..ffFFff..%
D:%..fFFFFf..%
D:%..fFFFFf..%
D:%..ffFFff..%
D:%%..ffff..%%
D: %%......%%
D:  %%%%%%%%

N:139:PoisonousSpring 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..gggg..%%
D:%..ggGGgg..%
D:%..gGGGGg..%
D:%..gGGGGg..%
D:%..ggGGgg..%
D:%%..gggg..%%
D: %%......%%
D:  %%%%%%%%

N:140:ColdZone 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..hhhh..%%
D:%..hhHHhh..%
D:%..hHHHHh..%
D:%..hHHHHh..%
D:%..hhHHhh..%
D:%%..hhhh..%%
D: %%......%%
D:  %%%%%%%%

N:141:ElectricalZone 1
X:16:8:10:12
D:  %%%%%%%%
D: %%......%%
D:%%..iiii..%%
D:%..iiIIii..%
D:%..iIIIIi..%
D:%..iIIIIi..%
D:%..iiIIii..%
D:%%..iiii..%%
D: %%......%%
D:  %%%%%%%%

結果

高電圧

うん、まだしょぼいですね。作るのならもっと、数も質も増やすべきです。折角v_info.txtで共通して使えるようになったのですから、Vaultでも各地形に囲われた要塞作っていいでしょう。

ついでにモンスターが該当耐性を持っているのみ地形に侵入できるよう修正

補足的なものです。こうして追加してきた地形をプレイヤーだけでなくモンスターも影響するように修正します。火耐性を持っていない敵が溶岩に侵入できないのと同様に、各属性への耐性がないと各地形に侵入できないようmonster_can_cross_terrain()を修正しておきます。

---
 src/monster1.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/monster1.c b/src/monster1.c
index bee3385..2e35540 100644
--- a/src/monster1.c
+++ b/src/monster1.c
@@ -2326,6 +2326,30 @@ bool monster_can_cross_terrain(FEAT_IDX feat, monster_race *r_ptr, BIT_FLAGS16 m
 		if (!(r_ptr->flagsr & RFR_EFF_IM_FIRE_MASK)) return FALSE;
 	}

+	/* Cold */
+	if (have_flag(f_ptr->flags, FF_COLD_PUDDLE))
+	{
+		if (!(r_ptr->flagsr & RFR_EFF_IM_COLD_MASK)) return FALSE;
+	}
+
+	/* Elec */
+	if (have_flag(f_ptr->flags, FF_ELEC_PUDDLE))
+	{
+		if (!(r_ptr->flagsr & RFR_EFF_IM_ELEC_MASK)) return FALSE;
+	}
+
+	/* Acid */
+	if (have_flag(f_ptr->flags, FF_ACID_PUDDLE))
+	{
+		if (!(r_ptr->flagsr & RFR_EFF_IM_ACID_MASK)) return FALSE;
+	}
+
+	/* Poison */
+	if (have_flag(f_ptr->flags, FF_POISON_PUDDLE))
+	{
+		if (!(r_ptr->flagsr & RFR_EFF_IM_POIS_MASK)) return FALSE;
+	}
+
 	return TRUE;
 }

--

最後に

今後とも精進して色々やっていきますのでどうぞよろしくお願いいたします。皆様良いお年を。



2018/12/27

[変愚蛮怒/開発]変愚蛮怒開発日誌part115…定期v_info.txt追加にコメント整理

ご覧ください。これが深き者としては不完全な寿命を時を止める冒涜的手段で補い、人々に絶望の日曜をもたらし続ける忌まわしき一族の住処です(発狂)

なお、この泥棒やオーク達はこの後スタッフに頂かれました。

Blasphemous E'sorlno's home.

あとついでくらいに高圧電線デスマッチみたいな地形も。

ElectricalZone 2 (Deathmatch)

本日のツッコミ(全2件) [ツッコミを入れる]

Nadir [属性地形が充実すると、自然領域なんかに地形操作魔法を追加したり、いわゆる「風水師」への発展など夢が広がりますね。]

deskull [ああ、昔有志のパッチがありましたよね。 そちらへの拡大、一度考えてみたいと思います。]


2018/12/28

[変愚蛮怒/開発]変愚蛮怒開発日誌part116…バグ一件修正

原因はget_mon_num() 中nasty生成を起こした際、逆に階層が浅くなりマイナス値を取ってアンダーフローを起こした結果モンスター種族IDが0を返すうえ、player_outfit() がpval=0の死体を生成するためでした。 この2件を抑止して修正完了。

ウィザードモードでアイテムを一度全部捨て、初期所持品を追加し直す処理を実装して、バグの再現を繰り返したらこうなりました。

さっきまで命だったものが辺り一面に転がる(AMZNZ)

DIE SET DOWN




2018/12/31

[変愚蛮怒/開発]変愚蛮怒開発日誌part119…定期v_infoにモンスター大量追加、リリースノート整理再開

年末最後の最後になって、創作面で世話になっていた人の訃報とか来てショックですが、正直、なればこそ自分も少しは急いでやることやってかんと思ったり。

k

今回ぐりっどばぐでHourierさんからいただいたコボルト系モンスターを同チャンネルで相談しつつ追加してみました。2.2.2正式リリースまでに調整します。

それといい加減Web、特にリリースノートの整理はせにゃならんと思っていますので、進めます。ひとまず皆さま良いお年を。