召還とリザレクション(蘇生)を作った


2017年 12月9日
 
 
 今回もそれなりに大変だった。

 隊列の処理が意外とややこしい。

 それと召還のユニットデータとステータスデータをどうするか、というのもある。
 
 
 では召還から。
 
 まず召還したユニットのユニット No.を控えておく変数を作る。

 dim smnfn,6

 召還されるのは隊列最大の 6を超えないはずなので配列は 6でよい(召還されたものが召還を使うのでもなければ最大は 4)

 戦闘ステータス bsは、死んだユニットの場所を使うので変更無し。
 
 
 ではソースを

#deffunc smon int smonf ; 召還

	c=0
	repeat 3,1
	 if fs=1 : un2=fn1(fs1+cnt)
	 if fs=2 : un2=fn2(fs2+cnt)
	  if un2!0 and ud(un2*udb+26)>0 : c+
	loop
	if c=3 : return ; 隊列がいっぱい

	f=0
	repeat ub-1,1 ; 空きユニットNo.ピック
	 un2=cnt : ug2=un2*udb ; ユニットNo.
	  if ud(ug2)=0 : f=1 : break
	loop
	if f=0 : return ; ユニットいっぱい


	if smonf=1 : cn=136 ; ゴーレム
	if smonf=2 : cn=169 ; ウィスプ
	if smonf=3 {
		r=rnd(3)
		 if r=0 : cn=184 ; ゾンビ
		 if r=1 : cn=185 ; グール
		 if r=2 : cn=186 ; スケルトン
	}
	if smonf=4{
		r=rnd(3)
		 if r=0 : cn=187 ; インプ
		 if r=1 : cn=196 ; デビル
		 if r=2 : cn=200 ; デーモン
	}

	repeat udb
	 ud(ug2+cnt)=0 ; データクリア
	loop

	ud(ug2)=cn	; cnキャラ種類
	repeat 9,1
	 ud(ug2+cnt)=cd(cnt,cn) ; データ
	loop
	repeat 5
	 ud(ug2+21+cnt)=ud(ug2+1+cnt) ; ステータスデータ
	loop
	ud(ug2+26)=ud(ug2+25) ; HP

	repeat 2 : ud(ug2+11+cnt)=ud(ug2+21) : loop ; 攻防値


	repeat 3 ; 空き戦闘ステータスにセット
	 if fs=1 : bsg=cnt*bsb
	 if fs=2 : bsg=(cnt+3)*bsb
	 if bs(bsg)=0 : bs(bsg)=un2 : break
	loop


	if fs=1 { ; 隊列にセット
	 n=ftp(fs,un) ; 召還者の隊列
	 switch n
	  case 1
	   smn=1
	   if fn1(fs1+2)>0 and ud(fn1(fs1+2)*udb+26)>0 : fn1(fs1+3)=fn1(fs1+2) ; 真ん中に誰かいる
	   fn1(fs1+2)=fn1(fs1+1) ; 一つ後ろへ詰める
	  swbreak
	  case 2
	   if fn1(fs1+1)=0 or ud(fn1(fs1+1)*udb+26)<=0 : smn=1 : swbreak ; 先頭が開いてる
	   fn1(fs1+3)=fn1(fs1+2) : smn=2 ; 一つ後ろへ詰める
	  swbreak
	  case 3
	   smn=2
	   if fn1(fs1+2)=0 or ud(fn1(fs1+2)*udb+26)<=0 : swbreak ; 真ん中が開いてる場合
	   fn1(fs1+1)=fn1(fs1+2) : fn1(fs1+2)=0 ; 一つ前へ詰める
	  swbreak
	 swend
	 fn1(fs1+smn)=un2 ; 召還
	}

	if fs=2 {
	 n=ftp(fs,un) ; 召還者の隊列
	 switch n
	  case 1
	   smn=1
	   if fn2(fs2+2)>0 and ud(fn2(fs2+2)*udb+26)>0 : fn2(fs2+3)=fn2(fs2+2) ; 真ん中に誰かいる
	   fn2(fs2+2)=fn2(fs2+1) ; 一つ後ろへ詰める
	  swbreak
	  case 2
	   if fn2(fs2+1)=0 or ud(fn2(fs2+1)*udb+26)<=0 : smn=1 : swbreak ; 先頭が開いてる
	   fn2(fs2+3)=fn2(fs2+2) : smn=2 ; 一つ後ろへ詰める
	  swbreak
	  case 3
	   smn=2
	   if fn2(fs2+2)=0 or ud(fn2(fs2+2)*udb+26)<=0 : swbreak ; 真ん中が開いてる場合
	   fn2(fs2+1)=fn2(fs2+2) : fn2(fs2+2)=0 ; 一つ前へ詰める
	  swbreak
	 swend
	 fn2(fs2+smn)=un2 ; 召還
	}

	repeat 6
	 if smnfn(cnt)=0 : smnfn(cnt)=un2 : break; 召還un格納
	loop

	return

 
 
 まず当然ながら隊列がいっぱいの場合は召還しない。

 ユニットデータは一般のデータ udの空きをそのまま使う。

 そうしないと戦闘プログラムで運用できない。

 その部隊の戦闘が終わったら smnfnに控えておいたユニットのデータを消す。

 ソースの隊列にセットの部分で、召還者の位置を元に隊列を詰めて召還する場所を確定する。

 意外とややこしいのが分かると思う。
 
 
 ではテスト

 光の召還、ウィル・オ・ウィスプ


 
 
 召還者の前に召還される。

 下の緑の表示でデバッグ確認用の smnfnを書いてる。

 召還されたユニットNO.は 113だが、これはマップ全部でユニットが 112作成されてるから。
 
 
 部隊が埋まるまで(といっても最大 2体という事だが)召還される。


 
 
 闇の召還、デモンズゲート(悪魔召還)


 
 
 3種の内ランダムで召還される。


 
 
 
 と、いう訳で次はリザレクション(蘇生)

 これの隊列処理が大変だった。

 蘇生位置の隊列の変更と、召還されたものの場合は潰さないといけない、どれを詰めてどれを潰すか?

 共通の法則は無いか、紙数枚に書いて丸一日思考錯誤していたが、自分にはどうにも法則は分からなかった。

 結局蘇生される者の元の隊列の位置を格納しておいて参照し、その場所と現在の隊列のユニット数を元に「ベタ処理」する事にした。

 dim sfn,7 ; 初期隊列un格納

 dim rfn,4 ; 蘇生候補と蘇生処理時の隊列
 
 
 ではソースを

#deffunc resur ; リザレクション


	repeat 3 : rfn(cnt)=0 : loop ; クリア

	if fs=1 : sfg=0
	if fs=2 : sfg=3

	c=0
	repeat 3,1 ; 死亡者ピック
	 rn=sfn(sfg+cnt) : if rn=0 : continue
	  if ud(rn*udb+26)<=0 : rfn(c)=rn : c+
	loop
	if c=0 : return ; 死亡者がいない

	r=rnd(c) : rn=rfn(r) ; 蘇生者選択

	repeat 3,1 ; 元の隊列場所
	 if rn=sfn(sfg+cnt) : rt=cnt : break
	loop

	repeat 4 : rfn(cnt)=0 : loop ; rfnリセット
	repeat 3,1 ; 処理用隊列変数に格納
	 if fs=1 : un2=fn1(fs1+cnt)
	 if fs=2 : un2=fn2(fs2+cnt)
	  ug2=un2*udb
	   if ud(ug2+26)>0 : rfn(cnt)=un2
	loop

	taif=0
	repeat 3,1 ; 隊列いくつ埋まっているか
	 n=rfn(cnt)
	 if n>0 and ud(n*udb+26)>0 : taif+
	loop

	repeat 1 ; 隊列が3つ埋まってる場合 ----------------------------
	 if taif!3 : break ; 3つじゃない

	 en1=smf(rfn(1)) : en2=smf(rfn(2)) : en3=smf(rfn(3)) ; 召還か

	 switch rt
	  case 1 ; 蘇生される者が1列目 -------------------------
	   if en1>0 : d_syo en1 : rfn(1)=rn : rsr_syo rn : swbreak ; 隊列1が召還の場合
	   if en2>0 and en3=0 { ; 隊列2だけ召還の場合
		d_syo en2 ; 隊列2の召還を消す
		 rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
	   if en3>0 { ; 隊列3が召還の場合
		d_syo en3 ; 隊列3の召還を消す
		 rfn(3)=rfn(2) : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
	  swbreak
	  case 2 ; 蘇生される者が2列目 -------------------------
; 例外1
	   if rfn(3)=sfn(sfg+1) { ; 隊列3に1が入ってる
		en1=smf(rfn(1)) : if en1>0 : d_syo en1 ; 1の召還を削除
		rfn(1)=rfn(2) : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
	   if rfn(1)=sfn(sfg+3) { ; 隊列1に3が入ってる
		en3=smf(rfn(3)) : if en3>0 : d_syo en3 ; 3の召還を削除
		rfn(3)=rfn(2) : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
; 例外2
	   if rfn(2)=sfn(sfg+1) { ; 隊列2に1が入ってる
		en1=smf(rfn(1)) : if en1>0 : d_syo en1 ; 1の召還を削除
		rfn(1)=rfn(2) : rfn(2)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
	   if rfn(2)=sfn(sfg+3) { ; 隊列2に3が入ってる
		en3=smf(rfn(3)) : if en3>0 : d_syo en3 ; 3の召還を削除
		rfn(3)=rfn(2) : rfn(2)=rn : rsr_syo rn ; 入れ替え
		swbreak
	   }
; その他
	   if en2>0 : d_syo en2 : rfn(2)=rn : rsr_syo rn ; 隊列2は召還
	  swbreak
	  case 3 ; 蘇生される者が3列目 -------------------------
	   if en2>0 and en1=0 { ; 隊列2だけ召還の場合
		d_syo en2 : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn ; 入れ替え
	   swbreak
	   }
; その他
	   if en1>0 { ; 隊列1、2が召還の場合
	    d_syo en1 ; 隊列1の召還は消す
	     rfn(1)=rfn(2) : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn ; 入れ替え
	   }
	  swbreak
	 swend
	loop
dgs "en1 ",en1,15
dgs "en2 ",en2,16
dgs "en3 ",en3,17
bpk
	repeat 1 ; 隊列が2つ埋まってる場合 ----------------------------
	 if taif!2 : break ; 2つじゃない

	 if rt=1 { ; 蘇生される者が1列目 -------------------------
	  if rfn(1)=0 : rfn(1)=rn : rsr_syo rn : break ; 隊列1が空
	  if rfn(2)=0 : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; 隊列2が空
	   rfn(3)=rfn(2) : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; 隊列3が空
	 }

	 if rt=2 { ; 蘇生される者が2列目 -------------------------
; 例外1
	  if rfn(3)>0 and rfn(3)=sfn(sfg+1) { ; 隊列3に1が入ってる
	   if rfn(2)=0 : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; 隊列2が空
	    rfn(1)=rfn(2) : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; その他
	  }
	  if rfn(1)>0 and rfn(1)=sfn(sfg+3) { ; 隊列1に3が入ってる
	   if rfn(2)=0 : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; 隊列2が空
	    rfn(3)=rfn(2) : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; その他
	  }
; 例外2
	  if rfn(2)>0 and rfn(2)=sfn(sfg+1) { ; 隊列2に1が入ってる
	   if rfn(3)=0 : rfn(3)=rn : rsr_syo rn : break ; 隊列3が空いてる
		rfn(1)=rfn(2) : rfn(2)=rn : rsr_syo rn ; 入れ替え
		break
	  }
	  if rfn(2)>0 and rfn(2)=sfn(sfg+3) { ; 隊列2に3が入ってる
	   if rfn(1)=0 : rfn(1)=rn : rsr_syo rn : break ; 隊列1が空いてる
		rfn(3)=rfn(2) : rfn(2)=rn : rsr_syo rn ; 入れ替え
		break
	  }
; その他
	  if rfn(1)=0 : rfn(1)=rfn(2) : rfn(2)=rn : rsr_syo rn : break ; 隊列1が空いてる
	  if rfn(3)=0 : rfn(3)=rfn(2) : rfn(2)=rn : rsr_syo rn : break ; 隊列3が空いてる
	 }

	 if rt=3 { ; 蘇生される者が3列目 -------------------------
	  if rfn(3)=0 : rfn(3)=rn : rsr_syo rn : break ; 隊列1が空
	  if rfn(2)=0 : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; 隊列2が空
	    rfn(1)=rfn(2) : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; 隊列3が空
	 }
	loop


	repeat 1 ; 隊列が1つ埋まってる場合 ----------------------------
	 if taif!1 : break ; 1つじゃない

	 if rt=1 { ; 蘇生される者が1列目 -------------------------
	  if rfn(1)=0 : rfn(1)=rn : rsr_syo rn : break ; 隊列1が空
	  if rfn(2)=0 : rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; 隊列2が空
	 }

	 if rt=2 { ; 蘇生される者が2列目 -------------------------
; 例外1
	  if rfn(3)>0 and rfn(3)=sfn(sfg+1) { ; 隊列3に1が入ってる
	   rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; 隊列2が空
	  }
	  if rfn(1)>0 and rfn(1)=sfn(sfg+3) { ; 隊列1に3が入ってる
	   rfn(2)=rfn(1) : rfn(1)=rn : rsr_syo rn : break ; 隊列2が空
	  }
; 例外2
	  if rfn(2)>0 and rfn(2)=sfn(sfg+1) { ; 隊列2に1が入ってる
		rfn(1)=rfn(2) : rfn(2)=rn : rsr_syo rn : break ; 入れ替え
		
	  }
	  if rfn(2)>0 and rfn(2)=sfn(sfg+3) { ; 隊列2に3が入ってる
		if rfn(1)=0 : rfn(1)=rn : rsr_syo rn : break ; 入れ替え
	  }
; その他
	  if rfn(2)=0 : rfn(2)=rn : rsr_syo rn : break ; 隊列2が空

	  if rfn(2)>0 : rfn(1)=rfn(2) ; 隊列2に召還がいる
	  rfn(2)=rn : rsr_syo rn : break ; 入れ替え
	 }

	 if rt=3 { ; 蘇生される者が3列目 -------------------------
	  if rfn(3)=0 : rfn(3)=rn : rsr_syo rn : break ; 隊列3が空
	  if rfn(2)=0 : rfn(2)=rfn(3) : rfn(3)=rn : rsr_syo rn : break ; 隊列2が空
	 }
	loop

	repeat 3,1 ; 隊列格納
	 if fs=1 : fn1(fs1+cnt)=rfn(cnt)
	 if fs=2 : fn2(fs2+cnt)=rfn(cnt)
	loop

	return

 
 
 隊列の処理だけでこれだけのソースになると言う事から、いかに意外とややこしいか分かってもらえるだろう。

 だからたぶん素質のあるプログラマーなら共通法則を導き出して簡素化するんだろうと思うが、自分はペーペーなのでそんなのサッパリ分からない。
 
 
 まず元の隊列を格納してある変数 sfnを元に死者がいるかどうか調べ、2名死んでる場合はどちらかランダムで選ぶ。

 次に同 sfnを参照して元のユニットが隊列の何番目だったかを確認する。

 あ、そうそう smfというのは、そのユニットが召還かどうか調べる関数(自作命令)です。

 隊列の変更はまず現在の隊列が何人いるかで処理を変える。

 3人全部埋まってる場合に、召還ユニットがあれば潰す事になる。

 分岐した処理それぞれで、復帰する隊列事に「ベタ処理」を敢行する。

 特別なのは 2列目の処理で、2列目に 1や 3が入っていると、詰め方が特殊になる。

 処理前に rfnに隊列を格納してから処理し、その後に現在の隊列 fn1、fn2に格納しなおす。

 こうする事で処理ソースを一つにしている。
 
 
 少し図を描こうかと思ったけど、どうにもめんどくさいので止めた。

 という訳で説明はこれくらいにして後はソースで理解してもらうとして(無茶だけど)テストする。
 
 
 召還とリザをセットする。


 
 
 先頭がやられた


 
 
 1個詰めて、召還者の前に召還をセット


 
 
 その後から召還を潰してリザ


 
 
 先頭が2名やられている状態で召還


 
 
 そしてリザ、この場合 1列目が蘇生された模様。


 
 
と、いうような感じです。
 
 
 
 これにて魔法は今の所コンプリートになりました。

 たぶんバグてんこもりだと思うけど、追々潰していこう。
 
 
 次はどうするかな?

 戻ってユニットの特殊能力を整備するかな。

 ではまた。
 
 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です