'use strict' #=============================================================================== # # 'SKELTIUS' the Side-Scrolling Shooter # # Copyright (C) 2022, Furutanian, All Rights Reserved # #=============================================================================== mod = (a, n) -> unless((it = a % n) < 0) then it else it + n rep = (n, c = ' ') -> s = ''; s += c for i in [0...n]; s #---------------------------------------------------------------- # 16 # ベクトル計算 | # dx, dy -> -x, -y | x, -y # | # v -> 0 _______|_______ # 63 | 32 # | class Vec # -x, y | x, y # | # 48 @vxyss = []; n = 64; for t in [1..5] # n: 方向の分解能, v: 方向, t: 速度倍率 vxys = []; for v in [0...n] rd = 2 * Math.PI * v / n vx = -Math.floor(Math.cos(rd) * 256 * t) vy = -Math.floor(Math.sin(rd) * 256 * t) vxys.push([vx, vy]) @vxyss.push(vxys) @v2vxy: (v, t0 = 2) -> # 方向 v -> 速度成分 [vx, vy] if(it = @vxyss[t0]) then it[v] else [@vxyss[0][v][0] * ++t0, @vxyss[0][v][1] * t0] @dxy2v: (dx, dy) -> # 目標との差分 dx, dy -> 方向 v v = 0; s = 8 if(dy > 0) then ([dx, dy] = [-dx, -dy]; v += 32) if(dx > 0) then ([dx, dy] = [ dy, -dx]; v += 16) if(dx > dy) then ([dx, dy] = [ dy, dx]; v += 15; s = -s) for i in [1..3] dx >>= 1; s >>= 1; if(dx > dy) then (v += s; dy -= dx) return(v) @dxy2vxy: (dx, dy, t0 = 2) -> # 目標との差分 dx, dy -> 速度成分 [vx, vy] @v2vxy(@dxy2v(dx, dy), t0) #---------------------------------------------------------------- # # 画面 # class Screen constructor: (_s, id) -> @bgs = _s['BGS'] @objs = _s['OBJS'] canvas = document.getElementById(id) @context = canvas.getContext('2d') r = canvas.getBoundingClientRect() @x0 = r.left; @y0 = r.top; @width = r.width; @height = r.height draw: -> @context.fillStyle = '#808080' @context.fillRect(0, 0, @width, @height) @bgs[2].draw() # BG2: 背景 for type, objs of @objs for obj in objs obj.draw() @bgs[1].draw() # BG1: 地形 @bgs[0].draw() # BG0: 情報 #---------------------------------------------------------------- # # イメージプレーン # class Plane @image_load = [] @image_src: (pats) -> for n in [0...pats.length] image = new Image; image.src = pats[n] @image_load.push(pats[n] = image) @image_complete: -> for image in @image_load unless(image.complete) then return(false) return(true) constructor: (_s) -> @_s = _s @context = _s['SCREEN'].context draw: -> #---------------------------------------------------------------- # # バックグラウンド # class BackGround extends Plane # Screen: 512 x 384 / 32 x 24 -> BackGround: 1024 x 768 / 64 x 48 Plane.image_src(@pats = [ 'images/bg.png' # 0: 256 x 256 ]) constructor: (_s) -> super(_s) @bg_canvas = document.createElement('canvas') @bg_canvas.width = @w2 = (@w = Math.ceil(_s['SCREEN'].width)) << 1; @w2p = @w2 >> 4 @bg_canvas.height = @h2 = (@h = Math.ceil(_s['SCREEN'].height)) << 1; @h2p = @h2 >> 4 @bg_context = @bg_canvas.getContext('2d') @bg_buf = []; @bg_buf.push([]) for i in [0...@w2p] # get 用バッファ @pos_x = 0; @pos_y = 0 draw: -> @context.drawImage(@bg_canvas, -@pos_x, -@pos_y); @pos_x -= @w2 @context.drawImage(@bg_canvas, -@pos_x, -@pos_y); @pos_y -= @h2 @context.drawImage(@bg_canvas, -@pos_x, -@pos_y); @pos_x += @w2 @context.drawImage(@bg_canvas, -@pos_x, -@pos_y); @pos_y += @h2 check_hit_bg: (x, y, off_xys = []) -> x += @pos_x; y += @pos_y return(true) if((@get(x >> 4, y >> 4) & 0xE0) != 0x80) # 0x80-9F は当たり判定なし地形 for off_xy in off_xys x += off_xy[0]; y += off_xy[1] return(true) if((@get(x >> 4, y >> 4) & 0xE0) != 0x80) return(false) scroll: (x, y) -> @pos_x = mod(x, @w2); @pos_y = mod(y, @h2) put: (x, y, c) -> @bg_context.clearRect(x4 = (x = mod(x, @w2p)) << 4, y4 = (y = mod(y, @h2p)) << 4, 16, 16) @bg_context.drawImage(BackGround.pats[0], c & 0xF0, c << 4 & 0xF0, 16, 16, x4, y4, 16, 16) @bg_buf[x][y] = c put_text: (x, y, s) -> for p in [0...s.length] @put(x++, y, s.charCodeAt(p)) fill: (c) -> for x in [0...@w2p] for y in [0...@h2p] @put(x, y, c) get: (x, y) -> @bg_buf[mod(x, @w2p)][mod(y, @h2p)] #---------------------------------------------------------------- # # オブジェクト # class Obj extends Plane constructor: (_s, x, y) -> super(_s) @objs = _s['OBJS'] @_px8 = (@pos_x = x) << 8; @_py8 = (@pos_y = y) << 8 # ドット x256 位置 @active = true check_hit: -> check_hit_obj: (t) -> cx = t.pos_x + t.ent_x - @pos_x >>> 0 return(false) unless(cx <= @ent_x + t.ent_x) # X No Hit cy = t.pos_y + t.ent_y - @pos_y >>> 0 return(false) unless(cy <= @ent_y + t.ent_y) # Y No Hit return(true) hit: -> destroy: -> @active = false #---------------------------------------------------------------- # # 自機 # class Ship extends Obj Plane.image_src(@pats = [ 'images/ship_0.png' # 0: 46 x 22 'images/ship_1.png' # 1: 'images/ship_d2.png' # 2: 'images/ship_d1.png' # 3: 'images/ship_d0.png' # 4: ]) constructor: (_s, x, y) -> super(_s, x, y) @bgs = _s['BGS'] @play = _s['PLAY'] @ent_x = 29; @ent_y = 1 # 当たり判定サイズ - 1 @drw_x = 8; @drw_y = 10 # パターン描画位置 @_vx8 = @_vy8 = 0 # 移動速度 @crush = false; @shadow = false # 爆発中; 当たり判定 draw: -> if(@crush and @crush < 104) then return pat = Ship.pats[tsc >> 2 & 1] @crush and pat = Ship.pats[1 + (@crush >> 3) & 3] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) left: -> @pos_x > 10 and @_vx8 = -2 << 8 up: -> @pos_y > 12 and @_vy8 = -2 << 8 right: -> @pos_x < 450 and @_vx8 = 2 << 8 down: -> @pos_y < 370 and @_vy8 = 2 << 8 shot: -> @objs['SHOTS'].push(new Shot(@_s, @pos_x - 8, @pos_y)) check_hit: -> if(@shadow) then return # 自機の当たり判定なし return(@hit()) if(@bgs[1].check_hit_bg(@pos_x, @pos_y, [[@ent_x, 0]])) # BG との当たり判定 for type in ['ENEMIES', 'ENEMIES_F', 'ESHOTS'] # 敵/敵弾との当たり判定 for t in @objs[type] continue if(t.shadow) # 相手の当たり判定なし continue unless(@check_hit_obj(t)) t.hit() # 相手に当たりを通知 return(@hit()) hit: -> @crush = 128; @shadow = true # 127: 0b11_11_111 -> 104: 0b11_01_000 d0: -> @_px8 += @_vx8; @_py8 += @_vy8; @_vx8 = @_vy8 = 0 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 if(@crush) if(--@crush < 8) @play['SCENE'].miss() #---------------------------------------------------------------- # # 自機ショット # class Shot extends Obj Plane.image_src(@pats = [ 'images/shot_0.png' # 0: 14 x 6 'images/shot_1.png' # 1: ]) constructor: (_s, x, y) -> super(_s, x, y) @bgs = _s['BGS'] @ent_x = 15; @ent_y = 1 @drw_x = -2; @drw_y = 2 @_vx8 = 16 << 8 draw: -> pat = Shot.pats[tsc & 1] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) check_hit: -> return(@destroy()) if(@bgs[1].check_hit_bg(@pos_x, @pos_y)) # BG との当たり判定 for type in ['ENEMIES', 'ENEMIES_F'] # 敵との当たり判定 for t in @objs[type] continue if(t.shadow) # 相手の当たり判定なし continue unless(@check_hit_obj(t)) t.hit() # 相手に当たりを通知 return(@destroy()) d0: -> @_px8 += @_vx8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 @destroy() if(@pos_x > 511) #---------------------------------------------------------------- # # 敵 # class Enemy extends Obj constructor: (_s, x, y) -> super(_s, x, y) @play = _s['PLAY'] #---------------------------------------------------------------- # # 敵機A # class BogieA extends Enemy Plane.image_src(@pats = [ 'images/bogiea_0.png' # 0: 30 x 14 'images/bogiea_1.png' # 1: 'images/bogiea_d2.png' # 2: 'images/bogiea_d1.png' # 3: 'images/bogiea_d0.png' # 4: ]) constructor: (_s, x, y) -> super(_s, x, y) @ship = @objs['SHIP'][0] @ent_x = 29; @ent_y = 13 @drw_x = 0; @drw_y = 0 @_vx8 = -3 << 8; @_vy8 = 1 << 8; @pat = 0 @crush = false; @shadow = false @tsc = 0; @acts = [ [ 48, 1], [ 48, 2] [ 96, 1] [144, 1], [144, 2] [192, 1] [999, 9] ] draw: -> pat = BogieA.pats[@pat] @crush and pat = BogieA.pats[1 + (@crush >> 3)] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) hit: -> @play['SCORE'] += 10 # 100 pts. @crush = 32; @shadow = true # 31: 0b11_111 -> 8: 0b01_000 d0: -> unless(@crush) @_px8 += @_vx8; @_py8 += @_vy8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 return(@destroy()) if(@pos_x < -@ent_x) # 退場 while(@tsc == @acts[0][0]) switch((act = @acts.shift())[1]) when 1 # 飛行方向変化 @_vy8 *= -1; ++@pat; @pat &= 1 when 2 # 射撃 @objs['ESHOTS'].push(new EShot(@_s, @pos_x + 12, @pos_y + 4, Vec.dxy2vxy(@ship.pos_x - @pos_x, @ship.pos_y - @pos_y))) when 9 return(@destroy()) else if(--@crush < 8) @destroy() ++@tsc #---------------------------------------------------------------- # # 地上物 # class Fixed extends Enemy constructor: (_s, x, y) -> super(_s, x, y) d0: -> if(it = @play['SCENE']._vx8) # スクロールに合わせて動く @_px8 -= it #---------------------------------------------------------------- # # 敵砲台 # class Battery extends Fixed Plane.image_src(@pats = [ 'images/bat_0.png' # 0: 22 x 22 'images/bat_1.png' # 1: 'images/bat_2.png' # 2: 'images/bat_3.png' # 3: 'images/bat_4.png' # 4: 'images/bat_d2.png' # 5: 'images/bat_d1.png' # 6: 'images/bat_d0.png' # 7: ]) constructor: (_s, x, y) -> super(_s, x, y) @ship = @objs['SHIP'][0] @ent_x = 21; @ent_y = 21 @drw_x = 0; @drw_y = 0 @crush = false; @shadow = false @tsc = 0 draw: -> v = Vec.dxy2v(@ship.pos_x - @pos_x, @ship.pos_y - @pos_y) pat = Battery.pats[(v + 4 & 0x3F) >> 3] @crush and pat = Battery.pats[4 + (@crush >> 3)] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) hit: -> @play['SCORE'] += 10 # 100 pts. @crush = 32; @shadow = true # 31: 0b11_111 -> 8: 0b01_000 d0: -> super() @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 return(@destroy()) if(@pos_x < -@ent_x) # 退場 unless(@crush) unless(@tsc & 0x3F) # 射撃 @objs['ESHOTS'].push(new EShot(@_s, @pos_x + 8, @pos_y + 8, Vec.dxy2vxy(@ship.pos_x - @pos_x, @ship.pos_y - @pos_y))) else if(--@crush < 8) @destroy() ++@tsc #---------------------------------------------------------------- # # シャッター # class Shutter extends Fixed Plane.image_src(@pats = [ 'images/shutter_0.png' # 0: 48 x 48 'images/shutter_1.png' # 1: ]) constructor: (_s, x, y, type) -> super(_s, x, y) @ent_x = 47; @ent_y = 47 @drw_x = 0; @drw_y = 0 @_vx8 = 0 << 8; @_vy8 = unless(@type = type) then 1 << 5 else -1 << 5 @tsc = 0 draw: -> pat = Shutter.pats[@type] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) d0: -> super() @_py8 += @_vy8 if(@tsc < 384) @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 return(@destroy()) if(@pos_x < -@ent_x) # 退場 ++@tsc #---------------------------------------------------------------- # # 敵弾 # class EShot extends Enemy Plane.image_src(@pats = [ 'images/eshot_0.png' # 0: 10 x 10 'images/eshot_1.png' # 1: ]) constructor: (_s, x, y, v) -> super(_s, x, y) @bgs = _s['BGS'] @ent_x = 5; @ent_y = 5 @drw_x = 2; @drw_y = 2 @_vx8 = v[0]; @_vy8 = v[1] draw: -> pat = EShot.pats[tsc >> 3 & 1] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) check_hit: -> return(@destroy()) if(@bgs[1].check_hit_bg(@pos_x + 3, @pos_y + 3)) # BG との当たり判定 hit: -> @destroy() d0: -> @_px8 += @_vx8; @_py8 += @_vy8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 return(@destroy()) if(@pos_x < -7 or @pos_x > 513 or @pos_y < -7 or @pos_y > 383) # 退場 #---------------------------------------------------------------- # # ボス1 # class Boss1 extends Enemy Plane.image_src(@pats = [ 'images/boss1_0.png' # 0: 190 x 126 'images/boss1_d0.png' # 1: 'images/boss1_d1.png' # 2: ]) constructor: (_s, x, y) -> super(_s, x, y) @ship = @objs['SHIP'][0] @ent_x = 131; @ent_y = 49 @drw_x = 42; @drw_y = 38 @_vy8 = -1 << 8 @vital = 3; @mode = 0 @crush = false; @shadow = false @tsc = 0 equip: -> # 砲門 2 門を装備 @parts = UPPER: new Boss1part(@_s, 'UPPER', @, 28, -36) LOWER: new Boss1part(@_s, 'LOWER', @, 28, 72) @objs['ENEMIES'].push(part) for name, part of @parts unequip: (name) -> # 砲門を破壊された delete(@parts[name]) Object.keys(@parts).length or @mode = 1 draw: -> pat = Boss1.pats[0] @crush and pat = Boss1.pats[1 + (@crush >> 3 & 1)] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) hit: -> if(--@vital <= 0) @play['SCORE'] += 500 # 5000 pts. part.destroy() for name, part of @parts @crush = 128; @shadow = true # 127: 0b111_1_111 @_vy8 = 1 << 8 @play['SCENE'].go() # スクロール再開 d0: -> @_py8 += @_vy8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 unless(@crush) unless(@tsc & 0x3F) # 移動反転 @_vy8 = if(@pos_y < @ship.pos_y) then 1 << 8 else -1 << 8 if(@mode and !(@tsc & 3)) # 回転撃ち @objs['ESHOTS'].push(new EShot(@_s, @pos_x + 84, @pos_y + 22, Vec.v2vxy(@tsc & 0x3F))) else if(--@crush < 8) @destroy() ++@tsc #---------------------------------------------------------------- # # ボス1パーツ # class Boss1part extends Enemy Plane.image_src(@pats = [ 'images/boss1p_0.png' # 0: 14 x 14 'images/boss1p_d0.png' # 1: 'images/boss1p_d1.png' # 2: 'images/boss1p_d0.png' # 3: ]) constructor: (_s, name, mother, off_x, off_y) -> super(_s, 0, 0) @ship = @objs['SHIP'][0] @name = name; @mother = mother @ent_x = 9; @ent_y = 13 @drw_x = 0; @drw_y = 0 @off_x = off_x; @off_y = off_y @vital = 3 @crush = false; @shadow = false @tsc = 0 draw: -> pat = Boss1part.pats[0] @crush and pat = Boss1part.pats[@crush >> 3] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) hit: -> if(--@vital <= 0) @play['SCORE'] += 50 # 500 pts. @crush = 32; @shadow = true # 31: 0b11_111 d0: -> @pos_x = @mother.pos_x + @off_x @pos_y = @mother.pos_y + @off_y unless(@crush) unless(@tsc & 0x7F) v = Vec.dxy2v(@ship.pos_x - @pos_x, @ship.pos_y - @pos_y) @objs['ESHOTS'].push(new EShot(@_s, @pos_x - 2, @pos_y + 4, Vec.v2vxy(v + 60 & 0x3F))) @objs['ESHOTS'].push(new EShot(@_s, @pos_x - 2, @pos_y + 4, Vec.v2vxy(v & 0x3F))) @objs['ESHOTS'].push(new EShot(@_s, @pos_x - 2, @pos_y + 4, Vec.v2vxy(v + 4 & 0x3F))) else if(--@crush < 8) @mother.unequip(@name) @destroy() ++@tsc #---------------------------------------------------------------- # # ボス2 # class Boss2 extends Enemy Plane.image_src(@pats = [ 'images/boss2_0.png' # 0: 184 x 88 'images/boss2_1.png' # 1: 'images/boss2_d0.png' # 2: 'images/boss2_d1.png' # 3: ]) constructor: (_s, x, y) -> super(_s, x, y) @ship = @objs['SHIP'][0] @ent_x = 143; @ent_y = 23 @drw_x = 24; @drw_y = 32 @_vy8 = -3 << 8 @vital = 3; @mode = 0 @crush = false; @shadow = false @tsc = 0 draw: -> pat = Boss2.pats[tsc >> 2 & 1] @crush and pat = Boss2.pats[2 + (@crush >> 3 & 1)] @context.drawImage(pat, @pos_x - @drw_x, @pos_y - @drw_y) hit: -> if(--@vital <= 0) @play['SCORE'] += 500 # 5000 pts. @crush = 128; @shadow = true # 127: 0b111_1_111 @_vy8 = 1 << 8 @play['SCENE'].go() # スクロール再開 d0: -> @_py8 += @_vy8 @pos_x = @_px8 >> 8; @pos_y = @_py8 >> 8 unless(@crush) unless(@tsc & 0x3F) # 移動反転 @_vy8 = if(@ship.pos_y < @pos_y) then -3 << 8 else 3 << 8 unless(@tsc & 0x3F) for v in [0...32] @objs['ESHOTS'].push(new EShot(@_s, @pos_x + 84, @pos_y + 22, Vec.v2vxy(v << 1))) unless(@tsc & 0x3F) for v in [0...32] @objs['ESHOTS'].push(new EShot(@_s, @pos_x + 84, @pos_y + 22, Vec.v2vxy(v << 1, 4))) else if(--@crush < 8) @destroy() ++@tsc #---------------------------------------------------------------- # # シーン # class Scene constructor: (_s) -> @_s = _s @bgs = _s['BGS'] @objs = _s['OBJS'] @play = _s['PLAY'] initialize: -> @change_to = false @tsc = 0 clear_objs: -> @objs['SHIP'] = [] @objs['SHOTS'] = [] @objs['ENEMIES'] = [] @objs['ENEMIES_F'] = [] @objs['ESHOTS'] = [] input: (inputs) -> while(it = inputs['EDGE'].shift()) it == 'ENTER' and @play['PAUSE'] = !@play['PAUSE'] d0: -> ++@tsc #---------------------------------------------------------------- # # タイトルデモ # class Demo extends Scene constructor: (_s) -> super(_s) initialize: -> super() @clear_objs() @bgs[0].scroll(-512, 0) @bgs[0].put_text( 0, y, rep(64)) for y in [2...24] @bgs[0].put_text(10, 7, '- SKELTIUS -') @bgs[0].put_text( 9, 13, 'PUSH ENTER KEY') @bgs[0].put_text( 7, 18, '; 2022 ITLINE INC.') @bgs[0].put_text( 7, 20, 'ALL RIGHTS RESERVED') @bgs[i].fill(0x20) for i in [1..2] input: (inputs) -> n = 0; while(n < (it = inputs['EDGE']).length) switch(it[n]) when 'ENTER' then it.splice(n, 1); @_s['DIRECTOR'].scramble(); @change_to = new Stage1(@_s) else ++n super(inputs) d0: -> @bgs[0].scroll((if(@tsc < 256) then @tsc << 1 else 512) - 512, 0) super() #---------------------------------------------------------------- # # ゲームオーバー # class GameOver extends Scene constructor: (_s) -> super(_s) @objs['SHIP'] = [] # TODO: ? initialize: -> super() @bgs[0].put_text(11, 11, 'GAME OVER') input: (inputs) -> n = 0; while(n < (it = inputs['EDGE']).length) switch(it[n]) when 'ENTER' then it.splice(n, 1); @_s['DIRECTOR'].initialize(); @change_to = new Demo(@_s) else ++n super(inputs) d0: -> super() #---------------------------------------------------------------- # # エンディング # class Ending extends Scene constructor: (_s) -> super(_s) @objs['SHIP'][0].shadow = true initialize: -> super() @bgs[0].scroll(512, 0) @bgs[0].put_text(32 + 8, 6, 'MISSION COMPLETED') @bgs[0].put_text(32 + 4, 9, 'YOU DESTROYED ALL ENEMIES') @bgs[0].put_text(32 + 8, 11, 'AND WAR IS OVER') @bgs[0].put_text(32 + 4, 14, 'YOU ARE THE BRAVEST PILOT') @bgs[0].put_text(32 + 5, 17, 'SEE YOU AGAIN NEXT GAME') @roll = [ ' THE CREDITS', '', '' ' PROGRAMED BY', '' ' FURUTANIAN' '', '', '', '', '', '', '', '', '', '' ' ; 2022 ITLINE INC.', '' ' ALL RIGHTS RESERVED', '', '' '', '', '', '', '', '', '', '', '', '--' ] input: (inputs) -> n = 0; while(n < (it = inputs['EDGE']).length) switch(it[n]) when 'ENTER' then it.splice(n, 1); @_s['DIRECTOR'].initialize(); @change_to = new Demo(@_s) else ++n super(inputs) d0: -> unless((trip = @tsc - 192) < 0) if(@roll.length > 0) unless(trip & 0x0F) @bgs[0].put_text(32, y = 24 + (trip >> 4), rep(32)) @bgs[0].put_text(32 + 4, y, l) if(l = @roll.shift()) @bgs[0].scroll(512, trip) super() #---------------------------------------------------------------- # # ステージ # class Stage extends Scene constructor: (_s) -> super(_s) initialize: -> super() @clear_objs() @bgs[0].scroll(0, 0) # BG0: 情報 @bgs[0].put_text(0, y, rep(32)) for y in [2...24] @name_x = 16 - (@name.length >> 1); @name_s = ' '.substr(0, @name.length) @bgs[0].put_text(@name_x, 11, @name) for x in [0...32] # BG1: 地形 for y in [0...24] @bgs[1].put(x, y, parseInt(@map[y].substr(x << 1, 2), 16)) @last_trip = -1; @_tx8 = @trip = 0; @go(1 << 8) # スクロール関連初期化 @bgs[1].scroll(@trip, 0) @objs['SHIP'].push(new Ship(@_s, 128, 192)) # 自機を配置 input: (inputs) -> unless((ship = @objs['SHIP'][0]).crush) inputs['LEVEL']['LEFT'] and ship.left() inputs['LEVEL']['UP'] and ship.up() inputs['LEVEL']['RIGHT'] and ship.right() inputs['LEVEL']['DOWN'] and ship.down() n = 0; while(n < (it = inputs['EDGE']).length) switch(it[n]) when 'SPACE' then it.splice(n, 1); ship.crush or ship.shot() else ++n super(inputs) go: (_vx8 = false) -> # スクロール開始 _vx8 and @last_vx8 = _vx8 @_vx8 = @last_vx8 stop: -> # スクロール停止 @_vx8 = 0 d0: -> @tsc == 180 and @bgs[0].put_text(@name_x, 11, @name_s) unless(@last_trip == @trip) unless(@trip & 0x0F) # 地形更新 x = 32 + (@trip >> 4) @bgs[1].put(x, y, parseInt(@map[y].substr(x << 1, 2), 16)) for y in [0...24] @last_trip = @trip; @_tx8 += @_vx8; @trip = @_tx8 >> 8 # スクロール @bgs[1].scroll(@trip, 0) @bgs[2].scroll(@trip >> 1, 0) super() miss: -> unless(@play['LEFT']--) then new GameOver(@_s) else false #---------------------------------------------------------------- # # ステージ1 # class Stage1 extends Stage constructor: (_s) -> @name = 'STAGE 1' @map = [ # 32 x 24 'B7A7B7A7B7A7B7A7B7A7B7A7B7A7A7B7A7B7A7A7A7A7B7A7B7A7A7B7A7B7A7B7A7B7A7B7A7A7B7A7B7A7B7A7B7A7A7B7A7B7A7B7A7A7B7A7B7A7A7B7A7A7B7A7' 'D8C8D8C8C8D8D8C8C8C8D8B8C8C8C8D8C8C8D8C8D8D8C8D8C8B8C8B8C8B8C8D8C8C8D8C8D8C8C8C8D8C8D8C8C8B8B8C8B8C8B8C8B8C8C8B8C8B8C8B8C8B8C8B8' '80808080808080808080808898988080808080808080808080889888988880808080808080808080808080809888889888988898889898889888988898889888' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808099808980808080808080998999898080808089998999899989808080808080808080808080808080899999899989998989998999899989999989' 'C9C9D9C9D9C9C9C9B9C9D9C9D9C9D9C9C9B9C9B9D9C9D9C9B9C9B9C9B9C9B9D9C9D9C9C9D9C9D9C9C9D9D9C9C9D9B9C9C9B9C9B9C9B9B9C9B9C9B9C9B9C9C9B9' 'AAAABAAABAAAAABAAABAAAAABAAABAAABAAAAABABAAABABABAAAAABABAAABABAAAAABABAAABAAAAABAAABAAABAAABAAABAAABAAABAAABAAAAABAAABAAABAAABA' ] # . . . , . . . + . . . , . . 16+ . . . , . . . + . . . , . . 32# . . . , . . . + . . . , . . 48+ . . . , . . . + . . . , . . 64# n = 0; for m in [ '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' ] # . . . , . . . + . . . , . . 72+ . . . , . . . + . . . , . . 96# . . . , . . . + . . . , . .112+ . . . , . . . + . . . , . .128# @map[n++] += m super(_s) initialize: -> super() for x in [0...64] # BG2: 背景 for y in [0...6] @bgs[2].put(x, y, 0x84) for y in [6...8] @bgs[2].put(x, y, 0x94) for y in [8...10] @bgs[2].put(x, y, 0x85) for y in [10...24] @bgs[2].put(x, y, 0x95) for x in [0...3] @bgs[2].put( x << 4, 11, 0x86) @bgs[2].put((x << 4) + 1, 11, 0x96) @bgs[2].put((x << 4) + 4, 12, 0x86) @bgs[2].put((x << 4) + 5, 12, 0x96) @acts = [ [ 8 << 4, 1, 128], [ 8 << 4, 2, (21 << 4) - 4] [16 << 4, 1, 256], [16 << 4, 2, (21 << 4) - 4] [24 << 4, 1, 128], [24 << 4, 2, (21 << 4) - 4] [32 << 4, 1, 256], [32 << 4, 2, (21 << 4) - 4], [32 << 4, 8] [48 << 4, 9] [999, 9] ] d0: -> unless(@last_trip == @trip) while(@trip == @acts[0][0]) switch((act = @acts.shift())[1]) when 1 # 敵機出現 @objs['ENEMIES'].push(new BogieA(@_s, 512, act[2])) when 2 # 砲台出現 @objs['ENEMIES'].push(new Battery(@_s, 512, act[2])) when 8 # ボス出現 @stop() @objs['ENEMIES'].push(boss = new Boss1(@_s, 256 + 42, 384 + 38)) boss.equip() when 9 # ステージ終了 @change_to = new Stage2(@_s) super() miss: -> @change_to = if(it = super()) then it else new Stage1(@_s) #---------------------------------------------------------------- # # ステージ2 # class Stage2 extends Stage constructor: (_s) -> @name = 'STAGE 2' @map = [ # 32 x 24 'ACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCC' 'ADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDACBCBCCCADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCDADBDBDCD' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ADBDBDCD8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ABBBBBCB8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' '80808080808080808080808080808080808080808080808080808080ACBCBCCC8080808080808080808080808080808080808080808080808080808080808080' 'ABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBACBCBCCCABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCBABBBBBCB' 'ACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCCACBCBCCC' ] # . . . , . . . + . . . , . . 16+ . . . , . . . + . . . , . . 32# . . . , . . . + . . . , . . 48+ . . . , . . . + . . . , . . 64# n = 0; for m in [ '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' '8080808080808080808080808080808080808080808080808080808080808080' ] # . . . , . . . + . . . , . . 72+ . . . , . . . + . . . , . . 96# . . . , . . . + . . . , . .112+ . . . , . . . + . . . , . .128# @map[n++] += m super(_s) initialize: -> super() for x in [0...32] # BG2: 背景 for y in [0...12] @bgs[2].put((x << 1) , (y << 1) , 0x8C) @bgs[2].put((x << 1) + 1, (y << 1) , 0x9C) @bgs[2].put((x << 1) , (y << 1) + 1, 0x8D) @bgs[2].put((x << 1) + 1, (y << 1) + 1, 0x9D) @acts = [ [ 0 << 4, 3, -56, 192] [ 8 << 4, 1, 128], [ 8 << 4, 2, (21 << 4) - 4] [16 << 4, 1, 256], [16 << 4, 2, (21 << 4) - 4] [24 << 4, 1, 128], [24 << 4, 2, (21 << 4) - 4] [32 << 4, 1, 256], [32 << 4, 2, (21 << 4) - 4], [32 << 4, 8] [48 << 4, 9] [999, 9] ] d0: -> unless(@last_trip == @trip) while(@trip == @acts[0][0]) switch((act = @acts.shift())[1]) when 1 # 敵機出現 @objs['ENEMIES'].push(new BogieA(@_s, 512, act[2])) when 2 # 砲台出現 @objs['ENEMIES'].push(new Battery(@_s, 512, act[2])) when 3 # シャッター出現 @objs['ENEMIES_F'].push(new Shutter(@_s, 512 + act[2], act[3] - 96, 0)) @objs['ENEMIES_F'].push(new Shutter(@_s, 512 + act[2], act[3] + 48, 1)) when 8 # ボス出現 @stop() @objs['ENEMIES'].push(new Boss2(@_s, 256 + 24, 384 + 32)) when 9 # ステージ終了 @change_to = new Ending(@_s) super() miss: -> @change_to = if(it = super()) then it else new Stage2(@_s) #---------------------------------------------------------------- # # 進行 # class Director constructor: (_s) -> @_s = _s @bgs = _s['BGS'] @objs = _s['OBJS'] @inputs = _s['INPUTS'] @play = _s['PLAY'] @bgs.push(new BackGround(_s)) # BG0: 情報 @bgs.push(new BackGround(_s)) # BG1: 地形 @bgs.push(new BackGround(_s)) # BG2: 背景 @play['SCORE'] = 0; @play['HISCORE'] = 3000 @bgs[0].put_text(0, 0, '1P 00 HI 30000 2P 00') @play['SCENE'] = new Demo(@_s) @play['SCENE'].initialize() initialize: -> @play['EXTEND'] = [2000, 7000, -7000] # EXTEND / EVERY 70000 @play['LEFT'] = 3; @update_left() scramble: -> @play['SCORE'] = 0; --@play['LEFT'] d0: -> @play['SCENE'].input(@inputs) # シーン(ステージ)に入力を渡す return if(@play['PAUSE']) for type, objs of @objs # 各オブジェクトの当たり判定 for obj in objs obj.check_hit() @play['SCENE'].d0() # 現在のシーンの処理 for type, objs of @objs # 各オブジェクトの移動, 破棄 n = 0; while(n < objs.length) objs[n].d0() unless(objs[n].active) then objs.splice(n, 1) else ++n @update_score() # SCORE 更新 while(@play['SCORE'] >= @play['EXTEND'][0]) # EXTEND es = @play['EXTEND'].shift() (it = @play['EXTEND'][0]) < 0 and @play['EXTEND'].unshift(es - it) # EVERY ++@play['LEFT']; @update_left() if(it = @play['SCENE'].change_to) # シーンチェンジ @play['SCENE'] = it @play['SCENE'].initialize() @update_left() update_score: -> @bgs[0].put_text( 2, 0, (' ' + (it = @play[ 'SCORE']).toString()).slice(-7)) @bgs[0].put_text(13, 0, (' ' + (@play['HISCORE'] = it).toString()).slice(-7)) if(it > @play['HISCORE']) update_left: -> n = 0; while(n < @play['LEFT']) @bgs[0].put(n++, 1, 0x82) @bgs[0].put(n, 1, 0x80) #=============================================================================== # # メイン # _s = BGS: [] OBJS: ENEMIES: [] ESHOTS: [] SHOTS: [] SHIP: [] ENEMIES_F: [] INPUTS: inputs = { LEVEL: {}, LEVEL_SENSE: { 13: 'ENTER', 32: 'SPACE', 37: 'LEFT', 38: 'UP', 39: 'RIGHT', 40: 'DOWN' },\ EDGE: [], EDGE_SENSE: { ENTER: true, SPACE: true } } PLAY: SCENE: false SCORE: false HISCORE: false EXTEND: false LEFT: false PAUSE: false #---------------------------------------------------------------- # # 各種入力 # keydown = (event) -> if(event.repeat) then return keyCode = if(it = inputs['LEVEL_SENSE'][event.keyCode]) then it else String.fromCharCode(event.keyCode) inputs['LEVEL'][keyCode] = true if(inputs['EDGE_SENSE'][keyCode]) then inputs['EDGE'].push(keyCode) document.onkeydown = keydown keyup = (event) -> keyCode = if(it = inputs['LEVEL_SENSE'][event.keyCode]) then it else String.fromCharCode(event.keyCode) inputs['LEVEL'][keyCode] = false document.onkeyup = keyup #---------------------------------------------------------------- # # 各種初期設定 # prep = -> unless(Plane.image_complete()) setTimeout(prep, 100) else _s['SCREEN'] = new Screen(_s, 'canvas1') _s['DIRECTOR'] = new Director(_s) _s['DIRECTOR'].initialize() vsync() #---------------------------------------------------------------- # # 垂直帰線割込みのシミュレート # tsc = 0; ints = []; spf = (1000 / 60) # フレーム速度(1000/FPS) vsync = -> ints.push(start = (new Date).getTime()); next = start + spf ints.shift() while(ints[0] < start - 1000) _s['SCREEN'].draw() _s['DIRECTOR'].d0() finish = (new Date).getTime() if(++tsc % 60 == 0) console.log(['now: ', start, 'frames: ', ints.length, ' load: ', Math.floor((finish - start) * 100 / spf), '%'].toString()) setTimeout(vsync, if((it = next - finish) > 0) then it else 0) prep() # TODO: サウンド関係