ウチツクシ

ゲームをしたり作ったり

XenMai ソースコード

HSPプログラムコンテスト2012に応募したゲームのソースコードです。

; XenMai
; ver.1.2
; http://dev.onionsoft.net/seed/info.ax?id=193

; HSP 3.31rc1

#runtime "hsptv"
#regcmd 18
#cmd hsptv_send $00

#include "obaq.as"

; outer wall (screen 640x480)
#const CENTER_X  -80
#const CENTER_Y  -60
#const AREA_WIDTH   640
#const AREA_HEIGHT  AREA_WIDTH * 3 / 4
#const AREA_LEFT    CENTER_X - AREA_WIDTH / 2
#const AREA_TOP     CENTER_Y - AREA_HEIGHT / 2
#const AREA_RIGHT   CENTER_X + AREA_WIDTH / 2
#const AREA_BOTTOM  CENTER_Y + AREA_HEIGHT / 2

#const MAT_WALL  MAT_WIRE2 | MAT_DELAY

; pi
#const PI_D2  M_PI / 2
#const PI_D4  M_PI / 4

; scene
#enum SC_TITLE = 0
#enum SC_MAIN

; pad
#uselib "winmm"
#func joyGetPosEx "joyGetPosEx" int, var

#const JS_BORDER_LOW   32768 - 4096
#const JS_BORDER_HIGH  32768 + 4096



; *start

  ; エリア背景作成
  buffer 1
  picload dir_tv + "sozai6.jpg"
  buffer 2
  picload dir_tv + "sozai12.jpg"
  gmode 6, 640, 480, 230
  gcopy 1

  ; scoreBoard にランキング情報を格納
  ; n*3行目:score, n*3+1行目:user, n*3+2行目:comment (n:0-29)
  notesel scoreBoard
  hsptv_send scoreBoard, -1

  dim jsdata, 15 : jsdata = 52, 255  ; JOYINFOEX
  jsButtonDef = 1, 2, 4

  ; 自機モデル
  ; 頂点登録は左周り(Y軸は下が正)
  t = "*4-/'/*4* '%-%* *,,**((**,"
  repeat 26
    myModel(cnt) = double(peek(t, cnt) - 42) / 10
  loop

  gsel

*init

  ; OBAQ 初期化
  qreset

  ; 外壁
  qborder AREA_LEFT, AREA_TOP, AREA_RIGHT, AREA_BOTTOM
  qgroup , $8, $3, $3   ; 的の衝突は除外しないと接触時の速度をちゃんと取れない
  qmat , MAT_WALL, $ff0000, $880000

  ; エリア背景
  ; サイズは外壁と合わせる x=AREA_WIDTH/2/cos(M_PI/4), y=x*3/4 少数切り捨て
  qaddpoly id, 4, , , , 452, 339, 0, $4, $b
  qmat id, MAT_SPR, 2

  ; 自機
  qaddmodel myObjId, myModel, , , , , 30, 30
  qgroup myObjId, $1, , $2
  qweight myObjId, 20
  qmat myObjId, MAT_WIRE2, 0, $bbbb66

  ; ゼログラ
  qgravity , 0

  zoom = 0.1

  dim frm
  dim rCnt
  dim slowCnt

  ; 壁ぶつけ得点用
  dim scoreE, 10
  ; scoreE は表示タイマーとフラグを兼用
  ; ↓は初期化しなくても大丈夫、なはず…(代入→参照の順)
  ;dim scoreX, 10
  ;dim scoreY, 10
  ;dim scoreA, 10

  ; 的出現固定(内容的に固定させないほうが良かったかも
  randomize 1

*mainLoop

  redraw 0
  color
  boxf

  stick key, 15 * scene  ; PadID変更用にトリガー
  joyGetPosEx jsID, jsdata
  jsStat = (stat == 0)
  jsButton = jsdata(8)
  if (scene) {  ; パッドでPadID変更させない
    x = jsdata(2)
    y = jsdata(3)
    key |= (x < JS_BORDER_LOW) + (y < JS_BORDER_LOW) * 2 + (x > JS_BORDER_HIGH) * 4 + (y > JS_BORDER_HIGH) * 8
  }

  notEsc = (key & 128) == 0  ; [Esc]

  frm++

  if (scene) {
    ; SC_MAIN

    ; 残タイム・esc処理
    leftTime = 100 - frm / 60
    if (leftTime * notEsc) : else {
      dim scene ; SC_TITLE
      submitable++
      qdel myObjId
    }

    rank = sqrt(0.13 * frm)
    rankVal = 44 - rank
    addRot = 0.025 + rank * 0.001

    qgetpos myObjId, myX, myY, myAng
    qgetspeed myObjId, mySpdX, mySpdY, mySpdAng

    ; 自機移動・回転
    dim r
    if (rCnt) {
      rCnt--
    } else {
      getkey k, 'Z' : k |= jsButton & jsButtonDef    : if (k) : r--
      getkey k, 'X' : k |= jsButton & jsButtonDef(1) : if (k) : r++
      if (r) {
        rCnt = rankVal
      }
    }
    lspd = limitf(0.15 - absf(mySpdAng), 0.05, 0.20)
    qspeed myObjId, lspd * (((key & 4) != 0) * (mySpdX < 0.8) - (key & 1) * (mySpdX > -0.8)), lspd * (((key & 8) != 0) * (mySpdY < 0.8) - ((key & 2) != 0) * (mySpdY > -0.8)), addRot * r * (0.5 * ((mySpdAng * r) < 0) + 1)

    ; ズームイン
    if (zoom < 1.4) {
      zoom += (1.5 - zoom) * 0.04
    }

    ; 的生成
    if (frm \ rankVal) : else {
      r = PI_D2 * rnd(4) + PI_D4
      x = cos(r) * 400
      y = sin(r) * 300
      t = 1.3 + rank / 30
      dir = atan(myY - y, myX - x)
      qaddpoly id, 3, x, y, dir - M_PI, 12, 12
      qspeed id, cos(dir) * t, sin(dir) * t
      qgroup id, $2
      qweight id, 10
      qmat id, MAT_WIRE2, 0, $ff0000
    }

    ; 自機と的との衝突判定
    getColi_id = myObjId
    getColi_proc = *coliproc_player
    gosub *getColi
  }

  ; 外壁と自機・的との衝突判定
  dim getColi_id
  getColi_proc = *coliproc_wall
  gosub *getColi

  ; ビュー
  t = zoom / 1.3
  qview zoom, zoom, 320 - myX * t, 240 - myY * t

  ; OBAQ内部の演算と描画
  qexec
  qdraw 1

  if (scene) {
    ; SC_MAIN
    ; 溜めエフェクト
    qcnvaxis x, y, myX, myY
    color 140, 140, 60
    sz = rCnt * (addRot * 4000 + 20) / rankVal
    circle x - sz, y - sz, x + sz, y + sz, 0
  } else {
    ; SC_TITLE
    palcolor 19
    ; ランキング
    repeat 30
      i = 3 * cnt
      noteget rScore,   i
      noteget rUser,    i + 1
      noteget rComment, i + 2
      pos 320 * (cnt >= 15) - 40, (cnt \ 15) * 20 + 90
      mes strf("%16d%4s  %s", rScore, rComment, rUser)
    loop
    ; タイトル
    mes "\n\n\n- XenMai -" ; 位置は上のランキングで調整
    ; ランクイン
    if ((score > rScore) * submitable) { 
      getkey k, 'R'
      if (k) {
        hsptv_send scoreBoard, score, "" + leftTime
        dim submitable
      }
      if ((frm \ 80) > 40) {
        pos 260, 55
        mes "R key to submit"
      }
    }
    ; アサインモードトグル
    if (key & 32) { ; [Enter]
      assMode ^= 1
      dim keyID
    }
    ; ボタンアサイン
    if (assMode) {
      jsID += ((key & 4) >> 2) - (key & 1)
      jsID &= 15
      pos 295, 400
      mes "Pad " + jsID
      if (jsStat) {
        if (jsButton != jsPreButton) {  ; 押しっぱなし回避
          jsPreButton = jsButton
          if (jsButton) {  ; 前回押してなかった
            jsButtonDef(keyID) = jsButton
            keyID++
            keyID \= 3
          }
        }
        pos 289
        mes "assign " + (keyID + 1)
      }
    } else {
      ; スタート
      if ((key & 16) | (jsButton & jsButtonDef(2))) {  ; [Space]
        scene++ ; SC_MAIN
        dim score
        getkey disableSlow, 'S' : enableSlow = disableSlow ^ 1
        goto *init
      }
    }
  }

  palcolor 19

  pos 200, 20
  mes strf("%8d%15d%c", score, leftTime, disableSlow * $22)

  ; 壁ぶつけ得点表示
  repeat 10
    if (scoreE(cnt)) {
      pos scoreX(cnt), scoreY(cnt)
      mes scoreA(cnt)
      scoreE(cnt)--
    }
  loop

  redraw

  if (slowCnt * scene * enableSlow) {
    await slowWait
    slowCnt--
  } else {
    await 15
  }

  goto *mainLoop


*getDist2
  return x * x + y * y

*getColi
  qcollision getColi_id
  dim preId
  repeat
    qgetcol id, x, y
    if (id < 1) : break  ; id 0 は外壁なので除外
    if (id <= preId) : continue ; 前回以下 id のログは読まない(id が小さい順に並ばない場合も…
    preId = id
    gosub getColi_proc
  loop
  return

*coliproc_player
  qgetpos id, x, y, r
  qinner in, x, y, myObjId
  if (in > 0) {  ; 重心が内部侵入
    score += 1000
    qdel id
    return
  }
  qgetspeed myObjId, x, y, r
  gosub *getDist2
  if (refdval > 2) {  ; スロー発生条件
    slowWait = 30 + (refdval - 2) * 30
    slowCnt = 8
  } else {
    dim slowCnt
  }
  score++
  qgetuser id, f, dummy, dummy
  if (f) : else {
    quser id, 1 ; 自機との初接触フラグ
    qmat id, MAT_WIRE, $ffff00
    score += 100
  }
  return

*coliproc_wall
  if (id == myObjId) {
    ; 死んでしまった
    dim scene ; SC_TITLE
    submitable++
  } else {
    if (scene) {
      qgetuser id, f, dummy, dummy
      if (f) {  ; 自機と接触している的のみ
        qgetpos id, x, y, r
        qcnvaxis x, y, x, y
        dir = atan(240 - y, 320 - x)
        scoreX(scrIdx) = 320 - cos(dir) * 150
        scoreY(scrIdx) = 240 - sin(dir) * 150
        qgetspeed id, x, y, r
        gosub *getDist2
        scoreA(scrIdx) = 10 * (refdval * 50) + 10
        score += scoreA(scrIdx)
        scoreE(scrIdx) = 100
        scrIdx++
        scrIdx \= 10
      }
    }
  }
  qdel id
  return

コードの利用はNYSLで。
あと、スクリプトのHTML変換に(X)HTMLコンバータ for HSP3を利用しています。はてなダイアリーにはシンタックスハイライト機能があるけど、HSPには対応していないのでかなり重宝します。cssで簡単に色も変えれるし。