// セッション8用サンプルアプリケーション // Programmed by. 鷹月ぐみな(ishigami@campus.ne.jp) unit PROTO2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, TGameB, MPlayer, MMSystem; type TForm1 = class(TForm) GW1: TGW; MediaPlayer1: TMediaPlayer; procedure FormCreate(Sender: TObject); procedure GW1_0_Create(Sender: TObject); procedure GW1_1_TitleInit(Sender: TObject); procedure GW1_2_TitleJob(Sender: TObject); procedure GW1_3_MainInit(Sender: TObject); procedure GW1_4_MainJob(Sender: TObject); procedure GW1_5_EndingInit(Sender: TObject); procedure GW1_6_EndingJob(Sender: TObject); procedure MediaPlayer1Notify(Sender: TObject); private { Private 宣言 } public { Public 宣言 } procedure SelectUnit(Sender: TObject); procedure DecPosition(Sender: TObject); procedure PlayerAutoMove(Sender: TObject); procedure EnemyThinking(Sender: TObject); procedure EnemyAutoMove(Sender: TObject); procedure PlayerRestore(Sender: TObject); procedure StatusPrint(pstr: String); procedure RangeCheck(Sender: TObject); procedure EnemyRangeCheck(Sender: TObject); procedure Arch(ax,ay,mv,di:integer; mway:String); procedure FourStick(Sender: TObject); procedure MapPrint(Sender: TObject); end; type xandy=record x,y,rank:integer; end; var Form1: TForm1; Epath: String; stat,mx,my,cx,cy,px,py,pmv,emv,cur,tog,golast,goflag: integer; tmx,tmy,slide,dmt:integer; zx,zy,ebanpei,enum:integer; egostr: String; ex,ey: array[1..5] of integer; mvrec: Packed Array[0..255, 0..255] of String; ecango: Packed array[1..400] of xandy; mf1,mf2,mf3: integer; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin // アプリケーションが最初に実行する部分 // 起動時のパスを取得する(カレントが変えられても大丈夫なように) Epath:=ExtractFilePath(Application.ExeName); end; procedure TForm1.GW1_0_Create(Sender: TObject); begin // TGW生成と同時に実行する部分 // ここは最初の1度しか実行される事はないため、そういう類の // 処理しかいれない // 音楽を鳴らしましょう MediaPlayer1.FileName:=Epath+'tikai.mid'; MediaPlayer1.Open; MediaPlayer1.Play; end; procedure TForm1.GW1_1_TitleInit(Sender: TObject); begin // タイトル初期化 end; procedure TForm1.GW1_2_TitleJob(Sender: TObject); begin // タイトル画面の処理 // 将来の拡張用に用意だけしておく(この講座では使わないかも?) GW1.Goto_MainInit; end; procedure TForm1.GW1_3_MainInit(Sender: TObject); begin // S-1) 初期処理 ---------------------------------------------------------- // パラメータの初期化などはこちらで stat:=2; // メインルーチンはこれで管理します mx:=10; my:=10; // mx,myはマップの左上位置(主人公は+9,+7) cx:=2; cy:=2; // cx,cy は、画面内カーソル表示位置 cur:=0; // キャラアニメーションパターン tog:=0; // トグルボタン mf1:=0; // 敵さんCのためのフラグ // 主人公キャラと敵キャラの座標類準備 px:=20; py:=20; pmv:=7; // px,pyは主人公位置、pmvは主人公の移動数 ex[1]:=25; ey[1]:=16; ex[2]:=30; ey[2]:=24; ex[3]:=20; ey[3]:=25; ex[4]:=36; ey[4]:=21; mf2:=ex[4]; mf3:=ey[4]; ex[5]:=30; ey[5]:=20; emv:=6; // マップデータの読みこみ(低速BG0番へのロード) GW1.BG_ChipDataRead(0,'mapdata.dat'); end; procedure TForm1.GW1_4_MainJob(Sender: TObject); begin // S-2) 〜 S-6) を、ステータスによって分岐 if stat=2 then SelectUnit(Sender) else if stat=3 then DecPosition(Sender) else if stat=4 then PlayerAutoMove(Sender) else if stat=5 then EnemyThinking(Sender) else if stat=6 then EnemyAutoMove(Sender) else PlayerRestore(Sender); // 今回、全状態においてマップチップが見えるのでここで共通処理 GW1.FloaterBGput(Epath+'bgchip2',0,mx*32,my*32,1); end; procedure TForm1.GW1_5_EndingInit(Sender: TObject); begin // エンディングといわれても…… end; procedure TForm1.GW1_6_EndingJob(Sender: TObject); begin // ゲームオーバー処理のために用意だけしていますが、 // 今回のスクリプトでは使わないのです end; procedure TForm1.SelectUnit(Sender: TObject); begin // S-2) ユニット選択(今回は1キャラしかないけど……)---------------------- // スペースキーを押すと、移動範囲計算処理、それからS-3)へ if (tog=0) and (GW1.boolButtonLeft) then begin if (mx+cx=px) and (my+cy=py) then begin SndPlaySound(PChar(Epath+'chi.wav'),SND_ASYNC); RangeCheck(Sender); stat:=3; tog:=1; end; end; // トグルのリセット if (tog=1) and not (GW1.boolBUttonLeft) then tog:=0; FourStick(Sender); // カーソル移動 StatusPrint('Players Turn:'); MapPrint(Sender); end; procedure TForm1.DecPosition(Sender: TObject); begin // S-3) 移動先座標選択 ------------------------------------------------------ // リターンキーを押すと、先の画面S-2)に戻ります if (GW1.boolButtonRight) then begin SndPlaySound(PChar(Epath+'chi.wav'),SND_ASYNC); stat:=2; end; FourStick(Sender); // カーソル移動 // スペースキーで移動開始します(大変かも……) if (tog=0) and (GW1.boolButtonLeft) and (GW1.BGf[1,mx+cx,my+cy]=0) then begin //if not ((px=mx+cx) and (py=my+cy)) then begin // アニメーション距離のセット golast:=Length(mvrec[mx+cx,my+cy]); goflag:=1; stat:=4; //end; end; // トグルのリセット if (tog=1) and not (GW1.boolBUttonLeft) then tog:=0; StatusPrint('Kiny''s Postion?'); MapPrint(Sender); // フィルタ表示 GW1.FloaterBGput(Epath+'filter',1,mx*32,my*32,20); end; procedure TForm1.PlayerAutoMove(Sender: TObject); var sr:String; begin // S-4) プレイヤー最短移動 -------------------------------------------------- // 移動アニメルーチン sr:=mvrec[mx+cx,my+cy]; if copy(sr,goflag,1)='1' then dec(py); if copy(sr,goflag,1)='2' then inc(px); if copy(sr,goflag,1)='3' then inc(py); if copy(sr,goflag,1)='4' then dec(px); inc(goflag); SndPlaySound(PChar(Epath+'kan.wav'),SND_ASYNC); if goflag>golast then begin goflag:=0; tog:=1; tmx:=mx; tmy:=my; enum:=1; // 敵1番さんからどうぞ stat:=5; slide:=1; dmt:=0; end; StatusPrint('Kiny''s Moving..'); MapPrint(Sender); // フィルタ表示 GW1.FloaterBGput(Epath+'filter',1,mx*32,my*32,20); end; procedure TForm1.EnemyThinking(Sender: TObject); var i,j,hirank,rankwait1,rankwait2,rankwait3,per,nomove:integer; begin // S-5) のはじめにやらなければいけないこと ------------------------------- // 座標スライド:現在のmx,my から、ex[1]-9,ey[1]-7に向けてスライドする // 本当はドット単位でスライドさせるべき if slide=1 then begin mx:=Trunc(tmx/10*(10-dmt)+(ex[enum]-10)/10*(dmt)); my:=Trunc(tmy/10*(10-dmt)+(ey[enum]-8)/10*(dmt)); dmt:=dmt+1; StatusPrint('Scroll Wait..'); MapPrint(Sender); if dmt>=11 then slide:=0; exit; end; // S-5) 敵側最適探索 ----------------------------------------------------- // 移動可能範囲検索→思考 mx:=ex[enum]-10; my:=ey[enum]-8; // マップ位置の切り替え // 敵の移動可能地点配列のクリア for i:=1 to 400 do begin ecango[i].x:=-1; ecango[i].y:=-1; ecango[i].rank:=0; // 得点付け(あとで重要になる) end; ebanpei:=0; nomove:=0; hirank:=600; EnemyRangeCheck(Sender); // mvrecの書き込まれた地点は選択肢候補 for i:=0 to 255 do for j:=0 to 255 do begin if mvrec[i,j]<>'' then begin ebanpei:=ebanpei+1; ecango[ebanpei].x:=i; ecango[ebanpei].y:=j; end; end; // プレイヤーとの単純距離差を測る per:=abs(px-ex[enum])+abs(py-ey[enum]); // ***** ここが、敵さんごとの思考ルーチン ***** if (enum=2) and (per>10) then nomove:=1; if (enum=3) then begin if (per>3) and (mf1=0) then nomove:=1; if (per<=3) then mf1:=1; end; if (enum=5) and (per>10) then nomove:=1; if nomove=1 then begin egostr:='0'; goflag:=1; golast:=1; end else begin // 移動全地点における距離差からランクを計算、高い所を目標値とする if enum<=3 then begin // 敵A〜Cはこのルーチンで使える hirank:=600; for i:=1 to ebanpei do begin rankwait1:=abs(ecango[i].x-ex[enum])+abs(ecango[i].y-ey[enum]); // 自分との距離 rankwait2:=abs(ecango[i].x-px)+abs(ecango[i].y-py); // プレイヤーとの距離 ecango[i].rank:=rankwait2*6+rankwait1; // ランク付け if hirank>ecango[i].rank then hirank:=ecango[i].rank; // ハイランク更新 end; end; if enum=4 then begin // 敵D(mf2,mf3からの単純距離差6以下である位置でしか動かない) hirank:=600; for i:=1 to ebanpei do begin rankwait1:=abs(ecango[i].x-mf2)+abs(ecango[i].y-mf3); rankwait2:=abs(ecango[i].x-px)+abs(ecango[i].y-py); rankwait3:=abs(ecango[i].x-ex[enum])+abs(ecango[i].y-ey[enum]); // 自分との距離 if rankwait1<=6 then ecango[i].rank:=rankwait2*6+rankwait3 else ecango[i].rank:=600; // ランク付け if hirank>ecango[i].rank then hirank:=ecango[i].rank; end; end; if enum=5 then begin // 敵E(単純3になる場所を確保しようとするルーチンはここ) hirank:=600; for i:=1 to ebanpei do begin rankwait2:=abs(ecango[i].x-px)+abs(ecango[i].y-py); rankwait3:=abs(ecango[i].x-ex[enum])+abs(ecango[i].y-ey[enum]); // 自分との距離 if rankwait2=3 then ecango[i].rank:=1+rankwait3 else ecango[i].rank:=rankwait2*6+rankwait3; if hirank>ecango[i].rank then hirank:=ecango[i].rank; end; end; egostr:='0'; goflag:=1; golast:=1; // 番兵的処置 for i:=1 to ebanpei do begin if ecango[i].rank=hirank then begin egostr:=mvrec[ecango[i].x,ecango[i].y]; // 目的地決定 golast:=Length(mvrec[ecango[i].x,ecango[i].y]); goflag:=1; break; end; end; end; StatusPrint('Enemy-'+InttoStr(enum)+' Thinking..'); MapPrint(Sender); stat:=6; end; procedure TForm1.EnemyAutoMove(Sender: TObject); begin // S-6) 敵さん側最短移動 -------------------------------------------------- if copy(egostr,goflag,1)='0' then begin goflag:=0; stat:=7; dmt:=0; tmx:=mx; tmy:=my; MapPrint(Sender); GW1.FloaterBGput(Epath+'filter',1,mx*32,my*32,20); end; if copy(egostr,goflag,1)='1' then dec(ey[enum]); if copy(egostr,goflag,1)='2' then inc(ex[enum]); if copy(egostr,goflag,1)='3' then inc(ey[enum]); if copy(egostr,goflag,1)='4' then dec(ex[enum]); inc(goflag); SndPlaySound(PChar(Epath+'kan.wav'),SND_ASYNC); if goflag>golast then begin goflag:=0; dmt:=0; tmx:=mx; tmy:=my; stat:=7; end; StatusPrint('Enemy-'+InttoStr(enum)+' Moving..'); MapPrint(Sender); // フィルタ表示 GW1.FloaterBGput(Epath+'filter',1,mx*32,my*32,20); if (stat=7) then begin if enum<=4 then begin enum:=enum+1; stat:=5; tmx:=mx; tmy:=my; slide:=1; dmt:=0; end else begin enum:=1; end; end; end; procedure TForm1.PlayerRestore(Sender: TObject); begin // 座標をmx → px-10、my → py-8 に戻すのです mx:=Trunc(tmx/10*(10-dmt)+(px-10)/10*(dmt)); my:=Trunc(tmy/10*(10-dmt)+(py-8)/10*(dmt)); dmt:=dmt+1; StatusPrint('Scroll Wait..'); MapPrint(Sender); if dmt>=11 then begin stat:=2; mx:=px-10; my:=py-8; cx:=10; cy:=8; end; end; // --- ここからはサブルーチンです ------------------------------------------- procedure TForm1.StatusPrint(pstr: String); begin GW1.FontSprtZ(7,7,24,0,0,0,0,128,pstr); GW1.FontSprtZ(8,6,24,0,0,0,0,128,pstr); GW1.FontSprtZ(6,5,24,0,0,0,0,128,pstr); GW1.FontSprtZ(6,6,24,255,255,128,0,128,pstr); end; procedure TForm1.RangeCheck(Sender: TObject); var i,j:integer; begin // フィルターを全解除 for i:=0 to 255 do for j:=0 to 255 do begin GW1.BGF[1,i,j]:=1; mvrec[i,j]:=''; end; // 主人公の位置から、移動距離 pmv 分への再帰探索をはじめる Arch(px,py-1,pmv,1,'1'); Arch(px+1,py,pmv,2,'2'); Arch(px,py+1,pmv,3,'3'); Arch(px-1,py,pmv,4,'4'); // 同じ位置のチェックをここで許可 mvrec[px,py]:='0'; GW1.BGf[1,px,py]:=0; end; procedure TForm1.EnemyRangeCheck(Sender: TObject); var i,j:integer; begin // フィルターを全解除 for i:=0 to 255 do for j:=0 to 255 do begin GW1.BGF[1,i,j]:=1; mvrec[i,j]:=''; end; // モンスターの位置から、移動距離 emv 分への再帰探索をはじめる // 実はこのemvは過ち。本当は10くらいで固定すべき Arch(ex[enum],ey[enum]-1,emv,1,'1'); Arch(ex[enum]+1,ey[enum],emv,2,'2'); Arch(ex[enum],ey[enum]+1,emv,3,'3'); Arch(ex[enum]-1,ey[enum],emv,4,'4'); // モンスターは、同じ位置のチェックを許される mvrec[ex[enum],ey[enum]]:='0'; GW1.BGf[1,ex[enum],ey[enum]]:=0; end; procedure TForm1.Arch(ax,ay,mv,di:integer; mway:String); var i,num,down:integer; begin // すでに地点情報が書きこまれている場所は無視する事 if (mvrec[ax,ay]<>'') and (Length(mway) > Length(mvrec[ax,ay])) then exit; // その座標にプレイヤーもしくはお仲間がでんと立っているならダメよん if (px=ax) and (py=ay) then exit; for i:=1 to 5 do begin if (ax=ex[i]) and (ay=ey[i]) then exit; end; // まず、その場所の地形情報を調べる。downは、地形を移動するために必要な距離数 down:=0; num:=GW1.BGf[0,ax,ay]; // 仮決定部 if (num>=1) and (num<=15) then down:=1; if num>=16 then down:=10; // 本決定部 if num=0 then down:=10; if (num=3) or (num=12) or (num=15) then down:=2; if (num=2) then down:=3; if (num=21) then down:=6; if mv-down<0 then exit; //移動できないなら調べてもムダですね // すくなくとも移動はできるようなんで、マークして次へ GW1.BGF[1,ax,ay]:=0; mvrec[ax,ay]:=mway; // --- モンスター移動の場合、可能座標を配列に記憶 {if stat=5 then begin ebanpei:=ebanpei+1; ecango[ebanpei].x:=ax; ecango[ebanpei].y:=ay; end; } if di<>3 then Arch(ax,ay-1,mv-down,1,mway+'1'); if di<>4 then Arch(ax+1,ay,mv-down,2,mway+'2'); if di<>1 then Arch(ax,ay+1,mv-down,3,mway+'3'); if di<>2 then Arch(ax-1,ay,mv-down,4,mway+'4'); end; procedure TForm1.FourStick(Sender: TObject); begin // カーソル移動判定部 if (GW1.boolKeyLeft) then begin SndPlaySound(PChar(Epath+'ki.wav'),SND_ASYNC); if cx<1 then dec(mx) else dec(cx); end else if (GW1.boolKeyRight) then begin SndPlaySound(PChar(Epath+'ki.wav'),SND_ASYNC); if cx>18 then inc(mx) else inc(cx); end; if (GW1.boolKeyUp) then begin SndPlaySound(PChar(Epath+'ki.wav'),SND_ASYNC); if cy<1 then dec(my) else dec(cy); end else if (GW1.boolKeyDown) then begin SndPlaySound(PChar(Epath+'ki.wav'),SND_ASYNC); if cy>13 then inc(my) else inc(cy); end; end; procedure TForm1.MapPrint(Sender: TObject); var i:integer; begin // マップ表示部(プレイヤーキャラ/敵キャラ/カーソル) // キャラクター表示 zx:=px-mx; zy:=py-my; GW1.putZ(Epath+'girl',1+(cur div 5),zx*32+16,zy*32-24,100+zy); GW1.putZ(Epath+'girl',3+(cur div 5),zx*32+16,zy*32+8,100+zy); // 敵キャラ表示(Zレイアは偉大なり) for i:=1 to 5 do begin zx:=ex[i]-mx; zy:=ey[i]-my; GW1.putZ(Epath+'eneh',1,zx*32+16,zy*32-24,100+zy); GW1.putZ(Epath+'enet',i,zx*32+16,zy*32+8,100+zy); end; // カーソル表示 if stat<=4 then GW1.put(Epath+'Cur',(cur div 5)+1,cx*32+16,cy*32+16); cur:=cur+1; if cur>=10 then cur:=0; end; procedure TForm1.MediaPlayer1Notify(Sender: TObject); begin // BGMの連続演奏処理 If MediaPlayer1.Mode=mpPlaying then exit; MediaPlayer1.Previous; MediaPlayer1.Play; end; end.