// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Doom.hsp // Last Modified:2006/09/14-20:50. // // 単純だけど奥が深い // 新感覚爽快連鎖系パズルゲーム(自称) // // ソースに分からないことがあった時、これを見れば幸せになれるかも // http://hp.vector.co.jp/authors/VA038334/forshort/index.html // いろんな意味で初心者お断りで分かりにくい資料で申しわけないですが、 // 質問とかは無しの方向で。「沈黙は金、雄弁は銀」といいますしね。(違 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 定数 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 完全版 ;#define USE_SND #ifdef USE_SND # define ctype MIDI_SND(%1,%2,%3) %1 | %2 << 8 | %3 << 16 # uselib "winmm.dll" # func midiOutOpen "midiOutOpen" var, int, int, int, int # func midiOutClose "midiOutClose" int # func midiOutShortMsg "midiOutShortMsg" int, int #endif #ifdef USE_SND # define SND(%1) if usesnd : midiOutShortMsg hmo, MIDI_SND( 0x90 + 9, ( %1 ), 80 ) # define SCORE_FILE_NAME "score" #else # define SND(%1) : #endif // ウィンドウID #enum MAIN_BUFFER_ID=0 #enum OBJ_BUFFER_ID=2 // ウィンドウ #const WINDOW_WIDTH 640 #const WINDOW_HEIGHT 480 // ブロック #const BLOCK_WIDTH 32 #const NUM_BLOCK_VERTICAL 14 #const NUM_BLOCK_HORIZON 8 #const NUM_BLOCK_VERTICAL_MIN1 NUM_BLOCK_VERTICAL - 1 #const NUM_BLOCK_HORIZON_MIN1 NUM_BLOCK_HORIZON - 1 #const NUM_CLOROS 4 // ビーム #const BEEM_VERTICAL_POSITION BLOCK_WIDTH * 2 // その他座標やら #const SIDE_MARGIN 192 #const INNER_BACK_LEFT_POSX SIDE_MARGIN - 4 #const INNER_BACK_RIGHT_POSX WINDOW_WIDTH - SIDE_MARGIN + 4 #const CHAR_POS_Y NUM_BLOCK_VERTICAL_MIN1 * BLOCK_WIDTH + 4 #const BORDER_POS_Y CHAR_POS_Y - 4 #const BORDER_POS_Y2 BORDER_POS_Y + 1 #const BORDER_RIGHT_POS_X WINDOW_WIDTH - SIDE_MARGIN #const SHOT_AGE_INITIAL_VALUE 16 // 整数値のメモリ占有サイズ #const SIZEOF_INT 4 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 本体 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - 初期化 - - // randomize #ifdef USE_SND midiOutOpen hmo, -1 midiOutShortMsg hmo, MIDI_SND( 0xc0 + 1, 45, 100 ) midiOutShortMsg hmo, MIDI_SND( 0xc0 + 2, 126, 100 ) onexit *_onexit usesnd = 1 exist SCORE_FILE_NAME if( strsize != -1 ) { bload SCORE_FILE_NAME, bestScore, 4, 0 } #endif // サイズケチるために変数に入れとく _MSGothic = MSGothic // - - オブジェクト用バッファ初期化 - - // buffer OBJ_BUFFER_ID font _MSGothic, BLOCK_WIDTH // 背景塗りつぶして color boxf // - - オブジェクト書く - - // ; こんな感じ・・・って分からないか ; ; 赤緑青紫 ; block用: □□□□ ; char用: △△△△ ; beem用: □□□□ ; repeat NUM_CLOROS // まずは中身塗って hsvcolor cnt * 46, 120, 240 pos cnt * BLOCK_WIDTH, 0 mes "■" mes "▲" mes "□" // 外枠黒く color 0x33, 0x33, 0x33 pos cnt * BLOCK_WIDTH, 0 mes "□" mes "△" loop // 適当に初期化 gosub*init // 背景色で使うので適当に決めとく bgcolor = rnd( NUM_CLOROS ) // メインバッファを対象にする gsel // メインループ repeat // 画面初期化 redraw : await 30 : redraw 0 // 外側の背景 hsvcolor bgcolor * 46, 96, 128 boxf // 内側の背景 hsvcolor bgcolor * 46, 64, 216 boxf INNER_BACK_LEFT_POSX, 0, INNER_BACK_RIGHT_POSX #ifdef USE_SND if( started ) { dist = level - karma dist = remain - 1 huga = 2 << limit( dist, 0, 3 ) title "" + huga if( ( cnt \ huga ) == 0 ) { SND 37 } if( cnt \ ( huga * 8 ) \ ( huga * 3 ) == 0 ) { SND 78 } if( cnt \ ( huga * 8 ) == 0 ) { SND 74 SND 28 } else : if( cnt \ ( huga * 4 ) == 0 ) { SND 83 SND 39 } } #endif // 情報系 font _MSGothic, 16, 17 if( started ) { pos 516, 4 mes strf( "score%8d", score ) mes strf( "level%8d", level ) dist = level - karma if( ( dist > 2 ) + ( cnt & 1 << dist ) ) { mes strf( "karma%8d", karma ) } } // ブロック repeat NUM_BLOCK_VERTICAL y = cnt repeat NUM_BLOCK_HORIZON // 一番下のブロックの位置を取得 if( blocks(cnt, y) ) { remain = NUM_BLOCK_VERTICAL_MIN1 - y } pos SIDE_MARGIN + cnt * BLOCK_WIDTH, y * BLOCK_WIDTH gmode 2, BLOCK_WIDTH, BLOCK_WIDTH gcopy OBJ_BUFFER_ID, BLOCK_WIDTH * ( blocks.cnt.y - 1 ) if( blocks.cnt.y ) { // ブロックあるなら // 中身を書く // 配列使うよりも( blockMarkType.cnt.y * 2 )番目の文字をstrmidで取り出したほうが安上がり color pos ginfo_cx + 8, ginfo_cy + 10 blockMarkType = "→↑←↓◇◇" mes strmid( blockMarkType, blockMark.cnt.y * 2, 2 ) } loop loop // 死んでるかどうか if( started ) { level = 15 - _level / 30 if( remain ) : else { if( dead ) : else { // 死んだ直後 redraw await 700 } fadeCount++ dead++ } } // 境界線 // ゲームが始まった後だけに表示させる。ただしブロックの残りが3つ以下のときは、その残量に応じて速度を変え点滅させる if( started * ( ( cnt & remain ) + ( remain > 3 ) ) ) { pget 0 boxf SIDE_MARGIN, BORDER_POS_Y, BORDER_RIGHT_POS_X, BORDER_POS_Y2 } // タイトル中、適当にタイルを光らせる為に、玉を生成 if( started ) : else { if( ( cnt \ 48 ) * ( shotAge ) ) : else { // 時間が来た or どっかいっちゃったら // 作り直し shotAge = SHOT_AGE_INITIAL_VALUE shotPosX = rnd( NUM_BLOCK_HORIZON ) shotPosY = rnd( NUM_BLOCK_VERTICAL ) shotColor = rnd( NUM_CLOROS ) } } stick key, dead * 255 // 死んだ直後は、63をトリガーキーとして指定する #ifdef USE_SND if( key & 128 ) { usesnd ^= 1 } #endif if( key & 255 ) { if( dead ) { // 死亡中にキーが押されたら早送り dim key fadeCount += 16 } _level++ karma++ if( key & 1 ) { // Left Key // 左に移動 charPosX-- } if( key & 4 ) { // Right Key // 右に移動 charPosX++ } if( key & 32 ) { // Enter Key _level-- score += 2 SND 34 } // charPosX を強制的に ( 0 ~ NUM_BLOCK_HORIZON - 1 ) に変化させる。 // if( charPosX < 0 ) : charPosX += NUM_BLOCK_HORIZON // if( charPosX > NUM_BLOCK_HORIZON ) : charPosX -= NUM_BLOCK_HORIZON // と大体同じ(大体) charPosX = ( charPosX + NUM_BLOCK_HORIZON ) \ NUM_BLOCK_HORIZON if( key & 8 ) { // Down Key // 変色 charColor++ SND 29 } if( key & 64 ) { // Ctrl Key charColor += 3 SND 30 } charColor \= NUM_CLOROS if( shotAge ) : else { // 弾打ってないなら if( key & 2 ) { // Up Key // 弾打つ shotPosX = charPosX shotPosY = NUM_BLOCK_VERTICAL direction = 1 shot++ shotAge = SHOT_AGE_INITIAL_VALUE shotAge2 = shotAge shotColor = charColor dim combos karma += 3 _level += 2 } if( key & 16 ) { // Space Bar SND 74 // 詰める karma++ repeat NUM_BLOCK_HORIZON x = cnt repeat NUM_BLOCK_VERTICAL y = cnt dup _blocks, blocks.x.cnt if( _blocks ) : else { // ブロックが無かったなら repeat NUM_BLOCK_VERTICAL - cnt, cnt dup _tblocks, blocks.x.cnt if( _tblocks ) { // 存在するブロックのうち一番近くにあった奴を選んで _blocks = _tblocks // 空だった奴の代わりにする blockMark.x.y = blockMark.x.cnt poke _tblocks // 今見つかったブロックは消す // ちょっと影を書く color pos x * BLOCK_WIDTH + SIDE_MARGIN, cnt * BLOCK_WIDTH + 1 font _MSGothic, BLOCK_WIDTH mes "□" break } loop } loop loop // 変化を分かりやすくするためにいったん止める redraw wait 10 } } } // 開始 if( started ) : else : if( key ) { // いろいろ初期化 started++ dim score dim karma dim combos dim shotAge } // コンボ数表示 if( shotAge2 > 0 && combos > 0 && started ) { shotAge2-- ; foo = ( 8 - shotAge2 ) * ( 8 - shotAge2 ) と同じ ; 加速度1で大きくなり、shotAge2が8を上回ったら、加速度1で小さくなる foo = 8 - shotAge2 foo *= foo // 一回目は影、二回目は本体 font _MSGothic, 80 - foo, 17 repeat 2 pos 300 - cnt + foo, 60 - cnt + foo mes combos hsvcolor combos * 16, 240, 240 // コンボ数によって色相を帰る loop } // キャラ if( started ) { gmode 2, BLOCK_WIDTH, BLOCK_WIDTH pos SIDE_MARGIN + charPosX * BLOCK_WIDTH, CHAR_POS_Y + fadeCount if( dead ) { huga = 4096 - ( 64 - fadeCount ) * ( 64 - fadeCount ) grotate OBJ_BUFFER_ID, BLOCK_WIDTH * charColor, BLOCK_WIDTH, 0.1 * fadeCount, huga, huga // 回転させながら拡大 if( fadeCount >= 64 ) { bgcolor = charColor } if( fadeCount >= 128 ) { gosub*init dim started dim dead } } else { gcopy OBJ_BUFFER_ID, BLOCK_WIDTH * charColor, BLOCK_WIDTH } } // 玉 if( shotAge > 0 ) { ; directionの値に基づいて玉を動かす ; 座標の計算方法一覧(参考までに) ; ; ; 配列を使ってみる ; sins = 0, 1, 0, -1 ; shotPosY -= sins.direction ; coss = 1, 0, -1, 0 ; shotPosX = ( shotPosX + coss.direction + NUM_BLOCK_HORIZON ) \ NUM_BLOCK_HORIZON ; ; ; 三角関数を使ってみる ; PI = 3.141592 ; shotPosY = ( shotPosY - int( sin( PI * 2.0 * direction / 4.0 ) * 1.1 ) ) ; shotPosX = ( shotPosX + int( cos( PI * 2.0 * direction / 4.0 ) * 1.1 ) + NUM_BLOCK_HORIZON ) \ NUM_BLOCK_HORIZON ; 力技を使ってみる(これが一番軽い) shotPosY -= ( 2 - direction ) \ 2 shotPosX = ( shotPosX + ( 1 - direction ) \ 2 + NUM_BLOCK_HORIZON ) \ NUM_BLOCK_HORIZON // 論理演算子使えば縮められる? // oldShotPosの配列のお尻から、新しい順番にshotPosが入るようにする // 普通は挿入->シフト->表示を分けてやるんだろうけど、サイズをケチるために一気にやってしまう oldShotPosX.4 = shotPosX oldShotPosY.4 = shotPosY repeat 4 dup _oldShotPosX, oldShotPosX.cnt dup _oldShotPosY, oldShotPosY.cnt _oldShotPosX = _oldShotPosX.1 // _oldShotPosX.1 は shotPosX( cnt + 1 )と同義 _oldShotPosY = _oldShotPosY.1 // こっちも pos SIDE_MARGIN + _oldShotPosX * BLOCK_WIDTH, _oldShotPosY * BLOCK_WIDTH - 1 gmode 5, BLOCK_WIDTH, BLOCK_WIDTH, 32 + cnt * 32 // cntが大きい(新しい)ほど明るく表示される gcopy OBJ_BUFFER_ID, BLOCK_WIDTH * shotColor, BEEM_VERTICAL_POSITION loop if( shotPosY < 0 || NUM_BLOCK_VERTICAL <= shotPosY ) { // はみ出た dim shotAge // ビームを消す } else { shotAge-- // 減らしていく dup _blocks, blocks.shotPosX.shotPosY if( _blocks == shotColor + 1 ) { combos++ karma-- score += combos + 15 shotAge = SHOT_AGE_INITIAL_VALUE shotAge2 = shotAge dup _blockMark, blockMark.shotPosX.shotPosY if( _blockMark < NUM_CLOROS ) { // 矢印 direction = _blockMark } else { // ◇ // 左に回転 // direction = ( direction - 1 ) \ NUM_CLOROS でもいいけど省エネの為にこうする direction += 3 direction \= NUM_CLOROS } if( started ) { // タイトル画面じゃないなら // ブロック消す poke _blocks #ifdef USE_SND if( usesnd ) : midiOutShortMsg hmo, MIDI_SND( 0x90 + 1, 64 + combos, 120 ) #endif } } } } else { // oldShotPosX に大きな数字を入れといて、表示されないように(正確には x = 0x01010101 の位置に表示される )する memset oldShotPosX, 1, 16, 0 // カルマがレベルを上回ったor死んだとき // 死んだときってのは、ボーダー越えたときにブロックが押し寄せてくるアレのために #ifdef USE_SND if( karma > level ) { SND 72 } #endif if( karma > level || dead ) { // ブロック増やす dim karma repeat NUM_BLOCK_HORIZON x = cnt // 下にずらしていく repeat NUM_BLOCK_VERTICAL_MIN1 y = NUM_BLOCK_VERTICAL - cnt - 1 blocks(x, y) = blocks(x, y - 1) blockMark(x, y) = blockMark(x, y - 1) loop ; blocks.x.0 = rnd( NUM_CLOROS ) + 1 の 配列の添え字0を省略 blocks.x = rnd( NUM_CLOROS ) + 1 blockMark.x = rnd( 6 ) loop } } // タイトルなんか if( started ) : else { // 情報系 pget 0, 0 font _MSGothic, 24, 17 pos 216, 352 mes "Best Score: " + bestScore mes "Last Score: " + lastScore if( cnt & 16 ) { // 点滅で操作を促す mes "Press Cursor" } // タイトル // 一度目は普通に書き、二回目は左上にずらして太字で書ことで、いかにもなタイトルにする repeat 2 pos 226 - cnt * 4, 40 - cnt * 6 font _MSGothic, 96, 16 + cnt mes "Doom" color loop } loop // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 初期化サブルーチン // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *init // ブロック生成 dim blocks, NUM_BLOCK_HORIZON, NUM_BLOCK_VERTICAL dim blockMark, NUM_BLOCK_HORIZON, NUM_BLOCK_VERTICAL /* repeat NUM_BLOCK_VERTICAL - 4 y = cnt repeat NUM_BLOCK_HORIZON blocks.cnt.y = rnd( NUM_CLOROS ) + 1 blockMark.cnt.y = rnd( 6 ) loop loop */ // 上のループの軽量版 repeat ( NUM_BLOCK_VERTICAL - 4 ) * NUM_BLOCK_HORIZON lpoke blocks, cnt * SIZEOF_INT, rnd( NUM_CLOROS ) + 1 lpoke blockMark, cnt * SIZEOF_INT, rnd( 6 ) loop dim fadeCount dim _level dim charPosX // ベストスコア更新 if( bestScore < score ) { bestScore = score } lastScore = score return // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // 終了時 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #ifdef USE_SND *_onexit midiOutClose hmo if( bestScore < score ) { bestScore = score } dialog "スコア保存しますか?", 2 if( stat == 7 ) { end } bsave SCORE_FILE_NAME, bestScore, 4, 0 end #endif