dim講座

※書きかけです。
うみ☆ぬこ - livedoor Blog(ブログ)の人が、多対多なFF風戦闘をNScripterでやりたがっている。そのためにdimを修得せねばっと言う判断は全くもって悪くない。てか素直でいい。
なので、dimについて、実践を踏みながら書いてみる。一人のためにテキストを書き起こすというのも悪くない。
なんかついでに、getparamの使い方も知りたいとのことなので、それも含めてテキストに。

ルールの想定

能力値

登場するキャラクターは敵味方問わず、以下の能力値を持つとする。

体力点
0になると死ぬ。プレイヤーキャラクターの初期値は2d6+12。
技術点
戦闘能力他、身体能力全般を意味する。プレイヤーキャラクターの初期値は1d6+6。
運命点
運試しに使う。プレイヤーキャラクターの初期値は1d6+6。
速度ゲージ
FF風に必要なので木に竹をついだような感じだが、導入してみる。
戦闘での振る舞い
  • 魔法はないので、物理攻撃一辺倒である。
  • 攻撃対象の選択は、プレイヤーは自由選択、敵はランダム選択。
行動順

※簡易なFF風にしてみる。
現在戦闘に参加している全てのキャラクターに対し、以下の手順を行う。

  • 速度ゲージに1d6を追加する。
  • 速度ゲージが100未満ならば、ここで終わり。次のキャラクターにうつる。
  • 速度ゲージから100を引く。
  • 行動を決定して行動する。
物理攻撃の仕方
  • 攻撃側が2d6を振り、技術点に足す。-a
  • 防御側が2d6を振り、技術点に足す。-b
  • a>bの場合、攻撃は成功。防御側の体力点を2点減らす。
  • 体力点が0以下になった場合、そのキャラクターは死亡する。
終了条件

敵味方、どちらかのサイドのキャラクターが全員死亡した場合、戦闘は終了する。

初期設定

とりあえず、プレイヤーキャラクターの名前は、ファファードとグレイマウザーくらいにしとく。

スクリプトの実際

 *define
; 変数の設定
numalias hp,100 ; 体力点は100番の数値変数を使う(つもり)
numalias tp,101 ; 技術点は101番の数値変数を使う(つもり)
numalias dp,102 ; 運命点は102番の数値変数を使う(つもり)
numalias is_use,103 ; そのキャラクター番号を使っているかどうかのフラグ。0は未使用。1以上は使用中。
numalias is_live,104 ; そのキャラクター番号のキャラクターが生きているかどうかのフラグ。0は死亡。1以上は健在。
numalias is_there,105 ; そのキャラクター番号のキャラクターがその場にいるかどうかのフラグ。0は不在。1以上は存在。デジョンとか食らうと0になります。
numalias side,106 ; そのキャラクターが敵か味方かのフラグ。0なら味方、1以上なら敵。数値によって、所属する陣営を変えて多勢力間戦闘に拡張できる(今回はしないが)
numalias speed,107 ; そのキャラクターのスピードゲージ
numalias name,108 ; そのキャラクターの名前のコード
numalias volume,109 ; そのキャラクターの番号(あれば)
numalias max_character,10 ; 一度に戦闘に参加できるキャラクターの最大数。いくらに設定してもよいが、多すぎるとメモリーが足りなくなって困る。
; dimの設定
dim ?hp[max_character] ; 体力点の入れ物をキャラクターの数だけ用意
dim ?tp[max_character] ; 技術点の入れ物をキャラクターの数だけ用意
dim ?dp[max_character] ; 運命点の入れ物をキャラクターの数だけ用意
dim ?is_use[max_character] ; 使用フラグの入れ物をキャラクターの数だけ用意
dim ?is_live[max_character] ; 使用フラグの入れ物をキャラクターの数だけ用意
dim ?is_there[max_character] ; 使用フラグの入れ物をキャラクターの数だけ用意
dim ?side[max_character] ; 陣営フラグの入れ物をキャラクターの数だけ用意。
dim ?speed[max_character] ; スピードゲージの入れ物をキャラクターの数だけ用意。
dim ?name[max_character] ; 名前の入れ物をキャラクターの数だけ用意。
dim ?volume[max_character] ; 番号の入れ物をキャラクターの数だけ用意。
; dim を行うと、数値変数とは別に配列変数が用意される。
; %hpと?hp[0]は別々の存在であり、どちらかを変えてももう一方には影響しない。
; ?hp[0]と?hp[1]も別々の存在であり、やはり相互に影響しない。
; ?hp[n][m]と多元配列も可能だが、そこそこにしよう。メモリーは有限だ。

; defsub は、自分で新しい命令を作るための命令である。
; 新しい命令はサブルーチンの形で作成する。サブルーチンはreturnで終わる一連の命令である。
; defsub diceを宣言したならば、ラベル*dicwが存在しなくてはならない。
; getparam はサブルーチンが命令に化けるために必要な命令である。

; サイコロ関連の新命令を作る。
defsub dice ; 任意の数のサイコロを転がす。
; dice %50,12 ← サイコロを12個転がして、その合計を%50に格納する。
defsub d1 ; サイコロ一個を転がす。
; d1 %75 ← サイコロを一個転がして、その合計を%75に格納する。
defsub d2 ; サイコロ二個を転がす。
defsub d3 ; サイコロ三個を転がす。
defsub dice2 ; 任意の面数の任意の数のサイコロを転がす。
; dice2 %80,3,8 ← 8面体サイコロを三つ転がして、その合計を%80に格納する。
numalias dice_num,201 ; ふるサイコロの数
numalias dice_result,202 ; 結果を格納する変数番号を格納する変数。
numalias dice_roll,203 ; サイコロ1個
numalias dice_aspect,204 ; サイコロの面数を格納する変数。普通は6だよね?
numalias dice_loop,205 ; サイコロルーチン用ループ変数。

defsub luck
numalias luck_result,210
numalias luck_character,211
numalias luck_temp,212

; 攻撃関連の新命令を作る。
defsub attack ; キャラクター番号を指定して攻撃を判定する命令。
; attack %50,2,3 ← キャラクター番号2がキャラクター番号3を攻撃する。その結果を%50に格納する。
; 0なら失敗、1なら成功である。
defsub damage ; キャラクター番号を指定して与えるダメージを判定する命令。
; damage %51,2,3 ← キャラクター番号2がキャラクター番号3を殴った時のダメージ値を、%51に格納する。
numalias attack_result,170
numalias attacker,171
numalias defender,172
numalias attp,173
numalias dftp,174
numalias damage_result,175

defsub battle
numalias character,150 ; 現在行動中のキャラクター番号
numalias battle_result,151 ; 戦闘の結果を格納する変数の番号
numalias character_loop,152 ; ループ用変数
numalias targetable_character,153 ; 攻撃可能キャラクターのリスト
dim ?targetable_character[max_character] ; 配列変数化
numalias target,154 ; 攻撃対象キャラクター
numalias character_temp,155
numalias side_num,156
numalias side_there,157
numalias battle_end,158

defsub get_name
numalias get_name_result,190
numalias get_name_character,191

defsub print_character
numalias print_character_num,180

game
; ちょっとしたコツだが、サブルーチンを記述する場所は、メインルーチンからは間違ってもいけない場所がよい。
; 具体的には、*define節のgameコマンドの後などである。

; サイコロ関係のサブルーチン
 *luck
getparam i%luck_result,%luck_character
mov %%luck_result,0
if ?dp[%luck_character]<1 return
d2 %luck_temp
notif ?dp[%luck_character]<%luck_temp mov %%luck_result,1
mov ?dp[%luck_character],?dp[%luck_character]-1
return

 *dice2
getparam i%dice_result,%dice_num,%dice_aspect
goto *dice_body3
 *dice
getparam i%dice_result,%dice_num
goto *dice_body2
 *d1
mov %dice_num,1:goto *dice_body1
 *d2
mov %dice_num,2:goto *dice_body1
 *d3
mov %dice_num,3:goto *dice_body1

 *dice_body1
getparam i%dice_result ; 結果を格納する数値変数の番号を取得する。
 *dice_body2
mov %dice_aspect,6 ; 面数を6に設定
 *dice_body3
mov %%dice_result,0 ; 結果を受け取る数値変数を0にする。
for %dice_loop=1 to %dice_num ; 振るサイコロの数を設定。
 rnd2 %dice_roll,1,%dice_aspect ; 指定された面数のサイコロを振る。
 add %%dice_result,%dice_roll ; その結果を結果に足す。
next ; forに戻って繰り返す。
return

; 攻撃の成否判定用のサブルーチン
 *attack
getparam i%attack_result,%attacker,%defender
d2 %attp:add %attp,?tp[%attacker]
d2 %dftp:add %dftp,?tp[%defender]
mov %%attack_result,0
if %attp > %dftp mov %%attack_result,1
return

; ダメージ量の計算用サブルーチン
 *damage
; 本来は、攻撃側の筋力や防御側の防具などを想定した計算式がここに入るが、今回のルールではどんな条件でも一律2点と決まっている。
; なので引数を取るがそれとは関係なく2を返す。
getparam i%damage_result,%attacker,%defender
mov %%damage_result,2
return

 *battle
getparam i%battle_result ; 戦闘の結果を格納する数値変数の番号を取得。
; 初期設定
; 生きてる連中は全部いる
for %character=0 to max_character
 if ?is_use[%character]=0 goto *battle_pre_loop
 mov ?is_live[%character],1
 mov ?is_there[%character],1
 *battle_pre_loop
next
mov %character,-1
; ループ開始
 *battle_loop2
; 終了チェック
; 終了条件は、どちらかの陣営の全員が、死亡ないし不在の時。
for %character_temp=0 to max_character ; 陣営ループ
mov %side_num,0
mov %side_there,0
for %character_loop=0 to max_character ; キャラクターループ
if ?is_use[%character_loop]=0 break
notif ?side[%character_loop]=%character_temp goto *battle_end_check_loop
inc %side_num
if ?is_live[%character_loop]=0 inc %side_there:goto *battle_end_check_loop
if ?is_there[%character_loop]=0 inc %side_there:goto *battle_end_check_loop
 *battle_end_check_loop
next
if %side_num=0 break
notif %side_num=%side_there goto *battle_end_check_loop2
mov %battle_end,1:break
 *battle_end_check_loop2
next
notif %battle_end=0 return
 *battle_loop

inc %character ; 行動するキャラクター番号を増やす。
if %character > max_character mov %character,0 ; もし、キャラクター番号が最大キャラクター数をこえたならば、0に戻す。
if ?is_use[%character]=0 goto *battle_loop ; 使ってないキャラクター番号なら飛ばす。
if ?is_there[%character]=0 goto *battle_loop ; 戦場からいないキャラクター番号なら飛ばす。
if ?is_live[%character]=0 goto *battle_loop ; 死亡しているキャラクター番号なら飛ばす。
d1 %speed:mov ?speed[%character],?speed[%character]+%speed ; スピードゲージを増やす。
if ?speed[%character]<100 goto *battle_loop ; スピードゲージが100以下なら、飛ばす。
; ここにお好みでスピードゲージの表示処理を入れる。
mov ?speed[%character],?speed[%character]-100
get_name $0,%character
textclear
print_character %character
; 攻撃可能な敵のリストアップ
for %character_loop=0 to max_character
mov ?targetable_character[%character_loop],-1
next
mov %targetable_character,-1
for %character_loop=0 to max_character
if ?is_use[%character_loop]=0 goto *battle_next ; 未使用キャラは攻撃不可能
if ?side[%character_loop]=?side[%character] goto *battle_next ; 同じ陣営は攻撃不可能
if ?is_live[%character_loop]=0 goto *battle_next ; 死亡キャラは攻撃不可能
if ?is_there[%character_loop]=0 goto *battle_next ; 非在キャラは攻撃不可能
; 上記のフィルターをくぐりぬけたキャラクターは攻撃対象にできる。
inc %targetable_character
mov ?targetable_character[%targetable_character],%character_loop ; リストに記載
 *battle_next
next
; 行動の選択。
; プレイヤーキャラクターならばプレイヤーが選択。
; そうでなければ、ランダムターゲット攻撃を選択。
if ?side[%character]=0 goto *battle_select_action
rnd2 %target,0,%targetable_character ; 攻撃対象の選択
mov %target,?targetable_character[%target]
goto *battle_attack
 *battle_select_action
selnum %20,"こうげき","とうそう"
if %20=0 goto *battle_select_target
$0はとうそうを試みた。
luck %20,%character
if %20=1 goto *battle_runaway
しかし、まわりこまれてしまった!@
goto *battle_loop
 *battle_runaway
$0は逃げ出した!@
mov ?is_there[%character],0
goto *battle_loop2
 *battle_select_target
textclear
for %character_loop=0 to max_character
mov %character_temp,%character_loop+10
get_name $%character_temp,?targetable_character[%character_loop]
next
if %targetable_character=1 selnum %target,$10
if %targetable_character=2 selnum %target,$10,$11
if %targetable_character=3 selnum %target,$10,$11,$12
if %targetable_character=4 selnum %target,$10,$11,$12,$13
if %targetable_character=5 selnum %target,$10,$11,$12,$13,$14
if %targetable_character=6 selnum %target,$10,$11,$12,$13,$14,$15
if %targetable_character=7 selnum %target,$10,$11,$12,$13,$14,$15,$16
mov %target,?targetable_character[%target]
 *battle_attack
get_name $1,%target
$0は$1を攻撃した!@
print_character %target
attack %20,%character,%target
if %20=0 puttext "しかし、失敗してしまった!@":goto *battle_loop
damage %20,%character,%target
%20のダメージを与えた!@
mov ?hp[%target],?hp[%target]-%20
if ?hp[%target]>0 goto *battle_loop
$1は死んだ!@
mov ?is_live[%target],0
goto *battle_loop2

 *get_name
getparam s%get_name_result,%get_name_character
mov $%get_name_result,"(名称不明)"
if %get_name_character<0 return
if %get_name_character>max_character return
if ?name[%get_name_character]=0 mov $%get_name_result,"ファファード"
if ?name[%get_name_character]=1 mov $%get_name_result,"グレイマウザー"
if ?name[%get_name_character]=2 mov $%get_name_result,"ゴブリン"
if ?volume[%get_name_character]=0 return
itoa2 $get_name_character,?volume[%get_name_character]
add $%get_name_result,$get_name_character
return

 *print_character
getparam %print_character_num
get_name $print_character_num,%print_character_num
$print_character_num
技術点:?tp[%print_character_num]
体力点:?hp[%print_character_num]
運命点:?dp[%print_character_num]
return

 *start
; 初期設定
; ファファードとグレイマウザーを設定する。
; 便宜上、ファファードのキャラクター番号を0、グレイマウザーのキャラクター番号を1とする。
for %0=0 to 1 ; ファファードとグレイマウザーの分、同じことを二回繰り返す。
d2 %hp:add %hp,12:mov ?hp[%0],%hp ; 体力点の設定
d1 %tp:add %tp,6:mov ?tp[%0],%tp ; 技術点の設定
d1 %dp:add %dp,6:mov ?dp[%0],%dp ; 運命点の設定
mov ?is_use[%0],1 ; 使用フラグを立てる。
mov ?is_live[%0],1 ; 健在フラグを立てる。
mov ?is_there[%0],1 ; 存在フラグを立てる。
mov ?side[%0],0 ; 陣営を0番陣営に設定する。
next ; for に戻る。
mov ?name[0],0 ; キャラクター名を「ファファード」を意味する0番に設定する。
mov ?volume[0],0 ; 番号はない。
mov ?name[1],1 ; キャラクター名を「グレイマウザー」を意味する1番に設定する。
mov ?volume[1],0 ; 番号はない。

; とりあえずぶつける敵を設定する。
; ゴブリン(技術点5・体力点5)を2-7匹程度。
d1 %10:inc %10 ; ゴブリンの数を%10に格納。
for %0=2 to 2+%10 ; ゴブリンの数だけキャラクターを初期化
mov ?hp[%0],5 ; 体力点の設定
mov ?tp[%0],5 ; 技術点の設定
mov ?dp[%0],0 ; 運命点の設定
mov ?is_use[%0],1 ; 使用フラグを立てる。
mov ?is_live[%0],1 ; 健在フラグを立てる。
mov ?is_there[%0],1 ; 存在フラグを立てる。
mov ?side[%0],1 ; 陣営を1番陣営(敵)に設定する。
mov ?name[%0],2 ; キャラクター名を「ゴブリン」を意味する2番に設定する。
mov ?volume[%0],%0-1 ; 量産型なので番号を設定する。
next
for %0=3+%10 to max_character ; 使わないキャラクターの入れ物を使用不可にする。
mov ?is_use[%0],0 ; 使用フラグを折る。
next

; 敵と味方の設定ができたので、戦闘を開始する。
battle %25 ; 戦闘

一応動く。書きかけ。戦闘の終了条件は検出できるが、どういう条件で終わったかをチェックできないようになっている。