【プチコン4講座】斧で切るための木を実装

プチコン4

プチコン4 きこりの与作

こんにちは。なおキーヌです。

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

インテリはインテリらしく計算式で動かすの続きの記事になります。

少し間が空いてしまいまして申し訳ございません。

ここまでで出現するキャラクターを実装は完了したので、
次はゲーム本編を作っていきましょう。

木が無いとただのワンパクくんホームランゲームになっているので、
ステージクリアのための切ることが出来る木の実装をしていきます。

それでは木こりゲーム作り第8回目、始めていきましょう。

  1. TPUTを使って木を描写する関数を作る
  2. 透明のスプライトを作って木の根元に衝突判定を付ける
  3. 斧で木を切ったときに衝突判定を取る

TPUTを使って木を描写する関数を作る

スプライトで木を作ろうとするとそもそもグラフィックが無いので、
テキストスクリーンのIDを変えて木を描写します。

スマイルツールを使って木の定義番号をメモって描写する用の関数を作って呼び出します。

とりあえず木はステージに2本固定にしておいて、それぞれ別IDで描写します。

なぜ別IDに描写するのかは、最後に説明します。

それでは以下のような関数を作って木を描写しましょう。

' 木描写
'──────────────────────────
DEF TREE_DRAW A_X,A_Y,A_Screen

  TPUT A_Screen,A_X+1,A_Y+0,&HEC93
  TPUT A_Screen,A_X+2,A_Y+0,&HEC94

  TPUT A_Screen,A_X+0,A_Y+1,&HECD3
  TPUT A_Screen,A_X+1,A_Y+1,&HECD4
  TPUT A_Screen,A_X+2,A_Y+1,&HECD5

  TPUT A_Screen,A_X+0,A_Y+2,&HED13
  TPUT A_Screen,A_X+1,A_Y+2,&HED14
  TPUT A_Screen,A_X+2,A_Y+2,&HED15

  TPUT A_Screen,A_X+0,A_Y+3,&HED53
  TPUT A_Screen,A_X+1,A_Y+3,&HED54
  TPUT A_Screen,A_X+2,A_Y+3,&HED55

  TPUT A_Screen,A_X+0,A_Y+4,&HED93
  TPUT A_Screen,A_X+1,A_Y+4,&HED94
  TPUT A_Screen,A_X+2,A_Y+4,&HED95

  TPUT A_Screen,A_X+0,A_Y+5,&HEDD3
  TPUT A_Screen,A_X+1,A_Y+5,&HEDD4
  TPUT A_Screen,A_X+2,A_Y+5,&HEDD5

end

木の描写関数はプチコン4でRPGを作ってた時に書いてたので流用しています。

倒れるモーション時に根元の草が不自然に見えるので根元だけ消しています。

早速この関数を使ってゲーム開始時に描写してみましょう。

TREE_DRAW 4,6,1
TREE_DRAW 17,6,1

プチコン4 出力結果1

これで木が描写できました。

透明のスプライトを作って木の根元に衝突判定を付ける

次に、木の根元にスプライトの衝突判定を利用したいので透明のスプライトを用意します。

設定画像は何でもいいので、HIDEモードにしておけば見た目は透明になります。

まずは位置合わせのために表示状態にしておきます。

木の描写関数の最後に記載しましょう。

' 木描写
'──────────────────────────
DEF TREE_DRAW A_X,A_Y,A_Screen

'~~木の描写省略~~

  SPSET 200, 0,1
  SPSET 201, 0,1

  SPCOL 200
  SPCOL 201

  SPOFS 200, 6*16-16, (6-5)*16
  SPOFS 201, 17*16-16, (6-5)*16

end

プチコン4 出力結果2

良い感じですね。

斧で木を切ったときに衝突判定を取る

本家では、木の左右に衝突判定を設けて両方切らなきゃ倒れませんでしたが、
処理の記述が増えるだけなので、時間節約のために1つにしました。

難易度は木の切る回数を調整すればOKです。

衝突判定も設けたので、あとは斧を振ったときに衝突しているかどうかを取りましょう。

ついでに木のHPと、連続ヒット対策に現在斧に当たっているかどうかの判定も準備しましょう。

LOOP

  ~~その他処理省略~~
  SLASH_WOOD
ENDLOOP



' 木描写
'──────────────────────────
DEF TREE_DRAW A_X,A_Y,A_Screen

'~~木の描写省略~~

  SPSET 200, 0,1
  SPSET 201, 0,1

  SPCOL 200
  SPCOL 201

  SPVAR 200, "HP", 10
  SPVAR 201, "HP", 10
  SPVAR 200, "HITTING", #FALSE
  SPVAR 201, "HITTING", #FALSE

  SPOFS 200, 6*16-16, (6-5)*16
  SPOFS 201, 17*16-16, (6-5)*16

end

' 斧と木の衝突判定処理
'──────────────────────────
DEF SLASH_WOOD
  VAR SP = SPHITSP(100,200,201)

  IF SP != -1 THEN
    IF SPVAR(SP,"HITTING") == #FALSE
      SPVAR SP, "HITTING" = #TRUE

      CASE SP
        WHEN 200
          PRINT "LEFT"
        WHEN 201
          PRINT "RIGHT"
        ENDCASE
      ENDIF
    ENDIF
  ENDIF
END

このままだと、1回ヒットしたら次にヒットしなくなってしまうので、
斧を振り終わったときに衝突判定中フラグをリセットするようにしましょう。

' 斧振り関数
'───────────────────────────────
DEF D_SWING_AXE
  IF SPVAR(0,"SWING") == #TRUE THEN
    ' 斧を出現させる
    SPSHOW 100

    ' 向きによって振る方向を変更
    IF SPVAR(0, "DIRECT") == #TRUE THEN

      ' 斧を指定位置まで回転させる
      IF SPVAR(100, "ROTATE") > -90 THEN
        SPVAR 100, "ROTATE", SPVAR(100, "ROTATE")-5
        SPROT 100, SPVAR(100, "ROTATE")
      ELSE
        SPHIDE 100
        SPVAR 100, "ROTATE", 0
        SPROT 100, SPVAR(100, "ROTATE")
        SPVAR 0,"SWING", #FALSE
      ENDIF
    ELSE

      ' 斧を指定位置まで回転させる
      IF SPVAR(100, "ROTATE") < 90 THEN
        SPVAR 100, "ROTATE", SPVAR(100, "ROTATE")+5
        SPROT 100, SPVAR(100, "ROTATE")
      ELSE
        SPHIDE 100
        SPVAR 100, "ROTATE", 0
        SPROT 100, SPVAR(100, "ROTATE")
        SPVAR 0,"SWING", #FALSE

        ' 斧を振り終わったら木の衝突判定をリセット
        SPVAR 200, "HITTING", #FALSE
        SPVAR 201, "HITTING", #FALSE
      ENDIF


    ENDIF

  ENDIF
END

斧の判定がちょっとおかしいですね……

一応あたってはいるので、とりあえず作ってしまってから判定調整を行いましょう。

次回は木のHPを0にしたら木が倒れる仕組みを作ります。

最初に木の描写のテキストスクリーンIDを変更したのはそれぞれを回転で傾けて倒したかったからです。

同じIDだと一緒に倒れてしまうので、別々のIDにしました。

最後に今回までのソースコードを記載しておきます。

それでは。

ACLS

' 地面と判定するための座標定数
CONST #GND = 160
' 神崎最大Y座標
CONST #KANZAKI_MAXUP = 176
' 出現までのフレーム数
CONST #WANPAKU_POP = 480


' ループ用変数
VAR G_I = 0
' 衝突判定用変数
VAR HITSP = -1
' ジャンプ配列アクセス用変数
VAR JPPC = 0
' ジャンプ配列
DIM JUMP_PROCESS[] = [-14,-10,-8,-6,-4,-2,2,4,6,8,10,14] 


' スプライト定義
SPSET 0, 1432
SPCOL 0
SPVAR 0, "X", 35
SPVAR 0, "Y", #GND
SPVAR 0, "DIRECT" , #FALSE
SPVAR 0, "SWING" , #FALSE
SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")
SPVAR 0, "JUMP" , #FALSE

' 斧スプライト定義(非表示にしておく)
SPSET 100, 161, 0
SPCOL 100
SPVAR 100, "ROTATE", 0
' アトリビュートの基準点を変更
SPHOME 100, 8, 16






' ワンパクくんの初期を非表示に設定
SPSET 1, 1436, 0
SPCOL 1

' 開始時にカウント開始するために1を入れておく
SPVAR 1, "HIDE", 1
' ワンパク君の座標変数
SPVAR 1, "X", -16
SPVAR 1, "Y", 160
' ワンパク君の向き変数
SPVAR 1, "DIRECT", #FALSE
' ワンパク君のスピード(後で使う)
SPVAR 1, "SPEED", 0
' ワンパク君が斧に当たったかどうか
SPVAR 1, "DAMAGE", #FALSE

' ワンパク君の座標設定
SPOFS 1, SPVAR(1,"X"),SPVAR(1,"Y")

' 神崎スプライト設定
SPSET 2, 1438, 0
SPCOL 2
SPVAR 2, "HIDE", 1
SPVAR 2, "X", 0
SPVAR 2, "Y", 240
SPVAR 2, "SPEED", 0
SPVAR 2, "DIRECT" , #FALSE
SPVAR 2, "DAMAGE", #FALSE
SPOFS 2, SPVAR(2,"X"),SPVAR(2,"Y")


' インテリスプライト設定
SPSET 3, 1442, 0
SPCOL 3
SPVAR 3, "HIDE", 1
SPVAR 3, "X", 0
SPVAR 3, "Y", 50
SPVAR 3, "SPEED", 0
SPVAR 3, "DIRECT" , #FALSE
SPOFS 3, SPVAR(3,"X"),SPVAR(3,"Y")


' 青空ぽい背景描写
FOR G_I=0 TO 240
  GLINE 0,I,400,I,RGB(I/2, 125+ROUND(I/2), 255)
NEXT

' 地面を描写
FOR G_I = 0 TO 24
  TPUT 0, G_I, 14, &HECC2
  TPUT 0, G_I, 14, &HED02
  TPUT 0, G_I, 14, &HED02
NEXT

' 木の描写
TREE_DRAW 4,6,1
TREE_DRAW 17,6,1


LOOP
  D_CONTROLLER

  ' 斧とハカセ以外のスプライトの判定を取る
  HITSP = SPHITSP(100,1,3)

  ' 敵の衝突制御
  ENEMY_COL_PROCESS

  ' 非表示敵の出現までのカウント
  HIDE_COUNT

  ' ワンパクくん移動処理
  WANPAKU_DASH

  ' 神崎移動処理
  KANZAKI_UPDOWN

  ' インテリ移動処理
  INTELI_FLOAT

  ' 斧と木の衝突判定
  SLASH_WOOD
ENDLOOP



' コントローラー関数
'───────────────────────────────
DEF D_CONTROLLER
  ' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
  VAR B = BUTTON(0)

  ' 斧を振っていなかったら
  IF SPVAR(0,"SWING") == #FALSE THEN
    ' 左ボタン処理
    IF (B AND 1 << #B_LLEFT) != 0 THEN
      SPVAR 0, "X", SPVAR(0,"X")-1
      SPVAR 0, "DIRECT", #TRUE
    ENDIF

    ' 右ボタン処理
    IF (B AND 1 << #B_LRIGHT) != 0 THEN
      SPVAR 0, "X", SPVAR(0,"X")+1
      SPVAR 0, "DIRECT", #FALSE
    ENDIF

    ' 向き変更
    IF SPVAR(0, "DIRECT") == #TRUE THEN
      SPCHR 0,,,32,32,#A_REVH
      SPCHR 100,,,,,#A_REVH
      ' 向きに合わせて斧の位置を変える
      SPOFS 100, SPVAR(0,"X"), SPVAR(0,"Y")+24
    ELSE
      SPCHR 0,,,32,32,0
      SPCHR 100,,,,,0
      ' 向きに合わせて斧の位置を変える
      SPOFS 100, SPVAR(0,"X")-32, SPVAR(0,"Y")+24
    ENDIF

    ' 移動処理
    IF SPVAR(0,"X") < -16 THEN
      SPVAR 0,"X", -16
    ELSEIF SPVAR(0,"X") > 400-16 THEN
      SPVAR 0,"X", 400-16
    ENDIF
    SPOFS 0, SPVAR(0,"X"), SPVAR(0,"Y")

    ' Yボタン処理
    IF (B AND 1 << #B_LRIGHT) != 0 THEN
      ' 振った音を鳴らす
      BEEP 100

      ' ハカセのグラを変更
      IF SPVAR(0, "DIRECT") == #TRUE && SPVAR(0,"JUMP") != #TRUE THEN
        SPCHR 0,1433,,32,32,#A_REVH
      ELSE
        SPCHR 0,1433,,32,32,0
      ENDIF

      ' 斧振りフラグON
      SPVAR 0,"SWING", #TRUE
    ENDIF


    ' ジャンプ中か調べる
    IF SPVAR(0,"JUMP") != #TRUE THEN
      ' ジャンプ中じゃなければAボタン処理が効く
      IF (B AND 1 << #B_RRIGHT) != 0 THEN
        ' ジャンプフラグをONにする
        SPVAR 0,"JUMP", #TRUE
        ' 上昇中フラグに切り替える
        SPVAR 0,"UPDOWN", #TRUE
      ENDIF
    ELSE
      ' 現在座標にジャンプ配列の該当する値を取り出して足す
      SPVAR 0, "Y", SPVAR(0,"Y") + JUMP_PROCESS[JPPC]
      ' 次のジャンプ配列にアクセスするためにインクリメント
      INC JPPC
      ' もしアクセス変数と配列の長さが同じになったら
      IF JPPC == LEN(JUMP_PROCESS)-1 THEN 
        ' プレイヤーのY座標補正
        SPVAR 0, "Y", #GND
        ' ジャンプフラグを解除
        SPVAR 0,"JUMP", #FALSE
        ' 次のジャンプのためにアクセス変数を初期化しておく
        JPPC = 0
      ENDIF
    ENDIF


  ENDIF
END


' 斧振り関数
'───────────────────────────────
DEF D_SWING_AXE
  IF SPVAR(0,"SWING") == #TRUE THEN
    ' 斧を出現させる
    SPSHOW 100

    ' 向きによって振る方向を変更
    IF SPVAR(0, "DIRECT") == #TRUE THEN

      ' 斧を指定位置まで回転させる
      IF SPVAR(100, "ROTATE") > -90 THEN
        SPVAR 100, "ROTATE", SPVAR(100, "ROTATE")-5
        SPROT 100, SPVAR(100, "ROTATE")
      ELSE
        SPHIDE 100
        SPVAR 100, "ROTATE", 0
        SPROT 100, SPVAR(100, "ROTATE")
        SPVAR 0,"SWING", #FALSE
      ENDIF
    ELSE

      ' 斧を指定位置まで回転させる
      IF SPVAR(100, "ROTATE") < 90 THEN
        SPVAR 100, "ROTATE", SPVAR(100, "ROTATE")+5
        SPROT 100, SPVAR(100, "ROTATE")
      ELSE
        SPHIDE 100
        SPVAR 100, "ROTATE", 0
        SPROT 100, SPVAR(100, "ROTATE")
        SPVAR 0,"SWING", #FALSE

        ' 斧を振り終わったら木の衝突判定をリセット
        SPVAR 200, "HITTING", #FALSE
        SPVAR 201, "HITTING", #FALSE
      ENDIF


    ENDIF

  ENDIF
END


' 消滅時のカウント
'───────────────────────────────
DEF HIDE_COUNT
  VAR SP_WANPAKU = 1
  VAR SP_KANZAKI = 2
  VAR SP_INTELI = 3

  IF SPVAR(SP_WANPAKU,"HIDE") >= 1 THEN
    SPVAR SP_WANPAKU, "HIDE", SPVAR(SP_WANPAKU, "HIDE")+1
    IF SPVAR(SP_WANPAKU, "HIDE") > #WANPAKU_POP THEN
      IF SPVAR(0,"X") < (400/2-1) THEN
        SPVAR SP_WANPAKU, "X", 400-16
        SPVAR SP_WANPAKU, "DIRECT", #TRUE
        SPCHR SP_WANPAKU, 128,352,32,32,#A_REVH

      ELSE
        SPVAR SP_WANPAKU, "X", -16
        SPVAR SP_WANPAKU, "DIRECT", #FALSE
        SPCHR SP_WANPAKU, 128,352,32,32,0
      ENDIF
      SPSHOW SP_WANPAKU
      SPVAR SP_WANPAKU, "DAMAGE", #FALSE
      SPVAR SP_WANPAKU, "Y", 160
      SPOFS SP_WANPAKU, SPVAR(SP_WANPAKU,"X"),SPVAR(SP_WANPAKU,"Y")
      SPVAR SP_WANPAKU, "SPEED", SPVAR(SP_WANPAKU,"SPEED") + 5
      SPVAR SP_WANPAKU, "HIDE", 0
    ENDIF
  ENDIF

  IF SPVAR(SP_KANZAKI,"HIDE") >= 1 THEN
    SPVAR SP_KANZAKI, "HIDE", SPVAR(SP_KANZAKI, "HIDE")+1
    IF SPVAR(1, "HIDE") > #WANPAKU_POP THEN

      SPVAR SP_KANZAKI, "X", SPVAR(0,"X")+16
      SPVAR SP_KANZAKI, "DIRECT", #FALSE
      SPVAR SP_KANZAKI, "DAMAGE", #FALSE

      SPVAR SP_KANZAKI, "Y", 240+32
      SPOFS SP_KANZAKI, SPVAR(SP_KANZAKI,"X"),SPVAR(SP_KANZAKI,"Y")
      SPVAR SP_KANZAKI, "SPEED", SPVAR(SP_KANZAKI,"SPEED") + 5
      SPVAR SP_KANZAKI, "HIDE", 0
      SPSHOW SP_KANZAKI
    ENDIF
  ENDIF

  IF SPVAR(SP_INTELI,"HIDE") >= 1 THEN
    SPVAR SP_INTELI, "HIDE", SPVAR(SP_INTELI, "HIDE")+1
    IF SPVAR(SP_INTELI, "HIDE") > #WANPAKU_POP THEN
      IF SPVAR(0,"X") < (400/2-1) THEN
        SPVAR SP_INTELI, "X", 400-16
        SPVAR SP_INTELI, "DIRECT", #TRUE
        SPCHR SP_INTELI, 288,352,32,32,#A_REVH
      ELSE
        SPVAR SP_INTELI, "X", -16
        SPVAR SP_INTELI, "DIRECT", #FALSE
        SPCHR SP_INTELI, 288,352,32,32,0
      ENDIF
      SPSHOW SP_INTELI
      SPVAR SP_INTELI, "Y", 50
      SPOFS SP_INTELI, SPVAR(SP_INTELI,"X"),SPVAR(SP_INTELI,"Y")
      SPVAR SP_INTELI, "SPEED", 0
      SPVAR SP_INTELI, "HIDE", 0
    ENDIF
  ENDIF

END

' ワンパク君移動関数
'───────────────────────────────
DEF WANPAKU_DASH
  SPID = 1
  IF SPSHOW(SPID) == 1 THEN
    IF SPVAR (SPID, "DIRECT") == #FALSE THEN
      IF SPVAR (SPID, "DAMAGE") == #FALSE THEN
        SPVAR SPID, "X", SPVAR(SPID, "X") + SPVAR(SPID, "SPEED")
      ELSE
        SPVAR SPID, "X", SPVAR(SPID, "X") -10
        SPVAR SPID, "Y", SPVAR(SPID, "Y") -10
      ENDIF
    ELSE
      IF SPVAR (SPID, "DAMAGE") == #FALSE THEN
        SPVAR SPID, "X", SPVAR(SPID, "X") - SPVAR(SPID, "SPEED")
      ELSE
        SPVAR SPID, "X", SPVAR(SPID, "X") +10
        SPVAR SPID, "Y", SPVAR(SPID, "Y") -10
      ENDIF
    ENDIF
  ENDIF
  SPOFS SPID, SPVAR(SPID, "X"), SPVAR(SPID, "Y")

  IF SPVAR(SPID, "X") > 400+32 || SPVAR(SPID, "X") < -32 THEN
    SPVAR SPID, "HIDE", 1
    SPHIDE SPID
  ENDIF

END


' 神崎移動関数
'───────────────────────────────
DEF KANZAKI_UPDOWN
  VAR SPID = 2
  IF SPSHOW(SPID) == 1 THEN
    IF SPVAR (SPID, "DAMAGE") == #FALSE THEN
      IF SPVAR (SPID, "DIRECT") == #FALSE THEN
        SPVAR SPID, "Y", SPVAR(SPID, "Y") - SPVAR(SPID, "SPEED")
      ELSE
        SPVAR SPID, "Y", SPVAR(SPID, "Y") + 10
      ENDIF
    ELSE
      SPVAR SPID, "Y", SPVAR(SPID, "Y") +10
    ENDIF
  ENDIF
  SPOFS SPID, SPVAR(SPID, "X"), SPVAR(SPID, "Y")

  IF SPVAR(SPID, "Y") < #KANZAKI_MAXUP THEN
    SPVAR SPID, "DIRECT", #TRUE
  ENIDF 

  IF SPVAR(SPID, "Y") > 240+32 THEN
    SPVAR SPID, "HIDE", 1
    SPHIDE SPID
  ENDIF
END

' インテリ移動関数
'───────────────────────────────
DEF INTELI_FLOAT
  SPID = 3
  IF SPSHOW(SPID) == 1 THEN
    IF SPVAR (SPID, "DIRECT") == #FALSE THEN
      SPVAR SPID, "X", SPVAR(SPID, "X") + 1
    ELSE
      SPVAR SPID, "X", SPVAR(SPID, "X") - 1
    ENDIF

    SPVAR SPID, "SPEED", SPVAR(SPID,"SPEED")+0.05
    SPOFS SPID, SPVAR(SPID,"X"),SPVAR(SPID,"Y")+SIN(SPVAR(SPID,"SPEED")) * 30

    IF SPVAR(SPID, "X") > 400+32 || SPVAR(SPID, "X") < -32 THEN
      SPVAR SPID, "HIDE", 1
      SPHIDE SPID
    ENDIF
  ENDIF
END


' エネミー判定処理
'───────────────────────────────
DEF ENEMY_COL_PROCESS
  FOR G_I = 0 TO 1
    IF HITSP == G_I && SPSHOW(G_I) == 1 THEN
      SPVAR G_I, "DAMAGE", #TRUE
      BEEP 120
    ENDIF
  NEXT
END

' 木描写
'──────────────────────────
DEF TREE_DRAW A_X,A_Y,A_Screen

  TPUT A_Screen,A_X+1,A_Y+0,&HEC93
  TPUT A_Screen,A_X+2,A_Y+0,&HEC94

  TPUT A_Screen,A_X+0,A_Y+1,&HECD3
  TPUT A_Screen,A_X+1,A_Y+1,&HECD4
  TPUT A_Screen,A_X+2,A_Y+1,&HECD5

  TPUT A_Screen,A_X+0,A_Y+2,&HED13
  TPUT A_Screen,A_X+1,A_Y+2,&HED14
  TPUT A_Screen,A_X+2,A_Y+2,&HED15

  TPUT A_Screen,A_X+0,A_Y+3,&HED53
  TPUT A_Screen,A_X+1,A_Y+3,&HED54
  TPUT A_Screen,A_X+2,A_Y+3,&HED55

  TPUT A_Screen,A_X+0,A_Y+4,&HED93
  TPUT A_Screen,A_X+1,A_Y+4,&HED94
  TPUT A_Screen,A_X+2,A_Y+4,&HED95

  TPUT A_Screen,A_X+0,A_Y+5,&HEDD3
  TPUT A_Screen,A_X+1,A_Y+5,&HEDD4
  TPUT A_Screen,A_X+2,A_Y+5,&HEDD5

  SPSET 200, 0,1
  SPSET 201, 0,1

  SPCOL 200
  SPCOL 201

  SPVAR 200, "HP", 10
  SPVAR 201, "HP", 10
  SPVAR 200, "HITTING", #FALSE
  SPVAR 201, "HITTING", #FALSE

  SPOFS 200, 6*16-16, (6-5)*16
  SPOFS 201, 17*16-16, (6-5)*16

END

' 斧と木の衝突判定処理
'──────────────────────────
DEF SLASH_WOOD
  VAR SP = SPHITSP(100,200,201)

  IF SP != -1 THEN
    IF SPVAR(SP,"HITTING") == #FALSE
      SPVAR SP, "HITTING" = #TRUE

      CASE SP
        WHEN 200
          PRINT "LEFT"
        WHEN 201
          PRINT "RIGHT"
        ENDCASE
      ENDIF
    ENDIF
  ENDIF
END