Compare commits

..

8 Commits

File diff suppressed because one or more lines are too long

@ -2,10 +2,22 @@
"infinite":false,
"layers":[
{
"data":[1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"data":[10, 14, 14, 14, 2684354570, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2684354573, 14, 14, 14, 1610612749, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2684354573, 14, 14, 14, 1610612749, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2684354573, 14, 14, 14, 1610612749, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2684354578, 13, 13, 13, 1610612752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":12,
"id":1,
"name":"Tile Layer 1",
"name":"Ground",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":24,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":12,
"id":2,
"name":"Wall",
"opacity":1,
"type":"tilelayer",
"visible":true,
@ -13,7 +25,7 @@
"x":0,
"y":0
}],
"nextlayerid":2,
"nextlayerid":3,
"nextobjectid":1,
"orientation":"orthogonal",
"renderorder":"left-down",
@ -23,7 +35,7 @@
{
"columns":3,
"firstgid":1,
"image":"pond.png",
"image":"..\/..\/MonogameTest\/Maps\/pondv2.bmp",
"imageheight":97,
"imagewidth":97,
"margin":0,
@ -32,6 +44,19 @@
"tilecount":9,
"tileheight":32,
"tilewidth":32
},
{
"columns":3,
"firstgid":10,
"image":"..\/..\/Tilemaps\/grass.png",
"imageheight":96,
"imagewidth":96,
"margin":0,
"name":"grass",
"spacing":0,
"tilecount":9,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../SeeNoEvil/SeeNoEvil.csproj" />
</ItemGroup>
</Project>

@ -1,18 +0,0 @@
using NUnit.Framework;
namespace SeeNoEvil.Tests
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
Assert.Pass();
}
}
}

@ -0,0 +1,140 @@
{
"name": "cat",
"image": "Master-no-background-01",
"width": 32,
"height": 32,
"animations": [
{
"id": 1,
"name": "Walk Left",
"frames": [
{
"id": 1,
"x": 32,
"y": 64,
"timer": 8
},
{
"id": 2,
"x": 64,
"y": 64,
"timer": 8
},
{
"id": 3,
"x": 0,
"y": 64,
"timer": 8
}
]
},
{
"id": 2,
"name": "Walk Right",
"frames": [
{
"id": 1,
"x": 32,
"y": 96,
"timer": 8
},
{
"id": 2,
"x": 64,
"y": 96,
"timer": 8
},
{
"id": 3,
"x": 0,
"y": 96,
"timer": 8
}
]
},
{
"id": 3,
"name": "Walk Up",
"frames": [
{
"id": 1,
"x": 32,
"y": 128,
"timer": 6
},
{
"id": 2,
"x": 0,
"y": 128,
"timer": 6
}
]
},
{
"id": 4,
"name": "Walk Down",
"frames": [
{
"id": 1,
"x": 32,
"y": 160,
"timer": 6
},
{
"id": 2,
"x": 0,
"y": 160,
"timer": 6
}
]
},
{
"id": 5,
"name": "Scared Left",
"frames": [
{
"id": 1,
"x": 96,
"y": 64,
"timer": 0
}
]
},
{
"id": 6,
"name": "Scared Right",
"frames": [
{
"id": 1,
"x": 96,
"y": 96,
"timer": 0
}
]
},
{
"id": 7,
"name": "Scared Up",
"frames": [
{
"id": 1,
"x": 64,
"y": 128,
"timer": 0
}
]
},
{
"id": 8,
"name": "Scared Down",
"frames": [
{
"id": 1,
"x": 64,
"y": 160,
"timer": 0
}
]
}
]
}

@ -0,0 +1,80 @@
{
"name": "ghost",
"image": "Master-no-background-01",
"width": 32,
"height": 32,
"animations": [
{
"id": 1,
"name": "Walk Left",
"frames": [
{
"id": 1,
"x": 0,
"y": 32,
"timer": 6
},
{
"id": 2,
"x": 32,
"y": 32,
"timer": 6
}
]
},
{
"id": 2,
"name": "Walk Right",
"frames": [
{
"id": 1,
"x": 0,
"y": 0,
"timer": 6
},
{
"id": 2,
"x": 32,
"y": 0,
"timer": 6
}
]
},
{
"id": 3,
"name": "Walk Up",
"frames": [
{
"id": 1,
"x": 64,
"y": 0,
"timer": 6
},
{
"id": 2,
"x": 64,
"y": 32,
"timer": 6
}
]
},
{
"id": 4,
"name": "Walk Down",
"frames": [
{
"id": 1,
"x": 96,
"y": 0,
"timer": 6
},
{
"id": 2,
"x": 96,
"y": 32,
"timer": 6
}
]
}
]
}

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace SeeNoEvil.Character {
public class AnimationController {
public string Name {get; private set;}
public string Image {get; private set;}
public int Width {get; private set;}
public int Height {get; private set;}
public bool Idle;
public Dictionary<int, Animation> Animations {get; private set;}
private Animation CurrentAnimation;
public Frame CurrentFrame {
get {
if(Idle) return CurrentAnimation.CurrentFrame;
else return CurrentAnimation.GetFrame();
}
}
public AnimationController(AnimationSetModel model) {
Name = model.Name;
Image = model.Image;
Width = model.Width;
Height = model.Height;
Animations = model.Animations.Aggregate(new Dictionary<int, Animation>(),
(animations, animation) => {
animations.Add(animation.Id, new Animation(animation));
return animations;
});
ChangeAnimation(1);
Idle = true;
}
public void ChangeAnimation(int animationId) {
if(Animations.TryGetValue(animationId, out CurrentAnimation))
CurrentAnimation.Reset();
}
}
public class Animation {
public int Id {get; set;}
public string Name {get; set;}
public FrameCollection Frames {get; set;}
public Frame CurrentFrame;
private int TotalFrames;
private int CurrentFrameId;
public Animation(AnimationModel model) {
Id = model.Id;
Name = model.Name;
Frames = new FrameCollection(model.Frames);
TotalFrames = model.Frames.Count();
}
public void Reset() {
CurrentFrameId = 1;
CurrentFrame = Frames[CurrentFrameId];
}
// TODO Is this super fuckin ugly? Seems like it
public Frame GetFrame() {
Frame result = CurrentFrame;
if(CurrentFrame.Timer == 1) {
CurrentFrameId = CurrentFrameId == TotalFrames ? 1 : CurrentFrameId + 1;
CurrentFrame = Frames[CurrentFrameId];
} else if(CurrentFrame.Timer > 1) CurrentFrame.Timer--;
return result;
}
}
public class FrameCollection {
private IEnumerable<Frame> Frames;
public Frame this[int i] {
get {
return Frames.Where(item => item.Id == i).First();
}
}
public FrameCollection(IEnumerable<Frame> frames) {
Frames = frames;
}
}
}

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace SeeNoEvil.Character {
public struct AnimationSetModel {
public string Name {get; set;}
public string Image {get; set;}
public int Width {get; set;}
public int Height {get; set;}
public IEnumerable<AnimationModel> Animations {get; set;}
}
public struct AnimationModel {
public int Id {get; set;}
public string Name {get; set;}
public IEnumerable<Frame> Frames{get; set;}
}
public struct Frame {
public int Id {get; set;}
public int X {get; set;}
public int Y {get; set;}
public int Timer {get; set;}
}
}

@ -0,0 +1,15 @@
using System.IO;
using System.Text.Json;
namespace SeeNoEvil.Character {
public static class AnimationParser {
public static AnimationSetModel ReadAnimationJson(string fileName) {
StreamReader streamReader = File.OpenText(fileName);
string text = streamReader.ReadToEnd();
var options = new JsonSerializerOptions {
PropertyNameCaseInsensitive = true,
};
return JsonSerializer.Deserialize<AnimationSetModel>(text, options);
}
}
}

@ -0,0 +1,76 @@
using Microsoft.Xna.Framework;
namespace SeeNoEvil.Character {
public class Cat : Character {
public Cat(Vector2 position, Direction facing) : base(position) {
AnimationController = new AnimationController(AnimationParser.ReadAnimationJson("SeeNoEvil/Animation/cat.json"));
Width = AnimationController.Width;
Height = AnimationController.Height;
Facing = facing;
ChooseAnimation(Facing);
}
public override void Move(Direction direction) {
if(!Moving) {
ChooseAnimation(direction);
base.Move(direction);
}
}
public Vector2 GetSight() {
Vector2 sight = Position;
for(int i = 0; i <= 9; i++) {
switch(Facing) {
case Direction.Up:
sight.Y -= Height;
break;
case Direction.Down:
sight.Y += Height;
break;
case Direction.Left:
sight.X -= Width;
break;
case Direction.Right:
sight.X += Width;
break;
}
if(!Field.TryWalk(sight)) break;
}
return sight;
}
public void ChooseAnimation(Direction direction) {
switch(direction) {
case Direction.Up:
AnimationController.ChangeAnimation(3);
break;
case Direction.Down:
AnimationController.ChangeAnimation(4);
break;
case Direction.Left:
AnimationController.ChangeAnimation(1);
break;
case Direction.Right:
AnimationController.ChangeAnimation(2);
break;
}
}
public void Scared() {
switch(Facing) {
case Direction.Up:
AnimationController.ChangeAnimation(7);
break;
case Direction.Down:
AnimationController.ChangeAnimation(8);
break;
case Direction.Left:
AnimationController.ChangeAnimation(5);
break;
case Direction.Right:
AnimationController.ChangeAnimation(6);
break;
}
}
}
}

@ -0,0 +1,94 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using SeeNoEvil.Level;
namespace SeeNoEvil.Character {
public class Character {
private Vector2 Destination {get; set;}
private Vector2 Velocity {get; set;}
public Vector2 Position {get; private set;}
private Texture2D SpriteSheet;
protected Vector2 StartingPosition;
protected AnimationController AnimationController;
protected int Width;
protected int Height;
protected PlayField Field;
public Direction Facing;
public bool Moving =>
!Destination.Equals(Vector2.Zero) &&
!Velocity.Equals(Vector2.Zero) &&
!Position.Equals(Destination);
public Character(Vector2 position) {
Position = position;
StartingPosition = position;
Destination = Vector2.Zero;
Velocity = Vector2.Zero;
}
public void Load(ContentManager content, PlayField playField) {
SpriteSheet = content.Load<Texture2D>(AnimationController.Image);
Field = playField;
}
// TODO Do I want to move every frame?
public void Update() {
if(Moving) {
Position = Vector2.Add(Position, Velocity);
AnimationController.Idle = false;
}
else AnimationController.Idle = true;
}
public void Draw(SpriteBatch spriteBatch) {
Frame currentFrame = AnimationController.CurrentFrame;
Rectangle srcRectangle = new Rectangle(currentFrame.X,
currentFrame.Y,
AnimationController.Width,
AnimationController.Height);
spriteBatch.Draw(SpriteSheet, Position, srcRectangle, Color.White);
}
public virtual void Move(Direction direction) {
if(!Moving) {
int velocity = 16;
int x = 0, y = 0;
switch(direction) {
case Direction.Up:
y = -1;
break;
case Direction.Down:
y = 1;
break;
case Direction.Left:
x = -1;
break;
case Direction.Right:
x = 1;
break;
}
var tryPosition = Vector2.Add(Position, new Vector2(Width*x, Height*y));
if(Field.TryWalk(tryPosition)) {
Destination = Vector2.Add(Position, new Vector2(Width*x, Height*y));
Velocity = new Vector2(x*velocity, y*velocity);
Facing = direction;
}
}
}
public void Reset() {
Position = StartingPosition;
Destination = Vector2.Zero;
Velocity = Vector2.Zero;
}
}
public enum Direction {
Up,
Down,
Left,
Right
}
}

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using SeeNoEvil.Level;
using SeeNoEvil.Tiled;
namespace SeeNoEvil.Character {
public class GhostController {
public List<Ghost> Ghosts {get; set;}
public GhostController(IEnumerable<ObjectCoordinate> coordinates) {
Ghosts = coordinates.Select(
coord => new Ghost(new Vector2(coord.X, coord.Y))
).ToList();
}
public void LoadAll(ContentManager content, PlayField playField) =>
Ghosts.ForEach(ghost => ghost.Load(content, playField));
public void DrawAll(SpriteBatch spriteBatch) =>
Ghosts.ForEach(ghost => ghost.Draw(spriteBatch));
public void UpdateAll() =>
Ghosts.ForEach(ghost => ghost.Update());
public void MoveGhosts() =>
Ghosts.ForEach(ghost => ghost.DecideMove());
public bool AreGhostsHere(Vector2 playerPosition, Vector2 playerSight, Direction direction) =>
Ghosts.Any(ghost => ghost.IsInSight(playerPosition, playerSight, direction));
}
public class Ghost : Character {
public Ghost(Vector2 position) : base(position) {
AnimationController = new AnimationController(AnimationParser.ReadAnimationJson("SeeNoEvil/Animation/ghost.json"));
Width = AnimationController.Width;
Height = AnimationController.Height;
Facing = Direction.Down;
}
public void DecideMove() {
Array values = Enum.GetValues(typeof(Direction));
Random random = new Random();
Direction randomDirection = (Direction)values.GetValue(random.Next(values.Length));
switch(randomDirection) {
case Direction.Up:
AnimationController.ChangeAnimation(3);
break;
case Direction.Down:
AnimationController.ChangeAnimation(4);
break;
case Direction.Left:
AnimationController.ChangeAnimation(1);
break;
case Direction.Right:
AnimationController.ChangeAnimation(2);
break;
}
base.Move(randomDirection);
}
// FIXME it's a bit better now but still
public bool IsInSight(Vector2 playerPosition, Vector2 playerSight, Direction direction) {
if(direction == Direction.Up)
return playerPosition.X == Position.X &&
(playerPosition.Y > Position.Y) && (Position.Y > playerSight.Y);
else if(direction == Direction.Down)
return playerPosition.X == Position.X &&
(playerPosition.Y < Position.Y) && (Position.Y < playerSight.Y);
else if(direction == Direction.Left)
return playerPosition.Y == Position.Y &&
(playerPosition.X > Position.X) && (Position.X > playerSight.X);
else
return playerPosition.Y == Position.Y &&
(playerPosition.X < Position.X) && (Position.X < playerSight.X);
}
}
}

@ -1,80 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SeeNoEvil.Tiled;
using SeeNoEvil.Level;
namespace SeeNoEvil
{
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TilemapLevel level;
Dictionary<string, Texture2D> tilesets;
Camera viewport;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
viewport.height = 720;
viewport.width = 1280;
viewport.x = 0;
viewport.y = 0;
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
level = new TilemapLevel("./Maps/MagicLandCsv.json");
level.LoadMap();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
tilesets = new Dictionary<string, Texture2D>();
tilesets = level.GetTilesetNames().Aggregate(new Dictionary<string, Texture2D>(),
(content, contentName) => {
content.Add(contentName, Content.Load<Texture2D>(contentName));
return content;
});
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
if(Keyboard.GetState().IsKeyDown(Keys.Down))
viewport.y += 16;
if(Keyboard.GetState().IsKeyDown(Keys.Up))
viewport.y -= 16;
if(Keyboard.GetState().IsKeyDown(Keys.Left))
viewport.x -= 16;
if(Keyboard.GetState().IsKeyDown(Keys.Right))
viewport.x += 16;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
level.Draw(viewport, spriteBatch, tilesets);
spriteBatch.End();
base.Draw(gameTime);
}
}
}

@ -1,21 +1,27 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace SeeNoEvil.Level {
public struct Camera {
public int width;
public int height;
public int x;
public int y;
}
public class Camera {
public Matrix Transform {get; private set;}
public Viewport Viewport {get; private set;}
public Vector2 Centre {get; set;}
// public class Camera {
// private Viewport m_dimension;
private Vector3 TranslationVector =>
new Vector3(-Centre.X + (Viewport.Width / 2),
-Centre.Y + (Viewport.Height / 2),
1);
// public Camera(int x, int y) {
// m_dimension.x = x;
// m_dimension.y = x;
// }
public Camera(Viewport _viewport) {
Viewport = _viewport;
//TODO This is to experiment
Centre = new Vector2(Viewport.Width / 2, Viewport.Height / 2);
}
// public Viewport GetDimension() {
// return m_dimension;
// }
// }
//FIXME Don't need velocity anymore?
public void Update(Vector2 position) {
Centre = position;
Transform = Matrix.CreateTranslation(TranslationVector);
}
}
}

@ -1,32 +1,60 @@
using System.Collections.Generic;
using System.Linq;
using SeeNoEvil.Tiled;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using SeeNoEvil.Tiled;
namespace SeeNoEvil.Level {
public class TilemapLevel {
private readonly string MapName;
private TiledMap Map;
private Dictionary<string, Texture2D> TilesetTextures;
public TilemapLevel(string tilemapName) {
MapName = tilemapName;
}
public void LoadMap() {
Map = TiledParser.ReadMapJson(MapName);
public void LoadMap(ContentManager content) {
Map = new TiledMap(TiledParser.ReadMapJson(MapName));
Map.LoadView();
Map.LoadObjects();
TilesetTextures = Map.GetTilesetNames().Aggregate(new Dictionary<string, Texture2D>(),
(textures, contentName) => {
textures.Add(contentName, content.Load<Texture2D>(contentName));
return textures;
});
}
private IEnumerable<string> GetTilesetNames() => Map.GetTilesetNames();
public Vector2 GetPlayerPosition() {
//FIXME This fuckin sucks
Map.Objects.TryGetValue("Cat", out List<ObjectCoordinate> catCoords);
ObjectCoordinate catCoord = catCoords.First();
return new Vector2(catCoord.X, catCoord.Y);
}
public PlayField GetPlayField() {
Map.View.TryGetValue("Ground", out List<TileLocation> ground);
return new PlayField(ground);
}
public IEnumerable<string> GetTilesetNames() {
return Map.GetTilesetNames();
public IEnumerable<ObjectCoordinate> GetGhostCoordinates() {
// FIXME this fuckin sucks too I think?
Map.Objects.TryGetValue("Ghosts", out List<ObjectCoordinate> ghosts);
return ghosts;
}
public void Draw(Camera viewport, SpriteBatch spritebatch, IDictionary<string, Texture2D> this_sucks) {
IEnumerable<TileLocation> locations = Map.DrawView(viewport);
locations.ToList().ForEach(tile => {
Texture2D this_one;
if(tile.tile.gid > 0 && this_sucks.TryGetValue(tile.tile.setName, out this_one)) {
spritebatch.Draw(this_one, tile.location, tile.tile.srcRectangle, Color.White);
public void Draw(SpriteBatch spriteBatch, string layer) {
List<TileLocation> locations;
if(Map.View.TryGetValue(layer, out locations))
locations.ForEach(tile => {
Texture2D layerTexture;
if(tile.tile.gid > 0 && TilesetTextures.TryGetValue(tile.tile.setName, out layerTexture)) {
spriteBatch.Draw(layerTexture, tile.location, tile.tile.srcRectangle, Color.White);
}
});
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using SeeNoEvil.Tiled;
namespace SeeNoEvil.Level {
public class PlayField {
private IEnumerable<TileLocation> Tiles {get; set;}
public PlayField(IEnumerable<TileLocation> tiles) {
Tiles = tiles;
}
public bool TryWalk(Vector2 newLocation) =>
Tiles.Any(tile => tile.location.Equals(newLocation) && tile.tile.gid != 0);
private bool Between(float pos1, float pos2, float bound) =>
(bound - pos1) >= (pos2 - pos1);
}
}

@ -7,7 +7,7 @@ namespace SeeNoEvil
[STAThread]
static void Main()
{
using (var game = new Game1())
using (var game = new SeeNoEvilGame())
game.Run();
}
}

@ -0,0 +1,110 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SeeNoEvil.Level;
using SeeNoEvil.Character;
namespace SeeNoEvil
{
public class SeeNoEvilGame : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TilemapLevel level;
Camera camera;
Cat player;
GhostController enemyController;
PlayField playField;
bool scared;
int scaredTimer = 0;
Ghost debugGhost;
public SeeNoEvilGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
debugGhost = null;
}
protected override void Initialize()
{
// level = new TilemapLevel("./Maps/MagicLandCsv.json");
// level = new TilemapLevel("./Maps/test.json");
level = new TilemapLevel("./Maps/level.json");
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
level.LoadMap(Content);
playField = level.GetPlayField();
player = new Cat(level.GetPlayerPosition(), Direction.Right);
player.Load(Content, playField);
enemyController = new GhostController(level.GetGhostCoordinates());
enemyController.LoadAll(Content, playField);
camera = new Camera(GraphicsDevice.Viewport);
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
if(scaredTimer == 0) {
if(Keyboard.GetState().IsKeyDown(Keys.Down)) {
player.Move(Direction.Down);
enemyController.MoveGhosts();
}
if(Keyboard.GetState().IsKeyDown(Keys.Up)) {
player.Move(Direction.Up);
enemyController.MoveGhosts();
}
if(Keyboard.GetState().IsKeyDown(Keys.Left)) {
player.Move(Direction.Left);
enemyController.MoveGhosts();
}
if(Keyboard.GetState().IsKeyDown(Keys.Right)) {
player.Move(Direction.Right);
enemyController.MoveGhosts();
}
player.Update();
enemyController.UpdateAll();
camera.Update(player.Position);
if(!player.Moving) {
scared = enemyController.AreGhostsHere(player.Position, player.GetSight(), player.Facing);
}
if(scared) {
player.Scared();
scaredTimer = 60;
}
} else if(scaredTimer == 1) {
scared = false;
player.Reset();
player.ChooseAnimation(player.Facing);
scaredTimer = 0;
} else scaredTimer--;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(
transformMatrix: camera.Transform
);
level.Draw(spriteBatch, "Sky");
level.Draw(spriteBatch, "Ground");
level.Draw(spriteBatch, "Bushes");
player.Draw(spriteBatch);
enemyController.DrawAll(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}

@ -0,0 +1,40 @@
// TODO Handle flipped tiles!
using System.Collections.Generic;
using System.Linq;
using SeeNoEvil.Level;
namespace SeeNoEvil.Tiled {
public class TileLayer {
public string Type {get; private set;}
public string Name {get; private set;}
public readonly List<DataCoordinate> DataCoordinates = new List<DataCoordinate>();
private int Width;
private int Height;
private List<uint> Data;
public TileLayer(MapLayerModel model) {
Name = model.Name;
Width = model.Width;
Height = model.Height;
Type = model.Type;
if(model.Type == "tilelayer") {
Data = model.Data.ToList();
BuildCoordinates();
}
}
private void BuildCoordinates() {
int row = 0;
int column = 0;
Data.ForEach(gid => {
DataCoordinates.Add(new DataCoordinate(column, row, gid));
if(column == Width-1) {
row++;
column = 0;
} else column++;
});
}
}
}

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
//TODO Do all these need to be public?
namespace SeeNoEvil.Tiled {
//TODO Can probably be a struct?
public class ObjectLayer {
public IEnumerable<ObjectCoordinate> Objects {get; private set;}
public string Name {get; private set;}
public ObjectLayer(MapLayerModel model) {
Name = model.Name;
Objects = model.Objects.Select(o => new ObjectCoordinate(o.Id,
o.X,
o.Y));
}
}
public struct ObjectCoordinate {
public ObjectCoordinate(int id, float x, float y) {
X = (int)Math.Round(x / 32)*32;
Y = (int)Math.Round(y / 32)*32;
Id = id;
}
public int X {get; set;}
public int Y {get; set;}
public int Id {get; set;}
}
}

@ -0,0 +1,77 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace SeeNoEvil.Tiled {
public class TileSet {
private string Image {get; set;}
private string Name {get; set;}
private int ImageHeight {get; set;}
private int ImageWidth {get; set;}
private int TileHeight {get; set;}
private int TileWidth {get; set;}
private int Spacing {get; set;}
private int Columns {get; set;}
private int FirstGid {get; set;}
private int TileCount {get; set;}
private Dictionary<uint, Tile> Tiles {get; set;}
private bool TilesLoaded {get; set;}
public TileSet(TileSetModel model) {
Image = model.Image;
Name = model.Name;
ImageHeight = model.ImageHeight;
ImageWidth = model.ImageWidth;
TileHeight = model.TileHeight;
TileWidth = model.TileWidth;
Spacing = model.Spacing;
Columns = model.Columns;
FirstGid = model.FirstGid;
TileCount = model.TileCount;
Tiles = new Dictionary<uint, Tile>();
TilesLoaded = false;
}
public bool ContainsTile(uint gid) =>
!(gid < FirstGid || gid > (FirstGid + TileCount - 1));
public Tile GetTile(uint gid) {
if(!TilesLoaded) LoadTiles();
Tile result;
Tiles.TryGetValue(gid, out result);
return result;
}
public string GetContentName() {
return Name;
}
private void LoadTiles() {
Tiles = new Dictionary<uint, Tile>();
int row = 0;
int rowPx = 0;
int column = 0;
int columnPx = 0;
for(int i = 0; i < TileCount; i++) {
Rectangle rectangle = new Rectangle(
columnPx,
rowPx,
TileWidth,
TileHeight
);
uint gid = (uint)(i + FirstGid);
Tiles.Add(gid, new Tile(gid, rectangle, Name));
if(column == Columns-1) {
row++;
rowPx += Spacing + TileHeight;
column = 0;
columnPx = 0;
} else {
column++;
columnPx += Spacing + TileWidth;
}
}
TilesLoaded = true;
}
}
}

@ -2,56 +2,71 @@ using System.Collections.Generic;
using Microsoft.Xna.Framework;
using System.Linq;
using SeeNoEvil.Level;
using System.Text.Json.Serialization;
namespace SeeNoEvil.Tiled {
public class TiledMap {
public TiledMap() => TileCache = new Dictionary<uint, Tile>();
public Dictionary<string, List<TileLocation>> View {get; private set;}
public Dictionary<string, List<ObjectCoordinate>> Objects {get; private set;}
private bool Infinite;
private List<TileLayer> TileLayers;
private List<ObjectLayer> ObjectLayers;
private List<TileSet> TileSets;
private int TileHeight;
private int TileWidth;
private int Rows;
private int Columns;
// TODO Do I really need this?
private Dictionary<uint, Tile> TileCache;
public TiledMap(TiledModel model) {
Infinite = model.Infinite;
TileLayers = model.Layers.Where(model => model.Type == "tilelayer")
.Select(model => new TileLayer(model)).ToList();
ObjectLayers = model.Layers.Where(model => model.Type == "objectgroup")
.Select(model => new ObjectLayer(model)).ToList();
TileSets = model.TileSets.Select(model => new TileSet(model)).ToList();
TileHeight = model.TileHeight;
TileWidth = model.TileWidth;
Rows = model.Height;
Columns = model.Width;
TileCache = new Dictionary<uint, Tile>();
View = new Dictionary<string, List<TileLocation>>();
}
public bool Infinite {get; set;}
public IEnumerable<MapLayer> Layers {get; set;}
public IEnumerable<TileSet> TileSets {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
private Dictionary<uint, Tile> TileCache {get; set;}
public IEnumerable<string> GetTilesetNames() =>
TileSets.Select(tileset => tileset.GetContentName());
public IEnumerable<TileLocation> DrawView(Camera viewport) {
Dictionary<string, IEnumerable<DataCoordinate>> layerData = new Dictionary<string, IEnumerable<DataCoordinate>>();
Layers.Where(layer => layer.Type == "tilelayer").ToList()
// Load Tile Locations for each layer
public void LoadView() {
// Get all tilelayers
Dictionary<string, List<DataCoordinate>> layerData = new Dictionary<string, List<DataCoordinate>>();
TileLayers.Where(layer => layer.Type == "tilelayer").ToList()
.ForEach(layer => {
layerData.Add(layer.Name, layer.GetVisibleTiles(viewport, TileHeight, TileWidth));
layerData.Add(layer.Name, layer.DataCoordinates);
});
HashSet<uint> gids = layerData.Aggregate(new HashSet<uint>(),
// Get all required unique gids and load to cache
LoadTiles(layerData.Aggregate(new HashSet<uint>(),
(tiles, layer) => {
layer.Value.ToList().ForEach(tile => tiles.Add(tile.gid));
foreach(var tile in layer.Value) {
tiles.Add(tile.gid);
}
return tiles;
});
IDictionary<uint, Tile> tiles = GetTilesetTiles(gids);
// return layerData.Aggregate(new List<TileLocation>(),
// (locations, layer) => {
// layer.Value.ToList().ForEach(coord => {
// Tile currTile;
// if(!tiles.TryGetValue(coord.gid, out currTile)) {
// currTile = new Tile();
// }
// Vector2 pxLocation = new Vector2(coord.x*TileWidth, coord.y*TileHeight);
// locations.Add(new TileLocation(currTile, pxLocation));
// });
// return locations;
// });
int column = 0, row = 0, columnPx = 0, rowPx = 0;
int viewportRows = viewport.height / TileHeight;
int viewportColumns = viewport.width / TileWidth;
return layerData.Aggregate(new List<TileLocation>(),
})
);
// build resulting tile location collection for each layer
View = layerData.Aggregate(new Dictionary<string, List<TileLocation>>(),
(locations, layer) => {
layer.Value.ToList().ForEach(coord => {
Tile currTile;
if(!tiles.TryGetValue(coord.gid, out currTile)) {
currTile = new Tile();
}
int column = 0, row = 0, columnPx = 0, rowPx = 0;
var layerLocations = new List<TileLocation>();
layer.Value.ForEach(coord => {
Tile currTile = new Tile();
if(coord.gid > 0)
TileCache.TryGetValue(coord.gid, out currTile);
Vector2 pxLocation = new Vector2(columnPx, rowPx);
locations.Add(new TileLocation(currTile, pxLocation));
if(column == viewportColumns) {
layerLocations.Add(new TileLocation(currTile, pxLocation));
if(column == (Columns-1)) {
row++;
rowPx += TileHeight;
column = columnPx = 0;
@ -60,175 +75,32 @@ namespace SeeNoEvil.Tiled {
columnPx += TileWidth;
}
});
locations.Add(layer.Key, layerLocations);
return locations;
});
}
public IEnumerable<string> GetTilesetNames() =>
TileSets.Aggregate(new List<string>(),
(names, tileset) => {
names.Add(tileset.GetContentName());
return names;
});
private IDictionary<uint, Tile> GetTilesetTiles(HashSet<uint> tiles) {
Dictionary<uint, Tile> loadedTiles = new Dictionary<uint, Tile>();
tiles.ToList().ForEach(gid => {
TileSet tileset = TileSets.FirstOrDefault(set => set.ContainsTile(gid));
Tile tile = tileset == null ? new Tile() : tileset.GetTile(gid);
loadedTiles.Add(gid, tile);
});
return loadedTiles;
}
}
public struct TileLocation {
public TileLocation(Tile tile, Vector2 location) {
this.tile = tile;
this.location = location;
}
public Tile tile;
public Vector2 location;
// Load objects on the level
public void LoadObjects() {
Objects = ObjectLayers.Aggregate(new Dictionary<string, List<ObjectCoordinate>>(),
// FIXME This is stupid naming
(layers, objects) => {
layers.Add(objects.Name, objects.Objects.ToList());
return layers;
}
public struct Tile {
public Tile(long _gid, Rectangle _srcRectangle, string _setName) {
gid = (uint)_gid;
srcRectangle = _srcRectangle;
setName = _setName;
}
public uint gid;
public Rectangle srcRectangle;
public string setName;
}
public class TileSet {
public TileSet() {
TilesLoaded = false;
}
public string Image {get; set;}
public string Name {get; set;}
public int ImageHeight {get; set;}
public int ImageWidth {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Spacing {get; set;}
public int Columns {get; set;}
public int FirstGid {get; set;}
public int TileCount {get; set;}
private IDictionary<uint, Tile> Tiles {get; set;}
private bool TilesLoaded {get; set;}
public Tile GetTile(uint gid) {
if(!TilesLoaded) {
LoadTiles();
}
Tile result;
Tiles.TryGetValue(gid, out result);
return result;
}
public bool ContainsTile(uint gid) {
return !(gid < FirstGid ||
gid > (FirstGid + TileCount - 1));
}
public string GetContentName() {
return Name;
}
private void LoadTiles() {
Tiles = new Dictionary<uint, Tile>();
int row = 0;
int rowPx = 0;
int column = 0;
int columnPx = 0;
for(int i = 0; i < TileCount; i++) {
Rectangle rectangle = new Rectangle(
columnPx,
rowPx,
TileWidth,
TileHeight
);
uint gid = (uint)(i + FirstGid);
Tiles.Add(gid, new Tile(gid, rectangle, Name));
if(column == Columns-1) {
row++;
rowPx += Spacing + TileHeight;
column = 0;
columnPx = 0;
} else {
column++;
columnPx += Spacing + TileWidth;
}
}
TilesLoaded = true;
}
}
public struct DataCoordinate {
public DataCoordinate(int _x, int _y, uint _gid) {
x = _x;
y = _y;
gid = _gid;
// Load tiles into cache if they don't exist
private void LoadTiles(HashSet<uint> tiles) {
foreach(uint gid in tiles) {
if(!TileCache.ContainsKey(gid)) {
TileSet tileset = TileSets.FirstOrDefault(set => set.ContainsTile(gid));
Tile tile = tileset == null ? new Tile() : tileset.GetTile(gid);
TileCache.Add(gid, tile);
}
public int x;
public int y;
public uint gid;
}
public class MapLayer {
public MapLayer() { }
public string Name {get; set;}
public int Width {get; set;}
public int Height {get; set;}
public IEnumerable<uint> Data {get; set;}
public string Type {get; set;}
public IEnumerable<DataCoordinate> DataCoordinates {get; set;}
public bool BuiltCoordinates {get; private set;}
public IEnumerable<DataCoordinate> GetVisibleTiles(Camera viewport, int tileHeight, int tileWidth) {
if(!BuiltCoordinates) {
BuildCoordinates();
}
int startColumn = viewport.x / tileWidth;
int endColumn = startColumn + (viewport.width / tileWidth);
int startRow = viewport.y / tileHeight;
int endRow = startRow + (viewport.height / tileHeight);
var coords = DataCoordinates.Where(coord =>
startRow <= coord.y &&
endRow >= coord.y &&
startColumn <= coord.x &&
endColumn >= coord.x
).ToList();
return DataCoordinates.Where(coord =>
startRow <= coord.y &&
endRow >= coord.y &&
startColumn <= coord.x &&
endColumn >= coord.x
);
}
private void BuildCoordinates() {
int row = 0;
int column = 0;
List<DataCoordinate> coordinates = new List<DataCoordinate>();
Data.ToList().ForEach(gid => {
coordinates.Add(new DataCoordinate(column, row, gid));
if(column == Width-1) {
row++;
column = 0;
} else column++;
});
DataCoordinates = coordinates;
BuiltCoordinates = true;
}
}
}

@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace SeeNoEvil.Tiled {
public struct TiledModel {
public bool Infinite {get; set;}
public IEnumerable<MapLayerModel> Layers {get; set;}
public IEnumerable<TileSetModel> TileSets {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Height {get; set;}
public int Width {get; set;}
}
public struct TileSetModel {
public string Image {get; set;}
public string Name {get; set;}
public int ImageHeight {get; set;}
public int ImageWidth {get; set;}
public int TileHeight {get; set;}
public int TileWidth {get; set;}
public int Spacing {get; set;}
public int Columns {get; set;}
public int FirstGid {get; set;}
public int TileCount {get; set;}
}
public struct MapLayerModel {
public string Name {get; set;}
public int Width {get; set;}
public int Height {get; set;}
public IEnumerable<uint> Data {get; set;}
public string Type {get; set;}
public IEnumerable<TiledObjectModel> Objects {get; set;}
}
public struct TiledObjectModel {
public int Id {get; set;}
public float X {get; set;}
public float Y {get; set;}
}
}

@ -3,13 +3,13 @@ using System.Text.Json;
namespace SeeNoEvil.Tiled {
public static class TiledParser {
public static TiledMap ReadMapJson(string fileName) {
public static TiledModel ReadMapJson(string fileName) {
StreamReader streamReader = File.OpenText(fileName);
string text = streamReader.ReadToEnd();
var options = new JsonSerializerOptions {
PropertyNameCaseInsensitive = true,
};
return JsonSerializer.Deserialize<TiledMap>(text, options);
return JsonSerializer.Deserialize<TiledModel>(text, options);
}
}
}

@ -0,0 +1,36 @@
using Microsoft.Xna.Framework;
namespace SeeNoEvil.Tiled {
public struct Tile {
public Tile(long _gid, Rectangle _srcRectangle, string _setName) {
gid = (uint)_gid;
srcRectangle = _srcRectangle;
setName = _setName;
}
public uint gid;
public Rectangle srcRectangle;
public string setName;
}
public struct TileLocation {
public TileLocation(Tile tile, Vector2 location) {
this.tile = tile;
this.location = location;
}
public Tile tile;
public Vector2 location;
}
public struct DataCoordinate {
public DataCoordinate(int _x, int _y, uint _gid) {
x = _x;
y = _y;
gid = _gid;
}
public int x;
public int y;
public uint gid;
}
}
Loading…
Cancel
Save