source-engine/vscript/languages/gm/scripts/Bomber.gm

1690 lines
39 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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