【プチコン講座】ステータスウィンドウを実装してみよう

プチコン

プチコン ステータスウィンドウ

こんにちは。継続の錬金術士なおキーヌです。

ブログ毎日更新は147日目になります。

前回「【プチコン講座】ウィンドウ生成を関数化&宝箱イベントを完成させよう」でようやく宝箱イベントを完成させることができました。

更に、ウィンドウを関数化したので自由にウィンドウを生成することが出来るようになっています!

今回はXボタンを押したらステータス画面を表示できるようにしていきましょう。

プレイヤーのステータスは既に配列として作成しているので、
それをウィンドウに描写するだけなので結構簡単だと思います。

それではプチコンでRPG作り第19回目を始めましょう。

  1. ウィンドウ生成関数を使ってみよう
  2. ステータスウィンドウをどう作るか考えてみよう
  3. ステータスウィンドウにステータスを描写しよう
  4. ステータス画面の開閉をできるようにしよう

ウィンドウ生成関数を使ってみよう

ウィンドウ関数を作ったので一回自由にウィンドウを表示してみましょう。

「D_WINDWOW_DRAW」関数をループ開始前に適当につかってみてください。

ウィンドウを好きな所に好きな大きさで表示できれば問題ないです。


D_WINDWOW_DRAW 0, 0, 50,50
D_WINDWOW_DRAW 100, 0, 50,300
D_WINDWOW_DRAW 50, 100, 100,20

実行すると好き勝手にウィンドウが開いている状態になっていると思います。

しかし、ちょっと設計ミスかもしれません。

左上の基準値が背景の左上になっていて、フレームを考慮していない状態になっています。

気になる人は関数を修正してみてください。

1つ基準値を変えれば全部変わってくれると思います。

ゲーム作りの進行にはあまり影響しないので私はこのまま進めます。

ステータスウィンドウをどう作るか考えてみよう

本来のRPGならメニュー画面を開いてからステータスなのですが、
今作っている1画面RPGは特にメニューはないのでステータス画面だけにします。

正直あまり見る意味もないのですが、一応RPG作成講座なので今後のためにも作っておこうと思います。

実は大半の構築は前回までで終わっているのであとはウィンドウを描写して、
保持しているステータスをウィンドウに描写するだけです。

問題はどこに表示するかなのですが、これは好きな所で構いません。

ステータス画面を開いている時は、メッセージウィンドウが開いている時と同じで
別シーンという扱いにした方が良いでしょう。

メニューを開いている時に動ける状態では都合が悪かったりするので止めておくのがベターです。

メニューを開いていても動くリアルタイムなゲームであればいいかもしれませんが、
大半のRPGでそれをやってしまうとユーザーのストレスになってしまいます。

ゲーム実況にするなら面白いかもしれませんが、普通にRPGをプレイするうえで
ステータス画面を開いている時にゲームが進行し続けているのはストレスにしかなりません。

ステータス画面描写の座標を決める

好きな所でいいと言ったものの初心者の方は戸惑うかと思うので、
ドラクエ1のステータスを参考にステータスウィンドウを作ってみましょう。

なのでステータスウィンドウを開くときに呼ばれる関数を作って、
その中で文字を描写するという形がスマートだと思います。

Xボタンが押されたらその関数を呼び出せばいいだけですからね。

それではウィンドウの座標を決めてしまいましょう。


VAR G_WINDOW_SIZES_LEN = 8

# ウィンドウサイズ用
@WINDOWSIZE
DATA 80,150,240,58,  10,10,112,77

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW
  D_WINDWOW_DRAW G_WINDOW_SIZES[4], G_WINDOW_SIZES[5], G_WINDOW_SIZES[6], G_WINDOW_SIZES[7]
END

ウィンドウサイズのデータにステータスウィンドウの座標と大きさを追加して、
ウィンドウサイズの配列の長さを8にしておきます。

そして、新たに「D_STATUS_WINDOW_DRAW」という関数を作って
その中でウィンドウを生成して先ほど作った座標を渡します。

一度関数を実行して左上のいい感じの位置にウィンドウがきていればOKです。

ステータスウィンドウにステータスを描写しよう

ウィンドウが用意できたので次は実際にステータスをウィンドウに描写してみましょう。

やることはメッセージ表示と殆ど同じでかなり簡単です。

既にプレイヤーステータスとして用意している配列「G_PLAYER_STATUS」から、
値を取り出してステータスウィンドウに描写します。

その前に、まずは一番数値が大きい時の状態で表示位置を決めたいので
まずはリテラルで文字を打ち込んで文字位置を調整しましょう。

後、グローバル配列を書くとプチコンエディタでは見にくくなるので、
ローカル配列を作って見やすくしましょう。

その前に、システムワードを少し変更します。

STRやらDEFになってたと思うので、順番とひらがなを変更してみました。

後、ポーションの数である「ヶ」は別に要らないかなと判断しました。

これも好みの問題なので会った方が良いという人は付けてみても大丈夫です。

先頭の「STATUS」はリテラルで問題ないと思うので一旦省きます。

「NEXT」も スラッシュで区切るので消しました。

@SYSTEMWORD
DATA "STATUS", "ポーション", "ヶ", "Lv", "HP", "STR", "DEF", "AGI", "EXP", "NEXT"@SYSTEMWORD
DATA "Lv", "HP", "こうげき", "ぼうぎょ", "すばやさ", "けいけんち", "ポーション"

これに伴いシステムワードの数も変更しておきました。

VAR G_SYSTEM_WORDS_LEN = 7

それではまずはリテラルでステータスの最大値を表示してみましょう!

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW
  # ローカル配列定義
  DIM WS[0]
  # グローバル配列の参照を格納
  WS = G_WINDOW_SIZES

  # ウィンドウの描写
  D_WINDWOW_DRAW WS[4], WS[5], WS[6], WS[7]

  # ステータス文字の描写
  GPUTCHR WS[4]+5, WS[5]+5,  "Lv       :     99"
  GPUTCHR WS[4]+5, WS[5]+15, "HP      :  99/99"
  GPUTCHR WS[4]+5, WS[5]+25, "こうげき  :     99"
  GPUTCHR WS[4]+5, WS[5]+35, "ぼうぎょ  :     99"
  GPUTCHR WS[4]+5, WS[5]+40, "すばやさ  :     99"
  GPUTCHR WS[4]+5, WS[5]+55, "けいけんち:999/999"
  GPUTCHR WS[4]+5, WS[5]+65, "ポーション:     99"
END

まずはこれで一回表示してみましょう。

いい感じにステータスウィンドウが出来上がったのではないでしょうか?

ステータス文字描写の部分、一定の規則性があると思うので
これをリライトしてみましょう。

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW

  ~~~省略~~~

  # ステータス文字の描写
  FOR G_I = 0 TO LEN(G_SYSTEM_WORDS_LEN)-1
    VAR STATUS = ""

    IF G_I == 0 THEN
      STATUS = "Lv       :     99"
    ELSEIF G_I == 1 THEN
      STATUS = "HP      :  99/99"
    ELSEIF G_I == 2 THEN
      STATUS = "こうげき  :     99"
    ELSEIF G_I == 3 THEN
      STATUS = "ぼうぎょ  :     99"
    ELSEIF G_I == 4 THEN
      STATUS = "すばやさ  :     99"
    ELSEIF G_I == 5 THEN
      STATUS = "けいけんち:999/999"
    ELSEIF G_I == 6 THEN
      STATUS = "ポーション:     99"
    ENDIF

    # 実際のステータスの描写
    GPUCHR  WS[4]+5, WS[5]+5 + (G_I*10), STATUS
  NEXT
END

……なんか逆に長くなった感じがします。

同じ感じに描写できていれば、次はこれを変数に置き換えてみましょう。

まずは空白無しで変数同士をコロンで挟んで表示してみます。

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW

  ~~~省略~~~

  # ステータス文字の描写
  VAR STATUS = ""
  FOR G_I = 0 TO G_SYSTEM_WORDS_LEN-1
    IF G_I == 0 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[0])
    ELSEIF G_I == 1 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[1])+"/"+STR$(G_PLAYER_STATUS[2])
    ELSEIF G_I == 2 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[3])
    ELSEIF G_I == 3 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[4])
    ELSEIF G_I == 4 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[5])
    ELSEIF G_I == 5 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[6])+"/999"
    ELSEIF G_I == 6 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[7])
    ENDIF

    # 実際のステータスの描写
    GPUCHR  WS[4]+5, WS[5]+5 + (G_I*10), STATUS
  NEXT
END

経験値のレベルアップテーブルはまだ用意していないので一旦999にでもしておいてください。

このまま最大値を表記しててもいいし、NEXT値にしてもいいです。好きな方で。

これを決めるのはゲーム完成前ぐらいでいいかと思います。

バランス調整もありますからね。

さて、描写してみましょう。

……いい感じにステータスを描写できましたが、ズレまくっているのが気になります。

コロンは揃えたいところですね。

プチコンというかプログラミング言語にには文字列の数を数える機能があります。

合計の文字数が満たない場合は空白を入れて調整するという関数を作っておいて、
いい感じにレイアウトをそろえるのがいいですが、今は面倒なので手動でやりましょう。

たぶん上手くやればこの分岐をずらーって書いているところを2・3行で書けそうです。

ですが今のところゲームシステムに影響はない場所なので最適化はせずに進めます。

もしレイアウトが気になる人は整えてみてください。

数値の桁を合わせたり結構面倒ですが、プログラミングの訓練になります。

ここの最適化はまた別の記事を1つ設けてそこでやろうかなとおもいました。

とりあえず関数の完成版コードを載せておきます。

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW
  # ローカル配列定義
  VAR STATUS = ""
  DIM WS[0]
  # グローバル配列の参照を格納
  WS = G_WINDOW_SIZES

  # ウィンドウの描写
  D_WINDWOW_DRAW WS[4], WS[5], WS[6], WS[7]

  # ステータス文字の描写
  FOR G_I = 0 TO G_SYSTEM_WORDS_LEN-1
    # ステータス文字の描写準備
    IF G_I == 0 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[0])
    ELSEIF G_I == 1 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[1])+"/"+STR$(G_PLAYER_STATUS[2])
    ELSEIF G_I == 2 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[3])
    ELSEIF G_I == 3 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[4])
    ELSEIF G_I == 4 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[5])
    ELSEIF G_I == 5 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[6])+"/999"
    ELSEIF G_I == 6 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[7])
    ENDIF

    # 実際のステータスの描写
    GPUCHR  WS[4]+5, WS[5]+5 + (G_I*10), STATUS
  NEXT
END

ステータス画面の開閉をできるようにしよう

ステータスの描写が出来たので、Xボタンを押して開閉できるようにしてみましょう。

まずはシーンのことは気にせずボタンを押したら開く・もう一度押したら閉じる関数を作ってみましょう。

VAR G_STATUS_FLAG = FALSE

DEF D_STATUS_WINDOW_TOGGLE
  # フラグを反転させる
  G_STATUS_FLAG = NOT G_STATUS_FLAG

  # フラグによって開くか閉じるかを決める
  IF STATUS_FLAG THEN
    D_STATUS_WINDOW_DRAW
  ELSE
    GCLS
  ENDIF
END

この関数を1回呼べば開いて、もう一度呼べば閉じます。

「D_STATUS_WINDOW_TOGGLE」をマップのXキーを押したときに呼び出してみましょう。

現状、起動したときにウィンドウを表示している状態になっているのでそれを消して下記に変更します。

# マップ用コントローラー関数
DEF D_CTRL_MAP_MODE

 ~~~ 省略 ~~~

  IF G_BTN_PRESS_COUNT[6] == 1 THEN
    PRINT "Xボタンが押されたよ!"
    D_STATUS_WINDOW_TOGGLE
  ENDIF
END

実行してみて、マップを移動中にXキーを押せば開くようになります。

あれ……意外とこのままでもいい気がしてきました。

ウィンドウの透明度をあげれば邪魔になりませんし意外とこのままでもいいかもしれません。

が、このままだとメッセージイベントが終わったらフラグが切り替わらないままステータス画面も消えてしまうので、
ステータスシーンは別途作っていきます。

ステータスシーンを作ろう

もうステータス画面を表示非表示が出来てしまったので、あとは専用のシーンを作るだけですね。

現状、マップモードでXボタンを押すと表示非表示が出来る状態になっていると思います。

それをシーン切り替えにしてフラグをステータスシーンのモノにしてみましょう。


# シーンの大元
DEF SCENE_PARENT

  IF G_SCENE_FLAG == 0 THEN
    D_CTRL_MAP_MODE
  ELSEIF G_SCENE_FLAG == 1 THEN
    IF G_EV_Q_FLAG == 0 THEN
      D_SHIFT_EV_DATA
    ELSEIF EV_Q_FLAG == 1 THEN
      D_EV_STOP_A_BTN
    ENDIF
  ELSEIF G_SCENE_FLAG == 2 THEN
    D_STATUS_MODE
  ENDIF

  D_BTN_PRESS

END

# マップ用コントローラー関数
DEF D_CTRL_MAP_MODE

 ~~~ 省略 ~~~

  IF G_BTN_PRESS_COUNT[6] == 1 THEN
    PRINT "Xボタンが押されたよ!"
    G_SCENE_FLAG = 2
  ENDIF
END

# ステータスフラグ反転
DEF D_STATUS_WINDOW_TOGGLE
  # フラグを反転させる
  G_STATUS_FLAG = NOT G_STATUS_FLAG

  # フラグによって開くか閉じるかを決める
  IF STATUS_FLAG THEN
    D_STATUS_WINDOW_DRAW
  ELSE
    # グラフィック全消去
    GCLS
    # シーンを元に戻す
    G_SCENE_FLAG = 0
  ENDIF
END

# ステータスモード
DEF D_STATUS_MODE
  # 初回にステータスウィンドウフラグをONにしておく
  IF NOT G_STATUS_FLAG THEN
    D_STATUS_WINDOW_TOGGLE
  ENDIF

  # Xボタン監視
  IF G_BTN_PRESS_COUNT[6] == 1 THEN
    D_STATUS_WINDOW_TOGGLE
  ENDIF

END

実行してみましょう。

プチコン 結果

移動中にXキーを押すとグリッド移動中でもプレイヤーが止まってメニューが表示されます。

そして話すと移動が再開します。

現在ポーションの値が1になっているので、宝箱を開いてポーションを獲得してみましょう。

そしてもう一度ステータスを開いてポーションの数が2になっていれば完璧です!

プチコン 結果

──次回からついにバトルシステムを構築していきます。

いきなりアクロバティックな動きがあるような戦闘シーンを作ることは難しいので、
かなりシンプルなバトルにするつもりです。

javascript版はキーを押して攻撃か回復かを選ぶターン性だったのですが、
プチコンではちょっとATB風のバトルを予定しています。

メーターが溜まったら攻撃を選択できる状態にします。

ターン性よりは作る難易度があがってしまいますが、
まったく同じことをやっていても私自信が成長しないのチャレンジします。

私が書くゲーム制作講座は過去の私のための講座です。

こんな講座があったらいいのになっていう講座にしあげていっています。

なのでチャレンジします。

最後に恒例のソースコードを貼っておきます。

流石にコードがかなり長くなってきましたね……

しかしバトルイベントが構築出来たらもうすぐ完成は見えてきます。

最後の踏ん張りどころなので頑張りましょう!

それでは。

ACLS

#------------------
# 変数定義
#------------------
VAR G_STOP_FLAG = TRUE # ゲームループフラグ
VAR G_I # 汎用変数

# シーンフラグ
VAR G_SCENE_FLAG = 0
VAR G_STATUS_FLAG = FALSE

# イベント用
VAR G_EV_Q_FLAG = 0 # イベントキューがあるかどうか
DIM G_EV_ID[32] # イベント用情報の配列(初回DATA読み込み用)
VAR G_EV_ID_NOW = 0 # 現在のイベントナンバー
VAR G_EV_STEP = 4 # 1つのイベントデータ数
DIM G_EV_QUEUE[0] # イベントキュー配列

# プレイヤー用
VAR G_PLAYER_NAME$ = ""
VAR G_PLAYER_STATUS_LEN = 8
DIM G_PLAYER_STATUS[0]

# ウィンドウサイズ用
VAR G_WINDOW_SIZES_LEN = 8
DIM G_WINDOW_SIZES[0]

# アイテム名用
VAR G_ITEMS_NAMES_LEN = 2
DIM G_ITEMS_NAMES[0]

# システムワード
VAR G_SYSTEM_WORDS_LEN = 7
DIM G_SYSTEM_WORDS[0]

# 敵用
VAR G_ENEMY_NAMES_LEN = 5
DIM G_ENEMY_NAMES[0]

# メッセージ用
VAR G_MSG_DATAS_LEN = 4
DIM G_MSG_DATAS[0]

# ボタン分の配列を用意
DIM G_BTN_PRESS_COUNT[13] # 全ボタンカウント配列
FILL G_BTN_PRESS_COUNT,0 # 一応定義したボタン配列を0で初期化しておく

# BG関連
VAR G_OX = 0, G_OY = 0 # BG読み込みのオフセット
VAR G_SZ=16, G_MW, G_MH # チップサイズ、マップ幅、マップ高さ
VAR G_BGW = CEIL(400/G_SZ), G_BGH = CEIL(240/G_SZ) # BG読み込み準備
DIM G_MAP[0] # マップレイヤー4枚+イベントレイヤー分

# スプライト用
VAR G_SP_PLAYER = 0 # PLAYERスプライトNo.
DIM G_SP_ANIM_NO[9] # スプライトアニメーション初期値変数 添え字=ID

#------------------
# データベース定義
#------------------
# 開始時に読み込むDATA軍(ラベル付けたほうが安全かもしれない
DATA 500,1040,920,1000,980,1020,269,269,269
DATA 1,5,5,2,  2,23,13,2,  3,4,11,2,  4,13,3,2,  5,22,4,2
DATA 6,6,7,1,  7,3,11,1,  8,22,3,1

# 宝箱イベントデータ
@EVENT006
DATA 7,  10,95,400  3,1,268  2,0,1  1,1,0, 10,12,0  1,2,0,  0,1,1
@EVENT006_1
DATA 2,  1,3,0,  0,0,0

# プレイヤー初期ステータス
@PLAYER_INIT_STATUS
DATA 1,20,20,3,2,3,0,1

# ウィンドウサイズ用
@WINDOWSIZE
DATA 80,150,240,58,  10,10,112,77

# ステータスウィンドウ用ワード
@SYSTEMWORD
DATA "Lv", "HP", "こうげき", "ぼうぎょ", "すばやさ", "けいけんち", "ポーション"

# アイテム名リスト
@ITEMDATA
DATA "ライフポーション", "せいすい"

# モンスター名リスト
@ENEMYNAME
DATA "リトルバット", "ゴブリン", "スケルトン", "マミー", "ゴースト"

# 定型文リスト
@MESSAGEDATA
DATA "は たからばこを あけた", "たからばこ には", "が はいっていた!", "からっぽ!"

# アニメーション配列初期値代入
D_FIRST_DATA_READ G_SP_ANIM_NO
# イベントID,X座標,Y座標代入
D_FIRST_DATA_READ G_EV_ID

# マップデータ準備
LOAD "DAT:TEST", G_MAP, 0 # マップデータのロード
G_MW = SHIFT(G_MAP) # 読み込んだデータ配列の0個目を配列から切り離して取得(1638415という数字が入ってる)
G_MH = G_MW AND &HFFFF : G_MW = G_MW >> 16 AND &HFFFF # 取り出したデータからシフト演算やらビット演算をする

# マップデータ描写
FOR G_I=0 TO 3
  BGSCREEN G_I, G_BGW, G_BGH, G_SZ # 1画面分のBG
  BGOFS 0,0,4-G_I
  BGLOAD I, -G_OX, ( -G_OY - ( G_I * G_MH )), G_MW, G_MH * ( G_I + 1 ), G_MAP # マップ描写
NEXT

# プレイヤー配置
D_SP_SETUP G_SP_PLAYER, 1, 0

# イベント配置
FOR G_I=0 TO LEN(G_EV_ID)-1 STEP G_EV_STEP
  D_SP_SETUP G_EV_ID[G_I], G_EV_ID[G_I+1], EV_ID[G_I+2]
NEXT

# 各種データの読み込み
D_DB_LOAD 0, G_PLAYER_STATUS_LEN, G_PLAYER_STATUS
D_DB_LOAD 1, G_SYSTEM_WORDS_LEN, G_SYSTEM_WORDS
D_DB_LOAD 2, G_ITEM_NAMES_LEN, G_ITEM_NAMES
D_DB_LOAD 3, G_ENEMY_NAMES_LEN, G_ENEMY_NAMES
D_DB_LOAD 4, G_MSG_DATAS_LEN, G_MSD_DATAS

# グラフィック画面を一番手前に
GPRIO 0

# ゲームループ
WHILE G_STOP_FLAG
  D_SCENE_PARENT
  VSYNC 1
WEND


# シーンの大元
DEF SCENE_PARENT

  IF G_SCENE_FLAG == 0 THEN
    D_CTRL_MAP_MODE
  ELSEIF G_SCENE_FLAG == 1 THEN
    IF G_EV_Q_FLAG == 0 THEN
      D_SHIFT_EV_DATA
    ELSEIF EV_Q_FLAG == 1 THEN
      D_EV_STOP_A_BTN
    ENDIF
  ELSEIF G_SCENE_FLAG == 2 THEN
    D_STATUS_MODE
  ENDIF

  D_BTN_PRESS

END

# マップ用コントローラー関数
DEF D_CTRL_MAP_MODE
  IF SPVAR(SP_PLAYER, 2) != 1 THEN
    VAR B = BUTTON()
    IF (B AND #UP) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #UP) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #UP
      ENDIF
    ELSEIF (B AND #DOWN) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #DOWN) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #DOWN
      ENDIF
    ELSEIF (B AND #LEFT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #LEFT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #LEFT
      ENDIF
    ELSEIF (B AND #RIGHT) > 0 THEN
      IF D_CHECK_COLLISION(G_SP_PLAYER, #RIGHT) == 1 THEN
        D_DIRECTION_MOVE SP_PLAYER, #RIGHT
      ENDIF
    ENDIF
  ELSE
    D_GRID_MOVE SP_PLAYER, (SPVAR SP_PLAYER, 3)
  ENDIF

  # 移動中もボタンを押せるようにしておく
  IF G_BTN_PRESS_COUNT[4] == 1 THEN
    PRINT "Aボタンが押されたよ!"
    D_EV_CHECK G_SP_PLAYER SPVAR(G_SP_PLAYER, 3)
  ENDIF

  IF G_BTN_PRESS_COUNT[6] == 1 THEN
    PRINT "Xボタンが押されたよ!"
    G_SCENE_FLAG = 2
  ENDIF
END


# 向きと移動フラグを設定
DEF D_DIRECTION_MOVE A_SPRITE_NO, A_SPRITE_DIRECTION
  SPVAR A_SPRITE_NO, 2, 1
  SPVAR A_SPRITE_NO, 3, A_DIRECTION
END

# 向いている方向に止まるまで歩き続ける(グリッド移動)
DEF D_GRID_MOVE SPRITE_NO, SPRITE_DIRECTION
  # 渡されたスプライトと向きによって移動方向を決定
  IF SPRITE_DIRECTION == #UP THEN
    SPVAR SPRITE_NO, 1, (SPVAR SPRITE_NO, 1) - 1
  ELSEIF SPRITE_DIRECTION == #DOWN THEN
    SPVAR SPRITE_NO, 1, (SPVAR SPRITE_NO, 1) + 1
  ELSEIF SPRITE_DIRECTION == #LEFT THEN
    SPVAR SPRITE_NO, 0, (SPVAR SPRITE_NO, 0) - 1
  ELSEIF SPRITE_DIRECTION == #RIGHT THEN
    SPVAR SPRITE_NO, 0, (SPVAR SPRITE_NO, 0) + 1
  ENDIF

  # 移動させる
  SPOFS SPRITE_NO,(SPVAR SPRITE_NO, 0),(SPVAR SPRITE_NO, 1)

  # 割った余りが0になったら動きを止める
  IF (SPVAR SPRITE_NO, 0) MOD G_SZ == 0 && (SPVAR G_SPRITE_NO, 1) MOD G_SZ == 0 THEN
    SPVAR G_SPRITE_NO, 2, 0
  ENDIF

END

# ドット座標からグリッド座標に変換
DEF D_GET_GRID_XY(A_X_OR_Y)
  RETURN A_X_OR_Y / G_SZ
END

# MAP配列のどこにいるかチェック
DEF D_GET_MAP_POSITION(A_X, A_Y, A_LAYER)
  VAR RETURN_POS = 0
  VAR GRID_X = D_GET_GRID_XY(A_X)
  VAR GRID_Y = D_GET_GRID_XY(A_Y)

  VAR PULS_LAYER = (G_MW * G_MH) * A_LAYER

  IF A_Y <= 0 THEN
    RETURN_POS = GRID_X + PLUS_LAYER
  ELSE
    RETURN_POS = (GRID_X + (GRID_Y * G_MW)) + PLUS_LAYER
  ENDIF

  RETURN RETURN_POS
END

# 当たり判定チェック
DEF D_CHECK_COLLISION(A_NO, A_DIRECTION)
  VAR MP1_SUM_POS = 0
  VAR EV_SUM_POS = 0

  VAR DIRECT = D_GET_DIRECT_POINT(A_DIRECTION)

  VAR MAP_POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 1)
  VAR EVENT_POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)

  MP1_SUM_POS = MAP_POS + DIRECT
  EV_SUM_POS = EVENT_POS + DIRECT

  IF G_MAP[MP1_SUM_POS] == 0 THEN
    IF G_MAP[EV_SUM_POS] == 0 THEN
      D_EV_LAYER_REWRITE A_NO, A_DIRECTION, 1
      RETURN 1
    ELSE
      RETURN 0
    ENDIF
  ELSE
    RETURN 0
  ENDIF
END

# スプライトセットアップ
DEF D_SP_SETUP A_NO, A_X, A_Y
  VAR F = 20
  VAR ANIM = G_SP_ANIM_NO[A_NO]

  SPSET A_NO ANIM
  SPVAR A_NO 0, G_SZ * A_X
  SPVAR A_NO 1, G_SZ * A_Y
  SPVAR A_NO 2, 0
  SPVAR A_NO 3, #DOWN
  SPOFS A_NO SPVAR(A_NO,0), SPVAR(A_NO,1),1

  D_EV_LAYER_REWRITE A_NO, 0, 1

  IF A_NO == 0 || G_EV_ID[(G_EV_STEP * A_NO)-1] == 2 THEN
    SPANIM A_NO, "I", F,ANIM, F,ANIM+1, F,ANIM+2, F,ANIM+3, 0 
  ENDIF
END

# イベントレイヤー書き換え
DEF D_EV_LAYER_REWRITE A_NO, A_DIRECT, A_FLAG

  VAR POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)

  IF A_FLAG > 0 THEN
    IF A_DIRECT > 0 THEN
      VAR AFTER_POS = POS + D_GET_DIRECT_POINT(A_DIRECT)
      G_MAP[AFTER_POS] = A_NO
    ELSE
      G_MAP[POS] = A_NO
    ENDIF
  ELSE
    VAR BEFORE_POS = POS - D_GET_DIRECT_POINT(A_DIRECT)
    G_MAP[BEFORE_POS] = 0
  ENDIF

END

# 方向定数から1歩前のグリッド座標を得るための値を得る
DEF D_GET_DIRECT_POINT(A_DIRECTION)
  IF A_DIRECTION == #UP THEN
    RETURN DIRECT = -MW
  ELSEIF A_DIRECTION == #DOWN THEN
    RETURN DIRECT = MW
  ELSEIF A_DIRECTION == #LEFT THEN
    RETURN DIRECT = -1
  ELSEIF A_DIRECTION == #RIGHT THEN
    RETURN DIRECT = 1
  ENDIF
END

# DATA初期読み込み
DEF D_FIRST_DATA_READ A_ARRAY
  FOR G_I=0 TO LEN(G_EV_ID)-1
    VAR N=0: READ N
    G_EV_ID[G_I] = N
  NEXT
END

# ボタンカウント関数
DEF D_BTN_PRESS_COUNT
  VAR B = BUTTON()

  # Aボタンカウント
  IF (B AND #A) > 0 THEN
    IF G_BTN_PRESS_COUNT[4] < 255 THEN 
      INC G_BTN_PRESS_COUNT[4]
    ENDIF
  ELSE
    # 1回でも離されたらカウントリセット
    G_BTN_PRESS_COUNT[4] = 0
  ENDIF

  # Xボタンカウント
  IF (B AND #X) > 0 THEN
    IF G_BTN_PRESS_COUNT[6] < 255 THEN 
      INC G_BTN_PRESS_COUNT[6]
    ENDIF
  ELSE
    # 1回でも離されたらカウントリセット
    G_BTN_PRESS_COUNT[6] = 0
  ENDIF

END

# 前方のイベントをチェックする
DEF D_EV_CHECK A_NO A_DIRECT

  VAR POS = D_GET_MAP_POSITION(SPVAR(A_NO,0), SPVAR(A_NO,1), 3)
  VAR AFTER_POS = POS + D_GET_DIRECT_POINT(A_DIRECT)

  VAR EV_ID = G_MAP[AFTER_POS]

 D_GET_EV_DATA EV_ID
END

# イベントリストからイベントキューへ挿入
DEF D_GET_EV_DATA A_ID
  VAR EV_COUNT = 0
  VAR EV_DATA = 0

  IF A_ID == 6 THEN
    IF SPVAR(A_ID, 4) == 0 THEN
      RESTORE @EVENT006
    ELSEIF SPVAR(A_ID, 4) == 1 THEN
      RESTORE @EVENT006_1
    ENDIF
    READ EV_COUNT
    FOR G_I=1 TO (EV_COUNT * 3)-1
      READ EV_DATA
      PUSH G_EV_QUEUE, EV_DATA
    NEXT
  ENDIF

  IF LEN(G_EV_QUEUE) > 0 THEN
    G_EV_ID_NOW = A_ID
    G_SCENE_FLAG = 1
  ENDIF
END

# イベントキュー取り出し
DEF D_SHIFT_EV_DATA
  VAR TYPE = SHIFT(G_EV_QUEUE)
  VAR EVENT = SHIFT(G_EV_QUEUE)
  VAR RESERVE = SHIFT(G_EV_QUEUE)

  IF TYPE== 0 THEN
    IF EVENT == 1 THEN
      SPVAR G_EV_ID_NOW, 4, RESERVE
    ENDIF
  ELSEIF TYPE == 1 THEN
    GCLS
    D_WINDOW_DRAW G_WINDOW_SIZES[0], G_WINDOW_SIZES[1], G_WINDOW_SIZES[2], G_WINDOW_SIZES[3]
    D_MSG_DRAW G_WINDOW_SIZES[0], G_WINDOW_SIZES[1], EVENT RESERVE
    G_EV_Q_FLAG = 1
  ELSEIF TYPE == 2 THEN
    IF EVENT == 1 THEN
     PRINT "ポーションのかず:" + STR$(G_PLAYER_STATUS[7])
      INC G_PLAYER_STATUS[7], RESERVE
     PRINT "ポーションのかず:" + STR$(G_PLAYER_STATUS[7])
    ENDIF
  ELSEIF TYPE == 3 THEN
    IF EVENT == 1 THEN
      SPSET G_EV_ID_NOW, RESERVE
    ENDIF
  ELSEIF TYPE == 10 THEN
    BEEP EVENT, RESERVE
  ENDIF

  IF LEN(G_EV_QUEUE) <= 0 THEN
    GCLS
    G_SCENE_FLAG = 0
  ENDIF
END

# ウィンドウクリエイター
DEF D_WINDOW_DRAW A_X, A_Y, A_W, A_H

  # カラー配列を定義
  VAR WC_BACK = RGB(0, 21, 151) # 背景色
  VAR WC_FRAME = RGB(128,126,129) # フレーム色
  VAR WC_HIGHLITE = RGB(255,255,255) # ハイライト色

  # 右下を計算
  VAR EX = A_W + A_X
  VAR EY = A_H + A_Y

  # 背景描写(塗りつぶし有り矩形)
  GFILL A_X, A_Y, EX, EY, WC_BACK

  # 矩形フレーム描写(塗りつぶし無し矩形)
  GBOX A_X-1, A_Y-1, EX+1, EY+1, WC_FRAME
  GBOX A_X-2, A_Y-2, EX+2, EY+2, WC_FRAME
  GBOX A_X-3, A_Y-3, EX+3, EY+3, WC_FRAME

  # ハイライトの角の部分(点)
  GPSET A_X-2, A_Y-2, WC_HIGHLITE
  GPSET A_X-2, EY+2, WC_HIGHLITE
  GPSET EX+2, A_Y-2, WC_HIGHLITE
  GPSET EX+2, EY+2, WC_HIGHLITE

  # ハイライトとフレームの直線部分
  GLINE A_X-3, A_Y-1, A_X-3, EY+1, WC_HIGHLITE
  GLINE A_X-4, A_Y-1, A_X-4, EY+1, WC_FRAME

  GLINE EX+3, A_Y-1, EX+3, EY+1, WC_HIGHLITE
  GLINE EX+4, A_Y-1, EX+4, EY+1, WC_FRAME

  GLINE A_X-1, A_Y-3, EX+1, A_Y-3, WC_HIGHLITE
  GLINE A_X-1, A_Y-4, EX+1, A_Y-4, WC_FRAME

  GLINE A_X-1, EY+3, EX+1, EY+3, WC_HIGHLITE
  GLINE A_X-1, EY+4, EX+1, EY+4, WC_FRAME

END

# メッセージ送り用
DEF D_EV_STOP_A_BTN
  IF G_BTN_PRESS_COUNT[4] == 1 THEN
    G_EV_Q_FLAG = 0
  ENDIF
END

# メッセージイベント
DEF D_MSG_DRAW A_X, A_Y, A_EVENT A_RESERVE
  IF A_EVENT == 1 THEN
    GPUTCHR A_X+5, A_Y+5, "プレイヤー" + G_MSG_DATAS[0]
  ELSEIF A_EVENT == 2 THEN
    GPUTCHR A_X+5, A_Y+5, G_MSG_DATAS[1] + G_ITEMS_NAMES[A_RESERVE] + G_MSG_DATAS[2]
  ELSEIF A_EVENT == 3 THEN
    GPUTCHR A_X+5, A_Y+5, G_MSG_DATAS[3]
  ENDIF
END

# データベースローダー
DEF D_DB_LOAD A_ID, A_LEN, A_ARY

  IF A_ID == 0 THEN
    VAR ARY_WORD = 0
  ELSE
    VAR ARY_WORD = ""
  ENDIF

  IF A_ID == 0 THEN
    RESTORE @PLAYER_INIT_STATUS
  ELSEIF A_ID == 1 THEN
    RESTORE @SYSTEMWORD
  ELSEIF A_ID == 2 THEN
    RESTORE @ITEMNAME
  ELSEIF A_ID == 3 THEN
    RESTORE @ENEMYNAME
  ELSEIF A_ID == 4 THEN
    RESTPRE @MESSAGEDATA
  ENDIF

  FOR G_I=0 to A_LEN-1
    READ ARY_WORD
    PUSH A_ARY, ARY_WORDS
    # 読み込み確認用
    PRINT A_ARY[G_I]
  NEXT
END

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW
  D_WINDWOW_DRAW G_WINDOW_SIZES[4], G_WINDOW_SIZES[5], G_WINDOW_SIZES[6], G_WINDOW_SIZES[7]
END

# ステータスウィンドウ描写
DEF D_STATUS_WINDOW_DRAW
  # ローカル配列定義
  VAR STATUS = ""
  DIM WS[0]
  # グローバル配列の参照を格納
  WS = G_WINDOW_SIZES

  # ウィンドウの描写
  D_WINDWOW_DRAW WS[4], WS[5], WS[6], WS[7]

  # ステータス文字の描写
  FOR G_I = 0 TO G_SYSTEM_WORDS_LEN-1
    # ステータス文字の描写準備
    IF G_I == 0 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[0])
    ELSEIF G_I == 1 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[1])+"/"+STR$(G_PLAYER_STATUS[2])
    ELSEIF G_I == 2 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[3])
    ELSEIF G_I == 3 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[4])
    ELSEIF G_I == 4 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[5])
    ELSEIF G_I == 5 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[6])+"/999"
    ELSEIF G_I == 6 THEN
      STATUS = G_SYSTEM_WORDS[G_I]+":"+ STR$(G_PLAYER_STATUS[7])
    ENDIF

    # 実際のステータスの描写
    GPUCHR  WS[4]+5, WS[5]+5 + (G_I*10), STATUS
  NEXT
END

# ウィンドウトグル
DEF D_STATUS_WINDOW_TOGGLE
  # フラグを反転させる
  G_STATUS_FLAG = NOT G_STATUS_FLAG

  # フラグによって開くか閉じるかを決める
  IF STATUS_FLAG THEN
    D_STATUS_WINDOW_DRAW
  ELSE
    GCLS
  ENDIF
END

# ステータスモード
DEF D_STATUS_MODE
  # 初回にステータスウィンドウフラグをONにしておく
  IF NOT G_STATUS_FLAG THEN
    D_STATUS_WINDOW_TOGGLE
  ENDIF

  # Xボタン監視
  IF G_BTN_PRESS_COUNT[6] == 1 THEN
    D_STATUS_WINDOW_TOGGLE
  ENDIF

END