summaryrefslogtreecommitdiff
path: root/eepers.adb
diff options
context:
space:
mode:
Diffstat (limited to 'eepers.adb')
-rw-r--r--eepers.adb391
1 files changed, 291 insertions, 100 deletions
diff --git a/eepers.adb b/eepers.adb
index 3dcee82..991150f 100644
--- a/eepers.adb
+++ b/eepers.adb
@@ -33,7 +33,13 @@ procedure Eepers is
Checkpoint_Sound: Sound;
Plant_Bomb_Sound: Sound;
Guard_Step_Sound: Sound;
+ Popup_Show_Sound: Sound;
Ambient_Music: Music;
+ Tutorial_Font: Font;
+ Death_Font: Font;
+
+ Tutorial_Font_Size: constant Int := 42;
+ Death_Font_Size: constant Int := 68;
DEVELOPMENT : constant Boolean := False;
@@ -66,6 +72,26 @@ procedure Eepers is
return Color_From_HSV(H, S, V);
end;
+ procedure Inc(X: in out Integer; Offset: Integer := 1) is
+ begin
+ X := X + Offset;
+ end;
+
+ procedure Dec(X: in out Integer; Offset: Integer := 1) is
+ begin
+ X := X - Offset;
+ end;
+
+ procedure Inc(X: in out Byte; Offset: Byte := 1) is
+ begin
+ X := X + Offset;
+ end;
+
+ procedure Dec(X: in out Byte; Offset: Byte := 1) is
+ begin
+ X := X - Offset;
+ end;
+
Palette_RGB: array (Palette) of Color := (others => (A => 255, others => 0));
Palette_HSV: array (Palette) of HSV := (others => (others => 0));
@@ -91,7 +117,7 @@ procedure Eepers is
begin
Open(F, In_File, File_Name);
while not End_Of_File(F) loop
- Line_Number := Line_Number + 1;
+ Inc(Line_Number);
declare
Line: Unbounded_String := To_Unbounded_String(Get_Line(F));
@@ -142,16 +168,21 @@ procedure Eepers is
Put_Line("WARNING: could not load colors from file " & File_Name & ": " & Exception_Message(E));
end;
- BASE_TURN_DURATION_SECS : constant Float := 0.125;
- TURN_DURATION_SECS : Float := BASE_TURN_DURATION_SECS;
- GUARD_ATTACK_COOLDOWN : constant Integer := 10;
- EEPER_EXPLOSION_DAMAGE : constant Float := 0.45;
- GUARD_TURN_REGENERATION : constant Float := 0.01;
- BOMB_GENERATOR_COOLDOWN : constant Integer := 10;
- GUARD_STEPS_LIMIT : constant Integer := 4;
- GUARD_STEP_LENGTH_LIMIT : constant Integer := 100;
- EXPLOSION_LENGTH : constant Integer := 10;
- EYES_ANGULAR_VELOCITY : constant Float := 10.0;
+ BASE_TURN_DURATION_SECS : constant Float := 0.125;
+ TURN_DURATION_SECS : Float := BASE_TURN_DURATION_SECS;
+ GUARD_ATTACK_COOLDOWN : constant Integer := 10;
+ EEPER_EXPLOSION_DAMAGE : constant Float := 0.45;
+ GUARD_TURN_REGENERATION : constant Float := 0.01;
+ BOMB_GENERATOR_COOLDOWN : constant Integer := 10;
+ GUARD_STEPS_LIMIT : constant Integer := 10;
+ GUARD_STEP_LENGTH_LIMIT : constant Integer := 100;
+ EXPLOSION_LENGTH : constant Integer := 10;
+ EYES_ANGULAR_VELOCITY : constant Float := 10.0;
+ TUTORIAL_MOVE_WAIT_TIME_SECS : constant C_Float := 5.0;
+ TUTORIAL_BOMB_WAIT_TIME_SECS : constant C_Float := 4.0;
+ TUTORIAL_SPRINT_WAIT_TIME_SECS : constant C_Float := 15.0;
+ POPUP_ANIMATION_DURATION : constant C_Float := 0.1;
+ RESTART_TIMEOUT_SECS : constant Double := 2.0;
type IVector2 is record
X, Y: Integer;
@@ -209,7 +240,12 @@ procedure Eepers is
return (A.X*S, A.Y*S);
end;
- type Item_Kind is (Item_None, Item_Key, Item_Bomb_Gen, Item_Checkpoint);
+ type Item_Kind is (
+ Item_None,
+ Item_Key,
+ Item_Bomb_Refill,
+ Item_Checkpoint,
+ Item_Bomb_Slot);
type Item is record
Kind: Item_Kind := Item_None;
Position: IVector2;
@@ -267,6 +303,7 @@ procedure Eepers is
Bombs: Integer := 0;
Bomb_Slots: Integer := 1;
Dead: Boolean := False;
+ Death_Time: Double;
end record;
type Eeper_Kind is (Eeper_Guard, Eeper_Mother, Eeper_Gnome, Eeper_Father);
@@ -295,7 +332,7 @@ procedure Eepers is
type Bomb_State_Array is array (1..10) of Bomb_State;
- type Eeper_Index is range 1..15;
+ type Eeper_Index is range 1..30;
type Eeper_Array is array (Eeper_Index) of Eeper_State;
type Checkpoint_State is record
@@ -309,6 +346,75 @@ procedure Eepers is
Bombs: Bomb_State_Array;
end record;
+ type Direction is (Left, Right, Up, Down);
+
+ Direction_Vector: constant array (Direction) of IVector2 := (
+ Left => (X => -1, Y => 0),
+ Right => (X => 1, Y => 0),
+ Up => (X => 0, Y => -1),
+ Down => (X => 0, Y => 1));
+
+ type Popup_State is record
+ Label: Char_Array(1..50);
+ Visible: Boolean := False;
+ Animation: C_Float := 0.0;
+ end record;
+
+ procedure Show_Popup(Popup: in out Popup_State; Text: String) is
+ Ignore: Size_t;
+ begin
+ if not Popup.Visible then
+ Play_Sound(Popup_Show_Sound);
+ end if;
+ Popup.Visible := True;
+ To_C(Text, Popup.Label, Ignore);
+ end;
+
+ procedure Hide_Popup(Popup: in out Popup_State) is
+ begin
+ Popup.Visible := False;
+ end;
+
+ procedure Draw_Popup(Popup: in out Popup_State; Start, Size: Vector2) is
+ begin
+ if Popup.Visible then
+ if Popup.Animation < 1.0 then
+ Popup.Animation := (Popup.Animation*POPUP_ANIMATION_DURATION + Get_Frame_Time)/POPUP_ANIMATION_DURATION;
+ end if;
+ else
+ if Popup.Animation > 0.0 then
+ Popup.Animation := (Popup.Animation*POPUP_ANIMATION_DURATION - Get_Frame_Time)/POPUP_ANIMATION_DURATION;
+ end if;
+ end if;
+
+ if Popup.Animation > 0.0 then
+ declare
+ Font_Size: constant C_Float := C_Float(Tutorial_Font_Size)*Popup.Animation;
+ Popup_Bottom_Margin: constant C_Float := Font_Size*0.05;
+ Label_Size: constant Vector2 := Measure_Text_Ex(Tutorial_Font, Popup.Label, Font_Size, 0.0);
+ Label_Position: constant Vector2 := Start + Size*(0.5, 0.0) - Label_Size*(0.5, 1.0) - (0.0, Popup_Bottom_Margin);
+ begin
+ Draw_Text_Ex(Tutorial_Font, Popup.Label, Label_Position + (-2.0, 2.0), Font_Size, 0.0, Palette_RGB(COLOR_WALL));
+ Draw_Text_Ex(Tutorial_Font, Popup.Label, Label_Position, Font_Size, 0.0, Palette_RGB(COLOR_PLAYER));
+ end;
+ end if;
+ end;
+
+ type Tutorial_Phase is (Tutorial_Move, Tutorial_Place_Bombs, Tutorial_Waiting_For_Sprint, Tutorial_Sprint, Tutorial_Done);
+ type Tutorial_State is record
+ Phase: Tutorial_Phase := Tutorial_Move;
+ Waiting: C_Float := 0.0;
+
+ Prev_Step_Timestamp: Double := 0.0;
+ Hurry_Count: Integer := 0;
+
+ Popup: Popup_State;
+
+ Knows_How_To_Move: Boolean := False;
+ Knows_How_To_Place_Bombs: Boolean := False;
+ Knows_How_To_Sprint: Boolean := False;
+ end record;
+
type Game_State is record
Map: Map_Access := Null;
Player: Player_State;
@@ -318,8 +424,13 @@ procedure Eepers is
Items: Item_Array;
Bombs: Bomb_State_Array;
- Camera_Position: Vector2 := (x => 0.0, y => 0.0);
+ Camera: Camera2D := (
+ offset => (x => 0.0, y => 0.0),
+ target => (x => 0.0, y => 0.0),
+ rotation => 0.0,
+ zoom => 1.0);
+ Tutorial: Tutorial_State;
Checkpoint: Checkpoint_State;
Duration_Of_Last_Turn: Double;
@@ -338,14 +449,6 @@ procedure Eepers is
return M1;
end;
- type Direction is (Left, Right, Up, Down);
-
- Direction_Vector: constant array (Direction) of IVector2 := (
- Left => (X => -1, Y => 0),
- Right => (X => 1, Y => 0),
- Up => (X => 0, Y => -1),
- Down => (X => 0, Y => 1));
-
function Inside_Of_Rect(Start, Size, Point: in IVector2) return Boolean is
begin
return Start <= Point and then Point < Start + Size;
@@ -572,25 +675,27 @@ procedure Eepers is
Level_Wall,
Level_Door,
Level_Checkpoint,
- Level_Bomb_Gen,
+ Level_Bomb_Refill,
Level_Barricade,
Level_Key,
Level_Player,
- Level_Father);
+ Level_Father,
+ Level_Bomb_Slot);
Level_Cell_Color: constant array (Level_Cell) of Color := (
- Level_None => Get_Color(16#00000000#),
- Level_Gnome => Get_Color(16#FF9600FF#),
- Level_Mother => Get_Color(16#96FF00FF#),
- Level_Guard => Get_Color(16#00FF00FF#),
- Level_Floor => Get_Color(16#FFFFFFFF#),
- Level_Wall => Get_Color(16#000000FF#),
- Level_Door => Get_Color(16#00FFFFFF#),
- Level_Checkpoint => Get_Color(16#FF00FFFF#),
- Level_Bomb_Gen => Get_Color(16#FF0000FF#),
- Level_Barricade => Get_Color(16#FF0096FF#),
- Level_Key => Get_Color(16#FFFF00FF#),
- Level_Player => Get_Color(16#0000FFFF#),
- Level_Father => Get_Color(16#265FDAFF#));
+ Level_None => Get_Color(16#00000000#),
+ Level_Gnome => Get_Color(16#FF9600FF#),
+ Level_Mother => Get_Color(16#96FF00FF#),
+ Level_Guard => Get_Color(16#00FF00FF#),
+ Level_Floor => Get_Color(16#FFFFFFFF#),
+ Level_Wall => Get_Color(16#000000FF#),
+ Level_Door => Get_Color(16#00FFFFFF#),
+ Level_Checkpoint => Get_Color(16#FF00FFFF#),
+ Level_Bomb_Refill => Get_Color(16#FF0000FF#),
+ Level_Barricade => Get_Color(16#FF0096FF#),
+ Level_Key => Get_Color(16#FFFF00FF#),
+ Level_Player => Get_Color(16#0000FFFF#),
+ Level_Father => Get_Color(16#265FDAFF#),
+ Level_Bomb_Slot => Get_Color(16#BC5353FF#));
function Cell_By_Color(Col: Color; Out_Cel: out Level_Cell) return Boolean is
begin
@@ -668,7 +773,8 @@ procedure Eepers is
Game.Map(Row, Column) := Cell_Floor;
when Level_Father =>
if Update_Camera then
- Game.Camera_Position := Screen_Size*0.5 - (To_Vector2((Column, Row))*Cell_Size + To_Vector2((7, 7))*Cell_Size*0.5);
+ Game.Camera.target := (To_Vector2((Column, Row)) + To_Vector2((7, 7))*0.5) * Cell_Size;
+ Game.Camera.offset := Screen_Size*0.5 - Cell_Size*0.5;
end if;
Spawn_Father(Game, (Column, Row));
Game.Map(Row, Column) := Cell_Floor;
@@ -678,9 +784,12 @@ procedure Eepers is
when Level_Checkpoint =>
Game.Map(Row, Column) := Cell_Floor;
Allocate_Item(Game, (Column, Row), Item_Checkpoint);
- when Level_Bomb_Gen =>
+ when Level_Bomb_Refill =>
Game.Map(Row, Column) := Cell_Floor;
- Allocate_Item(Game, (Column, Row), Item_Bomb_Gen);
+ Allocate_Item(Game, (Column, Row), Item_Bomb_Refill);
+ when Level_Bomb_Slot =>
+ Game.Map(Row, Column) := Cell_Floor;
+ Allocate_Item(Game, (Column, Row), Item_Bomb_Slot);
when Level_Barricade =>
Game.Map(Row, Column) := Cell_Barricade;
when Level_Key =>
@@ -760,13 +869,15 @@ procedure Eepers is
begin
Draw_Rectangle_V(To_Vector2(Item.Position)*Cell_Size + Cell_Size*0.5 - Checkpoint_Item_Size*0.5, Checkpoint_Item_Size, Palette_RGB(COLOR_CHECKPOINT));
end;
- when Item_Bomb_Gen =>
+ when Item_Bomb_Refill =>
if Item.Cooldown > 0 then
Draw_Bomb(Item.Position, Color_Brightness(Palette_RGB(COLOR_BOMB), -0.5));
Draw_Number(Item.Position, Item.Cooldown, Palette_RGB(COLOR_LABEL));
else
Draw_Bomb(Item.Position, Palette_RGB(COLOR_BOMB));
end if;
+ when Item_Bomb_Slot =>
+ Draw_Bomb(Item.Position, Palette_RGB(COLOR_DOORKEY));
end case;
end loop;
end;
@@ -825,17 +936,21 @@ procedure Eepers is
case Item.Kind is
when Item_None => null;
when Item_Key =>
- Game.Player.Keys := Game.Player.Keys + 1;
+ Inc(Game.Player.Keys);
Item.Kind := Item_None;
Play_Sound(Key_Pickup_Sound);
- when Item_Bomb_Gen => if
+ when Item_Bomb_Refill => if
Game.Player.Bombs < Game.Player.Bomb_Slots
and then Item.Cooldown <= 0
then
- Game.Player.Bombs := Game.Player.Bombs + 1;
+ Inc(Game.Player.Bombs);
Item.Cooldown := BOMB_GENERATOR_COOLDOWN;
Play_Sound(Bomb_Pickup_Sound);
end if;
+ when Item_Bomb_Slot =>
+ Item.Kind := Item_None;
+ Inc(Game.Player.Bomb_Slots);
+ Game.Player.Bombs := Game.Player.Bomb_Slots;
when Item_Checkpoint =>
Item.Kind := Item_None;
Game.Player.Bombs := Game.Player.Bomb_Slots;
@@ -846,7 +961,7 @@ procedure Eepers is
end loop;
when Cell_Door =>
if Game.Player.Keys > 0 then
- Game.Player.Keys := Game.Player.Keys - 1;
+ Dec(Game.Player.Keys);
Flood_Fill(Game, New_Position, Cell_Floor);
Game.Player.Position := New_Position;
Play_Sound(Open_Door_Sound);
@@ -857,6 +972,12 @@ procedure Eepers is
end;
end;
+ procedure Kill_Player(Game: in out Game_State) is
+ begin
+ Game.Player.Dead := True;
+ Game.Player.Death_Time := Get_Time;
+ end;
+
procedure Explode(Game: in out Game_State; Position: in IVector2) is
procedure Explode_Line(Dir: Direction) is
New_Position: IVector2 := Position;
@@ -871,7 +992,7 @@ procedure Eepers is
Game.Map(New_Position.Y, New_Position.X) := Cell_Explosion;
if New_Position = Game.Player.Position then
- Game.Player.Dead := True;
+ Kill_Player(Game);
end if;
for Eeper of Game.Eepers loop
@@ -904,20 +1025,17 @@ procedure Eepers is
);
procedure Game_Update_Camera(Game: in out Game_State) is
- Camera_Target: constant Vector2 :=
- Screen_Size*0.5 - To_Vector2(Game.Player.Position)*Cell_Size - Cell_Size*0.5;
- Camera_Velocity: constant Vector2 := (Camera_Target - Game.Camera_Position)*2.0;
- begin
- Game.Camera_Position := Game.Camera_Position + Camera_Velocity*Get_Frame_Time;
- end;
-
- function Game_Camera(Game: in Game_State) return Camera2D is
+ Camera_Target: constant Vector2 := To_Vector2(Game.Player.Position)*Cell_Size;
+ Camera_Offset: constant Vector2 := Screen_Size*0.5 - Cell_Size*0.5;
+ Camera_Velocity: constant Vector2 := (Camera_Target - Game.Camera.target)*2.0;
begin
- return (
- offset => Game.Camera_Position,
- target => (x => 0.0, y => 0.0),
- rotation => 0.0,
- zoom => 1.0);
+ Game.Camera.offset := Camera_Offset;
+ Game.Camera.target := Game.Camera.target + Camera_Velocity*Get_Frame_Time;
+ -- TODO: animate zoom similarly to Game.Camera.target
+ -- So it looks cool when you resize the game in the window mode.
+ -- TODO: The tutorial signs look gross on bigger screens.
+ -- We need to do something with the fonts
+ Game.Camera.zoom := C_Float'Max(Screen_Size.x/1920.0, Screen_Size.y/1080.0);
end;
function Interpolate_Positions(IPrev_Position, IPosition: IVector2; T: Float) return Vector2 is
@@ -946,7 +1064,7 @@ procedure Eepers is
begin
Q.Items((Q.Start + Q.Size) mod Command_Capacity) := C;
if Q.Size < Command_Capacity then
- Q.Size := Q.Size + 1;
+ Inc(Q.Size);
else
Q.Start := (Q.Start + 1) mod Command_Capacity;
end if;
@@ -958,18 +1076,18 @@ procedure Eepers is
return False;
end if;
C := Q.Items(Q.Start);
- Q.Size := Q.Size - 1;
+ Dec(Q.Size);
Q.Start := (Q.Start + 1) mod Command_Capacity;
return True;
end;
Command_Queue: Command_Queue_Record;
- Any_Key_Pressed: Boolean := False;
+ Holding_Shift: Boolean := False;
procedure Swallow_Player_Input is
begin
Command_Queue.Size := 0;
- Any_Key_Pressed := False;
+ Holding_Shift := False;
end;
procedure Game_Bombs_Turn(Game: in out Game_State) is
@@ -979,7 +1097,7 @@ procedure Eepers is
end loop;
for Bomb of Game.Bombs loop
if Bomb.Countdown > 0 then
- Bomb.Countdown := Bomb.Countdown - 1;
+ Dec(Bomb.Countdown);
if Bomb.Countdown <= 0 then
Play_Sound(Blast_Sound);
Explode(Game, Bomb.Position);
@@ -1059,7 +1177,7 @@ procedure Eepers is
when Eeper_Guard | Eeper_Mother =>
Recompute_Path_For_Eeper(Game, Me, GUARD_STEPS_LIMIT, GUARD_STEP_LENGTH_LIMIT);
if Eeper.Path(Eeper.Position.Y, Eeper.Position.X) = 0 then
- Game.Player.Dead := True;
+ Kill_Player(Game);
Eeper.Eyes := Eyes_Surprised;
elsif Eeper.Path(Eeper.Position.Y, Eeper.Position.X) > 0 then
if Eeper.Attack_Cooldown <= 0 then
@@ -1076,7 +1194,7 @@ procedure Eepers is
Position := Position + Direction_Vector(Dir);
if Within_Map(Game, Position) and then Eeper.Path(Position.Y, Position.X) = Current - 1 then
Available_Positions(Count) := Position;
- Count := Count + 1;
+ Inc(Count);
exit;
end if;
end loop;
@@ -1089,7 +1207,7 @@ procedure Eepers is
end;
Eeper.Attack_Cooldown := GUARD_ATTACK_COOLDOWN;
else
- Eeper.Attack_Cooldown := Eeper.Attack_Cooldown - 1;
+ Dec(Eeper.Attack_Cooldown);
end if;
if Eeper.Path(Eeper.Position.Y, Eeper.Position.X) = 1 then
@@ -1100,7 +1218,7 @@ procedure Eepers is
Eeper.Eyes_Target := Game.Player.Position;
if Inside_Of_Rect(Eeper.Position, Eeper.Size, Game.Player.Position) then
- Game.Player.Dead := True;
+ Kill_Player(Game);
end if;
else
Eeper.Eyes := Eyes_Closed;
@@ -1130,7 +1248,7 @@ procedure Eepers is
and then Eeper.Path(New_Position.Y, New_Position.X) > Eeper.Path(Position.Y, Position.X)
then
Available_Positions(Count) := New_Position;
- Count := Count + 1;
+ Inc(Count);
end if;
end;
end loop;
@@ -1155,9 +1273,9 @@ procedure Eepers is
procedure Game_Items_Turn(Game: in out Game_State) is
begin
for Item of Game.Items loop
- if Item.Kind = Item_Bomb_Gen then
+ if Item.Kind = Item_Bomb_Refill then
if Item.Cooldown > 0 then
- Item.Cooldown := Item.Cooldown - 1;
+ Dec(Item.Cooldown);
end if;
end if;
end loop;
@@ -1222,6 +1340,53 @@ procedure Eepers is
end loop;
end;
+ procedure Game_Tutorial(Game: in out Game_State) is
+ begin
+ case Game.Tutorial.Phase is
+ when Tutorial_Move =>
+ if Game.Tutorial.Knows_How_To_Move then
+ Game.Tutorial.Phase := Tutorial_Place_Bombs;
+ Game.Tutorial.Waiting := 0.0;
+ Hide_Popup(Game.Tutorial.Popup);
+ elsif Game.Tutorial.Waiting < TUTORIAL_MOVE_WAIT_TIME_SECS then
+ Game.Tutorial.Waiting := Game.Tutorial.Waiting + Get_Frame_Time;
+ else
+ Show_Popup(Game.Tutorial.Popup, "WASD to Move");
+ end if;
+ when Tutorial_Place_Bombs =>
+ if Game.Tutorial.Knows_How_To_Place_Bombs then
+ Game.Tutorial.Phase := Tutorial_Waiting_For_Sprint;
+ Hide_Popup(Game.Tutorial.Popup);
+ elsif Game.Player.Bombs > 0 then
+ if Game.Tutorial.Waiting < TUTORIAL_BOMB_WAIT_TIME_SECS then
+ Game.Tutorial.Waiting := Game.Tutorial.Waiting + Get_Frame_Time;
+ else
+ Show_Popup(Game.Tutorial.Popup, "SPACE to Place Bombs");
+ end if;
+ end if;
+ when Tutorial_Waiting_For_Sprint =>
+ if Game.Tutorial.Hurry_Count >= 10 then
+ Game.Tutorial.Phase := Tutorial_Sprint;
+ Game.Tutorial.Waiting := 0.0;
+ end if;
+ when Tutorial_Sprint =>
+ if Game.Tutorial.Knows_How_To_Sprint then
+ Game.Tutorial.Phase := Tutorial_Done;
+ Hide_Popup(Game.Tutorial.Popup);
+ else
+ if Game.Tutorial.Waiting < TUTORIAL_SPRINT_WAIT_TIME_SECS then
+ Show_Popup(Game.Tutorial.Popup, "Hold SHIFT to Sprint");
+ Game.Tutorial.Waiting := Game.Tutorial.Waiting + Get_Frame_Time;
+ else
+ Game.Tutorial.Phase := Tutorial_Done;
+ Hide_Popup(Game.Tutorial.Popup);
+ end if;
+ end if;
+ when Tutorial_Done => null;
+ end case;
+ Draw_Popup(Game.Tutorial.Popup, Screen_Player_Position(Game), Cell_Size);
+ end;
+
procedure Game_Player(Game: in out Game_State) is
Eyes_Angular_Direction: constant Float := Delta_Angle(Game.Player.Eyes_Angle, Look_At(To_Vector2(Game.Player.Position)*Cell_Size + Cell_Size*0.5, To_Vector2(Game.Player.Eyes_Target)*Cell_Size + Cell_Size*0.5));
begin
@@ -1232,7 +1397,7 @@ procedure Eepers is
Draw_Eyes(Screen_Player_Position(Game), Cell_Size, Game.Player.Eyes_Angle, Game.Player.Prev_Eyes, Game.Player.Eyes, Game.Turn_Animation);
end if;
- if Any_Key_Pressed then
+ if (Get_Time - Game.Player.Death_Time) > RESTART_TIMEOUT_SECS then
Game_Restore_Checkpoint(Game);
Game.Player.Dead := False;
end if;
@@ -1256,12 +1421,31 @@ procedure Eepers is
declare
Start_Of_Turn: constant Double := Get_Time;
begin
+ Game.Tutorial.Knows_How_To_Move := True;
+ if Holding_Shift then
+ Game.Tutorial.Knows_How_To_Sprint := True;
+ end if;
+
+ if Game.Tutorial.Phase = Tutorial_Waiting_For_Sprint then
+ declare
+ Step_Timestamp: constant Double := Get_Time;
+ Delta_Timestamp: constant Double := Step_Timestamp - Game.Tutorial.Prev_Step_Timestamp;
+ begin
+ if Delta_Timestamp < 0.2 Then
+ Inc(Game.Tutorial.Hurry_Count);
+ elsif Game.Tutorial.Hurry_Count > 0 then
+ Dec(Game.Tutorial.Hurry_Count);
+ end if;
+ Game.Tutorial.Prev_Step_Timestamp := Step_Timestamp;
+ end;
+ end if;
+
Game.Turn_Animation := 1.0;
Game_Explosions_Turn(Game);
Game_Items_Turn(Game);
Game_Player_Turn(Game, C.Dir);
- Game_Eepers_Turn(Game);
Game_Bombs_Turn(Game);
+ Game_Eepers_Turn(Game);
Game.Duration_Of_Last_Turn := Get_Time - Start_Of_Turn;
end;
when Command_Plant =>
@@ -1269,6 +1453,8 @@ procedure Eepers is
declare
Start_Of_Turn: constant Double := Get_Time;
begin
+ Game.Tutorial.Knows_How_To_Place_Bombs := True;
+
Game.Turn_Animation := 1.0;
Game_Explosions_Turn(Game);
Game_Items_Turn(Game);
@@ -1277,8 +1463,8 @@ procedure Eepers is
Game.Player.Prev_Eyes := Game.Player.Eyes;
Game.Player.Prev_Position := Game.Player.Position;
- Game_Eepers_Turn(Game);
Game_Bombs_Turn(Game);
+ Game_Eepers_Turn(Game);
if Game.Player.Bombs > 0 then
for Bomb of Game.Bombs loop
@@ -1288,7 +1474,7 @@ procedure Eepers is
exit;
end if;
end loop;
- Game.Player.Bombs := Game.Player.Bombs - 1;
+ Dec(Game.Player.Bombs);
Play_Sound(Plant_Bomb_Sound);
end if;
@@ -1320,23 +1506,27 @@ procedure Eepers is
end;
end loop;
- for Index in 1..Game.Player.Bombs loop
+ for Index in 1..Game.Player.Bomb_Slots loop
declare
- Position: constant Vector2 := (100.0 + C_float(Index - 1)*Cell_Size.X, 200.0);
+ Padding: constant C_Float := Cell_Size.X*0.5;
+ Position: constant Vector2 := (100.0 + C_float(Index - 1)*(Cell_Size.X + Padding), 200.0);
begin
- Draw_Circle_V(Position, Cell_Size.X*0.5, Palette_RGB(COLOR_BOMB));
+ if Index <= Game.Player.Bombs then
+ Draw_Circle_V(Position, Cell_Size.X*0.5, Palette_RGB(COLOR_BOMB));
+ else
+ Draw_Circle_V(Position, Cell_Size.X*0.5, Color_Brightness(Palette_RGB(COLOR_BOMB), -0.5));
+ end if;
end;
end loop;
if Game.Player.Dead then
declare
Label: constant Char_Array := To_C("You Died!");
- Label_Height: constant Integer := 48;
- Label_Width: constant Integer := Integer(Measure_Text(Label, Int(Label_Height)));
- Text_Size: constant Vector2 := To_Vector2((Label_Width, Label_Height));
+ Text_Size: constant Vector2 := Measure_Text_Ex(Death_Font, Label, C_Float(Death_Font_Size), 0.0);
Position: constant Vector2 := Screen_Size*0.5 - Text_Size*0.5;
begin
- Draw_Text(Label, Int(Position.X), Int(Position.Y), Int(Label_Height), Palette_RGB(COLOR_LABEL));
+ Draw_Text_Ex(Death_Font, Label, Position + (-2.0, 2.0), C_Float(Death_Font_Size), 0.0, Palette_RGB(COLOR_WALL));
+ Draw_Text_Ex(Death_Font, Label, Position, C_Float(Death_Font_Size), 0.0, Palette_RGB(COLOR_PLAYER));
end;
end if;
end;
@@ -1404,7 +1594,7 @@ procedure Eepers is
end;
Game: Game_State;
- Title: constant Char_Array := To_C("Eepers (v1.2)");
+ Title: constant Char_Array := To_C("Eepers (v1.4)");
Palette_Editor: Boolean := False;
Palette_Editor_Choice: Palette := Palette'First;
@@ -1440,6 +1630,11 @@ begin
Set_Sound_Pitch(Checkpoint_Sound, 0.8);
Guard_Step_Sound := Load_Sound(To_C("assets/sounds/guard-step.ogg")); -- https://opengameart.org/content/fire-whip-hit-yo-frankie
Plant_Bomb_Sound := Load_Sound(To_C("assets/sounds/plant-bomb.wav")); -- https://opengameart.org/content/ui-soundpack-by-m1chiboi-bleeps-and-clicks
+ Popup_Show_Sound := Load_Sound(To_C("assets/sounds/popup-show.wav")); -- https://opengameart.org/content/ui-soundpack-by-m1chiboi-bleeps-and-clicks
+ Tutorial_Font := Load_Font_Ex(To_C("assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf"), Tutorial_Font_Size, 0, 0);
+ Gen_Texture_Mipmaps(Tutorial_Font.Texture'Access);
+ Death_Font := Load_Font_Ex(To_C("assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf"), Death_Font_Size, 0, 0);
+ Gen_Texture_Mipmaps(Death_Font.Texture'Access);
Random_Integer.Reset(Gen);
Load_Colors("assets/colors.txt");
@@ -1454,10 +1649,11 @@ begin
Begin_Drawing;
Clear_Background(Palette_RGB(COLOR_BACKGROUND));
+ Holding_Shift := Boolean(Is_Key_Down(KEY_LEFT_SHIFT)) or else Boolean(Is_Key_Down(KEY_RIGHT_SHIFT));
if Game.Player.Dead then
Command_Queue.Size := 0;
else
- if (Boolean(Is_Key_Down(KEY_LEFT_SHIFT)) or else Boolean(Is_Key_Down(KEY_RIGHT_SHIFT))) and then Game.Turn_Animation <= 0.0 then
+ if Holding_Shift and then Game.Turn_Animation <= 0.0 then
if Is_Key_Down(KEY_A) or else Is_Key_Down(KEY_LEFT) then
Command_Queue.Size := 0;
Command_Enqueue(Command_Queue, (Kind => Command_Step, Dir => Left));
@@ -1492,7 +1688,7 @@ begin
Command_Enqueue(Command_Queue, (Kind => Command_Plant));
end if;
end if;
- if Boolean(Is_Key_Down(KEY_LEFT_SHIFT)) or else Boolean(Is_Key_Down(KEY_RIGHT_SHIFT)) then
+ if Holding_Shift then
TURN_DURATION_SECS := BASE_TURN_DURATION_SECS * 0.8;
else
if Command_Queue.Size /= 0 then
@@ -1502,11 +1698,6 @@ begin
end if;
end if;
- Any_Key_Pressed := False;
- while not Any_Key_Pressed and then Get_Key_Pressed /= KEY_NULL loop
- Any_Key_Pressed := True;
- end loop;
-
if DEVELOPMENT then
if Is_Key_Pressed(KEY_R) then
Load_Game_From_Image("assets/map.png", Game, Update_Player => False, Update_Camera => False);
@@ -1538,12 +1729,12 @@ begin
end if;
if Is_Key_Down(Keys(Up)) then
- Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component) := Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component) + 1;
+ Inc(Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component));
Palette_RGB(Palette_Editor_Choice) := HSV_To_RGB(Palette_HSV(Palette_Editor_Choice));
end if;
if Is_Key_Down(Keys(Down)) then
- Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component) := Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component) - 1;
+ Dec(Palette_HSV(Palette_Editor_Choice)(Palette_Editor_Component));
Palette_RGB(Palette_Editor_Choice) := HSV_To_RGB(Palette_HSV(Palette_Editor_Choice));
end if;
else
@@ -1577,12 +1768,13 @@ begin
end if;
Game_Update_Camera(Game);
- Begin_Mode2D(Game_Camera(Game));
+ Begin_Mode2D(Game.Camera);
Game_Cells(Game);
Game_Items(Game);
Game_Player(Game);
Game_Eepers(Game);
Game_Bombs(Game);
+ Game_Tutorial(Game);
if DEVELOPMENT then
if Is_Key_Down(KEY_P) then
for Row in Game.Map'Range(1) loop
@@ -1641,6 +1833,12 @@ begin
end;
-- TODO: Items in HUD may sometimes blend with the background
+-- TODO: Different palettes on each NG+
+-- TODO: NG+ must make the Game harder while retaining the collected bomb slots
+-- TODO: The gnome blocking trick was never properly explained.
+-- We should introduce an extra room that entirely relies on that mechanic,
+-- so it does not feel out of place, when you discover it on Mother.
+-- TODO: The puzzle with Gnome blocking Guard repeated twice (which sucks)
-- TODO: Footstep variation for Mother/Guard bosses (depending on the distance traveled?)
-- TODO: Footsteps for mother should be lower
-- TODO: Restarting should be considered a turn
@@ -1648,7 +1846,6 @@ end;
-- Or maybe we should just save Path Maps too?
-- TODO: If you are standing on the refilled bomb gen and place a bomb you should refill your bomb in that turn.
-- TODO: Checkpoints should be circles (like all the items)
--- TODO: Custom font
-- TODO: Mother should require several attacks before being "split"
-- TODO: Enemies should attack on zero just like a bomb.
-- TODO: Properly disablable DEV features
@@ -1658,17 +1855,11 @@ end;
-- TODO: Visual Clue that the Eeper is about to kill the Player when Completely outside of the Screen
-- - Cooldown ball is shaking
-- TODO: Cool animation for New Game
--- TODO: Tutorial
--- - Sign that says "WASD" to move when you start the game for the first time.
--- - And how to place the bomb on picking it up.
--- - How to sprint after you blow up firt Barricade.
-- TODO: Count the player's turns towards the final score of the game
-- We can even collect different stats, like bombs collected, bombs used,
-- times died etc.
-- TODO: Animate key when you pick it up
-- Smoothly move it into the HUD.
--- TODO: Different palettes depending on the area
--- Or maybe different palette for each NG+
-- TODO: Particles
-- - Player Death animation
-- - Eeper Death animation