Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1689 lines
39 KiB

//
// 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);