Commits

André Schnabel committed a5ff833 Draft

Adding files for initial import

Comments (0)

Files changed (56)

XNAPlayground.sln

+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XNAPlayground", "XNAPlayground\XNAPlayground\XNAPlayground.csproj", "{5E2A8597-927A-458E-84D9-944B9FC7C692}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XNAPlaygroundContent", "XNAPlayground\XNAPlaygroundContent\XNAPlaygroundContent.contentproj", "{B1896433-948C-47D0-9A47-335D8FF3B856}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Windows Phone Kopie von XNAPlayground", "XNAPlayground\XNAPlayground\Windows Phone Kopie von XNAPlayground.csproj", "{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|Windows Phone = Debug|Windows Phone
+		Debug|x86 = Debug|x86
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|Windows Phone = Release|Windows Phone
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|Mixed Platforms.Build.0 = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|Windows Phone.ActiveCfg = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|Windows Phone.Build.0 = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|x86.ActiveCfg = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Debug|x86.Build.0 = Debug|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|Mixed Platforms.Build.0 = Release|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|Windows Phone.ActiveCfg = Release|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|Windows Phone.Build.0 = Release|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|x86.ActiveCfg = Release|x86
+		{5E2A8597-927A-458E-84D9-944B9FC7C692}.Release|x86.Build.0 = Release|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Debug|Windows Phone.ActiveCfg = Debug|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Debug|x86.ActiveCfg = Debug|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Release|Mixed Platforms.ActiveCfg = Release|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Release|Windows Phone.ActiveCfg = Release|x86
+		{B1896433-948C-47D0-9A47-335D8FF3B856}.Release|x86.ActiveCfg = Release|x86
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Mixed Platforms.ActiveCfg = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Mixed Platforms.Build.0 = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Mixed Platforms.Deploy.0 = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Windows Phone.ActiveCfg = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Windows Phone.Build.0 = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|Windows Phone.Deploy.0 = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Debug|x86.ActiveCfg = Debug|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Mixed Platforms.ActiveCfg = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Mixed Platforms.Build.0 = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Mixed Platforms.Deploy.0 = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Windows Phone.ActiveCfg = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Windows Phone.Build.0 = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|Windows Phone.Deploy.0 = Release|Windows Phone
+		{A0C4C5B5-296D-46E4-89C2-19C4E7F0F8E3}.Release|x86.ActiveCfg = Release|Windows Phone
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

XNAPlayground/XNAPlayground/Background.png

Added
New image

XNAPlayground/XNAPlayground/Entities/DeathAnim.cs

+using Microsoft.Xna.Framework;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Entities {
+	internal class DeathAnim {
+		public const int NumFrames = 5;
+		private const long FrameDur = 250;
+		public Vector2 Pos;
+		public long SpawnTicks;
+
+		public DeathAnim(Vector2 pos) {
+			SpawnTicks = Utils.GetTicks();
+			Pos = pos;
+		}
+
+		public int GetFrameNr() {
+			long num = (Utils.GetTicks() - SpawnTicks)/FrameDur;
+			if(num >= NumFrames)
+				return -1;
+			return (int) num;
+		}
+	}
+}

XNAPlayground/XNAPlayground/Entities/Gib.cs

+using Microsoft.Xna.Framework;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Entities {
+	internal class Gib {
+		public Vector2 Pos;
+		public float Alpha;
+		public long SpawnTicks;
+		public int Type;
+		public const int NumGibs = 3;
+
+		public Gib(Vector2 pos, float alpha) {
+			Pos = pos;
+			Alpha = alpha;
+			SpawnTicks = Utils.GetTicks();
+			Type = Utils.Rand.Next(NumGibs);
+		}
+	}
+}

XNAPlayground/XNAPlayground/Entities/Player.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using XNAPlayground.Map;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Entities {
+	internal class Player : IDisposable {
+		public static readonly string[] Frames = new[] {"StandingLeft", "StandingRight", "WalkingLeft1", "WalkingLeft2", "WalkingRight1", "WalkingRight2"};
+		private const int NumWalkFrames = 2;
+		private const long FrameTicks = 40;
+		private const float MaxAccel = 5;
+		private const long StepFreq = 150;
+
+		private enum Dir {
+			LEFT,
+			RIGHT
+		}
+
+		private int curWalkFrame;
+		private long lastWalkFrameChg;
+		private Dir direction;
+		private float xaccel;
+		private float yaccel;
+		public Vector2 Pos;
+		private readonly Dictionary<string, Rectangle> regions;
+		private readonly GameMap map;
+		public float Width;
+		public float Height;
+		private readonly Sound jumpSnd;
+		private readonly Sound hitSnd;
+		private readonly Sound stepSnd;
+		private readonly Sound coinSnd;
+		private readonly List<Gib> gibs;
+
+		public Player(Vector2 pos, Dictionary<string, Rectangle> regions, GameMap map, List<Gib> gibs) {
+			this.gibs = gibs;
+			Pos = pos;
+			this.regions = regions;
+			Width = regions[Frames[0]].Width*0.8f;
+			Height = regions[Frames[0]].Height*0.8f;
+			this.map = map;
+			lastWalkFrameChg = Utils.GetTicks();
+			direction = Dir.LEFT;
+			RecalcCorners();
+			jumpSnd = new Sound("jump");
+			hitSnd = new Sound("hit");
+			stepSnd = new Sound("step");
+			coinSnd = new Sound("coin");
+		}
+
+		public void Reset(float x, float y) {
+			float xaccelT = yaccel = 0f;
+			xaccel = xaccelT;
+			Pos.X = x;
+			Pos.Y = y;
+			RecalcCorners();
+			RecalcCornersSolid();
+			hitSnd.Play();
+			//Gdx.input.vibrate(250);
+		}
+
+		public void Dispose() {
+			stepSnd.Dispose();
+			jumpSnd.Dispose();
+			hitSnd.Dispose();
+			coinSnd.Dispose();
+		}
+
+		public void Draw(SpriteBatch sb) {
+			bool standing = Math.Abs(GetXaccel()) < 0.1f;
+
+			if(Utils.GetTicks() - lastWalkFrameChg > FrameTicks) {
+				curWalkFrame = (curWalkFrame + 1)%NumWalkFrames;
+				lastWalkFrameChg = Utils.GetTicks();
+			}
+
+			int frameNum;
+
+			if(standing) {
+				frameNum = direction == Dir.LEFT ? 0 : 1;
+			}
+			else {
+				if(direction == Dir.LEFT) {
+					frameNum = 2 + curWalkFrame;
+				}
+				else {
+					frameNum = 4 + curWalkFrame;
+				}
+			}
+
+			sb.Draw(regions[Frames[frameNum]], Pos.X, Pos.Y);
+		}
+
+		public void MoveLeft(float delta) {
+			direction = Dir.LEFT;
+			MoveCommon(delta, -1f);
+		}
+
+		public void MoveRight(float delta) {
+			direction = Dir.RIGHT;
+			MoveCommon(delta, 1f);
+		}
+
+		private void MoveCommon(float delta, float factor) {
+			xaccel = GetXaccel() + factor*100.0f*delta;
+
+			if(GetXaccel() < 0 && GetXaccel() < -MaxAccel)
+				xaccel = -MaxAccel;
+			else if(GetXaccel() > 0 && GetXaccel() > MaxAccel)
+				xaccel = MaxAccel;
+		}
+
+		//private bool wallJump;
+		public void Jump() {
+			if(OnTopOfSolid() && !BelowSolid()) {
+				yaccel += 10.0f;
+				RecalcCornersSolid();
+				jumpSnd.Play();
+			}
+		}
+
+		public bool BelowSolid() {
+			/*recalcCorners();
+		recalcCornersSolid();*/
+			return map.IsSolidAtPos(corners[0].X, corners[0].Y + 0.2f) || map.IsSolidAtPos(corners[1].X, corners[1].Y + 0.2f);
+		}
+
+		public bool OnTopOfSolid() {
+			return map.IsSolidAtPos(corners[2].X, corners[2].Y - 0.2f) || map.IsSolidAtPos(corners[3].X, corners[3].Y - 0.2f);
+		}
+
+		public void RecalcCorners() {
+			corners[0] = Pos + (new Vector2(0.01f, 0.99f*Height)); // top left
+			corners[1] = Pos + (new Vector2(0.99f*Width, 0.99f*Height)); // top right
+			corners[2] = Pos + (new Vector2(0.01f, 0.01f)); // bottom left
+			corners[3] = Pos + (new Vector2(0.99f*Width, 0.01f)); // bottom right
+		}
+
+		public void RecalcCornersSolid() {
+			for(int i = 0; i < corners.Length; i++)
+				cornersSolid[i] = map.IsSolidAtPos(corners[i]);
+		}
+
+		private readonly Vector2[] corners = new Vector2[4];
+		private readonly bool[] cornersSolid = new bool[4];
+		private long lastStepTicks;
+
+		public bool InSolid() {
+			return corners.Any(t => map.IsSolidAtPos(t));
+		}
+
+		public Rectangle GetRect() {
+			return new Rectangle((int) Pos.X, (int) Pos.Y, (int) Width, (int) Height);
+		}
+
+		//private long jumpNum;
+		public bool Update(float delta) {
+			// horizontal movement		
+			Vector2 oldPos = Pos;
+
+			Pos.X += GetXaccel();
+
+			switch(Globals.Difficulty) {
+				case Globals.DifficultyLevel.Easy:
+					xaccel = GetXaccel()*0.8f; // Easy
+					break;
+				default:
+				case Globals.DifficultyLevel.Normal:
+					xaccel = GetXaccel()*0.94f; // Normal
+					break;
+				case Globals.DifficultyLevel.Hard:
+					xaccel = GetXaccel()*0.95f; // Hard
+					break;
+				case Globals.DifficultyLevel.Impossible:
+					xaccel = GetXaccel()*0.99f; // Impossible
+					break;
+			}
+
+			RecalcCorners();
+
+			if(InSolid()) {
+				Pos = oldPos;
+
+				RecalcCornersSolid();
+				if(cornersSolid[1] && cornersSolid[3]) {
+// && !wallJump && jumpNum < 4) { // hit right
+					yaccel += 10.0f;
+					direction = Dir.LEFT;
+					xaccel = GetXaccel() - 10.0f;
+					//wallJump = true;
+					jumpSnd.Play();
+					//jumpNum++;
+				}
+
+				if(cornersSolid[0] && cornersSolid[2]) {
+// && !wallJump && jumpNum < 4) { // hit left
+					yaccel += 10.0f;
+					direction = Dir.RIGHT;
+					xaccel = GetXaccel() + 10.0f;
+					//wallJump = true;
+					jumpSnd.Play();
+					//jumpNum++;
+				}
+			}
+
+			// vertical movement
+			oldPos = Pos;
+
+			yaccel = Math.Min(15.0f, yaccel);
+
+			Pos.Y += yaccel;
+			yaccel *= 0.9f;
+
+			RecalcCorners();
+
+			if(!OnTopOfSolid()) {
+				yaccel -= 0.5f;
+			}
+			else {
+				if(Math.Abs(GetXaccel()) > 0.1f && Utils.GetTicks() - lastStepTicks > StepFreq && !BelowSolid()) {
+					long id = stepSnd.Play();
+					stepSnd.SetVolume(id, 0.5f);
+					lastStepTicks = Utils.GetTicks();
+					gibs.Add(new Gib(Pos + (new Vector2(0.0f, Height/2)), 0.0f));
+				}
+			}
+
+			if(OutOfScr() || InSolid()) {
+				Pos = oldPos;
+			}
+
+			if(map.AtGoal(GetRect())) {
+				hitSnd.Play();
+				return true;
+			}
+
+			return false;
+		}
+
+		private bool OutOfScr() {
+			return Pos.X < 0 || Pos.X + Width + 32 >= Globals.ScrW || Pos.Y < 0 || Pos.Y + Height + 32 >= Globals.ScrH;
+		}
+
+		public void Reset() {
+			Reset(Pos.X, Pos.Y);
+		}
+
+		public void PickupCoin() {
+			long id = coinSnd.Play();
+			coinSnd.SetVolume(id, 0.5f);
+		}
+
+		public float GetXaccel() {
+			return xaccel;
+		}
+	}
+}

XNAPlayground/XNAPlayground/Entities/Razorblade.cs

+using Microsoft.Xna.Framework;
+
+namespace XNAPlayground.Entities {
+	internal class Razorblade {
+		public Vector2 Pos;
+		public float Alpha; // degrees
+		public static int RazorW;
+		public static int RazorH;
+		public bool Bloody;
+
+		public Razorblade(Vector2 pos, float alpha) {
+			Pos = pos;
+			Alpha = alpha;
+		}
+
+		public Rectangle GetRect() {
+			return new Rectangle((int) Pos.X, (int) Pos.Y, RazorW, RazorH);
+		}
+	}
+}

XNAPlayground/XNAPlayground/Entities/Spike.cs

+using System;
+using Microsoft.Xna.Framework;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Entities {
+	internal class Spike {
+		public static int SpikeW;
+		public static int SpikeH;
+		public Vector2 Pos;
+		public float ScaleY;
+		private float ctr;
+		public bool Bloody;
+
+		public Spike(Vector2 pos) {
+			Pos = pos;
+			ctr = (float) Utils.Rand.NextDouble()*360.0f;
+			Update();
+		}
+
+		public void Update() {
+			ctr += 0.5f; //Gdx.graphics.getDeltaTime();
+			ScaleY = (float)Math.Sin(MathHelper.ToRadians(ctr));
+		}
+
+		public bool IsActive() {
+			return ScaleY >= 0.5f;
+		}
+
+		public Rectangle GetRect() {
+			return new Rectangle((int) Pos.X, (int) Pos.Y, SpikeW, SpikeH);
+		}
+	}
+}

XNAPlayground/XNAPlayground/Game.ico

Added
New image

XNAPlayground/XNAPlayground/GameThumbnail.png

Added
New image

XNAPlayground/XNAPlayground/Globals.cs

+namespace XNAPlayground
+{
+	class Globals
+	{
+		public enum DifficultyLevel {
+			Easy,
+			Normal,
+			Hard,
+			Impossible
+		}
+
+		public static DifficultyLevel Difficulty = DifficultyLevel.Normal;
+
+		public const int ScrW = 800;
+		public const int ScrH = 480;
+
+		public static float ActualScrW = 800;
+		public static float ActualScrH = 480;
+	}
+}

XNAPlayground/XNAPlayground/MainGame.cs

+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Graphics;
+using XNAPlayground.Map;
+using XNAPlayground.Screens;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground {
+	public class MainGame : Game {
+		private readonly GraphicsDeviceManager graphics;
+
+		public static Matrix TransformMatrix {
+			get; private set;
+		}
+
+		public MainGame() {
+			instance = this;
+			graphics = new GraphicsDeviceManager(this);
+			Content.RootDirectory = "Content";
+			IsMouseVisible = true;
+#if !WINDOWS && !MAC
+			graphics.IsFullScreen = true;
+#endif
+		}
+
+		protected override void Initialize() {
+			TransformMatrix = Matrix.Identity;
+#if IOS
+			int actualW, actualH;
+			actualW = graphics.GraphicsDevice.DisplayMode.Width;
+			actualH = graphics.GraphicsDevice.DisplayMode.Height;
+			TransformMatrix *= Matrix.CreateScale(actualH / 800.0f, actualW / 480.0f, 1.0f);
+			Globals.ActualScrW = actualH;
+			Globals.ActualScrH = actualW;
+#else
+			graphics.PreferredBackBufferWidth = Globals.ScrW;
+			graphics.PreferredBackBufferHeight = Globals.ScrH;
+#endif
+
+			base.Initialize();
+		}
+
+		protected override void LoadContent() {
+			var spriteFont = Content.Load<SpriteFont>("sprfont");
+
+			Helpers.InitSheets(this);
+			Helpers.SetDefaultFont(spriteFont);
+
+			GameMap.LoadMapInfos();
+			SetScreen(new SplashScreen(this));
+		}
+
+		protected override void UnloadContent() {
+			Utils.SafeDispose(ref screen);
+			Helpers.DisposeSheets();
+		}
+
+		protected override void Update(GameTime gameTime) {
+			Utils.DeltaTime = gameTime.ElapsedGameTime.Ticks / 10000000.0f;
+
+			screen.Update();
+			base.Update(gameTime);
+		}
+
+		protected override void Draw(GameTime gameTime) {
+			Utils.DeltaTime = gameTime.ElapsedGameTime.Ticks / 10000000.0f;
+
+			GraphicsDevice.Clear(Color.Black);
+			screen.Render();
+			base.Draw(gameTime);
+		}
+
+		private Screen screen;
+
+		public void SetScreen(Screen newScreen) {
+			Utils.SafeDispose(ref screen);
+			screen = newScreen;
+			screen.Init();
+		}
+
+		public new void Exit() {
+			Utils.SafeDispose(ref screen);
+			Helpers.DisposeSheets();
+			Music.DisposeAll();
+			Sound.DisposeAll();
+			base.Exit();
+		}
+
+		private static MainGame instance;
+
+		public static MainGame GetInstance() {
+			return instance;
+		}
+	}
+}

XNAPlayground/XNAPlayground/Map/GameMap.cs

+using System.IO;
+using Microsoft.Xna.Framework;
+
+namespace XNAPlayground.Map {
+	internal class GameMap {
+		public const int MapW = 25;
+		public const int MapH = 15;
+		public const int CellW = 32;
+		public const int CellH = 32;
+		public const int NumMaps = 8;
+		public static MapInfo[] MapInfos = new MapInfo[NumMaps];
+		public const string SaveFilename = "LD23TinyWorld0x17Savegame";
+		private static readonly string[][] MapList = new[] {new[] {"4444444444444444444444444", "1    R5R CCC R4R  CCC RR5", "1  G  5       4         5", "1     5       4   6     5", "1     5                 5", "1     6                 5", "1          s   R  s     5", "1         53333333333   5", "1       s 5             5", "1    444444 4           5", "1      4    5Rs Cs sR C 5", "1  CC  3    5222222222225", "1      3  6             5", "1RRRRRR3s       1R1  S  5", "1111111111111111111111111"}, new[] {"1111111111111111111111111", "1        3R G  R5RRRRRRR1", "1        3   6  5       1", "1    s   3     R5       1", "1    2CCC3  44445R      1", "1    RCCC3 s    s       1", "1    2CCC3111 11111     1", "1    2CCC3  1R1         1", "1    2CCC3              1", "1    RCCCR      6       1", "1    2CCC3              1", "1    2CCC3              1", "1  S 2CCCR      6       1", "R    Rsss    s     s    1", "1111111111111111111111111"}, new[] {"1111111111111111111111111", "1SR     ss  s s s s s  G1", "1 R3  8777777777777777771", "1 R3    4  C C C       R1", "1 R3       CCCCC       C1", "1 R3 4 4   C C C       C1", "1                      C1", "1 R3 s  RCCCs   sCCCR  R1", "1 R7777777779   877779 C1", "1 R                    C1", "1 R  87777777777777779 C1", "1 R      s      s      C1", "1 R77777777777777779   C1", "1 ss  ss  ss  ss  ss   R1", "1111111111111111111111111"}, new[] {"1111111111111111111111111", "1C        2           CG1", "1C    C   2          sCC1", "1C        2     s    6  1", "1C        2    879      1", "1         2             1", "1             s       s 1", "1            3333   43331", "1               5       1", "1    s          44444   1", "1    6   6              1", "1                       1", "1             ss   ss   1", "1S   CCCCC    8777779 C 1", "11RRRRRRRRRRRRRRRRRRRRRR1"}, new[] {"4444444444444444444444444", "1       C  s   CCCCC    G", "1 8777R777777777777777791", "1          s            1", "13333C3333333333C333333 1", "1          s            R", "1 R55C5555555555C555555 1", "1          s            1", "14444C4444444444C444444 1", "1         CsC           R", "1 R33C3333333333C33333331", "1          s            1", "12222C2222222222C22222R 1", "1S     C   s    C       1", "1111111111111111111111111",}, new[] {"1111111111111111111111111", "1S2 R 4     RRRRRRRRRRRR1", "1 2   4 3               1", "1 2   4 3  6            1", "1 2 3 4 3    6          1", "1 2 3 4 3      6        1", "1 2 3 4 3        6      1", "1 2 3 4 3          6    1", "1 2 3 4 3            6  1", "1 2 3 4 3RRRRRRRRRRRR   1", "1 2 3 4 32222222222222  1", "1 2 3 4 3               1", "1 2 3 4 3 8777777777779R1", "1 s 3 s 3              G1", "1111111111111111111111111",}, new[] {"1111111111111111111111111", "1S                      R", "1879 C        C   C  6CCR", "1G3  6 C    C 6   6  3CCR", "1 3 R  6 C  6   R  RR3CCR", "1 3   R  6    R      3CCR", "1 3     R   R        3CCR", "1 3       R          3CCR", "1 3     R   R       43CCR", "1 3   R       R    443CCR", "1 3 R           R 4443CCR", "1 3CCCCCCCCCCCCCC44443CCR", "1 34455555555555555553CCR", "1s       s      s      sR", "1111111111111111111111111",}, new[] {"1111111111111111111111111", "1S  C             C     1", "14444444444444444444444 1", "1       C     C       5 R", "1 2111R11111111R11111 5 1", "1 2         C       2 5 1", "1 2 3444R44444R4444 R 5 R", "1 2 3    C    C  GR 2 5 1", "1 2 3 11111RR111115 2 5 1", "1 2 3  C        C  sR 5 R", "1 2 87777777777777779 5 1", "1 2  C            C  s5 1", "1 333333333333333333335 R", "1 C                C   s1", "1111111111111111111111111",}};
+		private string[] map;
+		private int curMapNum;
+		private Rectangle goalRect;
+
+		public GameMap(int mapNum) {
+			curMapNum = mapNum;
+			map = MapList[curMapNum];
+		}
+
+		public void NextMap() {
+			curMapNum++;
+			map = MapList[curMapNum];
+		}
+
+		public char GetCellAt(int x, int y) {
+			if(x >= map[0].Length || x < 0) return ' ';
+			if(y >= map.Length || y < 0) return ' ';
+			return map[MapH - 1 - y][x];
+		}
+
+		public char GetCellAtPos(Vector2 pos) {
+			return GetCellAtPos(pos.X, pos.Y);
+		}
+
+		public char GetCellAtPos(float x, float y) {
+			int itsY = (int) y/CellH;
+			int itsX = (int) x/CellW;
+			return GetCellAt(itsX, itsY);
+		}
+
+		public static bool IsSolid(char c) {
+			return c != ' ' && c != 'S' && c != 'G' && c != 'R' && c != 'C' && c != 's';
+		}
+
+		public bool AtGoal(Rectangle rect) {
+			return goalRect.Intersects(rect);
+		}
+
+		public bool IsSolidAtPos(float x, float y) {
+			return IsSolid(GetCellAtPos(x, y));
+		}
+
+		public bool IsSolidAtPos(Vector2 v) {
+			return IsSolidAtPos(v.X, v.Y);
+		}
+
+		public void SelectMap(int mapNum) {
+			curMapNum = mapNum;
+			map = MapList[curMapNum];
+			for(int x = 0; x < MapW; x++)
+				for(int y = 0; y < MapH; y++)
+					if (GetCellAt(x, y) == 'G')
+						goalRect = new Rectangle(x*CellW, y*CellH, CellW, CellH);
+		}
+
+		public void Finish(long ticks) {
+			MapInfos[curMapNum].Finished = true;
+			MapInfos[curMapNum].TicksNeeded = ticks;
+			SaveMapInfos();
+		}
+
+		public static void LoadMapInfos() {
+			MapInfo[] infos = MapInfos;
+
+			for(int i = 0; i < infos.Length; i++) {
+				infos[i] = new MapInfo(false, 0);
+			}
+#if WINDOWS
+			int counter = 0;
+			if(File.Exists(SaveFilename)) {
+				string fstr = File.ReadAllText(SaveFilename);
+				string[] lines = fstr.Split('\n');
+				foreach(string line in lines) {
+					if(counter >= infos.Length) break;
+					string[] parts = line.Split(':');
+					infos[counter].Finished = parts[0].Equals("yes");
+					infos[counter].TicksNeeded = long.Parse(parts[1]);
+					counter++;
+				}
+			}
+#endif
+		}
+
+		public static void SaveMapInfos() {
+#if WINDOWS
+			var mapInfoStr = "";
+			foreach(MapInfo info in MapInfos) {
+				mapInfoStr += (info.Finished) ? "yes" : "no";
+				mapInfoStr += ":";
+				mapInfoStr += info.TicksNeeded;
+				mapInfoStr += "\n";
+			}
+
+			File.WriteAllText(SaveFilename, mapInfoStr);
+#endif
+		}
+	}
+}

XNAPlayground/XNAPlayground/Map/MapInfo.cs

+namespace XNAPlayground.Map {
+	internal class MapInfo {
+		public bool Finished;
+		public long TicksNeeded;
+
+		public MapInfo(bool finished, long ticksNeeded) {
+			Finished = finished;
+			TicksNeeded = ticksNeeded;
+		}
+	}
+}

XNAPlayground/XNAPlayground/PhoneGameThumb.png

Added
New image

XNAPlayground/XNAPlayground/Program.cs

+#if IOS
+using MonoTouch.UIKit;
+using MonoTouch.Foundation;
+#elif MAC
+using MonoMac.AppKit;
+using MonoMac.Foundation;
+#endif
+
+namespace XNAPlayground {
+#if WINDOWS || XBOX
+	internal static class Program {
+		private static void Main(string[] args) {
+			using(var game = new MainGame()) {
+				game.Run();
+			}
+		}
+	}
+#elif IOS
+	[Register("AppDelegate")]
+    class Program : UIApplicationDelegate {
+		MainGame g;
+        public override bool FinishedLaunching(UIApplication app, NSDictionary options) {
+			g = new MainGame();
+			g.Run();
+			return true;
+		}
+		
+		static void Main(string[] args) {
+			UIApplication.Main(args, null, "AppDelegate");
+		}
+    }
+#elif MAC
+	static class Program {
+		static void Main (string[] args) {
+			NSApplication.Init ();
+			
+			using (var p = new NSAutoreleasePool ()) {
+				NSApplication.SharedApplication.Delegate = new AppDelegate();
+				NSApplication.Main(args);
+			}
+		}
+	}
+	
+	class AppDelegate : NSApplicationDelegate {
+		public override void FinishedLaunching (MonoMac.Foundation.NSObject notification) {
+			MainGame game = new MainGame();
+			game.Run ();
+		}
+		
+		public override bool ApplicationShouldTerminateAfterLastWindowClosed (NSApplication sender) {
+			return true;
+		}
+	}
+#elif ANDROID
+	[Activity (Label = "TimoratusXNADroid", MainLauncher = true)]
+	public class Activity : AndroidGameActivity {
+		protected override void OnCreate(Bundle bundle) {
+			base.OnCreate(bundle);
+			MainGame.Activity = this;
+			var g = new MainGame();
+			SetContentView(g.Window);
+			g.Run();
+		}
+	}
+#endif
+}

XNAPlayground/XNAPlayground/Properties/AppManifest.xml

+<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+>
+    <Deployment.Parts>
+    </Deployment.Parts>
+</Deployment>

XNAPlayground/XNAPlayground/Properties/AssemblyInfo.cs

+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über den folgenden 
+// Satz von Attributen kontrolliert. Ändern Sie diese Attributwerte, um die mit einer Assembly
+// verbundenen Informationen zu ändern.
+[assembly: AssemblyTitle("XNAPlayground")]
+[assembly: AssemblyProduct("XNAPlayground")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright ©  2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Bei Einstellung von ComVisible auf falsch sind die Typen in dieser Assembly für 
+// COM-Komponenten nicht sichtbar.  Wenn Sie von COM aus auf einen Typ in dieser Assembly 
+// zugreifen müssen, stellen Sie das Attribut ComVisible bei diesem Typ auf wahr ein. Nur Windows-
+// Assemblys unterstützen COM.
+[assembly: ComVisible(false)]
+
+// Auf Windows gilt die folgende GUID für die ID von typelib, wenn dieses
+// COM ausgesetzt ist. Auf anderen Plattformen identifiziert sie den
+// Titelspeichercontainer bei Bereitstellung dieser Assembly auf dem Gerät eindeutig.
+[assembly: Guid("2d303273-0dd4-4f91-87db-37f02fb4605a")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+//      Größere Version
+//      Kleinere Version 
+//      Build-Nummer
+//      Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]

XNAPlayground/XNAPlayground/Properties/WMAppManifest.xml

+<?xml version="1.0" encoding="utf-8" ?>
+<Deployment xmlns="http://schemas.microsoft.com/windowsphone/2009/deployment" AppPlatformVersion="7.1">
+  <App xmlns="" ProductID="{a0c4c5b5-296d-46e4-89c2-19c4e7f0f8e3}" Title="XNAPlayground" RuntimeType="XNA" Version="1.0.0.0" Genre="Apps.Normal" Author="" Description="" Publisher="">
+    <IconPath IsRelative="true" IsResource="false"></IconPath>
+    <Capabilities>
+      <Capability Name="ID_CAP_NETWORKING" />
+      <Capability Name="ID_CAP_LOCATION" />
+      <Capability Name="ID_CAP_SENSORS" />
+      <Capability Name="ID_CAP_MICROPHONE" />
+      <Capability Name="ID_CAP_MEDIALIB" />
+      <Capability Name="ID_CAP_GAMERSERVICES" />
+      <Capability Name="ID_CAP_PHONEDIALER" />
+      <Capability Name="ID_CAP_PUSH_NOTIFICATION" />
+      <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
+      <Capability Name="ID_CAP_IDENTITY_USER" />
+      <Capability Name="ID_CAP_IDENTITY_DEVICE" />
+
+      <!-- Windows Phone OS 7.1 Capabilities -->
+      <Capability Name="ID_CAP_ISV_CAMERA" />
+      <Capability Name="ID_CAP_CONTACTS" />
+      <Capability Name="ID_CAP_APPOINTMENTS" />
+    </Capabilities>
+    <Tasks>
+      <DefaultTask Name="_default"/>
+    </Tasks>
+    <Tokens>
+      <PrimaryToken TokenID="XNAPlaygroundToken" TaskName="_default">
+        <TemplateType5>
+          <BackgroundImageURI IsRelative="true" IsResource="false"></BackgroundImageURI>
+          <Count>0</Count>
+          <Title></Title>
+        </TemplateType5>
+      </PrimaryToken>
+    </Tokens>
+  </App>
+</Deployment>
+

XNAPlayground/XNAPlayground/Screens/MainScreen.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#if WINDOWS_PHONE || IOS
+using Microsoft.Xna.Framework.Input.Touch;
+#endif
+using XNAPlayground.Entities;
+using XNAPlayground.Map;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Screens {
+	internal class MainScreen : Screen {
+		private const int NumTiles = 9;
+		private SpriteBatch sb;
+		private SpriteCache sc;
+		private readonly Rectangle[] tileRegions = new Rectangle[NumTiles];
+		private Player player;
+		private Starfield starfield;
+		private GameMap map;
+		private Vector2 lastSpawnPos;
+		private long startTicks;
+		private Music song;
+		private long score;
+		//========================================================================================================
+		private readonly List<Vector2> coinPositions = new List<Vector2>();
+		private int coinW;
+		private int coinH;
+		private Rectangle coinRegion;
+		//========================================================================================================
+		private const long GibDecayTicks = 8000;
+		private readonly List<Gib> gibs = new List<Gib>();
+		private static int gibW, gibH;
+		private Rectangle[] gibRegions;
+
+		private void InitGibs() {
+			gibs.Clear();
+			gibRegions = new Rectangle[3];
+			for(int i = 0; i < gibRegions.Length; i++)
+				gibRegions[i] = Utils.GetRegion("gib" + (i + 1));
+
+			gibW = gibRegions[0].Width;
+			gibH = gibRegions[0].Height;
+		}
+
+		//========================================================================================================
+		private readonly List<Razorblade> razors = new List<Razorblade>();
+		private readonly Rectangle[] razorRegions = new Rectangle[2];
+
+		private void AddRazorblade(int x, int y) {
+			razors.Add(new Razorblade(new Vector2(x, y), (float) Utils.Rand.NextDouble()*360.0f));
+		}
+
+		private void InitRazorblades() {
+			razorRegions[0] = Utils.GetRegion("razorBlade");
+			razorRegions[1] = Utils.GetRegion("razorBladeBloody");
+			Razorblade.RazorW = razorRegions[0].Width;
+			Razorblade.RazorH = razorRegions[0].Height;
+		}
+
+		//========================================================================================================
+		private readonly List<Spike> spikes = new List<Spike>();
+		private readonly Rectangle[] spikeRegions = new Rectangle[2];
+
+		private void InitSpikes() {
+			spikeRegions[0] = Utils.GetRegion("spikes");
+			spikeRegions[1] = Utils.GetRegion("spikesBloody");
+			Spike.SpikeW = spikeRegions[0].Width;
+			Spike.SpikeH = spikeRegions[0].Height;
+		}
+
+		//========================================================================================================
+		private readonly List<DeathAnim> deathAnims = new List<DeathAnim>();
+		private Rectangle[] daRegions;
+
+		private void InitDeathAnims() {
+			daRegions = new Rectangle[DeathAnim.NumFrames];
+			for(int i = 0; i < daRegions.Length; i++)
+				daRegions[i] = Utils.GetRegion("deathAnim" + (i + 1));
+		}
+
+		//========================================================================================================	
+		private float coinCtr;
+		private readonly int selectedLevel;
+		//========================================================================================================	
+		public MainScreen(MainGame game, int selectedLevel) : base(game) {
+			this.selectedLevel = selectedLevel;
+		}
+
+		public override void Init() {
+			Helpers.SetCurSheet(Helpers.SheetType.Main);
+
+			sb = new SpriteBatch(Game.GraphicsDevice);
+
+			// Init sprite cache
+#if IOS
+			const bool useCompatMode = true; //false; //true;
+#else
+			const bool useCompatMode = false;
+#endif
+
+			sc = new SpriteCache(Game.GraphicsDevice, useCompatMode);
+
+			for(int i = 0; i < NumTiles; i++) {
+				tileRegions[i] = Utils.GetRegion("tile" + (i + 1));
+			}
+
+			var playerRegions = new Dictionary<string, Rectangle>();
+			foreach(string frame in Player.Frames) {
+				playerRegions[frame] = Utils.GetRegion("player" + frame);
+			}
+
+			if(map == null)
+				InitMap(selectedLevel);
+
+			player = new Player(DetermineSpawnPos(), playerRegions, map, gibs);
+
+			Rectangle starReg = Utils.GetRegion("star");
+			starfield = new Starfield(starReg, 0.25f);
+
+			song = new Music("levelloop");
+			song.SetLooping(true);
+			song.SetVolume(0.8f);
+
+			InitRazorblades();
+			InitGibs();
+			InitDeathAnims();
+			InitSpikes();
+
+			coinRegion = Utils.GetRegion("coin");
+			coinW = coinRegion.Width;
+			coinH = coinRegion.Height;
+
+			//Gdx.input.setCatchBackKey(true);
+			song.Play();
+		}
+
+		private void RebuildMapMesh() {
+			if(player != null)
+				player.Reset();
+
+			razors.Clear();
+			deathAnims.Clear();
+			gibs.Clear();
+			coinPositions.Clear();
+			spikes.Clear();
+
+			sc.Clear();
+
+			for(int y = 0; y < GameMap.MapH; y++) {
+				for(int x = 0; x < GameMap.MapW; x++) {
+					char c = map.GetCellAt(x, y);
+					if(!GameMap.IsSolid(c)) {
+						switch(c) {
+							case 'G':
+								sc.Add(Utils.GetRegion("goal"), x*GameMap.CellW, y*GameMap.CellH);
+								break;
+							case 'R':
+								AddRazorblade(x*GameMap.CellW, y*GameMap.CellH);
+								break;
+							case 'C':
+								coinPositions.Add(new Vector2(x*GameMap.CellW + 8, y*GameMap.CellH + 8));
+								break;
+							case 's':
+								if(Globals.Difficulty != Globals.DifficultyLevel.Easy)
+									spikes.Add(new Spike(new Vector2(x*GameMap.CellW, y*GameMap.CellH)));
+								break;
+						}
+
+						continue;
+					}
+					sc.Add(tileRegions[c - '1'], x*GameMap.CellW, y*GameMap.CellH);
+				}
+			}
+
+			DetermineSpawnPos();
+		}
+
+		private Vector2 DetermineSpawnPos() {
+			var spawnPos = new Vector2();
+			for(int y = 0; y < GameMap.MapH; y++) {
+				for(int x = 0; x < GameMap.MapW; x++) {
+					if(map.GetCellAt(x, y) == 'S') {
+						spawnPos.X = x*GameMap.CellW;
+						spawnPos.Y = y*GameMap.CellH;
+					}
+				}
+			}
+
+			lastSpawnPos = spawnPos;
+
+			return spawnPos;
+		}
+
+		public override void Update() {
+			float delta = Utils.GetDeltaTime();
+
+			ProcessInput(delta);
+
+			if(player.Update(delta)) {
+				map.Finish(Utils.GetTicks() - startTicks);
+				Game.SetScreen(new WorldScreen(Game));
+			}
+
+			CollectCoins();
+
+			UpdateSpikes();
+			UpdateRazorblades();
+			UpdateGibs();
+		}
+
+		public override void Render() {
+			RenderScene();
+		}
+
+		private void UpdateSpikes() {
+			Rectangle prect = player.GetRect();
+			foreach(Spike s in spikes) {
+				if(s.GetRect().Intersects(prect) && s.IsActive()) {
+					KillPlayer();
+					s.Bloody = true;
+				}
+
+				s.Update();
+			}
+		}
+
+		private void CollectCoins() {
+			var coinRect = new Rectangle(0, 0, coinW, coinH);
+
+			for(int i = coinPositions.Count - 1; i >= 0; i--) {
+				coinRect.X = (int) coinPositions[i].X;
+				coinRect.Y = (int) coinPositions[i].Y;
+
+				if(player.GetRect().Intersects(coinRect)) {
+					coinPositions.RemoveAt(i);
+					score += 50;
+					player.PickupCoin();
+				}
+			}
+		}
+
+		private void ProcessInput(float delta) {
+#if WINDOWS_PHONE || IOS
+			bool leftBorder = false, rightBorder = false;
+
+			TouchCollection touchCollection = TouchPanel.GetState();
+
+			foreach(TouchLocation touchLocation in touchCollection) {
+				//int x = (int)(Gdx.input.getX(i) *  (float)(Globals.ScrW / (float)Gdx.graphics.getWidth()));
+				if(touchLocation.State == TouchLocationState.Released) continue;
+				var x = (int)(touchLocation.Position.X * 800.0f / Globals.ActualScrW);
+				var y = (int)(touchLocation.Position.Y * 480.0f / Globals.ActualScrH);
+
+				if(y < 50) {
+					Game.SetScreen(new WorldScreen(Game));
+				}
+
+				if(x < 100)
+					leftBorder = true;
+				else if(x > Globals.ScrW - 100)
+					rightBorder = true;
+			}
+
+			if(leftBorder && rightBorder) {
+				if(player.GetXaccel() > 0.1f)
+					player.MoveRight(delta);
+				else if(player.GetXaccel() < -0.1f)
+					player.MoveLeft(delta);
+
+				player.Jump();
+			}
+			else if(leftBorder)
+				player.MoveLeft(delta);
+			else if(rightBorder)
+				player.MoveRight(delta);
+
+			if(GamePad.GetState(0).IsButtonDown(Buttons.Back))
+				Game.SetScreen(new WorldScreen(Game));
+#else
+			KeyboardState keyState = Keyboard.GetState();
+
+			if(keyState.IsKeyDown(Keys.Escape)) {
+				Game.SetScreen(new WorldScreen(Game));
+			}
+
+			if(keyState.IsKeyDown(Keys.Left))
+				player.MoveLeft(delta);
+			if(keyState.IsKeyDown(Keys.Right))
+				player.MoveRight(delta);
+			if(keyState.IsKeyDown(Keys.Space) || keyState.IsKeyDown(Keys.Up))
+				player.Jump();
+
+			if(Mouse.GetState().LeftButton == ButtonState.Pressed)
+				map.GetCellAtPos(Mouse.GetState().X, Mouse.GetState().Y);
+#endif
+		}
+
+		public void InitMap(int mapNum) {
+			if(map == null) {
+				map = new GameMap(mapNum);
+			}
+
+			map.SelectMap(mapNum);
+
+			RebuildMapMesh();
+
+			if(player != null)
+				player.Pos = DetermineSpawnPos();
+
+			startTicks = Utils.GetTicks();
+
+			score = 0;
+		}
+
+		private void UpdateRazorblades() {
+			Rectangle prect = player.GetRect();
+
+			foreach(Razorblade r in razors.Where(r => r.GetRect().Intersects(prect))) {
+				KillPlayer();
+				r.Bloody = true;
+			}
+		}
+
+		private void KillPlayer() {
+			gibs.Add(new Gib(player.Pos, 0.0f));
+			deathAnims.Add(new DeathAnim(player.Pos));
+			player.Reset(lastSpawnPos.X, lastSpawnPos.Y);
+			startTicks = Utils.GetTicks();
+		}
+
+		private void UpdateGibs() {
+			for(int i = gibs.Count - 1; i >= 0; i--) {
+				Gib g = gibs[i];
+
+				if(!map.IsSolidAtPos(g.Pos))
+					g.Pos.Y -= 0.5f;
+
+				if(Utils.GetTicks() - g.SpawnTicks > GibDecayTicks) {
+					gibs.RemoveAt(i);
+				}
+			}
+		}
+
+		private void RenderScene() {
+			sb.Begin(SpriteSortMode.Deferred, null, null, null, null, null, MainGame.TransformMatrix);
+			starfield.Draw(sb);
+			sb.End();
+
+			sc.Draw();
+
+			sb.Begin(SpriteSortMode.Deferred, null, null, null, null, null, MainGame.TransformMatrix);
+			foreach(Razorblade r in razors) {
+				r.Alpha += Utils.GetDeltaTime()*100f;
+				sb.Draw(razorRegions[r.Bloody ? 1 : 0], r.Pos.X, r.Pos.Y, Razorblade.RazorW/2.0f, Razorblade.RazorH/2.0f, Razorblade.RazorW, Razorblade.RazorH, 1.0f, 1.0f, r.Alpha);
+			}
+
+			foreach(Spike s in spikes.Where(s => s.ScaleY > 0)) {
+				sb.Draw(spikeRegions[s.Bloody ? 1 : 0], s.Pos.X, s.Pos.Y, 0f, 0f, Spike.SpikeW, Spike.SpikeH, 1.0f, s.ScaleY, 0.0f);
+			}
+
+			foreach(Gib g in gibs) {
+				sb.Draw(gibRegions[g.Type], g.Pos.X, g.Pos.Y, gibW/2.0f, gibH/2.0f, gibW, gibH, 1.0f, 1.0f, g.Alpha);
+			}
+
+			player.Draw(sb);
+
+			// Draw coins
+			var sinVal = (float)Math.Sin(MathHelper.ToRadians(coinCtr += 200f * Utils.GetDeltaTime()));
+			float rotZ = sinVal < 0.0f ? 180.0f : 0.0f;
+			float xOffset = (sinVal < 0.0f ? -coinW : 0.0f) + coinW / 2.0f;
+			foreach(Vector2 coinPos in coinPositions) {
+				sb.Draw(coinRegion, coinPos.X + xOffset, coinPos.Y, coinW/2.0f, coinH/2.0f, coinW, coinH, (float) Math.Abs(sinVal), 1.0f, rotZ);
+			}
+
+			UpdateAndDrawDeathAnims();
+
+			RenderInfo();
+
+			sb.End();
+		}
+
+		private void RenderInfo() {
+			string timeStr = GenTimeStr(Utils.GetTicks() - startTicks);
+
+			sb.Draw("Time: " + timeStr, 11, Globals.ScrH - 10, Color.Black);
+			sb.Draw("Time: " + timeStr, 10, Globals.ScrH - 11, Color.Yellow);
+
+			sb.Draw("Score: " + score, Globals.ScrW - 130, Globals.ScrH - 10, Color.Black);
+			sb.Draw("Score: " + score, Globals.ScrW - 131, Globals.ScrH - 11, Color.Yellow);
+		}
+
+		public static string GenTimeStr(long msecs) {
+			return (msecs/1000) + "." + (msecs%1000/10);
+		}
+
+		private void UpdateAndDrawDeathAnims() {
+			for(int i = deathAnims.Count - 1; i >= 0; i--) {
+				DeathAnim da = deathAnims[i];
+				da.Pos.Y += 2.0f;
+				float alpha = (Utils.GetTicks() - da.SpawnTicks)*0.1f;
+				float scale = 1.0f + alpha*0.05f;
+				int frameNum = da.GetFrameNr();
+				if(frameNum == -1)
+					deathAnims.RemoveAt(i);
+				else
+					sb.Draw(daRegions[frameNum], da.Pos.X, da.Pos.Y, player.Width/2, player.Height/2, player.Width, player.Height, scale, scale, alpha);
+			}
+		}
+
+		#region Implementation of IDisposable
+		public override void Dispose() {
+			song.Stop();
+			player.Dispose();
+			song.Dispose();
+			sb.Dispose();
+			sc.Dispose();
+		}
+		#endregion
+	}
+}

XNAPlayground/XNAPlayground/Screens/Screen.cs

+using System;
+
+namespace XNAPlayground.Screens {
+	public abstract class Screen : IDisposable {
+		protected MainGame Game;
+
+		protected Screen(MainGame game) {
+			Game = game;
+		}
+
+		public abstract void Init();
+		public abstract void Dispose();
+		public abstract void Update();
+		public abstract void Render();
+	}
+}

XNAPlayground/XNAPlayground/Screens/SplashScreen.cs

+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#if WINDOWS_PHONE || IOS
+using Microsoft.Xna.Framework.Input.Touch;
+#endif
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Screens {
+	internal class SplashScreen : Screen {
+		private SpriteBatch sb;
+		private Rectangle splashRegion;
+		private Rectangle glassesRegion;
+		private int glassesW;
+		private int glassesH;
+		private float glassesAlpha;
+		private const int StartY = Globals.ScrH;
+		private const int EndY = 205;
+		private float glassesY;
+		private Music song;
+
+		public SplashScreen(MainGame game) : base(game) {}
+
+		public override void Init() {
+			Helpers.SetCurSheet(Helpers.SheetType.Splash);
+
+			splashRegion = Utils.GetRegion("splash");
+			glassesRegion = Utils.GetRegion("glasses");
+			glassesW = glassesRegion.Width;
+			glassesH = glassesRegion.Height;
+			glassesAlpha = 0.0f;
+			glassesY = StartY;
+
+			song = new Music("splashloop");
+			song.SetLooping(true);
+			song.SetVolume(0.8f);
+			song.Play();
+			sb = new SpriteBatch(Game.GraphicsDevice);
+		}
+
+		public override void Dispose() {
+			song.Stop();
+			Utils.SafeDispose(ref song);
+			Utils.SafeDispose(ref sb);
+		}
+
+		#region Implementation of IScreen
+		public override void Update() {
+#if WINDOWS_PHONE || IOS
+			TouchCollection touchCollection = TouchPanel.GetState();
+
+			foreach(TouchLocation touchLocation in touchCollection) {
+				if(touchLocation.State == TouchLocationState.Pressed) {
+					Game.SetScreen(new WorldScreen(Game));	
+				}
+			}
+#else
+			KeyboardState keyState = Keyboard.GetState();
+
+			if(keyState.IsKeyDown(Keys.Escape)) Game.Exit();
+			else if (keyState.IsKeyDown(Keys.Enter) || Mouse.GetState().LeftButton == ButtonState.Pressed)
+				Game.SetScreen(new WorldScreen(Game));
+#endif
+		}
+
+		public override void Render() {
+			sb.Begin(SpriteSortMode.Deferred, null, null, null, null, null, MainGame.TransformMatrix);
+			sb.Draw(splashRegion, 0, 0);
+			glassesAlpha = 7.0f;
+			if(Math.Abs(glassesY - EndY) > 5.0f)
+				glassesY -= 2.0f;
+			sb.Draw(glassesRegion, 310, glassesY, glassesW/2.0f, glassesH/2.0f, glassesW, glassesH, 1.2f, 1.2f, glassesAlpha);
+			sb.End();
+		}
+		#endregion
+	}
+}

XNAPlayground/XNAPlayground/Screens/WorldScreen.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+#if WINDOWS_PHONE || IOS
+using Microsoft.Xna.Framework.Input.Touch;
+#endif
+using XNAPlayground.Map;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground.Screens {
+	internal class WorldScreen : Screen {
+		private SpriteBatch sb;
+		private Starfield sfield;
+		private readonly Rectangle[] playerRegions = new Rectangle[2];
+		private const float PlanetRadius = 64.0f;
+		private float playerAngleAccel;
+		private float playerAngle;
+		private Vector2 centerPos;
+		private Vector2 playerPos;
+		private float playerW;
+		private float playerH;
+		private Rectangle selCircleRegion;
+		private readonly Vector2[] levelPositions = new Vector2[GameMap.NumMaps];
+		private const float ToleranceDist = 30f;
+		private int selectedLevel;
+		private Music song;
+		private Sound stepSnd;
+		private Sound selectSnd;
+		private Vector2 offVec;
+		private long lastStepTicks;
+		private Rectangle flagRegion;
+		private Rectangle planetRegion;
+		private int pwidth;
+		private int pheight;
+		private bool showDifficultyOverlay;
+		private Rectangle buttonRegion;
+		private bool mouseWasReleased;
+		private int selectedButton = (int) Globals.DifficultyLevel.Normal;
+		private KeyManager keyManager;
+
+		#region Static state
+		public static bool HasSelectedDifficulty;
+		#endregion
+
+
+		public WorldScreen(MainGame game) : base(game) {}
+
+		public override void Init() {
+			Helpers.SetCurSheet(Helpers.SheetType.Main);
+
+			//Utils.fitScale(sb);
+
+			sb = new SpriteBatch(Game.GraphicsDevice);
+
+			sfield = new Starfield(Utils.GetRegion("star"), 0.01f);
+
+			planetRegion = Utils.GetRegion("planet");
+			pwidth = planetRegion.Width;
+			pheight = planetRegion.Height;
+
+			//sc.beginCache();
+			//sc.add(planetRegion, (Globals.ScrW - pwidth) / 2, (Globals.ScrH - pheight) / 2);		
+			//sc.endCache();
+
+			playerRegions[0] = Utils.GetRegion("playerStandingLeft");
+			playerRegions[1] = Utils.GetRegion("playerStandingRight");
+
+			playerW = playerRegions[0].Width;
+			playerH = playerRegions[0].Height;
+
+			playerAngle = MathHelper.Pi/2;
+
+			centerPos = new Vector2(400, 233);
+
+			selCircleRegion = Utils.GetRegion("selectionCircle");
+
+			levelPositions[0] = new Vector2(381f, 194f);
+			levelPositions[1] = new Vector2(456f, 260f);
+			levelPositions[2] = new Vector2(379f, 329f);
+			levelPositions[3] = new Vector2(311f, 265f);
+
+			levelPositions[4] = new Vector2(433f, 212f);
+			levelPositions[5] = new Vector2(434f, 313f);
+			levelPositions[6] = new Vector2(333f, 325f);
+			levelPositions[7] = new Vector2(330f, 209f);
+
+			for(int i = 0; i < levelPositions.Length; i++) {
+				levelPositions[i].Y = Globals.ScrH - levelPositions[i].Y;
+			}
+
+			selectedLevel = 0;
+
+			offVec = new Vector2(selCircleRegion.Width/2.0f, selCircleRegion.Height/2.0f);
+
+			song = new Music("spaceloop");
+			song.SetLooping(true);
+			song.SetVolume(0.8f);
+			song.Play();
+
+			selectSnd = new Sound("select");
+			stepSnd = new Sound("step");
+
+			flagRegion = Utils.GetRegion("flag");
+
+			showDifficultyOverlay = true;
+			mouseWasReleased = false;
+
+			InitButtons();
+
+			keyManager = new KeyManager(new[] {Keys.Up, Keys.Down, Keys.Escape, Keys.Enter});
+
+			song.Play();
+			keyManager.Reset();
+
+			if (HasSelectedDifficulty)
+				showDifficultyOverlay = false;
+		}
+
+		private void InitButtons() {
+			buttonRegion = Utils.GetRegion("button");
+			const float yCtr = Globals.ScrH - 180.0f;
+			float itsX = (Globals.ScrW - buttonRegion.Width)/2.0f;
+			const int off = 10;
+			AddButton("Easy", itsX, yCtr);
+			AddButton("Normal", itsX, yCtr - (buttonRegion.Height + off)*1);
+			AddButton("Hard", itsX, yCtr - (buttonRegion.Height + off)*2);
+			AddButton("Impossibru", itsX, yCtr - (buttonRegion.Height + off)*3);
+		}
+
+		private int lastSel = -1;
+
+		public override void Update() {
+			ProcessInput();
+
+			playerPos.X = centerPos.X + (float) Math.Cos(playerAngle)*PlanetRadius;
+			playerPos.Y = centerPos.Y + (float) Math.Sin(playerAngle)*PlanetRadius;
+
+			lastSel = selectedLevel;
+			selectedLevel = -1;
+
+			for(int i = 0; i < GameMap.NumMaps; i++) {
+				Vector2 ctr = levelPositions[i] + offVec;
+				if((playerPos - ctr).Length() < ToleranceDist) {
+					selectedLevel = i;
+				}
+			}
+
+			if(lastSel != selectedLevel && selectedLevel != -1) {
+				long id = selectSnd.Play();
+				selectSnd.SetVolume(id, 0.25f);
+			}
+		}
+
+		public override void Render() {
+			RenderScene();
+		}
+
+		private const long StepFreq = 150;
+
+		private void ProcessInput() {
+#if WINDOWS_PHONE || IOS
+			TouchCollection touchCollection = TouchPanel.GetState();
+			if(!mouseWasReleased && touchCollection.Count == 0) mouseWasReleased = true;
+			if(!mouseWasReleased) return;
+#endif
+
+			keyManager.Update();
+
+			if(keyManager.PressedOnce(Keys.Escape))
+				Game.Exit();
+
+			if(showDifficultyOverlay) {
+				if(keyManager.PressedOnce(Keys.Down)) {
+					selectedButton = (selectedButton + 1)%buttons.Count;
+				}
+
+				if(keyManager.PressedOnce(Keys.Up)) {
+					selectedButton = selectedButton - 1;
+					if(selectedButton < 0)
+						selectedButton += buttons.Count;
+				}
+
+				if(keyManager.PressedOnce(Keys.Enter)) {
+					Globals.Difficulty = (Globals.DifficultyLevel) selectedButton;
+					showDifficultyOverlay = false;
+					return;
+				}
+
+				var x = (int) (Mouse.GetState().X*(Globals.ScrW/(float) Globals.ScrW));
+				var y = (int) (Mouse.GetState().Y*(Globals.ScrH/(float) Globals.ScrH));
+				y = Globals.ScrH - y;
+
+#if IOS
+				if(touchCollection.Count > 0 && touchCollection[0].State != TouchLocationState.Released) {
+					x = (int)(touchCollection[0].Position.X * 800.0f / Globals.ActualScrW);
+					y = (int)(touchCollection[0].Position.Y * 480.0f / Globals.ActualScrH);
+				} else return;
+#else
+				if(Mouse.GetState().LeftButton == ButtonState.Released) {
+					foreach(Button btn in buttons.Where(btn => btn.Rect.Contains(x, y))) {
+						if(btn.Text.Equals("Easy")) {
+							selectedButton = 0;
+						}
+						else if(btn.Text.Equals("Normal")) {
+							selectedButton = 1;
+						}
+						else if(btn.Text.Equals("Hard")) {
+							selectedButton = 2;
+						}
+						else if(btn.Text.Equals("Impossibru")) {
+							selectedButton = 3;
+						}
+					}
+
+					if(!mouseWasReleased) mouseWasReleased = true;
+					return;
+				}
+
+				if(!mouseWasReleased)
+					return;
+#endif
+
+				foreach(Button btn in buttons.Where(btn => btn.Rect.Contains(x, y))) {
+					if(btn.Text.Equals("Easy")) {
+						Globals.Difficulty = Globals.DifficultyLevel.Easy;
+					}
+					else if(btn.Text.Equals("Normal")) {
+						Globals.Difficulty = Globals.DifficultyLevel.Normal;
+					}
+					else if(btn.Text.Equals("Hard")) {
+						Globals.Difficulty = Globals.DifficultyLevel.Hard;
+					}
+					else if(btn.Text.Equals("Impossibru")) {
+						Globals.Difficulty = Globals.DifficultyLevel.Impossible;
+					}
+
+					showDifficultyOverlay = false;
+					mouseWasReleased = false;
+					HasSelectedDifficulty = true;
+				}
+
+				return;
+			}
+
+			if(keyManager.PressedOnce(Keys.Enter) && selectedLevel != -1) {
+				var mscreen = new MainScreen(Game, selectedLevel);
+				Game.SetScreen(mscreen);
+			}
+
+#if WINDOWS_PHONE || IOS
+			bool leftBorder = false, rightBorder = false;
+
+			foreach(TouchLocation touchLocation in touchCollection) {
+				if (touchLocation.State == TouchLocationState.Released) continue;
+
+				var x = (int)(touchLocation.Position.X * 800.0f / Globals.ActualScrW);
+				var y = (int)(touchLocation.Position.Y * 480.0f / Globals.ActualScrH);
+
+				if (x < 100)
+					leftBorder = true;
+				else if (x > Globals.ScrW - 100)
+					rightBorder = true;
+
+				if (selectedLevel != -1 && x >= Globals.ScrW / 2 - 50 && x <= Globals.ScrW / 2 + 50 && y >= Globals.ScrH / 2 - 50 && y <= Globals.ScrH / 2 + 50 && mouseWasReleased) {
+					var mscreen = new MainScreen(Game, selectedLevel);
+					Game.SetScreen(mscreen);
+				}
+			}
+
+			if(leftBorder)
+				playerAngleAccel += 2f*Utils.GetDeltaTime();
+			if(rightBorder)
+				playerAngleAccel -= 2f*Utils.GetDeltaTime();
+#else
+			KeyboardState keyState = Keyboard.GetState();
+
+			if(keyState.IsKeyDown(Keys.Left)) {
+				playerAngleAccel += 2f*Utils.GetDeltaTime();
+			}
+			if(keyState.IsKeyDown(Keys.Right)) {
+				playerAngleAccel -= 2f*Utils.GetDeltaTime();
+			}
+
+			if((keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.Left)) && Utils.GetTicks() - lastStepTicks > StepFreq) {
+				long id = stepSnd.Play();
+				stepSnd.SetVolume(id, 0.125f);
+				lastStepTicks = Utils.GetTicks();
+			}
+#endif
+
+			/*if(Gdx.input.isButtonPressed(Buttons.LEFT)) {
+			System.out.println(Gdx.input.getX() + ": "+ Gdx.input.getY());
+			System.out.println(playerAngle);
+			}*/
+		}
+
+		private void RenderScene() {
+			sb.Begin(SpriteSortMode.Deferred, null, null, null, null, null, MainGame.TransformMatrix);
+			sfield.Draw(sb);
+			sb.Draw(planetRegion, (Globals.ScrW - pwidth)/2.0f, (Globals.ScrH - pheight)/2.0f);
+			sb.End();
+
+			sb.Begin(SpriteSortMode.Deferred, null, null, null, null, null, MainGame.TransformMatrix);
+			playerAngleAccel *= 0.8f;
+			playerAngle += playerAngleAccel;
+			float alpha = MathHelper.ToDegrees(playerAngle) - 90.0f;
+			sb.Draw(playerRegions[0], playerPos.X, playerPos.Y, playerW/2, playerH/2, playerW, playerH, 1.0f, 1.0f, alpha);
+
+			DrawTextWithShadow("TINY WORLD", Globals.ScrW/2 - 50, Globals.ScrH/2);
+
+			RenderScores();
+
+			if(selectedLevel != -1) {
+				Vector2 pos = levelPositions[selectedLevel];
+				sb.Draw(selCircleRegion, pos.X, /*Globals.ScrH-*/pos.Y);
+			}
+
+			if(showDifficultyOverlay) {
+				DrawButtons();
+			}
+
+			sb.End();
+		}
+
+		private class Button {
+			public Rectangle Rect;
+			public String Text;
+		}
+
+		private readonly List<Button> buttons = new List<Button>();
+
+		private void AddButton(String text, float x, float y) {
+			var btn = new Button {Rect = new Rectangle((int) x, (int) y, buttonRegion.Width, buttonRegion.Height), Text = text};
+			buttons.Add(btn);
+		}
+
+		private void DrawButtons() {
+			DrawTextWithShadow("Select Difficulty", Globals.ScrW/2 - 60, Globals.ScrH - 50);
+
+			int ctr = 0;
+			foreach(Button btn in buttons) {
+				sb.Draw(buttonRegion, btn.Rect.X, btn.Rect.Y);
+				sb.Draw(btn.Text, btn.Rect.X + 10, btn.Rect.Y + btn.Rect.Height/2, ctr == selectedButton ? Color.Red : Color.White);
+				ctr++;
+			}
+		}
+
+		private void DrawTextWithShadow(String text, float x, float y) {
+			sb.Draw(text, x + 1, y + 1, Color.Red);
+			sb.Draw(text, x, y);
+		}
+
+		private void RenderScores() {
+			int numFinished = 0;
+
+			for(int i = 0; i < GameMap.NumMaps; i++) {
+				if(GameMap.MapInfos[i].Finished) {
+					Vector2 pos = levelPositions[i];
+
+					switch(i) {
+						default:
+						case 0:
+							pos.X += 40;
+							pos.Y += 5;
+							break;
+						case 1:
+							pos.Y += 5;
+							break;
+						case 2:
+							pos.X += 10;
+							pos.Y += 30;
+							break;
+						case 3:
+							pos.X += 35;
+							pos.Y += 40;
+							break;
+						case 4:
+							pos.X += 20;
+							pos.Y -= 5;
+							break;
+						case 5:
+							pos.Y += 20;
+							break;
+						case 6:
+							pos.X += 25;
+							pos.Y += 45;
+							break;
+						case 7:
+							pos.X += 40;
+							pos.Y += 15;
+							break;
+					}
+					sb.Draw(flagRegion, pos.X, pos.Y);
+
+					switch(i) {
+						default:
+						case 0:
+							pos.X -= 40;
+							pos.Y += 55;
+							break;
+						case 1:
+							pos.X += 45;
+							pos.Y += 25;
+							break;
+						case 2:
+							pos.X -= 15;
+							pos.Y -= 30;
+							break;
+						case 3:
+							pos.X -= 110;
+							pos.Y -= 10;
+							break;
+						case 4:
+							pos.X += 40;
+							pos.Y += 60;
+							break;
+						case 5:
+							pos.X += 45;
+							pos.Y -= 10;
+							break;
+						case 6:
+							pos.X -= 80;
+							pos.Y -= 35;
+							break;
+						case 7:
+							pos.X -= 90;
+							pos.Y += 35;
+							break;
+					}
+
+					sb.Draw("Time:" + MainScreen.GenTimeStr(GameMap.MapInfos[i].TicksNeeded), pos.X, pos.Y);
+
+					numFinished++;
+				}
+			}
+
+			if(numFinished == GameMap.NumMaps) {
+				sb.Draw("CONGRATULATIONS, YOU'VE FINISHED ALL LEVELS!", Globals.ScrW/2 - 150, Globals.ScrH - 100, Color.Red);
+				sb.Draw("CONGRATULATIONS, YOU'VE FINISHED ALL LEVELS!", Globals.ScrW/2 - 149, Globals.ScrH - 100);
+			}
+		}
+
+		#region Implementation of IDisposable
+		public override void Dispose() {
+			song.Stop();
+			song.Dispose();
+
+			//stepSnd.Dispose();
+			selectSnd.Dispose();
+			
+			sb.Dispose();
+		}
+		#endregion
+	}
+}

XNAPlayground/XNAPlayground/Starfield.cs

+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using XNAPlayground.Utilities;
+
+namespace XNAPlayground {
+	internal class Starfield {
+		private const int NumStars = 400;
+		private readonly Rectangle starRegion;
+		private readonly Vector2[] starPositions = new Vector2[NumStars];
+		private readonly float[] starSizes = new float[NumStars];
+		private readonly float starSpeed;
+
+		public Starfield(Rectangle region, float starSpeed) {
+			starRegion = region;
+			var r = new Random();
+			this.starSpeed = starSpeed;
+
+			for(int i = 0; i < NumStars; i++) {
+				starPositions[i] = new Vector2(r.Next(Globals.ScrW), r.Next(Globals.ScrH));
+				starSizes[i] = starRegion.Width*(float) r.NextDouble();
+			}
+		}
+
+		public void Draw(SpriteBatch sb) {
+			for(int i = 0; i < NumStars; i++) {
+				starPositions[i].X -= starSpeed*starSizes[i];
+				if(starPositions[i].X < 0)
+					starPositions[i].X = Globals.ScrW - starPositions[i].X;
+
+				sb.Draw(starRegion, starPositions[i].X, starPositions[i].Y, starSizes[i], starSizes[i]);
+			}
+		}
+	}
+}

XNAPlayground/XNAPlayground/Utilities/Helpers.cs

+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using System.IO;
+
+#if IOS
+using Microsoft.Xna.Framework.Storage;
+#endif
+
+namespace XNAPlayground.Utilities {
+	internal static class Helpers {
+		private static Texture2D mainSheetTex, splashSheetTex, curSheetTex;
+