rem ================================================================== rem Blastemroids rem rem Enemy and player sprites by Ari Feldman, other sprites by Marcus. rem ================================================================== visible: vNextFrameTime vFrameHold visible: rem Enemy data. CEL_SIZE = 20 GRID_WIDTH = 12 GRID_HEIGHT = 6 vEnemies[GRID_WIDTH][GRID_HEIGHT][7] ENEMY_TYPE = 0 ENEMY_STATE = 1 ENEMY_STAMINA = 2 ENEMY_X = 3 ENEMY_Y = 4 ENEMY_BASE_FRAME = 5 ENEMY_FRAME = 6 vEnemiesF#[GRID_WIDTH][GRID_HEIGHT][5] ENEMY_X1 = 0 ENEMY_Y1 = 1 ENEMY_X2 = 2 ENEMY_Y2 = 3 ENEMY_PARAM = 4 vNextAttackTimer vEnemyCount vGridMotionAngle# = 0.0 vGridOffsetY rem Enemy bullets. MAX_ENEMY_BULLETS = 8 vEnemyBullets[MAX_ENEMY_BULLETS][2] BULLET_ACTIVE = 0 BULLET_FRAME = 1 vEnemyBulletsF#[MAX_ENEMY_BULLETS][4] BULLET_X = 0 BULLET_Y = 1 BULLET_DX = 2 BULLET_DY = 3 vEnemyBulletCounter vEnemyBulletTimer = 400 rem Player. vPlayerX vPlayerY vPlayerBulletType vPlayerBulletX vPlayerBulletY vPlayerShield vScore = 0 vLevel = 1 vLives = 3 vGameOver = false vGameOverTimer = 0 rem Explosions. MAX_EXPLOSIONS = 8 vExplosions[MAX_EXPLOSIONS][4] EXP_FRAME = 0 EXP_FRAME_COUNTER = 1 EXP_X = 2 EXP_Y = 3 vExplosionCounter = 0 rem Other. vScroll = 0 vCentiCounter = 0 rem Images. BACKGROUND_IMAGE = 0 PLAYER_IMAGE = 1 PLAYER_BULLETS_IMAGE = 2 EXPLOSION_IMAGE = 3 LOGO_IMAGE = 4 GAME_OVER_IMAGE = 5 ENEMY_BULLET_IMAGE = 10 ENEMY_IMAGE_BASE = 10 rem Sound. EXPLOSION_SOUND = 0 ENEMY_HIT_SOUND = 1 BULLET_SOUND = 2 hidden: rem Init window. set window 0, 0, 320, 240, true, true set color 0, 0, 0 cls rem Load. proc Load set redraw off rem Init. proc InitPlayer proc InitEnemies 24 + vLevel*2 proc InitExplosions play music 0 rem ================================================================== rem Main loop. rem ================================================================== proc InitFrameHold 60 do rem Maintain constant speed. proc HoldFrame vCentiCounter = (vCentiCounter + 1)%100 vScroll = (vScroll + 1)%240 vGridMotionAngle = vGridMotionAngle + 0.5 rem Init new level if all enemies are gone. if vEnemyCount = 0 vLevel = vLevel + 1 proc InitEnemies 24 + vLevel*2 endif rem Update. proc UpdatePlayer proc UpdateEnemies proc UpdateEnemyBullets proc UpdateExplosions if vGameOver vGameOverTimer = vGameOverTimer - 1 if vGameOverTimer = 0 vGameOver = false vLevel = 0 vScore = 0 vEnemyCount = 0 proc InitPlayer endif endif rem Draw. set color 255, 255, 255 draw image BACKGROUND_IMAGE, 0, vScroll draw image BACKGROUND_IMAGE, 0, vScroll - 240 proc DrawEnemies proc DrawEnemyBullets proc DrawPlayer proc DrawExplosions proc DrawScoreEtc if vGameOver set color 0, 0, 0, 128 cls set color 255, 255, 255 draw image GAME_OVER_IMAGE, (width(primary) - width(GAME_OVER_IMAGE))/2, 100 set caret width(primary)/2, 140 endif redraw until keydown(27) or not running() stop music 0 end rem ================================================================== rem Load data. rem ================================================================== procedure Load() rem create font 0, "impact", 16 rem save font 0, "data/impact16.txt", "data/impact16.bmp" if javac() load font 0, "data/impact16.txt", "data/impact16.png" else load font 0, "data/impact16.txt", "data/impact16.bmp" endif set color 255, 255, 255 load image LOGO_IMAGE, "data/logo.bmp" draw image LOGO_IMAGE, 160 - width(LOGO_IMAGE)/2, 60 set caret 160, 100 center "Use cursor keys to control your ship." center "Press space bar to shoot." center center "Loading . . ." wait 1000 if javac() load music 0, "data/bgmusic.aif" else load music 0, "data/bgmusic.it" endif load image GAME_OVER_IMAGE, "data/gameover.bmp" set image colorkey GAME_OVER_IMAGE, 0, 0, 0 create image BACKGROUND_IMAGE, 320, 240 set image BACKGROUND_IMAGE set color 0, 0, 0 cls for i = 0 to 199 set color 255, 255, 255, rnd(256) draw pixel rnd(320), rnd(240) next set image primary load image PLAYER_IMAGE, "data/player.bmp" set image colorkey PLAYER_IMAGE, 0, 0 ,0 set image grid PLAYER_IMAGE, 2, 1 load image PLAYER_BULLETS_IMAGE, "data/player_bullets.bmp" set image colorkey PLAYER_BULLETS_IMAGE, 0, 0, 0 load image EXPLOSION_IMAGE, "data/explosion.bmp" set image colorkey EXPLOSION_IMAGE, 0, 0, 0 set image grid EXPLOSION_IMAGE, 8, 1 load image ENEMY_IMAGE_BASE + 1, "data/enemy_0.bmp" load image ENEMY_IMAGE_BASE + 2, "data/enemy_1.bmp" load image ENEMY_IMAGE_BASE + 3, "data/enemy_2.bmp" set image colorkey ENEMY_IMAGE_BASE + 1, 0, 0 ,0 set image colorkey ENEMY_IMAGE_BASE + 2, 0, 0 ,0 set image colorkey ENEMY_IMAGE_BASE + 3, 0, 0 ,0 set image grid ENEMY_IMAGE_BASE + 1, 12, 1 set image grid ENEMY_IMAGE_BASE + 2, 12, 1 set image grid ENEMY_IMAGE_BASE + 3, 12, 1 load image ENEMY_BULLET_IMAGE, "data/enemy_bullet.bmp" set image colorkey ENEMY_BULLET_IMAGE, 0, 0, 0 set image grid ENEMY_BULLET_IMAGE, 2, 1 load sound EXPLOSION_SOUND, "data/explosion.wav" load sound ENEMY_HIT_SOUND, "data/enemy_hit.wav" load sound BULLET_SOUND, "data/bullet.wav" set color 255, 255, 0 center center "Click to start game!" wait mousebutton endproc rem ================================================================== rem Init player. rem ================================================================== procedure InitPlayer() vLives = 3 vPlayerX = 160 - width(PLAYER_IMAGE)/2 vPlayerY = 250 vPlayerShield = 200 vPlayerBulletType = 0 vGameOver = false endproc rem ================================================================== rem Update player. rem ================================================================== procedure UpdatePlayer() if vGameOver then return; if keydown(37) then vPlayerX = vPlayerX - 2 if keydown(39) then vPlayerX = vPlayerX + 2 if vPlayerY > 210 then vPlayerY = vPlayerY - 1 if vPlayerX < 0 then vPlayerX = 0 if vPlayerX > 320 - width(PLAYER_IMAGE) then vPlayerX = 320 - width(PLAYER_IMAGE) if keydown(" ") and vPlayerBulletType = 0 vPlayerBulletType = 1 vPlayerBulletX = vPlayerX vPlayerBulletY = vPlayerY + 8 play sound BULLET_SOUND endif if vPlayerShield > 0 then vPlayerShield = vPlayerShield - 1 if vPlayerBulletType > 0 vPlayerBulletY = vPlayerBulletY - 4 if vPlayerBulletY < -height(PLAYER_BULLETS_IMAGE) then vPlayerBulletType = 0 endif endproc rem ================================================================== rem Draw player rem ================================================================== procedure DrawPlayer() if vGameOver then return if vPlayerShield = 0 or vCentiCounter%8 < 4 then draw image PLAYER_IMAGE, vPlayerX, vPlayerY, (vCentiCounter%10)/5 if vPlayerBulletType > 0 draw image PLAYER_BULLETS_IMAGE, vPlayerBulletX, vPlayerBulletY endif endproc rem ================================================================== rem Lose a life. rem ================================================================== procedure LoseLife() if vPlayerShield = 0 proc AddExplosion vPlayerX + width(PLAYER_IMAGE)/2, vPlayerY + height(PLAYER_IMAGE)/2 vLives = vLives - 1 vPlayerY = 250 vPlayerX = 160 - width(PLAYER_IMAGE)/2 vPlayerShield = 200 if vLives < 0 vGameOver = true vGameOverTimer = 300 endif endif endproc rem ================================================================== rem Draw score etc. rem ================================================================== procedure DrawScoreEtc() set color 255, 255, 255 set caret 4, 4 write "Level: " set color 128, 255, 255 write vLevel set caret 64, 4 set color 255, 255, 255 write "Score: " set color 255, 255, 128 write vScore set color 255, 255, 255 for i = 0 to vLives - 1 draw image PLAYER_IMAGE, 320 - (i + 1)*(width(PLAYER_IMAGE) + 4), 4, 0 next endproc rem ================================================================== rem Init explosions. rem ================================================================== procedure InitExplosions() vExplosionCounter = 0 for i = 0 to MAX_EXPLOSIONS - 1 vExplosions[i][EXP_FRAME] = -1 next endproc rem ================================================================== rem Add an explosion at (x, y). rem ================================================================== procedure AddExplosion(x, y) vExplosions[vExplosionCounter][EXP_FRAME] = 0 vExplosions[vExplosionCounter][EXP_FRAME_COUNTER] = 0 vExplosions[vExplosionCounter][EXP_X] = x - width(EXPLOSION_IMAGE)/2 vExplosions[vExplosionCounter][EXP_Y] = y - height(EXPLOSION_IMAGE)/2 vExplosionCounter = (vExplosionCounter + 1)%MAX_EXPLOSIONS play sound EXPLOSION_SOUND endproc rem ================================================================== rem Update explosions. rem ================================================================== procedure UpdateExplosions() for i = 0 to MAX_EXPLOSIONS - 1 if vExplosions[i][EXP_FRAME] >= 0 vExplosions[i][EXP_FRAME_COUNTER] = (vExplosions[i][EXP_FRAME_COUNTER] + 1)%4 if vExplosions[i][EXP_FRAME_COUNTER] = 0 vExplosions[i][EXP_FRAME] = vExplosions[i][EXP_FRAME] + 1 if vExplosions[i][EXP_FRAME] = 8 then vExplosions[i][EXP_FRAME] = -1 endif endif next endproc rem ================================================================== rem Draw explosions. rem ================================================================== procedure DrawExplosions() for i = 0 to MAX_EXPLOSIONS - 1 if vExplosions[i][EXP_FRAME] >= 0 draw image EXPLOSION_IMAGE, vExplosions[i][EXP_X], vExplosions[i][EXP_Y], vExplosions[i][EXP_FRAME] endif next endproc rem ================================================================== rem Init enemies. rem ================================================================== procedure InitEnemies(count) vNextAttackTimer = 400 rem Clear. for y = 0 to GRID_HEIGHT - 1 for x = 0 to GRID_WIDTH - 1 vEnemies[x][y][ENEMY_TYPE] = 0 next next rem Add enemies. count = min(count, GRID_WIDTH*GRID_HEIGHT)/2 for i = 0 to count - 1 do x = rnd(GRID_WIDTH/2) y = rnd(GRID_HEIGHT) until vEnemies[x][y][ENEMY_TYPE] = 0 type = rnd(3) + 1 vEnemies[x][y][ENEMY_TYPE] = type vEnemies[x][y][ENEMY_STAMINA] = type vEnemies[x][y][ENEMY_STATE] = 0 vEnemies[x][y][ENEMY_BASE_FRAME] = 0 vEnemies[x][y][ENEMY_FRAME] = rnd(60) vEnemies[GRID_WIDTH - 1 - x][y][ENEMY_TYPE] = type vEnemies[GRID_WIDTH - 1 - x][y][ENEMY_STAMINA] = type vEnemies[GRID_WIDTH - 1 - x][y][ENEMY_STATE] = 0 vEnemies[GRID_WIDTH - 1 - x][y][ENEMY_BASE_FRAME] = 0 vEnemies[GRID_WIDTH - 1 - x][y][ENEMY_FRAME] = rnd(60) next vEnemyCount = count*2 vEnemyBulletCounter = 0 for i = 0 to MAX_ENEMY_BULLETS - 1 vEnemyBullets[i][BULLET_ACTIVE] = 0 next vGridOffsetY = -200 endproc rem ================================================================== rem Add an enemy bullet moving from (x, y) to player's position. rem ================================================================== procedure AddEnemyBullet(x, y) vEnemyBullets[vEnemyBulletCounter][BULLET_ACTIVE] = true vEnemyBullets[vEnemyBulletCounter][BULLET_FRAME] = 0 vEnemyBulletsF[vEnemyBulletCounter][BULLET_X] = float(x) vEnemyBulletsF[vEnemyBulletCounter][BULLET_Y] = float(y) dx# = float(vPlayerX) - float(x) dy# = float(vPlayerY) - float(y) k# = 1.0/sqr(dx*dx + dy*dy) vEnemyBulletsF[vEnemyBulletCounter][BULLET_DX] = dx*k*2.0 vEnemyBulletsF[vEnemyBulletCounter][BULLET_DY] = dy*k*2.0 vEnemyBulletCounter = (vEnemyBulletCounter + 1)%MAX_ENEMY_BULLETS endproc rem ================================================================== rem Update enemy bullets. rem ================================================================== procedure UpdateEnemyBullets() for i = 0 to MAX_ENEMY_BULLETS - 1 if vEnemyBullets[i][BULLET_ACTIVE] vEnemyBullets[i][BULLET_FRAME] = (vEnemyBullets[i][BULLET_FRAME] + 1)%20 vEnemyBulletsF[i][BULLET_X] = vEnemyBulletsF[i][BULLET_X] + vEnemyBulletsF[i][BULLET_DX] vEnemyBulletsF[i][BULLET_Y] = vEnemyBulletsF[i][BULLET_Y] + vEnemyBulletsF[i][BULLET_DY] if not vGameOver if ImageCol(ENEMY_BULLET_IMAGE, int(vEnemyBulletsF[i][BULLET_X]), int(vEnemyBulletsF[i][BULLET_Y]), PLAYER_IMAGE, vPlayerX, vPlayerY) vEnemyBullets[i][BULLET_ACTIVE] = false proc LoseLife endif endif endif next endproc rem ================================================================== rem Draw enemy bullets. rem ================================================================== procedure DrawEnemyBullets() for i = 0 to MAX_ENEMY_BULLETS - 1 if vEnemyBullets[i][BULLET_ACTIVE] draw image ENEMY_BULLET_IMAGE, int(vEnemyBulletsF[i][BULLET_X]), int(vEnemyBulletsF[i][BULLET_Y]), vEnemyBullets[i][BULLET_FRAME]/10 endif next endproc rem ================================================================== rem Return the x coordinate of an enemy at grid position (x, y). rem ================================================================== function GridX(x, y) return (320 - GRID_WIDTH*CEL_SIZE)/2 + x*CEL_SIZE + int(cos(vGridMotionAngle + float(y*24))*24.0) endfunc rem ================================================================== rem Return the y coordinate of an enemy at a certain grid y. rem ================================================================== function GridY(y) return 24 + y*CEL_SIZE + vGridOffsetY endfunc rem ================================================================== rem Update enemies. rem ================================================================== procedure UpdateEnemies() if vGridOffsetY < 0 then vGridOffsetY = vGridOffsetY + 1 vEnemyBulletTimer = vEnemyBulletTimer - 1 rem Update enemy attacks. vNextAttackTimer = vNextAttackTimer - 1 if vNextAttackTimer <= 0 rem Look for enemies that can attack and add them to a list. attackers[GRID_WIDTH*GRID_HEIGHT] count = 0 for y = 0 to GRID_HEIGHT - 1 for x = 0 to GRID_WIDTH - 1 if vEnemies[x][y][ENEMY_TYPE] > 0 and vEnemies[x][y][ENEMY_STATE] = 0 rem Add enemy to list of possible attackers. Convert its position rem in the grid to an index. attackers[count] = y*GRID_WIDTH + x count = count + 1 endif next next rem Any one interested? if count > 0 rem Get any enemy from the list. enemy = attackers[rnd(count)] rem Convert its position back to the 2D grid. x = enemy%GRID_WIDTH y = enemy/GRID_WIDTH rem Init its attack curve. vEnemies[x][y][ENEMY_STATE] = 1 vEnemiesF[x][y][ENEMY_PARAM] = 0.0 vEnemiesF[x][y][ENEMY_X1] = float(rnd(320)) vEnemiesF[x][y][ENEMY_X2] = float(rnd(320)) vEnemiesF[x][y][ENEMY_Y1] = 400.0 vEnemiesF[x][y][ENEMY_Y2] = float(GridY(y) - 100) rem Wait some until next attack. vNextAttackTimer = max(200 + rnd(100) - vLevel*50, 50) else vNextAttackTimer = 50 endif endif rem Move enemies. for y = 0 to GRID_HEIGHT - 1 for x = 0 to GRID_WIDTH - 1 if vEnemies[x][y][ENEMY_TYPE] > 0 vEnemies[x][y][ENEMY_FRAME] = (vEnemies[x][y][ENEMY_FRAME] + 1)%60 gx = GridX(x, y) gy = GridY(y) rem Is it attacking? if vEnemies[x][y][ENEMY_STATE] = 1 rem Shoot? prm# = vEnemiesF[x][y][ENEMY_PARAM] if vEnemyBulletTimer <= 0 and ((prm > 0.05 and prm < 0.1) or prm > 0.75) vEnemyBulletTimer = 50 + rnd(100) proc AddEnemyBullet vEnemies[x][y][ENEMY_X], vEnemies[x][y][ENEMY_Y] endif rem Update position. vEnemiesF[x][y][ENEMY_PARAM] = vEnemiesF[x][y][ENEMY_PARAM] + 0.0025 rem Back at where it started? if vEnemiesF[x][y][ENEMY_PARAM] >= 1.0 vEnemies[x][y][ENEMY_STATE] = 0 vEnemies[x][y][ENEMY_X] = gx + 2 vEnemies[x][y][ENEMY_Y] = gy + 6 vEnemies[x][y][ENEMY_BASE_FRAME] = 0 else p[] = FlyingEnemyPos(vEnemiesF[x][y][ENEMY_PARAM], float(gx), float(gy), vEnemiesF[x][y][ENEMY_X1], vEnemiesF[x][y][ENEMY_Y1], vEnemiesF[x][y][ENEMY_X2], vEnemiesF[x][y][ENEMY_Y2]) vEnemies[x][y][ENEMY_X] = p[0] + 4 vEnemies[x][y][ENEMY_Y] = p[1] + 6 vEnemies[x][y][ENEMY_BASE_FRAME] = FlyingEnemyDir(vEnemiesF[x][y][ENEMY_PARAM], float(gx), float(gy), vEnemiesF[x][y][ENEMY_X1], vEnemiesF[x][y][ENEMY_Y1], vEnemiesF[x][y][ENEMY_X2], vEnemiesF[x][y][ENEMY_Y2]) endif else vEnemies[x][y][ENEMY_X] = gx + 2 vEnemies[x][y][ENEMY_Y] = gy + 6 endif if ImageCol(vEnemies[x][y][ENEMY_TYPE] + ENEMY_IMAGE_BASE, vEnemies[x][y][ENEMY_X], vEnemies[x][y][ENEMY_Y], PLAYER_IMAGE, vPlayerX, vPlayerY) proc LoseLife endif rem Hit by player bullet? if vPlayerBulletType > 0 if ImageCol(vEnemies[x][y][ENEMY_TYPE] + ENEMY_IMAGE_BASE, vEnemies[x][y][ENEMY_X], vEnemies[x][y][ENEMY_Y], PLAYER_BULLETS_IMAGE, vPlayerBulletX, vPlayerBulletY) vPlayerBulletType = 0 vEnemies[x][y][ENEMY_STAMINA] = vEnemies[x][y][ENEMY_STAMINA] - 1 if vEnemies[x][y][ENEMY_STAMINA] = 0 img = ENEMY_IMAGE_BASE + vEnemies[x][y][ENEMY_TYPE] proc AddExplosion vEnemies[x][y][ENEMY_X] + width(img)/2, vEnemies[x][y][ENEMY_Y] + height(img)/2 if vEnemies[x][y][ENEMY_STATE] = 1 vScore = vScore + 20*vEnemies[x][y][ENEMY_TYPE] else vScore = vScore + 10*vEnemies[x][y][ENEMY_TYPE] endif vEnemies[x][y][ENEMY_TYPE] = 0 vEnemyCount = vEnemyCount - 1 else play sound ENEMY_HIT_SOUND endif endif endif endif next next endproc rem ================================================================== rem Return true if two image at specific locations intersect. rem ================================================================== function ImageCol(img0, x0, y0, img1, x1, y1) if x0 + width(img0) < x1 then return false if y0 + height(img0) < y1 then return false if x1 + width(img1) < x0 then return false if y1 + height(img1) < y0 then return false return true endfunc rem ================================================================== rem Calculate position for an attacking enemy. It's a special case of rem a cubic bezier curve, where the start and end point are the same. rem ================================================================== function FlyingEnemyPos[](param#, x#, y#, x1#, y1#, x2#, y2#) param = max#(param, 0.0) param = min#(param, 1.0) b0# = 1.0 - param b0 = b0*b0*b0 b1# = 1.0 - param b1 = b1*b1 b1 = b1*3.0*param b2# = 3.0*(1.0 - param)*param*param b3# = param*param*param return [int(b0*x + b1*x1 + b2*x2 + b3*x), int(b0*y + b1*y1 + b2*y2 + b3*y)] endfunc rem ================================================================== rem Return direction for enemy (a base frame for its image grid). rem Think I've messed up the code a little here ... never mind. rem ================================================================== function FlyingEnemyDir(param#, x#, y#, x1#, y1#, x2#, y2#) b0# = 1.0 - param b0 = -3.0*b0*b0 b1# = 1.0 - param b1 = 3.0*b1 - 6.0*param*b1 b2# = 6.0*param*(1.0 - param) - 3.0*param*param b3# = 3.0*param*param dx# = b0*x + b1*x1 + b2*x2 + b3*x dy# = b0*y + b1*y1 + b2*y2 + b3*y k# = 1.0/sqr(dx*dx + dy*dy) dx = dx*k dy = dy*k if dy <= 0.0 if abs#(dx) < 0.25 ret = 0 else if dx < 0.0 ret = 8 else ret = 10 endif endif else if abs#(dx) < 0.25 ret = 2 else if dx < 0.0 ret = 6 else ret = 4 endif endif endif return ret endproc rem ================================================================== rem Draw enemies. rem ================================================================== procedure DrawEnemies() for y = 0 to GRID_HEIGHT - 1 for x = 0 to GRID_WIDTH - 1 if vEnemies[x][y][ENEMY_TYPE] > 0 set color 255, 255, 255 draw image ENEMY_IMAGE_BASE + vEnemies[x][y][ENEMY_TYPE], vEnemies[x][y][ENEMY_X], vEnemies[x][y][ENEMY_Y], vEnemies[x][y][ENEMY_BASE_FRAME] + vEnemies[x][y][ENEMY_FRAME]/30 endif next next endproc rem ================================================================== rem Init frame hold rem ================================================================== procedure InitFrameHold(speed) vNextFrameTime = time() vFrameHold = 1000/speed endproc rem ================================================================== rem Hold frame to maintain constant speed. rem ================================================================== procedure HoldFrame() while time() < vNextFrameTime; wait 1; wend vNextFrameTime = vNextFrameTime + vFrameHold endproc