Merge pull request 'feat/TD-017-add-ghost-animation' (#16) from dev into main

Reviewed-on: #16
This commit is contained in:
gre-ilya 2026-02-28 17:24:23 +00:00
commit 9b00f9efb4
15 changed files with 117 additions and 109 deletions

View File

@ -2,29 +2,26 @@ unit arena_graphics_m;
interface
uses arena_m, creature_m, graphics_m, trace_m, level_m;
uses arena_m, creature_m, trace_m, level_m;
const
ArenaSymbol = ' ';
CaptureSymbol = '.';
procedure DrawAfterEnemyStep(var cr: creature; var a: arena);
procedure DrawAfterEnemyStep(var e: creature);
procedure DrawAfterHamsterStep(var h: creature; var t: tracePtr; var a: arena);
procedure DrawAliveEnemies(var e: creatureList);
procedure RedrawArea(var a: arena; arenaX, arenaY: integer);
procedure DrawArenaBorders(var a: arena);
procedure DrawCreature(var cr: creature);
procedure DrawFieldCell(arenaX, arenaY: integer; symbol: char);
procedure DrawCapturedCell(x, y: integer);
procedure DrawArenaEdges;
procedure DrawCompleteBar;
procedure DrawPause;
procedure DrawTrace(t: tracePtr);
procedure DrawHamster(var h: creature);
procedure FillCellsCapture(var a: arena);
procedure FillCompleteBar(s: integer);
procedure DrawSimpleCreature(var cr: creature);
procedure DrawArenaCell(x, y: integer; var a: arena);
procedure DrawInterface;
procedure DrawLevel(var level: levelState; life, score: integer);
procedure DrawLevelUnpause(var level: levelState);
procedure DrawLifesNumber(n: integer);
@ -32,11 +29,10 @@ procedure DrawScore(s: integer);
procedure EraseStepTrace(var hamster: creature; t: tracePtr);
procedure EraseLifesNumber(n: integer);
procedure EraseTrace(t: tracePtr; var a: arena);
procedure ErasePause;
implementation
uses ascii_arts_m, crt, math_m, hamster_m;
uses ascii_arts_m, crt, math_m, hamster_m, graphics_m;
const
ArenaPauseLowerMarginY = 14;
@ -93,6 +89,11 @@ begin
DrawFieldCell(cr.curX, cr.curY, cr.symbol)
end;
procedure DrawAfterEnemyStep(var e: creature);
begin
DrawCreature(e)
end;
procedure DrawAliveEnemies(var e: creatureList);
var
tmp: creatureItemPtr;
@ -101,19 +102,19 @@ begin
while tmp <> nil do
begin
if tmp^.cr^.alive then
DrawSimpleCreature(tmp^.cr^);
DrawAfterEnemyStep(tmp^.cr^);
tmp := tmp^.next
end
end;
procedure DrawAfterEnemyStep(var cr: creature; var a: arena);
procedure DrawFieldAscii(arenaX, arenaY, h, w: integer;
var a: array of string);
var
prevX, prevY: integer;
screenX, screenY: integer;
begin
prevX := cr.curX - cr.dX;
prevY := cr.curY - cr.dY;
DrawArenaCell(prevX, prevY, a);
DrawSimpleCreature(cr)
screenX := BorderSize + (arenaX - 1) * CellSize * WidthCoefficient;
screenY := InterfaceBarH + (arenaY - 1) * CellSize;
DrawAscii(screenX, screenY, h, a)
end;
procedure DrawStepTrace(t: tracePtr; hamsterDelta: integer);
@ -176,33 +177,32 @@ begin
HamsterHeight, BorderSymbol);
if x = InterfaceArenaCellX1 then
DrawLineY(InterfaceCellW * WidthCoefficient,
InterfaceBarH - HamsterHeight,
HamsterHeight, BorderSymbol);
InterfaceBarH - HamsterHeight, HamsterHeight, BorderSymbol);
if x = InterfaceArenaCellX2 then
DrawLineY(InterfaceCellW * 2 * WidthCoefficient + 1,
InterfaceBarH - HamsterHeight,
HamsterHeight, BorderSymbol);
InterfaceBarH - HamsterHeight, HamsterHeight, BorderSymbol);
if x = ArenaW then
DrawLineY(ArenaW * CellSize * WidthCoefficient,
InterfaceBarH - HamsterHeight,
HamsterHeight, BorderSymbol)
InterfaceBarH - HamsterHeight, HamsterHeight, BorderSymbol)
end;
procedure RedrawArea(var a: arena; arenaX, arenaY: integer);
var
i, j: integer;
begin
DrawArenaCell(arenaX, arenaY, a);
if arenaX - 1 > 0 then
DrawArenaCell(arenaX - 1, arenaY, a);
if arenaY - 1 > 0 then
DrawArenaCell(arenaX, arenaY - 1, a);
if arenaY - 2 > 0 then
DrawArenaCell(arenaX, arenaY - 2, a);
if (arenaX - 1 > 0) and (arenaY - 1 > 0) then
DrawArenaCell(arenaX - 1, arenaY - 1, a);
if (arenaX - 1 > 0) and (arenaY - 2 > 0) then
DrawArenaCell(arenaX - 1, arenaY - 2, a);
if arenaY = 1 then
RedrawInterfaceArea(arenaX)
for i := -1 to 1 do
begin
for j := -1 to 1 do
begin
if (arenaX + j > 0) and (arenaX + j < ArenaW + 1) and
(arenaY + i > 0) and (arenaY + i < ArenaH + 1) then
begin
if arenaY + i = 1 then
RedrawInterfaceArea(arenaX + j);
DrawArenaCell(arenaX + j, arenaY + i, a);
end
end
end
end;
procedure DrawAfterHamsterStep(var h: creature; var t: tracePtr; var a: arena);
@ -361,16 +361,6 @@ begin
GotoXY(1, 1)
end;
procedure DrawFieldAscii(arenaX, arenaY, h, w: integer;
var a: array of string);
var
screenX, screenY: integer;
begin
screenX := BorderSize + (arenaX - 1) * CellSize * WidthCoefficient;
screenY := InterfaceBarH + (arenaY - 1) * CellSize;
DrawAscii(screenX, screenY, h, a)
end;
procedure DrawArenaEdges;
begin
DrawRectangle(1, InterfaceBarH,
@ -487,7 +477,7 @@ procedure DrawHamsterRunX(var h: creature);
var
xIdx: integer;
begin
xIdx := h.curX div HamsterMovespeed mod HamsterRunNX + 1;
xIdx := h.curX div h.moveSpeed mod HamsterRunNX + 1;
if h.dX > 0 then
DrawFieldAscii(h.curX - HamsterWidth div WidthCoefficient div 2,
h.curY - HamsterHeight div 2,
@ -504,7 +494,7 @@ procedure DrawHamsterRunY(var h: creature);
var
yIdx: integer;
begin
yIdx := h.curY div HamsterMovespeed mod HamsterRunNY + 1;
yIdx := h.curY div h.moveSpeed mod HamsterRunNY + 1;
if h.dY > 0 then
DrawFieldAscii(h.curX - HamsterWidth div WidthCoefficient div 2,
h.curY - HamsterHeight div 2,
@ -530,4 +520,21 @@ begin
DrawHamsterRunY(h);
end;
procedure DrawCreature(var cr: creature);
var
asciiIdx: integer;
begin
case cr.t of
creatureHamster:
DrawHamster(cr);
creatureGhost:
begin
asciiIdx := cr.curX div cr.moveSpeed mod GhostRunN + 1;
DrawFieldAscii(cr.curX - GhostWidth div WidthCoefficient div 2,
cr.curY - GhostHeight div 2 + 1,
GhostHeight, GhostWidth, GhostAscii[asciiIdx])
end
end
end;
end.

View File

@ -9,7 +9,6 @@ const
ArenaW = 41;
TotalCells = ArenaW * ArenaH;
RandomCutThreshold = 25;
RandomOneToOne = 2;
type
arenaMatrix = array [1..ArenaH, 1..ArenaW] of boolean;
@ -18,25 +17,21 @@ type
captured, borders: arenaMatrix;
end;
function ArenaCellCaptured(x, y: integer; var a: arena): boolean;
function ArenaSplited(var h: creature; var t: tracePtr; var a: arena): boolean;
function GhostShouldTurn(var g: creature; var a: arena): boolean;
function
HamsterStepPossible(var h: creature; var t: tracePtr; var a: arena): boolean;
function IsOnBorder(var x, y: integer; var a: arena): boolean;
function IsOnEdge(var cr: creature): boolean;
function IsOnEdge(x, y: integer): boolean;
function RandomBool: boolean;
procedure ArenaCutPart(var hamster: creature; var t: tracePtr;
var cutOff: integer; var a: arena);
procedure InitArena(var a: arena);
procedure KillCapturedEnemies(var a: arena; var e: creatureList);
procedure EraseEnemies(var a: arena; var e: creatureList);
procedure MakeEnemySteps(var a: arena; var h: creature;
t: tracePtr; var e: creatureList);
procedure MakeHamsterStep(var h: creature; var t: tracePtr; var a: arena);
procedure SetArenaBorder(var t: tracePtr; var a: arena);
procedure TurnStubbornEnemies(var a: arena; var e: creatureList);
procedure TurnGhost(var g: creature; var a: arena);
implementation
@ -325,14 +320,6 @@ begin
StepBeyondEdge := StepBeyondEdgeX(cr) or StepBeyondEdgeY(cr)
end;
function RandomBool: boolean;
begin
if Random(RandomOneToOne) = 1 then
RandomBool := true
else
RandomBool := false
end;
function ChooseRandomCell(p1, p2: cellItem): cellItem;
var
rb: boolean;
@ -460,14 +447,13 @@ begin
g.dY := g.dY * -1
end;
procedure MakeEnemyStep(var e, h: creature; t: tracePtr; var a: arena);
procedure MakeEnemyStep(var e, h: creature; t: tracePtr);
var
prevX, prevY: integer;
begin
prevX := e.curX;
prevY := e.curY;
MakeStep(e);
DrawAfterEnemyStep(e, a);
if TraceCrossed(prevX, prevY, e, t) then
h.alive := false
end;
@ -524,6 +510,19 @@ begin
end
end;
procedure EraseEnemies(var a: arena; var e: creatureList);
var
tmp: creatureItemPtr;
begin
tmp := e.first;
while tmp <> nil do
begin
if tmp^.cr^.alive and not EnemyShouldTurn(tmp^.cr^, a) then
RedrawArea(a, tmp^.cr^.curX, tmp^.cr^.curY);
tmp := tmp^.next
end
end;
procedure MakeEnemySteps(var a: arena; var h: creature;
t: tracePtr; var e: creatureList);
var
@ -533,7 +532,7 @@ begin
while tmp <> nil do
begin
if tmp^.cr^.alive and not EnemyShouldTurn(tmp^.cr^, a) then
MakeEnemyStep(tmp^.cr^, h, t, a);
MakeEnemyStep(tmp^.cr^, h, t);
tmp := tmp^.next
end
end;

View File

@ -150,7 +150,7 @@ const
PauseHeight = 22;
PauseWidth = 76;
{ Too long strings :(, lets following linux styleguide }
{ Too long strings :(, lets follow linux styleguide }
PauseAscii: array[1..PauseHeight] of string = (
' _',
' | |',
@ -309,6 +309,23 @@ const
' / \ '
);
GhostHeight = 3;
GhostWidth = 7;
GhostRunN = 2;
GhostAscii: array[1..GhostRunN]
of array[1..GhostHeight] of string = (
(
' ___',
' /0 0\',
'\/VvV\/'
),
(
' ___',
' /0 0\',
'\_____/'
)
);
GameOverHeight = 40;
GameOverWidth = 63;
GameOverScreen: array[1..GameOverHeight] of string = (

View File

@ -32,13 +32,12 @@ procedure AppendCreature(var lst: creatureList; c: creaturePtr);
procedure DisposeCreatureList(var lst: creatureList);
procedure KillCreature(var cr: creature);
procedure MakeStep(var cr: creature);
procedure DrawCreature(var cr: creature);
procedure InitCreatureList(var lst: creatureList);
procedure StopCreature(var cr: creature);
implementation
uses arena_graphics_m, arena_m, math_m;
uses arena_graphics_m, arena_m, math_m, ascii_arts_m;
function RandomLR(l, r: integer): integer;
begin
@ -100,12 +99,4 @@ begin
cr.dY := 0
end;
procedure DrawCreature(var cr: creature);
begin
case cr.t of
creatureHamster:
DrawHamster(cr);
end
end;
end.

View File

@ -22,7 +22,6 @@ const
LevelSunN: array[enemyPackType] of integer = (
0, 1, 4, 2, 0, 2, 2, 2, 2, 0
);
{
LevelSnakeN: array[enemyPackType] of integer = (
0, 0, 0, 1, 2, 2, 2, 4, 2, 2

View File

@ -22,16 +22,15 @@ type
shutdown, newGame, unpause, levelInited, skipScene: boolean;
end;
procedure DecreaseLife(var life: integer);
procedure InitGame(var g: gameState);
procedure MainLoop(var g: gameState);
procedure NextExitState(var g: gameState);
procedure PreviousExitState(var g: gameState);
procedure MainLoop(var g: gameState);
implementation
uses arena_m, arena_graphics_m, crt, creature_m, ghost_m, graphics_m,
hamster_m, keys_m, trace_m, debug_m;
uses arena_m, arena_graphics_m, crt, creature_m, graphics_m, hamster_m,
keys_m, debug_m;
const
KeyDelayMs = 25;
@ -39,7 +38,7 @@ const
EraseLifeThreshold = 10;
AnnounceDelayMs = 1500;
LevelCompleteDelayMs = 1500;
LevelCount = 20;
LevelCount = 2;
StartLifeN = 3;
procedure DecreaseLife(var life: integer);
@ -156,8 +155,7 @@ begin
end
end;
procedure
GameKillHamster(var g: gameState; var level: levelState);
procedure GameKillHamster(var g: gameState; var level: levelState);
begin
if g.life <= 0 then
begin
@ -200,9 +198,10 @@ begin
EraseLevel;
break
end;
{Found bug: ghost didn't die in killed zone}
TurnStubbornEnemies(level.a, level.enemyList);
EraseEnemies(level.a, level.enemyList);
MakeEnemySteps(level.a, level.h, level.t, level.enemyList);
DrawAliveEnemies(level.enemyList);
if not level.h.alive then
GameKillHamster(g, level);
if g.curState = gameOver then
@ -214,7 +213,7 @@ begin
StopCreature(level.h);
if not ((level.h.dX = 0) and (level.h.dY = 0)) then
MakeHamsterStep(level.h, level.t, level.a);
DrawCreature(level.h) {Draw all creatures here}
DrawCreature(level.h)
end
end;
@ -347,7 +346,7 @@ begin
RunLevelState(g, level);
gameUnpauseLevel:
UnpauseLevel(g, level);
gameContinueLevel: {Maybe here should be gameStartLevel}
gameContinueLevel:
ContinueLevel(g, level);
gameOver:
RunGameOverState(g, level);

View File

@ -14,7 +14,7 @@ procedure InitRandomGhost(var g: creature);
implementation
uses arena_m, Math;
uses arena_m, Math, math_m;
procedure InitGhost(var g: creature; x, y, sigdx, sigdy: integer);
begin
@ -23,7 +23,7 @@ begin
g.curY := y;
g.dX := GhostStartDX * sigdx;
g.dY := GhostStartDY * sigdy;
g.movespeed := GhostMovespeed;
g.moveSpeed := GhostMovespeed;
g.alive := true;
g.animation := 1;
g.symbol := GhostSymbol

View File

@ -1,5 +1,5 @@
program go_hamster;
uses crt, keys_m, arena_graphics_m, graphics_m, game_m, debug_m;
uses crt, graphics_m, game_m, debug_m;
{uses crt, keys_m, arena_graphics_m, graphics_m, game_m,
ascii_digits_m, debug_m;}

View File

@ -6,18 +6,16 @@ uses arena_m, creature_m, trace_m, game_m, level_m;
const
BorderSize = 1;
BorderSymbol = '|';
CellSize = 2;
DigitSpaceWidth = 1;
DigitWidth = 6;
InterfaceH = 6;
WidthCoefficient = 2;
CellSize = 2;
BorderSymbol = '|';
DigitSpaceWidth = 1;
ScreenH = (ArenaH + InterfaceH) * CellSize + BorderSize;
ScreenW = (ArenaW - 1) * CellSize + BorderSize * 2; { 82 }
WidthCoefficient = 2;
procedure DrawAnnounce(lvl: integer);
procedure DrawAscii(x, y, h: integer; var a: array of string);
procedure DrawDigit(x, y, digit: integer);
procedure DrawExitState(s: exitState);
procedure DrawExit(var g: gameState);
procedure DrawGameOver;

View File

@ -26,7 +26,7 @@ begin
cr.curY := HamsterStartY;
cr.dX := HamsterStartDX;
cr.dY := HamsterStartDY;
cr.movespeed := HamsterMovespeed;
cr.moveSpeed := HamsterMovespeed;
cr.alive := true;
cr.symbol := HamsterSymbol
end;

View File

@ -2,7 +2,7 @@ unit keys_m;
interface
uses crt, creature_m, arena_m, game_m, trace_m, hamster_m, debug_m, level_m;
uses game_m, level_m;
const
ArrowDownOrd = -80;
@ -33,12 +33,11 @@ const
LOrd = 108;
{ Debug }
procedure GetKey(var keyCode: integer);
procedure HandleKey(var g: gameState; var level: levelState);
implementation
uses graphics_m;
uses crt, graphics_m, trace_m, creature_m, debug_m;
procedure GetKey(var keyCode: integer);
var

View File

@ -26,11 +26,6 @@ uses hamster_m, ghost_m, debug_m;
const
TotalProcent = 100;
{
BonusTurns = 45;
StartSpeedBonus = 0;
StartSlowBonus = 0;
}
function IsLevelComplete(var level: levelState): boolean;
var

View File

@ -2,6 +2,7 @@ unit math_m;
interface
function Clamp(val, min, max: integer): integer;
function RandomBool: boolean;
function Signum(a, b: integer): integer;
implementation
@ -33,4 +34,12 @@ begin
Abs := val
end;
function RandomBool: boolean;
begin
if Random(2) = 1 then
RandomBool := true
else
RandomBool := false
end;
end.

View File

@ -15,7 +15,7 @@ procedure InitRandomSun(var g: creature);
implementation
uses arena_m, Math;
uses arena_m, Math, math_m;
procedure InitSun(var g: creature; x, y, sigdx, sigdy: integer);
begin

View File

@ -18,16 +18,11 @@ type
function FindIndex(var t: tracePtr; x, y, curIdx: integer): integer;
function GetLength(var t: tracePtr): integer;
function IsOnTrace(var cr: creature; t: tracePtr): boolean;
function IsOnTrace(x, y: integer; t: tracePtr): boolean;
function
TraceCrossed(prevX, prevY: integer; var cr: creature; t: tracePtr): boolean;
procedure ChangeHamsterTrace(var h: creature; var t: tracePtr);
procedure DecreaseTrace(var hamster: creature; var t: tracePtr);
procedure DeleteTrace(var t: tracePtr);
procedure GetStart(var traceStart: tracePtr; t: tracePtr);
procedure IncreaseTrace(var hamster: creature; var t: tracePtr);
procedure Pop(var t: tracePtr);
implementation