// // Bomber By Kazys Stepanas // //-------------------------------------------------------------------------------------------------- // Globals. //-------------------------------------------------------------------------------------------------- global bgCol = CA.B_BLACK; global quit = false; global lastTouchedBlock = -1; global player; global gameLevel = 1; global blocks = table(count = 0); global powerups = table(count = 0); global enemies = table(count = 0); global blockDeadZone = table(x1 = 31, y1 = 8, x2 = 39, y2 = 14); global enemyDeadZone = table(x1 = 22, y1 = 7, x2 = 45, y2 = 16); // Thing types. global T_Space = 0; global T_Player = 1; global T_Bomb = 2; global T_Explosion = 3; global T_Block = 4; global T_Grid = 5; global T_Powerup = 6; global T_Enemy = 7; // Powerup types. global PT_Bomb = 1; global PT_Size = 2; global PT_Life = 3; // Dead zone types. global DT_None = 0; global DT_Block = 1; global DT_Enemy = 2; //-------------------------------------------------------------------------------------------------- // Grid control. //-------------------------------------------------------------------------------------------------- global grid = table(startX = 0, startY = 0, spaceX = 1, spaceY = 1, rows = 0, cols = 0); //-------------------------------------------------------------------------------------------------- // Constants //-------------------------------------------------------------------------------------------------- global screen = table(w = 80, h = 23); global statusY = 24; global bombFuse = 1.25; global explodeTime = 0.75; global explodeCycle = 0.05; global maxPlayerBombs = 10; global maxPlayerSize = 5; global maxPowerups = 20; global maxLives = 5; global startLives = 3; global scoreBlock = 10; global scoreNME = 50; global nmeMoveTime = 1.0; global powerupChance = 40; global maxLevel = 10; global maxBlockHits = 3; //-------------------------------------------------------------------------------------------------- // Graphics //-------------------------------------------------------------------------------------------------- global playerPix = "\2"; global playerDeadPix = "\5"; global bombPix = "\15"; global expPix = "+"; global gridPix = "\178"; global block1Pix = "\176"; global block2Pix = "\177"; global block3Pix = "\219"; global nmePix = "\232"; global lifePix = "\3"; global bombSizePix = "#"; global bombExtraPix = "*"; global spacePix = " "; //-------------------------------------------------------------------------------------------------- // Colours //-------------------------------------------------------------------------------------------------- global bgColour = 0; global fgColour = CA.F_RED | CA.F_GREEN; global playerColour = CA.F_GREEN | CA.F_BLUE;// | CA.F_INTENSITY; global gridColour = CA.F_RED; global blockColour = CA.F_RED | CA.F_GREEN | CA.F_INTENSITY; global nmeColour = CA.F_RED | CA.F_BLUE; global powerupColour = CA.F_GREEN | CA.F_INTENSITY; global puLifeColour = CA.F_RED; global statusColour = CA.F_RED | CA.F_GREEN; global bombColour = CA.F_BLUE | CA.F_INTENSITY; global expColourCount = 5; global expColours = array(expColourCount); global readyColour = CA.F_RED; expColours[0] = CA.F_RED | CA.F_INTENSITY; expColours[1] = CA.F_BLUE|CA.F_RED; expColours[2] = CA.F_RED|CA.F_GREEN | CA.F_INTENSITY; expColours[3] = CA.F_RED; expColours[4] = CA.F_RED|CA.F_GREEN; //-------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------- global IsPointWithin = function(a_ptx, a_pty, a_rx1, a_ry1, a_rx2, a_ry2) { return (a_ptx >= a_rx1 and a_ptx <= a_rx2 and a_pty >= a_ry1 and a_pty <= a_ry2); }; global OverlapRect = function(a_r1x1, a_r1y1, a_r1x2, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2) { // Test one rect against the other. if (IsPointWithin(a_r2x1, a_r2y1, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or IsPointWithin(a_r2x1, a_r2y2, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or IsPointWithin(a_r2x2, a_r2y2, a_r1x1, a_r1y1, a_r1x2, a_r1y2) or IsPointWithin(a_r2x2, a_r2y1, a_r1x1, a_r1y1, a_r1x2, a_r1y2)) { return true; } // Test the other rect. if (IsPointWithin(a_r1x1, a_r1y1, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or IsPointWithin(a_r1x1, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or IsPointWithin(a_r1x2, a_r1y2, a_r2x1, a_r2y1, a_r2x2, a_r2y2) or IsPointWithin(a_r1x2, a_r1y1, a_r2x1, a_r2y1, a_r2x2, a_r2y2)) { return true; } return false; }; global ScreenClampX = function(a_x) { if (a_x < 0) { return 0; } else if (a_x >= screen.w) { return screen.w-1; } return a_x; }; global ScreenClampY = function(a_y) { if (a_y < 0) { return 0; } else if (a_y >= screen.h) { return screen.h-1; } return a_y; }; global ScreenClamp = function() { .x = ScreenClampX(.x); .y = ScreenClampY(.y); }; global BombAt = function(a_x, a_y) { for (i = 0; i < player.nextBomb; i=i+1) { bomb = player.bombs[i]; if (bomb.x == a_x and bomb.y == a_y) { return true; } } return false; }; global PointOverlap = function(a_x1, a_y1, a_x2, a_y2) { return IsPointWithin(.x, .y, a_x1, a_y1, a_x2, a_y2); }; //-------------------------------------------------------------------------------------------------- // Player object. //-------------------------------------------------------------------------------------------------- global Player = function() { newPlayer = table(x = -1, y = -1); newPlayer.bombSize = 1; newPlayer.bombFuse = bombFuse; newPlayer.bombs = array(maxPlayerBombs); newPlayer.maxBombs = 1; newPlayer.nextBomb = 0; newPlayer.threadId = -1; newPlayer.lastCanMoveThing = null; newPlayer.dead = false; newPlayer.lives = startLives; newPlayer.score = 0; newPlayer.threadId = -1; //------------------------------------ //------------------------------------ newPlayer.Draw = function() { CATTRIB(bgColour | playerColour); if (!.dead) { XYTEXT(.x, .y, playerPix); } else { XYTEXT(.x, .y, playerDeadPix); } }; //------------------------------------ //------------------------------------ newPlayer.DrawStatus = function() { CATTRIB(bgColour | puLifeColour | CA.F_INTENSITY); XYTEXT(0, statusY, format("%s : %d ", lifePix, .lives)); CATTRIB(bgColour | powerupColour); XYTEXT(10, statusY, format("%s : %d ", bombExtraPix, .maxBombs)); XYTEXT(20, statusY, format("%s : %d ", bombSizePix, .bombSize)); CATTRIB(bgColour | statusColour); XYTEXT(50, statusY, format("Score : %d ", .score)); }; newPlayer.AddScore = function(a_add) { .score = .score + a_add; .DrawStatus(); }; //------------------------------------ //------------------------------------ newPlayer.CanMove = function(a_x, a_y) { thing = GetThingAt(a_x, a_y, false); .lastCanMoveThing = thing; return thing.type == T_Space or thing.type == T_Explosion or thing.type == T_Powerup or thing.type == T_Enemy; }; //------------------------------------ //------------------------------------ newPlayer.Move = function(a_dx, a_dy) { if (!.dead) { oldX = .x; oldY = .y; newX = ScreenClampX(.x + a_dx); newY = ScreenClampY(.y + a_dy); if (.CanMove(newX, newY)) { .x = newX; .y = newY; if (.lastCanMoveThing.type == T_Powerup) { .lastCanMoveThing.thing.Pickup(); } else if (.lastCanMoveThing.type == T_Explosion or .lastCanMoveThing.type == T_Enemy) { .Kill(); } RefreshXY(oldX, oldY); RefreshXY(.x, .y); } } }; //------------------------------------ //------------------------------------ newPlayer.Kill = function() { .dead = true; RefreshXY(.x, .y); }; //------------------------------------ //------------------------------------ newPlayer.DropBomb = function() { if (!.dead and .nextBomb < .maxBombs and !BombAt(.x, .y)) { newBomb = Bomb(.x, .y, .bombSize, .bombFuse, this); .bombs[.nextBomb] = newBomb; .nextBomb = .nextBomb + 1; newBomb.Draw(); } }; //------------------------------------ //------------------------------------ newPlayer.FreeBomb = function(a_bomb) { found = false; limit = .nextBomb; for (i = 0; i < limit; i=i+1) { if (.bombs[i] == a_bomb) { found = true; .nextBomb = .nextBomb - 1; } if (found) { if (i+1 < limit) { .bombs[i] = .bombs[i+1]; } else { .bombs[i] = null; } } } }; //------------------------------------ //------------------------------------ newPlayer.StartThread = function() { this:stateSet(.IdleState); }; //------------------------------------ //------------------------------------ newPlayer.IdleState = function() { while (!quit) { pressed = false; if (ISPRESSED(38)) { .Move(0, -1); pressed = true; } else if (ISPRESSED(40)) { .Move(0, 1); pressed = true; } else if (ISPRESSED(39)) { .Move(1, 0); pressed = true; } else if (ISPRESSED(37)) { .Move(-1, 0); pressed = true; } else if (ISPRESSED(' ')) { .DropBomb(); pressed = true; } if (pressed) { sleep(0.075); } else if (.dead) { this:stateSet(.DeadState); } else { yield(); } } }; //------------------------------------ //------------------------------------ newPlayer.DeadState = function() { sleep(explodeTime); if (.lives > 0) { .lives = .lives - 1; } if (.maxBombs > 1) { .maxBombs = .maxBombs - 1; } if (.bombSize > 1) { .bombSize = .bombSize - 1; } .DrawStatus(); while (!quit) { if (.lives > 0 and ISPRESSED(' ')) { while (!quit) { pos = .GetStartPos(); thing = GetThingAt(pos.x, pos.y, false); if (thing.type == T_Enemy) { thing.thing.Kill(); } sleep(0.2); oldX = .x; oldY = .y; .x = pos.x; .y = pos.y; .dead = false; RefreshXY(oldX, oldY); RefreshXY(.x, .y); this:stateSet(.IdleState); } } yield(); } }; //------------------------------------ //------------------------------------ newPlayer.Overlap = PointOverlap; //------------------------------------ //------------------------------------ newPlayer.Cheat = function() { .lives = 5; .maxBombs = 10; .bombSize = 5; .DrawStatus(); }; newPlayer.GetStartPos = function() { // Position near centre. ptx = grid.startX + (grid.rows/2)*grid.spaceX; pty = grid.startY + (grid.cols/2)*grid.spaceY; return table(x = ptx, y = pty); }; pos = newPlayer.GetStartPos(); newPlayer.x = pos.x; newPlayer.y = pos.y; newPlayer.Start = function() { .threadId = this:thread(.StartThread); }; newPlayer.Stop = function() { threadKill(.threadId); }; return newPlayer; }; //-------------------------------------------------------------------------------------------------- // Bomb object. //-------------------------------------------------------------------------------------------------- global Bomb = function(a_x, a_y, a_size, a_fuse, a_owner) { newBomb = table(x = a_x, y = a_y, size = a_size, fuse = a_fuse, owner = a_owner); newBomb.explode = false; newBomb.expMinX = 0; newBomb.expMaxX = 0; newBomb.expMinY = 0; newBomb.expMaxY = 0; newBomb.visible = true; newBomb.threadId = -1; newBomb.CountDown = function() { this:stateSet(.FuseState); }; newBomb.Explode = function() { this:stateSetOnThread(.threadId, .ExplodeState); }; newBomb.FuseState = function() { sleep(.fuse); this:stateSet(.ExplodeState); }; newBomb.ExplodeState = function() { .expMinX = ScreenClampX(.x - .size); .expMaxX = ScreenClampX(.x + .size); .expMinY = ScreenClampY(.y - .size); .expMaxY = ScreenClampY(.y + .size); .explode = true; .visible = false; .KillStuff(); .visible = true; .Draw(); count = 1+g_count; inc = explodeCycle/explodeTime; for (i = 0; i < explodeTime; i = i + inc) { sleep(explodeCycle); .Draw(); } .visible = false; RefreshRect(.expMinX, .expMinY, .expMaxX - .expMinX + 1, .expMaxY - .expMinY + 1); if (.owner != null) { .owner.FreeBomb(this); } exit(); }; newBomb.KillStuff = function() { // Kill stuff. for (x = .x; x >= .expMinX; x=x-1) { if (!.TryKill(GetThingAt(x, .y, true))) { .expMinX = x; break; } } for (x = .x+1; x <= .expMaxX; x=x+1) { if (!.TryKill(GetThingAt(x, .y, true))) { .expMaxX = x; break; } } for (y = .y; y >= .expMinY; y=y-1) { if (!.TryKill(GetThingAt(.x, y, true))) { .expMinY = y; break; } } for (y = .y+1; y <= .expMaxY; y=y+1) { if (!.TryKill(GetThingAt(.x, y, true))) { .expMaxY = y; break; } } }; newBomb.TryKill = function(a_thing) { if (a_thing.type == T_Block) { a_thing.thing.Kill(); return false; } else if (a_thing.type == T_Bomb and a_thing.thing != this) { a_thing.thing.Explode(); } else if (a_thing.type == T_Player or a_thing.type == T_Enemy) { a_thing.thing.Kill(); } return true; }; newBomb.Draw = function() { if (.visible) { if (.explode) { CATTRIB(bgColour | expColours[randint(0, expColourCount)]); for (x = .expMinX; x <= .expMaxX; x=x+1) { if (IsSpaceOnGrid(x, .y)) { XYTEXT(x, .y, expPix); } } for (y = .expMinY; y <= .expMaxY; y=y+1) { if (IsSpaceOnGrid(.x, y)) { XYTEXT(.x, y, expPix); } } } else { CATTRIB(bgColour | bombColour); XYTEXT(.x, .y, bombPix); } } }; newBomb.Overlap = function(a_x1, a_y1, a_x2, a_y2) { if (.explode) { return OverlapRect(.expMinX, .y, .expMaxX, .y+1, a_x1, a_y1, a_x2, a_y2) and OverlapRect(.x, .expMinY, .x+1, .expMaxY, a_x1, a_y1, a_x2, a_y2); } return IsPointWithin(.x, .y, a_x1, a_y1, a_x2, a_y2); }; newBomb.FuseThread = function() { sleep(.fuse); .Explode(); }; newBomb.threadId = newBomb:thread(newBomb.CountDown); newBomb:thread(newBomb.FuseThread); return newBomb; }; //-------------------------------------------------------------------------------------------------- // Block code. //-------------------------------------------------------------------------------------------------- global Block = function(a_x, a_y) { newBlock = table(x = a_x, y = a_y, visible = true); newBlock.hits = 1; newBlock.Draw = function() { if (.visible) { CATTRIB(bgColour | blockColour); if (.hits == 3) { XYTEXT(.x, .y, block3Pix); } else if (.hits == 2) { XYTEXT(.x, .y, block2Pix); } else { XYTEXT(.x, .y, block1Pix); } } }; newBlock.Overlap = PointOverlap; newBlock.KillThread = function() { yield(); yield(); if (.visible) { .visible = false; player.AddScore(scoreBlock); if (randint(0, 100) < powerupChance) { AddPowerup(.x, .y); } } }; newBlock.Kill = function() { .hits = .hits - 1; if (.hits == 0) { this:thread(.KillThread); } RefreshXY(.x, .y); }; return newBlock; }; global CreateBlocks = function(a_num) { global maxBlockHits; global blocks = table(count = 0, blocks = array(a_num)); for (i = 0; i < a_num; i=i+1) { pos = GetRandomFreePos(DT_Block, true); if (pos.x != -1 and pos.y != -1) { thing = GetThingAt(pos.x, pos.y, false); if (thing.type == DT_Block) { if (thing.thing.hits < maxBlockHits) { thing.thing.hits = thing.thing.hits + 1; } } else { blocks.blocks[i] = Block(pos.x, pos.y); blocks.count = i; } } } blocks.count = a_num; }; //-------------------------------------------------------------------------------------------------- // Powerup. //-------------------------------------------------------------------------------------------------- global Powerup = function(a_x, a_y, a_type) { newPu = table(x = a_x, y = a_y, type = a_type); if (a_type == PT_Bomb) { newPu.Draw = function() { CATTRIB(bgColour | powerupColour); XYTEXT(.x, .y, bombExtraPix); }; newPu.Pickup = function() { if (player.maxBombs < maxPlayerBombs) { player.maxBombs = player.maxBombs + 1; } player.DrawStatus(); RemovePowerup(this); }; } else if (a_type == PT_Size) { newPu.Draw = function() { CATTRIB(bgColour | powerupColour); XYTEXT(.x, .y, bombSizePix); }; newPu.Pickup = function() { if (player.bombSize < maxPlayerSize) { player.bombSize = player.bombSize + 1; } player.DrawStatus(); RemovePowerup(this); }; } else if (a_type == PT_Life) { newPu.Draw = function() { CATTRIB(bgColour | puLifeColour); XYTEXT(.x, .y, lifePix); }; newPu.Pickup = function() { if (player.lives < maxLives) { player.lives = player.lives + 1; } player.DrawStatus(); RemovePowerup(this); }; } else { newPu.Draw = function() { }; newPu.Pickup = function() { RemovePowerup(this); }; } newPu.Overlap = PointOverlap; return newPu; }; //-------------------------------------------------------------------------------------------------- // Powerup management. //-------------------------------------------------------------------------------------------------- global RandomPowerupType = function() { int = randint(0, 100); if (int < 49) { return PT_Bomb; } if (int < 98) { return PT_Size; } return PT_Life; }; global AddPowerup = function(a_x, a_y) { if (powerups.count < maxPowerups) { powerups.powerups[powerups.count] = Powerup(a_x, a_y, RandomPowerupType()); powerups.count = powerups.count + 1; RefreshXY(a_x, a_y); } }; global RemovePowerup = function(a_pu) { found = false; count = powerups.count; for (i = 0; i < count; i = i + 1) { if (powerups.powerups[i] == a_pu) { found = true; powerups.powerups[i] = null; powerups.count = powerups.count - 1; } if (found) { if (i+1 < count) { powerups.powerups[i] = powerups.powerups[i+1]; } else { powerups.powerups[i] = null; } } } }; global InitPowerups = function() { global powerups = table(count = 0, powerups = array(maxPowerups)); }; //-------------------------------------------------------------------------------------------------- // Enemies. //-------------------------------------------------------------------------------------------------- global CreateEnemies = function(a_num) { global enemies = table(count = 0, enemies = array(a_num)); for (i = 0; i < a_num; i=i+1) { pos = GetRandomFreePos(DT_Enemy, false); if (pos.x != -1 and pos.y != -1) { enemies.enemies[i] = Enemy(pos.x, pos.y); enemies.count = i+1; } } }; global Enemy = function(a_x, a_y) { newEnemy = table(x = a_x, y = a_y, dead = false); newEnemy.lastCanMoveThing = null; newEnemy.lastPos = table(x = a_x, y = a_y); newEnemy.threadId = -1; //------------------------------------ //------------------------------------ newEnemy.Draw = function() { if (!.dead) { CATTRIB(bgColour | nmeColour); XYTEXT(.x, .y, nmePix); } }; //------------------------------------ //------------------------------------ newEnemy.Kill = function() { player.AddScore(scoreNME); .dead = true; if (randint(0, 100) < powerupChance) { AddPowerup(.x, .y); } RefreshXY(.x, .y); }; //------------------------------------ //------------------------------------ newEnemy.Overlap = PointOverlap; //------------------------------------ // Movement. //------------------------------------ newEnemy.CanMove = function(a_x, a_y) { if (a_x < 0 or a_x >= screen.w or a_y < 0 or a_y > screen.h) { return false; } thing = GetThingAt(a_x, a_y, false); .lastCanMoveThing = thing; return thing.type == T_Space or thing.type == T_Exlposion or thing.type == T_Powerup or thing.type == T_Player; }; //------------------------------------ //------------------------------------ newEnemy.MoveX = function() { // Is the player on the same column? if (.y == player.y) { // Are we within a grid spacing? if (player.x - .x <= grid.spaceX + 1) { if (.x < player.x) { start = .x+1; end = player.x+1; moveX = start; } else { start = player.x+1; end = .x; moveX = end-1; } // Do we have a clear path. moveOk = true; for (x = start; moveOk and x < end; x=x+1) { if (!.CanMove(x, .y)) { moveOk = false; } } if (moveOk and .CanMove(moveX, .y)) { .MoveTo(moveX, .y); return true; } } } mx = 1; if (player.x < .x) { mx = -1; } if ((.x+mx != .lastPos.x or .y != .lastPos.y) and .CanMove(.x+mx, .y)) { .MoveTo(.x+mx, .y); return true; } return false; }; //------------------------------------ //------------------------------------ newEnemy.MoveY = function() { // Is the player on the same row? if (.x == player.x) { // Are we within a grid spacing? if (player.y - .y <= grid.spaceY + 1) { if (.y < player.y) { start = .y+1; end = player.y+1; moveY = start; } else { start = player.y+1; end = .y; moveY = end-1; } // Do we have a clear path. moveOk = true; for (y = start; moveOk and y < end; y=y+1) { if (!.CanMove(.x, y)) { moveOk = false; } } if (moveOk and .CanMove(.x, moveY)) { .MoveTo(.x, moveY); return true; } } } my = 1; if (player.y < .y) { my = -1; } if ((.x != .lastPos.x or .y+my != .lastPos.y) and .CanMove(.x, .y+my)) { .MoveTo(.x, .y+my); return true; } return false; }; //------------------------------------ //------------------------------------ newEnemy.MoveTo = function(a_x, a_y) { .lastPos.x = .x; .lastPos.y = .y; thing = GetThingAt(a_x, a_y, false); .x = a_x; .y = a_y; if (thing.type == T_Player) { player.Kill(); } else if (thing.type == T_Explosion) { .Kill(); } RefreshXY(.lastPos.x, .lastPos.y); RefreshXY(.x, .y); }; //------------------------------------ //------------------------------------ newEnemy.Move = function() { moved = false; if (!player.dead) { // Try move towards the player. preferX = abs(player.x - .x) > abs(player.y - .y); if (preferX) { moved = .MoveX(); if (!moved) { moved = .MoveY(); } } else { moved = .MoveY(); if (!moved) { moved = .MoveX(); } } // Try move away from the last position. if (!moved) { testX = 2*.x - .lastPos.x; testY = 2*.y - .lastPos.y; if (.CanMove(testX, testY)) { .MoveTo(testX, testY); moved = true; } } } if (!moved and (.lastPos.x != .x or .lastPos.y != .y) and .CanMove(.lastPos.x, .lastPos.y)) { .MoveTo(.lastPos.x, .lastPos.y); } }; //------------------------------------ // Thread functions and states. //------------------------------------ newEnemy.Start = function() { .threadId = this:thread(.IdleState); }; //------------------------------------ //------------------------------------ newEnemy.IdleState = function() { while (!quit and !.dead) { sleep(nmeMoveTime); .Move(); } }; newEnemy.Start(); return newEnemy; }; //-------------------------------------------------------------------------------------------------- // Refresh functions. //-------------------------------------------------------------------------------------------------- global Refresh = function() { CATTRIB(bgColour | fgColour); CLS(); // Draw blocks. for (b = 0; b < blocks.count; b=b+1) { blocks.blocks[b].Draw(); } // Draw powerups. for (p = 0; p < powerups.count; p=p+1) { powerups.powerups[p].Draw(); } // Draw enemies. for (e = 0; e < enemies.count; e=e+1) { enemies.enemies[e].Draw(); } // Draw bombs. for (b = 0; b < player.nextBomb; b=b+1) { player.bombs[b].Draw(); } player.Draw(); player.DrawStatus(); DrawGrid(); }; global RefreshXY = function(a_x, a_y) { RefreshRect(a_x, a_y, 1, 1); }; global RefreshRect = function(a_x, a_y, a_w, a_h) { // Clear the area. xLimit = a_x + a_w; yLimit = a_y + a_h; CATTRIB(bgColour | fgColour); for (x = a_x; x < xLimit; x=x+1) { for (y = a_y; y < yLimit; y=y+1) { XYTEXT(x, y, spacePix); } } // Draw blocks. for (b = 0; b < blocks.count; b=b+1) { blk = blocks.blocks[b]; if (blk.Overlap(a_x, a_y, xLimit, yLimit)) { blk.Draw(); } } // Draw powerups. for (p = 0; p < powerups.count; p=p+1) { pu = powerups.powerups[p]; if (pu.Overlap(a_x, a_y, xLimit, yLimit)) { pu.Draw(); } } // Draw enemies. for (e = 0; e < enemies.count; e=e+1) { nme = enemies.enemies[e]; if (nme.Overlap(a_x, a_y, xLimit, yLimit)) { nme.Draw(); } } // Draw bombs. for (b = 0; b < player.nextBomb; b=b+1) { bomb = player.bombs[b]; if (bomb.Overlap(a_x, a_y, xLimit, yLimit)) { bomb.Draw(); } } if (player.Overlap(a_x, a_y, xLimit, yLimit)) { player.Draw(); } DrawGridRect(a_x, a_y, a_w, a_h); }; //-------------------------------------------------------------------------------------------------- // Grid control. //-------------------------------------------------------------------------------------------------- global Grid = function(a_startX, a_startY, a_spaceX, a_spaceY) { grid.startX = a_startX; grid.startY = a_startY; grid.spaceX = a_spaceX; grid.spaceY = a_spaceY; grid.rows = (screen.w-grid.startX) / (grid.spaceX + 1); grid.cols = (screen.h-grid.startY) / (grid.spaceY + 1); if ((screen.w-grid.startX) % (grid.spaceX + 1) != 0) { grid.rows = grid.rows + 1; } if ((screen.h-grid.startY) % (grid.spaceY + 1) != 0) { grid.cols = grid.cols + 1; } }; global DrawGrid = function() { DrawGridRect(0, 0, screen.w, screen.h); }; global DrawGridRect = function(a_startX, a_startY, a_w, a_h) { xLimit = a_startX + a_w; yLimit = a_startY + a_h; CATTRIB(bgColour | gridColour); for (x = a_startX; x < xLimit; x=x+1) { for (y = a_startY; y < yLimit; y=y+1) { if (!IsSpaceOnGrid(x, y)) { XYTEXT(x, y, gridPix); } } } }; global IsSpaceOnGrid = function(a_x, a_y) { return (a_x - grid.startX) % (grid.spaceX+1) == 0 or (a_y - grid.startY) % (grid.spaceY+1) == 0; }; global IsFreePos = function(a_x, a_y, a_allowBlocks) { thing = GetThingAt(a_x, a_y, true); return thing.type == T_Space or (a_allowBlocks and thing.type == T_Block); }; global GetThingAt = function(a_x, a_y, a_ignoreExplosion) { if (!IsSpaceOnGrid(a_x, a_y)) { return table(thing = grid, type = T_Grid); } // Test blocks for (b = 0; b < blocks.count; b=b+1) { blk = blocks.blocks[b]; if (blk.visible and blk.x == a_x and blk.y == a_y) { return table(thing = blk, type = T_Block); } } // Test enemies. for (e = 0; e < enemies.count; e=e+1) { nme = enemies.enemies[e]; if (!nme.dead and nme.x == a_x and nme.y == a_y) { return table(thing = nme, type = T_Enemy); } } // Test player pos. if (!player.dead and a_x == player.x and a_y == player.y) { return table(thing = player, type = T_Player); } // Test bombs. for (b = 0; b < player.nextBomb; b=b+1) { bomb = player.bombs[b]; if (!bomb.explode) { if (bomb.x == a_x and bomb.y == a_y) { return table(thing = bomb, type = T_Bomb); } } else if (!a_ignoreExplosion and bomb.Overlap(a_x, a_y, a_x+1, a_y+1)) { return table(thing = bomb, type = T_Explosion); } } // Test powerups. for (p = 0; p < powerups.count; p=p+1) { pu = powerups.powerups[p]; if (pu.x == a_x and pu.y == a_y) { return table(thing = pu, type = T_Powerup); } } return table(thing = null, type = T_Space); }; global InDeadZone = function(a_dtType, a_x, a_y) { if (a_dtType == DT_Block) { return IsPointWithin(a_x, a_y, blockDeadZone.x1, blockDeadZone.y1, blockDeadZone.x2, blockDeadZone.y2); } if (a_dtType == DT_Enemy) { return IsPointWithin(a_x, a_y, enemyDeadZone.x1, enemyDeadZone.y1, enemyDeadZone.x2, enemyDeadZone.y2); } return false; }; global GetRandomFreePos1 = function(a_deadZoneType, a_allowBlocks, a_tries) { row = randint(0, grid.rows); col = randint(0, grid.cols); px = row*grid.spaceX + grid.startX; py = col*grid.spaceY + grid.startY; if (a_deadZoneType != DT_None and InDeadZone(a_deadZoneType, px, py)) { return GetRandomFreePos1(a_deadZoneType, a_tries); } if (IsFreePos(px, py)) { return table(x = px, y = py); } while (py < screen.h) { py=py+1; if ((a_deadZoneType == DT_None or !InDeadZone(a_deadZoneType, px, py)) and IsFreePos(px, py)) { return table(x = px, y = py); } } if (tries < 5) { return GetRandomFreePos1(a_tries+1); } return table(x = -1, y = -1); }; global GetRandomFreePos = function(a_deadZoneType, a_allowBlocks) { return GetRandomFreePos1(a_deadZoneType, a_allowBlocks, 0); }; //-------------------------------------------------------------------------------------------------- // Initialisation. //-------------------------------------------------------------------------------------------------- RenderRect = function(a_rect) { CATTRIB(bgColour | gridColour); for (i = a_rect.x1; i <= a_rect.x2; i=i+1) { for (j = a_rect.y1; j <= a_rect.y2; j=j+1) { XYTEXT(i, j, "o"); } } }; MemThread = function() { while (!quit) { CATTRIB(bgColour | statusColour); XYTEXT(65, statusY, format("Mem: %d ", sysGetMemoryUsage())); sleep(1.0); } }; //-------------------------------------------------------------------------------------------------- // Level thread. //-------------------------------------------------------------------------------------------------- global GameOver = function() { CATTRIB(bgColour | statusColour); CLS(); local score = player.score; KillAll(); sleep(0.3); global quit; while (!quit) { CATTRIB(bgColour | expColours[randint(0, expColourCount)]); x = 8; y = 8; XYTEXT(x, y, "__________________________________________________________"); y=y+1; XYTEXT(x, y, " __ __ "); y=y+1; XYTEXT(x, y, " / ) / ) "); y=y+1; XYTEXT(x, y, " / __ _ _ __ / / __ )__ "); y=y+1; XYTEXT(x, y, " / --, / ) / / ) /___) / / | / /___) / )"); y=y+1; XYTEXT(x, y, "_(____/___(___(_/_/__/_(___ ____(____/____|/__(___ _/_____"); y=y+1; CATTRIB(bgColour | statusColour); XYTEXT(0, statusY, format("Score : %d", score)); if (threadTime() < 1000 and ISPRESSED(' ')) { stateSet(TitleScreenState); } else if (ISPRESSED(27)) { quit = true; } sleep(randfloat(0.03, 0.3)); } global player = null; global blocks = null; global enemies = null; stateSet(TitleScreenState); }; global GameState = function() { global maxLevel; global player; global blocks; global enemies; global gameLevel; Grid(3, 2, 5, 5); if (player == null or player.lives <= 0) { player = Player(); } pos = player.GetStartPos(); player.x = pos.x; player.y = pos.y; global nmeMoveTime = 1.0 - gameLevel*0.1; InitPowerups(); CreateBlocks(min(10 + 10*gameLevel, 99)); CreateEnemies(5*gameLevel); yield(); Refresh(); yield(); player.Start(); local done = false; while (!quit and !done) { local count; done = true; if (ISPRESSED(27)) { global quit = true; break; } /* // Check blocks. count = blocks.count; for (i = 0; done and i < count; i=i+1) { blk = blocks.blocks[i]; if (blk.visible) { done = false; } } */ // Check enemies. count = enemies.count; for (i = 0; done and i < count; i=i+1) { nme = enemies.enemies[i]; if (!nme.dead) { done = false; } } // Check bombs. done = done and player.nextBomb == 0; // Check for lives. done = done or player.lives <= 0; sleep(0.3); } player.Stop(); if (!quit) { if (player.lives <= 0) { stateSet(GameOver); } global gameLevel; gameLevel=gameLevel+1; stateSet(LevelState); } KillAll(); }; global LevelState = function() { global maxLevel; global gameLevel; if (gameLevel > maxLevel) { gameLevel = maxLevel; } CATTRIB(bgColour | statusColour); CLS(); sleep(0.3); global quit; while (!quit) { CATTRIB(bgColour | expColours[randint(0, expColourCount)]); x = 17; y = 8; XYTEXT(x, y, "___________________________________________"); y=y+1; XYTEXT(x, y, " ____ ___ "); y=y+1; XYTEXT(x, y, " / ) / ( ) "); y=y+1; XYTEXT(x, y, " /___ / __ __ __ / / "); y=y+1; XYTEXT(x, y, " / | /___) / ) / / / / / "); y=y+1; XYTEXT(x, y, "_/_____|__(___ _(___(_(___/___(___/__o_____"); y=y+1; XYTEXT(x, y, " / "); y=y+1; XYTEXT(x, y, " (_ / "); y=y+1; if (threadTime() > 1000 and ISPRESSED(' ')) { CLS(); sleep(0.5); stateSet(GameState); } else if (ISPRESSED(27)) { quit = true; } sleep(randfloat(0.03, 0.3)); } KillAll(); }; global KillAll = function() { // Kill all threads. global player; global enemies; global blocks; if (player) { local count = player.nextBomb; for (i = 0; i < count; i=i+1) { threadKill(player.bombs[i].threadId); } threadKill(player.threadId); } if (enemies) { count = enemies.count; for (i = 0; i < count; i=i+1) { threadKill(enemies.enemies[i].threadId); } } }; global TitleScreenState = function() { CATTRIB(bgColour | statusColour); CLS(); sleep(0.3); while (!quit) { CATTRIB(bgColour | expColours[randint(0, expColourCount)]); x = 17; y = 8; XYTEXT(x, y, "_________________________________________"); y=y+1; XYTEXT(x, y, " ____ "); y=y+1; XYTEXT(x, y, " / ) / "); y=y+1; XYTEXT(x, y, " /__ / __ _ _ /__ __ )__ "); y=y+1; XYTEXT(x, y, " / ) / ) / / ) / ) /___) / )"); y=y+1; XYTEXT(x, y, "_/____/___(___/_/_/__/_(___/_(___ _/_____"); y=y+1; XYTEXT(x, y, "_________________________________________"); y=y+1; if (ISPRESSED(27)) { global quit = true; break; } else if (ISPRESSED(' ')) { global gameLevel = 1; stateSet(LevelState); } sleep(randfloat(0.03, 0.3)); } KillAll(); exit(); }; CURSOR(0, 0); // bool visible, percentage visible CATTRIB(fgColour | bgColour); thread(MemThread); quit = false; CLS(); stateSet(TitleScreenState);