Added exit/completion of the game/level.

This commit is contained in:
Sander Schobers 2023-06-05 05:56:54 +02:00
parent bbd8a56209
commit f97496a343
9 changed files with 77 additions and 34 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -7,6 +7,8 @@ pub const Object = @import("engine/object.zig").Object;
pub const OpaquePtr = @import("engine/opaque_ptr.zig").OpaquePtr;
pub const Point = @import("engine/point.zig").Point;
pub const PointF = @import("engine/point_f.zig").PointF;
pub const pt = Point.init;
pub const ptF = PointF.init;
pub const Rectangle = @import("engine/rectangle.zig").Rectangle;
pub const RectangleF = @import("engine/rectangle_f.zig").RectangleF;
pub const Scene = @import("engine/scene.zig").Scene;

View File

@ -5,6 +5,7 @@ pub const Point = struct {
x: i64,
y: i64,
pub const One = Point{ .x = 1, .y = 1 };
pub const Zero = Point{ .x = 0, .y = 0 };
pub fn init(x: i64, y: i64) Point {

View File

@ -6,6 +6,8 @@ pub const PointF = struct {
x: f32,
y: f32,
pub const Half = PointF{ .x = 0.5, .y = 0.5 };
pub const One = PointF{ .x = 1, .y = 1 };
pub const Zero = PointF{ .x = 0, .y = 0 };
pub fn init(x: f32, y: f32) PointF {

View File

@ -12,12 +12,18 @@ pub const Game = struct {
Right,
};
pub const State = enum {
InProgress,
Over,
Completed,
};
pub const TileSize = 32;
prng: std.rand.DefaultPrng = std.rand.DefaultPrng.init(0),
level: Level,
isOver: bool = false,
state: State = .InProgress,
starsCollected: usize = 0,
starsTotal: usize,
@ -70,7 +76,7 @@ pub const Game = struct {
}
pub fn drawSpriteFrame(self: *Game, spriteName: []const u8, frame: usize, x: i64, y: i64) void {
self.drawSpriteFrameP(spriteName, frame, engine.Point.init(x, y).float());
self.drawSpriteFrameP(spriteName, frame, engine.pt(x, y).float());
}
pub fn drawSpriteFrameP(self: *Game, spriteName: []const u8, frame: usize, position: engine.PointF) void {
@ -87,13 +93,13 @@ pub const Game = struct {
}
pub fn movePlayer(self: *Game, dt: f32) void {
if (self.isOver) return;
if (self.state != .InProgress) return;
const to = self.playerPosition.add(self.playerVelocity.multiply(dt));
self.playerPosition = to;
if (self.playerPosition.y > 21.5) {
self.isOver = true;
self.state = .Over;
return;
}
@ -124,21 +130,22 @@ pub const Game = struct {
}
self.playerDirection = if (self.playerVelocity.x == 0) self.playerDirection else if (self.playerVelocity.x > 0) Direction.Right else Direction.Left;
const playerCenter = self.playerPosition.add(engine.PointF.init(0.5, 0.5)).floor().integer();
const playerCenter = self.playerPosition.add(engine.PointF.Half).floor().integer();
var dx: i64 = -1;
while (dx <= 1) : (dx += 1) {
var dy: i64 = -1;
while (dy <= 1) : (dy += 1) {
const x = playerCenter.x + dx;
const y = playerCenter.y + dy;
const collectablePosition = engine.Point.init(x, y);
const collectablePosition = engine.pt(x, y);
if (self.level.collectables.get(x, y)) |collectable| {
if (self.collected.hasValue(x, y, true)) continue;
if (self.playerPosition.distance2(collectablePosition.float()) > 1.5) continue;
switch (collectable) {
Level.Collectable.Star => {
.Exit => self.state = .Completed,
.Star => {
self.starsCollected += 1;
self.collected.set(x, y, true) catch {};
},
@ -177,25 +184,25 @@ pub const Game = struct {
// return values are in view coordinates
pub fn tile(self: Game, x: f32, y: f32) engine.PointF {
return self.tileP(engine.PointF.init(x, y));
return self.tileP(engine.ptF(x, y));
}
pub fn tileCenter(self: Game, x: i64, y: i64) engine.PointF {
return self.tileCenterP(engine.Point.init(x, y));
return self.tileCenterP(engine.pt(x, y));
}
pub fn tileCenterP(self: Game, position: engine.Point) engine.PointF {
const center = engine.PointF.init(0.5, 0.5);
const center = engine.PointF.Half;
return self.tileP(position.float().add(center));
}
pub fn tileP(self: Game, position: engine.PointF) engine.PointF {
const relative = position.subtract(self.boundsInTiles.min);
return engine.PointF.init(relative.x / self.boundsInTiles.width(), relative.y / self.boundsInTiles.height());
return engine.ptF(relative.x / self.boundsInTiles.width(), relative.y / self.boundsInTiles.height());
}
pub fn tileTopLeft(self: Game, x: i64, y: i64) engine.PointF {
return self.tileTopLeftP(engine.Point.init(x, y));
return self.tileTopLeftP(engine.pt(x, y));
}
pub fn tileTopLeftP(self: Game, p: engine.Point) engine.PointF {

View File

@ -8,6 +8,7 @@ pub const Level = struct {
};
pub const Collectable = enum {
Exit,
Star,
};
@ -44,9 +45,10 @@ pub const Level = struct {
for (line, 0..) |tile, column| {
var x = @intCast(i64, column);
switch (tile) {
'P' => character = engine.Point.init(x, y),
'S' => try collectables.set(x, y, Collectable.Star),
'x' => try tiles.set(x, y, Tile.Grass),
'P' => character = engine.pt(x, y),
'S' => try collectables.set(x, y, .Star),
'E' => try collectables.set(x, y, .Exit),
'x' => try tiles.set(x, y, .Grass),
else => {},
}
}

View File

@ -24,7 +24,7 @@ pub const GameScene = struct {
}
pub fn handle(self: *GameScene, ctx: *Context, event: *allegro.Event) !void {
if (self.game.isOver) {
if (self.game.state != .InProgress) {
switch (event.type) {
allegro.c.ALLEGRO_EVENT_KEY_DOWN => {
switch (event.keyboard.keycode) {
@ -50,7 +50,7 @@ pub const GameScene = struct {
const maxFallVelocity: f32 = 23;
const maxHorizontalWalkVelocity: f32 = 7; // tiles/s
if (self.game.isOver) {
if (self.game.state != .InProgress) {
self.game.playerIdleAnimation.tick(t);
return;
}
@ -116,13 +116,16 @@ pub const GameScene = struct {
const randomDirtOffset = self.game.randomTileOffset(x, 99, 3);
self.game.drawSpriteFrame("tiles_dirt_32", 0 + randomDirtOffset, x, 19);
self.game.drawSpriteFrame("tiles_dirt_32", 3 + randomDirtOffset, x, 20);
}
var y = tileBounds.min.y;
while (y <= tileBounds.max.y) : (y += 1) {
var y = tileBounds.min.y;
while (y <= tileBounds.max.y) : (y += 1) {
x = tileBounds.min.x;
while (x < tileBounds.max.x) : (x += 1) {
if (tiles.get(x, y)) |tile| {
switch (tile) {
game.Level.Tile.Grass => {
const ordinals = tiles.getOrdinals(x, y, game.Level.Tile.Grass);
.Grass => {
const ordinals = tiles.getOrdinals(x, y, .Grass);
var offset: usize = 17;
switch (ordinals.count()) {
1 => {
@ -156,10 +159,14 @@ pub const GameScene = struct {
if (self.game.collected.hasValue(x, y, true)) continue;
switch (collectable) {
game.Level.Collectable.Star => {
const distanceToPlayer = engine.Point.init(x, y).float().distance(self.game.playerPosition);
.Star => {
const distanceToPlayer = engine.pt(x, y).float().distance(self.game.playerPosition);
self.game.drawSpriteFrame("item_star_32", self.game.starAnimation.currentOffset(@floatToInt(usize, @mod(distanceToPlayer * 0.54, 1) * 16)), x, y);
},
.Exit => {
const position = self.game.tileP(engine.pt(x, y).float().subtract(engine.ptF(0.5, 1)));
renderer.drawTextureV("exit_portal", position.x, position.y);
},
}
}
}
@ -167,11 +174,11 @@ pub const GameScene = struct {
const playerDirectionFrameOffset: usize = if (self.game.playerDirection == game.Game.Direction.Left) 0 else 12;
if (self.game.playerVelocity.y != 0) {
self.game.drawSpriteFrameP("character_lion_48", self.game.playerFallingAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.PointF.init(-0.25, -0.25)));
self.game.drawSpriteFrameP("character_lion_48", self.game.playerFallingAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.ptF(-0.25, -0.25)));
} else if (self.game.playerVelocity.x != 0 and self.game.playerVelocity.y == 0) {
self.game.drawSpriteFrameP("character_lion_48", self.game.playerWalkingAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.PointF.init(-0.25, -0.25)));
self.game.drawSpriteFrameP("character_lion_48", self.game.playerWalkingAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.ptF(-0.25, -0.25)));
} else {
self.game.drawSpriteFrameP("character_lion_48", self.game.playerIdleAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.PointF.init(-0.25, -0.25)));
self.game.drawSpriteFrameP("character_lion_48", self.game.playerIdleAnimation.current + playerDirectionFrameOffset, self.game.playerPosition.add(engine.ptF(-0.25, -0.25)));
}
ctx.renderer.printTextV("default", ctx.palette.background.text, 0.01, 0.01, .Left, "Stars collected: {d}", .{self.game.starsCollected});
@ -181,15 +188,25 @@ pub const GameScene = struct {
ctx.renderer.drawTintedSpriteFrameV("pie_charts_24", percentage - 1, ctx.palette.background.text, 0.97, 0.01);
}
if (self.game.isOver) {
const textColor = allegro.mapRgb(0x48, 0x91, 0x00);
const textColor = allegro.mapRgb(0x48, 0x91, 0x00);
if (self.game.state != .InProgress) {
ctx.renderer.textures.get("opaque").?.drawTintedScaled(allegro.mapRgba(0, 0, 0, 191), 0, 0, @intToFloat(f32, ctx.renderer.display.width()), @intToFloat(f32, ctx.renderer.display.height()));
ctx.renderer.drawTextV("default", textColor, 0.5, 0.2, .Center, "Game over");
ctx.renderer.drawSpriteFrameV("text_balloons", 0, 0.48, 0.40);
ctx.renderer.drawTextV("small", ctx.palette.background.text, 0.547, 0.436, .Center, "Do you want to");
ctx.renderer.drawTextV("small", ctx.palette.background.text, 0.547, 0.465, .Center, "try again?");
ctx.renderer.drawSpriteFrameV("character_lion_48", self.game.playerIdleAnimation.current + 12, 0.44, 0.57);
ctx.renderer.drawTextV("small", textColor, 0.5, 0.7, .Center, "[enter] try again | [escape] quit");
if (self.game.state == .Over) {
ctx.renderer.drawTextV("default", textColor, 0.5, 0.2, .Center, "Game over");
}
if (self.game.state == .Completed) {
ctx.renderer.drawTextV("default", textColor, 0.5, 0.2, .Center, "Level completed");
}
if (self.game.starsCollected < self.game.starsTotal) {
ctx.renderer.drawSpriteFrameV("text_balloons", 0, 0.45, 0.40);
ctx.renderer.drawTextV("small", ctx.palette.background.text, 0.517, 0.436, .Center, "Do you want to");
ctx.renderer.drawTextV("small", ctx.palette.background.text, 0.517, 0.465, .Center, "try again?");
ctx.renderer.drawSpriteFrameV("character_lion_48", self.game.playerIdleAnimation.current + 12, 0.41, 0.57);
ctx.renderer.drawTextV("small", textColor, 0.5, 0.7, .Center, "[enter] try again | [escape] quit");
}
}
if (ctx.showDebug) {

View File

@ -44,6 +44,7 @@ pub fn main() !void {
allegro.setNewBitmapFlags(allegro.NewBitmapFlags{ .MIN_LINEAR = false, .MAG_LINEAR = false });
try renderer.textures.addFromFile("character_lion_48", paths.AssetsDir ++ "/images/character_lion_48.png");
try renderer.textures.addFromFile("exit_portal", paths.AssetsDir ++ "/images/exit_portal.png");
try renderer.textures.addFromFile("item_star_32", paths.AssetsDir ++ "/images/item_star_32.png");
try renderer.textures.addFromFile("pie_charts_24", paths.AssetsDir ++ "/images/pie_charts_24.png");
try renderer.textures.addFromFile("text_balloons", paths.AssetsDir ++ "/images/text_balloons.png");

View File

@ -62,6 +62,17 @@ pub const Renderer = struct {
}
}
pub fn drawTexture(self: Renderer, textureName: []const u8, x: f32, y: f32) void {
if (self.textures.get(textureName)) |texture| {
texture.drawScaledUniform(x, y, self.viewport.scale);
}
}
pub fn drawTextureV(self: Renderer, textureName: []const u8, x: f32, y: f32) void {
const screen = self.viewport.viewToScreen(x, y);
self.drawTexture(textureName, screen.x, screen.y);
}
pub fn drawTextV(self: Renderer, fontName: []const u8, color: allegro.Color, x: f32, y: f32, alignment: TextAlignment, text: [*:0]const u8) void {
const screen = self.viewport.viewToScreen(x, y);
self.drawText(fontName, color, screen.x, screen.y, alignment, text);