トップ «前の日記(2012/12/11) 最新 次の日記(2012/12/13)» 編集

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分一本勝負


2012/12/12

[変愚蛮怒/開発]魔法の実装的問題例(変愚蛮怒 Ver2.1.1)

Twitterの#Hengbandクラスタより

ビオ略の記事より

D&Dからの伝統として、殺戮雲は悪臭雲の上位呪文であるらしい。にも関わらず、前者が地形に効果を及ぼさないのは別々に実装しているためではないかとの仮説だが、実際その通りだった。

以下do-spell.cより引用

	case 3:
#ifdef JP
		if (name) return "悪臭雲";
		if (desc) return "毒の球を放つ。";
#else
		if (name) return "Stinking Cloud";
		if (desc) return "Fires a ball of poison.";
#endif

		{
			int dam = 10 + plev / 2;
			int rad = 2;

			if (info) return info_damage(0, 0, dam);

			if (cast)
			{
				if (!get_aim_dir(&dir)) return NULL;

				fire_ball(GF_POIS, dir, dam, rad);
			}
		}
		break;
	case 10:
#ifdef JP
		if (name) return "殺戮雲";
		if (desc) return "自分を中心とした毒の球を発生させる。";
#else
		if (name) return "Cloud kill";
		if (desc) return "Generate a ball of poison centered on you.";
#endif

		{
			int dam = (30 + plev) * 2;
			int rad = plev / 10 + 2;

			if (info) return info_damage(0, 0, dam/2);

			if (cast)
			{
				project(0, rad, py, px, dam, GF_POIS, PROJECT_KILL | PROJECT_ITEM, -1);
			}
		}
		break;

双方、if(cast)内に魔法の実装を行なっている。

project()関数は要するに打撃以外のあらゆる作用を@さん、モンスター、地形、アイテムなどに及ぼすための関数を一まとめにしたものである。

本来一つに統一されているべきであろう、ボール型範囲魔法の処理は、殺戮雲のように直接project()関数を呼び出している場合と、悪臭雲のようにfire_ball()関数をはさんで行なっている形で二分している。

fire_ball()の実装は以下の通りで、最後のreturnと同じ行でproject()関数を呼び出している。

/*
 * Cast a ball spell
 * Stop if we hit a monster, act as a "ball"
 * Allow "target" mode to pass over monsters
 * Affect grids, objects, and monsters
 */
bool fire_ball(int typ, int dir, int dam, int rad)
{
	int tx, ty;

	int flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;

	if (typ == GF_CONTROL_LIVING) flg|= PROJECT_HIDE;
	/* Use the given direction */
	tx = px + 99 * ddx[dir];
	ty = py + 99 * ddy[dir];

	/* Hack -- Use an actual "target" */
	if ((dir == 5) && target_okay())
	{
		flg &= ~(PROJECT_STOP);
		tx = target_col;
		ty = target_row;
	}

	/* Analyze the "dir" and the "target".  Hurt items on floor. */
	return (project(0, rad, ty, tx, dam, typ, flg, -1));
}

今回の問題点は双方のproject()関数の引数である。GF_*は範囲魔法の属性、PROJECT_*は魔法の作用の対象やその特別な制限などを示している。悪臭雲と殺戮雲それぞれのproject関数に乗る引数は以下の通り。

悪臭雲:GF_POIS, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL
殺戮雲:GF_POIS, PROJECT_KILL | PROJECT_ITEM

GF_POISは共に毒属性の作用を表すがPROJECTフラグが異なっている。今回、悪臭雲で木が枯れて、殺戮雲では枯れない原因はこの内のPROJECT_GRIDが原因である。このフラグはproject()関数内のサブルーチンであり、地形に作用を及ぼす実装に当たるproject_f()(_fはfeatureと思われる)を呼び出すか呼び出さないかの基準になっているのである。

以下project()関数内引用。

	/* Check features */
	if (flg & (PROJECT_GRID))
	{
		/* Start with "dist" of zero */
		dist = 0;

		/* Scan for features */
		for (i = 0; i < grids; i++)
		{
			/* Hack -- Notice new "dist" values */
			if (gm[dist+1] == i) dist++;

			/* Get the grid location */
			y = gy[i];
			x = gx[i];

			/* Find the closest point in the blast */
			if (breath)
			{
				int d = dist_to_line(y, x, y1, x1, by, bx);

				/* Affect the grid */
				if (project_f(who, d, y, x, dam, typ)) notice = TRUE;
			}
			else
			{
				/* Affect the grid */
				if (project_f(who, dist, y, x, dam, typ)) notice = TRUE;
			}
		}
	}

変愚蛮怒までの*band系バリアントの問題点の根本がこの手の実装にある。今回はたまたま魔法の慣例的な作用がズレた訳だ。実装者は変愚の殺戮雲をあくまで地形に影響を及ぼさない魔法にしたかった可能性もあるが、いずれにせよあまり褒められた形には思えない。

古典的な、悪い意味でのハッカー的開発体制(雑だけど、巧く動くよう間に合わせる)の裏目というべきか。各人の設計思想がバラバラのまま、最初からある実装をあまり検討せずに機能追加を繰り返してきた経緯の具体例とも言える。この辺りはソースコードの違法建築物の如き様相の、ほんの例の一つに過ぎない。

[DarkSouls][Rough]本日の落書き

pixivに2ndのエロキャラの絵だけ晒して1stキャラの絵とか上げたことがないのでラフだけ晒しておく。レベル150、眷族プレイ向けの7周目キャラ。仮面巨人です(大嘘)。

Urbero

以下「ぼくのかんがえたかっこいい不死の英雄」設定。

 アストラ貴族とソルロンド貴族間の庶子。聖職者としてエリートの道を歩んだ後、アストラ東部の一領に司教として赴任。周辺諸侯との折衝と教区の統治を行いながら、火の時代黎明期の歴史と人間の始祖について、十数年の研究を行う。
 既にこの時から、人間性と闇の時代の兆しについておぼろげながらも仮説を抱き、グウィンを筆頭とする神族達に疑念と嫉妬、敵愾心を宿していた。

 三十代後半にダークリングが身に宿った後、聖騎士リロイの故事に習った儀式を経て、地位と権力を失いロードランへ。北の不死院に送られるまでに恨みを買っていた政敵からのお礼参りがあり、亡者となるギリギリまで衰える。しかしその妄執ぶりに、さらに性格を歪めながら自我を持ち直した。

 ロードランを放浪して遺物を調査していくにつれ、己の仮説の方向性が間違っていなかったことを確信していく。元より白教を権力装置としか見ていなかったため、墓王ニトと聖騎士リロイの出会いを経て墓王の眷属に転向。司教時代からの知識で、独自の祭式を編み出す。
 アノール・ロンドに至った後は神族とその下僕達を次々に殺害。彼等の所産である武具や魔術を略奪して己が力にすることに執着する。最初から疑っていたフラムトからの使命は蹴り、深淵でカアスの話に乗った。
 新たな信仰の対象として、唯一真摯に敬服していたニトにまで手をかけることには、流石に躊躇う所も多かったようだが、最終的には意を決して王のソウルを全て強奪。グウィン殺害後、世界の蛇達の前へ凱旋し、並行世界で同じ闇の王の運命を選んだ不死人達の列侯の一人となった。

 その後、司教時代の教区を本拠として東アストラ墓王教団を設立し、その教祖に収まる。「墓王ニトは闇の時代の不死人達を祝福するために、死に変わって災厄を生み出したのだ」という教義を説き、生前ならではの手腕で権力の座を保持しつつ、今に至る。
 時間遡行(周回)を繰り返すのは、闇の王の一人となった栄光の追体験と墓王参り、そして同じ闇の王との社交(殺し合い)のため。
 本性はおよそ聖職者と貴族の鼻持ちならない面を詰め込んだようなナルシストだが、同胞不死人の強者に対する敬意は強い。そういう相手に対しては打算混じりながらも礼(情けない土下座含む)を怠らない。闇の時代に不死人同士が遊戯として永遠に殺し合うことを心底楽しんでいる。

 性癖は両性愛者。聖職者になる以前、政治的事情から生き別れた私生児の娘がおり、彼女もまた闇の王として自分の世界にやってくることを期待、見つけ出してあわよくば娶ろうと企んでいる。