private void AddNextDualPortal()
		{

			//WriteLine("Orange View: " + PortalADir.RadiansToDegrees());
			//WriteLine("Blue View: " + PortalBDir.RadiansToDegrees());


			#region create a dual portal
			var PortalA = new PortalInfo
			{
				Color = 0xFF6A00,

			}.AddTo(Portals);

			EgoView.Sprites.Add(PortalA.Sprite);


			var PortalB = new PortalInfo
			{
				Color = 0xff00,

			}.AddTo(Portals);


			EgoView.Sprites.Add(PortalB.Sprite);
			#endregion

			DualPortals.Add(new DualPortal { Orange = PortalA, Blue = PortalB });

		}
		private void Initialize()
		{
			txtMain = new TextField
			{
				defaultTextFormat = new TextFormat
				{
					font = "Verdana",
					align = TextFormatAlign.LEFT,
					size = 10,
					color = 0xffffff
				},
				autoSize = TextFieldAutoSize.LEFT,
				text = "0"
			};

			AddFullscreenMenu();





			EgoView = new ViewEngineBase(DefaultWidth, DefaultHeight)
			{
				FloorAndCeilingVisible = false,

				ViewPosition = new Point { x = 4, y = 22 },
				ViewDirection = 90.DegreesToRadians(),

			};

			var Portals = new List<PortalInfo>();

			EgoView.ViewDirectionChanged += () => Portals.ForEach(Portal => Portal.View.ViewDirection = EgoView.ViewDirection);

			#region create a dual portal
			var PortalA = new PortalInfo
			{
				Color = 0xFF6A00,
				ViewVector = new Vector { Direction = EgoView.ViewDirection, Position = new Point { x = 4.5, y = 14 } },
				SpriteVector = new Vector { Direction = EgoView.ViewDirection, Position = new Point { x = 3.5, y = 20 } },
			}.AddTo(Portals);


			EgoView.Sprites.Add(PortalA.Sprite);


			var PortalB = new PortalInfo
			{
				Color = 0xff00,
				ViewVector = PortalA.SpriteVector,
				SpriteVector = PortalA.ViewVector,
			}.AddTo(Portals);


			EgoView.Sprites.Add(PortalB.Sprite);
			#endregion



			var Ego = default(SpriteInfo);


			EgoView.ViewPositionChanged +=
				delegate
				{
					foreach (var Portal in Portals)
					{
						var p = EgoView.SpritesFromPointOfView.SingleOrDefault(i => i.Sprite == Portal.Sprite);

						if (p != null)
						{
							if (p.Distance < Portal.Sprite.Range)
							{
								// we are going thro the portal, show it

								new Bitmap(EgoView.Buffer.clone())
								{
									scaleX = DefaultScale,
									scaleY = DefaultScale
								}.AttachTo(this).FadeOutAndOrphanize(1000 / 24, 0.2);

								Assets.SoundFiles.teleport.ToSoundAsset().play();

								// fixme: should use Ego.MovementDirection instead
								// currently stepping backwards into the portal will behave recursivly
								EgoView.ViewPosition = Portal.View.ViewPosition.MoveToArc(EgoView.ViewDirection, Portal.Sprite.Range + p.Distance);

								break;
							}
						}
					}

				};

			var CameraView = new ViewEngineBase(64, 48)
			{
			};


			EgoView.RenderOverlay += DrawMinimap;
			EgoView.FramesPerSecondChanged += () => txtMain.text = EgoView.FramesPerSecond + " fps " + new { EgoView.ViewPositionX, EgoView.ViewPositionY };

			EgoView.Image.AttachTo(this);

			txtMain.AttachTo(this);



			EgoView.Image.scaleX = DefaultScale;
			EgoView.Image.scaleY = DefaultScale;
			//this.filters = new[] { new BlurFilter() };


			KeyboardButton fKeyTurnLeft = new uint[] { Keyboard.LEFT, 'j', 'J', };
			KeyboardButton fKeyTurnRight = new uint[] { Keyboard.RIGHT, 'l', 'L', };

			KeyboardButton fKeyStrafeLeft = new uint[] { 'a', 'A' };
			KeyboardButton fKeyStrafeRight = new uint[] { 'd', 'D' };

			KeyboardButton fKeyUp = new uint[] { Keyboard.UP, 'i', 'I', 'w', 'W' };
			KeyboardButton fKeyDown = new uint[] { Keyboard.DOWN, 'k', 'K', 's', 'S' };


			stage.keyDown +=
				e =>
				{
					var key = e.keyCode;

					fKeyStrafeLeft.ProcessKeyDown(key);
					fKeyStrafeRight.ProcessKeyDown(key);
					fKeyTurnLeft.ProcessKeyDown(key);
					fKeyTurnRight.ProcessKeyDown(key);

					fKeyUp.ProcessKeyDown(key);
					fKeyDown.ProcessKeyDown(key);


				};

			stage.keyUp +=
				e =>
				{
					var key = e.keyCode;


					fKeyStrafeLeft.ProcessKeyUp(key);
					fKeyStrafeRight.ProcessKeyUp(key);

					fKeyTurnLeft.ProcessKeyUp(key);
					fKeyTurnRight.ProcessKeyUp(key);

					fKeyUp.ProcessKeyUp(key);
					fKeyDown.ProcessKeyUp(key);

				};


			Action UpdateEgoPosition =
				delegate
				{
					if (Ego != null)
					{
						Ego.Position = EgoView.ViewPosition;
						Ego.Direction = EgoView.ViewDirection;
					}
				};

			EgoView.ViewPositionChanged +=
				delegate
				{
					UpdateEgoPosition();
				};

			(1000 / 30).AtInterval(
					delegate
					{
						if (fKeyTurnRight.IsPressed)
							EgoView.ViewDirection += 10.DegreesToRadians();
						else if (fKeyTurnLeft.IsPressed)
							EgoView.ViewDirection -= 10.DegreesToRadians();

						if (fKeyUp.IsPressed || fKeyStrafeLeft.IsPressed || fKeyStrafeRight.IsPressed)
						{
							var d = EgoView.ViewDirection;



							if (fKeyStrafeLeft.IsPressed)
								d -= 90.DegreesToRadians();
							else if (fKeyStrafeRight.IsPressed)
								d += 90.DegreesToRadians();


							EgoView.MoveTo(
									EgoView.ViewPositionX + Math.Cos(d) * 0.2,
									EgoView.ViewPositionY + Math.Sin(d) * 0.2
							);
						}
						else if (fKeyDown.IsPressed)
							EgoView.MoveTo(
								 EgoView.ViewPositionX + Math.Cos(EgoView.ViewDirection) * -0.2,
								 EgoView.ViewPositionY + Math.Sin(EgoView.ViewDirection) * -0.2
						 );


					}
			);

			var UpdatePortals = true;

			stage.keyUp +=
				   e =>
				   {
					   if (e.keyCode == Keyboard.V)
					   {
						   UpdatePortals = !UpdatePortals;
					   }

					   if (e.keyCode == Keyboard.N)
					   {
						   EgoView.RenderLowQualityWalls = !EgoView.RenderLowQualityWalls;
					   }

					   if (e.keyCode == Keyboard.M)
					   {
						   DrawMinimapEnabled = !DrawMinimapEnabled;
					   }

					   if (e.keyCode == Keyboard.B)
					   {
						   EgoView.SpritesVisible = !EgoView.SpritesVisible;
					   }

					   if (e.keyCode == Keyboard.F)
					   {
						   EgoView.FloorAndCeilingVisible = !EgoView.FloorAndCeilingVisible;
					   }

					   if (e.keyCode == Keyboard.DELETE)
					   {
						   EgoView.Sprites.RemoveAll(p => p != Ego);
					   }
				   };


			Action<Bitmap[]> BitmapsLoadedAction =
				Bitmaps =>
				{
					if (Bitmaps == null)
						throw new Exception("No bitmaps");

					Func<Texture64[], Texture64[]> Reorder8 =
						p =>
							Enumerable.ToArray(
								from i in Enumerable.Range(0, 8)
								select p[(i + 6) % 8]
							);

					var BitmapStream = Bitmaps.Select(i => (Texture64)i).GetEnumerator();

					Func<Texture64[]> Next8 =
						delegate
						{
							// keeping compiler happy with full delegate form

							if (BitmapStream == null)
								throw new Exception("BitmapStream is null");

							return Reorder8(BitmapStream.Take(8));
						};


					var Stand = Next8();
					var Spawn = default(Func<SpriteInfo>);

					if (Bitmaps.Length == 8)
					{
						Spawn = () => CreateWalkingDummy(Stand);
					}
					else
					{
						var Walk = new[]
                        {
                            Next8(),
                            Next8(),
                            Next8(),
                            Next8(),
                        };



						Spawn = () => CreateWalkingDummy(Stand, Walk);
					}

					Ego = Spawn();


					UpdateEgoPosition();



					stage.keyUp +=
						  e =>
						  {
							  if (e.keyCode == Keyboard.SPACE)
							  {
								  var s = Spawn();

								  //s.Direction += 180.DegreesToRadians();

								  CameraView.ViewPosition = s.Position;
								  CameraView.ViewDirection = s.Direction;
							  }

							  if (e.keyCode == Keyboard.INSERT)
							  {
								  var s = Spawn();

								  s.Direction += 180.DegreesToRadians();
								  s.Position = Ego.Position.MoveToArc(Ego.Direction, 0.5);
							  }

							  if (e.keyCode == Keyboard.ENTER)
							  {
								  EgoView.ViewPosition = new Point { x = 4, y = 22 };
								  EgoView.ViewDirection = 270.DegreesToRadians();


							  }



							  if (e.keyCode == Keyboard.BACKSPACE)
							  {

								  (1000 / 30).AtInterval(
									  t =>
									  {
										  EgoView.ViewDirection += 18.DegreesToRadians();

										  if (t.currentCount == 10)
											  t.stop();
									  }
								  );
							  }
						  };
				};


			Assets.ZipFiles.MyZipFile
				.ToFiles()
				.Where(f => f.FileName.EndsWith(".png"))
				.ToBitmapArray(BitmapsLoadedAction);



			Assets.ZipFiles.MyStuff.ToFiles().ToBitmapDictionary(
					f =>
					{
						// ! important
						// ----------------------------------------------------
						// ! loading png via bytes affects pixel values
						// ! this is why map is in gif format

						EgoView.Map.WorldMap = Texture32.Of(f["Map1.gif"], false);

						Action<IEnumerator<Texture64.Entry>, Texture64, Action<SpriteInfo>> AddSpriteByTexture =
								  (SpaceForStuff, tex, handler) => SpaceForStuff.Take().Do(p => CreateDummy(tex).Do(handler).Position.To(p.XIndex + 0.5, p.YIndex + 0.5));

						var FreeSpaceForStuff = EgoView.Map.WorldMap.Entries.Where(i => i.Value == 0).Randomize().GetEnumerator();

						Action<Bitmap> AddSprite =
							e => AddSpriteByTexture(FreeSpaceForStuff, e, null);

						Assets.ZipFiles.MySprites.ToFiles().ToBitmapArray(
						   sprites =>
						   {
							   foreach (var s in sprites)
							   {
								   for (int i = 0; i < 3; i++)
								   {
									   AddSprite(s);
								   }
							   }
						   }
						);
						#region gold

						Assets.ZipFiles.MyGold.ToFiles().ToBitmapArray(
						   sprites =>
						   {
							   var GoldSprites = new List<SpriteInfo>();

							   foreach (var s in sprites)
							   {
								   for (int i = 0; i < 20; i++)
								   {
									   // compiler bug: get a delegate to BCL class
									   //AddSpriteByTexture(FreeSpaceForStuff, s, GoldSprites.Add);

									   AddSpriteByTexture(FreeSpaceForStuff, s,
										   k =>
										   {
											   k.Range = 0.5;
											   GoldSprites.Add(k);
										   }
									   );

								   }
							   }

							   var LastPosition = new Point();

							   EgoView.ViewPositionChanged +=
								   delegate
								   {
									   // only check for items each 0.5 distance travelled
									   if ((EgoView.ViewPosition - LastPosition).length < 0.5)
										   return;

									   Action Later = delegate { };


									   foreach (var Item in EgoView.SpritesFromPointOfView)
									   {
										   var Item_Sprite = Item.Sprite;

										   if (Item.Distance < Item_Sprite.Range)
										   {
											   if (GoldSprites.Contains(Item_Sprite))
											   {
												   // ding-ding-ding!

												   new Bitmap(new BitmapData(DefaultWidth, DefaultHeight, false, 0xffff00))
												   {
													   scaleX = DefaultScale,
													   scaleY = DefaultScale
												   }.AttachTo(this).FadeOutAndOrphanize(1000 / 24, 0.2);

												   InternalGotGold();
												   Later += () => EgoView.Sprites.Remove(Item_Sprite);
											   }
										   }
									   }

									   Later();

									   LastPosition = EgoView.ViewPosition;
								   };


						   }
						);
						#endregion

						#region ammo

						Assets.ZipFiles.ammo.ToFiles().ToBitmapArray(
						   sprites =>
						   {
							   var AmmoSprites = new List<SpriteInfo>();

							   foreach (var s in sprites)
							   {
								   for (int i = 0; i < 20; i++)
								   {
									   // compiler bug: get a delegate to BCL class
									   //AddSpriteByTexture(FreeSpaceForStuff, s, GoldSprites.Add);

									   AddSpriteByTexture(FreeSpaceForStuff, s,
										   k =>
										   {
											   k.Range = 0.5;
											   AmmoSprites.Add(k);
										   }
									   );

								   }
							   }

							   var LastPosition = new Point();

							   EgoView.ViewPositionChanged +=
								   delegate
								   {
									   // only check for items each 0.5 distance travelled
									   if ((EgoView.ViewPosition - LastPosition).length < 0.5)
										   return;

									   Action Later = delegate { };


									   foreach (var Item in EgoView.SpritesFromPointOfView)
									   {
										   var Item_Sprite = Item.Sprite;

										   if (Item.Distance < Item_Sprite.Range)
										   {
											   if (AmmoSprites.Contains(Item_Sprite))
											   {
												   // ding-ding-ding!

												   new Bitmap(new BitmapData(DefaultWidth, DefaultHeight, false, 0x8080ff))
												   {
													   scaleX = DefaultScale,
													   scaleY = DefaultScale
												   }.AttachTo(this).FadeOutAndOrphanize(1000 / 24, 0.2);


												   InternalGotAmmo();
												   

												   Later += () => EgoView.Sprites.Remove(Item_Sprite);
											   }
										   }
									   }

									   Later();

									   LastPosition = EgoView.ViewPosition;
								   };


						   }
						);
						#endregion

						Func<string, Texture64> t =
							texname => f[texname + ".png"];

						EgoView.FloorTexture = t("floor");
						EgoView.CeilingTexture = t("roof");



						var DynamicTextureBitmap = new Bitmap(new BitmapData(Texture64.SizeConstant, Texture64.SizeConstant, false, 0));
						Texture64 DynamicTexture = DynamicTextureBitmap;
						uint DynamicTextureKey = 0xffffff;

						EgoView.Map.WorldMap[2, 22] = DynamicTextureKey;
						EgoView.Map.WorldMap[3, 15] = DynamicTextureKey;


						EgoView.Map.Textures = new Dictionary<uint, Texture64>
                        {
                            {0xff0000, t("graywall")},
                            {0x0000ff, t("bluewall")},
                            {0x00ff00, t("greenwall")},
                            {0x7F3300, t("woodwall")},

                            {DynamicTextureKey, DynamicTexture}
                        };


						if (EgoView.CurrentTile != 0)
							throw new Exception("bad start position: " + new { EgoView.ViewPositionX, EgoView.ViewPositionY, EgoView.CurrentTile }.ToString());



						CameraView.Map.WorldMap = EgoView.Map.WorldMap;
						CameraView.Map.Textures = EgoView.Map.Textures;
						CameraView.Sprites = EgoView.Sprites;
						CameraView.ViewPosition = EgoView.ViewPosition;

						foreach (var Portal in Portals)
						{
							Portal.View.Map.WorldMap = EgoView.Map.WorldMap;
							Portal.View.Map.Textures = EgoView.Map.Textures;
							Portal.View.Sprites = EgoView.Sprites;
							Portal.AlphaMask = f["portalmask.png"];
						}


						EgoView.RenderScene();


						var MirrorFrame = f["mirror.png"];
						var counter = 0;

						stage.enterFrame += e =>
						{
							counter++;

							if (UpdatePortals)
							{
								// updateing it too often causes framerate to drop

								foreach (var Portal in Portals)
								{
									Portal.Update();
								}

								DynamicTextureBitmap.bitmapData.fillRect(DynamicTextureBitmap.bitmapData.rect, (uint)(counter * 8 % 256));
								var m = new Matrix();

								// to center
								m.translate(0, 10);
								// m.scale(0.3, 0.3);

								CameraView.RenderScene();

								DynamicTextureBitmap.bitmapData.draw(CameraView.Image.bitmapData, m);
								DynamicTextureBitmap.bitmapData.draw(MirrorFrame.bitmapData);

								DynamicTexture.Update();
							}

							EgoView.RenderScene();
						};






					}
				);

			AttachMovementInput(EgoView);
		}