summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2025-07-27 16:33:14 +0300
committerKyren223 <Kyren223@proton.me>2025-07-27 16:33:14 +0300
commitbad181b244bcd3b8207411d49db829bc39872662 (patch)
tree20c2c2d134ddca4988ae3784b1fba4b5e13b63f7
parentd8630d178fcb7e4bfe75cfe3623f0e6767ecbff1 (diff)
Added victory screen
-rw-r--r--src/main.zig157
1 files changed, 154 insertions, 3 deletions
diff --git a/src/main.zig b/src/main.zig
index 1c97bd9..3949d5a 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -706,6 +706,7 @@ const Game = struct {
main: ScreenMainMenu,
battle: ScreenBattle,
gameover: ScreenGameOver,
+ victory: ScreenVictory,
},
fn init() Game {
@@ -776,6 +777,7 @@ const Game = struct {
.screen_state = .{
.main = .init(),
// .gameover = .init(),
+ // .victory = .init(),
// .battle = .init(),
},
};
@@ -807,6 +809,9 @@ const Game = struct {
.gameover => {
self.screen_state.gameover.update(self, dt);
},
+ .victory => {
+ self.screen_state.victory.update(self, dt);
+ },
}
}
@@ -829,6 +834,9 @@ const Game = struct {
.gameover => {
self.screen_state.gameover.render(self);
},
+ .victory => {
+ self.screen_state.victory.render(self);
+ },
}
}
};
@@ -1080,7 +1088,151 @@ const ScreenGameOver = struct {
}
};
-// TODO: ScreenVictory
+const ScreenVictory = struct {
+ camera: rl.Camera2D,
+ pressed_play_again: bool = false,
+ pressed_menu: bool = false,
+ dt: f32 = -1.0,
+ text: [:0]const u8 = "You win!",
+ size: f32 = 22,
+ spacing: f32 = 1.0,
+ color: rl.Color = rl.Color.green,
+ measurement: rl.Vector2 = .{ .x = 0, .y = 0 },
+ widths: [8]f32 = undefined,
+ height: f32 = 0,
+ speed: f32 = 2.0,
+ animation_offset: f32 = 0.2,
+ animation_size: f32 = 0.3,
+ initiated: bool = false,
+
+ const button_menu_width: f32 = @floatFromInt(cell_size * 7);
+ const button_menu_height: f32 = @floatFromInt(cell_size);
+
+ fn init() ScreenVictory {
+ return .{
+ .camera = .{
+ .target = .{ .x = 128, .y = 128 },
+ .offset = .{
+ .x = @as(f32, @floatFromInt(screen_width)) / 2,
+ .y = @as(f32, @floatFromInt(screen_height)) / 2,
+ },
+ .rotation = 0,
+ .zoom = @as(f32, @floatFromInt(screen_height)) / world_height,
+ },
+ };
+ }
+
+ fn update(self: *ScreenVictory, game: *Game, dt: f32) void {
+ if (self.initiated) {
+ self.dt += dt;
+ }
+
+ self.updateCamera();
+
+ // Must be the last thing
+ if (self.pressed_play_again) {
+ game.screen_state = .{ .battle = .init() };
+ } else if (self.pressed_menu) {
+ game.screen_state = .{ .main = .init() };
+ }
+ }
+
+ fn updateCamera(self: *ScreenVictory) void {
+ screen_width = rl.getScreenWidth();
+ screen_height = rl.getScreenHeight();
+
+ self.camera.offset = .{
+ .x = @as(f32, @floatFromInt(screen_width)) / 2,
+ .y = @as(f32, @floatFromInt(screen_height)) / 2,
+ };
+
+ self.camera.target = .{
+ .x = world_width / 2.0,
+ .y = world_height / 2.0,
+ };
+
+ // Take the average between the ratios
+ // This avoids "cheating" by changing the ratio to an extreme value
+ // in order to see more terrain in a certain axis
+ const width_ratio = @as(f32, @floatFromInt(screen_width)) / world_width;
+ const height_ratio = @as(f32, @floatFromInt(screen_height)) / world_height;
+ self.camera.zoom = (width_ratio + height_ratio) / 2;
+ }
+
+ fn render(self: *ScreenVictory, game: *Game) void {
+ rl.beginMode2D(self.camera);
+ defer rl.endMode2D();
+
+ if (!self.initiated) {
+ var i: usize = 0;
+ const len: usize = self.text.len;
+ assert(len == self.widths.len);
+ while (i < len) : (i += 1) {
+ const substring = self.text[i..];
+ const measurement = rl.measureTextEx(game.font_title, substring, self.size, self.spacing);
+ if (i == 0) {
+ self.height = measurement.y;
+ }
+ self.widths[i] = measurement.x;
+ }
+ self.initiated = true;
+ }
+
+ // total width and height for the game over text
+ const tx = self.widths[0];
+ const ty = self.height;
+
+ // we have widths for the title with the start chopped off.
+ // let's calculate the right side
+ const rx = (world_width + tx) / 2;
+ const ry = (world_height - ty) / 2;
+ var i: usize = 0;
+ const len: usize = self.text.len;
+ while (i < len) {
+ const fi: f32 = @as(f32, @floatFromInt(i));
+ const width = self.widths[i];
+ const x = rx - width;
+ // feel free to find a better way to extract single characters:
+ const char = rl.textFormat("%c", .{self.text[i]});
+ const y = ry - ty * self.animation_size * sin((self.dt + self.animation_offset * fi) * self.speed);
+ rl.drawTextEx(game.font_title, char, .{ .x = x, .y = y }, self.size, self.spacing, self.color);
+ i += 1;
+ }
+
+ self.pressed_menu = labelButton(
+ game,
+ &self.camera,
+ "Back to main menu",
+ rl.Rectangle.init(
+ (world_width - ScreenVictory.button_menu_width) / 2,
+ (world_height - ScreenVictory.button_menu_height) / 2 + cell_size * 1.25,
+ ScreenVictory.button_menu_width,
+ ScreenVictory.button_menu_height,
+ ),
+ .{
+ .font = game.font_title,
+ .font_size = 10,
+ .spacing = 5,
+ },
+ );
+ self.pressed_play_again = labelButton(
+ game,
+ &self.camera,
+ "Play again",
+ rl.Rectangle.init(
+ (world_width - ScreenVictory.button_menu_width) / 2,
+ (world_height - ScreenVictory.button_menu_height) / 2 + cell_size * 2.25,
+ ScreenVictory.button_menu_width,
+ ScreenVictory.button_menu_height,
+ ),
+ .{
+ .font = game.font_normal,
+ .font_size = 10,
+ .spacing = 5,
+ },
+ );
+ }
+};
const ScreenBattle = struct {
camera: rl.Camera2D,
@@ -1134,8 +1286,7 @@ const ScreenBattle = struct {
self.wave_number += 1;
if (self.wave_number > 3) {
self.deinit();
- // TODO: victory screen instead of game over
- game.screen_state = .{ .gameover = .init() };
+ game.screen_state = .{ .victory = .init() };
} else {
self.wave.deinit();
self.wave = .init(self.wave_number);