From 2ca408a2a8da976ec5ea8d4e89430b0deaff03ed Mon Sep 17 00:00:00 2001 From: gre-ilya Date: Sat, 28 Feb 2026 22:23:36 +0500 Subject: [PATCH] feat/TD-017-add-ghost-animation --- src/arena_graphics_m.pas | 99 +++++++++++++++++++++------------------- src/arena_m.pas | 33 +++++++------- src/ascii_arts_m.pas | 19 +++++++- src/creature_m.pas | 11 +---- src/enemy_packs_m.pas | 1 - src/game_m.pas | 19 ++++---- src/ghost_m.pas | 4 +- src/gohamster.pas | 2 +- src/graphics_m.pas | 10 ++-- src/hamster_m.pas | 2 +- src/keys_m.pas | 5 +- src/level_m.pas | 5 -- src/math_m.pas | 9 ++++ src/sun_m.pas | 2 +- src/trace_m.pas | 5 -- 15 files changed, 117 insertions(+), 109 deletions(-) diff --git a/src/arena_graphics_m.pas b/src/arena_graphics_m.pas index d5c5176..bdf81f4 100644 --- a/src/arena_graphics_m.pas +++ b/src/arena_graphics_m.pas @@ -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. diff --git a/src/arena_m.pas b/src/arena_m.pas index e5816d4..2d9b0f9 100644 --- a/src/arena_m.pas +++ b/src/arena_m.pas @@ -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; diff --git a/src/ascii_arts_m.pas b/src/ascii_arts_m.pas index de592a8..44f2238 100644 --- a/src/ascii_arts_m.pas +++ b/src/ascii_arts_m.pas @@ -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 = ( diff --git a/src/creature_m.pas b/src/creature_m.pas index 5a53e67..19a68d2 100644 --- a/src/creature_m.pas +++ b/src/creature_m.pas @@ -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. diff --git a/src/enemy_packs_m.pas b/src/enemy_packs_m.pas index b332b20..777174a 100644 --- a/src/enemy_packs_m.pas +++ b/src/enemy_packs_m.pas @@ -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 diff --git a/src/game_m.pas b/src/game_m.pas index 5f8e0d4..af1ae95 100644 --- a/src/game_m.pas +++ b/src/game_m.pas @@ -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); diff --git a/src/ghost_m.pas b/src/ghost_m.pas index e1f1b0b..9a1d156 100644 --- a/src/ghost_m.pas +++ b/src/ghost_m.pas @@ -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 diff --git a/src/gohamster.pas b/src/gohamster.pas index 1fdeff9..7622761 100644 --- a/src/gohamster.pas +++ b/src/gohamster.pas @@ -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;} diff --git a/src/graphics_m.pas b/src/graphics_m.pas index 982fb2d..7ea8301 100644 --- a/src/graphics_m.pas +++ b/src/graphics_m.pas @@ -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; diff --git a/src/hamster_m.pas b/src/hamster_m.pas index 31eb792..9a528cf 100644 --- a/src/hamster_m.pas +++ b/src/hamster_m.pas @@ -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; diff --git a/src/keys_m.pas b/src/keys_m.pas index 3c2517f..bb87ce9 100644 --- a/src/keys_m.pas +++ b/src/keys_m.pas @@ -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 diff --git a/src/level_m.pas b/src/level_m.pas index 63892c0..b6162f2 100644 --- a/src/level_m.pas +++ b/src/level_m.pas @@ -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 diff --git a/src/math_m.pas b/src/math_m.pas index 51ec6ae..b7ecd77 100644 --- a/src/math_m.pas +++ b/src/math_m.pas @@ -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. diff --git a/src/sun_m.pas b/src/sun_m.pas index 230463b..d410c8f 100644 --- a/src/sun_m.pas +++ b/src/sun_m.pas @@ -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 diff --git a/src/trace_m.pas b/src/trace_m.pas index 1082ee1..fbc3113 100644 --- a/src/trace_m.pas +++ b/src/trace_m.pas @@ -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