From f4581ecaf82fcafd69a1b279f54f37b1824172a9 Mon Sep 17 00:00:00 2001 From: Colin McMillen Date: Thu, 27 Feb 2020 19:13:34 -0500 Subject: [PATCH] Player now takes damage from spikes. GitOrigin-RevId: e9096316218f2590aac74ce1055a0829f71bdff8 --- Shared/Geometry.cs | 7 ++++++- Shared/Player.cs | 37 +++++++++++++++++++++++++++++++------ Shared/SneakGame.cs | 8 ++++++-- Shared/Textures.cs | 4 ++-- Shared/World.cs | 10 ++++++++-- 5 files changed, 53 insertions(+), 13 deletions(-) diff --git a/Shared/Geometry.cs b/Shared/Geometry.cs index 242e0d9..fbc5e43 100644 --- a/Shared/Geometry.cs +++ b/Shared/Geometry.cs @@ -83,10 +83,15 @@ namespace SemiColinGames { public readonly struct AABB { public readonly Vector2 Position; // centroid public readonly Vector2 HalfSize; + public readonly Terrain Terrain; - public AABB(Vector2 position, Vector2 halfSize) { + public AABB(Vector2 position, Vector2 halfSize) : this(position, halfSize, Terrain.Empty) { + } + + public AABB(Vector2 position, Vector2 halfSize, Terrain terrain) { Position = position; HalfSize = halfSize; + Terrain = terrain; } public float Top { diff --git a/Shared/Player.cs b/Shared/Player.cs index 5092649..0494ff2 100644 --- a/Shared/Player.cs +++ b/Shared/Player.cs @@ -34,12 +34,13 @@ namespace SemiColinGames { private const int swordSwingMax = 6; private float ySpeed = 0; private double jumpTime = 0; + private float invincibilityTime = 0; public Player() { Health = MaxHealth; } - public int MaxHealth { get; private set; } = 5; + public int MaxHealth { get; private set; } = 3; public int Health { get; private set; } @@ -56,6 +57,8 @@ namespace SemiColinGames { return BoxOffset(position, 0); } + invincibilityTime -= modelTime; + Vector2 movement = HandleInput(modelTime, input); // Broad test: remove all collision targets nowhere near the player. @@ -71,11 +74,12 @@ namespace SemiColinGames { new Vector2(halfSize.X + Math.Abs(movement.X) + 1, halfSize.Y + Math.Abs(movement.Y) + 1)); foreach (var box in collisionTargets) { if (box.Intersect(largeBox) != null) { - Debug.AddRect(box, Color.Green); + // Debug.AddRect(box, Color.Green); candidates.Add(box); } } + bool harmedByCollision = false; Point[] movePoints = Line.Rasterize(0, 0, (int) movement.X, (int) movement.Y); for (int i = 1; i < movePoints.Length; i++) { int dx = movePoints[i].X - movePoints[i - 1].X; @@ -86,8 +90,12 @@ namespace SemiColinGames { bool reject = false; foreach (var box in candidates) { if (box.Intersect(player) != null) { + Debug.AddRect(box, Color.Cyan); reject = true; - break; + if (box.Terrain.IsHarmful) { + Debug.AddRect(box, Color.Red); + harmedByCollision = true; + } } } if (!reject) { @@ -100,8 +108,12 @@ namespace SemiColinGames { bool reject = false; foreach (var box in candidates) { if (box.Intersect(player) != null) { + Debug.AddRect(box, Color.Cyan); reject = true; - break; + if (box.Terrain.IsHarmful) { + Debug.AddRect(box, Color.Red); + harmedByCollision = true; + } } } if (!reject) { @@ -114,8 +126,12 @@ namespace SemiColinGames { AABB groundIntersect = BoxOffset(position, 1); foreach (var box in candidates) { if (groundIntersect.Intersect(box) != null) { + Debug.AddRect(box, Color.Cyan); standingOnGround = true; - break; + if (box.Terrain.IsHarmful) { + Debug.AddRect(box, Color.Red); + harmedByCollision = true; + } } } @@ -133,6 +149,11 @@ namespace SemiColinGames { Debug.AddRect(Box(position), Color.Orange); } + if (harmedByCollision && invincibilityTime <= 0) { + Health -= 1; + invincibilityTime = 0.6f; + } + if (movement.X > 0) { Facing = 1; } else if (movement.X < 0) { @@ -242,7 +263,11 @@ namespace SemiColinGames { Vector2 spriteCenter = new Vector2(spriteWidth / 2, spriteHeight / 2 + spriteCenterYOffset); SpriteEffects effect = Facing == 1 ? SpriteEffects.FlipHorizontally : SpriteEffects.None; - spriteBatch.Draw(Textures.Player.Get, position.ToVector2(), textureSource, Color.White, 0f, + Color color = Color.White; + if (invincibilityTime > 0 && invincibilityTime % 0.2f > 0.1f) { + color = new Color(0.5f, 0.5f, 0.5f, 0.5f); + } + spriteBatch.Draw(Textures.Player.Get, position.ToVector2(), textureSource, color, 0f, spriteCenter, Vector2.One, effect, 0f); } } diff --git a/Shared/SneakGame.cs b/Shared/SneakGame.cs index d42c56c..cb3c62b 100644 --- a/Shared/SneakGame.cs +++ b/Shared/SneakGame.cs @@ -24,7 +24,7 @@ namespace SemiColinGames { // attempt to draw the scene. This is a workaround for the fact that otherwise the first few // frames can be really slow to draw. int framesToSuppress; - int levelIdx = 0; + int levelIdx = -1; Scene scene; Player player; @@ -73,10 +73,10 @@ namespace SemiColinGames { framesToSuppress = 2; camera = new Camera(); player = new Player(); + levelIdx++; world = new World(Levels.ALL_LEVELS[levelIdx % Levels.ALL_LEVELS.Length]); scene?.Dispose(); scene = new Scene(GraphicsDevice, camera); - levelIdx++; GC.Collect(); GC.WaitForPendingFinalizers(); @@ -122,6 +122,10 @@ namespace SemiColinGames { player.Update(modelTime, input, world.CollisionTargets); linesOfSight.Update(player, world.CollisionTargets); camera.Update(player.Position, world.Width); + if (player.Health <= 0) { + world = new World(Levels.ALL_LEVELS[levelIdx % Levels.ALL_LEVELS.Length]); + player = new Player(); + } } base.Update(gameTime); diff --git a/Shared/Textures.cs b/Shared/Textures.cs index 9fc4f42..0b7ec64 100644 --- a/Shared/Textures.cs +++ b/Shared/Textures.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace SemiColinGames { - class TextureRef { + public class TextureRef { private static readonly List allTextures = new List(); public static void LoadAll(ContentManager content) { @@ -28,7 +28,7 @@ namespace SemiColinGames { } } - static class Textures { + public static class Textures { public static SpriteFont DebugFont; public static SpriteFont BannerFont; diff --git a/Shared/World.cs b/Shared/World.cs index 836c054..f3ac30c 100644 --- a/Shared/World.cs +++ b/Shared/World.cs @@ -6,7 +6,7 @@ using System.Linq; namespace SemiColinGames { - class Terrain { + public class Terrain { public static Terrain FromSymbol(char symbol) { if (mapping.ContainsKey(symbol)) { @@ -18,6 +18,7 @@ namespace SemiColinGames { private readonly static Dictionary mapping = new Dictionary(); + public static Terrain Empty = new Terrain('\0', false, Textures.Grassland, 0, 0); public static Terrain Grass = new Terrain('=', true, Textures.Grassland, 3, 0); public static Terrain GrassL = new Terrain('<', true, Textures.Grassland, 2, 0); public static Terrain GrassR = new Terrain('>', true, Textures.Grassland, 4, 0); @@ -49,6 +50,7 @@ namespace SemiColinGames { public static Terrain Mushroom = new Terrain('t', false, Textures.Grassland, 17, 2); public bool IsObstacle { get; private set; } + public bool IsHarmful { get; private set; } = false; public TextureRef Texture { get; private set; } public Rectangle TextureSource { get; private set; } @@ -58,6 +60,10 @@ namespace SemiColinGames { } mapping[symbol] = this; IsObstacle = isObstacle; + // TODO: don't hard-code just the one spike. + if (symbol == '^') { + IsHarmful = true; + } Texture = texture; int size = World.TileSize; TextureSource = new Rectangle(x * size, y * size, size, size); @@ -138,7 +144,7 @@ namespace SemiColinGames { for (int i = 0; i < tiles.Length; i++) { Vector2 center = new Vector2( tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y); - CollisionTargets[i + 1] = new AABB(center, halfSize); + CollisionTargets[i + 1] = new AABB(center, halfSize, tiles[i].Terrain); } // Add a final synthetic collisionTarget on the right side of the world.