Revert "Add .gitignore and .gitattributes."

This reverts commit 5c9f574644ecd78b112ea857d658f670ef4773e3.

GitOrigin-RevId: 277054282d105e4a5f185ac51983581c89b8a031
This commit is contained in:
Colin McMillen 2020-01-17 22:41:45 -05:00
parent cb1b78b875
commit ae8fa0d21d
20 changed files with 1017 additions and 0 deletions

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# sneak
mcmillen's first indie game

25
Shared/Camera.cs Normal file
View File

@ -0,0 +1,25 @@
using Microsoft.Xna.Framework;
using System;
// Good background reading, eventually:
// https://gamasutra.com/blogs/ItayKeren/20150511/243083/Scroll_Back_The_Theory_and_Practice_of_Cameras_in_SideScrollers.php
namespace SemiColinGames {
class Camera {
private Rectangle bbox = new Rectangle(0, 0, 1920 / 4, 1080 / 4);
public int Width { get => bbox.Width; }
public int Height { get => bbox.Height; }
public int Left { get => bbox.Left; }
public void Update(GameTime time, Point player) {
int diff = player.X - bbox.Center.X;
if (Math.Abs(diff) > 16) {
bbox.Offset((int) (diff * 0.1), 0);
}
if (bbox.Left < 0) {
bbox.Offset(-bbox.Left, 0);
}
// Debug.Toast($"p: {player.X}, {player.Y} c: {bbox.Center.X}");
}
}
}

78
Shared/Debug.cs Normal file
View File

@ -0,0 +1,78 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace SemiColinGames {
static class Debug {
struct DebugRect {
public Rectangle rect;
public Color color;
public DebugRect(Rectangle rect, Color color) {
this.rect = rect;
this.color = color;
}
}
public static bool Enabled;
static List<DebugRect> rects = new List<DebugRect>();
static Texture2D whiteTexture;
static string toast = null;
public static void Toast(string s) {
toast = s;
}
public static void WriteLine(string s) {
System.Diagnostics.Debug.WriteLine(s);
}
public static void WriteLine(string s, params object[] args) {
System.Diagnostics.Debug.WriteLine(s, args);
}
public static void Initialize(GraphicsDevice graphics) {
whiteTexture = new Texture2D(graphics, 1, 1);
whiteTexture.SetData(new Color[] { Color.White });
}
public static void Clear() {
rects.Clear();
}
public static void AddRect(Rectangle rect, Color color) {
rects.Add(new DebugRect(rect, color));
}
public static void DrawToast(SpriteBatch spriteBatch, SpriteFont font) {
if (toast == null) {
return;
}
spriteBatch.DrawString(font, toast, new Vector2(10, 40), Color.Teal);
toast = null;
}
public static void Draw(SpriteBatch spriteBatch, Camera camera) {
if (!Enabled) {
return;
}
foreach (var debugRect in rects) {
var rect = debugRect.rect;
rect.Offset(-camera.Left, 0);
var color = debugRect.color;
// top side
spriteBatch.Draw(
whiteTexture, new Rectangle(rect.Left, rect.Top, rect.Width, 1), color);
// bottom side
spriteBatch.Draw(
whiteTexture, new Rectangle(rect.Left, rect.Bottom - 1, rect.Width, 1), color);
// left side
spriteBatch.Draw(
whiteTexture, new Rectangle(rect.Left, rect.Top, 1, rect.Height), color);
// right side
spriteBatch.Draw(
whiteTexture, new Rectangle(rect.Right - 1, rect.Top, 1, rect.Height), color);
}
}
}
}

23
Shared/FpsCounter.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
namespace SemiColinGames {
class FpsCounter {
private double fps = 0;
private int[] frameTimes = new int[60];
private int idx = 0;
public int Fps {
get => (int) Math.Ceiling(fps);
}
public void Update() {
var now = Environment.TickCount; // ms
if (frameTimes[idx] != 0) {
var timeElapsed = now - frameTimes[idx];
fps = 1000.0 * frameTimes.Length / timeElapsed;
}
frameTimes[idx] = now;
idx = (idx + 1) % frameTimes.Length;
}
}
}

62
Shared/History.cs Normal file
View File

@ -0,0 +1,62 @@
using System;
namespace SemiColinGames {
// A History is a queue of fixed length N that records the N most recent items Add()ed to it.
// The mostly-recently-added item is found at index 0; the least-recently-added item is at index
// N-1. Items older than the History's size are automatically dropped. The underlying
// implementation is a fixed-size circular array; insertion and access are O(1).
//
// Example:
// h = new History<int>(3);
// h.Add(2); h.Add(3); h.Add(5);
// Console.WriteLine("{0} {1} {2}", h[0], h[1], h[2]); // 5 3 2
// h.Add(7);
// Console.WriteLine("{0} {1} {2}", h[0], h[1], h[2]); // 7 5 3
// h.Add(11); h.Add(13);
// Console.WriteLine("{0} {1} {2}", h[0], h[1], h[2]); // 13 11 7
class History<T> {
// Backing store for the History's items.
private readonly T[] items;
// Points at the most-recently-inserted item.
private int idx = 0;
public History(int length) {
items = new T[length];
}
public void Add(T item) {
idx++;
if (idx >= items.Length) {
idx -= items.Length;
}
items[idx] = item;
}
public int Length {
get => items.Length;
}
public T this[int age] {
get {
if (age < 0 || age >= items.Length) {
throw new IndexOutOfRangeException();
}
int lookup = idx - age;
if (lookup < 0) {
lookup += items.Length;
}
return items[lookup];
}
}
// This creates and populates a new array. It's O(n) and should probably only be used for tests.
public T[] ToArray() {
T[] result = new T[items.Length];
for (int i = 0; i < items.Length; i++) {
result[i] = this[i];
}
return result;
}
}
}

8
Shared/IDisplay.cs Normal file
View File

@ -0,0 +1,8 @@
using Microsoft.Xna.Framework;
namespace SemiColinGames {
public interface IDisplay {
void Initialize(GameWindow window, GraphicsDeviceManager graphics);
void SetFullScreen(bool fullScreen);
}
}

53
Shared/Input.cs Normal file
View File

@ -0,0 +1,53 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace SemiColinGames {
struct Input {
public bool Jump;
public bool Attack;
public Vector2 Motion;
public bool Exit;
public bool FullScreen;
public bool Debug;
public Input(GamePadState gamePad, KeyboardState keyboard) {
// First we process normal buttons.
Jump = gamePad.IsButtonDown(Buttons.A) || gamePad.IsButtonDown(Buttons.B) ||
keyboard.IsKeyDown(Keys.J);
Attack = gamePad.IsButtonDown(Buttons.X) || gamePad.IsButtonDown(Buttons.Y) ||
keyboard.IsKeyDown(Keys.K);
// Then special debugging sorts of buttons.
Exit = gamePad.IsButtonDown(Buttons.Start) || keyboard.IsKeyDown(Keys.Escape);
FullScreen = gamePad.IsButtonDown(Buttons.Back) || keyboard.IsKeyDown(Keys.F12) ||
keyboard.IsKeyDown(Keys.OemPlus);
Debug = gamePad.IsButtonDown(Buttons.LeftShoulder) || keyboard.IsKeyDown(Keys.OemMinus);
// Then potential motion directions. If the player attempts to input opposite directions at
// once (up & down or left & right), those inputs cancel out, resulting in no motion.
Motion = new Vector2();
Vector2 leftStick = gamePad.ThumbSticks.Left;
bool left = leftStick.X < -0.5 || gamePad.IsButtonDown(Buttons.DPadLeft) ||
keyboard.IsKeyDown(Keys.A);
bool right = leftStick.X > 0.5 || gamePad.IsButtonDown(Buttons.DPadRight) ||
keyboard.IsKeyDown(Keys.D);
bool up = leftStick.Y > 0.5 || gamePad.IsButtonDown(Buttons.DPadUp) ||
keyboard.IsKeyDown(Keys.W);
bool down = leftStick.Y < -0.5 || gamePad.IsButtonDown(Buttons.DPadDown) ||
keyboard.IsKeyDown(Keys.S);
if (left && !right) {
Motion.X = -1;
}
if (right && !left) {
Motion.X = 1;
}
if (up && !down) {
Motion.Y = 1;
}
if (down && !up) {
Motion.Y = -1;
}
}
}
}

21
Shared/LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Colin McMillen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

175
Shared/Player.cs Normal file
View File

@ -0,0 +1,175 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace SemiColinGames {
class Player {
enum Facing { Left, Right };
enum Pose { Walking, Standing, Crouching, Stretching, SwordSwing, Jumping };
private const int moveSpeed = 180;
private const int jumpSpeed = -600;
private const int gravity = 2400;
private Texture2D texture;
private const int spriteSize = 48;
private const int spriteWidth = 7;
private Point position = new Point(64, 16);
private int jumps = 0;
private Facing facing = Facing.Right;
private Pose pose = Pose.Jumping;
private double swordSwingTime = 0;
private double jumpTime = 0;
private float ySpeed = 0;
public Player(Texture2D texture) {
this.texture = texture;
}
public Point Position { get { return position; } }
private Rectangle Bbox(Point position) {
return new Rectangle(position.X - spriteWidth, position.Y - 7, spriteWidth * 2, 26);
}
public void Update(GameTime time, History<Input> input, List<Rectangle> collisionTargets) {
Point oldPosition = position;
Vector2 movement = HandleInput(time, input);
position = new Point((int) (oldPosition.X + movement.X), (int) (oldPosition.Y + movement.Y));
Rectangle oldBbox = Bbox(oldPosition);
Rectangle playerBbox = Bbox(position);
bool standingOnGround = false;
foreach (var rect in collisionTargets) {
playerBbox = Bbox(position);
// first we check for left-right collisions...
if (playerBbox.Intersects(rect)) {
if (oldBbox.Right <= rect.Left && playerBbox.Right > rect.Left) {
position.X = rect.Left - spriteWidth;
}
if (oldBbox.Left >= rect.Right && playerBbox.Left < rect.Right) {
position.X = rect.Right + spriteWidth;
}
playerBbox = Bbox(position);
}
// after fixing that, we check for hitting our head or hitting the ground.
if (playerBbox.Intersects(rect)) {
if (oldPosition.Y > position.Y) {
int diff = playerBbox.Top - rect.Bottom;
position.Y -= diff;
// TODO: set ySpeed = 0 here so that bonking our head actually reduces hangtime?
} else {
standingOnGround = true;
int diff = playerBbox.Bottom - rect.Top;
position.Y -= diff;
}
} else {
playerBbox.Height += 1;
if (playerBbox.Intersects(rect)) {
standingOnGround = true;
Debug.AddRect(rect, Color.Cyan);
} else {
Debug.AddRect(rect, Color.Green);
}
}
}
if (standingOnGround) {
jumps = 1;
ySpeed = 0;
Debug.AddRect(playerBbox, Color.Red);
} else {
jumps = 0;
Debug.AddRect(playerBbox, Color.Orange);
}
if (movement.X > 0) {
facing = Facing.Right;
} else if (movement.X < 0) {
facing = Facing.Left;
}
if (swordSwingTime > 0) {
pose = Pose.SwordSwing;
} else if (jumps == 0) {
pose = Pose.Jumping;
} else if (movement.X != 0) {
pose = Pose.Walking;
} else if (input[0].Motion.Y > 0) {
pose = Pose.Stretching;
} else if (input[0].Motion.Y < 0) {
pose = Pose.Crouching;
} else {
pose = Pose.Standing;
}
}
// Returns the desired (dx, dy) for the player to move this frame.
Vector2 HandleInput(GameTime time, History<Input> input) {
Vector2 result = new Vector2();
result.X = (int) (input[0].Motion.X * moveSpeed * time.ElapsedGameTime.TotalSeconds);
if (input[0].Jump && !input[1].Jump && jumps > 0) {
jumpTime = 0.3;
jumps--;
ySpeed = jumpSpeed;
}
if (input[0].Attack && !input[1].Attack && swordSwingTime <= 0) {
swordSwingTime = 0.3;
}
result.Y = ySpeed * (float) time.ElapsedGameTime.TotalSeconds;
ySpeed += gravity * (float) time.ElapsedGameTime.TotalSeconds;
jumpTime -= time.ElapsedGameTime.TotalSeconds;
swordSwingTime -= time.ElapsedGameTime.TotalSeconds;
return result;
}
private int SpriteIndex(Pose pose, GameTime time) {
int frameNum = (time.TotalGameTime.Milliseconds / 125) % 4;
if (frameNum == 3) {
frameNum = 1;
}
switch (pose) {
case Pose.Walking:
return 6 + frameNum;
case Pose.Stretching:
return 18 + frameNum;
case Pose.Jumping:
if (jumpTime > 0.2) {
return 15;
} else if (jumpTime > 0.1) {
return 16;
} else {
return 17;
}
case Pose.SwordSwing:
if (swordSwingTime > 0.2) {
return 30;
} else if (swordSwingTime > 0.1) {
return 31;
} else {
return 32;
}
case Pose.Crouching:
return 25;
case Pose.Standing:
default:
return 7;
}
}
public void Draw(SpriteBatch spriteBatch, Camera camera, GameTime time) {
int index = SpriteIndex(pose, time);
Rectangle textureSource = new Rectangle(index * spriteSize, 0, spriteSize, spriteSize);
Vector2 spriteCenter = new Vector2(spriteSize / 2, spriteSize / 2);
SpriteEffects effect = facing == Facing.Right ?
SpriteEffects.FlipHorizontally : SpriteEffects.None;
Vector2 drawPos = new Vector2(position.X - camera.Left, position.Y);
spriteBatch.Draw(texture, drawPos, textureSource, Color.White, 0f, spriteCenter,
Vector2.One, effect, 0f);
}
}
}

22
Shared/Shared.projitems Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>2785994a-a14f-424e-8e77-2e464d28747f</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>SemiColinGames</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Camera.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Input.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Debug.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FpsCounter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)History.cs" />
<Compile Include="$(MSBuildThisFileDirectory)IDisplay.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Player.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SneakGame.cs" />
<Compile Include="$(MSBuildThisFileDirectory)World.cs" />
</ItemGroup>
</Project>

26
Shared/Shared.shproj Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>2785994a-a14f-424e-8e77-2e464d28747f</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<ItemGroup>
<Compile Include="Camera.cs" />
<Compile Include="Debug.cs" />
<Compile Include="FpsCounter.cs" />
<Compile Include="History.cs" />
<Compile Include="IDisplay.cs" />
<Compile Include="SneakGame.cs" />
<Compile Include="Player.cs" />
<Compile Include="World.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Shared.projitems" />
</ItemGroup>
<Import Project="Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

145
Shared/SneakGame.cs Normal file
View File

@ -0,0 +1,145 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
namespace SemiColinGames {
public class SneakGame : Game {
GraphicsDeviceManager graphics;
RenderTarget2D renderTarget;
SpriteBatch spriteBatch;
SpriteFont font;
bool fullScreen = false;
IDisplay display;
History<Input> input = new History<Input>(2);
FpsCounter fpsCounter = new FpsCounter();
Texture2D grasslandBg1;
Texture2D grasslandBg2;
Player player;
World world;
Camera camera = new Camera();
public SneakGame() {
graphics = new GraphicsDeviceManager(this);
IsMouseVisible = true;
Content.RootDirectory = "Content";
}
// Performs initialization that's needed before starting to run.
protected override void Initialize() {
display = (IDisplay) Services.GetService(typeof(IDisplay));
display.Initialize(Window, graphics);
display.SetFullScreen(fullScreen);
Debug.Initialize(GraphicsDevice);
renderTarget = new RenderTarget2D(
GraphicsDevice, camera.Width, camera.Height, false /* mipmap */,
GraphicsDevice.PresentationParameters.BackBufferFormat, DepthFormat.Depth24);
base.Initialize();
}
// Called once per game. Loads all game content.
protected override void LoadContent() {
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("font");
player = new Player(Content.Load<Texture2D>("player_1x"));
world = new World(Content.Load<Texture2D>("grassland"));
grasslandBg1 = Content.Load<Texture2D>("grassland_bg1");
grasslandBg2 = Content.Load<Texture2D>("grassland_bg2");
}
// Called once per game. Unloads all game content.
protected override void UnloadContent() {
}
// Updates the game world.
protected override void Update(GameTime gameTime) {
Debug.Clear();
input.Add(new Input(GamePad.GetState(PlayerIndex.One), Keyboard.GetState()));
if (input[0].Exit) {
Exit();
}
if (input[0].FullScreen && !input[1].FullScreen) {
fullScreen = !fullScreen;
display.SetFullScreen(fullScreen);
}
if (input[0].Debug && !input[1].Debug) {
Debug.Enabled = !Debug.Enabled;
}
List<Rectangle> collisionTargets = world.CollisionTargets();
player.Update(gameTime, input, collisionTargets);
camera.Update(gameTime, player.Position);
base.Update(gameTime);
}
// Called when the game should draw itself.
protected override void Draw(GameTime gameTime) {
// We need to update the FPS counter in Draw() since Update() might get called more
// frequently, especially when gameTime.IsRunningSlowly.
fpsCounter.Update();
// Draw scene to RenderTarget.
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null);
// Draw background.
Rectangle bgSource = new Rectangle(
(int) (camera.Left * 0.25), 0, camera.Width, camera.Height);
Rectangle bgTarget = new Rectangle(0, 0, camera.Width, camera.Height);
spriteBatch.Draw(grasslandBg2, bgTarget, bgSource, Color.White);
bgSource = new Rectangle(
(int) (camera.Left * 0.5), 0, camera.Width, camera.Height);
spriteBatch.Draw(grasslandBg1, bgTarget, bgSource, Color.White);
// Draw player.
player.Draw(spriteBatch, camera, gameTime);
// Draw foreground tiles.
world.Draw(spriteBatch, camera);
// Draw debug rects.
Debug.Draw(spriteBatch, camera);
// Aaaaand we're done.
spriteBatch.End();
// Draw RenderTarget to screen.
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend,
SamplerState.PointClamp, DepthStencilState.Default,
RasterizerState.CullNone);
Rectangle drawRect = new Rectangle(
0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
spriteBatch.Draw(renderTarget, drawRect, Color.White);
if (Debug.Enabled) {
string fpsText = $"{GraphicsDevice.Viewport.Width}x{GraphicsDevice.Viewport.Height}, " +
$"{fpsCounter.Fps} FPS";
spriteBatch.DrawString(font, fpsText, new Vector2(10, 10), Color.Teal);
Debug.DrawToast(spriteBatch, font);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}

180
Shared/World.cs Normal file
View File

@ -0,0 +1,180 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.Linq;
namespace SemiColinGames {
enum Terrain {
Empty,
Grass,
GrassL,
GrassR,
Rock,
RockL,
RockR,
Water,
Block
}
class Tile {
Texture2D texture;
Terrain terrain;
Rectangle position;
public Tile(Texture2D texture, Terrain terrain, Rectangle position) {
this.texture = texture;
this.terrain = terrain;
this.position = position;
}
public Rectangle Position { get { return position; } }
public Terrain Terrain { get { return terrain; } }
public void Draw(SpriteBatch spriteBatch, Camera camera) {
int size = World.TileSize;
Vector2 drawPos = new Vector2(position.Left - camera.Left, position.Top);
switch (terrain) {
case Terrain.Grass: {
Rectangle source = new Rectangle(3 * size, 0 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.GrassL: {
Rectangle source = new Rectangle(2 * size, 0 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.GrassR: {
Rectangle source = new Rectangle(4 * size, 0 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.Rock: {
Rectangle source = new Rectangle(3 * size, 1 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.RockL: {
Rectangle source = new Rectangle(1 * size, 2 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.RockR: {
Rectangle source = new Rectangle(5 * size, 2 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.Water: {
Rectangle source = new Rectangle(9 * size, 2 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.Block: {
Rectangle source = new Rectangle(6 * size, 3 * size, size, size);
spriteBatch.Draw(texture, drawPos, source, Color.White);
break;
}
case Terrain.Empty:
default:
break;
}
}
}
class World {
public const int TileSize = 16;
int width;
int height;
Tile[,] tiles;
public int Width { get; }
public int Height { get; }
string worldString = @"
X
.
X <======> <==X X <=> <XX> XX .
XXX .
XXXX .
XXXXX .
X <X=X> <> <> <X> = <> X X X X <> X X XX X <=X> XXXXXX .
<> [] [] XX XX XXX XX XXXXXXX .
<> [] [] [] XXX XXX XXXX XXX <> <> XXXXXXXX
[]12345678[]123456[]123456789[]1234567890 123456 123456 12345 1234 12345 1234 123XXXX XXXX1234XXXXX XXXX1234[]123 1234567[]XXXXXXXXX12345678
===========================..========..======..=========..=========> <=============> <==============================================================> <=======..==============..==============================
...................................................................] [.............] [..............................................................] [.......................................................";
public World(Texture2D texture) {
string[] worldDesc = worldString.Split('\n');
width = worldDesc.AsQueryable().Max(a => a.Length);
height = worldDesc.Length;
Debug.WriteLine("world size: {0}x{1}", width, height);
tiles = new Tile[width, height];
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
Terrain terrain = Terrain.Empty;
if (i < worldDesc[j].Length) {
switch (worldDesc[j][i]) {
case '=':
terrain = Terrain.Grass;
break;
case '<':
terrain = Terrain.GrassL;
break;
case '>':
terrain = Terrain.GrassR;
break;
case '.':
terrain = Terrain.Rock;
break;
case '[':
terrain = Terrain.RockL;
break;
case ']':
terrain = Terrain.RockR;
break;
case '~':
terrain = Terrain.Water;
break;
case 'X':
terrain = Terrain.Block;
break;
case ' ':
default:
terrain = Terrain.Empty;
break;
}
}
var position = new Rectangle(i * TileSize, j * TileSize, TileSize, TileSize);
tiles[i, j] = new Tile(texture, terrain, position);
}
}
}
public void Draw(SpriteBatch spriteBatch, Camera camera) {
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
tiles[i, j].Draw(spriteBatch, camera);
}
}
}
public List<Rectangle> CollisionTargets() {
var result = new List<Rectangle>();
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
var t = tiles[i, j];
if (t.Terrain != Terrain.Empty) {
result.Add(t.Position);
}
}
}
return result;
}
}
}

View File

@ -0,0 +1,48 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
namespace SemiColinGames.Tests {
[TestClass]
public class HistoryTests {
[TestMethod]
public void TestLength() {
var h = new History<int>(3);
Assert.AreEqual(3, h.Length);
}
[TestMethod]
public void TestGetFromEmpty() {
var ints = new History<int>(3);
Assert.AreEqual(0, ints[0]);
Assert.AreEqual(0, ints[1]);
Assert.AreEqual(0, ints[2]);
var objects = new History<Object>(1);
Assert.AreEqual(null, objects[0]);
}
[TestMethod]
public void TestAdds() {
var h = new History<int>(3);
Assert.AreEqual("0 0 0", String.Join(" ", h.ToArray()));
h.Add(2);
Assert.AreEqual("2 0 0", String.Join(" ", h.ToArray()));
h.Add(3);
h.Add(5);
Assert.AreEqual("5 3 2", String.Join(" ", h.ToArray()));
h.Add(7);
Assert.AreEqual("7 5 3", String.Join(" ", h.ToArray()));
h.Add(11);
h.Add(13);
Assert.AreEqual("13 11 7", String.Join(" ", h.ToArray()));
}
[TestMethod]
public void TestThrowsExceptions() {
var h = new History<int>(3);
Assert.ThrowsException<IndexOutOfRangeException>(() => h[-1]);
Assert.ThrowsException<IndexOutOfRangeException>(() => h[3]);
}
}
}

View File

@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("SharedTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SharedTests")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("c86694a5-dd99-4421-aa2c-1230f11c10f8")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C86694A5-DD99-4421-AA2C-1230F11C10F8}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SharedTests</RootNamespace>
<AssemblyName>SharedTests</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="HistoryTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="..\Shared\Shared.projitems" Label="Shared" />
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" />
</Project>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSTest.TestAdapter" version="2.0.0" targetFramework="net472" />
<package id="MSTest.TestFramework" version="2.0.0" targetFramework="net472" />
</packages>

21
tools/LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Colin McMillen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,25 @@
sourceUrl = "git@github.com:mcmillen/sneak.git"
destinationUrl = "git@github.com:mcmillen/sneak-public.git"
core.workflow(
name = "default",
mode = "ITERATIVE",
origin = git.origin(
url = sourceUrl,
ref = "master",
),
destination = git.destination(
url = destinationUrl,
fetch = "master",
push = "master",
),
# Change path to the folder you want to publish publicly
origin_files = glob(["README.md", "tools/**", "Shared/**", "SharedTests/**", "Jumpy.Shared/**"]),
authoring = authoring.pass_thru("Unknown Author <nobody@semicolin.games>"),
# Change the path here to the folder you want to publish publicly
#transformations = [
# core.move("", ""),
#],
)

View File

@ -0,0 +1,6 @@
#/bin/sh
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
java -jar ~/bin/copybara_deploy.jar migrate $SCRIPTPATH/copy.bara.sky