aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Leyh <leyh.thomas@web.de>2016-07-24 08:14:18 +0200
committerThomas Leyh <leyh.thomas@web.de>2016-07-24 08:14:18 +0200
commitced3d03bdb3ce866d832e03fb212865140905a9a (patch)
tree2a16c2063a46d3c354ce1585029dda3124f6ad93
parent0394dccaf06e1009e591a6ff4d645895574724c1 (diff)
downloadV3-release.tar.gz
V3-release.tar.bz2
Add project files.v1.0release
-rw-r--r--GDD.pdfbin0 -> 1782703 bytes
-rw-r--r--LICENCE.txt23
-rw-r--r--README.en.md27
-rw-r--r--README.md52
-rw-r--r--Screenshots/screen1.jpgbin0 -> 402109 bytes
-rw-r--r--Screenshots/screen1_s.jpgbin0 -> 61808 bytes
-rw-r--r--Screenshots/screen2.jpgbin0 -> 344205 bytes
-rw-r--r--Screenshots/screen2_s.jpgbin0 -> 58565 bytes
-rw-r--r--Screenshots/screen3.jpgbin0 -> 366165 bytes
-rw-r--r--Screenshots/screen3_s.jpgbin0 -> 53758 bytes
-rw-r--r--V3.sln43
-rw-r--r--V3/AI/ActionState.cs25
-rw-r--r--V3/AI/AiState.cs27
-rw-r--r--V3/AI/IAction.cs24
-rw-r--r--V3/AI/IAiPlayer.cs51
-rw-r--r--V3/AI/IStrategy.cs17
-rw-r--r--V3/AI/IWorldView.cs21
-rw-r--r--V3/AI/Internal/AbstractAction.cs38
-rw-r--r--V3/AI/Internal/AiPlayer.cs244
-rw-r--r--V3/AI/Internal/AttackStrategy.cs48
-rw-r--r--V3/AI/Internal/IActionFactory.cs27
-rw-r--r--V3/AI/Internal/MoveAction.cs53
-rw-r--r--V3/AI/Internal/SpawnAction.cs42
-rw-r--r--V3/AI/Internal/WorldView.cs19
-rw-r--r--V3/AchievementsAndStatistics.cs31
-rw-r--r--V3/Bindings.cs75
-rw-r--r--V3/CONTROLS.md37
-rw-r--r--V3/CREDITS.md51
-rw-r--r--V3/Camera/CameraCentered.cs57
-rw-r--r--V3/Camera/CameraManager.cs71
-rw-r--r--V3/Camera/CameraScrolling.cs96
-rw-r--r--V3/Camera/CameraType.cs8
-rw-r--r--V3/Camera/ICamera.cs18
-rw-r--r--V3/ClassDiagram1.cd2
-rw-r--r--V3/Content/Buttons/Button-01.pngbin0 -> 99135 bytes
-rw-r--r--V3/Content/Buttons/Button-01_Pressed.pngbin0 -> 112006 bytes
-rw-r--r--V3/Content/Buttons/Button-02.pngbin0 -> 55248 bytes
-rw-r--r--V3/Content/Buttons/Button-02_Pressed.pngbin0 -> 66371 bytes
-rw-r--r--V3/Content/Buttons/Button-03.pngbin0 -> 64359 bytes
-rw-r--r--V3/Content/Buttons/Button-03_Pressed.pngbin0 -> 75343 bytes
-rw-r--r--V3/Content/Buttons/Button-04.pngbin0 -> 56617 bytes
-rw-r--r--V3/Content/Buttons/Button-04_Pressed.pngbin0 -> 67959 bytes
-rw-r--r--V3/Content/Buttons/Button-05.pngbin0 -> 70658 bytes
-rw-r--r--V3/Content/Buttons/Button-05_Pressed.pngbin0 -> 82438 bytes
-rw-r--r--V3/Content/Buttons/Button-06.pngbin0 -> 54510 bytes
-rw-r--r--V3/Content/Buttons/Button-06_Pressed.pngbin0 -> 66074 bytes
-rw-r--r--V3/Content/Buttons/Button-07.pngbin0 -> 72189 bytes
-rw-r--r--V3/Content/Buttons/Button-07_Pressed.pngbin0 -> 84575 bytes
-rw-r--r--V3/Content/Content.mgcb1092
-rw-r--r--V3/Content/Effects/blood_hit_01.pngbin0 -> 109054 bytes
-rw-r--r--V3/Content/Effects/blood_hit_02.pngbin0 -> 38081 bytes
-rw-r--r--V3/Content/Effects/blood_hit_03.pngbin0 -> 41343 bytes
-rw-r--r--V3/Content/Effects/blood_hit_04.pngbin0 -> 91450 bytes
-rw-r--r--V3/Content/Effects/blood_hit_05.pngbin0 -> 71750 bytes
-rw-r--r--V3/Content/Effects/blood_hit_06.pngbin0 -> 36776 bytes
-rw-r--r--V3/Content/Effects/blood_hit_08.pngbin0 -> 115643 bytes
-rw-r--r--V3/Content/Effects/explosion.pngbin0 -> 2623088 bytes
-rw-r--r--V3/Content/Effects/particlefx_03.pngbin0 -> 972652 bytes
-rw-r--r--V3/Content/Effects/particlefx_04.pngbin0 -> 821590 bytes
-rw-r--r--V3/Content/Effects/particlefx_05.pngbin0 -> 544650 bytes
-rw-r--r--V3/Content/Effects/quake.pngbin0 -> 122179 bytes
-rw-r--r--V3/Content/Fonts/Blutschrift.ttfbin0 -> 256596 bytes
-rw-r--r--V3/Content/Fonts/DeathFont.spritefont50
-rw-r--r--V3/Content/Fonts/DejaVuSans.ttfbin0 -> 756072 bytes
-rw-r--r--V3/Content/Fonts/MenuFont.spritefont50
-rw-r--r--V3/Content/Fonts/Siegesschriftzug.ttfbin0 -> 1081476 bytes
-rw-r--r--V3/Content/Fonts/UnitFont.spritefont50
-rw-r--r--V3/Content/Fonts/VictoryFont.spritefont50
-rw-r--r--V3/Content/Fonts/grabstein.ttfbin0 -> 21676 bytes
-rw-r--r--V3/Content/Maps/map_grassland.tmx988
-rw-r--r--V3/Content/Maps/techdemo.tmx1170
-rw-r--r--V3/Content/Maps/work_in_progress.tmx1258
-rw-r--r--V3/Content/Menu/Titel.pngbin0 -> 91996 bytes
-rw-r--r--V3/Content/Menu/arrow_white.pngbin0 -> 3981 bytes
-rw-r--r--V3/Content/Menu/mainscreen.jpgbin0 -> 3376885 bytes
-rw-r--r--V3/Content/Sounds/Afraid_to_Go.mp3bin0 -> 6975318 bytes
-rw-r--r--V3/Content/Sounds/Knight.wavbin0 -> 1947210 bytes
-rw-r--r--V3/Content/Sounds/Kosta_T_-_06.mp3bin0 -> 11340892 bytes
-rw-r--r--V3/Content/Sounds/Monster_Gigante-Doberman-1334685792.wavbin0 -> 1175132 bytes
-rw-r--r--V3/Content/Sounds/Mummy_Zombie-SoundBible.wavbin0 -> 357466 bytes
-rw-r--r--V3/Content/Sounds/SkeletonHorse.wavbin0 -> 2759074 bytes
-rw-r--r--V3/Content/Sounds/explode.wavbin0 -> 303992 bytes
-rw-r--r--V3/Content/Sounds/explodemini.wavbin0 -> 277340 bytes
-rw-r--r--V3/Content/Sounds/explosion1.oggbin0 -> 37165 bytes
-rw-r--r--V3/Content/Sounds/horse.wavbin0 -> 14032 bytes
-rw-r--r--V3/Content/Sounds/impactsplat01.oggbin0 -> 21825 bytes
-rw-r--r--V3/Content/Sounds/punch.wavbin0 -> 140058 bytes
-rw-r--r--V3/Content/Sounds/walking.wavbin0 -> 2102948 bytes
-rw-r--r--V3/Content/Sounds/zonk2.wavbin0 -> 95374 bytes
-rw-r--r--V3/Content/Sources/Horse.blendbin0 -> 11055184 bytes
-rw-r--r--V3/Content/Sources/Skeleton.blendbin0 -> 1101828 bytes
-rw-r--r--V3/Content/Sources/SkeletonHorse.blendbin0 -> 2900188 bytes
-rw-r--r--V3/Content/Sources/SkeletonHorse.pngbin0 -> 1013788 bytes
-rw-r--r--V3/Content/Sources/SkeletonRider.blendbin0 -> 4551660 bytes
-rw-r--r--V3/Content/Sources/SkeletonRider.pngbin0 -> 734006 bytes
-rw-r--r--V3/Content/Sources/ZombieWithClub.pngbin0 -> 921761 bytes
-rw-r--r--V3/Content/Sources/castle.xcfbin0 -> 525634 bytes
-rw-r--r--V3/Content/Sources/create_spritesheet.sh7
-rw-r--r--V3/Content/Sources/fleischklops.blendbin0 -> 2307520 bytes
-rw-r--r--V3/Content/Sources/fleischklops.xcfbin0 -> 1387154 bytes
-rw-r--r--V3/Content/Sources/horse_paint.pngbin0 -> 898326 bytes
-rw-r--r--V3/Content/Sources/horse_paint_sc.pngbin0 -> 1460647 bytes
-rw-r--r--V3/Content/Sources/horse_tack.pngbin0 -> 879871 bytes
-rw-r--r--V3/Content/Sources/houses_front.xcfbin0 -> 221614 bytes
-rw-r--r--V3/Content/Sources/houses_rear.xcfbin0 -> 205943 bytes
-rw-r--r--V3/Content/Sources/human_construction_set.xcfbin0 -> 10007151 bytes
-rw-r--r--V3/Content/Sources/human_construction_set_female.xcfbin0 -> 6728872 bytes
-rw-r--r--V3/Content/Sources/king.blendbin0 -> 4472284 bytes
-rw-r--r--V3/Content/Sources/king_head.pngbin0 -> 131639 bytes
-rw-r--r--V3/Content/Sources/necromancer.xcfbin0 -> 5307898 bytes
-rw-r--r--V3/Content/Sources/pathfinder.xcfbin0 -> 2470 bytes
-rw-r--r--V3/Content/Sources/prince.blendbin0 -> 3927028 bytes
-rw-r--r--V3/Content/Sources/prince.xcfbin0 -> 2523537 bytes
-rw-r--r--V3/Content/Sources/selection.xcfbin0 -> 151080 bytes
-rw-r--r--V3/Content/Sources/skeleton_horse.pngbin0 -> 1083757 bytes
-rw-r--r--V3/Content/Sources/the_triumph_of_death.jpgbin0 -> 3376885 bytes
-rw-r--r--V3/Content/Sources/zombie.blendbin0 -> 1563140 bytes
-rw-r--r--V3/Content/Sprites/WhiteRectangle.pngbin0 -> 67 bytes
-rw-r--r--V3/Content/Sprites/arrows.pngbin0 -> 4218 bytes
-rw-r--r--V3/Content/Sprites/buckler.pngbin0 -> 189331 bytes
-rw-r--r--V3/Content/Sprites/buckler_female.pngbin0 -> 150081 bytes
-rw-r--r--V3/Content/Sprites/chain.pngbin0 -> 680942 bytes
-rw-r--r--V3/Content/Sprites/chain_female.pngbin0 -> 594899 bytes
-rw-r--r--V3/Content/Sprites/cloth.pngbin0 -> 627879 bytes
-rw-r--r--V3/Content/Sprites/cloth_female.pngbin0 -> 560029 bytes
-rw-r--r--V3/Content/Sprites/cloud.pngbin0 -> 54576 bytes
-rw-r--r--V3/Content/Sprites/ellipse.pngbin0 -> 10943 bytes
-rw-r--r--V3/Content/Sprites/fleischklops.pngbin0 -> 2466843 bytes
-rw-r--r--V3/Content/Sprites/fog.pngbin0 -> 44834 bytes
-rw-r--r--V3/Content/Sprites/head.pngbin0 -> 105453 bytes
-rw-r--r--V3/Content/Sprites/head_bald.pngbin0 -> 107161 bytes
-rw-r--r--V3/Content/Sprites/head_chain.pngbin0 -> 145589 bytes
-rw-r--r--V3/Content/Sprites/head_chain_female.pngbin0 -> 134925 bytes
-rw-r--r--V3/Content/Sprites/head_female.pngbin0 -> 120410 bytes
-rw-r--r--V3/Content/Sprites/head_plate.pngbin0 -> 158766 bytes
-rw-r--r--V3/Content/Sprites/head_plate_female.pngbin0 -> 146096 bytes
-rw-r--r--V3/Content/Sprites/king.pngbin0 -> 822995 bytes
-rw-r--r--V3/Content/Sprites/longsword.pngbin0 -> 145022 bytes
-rw-r--r--V3/Content/Sprites/longsword_female.pngbin0 -> 156551 bytes
-rw-r--r--V3/Content/Sprites/necromancer.pngbin0 -> 705759 bytes
-rw-r--r--V3/Content/Sprites/necromancer_female.pngbin0 -> 659280 bytes
-rw-r--r--V3/Content/Sprites/nude.pngbin0 -> 632199 bytes
-rw-r--r--V3/Content/Sprites/nude_female.pngbin0 -> 567054 bytes
-rw-r--r--V3/Content/Sprites/plate.pngbin0 -> 702344 bytes
-rw-r--r--V3/Content/Sprites/plate_female.pngbin0 -> 623813 bytes
-rw-r--r--V3/Content/Sprites/prince.pngbin0 -> 855383 bytes
-rw-r--r--V3/Content/Sprites/selection.pngbin0 -> 1327 bytes
-rw-r--r--V3/Content/Sprites/shield.pngbin0 -> 253328 bytes
-rw-r--r--V3/Content/Sprites/shield_female.pngbin0 -> 257298 bytes
-rw-r--r--V3/Content/Sprites/shortsword.pngbin0 -> 91334 bytes
-rw-r--r--V3/Content/Sprites/shortsword_female.pngbin0 -> 99034 bytes
-rw-r--r--V3/Content/Sprites/skeleton.pngbin0 -> 950304 bytes
-rw-r--r--V3/Content/Sprites/skeleton_archer.pngbin0 -> 1082133 bytes
-rw-r--r--V3/Content/Sprites/skeleton_elite.pngbin0 -> 1191913 bytes
-rw-r--r--V3/Content/Sprites/skeleton_horse.pngbin0 -> 1013788 bytes
-rw-r--r--V3/Content/Sprites/skeleton_rider.pngbin0 -> 734006 bytes
-rw-r--r--V3/Content/Sprites/staff.pngbin0 -> 324639 bytes
-rw-r--r--V3/Content/Sprites/staff_female.pngbin0 -> 336664 bytes
-rw-r--r--V3/Content/Sprites/zombie.pngbin0 -> 801160 bytes
-rw-r--r--V3/Content/Sprites/zombie_club.pngbin0 -> 921761 bytes
-rw-r--r--V3/Content/Textures/EmptyPixel.pngbin0 -> 169 bytes
-rw-r--r--V3/Content/Textures/castle.pngbin0 -> 1668329 bytes
-rw-r--r--V3/Content/Textures/grassland.pngbin0 -> 430934 bytes
-rw-r--r--V3/Content/Textures/grassland_trees.pngbin0 -> 445999 bytes
-rw-r--r--V3/Content/Textures/grassland_water.pngbin0 -> 254809 bytes
-rw-r--r--V3/Content/Textures/houses_front.pngbin0 -> 287910 bytes
-rw-r--r--V3/Content/Textures/houses_rear.pngbin0 -> 302161 bytes
-rw-r--r--V3/Content/Textures/medieval_building_tiles.pngbin0 -> 515727 bytes
-rw-r--r--V3/Content/Textures/pathfinder.pngbin0 -> 289 bytes
-rw-r--r--V3/Data/DebugMode.cs21
-rw-r--r--V3/Data/GameState.cs64
-rw-r--r--V3/Data/IGameStateManager.cs20
-rw-r--r--V3/Data/IOptionsManager.cs18
-rw-r--r--V3/Data/IPathManager.cs30
-rw-r--r--V3/Data/ISaveGame.cs25
-rw-r--r--V3/Data/ISaveGameManager.cs25
-rw-r--r--V3/Data/Internal/GameStateManager.cs81
-rw-r--r--V3/Data/Internal/OptionsManager.cs65
-rw-r--r--V3/Data/Internal/PathManager.cs51
-rw-r--r--V3/Data/Internal/SaveGame.cs65
-rw-r--r--V3/Data/Internal/SaveGameManager.cs88
-rw-r--r--V3/Data/Options.cs105
-rw-r--r--V3/Effects/AbstractEffect.cs74
-rw-r--r--V3/Effects/BloodBang.cs8
-rw-r--r--V3/Effects/BloodExplosion.cs11
-rw-r--r--V3/Effects/BloodFountain.cs11
-rw-r--r--V3/Effects/EffectsManager.cs54
-rw-r--r--V3/Effects/Explosion.cs14
-rw-r--r--V3/Effects/HorseEffect.cs11
-rw-r--r--V3/Effects/IEffect.cs44
-rw-r--r--V3/Effects/IEffectsManager.cs31
-rw-r--r--V3/Effects/Quake.cs11
-rw-r--r--V3/Effects/SmokeBig.cs10
-rw-r--r--V3/Effects/SmokeMedium.cs13
-rw-r--r--V3/Effects/SmokeSmall.cs10
-rw-r--r--V3/Ellipse.cs31
-rw-r--r--V3/Faction.cs7
-rw-r--r--V3/Icon.icobin0 -> 32038 bytes
-rw-r--r--V3/Input/IInputManager.cs34
-rw-r--r--V3/Input/IKeyEvent.cs20
-rw-r--r--V3/Input/IMouseEvent.cs34
-rw-r--r--V3/Input/IMouseEventHandler.cs14
-rw-r--r--V3/Input/Internal/InputManager.cs141
-rw-r--r--V3/Input/Internal/KeyEvent.cs32
-rw-r--r--V3/Input/Internal/MouseEvent.cs57
-rw-r--r--V3/Input/MouseButton.cs9
-rw-r--r--V3/Map/AbstractLayer.cs259
-rw-r--r--V3/Map/Area.cs119
-rw-r--r--V3/Map/Constants.cs12
-rw-r--r--V3/Map/FloorLayer.cs21
-rw-r--r--V3/Map/FogOfWar.cs107
-rw-r--r--V3/Map/IMapManager.cs88
-rw-r--r--V3/Map/MapManager.cs97
-rw-r--r--V3/Map/ObjectLayer.cs16
-rw-r--r--V3/Map/Pathfinder.cs455
-rw-r--r--V3/Map/PathfindingGrid.cs125
-rw-r--r--V3/Map/SearchNode.cs31
-rw-r--r--V3/Map/TiledParser.cs256
-rw-r--r--V3/Map/Tileset.cs89
-rw-r--r--V3/Node.cs467
-rw-r--r--V3/Objects/AbstractBuilding.cs110
-rw-r--r--V3/Objects/AbstractCreature.cs911
-rw-r--r--V3/Objects/Arrow.cs109
-rw-r--r--V3/Objects/BuildingState.cs8
-rw-r--r--V3/Objects/Castle.cs15
-rw-r--r--V3/Objects/CreatureFactory.cs127
-rw-r--r--V3/Objects/FemalePeasant.cs50
-rw-r--r--V3/Objects/Forge.cs19
-rw-r--r--V3/Objects/IBasicCreatureFactory.cs28
-rw-r--r--V3/Objects/IBuilding.cs20
-rw-r--r--V3/Objects/ICreature.cs120
-rw-r--r--V3/Objects/IGameObject.cs43
-rw-r--r--V3/Objects/IObjectsManager.cs131
-rw-r--r--V3/Objects/IdGenerator.cs59
-rw-r--r--V3/Objects/King.cs33
-rw-r--r--V3/Objects/KingsGuard.cs62
-rw-r--r--V3/Objects/Knight.cs65
-rw-r--r--V3/Objects/MalePeasant.cs49
-rw-r--r--V3/Objects/Meatball.cs36
-rw-r--r--V3/Objects/Movement.cs20
-rw-r--r--V3/Objects/Movement/CountStepsMovement.cs16
-rw-r--r--V3/Objects/Movement/IMovable.cs43
-rw-r--r--V3/Objects/Movement/PlayerMovement.cs155
-rw-r--r--V3/Objects/Necromancer.cs61
-rw-r--r--V3/Objects/ObjectsManager.cs314
-rw-r--r--V3/Objects/Prince.cs33
-rw-r--r--V3/Objects/Selection.cs483
-rw-r--r--V3/Objects/Skeleton.cs65
-rw-r--r--V3/Objects/SkeletonElite.cs19
-rw-r--r--V3/Objects/SkeletonHorse.cs87
-rw-r--r--V3/Objects/Sprite/AbstractSpriteCreature.cs201
-rw-r--r--V3/Objects/Sprite/ArrowSprite.cs42
-rw-r--r--V3/Objects/Sprite/BucklerFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/BucklerSprite.cs7
-rw-r--r--V3/Objects/Sprite/ChainFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/ChainSprite.cs7
-rw-r--r--V3/Objects/Sprite/ClothFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/ClothSprite.cs7
-rw-r--r--V3/Objects/Sprite/EquipmentType.cs10
-rw-r--r--V3/Objects/Sprite/HeadBaldSprite.cs10
-rw-r--r--V3/Objects/Sprite/HeadChainFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/HeadChainSprite.cs7
-rw-r--r--V3/Objects/Sprite/HeadFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/HeadPlateFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/HeadPlateSprite.cs7
-rw-r--r--V3/Objects/Sprite/HeadSprite.cs7
-rw-r--r--V3/Objects/Sprite/ISpriteCreature.cs53
-rw-r--r--V3/Objects/Sprite/KingSprite.cs7
-rw-r--r--V3/Objects/Sprite/LongswordFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/LongswordSprite.cs7
-rw-r--r--V3/Objects/Sprite/MeatballSprite.cs11
-rw-r--r--V3/Objects/Sprite/NecromancerFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/NecromancerSprite.cs7
-rw-r--r--V3/Objects/Sprite/NudeFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/NudeSprite.cs7
-rw-r--r--V3/Objects/Sprite/PlateFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/PlateSprite.cs7
-rw-r--r--V3/Objects/Sprite/PrinceSprite.cs7
-rw-r--r--V3/Objects/Sprite/ShieldFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/ShieldSprite.cs7
-rw-r--r--V3/Objects/Sprite/ShortswordFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/ShortswordSprite.cs7
-rw-r--r--V3/Objects/Sprite/SkeletonArcherSprite.cs10
-rw-r--r--V3/Objects/Sprite/SkeletonEliteSprite.cs9
-rw-r--r--V3/Objects/Sprite/SkeletonHorseSprite.cs7
-rw-r--r--V3/Objects/Sprite/SkeletonRiderSprite.cs10
-rw-r--r--V3/Objects/Sprite/SkeletonSprite.cs9
-rw-r--r--V3/Objects/Sprite/StaffFemaleSprite.cs7
-rw-r--r--V3/Objects/Sprite/StaffSprite.cs7
-rw-r--r--V3/Objects/Sprite/ZombieSprite.cs11
-rw-r--r--V3/Objects/Sprite/ZombieWithClubSprite.cs11
-rw-r--r--V3/Objects/TextureObject.cs76
-rw-r--r--V3/Objects/Transformation.cs102
-rw-r--r--V3/Objects/Woodhouse.cs16
-rw-r--r--V3/Objects/Zombie.cs45
-rw-r--r--V3/OpenTK.dll.config25
-rw-r--r--V3/Program.cs20
-rw-r--r--V3/Properties/AssemblyInfo.cs35
-rw-r--r--V3/Quadtree.cs87
-rw-r--r--V3/Screens/AbstractScreen.cs57
-rw-r--r--V3/Screens/AchievementsScreen.cs221
-rw-r--r--V3/Screens/DeathScreen.cs140
-rw-r--r--V3/Screens/DebugScreen.cs92
-rw-r--r--V3/Screens/FpsCounter.cs46
-rw-r--r--V3/Screens/GameScreen.cs306
-rw-r--r--V3/Screens/HudScreen.cs349
-rw-r--r--V3/Screens/IDrawable.cs19
-rw-r--r--V3/Screens/IScreen.cs39
-rw-r--r--V3/Screens/IScreenFactory.cs27
-rw-r--r--V3/Screens/IScreenManager.cs35
-rw-r--r--V3/Screens/IUpdatable.cs16
-rw-r--r--V3/Screens/LoadScreen.cs124
-rw-r--r--V3/Screens/MainScreen.cs195
-rw-r--r--V3/Screens/MenuActions.cs208
-rw-r--r--V3/Screens/OptionsScreen.cs197
-rw-r--r--V3/Screens/PauseScreen.cs140
-rw-r--r--V3/Screens/ScreenManager.cs164
-rw-r--r--V3/Screens/StatisticsScreen.cs186
-rw-r--r--V3/Screens/TechdemoScreen.cs325
-rw-r--r--V3/Screens/VictoryScreen.cs76
-rw-r--r--V3/UpdatesPerSecond.cs55
-rw-r--r--V3/V3.csproj498
-rw-r--r--V3/V3Game.cs78
-rw-r--r--V3/Widgets/AbstractMenu.cs96
-rw-r--r--V3/Widgets/AbstractTextWidget.cs113
-rw-r--r--V3/Widgets/AchievementBox.cs91
-rw-r--r--V3/Widgets/Button.cs111
-rw-r--r--V3/Widgets/EmptyWidget.cs41
-rw-r--r--V3/Widgets/FormMenu.cs75
-rw-r--r--V3/Widgets/HorizontalOrientation.cs9
-rw-r--r--V3/Widgets/IBasicWidgetFactory.cs38
-rw-r--r--V3/Widgets/IClickable.cs14
-rw-r--r--V3/Widgets/IImageWidget.cs15
-rw-r--r--V3/Widgets/IMenu.cs33
-rw-r--r--V3/Widgets/IMenuFactory.cs9
-rw-r--r--V3/Widgets/ISelectable.cs27
-rw-r--r--V3/Widgets/ITextWidget.cs43
-rw-r--r--V3/Widgets/IWidget.cs34
-rw-r--r--V3/Widgets/Label.cs14
-rw-r--r--V3/Widgets/SelectButton.cs108
-rw-r--r--V3/Widgets/VerticalMenu.cs42
-rw-r--r--V3/Widgets/WidgetFactory.cs78
-rw-r--r--V3/packages.config6
343 files changed, 18300 insertions, 0 deletions
diff --git a/GDD.pdf b/GDD.pdf
new file mode 100644
index 0000000..7a16f49
--- /dev/null
+++ b/GDD.pdf
Binary files differ
diff --git a/LICENCE.txt b/LICENCE.txt
new file mode 100644
index 0000000..4f8f342
--- /dev/null
+++ b/LICENCE.txt
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2016
+ Anna Windbühler, Alexander Steinmark, Fabrizio Costea,
+ Thomas Leyh, Denis Veil, Robin Krahl
+
+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. \ No newline at end of file
diff --git a/README.en.md b/README.en.md
new file mode 100644
index 0000000..2aa3cb2
--- /dev/null
+++ b/README.en.md
@@ -0,0 +1,27 @@
+Vagram's Vicious Vengeance
+======================
+
+This video game was created at the [University of Freiburg](http://www.uni-freiburg.de/) during a lecture of the [Chair of Software Engineering](http://swt.informatik.uni-freiburg.de/). Six people with varying degrees of programming experience were randomly thrown together. They somehow made a game during summer 2016 which you can see here.
+
+You are playing the necromancer Vagram, former personal wizard of King Harry. But when he was shamefully banished from the kingdom he swore fierce revenge. Now he is back with his dark arts to bring death and ruin to the whole kingdom.
+
+The game is in german but there are no dialogs or anything like that. Most things should be self-explanatory.
+
+* [For basic controls see here.](V3/CONTROLS.md)
+* [See here for credits](V3/CREDITS.md) to the many talented people we used sounds and graphics from.
+
+Dependencies
+------------
+
+* [MonoGame 3.5](http://www.monogame.net/)
+* .Net 4.5 / alternatively [Mono 4.4](http://www.mono-project.com/)
+* [OpenAL](http://www.openal.org/)
+
+Screenshots
+-----------------
+
+[![Screenshot 1](Screenshots/screen1_s.jpg)](Screenshots/screen1.jpg)
+
+[![Screenshot 2](Screenshots/screen2_s.jpg)](Screenshots/screen2.jpg)
+
+[![Screenshot 3](Screenshots/screen3_s.jpg)](Screenshots/screen3.jpg) \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0068c6d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+Vagram's Vicious Vengeance
+======================
+
+[For an english version see here.](README.en.md)
+
+Dieses Videospiel ist im Rahmen des [Softwarepraktikums](https://sopra.informatik.uni-freiburg.de/) des [Lehrstuhls für Softwaretechnik](http://swt.informatik.uni-freiburg.de/) an der [Albert-Ludwigs-Universität Freiburg](http://www.uni-freiburg.de/) entstanden. Sechs zufällig zusammengewürfelte Leute mit unterschiedlicher Erfahrung saßen im Sommersemester 2016 zusammen und haben irgendwas zurechtgehackt.
+
+Verwendet wurde das [MonoGame-Framework](http://www.monogame.net/) und eine Menge freier Sounds und Grafiken, die gesondert [in den CREDITS](V3/CREDITS.md) gelistet werden.
+
+Die Steuerung erfolgt vorwiegend über die Maus und ist ziemlich konservativ. Dennoch gibt es eine [Übersicht über die möglichen Befehle hier](V3/CONTROLS.md).
+
+Zusammenfassung des Spiels
+--------------------------
+
+*"Pest und Verderben! Seid verflucht! Wie konntet Ihr mir das antun? Den Tod über eure Sippe! Über euer Land! Mit eigenen Händen werde ich dieses Königreich vernichten, und wenn es mich meine Seele kosten mag."*
+
+Als ehemaliger Hofzauberer Vagrant kennt Ihr nur noch ein Ziel: Rache an König Harry, der verantwortlich für den Tod Eurer Familie ist. Auf Euch allein gestellt ohne auch nur einen Verbündeten bleibt euch dazu nur ein Mittel: Die verbotene Kunst der Totenbeschwörung.
+
+Pilgert durch das Königreich und hinterlasst eine Spur des Verwüstung. Entvölkert ganze Dörfer und fügt die wiederbelebten Kadaver Eurer Zombiearmee hinzu. Zerstört wo und was Ihr nur könnt und nutzt die Überbleibsel zur Verstärkung Eurer willenlosen Streitkräfte. Verschmelzt in unheiligen Ritualen Eure Kreaturen zu noch mächtigeren Monstrositäten. Überrennt die verblendeten Vasallen des Königs mit euren Dienern aus Knochen und verwesendem Fleisch, denn sie haben es verdient. Jeder Nachkomme des verhassten Königs muss ausgemerzt werden um Euren Durst nach Rache zu stillen.
+
+In dieser Mischung aus Action-RPG und Echtzeitstrategie geht es nicht um Aufbau und Eroberung. Kein Stein darf auf dem anderen bleiben, wollt Ihr Erfolg haben. Im Rahmen der Kampagne führt ihr Kapitel für Kapitel das Königreich seinem Untergang entgegen. Und nun legt alle Skrupel ab, denn für Erlösung ist es längst zu spät.
+
+Alleinstellungsmerkmale
+-----------------------
+
+Bei Vagrant's Vicious Vengeance soll eine offensive und risikoreiche Spielweise gefördert werden. Aus diesem Grund wird einerseits auf Aufbauelemente verzichtet. Einheiten werden aus Kadavern erschaffen, die durch die menschliche Bevölkerung einer Karte begrenzt sind. Einheiten können ebenfalls verstärkt werden, lässt man sie Gebäude zertrümmern und die Überreste plündern. Auch diese sind nur begrenzt vorhanden.
+
+Andererseits ist der Totenbeschwörer selbst von zentraler Bedeutung: Ist er nicht anwesend, können die erschaffenen Einheiten nicht kontrolliert werden. Nur in seiner Gegenwart können Befehle erteilt und Spezialfähigkeiten aktiviert werden. Außerdem können Einheiten kombiniert werden, um noch mächtigere Zombies zu erschaffen. Für sich selbst genommen besitzt der Totenbeschwörer jedoch keine Angriffsmöglichkeiten, somit ist er also ohne beschworene Einheiten komplett wehrlos.
+
+*Weiteres langweiliges Palaver darf gerne dem [Game Design Document](GDD.pdf) entnommen werden.*
+
+Abhängigkeiten
+-------------------
+
+* [MonoGame 3.5](http://www.monogame.net/)
+* .Net 4.5 / alternativ [Mono 4.4](http://www.mono-project.com/)
+* [OpenAL](http://www.openal.org/)
+
+Screenshots
+-----------------
+
+[![Screenshot 1](Screenshots/screen1_s.jpg)](Screenshots/screen1.jpg)
+
+[![Screenshot 2](Screenshots/screen2_s.jpg)](Screenshots/screen2.jpg)
+
+[![Screenshot 3](Screenshots/screen3_s.jpg)](Screenshots/screen3.jpg)
+
+Bekannte Probleme
+--------------------
+
+* Unter Linux werden keine Umlaute angezeigt.
+* Temporärer Framerateeinbruch, wenn man sehr vielen Einheiten auf einmal den Laufbefehl gibt.
diff --git a/Screenshots/screen1.jpg b/Screenshots/screen1.jpg
new file mode 100644
index 0000000..5a550fa
--- /dev/null
+++ b/Screenshots/screen1.jpg
Binary files differ
diff --git a/Screenshots/screen1_s.jpg b/Screenshots/screen1_s.jpg
new file mode 100644
index 0000000..2c5355b
--- /dev/null
+++ b/Screenshots/screen1_s.jpg
Binary files differ
diff --git a/Screenshots/screen2.jpg b/Screenshots/screen2.jpg
new file mode 100644
index 0000000..d7a200c
--- /dev/null
+++ b/Screenshots/screen2.jpg
Binary files differ
diff --git a/Screenshots/screen2_s.jpg b/Screenshots/screen2_s.jpg
new file mode 100644
index 0000000..6ebe2fb
--- /dev/null
+++ b/Screenshots/screen2_s.jpg
Binary files differ
diff --git a/Screenshots/screen3.jpg b/Screenshots/screen3.jpg
new file mode 100644
index 0000000..9c48639
--- /dev/null
+++ b/Screenshots/screen3.jpg
Binary files differ
diff --git a/Screenshots/screen3_s.jpg b/Screenshots/screen3_s.jpg
new file mode 100644
index 0000000..5b1560c
--- /dev/null
+++ b/Screenshots/screen3_s.jpg
Binary files differ
diff --git a/V3.sln b/V3.sln
new file mode 100644
index 0000000..e9965be
--- /dev/null
+++ b/V3.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "V3", "V3\V3.csproj", "{4C475FE4-ECB6-4D25-B7E6-0B773A736A24}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A47C7086-4DF1-4BBC-885B-9CC9EA3D78F8}"
+ ProjectSection(SolutionItems) = preProject
+ GDD.pdf = GDD.pdf
+ LICENCE.txt = LICENCE.txt
+ README.en.md = README.en.md
+ README.md = README.md
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Screenshots", "Screenshots", "{1D632156-7EC9-4DBD-A79D-2E4686D35FBA}"
+ ProjectSection(SolutionItems) = preProject
+ Screenshots\screen1.jpg = Screenshots\screen1.jpg
+ Screenshots\screen1_s.jpg = Screenshots\screen1_s.jpg
+ Screenshots\screen2.jpg = Screenshots\screen2.jpg
+ Screenshots\screen2_s.jpg = Screenshots\screen2_s.jpg
+ Screenshots\screen3.jpg = Screenshots\screen3.jpg
+ Screenshots\screen3_s.jpg = Screenshots\screen3_s.jpg
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Larger Map|x86 = Larger Map|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Debug|x86.ActiveCfg = Debug|x86
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Debug|x86.Build.0 = Debug|x86
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Larger Map|x86.ActiveCfg = Larger Map|x86
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Larger Map|x86.Build.0 = Larger Map|x86
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Release|x86.ActiveCfg = Release|x86
+ {4C475FE4-ECB6-4D25-B7E6-0B773A736A24}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/V3/AI/ActionState.cs b/V3/AI/ActionState.cs
new file mode 100644
index 0000000..18996c8
--- /dev/null
+++ b/V3/AI/ActionState.cs
@@ -0,0 +1,25 @@
+namespace V3.AI
+{
+ /// <summary>
+ /// The state of an action taken by the computer player.
+ /// </summary>
+ public enum ActionState
+ {
+ /// <summary>
+ /// The action is waiting to be executed.
+ /// </summary>
+ Waiting,
+ /// <summary>
+ /// The action is currently being executed.
+ /// </summary>
+ Executing,
+ /// <summary>
+ /// The action has been done successfully.
+ /// </summary>
+ Done,
+ /// <summary>
+ /// The action failed.
+ /// </summary>
+ Failed
+ }
+}
diff --git a/V3/AI/AiState.cs b/V3/AI/AiState.cs
new file mode 100644
index 0000000..1bc0ab0
--- /dev/null
+++ b/V3/AI/AiState.cs
@@ -0,0 +1,27 @@
+namespace V3.AI
+{
+ /// <summary>
+ /// An action state for the AI player that is part of a strategy. A state
+ /// defines the specific actions to take (for example, defend peasants, or
+ /// attack enemy creatures).
+ /// </summary>
+ public enum AiState
+ {
+ /// <summary>
+ /// Waiting for the player actions.
+ /// </summary>
+ Idle,
+ /// <summary>
+ /// Defend peasants so that they don't become zombies.
+ /// </summary>
+ DefendPeasants,
+ /// <summary>
+ /// Attack enemy creatures.
+ /// </summary>
+ AttackCreatures,
+ /// <summary>
+ /// Attack the necromancer directly.
+ /// </summary>
+ AttackNecromancer
+ }
+}
diff --git a/V3/AI/IAction.cs b/V3/AI/IAction.cs
new file mode 100644
index 0000000..b68355f
--- /dev/null
+++ b/V3/AI/IAction.cs
@@ -0,0 +1,24 @@
+namespace V3.AI
+{
+ /// <summary>
+ /// An action that can be taken by the computer player.
+ /// </summary>
+ public interface IAction
+ {
+ /// <summary>
+ /// The current state of the action.
+ /// </summary>
+ ActionState State { get; }
+
+ /// <summary>
+ /// Start the execution of the action.
+ /// </summary>
+ void Start();
+
+ /// <summary>
+ /// Update the execution state. This method should be repateatingly
+ /// called as long as State is Executing.
+ /// </summary>
+ void Update();
+ }
+}
diff --git a/V3/AI/IAiPlayer.cs b/V3/AI/IAiPlayer.cs
new file mode 100644
index 0000000..7f5f81b
--- /dev/null
+++ b/V3/AI/IAiPlayer.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace V3.AI
+{
+ /// <summary>
+ /// A computer player that takes actions according to a specified strategy.
+ /// </summary>
+ public interface IAiPlayer
+ {
+ /// <summary>
+ /// The current world view of the player. It stores the knowledge of
+ /// the computer player based on the previous percepts.
+ /// </summary>
+ IWorldView WorldView { get; }
+ /// <summary>
+ /// The strategy of the player. The strategy is a state machine that
+ /// defines the current state.
+ /// </summary>
+ IStrategy Strategy { get; }
+ /// <summary>
+ /// The current state of the player. The state is one step of the
+ /// strategy, and defines the specific actions to take.
+ /// </summary>
+ AiState State { get; set; }
+ /// <summary>
+ /// The actions that the player wants to be executed. Updated by
+ /// Act.
+ /// </summary>
+ IList<IAction> Actions { get; }
+
+ /// <summary>
+ /// Executes one update cycle -- perception, acting and the execution
+ /// of actions.
+ /// </summary>
+ /// <param name="gameTime">the time since the last update</param>
+ void Update(GameTime gameTime);
+
+ /// <summary>
+ /// Update the AI's view of the game world.
+ /// </summary>
+ void Percept();
+
+ /// <summary>
+ /// Take actions based on the previous percepts, the current strategy
+ /// and state. Updates the list of actions stored in Actions. These
+ /// should be executed by the caller.
+ /// </summary>
+ void Act();
+ }
+}
diff --git a/V3/AI/IStrategy.cs b/V3/AI/IStrategy.cs
new file mode 100644
index 0000000..4bfb99c
--- /dev/null
+++ b/V3/AI/IStrategy.cs
@@ -0,0 +1,17 @@
+namespace V3.AI
+{
+ /// <summary>
+ /// A strategy for the computer player. A strategy is a finite state
+ /// machine.
+ /// </summary>
+ public interface IStrategy
+ {
+ /// <summary>
+ /// Updates the current state according to the game situtation.
+ /// </summary>
+ /// <param name="state">the current state</param>
+ /// <param name="worldView">the current view of the game world</param>
+ /// <returns>the next state indicated by this strategy</returns>
+ AiState Update(AiState state, IWorldView worldView);
+ }
+}
diff --git a/V3/AI/IWorldView.cs b/V3/AI/IWorldView.cs
new file mode 100644
index 0000000..b47322f
--- /dev/null
+++ b/V3/AI/IWorldView.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using V3.Objects;
+
+namespace V3.AI
+{
+ /// <summary>
+ /// Stores the knowledge of the computer player about the game world, and
+ /// is used for the evaluation of the strategy. It is also used to decide
+ /// which actions to take based on the current state.
+ /// </summary>
+ public interface IWorldView
+ {
+ int EnemyCount { get; set; }
+ int InitialPlebsCount { get; set; }
+ int PlebsCount { get; set; }
+ float NecromancerHealth { get; set; }
+ List<ICreature> IdlingKnights { get; }
+ List<ICreature> Targets { get; }
+ List<ICreature> Plebs { get; }
+ }
+}
diff --git a/V3/AI/Internal/AbstractAction.cs b/V3/AI/Internal/AbstractAction.cs
new file mode 100644
index 0000000..1b5f73e
--- /dev/null
+++ b/V3/AI/Internal/AbstractAction.cs
@@ -0,0 +1,38 @@
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Abstract implementation of IAction.
+ /// </summary>
+ public abstract class AbstractAction : IAction
+ {
+ /// <summary>
+ /// The current state of the action.
+ /// </summary>
+ public ActionState State { get; private set; } = ActionState.Waiting;
+
+ /// <summary>
+ /// Start the execution of the action.
+ /// </summary>
+ public virtual void Start()
+ {
+ State = ActionState.Executing;
+ }
+
+ /// <summary>
+ /// Update the execution state. This method should be repateatingly
+ /// called as long as State is Executing.
+ /// </summary>
+ public virtual void Update()
+ {
+ if (State != ActionState.Executing)
+ return;
+ State = GetNextState();
+ }
+
+ /// <summary>
+ /// Returns the next state of this action. It is guaranteed that the
+ /// current state is Executing.
+ /// </summary>
+ protected abstract ActionState GetNextState();
+ }
+}
diff --git a/V3/AI/Internal/AiPlayer.cs b/V3/AI/Internal/AiPlayer.cs
new file mode 100644
index 0000000..6040348
--- /dev/null
+++ b/V3/AI/Internal/AiPlayer.cs
@@ -0,0 +1,244 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Default implementation of IAiPlayer.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class AiPlayer : IAiPlayer
+ {
+ /// <summary>
+ /// The current world view of the player. It stores the knowledge of
+ /// the computer player based on the previous percepts.
+ /// </summary>
+ public IWorldView WorldView { get; } = new WorldView();
+ /// <summary>
+ /// The strategy of the player. The strategy is a state machine that
+ /// defines the current state.
+ /// </summary>
+ public IStrategy Strategy { get; } = new AttackStrategy();
+ /// <summary>
+ /// The current state of the player. The state is one step of the
+ /// strategy, and defines the specific actions to take.
+ /// </summary>
+ public AiState State { get; set; } = AiState.Idle;
+ /// <summary>
+ /// The actions that the player wants to be executed. Updated by
+ /// Act.
+ /// </summary>
+ public IList<IAction> Actions { get; } = new List<IAction>();
+
+ private readonly IActionFactory mActionFactory;
+ private readonly IBasicCreatureFactory mCreatureFactory;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly UpdatesPerSecond mUpS = new UpdatesPerSecond(1);
+ private readonly Random mRandom = new Random();
+ private TimeSpan mTimeSpan = TimeSpan.Zero;
+ private TimeSpan mTimeSpanSpawn = TimeSpan.Zero;
+ private int mMaxWaitBaseMs = (int) TimeSpan.FromSeconds(20).TotalMilliseconds;
+ private int mMaxWaitAddMs = (int) TimeSpan.FromSeconds(60).TotalMilliseconds;
+
+ /// <summary>
+ /// Creates a new AI player.
+ /// </summary>
+ public AiPlayer(IActionFactory actionFactory, IBasicCreatureFactory creatureFactory,
+ IObjectsManager objectsManager)
+ {
+ mActionFactory = actionFactory;
+ mCreatureFactory = creatureFactory;
+ mObjectsManager = objectsManager;
+ }
+
+ /// <summary>
+ /// Executes one update cycle -- perception, acting and the execution
+ /// of actions.
+ /// </summary>
+ /// <param name="gameTime">the time since the last update</param>
+ public void Update(GameTime gameTime)
+ {
+ mTimeSpan += gameTime.ElapsedGameTime;
+ if (!mUpS.IsItTime(gameTime))
+ return;
+ Percept();
+ Act();
+ foreach (var action in Actions)
+ {
+ if (action.State == ActionState.Waiting)
+ action.Start();
+ action.Update();
+ }
+ }
+
+ /// <summary>
+ /// Update the AI's view of the game world.
+ /// </summary>
+ public void Percept()
+ {
+ WorldView.EnemyCount = 0;
+ WorldView.PlebsCount = 0;
+ WorldView.NecromancerHealth = 0;
+ WorldView.IdlingKnights.Clear();
+ WorldView.Targets.Clear();
+ WorldView.Plebs.Clear();
+
+ foreach (var creature in mObjectsManager.CreatureList)
+ {
+ if (creature.Faction == Faction.Kingdom)
+ {
+ if (!creature.IsDead)
+ {
+ if (creature is Knight)
+ {
+ if (creature.MovementState == MovementState.Idle && creature.IsAttacking == null)
+ WorldView.IdlingKnights.Add(creature);
+ }
+ }
+ }
+ else if (creature.Faction == Faction.Undead)
+ {
+ if (!creature.IsDead)
+ {
+ if (!(creature is Necromancer))
+ WorldView.EnemyCount++;
+ else
+ WorldView.NecromancerHealth = (float) creature.Life / creature.MaxLife;
+ WorldView.Targets.Add(creature);
+ }
+ }
+ else if (creature.Faction == Faction.Plebs)
+ {
+ if (!creature.IsDead)
+ {
+ WorldView.Plebs.Add(creature);
+ }
+ }
+
+ WorldView.PlebsCount = WorldView.Plebs.Count;
+ if (WorldView.InitialPlebsCount < WorldView.PlebsCount)
+ WorldView.InitialPlebsCount = WorldView.PlebsCount;
+ }
+ }
+
+ private TimeSpan GetRandomTimeSpanSpawn()
+ {
+ var factor = Math.Max(0, 500 - WorldView.EnemyCount) / 500;
+ return TimeSpan.FromMilliseconds(mRandom.Next(mMaxWaitBaseMs)) +
+ TimeSpan.FromSeconds(mRandom.Next(factor * mMaxWaitAddMs));
+ }
+
+ /// <summary>
+ /// Take actions based on the previous percepts, the current strategy
+ /// and state.
+ /// </summary>
+ public void Act()
+ {
+ State = Strategy.Update(State, WorldView);
+
+ var completedActions = Actions.Where(
+ a => a.State == ActionState.Done || a.State == ActionState.Failed).ToList();
+ completedActions.ForEach(a => Actions.Remove(a));
+
+ if (State != AiState.Idle)
+ {
+ if (mTimeSpan >= mTimeSpanSpawn)
+ {
+ mTimeSpan -= mTimeSpanSpawn;
+ mTimeSpanSpawn = GetRandomTimeSpanSpawn();
+ SpawnKnight();
+ }
+ }
+
+ switch (State)
+ {
+ case AiState.Idle:
+ // nothing do to when idling
+ return;
+ case AiState.AttackCreatures:
+ // let all idling soldiers attack some creatures
+ if (WorldView.Targets.Count > 0)
+ {
+ foreach (var creature in WorldView.IdlingKnights)
+ {
+ ICreature target = null;
+ var distance = float.MaxValue;
+ foreach (var c in WorldView.Targets)
+ {
+ var d = Vector2.Distance(c.Position, creature.Position);
+ if (d < distance)
+ {
+ distance = d;
+ target = c;
+ }
+ }
+ creature.IsAttacking = target;
+ }
+ }
+ break;
+ case AiState.DefendPeasants:
+ if (WorldView.Plebs.Count > 0)
+ {
+ foreach (var creature in WorldView.IdlingKnights)
+ {
+ ICreature target = null;
+ var distance = float.MaxValue;
+ var threshold = (int) (creature.AttackRadius * 0.8);
+ foreach (var c in WorldView.Plebs)
+ {
+ var d = Vector2.Distance(c.Position, creature.Position);
+ if (d <= threshold)
+ {
+ target = null;
+ break;
+ }
+ // attempt to avoid clustering
+ /*if (mObjectsManager.GetObjectsInRectangle(c.SelectionRectangle).OfType<Knight>().Count() > 0)
+ {
+ continue;
+ }*/
+ if (d < distance)
+ {
+ distance = d;
+ target = c;
+ }
+ }
+ if (target != null)
+ {
+ var offset = target.Position - creature.Position;
+ offset.Normalize();
+ offset *= threshold;
+ Move(creature, target.Position + offset);
+ }
+ }
+ }
+ break;
+ case AiState.AttackNecromancer:
+ foreach (var c in WorldView.IdlingKnights)
+ {
+ c.IsAttacking = mObjectsManager.PlayerCharacter;
+ }
+ break;
+ }
+ }
+
+ private void SpawnKnight()
+ {
+ var knight = mCreatureFactory.CreateKnight();
+ var position = new Vector2(500, 500);
+ if (mObjectsManager.Castle != null)
+ position = mObjectsManager.Castle.Position;
+ var spawnAction = mActionFactory.CreateSpawnAction(knight, position);
+ Actions.Add(spawnAction);
+ }
+
+ private void Move(ICreature creature, Vector2 destination)
+ {
+ var moveAction = mActionFactory.CreateMoveAction(creature, destination);
+ Actions.Add(moveAction);
+ }
+ }
+}
diff --git a/V3/AI/Internal/AttackStrategy.cs b/V3/AI/Internal/AttackStrategy.cs
new file mode 100644
index 0000000..d108b1f
--- /dev/null
+++ b/V3/AI/Internal/AttackStrategy.cs
@@ -0,0 +1,48 @@
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// A simple strategy for the computer player that tells him to attack the
+ /// enemy creatures.
+ /// </summary>
+ internal class AttackStrategy : IStrategy
+ {
+ /// <summary>
+ /// Updates the current state according to the game situtation.
+ /// </summary>
+ /// <param name="state">the current state</param>
+ /// <param name="worldView">the current view of the game world</param>
+ /// <returns>the next state indicated by this strategy</returns>
+ public AiState Update(AiState state, IWorldView worldView)
+ {
+ switch (state)
+ {
+ case AiState.Idle:
+ if (worldView.InitialPlebsCount - worldView.PlebsCount > 3)
+ {
+ return AiState.DefendPeasants;
+ }
+ break;
+ case AiState.DefendPeasants:
+ if (worldView.PlebsCount < worldView.InitialPlebsCount * 0.75 || worldView.EnemyCount > 20)
+ {
+ return AiState.AttackCreatures;
+ }
+ break;
+ case AiState.AttackCreatures:
+ if (worldView.NecromancerHealth < 0.1)
+ {
+ return AiState.AttackNecromancer;
+ }
+ break;
+ case AiState.AttackNecromancer:
+ if (worldView.NecromancerHealth >= 0.1)
+ {
+ return AiState.AttackCreatures;
+ }
+ break;
+ }
+
+ return state;
+ }
+ }
+}
diff --git a/V3/AI/Internal/IActionFactory.cs b/V3/AI/Internal/IActionFactory.cs
new file mode 100644
index 0000000..eb62222
--- /dev/null
+++ b/V3/AI/Internal/IActionFactory.cs
@@ -0,0 +1,27 @@
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Creates IAction instances. Automatically implemented by Ninject.
+ /// </summary>
+ public interface IActionFactory
+ {
+ /// <summary>
+ /// Creates a new MoveAction to move the given creature to the given
+ /// destination.
+ /// </summary>
+ /// <param name="creature">the creature to mvoe</param>
+ /// <param name="destination">the destination of the creature</param>
+ MoveAction CreateMoveAction(ICreature creature, Vector2 destination);
+
+ /// <summary>
+ /// Creates a new SpawnAction that spawns the given creature at the
+ /// given position.
+ /// </summary>
+ /// <param name="creature">the creature to spawn</param>
+ /// <param name="position">the spawn position</param>
+ SpawnAction CreateSpawnAction(ICreature creature, Vector2 position);
+ }
+}
diff --git a/V3/AI/Internal/MoveAction.cs b/V3/AI/Internal/MoveAction.cs
new file mode 100644
index 0000000..fcbba54
--- /dev/null
+++ b/V3/AI/Internal/MoveAction.cs
@@ -0,0 +1,53 @@
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Moves a creature to a destination point.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class MoveAction : AbstractAction
+ {
+ private ICreature mCreature;
+ private Vector2 mDestination;
+
+ /// <summary>
+ /// Creates a new MoveAction to move the given creature to the given
+ /// destination.
+ /// </summary>
+ /// <param name="creature">the creature to mvoe</param>
+ /// <param name="destination">the destination of the creature</param>
+ public MoveAction(ICreature creature, Vector2 destination)
+ {
+ mCreature = creature;
+ mDestination = destination;
+ }
+
+ /// <summary>
+ /// Start the execution of the action.
+ /// </summary>
+ public override void Start()
+ {
+ mCreature.Move(mDestination);
+ base.Start();
+ }
+
+ protected override ActionState GetNextState()
+ {
+ switch (mCreature.MovementState)
+ {
+ case MovementState.Idle:
+ return ActionState.Done;
+ case MovementState.Attacking:
+ case MovementState.Dying:
+ return ActionState.Failed;
+ case MovementState.Moving:
+ return ActionState.Executing;
+ default:
+ return ActionState.Failed;
+ }
+ }
+
+ }
+}
diff --git a/V3/AI/Internal/SpawnAction.cs b/V3/AI/Internal/SpawnAction.cs
new file mode 100644
index 0000000..4df6225
--- /dev/null
+++ b/V3/AI/Internal/SpawnAction.cs
@@ -0,0 +1,42 @@
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Spawns a creature at a given position.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class SpawnAction : AbstractAction
+ {
+ private readonly IObjectsManager mObjectsManager;
+ private ICreature mCreature;
+ private Vector2 mPosition;
+
+ /// <summary>
+ /// Creates a new SpawnAction that spawns the given creature at the
+ /// given position.
+ /// </summary>
+ /// <param name="objectsManager">the linked objects manager</param>
+ /// <param name="creature">the creature to spawn</param>
+ /// <param name="position">the spawn position</param>
+ public SpawnAction(IObjectsManager objectsManager, ICreature creature, Vector2 position)
+ {
+ mObjectsManager = objectsManager;
+ mCreature = creature;
+ mPosition = position;
+ }
+
+ public override void Start()
+ {
+ mCreature.Position = mPosition;
+ mObjectsManager.CreateCreature(mCreature);
+ base.Start();
+ }
+
+ protected override ActionState GetNextState()
+ {
+ return ActionState.Done;
+ }
+ }
+}
diff --git a/V3/AI/Internal/WorldView.cs b/V3/AI/Internal/WorldView.cs
new file mode 100644
index 0000000..cedd0ae
--- /dev/null
+++ b/V3/AI/Internal/WorldView.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using V3.Objects;
+
+namespace V3.AI.Internal
+{
+ /// <summary>
+ /// Default implementation of IWorldView.
+ /// </summary>
+ internal class WorldView : IWorldView
+ {
+ public int EnemyCount { get; set; }
+ public int InitialPlebsCount { get; set; }
+ public int PlebsCount { get; set; }
+ public float NecromancerHealth { get; set; }
+ public List<ICreature> IdlingKnights { get; } = new List<ICreature>();
+ public List<ICreature> Targets { get; } = new List<ICreature>();
+ public List<ICreature> Plebs { get; } = new List<ICreature>();
+ }
+}
diff --git a/V3/AchievementsAndStatistics.cs b/V3/AchievementsAndStatistics.cs
new file mode 100644
index 0000000..65419d7
--- /dev/null
+++ b/V3/AchievementsAndStatistics.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace V3
+{
+ /// <summary>
+ /// This is a class for all datas of the achievementsand the statistics.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class AchievementsAndStatistics
+ {
+ public bool mKillPrince = false;
+ public bool mKillKing = false;
+ public bool mHellsNotWaiting = false;
+ public bool mKaboom = false;
+
+ public float mMarathonRunner = 0;
+ public float mIronMan = 0;
+ public int mMeatballCompany = 0;
+ public int mSkeletonHorseCavalry = 0;
+ public int mRightHandOfDeath = 0;
+ public int mMinimalist = 0;
+ public int mHundredDeadCorpses = 0;
+ public int mUndeadArmy = 0;
+
+ public int mKilledCreatures = 00000;
+ public int mLostServants = 00000;
+ public float mWalkedDistance = 00000;
+
+ public DateTime mUsedTime;
+ }
+}
diff --git a/V3/Bindings.cs b/V3/Bindings.cs
new file mode 100644
index 0000000..cc9b193
--- /dev/null
+++ b/V3/Bindings.cs
@@ -0,0 +1,75 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Ninject.Extensions.Factory;
+using Ninject.Modules;
+using V3.AI;
+using V3.AI.Internal;
+using V3.Camera;
+using V3.Data;
+using V3.Data.Internal;
+using V3.Effects;
+using V3.Input;
+using V3.Input.Internal;
+using V3.Map;
+using V3.Screens;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3
+{
+ /// <summary>
+ /// Defines the bindings of constants, factories and singletons for the
+ /// Ninject dependency injection framework.
+ /// </summary>
+ public sealed class Bindings : NinjectModule
+ {
+ private readonly V3Game mGame;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ /// <summary>
+ /// Creates a new Bindings instance for the given game and graphics
+ /// device manager.
+ /// </summary>
+ /// <param name="game">the game that uses this instance</param>
+ /// <param name="graphicsDeviceManager">the graphics device manager
+ /// instance to use in this instance</param>
+ public Bindings(V3Game game, GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mGame = game;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public override void Load()
+ {
+ // constants
+ Bind<ContentManager>().ToConstant(mGame.Content);
+ Bind<Game>().ToConstant(mGame);
+ Bind<GraphicsDeviceManager>().ToConstant(mGraphicsDeviceManager);
+
+ // factories
+ Bind<IActionFactory>().ToFactory();
+ Bind<IBasicCreatureFactory>().ToFactory();
+ Bind<IBasicWidgetFactory>().ToFactory();
+ Bind<IMenuFactory>().ToFactory();
+ Bind<IScreenFactory>().ToFactory();
+
+ // singletons
+ Bind<IGameStateManager>().To<GameStateManager>().InSingletonScope();
+ Bind<IInputManager>().To<InputManager>().InSingletonScope();
+ Bind<IOptionsManager>().To<OptionsManager>().InSingletonScope();
+ Bind<IScreenManager>().To<ScreenManager>().InSingletonScope();
+ Bind<CameraManager>().ToSelf().InSingletonScope();
+ Bind<IMapManager>().To<MapManager>().InSingletonScope();
+ Bind<IObjectsManager>().To<ObjectsManager>().InSingletonScope();
+ Bind<Pathfinder>().ToSelf().InSingletonScope();
+ Bind<Selection>().ToSelf().InSingletonScope();
+ Bind<IEffectsManager>().To<EffectsManager>().InSingletonScope();
+ Bind<AchievementsAndStatistics>().ToSelf().InSingletonScope();
+
+ // regular bindings
+ Bind<IAiPlayer>().To<AiPlayer>();
+ Bind<IPathManager>().To<PathManager>();
+ Bind<ISaveGameManager>().To<SaveGameManager>();
+ }
+ }
+}
diff --git a/V3/CONTROLS.md b/V3/CONTROLS.md
new file mode 100644
index 0000000..335f298
--- /dev/null
+++ b/V3/CONTROLS.md
@@ -0,0 +1,37 @@
+Controls
+========
+
+Mouse
+-----
+
+* Clicking left mouse button: Selecting the clicked unit when in range
+* Holding down left mouse button: Drawing a rectangle for selecting several units inside
+* Clicking right mouse button: Move selected units to mouse position
+
+Keyboard
+--------
+
+* ESC : Open menu and pause game
+* C : Toggle camera between scrolling and centered mode
+* 1 : Trigger meatball explosion
+* 2 : Summon zombies from corpses (or from a graveyard if you have no more minions)
+* 3 : Transform five zombies to a meatball
+* 4 : Transform one zombie to a skeleton
+* 5 : Transform three skeletons to a horse
+
+Techdemo
+--------
+
+When pressing a specific key once and klicking on the map you can create one unit.
+
+* F1 : Create zombie
+* F2 : Create skeleton
+* F3 : Create peasant
+* F4 : Create knight
+
+Cheats
+------
+
+* F5 : "Hippie mode"
+* F6 : Change necromancer gender
+* F8 : Toggle Fog of War on/off \ No newline at end of file
diff --git a/V3/CREDITS.md b/V3/CREDITS.md
new file mode 100644
index 0000000..c0e01e0
--- /dev/null
+++ b/V3/CREDITS.md
@@ -0,0 +1,51 @@
+Credits
+=======
+
+Most of the content - especially sprites and map textures - is taken from the FLARE game.
+http://flarerpg.org/
+https://github.com/clintbellanger/flare-game
+"All of Flare's art and data files are released under CC-BY-SA 3.0. Later versions are permitted."
+
+The maps were created with the Tiled Map Editor <http://www.mapeditor.org/>.
+Special thanks to Sabrina for pulling an all nighter and creating the large map for the main mission.
+
+Further content:
+----------------
+* Castle sprite from Feudal Wars (CC0)
+ <http://opengameart.org/content/western-european-castle-isometric-25d>
+* Animated particle effects #1 and #2 by para (CC0)
+ <http://opengameart.org/content/animated-particle-effects-1>
+ <http://opengameart.org/content/animated-particle-effects-2>
+* Explosion animation and sound by WrathGames Studio <http://wrathgames.com/blog> (CC-BY 3.0)
+ <http://opengameart.org/content/wgstudio-explosion-animation>
+ <http://opengameart.org/content/wgs-sound-fx-explosion-1>
+* The Fleischklops / Meatball sprite is licenced under CC BY 3.0.
+ It was created using Clint Bellanger's Zombie (CC BY 3.0)
+ http://opengameart.org/content/zombie-0
+
+Title screen:
+-------------
+* "The Triumph of Death" by Pieter Bruegel the Elder (1525 - 1569)
+ <https://pixabay.com/en/oil-painting-death-classical-1178909/>
+
+Music:
+------
+* Title screen music by Kosta T / Konstantin Trokay (CC BY-NC 4.0)
+ <http://freemusicarchive.org/music/kosta_t/>
+* Game screen music
+ <http://freemusicarchive.org/music/Unheard_Music_Concepts/Harbour/UnheardMusicConcepts__04_Afraid_to_Go>
+
+Sounds:
+-------
+* Wet squish, slurp impacts by Independent.nu (CC0):
+ <http://opengameart.org/content/8-wet-squish-slurp-impacts>
+* High Quality Explosions by Michel Baradari (CC-BY 3.0):
+ <http://opengameart.org/content/2-high-quality-explosions>
+* The Skeleton Horse sound
+ <http://soundbible.com/254-Horses-Galloping-Off.html>
+* The Knight sound
+ <http://soundbible.com/1329-Soldiers-Marching.html>
+* The Meatball sound
+ <http://soundbible.com/1963-Giant-Monster.html>
+* The walking sound of the remaining creatures
+ <http://soundbible.com/1432-Walking-On-Gravel.html> \ No newline at end of file
diff --git a/V3/Camera/CameraCentered.cs b/V3/Camera/CameraCentered.cs
new file mode 100644
index 0000000..cd04209
--- /dev/null
+++ b/V3/Camera/CameraCentered.cs
@@ -0,0 +1,57 @@
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.Camera
+{
+ /// <summary>
+ /// This is the Camera Class for a player-centered camera mode. Does not go outside the map.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class CameraCentered : ICamera
+ {
+ public int MapPixelWidth { get; set; }
+ public int MapPixelHeight { get; set; }
+ private int mMaxX;
+ private int mMaxY;
+ public Matrix Transform { get; set; }
+ private Point mCamCenter;
+ public Vector2 Location { get { return mCamCenter.ToVector2(); } set { mCamCenter = value.ToPoint(); } }
+ public Point ScreenSize => new Point(mGraphicsDeviceManager.GraphicsDevice.Viewport.Width, mGraphicsDeviceManager.GraphicsDevice.Viewport.Height);
+ public Rectangle ScreenRectangle => new Rectangle(mCamCenter, ScreenSize);
+
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ public CameraCentered(GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ private int ScreenWidth()
+ {
+ return mGraphicsDeviceManager.GraphicsDevice.Viewport.Width;
+ }
+
+ private int ScreenHeight()
+ {
+ return mGraphicsDeviceManager.GraphicsDevice.Viewport.Height;
+ }
+
+ /// <summary>
+ /// Updates the position of the camera related to the player's position.
+ /// </summary>
+ public void Update(ICreature player)
+ {
+ /*The center of the camera is by default in the upper left corner. To get the player in center,
+ simply substract the half of the screen values. Does not go outside the map.
+ */
+ mMaxX = MapPixelWidth - ScreenWidth();
+ mMaxY = MapPixelHeight / 2 - ScreenHeight();
+
+ mCamCenter = new Point(MathHelper.Clamp((int)(player.Position.X - ScreenWidth() * 0.5f), 0, mMaxX),
+ MathHelper.Clamp((int)(player.Position.Y - ScreenHeight() * 0.5f), 0, mMaxY));
+
+ //Transform matrix for the camera to make sure it is moving.
+ Transform = Matrix.CreateTranslation(new Vector3(-mCamCenter.ToVector2(), 0));
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Camera/CameraManager.cs b/V3/Camera/CameraManager.cs
new file mode 100644
index 0000000..b4d502e
--- /dev/null
+++ b/V3/Camera/CameraManager.cs
@@ -0,0 +1,71 @@
+using Microsoft.Xna.Framework;
+using System.Collections.Generic;
+using V3.Data;
+using V3.Objects;
+
+namespace V3.Camera
+{
+ /// <summary>
+ /// Stores and provides access to the possible cameras.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class CameraManager
+ {
+ private readonly IOptionsManager mOptionsManager;
+ private readonly CameraCentered mCameraCentered;
+ private readonly CameraScrolling mCameraScrolling;
+
+ /// <summary>
+ /// Creates a new CameraManager.
+ /// </summary>
+ public CameraManager(CameraCentered cameraCentered,
+ CameraScrolling cameraScrolling, IOptionsManager optionsManager)
+ {
+ mCameraCentered = cameraCentered;
+ mCameraScrolling = cameraScrolling;
+ mOptionsManager = optionsManager;
+ }
+
+ /// <summary>
+ /// Initializes the cameras with the given map data.
+ /// </summary>
+ public void Initialize(Point mapPixelSize)
+ {
+ // TODO: Warum *2? Irgendwas stimmt bei der Interpretation hier nicht.
+ var mapPixelHeight = mapPixelSize.Y * 2;
+ var mapPixelWidth = mapPixelSize.X;
+ var cameras = new List<ICamera> { mCameraCentered, mCameraScrolling };
+ foreach (var camera in cameras)
+ {
+ camera.MapPixelHeight = mapPixelHeight;
+ camera.MapPixelWidth = mapPixelWidth;
+ }
+ }
+
+ /// <summary>
+ /// Updates the cameras.
+ /// </summary>
+ public void Update(ICreature creature)
+ {
+ GetCamera().Update(creature);
+ if (mOptionsManager.Options.CameraType != CameraType.Scrolling)
+ mCameraScrolling.Location = GetCamera().Location;
+ }
+
+ /// <summary>
+ /// Returns the currently selected camera.
+ /// </summary>
+ public ICamera GetCamera()
+ {
+ switch (mOptionsManager.Options.CameraType)
+ {
+ case CameraType.Centered:
+ return mCameraCentered;
+ case CameraType.Scrolling:
+ return mCameraScrolling;
+ default:
+ return mCameraScrolling;
+ }
+ }
+ }
+}
diff --git a/V3/Camera/CameraScrolling.cs b/V3/Camera/CameraScrolling.cs
new file mode 100644
index 0000000..72e4911
--- /dev/null
+++ b/V3/Camera/CameraScrolling.cs
@@ -0,0 +1,96 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using V3.Objects;
+using MathHelper = Microsoft.Xna.Framework.MathHelper;
+using Vector2 = Microsoft.Xna.Framework.Vector2;
+
+namespace V3.Camera
+{
+ /// <summary>
+ /// This is the camera class for a map-scrolling camera mode. Does not move over the map.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class CameraScrolling : ICamera
+ {
+ public int MapPixelWidth { get; set; }
+ public int MapPixelHeight { get; set; }
+ private const int MinOffset = 10;
+ private const int CameraSpeed = 10;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private Point mLocation;
+ public Matrix Transform { get; set; }
+ private int mMaxX;
+ private int mMaxY;
+ public Vector2 Location {get {return mLocation.ToVector2();} set { mLocation = value.ToPoint(); } }
+ public Point ScreenSize => new Point(mGraphicsDeviceManager.GraphicsDevice.Viewport.Width, mGraphicsDeviceManager.GraphicsDevice.Viewport.Height);
+ public Rectangle ScreenRectangle => new Rectangle(mLocation, ScreenSize);
+
+ public CameraScrolling(GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public void Update(ICreature player)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+
+ mMaxX = MapPixelWidth - viewport.Width;
+ mMaxY = MapPixelHeight / 2 - viewport.Height;
+
+ var mouse = Mouse.GetState();
+ if (mouse.X < MinOffset)
+ {
+ MoveCameraLeft();
+ }
+
+ if (mouse.X > viewport.Width - MinOffset)
+ {
+ MoveCameraRight();
+ }
+
+ if (mouse.Y < MinOffset)
+ {
+ MoveCameraDown();
+ }
+
+ if (mouse.Y > viewport.Height - MinOffset)
+ {
+ MoveCameraUp();
+ }
+ Transform = Matrix.CreateTranslation(new Vector3(-mLocation.ToVector2(), 0));
+ }
+
+ /// <summary>
+ /// Move the Camera to the left. Does not go outside the map.
+ /// </summary>
+ private void MoveCameraLeft()
+ {
+ mLocation.X = MathHelper.Clamp(mLocation.X - CameraSpeed, 0, mMaxX);
+ }
+
+ /// <summary>
+ /// Move the Camera to the right. Does not go outside the map.
+ /// </summary>
+ private void MoveCameraRight()
+ {
+ mLocation.X = MathHelper.Clamp(mLocation.X + CameraSpeed, 0, mMaxX);
+ }
+
+ /// <summary>
+ /// Move the Camera up. Does not go outside the map.
+ /// </summary>
+ private void MoveCameraUp()
+ {
+ mLocation.Y = MathHelper.Clamp(mLocation.Y + CameraSpeed, 0, mMaxY);
+ }
+
+
+ /// <summary>
+ /// Move the Camera down. Does not go outside the map.
+ /// </summary>
+ private void MoveCameraDown()
+ {
+ mLocation.Y = MathHelper.Clamp(mLocation.Y - CameraSpeed, 0, mMaxY);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Camera/CameraType.cs b/V3/Camera/CameraType.cs
new file mode 100644
index 0000000..4e373d9
--- /dev/null
+++ b/V3/Camera/CameraType.cs
@@ -0,0 +1,8 @@
+namespace V3.Camera
+{
+ public enum CameraType
+ {
+ Centered,
+ Scrolling
+ }
+}
diff --git a/V3/Camera/ICamera.cs b/V3/Camera/ICamera.cs
new file mode 100644
index 0000000..4a8bbcd
--- /dev/null
+++ b/V3/Camera/ICamera.cs
@@ -0,0 +1,18 @@
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.Camera
+{
+ public interface ICamera
+ {
+ Matrix Transform { get; set; }
+ Vector2 Location { get; set; }
+ Point ScreenSize { get; }
+ Rectangle ScreenRectangle { get; }
+
+ void Update(ICreature player);
+
+ int MapPixelWidth { get; set; }
+ int MapPixelHeight { get; set; }
+ }
+ }
diff --git a/V3/ClassDiagram1.cd b/V3/ClassDiagram1.cd
new file mode 100644
index 0000000..7b89419
--- /dev/null
+++ b/V3/ClassDiagram1.cd
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram /> \ No newline at end of file
diff --git a/V3/Content/Buttons/Button-01.png b/V3/Content/Buttons/Button-01.png
new file mode 100644
index 0000000..7c1f22d
--- /dev/null
+++ b/V3/Content/Buttons/Button-01.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-01_Pressed.png b/V3/Content/Buttons/Button-01_Pressed.png
new file mode 100644
index 0000000..c67cf34
--- /dev/null
+++ b/V3/Content/Buttons/Button-01_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-02.png b/V3/Content/Buttons/Button-02.png
new file mode 100644
index 0000000..123a375
--- /dev/null
+++ b/V3/Content/Buttons/Button-02.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-02_Pressed.png b/V3/Content/Buttons/Button-02_Pressed.png
new file mode 100644
index 0000000..a017676
--- /dev/null
+++ b/V3/Content/Buttons/Button-02_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-03.png b/V3/Content/Buttons/Button-03.png
new file mode 100644
index 0000000..a06547d
--- /dev/null
+++ b/V3/Content/Buttons/Button-03.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-03_Pressed.png b/V3/Content/Buttons/Button-03_Pressed.png
new file mode 100644
index 0000000..a2fe634
--- /dev/null
+++ b/V3/Content/Buttons/Button-03_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-04.png b/V3/Content/Buttons/Button-04.png
new file mode 100644
index 0000000..42a60da
--- /dev/null
+++ b/V3/Content/Buttons/Button-04.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-04_Pressed.png b/V3/Content/Buttons/Button-04_Pressed.png
new file mode 100644
index 0000000..0cca164
--- /dev/null
+++ b/V3/Content/Buttons/Button-04_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-05.png b/V3/Content/Buttons/Button-05.png
new file mode 100644
index 0000000..3a2d99c
--- /dev/null
+++ b/V3/Content/Buttons/Button-05.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-05_Pressed.png b/V3/Content/Buttons/Button-05_Pressed.png
new file mode 100644
index 0000000..86b8b9e
--- /dev/null
+++ b/V3/Content/Buttons/Button-05_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-06.png b/V3/Content/Buttons/Button-06.png
new file mode 100644
index 0000000..1bc3448
--- /dev/null
+++ b/V3/Content/Buttons/Button-06.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-06_Pressed.png b/V3/Content/Buttons/Button-06_Pressed.png
new file mode 100644
index 0000000..5a6cc1e
--- /dev/null
+++ b/V3/Content/Buttons/Button-06_Pressed.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-07.png b/V3/Content/Buttons/Button-07.png
new file mode 100644
index 0000000..422e445
--- /dev/null
+++ b/V3/Content/Buttons/Button-07.png
Binary files differ
diff --git a/V3/Content/Buttons/Button-07_Pressed.png b/V3/Content/Buttons/Button-07_Pressed.png
new file mode 100644
index 0000000..5cbe2b9
--- /dev/null
+++ b/V3/Content/Buttons/Button-07_Pressed.png
Binary files differ
diff --git a/V3/Content/Content.mgcb b/V3/Content/Content.mgcb
new file mode 100644
index 0000000..bbaf13b
--- /dev/null
+++ b/V3/Content/Content.mgcb
@@ -0,0 +1,1092 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:DesktopGL
+/config:
+/profile:Reach
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+#---------------------------------- Content ---------------------------------#
+
+#begin Fonts/MenuFont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:TextureFormat=Color
+/build:Fonts/MenuFont.spritefont
+
+#begin Sprites/zombie.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/zombie.png
+
+#begin Textures/grassland.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/grassland.png
+
+#begin Textures/grassland_trees.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/grassland_trees.png
+
+#begin Textures/grassland_water.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/grassland_water.png
+
+#begin Textures/houses_front.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/houses_front.png
+
+#begin Textures/houses_rear.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/houses_rear.png
+
+#begin Sprites/skeleton.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/skeleton.png
+
+#begin Sprites/necromancer.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/necromancer.png
+
+#begin Sounds/walking.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/walking.wav
+
+#begin Maps/map_grassland.tmx
+/copy:Maps/map_grassland.tmx
+
+#begin Textures/pathfinder.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/pathfinder.png
+
+#begin Sprites/WhiteRectangle.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/WhiteRectangle.png
+
+#begin Fonts/UnitFont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:TextureFormat=Color
+/build:Fonts/UnitFont.spritefont
+
+#begin Maps/work_in_progress.tmx
+/copy:Maps/work_in_progress.tmx
+
+#begin Sprites/shield.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/shield.png
+
+#begin Sprites/longsword.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/longsword.png
+
+#begin Sprites/head_plate.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_plate.png
+
+#begin Sprites/head_plate_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_plate_female.png
+
+#begin Sprites/plate.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/plate.png
+
+#begin Sprites/cloth.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/cloth.png
+
+#begin Sprites/shield_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/shield_female.png
+
+#begin Sprites/longsword_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/longsword_female.png
+
+#begin Sprites/plate_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/plate_female.png
+
+#begin Sprites/cloth_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/cloth_female.png
+
+#begin Sprites/head_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_female.png
+
+#begin Sprites/nude_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/nude_female.png
+
+#begin Sprites/head_bald.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_bald.png
+
+#begin Sprites/head.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head.png
+
+#begin Sprites/nude.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/nude.png
+
+#begin Sprites/selection.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/selection.png
+
+#begin Sprites/skeleton_horse.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/skeleton_horse.png
+
+#begin Sprites/fleischklops.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/fleischklops.png
+
+#begin Sprites/cloud.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/cloud.png
+
+#begin Sprites/prince.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/prince.png
+
+#begin Buttons/Button-01.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-01.png
+
+#begin Buttons/Button-02.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-02.png
+
+#begin Buttons/Button-03.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-03.png
+
+#begin Buttons/Button-04.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-04.png
+
+#begin Buttons/Button-05.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-05.png
+
+#begin Buttons/Button-06.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-06.png
+
+#begin Buttons/Button-07.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-07.png
+
+#begin Textures/castle.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/castle.png
+
+#begin Menu/arrow_white.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Menu/arrow_white.png
+
+#begin Menu/mainscreen.jpg
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Menu/mainscreen.jpg
+
+#begin Sounds/Kosta_T_-_06.mp3
+/importer:Mp3Importer
+/processor:SongProcessor
+/processorParam:Quality=Best
+/build:Sounds/Kosta_T_-_06.mp3
+
+#begin Maps/techdemo.tmx
+/copy:Maps/techdemo.tmx
+
+#begin Textures/EmptyPixel.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Textures/EmptyPixel.png
+
+#begin Sounds/Knight.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/Knight.wav
+
+#begin Sounds/SkeletonHorse.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/SkeletonHorse.wav
+
+#begin Sounds/punch.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/punch.wav
+
+#begin Sounds/Mummy_Zombie-SoundBible.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/Mummy_Zombie-SoundBible.wav
+
+#begin Sounds/Monster_Gigante-Doberman-1334685792.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/Monster_Gigante-Doberman-1334685792.wav
+
+#begin Menu/Titel.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Menu/Titel.png
+
+#begin Sprites/skeleton_rider.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/skeleton_rider.png
+
+#begin Effects/particlefx_03.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/particlefx_03.png
+
+#begin Effects/particlefx_04.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/particlefx_04.png
+
+#begin Effects/particlefx_05.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/particlefx_05.png
+
+#begin Effects/blood_hit_01.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_01.png
+
+#begin Effects/blood_hit_02.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_02.png
+
+#begin Effects/blood_hit_03.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_03.png
+
+#begin Effects/blood_hit_04.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_04.png
+
+#begin Effects/blood_hit_05.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_05.png
+
+#begin Effects/blood_hit_06.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_06.png
+
+#begin Effects/blood_hit_08.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/blood_hit_08.png
+
+#begin Effects/explosion.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/explosion.png
+
+#begin Sounds/explosion1.ogg
+/importer:OggImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/explosion1.ogg
+
+#begin Sounds/impactsplat01.ogg
+/importer:OggImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/impactsplat01.ogg
+
+#begin Sprites/skeleton_elite.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/skeleton_elite.png
+
+#begin Sprites/staff_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/staff_female.png
+
+#begin Sprites/staff.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/staff.png
+
+#begin Sprites/necromancer_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/necromancer_female.png
+
+#begin Sprites/ellipse.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/ellipse.png
+
+#begin Sprites/fog.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/fog.png
+
+#begin Buttons/Button-01_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-01_Pressed.png
+
+#begin Buttons/Button-02_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-02_Pressed.png
+
+#begin Buttons/Button-03_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-03_Pressed.png
+
+#begin Buttons/Button-04_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-04_Pressed.png
+
+#begin Buttons/Button-05_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-05_Pressed.png
+
+#begin Buttons/Button-06_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-06_Pressed.png
+
+#begin Buttons/Button-07_Pressed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Buttons/Button-07_Pressed.png
+
+#begin Sprites/chain_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/chain_female.png
+
+#begin Sprites/head_chain_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_chain_female.png
+
+#begin Sprites/buckler_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/buckler_female.png
+
+#begin Sprites/shortsword_female.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/shortsword_female.png
+
+#begin Sprites/buckler.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/buckler.png
+
+#begin Sprites/shortsword.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/shortsword.png
+
+#begin Sprites/head_chain.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/head_chain.png
+
+#begin Sprites/chain.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/chain.png
+
+#begin Sprites/arrows.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/arrows.png
+
+#begin Effects/quake.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Effects/quake.png
+
+#begin Sprites/skeleton_archer.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/skeleton_archer.png
+
+#begin Sprites/zombie_club.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/zombie_club.png
+
+#begin Sprites/king.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:ColorKeyColor=255,0,255,255
+/processorParam:ColorKeyEnabled=True
+/processorParam:GenerateMipmaps=False
+/processorParam:PremultiplyAlpha=True
+/processorParam:ResizeToPowerOfTwo=False
+/processorParam:MakeSquare=False
+/processorParam:TextureFormat=Color
+/build:Sprites/king.png
+
+#begin Fonts/DeathFont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:TextureFormat=Color
+/build:Fonts/DeathFont.spritefont
+
+#begin Sounds/Afraid_to_Go.mp3
+/importer:Mp3Importer
+/processor:SongProcessor
+/processorParam:Quality=Best
+/build:Sounds/Afraid_to_Go.mp3
+
+#begin Fonts/VictoryFont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/processorParam:TextureFormat=Color
+/build:Fonts/VictoryFont.spritefont
+
+#begin Sounds/explode.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/explode.wav
+
+#begin Sounds/explodemini.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/explodemini.wav
+
+#begin Sounds/horse.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/horse.wav
+
+#begin Sounds/zonk2.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/processorParam:Quality=Best
+/build:Sounds/zonk2.wav
+
diff --git a/V3/Content/Effects/blood_hit_01.png b/V3/Content/Effects/blood_hit_01.png
new file mode 100644
index 0000000..4ce41d8
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_01.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_02.png b/V3/Content/Effects/blood_hit_02.png
new file mode 100644
index 0000000..b4ce9ba
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_02.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_03.png b/V3/Content/Effects/blood_hit_03.png
new file mode 100644
index 0000000..0f827f4
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_03.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_04.png b/V3/Content/Effects/blood_hit_04.png
new file mode 100644
index 0000000..bf122a2
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_04.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_05.png b/V3/Content/Effects/blood_hit_05.png
new file mode 100644
index 0000000..a177ce9
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_05.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_06.png b/V3/Content/Effects/blood_hit_06.png
new file mode 100644
index 0000000..51aa729
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_06.png
Binary files differ
diff --git a/V3/Content/Effects/blood_hit_08.png b/V3/Content/Effects/blood_hit_08.png
new file mode 100644
index 0000000..87b6794
--- /dev/null
+++ b/V3/Content/Effects/blood_hit_08.png
Binary files differ
diff --git a/V3/Content/Effects/explosion.png b/V3/Content/Effects/explosion.png
new file mode 100644
index 0000000..0f4f3e9
--- /dev/null
+++ b/V3/Content/Effects/explosion.png
Binary files differ
diff --git a/V3/Content/Effects/particlefx_03.png b/V3/Content/Effects/particlefx_03.png
new file mode 100644
index 0000000..2649c85
--- /dev/null
+++ b/V3/Content/Effects/particlefx_03.png
Binary files differ
diff --git a/V3/Content/Effects/particlefx_04.png b/V3/Content/Effects/particlefx_04.png
new file mode 100644
index 0000000..346695e
--- /dev/null
+++ b/V3/Content/Effects/particlefx_04.png
Binary files differ
diff --git a/V3/Content/Effects/particlefx_05.png b/V3/Content/Effects/particlefx_05.png
new file mode 100644
index 0000000..d046357
--- /dev/null
+++ b/V3/Content/Effects/particlefx_05.png
Binary files differ
diff --git a/V3/Content/Effects/quake.png b/V3/Content/Effects/quake.png
new file mode 100644
index 0000000..6fe7ad0
--- /dev/null
+++ b/V3/Content/Effects/quake.png
Binary files differ
diff --git a/V3/Content/Fonts/Blutschrift.ttf b/V3/Content/Fonts/Blutschrift.ttf
new file mode 100644
index 0000000..da803c1
--- /dev/null
+++ b/V3/Content/Fonts/Blutschrift.ttf
Binary files differ
diff --git a/V3/Content/Fonts/DeathFont.spritefont b/V3/Content/Fonts/DeathFont.spritefont
new file mode 100644
index 0000000..8cbab91
--- /dev/null
+++ b/V3/Content/Fonts/DeathFont.spritefont
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+ <Asset Type="Graphics:FontDescription">
+ <FontName>Blutschrift</FontName>
+ <Size>50</Size>
+ <Spacing>3</Spacing>
+ <Style>Regular</Style>
+ <CharacterRegions>
+ <CharacterRegion>
+ <Start>&#32;</Start>
+ <End>&#126;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ä" -->
+ <CharacterRegion>
+ <Start>&#228;</Start>
+ <End>&#228;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ö" -->
+ <CharacterRegion>
+ <Start>&#246;</Start>
+ <End>&#246;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ü" -->
+ <CharacterRegion>
+ <Start>&#252;</Start>
+ <End>&#252;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ä" -->
+ <CharacterRegion>
+ <Start>&#196;</Start>
+ <End>&#196;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ö" -->
+ <CharacterRegion>
+ <Start>&#214;</Start>
+ <End>&#214;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ü" -->
+ <CharacterRegion>
+ <Start>&#220;</Start>
+ <End>&#220;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ß" -->
+ <CharacterRegion>
+ <Start>&#223;</Start>
+ <End>&#223;</End>
+ </CharacterRegion>
+ </CharacterRegions>
+ </Asset>
+</XnaContent> \ No newline at end of file
diff --git a/V3/Content/Fonts/DejaVuSans.ttf b/V3/Content/Fonts/DejaVuSans.ttf
new file mode 100644
index 0000000..de12789
--- /dev/null
+++ b/V3/Content/Fonts/DejaVuSans.ttf
Binary files differ
diff --git a/V3/Content/Fonts/MenuFont.spritefont b/V3/Content/Fonts/MenuFont.spritefont
new file mode 100644
index 0000000..e6362fb
--- /dev/null
+++ b/V3/Content/Fonts/MenuFont.spritefont
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+ <Asset Type="Graphics:FontDescription">
+ <FontName>grabstein</FontName>
+ <Size>20</Size>
+ <Spacing>3</Spacing>
+ <Style>Regular</Style>
+ <CharacterRegions>
+ <CharacterRegion>
+ <Start>&#32;</Start>
+ <End>&#126;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ä" -->
+ <CharacterRegion>
+ <Start>&#228;</Start>
+ <End>&#228;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ö" -->
+ <CharacterRegion>
+ <Start>&#246;</Start>
+ <End>&#246;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ü" -->
+ <CharacterRegion>
+ <Start>&#252;</Start>
+ <End>&#252;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ä" -->
+ <CharacterRegion>
+ <Start>&#196;</Start>
+ <End>&#196;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ö" -->
+ <CharacterRegion>
+ <Start>&#214;</Start>
+ <End>&#214;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ü" -->
+ <CharacterRegion>
+ <Start>&#220;</Start>
+ <End>&#220;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ß" -->
+ <CharacterRegion>
+ <Start>&#223;</Start>
+ <End>&#223;</End>
+ </CharacterRegion>
+ </CharacterRegions>
+ </Asset>
+</XnaContent> \ No newline at end of file
diff --git a/V3/Content/Fonts/Siegesschriftzug.ttf b/V3/Content/Fonts/Siegesschriftzug.ttf
new file mode 100644
index 0000000..59dd36d
--- /dev/null
+++ b/V3/Content/Fonts/Siegesschriftzug.ttf
Binary files differ
diff --git a/V3/Content/Fonts/UnitFont.spritefont b/V3/Content/Fonts/UnitFont.spritefont
new file mode 100644
index 0000000..fa4bf9f
--- /dev/null
+++ b/V3/Content/Fonts/UnitFont.spritefont
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+ <Asset Type="Graphics:FontDescription">
+ <FontName>grabstein</FontName>
+ <Size>12</Size>
+ <Spacing>3</Spacing>
+ <Style>Regular</Style>
+ <CharacterRegions>
+ <CharacterRegion>
+ <Start>&#32;</Start>
+ <End>&#126;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ä" -->
+ <CharacterRegion>
+ <Start>&#228;</Start>
+ <End>&#228;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ö" -->
+ <CharacterRegion>
+ <Start>&#246;</Start>
+ <End>&#246;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ü" -->
+ <CharacterRegion>
+ <Start>&#252;</Start>
+ <End>&#252;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ä" -->
+ <CharacterRegion>
+ <Start>&#196;</Start>
+ <End>&#196;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ö" -->
+ <CharacterRegion>
+ <Start>&#214;</Start>
+ <End>&#214;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ü" -->
+ <CharacterRegion>
+ <Start>&#220;</Start>
+ <End>&#220;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ß" -->
+ <CharacterRegion>
+ <Start>&#223;</Start>
+ <End>&#223;</End>
+ </CharacterRegion>
+ </CharacterRegions>
+ </Asset>
+</XnaContent> \ No newline at end of file
diff --git a/V3/Content/Fonts/VictoryFont.spritefont b/V3/Content/Fonts/VictoryFont.spritefont
new file mode 100644
index 0000000..28d695a
--- /dev/null
+++ b/V3/Content/Fonts/VictoryFont.spritefont
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
+ <Asset Type="Graphics:FontDescription">
+ <FontName>Siegesschriftzug</FontName>
+ <Size>50</Size>
+ <Spacing>3</Spacing>
+ <Style>Regular</Style>
+ <CharacterRegions>
+ <CharacterRegion>
+ <Start>&#32;</Start>
+ <End>&#126;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ä" -->
+ <CharacterRegion>
+ <Start>&#228;</Start>
+ <End>&#228;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ö" -->
+ <CharacterRegion>
+ <Start>&#246;</Start>
+ <End>&#246;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ü" -->
+ <CharacterRegion>
+ <Start>&#252;</Start>
+ <End>&#252;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ä" -->
+ <CharacterRegion>
+ <Start>&#196;</Start>
+ <End>&#196;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ö" -->
+ <CharacterRegion>
+ <Start>&#214;</Start>
+ <End>&#214;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "Ü" -->
+ <CharacterRegion>
+ <Start>&#220;</Start>
+ <End>&#220;</End>
+ </CharacterRegion>
+ <!-- Character Region for the letter "ß" -->
+ <CharacterRegion>
+ <Start>&#223;</Start>
+ <End>&#223;</End>
+ </CharacterRegion>
+ </CharacterRegions>
+ </Asset>
+</XnaContent> \ No newline at end of file
diff --git a/V3/Content/Fonts/grabstein.ttf b/V3/Content/Fonts/grabstein.ttf
new file mode 100644
index 0000000..711b3a1
--- /dev/null
+++ b/V3/Content/Fonts/grabstein.ttf
Binary files differ
diff --git a/V3/Content/Maps/map_grassland.tmx b/V3/Content/Maps/map_grassland.tmx
new file mode 100644
index 0000000..9162f2a
--- /dev/null
+++ b/V3/Content/Maps/map_grassland.tmx
@@ -0,0 +1,988 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="staggered" renderorder="right-down" width="50" height="100" tilewidth="64" tileheight="32" staggeraxis="y" staggerindex="odd" nextobjectid="10">
+ <properties>
+ <property name="gridsize" type="int" value="16"/>
+ </properties>
+ <tileset firstgid="1" name="grassland" tilewidth="64" tileheight="128" tilecount="128" columns="16">
+ <image source="../Textures/grassland.png" width="1024" height="1024"/>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="48">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="49">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="50">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="51">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="52">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="53">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="54">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="55">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="56">
+ <properties>
+ <property name="collision" value="00000000000000000000011111110000"/>
+ </properties>
+ </tile>
+ <tile id="57">
+ <properties>
+ <property name="collision" value="00000000000000000000001000110010"/>
+ </properties>
+ </tile>
+ <tile id="58">
+ <properties>
+ <property name="collision" value="00000000000000000000010011000100"/>
+ </properties>
+ </tile>
+ <tile id="59">
+ <properties>
+ <property name="collision" value="00000000000000000000111011110000"/>
+ </properties>
+ </tile>
+ <tile id="60">
+ <properties>
+ <property name="collision" value="00000000000000000000001011110100"/>
+ </properties>
+ </tile>
+ <tile id="61">
+ <properties>
+ <property name="collision" value="00000000000000000000010011110010"/>
+ </properties>
+ </tile>
+ <tile id="75">
+ <properties>
+ <property name="collision" value="00000000000000001111111111111111"/>
+ </properties>
+ </tile>
+ <tile id="80">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="81">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="82">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="83">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="84">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="85">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="86">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="87">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="88">
+ <properties>
+ <property name="collision" value="00000000000000000000000101100000"/>
+ </properties>
+ </tile>
+ <tile id="89">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="90">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="91">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="92">
+ <properties>
+ <property name="collision" value="00000000000000000000000001110000"/>
+ </properties>
+ </tile>
+ <tile id="93">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="94">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="95">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="112">
+ <properties>
+ <property name="collision" value="00000000000000000000000011100000"/>
+ </properties>
+ </tile>
+ <tile id="113">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="114">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="115">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="116">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="117">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="118">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="119">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="120">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="121">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="122">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="123">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="124">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="125">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="126">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="127">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="129" name="grassland_trees" tilewidth="128" tileheight="256" tilecount="16" columns="8">
+ <tileoffset x="-32" y="0"/>
+ <image source="../Textures/grassland_trees.png" width="1024" height="512"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="145" name="grassland_water" tilewidth="64" tileheight="64" tilecount="64" columns="16">
+ <tileoffset x="0" y="32"/>
+ <image source="../Textures/grassland_water.png" width="1024" height="256"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="16">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="17">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="18">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="19">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="20">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="21">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="22">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="23">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="209" name="houses_rear" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-64" y="32"/>
+ <image source="../Textures/houses_rear.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="221" name="houses_front" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-32" y="32"/>
+ <image source="../Textures/houses_front.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="233" name="castle" tilewidth="1008" tileheight="848" tilecount="3" columns="3">
+ <tileoffset x="-504" y="224"/>
+ <image source="../Textures/castle.png" width="3024" height="848"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <layer name="floor" width="50" height="100">
+ <data encoding="csv">
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,153,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,145,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,156,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,152,151,1,1,1,1,1,1,1,1,1,1,1,1,1,24,31,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,155,1,1,1,1,1,1,1,1,1,1,1,1,1,24,24,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,24,23,24,24,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,24,23,23,23,24,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,24,23,23,23,23,23,32,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+153,153,153,1,1,1,153,1,1,1,1,1,1,1,24,23,29,29,29,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+165,165,150,153,153,149,154,1,1,1,1,1,1,24,23,1,1,1,1,1,1,1,1,1,153,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+177,163,163,165,165,165,151,1,1,1,1,1,1,24,23,1,1,1,1,1,1,1,1,1,145,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+155,155,155,148,177,151,1,1,1,1,1,1,24,23,1,1,1,1,1,1,1,1,1,145,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,155,155,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,145,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,145,187,187,187,194,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,145,187,187,187,198,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,149,187,187,187,208,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,156,187,187,187,206,187,187,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,152,187,187,198,187,187,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,152,187,187,187,187,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,164,187,187,187,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,160,187,187,187,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,152,187,187,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,164,187,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,160,187,187,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,164,187,187,158,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,187,187,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,31,24,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,187,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,23,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,187,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,23,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,187,150,1,1,1,1,1,1,1,1,153,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,187,150,1,1,1,1,1,1,1,149,146,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,187,154,1,1,1,1,1,1,149,181,146,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,166,1,1,1,1,1,1,149,177,181,146,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,192,187,150,1,1,1,1,1,156,177,177,181,150,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,187,187,154,1,1,1,1,1,164,177,181,181,150,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,164,187,166,1,1,1,1,1,145,177,181,181,181,150,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,187,187,150,1,1,1,1,145,177,181,181,181,181,150,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,1,1,145,177,181,181,181,181,181,146,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,1,145,177,181,181,181,181,181,177,146,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,187,187,150,1,145,177,180,167,163,163,163,163,163,154,1,1,1,1,1,1,1,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,146,145,180,180,151,155,155,155,155,155,155,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,165,180,180,166,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,164,180,180,180,180,180,154,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,156,180,180,180,180,180,166,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,180,180,180,154,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,180,180,166,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,180,180,154,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,180,180,167,151,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,148,167,151,159,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,159,159,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,
+11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11
+</data>
+ </layer>
+ <layer name="objects" width="50" height="100">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,128,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,216,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,123,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,0,
+0,0,0,0,0,0,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,83,0,0,0,0,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,127,0,226,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+126,109,0,0,0,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,0,0,99,0,0,99,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,0,0,0,0,0,0,0,133,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,125,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,106,0,0,0,0,133,0,99,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,214,0,0,0,0,0,0,0,135,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,0,0,127,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,113,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,0,0,0,0,0,129,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,210,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,125,0,103,0,0,0,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,0,0,0,0,0,99,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,0,0,
+0,0,0,0,88,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,218,110,110,110,110,0,0,0,0,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,99,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,98,117,0,0,0,0,99,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,218,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,211,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,227,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,209,0,210,0,211,0,0,0,224,0,225,0,226,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,233,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,212,0,213,0,214,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,221,0,222,0,223,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,215,0,216,0,217,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,227,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,228,0,229,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,218,0,219,0,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,230,0,231,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+</data>
+ </layer>
+ <objectgroup name="area">
+ <object id="4" type="village" x="129" y="434" width="791" height="561">
+ <properties>
+ <property name="chance" type="float" value="0.5"/>
+ <property name="density" type="float" value="0.5"/>
+ </properties>
+ </object>
+ <object id="5" type="village" x="2130" y="79" width="980" height="537">
+ <properties>
+ <property name="chance" type="float" value="0.5"/>
+ <property name="density" type="float" value="0.10000000000000001"/>
+ </properties>
+ </object>
+ <object id="8" name="Burg Leutstein" type="castle" x="1832" y="1012" width="1136" height="572">
+ <properties>
+ <property name="chance" type="float" value="0.050000000000000003"/>
+ <property name="density" type="float" value="0.5"/>
+ </properties>
+ </object>
+ <object id="9" type="graveyard" x="51" y="29" width="392" height="233"/>
+ </objectgroup>
+</map>
diff --git a/V3/Content/Maps/techdemo.tmx b/V3/Content/Maps/techdemo.tmx
new file mode 100644
index 0000000..b26a064
--- /dev/null
+++ b/V3/Content/Maps/techdemo.tmx
@@ -0,0 +1,1170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="staggered" renderorder="right-down" width="50" height="200" tilewidth="64" tileheight="32" staggeraxis="y" staggerindex="odd" nextobjectid="4">
+ <properties>
+ <property name="gridsize" type="int" value="16"/>
+ </properties>
+ <tileset firstgid="1" name="grassland" tilewidth="64" tileheight="128" tilecount="128" columns="16">
+ <image source="../Textures/grassland.png" width="1024" height="1024"/>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="48">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="49">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="50">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="51">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="52">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="53">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="54">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="55">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="56">
+ <properties>
+ <property name="collision" value="00000000000000000000011111110000"/>
+ </properties>
+ </tile>
+ <tile id="57">
+ <properties>
+ <property name="collision" value="00000000000000000000001000110010"/>
+ </properties>
+ </tile>
+ <tile id="58">
+ <properties>
+ <property name="collision" value="00000000000000000000010011000100"/>
+ </properties>
+ </tile>
+ <tile id="59">
+ <properties>
+ <property name="collision" value="00000000000000000000111011110000"/>
+ </properties>
+ </tile>
+ <tile id="60">
+ <properties>
+ <property name="collision" value="00000000000000000000001011110100"/>
+ </properties>
+ </tile>
+ <tile id="61">
+ <properties>
+ <property name="collision" value="00000000000000000000010011110010"/>
+ </properties>
+ </tile>
+ <tile id="75">
+ <properties>
+ <property name="collision" value="00000000000000001111111111111111"/>
+ </properties>
+ </tile>
+ <tile id="80">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="81">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="82">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="83">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="84">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="85">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="86">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="87">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="88">
+ <properties>
+ <property name="collision" value="00000000000000000000000101100000"/>
+ </properties>
+ </tile>
+ <tile id="89">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="90">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="91">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="92">
+ <properties>
+ <property name="collision" value="00000000000000000000000001110000"/>
+ </properties>
+ </tile>
+ <tile id="93">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="94">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="95">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="112">
+ <properties>
+ <property name="collision" value="00000000000000000000000011100000"/>
+ </properties>
+ </tile>
+ <tile id="113">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="114">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="115">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="116">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="117">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="118">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="119">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="120">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="121">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="122">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="123">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="124">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="125">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="126">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="127">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="129" name="grassland_trees" tilewidth="128" tileheight="256" tilecount="16" columns="8">
+ <tileoffset x="-32" y="0"/>
+ <image source="../Textures/grassland_trees.png" width="1024" height="512"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="145" name="grassland_water" tilewidth="64" tileheight="64" tilecount="64" columns="16">
+ <tileoffset x="0" y="32"/>
+ <image source="../Textures/grassland_water.png" width="1024" height="256"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="16">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="17">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="18">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="19">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="20">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="21">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="22">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="23">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="209" name="houses_rear" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-64" y="32"/>
+ <image source="../Textures/houses_rear.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="221" name="houses_front" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-32" y="32"/>
+ <image source="../Textures/houses_front.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="233" name="castle" tilewidth="1008" tileheight="848" tilecount="3" columns="3">
+ <tileoffset x="-504" y="224"/>
+ <image source="../Textures/castle.png" width="3024" height="848"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <layer name="floor" width="50" height="200">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,9,8,3,3,3,7,16,9,16,1,10,7,8,7,10,3,4,5,7,1,11,8,8,6,2,10,16,4,11,5,2,9,9,1,15,7,13,3,8,10,6,16,11,13,16,11,4,0,
+0,0,11,10,1,13,16,11,16,14,10,15,6,16,2,11,13,4,2,11,2,15,12,14,8,10,4,8,7,4,3,1,10,16,8,8,1,4,5,12,16,13,1,5,2,11,7,13,3,3,
+0,3,3,15,8,16,7,7,6,5,3,8,4,1,7,2,9,5,2,9,14,6,8,1,15,10,12,2,12,11,8,8,4,3,5,14,13,3,1,5,11,9,10,15,7,10,3,15,12,0,
+0,4,1,3,13,2,11,9,8,9,12,7,8,1,14,15,11,9,4,15,10,7,9,8,2,13,2,7,14,2,12,1,14,7,11,3,10,6,13,2,6,1,8,14,11,2,10,11,11,11,
+3,7,10,12,10,5,9,9,5,2,16,4,7,14,2,10,6,16,9,12,1,7,8,3,12,6,5,16,3,9,16,7,6,16,14,7,15,3,8,16,12,16,3,14,1,13,5,15,11,0,
+0,2,7,5,4,4,2,2,16,2,10,13,14,14,7,9,9,7,11,1,13,8,7,15,15,7,6,7,1,9,3,13,4,13,2,15,3,1,5,7,6,15,11,14,1,11,15,7,6,8,
+16,16,2,9,13,14,16,1,12,14,4,12,14,16,11,13,16,11,16,3,8,4,7,5,9,8,14,8,13,16,16,9,11,14,14,16,4,2,8,16,4,2,6,7,16,16,12,12,9,0,
+0,1,16,2,16,3,6,8,8,7,7,3,3,6,10,11,15,9,6,10,12,8,2,8,9,15,8,1,7,8,14,15,8,13,15,13,11,10,4,7,3,11,8,9,3,13,4,1,14,12,
+8,14,16,5,10,15,1,11,6,13,1,2,5,2,16,3,15,9,13,7,9,7,5,5,8,4,3,1,16,4,3,16,16,10,11,4,10,7,5,14,11,11,4,12,1,6,14,13,7,0,
+0,10,15,4,10,6,14,5,3,15,13,10,1,10,1,11,3,15,16,3,11,12,9,16,16,2,12,8,13,9,14,3,1,10,9,14,2,6,12,11,7,16,10,16,15,1,11,7,11,5,
+12,4,5,3,12,2,8,1,3,11,16,13,9,5,6,6,9,10,6,1,16,6,15,1,14,7,1,14,5,14,1,2,9,14,3,10,2,13,16,5,10,3,4,4,12,11,11,12,8,0,
+0,7,16,1,11,2,14,16,9,7,7,1,7,4,6,8,9,3,8,2,10,4,8,3,16,15,6,8,8,6,13,7,8,4,13,15,14,11,2,7,2,6,16,13,3,8,1,9,8,14,
+8,8,7,13,15,7,2,4,15,13,6,16,10,14,1,11,8,5,5,3,8,6,13,13,16,8,10,7,10,12,2,2,9,7,2,7,7,6,15,7,1,11,3,7,14,16,16,4,11,0,
+0,4,14,16,13,6,3,6,7,1,14,6,4,8,11,11,16,12,6,12,2,9,3,11,14,9,6,9,6,3,13,1,6,8,4,13,9,7,3,2,5,12,15,16,12,15,9,8,15,4,
+8,10,16,11,1,11,10,11,13,8,12,10,3,9,2,13,6,1,4,4,1,12,3,9,13,3,11,1,10,7,3,11,5,5,12,2,3,5,3,10,7,7,13,1,11,9,12,1,5,0,
+0,7,16,9,7,7,14,5,11,6,10,9,5,16,1,10,3,16,16,12,4,4,8,3,6,8,7,6,9,9,13,10,13,7,11,5,2,16,16,14,10,3,14,13,6,14,7,9,8,4,
+12,9,9,14,3,15,13,7,11,1,16,9,1,16,11,12,15,11,8,7,13,2,16,15,2,1,7,10,4,2,3,8,6,16,8,10,12,3,6,1,6,16,15,11,4,15,1,2,4,0,
+0,4,8,13,6,9,13,7,2,1,7,14,5,10,9,13,7,10,11,3,16,10,10,5,1,9,8,9,12,9,10,14,10,2,9,3,6,13,5,16,2,13,2,14,3,4,5,13,3,13,
+5,11,11,3,9,12,2,11,3,7,14,5,3,15,10,4,12,9,16,14,2,13,15,13,13,4,7,10,6,11,12,5,10,1,3,13,8,7,3,5,1,1,9,3,9,6,15,13,6,0,
+0,14,3,15,7,13,11,7,14,14,6,16,9,15,11,1,4,5,12,13,4,13,2,14,9,14,1,6,7,6,2,15,15,12,10,14,8,1,3,13,2,12,3,2,5,14,5,10,3,16,
+7,9,4,8,16,5,5,3,7,15,4,15,3,6,11,5,1,14,12,5,5,2,11,5,7,4,8,1,9,11,4,14,1,6,10,4,12,10,10,3,15,2,13,4,3,1,15,2,11,0,
+0,11,11,9,16,15,16,6,5,16,12,13,16,10,5,7,15,3,15,7,12,14,11,16,12,11,4,15,4,6,5,15,3,7,3,5,4,3,16,7,16,13,11,3,11,1,15,6,8,12,
+8,4,11,13,11,14,9,14,11,15,11,14,9,11,14,7,16,4,13,14,6,5,11,4,13,1,5,2,15,10,3,15,2,10,10,9,6,4,5,11,6,9,13,9,6,2,4,1,7,0,
+0,10,3,13,10,8,15,2,12,14,12,10,4,16,2,8,2,15,11,16,8,16,1,6,7,7,14,14,4,9,14,14,16,11,10,11,16,3,1,11,6,7,12,4,9,14,11,13,14,14,
+7,11,4,11,7,6,5,12,11,7,14,10,3,8,10,1,2,5,14,10,6,7,3,12,12,3,14,16,2,2,16,9,1,5,13,11,3,1,5,8,15,7,7,1,12,6,15,5,14,0,
+0,13,6,10,12,13,1,16,11,3,2,5,12,16,2,14,16,9,11,2,14,9,16,12,1,13,5,13,10,8,4,10,11,16,12,12,13,2,15,2,14,14,5,12,14,9,9,4,4,11,
+6,6,10,16,10,2,6,5,8,6,1,15,12,3,5,9,5,16,4,2,1,3,15,8,6,5,9,16,6,3,9,14,12,11,10,11,1,14,5,16,2,15,10,11,14,6,10,10,8,0,
+0,4,13,6,16,1,6,4,8,1,2,1,14,2,14,15,10,10,2,8,16,9,12,1,11,5,4,3,14,14,9,4,6,13,4,16,8,2,7,12,16,7,9,6,4,14,14,3,3,16,
+16,8,16,11,4,3,3,11,6,1,7,4,5,11,5,4,6,4,15,15,5,3,11,15,5,5,8,8,15,16,9,15,1,14,10,4,3,2,11,11,1,3,5,16,13,15,5,4,1,0,
+0,12,6,2,12,12,1,13,1,16,16,10,5,12,6,4,13,1,6,2,8,12,15,6,4,6,10,2,8,6,16,2,11,14,4,14,15,15,6,13,2,2,4,15,16,2,6,4,16,1,
+10,3,7,7,5,14,13,7,15,12,15,8,8,5,16,15,8,10,15,12,3,8,5,8,8,5,5,7,15,4,15,7,11,15,9,2,11,14,7,12,6,1,7,2,15,4,16,14,4,0,
+0,16,4,9,14,12,13,8,13,3,10,9,8,3,1,2,9,10,12,10,7,7,3,7,12,9,15,1,2,16,12,1,3,13,3,14,9,1,15,3,1,12,11,8,5,1,5,8,2,2,
+14,9,13,8,12,1,8,1,1,10,10,1,10,1,15,11,1,12,2,14,1,5,12,8,2,16,4,8,10,2,1,8,16,14,7,4,6,6,9,2,15,9,8,12,12,5,5,6,11,0,
+0,4,2,12,14,2,13,6,12,2,2,4,3,8,2,4,5,14,1,10,7,1,8,14,5,4,5,12,15,13,9,6,10,8,10,1,12,4,4,7,5,6,6,6,4,7,4,2,2,3,
+16,14,8,1,1,6,5,7,7,2,1,6,11,8,14,4,13,16,3,12,12,6,11,10,11,5,1,11,14,15,3,16,14,4,2,14,4,11,1,9,13,9,6,12,8,9,10,15,6,0,
+0,15,5,8,15,3,16,12,4,14,8,11,3,4,4,5,7,16,4,3,4,6,16,4,6,2,7,16,1,10,5,9,4,11,9,9,3,10,14,2,14,6,7,15,10,6,14,2,8,10,
+8,1,13,10,3,12,16,9,15,1,10,15,7,12,10,2,11,14,5,16,7,15,7,11,7,15,3,15,3,1,16,11,5,3,15,6,1,6,5,14,16,3,15,7,1,10,16,11,10,0,
+0,9,7,12,16,2,8,4,13,9,8,5,14,16,1,16,2,5,6,2,2,4,6,3,5,5,16,12,15,13,13,13,15,11,2,7,1,13,3,4,15,14,13,13,12,3,13,6,12,16,
+15,15,14,14,11,11,14,2,3,11,14,16,9,11,11,10,6,2,12,14,16,3,6,2,7,10,4,8,6,14,15,13,16,16,8,7,7,13,13,8,3,12,4,11,8,12,15,7,7,0,
+0,16,8,1,10,8,1,10,2,4,11,3,3,1,16,11,2,13,13,14,2,6,12,11,7,10,12,16,7,8,8,14,7,15,15,2,12,16,15,15,13,12,6,4,1,2,4,4,15,5,
+15,7,7,15,1,1,14,3,9,6,8,7,1,3,8,7,5,4,16,14,11,3,15,6,12,7,3,5,13,10,8,2,2,15,11,3,12,11,4,10,13,16,12,9,5,16,10,16,1,0,
+0,6,5,2,1,5,3,10,8,12,8,13,1,6,16,11,16,7,12,6,16,2,16,8,2,4,7,12,7,10,13,4,10,8,6,5,7,6,9,11,14,1,1,2,6,9,15,15,9,4,
+5,5,10,8,3,13,15,14,6,5,11,3,13,11,12,12,8,14,5,4,11,1,11,6,14,8,6,16,7,11,16,2,3,4,5,15,15,15,3,14,12,10,13,6,11,8,7,1,3,0,
+0,1,16,13,16,4,6,12,12,4,9,15,14,13,13,7,6,4,16,11,15,6,6,15,9,8,13,10,13,9,11,1,16,15,15,12,10,8,16,16,1,13,14,9,15,14,1,11,10,2,
+10,13,11,1,2,2,16,15,7,7,15,16,6,10,2,9,3,2,5,3,5,12,14,13,14,11,7,15,2,10,1,5,15,8,5,4,8,12,1,4,7,4,10,7,12,3,11,5,14,0,
+0,3,6,7,11,13,5,1,16,7,10,15,14,6,16,11,1,11,2,11,3,3,6,5,7,9,1,7,15,2,1,15,8,2,2,6,14,8,13,1,7,1,7,4,6,3,2,12,11,6,
+9,6,2,9,2,12,2,14,16,9,3,12,9,4,15,7,13,14,12,13,7,2,11,8,13,7,7,10,14,13,4,2,6,16,2,7,8,4,12,14,5,11,6,2,7,14,15,13,2,0,
+0,2,8,13,4,7,1,16,14,12,1,16,2,3,9,5,2,12,13,8,2,8,15,3,3,8,4,16,12,14,16,8,15,14,7,7,2,11,15,9,11,7,13,8,15,7,8,1,6,16,
+13,3,5,7,15,4,7,13,11,14,15,3,14,5,11,3,4,11,5,8,9,2,10,8,6,8,16,10,3,11,1,4,7,7,16,8,6,7,4,9,5,4,13,6,5,5,15,16,7,0,
+0,14,5,12,14,12,11,9,11,6,1,2,8,6,3,11,3,9,2,12,14,2,3,1,2,6,15,11,10,1,6,14,7,3,9,6,13,15,15,16,5,14,5,14,15,13,10,9,12,13,
+7,3,7,16,15,4,16,3,4,2,1,13,12,6,9,8,16,10,11,1,11,14,10,7,12,2,15,11,2,8,2,3,16,5,16,4,12,3,7,8,14,11,8,10,15,10,8,9,13,0,
+0,10,9,10,8,13,10,11,1,1,11,15,11,15,3,13,14,8,14,3,13,7,3,7,3,14,16,7,2,8,14,12,6,10,12,5,3,15,2,10,5,12,1,13,2,15,3,7,7,12,
+14,16,15,1,8,8,7,9,3,9,5,9,16,12,13,7,6,1,11,10,3,11,15,6,11,9,1,16,9,10,15,7,15,10,13,14,4,7,7,1,6,7,15,1,12,7,11,5,15,0,
+0,10,7,7,11,9,8,2,8,10,13,2,9,8,13,1,12,9,13,4,12,16,13,12,15,15,10,8,8,2,15,2,9,1,1,3,13,3,4,13,13,8,5,1,14,1,2,12,3,8,
+16,16,9,4,4,5,16,5,1,9,6,6,7,3,9,15,16,15,16,4,16,9,12,5,6,2,10,10,12,10,16,7,7,10,1,9,4,4,8,9,13,6,12,11,6,10,12,11,13,0,
+0,5,3,12,16,8,15,7,9,9,13,7,11,16,11,2,11,4,6,1,16,14,14,11,16,6,15,10,2,6,11,8,14,8,16,14,12,4,16,8,5,7,11,15,1,12,2,15,11,14,
+2,15,11,5,12,16,5,15,16,9,7,8,2,6,13,15,6,5,1,16,12,1,13,5,7,4,8,6,3,5,8,12,14,12,1,13,5,15,6,13,16,16,9,15,9,2,9,4,7,0,
+0,9,13,4,1,5,15,1,2,8,12,11,14,11,5,14,15,12,13,9,4,13,14,16,16,4,6,14,12,14,8,14,4,9,10,3,9,9,7,4,1,7,15,10,5,1,6,7,5,8,
+4,6,5,6,1,9,5,11,12,7,10,2,6,8,6,15,4,8,16,4,14,4,14,10,12,12,13,8,10,3,9,12,7,14,3,3,4,4,11,14,4,16,7,16,13,14,14,16,8,0,
+0,10,5,13,15,9,3,4,5,7,4,6,16,14,5,14,5,1,6,6,5,12,4,12,5,2,3,8,8,16,14,12,5,10,12,15,14,14,9,4,11,1,11,4,1,2,1,3,8,8,
+12,10,16,15,8,7,16,2,6,7,1,3,16,15,10,12,4,11,3,10,10,12,16,4,15,12,8,5,6,9,11,15,9,9,13,11,8,9,11,9,11,10,5,15,15,6,6,8,7,0,
+0,14,6,10,12,2,5,7,8,12,8,10,13,1,3,13,15,3,10,8,14,14,11,14,7,14,2,15,5,8,7,10,12,16,13,7,16,6,6,12,16,6,14,12,7,9,1,6,13,3,
+1,3,11,16,5,7,8,5,3,10,2,7,9,2,12,9,14,1,4,9,5,16,16,16,10,5,3,15,5,1,9,5,7,13,10,7,13,14,6,10,14,4,9,9,9,9,11,12,13,0,
+0,1,1,12,9,9,5,4,12,10,9,14,4,7,10,6,12,2,5,5,3,16,7,8,7,3,14,11,5,16,9,11,12,12,7,4,12,12,8,4,3,7,10,10,4,2,15,3,13,5,
+2,13,10,10,11,3,14,5,4,4,4,10,14,13,12,13,5,9,1,16,7,2,1,4,14,1,5,10,3,12,5,15,15,9,8,16,7,11,4,8,15,6,14,16,13,10,6,2,16,0,
+0,2,11,13,7,15,6,12,5,7,13,11,10,7,9,4,15,1,7,1,5,12,8,3,14,2,2,16,2,3,15,16,15,1,15,2,6,6,4,8,11,15,7,10,10,12,5,3,1,3,
+7,5,3,9,14,3,16,15,13,14,9,8,6,16,10,2,13,9,15,13,8,10,9,12,1,6,10,2,2,4,6,9,10,13,6,9,10,11,2,15,5,12,7,11,15,1,4,13,2,0,
+0,12,11,1,12,4,11,16,8,1,14,15,3,4,9,10,5,4,9,5,14,1,16,13,6,4,13,8,16,15,5,1,4,12,6,1,6,13,6,5,16,13,5,5,5,16,10,16,1,1,
+5,9,13,16,7,12,12,10,3,3,1,2,14,4,5,10,5,12,7,9,12,13,15,13,11,16,9,10,4,4,9,11,12,1,7,2,16,3,6,7,5,13,10,8,4,9,3,10,14,0,
+0,3,7,1,7,8,8,10,10,15,16,11,9,13,8,4,3,15,6,2,2,3,8,1,14,16,14,5,11,12,16,3,8,7,6,10,2,15,2,5,3,15,4,15,16,14,8,11,12,15,
+10,9,4,7,7,13,11,8,14,8,13,14,14,4,8,9,1,8,2,5,9,4,11,7,6,3,1,16,16,11,8,10,11,12,1,11,5,16,9,3,6,13,3,11,1,1,16,9,3,0,
+0,16,3,10,14,6,9,3,11,11,13,16,1,1,14,5,9,9,13,4,11,5,11,14,15,9,8,8,16,9,7,5,7,7,8,9,15,9,15,9,2,4,16,11,12,13,13,3,8,10,
+4,1,6,8,4,14,12,7,11,10,16,12,8,6,7,5,7,14,6,15,2,16,15,7,3,7,153,7,2,13,14,7,6,8,2,14,3,16,8,3,13,11,13,11,6,10,8,6,13,0,
+0,16,1,7,3,10,7,4,1,1,6,14,6,12,4,15,2,4,9,1,7,8,4,6,153,153,149,150,2,2,3,16,5,12,10,13,11,8,9,12,15,11,4,4,14,15,4,2,12,1,
+8,2,8,11,10,2,11,4,9,3,15,8,11,7,11,2,6,6,8,3,6,8,153,149,165,165,189,150,8,6,3,13,11,15,14,5,4,8,13,7,9,6,7,1,5,16,6,5,2,0,
+0,4,1,15,5,8,2,15,1,13,1,6,11,12,15,16,15,6,6,3,4,4,149,165,181,167,167,188,150,14,9,2,9,7,7,6,15,1,11,12,15,13,1,14,3,10,14,10,11,12,
+2,15,5,7,6,4,14,12,11,15,3,2,8,10,6,7,10,8,15,15,4,149,179,151,155,155,152,183,150,6,4,16,15,6,12,13,4,13,6,3,6,4,1,13,15,8,4,12,1,0,
+0,11,4,9,16,4,1,11,3,9,6,13,12,1,16,10,5,12,10,10,9,149,180,151,11,2,7,152,184,150,14,5,2,1,11,9,16,10,9,1,15,6,13,2,3,13,9,9,13,6,
+7,10,7,14,4,2,8,6,9,14,5,3,11,4,8,9,10,4,4,5,149,188,151,13,12,4,15,152,189,150,11,6,11,13,10,10,7,7,6,1,7,5,2,7,1,1,11,3,10,0,
+0,3,7,9,16,10,13,13,8,13,11,11,5,16,8,15,14,9,11,8,149,177,151,3,6,1,3,14,152,179,150,8,16,9,8,1,3,10,9,9,7,11,6,15,6,2,10,5,12,10,
+2,4,5,12,12,10,2,5,12,12,4,10,2,3,11,10,15,2,4,156,183,166,13,8,16,6,2,8,152,183,150,10,7,13,1,2,15,9,11,6,7,9,14,8,5,8,14,10,11,0,
+0,7,4,16,6,2,5,3,5,1,14,14,8,6,4,14,6,8,16,2,168,180,154,16,4,13,3,1,8,168,182,150,15,12,12,7,7,1,12,5,2,6,6,8,5,14,4,16,12,5,
+4,2,4,14,8,8,4,6,9,7,8,4,5,15,7,9,11,2,8,156,188,151,7,10,15,12,1,3,156,179,184,150,1,7,9,12,7,4,16,9,7,12,16,8,2,3,6,15,12,0,
+0,5,2,7,10,5,1,8,4,4,16,2,12,8,1,6,7,14,14,15,168,151,9,15,12,5,15,3,12,148,184,181,150,6,16,14,16,7,13,5,13,3,6,2,7,3,2,12,7,8,
+12,6,14,15,2,6,14,14,10,9,11,13,14,2,3,9,2,10,12,149,151,2,16,5,14,15,2,16,10,148,181,182,150,4,8,14,10,11,1,3,9,1,1,16,10,16,12,7,3,0,
+0,10,1,11,15,5,13,6,13,10,14,7,2,4,13,6,12,10,15,149,166,16,4,6,5,7,11,7,16,12,148,189,180,150,3,5,6,4,14,14,7,14,14,6,1,9,15,8,4,14,
+5,8,1,1,1,11,8,15,4,11,13,16,12,10,11,12,4,6,156,184,154,6,3,8,4,14,8,14,4,8,148,182,189,150,1,16,13,16,15,1,12,11,11,10,12,5,5,9,10,0,
+0,4,10,3,4,3,6,1,15,15,13,12,15,3,3,16,14,13,9,152,166,9,2,6,4,4,2,2,2,12,12,148,180,179,150,3,16,11,1,5,10,1,7,2,8,16,11,9,7,8,
+14,4,7,13,13,11,16,2,3,1,8,15,7,6,12,6,14,9,11,168,154,8,14,7,7,16,1,9,12,11,12,148,177,179,158,4,7,13,2,13,7,3,7,4,8,8,6,15,4,0,
+0,5,1,11,2,5,11,13,5,9,1,6,7,3,12,9,13,8,9,156,166,4,9,3,2,5,4,9,15,15,3,5,168,178,166,2,6,5,2,16,8,14,14,16,15,11,6,12,4,8,
+1,5,14,15,4,13,5,15,3,13,1,10,5,4,11,9,6,5,1,168,154,1,20,15,12,2,11,4,4,15,4,156,189,188,154,13,4,15,13,12,15,16,1,16,11,12,11,4,7,0,
+0,3,2,6,12,14,2,11,14,5,8,5,12,14,10,13,9,9,7,149,166,13,9,17,7,11,14,12,8,11,16,8,164,180,166,9,11,1,10,15,16,2,14,14,9,14,7,6,11,8,
+11,12,16,9,13,13,9,8,3,1,4,11,1,6,15,3,2,3,156,185,154,4,4,17,5,12,13,12,13,5,4,145,180,179,154,13,16,1,3,16,7,8,13,3,1,7,12,12,11,0,
+0,16,16,4,6,4,1,11,11,5,11,14,6,9,4,10,12,1,15,152,166,11,10,4,18,27,2,8,7,14,15,156,189,177,166,2,4,13,8,9,6,14,3,5,1,13,2,7,12,15,
+7,11,3,7,1,7,13,1,3,1,9,7,8,14,8,11,7,14,6,168,146,8,9,4,17,32,2,1,11,10,11,164,181,181,154,9,15,15,14,10,9,15,2,10,13,15,13,14,8,0,
+0,10,3,10,5,2,1,10,16,10,5,6,1,9,1,13,7,7,5,156,187,150,5,10,30,17,2,1,3,7,15,156,181,178,166,4,1,8,8,5,1,3,13,7,15,14,3,16,14,4,
+16,10,14,12,2,6,13,13,7,13,2,16,14,13,12,11,9,4,11,168,189,150,13,16,18,17,15,15,13,12,13,164,182,184,154,8,9,8,12,16,13,13,13,5,11,16,6,2,10,0,
+0,9,16,4,5,2,5,3,4,12,5,13,4,13,1,2,8,15,13,156,180,189,150,6,13,17,17,11,12,6,2,156,188,183,166,5,10,13,2,12,2,10,1,12,1,14,6,11,16,7,
+4,15,15,8,8,16,15,14,2,14,5,12,11,13,2,7,13,1,10,152,188,186,150,16,2,29,25,1,7,8,2,164,184,187,154,5,13,14,1,10,14,12,8,14,8,10,12,7,10,0,
+0,4,7,16,12,3,4,13,15,5,12,15,7,8,15,14,8,15,8,11,168,182,184,150,1,6,2,12,12,10,16,149,178,185,151,1,15,2,1,7,12,13,4,1,15,9,4,12,15,15,
+8,8,2,4,5,16,8,7,2,5,16,13,2,7,16,4,11,13,1,156,184,179,189,150,5,16,15,9,5,12,156,179,178,166,1,8,16,9,6,10,1,15,3,2,3,15,15,15,16,0,
+0,12,6,11,14,7,9,5,4,16,1,8,3,9,6,6,10,3,12,14,148,178,179,179,194,5,1,14,11,12,8,164,186,179,154,4,13,14,16,16,3,3,5,1,6,4,13,12,8,7,
+3,1,11,1,11,12,2,8,12,3,4,1,5,1,12,11,13,8,16,15,148,179,185,206,150,13,1,3,13,8,149,186,189,151,3,13,13,7,10,8,2,7,8,4,3,8,11,6,13,0,
+0,15,6,11,8,9,13,12,11,6,9,16,16,10,13,13,3,12,14,10,2,148,184,208,189,150,12,15,8,1,156,178,178,151,1,1,3,3,10,2,13,15,5,6,11,5,12,7,12,15,
+16,4,5,14,7,8,13,15,13,6,5,2,4,1,4,8,10,9,2,1,12,148,206,180,180,150,9,2,4,14,164,177,151,1,12,7,9,14,8,5,14,12,7,6,1,16,5,15,4,0,
+0,7,15,16,6,2,5,15,1,15,4,12,7,11,10,6,15,8,7,3,6,13,196,180,186,188,150,12,3,3,156,186,151,7,4,6,6,13,12,1,1,16,3,9,16,4,11,1,13,15,
+14,5,2,5,9,11,11,13,3,11,12,3,9,16,2,13,9,8,11,1,3,16,152,179,181,186,150,7,8,14,164,151,15,14,5,10,13,6,1,13,14,2,3,3,3,15,10,2,13,0,
+0,3,3,10,5,5,6,2,9,13,10,2,3,16,11,6,12,11,15,13,6,5,5,148,178,177,189,150,153,153,149,151,14,5,10,5,10,15,13,7,16,5,7,11,5,16,10,13,7,2,
+1,15,7,8,16,14,3,8,2,10,10,4,7,7,7,5,8,2,14,9,3,12,9,148,178,189,180,165,165,161,151,12,15,7,14,1,2,1,7,4,7,4,5,10,4,11,7,15,4,0,
+0,16,5,16,2,8,12,4,3,14,1,8,8,8,4,11,14,7,15,2,14,11,10,4,148,183,187,184,185,189,151,8,9,13,1,2,11,13,1,6,2,5,3,8,2,14,8,6,11,15,
+6,12,12,5,14,8,16,3,5,9,14,7,15,3,14,2,8,4,2,14,9,5,8,3,148,182,184,178,177,151,4,15,11,8,8,7,14,15,12,7,15,14,2,11,4,14,5,5,15,0,
+0,12,6,4,6,16,7,16,13,2,10,11,14,13,11,6,5,9,6,5,13,1,6,8,4,148,179,179,167,151,12,9,16,11,12,14,1,1,8,7,6,13,15,7,16,11,15,6,14,10,
+13,14,14,10,3,13,15,6,2,4,9,2,15,12,16,15,12,12,5,10,7,10,5,2,15,148,163,151,159,14,1,2,11,8,4,13,5,7,1,8,7,10,1,9,8,13,9,4,15,0,
+0,16,9,16,1,13,16,13,4,4,4,11,11,13,1,14,6,8,10,16,9,7,4,14,12,2,159,155,11,5,11,7,10,1,11,4,12,10,2,14,14,10,12,10,13,15,12,16,13,11,
+9,10,13,8,14,7,8,11,12,5,14,16,7,9,10,13,4,12,14,11,13,8,9,8,11,12,16,2,12,7,9,10,15,12,15,4,8,15,11,6,11,9,13,8,15,2,4,6,13,0,
+0,14,15,14,2,7,7,12,15,14,15,8,8,5,4,15,15,6,15,10,16,5,13,6,13,12,8,13,4,2,15,15,8,1,11,4,7,15,13,14,6,11,16,7,4,9,5,2,6,10,
+9,9,3,14,8,15,1,2,8,1,15,11,15,10,8,2,8,16,10,5,8,13,12,10,7,1,11,14,5,4,9,3,8,4,16,2,14,6,16,12,9,5,16,6,4,1,13,14,15,0,
+0,12,7,3,13,12,4,9,15,2,14,9,13,9,12,13,8,10,5,11,10,9,9,6,11,8,8,14,16,3,11,15,10,5,10,16,6,5,9,6,15,6,7,3,10,6,2,5,10,3,
+1,10,14,13,15,6,3,12,6,14,6,5,3,3,1,12,12,1,9,4,4,13,11,9,7,10,2,15,10,1,1,7,16,9,7,13,4,1,13,6,11,6,6,2,7,11,15,12,3,0,
+0,14,12,8,1,12,3,16,6,15,8,7,4,6,3,14,11,3,9,4,9,6,2,12,14,1,6,7,10,8,13,8,13,11,16,2,10,16,9,8,1,8,1,15,4,10,3,11,6,8,
+5,9,1,6,14,16,10,3,3,3,15,8,11,6,15,8,15,10,12,4,7,16,2,11,6,6,15,11,1,10,12,1,10,8,3,8,1,10,13,10,6,9,15,15,6,12,16,12,14,0,
+0,9,16,6,4,5,3,12,8,15,16,14,13,16,7,8,16,7,15,12,15,7,2,14,16,9,15,9,12,7,8,11,16,3,6,6,14,3,5,2,13,14,5,12,7,5,13,3,13,9,
+15,16,16,9,4,9,13,13,13,2,11,16,8,16,10,15,6,12,12,13,12,16,7,13,1,9,2,6,12,7,8,1,10,12,5,8,8,12,8,9,8,14,3,6,11,13,10,16,7,0,
+0,10,9,2,7,2,14,13,7,2,10,7,10,15,16,7,2,2,13,14,5,1,6,4,4,9,6,9,10,14,4,9,6,15,13,4,4,2,8,5,7,5,3,15,8,1,7,15,13,4,
+3,9,6,12,10,4,7,5,5,15,8,3,5,3,2,4,12,2,5,13,8,1,9,6,9,16,9,2,9,1,11,7,6,16,7,3,6,14,16,11,4,2,12,9,4,10,9,16,14,0,
+0,7,5,8,9,15,1,7,1,16,15,14,1,16,6,10,15,16,9,1,10,15,8,10,3,10,15,6,2,6,8,3,16,9,7,11,15,3,14,6,1,1,12,6,1,14,11,11,9,15,
+10,6,11,9,14,1,15,1,10,11,12,6,7,8,2,7,12,6,12,15,6,13,13,3,16,14,4,3,14,7,11,10,15,15,7,11,12,8,5,6,8,14,14,4,2,8,7,10,4,0,
+0,13,9,8,1,6,14,14,6,14,8,2,9,1,13,6,16,11,2,16,16,8,13,3,16,5,7,5,7,16,6,11,14,6,2,11,13,5,4,12,12,13,5,6,4,13,14,15,15,5,
+15,2,5,13,6,6,14,16,6,10,4,5,2,16,11,3,15,9,5,4,13,2,2,13,12,11,1,3,10,13,16,8,4,5,11,15,5,11,12,8,7,6,12,1,3,12,4,8,14,0,
+0,11,13,12,6,14,8,7,7,5,9,4,16,13,8,12,12,11,4,12,12,4,15,5,3,5,16,13,11,14,2,15,2,4,5,10,10,16,15,2,10,16,6,14,12,6,9,11,16,9,
+6,9,9,15,12,6,14,4,1,13,13,2,4,6,14,2,5,15,12,2,8,5,2,1,4,8,1,16,1,13,9,6,10,8,16,7,12,2,12,15,7,5,7,8,15,15,12,2,8,0,
+0,10,6,2,13,6,12,4,13,4,6,16,7,15,1,12,14,5,1,3,10,2,3,12,12,6,15,5,16,12,3,5,8,8,5,10,15,4,6,13,10,4,12,13,4,6,10,4,2,13,
+6,13,8,9,3,10,2,2,7,11,11,4,15,10,1,5,11,10,8,1,4,2,13,11,8,15,1,12,13,7,12,5,13,6,14,12,12,12,11,14,2,1,16,14,13,6,14,7,4,0,
+0,10,16,15,6,2,2,16,5,5,10,1,14,8,15,5,1,11,8,9,8,10,7,10,12,16,9,14,3,2,11,9,12,2,3,13,6,16,5,5,11,5,2,13,2,11,6,10,14,6,
+8,2,2,3,9,11,10,16,6,7,15,14,12,1,10,11,16,10,15,14,5,15,13,12,4,10,11,16,4,13,12,3,6,4,16,7,4,7,12,9,3,8,5,16,4,4,8,13,1,0,
+0,13,4,1,1,14,15,1,8,11,13,10,3,1,7,6,1,9,5,16,4,15,2,5,15,16,7,16,4,7,14,14,4,3,4,12,5,15,3,9,9,10,8,2,5,3,16,6,14,9,
+15,6,10,12,15,16,2,1,13,5,3,10,14,11,6,12,1,4,4,8,7,2,4,16,14,6,13,2,3,13,1,12,2,5,4,11,5,9,7,10,3,3,9,1,4,12,9,8,13,0,
+0,11,15,15,2,1,14,16,2,7,3,9,12,5,3,5,2,5,6,15,7,6,13,11,3,13,10,14,5,1,5,2,14,16,9,7,5,11,7,16,3,8,4,7,8,1,10,6,9,6,
+5,14,9,5,11,4,2,3,13,10,2,1,15,16,14,8,6,11,12,5,8,15,3,9,15,4,16,3,7,7,15,8,15,4,16,15,9,10,6,1,7,13,5,9,1,1,6,5,14,0,
+0,7,14,12,4,10,1,5,12,7,5,10,2,16,6,1,13,1,5,10,10,10,13,9,1,7,1,16,16,12,14,5,1,4,6,9,3,5,6,5,15,11,5,9,7,11,15,1,4,5,
+15,1,11,4,10,9,9,4,7,3,16,10,3,14,5,10,15,16,6,12,6,5,8,16,4,16,4,11,2,10,12,11,10,4,8,3,15,3,4,12,11,5,7,6,13,12,10,8,12,0,
+0,3,2,1,8,3,6,9,12,6,5,9,8,4,13,12,10,14,10,12,12,4,5,13,3,1,1,2,4,12,1,9,12,12,16,12,9,15,11,2,2,5,6,16,12,6,6,5,8,9,
+3,13,11,1,5,7,10,1,16,9,3,9,5,6,13,8,16,9,9,8,7,10,3,13,12,10,1,7,10,7,4,14,7,5,7,7,4,7,15,16,11,7,14,1,3,13,14,15,11,0,
+0,13,16,6,12,4,7,13,2,6,3,1,16,16,5,8,4,1,11,8,16,6,8,9,5,15,12,1,4,12,9,9,14,12,2,9,8,1,8,10,3,11,15,4,3,12,12,3,12,6,
+13,15,5,16,14,7,12,8,3,8,9,11,3,13,16,12,2,1,5,12,1,11,10,9,2,2,14,2,5,11,2,3,7,10,1,7,6,16,8,8,11,16,1,13,1,7,5,13,16,0,
+0,13,9,11,7,3,2,6,14,4,9,8,8,1,2,13,11,1,9,6,4,16,11,9,3,13,13,1,15,14,5,12,1,11,15,13,8,1,13,13,6,4,4,7,2,9,13,12,10,11,
+3,16,2,9,14,5,9,15,8,13,12,10,12,3,14,9,5,7,4,13,2,9,7,6,4,14,1,12,1,13,16,4,12,13,2,11,1,12,11,12,9,6,14,6,3,12,2,11,15,0,
+0,10,9,15,10,14,9,12,1,15,8,11,3,10,3,8,15,9,12,7,11,10,15,2,8,5,16,16,16,11,12,12,1,5,9,12,7,6,4,15,4,6,3,9,1,10,6,11,2,11,
+5,8,7,16,12,6,8,10,2,6,13,13,7,15,10,10,2,15,9,13,16,11,10,6,4,16,4,10,11,15,6,8,3,16,2,3,5,2,4,5,15,12,13,16,4,8,4,10,16,0,
+0,15,2,10,4,4,1,14,3,16,8,4,11,15,15,9,10,9,12,2,12,4,1,2,11,2,14,10,11,15,12,7,13,7,5,16,16,1,11,11,13,1,1,10,13,6,2,11,2,13,
+3,15,5,13,7,6,12,8,8,11,4,5,16,12,5,8,6,4,3,1,10,8,10,10,6,7,3,8,15,11,16,10,6,8,14,9,3,12,3,9,13,2,3,9,1,13,9,1,10,0,
+0,12,14,16,16,9,9,8,3,11,13,8,13,5,5,5,1,14,12,6,2,11,15,15,5,2,10,8,14,10,9,8,3,10,11,5,8,9,12,8,4,1,12,10,14,10,9,3,13,13,
+15,12,13,5,4,6,2,1,13,16,6,10,7,2,11,7,12,14,5,11,6,9,3,8,11,2,5,14,3,11,7,12,2,1,6,16,11,2,12,6,15,7,2,9,16,12,16,1,11,0,
+0,2,12,2,4,2,10,11,8,6,11,1,12,16,10,11,4,7,5,1,9,7,13,1,7,15,12,14,16,5,12,15,9,2,13,14,15,3,5,12,14,11,13,14,12,15,2,12,16,12,
+6,5,6,3,6,10,10,3,13,1,4,15,15,7,11,12,5,8,9,2,8,8,8,11,6,9,8,4,15,2,8,13,3,10,13,7,8,14,11,7,4,10,11,2,2,15,8,4,12,0,
+0,6,9,10,6,15,8,6,10,14,16,14,11,2,3,3,4,3,7,4,11,4,15,5,12,12,3,4,14,13,5,6,15,3,16,10,4,15,7,8,14,13,5,15,4,1,14,6,7,7,
+13,3,14,5,11,16,9,5,13,8,16,13,13,16,7,3,9,3,9,7,8,3,7,12,6,8,9,15,7,16,13,1,3,1,8,11,11,6,6,11,5,1,6,6,14,11,3,10,9,0,
+0,1,7,9,1,11,13,8,15,13,4,14,11,14,1,10,11,1,11,3,16,5,15,16,10,6,12,1,4,11,11,6,5,10,2,10,1,3,3,3,9,5,12,7,9,9,9,5,12,7,
+5,11,8,1,14,16,9,5,16,10,14,9,6,10,4,10,1,11,4,4,1,4,16,8,12,6,6,5,6,12,15,5,13,13,12,9,7,7,11,16,8,7,3,1,11,2,6,16,8,0,
+0,11,8,15,15,2,14,4,12,16,15,1,8,5,7,14,12,2,15,3,5,8,2,9,13,7,12,9,15,3,9,15,8,12,5,4,6,7,13,4,4,6,16,10,1,13,10,2,11,1,
+5,3,11,8,6,12,6,6,12,16,13,10,16,6,6,1,7,15,6,4,4,14,15,12,9,1,4,1,8,7,4,8,15,14,13,4,13,1,14,10,14,9,16,10,12,15,15,3,1,0,
+0,16,3,5,10,12,16,9,16,10,10,2,16,2,2,2,16,15,14,15,7,16,11,1,4,10,6,8,6,6,4,7,6,16,14,4,12,11,16,13,9,13,12,2,16,1,7,8,9,6,
+13,16,16,4,8,1,11,6,6,3,15,11,11,5,10,2,15,10,13,1,12,7,10,12,14,2,9,12,6,5,16,12,3,13,11,10,10,7,8,5,3,6,5,16,5,14,6,16,12,0,
+0,8,14,11,8,12,15,14,13,2,13,1,4,16,16,4,11,3,10,3,6,1,5,14,5,8,4,2,14,15,2,16,16,6,2,4,4,4,16,3,9,10,1,9,8,1,2,12,3,10,
+9,12,4,3,14,16,2,5,1,14,9,3,6,10,2,12,9,2,12,9,14,6,13,6,13,16,1,7,6,14,11,8,15,8,5,8,5,8,14,16,10,1,13,12,9,14,15,3,1,0,
+0,3,13,14,11,4,2,5,1,3,2,8,8,6,6,10,13,12,4,8,14,4,13,15,4,8,2,8,2,13,14,5,12,6,8,9,15,4,5,2,6,16,7,8,8,5,9,3,8,7,
+15,4,5,16,15,6,4,8,1,7,6,3,14,14,16,14,13,12,6,14,12,14,3,5,5,9,16,15,12,15,13,3,1,11,7,16,13,3,4,2,3,8,6,16,2,5,8,8,3,0,
+0,11,3,9,5,15,16,11,11,5,8,4,14,11,7,2,3,1,10,7,16,7,16,1,10,12,8,2,10,4,16,10,6,13,9,13,6,12,1,11,2,2,11,8,4,2,6,7,5,15,
+6,12,9,11,13,4,11,16,8,16,4,3,13,7,16,10,14,14,11,10,16,10,14,5,2,13,2,15,3,8,12,6,3,10,3,10,13,2,10,8,12,13,3,13,10,4,14,1,14,0,
+0,4,4,12,4,12,8,12,16,2,9,14,4,3,5,3,8,4,15,16,9,12,13,16,7,7,12,2,10,7,12,8,10,1,16,11,8,14,2,6,5,13,16,9,10,2,11,11,4,3,
+12,12,6,11,13,13,3,6,4,13,4,12,8,3,2,12,6,10,10,11,10,13,10,14,6,12,5,12,11,7,13,1,15,3,10,9,10,6,3,4,5,9,6,4,14,6,16,13,3,0,
+0,3,6,9,12,12,7,1,9,5,14,10,13,13,16,5,11,10,12,14,14,10,9,8,8,7,7,14,7,15,14,5,14,12,14,10,10,14,2,2,16,14,10,11,16,16,1,11,14,13,
+10,4,16,16,4,6,3,9,15,13,10,16,1,3,6,4,8,8,16,7,7,4,5,4,6,2,11,12,8,7,13,5,2,14,13,13,1,8,3,8,10,11,15,1,6,5,6,10,6,0,
+0,2,2,14,4,11,15,6,9,1,8,16,16,13,11,9,5,1,4,10,4,11,13,8,3,10,10,7,5,3,12,4,10,14,3,11,15,10,8,4,13,7,2,2,8,14,6,7,3,7,
+6,8,5,16,8,16,14,4,14,7,4,4,8,11,13,14,2,8,7,10,15,3,14,12,15,14,5,1,12,1,13,16,12,4,14,12,12,4,14,3,6,13,11,10,15,3,14,5,4,0,
+0,8,16,7,13,7,5,9,14,4,10,13,12,2,4,16,11,3,8,12,16,11,11,12,16,13,2,12,3,14,4,9,13,1,7,14,6,11,3,11,6,4,16,12,5,16,6,16,11,6,
+15,6,12,7,5,16,7,1,1,11,9,7,11,5,11,15,10,10,2,10,1,3,3,12,6,12,15,4,9,10,9,1,12,5,15,2,13,1,2,1,11,1,12,9,16,7,10,4,9,0,
+0,8,2,2,3,15,9,1,2,13,11,7,12,13,5,12,3,14,11,7,9,5,4,7,10,11,11,15,3,11,1,6,6,7,10,9,10,7,15,7,10,3,14,5,7,12,9,12,5,4,
+6,11,3,15,10,8,6,12,13,4,1,14,1,16,13,10,7,3,6,5,6,5,11,10,5,16,13,4,14,16,16,2,15,6,9,3,6,3,1,12,5,11,4,1,1,6,14,12,16,0,
+0,9,1,3,6,12,12,3,4,12,8,13,11,4,13,6,7,7,9,16,13,1,6,3,8,7,4,15,14,7,12,15,12,6,16,3,9,2,12,7,2,1,16,13,15,4,9,1,16,1,
+6,13,8,1,7,7,6,3,3,6,10,16,5,14,2,4,15,8,1,6,7,2,4,14,12,4,12,14,3,8,1,15,8,3,1,14,12,14,2,16,8,9,16,16,11,10,12,15,9,0,
+0,13,9,7,15,1,3,4,15,1,4,1,6,10,5,11,5,5,12,7,9,7,13,11,10,3,7,5,13,7,4,4,6,14,14,16,1,15,9,16,11,16,6,5,11,16,4,14,4,13,
+9,12,6,1,12,6,9,8,2,1,3,12,4,11,10,1,8,13,16,4,10,10,1,14,5,16,2,1,13,7,6,12,15,3,7,6,3,15,11,2,14,5,3,13,13,6,9,14,7,0,
+0,4,6,3,14,13,9,10,1,3,8,12,16,15,3,3,2,10,11,8,1,15,14,6,5,8,15,8,7,1,15,10,15,5,15,7,10,1,1,12,1,1,10,6,15,3,11,2,15,5,
+4,9,16,9,15,3,7,5,14,10,6,12,2,6,4,5,6,12,4,6,6,4,11,4,13,16,8,1,4,2,12,16,1,12,6,16,10,13,3,16,3,3,13,13,16,9,15,3,16,0,
+0,16,12,16,15,14,7,11,4,2,12,2,4,6,13,3,11,11,1,16,11,5,4,16,8,12,5,12,13,14,8,6,8,12,7,16,12,12,14,4,1,8,15,13,3,1,1,13,3,10,
+11,8,1,12,16,2,9,14,9,10,8,5,16,4,10,11,4,5,16,15,7,4,12,1,12,10,2,6,9,5,10,3,6,4,14,15,14,9,9,7,14,6,10,7,8,4,3,16,12,0,
+0,7,6,13,11,9,10,13,14,15,10,3,6,4,4,16,14,14,12,13,5,10,16,12,5,14,5,7,1,7,9,16,3,13,5,2,15,15,7,1,16,5,6,9,10,16,5,15,10,7,
+13,2,12,3,6,10,4,13,2,8,3,14,3,11,8,8,4,9,8,3,5,13,15,16,13,11,15,4,10,12,16,12,15,14,3,10,7,9,12,7,4,8,8,4,11,15,10,7,13,0,
+0,10,16,8,7,11,9,5,5,8,5,16,12,10,12,4,2,3,1,12,10,3,6,14,3,6,9,11,8,4,2,3,2,4,8,2,4,6,15,14,1,9,4,14,6,8,6,16,13,0,
+0,9,10,2,12,13,4,7,9,13,12,16,11,7,1,13,6,6,8,3,5,16,4,6,16,2,16,2,6,8,3,14,5,6,13,12,1,10,11,8,13,14,3,8,3,5,4,14,0,0,
+0,0,3,7,1,5,15,2,15,11,6,7,6,13,7,2,4,16,1,2,9,10,4,2,4,16,15,2,6,9,2,1,15,11,16,12,16,15,9,4,12,11,15,9,4,8,5,12,0,0,
+0,0,10,9,1,14,10,3,5,2,10,16,12,11,5,13,15,15,1,9,4,3,4,13,16,9,1,3,6,1,11,4,8,12,4,7,6,6,10,15,15,13,12,2,16,11,6,0,0,0,
+0,0,0,2,9,7,5,12,5,9,15,12,12,8,5,11,11,9,4,7,2,3,16,2,8,4,1,3,6,12,2,6,11,1,6,12,10,15,2,8,11,3,14,3,10,16,11,0,0,0,
+0,0,0,15,14,1,7,10,1,15,10,12,16,5,3,12,8,13,10,5,11,1,8,2,11,14,10,3,13,6,14,9,11,11,6,9,14,1,6,7,3,2,4,15,11,6,0,0,0,0
+</data>
+ </layer>
+ <layer name="objects" width="50" height="200">
+ <data encoding="csv">
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,
+0,0,0,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,64,64,64,
+0,0,0,37,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,38,64,64,
+0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,38,64,64,
+0,0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,38,64,
+0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,38,64,
+0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,0,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,0,0,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,0,0,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,0,0,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,0,0,0,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,0,0,0,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,133,0,94,0,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,91,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,0,104,92,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,0,0,0,92,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,98,92,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,0,0,0,92,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,92,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,0,0,134,92,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,0,0,125,0,0,92,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,125,98,0,0,99,92,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,99,92,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,101,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,103,0,101,125,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,125,0,0,0,0,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,50,64,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,
+0,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,42,
+44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,0,
+0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,0,
+0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,0,0,
+0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,0,0,
+0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,0,0,0,
+0,0,0,40,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,39,0,0,0,
+0,0,0,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,0,0,0,0
+</data>
+ </layer>
+ <objectgroup name="area">
+ <object id="3" name="Insel des Wahns" type="graveyard" x="1329" y="1388" width="729" height="339"/>
+ </objectgroup>
+</map>
diff --git a/V3/Content/Maps/work_in_progress.tmx b/V3/Content/Maps/work_in_progress.tmx
new file mode 100644
index 0000000..d521185
--- /dev/null
+++ b/V3/Content/Maps/work_in_progress.tmx
@@ -0,0 +1,1258 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<map version="1.0" orientation="staggered" renderorder="right-down" width="101" height="201" tilewidth="64" tileheight="32" staggeraxis="y" staggerindex="odd" backgroundcolor="#000000" nextobjectid="40">
+ <properties>
+ <property name="gridsize" type="int" value="16"/>
+ </properties>
+ <tileset firstgid="1" name="grassland" tilewidth="64" tileheight="128" tilecount="128" columns="16">
+ <image source="../Textures/grassland.png" width="1024" height="1024"/>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100100"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100010"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="00000000000000000000001101100000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="00000000000000000000110001100000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="00000000000000000000010001000100"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="00000000000000000000000011110000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="00000000000000000000001000100010"/>
+ </properties>
+ </tile>
+ <tile id="48">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="49">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="50">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="51">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="52">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="53">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="54">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="55">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="56">
+ <properties>
+ <property name="collision" value="00000000000000000000011111110000"/>
+ </properties>
+ </tile>
+ <tile id="57">
+ <properties>
+ <property name="collision" value="00000000000000000000001000110010"/>
+ </properties>
+ </tile>
+ <tile id="58">
+ <properties>
+ <property name="collision" value="00000000000000000000010011000100"/>
+ </properties>
+ </tile>
+ <tile id="59">
+ <properties>
+ <property name="collision" value="00000000000000000000111011110000"/>
+ </properties>
+ </tile>
+ <tile id="60">
+ <properties>
+ <property name="collision" value="00000000000000000000001011110100"/>
+ </properties>
+ </tile>
+ <tile id="61">
+ <properties>
+ <property name="collision" value="00000000000000000000010011110010"/>
+ </properties>
+ </tile>
+ <tile id="75">
+ <properties>
+ <property name="collision" value="00000000000000001111111111111111"/>
+ </properties>
+ </tile>
+ <tile id="80">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="81">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="82">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="83">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="84">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="85">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="86">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="87">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100110"/>
+ </properties>
+ </tile>
+ <tile id="88">
+ <properties>
+ <property name="collision" value="00000000000000000000000101100000"/>
+ </properties>
+ </tile>
+ <tile id="89">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="90">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="91">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="92">
+ <properties>
+ <property name="collision" value="00000000000000000000000001110000"/>
+ </properties>
+ </tile>
+ <tile id="93">
+ <properties>
+ <property name="collision" value="00000000000000000000000000100000"/>
+ </properties>
+ </tile>
+ <tile id="94">
+ <properties>
+ <property name="collision" value="00000000000000000000000001000000"/>
+ </properties>
+ </tile>
+ <tile id="95">
+ <properties>
+ <property name="collision" value="00000000000000000000100001100000"/>
+ </properties>
+ </tile>
+ <tile id="112">
+ <properties>
+ <property name="collision" value="00000000000000000000000011100000"/>
+ </properties>
+ </tile>
+ <tile id="113">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="114">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="115">
+ <properties>
+ <property name="collision" value="00000000000000000000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="116">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="117">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="118">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="119">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="120">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="121">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="122">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="123">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="124">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="125">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="126">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ <tile id="127">
+ <properties>
+ <property name="collision" value="00000000000000000000011001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="129" name="grassland_trees" tilewidth="128" tileheight="256" tilecount="16" columns="8">
+ <tileoffset x="-32" y="0"/>
+ <image source="../Textures/grassland_trees.png" width="1024" height="512"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001100000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="145" name="grassland_water" tilewidth="64" tileheight="64" tilecount="64" columns="16">
+ <tileoffset x="0" y="32"/>
+ <image source="../Textures/grassland_water.png" width="1024" height="256"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="0010011000000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="0100011000000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="0110010000000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="0110001000000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="12">
+ <properties>
+ <property name="collision" value="0000011000000000"/>
+ </properties>
+ </tile>
+ <tile id="13">
+ <properties>
+ <property name="collision" value="0100010000000000"/>
+ </properties>
+ </tile>
+ <tile id="14">
+ <properties>
+ <property name="collision" value="1111000000000000"/>
+ </properties>
+ </tile>
+ <tile id="15">
+ <properties>
+ <property name="collision" value="0010001000000000"/>
+ </properties>
+ </tile>
+ <tile id="16">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="17">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="18">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="19">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="20">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="21">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="22">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="23">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="32">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="33">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="34">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="35">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="36">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="37">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="38">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="39">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="40">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="41">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="42">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="43">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="44">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="45">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="46">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ <tile id="47">
+ <properties>
+ <property name="collision" value="0110011000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="209" name="houses_rear" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-64" y="32"/>
+ <image source="../Textures/houses_rear.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000011000000000011110000000011111100000011111111000000111111000000001100000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="221" name="houses_front" tilewidth="192" tileheight="192" tilecount="12" columns="3">
+ <tileoffset x="-32" y="32"/>
+ <image source="../Textures/houses_front.png" width="576" height="768"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="3">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="4">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="5">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="6">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="7">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="8">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="9">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="10">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ <tile id="11">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000011000000001111000000111111000011111111000011111100000000110000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="233" name="castle" tilewidth="1008" tileheight="848" tilecount="3" columns="3">
+ <tileoffset x="-504" y="224"/>
+ <image source="../Textures/castle.png" width="3024" height="848"/>
+ <tile id="0">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="1">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ <tile id="2">
+ <properties>
+ <property name="collision" value="000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000111100000000000000000000000000000000000000000000000000000000011111111000000000000000000000000000000000000000000000000000001111111111110000000000000000000000000000000000000000000000000111111111111111100000000000000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000001111111111111111111111110000000000000000000000000000000000000111111111111111111111111111100000000000000000000000000000000011111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111110000000000000111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111110000000001111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000111111111111111111111111111111111111111111111111111111110000000011111111111111111111111111111111111111111111111111111110000000000011111111111111111111111111111111111111111111111111100000000000011111111111111111111111111111111111111111000000000000000000000011111111111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111000000000000000000000000011111111111111111111111111111111111111000000000000000000000000000111111111111111111111111111110111000000000000000000000000000000001111111111111111111111110001100000000000000000000000000000000000011111111111111111111000000000000000000000000000000000000000000000111100001111111100000000000000000000000000000000000000000000000011000001111111100000000000000000000000000000000000000000000000000000001111111100000000000000000000000000000000000000000000000000000000011110000000000000000000000000000"/>
+ </properties>
+ </tile>
+ </tileset>
+ <tileset firstgid="236" name="grassland" tilewidth="64" tileheight="128" tilecount="128" columns="16">
+ <image source="../Textures/grassland.png" width="1024" height="1024"/>
+ </tileset>
+ <layer name="floor" width="101" height="201" offsetx="-5" offsety="-3">
+ <data encoding="csv">
+177,177,177,189,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,76,76,0,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,63,63,63,63,63,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,76,76,76,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,63,63,63,63,63,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,76,76,76,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,63,63,63,63,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,177,177,151,152,177,177,177,177,177,177,76,0,1,1,1,1,1,1,1,1,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,63,63,63,63,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,177,147,155,4,152,177,177,177,177,177,76,76,0,1,1,1,1,1,1,1,76,0,0,76,76,76,76,76,76,76,76,76,76,76,76,1,1,63,63,63,0,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,147,4,4,4,152,177,177,177,177,177,76,0,1,1,1,1,1,1,1,1,76,0,76,76,0,76,76,76,76,76,76,76,76,76,1,1,1,63,63,63,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,177,147,4,4,4,4,168,177,177,177,177,76,76,76,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,63,63,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,151,4,4,4,4,149,177,177,177,177,177,76,76,76,76,1,1,1,1,1,1,1,76,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,63,63,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,166,4,4,4,4,156,177,177,177,155,148,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,1,1,63,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,154,4,4,4,4,164,177,177,154,1,148,76,76,76,1,1,1,1,1,1,1,1,1,1,1,0,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,0,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,177,177,177,177,166,4,4,4,4,145,177,177,162,1,1,155,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,0,1,1,1,1,1,1,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,177,167,177,177,177,146,4,4,4,149,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,21,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,177,167,151,164,177,177,177,150,4,4,145,177,177,155,155,1,153,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,147,159,160,177,177,177,177,150,153,149,177,177,154,1,1,149,154,1,1,1,1,1,1,1,255,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,177,147,1,1,148,177,177,177,177,165,165,177,177,162,1,1,145,166,1,1,1,1,1,1,1,250,1,1,1,1,1,1,1,76,0,76,76,76,76,76,1,1,1,1,248,260,2,253,1,1,1,1,1,1,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,147,1,1,1,155,177,177,177,177,177,177,177,177,154,1,149,177,154,1,1,1,1,1,1,1,238,255,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,2,2,1,266,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,177,147,1,1,1,1,156,0,177,177,177,177,177,177,155,1,145,177,166,157,1,1,1,1,1,1,242,1,1,254,253,1,1,1,1,76,76,76,76,76,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,21,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,147,1,1,1,1,1,159,155,152,177,177,177,177,158,1,156,177,177,161,154,1,1,1,1,1,1,246,1,256,1,266,1,1,1,1,76,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,21,21,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,177,147,1,1,1,1,1,1,1,1,152,177,177,177,162,1,1,152,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,21,21,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,147,1,1,1,1,1,1,1,1,1,168,177,177,177,154,1,1,152,177,177,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,248,260,2,253,1,1,1,1,1,1,1,21,21,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,162,1,1,1,1,1,1,1,1,1,156,177,177,177,162,1,1,1,152,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,63,1,1,266,263,1,266,1,1,1,1,1,1,1,21,21,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,154,1,1,1,1,1,1,1,1,1,168,177,177,177,154,1,153,1,155,155,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,21,1,1,1,1,1,1,1,1,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+177,151,1,1,241,242,243,1,1,1,1,149,177,177,177,162,1,156,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,21,21,1,1,1,1,1,1,1,1,21,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+151,1,1,1,245,266,247,1,1,1,149,177,177,177,177,154,1,168,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,64,1,21,1,1,1,1,1,1,1,1,21,21,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+166,153,1,1,249,267,1,1,1,1,149,177,177,177,177,151,1,156,167,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,63,1,1,21,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+165,150,1,1,237,238,1,1,1,1,152,177,177,177,162,1,1,159,168,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,21,1,1,1,1,1,1,21,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+183,183,150,1,241,255,1,242,243,1,1,168,177,177,177,146,1,1,156,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,21,1,1,1,1,1,21,21,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+183,162,1,1,245,255,1,266,247,1,149,177,177,177,177,150,1,1,159,1,1,153,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,21,1,1,1,1,21,21,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,177,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+183,183,154,1,249,250,1,267,1,1,1,177,177,177,177,192,150,1,1,1,1,149,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,21,1,1,1,21,21,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,177,177,76,76,76,76,76,76,76,76,76,76,76,76,76,76,
+183,162,1,1,237,238,255,238,1,1,145,177,177,177,192,177,150,1,1,1,149,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,21,1,1,21,21,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,76,76,76,76,177,177,177,1,1,1,1,1,76,76,76,76,76,76,76,1,1,76,
+183,183,154,1,241,242,1,255,1,1,149,177,177,177,177,177,177,154,1,1,156,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,177,177,177,177,1,1,1,1,76,76,1,1,76,76,76,1,1,1,
+183,147,1,1,1,1,245,255,1,1,148,177,177,177,177,177,166,1,1,1,168,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,21,21,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+183,147,1,1,1,1,249,250,1,1,1,148,177,177,177,177,177,150,1,1,1,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,21,21,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+162,1,1,1,1,1,237,238,255,1,1,164,177,151,155,177,177,150,1,1,152,177,151,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,164,177,151,1,4,4,4,1,4,1,4,4,4,4,1,1,4,1,
+177,150,1,1,1,1,241,242,1,1,1,149,177,151,1,160,177,192,8,1,1,152,151,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,145,177,151,1,4,4,4,4,4,4,4,4,4,4,4,4,1,1,
+177,150,157,1,1,1,1,1,1,1,145,177,166,1,1,148,177,151,1,1,1,159,1,1,1,1,1,1,1,1,250,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,145,177,151,4,1,4,4,4,4,4,4,4,4,4,4,4,4,1,1,
+177,177,165,158,1,1,1,1,1,1,145,177,177,146,1,1,152,162,1,1,1,1,1,1,1,1,1,1,1,1,238,255,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,149,177,166,4,1,4,4,4,4,4,4,4,4,4,4,4,4,4,1,
+177,177,147,1,1,1,1,1,1,149,177,177,147,1,1,1,152,146,1,1,1,1,1,1,1,1,1,1,1,1,242,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,
+177,177,162,1,1,1,1,1,1,149,177,177,162,1,1,1,157,168,146,1,1,1,1,1,1,1,1,1,1,1,246,1,256,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,
+177,177,154,1,1,1,157,1,1,177,177,177,146,1,1,160,165,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,63,76,76,76,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,148,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,162,153,1,1,149,146,1,149,177,177,177,1,1,1,148,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,151,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,165,1,1,145,192,146,193,177,177,177,147,1,1,1,159,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,146,1,149,192,192,161,197,177,177,162,1,1,1,1,160,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,166,1,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,146,145,177,177,177,177,201,177,177,158,1,1,1,1,164,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,165,177,177,177,177,177,201,177,147,1,1,1,1,145,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,205,147,1,1,1,1,156,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,177,195,1,1,1,1,1,168,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,21,21,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,1,177,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,147,1,1,1,1,1,160,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,21,21,1,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,156,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,162,1,1,1,1,1,1,152,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,146,1,251,248,249,250,1,164,177,158,1,1,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,177,177,146,260,254,253,261,1,160,177,147,1,8,157,153,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,177,177,177,177,155,177,154,263,253,266,242,1,148,162,153,153,149,165,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,21,21,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+177,177,177,177,177,159,159,159,177,154,160,147,1,1,1,1,1,1,148,161,165,165,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,21,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+176,177,177,177,158,1,1,160,151,1,159,1,1,1,1,1,1,1,164,177,177,177,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,248,260,254,253,1,1,1,21,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+176,176,177,177,167,1,1,1,159,1,1,1,1,1,1,1,1,1,156,177,177,167,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,266,263,1,266,1,1,1,1,21,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+64,176,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,164,177,151,152,167,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,21,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+0,176,176,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,177,151,1,159,152,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+1,176,159,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,164,166,1,1,1,152,166,1,157,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+0,176,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,154,1,1,1,168,154,145,146,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,166,1,1,1,156,166,149,177,146,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+0,1,1,1,1,1,241,242,243,240,1,1,1,1,1,1,1,1,1,168,154,1,1,1,168,165,177,177,154,1,248,260,254,253,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+1,1,1,1,1,1,245,266,247,244,251,248,249,250,1,1,1,1,160,162,153,1,1,145,177,177,177,151,1,1,266,263,1,266,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,177,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+0,1,1,1,1,1,249,267,1,248,260,254,253,261,1,1,1,1,1,168,161,146,1,149,177,177,177,162,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+1,1,1,1,1,1,237,238,1,266,263,1,266,242,1,1,1,1,156,177,177,146,145,177,177,177,177,146,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,1,1,1,1,1,241,255,1,1,1,1,1,1,1,1,1,1,1,168,163,177,165,177,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,4,4,32,28,24,4,4,4,4,4,4,1,4,4,4,4,4,
+76,1,1,1,1,1,245,255,1,1,1,1,1,1,1,1,1,1,160,151,155,177,177,177,163,163,177,177,194,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,146,4,4,4,23,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,0,1,1,1,1,249,250,1,1,1,1,1,1,1,1,1,1,1,159,1,156,177,177,155,155,148,177,198,146,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,146,4,23,24,32,4,4,4,4,4,4,4,4,4,4,4,4,
+76,1,1,1,1,1,237,238,255,1,1,1,1,1,1,1,1,1,1,1,1,152,163,150,1,1,152,200,177,150,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,150,4,4,4,32,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,1,1,1,1,241,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,155,152,150,1,1,196,177,177,150,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,194,23,32,28,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,1,1,1,1,245,246,1,256,242,243,240,241,242,243,240,241,1,243,240,241,242,159,240,241,242,148,177,177,150,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,200,194,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,1,1,1,1,246,247,244,245,246,247,244,261,1,247,244,245,246,247,244,245,246,247,152,177,177,150,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,202,200,194,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,250,251,248,249,250,251,248,249,250,251,248,1,1,1,248,249,250,251,248,249,250,251,148,177,177,150,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,63,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,202,202,200,146,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,76,1,1,238,239,236,237,238,239,236,237,238,239,236,1,1,1,1,1,1,1,255,1,1,239,1,148,177,177,150,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,200,202,202,178,158,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,76,1,1,242,243,240,241,242,243,240,241,242,243,240,1,1,1,1,1,1,243,255,1,1,243,1,148,177,177,150,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,196,200,202,178,147,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,76,76,1,246,247,244,245,266,247,244,245,246,247,244,1,1,1,1,1,1,1,250,1,1,1,1,1,148,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,196,200,177,162,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,76,1,1,250,251,248,249,267,1,248,267,250,251,248,1,1,1,1,1,1,1,238,255,1,1,1,1,148,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,196,177,177,146,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,76,1,1,238,239,236,237,238,1,266,237,238,239,236,237,262,239,236,1,1,1,242,1,1,1,1,1,1,152,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,146,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,242,243,240,241,255,1,1,241,242,243,240,241,242,1,1,1,1,243,246,1,256,1,1,1,1,152,177,177,154,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,146,4,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,267,247,244,245,255,1,1,245,246,247,244,263,1,247,244,1,1,1,244,245,246,1,1,1,1,1,168,177,166,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,150,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,1,1,1,1,250,251,248,249,250,1,1,249,250,251,248,1,1,1,1,1,1,248,260,254,253,251,1,1,1,156,177,177,154,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,146,4,4,4,4,4,4,4,4,4,4,4,4,
+76,76,0,1,1,1,238,239,236,237,238,255,1,1,255,239,236,237,238,239,236,1,1,266,263,1,266,1,1,1,1,1,152,177,166,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,146,4,4,4,4,4,4,4,4,4,4,4,
+76,76,1,1,1,1,242,243,240,241,242,1,1,253,242,243,240,241,242,243,1,1,1,1,1,1,1,243,1,1,1,1,168,177,150,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,177,146,4,4,4,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,246,247,244,245,246,1,256,1,246,247,257,245,262,247,244,245,246,247,244,245,1,247,1,1,1,1,156,177,177,150,246,1,256,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,177,177,177,177,146,4,157,4,4,4,4,4,4,4,4,
+76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,154,1,4,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,248,260,254,253,1,1,1,1,152,177,177,177,177,150,149,146,4,4,4,4,4,4,4,4,
+76,76,76,76,76,1,1,1,1,1,1,1,241,242,243,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,151,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,266,263,1,266,1,1,1,1,1,152,177,177,177,177,161,178,150,4,4,4,4,4,4,4,
+76,76,76,76,76,1,1,1,1,1,1,1,245,266,247,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,1,1,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,146,4,4,153,153,153,153,149,
+76,76,76,76,76,76,76,1,1,1,1,1,249,267,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,150,1,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,178,177,146,153,149,165,165,165,165,
+76,76,76,76,76,76,76,1,1,1,1,1,237,238,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,154,1,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,165,165,177,177,177,177,177,
+76,76,76,76,76,76,76,76,1,1,1,241,242,243,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,151,1,1,246,1,256,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,76,76,1,76,76,1,1,1,245,266,247,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,76,76,76,76,76,1,1,1,249,267,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,154,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,76,76,76,76,1,1,1,1,237,238,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,166,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,167,167,167,177,177,177,177,177,177,177,177,
+76,76,76,76,76,1,1,1,1,1,1,241,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,150,1,1,1,1,1,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,159,159,155,152,177,177,177,177,177,177,177,
+76,76,76,76,1,1,1,1,241,242,243,245,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,150,1,1,1,1,1,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,177,
+76,76,76,76,1,241,242,243,245,266,247,249,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,150,1,1,1,1,1,242,1,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,
+76,76,1,1,1,245,266,247,249,267,1,237,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,150,1,1,1,1,1,246,1,256,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,63,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,
+76,76,1,241,242,243,267,1,237,238,1,241,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,63,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,167,167,167,167,167,
+76,0,1,245,266,247,238,1,241,255,1,249,267,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,155,155,155,155,155,155,
+76,76,1,249,267,1,255,1,245,255,1,237,238,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,237,238,1,255,1,249,250,1,241,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,63,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,241,255,1,250,1,237,238,255,245,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,167,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,245,255,1,238,255,241,242,1,249,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,152,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,63,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,249,250,1,241,242,243,255,1,237,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,151,1,152,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,63,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,0,237,238,255,245,266,247,255,1,241,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,166,1,1,152,177,177,158,1,1,1,1,1,1,1,1,1,1,1,1,63,76,76,76,76,1,63,76,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,241,242,1,249,267,1,242,243,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,168,150,1,1,152,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,76,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,1,1,1,237,238,1,266,247,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,156,177,154,1,1,152,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,76,64,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,76,1,1,241,255,1,267,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,168,166,1,1,1,168,177,146,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,63,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,76,1,76,245,255,1,238,1,1,1,248,260,254,253,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,156,177,154,1,1,160,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,63,63,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,249,250,1,255,1,1,1,266,263,1,266,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,168,166,1,1,1,152,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,63,0,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,237,238,255,255,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,156,177,154,1,1,1,152,177,177,146,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,64,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,241,242,1,250,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,168,166,1,1,1,1,152,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,1,1,237,238,255,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,156,177,154,1,1,1,1,152,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,1,1,241,242,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,168,151,1,1,1,1,1,152,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,1,1,1,1,1,1,156,166,1,1,1,1,1,1,168,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,
+76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,168,154,1,1,1,1,1,156,177,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,4,1,1,1,1,1,1,1,
+76,76,1,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,156,166,1,1,1,1,1,1,152,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,4,1,1,1,1,1,1,1,1,
+76,76,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,168,154,1,1,1,1,1,1,152,177,177,177,146,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,4,1,1,1,1,1,1,
+76,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,156,166,1,1,1,1,1,1,1,152,177,177,177,146,1,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,168,154,1,1,1,1,1,1,1,152,177,177,177,150,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,156,166,1,1,1,1,1,1,1,1,152,177,177,177,146,153,1,1,1,1,1,1,250,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,168,154,1,1,1,1,1,1,1,1,152,177,177,177,165,150,1,1,1,1,1,238,255,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,
+0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,160,166,1,1,1,1,1,1,1,1,1,152,177,177,177,177,150,1,1,1,1,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,1,1,168,154,1,1,1,1,1,1,1,1,1,152,177,177,177,177,146,1,1,1,1,246,1,256,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,145,166,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,4,145,177,154,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,150,1,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,
+76,0,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,1,255,1,1,1,1,1,255,1,1,1,168,177,177,177,177,146,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,
+76,1,1,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,1,1,255,1,1,1,1,1,255,1,1,156,177,177,177,177,177,150,1,1,1,1,1,1,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,166,1,1,250,1,1,1,1,1,250,1,1,1,168,177,177,177,177,177,150,1,1,1,1,1,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,154,1,1,238,255,1,1,1,1,238,255,1,156,177,177,177,177,177,177,150,1,1,1,1,1,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,166,1,1,242,1,1,1,1,1,242,1,1,1,148,167,177,177,177,177,177,150,1,1,1,1,1,246,1,256,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,154,1,1,246,1,256,1,1,1,246,1,256,1,159,152,177,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,194,1,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,163,177,177,177,206,150,1,1,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,159,148,177,177,208,177,150,1,1,1,1,1,1,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,177,208,177,177,150,157,153,1,1,1,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,206,177,177,177,165,165,150,1,1,1,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,196,177,177,177,177,177,177,150,1,1,1,246,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,177,151,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,148,177,177,177,177,177,177,150,1,1,1,1,1,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,151,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,1,1,148,177,177,177,177,177,177,150,1,1,1,1,250,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,166,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,148,167,167,177,177,177,177,150,1,1,1,1,238,255,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,154,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,159,159,152,177,177,177,177,150,1,1,1,242,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,2,152,177,177,177,177,150,1,1,1,246,1,256,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,166,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,152,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,154,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,
+80,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,168,151,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,2,152,177,177,177,162,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,145,151,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,255,1,1,1,152,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,153,145,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,255,1,1,1,1,152,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,161,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,250,1,1,1,1,152,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,1,1,1,1,238,255,1,1,1,1,152,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,242,1,1,1,1,1,152,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,248,260,254,253,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,166,1,1,1,1,1,1,255,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,246,1,256,1,1,1,1,152,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,266,263,1,266,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,158,1,1,1,1,1,1,255,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,152,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,147,1,1,1,1,1,1,250,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,0,1,1,1,1,1,1,1,1,1,1,152,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,147,1,1,1,1,1,1,1,238,255,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,152,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,153,1,1,1,145,162,1,1,1,1,1,1,1,242,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,0,1,1,1,1,1,1,1,1,1,1,1,168,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,146,1,1,145,177,154,1,1,1,1,1,1,1,246,1,256,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,156,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,150,2,149,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,148,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,146,145,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,148,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+64,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,177,177,165,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,148,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,64,
+76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,168,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,153,149,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,161,177,177,177,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,148,177,177,177,177,177,177,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,164,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,152,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,
+76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,156,167,177,177,158,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,
+76,1,1,1,1,1,1,1,17,1,1,1,1,1,1,1,1,1,1,160,177,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,64,1,1,1,1,1,1,1,1,1,1,1,1,1,155,152,177,162,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,160,177,
+76,1,1,1,1,153,153,153,1,1,1,1,1,1,1,1,1,1,1,148,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,2,152,177,158,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,
+76,11,1,1,1,156,165,165,154,1,1,1,1,1,1,1,1,1,1,157,168,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,255,1,1,1,1,1,1,1,76,76,76,1,1,1,2,168,162,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,
+76,11,1,1,1,168,177,166,1,1,1,1,1,1,1,1,1,1,156,165,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,255,1,1,1,1,1,1,76,76,76,76,1,1,1,156,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,
+76,11,1,1,1,156,177,177,154,1,1,1,1,1,1,1,1,1,1,168,177,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,250,1,1,1,1,1,1,1,76,76,76,76,1,1,2,168,177,158,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,177,
+76,11,1,17,1,168,177,166,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,238,255,1,1,1,1,1,76,76,76,76,76,1,1,156,177,162,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,177,
+76,1,11,1,1,156,177,177,154,1,1,1,1,1,1,1,1,1,1,168,177,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,242,1,1,1,1,1,1,1,76,76,76,76,1,1,2,168,177,150,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,
+1,11,4,1,1,167,167,151,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,246,1,256,255,1,1,1,76,76,76,76,76,1,1,156,177,177,158,1,1,1,1,1,1,153,1,1,1,1,1,1,157,157,157,1,1,1,1,1,1,1,1,1,168,177,177,177,
+0,11,11,1,1,1,159,155,1,1,1,1,1,1,1,1,1,1,1,168,177,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,255,1,1,1,76,76,76,76,76,1,1,2,148,177,162,157,157,157,157,157,153,149,150,153,1,1,1,1,149,165,165,150,1,1,1,1,1,1,1,1,193,177,177,177,
+0,11,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,250,1,1,1,76,76,76,76,76,1,1,1,148,177,165,165,165,165,165,165,165,177,165,146,1,1,1,145,177,177,177,150,1,1,1,1,1,1,1,145,197,177,177,177,
+0,11,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,177,177,177,177,177,177,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,238,255,1,1,76,76,76,76,76,1,1,1,1,148,177,181,181,181,181,181,181,181,177,177,150,1,1,149,177,177,177,177,194,1,1,1,1,1,1,149,177,207,177,177,
+0,11,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,177,177,177,177,177,177,177,177,177,177,154,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,242,1,1,1,76,76,76,76,76,1,1,1,1,148,181,181,181,181,181,181,181,177,177,177,194,157,145,177,177,177,177,198,150,1,1,153,153,153,145,177,177,203,177,177,
+76,11,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,152,177,177,177,177,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,246,1,256,1,76,76,76,76,76,1,1,1,1,1,164,181,181,181,181,181,181,177,177,177,198,161,165,177,177,177,177,202,177,150,157,193,165,165,165,177,177,177,203,177,
+76,11,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,152,177,177,198,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,149,181,181,181,181,181,181,181,177,177,204,177,177,177,177,177,177,202,177,177,165,165,197,177,177,177,177,177,177,205,177,
+76,0,11,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,1,168,177,202,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,145,181,181,181,181,181,181,181,177,177,204,177,177,177,177,177,177,202,177,177,177,177,177,201,177,177,177,177,177,177,177,
+0,0,11,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,156,177,202,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,149,181,181,181,181,181,181,181,181,177,204,177,177,177,177,177,177,202,177,177,177,177,177,177,201,177,177,177,177,177,177,177,
+0,0,1,1,1,1,1,1,1,1,1,76,76,76,76,0,1,1,1,1,152,198,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,156,181,181,181,181,181,181,181,181,181,204,177,177,177,177,177,177,202,177,177,177,177,177,177,177,201,177,177,177,177,177,177,
+0,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,196,177,177,177,177,177,151,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,168,181,181,181,177,181,181,181,181,206,177,177,177,177,177,177,200,177,177,177,177,177,177,177,177,201,177,177,177,177,177,177,
+0,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,1,1,1,1,1,152,167,177,177,177,166,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,1,1,156,181,181,181,177,177,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,201,177,177,177,177,177,
+0,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,1,1,1,1,1,155,152,167,167,167,158,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,201,177,177,177,177,177,
+0,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,1,1,1,1,1,1,155,155,155,155,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,156,181,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,199,177,177,177,177,
+0,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,168,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+0,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,181,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,145,177,181,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,149,177,177,181,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,0,0,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,1,1,1,1,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,149,177,177,181,177,181,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,0,0,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,1,1,1,76,76,76,76,76,76,1,76,76,1,1,1,1,1,1,1,1,1,76,76,76,76,1,149,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,0,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,1,1,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,76,1,1,76,76,76,76,76,156,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,1,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,76,76,1,76,76,76,76,76,1,168,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,
+76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,1,1,1,1,1,1,1,1,1,1,1,1,1,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,156,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177,177
+</data>
+ </layer>
+ <layer name="objects" width="101" height="201">
+ <data encoding="csv">
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,45,64,64,49,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,63,64,63,64,63,64,64,64,64,64,45,64,64,64,64,64,64,64,45,64,64,64,64,64,64,64,64,64,64,64,64,64,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,64,64,64,37,34,45,41,45,45,64,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,63,63,63,63,63,63,41,45,45,64,33,38,64,64,64,64,64,64,33,38,64,64,64,64,64,64,64,64,64,64,64,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,174,64,38,64,64,37,64,53,53,49,49,34,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,63,63,63,63,63,37,53,53,38,37,64,38,64,64,64,64,64,33,64,38,64,64,64,64,64,64,64,64,64,64,64,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,173,64,64,38,64,44,64,64,111,64,64,64,38,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,63,63,63,64,64,33,64,137,64,53,64,64,38,64,64,64,64,37,64,64,38,41,41,41,64,64,64,64,64,64,63,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,111,109,108,64,64,172,64,64,64,46,64,36,64,64,64,64,64,64,42,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,45,63,63,63,64,37,64,64,64,64,64,127,64,38,64,64,64,37,64,64,209,53,49,53,46,64,64,64,64,64,63,63,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,116,111,109,64,64,64,64,64,64,39,64,64,36,64,64,64,138,64,50,63,64,64,41,64,64,64,64,64,64,64,64,64,64,64,33,38,63,63,64,44,64,125,109,64,64,64,64,137,38,64,64,33,137,64,64,64,129,64,35,64,64,64,64,63,63,63,63,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,113,64,64,64,64,64,64,39,64,64,63,36,64,59,64,111,64,34,45,64,37,38,64,64,64,64,64,64,64,64,64,64,33,64,34,63,63,64,56,64,64,64,127,64,64,64,64,38,41,37,64,64,64,64,86,64,35,64,64,64,64,64,63,63,63,63,64,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,64,64,54,64,64,64,63,52,64,60,64,64,58,53,34,37,64,34,64,64,64,64,64,64,64,64,64,33,125,64,38,63,64,33,64,64,109,104,64,64,64,64,64,53,53,64,111,88,64,64,64,50,64,64,64,64,64,63,63,63,63,64,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,109,108,64,64,64,64,64,38,64,64,63,37,64,64,64,64,57,64,64,53,64,64,42,64,64,64,64,64,64,64,64,33,103,64,64,34,63,48,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,34,64,64,64,64,63,0,64,63,64,64,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,134,61,109,64,64,64,64,64,64,142,38,41,45,33,64,111,64,64,64,64,64,106,64,134,54,63,64,64,64,64,64,64,64,33,125,64,125,64,34,41,52,127,64,64,64,64,64,227,64,64,64,64,64,64,64,64,64,64,64,38,64,64,64,64,0,0,63,64,64,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,53,49,44,64,64,64,64,87,64,64,106,64,111,64,46,64,64,64,64,64,64,64,37,64,64,64,109,64,49,49,64,64,64,64,125,64,64,64,64,144,64,64,64,64,108,64,64,64,209,34,64,64,64,63,0,0,64,64,64,0,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,115,111,64,64,64,64,64,64,0,116,64,38,36,138,64,64,64,64,64,111,64,104,64,35,64,64,64,64,64,64,64,44,125,64,125,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,88,64,64,64,34,64,64,64,0,63,64,64,64,0,0,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,129,64,64,64,64,64,64,64,64,64,64,0,0,0,0,109,64,42,52,64,64,64,64,64,64,64,104,64,54,64,64,64,64,64,64,64,64,56,64,64,64,109,64,64,64,109,64,125,64,64,64,64,64,64,64,64,215,64,64,108,64,64,64,64,209,38,64,64,64,0,64,64,64,64,0,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,64,39,37,111,64,58,111,64,104,103,104,64,64,42,64,64,64,64,64,64,64,37,64,125,64,64,64,0,64,109,64,64,64,64,227,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,45,45,45,56,64,64,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,39,37,64,104,57,64,111,104,64,64,104,64,54,64,64,64,64,64,64,64,44,64,103,64,64,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,86,53,49,49,34,64,64,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,129,345,346,347,64,64,64,64,64,64,64,64,64,113,0,0,0,0,64,44,64,64,64,64,111,111,64,64,104,64,64,42,64,64,64,64,64,64,64,56,64,64,64,0,0,64,64,64,64,64,64,64,64,64,64,64,143,64,64,61,64,64,64,64,64,111,64,86,64,64,64,64,38,64,64,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,116,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,42,52,64,64,64,64,64,111,64,64,111,54,43,45,64,64,64,64,64,64,33,137,64,64,0,64,64,125,64,137,64,64,64,144,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,129,64,38,64,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,346,64,64,64,64,64,64,64,64,109,0,0,0,0,54,44,64,64,106,64,64,103,106,64,104,64,46,37,38,64,64,64,64,64,33,64,64,64,0,0,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,144,64,64,64,64,111,64,64,64,64,64,108,64,34,64,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,345,347,64,64,64,64,64,64,109,0,0,0,0,114,34,36,64,104,64,103,64,64,64,103,104,50,33,64,38,41,45,45,0,33,64,92,64,109,0,64,125,64,125,64,64,227,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,209,64,64,64,38,64,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,345,108,346,64,64,64,64,64,0,0,0,0,0,0,113,34,43,56,104,64,64,64,111,103,64,64,53,64,64,53,49,53,46,48,64,64,92,64,64,64,64,64,64,64,64,64,64,64,64,64,64,218,64,64,64,64,64,64,215,64,64,111,87,64,64,64,64,64,138,64,38,63,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,108,113,64,64,64,64,64,0,0,0,0,0,0,64,34,37,104,104,111,64,111,64,64,106,64,111,103,64,64,64,54,64,40,64,64,0,64,64,125,64,125,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,113,64,0,0,83,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,64,64,53,64,64,104,64,103,64,64,64,64,64,64,64,64,64,64,42,64,40,64,0,0,64,103,64,103,64,64,64,64,64,64,64,64,215,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,39,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,129,0,221,64,64,64,132,64,64,64,64,64,64,64,0,0,0,0,109,109,64,64,64,64,64,106,64,64,104,104,111,64,64,64,64,64,114,54,64,64,40,64,64,64,64,64,125,64,64,64,144,64,64,64,64,64,64,64,64,64,64,143,64,64,64,64,64,64,64,64,64,64,64,111,64,39,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,108,0,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,109,64,64,64,64,103,64,64,64,104,104,64,111,64,64,64,138,64,64,46,64,64,56,64,64,92,64,64,64,64,227,64,64,64,64,64,64,64,64,144,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,129,39,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,64,64,109,64,64,64,64,111,64,104,104,103,142,111,64,64,106,64,54,64,64,37,64,64,64,92,64,64,64,64,64,64,64,64,64,64,64,83,64,64,64,64,64,64,64,64,144,64,64,64,64,114,64,64,111,50,64,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,0,64,64,87,64,64,64,64,64,64,64,64,0,0,0,0,116,0,0,140,109,64,64,111,104,64,104,111,104,64,64,104,64,64,64,64,38,64,44,64,64,64,64,92,64,64,64,64,64,64,64,64,143,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,38,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,140,0,0,0,0,0,0,0,109,103,111,104,64,111,64,111,64,64,104,121,64,64,64,64,38,64,40,64,64,64,64,92,64,64,85,64,64,64,64,64,64,64,64,106,64,64,218,64,64,64,64,64,64,215,64,64,64,111,64,64,64,38,64,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,64,103,64,64,104,111,64,111,64,64,64,64,64,64,64,64,64,42,64,40,64,106,64,64,92,227,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,121,64,64,42,64,64,64,64,64,64,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,4,
+64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,0,0,0,0,111,64,64,64,64,104,64,111,103,104,64,64,104,64,64,64,39,64,64,56,64,64,64,64,64,64,64,64,64,64,218,64,64,64,64,64,64,215,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,64,64,64,64,64,64,33,53,49,53,49,49,53,49,53,49,53,49,53,49,53,38,4,
+64,64,64,345,108,121,64,0,64,64,108,64,64,64,64,64,64,64,111,64,0,0,0,0,64,64,64,64,64,64,106,111,103,64,64,114,104,64,104,138,54,64,64,44,64,116,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,143,64,64,64,64,64,64,64,64,64,64,34,64,64,64,64,64,33,64,0,4,4,4,4,4,4,4,4,4,4,4,4,4,38,
+64,64,64,108,64,0,64,345,347,64,64,64,64,64,64,64,64,111,64,64,0,0,0,0,106,64,104,64,64,64,103,111,64,64,64,64,104,64,104,64,42,64,64,56,64,64,64,64,64,64,64,64,64,215,64,64,64,64,64,64,64,64,85,144,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,64,64,64,64,33,100,64,0,64,116,64,64,64,64,64,64,64,64,64,64,64,46,
+64,64,64,64,64,64,64,345,64,346,64,64,64,64,64,64,64,64,64,64,160,0,0,0,64,64,104,64,64,104,64,111,64,104,64,64,64,64,111,64,54,64,64,44,64,64,64,144,64,64,64,64,64,64,64,64,64,64,64,143,64,64,85,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,34,45,45,45,33,64,64,64,0,111,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,113,64,64,64,64,64,132,64,64,64,64,64,64,64,64,64,64,0,0,0,0,64,111,64,104,64,64,64,64,111,104,64,64,64,64,111,64,42,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,53,53,53,53,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,
+64,64,64,64,64,64,64,64,113,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,64,64,64,104,64,106,64,104,64,104,64,64,64,64,64,116,54,64,64,44,64,64,64,64,64,64,64,64,64,64,64,144,64,64,64,64,64,64,64,64,64,64,218,64,64,64,230,64,64,64,64,111,64,64,64,64,64,64,64,64,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,64,1,64,108,64,64,64,64,64,64,129,115,64,64,0,0,141,0,0,0,111,0,103,64,64,64,64,64,64,64,64,64,64,103,64,64,64,42,64,64,56,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,138,64,64,64,64,64,64,64,64,64,64,64,64,138,64,64,64,64,64,64,64,64,64,64,64,64,64,46,
+64,64,64,64,345,347,64,64,64,64,64,64,64,64,64,109,64,0,0,0,0,0,0,0,0,64,64,142,64,64,64,64,64,64,64,64,64,64,64,64,54,64,64,44,64,64,64,64,64,64,64,64,64,64,64,106,64,64,218,64,64,64,64,64,64,215,64,64,64,64,64,64,64,64,64,64,108,114,64,64,64,64,64,64,64,111,64,108,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,108,345,64,346,64,64,64,64,64,64,64,64,109,64,0,0,0,0,0,0,0,0,104,64,64,64,106,64,64,103,106,64,64,64,64,64,64,42,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,114,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,
+64,64,64,64,64,64,64,64,64,64,64,64,64,109,118,114,64,0,0,0,0,0,0,0,0,64,104,64,64,64,103,64,64,106,64,142,64,116,64,64,39,64,64,44,64,64,64,64,64,64,218,64,64,143,64,64,64,215,64,64,64,64,64,64,64,64,64,64,230,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,64,132,64,64,64,64,64,64,64,64,115,109,64,64,0,111,0,0,0,134,90,0,64,64,103,104,64,64,64,64,64,64,64,64,64,64,39,64,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,103,64,0,0,0,0,0,0,89,0,209,64,64,64,64,64,64,64,64,104,64,64,64,64,54,64,64,64,44,64,64,212,64,64,64,64,144,64,64,64,64,64,64,64,64,64,64,64,143,64,64,64,64,64,64,64,64,108,64,64,121,64,64,64,98,64,64,100,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,89,0,0,64,92,64,64,104,111,104,64,64,64,64,111,64,64,42,64,64,64,40,64,64,64,64,64,115,64,64,64,64,64,64,64,82,144,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,46,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,116,0,64,82,92,64,64,64,104,64,111,64,64,111,64,138,54,64,64,64,64,40,64,64,85,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,64,111,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,90,0,0,0,64,64,112,111,64,64,64,111,64,64,64,103,64,64,38,64,64,64,64,56,64,88,64,64,64,64,64,64,64,64,64,64,64,64,64,64,218,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,
+64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,0,0,0,0,89,0,111,0,64,64,64,111,64,64,64,64,64,114,64,64,103,64,64,38,64,64,64,44,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,230,64,64,64,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,212,0,111,0,0,112,64,64,64,64,106,64,64,121,64,134,64,64,111,64,42,64,64,64,56,64,64,64,64,106,64,64,64,218,64,64,64,64,64,64,215,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,42,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,209,64,0,0,0,0,0,0,0,112,64,64,64,64,92,64,64,142,64,64,64,64,64,64,64,35,64,64,64,37,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,224,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,0,0,0,0,86,0,0,0,64,64,115,88,92,64,64,64,64,106,64,64,64,64,54,64,64,64,37,64,64,64,64,64,64,109,64,64,64,144,64,64,143,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,138,64,64,100,100,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,46,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,86,0,113,0,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,46,64,64,44,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,144,64,64,98,64,64,64,64,64,64,64,64,90,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,0,0,0,122,64,64,64,64,90,104,64,64,64,64,64,64,64,64,54,41,45,64,40,64,64,64,64,64,64,64,109,116,64,64,64,64,64,64,64,64,64,64,230,64,64,64,64,64,64,111,64,64,64,64,64,89,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,46,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,0,0,0,143,0,0,0,0,64,64,64,64,89,64,104,64,64,64,64,64,64,64,103,53,53,46,64,40,116,64,64,64,64,64,138,64,64,64,122,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,0,0,0,0,0,0,0,0,64,212,64,89,64,64,111,64,111,64,64,64,64,64,64,64,39,64,45,56,64,64,64,64,64,64,64,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,89,64,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,233,64,0,0,64,64,64,64,42,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,0,0,0,0,0,0,0,0,64,64,64,64,64,64,111,64,64,111,64,64,64,138,64,64,39,64,48,53,64,64,64,212,64,64,64,109,64,64,64,64,218,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,90,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,64,64,64,64,54,
+64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,0,0,0,0,0,0,0,0,64,64,64,64,64,114,111,64,64,64,64,129,64,64,64,39,64,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,230,64,64,64,64,64,64,64,64,64,64,64,64,93,105,64,64,64,105,64,64,64,64,138,64,64,64,64,64,64,64,64,0,64,0,64,64,64,64,42,
+35,36,64,64,64,64,64,64,64,64,64,113,64,64,64,64,64,0,0,0,0,0,0,0,0,64,64,64,64,64,64,111,129,64,64,64,64,64,111,64,54,64,64,64,56,64,64,64,64,122,64,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,0,64,64,64,64,54,
+64,36,64,64,64,64,109,132,64,129,64,64,64,64,64,64,64,108,0,0,0,0,0,0,0,64,64,64,112,64,64,111,64,64,64,103,106,64,111,64,42,64,64,44,64,121,64,64,86,104,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,224,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,0,64,64,64,46,
+64,64,36,64,64,221,64,70,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,112,64,64,111,64,104,64,64,64,111,64,64,39,64,64,64,56,64,64,64,64,64,104,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,224,64,64,111,64,108,64,64,64,64,64,64,108,64,111,64,64,64,64,64,64,0,64,64,0,64,64,64,54,
+64,64,52,108,64,64,70,64,0,209,64,108,64,113,64,224,64,64,64,64,113,64,109,64,64,64,64,64,64,143,64,111,64,104,142,64,64,104,116,39,64,64,64,48,64,64,64,64,64,64,64,64,64,109,64,64,64,64,144,64,144,64,64,106,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,115,64,64,64,64,64,64,64,0,64,64,0,64,64,64,46,
+64,64,37,108,108,64,64,64,64,0,64,64,64,64,64,64,64,64,64,64,64,109,109,116,64,143,64,64,64,64,64,106,121,104,64,64,64,64,64,39,64,64,64,64,56,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,109,106,64,121,64,64,64,64,64,64,64,64,111,111,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,0,64,64,64,0,64,108,64,54,
+41,37,95,108,89,64,64,64,64,64,64,64,64,64,64,64,108,92,64,64,109,134,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,50,64,64,64,64,48,64,106,109,109,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,64,0,64,116,64,64,46,
+44,53,64,96,89,64,64,87,64,64,84,64,64,64,64,89,108,108,64,64,64,109,115,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,46,64,64,64,64,56,106,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,99,64,64,111,64,64,64,64,64,64,0,64,64,0,64,64,64,64,54,
+52,64,64,89,64,64,83,64,64,64,63,64,64,64,89,108,108,121,64,64,64,64,109,64,64,64,64,64,141,64,64,64,64,111,64,64,64,64,50,64,64,64,64,48,64,106,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,143,64,138,64,64,64,64,64,64,119,64,64,64,64,64,64,64,0,64,0,64,64,64,64,64,46,
+44,64,64,64,64,64,64,64,64,64,63,64,64,64,64,92,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,106,46,64,64,64,64,56,64,106,64,64,64,64,64,212,64,64,64,64,212,64,64,64,64,64,64,115,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,0,0,64,0,64,64,64,64,54,
+36,64,64,64,64,64,64,64,64,63,63,64,64,64,64,92,93,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,129,64,64,50,64,64,64,64,48,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,106,64,64,98,64,64,111,64,114,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,0,64,116,64,64,64,64,64,46,
+64,36,130,115,64,64,64,64,64,63,63,64,64,64,64,64,92,64,64,64,64,64,64,64,64,64,64,64,64,64,69,64,64,64,64,103,64,64,64,46,64,64,64,45,56,64,138,64,64,64,64,64,64,83,64,64,64,64,64,64,64,98,106,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,108,64,64,99,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,138,54,
+64,52,64,64,64,64,64,64,64,346,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,50,64,64,64,48,53,64,64,106,64,64,64,111,64,64,64,64,64,84,82,64,64,64,64,64,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,98,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,46,
+64,44,64,64,64,212,67,64,64,345,347,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,138,64,46,64,64,64,40,64,116,64,106,92,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,36,64,86,64,64,67,64,64,116,64,346,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,129,64,64,116,64,64,64,54,45,64,64,64,40,64,116,64,106,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,39,56,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,46,
+64,64,40,64,64,64,73,108,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,124,64,64,64,64,64,64,64,64,64,64,64,64,53,46,64,64,45,52,64,64,64,64,92,64,64,64,64,64,64,64,116,64,64,64,64,64,39,44,64,64,64,64,64,64,64,64,64,64,98,64,64,111,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,108,54,
+64,64,40,64,64,67,64,108,64,64,64,64,64,64,64,64,64,64,115,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,39,64,64,37,53,64,64,64,64,64,115,64,64,109,64,64,64,64,116,64,64,143,64,39,64,40,56,64,64,64,64,64,64,138,64,64,64,64,64,143,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,138,64,64,64,46,
+64,64,64,40,64,64,88,64,64,109,64,93,64,209,134,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,104,111,64,64,39,64,64,37,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,39,64,64,48,64,64,64,64,64,64,64,138,64,64,105,64,64,111,64,64,64,99,64,64,64,64,64,119,64,64,64,64,64,111,115,64,64,64,54,
+64,64,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,138,64,64,111,64,39,64,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,54,64,64,64,56,138,64,64,64,143,64,64,64,64,64,105,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,138,64,111,64,64,64,64,64,46,
+64,64,64,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,50,45,64,64,56,64,64,64,64,109,109,64,64,64,64,64,121,64,64,64,64,121,64,109,42,64,64,48,64,64,116,64,64,64,64,64,64,64,105,64,115,111,64,64,64,64,64,64,64,64,111,116,64,64,64,64,111,64,64,64,64,64,54,
+64,64,64,64,56,64,66,64,64,64,64,64,64,64,114,64,64,108,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,135,64,53,34,64,37,64,64,64,121,64,64,64,109,64,92,64,64,64,64,64,64,64,64,64,54,64,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,124,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,46,
+64,64,64,64,44,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,37,64,64,64,64,64,64,64,106,64,64,92,64,64,64,64,64,64,64,64,64,42,64,64,37,64,64,108,64,64,64,108,64,131,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,54,
+64,64,64,64,56,66,66,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,103,64,64,64,64,53,64,64,64,64,64,64,64,64,64,64,64,92,64,64,64,138,64,93,64,64,54,64,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,111,64,64,64,46,
+64,64,64,64,37,64,66,66,109,108,64,64,64,64,116,346,64,64,64,64,64,64,140,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,143,64,64,64,111,96,64,64,64,64,94,64,64,54,46,64,64,56,64,64,64,64,64,64,64,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,115,111,64,64,64,54,
+64,64,64,33,116,64,66,64,64,64,108,64,64,64,64,345,347,64,64,140,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,89,64,106,64,38,64,64,48,64,64,64,64,64,111,64,116,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,64,138,64,64,64,64,64,64,64,64,64,46,
+64,64,64,37,64,64,64,64,64,64,64,64,64,64,64,345,64,346,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,121,64,64,103,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,115,64,38,64,64,36,64,64,64,64,64,111,64,64,64,64,105,64,64,64,64,64,116,64,64,99,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,33,64,64,64,212,64,66,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,140,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,115,64,42,64,64,36,64,64,108,64,64,64,64,64,105,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,138,64,46,
+64,64,44,64,63,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,116,64,64,64,64,64,64,64,141,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,115,64,64,64,116,64,64,64,106,64,64,64,64,39,64,64,64,52,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,54,
+64,64,36,129,64,108,64,64,66,64,66,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,39,64,64,64,37,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,64,64,64,64,64,111,64,64,64,64,64,46,
+64,64,64,36,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,129,64,64,64,129,64,139,64,64,64,64,64,64,64,64,64,64,64,143,64,64,64,64,64,39,64,64,64,37,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,
+64,64,64,43,40,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,139,116,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,39,64,64,64,44,64,64,64,64,64,64,135,111,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,53,
+64,64,64,64,64,40,64,64,64,0,64,64,64,64,109,64,115,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,54,64,64,64,64,56,64,64,64,64,64,64,111,64,64,64,64,64,64,115,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,63,52,64,64,0,0,64,64,64,141,64,64,64,64,64,64,64,64,64,64,64,143,64,64,64,114,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,46,64,64,64,37,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,44,35,52,64,0,64,64,64,64,64,64,140,64,64,64,64,64,64,64,64,121,346,64,64,64,64,64,64,64,64,64,106,103,64,64,64,64,104,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,131,54,64,64,64,37,64,121,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,43,44,64,64,0,108,64,64,64,64,64,64,121,64,64,64,64,64,132,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,42,64,64,44,64,64,64,64,138,64,64,64,111,64,64,64,64,64,64,64,64,64,64,115,64,64,64,64,99,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,40,64,64,64,64,64,64,108,64,346,64,64,136,64,64,133,64,64,64,64,64,64,64,140,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,131,64,64,64,64,64,108,64,64,64,64,64,64,54,64,64,64,40,64,64,64,64,64,64,64,115,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,40,64,64,64,64,64,64,108,64,64,64,64,64,335,64,332,64,347,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,106,64,64,64,64,108,138,64,64,108,64,64,64,64,64,64,38,64,64,64,40,64,64,64,64,64,64,64,111,64,105,116,64,64,64,64,64,98,64,108,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,64,40,55,64,64,64,64,64,64,64,64,64,140,64,346,64,64,344,64,64,64,129,64,64,64,64,64,64,64,64,111,64,64,111,111,64,106,64,64,64,64,64,64,64,64,64,108,64,106,64,64,64,64,64,38,64,64,64,56,64,64,64,64,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,100,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,63,47,40,64,64,64,64,64,64,64,64,64,64,64,64,64,339,64,64,64,64,344,0,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,46,64,64,37,64,64,64,64,64,108,108,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,100,64,
+64,64,64,64,64,45,45,45,45,63,40,64,64,64,347,347,0,347,64,64,142,64,336,64,346,64,64,346,347,64,140,64,64,137,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,143,64,64,104,64,64,35,64,64,33,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,111,64,111,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,37,53,53,53,38,64,56,64,64,64,64,64,0,340,64,64,64,64,64,64,64,64,64,64,133,342,64,64,64,64,64,64,64,64,64,139,64,108,64,64,64,123,64,64,64,64,64,64,64,64,64,64,64,64,64,64,35,64,64,37,64,64,64,64,64,116,64,64,64,64,64,64,64,108,64,64,64,64,111,64,64,111,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,37,64,106,64,64,38,37,64,64,347,64,333,64,0,342,347,64,64,345,137,64,64,64,64,0,64,64,64,344,345,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,35,64,64,33,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,111,64,109,64,64,64,64,64,64,64,64,64,100,64,64,64,
+64,64,64,33,64,126,64,109,136,53,64,64,64,64,64,64,64,64,347,0,64,140,64,64,0,64,64,0,64,64,64,64,64,64,66,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,35,64,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,98,64,64,109,64,64,64,64,64,64,64,64,64,64,64,0,
+64,63,41,33,126,109,64,126,64,64,136,64,142,64,64,116,64,64,64,346,346,0,64,64,0,334,135,140,64,64,64,132,64,64,175,66,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,35,64,64,64,40,64,64,64,64,64,64,64,105,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,109,64,64,64,64,115,64,64,64,64,64,64,64,
+64,48,53,64,106,64,64,64,64,109,64,64,64,64,64,64,64,64,64,142,64,0,64,64,0,64,64,64,64,64,64,64,64,64,66,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,36,64,50,64,64,64,64,52,64,131,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,116,64,64,105,64,64,64,109,64,105,64,64,64,64,64,64,64,64,64,0,
+64,63,36,136,109,64,128,64,128,64,64,64,64,64,64,64,64,133,64,64,0,0,64,136,64,0,64,64,64,129,64,64,64,64,66,66,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,46,36,64,38,64,64,64,37,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,121,64,64,64,64,64,64,64,64,50,
+64,63,36,64,126,64,64,64,104,64,64,64,64,114,64,345,64,64,64,64,0,64,64,64,65,64,133,64,335,64,64,64,64,64,66,175,64,64,64,64,64,64,64,104,64,64,64,143,64,64,64,64,111,64,64,35,64,36,64,38,64,64,37,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,105,64,98,64,64,64,64,64,64,64,64,64,64,42,
+64,64,64,52,64,104,64,64,64,64,64,64,140,64,64,344,137,64,345,141,64,64,139,64,65,65,64,337,64,0,64,64,64,64,66,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,111,64,64,35,64,64,36,64,42,64,33,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,115,64,64,64,64,105,64,64,64,64,64,64,50,
+64,64,44,64,64,64,106,109,64,64,64,64,64,64,64,64,333,64,64,64,0,64,65,64,65,65,64,64,64,64,64,343,346,64,64,64,64,64,64,64,64,108,64,64,64,106,111,64,64,64,64,64,64,39,43,64,64,64,52,35,64,37,64,64,64,64,64,64,104,64,64,64,64,64,108,64,64,64,108,64,98,64,64,64,133,64,64,64,64,64,64,64,105,64,64,64,64,64,115,64,42,
+64,64,64,56,128,109,64,64,64,64,64,64,64,64,64,64,64,64,347,141,0,64,65,344,65,65,65,64,64,64,64,64,347,64,175,64,64,64,64,64,139,64,64,64,64,64,111,64,64,64,64,64,64,39,64,64,64,64,48,50,64,44,64,64,121,64,64,143,64,104,64,64,64,64,108,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,109,64,109,50,
+64,64,44,64,64,126,64,64,64,64,64,64,64,64,137,64,64,345,347,64,64,64,65,345,65,64,65,0,131,144,116,142,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,111,64,54,64,64,64,63,64,52,42,64,40,64,64,64,64,64,64,64,104,64,64,64,64,64,108,64,64,64,64,109,64,64,109,64,64,64,64,64,98,105,64,64,64,64,64,64,64,64,109,42,
+64,64,64,36,64,64,64,64,64,64,64,64,64,121,64,64,140,64,64,64,64,65,65,54,43,43,56,65,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,108,64,64,64,64,46,64,64,63,63,37,39,64,64,40,64,63,64,64,64,64,104,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,50,
+64,64,64,36,64,63,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,65,65,42,65,44,64,65,347,340,64,64,64,64,64,64,98,64,64,64,64,64,64,64,64,111,64,108,64,64,64,64,54,64,64,64,63,33,39,64,64,64,52,63,63,63,63,63,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,98,64,64,64,109,42,
+64,64,64,64,36,63,64,64,64,64,64,64,64,64,0,64,116,64,133,64,64,65,65,39,64,64,40,65,64,64,64,64,64,64,64,64,133,64,64,64,64,64,64,111,64,111,64,64,64,64,64,64,64,46,64,64,63,37,54,64,64,64,33,63,63,63,63,63,63,64,64,64,64,111,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,50,
+64,64,64,64,47,47,40,64,64,64,64,64,64,64,0,64,64,64,64,64,143,65,39,64,65,64,40,65,141,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,139,64,64,64,64,64,139,64,54,64,64,64,37,64,42,64,64,33,63,63,1,63,63,63,64,64,64,64,64,64,111,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,139,64,109,42,
+64,64,64,64,64,64,64,40,64,64,64,140,64,64,0,64,64,64,64,64,64,39,43,64,65,64,65,56,64,64,64,114,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,38,64,64,48,64,54,64,64,37,63,63,1,1,63,63,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,50,
+64,64,64,64,64,64,64,56,64,64,64,64,64,64,0,64,64,64,132,64,39,65,65,64,65,65,44,121,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,64,64,52,64,38,64,48,64,63,63,35,1,63,63,64,64,64,64,64,64,64,64,105,64,64,64,39,43,43,43,43,40,64,39,43,40,64,64,64,64,64,109,64,64,64,64,42,
+64,64,64,64,64,64,64,44,64,64,64,346,64,64,0,64,0,133,64,64,54,65,65,64,65,65,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,64,33,64,64,38,41,56,63,63,35,48,1,63,63,64,64,116,64,64,64,64,64,64,64,64,39,64,64,64,64,64,40,39,64,64,40,64,64,139,116,109,64,64,64,64,50,
+64,64,64,64,64,64,41,56,64,64,64,64,347,347,0,335,0,332,64,347,42,65,64,64,65,65,41,56,64,64,64,137,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,131,64,111,64,64,64,111,38,37,64,64,64,53,53,64,63,35,64,36,1,63,64,64,64,64,64,64,64,116,64,64,64,39,64,64,64,64,64,64,43,64,64,64,43,40,64,64,64,109,64,64,64,64,42,
+64,64,64,64,64,64,37,53,64,64,64,64,64,64,0,64,0,64,64,344,39,65,65,65,65,65,37,53,64,64,64,64,64,64,64,64,98,64,210,64,64,64,64,64,64,64,64,111,64,64,111,64,64,111,64,49,64,64,64,64,64,64,63,50,41,64,36,63,63,143,64,64,64,133,64,64,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,64,40,64,64,64,64,98,64,64,50,
+64,64,64,64,64,44,64,140,64,64,64,347,64,64,0,64,64,0,339,39,0,65,65,65,65,44,64,64,64,64,344,345,64,64,64,64,64,64,64,64,64,64,64,64,64,111,111,111,111,64,64,64,64,111,64,64,64,64,64,64,64,63,63,49,42,64,36,63,64,64,64,64,64,64,105,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,64,64,40,64,64,64,64,64,64,64,42,
+64,64,64,64,64,64,40,64,64,64,64,129,347,64,0,142,347,0,64,54,41,65,65,65,65,65,36,64,64,64,64,64,345,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,111,64,64,64,64,64,64,64,104,63,64,39,64,64,36,63,64,64,64,64,64,64,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,64,64,64,56,64,105,64,64,64,64,50,
+64,64,64,64,64,64,52,346,64,64,136,64,340,133,0,64,64,346,64,53,38,65,65,65,65,65,36,64,64,64,64,64,347,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,63,63,39,64,64,64,56,64,64,64,64,64,64,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,64,64,64,44,64,64,64,64,64,64,64,42,
+64,64,64,45,64,64,44,64,64,64,333,335,64,332,0,347,64,345,64,64,64,42,65,65,65,64,65,36,64,140,64,64,64,64,64,64,64,64,64,122,64,64,64,64,64,64,64,64,64,64,64,64,105,64,64,64,64,108,64,64,64,64,63,39,64,64,64,48,64,64,64,64,64,64,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,56,64,98,64,64,139,64,50,
+64,64,33,38,45,41,56,64,64,64,64,64,346,64,0,344,64,64,137,0,35,65,65,65,64,64,41,56,116,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,108,64,131,64,63,54,64,64,64,64,56,64,64,64,64,64,64,64,64,54,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,33,64,64,98,64,64,64,64,42,
+64,64,33,64,53,53,53,64,64,64,64,64,64,345,0,346,64,64,64,64,35,64,65,65,64,64,37,53,64,64,64,64,138,347,64,64,111,121,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,63,38,64,64,64,37,64,64,64,64,64,64,64,64,64,42,64,64,64,64,64,64,64,64,64,64,64,64,64,64,37,64,64,64,64,64,64,64,50,
+64,33,64,64,64,64,64,129,347,64,64,142,64,336,142,346,345,64,64,54,64,65,65,65,64,33,65,345,64,64,64,64,64,64,64,64,111,121,105,98,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,63,38,63,64,37,63,64,64,64,64,64,64,64,64,54,64,64,64,64,64,64,64,64,64,64,64,64,64,64,33,64,64,64,64,64,64,64,64,42,
+64,37,64,59,64,64,64,64,340,64,64,64,133,64,0,64,64,64,136,133,38,64,64,64,64,37,65,0,64,343,346,64,64,64,64,64,111,64,64,105,64,133,64,64,64,64,64,64,64,64,64,121,64,64,64,64,108,108,64,64,64,64,63,64,38,63,37,64,63,64,64,64,64,64,64,64,64,38,64,64,64,64,64,64,64,64,64,64,64,64,64,33,64,64,64,64,64,64,64,64,50,
+44,64,64,60,64,64,333,64,64,342,64,64,64,345,64,64,347,65,64,64,34,64,64,335,33,64,64,64,64,64,347,64,345,64,64,64,111,64,64,111,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,63,64,38,37,64,63,64,64,64,64,64,64,109,64,64,38,64,64,64,64,64,64,45,64,64,64,41,64,37,64,64,64,64,64,64,64,64,64,42,
+64,36,64,64,64,64,64,64,64,64,64,137,137,64,141,64,64,64,65,64,64,38,64,64,33,64,64,64,64,64,64,64,64,64,64,64,64,111,64,111,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,108,64,63,64,63,53,64,63,64,64,64,139,64,64,109,109,64,64,46,64,64,64,64,64,37,38,64,64,33,34,33,64,64,64,64,64,39,47,36,64,50,
+64,36,122,64,87,64,64,64,64,64,64,346,0,64,64,342,334,65,64,64,64,34,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,111,111,111,64,105,64,64,64,64,64,64,64,64,64,64,143,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,45,45,41,41,41,37,64,38,64,37,64,53,64,98,109,64,64,39,64,64,36,64,64,
+64,64,52,64,64,64,64,64,64,64,142,64,64,64,64,114,64,64,64,64,64,64,38,64,64,64,64,64,64,114,64,108,64,64,64,64,102,64,64,111,64,105,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,53,53,53,53,53,53,64,64,34,33,64,64,64,64,64,64,64,39,64,64,64,36,50,
+64,37,64,64,64,64,64,64,133,116,64,64,345,114,114,0,64,64,64,64,64,64,38,41,56,64,142,64,114,64,64,108,64,64,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,117,64,64,64,64,64,64,64,64,64,64,49,64,64,64,64,64,64,64,39,64,64,64,64,36,42,
+64,44,64,64,64,64,64,64,64,64,64,114,64,64,64,64,142,133,64,335,64,64,64,53,53,64,64,64,64,64,129,64,64,64,64,64,64,64,105,64,111,64,107,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,39,64,64,64,64,64,47,
+64,40,64,64,64,64,64,137,64,64,114,64,64,64,64,64,64,347,64,64,64,64,64,347,340,64,64,64,64,64,106,64,64,64,64,64,64,64,105,64,111,64,107,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,109,109,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,104,64,54,64,64,64,64,64,64,64,
+64,64,40,64,64,64,64,333,64,64,64,342,64,133,64,142,64,64,64,64,64,64,334,64,64,64,64,64,64,106,110,64,64,64,64,64,64,64,105,64,111,64,107,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,139,64,64,64,64,64,64,64,64,64,109,109,64,64,106,116,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,64,64,64,64,64,64,
+64,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,110,64,64,64,64,64,64,64,64,105,64,111,64,121,111,64,102,64,64,64,64,64,64,64,64,64,64,111,111,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,107,64,64,64,64,107,64,64,64,140,64,64,64,109,109,107,64,64,64,50,41,64,64,64,64,64,64,
+64,41,41,56,64,64,64,64,345,347,64,64,64,64,64,64,64,337,64,131,144,64,142,64,64,64,64,110,64,110,64,64,64,64,64,64,64,105,111,64,64,64,64,111,111,64,102,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,109,117,64,64,64,107,64,106,64,64,64,64,64,109,64,64,106,64,64,64,107,64,64,116,64,53,38,64,64,64,64,64,
+37,53,53,64,64,64,64,64,64,64,64,64,142,64,64,64,64,64,64,64,64,64,64,64,64,64,64,110,64,64,64,64,64,64,64,111,64,64,111,64,64,64,111,64,111,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,131,64,64,116,64,109,64,64,64,64,64,106,64,64,64,64,64,109,64,64,64,64,106,106,107,64,64,64,64,64,64,34,64,64,64,64,64,
+44,64,64,64,64,64,64,64,64,64,64,134,64,347,121,64,116,64,64,347,340,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,111,64,64,102,98,64,111,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,109,106,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,34,64,64,64,64,
+36,64,64,64,64,64,64,64,64,133,64,64,64,64,64,64,64,64,334,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,98,64,64,102,98,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,106,64,64,64,109,64,64,64,64,64,64,104,64,106,116,64,64,114,64,64,64,64,64,64,64,64,38,64,64,64,64,
+64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,111,64,64,102,98,64,64,64,121,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,109,64,64,64,64,64,64,109,64,106,109,64,64,64,64,64,64,107,106,64,64,64,64,38,64,64,64,
+41,56,64,64,64,64,64,64,110,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,142,64,64,64,108,64,64,64,64,64,64,111,64,111,64,102,64,98,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,114,64,64,106,64,64,64,64,109,64,114,64,64,64,64,64,106,109,64,64,64,64,64,64,107,106,64,64,64,64,34,64,64,64,
+44,53,64,64,141,142,64,144,64,110,64,64,64,64,64,64,64,64,64,64,64,64,116,129,64,64,64,64,64,64,64,64,64,64,64,121,111,64,64,121,102,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,64,107,64,64,109,64,64,64,64,109,64,64,64,64,64,64,64,106,64,109,64,64,64,109,109,64,64,106,64,64,109,64,38,64,64,
+36,64,64,64,64,64,64,64,110,64,64,64,64,64,64,64,64,64,64,64,108,64,115,64,64,64,64,64,64,64,64,64,64,64,64,64,111,107,64,64,102,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,104,64,109,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,106,64,64,109,64,38,64,64,
+64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,102,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,109,64,64,64,64,109,64,107,64,64,64,64,64,64,64,106,64,64,64,64,107,64,106,64,107,64,64,64,38,64,
+64,36,64,64,64,64,64,64,64,64,64,64,134,64,64,64,64,129,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,111,64,64,64,64,64,39,36,64,64,64,121,102,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,109,64,107,64,109,64,64,64,64,106,90,91,64,64,64,64,64,64,116,64,64,64,64,38,64,
+64,64,36,55,64,64,64,64,64,64,64,64,64,108,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,107,64,111,64,64,64,64,64,64,39,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,107,64,109,109,64,107,64,109,64,64,109,64,107,93,64,92,64,140,64,109,64,64,64,64,64,64,64,42,
+64,64,47,36,64,64,64,64,64,64,64,64,64,106,64,142,64,64,64,64,64,64,64,64,64,64,64,64,110,64,64,64,64,64,64,107,111,64,64,64,64,64,64,39,64,64,36,64,64,64,136,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,107,64,109,109,109,64,64,64,64,64,64,64,64,109,64,104,64,64,64,64,64,39,64,
+64,64,45,64,52,64,64,64,64,64,64,64,64,64,64,64,64,64,64,114,64,64,64,64,64,114,64,138,110,64,64,64,64,64,105,107,98,135,64,64,64,121,64,39,64,64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,123,64,64,64,64,64,64,109,107,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,39,64,
+63,37,38,44,64,64,64,64,64,64,64,64,64,110,64,64,64,64,64,64,64,64,64,64,64,64,64,110,64,64,64,64,64,105,64,98,111,102,111,111,64,64,54,64,64,64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,39,64,64,
+64,33,64,38,52,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,134,64,64,64,64,64,107,105,64,64,64,64,111,111,64,64,64,42,64,64,64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,89,92,93,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,39,64,64,
+37,63,64,53,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,134,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,50,64,64,64,64,64,40,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,107,64,116,89,64,92,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,109,54,41,64,63,
+44,63,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,64,106,108,64,64,64,134,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,107,64,64,38,64,64,64,64,64,40,64,64,64,102,121,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,89,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,53,38,63,
+36,64,64,64,64,64,64,134,64,64,138,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,107,111,64,64,38,64,64,64,64,64,40,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,109,109,64,64,64,104,109,117,64,64,93,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,38,63,
+64,36,64,116,64,64,64,64,132,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,121,64,64,111,98,64,64,38,64,64,64,64,64,40,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,109,107,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,46,
+64,36,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,114,64,64,64,64,64,64,64,64,64,64,64,64,64,64,121,64,111,64,111,64,105,64,98,64,64,64,38,64,64,64,64,64,40,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,39,63,
+64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,142,64,64,64,64,138,64,64,106,64,64,64,64,64,64,64,64,64,64,111,64,105,64,64,64,64,64,64,42,64,64,64,64,64,40,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,221,64,64,64,64,39,64,
+64,64,56,64,138,64,64,64,64,64,64,123,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,111,64,111,121,64,64,64,64,35,64,64,64,64,64,64,40,64,64,64,64,111,64,105,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,41,64,
+64,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,110,64,64,64,64,64,64,64,64,64,64,111,64,105,64,64,111,105,64,64,64,64,35,64,64,64,64,64,64,64,40,64,64,64,64,111,105,64,64,64,64,64,64,64,64,117,64,64,114,64,64,64,64,64,64,64,64,64,64,64,64,109,64,221,64,64,64,64,64,64,64,64,64,64,64,64,64,53,42,
+64,64,56,64,64,64,64,64,114,64,64,64,64,64,64,110,110,64,64,106,110,110,64,64,64,64,64,64,64,64,64,64,64,111,121,105,64,107,111,64,64,64,64,35,64,64,64,64,64,64,64,64,56,64,105,64,64,111,121,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,114,64,64,64,64,64,109,64,64,82,64,64,64,209,64,109,64,64,64,64,64,64,54,64,
+64,64,37,64,64,64,64,64,64,64,64,64,64,64,64,107,64,110,64,106,64,64,64,64,110,106,64,64,64,64,64,64,64,64,105,64,64,64,64,64,64,64,64,35,64,64,64,64,64,64,64,64,48,64,105,64,64,98,64,105,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,42,
+64,33,64,64,64,64,64,64,64,64,64,64,64,64,107,64,64,110,64,64,64,64,64,64,110,64,64,64,64,64,64,105,64,111,105,64,64,64,107,64,64,64,35,64,64,64,64,64,64,64,64,64,56,64,105,64,64,64,64,105,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,64,64,64,64,221,64,64,109,64,64,64,64,64,64,64,64,109,106,64,106,64,64,64,54,64,
+64,37,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,105,105,105,107,64,111,111,64,102,64,64,50,64,64,64,64,64,64,64,64,64,48,64,105,111,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,106,64,64,114,64,64,109,64,64,64,64,64,64,109,64,64,106,64,64,64,84,64,109,106,64,107,64,64,64,42,
+44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,102,64,64,105,107,64,64,111,64,102,64,64,34,64,64,64,64,64,64,64,64,64,56,64,105,102,111,111,64,107,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,109,64,64,83,64,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,107,64,209,54,64,
+64,36,64,64,64,64,64,64,64,64,64,64,64,138,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,105,64,111,107,111,64,64,64,64,64,64,34,64,64,64,64,64,64,64,64,48,64,105,64,64,64,111,111,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,109,64,64,64,83,64,64,64,64,64,64,64,107,64,64,64,64,64,64,64,107,64,64,64,42,
+64,36,64,64,64,64,138,64,64,64,142,64,64,64,64,64,64,64,113,64,64,64,64,64,64,64,64,64,64,64,64,64,107,111,64,64,64,64,64,64,64,64,64,34,64,64,64,64,64,64,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,109,64,107,64,64,64,64,64,64,64,106,64,64,107,64,64,64,64,64,64,64,64,64,64,54,64,
+64,64,36,64,134,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,64,64,111,64,102,111,111,121,64,105,64,64,64,34,41,41,64,64,64,64,64,48,64,64,64,64,135,107,64,111,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,104,64,64,64,64,64,64,64,64,64,64,42,
+64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,64,64,64,64,109,64,64,64,64,102,64,111,64,64,105,64,64,64,53,53,38,64,64,64,64,64,56,64,64,64,64,107,64,64,111,64,64,64,64,64,64,64,64,64,107,64,109,64,64,64,64,106,64,107,64,64,64,64,64,106,64,64,64,64,64,64,64,64,64,64,64,64,64,54,64,
+64,64,64,56,64,103,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,64,64,64,64,109,64,64,98,111,102,111,111,64,64,64,64,64,64,64,64,64,38,64,64,64,64,37,64,98,64,111,64,107,121,111,64,64,64,64,64,64,64,64,64,64,107,109,64,64,64,64,64,106,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,122,42,
+64,64,44,116,64,103,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,64,64,102,64,64,111,64,102,64,64,64,98,64,64,64,64,127,64,42,64,64,64,37,64,64,121,64,111,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,109,64,104,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,64,
+64,64,64,56,64,103,64,64,64,64,64,64,64,64,113,64,64,64,64,64,64,64,99,64,99,64,64,64,64,64,64,102,64,64,111,64,102,64,64,64,98,64,105,64,64,64,64,39,64,64,64,37,64,64,64,64,105,111,64,64,111,105,121,64,64,64,64,64,64,64,64,64,64,109,64,64,64,104,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,42,
+64,64,33,64,64,64,64,64,64,64,64,64,64,64,64,64,129,64,109,64,64,64,64,99,99,64,64,64,64,64,64,102,121,64,64,111,102,64,64,64,98,136,64,64,64,64,39,64,64,64,44,64,64,64,64,105,111,64,64,111,64,105,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,88,64,64,64,64,64,209,64,109,64,54,45,
+64,64,37,64,64,64,117,64,64,64,64,64,64,64,64,64,64,64,109,64,64,99,64,64,64,64,64,64,64,64,64,109,102,64,64,111,111,64,64,64,98,64,64,64,64,64,39,64,64,64,64,40,64,64,64,64,64,64,64,111,105,64,64,64,64,64,64,64,64,64,64,64,64,64,117,64,64,64,109,64,64,64,64,64,64,64,64,209,64,64,64,221,64,64,64,64,64,109,64,64,53,
+64,33,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,64,99,99,64,64,64,109,102,64,64,64,64,107,111,98,64,64,107,64,64,54,64,64,64,64,64,40,64,64,64,64,64,98,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,221,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,
+64,37,64,64,64,64,64,64,64,209,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,64,64,64,64,64,64,109,102,64,64,64,64,64,107,98,64,64,107,64,64,64,38,64,64,64,64,64,40,64,64,64,64,64,64,111,64,64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,104,64,64,64,64,64,64,64,
+44,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,134,64,64,64,99,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,111,98,64,64,102,64,64,64,38,64,64,64,64,41,40,64,64,64,64,64,105,111,64,64,64,64,64,64,64,64,64,64,64,64,116,64,64,64,64,64,64,109,64,85,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,87,64,64,64,
+64,40,64,64,64,64,64,64,64,64,64,134,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,99,99,64,64,109,64,64,64,64,64,64,64,111,64,64,64,102,121,64,64,38,64,64,64,37,38,40,64,64,64,64,64,111,64,64,64,64,64,55,64,64,64,64,64,64,64,107,64,64,64,64,64,109,64,64,85,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,
+64,56,64,64,64,64,64,64,64,64,64,64,64,64,109,116,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,109,64,107,64,111,121,64,64,111,64,64,64,121,64,64,63,38,64,64,37,64,38,56,64,64,111,64,111,64,64,64,64,39,43,56,64,64,64,64,64,64,64,64,64,106,64,64,64,109,64,64,64,64,109,64,64,64,104,64,64,64,64,64,109,64,64,64,64,64,64,
+64,37,64,221,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,99,64,64,64,64,99,64,64,64,64,109,105,64,111,121,105,64,111,105,64,64,111,64,64,64,63,38,41,37,64,64,53,64,64,102,111,111,64,64,64,64,39,64,44,64,64,64,64,64,64,64,64,64,106,64,64,64,64,106,64,64,64,64,106,64,64,64,109,64,64,64,64,109,64,106,64,64,64,100,
+44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,99,64,64,64,64,64,64,64,64,64,64,64,109,105,64,111,121,105,64,111,105,64,64,111,64,64,64,64,53,53,64,58,64,64,64,64,102,64,111,64,64,39,43,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,64,106,107,64,64,64,64,64,64,64,109,106,64,64,64,64,64,
+64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,109,105,64,111,64,105,64,102,102,111,111,64,98,64,64,64,64,64,82,57,64,64,64,64,111,111,111,64,64,39,64,64,64,37,64,64,64,64,64,64,0,0,0,0,0,104,64,64,64,109,64,64,64,106,107,64,64,64,64,64,64,64,107,64,64,64,64,64,64,
+44,64,64,64,64,64,64,64,64,64,117,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,102,64,111,64,64,64,64,111,64,64,111,98,111,107,64,59,64,64,64,64,64,64,107,107,64,111,64,54,64,64,64,37,64,64,64,64,64,64,64,0,0,0,87,0,0,64,64,64,109,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,100,64,
+64,40,64,64,64,64,64,64,64,64,109,64,64,64,64,109,103,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,102,64,111,64,64,64,111,111,64,64,64,98,111,111,107,64,60,64,64,87,64,64,111,107,64,64,64,64,46,64,64,48,55,55,64,64,64,64,64,0,0,0,0,0,0,64,64,64,64,109,64,64,64,64,64,64,64,64,106,64,109,64,64,64,64,64,64,64,
+1,56,64,109,64,64,64,64,64,64,109,64,64,64,64,109,64,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,64,64,64,111,64,64,64,64,111,111,64,64,64,64,111,64,64,64,64,64,64,58,111,64,64,64,64,136,54,64,64,64,43,47,56,64,64,64,64,64,0,0,0,0,0,0,64,64,64,64,87,64,64,64,64,64,64,64,109,109,109,64,64,64,64,64,169,64,64,
+1,37,64,64,64,64,64,64,64,64,64,64,138,64,64,64,64,64,109,64,64,99,174,64,64,64,64,64,64,64,64,102,64,64,64,64,111,64,64,64,64,111,64,64,64,64,102,59,102,64,64,64,64,57,111,64,64,64,64,64,64,38,64,64,64,64,48,64,64,64,64,64,0,0,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,170,64,
+37,64,122,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,64,173,64,64,64,64,64,64,64,64,64,64,64,64,64,64,111,64,64,64,107,111,64,64,107,64,102,60,64,64,107,64,64,111,64,64,64,64,64,64,64,42,64,64,64,64,56,64,64,64,64,64,0,0,0,0,0,0,64,64,64,64,64,64,64,175,64,175,64,64,87,64,64,64,64,64,64,64,64,171,64,
+44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,106,64,64,64,172,64,64,64,64,64,64,99,64,64,64,64,64,133,64,111,98,64,64,64,107,64,64,64,102,64,64,64,64,64,64,64,121,64,64,64,64,64,107,64,35,64,64,64,64,37,64,64,64,64,64,0,0,0,0,0,0,64,174,64,64,64,64,64,175,174,64,175,64,64,64,64,64,64,64,64,64,64,64,64,
+36,64,64,64,64,113,64,64,224,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,99,64,64,64,64,64,64,64,64,111,107,64,64,64,111,64,64,64,102,64,105,64,122,64,59,111,64,64,98,121,64,64,107,35,64,64,64,64,37,64,64,64,64,64,64,0,0,0,0,0,0,173,64,64,64,64,64,175,173,64,175,64,64,64,64,64,64,64,64,64,169,64,64,64,
+63,36,64,64,64,64,64,64,64,64,64,64,64,35,36,64,64,64,64,64,99,64,64,64,64,99,64,64,64,64,64,64,64,64,64,64,121,107,64,64,64,64,111,64,64,102,111,105,64,64,64,64,60,64,64,98,98,64,102,64,54,64,64,64,64,48,64,64,64,64,64,64,0,0,0,0,100,0,172,64,64,64,64,64,175,172,64,64,64,64,64,175,176,64,64,64,64,64,170,64,64,
+64,56,64,64,64,64,109,64,64,64,64,64,35,64,36,64,113,106,64,64,64,64,64,64,64,64,64,99,64,64,64,64,64,64,64,121,111,111,111,111,111,111,111,111,111,64,64,133,64,64,64,64,64,64,64,64,64,102,64,64,42,64,64,64,64,36,64,121,64,64,64,64,0,0,100,0,100,0,64,64,64,64,174,175,64,64,64,64,64,174,64,175,175,64,64,64,100,64,171,64,64,
+64,44,64,64,64,64,64,109,64,64,64,64,35,64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,121,111,64,64,64,64,64,64,64,64,64,111,105,121,64,64,64,64,64,64,64,64,64,64,64,64,64,64,54,64,64,64,64,64,36,64,136,64,64,64,0,0,0,0,100,0,64,64,64,64,173,175,64,64,64,64,64,173,64,175,64,175,64,169,64,64,64,64,64,
+64,36,64,64,64,64,64,64,64,64,64,35,64,64,64,52,64,64,64,64,64,64,64,64,64,64,99,64,64,64,107,64,64,105,64,64,64,64,64,64,64,64,111,102,64,121,64,64,58,64,59,64,64,64,64,64,64,64,64,64,42,64,64,64,64,64,36,0,0,0,0,64,0,100,0,0,0,0,64,64,64,172,175,64,64,64,64,64,172,64,64,64,64,175,64,170,64,64,64,64,64,
+64,1,56,64,64,64,64,64,64,64,64,35,64,64,64,48,64,64,64,64,64,64,64,64,99,64,99,64,64,64,64,64,64,105,64,107,64,64,64,64,64,64,111,102,121,64,64,64,57,64,64,60,64,105,64,64,64,64,64,64,54,64,64,64,64,64,64,56,0,0,0,64,64,64,64,64,64,64,64,64,64,175,175,64,64,64,64,64,64,64,64,64,64,64,175,64,171,100,64,64,64,
+1,44,64,64,212,64,64,64,64,64,35,64,64,64,64,40,64,64,64,64,64,64,99,99,64,64,64,64,64,64,64,109,111,105,111,111,64,64,64,64,111,111,64,102,121,64,64,64,64,64,64,64,105,107,107,64,64,64,64,64,34,64,64,64,64,41,48,64,0,0,0,64,64,64,64,64,64,64,64,64,174,175,64,100,64,64,64,174,64,64,100,64,169,64,175,64,64,64,64,64,64,
+1,1,56,64,64,64,64,64,64,64,35,64,64,64,64,64,56,64,64,64,64,64,64,64,64,99,64,64,64,64,64,109,64,105,64,64,111,105,64,64,64,105,64,102,64,64,64,64,64,121,64,64,64,64,64,102,64,64,111,121,64,34,64,45,64,33,38,56,0,0,0,100,64,64,64,100,100,64,64,64,173,175,64,64,64,64,64,173,64,64,64,64,64,170,64,175,64,64,64,64,64,
+1,37,64,64,64,64,117,64,39,47,64,64,64,64,64,48,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,105,64,64,64,105,64,64,64,105,105,102,64,64,64,64,64,64,64,64,64,64,64,102,64,64,64,64,64,64,34,37,38,37,64,53,64,0,0,0,64,64,64,64,64,64,64,64,172,64,64,64,64,100,64,172,64,64,64,64,64,64,171,64,175,175,64,64,64,64,
+1,37,64,64,64,64,64,64,39,64,64,64,64,64,64,64,36,64,64,212,64,64,64,64,63,64,64,64,64,102,64,64,102,64,64,105,105,64,64,64,64,64,64,64,64,64,64,64,64,98,64,64,64,64,64,64,64,64,64,64,64,64,49,64,49,64,64,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,100,64,64,64,64,64,64,100,64,64,64,64,175,175,64,64,64,
+37,64,138,64,64,64,64,39,64,64,64,64,64,64,64,64,36,64,64,64,64,64,64,64,64,102,109,109,109,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,121,64,64,64,64,64,64,64,64,64,64,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,100,64,64,64,64,169,64,64,64,64,64,64,
+44,1,64,114,64,64,64,39,64,64,64,64,64,64,64,64,64,36,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,51,64,64,64,64,107,64,64,64,64,64,64,64,64,64,64,64,64,64,64,121,121,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,170,64,64,64,64,64,
+40,1,64,64,134,64,39,64,64,64,64,64,64,64,64,64,64,36,64,64,64,64,64,39,40,64,102,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,51,35,43,52,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,171,64,64,64,64,64,
+64,40,64,64,64,64,39,64,64,64,64,64,64,64,64,64,64,64,36,64,64,64,64,39,63,52,64,102,64,64,64,64,64,64,64,64,64,64,64,51,64,64,64,64,35,43,64,44,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,0,0,0,0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,40,1,64,1,35,64,64,64,64,64,64,64,64,64,64,64,64,36,64,64,64,39,64,48,64,64,64,64,64,64,64,64,64,64,64,64,64,35,36,64,64,64,35,64,64,64,36,64,64,51,51,64,64,64,64,64,64,64,64,64,39,43,43,40,64,64,0,0,100,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,40,1,1,35,64,64,64,64,64,64,64,64,64,64,64,64,64,36,64,64,39,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,35,64,36,64,64,35,64,64,64,64,36,64,35,47,40,64,64,64,64,64,35,56,64,39,64,64,64,40,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,40,35,47,64,64,64,64,64,64,64,64,64,64,64,64,64,63,36,55,39,64,64,37,64,64,64,64,64,64,64,64,64,64,64,64,35,64,64,36,64,35,64,64,64,64,64,36,35,64,64,40,64,64,64,64,35,44,64,39,64,64,64,64,56,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,43,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,43,43,64,64,37,64,64,64,64,64,64,64,64,64,64,64,64,35,64,64,64,36,35,64,64,64,64,64,64,43,64,64,64,40,64,64,64,35,64,40,39,64,64,64,64,48,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,44,55,55,55,55,55,55,55,55,55,55,55,55,35,64,64,64,64,43,64,64,64,64,64,64,64,64,64,64,64,43,43,40,35,64,64,43,64,64,64,64,64,40,64,64,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,43,43,47,43,43,43,43,43,43,43,43,43,43,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,43,64,64,64,64,64,64,64,64,64,43,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
+</data>
+ </layer>
+ <objectgroup name="area" visible="0">
+ <object id="5" name="Köhlersgrund" type="village" x="196" y="751" width="1148" height="590">
+ <properties>
+ <property name="chance" type="float" value="0.5"/>
+ <property name="density" type="float" value="0.5"/>
+ </properties>
+ </object>
+ <object id="6" name="Friedhof der Köhler" type="graveyard" x="212" y="1490" width="369" height="190">
+ <properties>
+ <property name="chance" type="float" value="0"/>
+ <property name="density" type="float" value="0"/>
+ </properties>
+ </object>
+ <object id="12" name="Schloss Eulenstein" type="castle" x="5156" y="728" width="1136" height="776">
+ <properties>
+ <property name="chance" type="float" value="0.20000000000000001"/>
+ <property name="density" type="float" value="0.69999999999999996"/>
+ </properties>
+ </object>
+ <object id="14" name="Hippielager" type="village" x="1742" y="122" width="238" height="136">
+ <properties>
+ <property name="chance" type="float" value="1"/>
+ <property name="density" type="float" value="0.5"/>
+ </properties>
+ </object>
+ <object id="15" name="Bastis Holzfällerstube" type="village" x="1436" y="672" width="404" height="162">
+ <properties>
+ <property name="chance" type="float" value="0.80000000000000004"/>
+ <property name="density" type="float" value="0.69999999999999996"/>
+ </properties>
+ </object>
+ <object id="17" name="Güldene Ruh" type="graveyard" x="2830" y="34" width="772" height="356">
+ <properties>
+ <property name="chance" type="float" value="0"/>
+ <property name="density" type="float" value="0"/>
+ </properties>
+ </object>
+ <object id="21" name="Sommerhaid" type="village" x="3272" y="366" width="1092" height="466">
+ <properties>
+ <property name="chance" type="float" value="0.40000000000000002"/>
+ <property name="density" type="float" value="1"/>
+ </properties>
+ </object>
+ <object id="23" name="Sommerhaid, Bettlerviertel" type="village" x="3778" y="134" width="814" height="230">
+ <properties>
+ <property name="chance" type="float" value="0.29999999999999999"/>
+ <property name="density" type="float" value="0.80000000000000004"/>
+ </properties>
+ </object>
+ <object id="25" name="Stillswasser" type="village" x="4746" y="2176" width="1664" height="772">
+ <properties>
+ <property name="chance" type="float" value="0.5"/>
+ <property name="density" type="float" value="0.34999999999999998"/>
+ </properties>
+ </object>
+ <object id="28" name="Lindenwäldle" type="village" x="622" y="1516" width="1470" height="1032">
+ <properties>
+ <property name="chance" type="float" value="0.29999999999999999"/>
+ <property name="density" type="float" value="0.29999999999999999"/>
+ </properties>
+ </object>
+ <object id="30" name="Säuferstal" type="village" x="180" y="2641" width="508" height="336">
+ <properties>
+ <property name="chance" type="float" value="1"/>
+ <property name="density" type="float" value="0.5"/>
+ </properties>
+ </object>
+ <object id="32" name="Lager der armen Holzfäller" type="village" x="2678" y="2749" width="950" height="338">
+ <properties>
+ <property name="chance" type="float" value="0.69999999999999996"/>
+ <property name="density" type="float" value="0.40000000000000002"/>
+ </properties>
+ </object>
+ <object id="37" name="Sommerhaid, Haidenviertel" type="village" x="2888" y="832" width="1044" height="460">
+ <properties>
+ <property name="chance" type="float" value="0.29999999999999999"/>
+ <property name="density" type="float" value="0.59999999999999998"/>
+ </properties>
+ </object>
+ <object id="39" name="Der ebene Wald" type="village" x="2100" y="1518" width="652" height="1570">
+ <properties>
+ <property name="chance" type="float" value="0.40000000000000002"/>
+ <property name="density" type="float" value="0.29999999999999999"/>
+ </properties>
+ </object>
+ </objectgroup>
+</map>
diff --git a/V3/Content/Menu/Titel.png b/V3/Content/Menu/Titel.png
new file mode 100644
index 0000000..d22295f
--- /dev/null
+++ b/V3/Content/Menu/Titel.png
Binary files differ
diff --git a/V3/Content/Menu/arrow_white.png b/V3/Content/Menu/arrow_white.png
new file mode 100644
index 0000000..8e4d2e2
--- /dev/null
+++ b/V3/Content/Menu/arrow_white.png
Binary files differ
diff --git a/V3/Content/Menu/mainscreen.jpg b/V3/Content/Menu/mainscreen.jpg
new file mode 100644
index 0000000..654eaa6
--- /dev/null
+++ b/V3/Content/Menu/mainscreen.jpg
Binary files differ
diff --git a/V3/Content/Sounds/Afraid_to_Go.mp3 b/V3/Content/Sounds/Afraid_to_Go.mp3
new file mode 100644
index 0000000..beb828b
--- /dev/null
+++ b/V3/Content/Sounds/Afraid_to_Go.mp3
Binary files differ
diff --git a/V3/Content/Sounds/Knight.wav b/V3/Content/Sounds/Knight.wav
new file mode 100644
index 0000000..cb088af
--- /dev/null
+++ b/V3/Content/Sounds/Knight.wav
Binary files differ
diff --git a/V3/Content/Sounds/Kosta_T_-_06.mp3 b/V3/Content/Sounds/Kosta_T_-_06.mp3
new file mode 100644
index 0000000..3d4ac62
--- /dev/null
+++ b/V3/Content/Sounds/Kosta_T_-_06.mp3
Binary files differ
diff --git a/V3/Content/Sounds/Monster_Gigante-Doberman-1334685792.wav b/V3/Content/Sounds/Monster_Gigante-Doberman-1334685792.wav
new file mode 100644
index 0000000..4bfe6a4
--- /dev/null
+++ b/V3/Content/Sounds/Monster_Gigante-Doberman-1334685792.wav
Binary files differ
diff --git a/V3/Content/Sounds/Mummy_Zombie-SoundBible.wav b/V3/Content/Sounds/Mummy_Zombie-SoundBible.wav
new file mode 100644
index 0000000..cd3630c
--- /dev/null
+++ b/V3/Content/Sounds/Mummy_Zombie-SoundBible.wav
Binary files differ
diff --git a/V3/Content/Sounds/SkeletonHorse.wav b/V3/Content/Sounds/SkeletonHorse.wav
new file mode 100644
index 0000000..7804c61
--- /dev/null
+++ b/V3/Content/Sounds/SkeletonHorse.wav
Binary files differ
diff --git a/V3/Content/Sounds/explode.wav b/V3/Content/Sounds/explode.wav
new file mode 100644
index 0000000..8c1d089
--- /dev/null
+++ b/V3/Content/Sounds/explode.wav
Binary files differ
diff --git a/V3/Content/Sounds/explodemini.wav b/V3/Content/Sounds/explodemini.wav
new file mode 100644
index 0000000..bcad45b
--- /dev/null
+++ b/V3/Content/Sounds/explodemini.wav
Binary files differ
diff --git a/V3/Content/Sounds/explosion1.ogg b/V3/Content/Sounds/explosion1.ogg
new file mode 100644
index 0000000..64c1ab6
--- /dev/null
+++ b/V3/Content/Sounds/explosion1.ogg
Binary files differ
diff --git a/V3/Content/Sounds/horse.wav b/V3/Content/Sounds/horse.wav
new file mode 100644
index 0000000..5715ad1
--- /dev/null
+++ b/V3/Content/Sounds/horse.wav
Binary files differ
diff --git a/V3/Content/Sounds/impactsplat01.ogg b/V3/Content/Sounds/impactsplat01.ogg
new file mode 100644
index 0000000..731cf83
--- /dev/null
+++ b/V3/Content/Sounds/impactsplat01.ogg
Binary files differ
diff --git a/V3/Content/Sounds/punch.wav b/V3/Content/Sounds/punch.wav
new file mode 100644
index 0000000..395bb5c
--- /dev/null
+++ b/V3/Content/Sounds/punch.wav
Binary files differ
diff --git a/V3/Content/Sounds/walking.wav b/V3/Content/Sounds/walking.wav
new file mode 100644
index 0000000..894dff4
--- /dev/null
+++ b/V3/Content/Sounds/walking.wav
Binary files differ
diff --git a/V3/Content/Sounds/zonk2.wav b/V3/Content/Sounds/zonk2.wav
new file mode 100644
index 0000000..77e37b4
--- /dev/null
+++ b/V3/Content/Sounds/zonk2.wav
Binary files differ
diff --git a/V3/Content/Sources/Horse.blend b/V3/Content/Sources/Horse.blend
new file mode 100644
index 0000000..017b12c
--- /dev/null
+++ b/V3/Content/Sources/Horse.blend
Binary files differ
diff --git a/V3/Content/Sources/Skeleton.blend b/V3/Content/Sources/Skeleton.blend
new file mode 100644
index 0000000..0a8fd5d
--- /dev/null
+++ b/V3/Content/Sources/Skeleton.blend
Binary files differ
diff --git a/V3/Content/Sources/SkeletonHorse.blend b/V3/Content/Sources/SkeletonHorse.blend
new file mode 100644
index 0000000..3520f1e
--- /dev/null
+++ b/V3/Content/Sources/SkeletonHorse.blend
Binary files differ
diff --git a/V3/Content/Sources/SkeletonHorse.png b/V3/Content/Sources/SkeletonHorse.png
new file mode 100644
index 0000000..e0fc808
--- /dev/null
+++ b/V3/Content/Sources/SkeletonHorse.png
Binary files differ
diff --git a/V3/Content/Sources/SkeletonRider.blend b/V3/Content/Sources/SkeletonRider.blend
new file mode 100644
index 0000000..24d7161
--- /dev/null
+++ b/V3/Content/Sources/SkeletonRider.blend
Binary files differ
diff --git a/V3/Content/Sources/SkeletonRider.png b/V3/Content/Sources/SkeletonRider.png
new file mode 100644
index 0000000..681af75
--- /dev/null
+++ b/V3/Content/Sources/SkeletonRider.png
Binary files differ
diff --git a/V3/Content/Sources/ZombieWithClub.png b/V3/Content/Sources/ZombieWithClub.png
new file mode 100644
index 0000000..bea8eb3
--- /dev/null
+++ b/V3/Content/Sources/ZombieWithClub.png
Binary files differ
diff --git a/V3/Content/Sources/castle.xcf b/V3/Content/Sources/castle.xcf
new file mode 100644
index 0000000..50a7f8a
--- /dev/null
+++ b/V3/Content/Sources/castle.xcf
Binary files differ
diff --git a/V3/Content/Sources/create_spritesheet.sh b/V3/Content/Sources/create_spritesheet.sh
new file mode 100644
index 0000000..eda0bd3
--- /dev/null
+++ b/V3/Content/Sources/create_spritesheet.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+rows=8 # Possible directions (== 8 in isometric view)
+columns=32 # Number of sprites per direction
+cd out
+montage *.png -mode concatenate -tile ${columns}x${rows} -background none out.png
+mv out.png ../
+cd ..
diff --git a/V3/Content/Sources/fleischklops.blend b/V3/Content/Sources/fleischklops.blend
new file mode 100644
index 0000000..ce145da
--- /dev/null
+++ b/V3/Content/Sources/fleischklops.blend
Binary files differ
diff --git a/V3/Content/Sources/fleischklops.xcf b/V3/Content/Sources/fleischklops.xcf
new file mode 100644
index 0000000..322f745
--- /dev/null
+++ b/V3/Content/Sources/fleischklops.xcf
Binary files differ
diff --git a/V3/Content/Sources/horse_paint.png b/V3/Content/Sources/horse_paint.png
new file mode 100644
index 0000000..74e4dd5
--- /dev/null
+++ b/V3/Content/Sources/horse_paint.png
Binary files differ
diff --git a/V3/Content/Sources/horse_paint_sc.png b/V3/Content/Sources/horse_paint_sc.png
new file mode 100644
index 0000000..453703a
--- /dev/null
+++ b/V3/Content/Sources/horse_paint_sc.png
Binary files differ
diff --git a/V3/Content/Sources/horse_tack.png b/V3/Content/Sources/horse_tack.png
new file mode 100644
index 0000000..4f6a0b8
--- /dev/null
+++ b/V3/Content/Sources/horse_tack.png
Binary files differ
diff --git a/V3/Content/Sources/houses_front.xcf b/V3/Content/Sources/houses_front.xcf
new file mode 100644
index 0000000..680596d
--- /dev/null
+++ b/V3/Content/Sources/houses_front.xcf
Binary files differ
diff --git a/V3/Content/Sources/houses_rear.xcf b/V3/Content/Sources/houses_rear.xcf
new file mode 100644
index 0000000..d711b47
--- /dev/null
+++ b/V3/Content/Sources/houses_rear.xcf
Binary files differ
diff --git a/V3/Content/Sources/human_construction_set.xcf b/V3/Content/Sources/human_construction_set.xcf
new file mode 100644
index 0000000..d0fb838
--- /dev/null
+++ b/V3/Content/Sources/human_construction_set.xcf
Binary files differ
diff --git a/V3/Content/Sources/human_construction_set_female.xcf b/V3/Content/Sources/human_construction_set_female.xcf
new file mode 100644
index 0000000..8c29a55
--- /dev/null
+++ b/V3/Content/Sources/human_construction_set_female.xcf
Binary files differ
diff --git a/V3/Content/Sources/king.blend b/V3/Content/Sources/king.blend
new file mode 100644
index 0000000..3bc5379
--- /dev/null
+++ b/V3/Content/Sources/king.blend
Binary files differ
diff --git a/V3/Content/Sources/king_head.png b/V3/Content/Sources/king_head.png
new file mode 100644
index 0000000..9bbc3c9
--- /dev/null
+++ b/V3/Content/Sources/king_head.png
Binary files differ
diff --git a/V3/Content/Sources/necromancer.xcf b/V3/Content/Sources/necromancer.xcf
new file mode 100644
index 0000000..dad43ad
--- /dev/null
+++ b/V3/Content/Sources/necromancer.xcf
Binary files differ
diff --git a/V3/Content/Sources/pathfinder.xcf b/V3/Content/Sources/pathfinder.xcf
new file mode 100644
index 0000000..7833cea
--- /dev/null
+++ b/V3/Content/Sources/pathfinder.xcf
Binary files differ
diff --git a/V3/Content/Sources/prince.blend b/V3/Content/Sources/prince.blend
new file mode 100644
index 0000000..c7c35a8
--- /dev/null
+++ b/V3/Content/Sources/prince.blend
Binary files differ
diff --git a/V3/Content/Sources/prince.xcf b/V3/Content/Sources/prince.xcf
new file mode 100644
index 0000000..14bbcf9
--- /dev/null
+++ b/V3/Content/Sources/prince.xcf
Binary files differ
diff --git a/V3/Content/Sources/selection.xcf b/V3/Content/Sources/selection.xcf
new file mode 100644
index 0000000..bbe3202
--- /dev/null
+++ b/V3/Content/Sources/selection.xcf
Binary files differ
diff --git a/V3/Content/Sources/skeleton_horse.png b/V3/Content/Sources/skeleton_horse.png
new file mode 100644
index 0000000..ed78f4a
--- /dev/null
+++ b/V3/Content/Sources/skeleton_horse.png
Binary files differ
diff --git a/V3/Content/Sources/the_triumph_of_death.jpg b/V3/Content/Sources/the_triumph_of_death.jpg
new file mode 100644
index 0000000..654eaa6
--- /dev/null
+++ b/V3/Content/Sources/the_triumph_of_death.jpg
Binary files differ
diff --git a/V3/Content/Sources/zombie.blend b/V3/Content/Sources/zombie.blend
new file mode 100644
index 0000000..0ae1de3
--- /dev/null
+++ b/V3/Content/Sources/zombie.blend
Binary files differ
diff --git a/V3/Content/Sprites/WhiteRectangle.png b/V3/Content/Sprites/WhiteRectangle.png
new file mode 100644
index 0000000..7d3a386
--- /dev/null
+++ b/V3/Content/Sprites/WhiteRectangle.png
Binary files differ
diff --git a/V3/Content/Sprites/arrows.png b/V3/Content/Sprites/arrows.png
new file mode 100644
index 0000000..61a2db9
--- /dev/null
+++ b/V3/Content/Sprites/arrows.png
Binary files differ
diff --git a/V3/Content/Sprites/buckler.png b/V3/Content/Sprites/buckler.png
new file mode 100644
index 0000000..1253a60
--- /dev/null
+++ b/V3/Content/Sprites/buckler.png
Binary files differ
diff --git a/V3/Content/Sprites/buckler_female.png b/V3/Content/Sprites/buckler_female.png
new file mode 100644
index 0000000..3dd42a1
--- /dev/null
+++ b/V3/Content/Sprites/buckler_female.png
Binary files differ
diff --git a/V3/Content/Sprites/chain.png b/V3/Content/Sprites/chain.png
new file mode 100644
index 0000000..375f887
--- /dev/null
+++ b/V3/Content/Sprites/chain.png
Binary files differ
diff --git a/V3/Content/Sprites/chain_female.png b/V3/Content/Sprites/chain_female.png
new file mode 100644
index 0000000..e3b9c59
--- /dev/null
+++ b/V3/Content/Sprites/chain_female.png
Binary files differ
diff --git a/V3/Content/Sprites/cloth.png b/V3/Content/Sprites/cloth.png
new file mode 100644
index 0000000..3787867
--- /dev/null
+++ b/V3/Content/Sprites/cloth.png
Binary files differ
diff --git a/V3/Content/Sprites/cloth_female.png b/V3/Content/Sprites/cloth_female.png
new file mode 100644
index 0000000..f4d68b8
--- /dev/null
+++ b/V3/Content/Sprites/cloth_female.png
Binary files differ
diff --git a/V3/Content/Sprites/cloud.png b/V3/Content/Sprites/cloud.png
new file mode 100644
index 0000000..1e20701
--- /dev/null
+++ b/V3/Content/Sprites/cloud.png
Binary files differ
diff --git a/V3/Content/Sprites/ellipse.png b/V3/Content/Sprites/ellipse.png
new file mode 100644
index 0000000..c3e0c2c
--- /dev/null
+++ b/V3/Content/Sprites/ellipse.png
Binary files differ
diff --git a/V3/Content/Sprites/fleischklops.png b/V3/Content/Sprites/fleischklops.png
new file mode 100644
index 0000000..b040c76
--- /dev/null
+++ b/V3/Content/Sprites/fleischklops.png
Binary files differ
diff --git a/V3/Content/Sprites/fog.png b/V3/Content/Sprites/fog.png
new file mode 100644
index 0000000..abb5572
--- /dev/null
+++ b/V3/Content/Sprites/fog.png
Binary files differ
diff --git a/V3/Content/Sprites/head.png b/V3/Content/Sprites/head.png
new file mode 100644
index 0000000..ff497fa
--- /dev/null
+++ b/V3/Content/Sprites/head.png
Binary files differ
diff --git a/V3/Content/Sprites/head_bald.png b/V3/Content/Sprites/head_bald.png
new file mode 100644
index 0000000..51796a4
--- /dev/null
+++ b/V3/Content/Sprites/head_bald.png
Binary files differ
diff --git a/V3/Content/Sprites/head_chain.png b/V3/Content/Sprites/head_chain.png
new file mode 100644
index 0000000..9565715
--- /dev/null
+++ b/V3/Content/Sprites/head_chain.png
Binary files differ
diff --git a/V3/Content/Sprites/head_chain_female.png b/V3/Content/Sprites/head_chain_female.png
new file mode 100644
index 0000000..f016527
--- /dev/null
+++ b/V3/Content/Sprites/head_chain_female.png
Binary files differ
diff --git a/V3/Content/Sprites/head_female.png b/V3/Content/Sprites/head_female.png
new file mode 100644
index 0000000..3849fae
--- /dev/null
+++ b/V3/Content/Sprites/head_female.png
Binary files differ
diff --git a/V3/Content/Sprites/head_plate.png b/V3/Content/Sprites/head_plate.png
new file mode 100644
index 0000000..5c7f5b7
--- /dev/null
+++ b/V3/Content/Sprites/head_plate.png
Binary files differ
diff --git a/V3/Content/Sprites/head_plate_female.png b/V3/Content/Sprites/head_plate_female.png
new file mode 100644
index 0000000..709d24e
--- /dev/null
+++ b/V3/Content/Sprites/head_plate_female.png
Binary files differ
diff --git a/V3/Content/Sprites/king.png b/V3/Content/Sprites/king.png
new file mode 100644
index 0000000..18b5ea7
--- /dev/null
+++ b/V3/Content/Sprites/king.png
Binary files differ
diff --git a/V3/Content/Sprites/longsword.png b/V3/Content/Sprites/longsword.png
new file mode 100644
index 0000000..7e12f43
--- /dev/null
+++ b/V3/Content/Sprites/longsword.png
Binary files differ
diff --git a/V3/Content/Sprites/longsword_female.png b/V3/Content/Sprites/longsword_female.png
new file mode 100644
index 0000000..34f4480
--- /dev/null
+++ b/V3/Content/Sprites/longsword_female.png
Binary files differ
diff --git a/V3/Content/Sprites/necromancer.png b/V3/Content/Sprites/necromancer.png
new file mode 100644
index 0000000..65258d5
--- /dev/null
+++ b/V3/Content/Sprites/necromancer.png
Binary files differ
diff --git a/V3/Content/Sprites/necromancer_female.png b/V3/Content/Sprites/necromancer_female.png
new file mode 100644
index 0000000..6d34095
--- /dev/null
+++ b/V3/Content/Sprites/necromancer_female.png
Binary files differ
diff --git a/V3/Content/Sprites/nude.png b/V3/Content/Sprites/nude.png
new file mode 100644
index 0000000..88f1588
--- /dev/null
+++ b/V3/Content/Sprites/nude.png
Binary files differ
diff --git a/V3/Content/Sprites/nude_female.png b/V3/Content/Sprites/nude_female.png
new file mode 100644
index 0000000..fdd5b33
--- /dev/null
+++ b/V3/Content/Sprites/nude_female.png
Binary files differ
diff --git a/V3/Content/Sprites/plate.png b/V3/Content/Sprites/plate.png
new file mode 100644
index 0000000..f4d49c8
--- /dev/null
+++ b/V3/Content/Sprites/plate.png
Binary files differ
diff --git a/V3/Content/Sprites/plate_female.png b/V3/Content/Sprites/plate_female.png
new file mode 100644
index 0000000..570afff
--- /dev/null
+++ b/V3/Content/Sprites/plate_female.png
Binary files differ
diff --git a/V3/Content/Sprites/prince.png b/V3/Content/Sprites/prince.png
new file mode 100644
index 0000000..9a8e5d9
--- /dev/null
+++ b/V3/Content/Sprites/prince.png
Binary files differ
diff --git a/V3/Content/Sprites/selection.png b/V3/Content/Sprites/selection.png
new file mode 100644
index 0000000..4ce0e93
--- /dev/null
+++ b/V3/Content/Sprites/selection.png
Binary files differ
diff --git a/V3/Content/Sprites/shield.png b/V3/Content/Sprites/shield.png
new file mode 100644
index 0000000..a5b1068
--- /dev/null
+++ b/V3/Content/Sprites/shield.png
Binary files differ
diff --git a/V3/Content/Sprites/shield_female.png b/V3/Content/Sprites/shield_female.png
new file mode 100644
index 0000000..d4b074b
--- /dev/null
+++ b/V3/Content/Sprites/shield_female.png
Binary files differ
diff --git a/V3/Content/Sprites/shortsword.png b/V3/Content/Sprites/shortsword.png
new file mode 100644
index 0000000..e10adf8
--- /dev/null
+++ b/V3/Content/Sprites/shortsword.png
Binary files differ
diff --git a/V3/Content/Sprites/shortsword_female.png b/V3/Content/Sprites/shortsword_female.png
new file mode 100644
index 0000000..d1f097e
--- /dev/null
+++ b/V3/Content/Sprites/shortsword_female.png
Binary files differ
diff --git a/V3/Content/Sprites/skeleton.png b/V3/Content/Sprites/skeleton.png
new file mode 100644
index 0000000..09d6464
--- /dev/null
+++ b/V3/Content/Sprites/skeleton.png
Binary files differ
diff --git a/V3/Content/Sprites/skeleton_archer.png b/V3/Content/Sprites/skeleton_archer.png
new file mode 100644
index 0000000..5bdbc02
--- /dev/null
+++ b/V3/Content/Sprites/skeleton_archer.png
Binary files differ
diff --git a/V3/Content/Sprites/skeleton_elite.png b/V3/Content/Sprites/skeleton_elite.png
new file mode 100644
index 0000000..982c031
--- /dev/null
+++ b/V3/Content/Sprites/skeleton_elite.png
Binary files differ
diff --git a/V3/Content/Sprites/skeleton_horse.png b/V3/Content/Sprites/skeleton_horse.png
new file mode 100644
index 0000000..e0fc808
--- /dev/null
+++ b/V3/Content/Sprites/skeleton_horse.png
Binary files differ
diff --git a/V3/Content/Sprites/skeleton_rider.png b/V3/Content/Sprites/skeleton_rider.png
new file mode 100644
index 0000000..681af75
--- /dev/null
+++ b/V3/Content/Sprites/skeleton_rider.png
Binary files differ
diff --git a/V3/Content/Sprites/staff.png b/V3/Content/Sprites/staff.png
new file mode 100644
index 0000000..0200876
--- /dev/null
+++ b/V3/Content/Sprites/staff.png
Binary files differ
diff --git a/V3/Content/Sprites/staff_female.png b/V3/Content/Sprites/staff_female.png
new file mode 100644
index 0000000..3a526d5
--- /dev/null
+++ b/V3/Content/Sprites/staff_female.png
Binary files differ
diff --git a/V3/Content/Sprites/zombie.png b/V3/Content/Sprites/zombie.png
new file mode 100644
index 0000000..a68091a
--- /dev/null
+++ b/V3/Content/Sprites/zombie.png
Binary files differ
diff --git a/V3/Content/Sprites/zombie_club.png b/V3/Content/Sprites/zombie_club.png
new file mode 100644
index 0000000..bea8eb3
--- /dev/null
+++ b/V3/Content/Sprites/zombie_club.png
Binary files differ
diff --git a/V3/Content/Textures/EmptyPixel.png b/V3/Content/Textures/EmptyPixel.png
new file mode 100644
index 0000000..626a832
--- /dev/null
+++ b/V3/Content/Textures/EmptyPixel.png
Binary files differ
diff --git a/V3/Content/Textures/castle.png b/V3/Content/Textures/castle.png
new file mode 100644
index 0000000..e19dd45
--- /dev/null
+++ b/V3/Content/Textures/castle.png
Binary files differ
diff --git a/V3/Content/Textures/grassland.png b/V3/Content/Textures/grassland.png
new file mode 100644
index 0000000..0a56056
--- /dev/null
+++ b/V3/Content/Textures/grassland.png
Binary files differ
diff --git a/V3/Content/Textures/grassland_trees.png b/V3/Content/Textures/grassland_trees.png
new file mode 100644
index 0000000..b2bced3
--- /dev/null
+++ b/V3/Content/Textures/grassland_trees.png
Binary files differ
diff --git a/V3/Content/Textures/grassland_water.png b/V3/Content/Textures/grassland_water.png
new file mode 100644
index 0000000..2083506
--- /dev/null
+++ b/V3/Content/Textures/grassland_water.png
Binary files differ
diff --git a/V3/Content/Textures/houses_front.png b/V3/Content/Textures/houses_front.png
new file mode 100644
index 0000000..94513d4
--- /dev/null
+++ b/V3/Content/Textures/houses_front.png
Binary files differ
diff --git a/V3/Content/Textures/houses_rear.png b/V3/Content/Textures/houses_rear.png
new file mode 100644
index 0000000..64012e1
--- /dev/null
+++ b/V3/Content/Textures/houses_rear.png
Binary files differ
diff --git a/V3/Content/Textures/medieval_building_tiles.png b/V3/Content/Textures/medieval_building_tiles.png
new file mode 100644
index 0000000..154be4e
--- /dev/null
+++ b/V3/Content/Textures/medieval_building_tiles.png
Binary files differ
diff --git a/V3/Content/Textures/pathfinder.png b/V3/Content/Textures/pathfinder.png
new file mode 100644
index 0000000..ae86dfb
--- /dev/null
+++ b/V3/Content/Textures/pathfinder.png
Binary files differ
diff --git a/V3/Data/DebugMode.cs b/V3/Data/DebugMode.cs
new file mode 100644
index 0000000..b93ae7a
--- /dev/null
+++ b/V3/Data/DebugMode.cs
@@ -0,0 +1,21 @@
+namespace V3.Data
+{
+ /// <summary>
+ /// A debug level that can be set by the player.
+ /// </summary>
+ public enum DebugMode
+ {
+ /// <summary>
+ /// Disable all debug utils.
+ /// </summary>
+ Off,
+ /// <summary>
+ /// Show the FPS counter.
+ /// </summary>
+ Fps,
+ /// <summary>
+ /// Show all debug information.
+ /// </summary>
+ Full
+ }
+}
diff --git a/V3/Data/GameState.cs b/V3/Data/GameState.cs
new file mode 100644
index 0000000..b38ec46
--- /dev/null
+++ b/V3/Data/GameState.cs
@@ -0,0 +1,64 @@
+using Microsoft.Xna.Framework;
+using System;
+using System.Collections.Generic;
+using V3.AI;
+using V3.Objects;
+
+namespace V3.Data
+{
+ /// <summary>
+ /// Stores the current state of the game (the data that must be stored in
+ /// a save game). All members should be public and serializable.
+ /// </summary>
+ [Serializable]
+ public sealed class GameState
+ {
+ public List<CreatureData> mCreatures = new List<CreatureData>();
+ public List<Rectangle> mFog = new List<Rectangle>();
+ public Vector2 mCameraPosition;
+ public AiState mAiState = AiState.Idle;
+ }
+
+ public enum CreatureType
+ {
+ FemalePeasant,
+ King,
+ KingsGuard,
+ Knight,
+ MalePeasant,
+ Meatball,
+ Necromancer,
+ Prince,
+ Skeleton,
+ SkeletonHorse,
+ Zombie
+ }
+
+ public sealed class CreatureData
+ {
+ public CreatureType Type { get; set; }
+ public int Id { get; set; }
+ public int Life { get; set; }
+ public int MaxLife { get; set; }
+ public int Attack { get; set; }
+ public TimeSpan Recovery { get; set; }
+ public bool IsUpgraded { get; set; }
+ public float PositionX { get; set; }
+ public float PositionY { get; set; }
+ public MovementDirection MovementDirection { get; set; }
+ public MovementState MovementState { get; set; }
+ public MovementData MovementData { get; set; }
+ public int IsAttackingId { get; set; }
+ public bool Mounted { get; set; }
+ public int SkeletonId { get; set; }
+ // IsAttackingBuilding
+ }
+
+ public sealed class MovementData
+ {
+ public List<Vector2> Path { get; set; }
+ public int Step { get; set; }
+ public Vector2 LastMovement { get; set; }
+ public bool IsMoving { get; set; }
+ }
+}
diff --git a/V3/Data/IGameStateManager.cs b/V3/Data/IGameStateManager.cs
new file mode 100644
index 0000000..3a7f901
--- /dev/null
+++ b/V3/Data/IGameStateManager.cs
@@ -0,0 +1,20 @@
+namespace V3.Data
+{
+ /// <summary>
+ /// Stores the current game state.
+ /// </summary>
+ public interface IGameStateManager
+ {
+ /// <summary>
+ /// Stores the current game state and returns it.
+ /// </summary>
+ /// <returns>the current game state</returns>
+ GameState GetGameState();
+
+ /// <summary>
+ /// Restores the given game state.
+ /// </summary>
+ /// <param name="gameState">the game state to restore</param>
+ void LoadGameState(GameState gameState);
+ }
+}
diff --git a/V3/Data/IOptionsManager.cs b/V3/Data/IOptionsManager.cs
new file mode 100644
index 0000000..6910107
--- /dev/null
+++ b/V3/Data/IOptionsManager.cs
@@ -0,0 +1,18 @@
+namespace V3.Data
+{
+ /// <summary>
+ /// Handles the storing and loading of game options to the hard disk.
+ /// </summary>
+ public interface IOptionsManager
+ {
+ /// <summary>
+ /// The current options.
+ /// </summary>
+ Options Options { get; }
+
+ /// <summary>
+ /// Saves the current options to the hard disk.
+ /// </summary>
+ void SaveOptions();
+ }
+}
diff --git a/V3/Data/IPathManager.cs b/V3/Data/IPathManager.cs
new file mode 100644
index 0000000..5574b35
--- /dev/null
+++ b/V3/Data/IPathManager.cs
@@ -0,0 +1,30 @@
+namespace V3.Data
+{
+ /// <summary>
+ /// Provides access to the default applications path, i. e. the
+ /// directories where save games, achievements and other persistent data
+ /// can be stored.
+ /// </summary>
+ public interface IPathManager
+ {
+ /// <summary>
+ /// The base directory for persistent application data.
+ /// </summary>
+ string AppDirectory { get; }
+
+ /// <summary>
+ /// The directory for save games.
+ /// </summary>
+ string SaveGameDirectory { get; }
+
+ /// <summary>
+ /// The file to store the options in.
+ /// </summary>
+ string OptionsFile { get; }
+
+ /// <summary>
+ /// Creates the application directories that do not already exist.
+ /// </summary>
+ void CreateMissingDirectories();
+ }
+} \ No newline at end of file
diff --git a/V3/Data/ISaveGame.cs b/V3/Data/ISaveGame.cs
new file mode 100644
index 0000000..6f954e1
--- /dev/null
+++ b/V3/Data/ISaveGame.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace V3.Data
+{
+ /// <summary>
+ /// Stores a game state with some metadata.
+ /// </summary>
+ public interface ISaveGame : IComparable<ISaveGame>
+ {
+ /// <summary>
+ /// The creation time of this save game in local time.
+ /// </summary>
+ DateTime Timestamp { get; set; }
+
+ /// <summary>
+ /// The compability version of this save game.
+ /// </summary>
+ int Version { get; set; }
+
+ /// <summary>
+ /// The data stored in this save game.
+ /// </summary>
+ GameState GameState { get; set; }
+ }
+} \ No newline at end of file
diff --git a/V3/Data/ISaveGameManager.cs b/V3/Data/ISaveGameManager.cs
new file mode 100644
index 0000000..f4e810f
--- /dev/null
+++ b/V3/Data/ISaveGameManager.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace V3.Data
+{
+ /// <summary>
+ /// Stores and manages the game state in save games.
+ /// </summary>
+ public interface ISaveGameManager
+ {
+ /// <summary>
+ /// Creates and persists a new save game of the given data with the
+ /// title.
+ /// </summary>
+ /// <param name="gameState">the data to store</param>
+ void CreateSaveGame(GameState gameState);
+
+ /// <summary>
+ /// Loads all available save games and returns them ordered by the
+ /// creation date.
+ /// </summary>
+ /// <returns>a list of all available save games, orderd by creation
+ /// data</returns>
+ List<ISaveGame> GetSaveGames();
+ }
+} \ No newline at end of file
diff --git a/V3/Data/Internal/GameStateManager.cs b/V3/Data/Internal/GameStateManager.cs
new file mode 100644
index 0000000..53ef3de
--- /dev/null
+++ b/V3/Data/Internal/GameStateManager.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using V3.Camera;
+using V3.Map;
+using V3.Objects;
+
+namespace V3.Data.Internal
+{
+ /// <summary>
+ /// Default implementation of IGameStateManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class GameStateManager : IGameStateManager
+ {
+ private readonly CameraManager mCameraManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly IMapManager mMapManager;
+ private readonly IObjectsManager mObjectsManager;
+
+ public GameStateManager(CameraManager cameraManager, CreatureFactory creatureFactory,
+ IMapManager mapManager, IObjectsManager objectsManager)
+ {
+ mCameraManager = cameraManager;
+ mCreatureFactory = creatureFactory;
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ }
+
+ /// <summary>
+ /// Restores the given game state.
+ /// </summary>
+ public GameState GetGameState()
+ {
+ var gameState = new GameState();
+ foreach (var obj in mObjectsManager.CreatureList)
+ {
+ gameState.mCreatures.Add(obj.SaveData());
+ }
+ gameState.mCameraPosition = mCameraManager.GetCamera().Location;
+ return gameState;
+ }
+
+ /// <summary>
+ /// Restores the given game state.
+ /// </summary>
+ /// <param name="gameState">the game state to restore</param>
+ public void LoadGameState(GameState gameState)
+ {
+ mObjectsManager.Clear();
+ mObjectsManager.ImportMapObjects(mMapManager.GetObjects());
+ var creatures = new Dictionary<int, ICreature>();
+
+ // load creatures
+ foreach (var creatureData in gameState.mCreatures)
+ {
+ ICreature creature = mCreatureFactory.CreateCreature(creatureData.Type, creatureData.Id);
+ if (creature == null)
+ continue;
+ creature.LoadData(creatureData);
+ if (creature is Necromancer)
+ mObjectsManager.CreatePlayerCharacter((Necromancer) creature);
+ else if (creature is King) // || creature is Prince
+ mObjectsManager.CreateBoss(creature);
+ else
+ mObjectsManager.CreateCreature(creature);
+
+ creatures.Add(creature.Id, creature);
+ }
+
+ // fix references
+ foreach (var creatureData in gameState.mCreatures)
+ {
+ if (!creatures.ContainsKey(creatureData.Id))
+ continue;
+ creatures[creatureData.Id].LoadReferences(creatureData, creatures);
+ }
+
+ if (mCameraManager.GetCamera() is CameraScrolling)
+ mCameraManager.GetCamera().Location = gameState.mCameraPosition;
+ }
+ }
+}
diff --git a/V3/Data/Internal/OptionsManager.cs b/V3/Data/Internal/OptionsManager.cs
new file mode 100644
index 0000000..d581ba5
--- /dev/null
+++ b/V3/Data/Internal/OptionsManager.cs
@@ -0,0 +1,65 @@
+using System.IO;
+using System.Runtime.Serialization;
+using System.Xml.Serialization;
+using Ninject;
+
+namespace V3.Data.Internal
+{
+ /// <summary>
+ /// Default implementation of IOptionsManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class OptionsManager : IOptionsManager, IInitializable
+ {
+ /// <summary>
+ /// The current options.
+ /// </summary>
+ public Options Options { get; private set; }
+
+ private readonly IPathManager mPathManager;
+ private readonly XmlSerializer mSerializer = new XmlSerializer(typeof(Options));
+
+ /// <summary>
+ /// Creates a new OptionsManager.
+ /// </summary>
+ public OptionsManager(IPathManager pathManager)
+ {
+ mPathManager = pathManager;
+ }
+
+ public void Initialize()
+ {
+ Options = LoadOptions();
+ }
+
+ /// <summary>
+ /// Saves the current options to the hard disk.
+ /// </summary>
+ public void SaveOptions()
+ {
+ var stream = new FileStream(mPathManager.OptionsFile, FileMode.Create, FileAccess.Write);
+ mSerializer.Serialize(stream, Options);
+ stream.Close();
+ }
+
+ private Options LoadOptions()
+ {
+ if (!File.Exists(mPathManager.OptionsFile))
+ return new Options();
+ var stream = new FileStream(mPathManager.OptionsFile, FileMode.Open, FileAccess.Read);
+ try
+ {
+ return (Options)mSerializer.Deserialize(stream);
+ }
+ catch (SerializationException)
+ {
+ // ignore so far
+ }
+ finally
+ {
+ stream.Close();
+ }
+ return new Options();
+ }
+ }
+}
diff --git a/V3/Data/Internal/PathManager.cs b/V3/Data/Internal/PathManager.cs
new file mode 100644
index 0000000..3f4ad9d
--- /dev/null
+++ b/V3/Data/Internal/PathManager.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+
+namespace V3.Data.Internal {
+ /// <summary>
+ /// Default implementation of IPathManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class PathManager : IPathManager
+ {
+ /// <summary>
+ /// The base directory for persistent application data.
+ /// </summary>
+ public string AppDirectory { get; }
+
+ /// <summary>
+ /// The file to store the options in.
+ /// </summary>
+ public string OptionsFile { get; }
+
+ /// <summary>
+ /// The directory for save games.
+ /// </summary>
+ public string SaveGameDirectory { get; }
+
+ /// <summary>
+ /// Creates a new path manager and initializes the paths, but does not
+ /// create the directories if they don’t already exist.
+ /// </summary>
+ public PathManager()
+ {
+ var localAppDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ AppDirectory = $"{localAppDir}/V3";
+ SaveGameDirectory = $"{AppDirectory}/SaveGames";
+ OptionsFile = $"{AppDirectory}/Options.xml";
+ }
+
+ /// <summary>
+ /// Creates the application directories that do not already exist.
+ /// </summary>
+ public void CreateMissingDirectories()
+ {
+ string[] directories = { AppDirectory, SaveGameDirectory };
+ foreach (var directory in directories)
+ {
+ if (!Directory.Exists(directory))
+ Directory.CreateDirectory(directory);
+ }
+ }
+ }
+}
diff --git a/V3/Data/Internal/SaveGame.cs b/V3/Data/Internal/SaveGame.cs
new file mode 100644
index 0000000..9ff3586
--- /dev/null
+++ b/V3/Data/Internal/SaveGame.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace V3.Data.Internal
+{
+ // TODO: once the game state is getting larger, we have to separate the
+ // save game metadata from the game state.
+
+ /// <summary>
+ /// A save game that has a timestamp and a title, and that can store
+ /// the game state.
+ /// </summary>
+ [Serializable]
+ public sealed class SaveGame : ISaveGame
+ {
+ /// <summary>
+ /// The creation time of this save game in local time.
+ /// </summary>
+ public DateTime Timestamp { get; set; }
+
+ /// <summary>
+ /// The compability version of this save game.
+ /// </summary>
+ public int Version { get; set; }
+
+ /// <summary>
+ /// The data stored in this save game.
+ /// </summary>
+ public GameState GameState { get; set; }
+
+ /// <summary>
+ /// Empty constructor for serialization.
+ /// </summary>
+ private SaveGame()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new save game from the given data.
+ /// </summary>
+ /// <param name="timestamp">the creation time of the save game</param>
+ /// <param name="version">the compability version of the save game</param>
+ /// <param name="gameState">the game state to store in the save game</param>
+ internal SaveGame(DateTime timestamp, int version, GameState gameState)
+ {
+ Timestamp = timestamp;
+ Version = version;
+ GameState = gameState;
+ }
+
+ /// <summary>
+ /// Compares this save game object to another save game object based
+ /// on the creation time.
+ /// </summary>
+ /// <param name="saveGame">the save game to compare this save game
+ /// with</param>
+ /// <returns>a value less than zero if this save game has been saved
+ /// before the given save game, zero if they have been saved at the
+ /// same time and a value greater than zero if the given save game has
+ /// been saved before the given one</returns>
+ public int CompareTo(ISaveGame saveGame)
+ {
+ return Timestamp.CompareTo(saveGame.Timestamp);
+ }
+ }
+}
diff --git a/V3/Data/Internal/SaveGameManager.cs b/V3/Data/Internal/SaveGameManager.cs
new file mode 100644
index 0000000..1c52b50
--- /dev/null
+++ b/V3/Data/Internal/SaveGameManager.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Xml.Serialization;
+
+namespace V3.Data.Internal
+{
+ /// <summary>
+ /// Default implementation if ISaveGameManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class SaveGameManager : ISaveGameManager
+ {
+ private readonly IPathManager mPathManager;
+
+ private const int CurrentVersion = 1;
+ private readonly XmlSerializer mFormatter = new XmlSerializer(typeof(SaveGame));
+
+ /// <summary>
+ /// Creates a new SaveGameManager. The save game directory must already
+ /// be created.
+ /// </summary>
+ public SaveGameManager(IPathManager pathManager)
+ {
+ mPathManager = pathManager;
+ }
+
+ /// <summary>
+ /// Creates and persists a new save game of the given data with the
+ /// title.
+ /// </summary>
+ /// <param name="gameState">the data to store</param>
+ public void CreateSaveGame(GameState gameState)
+ {
+ var saveGame = new SaveGame(DateTime.Now, CurrentVersion, gameState);
+ var fileName = GetUniqueFileName();
+ var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write);
+ mFormatter.Serialize(stream, saveGame);
+ stream.Close();
+ }
+
+ /// <summary>
+ /// Loads all available save games and returns them ordered by the
+ /// creation date.
+ /// </summary>
+ /// <returns>a list of all available save games, orderd by creation
+ /// data</returns>
+ public List<ISaveGame> GetSaveGames()
+ {
+ var saveGames = new List<ISaveGame>();
+ foreach (var file in Directory.GetFiles(mPathManager.SaveGameDirectory))
+ {
+ var stream = new FileStream(file, FileMode.Open, FileAccess.Read);
+ try
+ {
+ var saveGame = (ISaveGame)mFormatter.Deserialize(stream);
+ if (saveGame.Version == CurrentVersion)
+ saveGames.Add(saveGame);
+ }
+ catch (SerializationException)
+ {
+ // ignore so far
+ }
+ stream.Close();
+ }
+ saveGames.Sort();
+ return saveGames;
+ }
+
+ private string GetUniqueFileName()
+ {
+ var index = 1;
+ var fileName = GetFileName(index);
+ while (File.Exists(fileName))
+ {
+ index++;
+ fileName = GetFileName(index);
+ }
+ return fileName;
+ }
+
+ private string GetFileName(int index)
+ {
+ return $"{mPathManager.SaveGameDirectory}/{index:000}.xml";
+ }
+ }
+}
diff --git a/V3/Data/Options.cs b/V3/Data/Options.cs
new file mode 100644
index 0000000..38670dc
--- /dev/null
+++ b/V3/Data/Options.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using V3.Camera;
+
+namespace V3.Data
+{
+ /// <summary>
+ /// The graphics options.
+ /// </summary>
+ [Serializable]
+ public sealed class Options
+ {
+ /// <summary>
+ /// All available screen resolutions.
+ /// </summary>
+ public static List<Point> Resolutions { get; } = new List<Point>
+ {
+ new Point(800, 480),
+ new Point(800, 600),
+ new Point(1024, 768),
+ new Point(1280, 800),
+ new Point(1280, 1024),
+ new Point(1366, 768),
+ new Point(1920, 1080)
+ };
+
+ /// <summary>
+ /// All available camera types.
+ /// </summary>
+ public static List<CameraType> CameraTypes { get; } =
+ Enum.GetValues(typeof (CameraType)).Cast<CameraType>().ToList();
+
+ /// <summary>
+ /// All available debug modes.
+ /// </summary>
+ public static List<DebugMode> DebugModes { get; } =
+ Enum.GetValues(typeof (DebugMode)).Cast<DebugMode>().ToList();
+
+ /// <summary>
+ /// All available volume settings.
+ /// </summary>
+ public static List<int> Volumes { get; } = new List<int>()
+ {
+ 10,
+ 20,
+ 30,
+ 40,
+ 50,
+ 60,
+ 70,
+ 80,
+ 90,
+ 100
+ };
+ private static readonly Point sDefaultResolution = Resolutions[0];
+ private static readonly bool sDefaultIsFullScreen = false;
+ private static readonly DebugMode sDefaultDebugMode = DebugMode.Off;
+ private static readonly CameraType sDefaultCameraType = CameraType.Scrolling;
+ private static readonly bool sDefaultIsMuted = false;
+ private static readonly int sDefaultVolume = 100;
+
+ /// <summary>
+ /// The current screen resolution.
+ /// </summary>
+ public Point Resolution { get; set; } = sDefaultResolution;
+
+ /// <summary>
+ /// True if the game should be run in full screen, otherwise false.
+ /// </summary>
+ public bool IsFullScreen { get; set; } = sDefaultIsFullScreen;
+
+ /// <summary>
+ /// The current debug mode.
+ /// </summary>
+ public DebugMode DebugMode { get; set; } = sDefaultDebugMode;
+
+ /// <summary>
+ /// The current camera type.
+ /// </summary>
+ public CameraType CameraType { get; set; } = sDefaultCameraType;
+
+ /// <summary>
+ /// True if the sound is muted, otherwise false.
+ /// </summary>
+ public bool IsMuted { get; set; } = sDefaultIsMuted;
+
+ /// <summary>
+ /// The volume to use for the sound (if the sound is not muted), range
+ /// 0 .. 100.
+ /// </summary>
+ public int Volume { get; set; } = sDefaultVolume;
+
+ /// <summary>
+ /// Returns the effective volume with regard to the mute and volume
+ /// settings.
+ /// </summary>
+ /// <returns>the effective volume that should be used for sound</returns>
+ public float GetEffectiveVolume()
+ {
+ return IsMuted ? 0 : ((float) Volume) / 100;
+ }
+ }
+}
diff --git a/V3/Effects/AbstractEffect.cs b/V3/Effects/AbstractEffect.cs
new file mode 100644
index 0000000..c3468c0
--- /dev/null
+++ b/V3/Effects/AbstractEffect.cs
@@ -0,0 +1,74 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Data;
+
+namespace V3.Effects
+{
+ public abstract class AbstractEffect : IEffect
+ {
+ private Texture2D mTexture;
+ private int mSpritesPerRow;
+ private int mTotalSprites;
+ private int mAnimationState;
+ private Rectangle mAnimationRectangle;
+ private SoundEffect mSoundEffect;
+ private SoundEffectInstance mSoundEffectInstance;
+
+ public bool IsPlaying { get; private set; }
+ protected virtual UpdatesPerSecond UpdatesPerSecond { get; } = new UpdatesPerSecond(24);
+ protected abstract string TextureFile { get; }
+ protected virtual Point SpriteSize { get; } = new Point(128, 128);
+ protected virtual string SoundFile { get; } = String.Empty;
+
+ public void LoadContent(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Effects/" + TextureFile);
+ mSpritesPerRow = mTexture.Width / SpriteSize.X;
+ mTotalSprites = mSpritesPerRow * (mTexture.Height / SpriteSize.Y);
+ if (SoundFile.Length != 0)
+ {
+ mSoundEffect = contentManager.Load<SoundEffect>("Sounds/" + SoundFile);
+ mSoundEffectInstance = mSoundEffect.CreateInstance();
+ }
+ }
+
+ public void PlayOnce(Point position, Point size, IOptionsManager optionsManager)
+ {
+ mAnimationRectangle = new Rectangle(position - size / new Point(2, 2), size);
+ IsPlaying = true;
+ if (SoundFile.Length != 0)
+ {
+ mSoundEffectInstance.Volume = optionsManager.Options.GetEffectiveVolume();
+ mSoundEffectInstance.Play();
+ }
+ }
+
+ public void Update(GameTime gameTime)
+ {
+ if (!IsPlaying) return;
+ if (UpdatesPerSecond.IsItTime(gameTime))
+ {
+ if (mAnimationState < mTotalSprites)
+ {
+ mAnimationState++;
+ }
+ else
+ {
+ mAnimationState = 0;
+ IsPlaying = false;
+ }
+ }
+ }
+
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ if (!IsPlaying) return;
+ spriteBatch.Draw(mTexture, mAnimationRectangle,
+ new Rectangle(new Point(mAnimationState % mSpritesPerRow * SpriteSize.X, mAnimationState / mSpritesPerRow * SpriteSize.Y), SpriteSize),
+ Color.White);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/BloodBang.cs b/V3/Effects/BloodBang.cs
new file mode 100644
index 0000000..f5e1f3c
--- /dev/null
+++ b/V3/Effects/BloodBang.cs
@@ -0,0 +1,8 @@
+namespace V3.Effects
+{
+ public class BloodBang : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "blood_hit_04";
+ protected override string SoundFile { get; } = "impactsplat01";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/BloodExplosion.cs b/V3/Effects/BloodExplosion.cs
new file mode 100644
index 0000000..baa38d4
--- /dev/null
+++ b/V3/Effects/BloodExplosion.cs
@@ -0,0 +1,11 @@
+namespace V3.Effects
+{
+ /// <summary>
+ /// A round explosion of blood.
+ /// </summary>
+ public sealed class BloodExplosion : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "blood_hit_08";
+ protected override string SoundFile { get; } = "impactsplat01";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/BloodFountain.cs b/V3/Effects/BloodFountain.cs
new file mode 100644
index 0000000..5684e34
--- /dev/null
+++ b/V3/Effects/BloodFountain.cs
@@ -0,0 +1,11 @@
+namespace V3.Effects
+{
+ /// <summary>
+ /// A small fountain of blood.
+ /// </summary>
+ public sealed class BloodFountain : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "blood_hit_02";
+ protected override string SoundFile { get; } = "impactsplat01";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/EffectsManager.cs b/V3/Effects/EffectsManager.cs
new file mode 100644
index 0000000..a175e34
--- /dev/null
+++ b/V3/Effects/EffectsManager.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Data;
+
+namespace V3.Effects
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class EffectsManager : IEffectsManager
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly List<IEffect> mActiveEffects = new List<IEffect>();
+
+ public EffectsManager(ContentManager contentManager, IOptionsManager optionsManager)
+ {
+ mContentManager = contentManager;
+ mOptionsManager = optionsManager;
+ }
+
+ public void Update(GameTime gameTime)
+ {
+ var doneEffects = new List<IEffect>();
+ foreach (var effect in mActiveEffects)
+ {
+ effect.Update(gameTime);
+ if (!effect.IsPlaying)
+ {
+ doneEffects.Add(effect);
+ }
+ }
+ foreach (var doneEffect in doneEffects)
+ {
+ mActiveEffects.Remove(doneEffect);
+ }
+ }
+
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ foreach (var effect in mActiveEffects)
+ {
+ effect.Draw(spriteBatch);
+ }
+ }
+
+ public void PlayOnce(IEffect effect, Point position, Point size)
+ {
+ effect.LoadContent(mContentManager);
+ mActiveEffects.Add(effect);
+ effect.PlayOnce(position, size, mOptionsManager);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/Explosion.cs b/V3/Effects/Explosion.cs
new file mode 100644
index 0000000..afcfdf4
--- /dev/null
+++ b/V3/Effects/Explosion.cs
@@ -0,0 +1,14 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Effects
+{
+ /// <summary>
+ /// A large explosion with sound.
+ /// </summary>
+ public class Explosion : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "explosion";
+ protected override Point SpriteSize { get; } = new Point(320, 240);
+ protected override string SoundFile { get; } = "explosion1";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/HorseEffect.cs b/V3/Effects/HorseEffect.cs
new file mode 100644
index 0000000..a09fe60
--- /dev/null
+++ b/V3/Effects/HorseEffect.cs
@@ -0,0 +1,11 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Effects
+{
+ public sealed class HorseEffect : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "quake";
+ protected override Point SpriteSize { get; } = new Point(256, 128);
+ protected override string SoundFile { get; } = "horse";
+ }
+}
diff --git a/V3/Effects/IEffect.cs b/V3/Effects/IEffect.cs
new file mode 100644
index 0000000..73cf1ef
--- /dev/null
+++ b/V3/Effects/IEffect.cs
@@ -0,0 +1,44 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Data;
+
+namespace V3.Effects
+{
+ /// <summary>
+ /// Interface for a single effect.
+ /// </summary>
+ public interface IEffect
+ {
+ /// <summary>
+ /// Is the effect playing at the moment?
+ /// </summary>
+ bool IsPlaying { get; }
+
+ /// <summary>
+ /// Play the specific effect once, do not loop.
+ /// </summary>
+ /// <param name="position">Position where effect should be played. Points to the middle of the effect animation.</param>
+ /// <param name="size">Size of the effect.</param>
+ /// <param name="optionsManager">For checking the volume of the sound if there is one.</param>
+ void PlayOnce(Point position, Point size, IOptionsManager optionsManager);
+
+ /// <summary>
+ /// Update the effect.
+ /// </summary>
+ /// <param name="gameTime">Game time used for checking animation duration.</param>
+ void Update(GameTime gameTime);
+
+ /// <summary>
+ /// Draw the effect.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ void Draw(SpriteBatch spriteBatch);
+
+ /// <summary>
+ /// Load graphics and possibly sound for the effect.
+ /// </summary>
+ /// <param name="contentManager">Content manager used.</param>
+ void LoadContent(ContentManager contentManager);
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/IEffectsManager.cs b/V3/Effects/IEffectsManager.cs
new file mode 100644
index 0000000..3ea6154
--- /dev/null
+++ b/V3/Effects/IEffectsManager.cs
@@ -0,0 +1,31 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Effects
+{
+ /// <summary>
+ /// Interface for managing visual effects like explosions and stuff.
+ /// </summary>
+ public interface IEffectsManager
+ {
+ /// <summary>
+ /// Update all effects.
+ /// </summary>
+ /// <param name="gameTime">Game time used for calculation effects duration.</param>
+ void Update(GameTime gameTime);
+
+ /// <summary>
+ /// Draw all effects.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ void Draw(SpriteBatch spriteBatch);
+
+ /// <summary>
+ /// Play an effect once, then delete it.
+ /// </summary>
+ /// <param name="effect">Which effect to play.</param>
+ /// <param name="position">Position where effect should be played. Points to the middle of the effect animation.</param>
+ /// <param name="size">Size of the effect.</param>
+ void PlayOnce(IEffect effect, Point position, Point size);
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/Quake.cs b/V3/Effects/Quake.cs
new file mode 100644
index 0000000..c08825d
--- /dev/null
+++ b/V3/Effects/Quake.cs
@@ -0,0 +1,11 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Effects
+{
+ public class Quake : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "quake";
+ protected override Point SpriteSize { get; } = new Point(256, 128);
+ protected override string SoundFile { get; } = "explodemini";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/SmokeBig.cs b/V3/Effects/SmokeBig.cs
new file mode 100644
index 0000000..3dfce2a
--- /dev/null
+++ b/V3/Effects/SmokeBig.cs
@@ -0,0 +1,10 @@
+namespace V3.Effects
+{
+ /// <summary>
+ /// A large ring of smoke, spreading over some area.
+ /// </summary>
+ public sealed class SmokeBig : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "particlefx_03";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/SmokeMedium.cs b/V3/Effects/SmokeMedium.cs
new file mode 100644
index 0000000..09447fa
--- /dev/null
+++ b/V3/Effects/SmokeMedium.cs
@@ -0,0 +1,13 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace V3.Effects
+{
+ /// <summary>
+ /// A medium sized ring of smoke, spreading over some area.
+ /// </summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global")]
+ public sealed class SmokeMedium : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "particlefx_04";
+ }
+} \ No newline at end of file
diff --git a/V3/Effects/SmokeSmall.cs b/V3/Effects/SmokeSmall.cs
new file mode 100644
index 0000000..f2f2dce
--- /dev/null
+++ b/V3/Effects/SmokeSmall.cs
@@ -0,0 +1,10 @@
+namespace V3.Effects
+{
+ /// <summary>
+ /// A small ring of smoke, spreading over some area.
+ /// </summary>
+ public sealed class SmokeSmall : AbstractEffect
+ {
+ protected override string TextureFile { get; } = "particlefx_05";
+ }
+} \ No newline at end of file
diff --git a/V3/Ellipse.cs b/V3/Ellipse.cs
new file mode 100644
index 0000000..076aff0
--- /dev/null
+++ b/V3/Ellipse.cs
@@ -0,0 +1,31 @@
+using System;
+using Microsoft.Xna.Framework;
+
+namespace V3
+{
+ public struct Ellipse
+ {
+ private Vector2 Center { get; }
+ private float Width { get; }
+ private float Height { get; }
+
+ public Rectangle BoundaryRectangle => new Rectangle((Center - new Vector2(Width / 2, Height / 2)).ToPoint(), new Vector2(Width, Height).ToPoint());
+
+ internal Ellipse(Vector2 center, float width, float height)
+ {
+ Center = center;
+ Width = width;
+ Height = height;
+ }
+
+ internal bool Contains(Vector2 position)
+ {
+ if (Math.Pow(position.X - Center.X, 2) / Math.Pow(Width/2, 2) +
+ Math.Pow(position.Y - Center.Y, 2) / Math.Pow(Height/2, 2) <= 1)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/V3/Faction.cs b/V3/Faction.cs
new file mode 100644
index 0000000..96b2f9b
--- /dev/null
+++ b/V3/Faction.cs
@@ -0,0 +1,7 @@
+namespace V3
+{
+ public enum Faction
+ {
+ Undead, Kingdom, Plebs
+ }
+} \ No newline at end of file
diff --git a/V3/Icon.ico b/V3/Icon.ico
new file mode 100644
index 0000000..38c64b5
--- /dev/null
+++ b/V3/Icon.ico
Binary files differ
diff --git a/V3/Input/IInputManager.cs b/V3/Input/IInputManager.cs
new file mode 100644
index 0000000..b092ebb
--- /dev/null
+++ b/V3/Input/IInputManager.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+
+namespace V3.Input
+{
+ /// <summary>
+ /// Watches the state of the mouse and keyboard and creates events if a
+ /// change (key pressed or released) is detected. To use this class, call
+ /// Update in every update round. After the update, you may access the
+ /// generated events in KeyEvents and MouseEvents. The input manager only
+ /// watches the mouse buttons and keys listed in sWatchedKeys and
+ /// sWatchedButtons.
+ /// </summary>
+ public interface IInputManager
+ {
+ /// <summary>
+ /// The key events that were generated during the last update. Reset
+ /// in the next update.
+ /// </summary>
+ ICollection<IKeyEvent> KeyEvents { get; }
+
+ /// <summary>
+ /// The mouse events that were generated during the last update. Reset in
+ /// the next update.
+ /// </summary>
+ ICollection<IMouseEvent> MouseEvents { get; }
+
+ /// <summary>
+ /// Updates the keyboard and mouse status and generates the key and mouse
+ /// events in KeyEvents and MouseEvents if changes were detected. Should
+ /// be called once every update, before doing something else.
+ /// </summary>
+ void Update();
+ }
+} \ No newline at end of file
diff --git a/V3/Input/IKeyEvent.cs b/V3/Input/IKeyEvent.cs
new file mode 100644
index 0000000..42b99c6
--- /dev/null
+++ b/V3/Input/IKeyEvent.cs
@@ -0,0 +1,20 @@
+using Microsoft.Xna.Framework.Input;
+
+namespace V3.Input
+{
+ /// <summary>
+ /// An event that is triggered if a key is pressed or released on the
+ /// keyboard.
+ /// </summary>
+ public interface IKeyEvent
+ {
+ /// <summary>
+ /// The key that was pressed or released.
+ /// </summary>
+ Keys Key { get; }
+ /// <summary>
+ /// The type of the event (key pressed or released?).
+ /// </summary>
+ KeyState KeyState { get; }
+ }
+} \ No newline at end of file
diff --git a/V3/Input/IMouseEvent.cs b/V3/Input/IMouseEvent.cs
new file mode 100644
index 0000000..37b221d
--- /dev/null
+++ b/V3/Input/IMouseEvent.cs
@@ -0,0 +1,34 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace V3.Input
+{
+ /// <summary>
+ /// An event that is sent when a mouse button is pressed or released.
+ /// </summary>
+ public interface IMouseEvent
+ {
+ /// <summary>
+ /// The mouse button that was pressed or released.
+ /// </summary>
+ MouseButton MouseButton { get; }
+ /// <summary>
+ /// The state of the mouse button (pressed or released?).
+ /// </summary>
+ ButtonState ButtonState { get; }
+ /// <summary>
+ /// The position where the mouse button was pressed the last time.
+ /// </summary>
+ Point PositionPressed { get; }
+ /// <summary>
+ /// The position where the mouse button was released if this is a
+ /// release event, null otherwise.
+ /// </summary>
+ Point? PositionReleased { get; }
+ /// <summary>
+ /// True if PositionReleased is a valid on-screen position, otherwise
+ /// false.
+ /// </summary>
+ bool ReleasedOnScreen { get; }
+ }
+}
diff --git a/V3/Input/IMouseEventHandler.cs b/V3/Input/IMouseEventHandler.cs
new file mode 100644
index 0000000..7c8701c
--- /dev/null
+++ b/V3/Input/IMouseEventHandler.cs
@@ -0,0 +1,14 @@
+namespace V3.Input
+{
+ /// <summary>
+ /// Handles mouse events.
+ /// </summary>
+ public interface IMouseEventHandler
+ {
+ /// <summary>
+ /// Handle the given mouse event, if applicable.
+ /// </summary>
+ /// <param name="mouseEvent">the mouse event to handle</param>
+ void HandleMouseEvent(IMouseEvent mouseEvent);
+ }
+}
diff --git a/V3/Input/Internal/InputManager.cs b/V3/Input/Internal/InputManager.cs
new file mode 100644
index 0000000..8e6ce2e
--- /dev/null
+++ b/V3/Input/Internal/InputManager.cs
@@ -0,0 +1,141 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+
+namespace V3.Input.Internal
+{
+ /// <summary>
+ /// Watches the state of the mouse and keyboard and creates events if a
+ /// change (key pressed or released) is detected. To use this class, call
+ /// Update in every update round. After the update, you may access the
+ /// generated events in KeyEvents and MouseEvents. The input manager only
+ /// watches the mouse buttons and keys listed in sWatchedKeys and
+ /// sWatchedButtons.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class InputManager : IInputManager, IInitializable
+ {
+ /// <summary>
+ /// The key events that were generated during the last update. Reset
+ /// in the next update.
+ /// </summary>
+ public ICollection<IKeyEvent> KeyEvents { get; } = new HashSet<IKeyEvent>();
+ /// <summary>
+ /// The mouse events that were generated during the last update. Reset in
+ /// the next update.
+ /// </summary>
+ public ICollection<IMouseEvent> MouseEvents { get; } = new HashSet<IMouseEvent>();
+
+ private static readonly ICollection<Keys> sWatchedKeys = new List<Keys> { Keys.Enter, Keys.Escape, Keys.E, Keys.L, Keys.Q, Keys.S, Keys.F1, Keys.F2 , Keys.F3, Keys.F4, Keys.F5, Keys.F6, Keys.F7, Keys.F8 };
+
+ private static readonly ICollection<MouseButton> sWatchedButtons = new List<MouseButton> { MouseButton.Left, MouseButton.Right, MouseButton.Middle };
+
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ private readonly IDictionary<Keys, KeyState> mKeyStates = new Dictionary<Keys, KeyState>();
+
+ private readonly IDictionary<MouseButton, ButtonState> mButtonStates = new Dictionary<MouseButton, ButtonState>();
+
+ private readonly IDictionary<MouseButton, Point?> mButtonPositions = new Dictionary<MouseButton, Point?>();
+
+ /// <summary>
+ /// Creates a new input manager.
+ /// </summary>
+ public InputManager(GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public void Initialize()
+ {
+ foreach (var key in sWatchedKeys)
+ mKeyStates.Add(key, KeyState.Up);
+ foreach (var button in sWatchedButtons)
+ {
+ mButtonStates.Add(button, ButtonState.Released);
+ mButtonPositions.Add(button, null);
+ }
+ }
+
+ /// <summary>
+ /// Updates the keyboard and mouse status and generates the key and mouse
+ /// events in KeyEvents and MouseEvents if changes were detected. Should
+ /// be called once every update, before doing something else.
+ /// </summary>
+ public void Update()
+ {
+ UpdateKeyboard();
+ UpdateMouse();
+ }
+
+ private void UpdateKeyboard()
+ {
+ KeyEvents.Clear();
+
+ var state = Keyboard.GetState();
+ foreach (var key in sWatchedKeys)
+ {
+ var newState = state[key];
+ if (newState != mKeyStates[key])
+ {
+ mKeyStates[key] = newState;
+ KeyEvents.Add(new KeyEvent(key, newState));
+ }
+ }
+ }
+
+ private void UpdateMouse()
+ {
+ MouseEvents.Clear();
+
+ var state = Mouse.GetState();
+ foreach (var button in sWatchedButtons)
+ {
+ var newState = GetButtonState(state, button);
+ if (newState != mButtonStates[button])
+ {
+ var position = new Point(state.X, state.Y);
+ var positionPressed = position;
+ Point? positionReleased = null;
+ if (newState == ButtonState.Released)
+ {
+ if (mButtonPositions[button].HasValue)
+ positionPressed = mButtonPositions[button].Value;
+ positionReleased = position;
+ }
+
+ mButtonStates[button] = newState;
+ mButtonPositions[button] = position;
+
+ var releasedOnScreen = false;
+ if (positionReleased.HasValue)
+ releasedOnScreen = IsPointOnScreen(positionReleased.Value);
+
+ MouseEvents.Add(new MouseEvent(button, newState, positionPressed, positionReleased, releasedOnScreen));
+ }
+ }
+ }
+
+ private bool IsPointOnScreen(Point point)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ return point.X >= 0 && point.X <= viewport.Width && point.Y >= 0 && point.Y <= viewport.Height;
+ }
+
+ private static ButtonState GetButtonState(MouseState state, MouseButton button)
+ {
+ switch (button)
+ {
+ case MouseButton.Left:
+ return state.LeftButton;
+ case MouseButton.Right:
+ return state.RightButton;
+ case MouseButton.Middle:
+ return state.MiddleButton;
+ default:
+ return state.LeftButton;
+ }
+ }
+ }
+}
diff --git a/V3/Input/Internal/KeyEvent.cs b/V3/Input/Internal/KeyEvent.cs
new file mode 100644
index 0000000..b4a450e
--- /dev/null
+++ b/V3/Input/Internal/KeyEvent.cs
@@ -0,0 +1,32 @@
+using Microsoft.Xna.Framework.Input;
+
+namespace V3.Input.Internal
+{
+ /// <summary>
+ /// Default implementation of an event that is triggered if a key is
+ /// pressed or released on the keyboard.
+ /// </summary>
+ internal sealed class KeyEvent : IKeyEvent
+ {
+ /// <summary>
+ /// The key that was pressed or released.
+ /// </summary>
+ public Keys Key { get; }
+ /// <summary>
+ /// The type of the event (key pressed or released?).
+ /// </summary>
+ public KeyState KeyState { get; }
+
+ /// <summary>
+ /// Creates a new key event with the given data.
+ /// </summary>
+ /// <param name="key">the key that was pressed or released</param>
+ /// <param name="keyState">the type of the event (presesd or
+ /// released?)</param>
+ public KeyEvent(Keys key, KeyState keyState)
+ {
+ Key = key;
+ KeyState = keyState;
+ }
+ }
+}
diff --git a/V3/Input/Internal/MouseEvent.cs b/V3/Input/Internal/MouseEvent.cs
new file mode 100644
index 0000000..f8024aa
--- /dev/null
+++ b/V3/Input/Internal/MouseEvent.cs
@@ -0,0 +1,57 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Input;
+
+namespace V3.Input.Internal
+{
+ /// <summary>
+ /// Default implementation of an event that is sent when a mouse button is
+ /// pressed or released.
+ /// </summary>
+ internal sealed class MouseEvent : IMouseEvent
+ {
+ /// <summary>
+ /// The mouse button that was pressed or released.
+ /// </summary>
+ public MouseButton MouseButton { get; }
+ /// <summary>
+ /// The state of the mouse button (pressed or released?).
+ /// </summary>
+ public ButtonState ButtonState { get; }
+ /// <summary>
+ /// The position where the mouse button was pressed the last time.
+ /// </summary>
+ public Point PositionPressed { get; }
+ /// <summary>
+ /// The position where the mouse button was released if this is a
+ /// release event, null otherwise.
+ /// </summary>
+ public Point? PositionReleased { get; }
+ /// <summary>
+ /// True if PositionReleased is a valid on-screen position, otherwise
+ /// false.
+ /// </summary>
+ public bool ReleasedOnScreen { get; }
+
+ /// <summary>
+ /// Creates a new mouse event with the given data.
+ /// </summary>
+ /// <param name="mouseButton">the mouse button that was pressed or
+ /// released</param>
+ /// <param name="buttonState">the type of the event (pressed or
+ /// released?)</param>
+ /// <param name="positionPressed">the position of the last press of
+ /// the button</param>
+ /// <param name="positionReleased">the position of the release of the
+ /// button if this is a release event, or null otherwise</param>
+ /// <param name="releasedOnScreen">true if positionReleased is a valid
+ /// on-screen position.</param>
+ public MouseEvent(MouseButton mouseButton, ButtonState buttonState, Point positionPressed, Point? positionReleased, bool releasedOnScreen)
+ {
+ MouseButton = mouseButton;
+ ButtonState = buttonState;
+ PositionPressed = positionPressed;
+ PositionReleased = positionReleased;
+ ReleasedOnScreen = releasedOnScreen;
+ }
+ }
+}
diff --git a/V3/Input/MouseButton.cs b/V3/Input/MouseButton.cs
new file mode 100644
index 0000000..616872b
--- /dev/null
+++ b/V3/Input/MouseButton.cs
@@ -0,0 +1,9 @@
+namespace V3.Input
+{
+ public enum MouseButton
+ {
+ Left,
+ Right,
+ Middle
+ }
+}
diff --git a/V3/Map/AbstractLayer.cs b/V3/Map/AbstractLayer.cs
new file mode 100644
index 0000000..efc626d
--- /dev/null
+++ b/V3/Map/AbstractLayer.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Objects;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// A drawable map layer usually created from a Tiled map file.
+ /// </summary>
+ public abstract class AbstractLayer
+ {
+ private const int CellHeight = Constants.CellHeight;
+ private const int CellWidth = Constants.CellWidth;
+
+ private readonly int mTileWidth;
+ private readonly int mTileHeight;
+ private readonly int mMapWidth;
+ private readonly int mMapHeight;
+ private readonly List<IGameObject> mTextureObjects = new List<IGameObject>();
+ private readonly int[,] mTileArray;
+ private readonly SortedList<int, Tileset> mTilesets;
+
+ protected AbstractLayer(int tileWidth,
+ int tileHeight,
+ int mapWidth,
+ int mapHeight,
+ int[,] tileArray,
+ SortedList<int, Tileset> tilesets)
+ {
+ mTileWidth = tileWidth;
+ mTileHeight = tileHeight;
+ mMapWidth = mapWidth;
+ mMapHeight = mapHeight;
+ mTilesets = tilesets;
+ if (tileArray.Length == mapWidth * mapHeight)
+ {
+ mTileArray = tileArray;
+ }
+ else
+ {
+ throw new Exception("Error constructing map layer. Map size does not fit the map description.");
+ }
+ }
+
+ /// <summary>
+ /// Create the map objects according to the given map array.
+ /// </summary>
+ public void CreateObjects()
+ {
+ var firstgridList = mTilesets.Keys;
+ for (int i = 0; i < mMapHeight; i++)
+ {
+ int horizontalOffset = (i % 2 == 0) ? (-mTileWidth / 2) : 0;
+ for (int j = 0; j < mMapWidth; j++)
+ {
+ int tileId = mTileArray[i, j];
+ // Checks which tileset needs to be used for the specific tile ID at position [i, j]
+ for (int k = firstgridList.Count - 1; k >= 0; k--)
+ {
+ if (tileId == 0)
+ {
+ // This does generally nothing. But you can overwrite GenerateNullObject() for other behaviour.
+ TextureObject objectToInsert = GenerateNullObject();
+ if (objectToInsert != null)
+ {
+ mTextureObjects.Add(objectToInsert);
+ }
+ break;
+ }
+ else if (tileId >= firstgridList[k])
+ {
+ Tileset tileset = mTilesets.Values[k];
+ int firstgrid = firstgridList[k];
+ Point position = SelectPosition(j, i, horizontalOffset);
+ Point textureSize = new Point(tileset.TileWidth, tileset.TileHeight);
+ Point destination = SelectDestination(j, i, horizontalOffset, tileset.OffsetX, tileset.TileHeight, tileset.OffsetY);
+ Point source = SelectSource(tileId, firstgrid, tileset.TileWidth, tileset.TileHeight, tileset.Columns);
+ IGameObject objectToInsert;
+ if (tileset.Name == "houses_rear" || tileset.Name == "houses_front")
+ {
+ if (source.Y < textureSize.Y * 2)
+ {
+ int initialDamage = 0;
+ if (source.X / textureSize.X == 1)
+ {
+ initialDamage = 50;
+ }
+ else if (source.X / textureSize.X == 2)
+ {
+ initialDamage = 80;
+ }
+ IBuilding building = new Woodhouse(position.ToVector2(), new Rectangle(destination, textureSize), tileset.Name, source.Y % 384 == 0 ? BuildingFace.SW : BuildingFace.NO);
+ building.TakeDamage(initialDamage);
+ objectToInsert = building;
+ }
+ else
+ {
+ int initialDamage = 0;
+ if (source.X / textureSize.X == 1)
+ {
+ initialDamage = 60;
+ }
+ else if (source.X / textureSize.X == 2)
+ {
+ initialDamage = 100;
+ }
+ IBuilding building = new Forge(position.ToVector2(), new Rectangle(destination, textureSize), tileset.Name, source.Y % 384 == 0 ? BuildingFace.SW : BuildingFace.NO);
+ building.TakeDamage(initialDamage);
+ objectToInsert = building;
+ }
+ }
+ else if (tileset.Name == "castle")
+ {
+ IBuilding building = new Objects.Castle(position.ToVector2(), new Rectangle(destination, textureSize), tileset.Name, BuildingFace.SW);
+ objectToInsert = building;
+ }
+ else
+ {
+ objectToInsert = new TextureObject(position,
+ destination,
+ textureSize,
+ source,
+ tileset.Name);
+ }
+ mTextureObjects.Add(objectToInsert);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ protected virtual TextureObject GenerateNullObject()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Loads the image files needed for drawing the tilesets.
+ /// </summary>
+ /// <param name="contentManager">Content manager used for loading the ressources.</param>
+ public void LoadContent(ContentManager contentManager)
+ {
+ mTextureObjects.ForEach(o => o.LoadContent(contentManager));
+ }
+
+ /// <summary>
+ /// Draws only the parts of the map which are visible. More efficient than the other Draw-Method.
+ /// Not very robust and maybe does not work correctly most layers.
+ /// This is because of gaps in the list of game objects.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ /// <param name="camera">Needed to tell which objects of the map are looked upon.</param>
+ public void Draw(SpriteBatch spriteBatch, ICamera camera)
+ {
+ int tilesHorizontal = camera.ScreenSize.X / mTileWidth;
+ int tilesVertical = camera.ScreenSize.Y * 2 / mTileHeight;
+ int horizontalStart = camera.ScreenRectangle.X / mTileWidth;
+ int verticalStart = camera.ScreenRectangle.Y * 2 / mTileHeight;
+ /*
+ for (int i = 0; i < tilesVertical; i++)
+ {
+ for (int j = 0; j < tilesHorizontal; j++)
+ {
+ mTextureObjects[horizontalStart + j].Draw(spriteBatch);
+ }
+ }
+ */
+ for (int j = 0; j < tilesVertical + 2; j++)
+ {
+ for (int i = 0; i < tilesHorizontal + 2; i++)
+ {
+ int index = i + horizontalStart + (j + verticalStart) * mMapWidth;
+ if (index < mTextureObjects.Count)
+ {
+ mTextureObjects[index].Draw(spriteBatch);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Extract a collision grid from the map layer. Used in pathfinding.
+ /// </summary>
+ /// <returns>A two dimensional boolean collision grid.</returns>
+ public bool[,] ExtractCollisions()
+ {
+ int gridHeight = (mMapHeight - 1) * mTileHeight/ CellHeight / 2;
+ int gridWidth = (mMapWidth - 1) * mTileWidth / CellWidth;
+ bool[,] collisionGrid = new bool[gridHeight, gridWidth];
+ var firstgridList = mTilesets.Keys;
+ for (int i = 0; i < mMapHeight; i++)
+ {
+ for (int j = 0; j < mMapWidth; j++)
+ {
+ int tileId = mTileArray[i, j];
+ for (int k = firstgridList.Count - 1; k >= 0; k--)
+ {
+ if (tileId >= firstgridList[k])
+ {
+ Tileset tileset = mTilesets.Values[k];
+ int firstgrid = firstgridList[k];
+ tileId -= firstgrid;
+ bool[,] collisionData;
+ // Is there even collision data for the specific tile ID?
+ if (tileset.TileCollisions.TryGetValue(tileId, out collisionData))
+ {
+ int cellOffset = (i % 2 == 0 ? -mTileWidth / 2 : 0) / CellWidth;
+ int cellsHorizontal = mTileWidth / CellWidth;
+ int cellsVertical = mTileHeight / CellHeight;
+ int iStart = (i - 1) * cellsVertical / 2 + cellsVertical - tileset.CollisionHeight + tileset.OffsetY / CellHeight;
+ int jStart = j * cellsHorizontal + cellOffset + tileset.OffsetX / CellWidth;
+ for (int iData = 0; iData < tileset.CollisionHeight; iData++)
+ {
+ for (int jData = 0; jData < tileset.CollisionWidth; jData++)
+ {
+ // Do we even need to update collisionGrid?
+ if (iStart + iData >= 0 && iStart + iData < gridHeight && jStart + jData >= 0 && jStart + jData < gridWidth &&
+ collisionData[iData, jData] && !collisionGrid[iStart + iData, jStart + jData])
+ {
+ collisionGrid[iStart + iData, jStart + jData] = collisionData[iData, jData];
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return collisionGrid;
+ }
+
+ public List<IGameObject> ExtractObjects()
+ {
+ return mTextureObjects;
+ }
+
+ private Point SelectDestination(int x, int y, int xOffset, int tileXOffset, int tileHeight, int tileYOffset)
+ {
+ return new Point(x * mTileWidth + xOffset + tileXOffset,
+ (y - 1) * (mTileHeight / 2) - tileHeight + mTileHeight + tileYOffset);
+ }
+
+ private Point SelectSource(int tileId, int firstgrid, int tileWidth, int tileHeight, int tilesPerRow)
+ {
+ return new Point((tileId - firstgrid) % tilesPerRow * tileWidth, (tileId - firstgrid) / tilesPerRow * tileHeight);
+ }
+
+ private Point SelectPosition(int x, int y, int xOffset)
+ {
+ return new Point(x * mTileWidth + xOffset + mTileWidth / 2, y * (mTileHeight / 2));
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Map/Area.cs b/V3/Map/Area.cs
new file mode 100644
index 0000000..5712c6e
--- /dev/null
+++ b/V3/Map/Area.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using V3.Objects;
+
+namespace V3.Map
+{
+ public enum AreaType
+ {
+ Village, // has no soldiers or knights, only peasants
+ Castle, // many knights patrolling around
+ Graveyard // here you can respawn zombies
+ }
+
+ /// <summary>
+ /// Holding area data of the map. Later used for generating enemies.
+ /// </summary>
+ public sealed class Area
+ {
+ private const int DistanceHorizontal = 64;
+ private const int DistanceVertical = 32;
+ private readonly AreaType mType;
+ private Rectangle mArea;
+ private readonly double mDensity;
+ private readonly double mChance;
+
+ /// <summary>
+ /// Gets the area type.
+ /// </summary>
+ public AreaType Type => mType;
+
+ /// <summary>
+ /// Creates a new area for generating population.
+ /// </summary>
+ /// <param name="type">Which type of area. Determines which population is spawned.</param>
+ /// <param name="data">The size and position of the area.</param>
+ /// <param name="density">The population density. Together with chance.</param>
+ /// <param name="chance">The chance that a creature is actually created.</param>
+ /// <param name="name">Name of the area as shown in the game.</param>
+ // ReSharper disable once UnusedParameter.Local
+ public Area(string type, Rectangle data, double density = 0d, double chance = 0d, string name = "")
+ {
+ switch (type)
+ {
+ case "village":
+ mType = AreaType.Village;
+ break;
+ case "castle":
+ mType = AreaType.Castle;
+ break;
+ case "graveyard":
+ mType = AreaType.Graveyard;
+ break;
+ default:
+ throw new Exception("Error parsing the map. There is no behaviour defined for objects of type " + type + ".");
+ }
+ if (density > 1d || chance > 1d || density < 0d || chance < 0d)
+ {
+ throw new Exception("Error when parsing area data from map. Density and/or chance is not in range 0.0 to 1.0.");
+ }
+ mArea = data;
+ mDensity = density;
+ mChance = chance;
+ }
+
+ /// <summary>
+ /// Creates the initial population for this area.
+ /// </summary>
+ /// <param name="creatureFactory">The factory used for creating creatures.</param>
+ /// <param name="pathfinder">Used for checking collisions when creating population.</param>
+ /// <returns></returns>
+ public List<ICreature> GetPopulation(CreatureFactory creatureFactory, Pathfinder pathfinder)
+ {
+ var population = new List<ICreature>();
+ if (mDensity <= 0) return population; // Catch division by zero.
+ var rndInt = new Random();
+ var rnd = new Random();
+ for (double i = DistanceVertical / mDensity + mArea.Y; i < mArea.Height + mArea.Y; i += DistanceVertical / mDensity )
+ {
+ for (double j = DistanceHorizontal / mDensity + mArea.X; j < mArea.Width + mArea.X; j += DistanceHorizontal / mDensity )
+ {
+ if (mChance < rnd.NextDouble()) continue;
+ var position = new Vector2((float) j, (float) i);
+ if (mType == AreaType.Village)
+ {
+ ICreature peasant;
+ if (rnd.NextDouble() < 0.5d)
+ {
+ peasant = creatureFactory.CreateMalePeasant(position, (MovementDirection)rndInt.Next(8));
+ }
+ else
+ {
+ peasant = creatureFactory.CreateFemalePeasant(position, (MovementDirection)rndInt.Next(8));
+ }
+ if (!pathfinder.AllWalkable(peasant.BoundaryRectangle)) continue;
+ population.Add(peasant);
+ }
+ else if (mType == AreaType.Castle)
+ {
+ ICreature guard = creatureFactory.CreateKingsGuard(position, (MovementDirection)rndInt.Next(8));
+ if (!pathfinder.AllWalkable(guard.BoundaryRectangle)) continue;
+ population.Add(guard);
+ }
+ }
+ }
+ return population;
+ }
+
+ /// <summary>
+ /// Is a given creature standing in the area?
+ /// </summary>
+ /// <param name="creature">Check for this creature.</param>
+ /// <returns></returns>
+ public bool Contains(ICreature creature)
+ {
+ return mArea.Contains(creature.Position.ToPoint());
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Map/Constants.cs b/V3/Map/Constants.cs
new file mode 100644
index 0000000..2de2e25
--- /dev/null
+++ b/V3/Map/Constants.cs
@@ -0,0 +1,12 @@
+namespace V3.Map
+{
+ /// <summary>
+ /// Constants for describing the size of the cells of the collision grid.
+ /// Important for pathfinding.
+ /// </summary>
+ public static class Constants
+ {
+ public const int CellHeight = 16;
+ public const int CellWidth = 16;
+ }
+} \ No newline at end of file
diff --git a/V3/Map/FloorLayer.cs b/V3/Map/FloorLayer.cs
new file mode 100644
index 0000000..ebe05f0
--- /dev/null
+++ b/V3/Map/FloorLayer.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using V3.Objects;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// The floor of the map consisting of the ground to walk on, grass or water.
+ /// </summary>
+ public sealed class FloorLayer : AbstractLayer
+ {
+ public FloorLayer(int tileWidth, int tileHeight, int mapWidth, int mapHeight, int[,] tileArray, SortedList<int, Tileset> tilesets)
+ : base(tileWidth, tileHeight, mapWidth, mapHeight, tileArray, tilesets)
+ {
+ }
+
+ protected override TextureObject GenerateNullObject()
+ {
+ return new TextureObject();
+ }
+ }
+}
diff --git a/V3/Map/FogOfWar.cs b/V3/Map/FogOfWar.cs
new file mode 100644
index 0000000..a243b60
--- /dev/null
+++ b/V3/Map/FogOfWar.cs
@@ -0,0 +1,107 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Objects;
+
+namespace V3.Map
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class FogOfWar
+ {
+ private const int FogRange = 64;
+ private const int SightRadius = 1000;
+ private Point mMapSize;
+ private Texture2D mFog;
+ private readonly List<Rectangle> mFogRectangle = new List<Rectangle>();
+
+ /// <summary>
+ /// Get the size of the map and create an boolean array
+ /// </summary>
+ /// <param name="size">the size of the map</param>
+ public void LoadGrid(Point size)
+ {
+ mMapSize = size;
+ CreateArray();
+ }
+
+ /// <summary>
+ /// An array so save whether the sprites already walked on this area
+ /// </summary>
+ private void CreateArray()
+ {
+ for (int i = -FogRange; i < mMapSize.Y; i += FogRange)
+ {
+ for (int j = -FogRange * 2; j < mMapSize.X; j += FogRange)
+ {
+ mFogRectangle.Add(new Rectangle(j, i, mFog.Width, mFog.Height));
+ }
+ }
+ }
+
+ /// <summary>
+ /// The position from creatures which can open the fog
+ /// </summary>
+ /// <param name="creature">creatures which are able to open the fog</param>
+ public void Update(ICreature creature)
+ {
+ Ellipse creatureEllipse = new Ellipse(creature.Position, SightRadius, SightRadius);
+ var markedForDeletion = new List<Rectangle>();
+ foreach (var fog in mFogRectangle)
+ {
+ if (!creature.IsDead && creatureEllipse.Contains(fog.Center.ToVector2()))
+ {
+ markedForDeletion.Add(fog);
+ }
+ }
+ foreach (var fogToDelete in markedForDeletion)
+ {
+ mFogRectangle.Remove(fogToDelete);
+ }
+ }
+
+ /// <summary>
+ /// The sprite for the fog
+ /// </summary>
+ /// <param name="content"></param>
+ public void LoadContent(ContentManager content)
+ {
+ mFog = content.Load<Texture2D>("Sprites/cloud");
+ }
+
+ /// <summary>
+ /// Try to draw fog of war efficiently.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ public void DrawFog(SpriteBatch spriteBatch)
+ {
+ /*
+ var screen = camera.ScreenRectangle;
+ int fogPerRow = (mMapSize.X + FogRange) / FogRange;
+ int fogPerColumn = (mMapSize.Y + FogRange) / FogRange;
+ for (int i = screen.Y / FogRange; i < (screen.Y + screen.Height) / FogRange; i++)
+ {
+ for (int j = screen.X / FogRange; j < (screen.X + screen.Width) / FogRange; j++)
+ {
+ spriteBatch.Draw(mFog, mFogRectangle[i * fogPerRow], Color.Black);
+ }
+ }
+ */
+ foreach (var fog in mFogRectangle)
+ {
+ spriteBatch.Draw(mFog, fog, Color.Black);
+ }
+ }
+
+ public void SetFog(List<Rectangle> fog)
+ {
+ mFogRectangle.Clear();
+ mFogRectangle.AddRange(fog);
+ }
+
+ public List<Rectangle> GetFog()
+ {
+ return mFogRectangle;
+ }
+ }
+}
diff --git a/V3/Map/IMapManager.cs b/V3/Map/IMapManager.cs
new file mode 100644
index 0000000..9333d63
--- /dev/null
+++ b/V3/Map/IMapManager.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Objects;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// Manager for loading and drawing game maps. Also holds information about map attributes.
+ /// </summary>
+ [SuppressMessage("ReSharper", "UnusedMember.Global")]
+ public interface IMapManager
+ {
+ /// <summary>
+ /// A list of map areas (rectangle-sized).
+ /// </summary>
+ List<Area> Areas { get; }
+
+ /// <summary>
+ /// Size of the shown map in pixels.
+ /// </summary>
+ Point SizeInPixel { get; }
+ /// <summary>
+ /// Size of the map in tiles. (Some tiles are cut off at the edges.)
+ /// </summary>
+ Point SizeInTiles { get; }
+ /// <summary>
+ /// Size of a single tile in pixels.
+ /// </summary>
+ Point TileSize { get; }
+ /// <summary>
+ /// Number of cells the pathfinding grid consists of.
+ /// </summary>
+ Point PathfindingGridSize { get; }
+ /// <summary>
+ /// Size of a single cell of the pathfinding grid in pixels.
+ /// </summary>
+ Point PathfindingCellSize { get; }
+ /// <summary>
+ /// File name of the loaded map (without suffix).
+ /// </summary>
+ string FileName { get; }
+ /// <summary>
+ /// Efficiently draw the floor layer. Only draw the tiles seen by the camera.
+ /// </summary>
+ /// <param name="spriteBatch"></param>
+ /// <param name="camera"></param>
+ void DrawFloor(SpriteBatch spriteBatch, ICamera camera);
+ /// <summary>
+ /// Load a map file and create the map layers and pathfinding information.
+ /// </summary>
+ /// <param name="fileName">Name of the map file (without suffix).</param>
+ void Load(string fileName);
+ /// <summary>
+ /// Returns all objects in the objects layer.
+ /// </summary>
+ /// <returns>List of all static game objects imported from the map.</returns>
+ List<IGameObject> GetObjects();
+ /// <summary>
+ /// Returns the pathfinding grid for passing to the pathfinder.
+ /// </summary>
+ /// <returns>A grid used for pathfinding.</returns>
+ PathfindingGrid GetPathfindingGrid();
+ /// <summary>
+ /// Efficiently draw the pathfinding grid. For debugging purposes.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ /// <param name="camera">Current camera for calculating the shown screen.</param>
+ void DrawPathfindingGrid(SpriteBatch spriteBatch, ICamera camera);
+
+ /// <summary>
+ /// Draws the minimap to specified position.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ /// <param name="position">Where to draw the minimap and which size.</param>
+ void DrawMinimap(SpriteBatch spriteBatch, Rectangle position);
+
+ /// <summary>
+ /// Automatically creates an initial population from the map data and returns it.
+ /// </summary>
+ /// <param name="creatureFactory">Factory for creating creatues.</param>
+ /// <param name="pathfinder">Pathfinder is used for checking collisions when creating creatures.</param>
+ /// <returns>Initial population in a list.</returns>
+ List<ICreature> GetPopulation(CreatureFactory creatureFactory, Pathfinder pathfinder);
+ }
+} \ No newline at end of file
diff --git a/V3/Map/MapManager.cs b/V3/Map/MapManager.cs
new file mode 100644
index 0000000..f62278c
--- /dev/null
+++ b/V3/Map/MapManager.cs
@@ -0,0 +1,97 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Objects;
+
+namespace V3.Map
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class MapManager : IMapManager
+ {
+ private TiledParser mTiledParser;
+ private FloorLayer mFloorLayer;
+ private ObjectLayer mObjectLayer;
+ private List<Area> mAreas;
+ private PathfindingGrid mPathfindingGrid;
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ public List<Area> Areas => mAreas;
+
+ public Point SizeInPixel { get; private set; }
+ public Point SizeInTiles { get; private set; }
+ public Point TileSize { get; private set; }
+ public Point PathfindingGridSize { get; private set; }
+ public Point PathfindingCellSize { get; private set; }
+ public string FileName { get; private set; }
+
+ public MapManager(ContentManager contentManager, GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public void DrawFloor(SpriteBatch spriteBatch, ICamera camera)
+ {
+ mFloorLayer.Draw(spriteBatch, camera);
+ }
+
+ public void Load(string fileName)
+ {
+ mTiledParser = new TiledParser();
+ // Parse map data.
+ mTiledParser.Parse(fileName);
+ FileName = fileName;
+ TileSize = new Point(mTiledParser.TileWidth, mTiledParser.TileHeight);
+ SizeInTiles = new Point(mTiledParser.MapWidth, mTiledParser.MapHeight);
+ SizeInPixel = new Point((SizeInTiles.X - 1) * TileSize.X, SizeInTiles.Y / 2 * TileSize.Y - TileSize.Y / 2);
+ // Create floor layer of the map.
+ mFloorLayer = new FloorLayer(mTiledParser.TileWidth, mTiledParser.TileHeight, mTiledParser.MapWidth, mTiledParser.MapHeight, mTiledParser.MapLayers[0], mTiledParser.TileSets);
+ mFloorLayer.CreateObjects();
+ mFloorLayer.LoadContent(mContentManager);
+ // Create object layer of the map.
+ mObjectLayer = new ObjectLayer(mTiledParser.TileWidth, mTiledParser.TileHeight, mTiledParser.MapWidth, mTiledParser.MapHeight, mTiledParser.MapLayers[1], mTiledParser.TileSets);
+ mObjectLayer.CreateObjects();
+ mObjectLayer.LoadContent(mContentManager);
+ // Get areas from the map
+ mAreas = mTiledParser.Areas;
+ // Create pathfinding grid used in the pathfinder.
+ mPathfindingGrid = new PathfindingGrid(mTiledParser.MapWidth, mTiledParser.MapHeight, mTiledParser.TileWidth, mTiledParser.TileHeight);
+ mPathfindingGrid.LoadContent(mContentManager);
+ mPathfindingGrid.CreateCollisions(mFloorLayer.ExtractCollisions());
+ mPathfindingGrid.CreateCollisions(mObjectLayer.ExtractCollisions());
+ PathfindingGridSize = new Point(mPathfindingGrid.mGridWidth, mPathfindingGrid.mGridHeight);
+ PathfindingCellSize = new Point(Constants.CellWidth, Constants.CellHeight);
+ // Create Minimap texture from pathfinding grid.
+ mPathfindingGrid.CreateMinimap(mGraphicsDeviceManager.GraphicsDevice);
+ }
+
+ public List<IGameObject> GetObjects()
+ {
+ return mObjectLayer.ExtractObjects();
+ }
+
+ public List<ICreature> GetPopulation(CreatureFactory creatureFactory, Pathfinder pathfinder)
+ {
+ return mAreas.SelectMany(area => area.GetPopulation(creatureFactory, pathfinder)).ToList();
+ }
+
+ public PathfindingGrid GetPathfindingGrid()
+ {
+ return mPathfindingGrid;
+ }
+
+ public void DrawPathfindingGrid(SpriteBatch spriteBatch, ICamera camera)
+ {
+ mPathfindingGrid.Draw(spriteBatch, camera);
+ }
+
+ public void DrawMinimap(SpriteBatch spriteBatch, Rectangle position)
+ {
+ mPathfindingGrid.DrawSmallGrid(spriteBatch, position);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Map/ObjectLayer.cs b/V3/Map/ObjectLayer.cs
new file mode 100644
index 0000000..0e9d13c
--- /dev/null
+++ b/V3/Map/ObjectLayer.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// The map objects which are the same layer as the moving creatutes.
+ /// Buildings, flowers, trees etc.
+ /// </summary>
+ public sealed class ObjectLayer : AbstractLayer
+ {
+ public ObjectLayer(int tileWidth, int tileHeight, int mapWidth, int mapHeight, int[,] tileArray, SortedList<int, Tileset> tilesets)
+ : base(tileWidth, tileHeight, mapWidth, mapHeight, tileArray, tilesets)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Map/Pathfinder.cs b/V3/Map/Pathfinder.cs
new file mode 100644
index 0000000..3874d0b
--- /dev/null
+++ b/V3/Map/Pathfinder.cs
@@ -0,0 +1,455 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+
+namespace V3.Map
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class Pathfinder
+ {
+ private const int CellHeight = Constants.CellHeight;
+ private const int CellWidth = Constants.CellWidth;
+
+ // An array of walkable search nodes
+ private SearchNode[,] mSearchNodes;
+
+ // The width of the map
+ private int mLevelWidth;
+
+ // the height of the map
+ private int mLevelHeight;
+
+ // List for nodes that are available to search
+ private readonly List<SearchNode> mOpenList = new List<SearchNode>();
+
+ // List for nodes that are NOT available to search
+ private readonly List<SearchNode> mClosedList = new List<SearchNode>();
+
+ //Calculates the distance between two (vector)points
+ private float Heuristic(Vector2 position, Vector2 goal)
+ {
+ return (goal - position).Length(); // Manhattan distance
+ }
+
+ public void LoadGrid(PathfindingGrid map)
+ {
+ mLevelWidth = map.mGridWidth;
+ mLevelHeight = map.mGridHeight;
+ InitializeSearchNodes(map);
+ }
+
+ private void InitializeSearchNodes(PathfindingGrid map)
+ {
+ mSearchNodes = new SearchNode[mLevelWidth, mLevelHeight];
+
+ // Creates a searchnode for each tile
+ for (int x = 0; x < mLevelWidth; x++)
+ {
+ for (int y = 0; y < mLevelHeight; y++)
+ {
+ SearchNode node = new SearchNode();
+
+ node.mPosition = new Vector2(x, y);
+
+ // Walk only on walkable tiles
+ node.mWalkable = map.GetIndex(x, y) == 0;
+
+ // Stores nodes that can be walked on
+ if (node.mWalkable)
+ {
+ node.mNeighbors = new SearchNode[4];
+ mSearchNodes[x, y] = node;
+ }
+ }
+ }
+
+ for (int x = 0; x < mLevelWidth; x++)
+ {
+ for (int y = 0; y < mLevelHeight; y++)
+ {
+ SearchNode node = mSearchNodes[x, y];
+
+ // Note only walkable nodes
+ if (node == null || node.mWalkable == false)
+ continue;
+
+
+ // The neighbors for every node
+ Vector2[] neighbors =
+ {
+ new Vector2(x, y - 1), // Node above the current
+ new Vector2(x, y + 1), // Node below the current
+ new Vector2(x - 1, y), // Node to the left
+ new Vector2(x + 1, y) // Node to the right
+ };
+
+ for (int i = 0; i < neighbors.Length; i++)
+ {
+ Vector2 position = neighbors[i];
+
+ // Test whether this neighbor is part of the map
+ if (position.X < 0 || position.X > mLevelWidth - 1 || position.Y < 0 ||
+ position.Y > mLevelHeight - 1)
+ continue;
+
+ SearchNode neighbor = mSearchNodes[(int)position.X, (int)position.Y];
+
+ // Keep a reference to the nodes that can be walked on
+ if (neighbor == null || neighbor.mWalkable == false)
+ continue;
+
+ // A reference to the neighbor
+ node.mNeighbors[i] = neighbor;
+ }
+ }
+ }
+ }
+
+ // Reset the state of the search node
+ private void ResetSearchNodes()
+ {
+ mOpenList.Clear();
+ mClosedList.Clear();
+
+ for (int x = 0; x < mLevelWidth; x++)
+ {
+ for (int y = 0; y < mLevelHeight; y++)
+ {
+ SearchNode node = mSearchNodes[x, y];
+
+ if (node == null)
+ continue;
+
+ node.mInOpenList = false;
+ node.mInClosedList = false;
+ node.mDistanceTraveled = float.MaxValue;
+ node.mDistanceToGoal = float.MaxValue;
+ }
+ }
+ }
+
+ // Returns the node with the smallest distance
+ private SearchNode FindBestNode()
+ {
+ SearchNode currentTile = mOpenList[0];
+
+ float smallestDistanceToGoal = float.MaxValue;
+
+ // Find the closest node to the goal
+ for (int i = 0; i < mOpenList.Count; i++)
+ {
+ if (mOpenList[i].mDistanceToGoal < smallestDistanceToGoal)
+ {
+ currentTile = mOpenList[i];
+ smallestDistanceToGoal = currentTile.mDistanceToGoal;
+ }
+ }
+ return currentTile;
+ }
+
+ // Use parent field to trace a path from search node to start node
+ private List<Vector2> FindFinalPath(SearchNode startNode, SearchNode endNode)
+ {
+ int counter = 0;
+
+ if (startNode == endNode)
+ {
+ return new List<Vector2>();
+ }
+
+ mClosedList.Add(endNode);
+
+ SearchNode parentTile = endNode.mParent;
+
+ // Find the best path
+ while (parentTile != startNode)
+ {
+ mClosedList.Add(parentTile);
+ parentTile = parentTile.mParent;
+ }
+
+ // Path from position to goal (from tile to tile)
+ List<Vector2> betaPath = new List<Vector2>();
+
+ // Final path after RayCasting
+ List<Vector2> finalPath = new List<Vector2>();
+
+ // Reverse the path and transform into the map
+ for (int i = mClosedList.Count - 1; i >= 0; i--)
+ {
+ betaPath.Add(new Vector2(mClosedList[i].mPosition.X * CellWidth + 8, mClosedList[i].mPosition.Y * CellHeight + 8));
+ }
+
+ // Short the path via RayCasting
+ for (int i = 1; i < betaPath.Count;)
+ {
+ if (!RayCast(betaPath[counter], betaPath[i]))
+ {
+ finalPath.Add(betaPath[i - 1]);
+ counter = i - 1;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ finalPath.Add(betaPath[betaPath.Count - 1]);
+ return finalPath;
+ }
+
+ //Test Points
+ private Vector2 CheckStartNode(Vector2 startNode)
+ {
+ var start = startNode;
+
+ var startXPos = startNode;
+ var startXNeg = startNode;
+ var startYPos = startNode;
+ var startYNeg = startNode;
+
+ // When sprite is blocked out of map, he returns to the edge of the map
+ if (startNode.X > mLevelWidth - 2)
+ startNode.X = mLevelWidth - 2;
+ if (startNode.X < 2)
+ startNode.X = 2;
+ if (startNode.Y < 4)
+ startNode.Y = 4;
+ if (startNode.Y > mLevelHeight - 2)
+ startNode.Y = mLevelHeight - 2;
+
+ // When sprite stays on a null-position, he goes to the nearest non null-position around that null-position
+ while (mSearchNodes[(int)start.X, (int)start.Y] == null)
+ {
+ if (startXPos.X < mLevelWidth)
+ startXPos.X++;
+ if (startXNeg.X > 0)
+ startXNeg.X--;
+ if (startYPos.Y < mLevelHeight)
+ startYPos.Y++;
+ if (startYNeg.Y > 0)
+ startYNeg.Y--;
+
+ if (mSearchNodes[(int)startXPos.X, (int)start.Y] != null)
+ {
+ start.X = startXPos.X;
+ return start;
+ }
+ if (mSearchNodes[(int)startXNeg.X, (int)start.Y] != null)
+ {
+ start.X = startXNeg.X;
+ return start;
+ }
+ if (mSearchNodes[(int)start.X, (int)startYPos.Y] != null)
+ {
+ start.Y = startYPos.Y;
+ return start;
+ }
+ if (mSearchNodes[(int)start.X, (int)startYNeg.Y] != null)
+ {
+ start.Y = startYNeg.Y;
+ return start;
+ }
+ }
+ return start;
+ }
+
+ private Vector2 CheckEndNode(Vector2 endNode)
+ {
+ var end = endNode;
+
+ var endXPos = endNode;
+ var endXNeg = endNode;
+ var endYPos = endNode;
+ var endYNeg = endNode;
+
+ // When goal is null-position, the goal will be the nearest non null-position around that null-position
+ while (mSearchNodes[(int) end.X, (int) end.Y] == null)
+ {
+ if(endXPos.X < mLevelWidth - 3)
+ endXPos.X++;
+ if(endXNeg.X > 0)
+ endXNeg.X--;
+ if(endYPos.Y < mLevelHeight - 3)
+ endYPos.Y++;
+ if(endYNeg.Y > 0)
+ endYNeg.Y--;
+
+ if (endXPos.X > mLevelWidth - 3)
+ break;
+ if (endXNeg.X < 0)
+ break;
+ if (endYPos.Y > mLevelHeight - 3)
+ break;
+ if (endYNeg.Y < 0)
+ break;
+
+ if (mSearchNodes[(int)endXPos.X, (int)end.Y] != null)
+ {
+ end.X = endXPos.X;
+ return end;
+ }
+ if (mSearchNodes[(int)endXNeg.X, (int)end.Y] != null)
+ {
+ end.X = endXNeg.X;
+ return end;
+ }
+ if (mSearchNodes[(int)end.X, (int)endYPos.Y] != null)
+ {
+ end.Y = endYPos.Y;
+ return end;
+ }
+ if (mSearchNodes[(int)end.X, (int)endYNeg.Y] != null)
+ {
+ end.Y = endYNeg.Y;
+ return end;
+ }
+ }
+ return end;
+ }
+
+ // Finds the best path
+ public List<Vector2> FindPath(Vector2 startPoint, Vector2 endPoint)
+ {
+ // Start to find path if startpoint and endpoint are different
+ if (startPoint == endPoint)
+ {
+ return new List<Vector2>();
+ }
+
+ // Sprite don't walk out of the map
+ if (endPoint.Y > mLevelHeight - 2 || endPoint.Y < 4 || endPoint.X > mLevelWidth - 2 || endPoint.X < 2)
+ {
+ return new List<Vector2>();
+ }
+
+ // Test nodes for their validity
+ startPoint = CheckStartNode(startPoint);
+ endPoint = CheckEndNode(endPoint);
+
+ /*
+ * Clear the open and closed lists.
+ * reset each's node F and G values
+ */
+ ResetSearchNodes();
+
+ // Store references to the start and end nodes for convenience
+ SearchNode startNode = mSearchNodes[(int)startPoint.X, (int)startPoint.Y];
+ SearchNode endNode = mSearchNodes[(int)endPoint.X, (int)endPoint.Y];
+
+ /*
+ * Set the start node’s G value to 0 and its F value to the
+ * estimated distance between the start node and goal node
+ * (this is where our H function comes in) and add it to the open list
+ */
+ if (startNode != null)
+ {
+ startNode.mInOpenList = true;
+
+ startNode.mDistanceToGoal = Heuristic(startPoint, endPoint);
+ startNode.mDistanceTraveled = 0;
+
+ mOpenList.Add(startNode);
+ }
+
+ /*
+ * While the OpenList is not empty:
+ */
+ while (mOpenList.Count > 0)
+ {
+ // Loop the open list and find the node with the smallest F value
+ SearchNode currentNode = FindBestNode();
+
+ // If the open list ist empty or a node can't be found
+ if (currentNode == null)
+ break;
+
+ // If the active node ist the goal node, we will find and return the path
+ if (currentNode == endNode)
+ return FindFinalPath(startNode, endNode); // Trace our path back to the start
+
+ // Else, for each of the active node's neighbors
+ for (int i = 0; i < currentNode.mNeighbors.Length; i++)
+ {
+ SearchNode neighbor = currentNode.mNeighbors[i];
+
+ // Make sure that the neighbor can be walked on
+ if (neighbor == null || !neighbor.mWalkable)
+ continue;
+
+ // Calculate a new G Value for the neighbors node
+ float distanceTraveled = currentNode.mDistanceTraveled + 1;
+
+ // An estimate of t he distance from this node to the end node
+ float heuristic = Heuristic(neighbor.mPosition, endPoint);
+
+ if (!neighbor.mInOpenList && !neighbor.mInClosedList)
+ {
+ // Set the neighbors node G value to the G value
+ neighbor.mDistanceTraveled = distanceTraveled;
+
+ // Set the neighboring node's F value to the new G value + the estimated
+ // distance between the neighbouring node and goal node
+ neighbor.mDistanceToGoal = distanceTraveled + heuristic;
+
+ // The neighbouring node's mParent property to point at the active node
+ neighbor.mParent = currentNode;
+
+ // Add the neighboring node to the open list
+ neighbor.mInOpenList = true;
+ mOpenList.Add(neighbor);
+ }
+
+ // Else if the neighboring node is in open or closed list
+ else if (neighbor.mInOpenList || neighbor.mInClosedList)
+ {
+ if (neighbor.mDistanceTraveled > distanceTraveled)
+ {
+ neighbor.mDistanceTraveled = distanceTraveled;
+ neighbor.mDistanceToGoal = distanceTraveled + heuristic;
+
+ neighbor.mParent = currentNode;
+ }
+ }
+ }
+
+ // Remove active node from the open list and add to the closed list
+ mOpenList.Remove(currentNode);
+ currentNode.mInOpenList = true;
+
+ }
+
+ // No path could be found
+ return new List<Vector2>();
+ }
+
+ // Check whether an area is completely walkable in given rectangle
+ public bool AllWalkable(Rectangle rectangle)
+ {
+ for (int x = rectangle.X; x <= rectangle.X + rectangle.Width; x++)
+ {
+ for (int y = rectangle.Y; y <= rectangle.Y + rectangle.Height; y++)
+ {
+ if (mSearchNodes[x / 16, y / 16] == null)
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //Raycasting
+ private bool RayCast(Vector2 start, Vector2 goal)
+ {
+ var direction = goal - start;
+ var currentPos = start;
+ direction.Normalize();
+ //direction = direction * 8;
+
+ while (Vector2.Distance(currentPos, goal) > 1f)
+ {
+ if (mSearchNodes[(int)currentPos.X / 16, (int)currentPos.Y / 16] == null)
+ return false;
+ currentPos += direction;
+ }
+ return true;
+ }
+ }
+}
diff --git a/V3/Map/PathfindingGrid.cs b/V3/Map/PathfindingGrid.cs
new file mode 100644
index 0000000..266ea02
--- /dev/null
+++ b/V3/Map/PathfindingGrid.cs
@@ -0,0 +1,125 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// Tells the pathfinder where you can walk.
+ /// </summary>
+ public sealed class PathfindingGrid
+ {
+ private const int CellHeight = Constants.CellHeight;
+ private const int CellWidth = Constants.CellWidth;
+
+ private readonly bool[,] mArray;
+ public readonly int mGridWidth;
+ public readonly int mGridHeight;
+ private Texture2D mTexture;
+ private Texture2D mMinimapTexture;
+
+ public PathfindingGrid(int mapWidth, int mapHeight, int tileWidth, int tileHeight)
+ {
+ mGridHeight = (mapHeight - 1) * tileHeight / CellHeight / 2;
+ mGridWidth = (mapWidth - 1) * tileWidth / CellWidth;
+ mArray = new bool[mGridHeight, mGridWidth];
+ }
+
+ /// <summary>
+ /// Compares the pathfinding grid with the given collision grid and adjusts the former.
+ /// If a cell of the pathfinding grid is false and the cell at the same position of the
+ /// collision grid is true, switch false to true.
+ /// </summary>
+ /// <param name="collisionGrid">A grid of the same size as the pathfinding grid.</param>
+ public void CreateCollisions(bool[,] collisionGrid)
+ {
+ if (collisionGrid.Length == mGridWidth * mGridHeight)
+ {
+ for (int i = 0; i < mGridHeight; i++)
+ {
+ for (int j = 0; j < mGridWidth; j++)
+ {
+ if (!mArray[i, j])
+ {
+ mArray[i, j] = collisionGrid[i, j];
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new Exception("Error creating the collision grid. Object layer data and collision grid data do not fit.");
+ }
+ }
+
+ /// <summary>
+ /// Load content for visual representation of the pathfinding grid.
+ /// </summary>
+ /// <param name="contentManager">Use this content manager.</param>
+ public void LoadContent(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Textures/pathfinder");
+ //mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+ /// <summary>
+ /// A visual representation of the pathfinding grid. Drawn efficiently.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ /// <param name="camera">For only drawing on the shown part of the map.</param>
+ public void Draw(SpriteBatch spriteBatch, ICamera camera)
+ {
+ Point startPosition = camera.Location.ToPoint() / new Point(CellWidth, CellHeight);
+ Point tilesOnScreen = camera.ScreenSize / new Point(CellWidth, CellHeight) + new Point(1, 1) + startPosition;
+ for (int i = startPosition.Y; i < tilesOnScreen.Y && i < mGridHeight; i++)
+ {
+ for (int j = startPosition.X; j < tilesOnScreen.X && j < mGridWidth; j++)
+ {
+ Rectangle destinationRectangle = new Rectangle(j * CellWidth, i * CellHeight, CellWidth, CellHeight);
+ Rectangle sourceRectangle = new Rectangle(mArray[i, j] ? CellWidth : 0, 0, CellWidth, CellHeight);
+ spriteBatch.Draw(mTexture, destinationRectangle, sourceRectangle, Color.White);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the value at the specified position of the collision array.
+ /// </summary>
+ /// <param name="cellX">Position at the horizontal axis.</param>
+ /// <param name="cellY">Position at the vertical axis.</param>
+ /// <returns>Returns 0 if you can walk at the specified position, 1 otherwise.</returns>
+ public int GetIndex(int cellX, int cellY)
+ {
+ //if (cellX < 0 || cellX > mGridWidth - 1 || cellY < 0 || cellY > mGridHeight - 1)
+ // return 0;
+ return mArray[cellY, cellX] ? 1 : 0;
+ }
+
+ /// <summary>
+ /// Draws a small version of the pathfinding grid to the screen.
+ /// Useful for the minimap.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ /// <param name="position">Where to draw in pixel coordinates and which size. In pixels.</param>
+ public void DrawSmallGrid(SpriteBatch spriteBatch, Rectangle position)
+ {
+ spriteBatch.Draw(mMinimapTexture, position, Color.White);
+ }
+
+ public void CreateMinimap(GraphicsDevice device)
+ {
+ Color[] colors = new Color[mGridWidth * mGridHeight];
+ for (int i = 0; i < mGridHeight; i++)
+ {
+ for (int j = 0; j < mGridWidth; j++)
+ {
+ colors[i * mGridWidth + j ] = mArray[i, j] ? Color.DarkGray : Color.Green;
+ }
+ }
+ mMinimapTexture = new Texture2D(device, mGridWidth, mGridHeight);
+ mMinimapTexture.SetData(colors);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Map/SearchNode.cs b/V3/Map/SearchNode.cs
new file mode 100644
index 0000000..db49f14
--- /dev/null
+++ b/V3/Map/SearchNode.cs
@@ -0,0 +1,31 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Map
+{
+ class SearchNode
+ {
+ // Location on the map
+ public Vector2 mPosition;
+
+ // If true, the sprite can walk on
+ public bool mWalkable;
+
+ //
+ public SearchNode[] mNeighbors;
+
+ // Previous node
+ public SearchNode mParent;
+
+ // Check whether a node is in the open list
+ public bool mInOpenList;
+
+ // Check whether a node is in the closed list
+ public bool mInClosedList;
+
+ // DIstance from the start node to the goal node (F value)
+ public float mDistanceToGoal;
+
+ // Distance traveled from the spawn point (G value)
+ public float mDistanceTraveled;
+ }
+} \ No newline at end of file
diff --git a/V3/Map/TiledParser.cs b/V3/Map/TiledParser.cs
new file mode 100644
index 0000000..8171ecf
--- /dev/null
+++ b/V3/Map/TiledParser.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Xml;
+using Microsoft.Xna.Framework;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// Parser for the tmx format of the Tiled Map Editor.
+ /// Reads XML file and returns corresponding data objects.
+ /// </summary>
+ public sealed class TiledParser
+ {
+ private string mFileName;
+ // Map Data:
+ public int MapWidth { get; private set; }
+ public int MapHeight { get; private set; }
+ public int TileWidth { get; private set; }
+ public int TileHeight { get; private set; }
+ public SortedList<int, Tileset> TileSets { get; } = new SortedList<int, Tileset>();
+ public List<int[,]> MapLayers { get; } = new List<int[,]>();
+ public List<Area> Areas { get; } = new List<Area>();
+
+ /// <summary>
+ /// Parse the tmx file and hold data in instance properties.
+ /// </summary>
+ public void Parse(string fileName)
+ {
+ mFileName = fileName;
+ string directory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
+ string fullPath = directory + "/Content/Maps/" + mFileName + ".tmx";
+ int p = (int)Environment.OSVersion.Platform;
+ if ((p == 4) || (p == 6) || (p == 128)) // Running on Unix
+ fullPath = fullPath.Substring(5);
+#if DEBUG
+ Console.WriteLine("Loading Map: " + fullPath);
+#endif
+ XmlReader reader = XmlReader.Create(fullPath);
+ while (reader.Read())
+ {
+ if (reader.IsStartElement())
+ {
+ switch (reader.Name)
+ {
+ case "map":
+ ParseMapData(reader);
+ break;
+ case "tileset":
+ ParseTilesetData(reader);
+ break;
+ case "layer":
+ ParseLayerData(reader);
+ break;
+ case "objectgroup":
+ ParseObjectgroup(reader);
+ break;
+ }
+ }
+ }
+ }
+
+ private void ParseMapData(XmlReader reader)
+ {
+ while (reader.MoveToNextAttribute())
+ {
+ switch (reader.Name)
+ {
+ case "width":
+ MapWidth = reader.ReadContentAsInt();
+ break;
+ case "height":
+ MapHeight = reader.ReadContentAsInt();
+ break;
+ case "tilewidth":
+ TileWidth = reader.ReadContentAsInt();
+ break;
+ case "tileheight":
+ TileHeight = reader.ReadContentAsInt();
+ break;
+ }
+ }
+ reader.MoveToElement();
+ }
+
+ private void ParseTilesetData(XmlReader reader)
+ {
+ if (reader.HasAttributes)
+ {
+ List<string> tilesetAttributes = new List<string>();
+ // Read attributes firstgid, name, tilewidth, tileheight, tilecount, columns.
+ for (int i = 0; i < reader.AttributeCount; i++)
+ {
+ tilesetAttributes.Add(reader[i]);
+ }
+ reader.MoveToElement();
+ // Read attributes for tileoffset x and y if existing.
+ while (reader.Read())
+ {
+ if (reader.Name == "tileoffset")
+ {
+ if (reader.IsStartElement())
+ {
+ for (int i = 0; i < reader.AttributeCount; i++)
+ {
+ tilesetAttributes.Add(reader[i]);
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if (reader.Name == "tile" || reader.Name == "tileset")
+ {
+ break;
+ }
+ }
+ if (tilesetAttributes.Count == 6)
+ {
+ TileSets.Add(int.Parse(tilesetAttributes[0]), new Tileset(tilesetAttributes[1], int.Parse(tilesetAttributes[2]),
+ int.Parse(tilesetAttributes[3]), int.Parse(tilesetAttributes[5])));
+ }
+ else if (tilesetAttributes.Count == 8)
+ {
+ TileSets.Add(int.Parse(tilesetAttributes[0]), new Tileset(tilesetAttributes[1], int.Parse(tilesetAttributes[2]),
+ int.Parse(tilesetAttributes[3]), int.Parse(tilesetAttributes[5]),
+ int.Parse(tilesetAttributes[6]), int.Parse(tilesetAttributes[7])));
+ }
+ else
+ {
+ throw new Exception("Error parsing tileset element in " + mFileName + ".tmx. Does not contain necessary attributes.");
+ }
+ ParseCollisionData(reader, int.Parse(tilesetAttributes[0]));
+ }
+ }
+
+ private void ParseLayerData(XmlReader reader)
+ {
+ while (reader.MoveToNextAttribute())
+ {
+ if (reader.Name == "width")
+ {
+ // TODO: Try catching exceptions and throw specific ones.
+ int width = reader.ReadContentAsInt();
+ reader.MoveToNextAttribute();
+ int height = reader.ReadContentAsInt();
+ reader.MoveToElement();
+ reader.ReadToDescendant("data");
+ MapLayers.Add(new int[height, width]);
+ int currentLayerIndex = MapLayers.Count - 1;
+ // Map data is in CSV format, therefore split at comma.
+ string[] layerData = reader.ReadString().Split(',');
+ for (int i = 0; i < height; i++)
+ {
+ for (int j = 0; j < width; j++)
+ {
+ MapLayers[currentLayerIndex][i, j] = int.Parse(layerData[i * width + j]);
+ }
+ }
+ }
+ }
+ reader.MoveToElement();
+ }
+
+ private void ParseCollisionData(XmlReader reader, int currentTileset)
+ {
+ do
+ {
+ if (!reader.IsStartElement() && reader.Name == "tileset")
+ {
+ // If the end of the tileset note is reached, leave loop.
+ break;
+ }
+ if (reader.IsStartElement() && reader.Name == "tile" && reader.HasAttributes)
+ {
+ string tileId = reader[0];
+ reader.MoveToElement();
+ while (reader.ReadToDescendant("property"))
+ {
+ if (reader.MoveToAttribute("name") && reader.Value == "collision")
+ {
+ reader.MoveToNextAttribute();
+ string collisionData = reader.Value;
+ Tileset tileset = TileSets[currentTileset];
+ if (tileId != null) tileset.AddCollisionData(int.Parse(tileId), collisionData);
+ }
+ }
+ }
+ }
+ while (reader.Read()) ;
+ }
+
+ private void ParseObjectgroup(XmlReader reader)
+ {
+ do
+ {
+ if (!reader.IsStartElement() && reader.Name == "objectgroup")
+ break;
+ if (reader.IsStartElement() && reader.Name == "object")
+ {
+ ParseAreaData(reader);
+ }
+ } while (reader.Read());
+ }
+
+ private void ParseAreaData(XmlReader reader)
+ {
+ string type;
+ string name = "";
+ double density = 0;
+ double chance = 0;
+ Rectangle rectangle;
+ if (reader.AttributeCount == 7)
+ {
+ name = reader[1];
+ type = reader[2];
+ if (!(reader[3] != null && reader[4] != null && reader[5] != null && reader[6] != null))
+ return;
+ rectangle = new Rectangle(int.Parse(reader[3]), int.Parse(reader[4]), int.Parse(reader[5]), int.Parse(reader[6]));
+ }
+ else if (reader.AttributeCount == 6)
+ {
+ type = reader[1];
+ if (!(reader[2] != null && reader[3] != null && reader[4] != null && reader[5] != null))
+ return;
+ rectangle = new Rectangle(int.Parse(reader[2]), int.Parse(reader[3]), int.Parse(reader[4]), int.Parse(reader[5]));
+ }
+ else
+ {
+ throw new Exception("Error parsing the map. One of the objects has not the right number of attributes, specifically: " + reader.AttributeCount);
+ }
+ reader.MoveToElement();
+ while (reader.Read())
+ {
+ if (!reader.IsStartElement() && reader.Name == "properties")
+ break;
+ if (reader.Name == "property" && reader.HasAttributes)
+ {
+ if (reader[2] == null) return;
+ if (reader[0] == "chance")
+ {
+ chance = double.Parse(reader[2], CultureInfo.InvariantCulture);
+ }
+ else if (reader[0] == "density")
+ {
+ density = double.Parse(reader[2], CultureInfo.InvariantCulture);
+ }
+ reader.MoveToElement();
+ }
+ }
+ Area area = new Area(type, rectangle, density, chance, name);
+ Areas.Add(area);
+ }
+ }
+}
diff --git a/V3/Map/Tileset.cs b/V3/Map/Tileset.cs
new file mode 100644
index 0000000..aa12885
--- /dev/null
+++ b/V3/Map/Tileset.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+
+namespace V3.Map
+{
+ /// <summary>
+ /// Class for holding information needed of Tilesets. Needed to draw the map.
+ /// </summary>
+ public sealed class Tileset
+ {
+ private const int CellHeight = Constants.CellHeight;
+ private const int CellWidth = Constants.CellWidth;
+
+ /// <summary>
+ /// Name of the tileset, often the filename.
+ /// </summary>
+ public string Name { get; }
+ /// <summary>
+ /// Tile width of each tile in pixel.
+ /// </summary>
+ public int TileWidth { get; }
+ /// <summary>
+ /// Tile height of each tile in pixel.
+ /// </summary>
+ public int TileHeight { get; }
+
+ /// <summary>
+ /// Columns of tiles of the tileset image.
+ /// </summary>
+ public int Columns { get; private set; }
+ /// <summary>
+ /// When tile is drawn, is there an offset needed on the X axis for correct display.
+ /// </summary>
+ public int OffsetX { get; private set; }
+ /// <summary>
+ ///
+ /// When tile is drawn, is there an offset needed on the Y axis for correct display.
+ /// </summary>
+ public int OffsetY { get; private set; }
+ /// <summary>
+ /// Each tile of the tileset, represented by an integer, can hold collision data consisting of a two dimensional
+ /// array of boolean values. Its size is described by CollisionWidth and CollisionHeight.
+ /// </summary>
+ public Dictionary<int, bool[,]> TileCollisions { get; }
+ public int CollisionWidth => TileWidth / CellWidth;
+ public int CollisionHeight => TileHeight / CellHeight;
+
+ public Tileset(string name, int tileWidth, int tileHeight, int columns, int offsetX = 0, int offsetY = 0)
+ {
+ Name = name;
+ TileWidth = tileWidth;
+ TileHeight = tileHeight;
+ Columns = columns;
+ OffsetX = offsetX;
+ OffsetY = offsetY;
+ // TODO: Fill dictionary with TiledParser.
+ TileCollisions = new Dictionary<int, bool[,]>();
+ }
+
+ /// <summary>
+ /// Add an entry to the collision dictionary for the specific tile.
+ /// </summary>
+ /// <param name="tileId">The tile ID in the tileset.</param>
+ /// <param name="collisionData">The corresponding collision data as string of '0' and '1'.</param>
+ public void AddCollisionData(int tileId, string collisionData)
+ {
+ int gridWidth = CollisionWidth;
+ int gridHeight = CollisionHeight;
+ bool[,] dataArray = new bool[gridHeight, gridWidth];
+ for (int i = 0; i < gridHeight; i++)
+ {
+ for (int j = 0; j < gridWidth; j++)
+ {
+ try
+ {
+ dataArray[i, j] = collisionData[i * gridWidth + j] == '1';
+ }
+ catch (IndexOutOfRangeException e)
+ {
+ throw new IndexOutOfRangeException("Inconsistencies with the collision data of Tile "
+ + tileId + " in tileset " + Name + ". Check corresponding tmx file or" +
+ "contact the programmer: Thomas.", e);
+ }
+ }
+ }
+ TileCollisions.Add(tileId, dataArray);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Node.cs b/V3/Node.cs
new file mode 100644
index 0000000..7c01820
--- /dev/null
+++ b/V3/Node.cs
@@ -0,0 +1,467 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Objects;
+using Rectangle = Microsoft.Xna.Framework.Rectangle;
+
+namespace V3
+{
+ public sealed class Node
+ {
+ /// <summary>
+ /// Represents the Rectangle if a Quad gets split
+ /// </summary>
+ private Node mRt; // The rectangle on the right top
+ private Node mLt; // The rectangle on the left top
+ private Node mRb; // The rectangle on the right bottom
+ private Node mLb; // The rectangle on the left bottom
+ private readonly Node mParent; // the parent of a node
+ private Rectangle mRectangle;
+ private readonly int mPositionX; // X position of the current rectangle
+ private readonly int mPositionY; // Y position of the current rectangle
+ private readonly int mSizeX; // length of the current rectangle
+ private readonly int mSizeY; // width of the current rectangle
+ private readonly int mCenterX; // center of the current rectangle
+ private readonly int mCenterY;
+ private int mCount;
+ private Rectangle mCreatureRectangle;
+ private MovementDirection mMovementDirectionItem2;
+ private MovementState mMovementStateItem;
+ private MovementState mMovementStateItem2;
+
+ /// <summary>
+ /// The list of each node where the objects in each rectangle are saved
+ /// </summary>
+ private List<IGameObject> mObjectList = new List<IGameObject>();
+ private int Count => ObjectCount();
+
+ /// <summary>
+ /// initialize the Node
+ /// </summary>
+ /// <param name="currentRectangle">the current Rectangle/size of the Node</param>
+ /// <param name="p">the parent of the node</param>
+ public Node(Rectangle currentRectangle, Node p)
+ {
+ mParent = p;
+ mRectangle = currentRectangle;
+ mPositionY = mRectangle.Y;
+ mPositionX = mRectangle.X;
+ mSizeX = mRectangle.Width;
+ mSizeY = mRectangle.Height;
+ mCenterY = mRectangle.Y + mRectangle.Height / 2;
+ mCenterX = mRectangle.X + mRectangle.Width / 2;
+ }
+
+ /// <summary>
+ /// divided the rectangle in 4 small rectangles locaded in it self
+ /// </summary>
+ private void CreateSubnodes()
+ {
+ if (mLt == null)
+ {
+ mLt = new Node(new Rectangle(mPositionX - 1, mPositionY - 1, mSizeX / 2 + 2, mSizeY / 2 + 2),
+ this);
+ mRt = new Node(new Rectangle(mPositionX + mSizeX / 2, mPositionY - 1, mSizeX / 2 + 1, mSizeY / 2 + 2),
+ this);
+ mLb = new Node(new Rectangle(mPositionX - 1, mPositionY + mSizeY / 2, mSizeX / 2 + 2, mSizeY / 2 + 1),
+ this);
+ mRb = new Node( new Rectangle(mPositionX + mSizeX / 2, mPositionY + mSizeY / 2, mSizeX / 2 + 1, mSizeY / 2 + 1),
+ this);
+ }
+ }
+
+ /// <summary>
+ /// This method is looking in which rectangle the object is located.
+ /// At first it checks in which part (right top, left top...) of the rectangle the object is
+ /// and if its posibible the rectangle gets split in for new rectangles the same is happening again.
+ /// </summary>
+ /// <param name="item">Type of Creature including their position.</param>
+ public void AddtoSubNode(IGameObject item)
+ {
+ mCreatureRectangle = item.BoundaryRectangle;
+
+ if (mRectangle.Contains(mCreatureRectangle))
+ {
+ if (mCreatureRectangle.Right < mCenterX && (mCreatureRectangle.Y + mCreatureRectangle.Height) < mCenterY)
+ {
+ CreateSubnodes(); // splits subnode in more subnodes
+ mLt.AddtoSubNode(item);
+ }
+ else if (mCreatureRectangle.X > mCenterX && (mCreatureRectangle.Y + mCreatureRectangle.Height) < mCenterY)
+ {
+ CreateSubnodes();
+ mRt.AddtoSubNode(item);
+ }
+ else if (mCreatureRectangle.Right < mCenterX && mCreatureRectangle.Y > mCenterY)
+ {
+ CreateSubnodes();
+ mLb.AddtoSubNode(item);
+ }
+ else if (mCreatureRectangle.X > mCenterX && mCreatureRectangle.Y > mCenterY)
+ {
+ CreateSubnodes();
+ mRb.AddtoSubNode(item);
+ }
+ else
+ {
+ mObjectList.Add(item); // the object gets added to the objectlist of the current node/subnode
+ //CheckCollission(mObjectList);
+ mCount++;
+ if (mCount == 8)
+ {
+ CheckCollission(mObjectList);
+ }
+ else if (mCount > 8)
+ {
+ mCount = 0;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// checks if the Objects in the same Quad are intersecting
+ /// </summary>
+ /// <param name="objectList">list with the objects in the same quad</param>
+ //void CheckCollission(List<AbstractCreature> objectList)
+ void CheckCollission(List<IGameObject> objectList)
+ {
+ // if (mParent != null)
+ // {
+ // mList2 = mParent.mObjectList;
+ // }
+
+ foreach (var obj in objectList)
+ {
+ foreach (var obj2 in objectList)
+ {
+ if (Equals(obj, obj2))
+ {
+ continue;
+ }
+ if (obj.BoundaryRectangle.Intersects(obj2.BoundaryRectangle))
+ //if (obj.BoundaryRectangle.X < obj2.BoundaryRectangle.X + obj2.BoundaryRectangle.Width &&
+ //obj.BoundaryRectangle.X + obj.BoundaryRectangle.Width > obj2.BoundaryRectangle.X &&
+ //obj.BoundaryRectangle.Y < obj2.BoundaryRectangle.Y + obj2.BoundaryRectangle.Height &&
+ //obj.BoundaryRectangle.Height + obj.BoundaryRectangle.Y > obj2.BoundaryRectangle.Y)
+ {
+
+ if (!(obj is ICreature)) return;
+ if (!(obj2 is ICreature)) return;
+ ICreature creature = (ICreature)obj;
+ mMovementStateItem = creature.MovementState;
+ ICreature creature2 = (ICreature)obj2;
+ mMovementStateItem2 = creature2.MovementState;
+ if (mMovementStateItem != MovementState.Dying && (mMovementStateItem2 != MovementState.Dying))
+ {
+ HandleCollision(obj, obj2);
+ }
+ }
+ }
+ }
+
+ //if (mParent != null)
+ //{
+ // foreach (var obj in mList2)
+ // {
+ // foreach (var obj2 in objectList)
+ // {
+ // if (obj == obj2)
+ // {
+ // continue;
+ // }
+ // if (obj.BoundaryRectangle.Intersects(obj2.BoundaryRectangle))
+ // //if (obj.BoundaryRectangle.X < obj2.BoundaryRectangle.X + obj2.BoundaryRectangle.Width &&
+ // // obj.BoundaryRectangle.X + obj.BoundaryRectangle.Width > obj2.BoundaryRectangle.X &&
+ // // obj.BoundaryRectangle.Y < obj2.BoundaryRectangle.Y + obj2.BoundaryRectangle.Height &&
+ // // obj.BoundaryRectangle.Height + obj.BoundaryRectangle.Y > obj2.BoundaryRectangle.Y)
+ // {
+ // if (!(obj is ICreature)) return;
+ // if (!(obj2 is ICreature)) return;
+ // //if (obj.GetType() != typeof(ICreature)) return;
+ // ICreature creature = (ICreature)obj;
+ // mMovementStateItem = creature.MovementState;
+ // ICreature creature2 = (ICreature)obj2;
+ // mMovementStateItem2 = creature2.MovementState;
+ // if (mMovementStateItem != MovementState.Dying && (mMovementStateItem2 != MovementState.Dying))
+ // {
+ // HandleCollision(obj, obj2);
+ // }
+ // //mCollision = true;
+ // }
+ // }
+ // }
+ //}
+ }
+
+ /// <summary>
+ /// If collsion is detected the object gets moved away so there is no collision anymore
+ /// </summary>
+ /// <param name="item">the object which collid with another object</param>
+ /// <param name="item2">the other object</param>
+ //void HandleCollision(AbstractCreature item, AbstractCreature item2)
+ void HandleCollision(IGameObject item, IGameObject item2)
+ {
+ ICreature creature = (ICreature)item;
+ ICreature creature2 = (ICreature)item2;
+ mMovementDirectionItem2 = creature2.MovementDirection;
+
+ if (mMovementDirectionItem2 == MovementDirection.S)
+ {
+ creature.Position = new Vector2(item.Position.X + 1, item.Position.Y);
+ }
+ if (mMovementDirectionItem2 == MovementDirection.N)
+ {
+ creature.Position = new Vector2(item.Position.X + 1, item.Position.Y);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.O)
+ {
+ creature.Position = new Vector2(item.Position.X, item.Position.Y + 1);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.W)
+ {
+ creature.Position = new Vector2(item.Position.X, item.Position.Y + 1);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.SO)
+ {
+ creature.Position = new Vector2(item.Position.X - 1, item.Position.Y);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.NO)
+ {
+ creature.Position = new Vector2(item.Position.X + 1, item.Position.Y - 1);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.NW)
+ {
+ creature.Position = new Vector2(item.Position.X - 1, item.Position.Y + 1);
+ }
+ else if (mMovementDirectionItem2 == MovementDirection.SW)
+ {
+ creature.Position = new Vector2(item.Position.X + 1, item.Position.Y);
+ }
+ else
+ {
+ creature.Position = new Vector2(item.Position.X + 1, item.Position.Y + 1);
+ }
+ }
+
+ /// <summary>
+ /// Clears the QuadTree of all objects, including any objects living in its children.
+ /// </summary>
+ public void Clear()
+ {
+ // Clear out the children, if we have any
+ if (mLt != null)
+ {
+ mLt.Clear();
+ mRt.Clear();
+ mLb.Clear();
+ mRb.Clear();
+ }
+
+ // Clear any objects at this level
+ if (mObjectList != null)
+ {
+ mObjectList.Clear();
+ mObjectList = null;
+ }
+
+ // Set the children to null
+ mLt = null;
+ mRt = null;
+ mLb = null;
+ mRb = null;
+ }
+
+ /// <summary>
+ /// Get the total for all objects in this QuadTree, including children.
+ /// </summary>
+ /// <returns>The number of objects contained within this QuadTree and its children.</returns>
+ private int ObjectCount()
+ {
+ int count = 0;
+
+ // Add the objects at this level
+ if (mObjectList != null) count += mObjectList.Count;
+
+ // Add the objects that are contained in the children
+ if (mLt != null)
+ {
+ count += mLt.ObjectCount();
+ count += mRt.ObjectCount();
+ count += mLb.ObjectCount();
+ count += mRb.ObjectCount();
+ }
+
+ return count;
+ }
+
+
+ /// <summary>
+ /// Deletes an item from this QuadTree. If the object is removed causes this Quad to have no objects in its children,
+ /// it's children will be removed as well.
+ /// </summary>
+ /// <param name="item"></param>
+ public void Delete(IGameObject item)
+ {
+ if (mObjectList.Contains(item))
+ {
+ mObjectList.Remove(item);
+ }
+ // If we didn't find the object in this tree, try to delete from its children
+ else if (mLt != null)
+ {
+ mLt.Delete(item);
+ mRt.Delete(item);
+ mLb.Delete(item);
+ mRb.Delete(item);
+ }
+
+ // If all the children are empty, delete all the children
+ if (mLt?.Count == 0 &&
+ mRt.Count == 0 &&
+ mLb.Count == 0 &&
+ mRb.Count == 0)
+ {
+ mLt = null;
+ mRt = null;
+ mLb = null;
+ mRb = null;
+ }
+ }
+
+ /// <summary>
+ /// If the Object isnt in his old Rectangle anymore it have to move
+ /// the object to the parent(s) Rectangle until it fits, and optionally going back down into children
+ /// </summary>
+ /// <param name="item">Actuell Creature</param>
+ private void Move(IGameObject item)
+ {
+ if (mParent != null && mParent.mRectangle.Contains(item.BoundaryRectangle))
+ {
+ mParent.AddtoSubNode(item);
+ }
+ else if (mParent == null)
+ {
+ AddtoSubNode(item);
+ }
+ else
+ {
+ mParent.Move(item);
+ }
+
+ }
+
+ /// <summary>
+ /// If four Subnodes are empty the get deleted
+ /// </summary>
+ private void RemoveEmptyNodes()
+ {
+ // If all the children are empty, delete all the children
+ if (mLt?.Count == 0 &&
+ mRt.Count == 0 &&
+ mLb.Count == 0 &&
+ mRb.Count == 0)
+ {
+ mLt = null;
+ mRt = null;
+ mLb = null;
+ mRb = null;
+ }
+ mLt?.RemoveEmptyNodes();
+ mRt?.RemoveEmptyNodes();
+ mLb?.RemoveEmptyNodes();
+ mRb?.RemoveEmptyNodes();
+ }
+
+
+ /// <summary>
+ /// Gibt einem alle Argumente die Innerhalb des rectangles sind zurück in der Liste objectInRecList
+ /// </summary>
+ /// <param name="rectangle">Der Bereich aus dem man alle Objecte haben möchte</param>
+ /// <param name="objectInRecList">Die Liste, in der alle Objekte enthalten sind, die sich im gefragten Rectangle aufhalten</param>
+ /// <returns></returns>
+ public List<IGameObject> GetObjectsInRectangle(Rectangle rectangle, List<IGameObject> objectInRecList)
+ {
+ if (rectangle.Intersects(mRectangle))
+ {
+ if (mObjectList != null)
+ {
+ foreach (var obj in mObjectList)
+ {
+ if (obj.GetSelf() != null)
+ {
+ objectInRecList.Add(obj);
+ }
+ }
+ }
+ if (mLt != null)
+ {
+ mLt.GetObjectsInRectangle(rectangle, objectInRecList);
+ mRt.GetObjectsInRectangle(rectangle, objectInRecList);
+ mLb.GetObjectsInRectangle(rectangle, objectInRecList);
+ mRb.GetObjectsInRectangle(rectangle, objectInRecList);
+ }
+ }
+ return objectInRecList;
+ }
+
+
+ /// <summary>
+ /// Updates the position of the object in the Quadtree. If they changed their position
+ /// they get deleted and added again at the correct position.
+ /// </summary>
+ public void Update1()
+ {
+ List<IGameObject> copyList = new List<IGameObject>(mObjectList);
+ //CheckCollission(mObjectList);
+ foreach (IGameObject obj in copyList)
+ {
+ if (!(obj is ICreature))
+ continue;
+ Delete(obj);
+ if (mRectangle.Contains(obj.BoundaryRectangle))
+ {
+ AddtoSubNode(obj);
+ }
+ // if Creature isnt there anymore -> move
+ else
+ {
+ Move(obj);
+ break;
+ }
+ }
+ mLt?.Update1();
+ mRt?.Update1();
+ mRb?.Update1();
+ mLb?.Update1();
+ RemoveEmptyNodes();
+ }
+
+ /// <summary>
+ /// Makes the Quadtree visible
+ /// </summary>
+ /// <param name="spriteBatch">to draw the Rectangles</param>
+ /// <param name="texture"></param>
+ public void DrawQuadtree(SpriteBatch spriteBatch, Texture2D texture)
+ {
+ //spriteBatch.Draw(mQuadtree.Texture, mRectangle, Color.Black);
+ spriteBatch.Draw(texture, new Rectangle(mPositionX, mPositionY, mSizeX, 2), Color.Black);
+ spriteBatch.Draw(texture, new Rectangle(mPositionX, mPositionY, 2, mSizeY), Color.Black);
+ spriteBatch.Draw(texture, new Rectangle(mPositionX + mSizeX, mPositionY, 2, mSizeY), Color.Black);
+ spriteBatch.Draw(texture, new Rectangle(mPositionX, mPositionY + mSizeY, mSizeX, 2), Color.Black);
+ mLt?.DrawQuadtree(spriteBatch, texture);
+ mRt?.DrawQuadtree(spriteBatch, texture);
+ mLb?.DrawQuadtree(spriteBatch, texture);
+ mRb?.DrawQuadtree(spriteBatch, texture);
+
+ // Draws the rectangle of each Creature
+ //foreach (var obj in mObjectList)
+ //{
+ // spriteBatch.Draw(mQuadtree.Texture, new Rectangle(obj.BoundaryRectangle.X, obj.BoundaryRectangle.Y, obj.BoundaryRectangle.Width, 5), Color.Red);
+ // spriteBatch.Draw(mQuadtree.Texture, new Rectangle(obj.BoundaryRectangle.X, obj.BoundaryRectangle.Y, 5, obj.BoundaryRectangle.Height), Color.Red);
+ // spriteBatch.Draw(mQuadtree.Texture, new Rectangle(obj.BoundaryRectangle.X + obj.BoundaryRectangle.Width, obj.BoundaryRectangle.Y, 5, obj.BoundaryRectangle.Height), Color.Red);
+ // spriteBatch.Draw(mQuadtree.Texture, new Rectangle(obj.BoundaryRectangle.X, obj.BoundaryRectangle.Y + obj.BoundaryRectangle.Height, obj.BoundaryRectangle.Width, 5), Color.Red);
+ //}
+ }
+ }
+}
diff --git a/V3/Objects/AbstractBuilding.cs b/V3/Objects/AbstractBuilding.cs
new file mode 100644
index 0000000..bc6061c
--- /dev/null
+++ b/V3/Objects/AbstractBuilding.cs
@@ -0,0 +1,110 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Objects
+{
+ public abstract class AbstractBuilding : IBuilding
+ {
+ private readonly string mTextureName;
+ private Texture2D mTexture;
+ private readonly BuildingFace mFacing;
+ private Rectangle mTilesetRectangle;
+
+ public Vector2 Position { get; set; }
+ protected abstract int MaxRobustness { get; }
+ public abstract int Robustness { get; protected set; }
+ public abstract string Name { get; }
+ public virtual int MaxGivesWeapons { get; protected set; }
+ private bool mIsDestroyed;
+ public Rectangle BoundaryRectangle { get; }
+ public int Id { get; }
+
+ protected AbstractBuilding(Vector2 position, Rectangle size, string textureName, BuildingFace facing)
+ {
+ Position = position;
+ Id = IdGenerator.GetNextId();
+ // TODO: Hella ugly code
+ mTilesetRectangle = size;
+ // Boundary rectangle is smaller than the texture size:
+ if (this is Castle)
+ {
+ BoundaryRectangle = new Rectangle(size.X + 96, size.Y + 296, 900, 500);
+ }
+ else
+ {
+ BoundaryRectangle = new Rectangle(size.X, size.Y + size.Height / 2, size.Width * 4 / 5, size.Height / 2);
+ }
+ mTextureName = textureName;
+ mFacing = facing;
+ }
+
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ int status = 0;
+ if (mIsDestroyed)
+ {
+ status = 2;
+ }
+ else if (Robustness < MaxRobustness / 2)
+ {
+ status = 1;
+ }
+ Rectangle source = new Rectangle(status * mTilesetRectangle.Width, (mFacing == BuildingFace.SW ? 0 : 1) * mTilesetRectangle.Height + (this is Forge ? 384 : 0), mTilesetRectangle.Width, mTilesetRectangle.Height);
+ spriteBatch.Draw(mTexture, mTilesetRectangle, source, Color.White);
+ }
+
+
+ public void LoadContent(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Textures/" + mTextureName);
+ //mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+ public void TakeDamage(int damage)
+ {
+ if (Robustness > 0)
+ {
+ Robustness -= damage;
+ }
+
+ if (Robustness <= 0)
+ {
+ Destroyed();
+ }
+ }
+
+ public void UpgradeCounter()
+ {
+ MaxGivesWeapons -= 1;
+ }
+
+
+ private void Destroyed()
+ {
+ mIsDestroyed = true;
+ }
+
+ public IGameObject GetSelf()
+ {
+ return this;
+ }
+
+ public override bool Equals(Object obj)
+ {
+ if (obj == null)
+ return false;
+ if (obj == this)
+ return true;
+ if (!(obj is IGameObject))
+ return false;
+ return Id.Equals(((IGameObject) obj).Id);
+ }
+
+ public override int GetHashCode()
+ {
+ return Id;
+ }
+ }
+}
diff --git a/V3/Objects/AbstractCreature.cs b/V3/Objects/AbstractCreature.cs
new file mode 100644
index 0000000..a4ac419
--- /dev/null
+++ b/V3/Objects/AbstractCreature.cs
@@ -0,0 +1,911 @@
+using System;
+using System.Collections.Generic;
+using Castle.Core.Internal;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Abstract class for deriving all moving objects in the game,
+ /// be it the player, his minions or the enemy.
+ /// </summary>
+ public abstract class AbstractCreature : ICreature
+ {
+ // **************************************************
+ // Internal variables, managers and values.
+ protected virtual Point CreatureSize { get; } = new Point(24);
+ protected virtual Point BoundaryShift { get; } = new Point(-12, -16);
+ protected virtual Point SelectionSize { get; } = new Point(48, 64);
+ protected virtual Point SelectionShift { get; } = new Point(-24, -40);
+ /// <summary>
+ /// Array of sprites for drawing the creature. Can have up to four entries for (ordered): Body, Head, Weapon, Offhand
+ /// </summary>
+ protected abstract ISpriteCreature[] Sprite { get; }
+ protected abstract IMovable MovementScheme { get; }
+ protected abstract CreatureType Type { get; }
+ private Texture2D mOnePixelTexture;
+ private Texture2D mSelectionTexture;
+ private readonly Pathfinder mPathfinder;
+ private readonly ContentManager mContentManager;
+ private readonly IOptionsManager mOptionsManager;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+ private static readonly Random sRandom = new Random();
+ private static readonly object sYncLock = new object();
+ private List<Arrow> mArrowList = new List<Arrow>();
+
+#if NO_AUDIO
+#else
+ private SoundEffect mSoundEffect;
+ private SoundEffectInstance mSoundEffectInstance;
+ private SoundEffect mSoundEffectHorse;
+ private SoundEffectInstance mSoundEffectInstanceHorse;
+ private SoundEffect mSoundEffectKnight;
+ private SoundEffectInstance mSoundEffectInstanceKnight;
+ private SoundEffect mSoundEffectFight;
+ private SoundEffectInstance mSoundEffectInstanceFight;
+ private SoundEffect mSoundEffectMeatball;
+ private SoundEffectInstance mSoundEffectInstanceMeatball;
+#endif
+ private bool mIsDead;
+ //private float mReculate = 1.0f;
+
+ // **************************************************
+ // Public variables and important attributes of the creature:
+ public abstract string Name { get; protected set; }
+ public abstract int Life { get; protected set; }
+ public abstract int MaxLife { get; protected set; }
+ public abstract int Speed { get; }
+ public abstract int Attack { get; protected set; }
+ public abstract int AttackRadius { get; protected set; }
+ public abstract int SightRadius { get; protected set; }
+ public abstract TimeSpan TotalRecovery { get;}
+ public abstract TimeSpan Recovery { get; set; }
+ public Vector2 Position { get; set; }
+ public Vector2 InitialPosition { private get; set; }
+ public abstract Faction Faction { get; }
+ public int Id { get; }
+
+ public bool IsDead
+ {
+ get { return Life <= 0; }
+ }
+
+ public bool IsUpgraded { get; set; }
+
+ public abstract IBuilding IsAttackingBuilding { get; set; }
+ public MovementDirection MovementDirection { get; set; }
+ public MovementState MovementState { get; set; } = MovementState.Idle;
+ public Rectangle SelectionRectangle => new Rectangle(Position.ToPoint() + SelectionShift, SelectionSize);
+ public Rectangle BoundaryRectangle => new Rectangle(Position.ToPoint() + BoundaryShift, CreatureSize);
+ public bool IsSelected { get; set; }
+ private static int sMaxNumberOfSoundHorse;
+ private static int sMaxNumberOfSoundMeatball;
+ private static int sMaxNumberOfSoundZombie;
+ private static int sMaxNumberOfSoundKnight;
+
+ public abstract ICreature IsAttacking { get; set; }
+
+ /// <summary>
+ /// Color of the rectangle displayed when creature is selected.
+ /// </summary>
+ protected virtual Color SelectionColor
+ {
+ get
+ {
+ switch (Faction)
+ {
+ case Faction.Undead:
+ return Color.Red;
+ case Faction.Kingdom:
+ return Color.Blue;
+ case Faction.Plebs:
+ return Color.Green;
+ default:
+ return Color.Gray;
+ }
+ }
+ }
+
+ protected AbstractCreature(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ {
+ mPathfinder = pathfinder;
+ mContentManager = contentManager;
+ mOptionsManager = optionsManager;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ Id = IdGenerator.GetNextId();
+ LoadContent(mContentManager);
+ }
+
+ /// <summary>
+ /// The specific creature takes damage. If its life falls below zero it dies.
+ /// </summary>
+ /// <param name="damage">Amount of received damage.</param>
+ public void TakeDamage(int damage)
+ {
+ if (Life > 0)
+ {
+ Life -= damage;
+ }
+
+ if (Life <= 0)
+ {
+ Die();
+ }
+ }
+
+ /// <summary>
+ /// Loads content files needed for drawing the creature.
+ /// </summary>
+ /// <param name="contentManager">Content manager used.</param>
+ public void LoadContent(ContentManager contentManager)
+ {
+ Sprite.ForEach(e => e?.Load(contentManager));
+ mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mSelectionTexture = contentManager.Load<Texture2D>("Sprites/selection");
+#if NO_AUDIO
+#else
+ try
+ {
+ mSoundEffect = contentManager.Load<SoundEffect>("Sounds/walking");
+ mSoundEffectInstance = mSoundEffect.CreateInstance();
+ mSoundEffectHorse = contentManager.Load<SoundEffect>("Sounds/SkeletonHorse");
+ mSoundEffectInstanceHorse = mSoundEffectHorse.CreateInstance();
+ mSoundEffectKnight = contentManager.Load<SoundEffect>("Sounds/Knight");
+ mSoundEffectInstanceKnight = mSoundEffectKnight.CreateInstance();
+ mSoundEffectFight = contentManager.Load<SoundEffect>("Sounds/punch");
+ mSoundEffectInstanceFight = mSoundEffectFight.CreateInstance();
+ mSoundEffectMeatball = contentManager.Load<SoundEffect>("Sounds/Monster_Gigante-Doberman-1334685792");
+ mSoundEffectInstanceMeatball = mSoundEffectMeatball.CreateInstance();
+ }
+ catch (DllNotFoundException)
+ {
+ // HACK: ignore sound-related errors as the sound is currently
+ // not working on the pool computers
+ }
+ catch (NoAudioHardwareException)
+ {
+ // HACK: ignore sound-related errors as the sound is currently
+ // not working on the pool computers
+ }
+#endif
+ }
+
+ /// <summary>
+ /// The creature should move to the specified destination if possible.
+ /// </summary>
+ /// <param name="destination">The desired destination.</param>
+ public void Move(Vector2 destination)
+ {
+ MovementScheme.FindPath(mPathfinder, Position, destination);
+ }
+
+ public void Update(GameTime gameTime, ICreature playerCharacter,
+ bool rightButtonPressed, Vector2 rightButtonPosition, Quadtree quadtree, ICamera camera)
+ {
+ // If Creature is dead, don't update anymore.
+ Sprite.ForEach(e => e?.PlayAnimation(gameTime));
+ if (mIsDead)
+ {
+ return;
+ }
+ bool showedByCamera = camera.ScreenRectangle.Contains(Position);
+#if NO_AUDIO
+#else
+
+ // update the volume according to the current setting
+ if (mSoundEffectInstance != null)
+ mSoundEffectInstance.Volume = mOptionsManager.Options.GetEffectiveVolume()*0.1f;
+ if (mSoundEffectInstanceHorse != null)
+ mSoundEffectInstanceHorse.Volume = mOptionsManager.Options.GetEffectiveVolume() * 0.07f;
+ if (mSoundEffectInstanceKnight != null)
+ mSoundEffectInstanceKnight.Volume = mOptionsManager.Options.GetEffectiveVolume() * 0.07f;
+ if (mSoundEffectInstanceFight != null)
+ mSoundEffectInstanceFight.Volume = mOptionsManager.Options.GetEffectiveVolume() * 0.07f;
+ if (mSoundEffectInstanceMeatball != null)
+ mSoundEffectInstanceMeatball.Volume = mOptionsManager.Options.GetEffectiveVolume() * 0.07f;
+#endif
+ if (MovementScheme.IsMoving)
+ {
+ MovementState = MovementState.Moving;
+ Position += MovementScheme.GiveNewPosition(Position, Speed);
+ MovementDirection = MovementScheme.GiveMovementDirection();
+#if NO_AUDIO
+#else
+ try
+ {
+ if (showedByCamera)
+ {
+ if ((this is Zombie || this is FemalePeasant || this is MalePeasant || this is Necromancer) &&
+ sMaxNumberOfSoundZombie < 3)
+ {
+ if (mSoundEffectInstance != null)
+ {
+ mSoundEffectInstance.Play();
+ sMaxNumberOfSoundZombie++;
+ }
+
+ }
+ if (this is SkeletonHorse && sMaxNumberOfSoundHorse < 3)
+ {
+ if (mSoundEffectInstanceHorse != null)
+ {
+ mSoundEffectInstanceHorse.Play();
+ sMaxNumberOfSoundHorse++;
+ }
+
+ }
+ if (this is Knight && sMaxNumberOfSoundKnight < 3)
+ {
+ if (mSoundEffectInstanceKnight != null)
+ {
+ mSoundEffectInstanceKnight.Play();
+ sMaxNumberOfSoundKnight++;
+ }
+
+ }
+ if (this is Meatball && sMaxNumberOfSoundMeatball < 3)
+ {
+ if (mSoundEffectInstanceMeatball != null)
+ {
+ mSoundEffectInstanceMeatball.Play();
+ sMaxNumberOfSoundMeatball++;
+ }
+
+ }
+ }
+ }
+ catch (InstancePlayLimitException)
+ {
+ // HACK: ignore sound-related errors as the sound is currently
+ // not working on the pool computers
+ }
+#endif
+ }
+ else
+ {
+#if NO_AUDIO
+#else
+ if (mSoundEffectInstance != null)
+ {
+ mSoundEffectInstance.Stop();
+ sMaxNumberOfSoundZombie--;
+ }
+ if (mSoundEffectInstanceHorse != null)
+ {
+ mSoundEffectInstanceHorse.Stop();
+ sMaxNumberOfSoundHorse--;
+ }
+ if (mSoundEffectInstanceKnight != null)
+ {
+ mSoundEffectInstanceKnight.Stop();
+ sMaxNumberOfSoundKnight--;
+ }
+
+ if (mSoundEffectInstanceMeatball != null)
+ {
+ mSoundEffectInstanceMeatball.Stop();
+ sMaxNumberOfSoundMeatball--;
+ }
+#endif
+ MovementState = MovementState.Idle;
+ }
+ if (IsDead)
+ {
+ if (mSoundEffectInstance != null)
+ {
+ mSoundEffectInstance.Stop();
+ sMaxNumberOfSoundZombie--;
+ }
+ if (mSoundEffectInstanceHorse != null)
+ {
+ mSoundEffectInstanceHorse.Stop();
+ sMaxNumberOfSoundHorse--;
+ }
+ if (mSoundEffectInstanceKnight != null)
+ {
+ mSoundEffectInstanceKnight.Stop();
+ sMaxNumberOfSoundKnight--;
+ }
+
+ if (mSoundEffectInstanceMeatball != null)
+ {
+ mSoundEffectInstanceMeatball.Stop();
+ sMaxNumberOfSoundMeatball--;
+ }
+ }
+
+ /*
+ *
+ * Random movement of Zombies
+ *
+ */
+ #region Random Movement
+ Ellipse necroArea = new Ellipse(new Vector2((int)playerCharacter.Position.X, (int)playerCharacter.Position.Y), 1280, 640);
+ float necroDistance = Vector2.Distance(Position, playerCharacter.Position);
+ List<Vector2> randomMoveVector = new List<Vector2>();
+
+
+ int rndX = RandomNumber(0, 20);
+ int rndY = RandomNumber(0, 20);
+
+ randomMoveVector.Add(new Vector2(rndX, rndY));
+ randomMoveVector.Add(new Vector2(-rndX, -rndY));
+ randomMoveVector.Add(new Vector2(rndX, -rndY));
+ randomMoveVector.Add(new Vector2(-rndX, rndY));
+
+ int rndNumber;
+
+ if (!(this is Necromancer) &&
+ Faction.Equals(Faction.Undead) &&
+ MovementState == MovementState.Idle &&
+ !necroArea.Contains(Position))
+ {
+ rndNumber = RandomNumber(1, 60);
+ if (rndNumber == 7)
+ {
+ int rndMoveVector = RandomNumber(0, 400);
+ Move(Position + randomMoveVector[rndMoveVector%4]);
+ }
+ }
+ if (Faction.Equals(Faction.Plebs))
+ {
+ rndNumber = RandomNumber(1, 60);
+ if (rndNumber == 7)
+ {
+ int rndMoveVector = RandomNumber(0, 400);
+ Move(Position + randomMoveVector[rndMoveVector%4]);
+ }
+
+ }
+ #endregion
+
+ List<IGameObject> defenders = new List<IGameObject>();
+ if (IsAttacking == null)
+ {
+ // Get the quadtree of the sight radius.
+ defenders = quadtree.GetObjectsInRectangle(new Rectangle((int) Position.X - SightRadius/2, (int) Position.Y - SightRadius/2, SightRadius, SightRadius));
+ // Returns if nothing is in sight.
+ if (defenders.Count == 0) return;
+ }
+
+ bool attacking = false;
+
+ /*
+ *
+ * COMMAND ATTACKING OF BUILDINGS
+ *
+ */
+ #region Command Attack
+
+ // Distance between Undead and Necromancer
+ if (Faction.Equals(Faction.Undead) && !(this is Necromancer))
+ {
+ // If Undead are in sight distance of the Necromancer, do stuff.
+ if (IsSelected && (int)necroDistance <= playerCharacter.AttackRadius)
+ {
+ // Get all the objects of the quadtree of the sightradius of the Necromancer
+ if (rightButtonPressed)
+ {
+ IsAttackingBuilding = null;
+ var objectsUnderMouse = quadtree.GetObjectsInRectangle(new Rectangle(rightButtonPosition.ToPoint(), new Point(1, 1)));
+ foreach (var obj in objectsUnderMouse)
+ {
+ var building = obj as IBuilding;
+ if (building == null || !building.BoundaryRectangle.Contains(rightButtonPosition) || building.Robustness == 0) continue;
+ IsAttackingBuilding = building;
+ }
+ }
+ }
+ if (IsAttackingBuilding != null )
+ {
+ if ((int)Vector2.Distance(Position, ComputeMoVector(IsAttackingBuilding)) <= AttackRadius && IsAttackingBuilding.Robustness >= 0) // (int)Vector2.Distance(Position, IsAttackingBuilding.Position) <= AttackRadius * 2
+ {
+ //var building = IsAttacking as IBuilding;
+ // Attacking
+ Recovery -= gameTime.ElapsedGameTime;
+ if (Recovery < TimeSpan.Zero)
+ {
+ Recovery = TimeSpan.Zero;
+ }
+ MovementState = MovementState.Attacking; // PlayAnimationOnce(MovementState.Attacking, TotalRecovery);
+ if (Recovery <= TimeSpan.Zero)
+ {
+ IsAttackingBuilding.TakeDamage(Attack);
+ Recovery = TotalRecovery; // Cooldown
+
+ // Throw an arrow
+ if (AttackRadius > 300)
+ CreateArrow(Position, IsAttackingBuilding.Position);
+ }
+ // If house is dead.
+ if (IsAttackingBuilding.Robustness <= 20)
+ {
+ MovementState = MovementState.Idle;
+
+ // Undead getting stronger if building is dead.
+ switch (IsAttackingBuilding.Name)
+ {
+ case "Schmiede":
+ if (IsAttackingBuilding.MaxGivesWeapons > 0 && !IsUpgraded)
+ {
+ MaxLife += 20;
+ Life += 20;
+ IsUpgraded = true;
+ IsAttackingBuilding.UpgradeCounter();
+ }
+ break;
+ case "Holzhaus":
+ if (IsAttackingBuilding.MaxGivesWeapons > 0)
+ {
+ if (this is Skeleton && !IsUpgraded)
+ {
+ ChangeEquipment(EquipmentType.Body, new SkeletonArcherSprite());
+ AttackRadius += 500;
+ SightRadius += 500;
+ IsUpgraded = true;
+ IsAttackingBuilding.UpgradeCounter();
+ }
+ if (this is Zombie && !IsUpgraded)
+ {
+ ChangeEquipment(EquipmentType.Body, new ZombieWithClubSprite());
+ Attack += 10;
+ IsUpgraded = true;
+ IsAttackingBuilding.UpgradeCounter();
+ }
+ }
+ break;
+ }
+
+ if (IsAttackingBuilding.Robustness <= 0) IsAttackingBuilding = null;
+ }
+ }
+ else
+ {
+ Move(ComputeMoVector(IsAttackingBuilding));
+ }
+ }
+ }
+
+ #endregion
+ foreach (var arrow in mArrowList)
+ {
+ arrow.UpdateArrow();
+ }
+ /*
+ *
+ * AUTO ATTACKING OF CREATURES
+ *
+ */
+ #region Auto-Attack
+
+ // Don't auto attack if Zombie got command from Necromancer.
+ if (IsAttackingBuilding != null) return;
+
+ if (IsAttacking == null)
+ {
+ List<ICreature> creatures = new List<ICreature>();
+ foreach (var defender in defenders)
+ {
+ var creature = defender as ICreature;
+ if (creature == null) continue;
+ creatures.Add(creature);
+ }
+
+ ICreature attackableCreature = null;
+ float sightDistance = SightRadius;
+
+ // Compute the nearest enemy.
+ foreach (var defender in creatures)
+ {
+ // Zombies and Knights only.
+ if (Faction.Equals(Faction.Undead) || Faction.Equals(Faction.Kingdom))
+ {
+ // If attacker-type == defender-type go to next possible defender.
+ if ((Faction.Equals(Faction.Undead) && !defender.Faction.Equals(Faction.Undead)) ||
+ (Faction.Equals(Faction.Kingdom) && defender.Faction.Equals(Faction.Undead)))
+ {
+ if (defender.Life > 0)
+ {
+ // Compute the distance of attacker and possible defender.
+ float distanceTest = Vector2.Distance(Position, defender.Position);
+
+ if (distanceTest <= sightDistance)
+ {
+ sightDistance = distanceTest;
+ attackableCreature = defender;
+ }
+ // IsAttacking = defender;
+ if (attackableCreature != null)
+ {
+ IsAttacking = defender;
+
+ }
+ }
+ }
+ }
+ }
+ if (attackableCreature == null)
+ {
+ IsAttacking = null;
+ }
+ }
+
+ if (IsAttacking == null) return;
+ /*
+ *
+ * ATTACKING OF CREATURES
+ *
+ */
+ if ((int)Vector2.Distance(Position, IsAttacking.Position) > AttackRadius &&
+ !MovementScheme.IsMoving)
+ {
+ // Do not move if attacker is already in attackrange
+ if (!attacking)
+ {
+ Move(ComputeMoVector(IsAttacking));
+ }
+ }
+
+ if (IsAttacking.IsDead || (int)Vector2.Distance(Position, IsAttacking.Position) > SightRadius)
+ {
+ IsAttacking = null;
+ MovementState = MovementState.Idle;
+ return;
+ }
+ // Attacking
+ Recovery -= gameTime.ElapsedGameTime;
+ if (Recovery < TimeSpan.Zero)
+ {
+ Recovery = TimeSpan.Zero;
+ }
+
+ // If attacker is in the attack radius of defender
+ if ((int)Vector2.Distance(Position, IsAttacking.Position) <= AttackRadius)
+ {
+ if (Recovery <= TimeSpan.Zero)
+ {
+ IsAttacking.TakeDamage(Attack);
+ PlayAnimationOnce(MovementState.Attacking, TotalRecovery);
+ Recovery = TotalRecovery; // Cooldown
+
+ // Throw an arrow
+ if (AttackRadius > 300 && IsAttacking != null)
+ CreateArrow(Position, IsAttacking.Position);
+ }
+ }
+
+ #endregion
+ }
+
+ public void PlayAnimationOnce(MovementState animation, TimeSpan duration)
+ {
+ Sprite.ForEach(e => e.PlayOnce(animation, duration));
+ }
+
+ /// <summary>
+ /// Draw the creature on the screen.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ if (IsSelected) DrawSelection(spriteBatch);
+ Sprite.ForEach(e => e?.Draw(spriteBatch, Position, MovementState, MovementDirection));
+ if (IsSelected) DrawLifeRectangle(spriteBatch);
+ foreach (var arrow in mArrowList)
+ {
+ arrow.DrawArrow(spriteBatch);
+ }
+ }
+
+ public void DrawStatic(SpriteBatch spriteBatch, Point position)
+ {
+ Sprite.ForEach(e => e?.DrawStatic(spriteBatch, position, MovementState.Idle, MovementDirection.S));
+ }
+
+ protected virtual void Die()
+ {
+ if (Faction == Faction.Plebs || Faction == Faction.Kingdom)
+ {
+ mAchievementsAndStatistics.mHundredDeadCorpses += 1;
+ mAchievementsAndStatistics.mUndeadArmy += 1;
+ mAchievementsAndStatistics.mRightHandOfDeath += 1;
+ mAchievementsAndStatistics.mKilledCreatures += 1;
+ }
+ else if (Faction == Faction.Undead)
+ {
+ mAchievementsAndStatistics.mLostServants += 1;
+ }
+ mIsDead = true;
+ Life = 0;
+ MovementState = MovementState.Dying;
+ }
+
+ private void DrawSelection(SpriteBatch spriteBatch)
+ {
+ // TODO: Remove the magic vector and adjust position.
+ spriteBatch.Draw(mSelectionTexture, Position - new Vector2(32, 16), SelectionColor);
+ /*
+ spriteBatch.Draw(mOnePixelTexture, new Rectangle(BoundaryRectangle.X, BoundaryRectangle.Y, BoundaryRectangle.Width, 2), SelectionColor);
+ spriteBatch.Draw(mOnePixelTexture, new Rectangle(BoundaryRectangle.X, BoundaryRectangle.Y + BoundaryRectangle.Height, BoundaryRectangle.Width, 2), SelectionColor);
+ spriteBatch.Draw(mOnePixelTexture, new Rectangle(BoundaryRectangle.X, BoundaryRectangle.Y, 2, BoundaryRectangle.Height), SelectionColor);
+ spriteBatch.Draw(mOnePixelTexture, new Rectangle(BoundaryRectangle.X + BoundaryRectangle.Width, BoundaryRectangle.Y, 2, BoundaryRectangle.Height), SelectionColor);
+ */
+ }
+
+ private void DrawLifeRectangle(SpriteBatch spriteBatch)
+ {
+ var backgroundRectangle = new Rectangle(SelectionRectangle.X, SelectionRectangle.Y - 12, SelectionRectangle.Width, SelectionRectangle.Height / 10);
+ var lifeBarRectangle = new Rectangle(SelectionRectangle.X + 2, SelectionRectangle.Y - 11, SelectionRectangle.Width - 3, SelectionRectangle.Height / 10 - 2);
+ spriteBatch.Draw(mOnePixelTexture, backgroundRectangle, Color.Black * 0.7f);
+ spriteBatch.Draw(mOnePixelTexture, new Rectangle(lifeBarRectangle.X, lifeBarRectangle.Y, lifeBarRectangle.Width * Life / MaxLife, lifeBarRectangle.Height), Color.Firebrick);
+ }
+
+ /// <summary>
+ /// Change the equipment/sprite of the creature to something other.
+ /// </summary>
+ /// <param name="equipmentType">Which part of the equipment should be changed.</param>
+ /// <param name="sprite">Which sprite to use instead.</param>
+ public void ChangeEquipment(EquipmentType equipmentType, ISpriteCreature sprite)
+ {
+ if (Sprite.Length > (int) equipmentType)
+ {
+ sprite.Load(mContentManager);
+ Sprite[(int)equipmentType] = sprite;
+ }
+#if DEBUG
+ else
+ {
+ throw new Exception("Creature does not have a " + equipmentType.ToString() + " slot. For further information talk to Thomas.");
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Returns the object instance without modifications.
+ /// </summary>
+ /// <returns>This object.</returns>
+ public virtual IGameObject GetSelf()
+ {
+ return this;
+ }
+
+ public void ResetPosition()
+ {
+ Position = InitialPosition;
+ }
+
+ #region Compute Move Distance
+ /// <summary>
+ /// Returns vector for the moving distance to attack.
+ /// </summary>
+ /// <returns>Vector.</returns>
+ private Vector2 ComputeMoVector(IGameObject gameObject)
+ {
+ Vector2 goToPosition = new Vector2(Position.X, Position.Y);
+
+ /*
+ * P
+ * D
+ *
+ */
+ if (Position.X <= gameObject.BoundaryRectangle.Left &&
+ Position.Y <= gameObject.BoundaryRectangle.Top)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Left - AttackRadius * 0.5f,
+ gameObject.BoundaryRectangle.Top - AttackRadius * 0.5f);
+ }
+ /*
+ * P
+ * D
+ *
+ */
+ if (Position.X >= gameObject.BoundaryRectangle.Left &&
+ Position.X <= gameObject.BoundaryRectangle.Right &&
+ Position.Y <= gameObject.BoundaryRectangle.Top)
+ {
+ goToPosition = new Vector2(Position.X,
+ gameObject.BoundaryRectangle.Top - AttackRadius * 0.5f);
+ }
+ /*
+ * P
+ * D
+ *
+ */
+ if (Position.X >= gameObject.BoundaryRectangle.Right &&
+ Position.Y <= gameObject.BoundaryRectangle.Top)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Right - AttackRadius * 0.5f,
+ gameObject.BoundaryRectangle.Top - AttackRadius * 0.5f);
+ }
+ /*
+ *
+ * D P
+ *
+ */
+ if (Position.X >= gameObject.BoundaryRectangle.Right &&
+ Position.Y <= gameObject.BoundaryRectangle.Bottom &&
+ Position.Y >= gameObject.BoundaryRectangle.Top)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Right - AttackRadius * 0.5f,
+ Position.Y);
+ }
+ /*
+ *
+ * D
+ * P
+ */
+ if (Position.X >= gameObject.BoundaryRectangle.Right &&
+ Position.Y >= gameObject.BoundaryRectangle.Bottom)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Right - AttackRadius * 0.5f,
+ gameObject.BoundaryRectangle.Bottom - AttackRadius * 0.5f);
+ }
+ /*
+ *
+ * D
+ * P
+ */
+ if (Position.X >= gameObject.BoundaryRectangle.Left &&
+ Position.X <= gameObject.BoundaryRectangle.Right &&
+ Position.Y >= gameObject.BoundaryRectangle.Bottom)
+ {
+ goToPosition = new Vector2(Position.X,
+ gameObject.BoundaryRectangle.Bottom - AttackRadius * 0.5f);
+ }
+ /*
+ *
+ * D
+ * P
+ */
+ if (Position.X <= gameObject.BoundaryRectangle.Left &&
+ Position.Y >= gameObject.BoundaryRectangle.Bottom)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Left - AttackRadius * 0.5f,
+ gameObject.BoundaryRectangle.Bottom - AttackRadius * 0.5f);
+ }
+ /*
+ *
+ * P D
+ *
+ */
+ if (Position.X <= gameObject.BoundaryRectangle.Left &&
+ Position.Y <= gameObject.BoundaryRectangle.Bottom &&
+ Position.Y >= gameObject.BoundaryRectangle.Top)
+ {
+ goToPosition =
+ new Vector2(gameObject.BoundaryRectangle.Left - AttackRadius * 0.5f,
+ Position.Y);
+ }
+
+ return goToPosition;
+ }
+ #endregion
+
+ public override bool Equals(Object obj)
+ {
+ if (obj == null)
+ return false;
+ if (obj == this)
+ return true;
+ if (!(obj is IGameObject))
+ return false;
+ return Id.Equals(((IGameObject) obj).Id);
+ }
+
+ public override int GetHashCode()
+ {
+ return Id;
+ }
+
+ /// <summary>
+ /// Returns synchronized random value.
+ /// </summary>
+ /// <returns>Vector.</returns>
+ /// <param name="min">Lower bound for random value.</param>
+ /// <param name="max">Upper bound +1 for random value.</param>
+ private static int RandomNumber(int min, int max)
+ {
+ // synchronize
+ lock (sYncLock)
+ {
+ return sRandom.Next(min, max);
+ }
+ }
+
+ public void Heal(int amount)
+ {
+ if (Life + amount < MaxLife)
+ {
+ Life += amount;
+ }
+ else
+ {
+ Life = MaxLife;
+ }
+ }
+
+ public void Empower(int modifier)
+ {
+ MaxLife *= modifier;
+ Life *= modifier;
+ }
+
+ private void CreateArrow(Vector2 start, Vector2 goal)
+ {
+ if (mArrowList.Count >= 60) mArrowList.Clear();
+ Arrow arrow = new Arrow(start, goal);
+ arrow.LoadArrow(mContentManager);
+ mArrowList.Add(arrow);
+ }
+
+ /// <summary>
+ /// Save this creature’s data to a CreatureData object.
+ /// </summary>
+ /// <returns>the CreatureData object with the status of this creature</returns>
+ public virtual CreatureData SaveData()
+ {
+ var creatureData = new CreatureData();
+ creatureData.Type = Type;
+ creatureData.Id = Id;
+ creatureData.Life = Life;
+ creatureData.MaxLife = MaxLife;
+ creatureData.Attack = Attack;
+ creatureData.Recovery = Recovery;
+ creatureData.IsUpgraded = IsUpgraded;
+ creatureData.PositionX = Position.X;
+ creatureData.PositionY = Position.Y;
+ creatureData.MovementDirection = MovementDirection;
+ creatureData.MovementState = MovementState;
+ creatureData.MovementData = MovementScheme.SaveData();
+ // references
+ if (IsAttacking != null)
+ creatureData.IsAttackingId = IsAttacking.Id;
+ return creatureData;
+ }
+
+ /// <summary>
+ /// Restore the creature's state from the given data.
+ /// </summary>
+ /// <param name="creatureData">the state of the creature to restore</param>
+ public virtual void LoadData(CreatureData creatureData)
+ {
+ // ID is set by IdGenerator.SetIdOnce
+ Life = creatureData.Life;
+ MaxLife = creatureData.MaxLife;
+ Attack = creatureData.Attack;
+ Recovery = creatureData.Recovery;
+ IsUpgraded = creatureData.IsUpgraded;
+ Position = new Vector2(creatureData.PositionX, creatureData.PositionY);
+ MovementDirection = creatureData.MovementDirection;
+ MovementState = creatureData.MovementState;
+ MovementScheme.LoadData(creatureData.MovementData);
+
+ if (Life <= 0)
+ Die();
+ }
+
+ /// <summary>
+ /// Restore the creature's references to other creatures from the given data.
+ /// </summary>
+ /// <param name="data">the state of the creature to restore</param>
+ /// <param name="creatures">the list of all creatures by ID</param>
+ public virtual void LoadReferences(CreatureData data, Dictionary<int, ICreature> creatures)
+ {
+ if (creatures.ContainsKey(data.IsAttackingId))
+ IsAttacking = creatures[data.IsAttackingId];
+ }
+ }
+}
diff --git a/V3/Objects/Arrow.cs b/V3/Objects/Arrow.cs
new file mode 100644
index 0000000..57200b2
--- /dev/null
+++ b/V3/Objects/Arrow.cs
@@ -0,0 +1,109 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ public sealed class Arrow
+ {
+ //Drawing an arrow
+ private const int SpeedModifier = 10;
+ private readonly ArrowSprite mArrow;
+ private Vector2 mArrowPosition;
+ private Vector2 mArrowGoal;
+ private Vector2 mDirection;
+ private bool mArrowDraw;
+ private readonly MovementDirection mMovementDirection;
+
+ public Arrow(Vector2 start, Vector2 goal)
+ {
+ mArrowPosition = start;
+ mArrowGoal = goal;
+ mDirection = goal - start;
+ mDirection.Normalize();
+ mMovementDirection = GiveMovementDirection(mDirection);
+ mArrow = new ArrowSprite();
+ mArrowDraw = true;
+ }
+
+ public void LoadArrow(ContentManager contentManager)
+ {
+ mArrow.Load(contentManager);
+ }
+
+ public void DrawArrow(SpriteBatch spriteBatch)
+ {
+ if (mArrowDraw)
+ mArrow.Draw(spriteBatch, mArrowPosition, MovementState.Idle, mMovementDirection);
+ }
+
+ /// <summary>
+ /// The moving for arrow
+ /// </summary>
+ public void UpdateArrow()
+ {
+ if (mArrowDraw)
+ mArrowPosition += mDirection * SpeedModifier;
+
+ if (Vector2.Distance(mArrowPosition, mArrowGoal) < 1f * SpeedModifier)
+ mArrowDraw = false;
+ }
+
+ /// <summary>
+ /// Calculates the direction the creature is looking when moving.
+ /// Method copied from PlayerMovement class.
+ /// </summary>
+ private MovementDirection GiveMovementDirection(Vector2 direction)
+ {
+ // |\
+ // |α\ α == 22.5°
+ // b| \ 1 β == 67.5°
+ // | β\
+ // ──────
+ // a
+ const float b = 0.92f; // b == sin β
+ const float a = 0.38f; // a == sin α
+ MovementDirection movementDirection;
+ if (direction.X < -b)
+ {
+ movementDirection = MovementDirection.W;
+ }
+ else if (direction.X > b)
+ {
+ movementDirection = MovementDirection.O;
+ }
+ else if (direction.Y > 0)
+ {
+ if (direction.X < -a)
+ {
+ movementDirection = MovementDirection.SW;
+ }
+ else if (direction.X > a)
+ {
+ movementDirection = MovementDirection.SO;
+ }
+ else
+ {
+ movementDirection = MovementDirection.S;
+ }
+ }
+ else
+ {
+ if (direction.X < -a)
+ {
+ movementDirection = MovementDirection.NW;
+ }
+ else if (direction.X > a)
+ {
+ movementDirection = MovementDirection.NO;
+ }
+ else
+ {
+ movementDirection = MovementDirection.N;
+ }
+ }
+ return movementDirection;
+ }
+ }
+}
diff --git a/V3/Objects/BuildingState.cs b/V3/Objects/BuildingState.cs
new file mode 100644
index 0000000..79c621e
--- /dev/null
+++ b/V3/Objects/BuildingState.cs
@@ -0,0 +1,8 @@
+// ReSharper disable InconsistentNaming
+namespace V3.Objects
+{
+ public enum BuildingFace
+ {
+ SW, NO
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Castle.cs b/V3/Objects/Castle.cs
new file mode 100644
index 0000000..fd04ddd
--- /dev/null
+++ b/V3/Objects/Castle.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Objects
+{
+ public sealed class Castle : AbstractBuilding
+ {
+ public override string Name { get; } = "Burg";
+ protected override int MaxRobustness { get; } = 800;
+ public override int Robustness { get; protected set; } = 800;
+
+ public Castle(Vector2 position, Rectangle size, string textureName, BuildingFace facing) : base(position, size, textureName, facing)
+ {
+ }
+ }
+}
diff --git a/V3/Objects/CreatureFactory.cs b/V3/Objects/CreatureFactory.cs
new file mode 100644
index 0000000..238ea41
--- /dev/null
+++ b/V3/Objects/CreatureFactory.cs
@@ -0,0 +1,127 @@
+using System;
+using Microsoft.Xna.Framework;
+using V3.Data;
+
+namespace V3.Objects
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class CreatureFactory
+ {
+ private readonly IBasicCreatureFactory mFactory;
+ private readonly Random mRnd = new Random();
+
+ public CreatureFactory(IBasicCreatureFactory factory)
+ {
+ mFactory = factory;
+ }
+
+ public ICreature CreateCreature(CreatureType type, int id)
+ {
+ IdGenerator.SetIdOnce(id);
+ switch (type)
+ {
+ case CreatureType.FemalePeasant:
+ return mFactory.CreateFemalePeasant();
+ case CreatureType.King:
+ return mFactory.CreateKing();
+ case CreatureType.KingsGuard:
+ return mFactory.CreateKingsGuard();
+ case CreatureType.Knight:
+ return mFactory.CreateKnight();
+ case CreatureType.MalePeasant:
+ return mFactory.CreateMalePeasant();
+ case CreatureType.Meatball:
+ return mFactory.CreateMeatball();
+ case CreatureType.Necromancer:
+ return mFactory.CreateNecromancer();
+ case CreatureType.Prince:
+ return mFactory.CreatePrince();
+ case CreatureType.Skeleton:
+ return mFactory.CreateSkeleton();
+ case CreatureType.Zombie:
+ return mFactory.CreateZombie();
+ default:
+ IdGenerator.ClearIdOnce();
+ return null;
+ }
+ }
+
+ public MalePeasant CreateMalePeasant(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateMalePeasant(), position, movementDirection);
+ }
+
+ public FemalePeasant CreateFemalePeasant(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateFemalePeasant(), position, movementDirection);
+ }
+
+ public Necromancer CreateNecromancer(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateNecromancer(), position, movementDirection);
+ }
+
+ public Skeleton CreateSkeleton(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateSkeleton(), position, movementDirection);
+ }
+
+ public SkeletonElite CreateSkeletonElite(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateSkeletonElite(), position, movementDirection);
+ }
+
+ public Zombie CreateZombie(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateZombie(), position, movementDirection);
+ }
+
+ public Knight CreateKnight(Vector2 position, MovementDirection movementDirection)
+ {
+ Knight knight = CreateCreature(mFactory.CreateKnight(), position, movementDirection);
+ if (mRnd.Next(3) == 0)
+ {
+ knight.MakeFemale();
+ }
+ return knight;
+ }
+
+ public KingsGuard CreateKingsGuard(Vector2 position, MovementDirection movementDirection)
+ {
+ KingsGuard guard = CreateCreature(mFactory.CreateKingsGuard(), position, movementDirection);
+ if (mRnd.Next(3) == 0)
+ {
+ guard.MakeFemale();
+ }
+ return guard;
+ }
+
+ public SkeletonHorse CreateSkeletonHorse(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateSkeletonHorse(), position, movementDirection);
+ }
+
+ public Meatball CreateMeatball(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateMeatball(), position, movementDirection);
+ }
+
+ public Prince CreatePrince(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreatePrince(), position, movementDirection);
+ }
+
+ public King CreateKing(Vector2 position, MovementDirection movementDirection)
+ {
+ return CreateCreature(mFactory.CreateKing(), position, movementDirection);
+ }
+
+ private T CreateCreature<T>(T creature, Vector2 position, MovementDirection movementDirection) where T: ICreature
+ {
+ creature.Position = position;
+ creature.InitialPosition = position;
+ creature.MovementDirection = movementDirection;
+ return creature;
+ }
+ }
+}
diff --git a/V3/Objects/FemalePeasant.cs b/V3/Objects/FemalePeasant.cs
new file mode 100644
index 0000000..855685c
--- /dev/null
+++ b/V3/Objects/FemalePeasant.cs
@@ -0,0 +1,50 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Class for simple peasants which will be transformed into zombies.
+ /// </summary>
+ public sealed class FemalePeasant : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Dorfbewohnerin";
+ public override int Life { get; protected set; } = 20;
+ public override int MaxLife { get; protected set; } = 20;
+ public override int Speed { get; } = 10;
+ public override int Attack { get; protected set; } = 5;
+ public override int AttackRadius { get; protected set; } = 0;
+ public override int SightRadius { get; protected set; } = 20;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.3);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new ClothFemaleSprite(), new HeadFemaleSprite(), null};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.FemalePeasant;
+ public override Faction Faction { get; } = Faction.Plebs;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+
+ public FemalePeasant(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ public void RemoveArmor()
+ {
+ if (Sprite[0] is NudeFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new ClothFemaleSprite());
+ }
+ else if (Sprite[0] is ClothFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeFemaleSprite());
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Forge.cs b/V3/Objects/Forge.cs
new file mode 100644
index 0000000..1da3970
--- /dev/null
+++ b/V3/Objects/Forge.cs
@@ -0,0 +1,19 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A Forge which can be attacked.
+ /// </summary>
+ public sealed class Forge : AbstractBuilding
+ {
+ public override string Name { get; } = "Schmiede";
+ protected override int MaxRobustness { get; } = 200;
+ public override int Robustness { get; protected set; } = 200;
+ public override int MaxGivesWeapons { get; protected set; } = 10;
+
+ public Forge(Vector2 position, Rectangle size, string textureName, BuildingFace facing) : base(position, size, textureName, facing)
+ {
+ }
+ }
+}
diff --git a/V3/Objects/IBasicCreatureFactory.cs b/V3/Objects/IBasicCreatureFactory.cs
new file mode 100644
index 0000000..9b7a88b
--- /dev/null
+++ b/V3/Objects/IBasicCreatureFactory.cs
@@ -0,0 +1,28 @@
+namespace V3.Objects
+{
+ public interface IBasicCreatureFactory
+ {
+ MalePeasant CreateMalePeasant();
+
+ FemalePeasant CreateFemalePeasant();
+
+ Necromancer CreateNecromancer();
+
+ Skeleton CreateSkeleton();
+ SkeletonElite CreateSkeletonElite();
+
+ Zombie CreateZombie();
+
+ Knight CreateKnight();
+
+ SkeletonHorse CreateSkeletonHorse();
+
+ Meatball CreateMeatball();
+
+ Prince CreatePrince();
+
+ King CreateKing();
+
+ KingsGuard CreateKingsGuard();
+ }
+}
diff --git a/V3/Objects/IBuilding.cs b/V3/Objects/IBuilding.cs
new file mode 100644
index 0000000..0fdd0aa
--- /dev/null
+++ b/V3/Objects/IBuilding.cs
@@ -0,0 +1,20 @@
+namespace V3.Objects
+{
+ public interface IBuilding : IGameObject
+ {
+ int Robustness { get; }
+ string Name { get; }
+ int MaxGivesWeapons { get; }
+
+ /// <summary>
+ /// Building takes specific amount of damage. Substracted from Robustness.
+ /// </summary>
+ /// <param name="damage">TakeDamage taken</param>
+ void TakeDamage(int damage);
+
+ /// <summary>
+ /// Building can give a fixed amount of upgrades.
+ /// </summary>
+ void UpgradeCounter();
+ }
+}
diff --git a/V3/Objects/ICreature.cs b/V3/Objects/ICreature.cs
new file mode 100644
index 0000000..891ada7
--- /dev/null
+++ b/V3/Objects/ICreature.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Data;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A moving game object.
+ /// </summary>
+ public interface ICreature : IGameObject
+ {
+ string Name { get; }
+ Vector2 InitialPosition { set; }
+ int Life { get; }
+ int MaxLife { get; }
+ int Speed { get; }
+ int Attack { get; }
+ int AttackRadius { get; }
+ int SightRadius { get; }
+ TimeSpan TotalRecovery { get; }
+ TimeSpan Recovery { get; set; }
+ /// <summary>
+ /// Area where the creature is standing. Used for collisions.
+ /// </summary>
+ /// <summary>
+ /// Where you can click to select the creature.
+ /// </summary>
+ Rectangle SelectionRectangle { get; }
+ bool IsSelected { get; set; }
+ ICreature IsAttacking { get; set; }
+ IBuilding IsAttackingBuilding { get; set; }
+ MovementDirection MovementDirection { get; set; }
+ MovementState MovementState { get; set; }
+ Faction Faction { get; }
+ bool IsDead { get; }
+ bool IsUpgraded { get; set; }
+
+ /// <summary>
+ /// Creature takes specific amount of damage. Substracted from Life.
+ /// </summary>
+ /// <param name="damage">TakeDamage taken.</param>
+ void TakeDamage(int damage);
+
+ /// <summary>
+ /// Give command to move to desired destination. Not instant movement.
+ /// </summary>
+ /// <param name="destination">Destination in pixels.</param>
+ void Move (Vector2 destination);
+
+ //void ImportentMove(IGameObject creature);
+
+ /// <summary>
+ /// Draws a static non-animated sprite (for HUD) at specified position.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ /// <param name="position">Position where to draw the sprite.</param>
+ void DrawStatic(SpriteBatch spriteBatch, Point position);
+
+ /// <summary>
+ /// Update the creature behaviour and animation.
+ /// </summary>
+ void Update(GameTime gameTime, ICreature playerCharacter,
+ bool rightButtonPressed, Vector2 rightButtonPosition, Quadtree quadtree, ICamera camera);
+
+ /// <summary>
+ /// Change the equipment/sprite of the creature to something other.
+ /// If in debug mode the function throws an exception if the creature does not have the specified equipment slots.
+ /// </summary>
+ /// <param name="equipmentType">Which part of the equipment should be changed.</param>
+ /// <param name="sprite">Which sprite to use instead.</param>
+ void ChangeEquipment(EquipmentType equipmentType, ISpriteCreature sprite);
+
+ /// <summary>
+ /// Sets back the position of the creature to its state when created.
+ /// </summary>
+ void ResetPosition();
+
+ /// <summary>
+ /// Plays the specified animation fully, but only once.
+ /// </summary>
+ /// <param name="animation">For which movement state the animation should be played.</param>
+ /// <param name="duration">How long (or how slow) should the animation be?</param>
+ void PlayAnimationOnce(MovementState animation, TimeSpan duration);
+
+ /// <summary>
+ /// Heals the creature. Can not go over MaxLife.
+ /// </summary>
+ /// <param name="amount">How much HP the creature gains.</param>
+ void Heal(int amount);
+
+ /// <summary>
+ /// Creature gets more life and maxlife. Used for testing in Techdemo.
+ /// </summary>
+ /// <param name="modifier">Multiplyier for Life.</param>
+ void Empower(int modifier);
+
+ /// <summary>
+ /// Save this creature’s data to a CreatureData object.
+ /// </summary>
+ /// <returns>the CreatureData object with the status of this creature</returns>
+ CreatureData SaveData();
+
+ /// <summary>
+ /// Restore the creature's state from the given data.
+ /// </summary>
+ /// <param name="data">the state of the creature to restore</param>
+ void LoadData(CreatureData data);
+
+ /// <summary>
+ /// Restore the creature's references to other creatures from the given data.
+ /// </summary>
+ /// <param name="data">the state of the creature to restore</param>
+ /// <param name="creatures">the list of all creatures by ID</param>
+ void LoadReferences(CreatureData data, Dictionary<int, ICreature> creatures);
+ }
+}
diff --git a/V3/Objects/IGameObject.cs b/V3/Objects/IGameObject.cs
new file mode 100644
index 0000000..556e918
--- /dev/null
+++ b/V3/Objects/IGameObject.cs
@@ -0,0 +1,43 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A game object which is placed on the map.
+ /// </summary>
+ public interface IGameObject
+ {
+ Vector2 Position { get; set; }
+
+ /// <summary>
+ /// A unique ID for this game object, with which it can be identified.
+ /// All implementations should use the IdGenerator to generate this ID.
+ /// </summary>
+ int Id { get; }
+
+ /// <summary>
+ /// Draws the game object on the screen.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ void Draw(SpriteBatch spriteBatch);
+
+ /// <summary>
+ /// The size of the object.
+ /// </summary>
+ Rectangle BoundaryRectangle { get; }
+
+ /// <summary>
+ /// Loads needed graphics.
+ /// </summary>
+ /// <param name="contentManager">Content manager used. </param>
+ void LoadContent(ContentManager contentManager);
+
+ /// <summary>
+ /// Returns the object instance without modifications.
+ /// </summary>
+ /// <returns>This object.</returns>
+ IGameObject GetSelf();
+ }
+}
diff --git a/V3/Objects/IObjectsManager.cs b/V3/Objects/IObjectsManager.cs
new file mode 100644
index 0000000..e8fa17b
--- /dev/null
+++ b/V3/Objects/IObjectsManager.cs
@@ -0,0 +1,131 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Map;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Objects manager for all game objects, be is creatures or buildings or even simple landscape objects.
+ /// </summary>
+ public interface IObjectsManager
+ {
+ //***** FOR TESTING PURPOSES!
+ List<ICreature> AddToSelectables { get; }
+ List<ICreature> CreatureList { get; }
+ List<ICreature> UndeadCreatures { get; }
+ //List<ICreature> KingdromCreatures { get; }
+ //List<ICreature> PlebCreatures { get; }
+ //***** NOT FOR TESTING PURPOSES ANYMORE!
+
+ /// <summary>
+ /// Gets the current player character. Usually the necromancer.
+ /// Do not set directly! Use CreatePlayerCharacter() instead!
+ /// </summary>
+ ICreature PlayerCharacter { get; }
+
+ ICreature Boss { get; }
+
+ ICreature Prince { get; }
+
+ Castle Castle { get; }
+
+ /// <summary>
+ /// If you load a new map with new objects you need to initialize the objects manager again.
+ /// (Or else you have all the current objects on the new map.)
+ /// </summary>
+ /// <param name="mapManager"></param>
+ void Initialize(IMapManager mapManager);
+
+ /// <summary>
+ /// Removes all objects from the object manager.
+ /// </summary>
+ void Clear();
+
+ /// <summary>
+ /// Creates the player character. This should be the first thing you do
+ /// after you created or initialized the objects manager.
+ /// </summary>
+ /// <param name="necromancer"></param>
+ void CreatePlayerCharacter(Necromancer necromancer);
+
+ /// <summary>
+ /// Creates the boss of the level. Game is won if the boss is killed.
+ /// </summary>
+ /// <param name="boss">Some ICreature to kill for winning.</param>
+ void CreateBoss(ICreature boss);
+
+ /// <summary>
+ /// Create the prince, a small boss.
+ /// </summary>
+ /// <param name="prince">Some hard ICreature to kill.</param>
+ void CreatePrince(ICreature prince);
+
+ /// <summary>
+ /// Creates a creature for the game and inserts it in the appropriate data structures.
+ /// </summary>
+ /// <param name="creature"></param>
+ void CreateCreature(ICreature creature);
+
+ /// <summary>
+ /// Removes specified creature from the game.
+ /// </summary>
+ /// <param name="creature">The creature to be removed.</param>
+ void RemoveCreature(ICreature creature);
+
+ /// <summary>
+ /// Draws all currently shown objects on the map.
+ /// </summary>
+ /// <param name="spriteBatch">The sprite batch used.</param>
+ /// <param name="camera">Camera for calculating which objects need to be drawn.</param>
+ void Draw(SpriteBatch spriteBatch, ICamera camera);
+
+ /// <summary>
+ /// Draws a visual representation of the Quadtree. For debugging purposes.
+ /// </summary>
+ /// <param name="spriteBatch"></param>
+ void DrawQuadtree(SpriteBatch spriteBatch);
+
+ /// <summary>
+ /// Update the behaviour of all creatures on the map.
+ /// </summary>
+ /// <param name="gameTime">Current game time.</param>
+ /// <param name="rightButtonPressed">Did the player press the right mouse button?</param>
+ /// <param name="rightButtonPosition">Where is the mouse currently?</param>
+ /// <param name="camera">Camera for checking where to do important updates.</param>
+ void Update(GameTime gameTime, bool rightButtonPressed, Vector2 rightButtonPosition, ICamera camera);
+
+ /// <summary>
+ /// Imports the TextureObjects from the objects map layer for drawing things in the right order.
+ /// </summary>
+ /// <param name="textureObjects"></param>
+ void ImportMapObjects(List<IGameObject> textureObjects);
+
+ /// <summary>
+ /// Get all objects in the given rectangles.
+ /// </summary>
+ /// <param name="rectangle">The rectangle which defines the area of the returnes objects.</param>
+ /// <returns>Game objects in the rectangle.</returns>
+ List<IGameObject> GetObjectsInRectangle(Rectangle rectangle);
+
+ /// <summary>
+ /// Gets all creatures which are in the given ellipse area.
+ /// </summary>
+ /// <param name="ellipse">To check if creature is in ellipse area.</param>
+ /// <returns></returns>
+ List<ICreature> GetCreaturesInEllipse(Ellipse ellipse);
+
+ /// <summary>
+ /// Playing around with some cheating codes.
+ /// </summary>
+ void ExposeTheLiving();
+
+ /// <summary>
+ /// Checks if a creature is standing in a graveyard area.
+ /// </summary>
+ /// <param name="creature">Check for this creature.</param>
+ /// <returns>True when standing in graveyard area.</returns>
+ bool InGraveyardArea(ICreature creature);
+ }
+}
diff --git a/V3/Objects/IdGenerator.cs b/V3/Objects/IdGenerator.cs
new file mode 100644
index 0000000..08976ca
--- /dev/null
+++ b/V3/Objects/IdGenerator.cs
@@ -0,0 +1,59 @@
+using System;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Static helper class that generates unique IDs for game objects.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class IdGenerator
+ {
+ private static int sCurrentId;
+
+ private static int? sIdOnce;
+
+ private IdGenerator()
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Get an ID for a new game object.
+ /// </summary>
+ /// <returns>the ID to use for a new game object</returns>
+ public static int GetNextId()
+ {
+ int id;
+ if (sIdOnce.HasValue)
+ {
+ id = sIdOnce.Value;
+ ClearIdOnce();
+ }
+ else
+ {
+ id = sCurrentId;
+ sCurrentId++;
+ }
+
+ return id;
+ }
+
+ /// <summary>
+ /// Sets the ID to use only for the next object that is created.
+ /// </summary>
+ /// <param name="id">the id for the next object</param>
+ public static void SetIdOnce(int id)
+ {
+ sIdOnce = id;
+ }
+
+ /// <summary>
+ /// Clear the ID stored by SetIdOnce that is used only for the
+ /// next object.
+ /// </summary>
+ public static void ClearIdOnce()
+ {
+ sIdOnce = null;
+ }
+ }
+}
diff --git a/V3/Objects/King.cs b/V3/Objects/King.cs
new file mode 100644
index 0000000..b1c9713
--- /dev/null
+++ b/V3/Objects/King.cs
@@ -0,0 +1,33 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class King : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "König Harry";
+ public override int Life { get; protected set; } = 10000;
+ public override int MaxLife { get; protected set; } = 10000;
+ public override int Speed { get; } = 10;
+ public override int Attack { get; protected set; } = 45;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 500;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.8);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = { new KingSprite() };
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.King;
+ public override Faction Faction { get; } = Faction.Kingdom;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public King(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics) : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+ }
+}
diff --git a/V3/Objects/KingsGuard.cs b/V3/Objects/KingsGuard.cs
new file mode 100644
index 0000000..a0f3bc5
--- /dev/null
+++ b/V3/Objects/KingsGuard.cs
@@ -0,0 +1,62 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ public sealed class KingsGuard : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Königliche Garde";
+ public override int Life { get; protected set; } = 200;
+ public override int MaxLife { get; protected set; } = 200;
+ public override int Speed { get; } = 7;
+ public override int Attack { get; protected set; } = 25;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 200;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.5);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = { new PlateSprite(), new HeadPlateSprite(), new LongswordSprite(), new ShieldSprite() };
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.KingsGuard;
+ public override Faction Faction { get; } = Faction.Kingdom;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public KingsGuard(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager,achievementsAndStatistics)
+ {
+ }
+
+ public void MakeFemale()
+ {
+ ChangeEquipment(EquipmentType.Body, new PlateFemaleSprite());
+ ChangeEquipment(EquipmentType.Head, new HeadPlateFemaleSprite());
+ ChangeEquipment(EquipmentType.Weapon, new LongswordFemaleSprite());
+ ChangeEquipment(EquipmentType.Offhand, new ShieldFemaleSprite());
+ }
+
+ public void RemoveArmor()
+ {
+ if (Sprite[(int)EquipmentType.Body] is PlateSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeSprite());
+ }
+ else if (Sprite[(int)EquipmentType.Body] is PlateFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeFemaleSprite());
+ }
+ else if (Sprite[(int)EquipmentType.Body] is NudeSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new PlateSprite());
+ }
+ else if (Sprite[(int)EquipmentType.Body] is NudeFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new PlateFemaleSprite());
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Knight.cs b/V3/Objects/Knight.cs
new file mode 100644
index 0000000..ef31f2a
--- /dev/null
+++ b/V3/Objects/Knight.cs
@@ -0,0 +1,65 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A knight which fights against the necromancer.
+ /// </summary>
+ public sealed class Knight : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Ritter";
+ public override int Life { get; protected set; } = 120;
+ public override int MaxLife { get; protected set; } = 120;
+ public override int Speed { get; } = 8;
+ public override int Attack { get; protected set; } = 15;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 200;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.5);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new ChainSprite(), new HeadChainSprite(), new ShortswordSprite(), new BucklerSprite()};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.Knight;
+ public override Faction Faction { get; } = Faction.Kingdom;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public Knight(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ public void MakeFemale()
+ {
+ ChangeEquipment(EquipmentType.Body, new ChainFemaleSprite());
+ ChangeEquipment(EquipmentType.Head, new HeadChainFemaleSprite());
+ ChangeEquipment(EquipmentType.Weapon, new ShortswordFemaleSprite());
+ ChangeEquipment(EquipmentType.Offhand, new BucklerFemaleSprite());
+ }
+
+ public void RemoveArmor()
+ {
+ if (Sprite[(int) EquipmentType.Body] is ChainSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeSprite());
+ }
+ else if (Sprite[(int) EquipmentType.Body] is ChainFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeFemaleSprite());
+ }
+ else if (Sprite[(int)EquipmentType.Body] is NudeSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new ChainSprite());
+ }
+ else if (Sprite[(int)EquipmentType.Body] is NudeFemaleSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new ChainFemaleSprite());
+ }
+ }
+ }
+}
diff --git a/V3/Objects/MalePeasant.cs b/V3/Objects/MalePeasant.cs
new file mode 100644
index 0000000..1250412
--- /dev/null
+++ b/V3/Objects/MalePeasant.cs
@@ -0,0 +1,49 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Class for simple peasants which will be transformed into zombies.
+ /// </summary>
+ public sealed class MalePeasant : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Dorfbewohner";
+ public override int Life { get; protected set; } = 24;
+ public override int MaxLife { get; protected set; } = 24;
+ public override int Speed { get; } = 10;
+ public override int Attack { get; protected set; } = 5;
+ public override int AttackRadius { get; protected set; } = 0;
+ public override int SightRadius { get; protected set; } = 20;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.3);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new ClothSprite(), new HeadSprite(), null};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.MalePeasant;
+ public override Faction Faction { get; } = Faction.Plebs;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public MalePeasant(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ public void RemoveArmor()
+ {
+ if (Sprite[0] is NudeSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new ClothSprite());
+ }
+ else if (Sprite[0] is ClothSprite)
+ {
+ ChangeEquipment(EquipmentType.Body, new NudeSprite());
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Meatball.cs b/V3/Objects/Meatball.cs
new file mode 100644
index 0000000..144ef90
--- /dev/null
+++ b/V3/Objects/Meatball.cs
@@ -0,0 +1,36 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ public sealed class Meatball : AbstractCreature
+ {
+ public Meatball(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics) : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ protected override ISpriteCreature[] Sprite { get; } = { new MeatballSprite() };
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.Meatball;
+ public override string Name { get; protected set; } = "Fleischball";
+ public override int Life { get; protected set; } = 200;
+ public override int MaxLife { get; protected set; } = 200;
+ public override int Speed { get; } = 5;
+ public override int Attack { get; protected set; } = 70;
+ public override int AttackRadius { get; protected set; } = 60;
+ public override int SightRadius { get; protected set; } = 100;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(1);
+ public override TimeSpan Recovery { get; set; }
+ public override Faction Faction { get; } = Faction.Undead;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ protected override Point CreatureSize { get; } = new Point(36);
+ protected override Point BoundaryShift { get; } = new Point(-18, -24);
+ }
+}
diff --git a/V3/Objects/Movement.cs b/V3/Objects/Movement.cs
new file mode 100644
index 0000000..3dfa01b
--- /dev/null
+++ b/V3/Objects/Movement.cs
@@ -0,0 +1,20 @@
+// ReSharper disable InconsistentNaming
+namespace V3.Objects
+{
+ /// <summary>
+ /// Cardinal directions to show where the specific creature is facing.
+ /// Because of the isometric viewpoint N(orth) is the upper right.
+ /// </summary>
+ public enum MovementDirection
+ {
+ W, NW, N, NO, O, SO, S, SW
+ }
+
+ /// <summary>
+ /// The basic movement states a creature must hold.
+ /// </summary>
+ public enum MovementState
+ {
+ Idle, Moving, Attacking, Dying, Special
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Movement/CountStepsMovement.cs b/V3/Objects/Movement/CountStepsMovement.cs
new file mode 100644
index 0000000..e8c24c4
--- /dev/null
+++ b/V3/Objects/Movement/CountStepsMovement.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Objects.Movement
+{
+ public class CountStepsMovement : PlayerMovement
+ {
+ public float WalkedPixels { get; private set; }
+
+ public override Vector2 GiveNewPosition(Vector2 currentPosition, int speed)
+ {
+ var movedDistance = base.GiveNewPosition(currentPosition, speed);
+ WalkedPixels += Vector2.Distance(currentPosition, currentPosition + movedDistance);
+ return movedDistance;
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Movement/IMovable.cs b/V3/Objects/Movement/IMovable.cs
new file mode 100644
index 0000000..659708b
--- /dev/null
+++ b/V3/Objects/Movement/IMovable.cs
@@ -0,0 +1,43 @@
+using Microsoft.Xna.Framework;
+using V3.Data;
+using V3.Map;
+
+namespace V3.Objects.Movement
+{
+ public interface IMovable
+ {
+ /// <summary>
+ /// Calculates a path without collisions to desired destination.
+ /// </summary>
+ /// <param name="pathfinder">Pathfinder to use.</param>
+ /// <param name="position">Current position in pixel.</param>
+ /// <param name="destination">Destination in pixel.</param>
+ void FindPath(Pathfinder pathfinder, Vector2 position, Vector2 destination);
+
+ /// <summary>
+ /// Uses pathfinder to for steady movement to new transition.
+ /// </summary>
+ /// <param name="currentPosition">Current position in pixel.</param>
+ /// <param name="speed">Movement speed of the creature.</param>
+ /// <returns>Normalized vector * speed which represents a small step in the direction of desired destination.(</returns>
+ Vector2 GiveNewPosition(Vector2 currentPosition, int speed);
+
+ /// <summary>
+ /// Calculates the direction the creature is looking when moving.
+ /// </summary>
+ MovementDirection GiveMovementDirection();
+ bool IsMoving { get; }
+
+ /// <summary>
+ /// Save the movement data to a MovementData object.
+ /// </summary>
+ /// <returns>the MovementData object with the current status</returns>
+ MovementData SaveData();
+
+ /// <summary>
+ /// Restore the movement state from the given data.
+ /// </summary>
+ /// <param name="movementData">the state of the movement to restore</param>
+ void LoadData(MovementData movementData);
+ }
+}
diff --git a/V3/Objects/Movement/PlayerMovement.cs b/V3/Objects/Movement/PlayerMovement.cs
new file mode 100644
index 0000000..326503f
--- /dev/null
+++ b/V3/Objects/Movement/PlayerMovement.cs
@@ -0,0 +1,155 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using V3.Data;
+using V3.Map;
+
+namespace V3.Objects.Movement
+{
+ // TODO: Rename the class. Current name is unfitting. (But what is fitting?)
+ /// <summary>
+ /// Movement scheme for moving an object with the pathfinder.
+ /// </summary>
+ public class PlayerMovement : IMovable
+ {
+ private const int CellHeight = Constants.CellHeight;
+ private const int CellWidth = Constants.CellWidth;
+ private const float SpeedModifier = 0.25f;
+
+ private List<Vector2> mPath;
+ private int mStep;
+ private Vector2 mLastMovement;
+
+ public bool IsMoving { get; private set; }
+
+ /// <summary>
+ /// Calculates a path without collisions to desired destination.
+ /// </summary>
+ /// <param name="pathfinder">Pathfinder to use.</param>
+ /// <param name="position">Current position in pixel.</param>
+ /// <param name="destination">Destination in pixel.</param>
+ public void FindPath(Pathfinder pathfinder, Vector2 position, Vector2 destination)
+ {
+ mStep = 0;
+ mPath = pathfinder.FindPath(new Vector2((int)(position.X / CellWidth), (int)(position.Y / CellHeight)),
+ new Vector2((int)(destination.X / CellWidth), (int)(destination.Y / CellHeight)));
+ IsMoving = mPath.Count > 0;
+ }
+
+ /// <summary>
+ /// Uses pathfinder to for steady movement to new transition.
+ /// </summary>
+ /// <param name="currentPosition">Current position in pixel.</param>
+ /// <param name="speed">Movement speed of the creature.</param>
+ /// <returns>Normalized vector * speed which represents a small step in the direction of desired destination.(</returns>
+ public virtual Vector2 GiveNewPosition(Vector2 currentPosition, int speed)
+ {
+ Vector2 nextPosition = mPath[mStep];
+ Vector2 newPosition = nextPosition - currentPosition;
+ newPosition.Normalize();
+ float distanceToDestination = Vector2.Distance(nextPosition, currentPosition);
+ if (distanceToDestination < SpeedModifier * speed)
+ {
+ if (mStep == mPath.Count - 1)
+ {
+ IsMoving = false;
+ }
+ else
+ {
+ mStep++;
+ }
+ }
+ mLastMovement = newPosition;
+ return newPosition * SpeedModifier * speed;
+ }
+
+ /// <summary>
+ /// Calculates the direction the creature is looking when moving.
+ /// </summary>
+ public MovementDirection GiveMovementDirection()
+ {
+ // |\
+ // |α\ α == 22.5°
+ // b| \ 1 β == 67.5°
+ // | β\
+ // ──────
+ // a
+ const float b = 0.92f; // b == sin β
+ const float a = 0.38f; // a == sin α
+ Vector2 direction = mLastMovement;
+ MovementDirection movementDirection;
+ if (direction.X < -b)
+ {
+ movementDirection = MovementDirection.W;
+ }
+ else if (direction.X > b)
+ {
+ movementDirection = MovementDirection.O;
+ }
+ else if (direction.Y > 0)
+ {
+ if (direction.X < -a)
+ {
+ movementDirection = MovementDirection.SW;
+ }
+ else if (direction.X > a)
+ {
+ movementDirection = MovementDirection.SO;
+ }
+ else
+ {
+ movementDirection = MovementDirection.S;
+ }
+ }
+ else
+ {
+ if (direction.X < -a)
+ {
+ movementDirection = MovementDirection.NW;
+ }
+ else if (direction.X > a)
+ {
+ movementDirection = MovementDirection.NO;
+ }
+ else
+ {
+ movementDirection = MovementDirection.N;
+ }
+ }
+ return movementDirection;
+ }
+ /// <summary>
+ /// Save the movement data to a MovementData object.
+ /// </summary>
+ /// <returns>the MovementData object with the current status</returns>
+ public MovementData SaveData()
+ {
+ var data = new MovementData();
+ data.IsMoving = IsMoving;
+ if (IsMoving)
+ {
+ data.Path = mPath;
+ data.Step = mStep;
+ data.LastMovement = mLastMovement;
+ }
+ return data;
+ }
+
+ /// <summary>
+ /// Restore the movement state from the given data.
+ /// </summary>
+ /// <param name="movementData">the state of the movement to restore</param>
+ public void LoadData(MovementData movementData)
+ {
+ if (movementData == null)
+ return;
+
+ IsMoving = movementData.IsMoving;
+ if (IsMoving)
+ {
+ mPath = movementData.Path;
+ mStep = movementData.Step;
+ mLastMovement = movementData.LastMovement;
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Necromancer.cs b/V3/Objects/Necromancer.cs
new file mode 100644
index 0000000..b13650d
--- /dev/null
+++ b/V3/Objects/Necromancer.cs
@@ -0,0 +1,61 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A class for the player character: The Necromancer.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Necromancer : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Vagram der Grausame";
+ public override int Life { get; protected set; } = 500;
+ public override int MaxLife { get; protected set; } = 500;
+ public override int Speed { get; } = 12;
+ public override int Attack { get; protected set; } = 0;
+ public override int AttackRadius { get; protected set; } = 500;
+ public override int SightRadius { get; protected set; } = 0;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(1);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new NecromancerSprite(), new StaffSprite()};
+ protected override IMovable MovementScheme { get; } = new CountStepsMovement();
+ protected override CreatureType Type { get; } = CreatureType.Necromancer;
+ public override Faction Faction { get; } = Faction.Undead;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+ public float WalkedPixels => ((CountStepsMovement) MovementScheme).WalkedPixels;
+
+ public Necromancer(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ public void ChangeSex()
+ {
+ ISpriteCreature sprite0;
+ ISpriteCreature sprite1;
+ if (Sprite[0] is NecromancerSprite)
+ {
+ sprite0 = new NecromancerFemaleSprite();
+ sprite1 = new StaffFemaleSprite();
+ }
+ else
+ {
+ sprite0 = new NecromancerSprite();
+ sprite1 = new StaffSprite();
+ }
+
+ ChangeEquipment(EquipmentType.Body, sprite0);
+ if (Sprite.Length >= 2 && Sprite[(int) EquipmentType.Head] != null)
+ {
+ ChangeEquipment(EquipmentType.Head, sprite1);
+ }
+ }
+ }
+}
diff --git a/V3/Objects/ObjectsManager.cs b/V3/Objects/ObjectsManager.cs
new file mode 100644
index 0000000..e3ef568
--- /dev/null
+++ b/V3/Objects/ObjectsManager.cs
@@ -0,0 +1,314 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Camera;
+using V3.Effects;
+using V3.Map;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Objects manager for all game objects, be is creatures or buildings or even simple landscape objects.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class ObjectsManager : IObjectsManager
+ {
+ private List<List<ICreature>> mCreaturesByFactions;
+ private List<ICreature> mCreatureList;
+ private List<ICreature> mAddToSelectables;
+ private Quadtree mTextureQuadtree;
+ private Quadtree mInteractableQuadtree;
+ private readonly ContentManager mContentManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly IEffectsManager mEffectsManager;
+ private Rectangle mMapRectangle;
+ private List<Area> mAreas;
+ private readonly UpdatesPerSecond mFewerUpdates = new UpdatesPerSecond(15);
+ private readonly UpdatesPerSecond mEffectsCounter = new UpdatesPerSecond(0.2);
+
+ public List<ICreature> AddToSelectables => mAddToSelectables;
+ public List<ICreature> CreatureList => mCreatureList;
+ public List<ICreature> UndeadCreatures => mCreaturesByFactions[(int) Faction.Undead];
+ //public List<ICreature> KingdromCreatures => mCreaturesByFactions[(int) Faction.Kingdom];
+ //public List<ICreature> PlebCreatures => mCreaturesByFactions[(int) Faction.Plebs];
+ public ICreature PlayerCharacter { get; private set; }
+ public ICreature Boss { get; private set; }
+ public ICreature Prince { get; private set; }
+ public Castle Castle { get; private set; }
+
+ public ObjectsManager(ContentManager contentManager, CreatureFactory creatureFactory,
+ IEffectsManager effectsManager)
+ {
+ mContentManager = contentManager;
+ mCreatureFactory = creatureFactory;
+ mEffectsManager = effectsManager;
+ }
+
+ public void Initialize(IMapManager mapManager)
+ {
+ Clear();
+ mAddToSelectables = new List<ICreature>();
+ mCreatureList = new List<ICreature>();
+ mCreaturesByFactions = new List<List<ICreature>>();
+ // Make three lists because we have three factions.
+ for (int i = 0; i < 3; i++)
+ {
+ mCreaturesByFactions.Add(new List<ICreature>());
+ }
+ // Gets the initial values for the Quad Tree from the current map.
+ mInteractableQuadtree = new Quadtree(mapManager.SizeInPixel + new Point(128, 128));
+ mTextureQuadtree = new Quadtree(mapManager.SizeInPixel + new Point(128, 128));
+ mInteractableQuadtree.LoadContent(mContentManager);
+ mTextureQuadtree.LoadContent(mContentManager);
+
+ // Import map objects for drawing.
+ ImportMapObjects(mapManager.GetObjects());
+
+ // Save Map size in ObjectsManager.
+ mMapRectangle = new Rectangle(new Point(0), mapManager.SizeInPixel);
+
+ mAreas = mapManager.Areas;
+ }
+
+ public void Clear()
+ {
+ CreatureList?.Clear();
+ mCreaturesByFactions?.Clear();
+ for (int i = 0; i < 3; i++)
+ {
+ mCreaturesByFactions?.Add(new List<ICreature>());
+ }
+ PlayerCharacter = null;
+ Boss = null;
+ Prince = null;
+ Castle = null;
+ mInteractableQuadtree?.Clear();
+ mTextureQuadtree?.Clear();
+ }
+
+ public void CreatePlayerCharacter(Necromancer necromancer)
+ {
+ PlayerCharacter = necromancer;
+ AddCreature(necromancer);
+ }
+
+ public void CreateBoss(ICreature boss)
+ {
+ Boss = boss;
+ AddCreature(boss);
+ }
+
+ public void CreatePrince(ICreature prince)
+ {
+ Prince = prince;
+ AddCreature(prince);
+ }
+
+ public void CreateCreature(ICreature creature)
+ {
+ AddCreature(creature);
+ }
+
+ public void RemoveCreature(ICreature creature)
+ {
+ mCreatureList.Remove(creature);
+ mCreaturesByFactions[(int) creature.Faction].Remove(creature);
+ mInteractableQuadtree.RemoveItem(creature);
+ }
+
+ public void Draw(SpriteBatch spriteBatch, ICamera camera)
+ {
+ var objectsToDraw = mInteractableQuadtree.GetObjectsInRectangle(camera.ScreenRectangle);
+ var texturesToDraw = mTextureQuadtree.GetObjectsInRectangle(camera.ScreenRectangle);
+ IEnumerable<IGameObject> ordered = from obj in objectsToDraw.Concat(texturesToDraw) orderby obj.Position.Y select obj;
+ foreach (var obj in ordered)
+ {
+ obj.Draw(spriteBatch);
+ }
+ }
+
+ public void DrawQuadtree(SpriteBatch spriteBatch)
+ {
+ mInteractableQuadtree.Draw(spriteBatch);
+ mTextureQuadtree.Draw(spriteBatch);
+ }
+
+ public void Update(GameTime gameTime, bool rightButtonPressed, Vector2 rightButtonPosition, ICamera camera)
+ {
+ foreach (var creature in mCreatureList)
+ {
+ if (creature.GetSelf() == null) continue;
+ creature.Update(gameTime, PlayerCharacter, rightButtonPressed, rightButtonPosition, mInteractableQuadtree, camera);
+
+
+ // Create a boss if castle is down
+ if (creature.IsAttackingBuilding != null && creature.IsAttackingBuilding.Name == "Burg")
+ {
+ if (creature.IsAttackingBuilding.Robustness <= 50 && Boss == null)
+ {
+ var king = mCreatureFactory.CreateKing(new Vector2(creature.IsAttackingBuilding.Position.X - 300, creature.IsAttackingBuilding.Position.Y + 130), MovementDirection.S); //
+ var knight1 = mCreatureFactory.CreateKnight(new Vector2(creature.IsAttackingBuilding.Position.X - 320, creature.IsAttackingBuilding.Position.Y + 110), MovementDirection.S); //
+ var knight2 = mCreatureFactory.CreateKnight(new Vector2(creature.IsAttackingBuilding.Position.X - 280, creature.IsAttackingBuilding.Position.Y + 150), MovementDirection.S); //
+ CreateBoss(king);
+ AddCreature(knight1);
+ AddCreature(knight2);
+ break;
+ }
+ //break;
+ }
+ // Checks if the creature moved out of the map and resets its position as appropriate.
+ if (!mMapRectangle.Contains(creature.Position))
+ {
+ creature.ResetPosition();
+ }
+ }
+ if (mFewerUpdates.IsItTime(gameTime))
+ {
+ mInteractableQuadtree.Update();
+ }
+
+ #region Boss Special Attack
+ // Play some cool effects when boss is spawned so he looks cooler.
+ // Added a small special attack which does at bit AoE damage.
+ if (mEffectsCounter.IsItTime(gameTime))
+ {
+ if (Boss != null && camera.ScreenRectangle.Contains(Boss.Position))
+ {
+ mEffectsManager.PlayOnce(new Quake(), Boss.Position.ToPoint(), new Point(256, 128));
+ var aoeRadius = new Rectangle(Boss.Position.ToPoint() - new Point(128), new Point(256));
+ var effectedCreatures = mInteractableQuadtree.GetObjectsInRectangle(aoeRadius);
+ foreach (var obj in effectedCreatures)
+ {
+ var creature = obj as ICreature;
+ if (creature != null && creature.Faction == Faction.Undead && creature.BoundaryRectangle.Intersects(aoeRadius))
+ {
+ creature.TakeDamage(10);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Necromancer stuff
+ if (PlayerCharacter.IsSelected && rightButtonPressed)
+ {
+ var objectsUnderMouse =
+ mInteractableQuadtree.GetObjectsInRectangle(new Rectangle(rightButtonPosition.ToPoint(), new Point(1, 1)));
+ foreach (var obj in objectsUnderMouse)
+ {
+ var creature = obj as ICreature;
+ if (creature == null || !creature.SelectionRectangle.Contains(rightButtonPosition) || !creature.IsDead || creature.Faction == Faction.Undead) continue;
+ if (Vector2.Distance(PlayerCharacter.Position, creature.Position) > PlayerCharacter.AttackRadius) continue;
+ RemoveCreature(creature);
+ if (creature is KingsGuard || creature is King || creature is Prince)
+ {
+ CreateCreature(mCreatureFactory.CreateSkeletonElite(creature.Position, creature.MovementDirection));
+ }
+ else
+ {
+ CreateCreature(mCreatureFactory.CreateZombie(creature.Position, creature.MovementDirection));
+ }
+ PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ mEffectsManager.PlayOnce(new BloodBang(), creature.Position.ToPoint(), new Point(128));
+ break;
+ }
+ }
+ #endregion Necromancer stuff
+
+ #region SkeletonRider
+
+ var recMouse = new Rectangle(rightButtonPosition.ToPoint(), new Point(1, 1));
+ foreach (var creature in UndeadCreatures)
+ {
+ Skeleton skeleton = creature as Skeleton;
+ if (skeleton != null)
+ {
+ if(skeleton.IsSelected && rightButtonPressed)
+ {
+ var atMousPosition = mInteractableQuadtree.GetObjectsInRectangle(recMouse);
+ foreach (var wannabehorse in atMousPosition)
+ {
+ var horse = wannabehorse as SkeletonHorse;
+ if (horse != null && Vector2.Distance(skeleton.Position, horse.Position) < 32)
+ {
+ horse.Mount(skeleton);
+ horse.IsSelected = true;
+ mAddToSelectables.Add(horse);
+ }
+ }
+ }
+ }
+ }
+ #endregion SkeletonRider
+ }
+
+ private void AddCreature(ICreature creature)
+ {
+ if (!mMapRectangle.Contains(creature.BoundaryRectangle)) return;
+ mCreatureList.Add(creature);
+ mCreaturesByFactions[(int) creature.Faction].Add(creature);
+ mInteractableQuadtree.Insert(creature);
+ }
+
+ public void ImportMapObjects(List<IGameObject> textureObjects)
+ {
+ foreach (var obj in textureObjects)
+ {
+ if (obj is IBuilding)
+ {
+ mInteractableQuadtree.Insert(obj);
+ if (obj is Castle)
+ Castle = (Castle) obj;
+ }
+ else
+ {
+ mTextureQuadtree.Insert(obj);
+ }
+ }
+ }
+
+ public List<IGameObject> GetObjectsInRectangle(Rectangle rectangle)
+ {
+ return mInteractableQuadtree.GetObjectsInRectangle(rectangle);
+ }
+
+ public List<ICreature> GetCreaturesInEllipse(Ellipse ellipse)
+ {
+ var setToReturn = new List<ICreature>();
+ foreach (var obj in mInteractableQuadtree.GetObjectsInRectangle(ellipse.BoundaryRectangle))
+ {
+ ICreature creature = obj as ICreature;
+ if (creature != null && ellipse.Contains(creature.Position))
+ {
+ setToReturn.Add(creature);
+ }
+ }
+ return setToReturn;
+ }
+
+ public void ExposeTheLiving()
+ {
+ foreach (var creature in mCreatureList)
+ {
+ if (creature.Faction == Faction.Plebs)
+ {
+ (creature as MalePeasant)?.RemoveArmor();
+ (creature as FemalePeasant)?.RemoveArmor();
+ }
+ else if (creature.Faction == Faction.Kingdom)
+ {
+ (creature as Knight)?.RemoveArmor();
+ (creature as KingsGuard)?.RemoveArmor();
+ }
+ }
+ }
+
+ public bool InGraveyardArea(ICreature creature)
+ {
+ return mAreas.Where(area => area.Type == AreaType.Graveyard).Select(area => area.Contains(creature)).Any(contains => contains);
+ }
+ }
+}
diff --git a/V3/Objects/Prince.cs b/V3/Objects/Prince.cs
new file mode 100644
index 0000000..b806f41
--- /dev/null
+++ b/V3/Objects/Prince.cs
@@ -0,0 +1,33 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Prince : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Prinz Erhard";
+ public override int Life { get; protected set; } = 6000;
+ public override int MaxLife { get; protected set; } = 6000;
+ public override int Speed { get; } = 10;
+ public override int Attack { get; protected set; } = 35;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 500;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.5);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = { new PrinceSprite() };
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.Prince;
+ public override Faction Faction { get; } = Faction.Kingdom;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public Prince(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics) : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+ }
+}
diff --git a/V3/Objects/Selection.cs b/V3/Objects/Selection.cs
new file mode 100644
index 0000000..4ec7ccc
--- /dev/null
+++ b/V3/Objects/Selection.cs
@@ -0,0 +1,483 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Audio;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Data;
+using V3.Effects;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Class for selecting creatures, holding the selection and drawing the selection rectangle on the screen.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Selection
+ {
+ private IObjectsManager mObjectsManager;
+ private IEffectsManager mEffectsManager;
+ private readonly Color mColor = Color.Black;
+ private Texture2D mTexture;
+ public List<ICreature> SelectedCreatures { get; private set; } = new List<ICreature>();
+ private List<ICreature> TransformList { get; } = new List<ICreature>();
+ private readonly CreatureFactory mCreatureFactory;
+ private int mDeadBodies;
+ private int mExplosionDeaths;
+ private Ellipse NecroArea { get; set; }
+ public Rectangle LastSelection { get; private set; }
+ private SoundEffect mSoundEffect;
+ private SoundEffectInstance mSoundEffectInstance;
+ private IOptionsManager mOptionsManager;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+
+ public Selection(ContentManager contentManager, CreatureFactory creatureFactory, IObjectsManager objectsManager,
+ IEffectsManager effectsManager, IOptionsManager optionsmanager, AchievementsAndStatistics achievementsAndStatistics)
+ {
+ mCreatureFactory = creatureFactory;
+ mObjectsManager = objectsManager;
+ mEffectsManager = effectsManager;
+ mOptionsManager = optionsmanager;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ LoadContent(contentManager);
+ }
+
+ private void LoadContent(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mSoundEffect = contentManager.Load<SoundEffect>("Sounds/zonk2");
+ mSoundEffectInstance = mSoundEffect.CreateInstance();
+ }
+
+ /// <summary>
+ /// Select all objects in the area of the rectangle between origin and destination.
+ /// </summary>
+ /// <param name="origin">Position in pixels where the mouse button was pressed down.</param>
+ /// <param name="destination">Position in pixels where the mouse button was released.</param>
+ public void Select(Point origin, Point destination)
+ {
+ // Clean last selection
+ SelectedCreatures.ForEach(e => e.IsSelected=false);
+ SelectedCreatures.Clear();
+
+ // New selection
+ int x, y, width, height;
+ if (origin.X > destination.X)
+ {
+ x = destination.X;
+ width = origin.X - destination.X;
+ }
+ else
+ {
+ x = origin.X;
+ width = destination.X - origin.X;
+ }
+ if (origin.Y > destination.Y)
+ {
+ y = destination.Y;
+ height = origin.Y - destination.Y;
+ }
+ else
+ {
+ y = origin.Y;
+ height = destination.Y - origin.Y;
+ }
+
+ // Creates area for necromancer
+ var necroPos = mObjectsManager.PlayerCharacter;
+ NecroArea = new Ellipse(new Vector2((int)necroPos.Position.X, (int)necroPos.Position.Y), 1280, 640);
+
+ // List for all objects in the current selection
+ LastSelection = new Rectangle(x, y, width, height);
+ var selectedObjects = mObjectsManager.GetObjectsInRectangle(LastSelection);
+
+ // Lists to seperate undead from enemy creatures
+ var enemyCreatures = new List<ICreature>();
+ var undeadCreatures = new List<ICreature>();
+
+ foreach (var obj in selectedObjects)
+ {
+ var creature = obj as ICreature;
+ // Give priority when selecting undead creatures.
+ if (creature != null && !creature.IsDead && LastSelection.Intersects(creature.SelectionRectangle) && NecroArea.Contains(creature.Position))
+ {
+ if (creature.Faction != Faction.Undead)
+ {
+ enemyCreatures.Add(creature);
+ }
+ else
+ {
+ undeadCreatures.Add(creature);
+ }
+ }
+ }
+ SelectedCreatures = undeadCreatures.Count == 0 ? enemyCreatures : undeadCreatures;
+
+ // Make all selectable creatures selected
+ foreach (var selectedCreature in SelectedCreatures)
+ {
+ selectedCreature.IsSelected = true;
+ }
+ }
+
+ /// <summary>
+ /// Draw the selection rectangle on the screen.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used.</param>
+ /// <param name="origin">Position in pixels where the mouse button was pressed down.</param>
+ /// <param name="destination">Position in pixels where the mouse button was released.</param>
+ public void Draw(SpriteBatch spriteBatch, Point origin, Point destination)
+ {
+ if (origin.X > destination.X)
+ {
+ spriteBatch.Draw(mTexture, new Rectangle(destination.X, origin.Y, origin.X - destination.X, 2), mColor);
+ spriteBatch.Draw(mTexture, new Rectangle(destination.X, destination.Y, origin.X - destination.X, 2), mColor);
+ }
+ else
+ {
+ spriteBatch.Draw(mTexture, new Rectangle(origin.X, origin.Y, destination.X - origin.X, 2), mColor);
+ spriteBatch.Draw(mTexture, new Rectangle(origin.X, destination.Y, destination.X - origin.X, 2), mColor);
+ }
+ if (origin.Y > destination.Y)
+ {
+ spriteBatch.Draw(mTexture, new Rectangle(origin.X, destination.Y, 2, origin.Y - destination.Y), mColor);
+ spriteBatch.Draw(mTexture, new Rectangle(destination.X, destination.Y, 2, origin.Y - destination.Y), mColor);
+ }
+ else
+ {
+ spriteBatch.Draw(mTexture, new Rectangle(origin.X, origin.Y, 2, destination.Y - origin.Y), mColor);
+ spriteBatch.Draw(mTexture, new Rectangle(destination.X, origin.Y, 2, destination.Y - origin.Y), mColor);
+ }
+ }
+
+ /// <summary>
+ /// Press 1 to trigger the specialattack of the meatball
+ /// All non undead creatures at the distance of 200 pixels take damage
+ /// After the attack the meatball disappears and three zombies will be created
+ /// </summary>
+ public void Specialattack()
+ {
+ foreach (var creature in SelectedCreatures)
+ {
+ var meatball = creature as Meatball;
+ if (meatball != null)
+ {
+ // Creates radius for meatballs explosion
+ var explosionRadius = new Rectangle((int)meatball.Position.X - 200, (int)meatball.Position.Y - 200, 400, 400);
+ var objectsInMeatballArea = mObjectsManager.GetObjectsInRectangle(explosionRadius);
+
+ // Plays sounds and effects
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ mEffectsManager.PlayOnce(new Explosion(), meatball.Position.ToPoint(), explosionRadius.Size * new Point(3, 2));
+
+ // All attackable creatures take damage (own undead creatures won't take damage)
+ foreach (var attackable in objectsInMeatballArea)
+ {
+ var toAttack = attackable as ICreature;
+ if (toAttack != null && toAttack.Faction != Faction.Undead)
+ {
+ toAttack.TakeDamage(meatball.Attack);
+ if (toAttack.IsDead)
+ mExplosionDeaths += 1;
+ }
+ }
+
+ if (mExplosionDeaths >= 10)
+ mAchievementsAndStatistics.mKaboom = true;
+ mExplosionDeaths = 0;
+
+ // The new zombies after explosion
+ var zombie1 = mCreatureFactory.CreateZombie(meatball.Position, meatball.MovementDirection);
+ var zombie2 = mCreatureFactory.CreateZombie(new Vector2(meatball.Position.X + 50, meatball.Position.Y + 50), meatball.MovementDirection);
+ var zombie3 = mCreatureFactory.CreateZombie(new Vector2(meatball.Position.X - 50, meatball.Position.Y - 50), meatball.MovementDirection);
+
+ // Makes zombies be selected after explosion
+ zombie1.IsSelected = true;
+ zombie2.IsSelected = true;
+ zombie3.IsSelected = true;
+ meatball.IsSelected = false;
+ SelectedCreatures.Add(zombie1);
+ SelectedCreatures.Add(zombie2);
+ SelectedCreatures.Add(zombie3);
+ SelectedCreatures.Remove(meatball);
+
+ // Remove meatball from game
+ mObjectsManager.RemoveCreature(meatball);
+
+ // Add zombies to game
+ mObjectsManager.CreateCreature(zombie1);
+ mObjectsManager.CreateCreature(zombie2);
+ mObjectsManager.CreateCreature(zombie3);
+
+ // For every zombie the necromancer gets healed
+ var heal = mObjectsManager.PlayerCharacter.Life * 0.12;
+ mObjectsManager.PlayerCharacter.Heal((int)heal);
+
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Press 2 to create zombies from dead bodies
+ /// At least one dead body should be in necromancers area
+ /// </summary>
+ public void TransformZombie()
+ {
+ // Creates area for necromancer
+ var necroPos = mObjectsManager.PlayerCharacter;
+ NecroArea = new Ellipse(new Vector2((int) necroPos.Position.X, (int) necroPos.Position.Y), 1280, 640);
+
+ // Get all creatures in necromancers area
+ var necroArea = mObjectsManager.GetCreaturesInEllipse(NecroArea);
+
+ // Every dead body in necromancer area will be transformed to an zombie
+ // The prince will be transformed to an elite zombie
+ foreach (var creature in necroArea)
+ {
+ if (creature.Faction == Faction.Plebs || creature.Faction == Faction.Kingdom)
+ {
+ if (creature.IsDead)
+ {
+ ICreature zombie;
+ if (creature is KingsGuard || creature is King || creature is Prince)
+ zombie = mCreatureFactory.CreateSkeletonElite(creature.Position, creature.MovementDirection);
+ else
+ zombie = mCreatureFactory.CreateZombie(creature.Position, creature.MovementDirection);
+
+ // Add zombie to game
+ mObjectsManager.CreateCreature(zombie);
+
+ // Remove dead body from game
+ mObjectsManager.RemoveCreature(creature);
+
+ // Play sounds and effects
+ mEffectsManager.PlayOnce(new BloodBang(), zombie.Position.ToPoint(), new Point(128));
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special,
+ TimeSpan.FromSeconds(0.5d));
+
+ // The number of dead bodies
+ mDeadBodies++;
+
+ // For every zombie the necromancer gets healed
+ var heal = mObjectsManager.PlayerCharacter.Life * 0.04;
+ mObjectsManager.PlayerCharacter.Heal((int)heal);
+ }
+ }
+ }
+
+ // Counts dead bodies in necromancers area and undead creatures
+ var set = mDeadBodies + mObjectsManager.UndeadCreatures.Count;
+ if (mObjectsManager.InGraveyardArea(mObjectsManager.PlayerCharacter) && set < 6)
+ {
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ for (int i = 1; i <= 6 - set; i++)
+ {
+ var positionX = mObjectsManager.PlayerCharacter.Position.X + (float)Math.Sin(i) * 75;
+ var positionY = mObjectsManager.PlayerCharacter.Position.Y + (float)Math.Cos(i) * 75;
+ var zombie = mCreatureFactory.CreateZombie(new Vector2(positionX, positionY), mObjectsManager.PlayerCharacter.MovementDirection);
+ mObjectsManager.CreateCreature(zombie);
+ zombie.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(1));
+ }
+ }
+ mDeadBodies = 0;
+ }
+
+ /// <summary>
+ /// Press 3 to create a meatball in exchange for five zombie
+ /// At least five zombies should be selected
+ /// </summary>
+ public void TransformMeatball()
+ {
+ // The cost for this transformation
+ var cost = mObjectsManager.PlayerCharacter.MaxLife * 0.12;
+
+ // Transform only when necromancer life minus the transformations cost is not lower than 1
+ if (mObjectsManager.PlayerCharacter.Life - (int)cost >= 1)
+ {
+ // Puts all zombies in a seperate list called TransformList
+ foreach (var creature in SelectedCreatures)
+ {
+ var zombie = creature as Zombie;
+ if (zombie != null && TransformList.Count < 5)
+ {
+ TransformList.Add(zombie);
+ }
+ }
+
+ // Creates the meatball and makes him selected
+ if (TransformList.Count >= 5)
+ {
+ // Delete zombies from game and SelectedCratures
+ foreach (var zombie in TransformList)
+ {
+ SelectedCreatures.Remove(zombie);
+ mObjectsManager.RemoveCreature(zombie);
+ }
+
+ // Add meatball to game
+ // Position will be randomly chosen from one of the zombies
+ var randomNumber = new Random();
+ var positionMeatball = randomNumber.Next(5);
+ var meatball = mCreatureFactory.CreateMeatball(TransformList[positionMeatball].Position,
+ TransformList[positionMeatball].MovementDirection);
+ meatball.IsSelected = true;
+ SelectedCreatures.Add(meatball);
+ mObjectsManager.CreateCreature(meatball);
+
+ // Plays sounds and effects
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ mEffectsManager.PlayOnce(new BloodExplosion(), meatball.Position.ToPoint(), new Point(256));
+
+ // Necromancer takes damage
+ mObjectsManager.PlayerCharacter.TakeDamage((int)cost);
+ }
+ TransformList.Clear();
+ }
+ else
+ {
+ // Play sound when transformation not possible
+ mSoundEffectInstance.Volume = mOptionsManager.Options.GetEffectiveVolume();
+ mSoundEffectInstance.Play();
+ }
+ }
+
+
+ /// <summary>
+ /// Press 4 to create an skeleton in exchange for an zombie
+ /// At least one zombie should be selected
+ /// </summary>
+ public void TransformSkeleton()
+ {
+ // Iterates through SelectedCreatures and takes the first zombie
+ foreach (var creature in SelectedCreatures)
+ {
+ var zombie = creature as Zombie;
+ if (zombie != null)
+ {
+ // Delete zombie
+ SelectedCreatures.Remove(zombie);
+ mObjectsManager.RemoveCreature(zombie);
+
+ // Add skeleton
+ var skeleton = mCreatureFactory.CreateSkeleton(zombie.Position, zombie.MovementDirection);
+ skeleton.IsSelected = true;
+ SelectedCreatures.Add(skeleton);
+ mObjectsManager.CreateCreature(skeleton);
+
+ // Plays sounds and effects
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ mEffectsManager.PlayOnce(new BloodFountain(), zombie.Position.ToPoint() - new Point(0, 64), new Point(128));
+
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Press 5 to create an skeletonhorse in exchange for three skeletons
+ /// At least three skeletons should be selected
+ /// </summary>
+ public void TransformSkeletonHorse()
+ {
+ // The cost for this transformation
+ var cost = mObjectsManager.PlayerCharacter.MaxLife * 0.06;
+
+ // Transform only when necromancer life minus the transformations cost is not lower than 1
+ if (mObjectsManager.PlayerCharacter.Life - (int)cost >= 1)
+ {
+
+ // Puts all skeletons in a seperate list called TransformList
+ foreach (var creature in SelectedCreatures)
+ {
+ var skeleton = creature as Skeleton;
+ if (skeleton != null && TransformList.Count < 3)
+ {
+ TransformList.Add(skeleton);
+ }
+ }
+
+ // Creates the skeletonhorse and makes him selected
+ if (TransformList.Count >= 3)
+ {
+ // Delete zombies from game and SelectedCratures
+ foreach (var skeleton in TransformList)
+ {
+ SelectedCreatures.Remove(skeleton);
+ mObjectsManager.RemoveCreature(skeleton);
+ }
+
+ // Add skeletonhorse to game
+ // Position will be randomly chosen from one of the skeletons
+ var randomNumber = new Random();
+ var positionHorse = randomNumber.Next(3);
+ var skeletonHorse = mCreatureFactory.CreateSkeletonHorse(TransformList[positionHorse].Position,
+ TransformList[positionHorse].MovementDirection);
+ skeletonHorse.IsSelected = true;
+ SelectedCreatures.Add(skeletonHorse);
+ mObjectsManager.CreateCreature(skeletonHorse);
+
+ // Plays sounds and effects
+ mObjectsManager.PlayerCharacter.PlayAnimationOnce(MovementState.Special, TimeSpan.FromSeconds(0.5d));
+ mEffectsManager.PlayOnce(new HorseEffect(), skeletonHorse.Position.ToPoint() - new Point(0, 16), new Point(128));
+
+
+ // Necromancer takes damage
+ mObjectsManager.PlayerCharacter.TakeDamage((int)cost);
+ }
+ TransformList.Clear();
+ }
+ else
+ {
+ // Play sound when transformation not possible
+ mSoundEffectInstance.Volume = mOptionsManager.Options.GetEffectiveVolume();
+ mSoundEffectInstance.Play();
+ }
+ }
+
+ /// <summary>
+ /// If creature leaves area og the necromancer, he gets disselected
+ /// </summary>
+ public void UpdateSelection()
+ {
+ // Creates area for necromancer
+ var necroPos = mObjectsManager.PlayerCharacter;
+ NecroArea = new Ellipse(new Vector2((int)necroPos.Position.X, (int)necroPos.Position.Y), 1280, 640);
+
+ foreach (var creature in SelectedCreatures)
+ {
+ if (!NecroArea.Contains(creature.Position) || creature.IsDead)
+ creature.IsSelected = false;
+ }
+
+ if (mObjectsManager.AddToSelectables.Count > 0)
+ {
+ SelectedCreatures.Add(mObjectsManager.AddToSelectables[0]);
+ mObjectsManager.AddToSelectables.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Give movement command to selected creatures.
+ /// </summary>
+ /// <param name="destination">Desired destination in pixels.</param>
+ public void Move(Vector2 destination)
+ {
+ if (SelectedCreatures.Count == 0) return;
+ var centreCreature = SelectedCreatures[0];
+ // You can not move enemy creatures.
+ if (centreCreature.Faction != Faction.Undead) return;
+ centreCreature.Move(destination);
+
+ for (int i = 1; i < SelectedCreatures.Count; i++)
+ {
+ Vector2 flockDestination = destination;
+ Vector2 distance = centreCreature.Position - SelectedCreatures[i].Position;
+ distance.Normalize();
+ distance *= 70;
+ flockDestination -= distance;
+ SelectedCreatures[i].Move(flockDestination);
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Skeleton.cs b/V3/Objects/Skeleton.cs
new file mode 100644
index 0000000..58eda62
--- /dev/null
+++ b/V3/Objects/Skeleton.cs
@@ -0,0 +1,65 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A skeleton controlled by the necromancer.
+ /// </summary>
+ public class Skeleton : AbstractCreature
+ {
+ /// <summary>
+ /// If the skeleton is mounted, it is not updated and drawn anymore because
+ /// instead the horse sprite is moved.
+ /// </summary>
+ public bool Mounted { private get; set; }
+
+ public override string Name { get; protected set; } = "Skelett";
+ public override int Life { get; protected set; } = 100;
+ public override int MaxLife { get; protected set; } = 100;
+ public override int Speed { get; } = 10;
+ public override int Attack { get; protected set; } = 10;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 150;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.5);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new SkeletonSprite()};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.Skeleton;
+ public override Faction Faction { get; } = Faction.Undead;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ // ReSharper disable once MemberCanBeProtected.Global
+ public Skeleton(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager,achievementsAndStatistics)
+ {
+ }
+
+ public override IGameObject GetSelf()
+ {
+ return Mounted ? null : base.GetSelf();
+ }
+
+ public override CreatureData SaveData()
+ {
+ var data = base.SaveData();
+ data.Mounted = Mounted;
+ return data;
+ }
+
+ public override void LoadData(CreatureData data)
+ {
+ base.LoadData(data);
+ Mounted = data.Mounted;
+
+ if (IsUpgraded)
+ ChangeEquipment(EquipmentType.Body, new SkeletonArcherSprite());
+ }
+ }
+}
diff --git a/V3/Objects/SkeletonElite.cs b/V3/Objects/SkeletonElite.cs
new file mode 100644
index 0000000..ac3c6c4
--- /dev/null
+++ b/V3/Objects/SkeletonElite.cs
@@ -0,0 +1,19 @@
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ public sealed class SkeletonElite : Skeleton
+ {
+ public override int Life { get; protected set; } = 200;
+ public override int MaxLife { get; protected set; } = 200;
+ public override int Attack { get; protected set; } = 30;
+ protected override ISpriteCreature[] Sprite { get; } = { new SkeletonEliteSprite() };
+
+ public SkeletonElite(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics) : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/SkeletonHorse.cs b/V3/Objects/SkeletonHorse.cs
new file mode 100644
index 0000000..6cd441d
--- /dev/null
+++ b/V3/Objects/SkeletonHorse.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ public sealed class SkeletonHorse : AbstractCreature
+ {
+ public SkeletonHorse(ContentManager contentManager, Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics) : base(contentManager, pathfinder, optionsManager, achievementsAndStatistics)
+ {
+ }
+
+ private Skeleton mSkeleton;
+
+ protected override ISpriteCreature[] Sprite { get; } = {new SkeletonHorseSprite()};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.SkeletonHorse;
+ public override string Name { get; protected set; } = "Totenpferd";
+ public override int Life { get; protected set; } = 150;
+ public override int MaxLife { get; protected set; } = 150;
+ public override int Speed { get; } = 20;
+ public override int Attack { get; protected set; }
+ public override int AttackRadius { get; protected set; }
+ public override int SightRadius { get; protected set; }
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(0.5);
+ public override TimeSpan Recovery { get; set; }
+ public override Faction Faction { get; } = Faction.Undead;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ protected override Point CreatureSize { get; } = new Point(36);
+ protected override Point BoundaryShift { get; } = new Point(-18, -24);
+
+ /// <summary>
+ /// A skeleton mounts a horse, becoming a skeleton rider.
+ /// </summary>
+ /// <param name="skeleton">The skeleton which mounts the horse.</param>
+ public void Mount(Skeleton skeleton)
+ {
+ if (IsDead || skeleton.IsDead || mSkeleton != null) return;
+ skeleton.Mounted = true;
+ mSkeleton = skeleton;
+ ChangeEquipment(EquipmentType.Body, new SkeletonRiderSprite());
+ Attack = skeleton.Attack;
+ AttackRadius = skeleton.AttackRadius * 2;
+ // Sight radius is higher because skeleton is mounted.
+ SightRadius = skeleton.SightRadius * 2;
+ Name = "Skelettreiter";
+ }
+
+ protected override void Die()
+ {
+ if (mSkeleton != null)
+ {
+ mSkeleton.Position = Position;
+ mSkeleton.Mounted = false;
+ mSkeleton = null;
+ ChangeEquipment(EquipmentType.Body, new SkeletonHorseSprite());
+ }
+ base.Die();
+ }
+
+ public override CreatureData SaveData()
+ {
+ var data = base.SaveData();
+ if (mSkeleton != null)
+ data.SkeletonId = mSkeleton.Id;
+ return data;
+ }
+
+ public override void LoadReferences(CreatureData data, Dictionary<int, ICreature> creatures)
+ {
+ base.LoadReferences(data, creatures);
+ if (creatures.ContainsKey(data.SkeletonId))
+ {
+ var creature = creatures[data.SkeletonId];
+ if (creature is Skeleton)
+ Mount((Skeleton) creature);
+ }
+ }
+ }
+}
diff --git a/V3/Objects/Sprite/AbstractSpriteCreature.cs b/V3/Objects/Sprite/AbstractSpriteCreature.cs
new file mode 100644
index 0000000..bde139e
--- /dev/null
+++ b/V3/Objects/Sprite/AbstractSpriteCreature.cs
@@ -0,0 +1,201 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Objects.Sprite
+{
+ public abstract class AbstractSpriteCreature : ISpriteCreature
+ {
+ // How many frames are there for each animation type?
+ protected virtual int IdleFrames { get; } = 4;
+ protected virtual int MovingFrames { get; } = 8;
+ protected virtual int AttackingFrames { get; } = 4;
+ protected virtual int DyingFrames { get; } = 6;
+ protected virtual int SpecialFrames { get; } = 4;
+
+ /// The row of the specific textures in the texture file.
+ protected virtual int IdleTextureIndex { get; } = 0;
+ protected virtual int MovingTextureIndex { get; } = 4;
+ protected virtual int AttackingTextureIndex { get; } = 12;
+ protected virtual int DyingTextureIndex { get; } = 18;
+ protected virtual int SpecialTextureIndex { get; } = 24;
+
+
+ // Specify the size of a single animation frame in pixel.
+ private const int SpriteWidth = 128;
+ private const int SpriteHeight = 128;
+ private readonly Point mSpriteShift = new Point(-SpriteWidth / 2, -SpriteHeight * 3 / 4);
+ private readonly Point mSpriteSize = new Point(SpriteWidth, SpriteHeight);
+
+ private double mTimeSinceUpdate;
+ private Texture2D mTexture;
+ // How much time until animation frame is changed (in milliseconds).
+ private const int UpdatesPerMSec = 125; // 8 FPS
+ private MovementState mLastMovementState = MovementState.Idle;
+ private MovementState mCurrentMovementState = MovementState.Idle;
+ private int mMaxAnimationSteps;
+ private int mMovementStateRow;
+ private bool mIdleBackwardsLoop;
+ private int mAnimationState;
+
+ // Fields for PlayOnce method.
+ private bool mPriorityAnimation;
+ private UpdatesPerSecond mUpS;
+
+ protected abstract string TextureFile { get; }
+
+ /// <summary>
+ /// Loads the texture file and prepares animations.
+ /// </summary>
+ /// <param name="contentManager"></param>
+ public void Load(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Sprites/" + TextureFile);
+ mMaxAnimationSteps = IdleFrames;
+ }
+
+ /// <summary>
+ /// Draws the sprite on the screen.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ /// <param name="position">Position on the screen where sprite is drawn to.</param>
+ /// <param name="movementState">What moveset will be used? (Moving, Attacking...)</param>
+ /// <param name="movementDirection">Where does the sprite face?</param>
+ public void Draw(SpriteBatch spriteBatch, Vector2 position, MovementState movementState, MovementDirection movementDirection)
+ {
+ mCurrentMovementState = movementState;
+ Point sourceLocation = new Point((mMovementStateRow + mAnimationState) * SpriteWidth,
+ (int) movementDirection * SpriteHeight);
+ Rectangle sourceRectangle = new Rectangle(sourceLocation, mSpriteSize);
+ spriteBatch.Draw(mTexture, position + new Vector2(mSpriteShift.X, mSpriteShift.Y), sourceRectangle, Color.White);
+ }
+
+ public void DrawStatic(SpriteBatch spriteBatch, Point position, MovementState movementState, MovementDirection movementDirection)
+ {
+ Point sourceLocation = new Point((int)movementState * SpriteWidth, (int)movementDirection * SpriteHeight);
+ Rectangle destinationRectangle = new Rectangle(position + mSpriteShift, mSpriteSize);
+ Rectangle sourceRectangle = new Rectangle(sourceLocation, mSpriteSize);
+ spriteBatch.Draw(mTexture, destinationRectangle, sourceRectangle, Color.White);
+ }
+
+ /// <summary>
+ /// Change the sprite to show an animation.
+ /// </summary>
+ /// <param name="gameTime">Elapsed game time is used for calculating FPS.</param>
+ public void PlayAnimation(GameTime gameTime)
+ {
+ // Playing a single animation cycle just once with higher priority.
+ if (mPriorityAnimation)
+ {
+ if (mAnimationState < mMaxAnimationSteps - 1)
+ {
+ if (mUpS.IsItTime(gameTime))
+ {
+ mAnimationState++;
+ }
+ return;
+ }
+ mAnimationState = 0;
+ mCurrentMovementState = mLastMovementState;
+ SelectFrames(mCurrentMovementState);
+ mPriorityAnimation = false;
+ return;
+ }
+
+ // Change texture for showing animations according to elapsed game time.
+ mTimeSinceUpdate += gameTime.ElapsedGameTime.TotalMilliseconds;
+ if (mTimeSinceUpdate < UpdatesPerMSec)
+ {
+ return;
+ }
+ mTimeSinceUpdate = 0;
+
+ // Check which specific animation sprites need to be used.
+ // Only check if movement state is changed.
+ if (mCurrentMovementState != mLastMovementState)
+ {
+ SelectFrames(mCurrentMovementState);
+ mAnimationState = 0;
+ mLastMovementState = mCurrentMovementState;
+ }
+
+ // Idle animation is looped back and forth.
+ if (mIdleBackwardsLoop)
+ {
+ mAnimationState--;
+ if (mAnimationState <= 0)
+ {
+ mIdleBackwardsLoop = false;
+ }
+ return;
+ }
+
+ if (mAnimationState < mMaxAnimationSteps - 1)
+ {
+ mAnimationState++;
+ }
+ else
+ {
+ if (mLastMovementState == MovementState.Idle)
+ {
+ mIdleBackwardsLoop = true;
+ mAnimationState--;
+ }
+ else if (mLastMovementState == MovementState.Dying)
+ {
+ }
+ else
+ {
+ mAnimationState = 0;
+ }
+ }
+ }
+
+ private void SelectFrames(MovementState currentMovementState)
+ {
+ switch (currentMovementState)
+ {
+ case MovementState.Idle:
+ mMaxAnimationSteps = IdleFrames;
+ mMovementStateRow = IdleTextureIndex;
+ mIdleBackwardsLoop = false;
+ break;
+ case MovementState.Moving:
+ mMaxAnimationSteps = MovingFrames;
+ mMovementStateRow = MovingTextureIndex;
+ break;
+ case MovementState.Attacking:
+ mMaxAnimationSteps = AttackingFrames;
+ mMovementStateRow = AttackingTextureIndex;
+ break;
+ case MovementState.Dying:
+ mMaxAnimationSteps = DyingFrames;
+ mMovementStateRow = DyingTextureIndex;
+ break;
+ case MovementState.Special:
+ mMaxAnimationSteps = SpecialFrames;
+ mMovementStateRow = SpecialTextureIndex;
+ break;
+ default:
+ mMaxAnimationSteps = 1; // No Animation if default case is reached.
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Plays the specified animation fully, but only once.
+ /// </summary>
+ /// <param name="animation">For which movement state the animation should be played.</param>
+ /// <param name="duration">How long (or how slow) should the animation be?</param>
+ public void PlayOnce(MovementState animation, TimeSpan duration)
+ {
+ mLastMovementState = mCurrentMovementState;
+ mCurrentMovementState = animation;
+ mPriorityAnimation = true;
+ SelectFrames(animation);
+ mAnimationState = 0;
+ mUpS = new UpdatesPerSecond(1d / (duration.TotalSeconds / mMaxAnimationSteps));
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ArrowSprite.cs b/V3/Objects/Sprite/ArrowSprite.cs
new file mode 100644
index 0000000..76e34b7
--- /dev/null
+++ b/V3/Objects/Sprite/ArrowSprite.cs
@@ -0,0 +1,42 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Objects.Sprite
+{
+ /// <summary>
+ /// A simple arrow from different directions without animations.
+ /// </summary>
+ public sealed class ArrowSprite : ISpriteCreature
+ {
+ private Texture2D mTexture;
+ private const int Size = 64;
+
+ public void Load(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Sprites/arrows");
+ }
+
+ public void Draw(SpriteBatch spriteBatch, Vector2 position, MovementState movementState, MovementDirection movementDirection)
+ {
+ spriteBatch.Draw(mTexture, position - new Vector2(Size / 2f), new Rectangle(0, Size * (int) movementDirection, Size, Size), Color.White);
+ }
+
+ public void DrawStatic(SpriteBatch spriteBatch,
+ Point position,
+ MovementState movementState,
+ MovementDirection movementDirection)
+ {
+ Draw(spriteBatch, position.ToVector2(), movementState, movementDirection);
+ }
+
+ public void PlayAnimation(GameTime gameTime)
+ {
+ }
+
+ public void PlayOnce(MovementState animation, TimeSpan duration)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/BucklerFemaleSprite.cs b/V3/Objects/Sprite/BucklerFemaleSprite.cs
new file mode 100644
index 0000000..7d72869
--- /dev/null
+++ b/V3/Objects/Sprite/BucklerFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class BucklerFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "buckler_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/BucklerSprite.cs b/V3/Objects/Sprite/BucklerSprite.cs
new file mode 100644
index 0000000..c55ed81
--- /dev/null
+++ b/V3/Objects/Sprite/BucklerSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class BucklerSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "buckler";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ChainFemaleSprite.cs b/V3/Objects/Sprite/ChainFemaleSprite.cs
new file mode 100644
index 0000000..eddfe9d
--- /dev/null
+++ b/V3/Objects/Sprite/ChainFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ChainFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "chain_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ChainSprite.cs b/V3/Objects/Sprite/ChainSprite.cs
new file mode 100644
index 0000000..33e7042
--- /dev/null
+++ b/V3/Objects/Sprite/ChainSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ChainSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "chain";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ClothFemaleSprite.cs b/V3/Objects/Sprite/ClothFemaleSprite.cs
new file mode 100644
index 0000000..3edaec1
--- /dev/null
+++ b/V3/Objects/Sprite/ClothFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ClothFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "cloth_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ClothSprite.cs b/V3/Objects/Sprite/ClothSprite.cs
new file mode 100644
index 0000000..b3aaff0
--- /dev/null
+++ b/V3/Objects/Sprite/ClothSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ClothSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "cloth";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/EquipmentType.cs b/V3/Objects/Sprite/EquipmentType.cs
new file mode 100644
index 0000000..611c35d
--- /dev/null
+++ b/V3/Objects/Sprite/EquipmentType.cs
@@ -0,0 +1,10 @@
+namespace V3.Objects.Sprite
+{
+ /// <summary>
+ /// Different types of equipment slots.
+ /// </summary>
+ public enum EquipmentType
+ {
+ Body, Head, Weapon, Offhand
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadBaldSprite.cs b/V3/Objects/Sprite/HeadBaldSprite.cs
new file mode 100644
index 0000000..7b01e16
--- /dev/null
+++ b/V3/Objects/Sprite/HeadBaldSprite.cs
@@ -0,0 +1,10 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace V3.Objects.Sprite
+{
+ [SuppressMessage("ReSharper", "UnusedMember.Global")]
+ public sealed class HeadBaldSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_bald";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadChainFemaleSprite.cs b/V3/Objects/Sprite/HeadChainFemaleSprite.cs
new file mode 100644
index 0000000..8d8ed90
--- /dev/null
+++ b/V3/Objects/Sprite/HeadChainFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class HeadChainFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_chain_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadChainSprite.cs b/V3/Objects/Sprite/HeadChainSprite.cs
new file mode 100644
index 0000000..31476f1
--- /dev/null
+++ b/V3/Objects/Sprite/HeadChainSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class HeadChainSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_chain";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadFemaleSprite.cs b/V3/Objects/Sprite/HeadFemaleSprite.cs
new file mode 100644
index 0000000..b8649aa
--- /dev/null
+++ b/V3/Objects/Sprite/HeadFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class HeadFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadPlateFemaleSprite.cs b/V3/Objects/Sprite/HeadPlateFemaleSprite.cs
new file mode 100644
index 0000000..8ad7917
--- /dev/null
+++ b/V3/Objects/Sprite/HeadPlateFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class HeadPlateFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_plate_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadPlateSprite.cs b/V3/Objects/Sprite/HeadPlateSprite.cs
new file mode 100644
index 0000000..fa237b3
--- /dev/null
+++ b/V3/Objects/Sprite/HeadPlateSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class HeadPlateSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head_plate";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/HeadSprite.cs b/V3/Objects/Sprite/HeadSprite.cs
new file mode 100644
index 0000000..303bb8a
--- /dev/null
+++ b/V3/Objects/Sprite/HeadSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class HeadSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "head";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ISpriteCreature.cs b/V3/Objects/Sprite/ISpriteCreature.cs
new file mode 100644
index 0000000..1339b2a
--- /dev/null
+++ b/V3/Objects/Sprite/ISpriteCreature.cs
@@ -0,0 +1,53 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Objects.Sprite
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface ISpriteCreature
+ {
+ /// <summary>
+ /// Loads the texture file and prepares animations.
+ /// </summary>
+ /// <param name="contentManager">Content manager used.</param>
+ void Load(ContentManager contentManager);
+
+ /// <summary>
+ /// Draws the sprite on the screen.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ /// <param name="position">Position on the screen in pixels where the sprite should stand.</param>
+ /// <param name="movementState">What moveset will be used? (Moving, Attacking...)</param>
+ /// <param name="movementDirection">Where does the sprite face to?</param>
+ void Draw(SpriteBatch spriteBatch, Vector2 position, MovementState movementState, MovementDirection movementDirection);
+
+ /// <summary>
+ /// Draws a static image of the sprite. No animations.
+ /// </summary>
+ /// <param name="spriteBatch">Sprite batch used for drawing.</param>
+ /// <param name="position">Position of the sprite in pixels. (Where are the feet of the sprite placed.</param>
+ /// <param name="movementState">What moveset will be used? (Moving, Attacking...)</param>
+ /// <param name="movementDirection">Where does the sprite face to?</param>
+ void DrawStatic(SpriteBatch spriteBatch,
+ Point position,
+ MovementState movementState,
+ MovementDirection movementDirection);
+
+ /// <summary>
+ /// Change the sprite to show an animation.
+ /// </summary>
+ /// <param name="gameTime">Elapsed game time is used for calculating FPS.</param>
+ void PlayAnimation(GameTime gameTime);
+
+ /// <summary>
+ /// Plays the specified animation fully, but only once.
+ /// </summary>
+ /// <param name="animation">For which movement state the animation should be played.</param>
+ /// <param name="duration">How long (or how slow) should the animation be?</param>
+ void PlayOnce(MovementState animation, TimeSpan duration);
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/KingSprite.cs b/V3/Objects/Sprite/KingSprite.cs
new file mode 100644
index 0000000..0640514
--- /dev/null
+++ b/V3/Objects/Sprite/KingSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class KingSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "king";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/LongswordFemaleSprite.cs b/V3/Objects/Sprite/LongswordFemaleSprite.cs
new file mode 100644
index 0000000..fc10b7c
--- /dev/null
+++ b/V3/Objects/Sprite/LongswordFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class LongswordFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "longsword_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/LongswordSprite.cs b/V3/Objects/Sprite/LongswordSprite.cs
new file mode 100644
index 0000000..7ac1850
--- /dev/null
+++ b/V3/Objects/Sprite/LongswordSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class LongswordSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "longsword";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/MeatballSprite.cs b/V3/Objects/Sprite/MeatballSprite.cs
new file mode 100644
index 0000000..eeafbe9
--- /dev/null
+++ b/V3/Objects/Sprite/MeatballSprite.cs
@@ -0,0 +1,11 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class MeatballSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "fleischklops";
+ protected override int AttackingFrames { get; } = 12;
+ protected override int DyingFrames { get; } = 8;
+ protected override int AttackingTextureIndex { get; } = 12;
+ protected override int DyingTextureIndex { get; } = 24;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/NecromancerFemaleSprite.cs b/V3/Objects/Sprite/NecromancerFemaleSprite.cs
new file mode 100644
index 0000000..ffecd74
--- /dev/null
+++ b/V3/Objects/Sprite/NecromancerFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class NecromancerFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "necromancer_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/NecromancerSprite.cs b/V3/Objects/Sprite/NecromancerSprite.cs
new file mode 100644
index 0000000..494ba13
--- /dev/null
+++ b/V3/Objects/Sprite/NecromancerSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class NecromancerSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "necromancer";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/NudeFemaleSprite.cs b/V3/Objects/Sprite/NudeFemaleSprite.cs
new file mode 100644
index 0000000..e2aaa2f
--- /dev/null
+++ b/V3/Objects/Sprite/NudeFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class NudeFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "nude_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/NudeSprite.cs b/V3/Objects/Sprite/NudeSprite.cs
new file mode 100644
index 0000000..1056335
--- /dev/null
+++ b/V3/Objects/Sprite/NudeSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class NudeSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "nude";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/PlateFemaleSprite.cs b/V3/Objects/Sprite/PlateFemaleSprite.cs
new file mode 100644
index 0000000..af3bd79
--- /dev/null
+++ b/V3/Objects/Sprite/PlateFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class PlateFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "plate_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/PlateSprite.cs b/V3/Objects/Sprite/PlateSprite.cs
new file mode 100644
index 0000000..c8495a0
--- /dev/null
+++ b/V3/Objects/Sprite/PlateSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class PlateSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "plate";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/PrinceSprite.cs b/V3/Objects/Sprite/PrinceSprite.cs
new file mode 100644
index 0000000..efe9d18
--- /dev/null
+++ b/V3/Objects/Sprite/PrinceSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class PrinceSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "prince";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ShieldFemaleSprite.cs b/V3/Objects/Sprite/ShieldFemaleSprite.cs
new file mode 100644
index 0000000..0e70545
--- /dev/null
+++ b/V3/Objects/Sprite/ShieldFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ShieldFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "shield_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ShieldSprite.cs b/V3/Objects/Sprite/ShieldSprite.cs
new file mode 100644
index 0000000..5830a45
--- /dev/null
+++ b/V3/Objects/Sprite/ShieldSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ShieldSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "shield";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ShortswordFemaleSprite.cs b/V3/Objects/Sprite/ShortswordFemaleSprite.cs
new file mode 100644
index 0000000..a046bb0
--- /dev/null
+++ b/V3/Objects/Sprite/ShortswordFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class ShortswordFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "shortsword_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ShortswordSprite.cs b/V3/Objects/Sprite/ShortswordSprite.cs
new file mode 100644
index 0000000..352631f
--- /dev/null
+++ b/V3/Objects/Sprite/ShortswordSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class ShortswordSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "shortsword";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/SkeletonArcherSprite.cs b/V3/Objects/Sprite/SkeletonArcherSprite.cs
new file mode 100644
index 0000000..1891939
--- /dev/null
+++ b/V3/Objects/Sprite/SkeletonArcherSprite.cs
@@ -0,0 +1,10 @@
+namespace V3.Objects.Sprite
+{
+ public class SkeletonArcherSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "skeleton_archer";
+ protected override int AttackingFrames { get; } = 4;
+ protected override int AttackingTextureIndex { get; } = 28;
+ protected override int DyingTextureIndex { get; } = 22;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/SkeletonEliteSprite.cs b/V3/Objects/Sprite/SkeletonEliteSprite.cs
new file mode 100644
index 0000000..99aa94b
--- /dev/null
+++ b/V3/Objects/Sprite/SkeletonEliteSprite.cs
@@ -0,0 +1,9 @@
+namespace V3.Objects.Sprite
+{
+ public class SkeletonEliteSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "skeleton_elite";
+ protected override int AttackingFrames { get; } = 4;
+ protected override int DyingTextureIndex { get; } = 22;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/SkeletonHorseSprite.cs b/V3/Objects/Sprite/SkeletonHorseSprite.cs
new file mode 100644
index 0000000..f26b58b
--- /dev/null
+++ b/V3/Objects/Sprite/SkeletonHorseSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class SkeletonHorseSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "skeleton_horse";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/SkeletonRiderSprite.cs b/V3/Objects/Sprite/SkeletonRiderSprite.cs
new file mode 100644
index 0000000..278d8c7
--- /dev/null
+++ b/V3/Objects/Sprite/SkeletonRiderSprite.cs
@@ -0,0 +1,10 @@
+namespace V3.Objects.Sprite
+{
+ public class SkeletonRiderSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "skeleton_rider";
+ protected override int AttackingFrames { get; } = 5;
+ protected override int DyingTextureIndex { get; } = 0;
+ protected override int DyingFrames { get; } = 0;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/SkeletonSprite.cs b/V3/Objects/Sprite/SkeletonSprite.cs
new file mode 100644
index 0000000..15f7246
--- /dev/null
+++ b/V3/Objects/Sprite/SkeletonSprite.cs
@@ -0,0 +1,9 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class SkeletonSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "skeleton";
+ protected override int AttackingFrames { get; } = 4;
+ protected override int DyingTextureIndex { get; } = 22;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/StaffFemaleSprite.cs b/V3/Objects/Sprite/StaffFemaleSprite.cs
new file mode 100644
index 0000000..5e4120c
--- /dev/null
+++ b/V3/Objects/Sprite/StaffFemaleSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class StaffFemaleSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "staff_female";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/StaffSprite.cs b/V3/Objects/Sprite/StaffSprite.cs
new file mode 100644
index 0000000..aac7bed
--- /dev/null
+++ b/V3/Objects/Sprite/StaffSprite.cs
@@ -0,0 +1,7 @@
+namespace V3.Objects.Sprite
+{
+ public class StaffSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "staff";
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ZombieSprite.cs b/V3/Objects/Sprite/ZombieSprite.cs
new file mode 100644
index 0000000..0cd1a19
--- /dev/null
+++ b/V3/Objects/Sprite/ZombieSprite.cs
@@ -0,0 +1,11 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ZombieSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "zombie";
+ protected override int AttackingFrames { get; } = 8;
+ protected override int DyingTextureIndex { get; } = 22;
+ protected override int SpecialTextureIndex { get; } = 36;
+ protected override int SpecialFrames { get; } = 8;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Sprite/ZombieWithClubSprite.cs b/V3/Objects/Sprite/ZombieWithClubSprite.cs
new file mode 100644
index 0000000..60abc24
--- /dev/null
+++ b/V3/Objects/Sprite/ZombieWithClubSprite.cs
@@ -0,0 +1,11 @@
+namespace V3.Objects.Sprite
+{
+ public sealed class ZombieWithClubSprite : AbstractSpriteCreature
+ {
+ protected override string TextureFile { get; } = "zombie_club";
+ protected override int AttackingFrames { get; } = 8;
+ protected override int DyingTextureIndex { get; } = 22;
+ protected override int SpecialTextureIndex { get; } = 36;
+ protected override int SpecialFrames { get; } = 8;
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/TextureObject.cs b/V3/Objects/TextureObject.cs
new file mode 100644
index 0000000..b7da6c0
--- /dev/null
+++ b/V3/Objects/TextureObject.cs
@@ -0,0 +1,76 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using System;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// Texture objects represent map data like tiles and map objects which are placed on the screen.
+ /// </summary>
+ public sealed class TextureObject : IGameObject
+ {
+ public Vector2 Position { get; set; }
+ public int Id { get; }
+ public Rectangle BoundaryRectangle => new Rectangle(mDrawPosition, mTextureSize);
+ private readonly string mTextureName;
+ private readonly Point mDrawPosition;
+ private readonly Point mTextureSize;
+ private Texture2D mTexture;
+ private readonly Point mTextureSource;
+
+ /// <summary>
+ /// Creates an empty TextureObject.
+ /// </summary>
+ public TextureObject()
+ {
+ Id = IdGenerator.GetNextId();
+ Position = Vector2.Zero;
+ mDrawPosition = Point.Zero;
+ mTextureSize = Point.Zero;
+ mTextureName = "EmptyPixel";
+ mTextureSource = Point.Zero;
+ }
+
+ public TextureObject(Point position, Point drawPosition, Point textureSize, Point textureSource, string textureName)
+ {
+ Position = position.ToVector2();
+ mDrawPosition = drawPosition;
+ mTextureSize = textureSize;
+ mTextureName = textureName;
+ mTextureSource = textureSource;
+ }
+
+ public void LoadContent(ContentManager contentManager)
+ {
+ mTexture = contentManager.Load<Texture2D>("Textures/" + mTextureName);
+ //mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ spriteBatch.Draw(mTexture, BoundaryRectangle, new Rectangle(mTextureSource, mTextureSize), Color.White);
+ }
+
+ public IGameObject GetSelf()
+ {
+ return this;
+ }
+
+ public override bool Equals(Object obj)
+ {
+ if (obj == null)
+ return false;
+ if (obj == this)
+ return true;
+ if (!(obj is IGameObject))
+ return false;
+ return Id.Equals(((IGameObject) obj).Id);
+ }
+
+ public override int GetHashCode()
+ {
+ return Id;
+ }
+ }
+}
diff --git a/V3/Objects/Transformation.cs b/V3/Objects/Transformation.cs
new file mode 100644
index 0000000..1b1cf86
--- /dev/null
+++ b/V3/Objects/Transformation.cs
@@ -0,0 +1,102 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+
+namespace V3.Objects
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Transformation
+ {
+ public IObjectsManager ObjectsManager { private get; set; }
+ private KeyboardState mOldStateSpecialattack;
+ private KeyboardState mOldStateZombie;
+ private KeyboardState mOldStateMeatball;
+ private KeyboardState mOldStateSkeleton;
+ private KeyboardState mOldStateHorse;
+ internal Selection mSelection;
+ private Texture2D mNecroArea;
+
+ /// <summary>
+ /// Call for transformations
+ /// </summary>
+ public void Transform()
+ {
+ KeyboardState newState = Keyboard.GetState();
+
+ // Specialattack for Meatball (press 1)
+ if (newState.IsKeyDown(Keys.D1))
+ {
+ if (!mOldStateSpecialattack.IsKeyDown(Keys.D1))
+ {
+ mSelection.Specialattack();
+ }
+ }
+ mOldStateSpecialattack = newState;
+
+ // Transform Zombie (press 2)
+ if (newState.IsKeyDown(Keys.D2))
+ {
+ if (!mOldStateZombie.IsKeyDown(Keys.D2))
+ {
+ mSelection.TransformZombie();
+ }
+ }
+ mOldStateZombie = newState;
+
+ // Transform Meatball (press 3)
+ // Nneed five zombies
+ if (newState.IsKeyDown(Keys.D3))
+ {
+ if (!mOldStateMeatball.IsKeyDown(Keys.D3))
+ {
+ mSelection.TransformMeatball();
+ }
+ }
+ mOldStateMeatball = newState;
+
+ // Transform Skeleton (press 4)
+ // Need one zombie
+ if (newState.IsKeyDown(Keys.D4))
+ {
+ if (!mOldStateSkeleton.IsKeyDown(Keys.D4))
+ {
+ mSelection.TransformSkeleton();
+ }
+ }
+ mOldStateSkeleton = newState;
+
+ // Transform SkeletonHorse (press 5)
+ // Nneed 3 skeletons
+ if (newState.IsKeyDown(Keys.D5))
+ {
+ if (!mOldStateHorse.IsKeyDown(Keys.D5))
+ {
+ mSelection.TransformSkeletonHorse();
+ }
+ }
+ mOldStateHorse = newState;
+ }
+
+ //Draw(SpriteBatch spriteBatch, Vector2 position, MovementState movementState, MovementDirection movementDirection)
+ /// <summary>
+ /// Load the selection and ellipse sprites for necromancers area
+ /// </summary>
+ /// <param name="contentManager">the content manager</param>
+ public void LoadArea(ContentManager contentManager)
+ {
+ //mNecroArea = contentManager.Load<Texture2D>("Sprites/selection");
+ mNecroArea = contentManager.Load<Texture2D>("Sprites/ellipse");
+ }
+
+ /// <summary>
+ /// Draw the area for necromancer
+ /// </summary>
+ /// <param name="spriteBatch">the sprite batch to use for drawing the object</param>
+ public void DrawNecroArea(SpriteBatch spriteBatch)
+ {
+ //spriteBatch.Draw(mNecroArea, ObjectsManager.PlayerCharacter.Position - new Vector2(800, 400), null, Color.Red*0.3f, 0, Vector2.Zero, 25, SpriteEffects.None, 0);
+ spriteBatch.Draw(mNecroArea, ObjectsManager.PlayerCharacter.Position - new Vector2(640, 320), null, Color.Red*0.5f, 0, Vector2.Zero, 2.5f, SpriteEffects.None, 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Objects/Woodhouse.cs b/V3/Objects/Woodhouse.cs
new file mode 100644
index 0000000..82c961b
--- /dev/null
+++ b/V3/Objects/Woodhouse.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Objects
+{
+ public sealed class Woodhouse : AbstractBuilding
+ {
+ public override string Name { get; } = "Holzhaus";
+ protected override int MaxRobustness { get; } = 130;
+ public override int Robustness { get; protected set; } = 130;
+ public override int MaxGivesWeapons { get; protected set; } = 10;
+
+ public Woodhouse(Vector2 position, Rectangle size, string textureName, BuildingFace facing) : base(position, size, textureName, facing)
+ {
+ }
+ }
+}
diff --git a/V3/Objects/Zombie.cs b/V3/Objects/Zombie.cs
new file mode 100644
index 0000000..635634c
--- /dev/null
+++ b/V3/Objects/Zombie.cs
@@ -0,0 +1,45 @@
+using System;
+using Microsoft.Xna.Framework.Content;
+using V3.Data;
+using V3.Map;
+using V3.Objects.Movement;
+using V3.Objects.Sprite;
+
+namespace V3.Objects
+{
+ /// <summary>
+ /// A simple zombie controlled by the necromancer.
+ /// </summary>
+ public sealed class Zombie : AbstractCreature
+ {
+ public override string Name { get; protected set; } = "Zombie";
+ public override int Life { get; protected set; } = 150;
+ public override int MaxLife { get; protected set; } = 150;
+ public override int Speed { get; } = 5;
+ public override int Attack { get; protected set; } = 8;
+ public override int AttackRadius { get; protected set; } = 48;
+ public override int SightRadius { get; protected set; } = 150;
+ public override TimeSpan TotalRecovery { get; } = TimeSpan.FromSeconds(1);
+ public override TimeSpan Recovery { get; set; }
+ protected override ISpriteCreature[] Sprite { get; } = {new ZombieSprite()};
+ protected override IMovable MovementScheme { get; } = new PlayerMovement();
+ protected override CreatureType Type { get; } = CreatureType.Zombie;
+ public override Faction Faction { get; } = Faction.Undead;
+ public override ICreature IsAttacking { get; set; }
+ public override IBuilding IsAttackingBuilding { get; set; }
+
+ public Zombie(ContentManager contentManager,
+ Pathfinder pathfinder, IOptionsManager optionsManager, AchievementsAndStatistics achievementsAndStatistics)
+ : base(contentManager, pathfinder, optionsManager,achievementsAndStatistics)
+ {
+ }
+
+ public override void LoadData(CreatureData data)
+ {
+ base.LoadData(data);
+
+ if (IsUpgraded)
+ ChangeEquipment(EquipmentType.Body, new ZombieWithClubSprite());
+ }
+ }
+}
diff --git a/V3/OpenTK.dll.config b/V3/OpenTK.dll.config
new file mode 100644
index 0000000..3f888cc
--- /dev/null
+++ b/V3/OpenTK.dll.config
@@ -0,0 +1,25 @@
+<configuration>
+ <dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
+ <dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
+ <dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
+ <dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
+ <dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
+ <dllmap os="linux" dll="libX11" target="libX11.so.6"/>
+ <dllmap os="linux" dll="libXi" target="libXi.so.6"/>
+ <dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0.disabled"/>
+ <dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
+ <dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
+ <dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
+ <dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
+ <dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
+ <dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
+ <dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
+ <dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
+ <!-- XQuartz compatibility (X11 on Mac) -->
+ <dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
+ <dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
+ <dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
+ <dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
+ <dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
+ <dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
+</configuration>
diff --git a/V3/Program.cs b/V3/Program.cs
new file mode 100644
index 0000000..aa1fdcd
--- /dev/null
+++ b/V3/Program.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace V3
+{
+ /// <summary>
+ /// The main class.
+ /// </summary>
+ public static class Program
+ {
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ [STAThread]
+ static void Main()
+ {
+ using (var game = new V3Game())
+ game.Run();
+ }
+ }
+}
diff --git a/V3/Properties/AssemblyInfo.cs b/V3/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4f905d4
--- /dev/null
+++ b/V3/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("V3")]
+[assembly: AssemblyProduct("V3")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("0c41bf76-037f-47fb-b16a-0c32567c6067")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/V3/Quadtree.cs b/V3/Quadtree.cs
new file mode 100644
index 0000000..dfc088f
--- /dev/null
+++ b/V3/Quadtree.cs
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Objects;
+
+namespace V3
+{
+ public sealed class Quadtree
+ {
+ private Node mRoot; // The first "Node" of the Quadtree
+ private ContentManager mContentManager;
+ private readonly Point mMaxSize;
+
+ /// <summary>
+ /// Generates Quadtree
+ /// </summary>
+ /// <param name="maxSize">Gives the biggest/first Rectangle of the Quadtree. This should be the sice of the howl map.</param>
+ public Quadtree(Point maxSize)
+ {
+ mRoot = new Node(new Rectangle(new Point(-128, -128), maxSize), null);
+ mMaxSize = maxSize;
+ }
+
+ /// <summary>
+ /// Updates the Quadtree. This is importent for the movements of the Objects.
+ /// </summary>
+ public void Update()
+ {
+ mRoot.Update1();
+ }
+
+ /// <summary>
+ /// You call this method if you want to Inster an Object to the Quadtree.
+ /// </summary>
+ /// <param name="item">Type of Creature including their position.</param>
+ public void Insert(IGameObject item)
+ {
+ mRoot.AddtoSubNode(item);
+ }
+
+ /// <summary>
+ /// DMakes the Rectangles of the Quadtree visible.
+ /// </summary>
+ /// <param name="spriteBatch"></param>
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ mRoot.DrawQuadtree(spriteBatch, Texture);
+ }
+
+ public List<IGameObject> GetObjectsInRectangle(Rectangle rectangle)
+ {
+ List<IGameObject> objectList = new List<IGameObject>();
+ return mRoot.GetObjectsInRectangle(rectangle, objectList);
+ }
+
+ /// <summary>
+ /// Deletes an Object out of the Quadtree.
+ /// </summary>
+ /// <param name="item">Type of Creature including their position.</param>
+ public void RemoveItem(IGameObject item)
+ {
+ mRoot.Delete(item);
+ }
+
+ /// <summary>
+ /// Loads the content to draw the Rectangels of the Quadtree.
+ /// </summary>
+ /// <param name="contentManager"></param>
+ public void LoadContent(ContentManager contentManager)
+ {
+ mContentManager = contentManager;
+ Texture = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+ /// <summary>
+ /// Deletes all elements from the quadtree.
+ /// </summary>
+ public void Clear()
+ {
+ mRoot?.Clear();
+ mRoot = new Node(new Rectangle(new Point(-50, -50), mMaxSize), null);
+ }
+
+ private Texture2D Texture { get; set; }
+ }
+}
diff --git a/V3/Screens/AbstractScreen.cs b/V3/Screens/AbstractScreen.cs
new file mode 100644
index 0000000..becd38d
--- /dev/null
+++ b/V3/Screens/AbstractScreen.cs
@@ -0,0 +1,57 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using V3.Input;
+
+namespace V3.Screens
+{
+ public abstract class AbstractScreen : IScreen
+ {
+ /// <summary>
+ /// Indicates whether screens below this one should be updated.
+ /// </summary>
+ public bool UpdateLower { get; }
+
+ /// <summary>
+ /// Indicates whether screens below this one should be drawn.
+ /// </summary>
+ public bool DrawLower { get; }
+
+ protected AbstractScreen(bool updateLower, bool drawLower)
+ {
+ UpdateLower = updateLower;
+ DrawLower = drawLower;
+ }
+
+ /// <summary>
+ /// Handles the given key event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="keyEvent">the key event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ public virtual bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ /// <summary>
+ /// Handles the given mouse event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="mouseEvent">the mouse event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ public virtual bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ return true;
+ }
+
+ public virtual void Update(GameTime gameTime)
+ {
+ }
+
+ public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ }
+ }
+}
diff --git a/V3/Screens/AchievementsScreen.cs b/V3/Screens/AchievementsScreen.cs
new file mode 100644
index 0000000..096fb81
--- /dev/null
+++ b/V3/Screens/AchievementsScreen.cs
@@ -0,0 +1,221 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the AchievementsAndStatistics.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class AchievementsScreen : AbstractScreen, IInitializable
+ {
+ //private AchievementBox mFirstSteps;
+ private AchievementBox mKillPrince;
+ private AchievementBox mKillKing;
+ //private AchievementBox mWarmasterAndWizard;
+ private AchievementBox mKaboom;
+ private AchievementBox mMarathonRunner;
+ private AchievementBox mIronMan;
+ private AchievementBox mMeatballCompany;
+ private AchievementBox mSkeletonHorseCavalry;
+ private AchievementBox mRightHandOfDeath;
+ private AchievementBox mMinimalist;
+ private AchievementBox mHundredDeadCorpses;
+ private AchievementBox mUndeadArmy;
+ private AchievementBox mHellsNotWaiting;
+
+
+ private readonly ContentManager mContentManager;
+ private readonly IMenuFactory mMenuFactory;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly AchievementsAndStatistics mAchievementsAndStatistics;
+ private readonly ObjectsManager mObjectsManager;
+
+ private Button mButtonBack;
+ private SelectButton mSelectPage;
+
+ private List<IMenu> mMenuList = new List<IMenu>();
+ private Texture2D mRectangle;
+
+ public AchievementsScreen(ContentManager contentManager, MenuActions menuActions, WidgetFactory widgetFactory,
+ IMenuFactory menuFactory, AchievementsAndStatistics achievementsAndStatistics, ObjectsManager objectsManager)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenuFactory = menuFactory;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ mObjectsManager = objectsManager;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mSelectPage = mWidgetFactory.CreateSelectButton();
+
+ var menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mKillPrince = mWidgetFactory.CreateAchievementBox();
+ mKillPrince.SetText("Erbfolge aufgehalten", "Vernichtet Prinz Erhard.");
+ menu.Widgets.Add(mKillPrince);
+
+ mKaboom = mWidgetFactory.CreateAchievementBox();
+ mKaboom.SetText("KABUMM!!!", "Tötet mindestens 10 Gegner mit einer einzigen Fleischklops-Explosion.");
+ menu.Widgets.Add(mKaboom);
+
+ mKillKing = mWidgetFactory.CreateAchievementBox();
+ mKillKing.SetText("Königsmord", "Nehmt eure Vergeltung am König und tötet ihn.");
+ menu.Widgets.Add(mKillKing);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mHundredDeadCorpses = mWidgetFactory.CreateAchievementBox();
+ mHundredDeadCorpses.SetText("Leichenfledderer", "Tötet in einer Mission mindestens 100 Gegner.");
+ menu.Widgets.Add(mHundredDeadCorpses);
+
+ mUndeadArmy = mWidgetFactory.CreateAchievementBox();
+ mUndeadArmy.SetText("Untote Armee", "Tötet in einer Mission mindestens 1000 Gegner.");
+ menu.Widgets.Add(mUndeadArmy);
+
+ mRightHandOfDeath = mWidgetFactory.CreateAchievementBox();
+ mRightHandOfDeath.SetText("Die erbarmungslose rechte Hand des Todes", "Tötet insgesamt 10 000 Gegner.");
+ menu.Widgets.Add(mRightHandOfDeath);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mMeatballCompany = mWidgetFactory.CreateAchievementBox();
+ mMeatballCompany.SetText("Fleischpanzer-Kompanie", "Erschafft und befehligt in einer Mission 10 Fleischklopse gleichzeitig.");
+ menu.Widgets.Add(mMeatballCompany);
+
+ mSkeletonHorseCavalry = mWidgetFactory.CreateAchievementBox();
+ mSkeletonHorseCavalry.SetText("Klappernde Kavallerie", "Erschafft und befehligt in einer Mission 25 Skelettpferde gleichzeitig.");
+ menu.Widgets.Add(mSkeletonHorseCavalry);
+
+ mMinimalist = mWidgetFactory.CreateAchievementBox();
+ mMinimalist.SetText("Minimalist", "Beendet eine Mission und setzt dabei weniger als 100 Einheiten ein.");
+ menu.Widgets.Add(mMinimalist);
+
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateVerticalMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mSelectPage);
+
+ mHellsNotWaiting = mWidgetFactory.CreateAchievementBox();
+ mHellsNotWaiting.SetText("Die Hölle wartet nicht", "Beendet eine Mission in weniger als 5 Minuten.");
+ menu.Widgets.Add(mHellsNotWaiting);
+
+ mMarathonRunner = mWidgetFactory.CreateAchievementBox();
+ mMarathonRunner.SetText("Marathonläufer", "Legt in einer Mission mindestens 1000m zurück.");
+ menu.Widgets.Add(mMarathonRunner);
+
+ mIronMan = mWidgetFactory.CreateAchievementBox();
+ mIronMan.SetText("Der Iron Man", "Legt insgesamt eine Strecke von 10 000m zurück.");
+ menu.Widgets.Add(mIronMan);
+
+ menu.Widgets.Add(mButtonBack);
+
+ for (var i = 0; i < mMenuList.Count; i++)
+ mSelectPage.Values.Add($"Seite {i + 1}");
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ GetCurrentMenu().HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ GetCurrentMenu().Update();
+
+ // achievement datas. if one value becomes a given value, the corresponding achievement will be enabled.
+
+ if (mAchievementsAndStatistics.mKillPrince)
+ mKillPrince.IsEnabled = true;
+ if (mAchievementsAndStatistics.mKillKing)
+ mKillKing.IsEnabled = true;
+ if (mAchievementsAndStatistics.mHellsNotWaiting)
+ mHellsNotWaiting.IsEnabled = true;
+ if (mAchievementsAndStatistics.mKaboom)
+ mKaboom.IsEnabled = true;
+
+ if (mAchievementsAndStatistics.mMarathonRunner >= 1000)
+ mMarathonRunner.IsEnabled = true;
+ if (mAchievementsAndStatistics.mIronMan >= 10000)
+ mIronMan.IsEnabled = true;
+ if (mAchievementsAndStatistics.mMeatballCompany >= 10)
+ mMeatballCompany.IsEnabled = true;
+ if (mAchievementsAndStatistics.mSkeletonHorseCavalry >= 25)
+ mSkeletonHorseCavalry.IsEnabled = true;
+ if (mAchievementsAndStatistics.mRightHandOfDeath >= 10000)
+ mRightHandOfDeath.IsEnabled = true;
+ if (mAchievementsAndStatistics.mMinimalist <= 100 && mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ mMinimalist.IsEnabled = true;
+ if (mAchievementsAndStatistics.mHundredDeadCorpses >= 100)
+ mHundredDeadCorpses.IsEnabled = true;
+ if (mAchievementsAndStatistics.mUndeadArmy >= 1000)
+ mUndeadArmy.IsEnabled = true;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var menu = GetCurrentMenu();
+ var backgroundRectangle = new Rectangle((int)menu.Position.X,
+ (int)menu.Position.Y, (int)menu.Size.X,
+ (int)menu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.WhiteSmoke);
+ spriteBatch.End();
+
+ menu.Draw(spriteBatch);
+ }
+
+ private IMenu GetCurrentMenu()
+ {
+ return mMenuList[mSelectPage.SelectedIndex];
+ }
+ }
+}
diff --git a/V3/Screens/DeathScreen.cs b/V3/Screens/DeathScreen.cs
new file mode 100644
index 0000000..230d40a
--- /dev/null
+++ b/V3/Screens/DeathScreen.cs
@@ -0,0 +1,140 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class DeathScreen : AbstractScreen, IInitializable
+ {
+ private static TimeSpan sTotalDelay = TimeSpan.FromSeconds(2);
+
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Button mMenuButton;
+ private Button mCloseGameButton;
+ private Vector2 mButtonPosition;
+ private Vector2 mCenter;
+ private Vector2 mFontCenter;
+ private SpriteFont mDeathFont;
+ private Texture2D mRectangle;
+
+ private TimeSpan mDelayTimer = sTotalDelay;
+
+ /// <summary>
+ /// Creates a death screen if the players health reaches 0.
+ /// </summary>
+
+ public DeathScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager,
+ MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mDelayTimer <= TimeSpan.Zero)
+ {
+ mMenuButton.HandleMouseEvent(mouseEvent);
+ mCloseGameButton.HandleMouseEvent(mouseEvent);
+ }
+ return false;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mDeathFont = mContentManager.Load<SpriteFont>("Fonts/DeathFont");
+
+ mMenuButton = mWidgetFactory.CreateButton("Zum Hauptmenü");
+ mMenuButton.Position = Vector2.Zero;
+ mMenuButton.PaddingX = 10;
+ mMenuButton.PaddingY = 0;
+ mMenuButton.Size = mMenuButton.GetMinimumSize();
+ mMenuButton.BackgroundColor = Color.Gray * 0.7f;
+
+ mCloseGameButton = mWidgetFactory.CreateButton("Spiel schließen");
+ mCloseGameButton.Position = Vector2.Zero;
+ mCloseGameButton.PaddingX = 10;
+ mCloseGameButton.PaddingY = 0;
+ mCloseGameButton.Size = mCloseGameButton.GetMinimumSize();
+ mCloseGameButton.BackgroundColor = Color.Gray * 0.7f;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+
+ mCenter = new Vector2(viewport.Width / 2f, viewport.Height / 2f);
+ mFontCenter = mDeathFont.MeasureString("Ihr seid tot") / 2;
+ mButtonPosition = new Vector2(mCenter.X - mFontCenter.X * 2/3, mCenter.Y + mFontCenter.Y * 2);
+
+ if (mDelayTimer > TimeSpan.Zero)
+ mDelayTimer -= gameTime.ElapsedGameTime;
+
+ mMenuButton.Position = mButtonPosition;
+ mCloseGameButton.Position = mMenuButton.Position + new Vector2(mFontCenter.X * 3/ 4, 0);
+
+ if (mMenuButton.IsClicked)
+ {
+ mMenuActions.OpenMainScreen();
+ }
+ else if (mCloseGameButton.IsClicked)
+ {
+ mMenuActions.Exit();
+ }
+
+ mMenuButton.IsSelected = mMenuButton.CheckSelected(Mouse.GetState().Position);
+ mMenuButton.IsClicked = false;
+
+ mCloseGameButton.IsSelected = mCloseGameButton.CheckSelected(Mouse.GetState().Position);
+ mCloseGameButton.IsClicked = false;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ float displayRatio = (float) (1 - mDelayTimer.TotalMilliseconds / sTotalDelay.TotalMilliseconds);
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle,
+ mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds,
+ Color.Black * 0.5f * displayRatio);
+
+ spriteBatch.DrawString(mDeathFont,
+ "Ihr seid tot",
+ mCenter,
+ Color.Firebrick * displayRatio,
+ 0,
+ mFontCenter,
+ 1.0f,
+ SpriteEffects.None,
+ 0.5f);
+
+ if (mDelayTimer <= TimeSpan.Zero)
+ {
+ mMenuButton.Draw(spriteBatch);
+ mCloseGameButton.Draw(spriteBatch);
+ }
+ spriteBatch.End();
+ }
+ }
+}
diff --git a/V3/Screens/DebugScreen.cs b/V3/Screens/DebugScreen.cs
new file mode 100644
index 0000000..a1e0820
--- /dev/null
+++ b/V3/Screens/DebugScreen.cs
@@ -0,0 +1,92 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Data;
+using V3.Input;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// A special screen that counts and show the number of frames per
+ /// second (if debug is enabled). This screen is not added to the
+ /// screen stack, but is always drawn and updated by the screen manager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class DebugScreen : AbstractScreen, IInitializable
+ {
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly WidgetFactory mWidgetFactory;
+ private FpsCounter mFpsCounter;
+ private Label mFpsLabel;
+ private Label mUnitCountLabel;
+ private int mUnitCount;
+
+ /// <summary>
+ /// Creates a new debug screen.
+ /// </summary>
+ public DebugScreen(GraphicsDeviceManager graphicsDeviceManager,
+ IObjectsManager objectsManager, IOptionsManager optionsManager,
+ WidgetFactory widgetFactory) : base(true, true)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mObjectsManager = objectsManager;
+ mOptionsManager = optionsManager;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mFpsCounter = new FpsCounter();
+
+ mFpsLabel = mWidgetFactory.CreateLabel("");
+ mFpsLabel.PaddingX = 10;
+ mFpsLabel.PaddingY = 0;
+ mFpsLabel.HorizontalOrientation = HorizontalOrientation.Left;
+ mFpsLabel.Color = Color.Red;
+
+ mUnitCountLabel = mWidgetFactory.CreateLabel("");
+ mUnitCountLabel.PaddingX = 10;
+ mUnitCountLabel.PaddingY = 0;
+ mUnitCountLabel.HorizontalOrientation = HorizontalOrientation.Left;
+ mUnitCountLabel.Color = Color.Red;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ return false;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ mFpsCounter.Update(gameTime);
+ mUnitCount = mObjectsManager.CreatureList?.Count ?? 0;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mFpsCounter.AddFrame();
+
+ if (mOptionsManager.Options.DebugMode == DebugMode.Off)
+ return;
+
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+
+ mFpsLabel.Text = $"FPS: {mFpsCounter.Fps} " + (gameTime.IsRunningSlowly ? "!" : "");
+ mFpsLabel.Size = mFpsLabel.GetMinimumSize();
+ mFpsLabel.Position = new Vector2(0, viewport.Height - mFpsLabel.Size.Y);
+
+ mUnitCountLabel.Text = $"# units: {mUnitCount}";
+ mUnitCountLabel.Size = mUnitCountLabel.GetMinimumSize();
+ mUnitCountLabel.Position = mFpsLabel.Position - new Vector2(0, mFpsLabel.Size.Y);
+
+ spriteBatch.Begin();
+ mFpsLabel.Draw(spriteBatch);
+ mUnitCountLabel.Draw(spriteBatch);
+ spriteBatch.End();
+ }
+ }
+}
diff --git a/V3/Screens/FpsCounter.cs b/V3/Screens/FpsCounter.cs
new file mode 100644
index 0000000..5c3ac0c
--- /dev/null
+++ b/V3/Screens/FpsCounter.cs
@@ -0,0 +1,46 @@
+using System;
+using Microsoft.Xna.Framework;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Counts the frames per second based on the elapsed time and the number
+ /// of frames that have been drawn. Call Update in each Update, and
+ /// AddFrame in each Draw.
+ /// </summary>
+ public sealed class FpsCounter
+ {
+ /// <summary>
+ /// The current frames per second.
+ /// </summary>
+ public int Fps { get; private set; }
+
+ private int mFrameCount;
+ private TimeSpan mTimeSpan = TimeSpan.Zero;
+
+ /// <summary>
+ /// Updates the elapsed time and -- once every second -- the fps value.
+ /// </summary>
+ /// <param name="gameTime">the elapsed game time</param>
+ public void Update(GameTime gameTime)
+ {
+ mTimeSpan += gameTime.ElapsedGameTime;
+
+ if (mTimeSpan > TimeSpan.FromSeconds(1))
+ {
+ mTimeSpan -= TimeSpan.FromSeconds(1);
+ Fps = mFrameCount;
+ mFrameCount = 0;
+ }
+ }
+
+ /// <summary>
+ /// Registers that a frame has been drawn. Should be called once for
+ /// every Draw.
+ /// </summary>
+ public void AddFrame()
+ {
+ mFrameCount++;
+ }
+ }
+}
diff --git a/V3/Screens/GameScreen.cs b/V3/Screens/GameScreen.cs
new file mode 100644
index 0000000..e37b100
--- /dev/null
+++ b/V3/Screens/GameScreen.cs
@@ -0,0 +1,306 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.AI;
+using V3.Camera;
+using V3.Data;
+using V3.Effects;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The main game screen.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class GameScreen : AbstractScreen, IInitializable
+ {
+ private readonly CameraManager mCameraManager;
+ private readonly ContentManager mContentManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly Selection mSelection;
+ private readonly Transformation mTransformation;
+ private readonly IAiPlayer mAiPlayer;
+ private readonly IMapManager mMapManager;
+ private readonly IEffectsManager mEffectsManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly Pathfinder mPathfinder;
+ private readonly Texture2D mOnePixelTexture;
+ private readonly FogOfWar mFog;
+ private bool mFogOfWarActivaded = true;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+ private int mFogCounter;
+ // Fields for handling mouse input.
+ private Point mInitialClickPosition;
+ private bool mLeftButtonPressed;
+ private bool mRightButtonPressed;
+ private Vector2 mRightButtonPosition;
+
+ /// <summary>
+ /// Creates a new game screen.
+ /// </summary>
+ public GameScreen(IOptionsManager optionsManager, CameraManager cameraManager,
+ ContentManager contentManager, CreatureFactory creatureFactory,
+ GraphicsDeviceManager graphicsDeviceManager, IMapManager mapManager,
+ MenuActions menuActions, IAiPlayer aiPlayer, IObjectsManager objectsManager,
+ Pathfinder pathfinder, Selection selection, FogOfWar fog, Transformation transformation,
+ IEffectsManager effectsManager, AchievementsAndStatistics achievementsAndStatistics) : base(false, false)
+ {
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mCameraManager = cameraManager;
+ mOptionsManager = optionsManager;
+ mContentManager = contentManager;
+ mCreatureFactory = creatureFactory;
+ mEffectsManager = effectsManager;
+ mTransformation = transformation;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mAiPlayer = aiPlayer;
+ mPathfinder = pathfinder;
+ mSelection = selection;
+ mFog = fog;
+ mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mLeftButtonPressed)
+ {
+ mLeftButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ else
+ {
+ if (mLeftButtonPressed)
+ {
+ mSelection.Select(mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ mouseEvent.PositionReleased.GetValueOrDefault() + mCameraManager.GetCamera().Location.ToPoint());
+ mLeftButtonPressed = false;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mRightButtonPressed)
+ {
+ mRightButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (mouseEvent.PositionReleased != null && mouseEvent.ReleasedOnScreen)
+ {
+ mRightButtonPressed = false;
+ mRightButtonPosition = mouseEvent.PositionReleased.Value.ToVector2() + mCameraManager.GetCamera().Location;
+ mSelection.Move(mouseEvent.PositionReleased.Value.ToVector2()
+ + mCameraManager.GetCamera().Location);
+ }
+ }
+ return true;
+ }
+
+ public void Initialize()
+ {
+#if NEWMAP
+ mMapManager.Load("work_in_progress");
+#else
+ mMapManager.Load("map_grassland");
+#endif
+ mObjectsManager.Initialize(mMapManager);
+ mCameraManager.Initialize(mMapManager.SizeInPixel);
+ mPathfinder.LoadGrid(mMapManager.GetPathfindingGrid());
+ mFog.LoadContent(mContentManager);
+ mFog.LoadGrid(mMapManager.SizeInPixel);
+ mTransformation.mSelection = mSelection;
+ mTransformation.LoadArea(mContentManager);
+ mTransformation.ObjectsManager = mObjectsManager;
+ }
+
+ /// <summary>
+ /// Create the initial population as designated by the map manager,
+ /// the player character, and possibly other creatures.
+ /// </summary>
+ public void CreateInitialPopulation()
+ {
+ // Create initial population.
+ foreach (var creature in mMapManager.GetPopulation(mCreatureFactory, mPathfinder))
+ {
+ mObjectsManager.CreateCreature(creature);
+ }
+
+#if NEWMAP
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(385, 420), MovementDirection.S);
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ // Create Prince and his guard.
+ mObjectsManager.CreatePrince(mCreatureFactory.CreatePrince(new Vector2(6139, 3039), MovementDirection.SO));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5794, 2900), MovementDirection.N));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5858, 2900), MovementDirection.N));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(5936, 2885), MovementDirection.NW));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(6000, 2885), MovementDirection.NW));
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKingsGuard(new Vector2(6064, 2885), MovementDirection.NW));
+#else
+ // Add creatures for testing purposes.
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(300, 150), MovementDirection.S);
+ var zombie1 = mCreatureFactory.CreateZombie(new Vector2(800, 200), MovementDirection.S);
+ var zombie2 = mCreatureFactory.CreateZombie(new Vector2(850, 250), MovementDirection.S);
+ var zombie3 = mCreatureFactory.CreateZombie(new Vector2(900, 200), MovementDirection.S);
+ var zombie4 = mCreatureFactory.CreateZombie(new Vector2(950, 250), MovementDirection.S);
+ var zombie5 = mCreatureFactory.CreateZombie(new Vector2(1000, 200), MovementDirection.S);
+ var zombieToGo = mCreatureFactory.CreateZombie(new Vector2(400, 400), MovementDirection.S);
+ var knight = mCreatureFactory.CreateKnight(new Vector2(1500, 210), MovementDirection.W);
+ var horse = mCreatureFactory.CreateSkeletonHorse(new Vector2(1300, 500), MovementDirection.SW);
+ var prince = mCreatureFactory.CreatePrince(new Vector2(1800, 1000), MovementDirection.S);
+ var meatball = mCreatureFactory.CreateMeatball(new Vector2(350, 150), MovementDirection.S);
+ // Add creatures to obects manager.
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ mObjectsManager.CreatePrince(prince);
+ mObjectsManager.CreateCreature(zombie1);
+ mObjectsManager.CreateCreature(zombie2);
+ mObjectsManager.CreateCreature(zombie3);
+ mObjectsManager.CreateCreature(zombie4);
+ mObjectsManager.CreateCreature(zombie5);
+ mObjectsManager.CreateCreature(zombieToGo);
+ mObjectsManager.CreateCreature(knight);
+ mObjectsManager.CreateCreature(horse);
+ mObjectsManager.CreateCreature(meatball);
+#endif
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.OpenPauseScreen();
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F5)
+ {
+ Rectangle cameraRectangle = mCameraManager.GetCamera().ScreenRectangle;
+ mEffectsManager.PlayOnce(new SmokeBig(), cameraRectangle.Center, cameraRectangle.Size);
+ mObjectsManager.ExposeTheLiving();
+ }
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F6)
+ {
+ (mObjectsManager.PlayerCharacter as Necromancer)?.ChangeSex();
+ mEffectsManager.PlayOnce(new SmokeSmall(), mObjectsManager.PlayerCharacter.Position.ToPoint(), new Point(256));
+ }
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.F8)
+ {
+ mFogOfWarActivaded = !mFogOfWarActivaded;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public override void Update(GameTime gameTime)
+ {
+#if DEBUG
+#else
+ try
+ {
+#endif
+ if (mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ mAchievementsAndStatistics.mKillKing = true;
+ if (mObjectsManager.Prince != null && mObjectsManager.Prince.IsDead)
+ mAchievementsAndStatistics.mKillPrince = true;
+ mObjectsManager.Update(gameTime, mRightButtonPressed, mRightButtonPosition, mCameraManager.GetCamera());
+ mCameraManager.Update(mObjectsManager.PlayerCharacter);
+ mEffectsManager.Update(gameTime);
+ mAiPlayer.Update(gameTime);
+
+ // Call for Transformations
+ mTransformation.Transform();
+
+ // Check whether creature is in Necromancers Radius
+ mSelection.UpdateSelection();
+
+ // Update for FogOfWar.
+ for (int i = mFogCounter; i < mObjectsManager.UndeadCreatures.Count; i += 30)
+ {
+ mFog.Update(mObjectsManager.UndeadCreatures[i]);
+ }
+ if (mFogCounter < 30)
+ mFogCounter++;
+ else
+ mFogCounter = 0;
+
+#if DEBUG
+#else
+ }
+ catch (System.Exception e)
+ {
+ System.Console.WriteLine(e.Message);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mGraphicsDeviceManager.GraphicsDevice.Clear(Color.Black);
+ spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend,
+ null, null, null, null, mCameraManager.GetCamera().Transform);
+ mMapManager.DrawFloor(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.Draw(spriteBatch, mCameraManager.GetCamera());
+ mTransformation.DrawNecroArea(spriteBatch);
+ mEffectsManager.Draw(spriteBatch);
+ // Draws the selection rectangle when left mouse button is pressed.
+ if (mLeftButtonPressed)
+ mSelection.Draw(spriteBatch, mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ Mouse.GetState().Position + mCameraManager.GetCamera().Location.ToPoint());
+ if (mFogOfWarActivaded) mFog.DrawFog(spriteBatch);
+ if (mOptionsManager.Options.DebugMode == DebugMode.Full)
+ {
+ mMapManager.DrawPathfindingGrid(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.DrawQuadtree(spriteBatch);
+ DrawLastSelection(spriteBatch, mSelection.LastSelection);
+ }
+ spriteBatch.End();
+ }
+
+ private void DrawLastSelection(SpriteBatch spriteBatch, Rectangle selection)
+ {
+ spriteBatch.Draw(mOnePixelTexture, selection, new Color(Color.Black, 100));
+ }
+
+ public void SetFog(List<Rectangle> fog)
+ {
+ mFog.SetFog(fog);
+ }
+
+ public List<Rectangle> GetFog()
+ {
+ return mFog.GetFog();
+ }
+
+ public void SetAiState(AiState state)
+ {
+ mAiPlayer.State = state;
+ }
+
+ public AiState GetAiState()
+ {
+ return mAiPlayer.State;
+ }
+ }
+}
diff --git a/V3/Screens/HudScreen.cs b/V3/Screens/HudScreen.cs
new file mode 100644
index 0000000..123ba8a
--- /dev/null
+++ b/V3/Screens/HudScreen.cs
@@ -0,0 +1,349 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.Data;
+using V3.Camera;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Creates a new HUD screen.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class HudScreen : AbstractScreen, IInitializable
+ {
+ private readonly MenuActions mMenuActions;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly ContentManager mContentManager;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly CameraManager mCameraManager;
+ private readonly IMapManager mMapManager;
+ private readonly IObjectsManager mObjectsManager;
+ private AchievementsAndStatistics mAchievementsAndStatistics;
+
+ private KeyboardState mCurrentState;
+ private KeyboardState mPreviousState;
+ private SpriteFont mFont;
+
+ private List<Button> mButtons = new List<Button>();
+ private Button mPauseButton;
+ private Button mCameraButton;
+ private Button mButton1;
+ private Button mButton2;
+ private Button mButton3;
+ private Button mButton4;
+ private Button mButton5;
+
+ private Rectangle mLifeBarRectangle;
+ private Rectangle mBackgroundRectangle;
+ private Rectangle mUnitRectangle;
+ private Rectangle mUnitInformationRectangle;
+ private Rectangle mMiniMapRectangle;
+ private Rectangle mMiniMapCamera;
+
+ private Texture2D mRectangle;
+
+ private Point mRectanglePos;
+ private Point mRectangleSize;
+ private int mCurrentHealth;
+ private int mMaxHealth;
+ private int mCounter;
+
+ private Vector2 mCreatureNamePosition;
+ private readonly Selection mSelection;
+ private int mScaleFactorX;
+ private int mScaleFactorY;
+
+ public HudScreen(ContentManager contentManager, GraphicsDeviceManager graphicsDeviceManager,
+ IOptionsManager optionsManager, MenuActions menuActions,
+ WidgetFactory widgetFactory, Selection selection, CameraManager cameraManager,
+ IMapManager mapManager, IObjectsManager objectsManager,
+ AchievementsAndStatistics achievementsAndStatistics): base(true, true)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mContentManager = contentManager;
+ mOptionsManager = optionsManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mSelection = selection;
+ mCameraManager = cameraManager;
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mFont = mContentManager.Load<SpriteFont>("Fonts/UnitFont");
+
+ mPauseButton = CreateButton("Buttons/Button-06",
+ "Pause",
+ "Pausiert das Spiel und \n" +
+ "öffnet das Pausemenü.");
+ mCameraButton = CreateButton("Buttons/Button-07",
+ "Kamera",
+ "Ändert die Kameraeinstellung zu \n" +
+ "zentrierter oder schiebender Kamera. ");
+ mButton1 = CreateButton("Buttons/Button-01",
+ "Explosion",
+ "Lasst den Fleischklops explodieren \n" +
+ "und fügt im Umkreis Schaden zu.");
+ mButton2 = CreateButton("Buttons/Button-02",
+ "Zombie beschwören",
+ "Wiederbelebt alle toten Gegner im Umkreis \n" +
+ "und lasst sie für euch kämpfen.");
+ mButton3 = CreateButton("Buttons/Button-03",
+ "Zombies verwandeln",
+ "Verschmelzt 5 ausgewählte Zombies \n" +
+ "zu einem Fleischklops.");
+ mButton4 = CreateButton("Buttons/Button-04",
+ "Skelett erschaffen",
+ "Verwandelt einen Zombie zu einem \n" +
+ "Skelett.");
+ mButton5 = CreateButton("Buttons/Button-05",
+ "Totenpferd beschwören",
+ "Vereinigt 3 Skelette zu einem \n" +
+ "Totenpferd.");
+
+ if (mObjectsManager.PlayerCharacter != null)
+ {
+ mCurrentHealth = mObjectsManager.PlayerCharacter.Life;
+ mMaxHealth = mObjectsManager.PlayerCharacter.MaxLife;
+ }
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mButtons.ForEach(b => b.HandleMouseEvent(mouseEvent));
+ if (mMiniMapRectangle.Contains(mouseEvent.PositionPressed))
+ {
+ return true;
+ }
+ foreach (var button in mButtons)
+ {
+ if (button.Rectangle.Contains(mouseEvent.PositionPressed))
+ return true;
+ if (mouseEvent.PositionReleased.HasValue && button.Rectangle.Contains(mouseEvent.PositionReleased.Value))
+ return true;
+ }
+
+ return false;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ mRectanglePos = new Point(viewport.Width / 2 - viewport.Width / 8, viewport.Height - viewport.Height / 20 - 2);
+ mRectangleSize = new Point(viewport.Width / 4, viewport.Height / 20);
+ mCurrentHealth = mObjectsManager.PlayerCharacter.Life;
+
+ mLifeBarRectangle = new Rectangle(mRectanglePos.X, mRectanglePos.Y, mRectangleSize.X * mCurrentHealth / mMaxHealth, mRectangleSize.Y);
+ mBackgroundRectangle = new Rectangle(mRectanglePos - new Point(2, 2), mRectangleSize + new Point(4, 4));
+ mUnitInformationRectangle = new Rectangle(viewport.Width * 2/3, viewport.Height * 7/8, viewport.Width / 3, viewport.Height / 8);
+ mUnitRectangle = new Rectangle(mUnitInformationRectangle.X + mUnitInformationRectangle.Width * 2 / 3, mUnitInformationRectangle.Y + 5, mUnitInformationRectangle.Width / 3 - 15, mUnitInformationRectangle.Height - 10);
+ mCreatureNamePosition = new Vector2(mUnitInformationRectangle.X + mUnitInformationRectangle.Width / 20, mUnitInformationRectangle.Y + mUnitInformationRectangle.Height / 20);
+
+ // Logic for camera toggle
+ mPreviousState = mCurrentState;
+ mCurrentState = Keyboard.GetState();
+
+ if (mCameraButton.IsClicked || (mCurrentState.IsKeyDown(Keys.C) && mPreviousState.IsKeyUp(Keys.C)))
+ {
+ if (mOptionsManager.Options.CameraType == CameraType.Centered)
+ {
+ mOptionsManager.Options.CameraType = CameraType.Scrolling;
+ }
+ else if (mOptionsManager.Options.CameraType == CameraType.Scrolling)
+ {
+ mOptionsManager.Options.CameraType = CameraType.Centered;
+ }
+ mMenuActions.SaveOptions();
+ mMenuActions.ApplyOptions();
+ }
+
+ // Logic for opening the death screen if the player's life reaches 0 or the victory screen if the boss is defeated.
+ if (mObjectsManager.PlayerCharacter.IsDead)
+ {
+ mCounter++;
+ if (mCounter == 120)
+ {
+ mMenuActions.OpenDeathScreen();
+ mCounter = 0;
+ }
+ }
+
+ else if (mObjectsManager.Boss != null && mObjectsManager.Boss.IsDead)
+ {
+ mCounter++;
+ if (mCounter == 120)
+ {
+ mAchievementsAndStatistics.mUsedTime -= TimeSpan.FromSeconds(2);
+ if (gameTime.TotalGameTime <= TimeSpan.FromMinutes(5))
+ mAchievementsAndStatistics.mHellsNotWaiting = true;
+ mMenuActions.OpenVictoryScreen();
+ mCounter = 0;
+ }
+ }
+
+ // Define the Minimap into the upper right corner and the scale factors needed.
+
+ mMiniMapRectangle = new Rectangle(viewport.Width * 3 / 4, 0, viewport.Width * 1 / 4, viewport.Height * 1 / 4);
+ mScaleFactorX = mCameraManager.GetCamera().MapPixelWidth / mMiniMapRectangle.Width;
+ mScaleFactorY = mCameraManager.GetCamera().MapPixelHeight / 2 / mMiniMapRectangle.Height;
+ mMiniMapCamera = new Rectangle(mMiniMapRectangle.X + (int)mCameraManager.GetCamera().Location.X / mScaleFactorX,
+ mMiniMapRectangle.Y + (int)mCameraManager.GetCamera().Location.Y / mScaleFactorY,
+ viewport.Width / mScaleFactorX, viewport.Height / mScaleFactorY);
+
+ if (mMiniMapRectangle.Contains(Mouse.GetState().Position))
+ {
+ if (Mouse.GetState().LeftButton == ButtonState.Pressed)
+ {
+ float newCameraLocationX = MathHelper.Clamp(((float) Mouse.GetState().Position.X - mMiniMapRectangle.X) * mScaleFactorX -
+ mMiniMapCamera.Width / 2f * mScaleFactorX, 0, mCameraManager.GetCamera().MapPixelWidth - viewport.Width);
+ float newCameraLocationY = MathHelper.Clamp(((float) Mouse.GetState().Position.Y - mMiniMapRectangle.Y) * mScaleFactorY -
+ mMiniMapCamera.Height / 2f * mScaleFactorY, 0, mCameraManager.GetCamera().MapPixelHeight / 2f - viewport.Height);
+ mCameraManager.GetCamera().Location = new Vector2(newCameraLocationX, newCameraLocationY);
+ }
+ }
+
+ // Call for transformations
+ if (mButton1.IsClicked)
+ mSelection.Specialattack();
+ if (mButton2.IsClicked)
+ mSelection.TransformZombie();
+ if (mButton3.IsClicked)
+ mSelection.TransformMeatball();
+ if (mButton4.IsClicked)
+ mSelection.TransformSkeleton();
+ if (mButton5.IsClicked)
+ mSelection.TransformSkeletonHorse();
+
+ UpdateButtons();
+
+ foreach (var unit in mSelection.SelectedCreatures)
+ {
+ if (unit is Meatball)
+ mAchievementsAndStatistics.mMeatballCompany += 1;
+ else if (unit is SkeletonHorse)
+ mAchievementsAndStatistics.mSkeletonHorseCavalry += 1;
+ }
+
+ mAchievementsAndStatistics.mWalkedDistance += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mMarathonRunner += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mIronMan += ((Necromancer)mObjectsManager.PlayerCharacter).WalkedPixels / 64;
+ mAchievementsAndStatistics.mUsedTime += gameTime.ElapsedGameTime;
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ mButtons.ForEach(b => b.Draw(spriteBatch));
+ spriteBatch.Draw(mRectangle, mBackgroundRectangle, Color.Black * 0.7f);
+ spriteBatch.Draw(mRectangle, mLifeBarRectangle, Color.Firebrick);
+ spriteBatch.Draw(mRectangle, mUnitInformationRectangle, Color.Chocolate * 0.7f);
+ spriteBatch.Draw(mRectangle, mUnitRectangle, Color.Gainsboro * 0.6f);
+
+ // Draw the minimap.
+ mMapManager.DrawMinimap(spriteBatch, mMiniMapRectangle);
+
+ // Draw a rectangle on the part of the MiniMap that is shown by the camera.
+ // Upper line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y, mMiniMapCamera.Width, 1), Color.White);
+ // Left line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y, 1, mMiniMapCamera.Height), Color.White);
+ // Right line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X + mMiniMapCamera.Width, mMiniMapCamera.Y, 1, mMiniMapCamera.Height), Color.White);
+ // Bottom line
+ spriteBatch.Draw(mRectangle, new Rectangle(mMiniMapCamera.X, mMiniMapCamera.Y + mMiniMapCamera.Height, mMiniMapCamera.Width, 1), Color.White);
+
+ foreach (var creature in mSelection.SelectedCreatures)
+ {
+ if (creature.IsSelected && mSelection.SelectedCreatures.Count == 1)
+ {
+ spriteBatch.DrawString(mFont, creature.Name, mCreatureNamePosition, Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Leben: " + creature.Life,
+ mCreatureNamePosition + new Vector2(0, 20),
+ Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Kraft: " + creature.Attack,
+ mCreatureNamePosition + new Vector2(0, 40),
+ Color.Black);
+ creature.DrawStatic(spriteBatch,
+ new Point(mUnitRectangle.X + mUnitRectangle.Width / 2, mUnitRectangle.Y + mUnitRectangle.Height * 3/4));
+ }
+ else if (mSelection.SelectedCreatures.Count > 1)
+ {
+ var showedCreature = mSelection.SelectedCreatures[0];
+ spriteBatch.DrawString(mFont, showedCreature.Name, mCreatureNamePosition, Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Leben: " + showedCreature.Life,
+ mCreatureNamePosition + new Vector2(0, 20),
+ Color.Black);
+ spriteBatch.DrawString(mFont,
+ "Kraft: " + showedCreature.Attack,
+ mCreatureNamePosition + new Vector2(0, 40),
+ Color.Black);
+ showedCreature.DrawStatic(spriteBatch,
+ new Point(mUnitRectangle.X + mUnitRectangle.Width / 2, mUnitRectangle.Y + mUnitRectangle.Height * 3 / 4));
+ }
+ }
+ spriteBatch.End();
+ }
+
+ private void UpdateButtons()
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ // Button Size
+ var buttonLength = viewport.Height / 12;
+ var buttonSize = new Vector2(buttonLength, buttonLength);
+ mButtons.ForEach(b => b.Size = buttonSize);
+
+ // Rectangles for the buttons
+ var buttonXVector = new Vector2(buttonLength + 2, 0);
+ mPauseButton.Position = new Vector2(5, 5);
+ mCameraButton.Position = new Vector2(mPauseButton.Position.X + mPauseButton.Size.X + 5, 5);
+ mButton1.Position = new Vector2(2, viewport.Height - buttonLength);
+ mButton2.Position = mButton1.Position + buttonXVector;
+ mButton3.Position = mButton2.Position + buttonXVector;
+ mButton4.Position = mButton3.Position + buttonXVector;
+ mButton5.Position = mButton4.Position + buttonXVector;
+
+ if (mPauseButton.IsClicked)
+ mMenuActions.OpenPauseScreen();
+
+ var mousePosition = Mouse.GetState().Position;
+ mButtons.ForEach(b => b.IsSelected = b.CheckSelected(mousePosition));
+ mButtons.ForEach(b => b.IsClicked = false);
+ }
+
+ private Button CreateButton(string textureName, string tooltipTitle, string tooltip)
+ {
+ return CreateButton(mContentManager.Load<Texture2D>(textureName), tooltipTitle, tooltip);
+ }
+
+ private Button CreateButton(Texture2D texture, string tooltipTitle, string tooltip)
+ {
+ var button = mWidgetFactory.CreateButton("");
+ button.Position = Vector2.Zero;
+ button.PaddingX = 15;
+ button.PaddingY = 5;
+ button.Size = button.GetMinimumSize();
+ button.Image = texture;
+ button.Tooltip = tooltip;
+ button.TooltipTitle = tooltipTitle;
+ mButtons.Add(button);
+ return button;
+ }
+ }
+}
diff --git a/V3/Screens/IDrawable.cs b/V3/Screens/IDrawable.cs
new file mode 100644
index 0000000..b940d94
--- /dev/null
+++ b/V3/Screens/IDrawable.cs
@@ -0,0 +1,19 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// An object that can be drawn using a sprite batch.
+ /// </summary>
+ public interface IDrawable
+ {
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ void Draw(GameTime gameTime, SpriteBatch spriteBatch);
+ }
+}
diff --git a/V3/Screens/IScreen.cs b/V3/Screens/IScreen.cs
new file mode 100644
index 0000000..197aaec
--- /dev/null
+++ b/V3/Screens/IScreen.cs
@@ -0,0 +1,39 @@
+using V3.Input;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// A screen that can be handled by a screen manager and that provides
+ /// information on how to deal with other screens below this one.
+ /// </summary>
+ public interface IScreen : IDrawable, IUpdateable
+ {
+ /// <summary>
+ /// Indicates whether screens below this one should be updated.
+ /// </summary>
+ bool UpdateLower { get; }
+
+ /// <summary>
+ /// Indicates whether screens below this one should be drawn.
+ /// </summary>
+ bool DrawLower { get; }
+
+ /// <summary>
+ /// Handles the given key event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="keyEvent">the key event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ bool HandleKeyEvent(IKeyEvent keyEvent);
+
+ /// <summary>
+ /// Handles the given mouse event and returns whether it should be passed
+ /// to the screens below this one.
+ /// </summary>
+ /// <param name="mouseEvent">the mouse event that occurred</param>
+ /// <returns>true if the event has been handeled by this screen and
+ /// should not be passed to the lower screens, false otherwise</returns>
+ bool HandleMouseEvent(IMouseEvent mouseEvent);
+ }
+}
diff --git a/V3/Screens/IScreenFactory.cs b/V3/Screens/IScreenFactory.cs
new file mode 100644
index 0000000..a2d844b
--- /dev/null
+++ b/V3/Screens/IScreenFactory.cs
@@ -0,0 +1,27 @@
+namespace V3.Screens
+{
+ public interface IScreenFactory
+ {
+ GameScreen CreateGameScreen();
+
+ HudScreen CreateHudScreen();
+
+ LoadScreen CreateLoadScreen();
+
+ MainScreen CreateMainScreen();
+
+ PauseScreen CreatePauseScreen();
+
+ OptionsScreen CreateOptionsScreen();
+
+ DeathScreen CreateDeathScreen();
+
+ VictoryScreen CreateVictoryScreen();
+
+ TechdemoScreen CreaTechdemoScreen();
+
+ StatisticsScreen CreateStatisticsScreen();
+
+ AchievementsScreen CreateAchievementsScreen();
+ }
+}
diff --git a/V3/Screens/IScreenManager.cs b/V3/Screens/IScreenManager.cs
new file mode 100644
index 0000000..e61c49c
--- /dev/null
+++ b/V3/Screens/IScreenManager.cs
@@ -0,0 +1,35 @@
+namespace V3.Screens
+{
+ /// <summary>
+ /// Handles screens using a screen stack. You can add screens to the
+ /// foreground using the AddScreen method, remove screens from the
+ /// foreground using the RemoveScreen method and remove all screens using
+ /// the Clear method. The top screen is always drawn and updated, and
+ /// it can decide whether lower screens should be drawn and/or updated
+ /// too. The screens are updated from top to bottom, and drawn from
+ /// bottom to top.
+ /// </summary>
+ public interface IScreenManager : IDrawable, IUpdateable
+ {
+ /// <summary>
+ /// Adds a screen to the foreground.
+ /// </summary>
+ /// <param name="screen">the screen to add in the foreground</param>
+ void AddScreen(IScreen screen);
+
+ /// <summary>
+ /// Removes the given screen if it is on the top of the screen stack.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ /// <returns>true if the screen was on top and has been removed,
+ /// false otherwise</returns>
+ void RemoveScreen(IScreen screen);
+
+ /// <summary>
+ /// Clears the screen stack.
+ /// </summary>
+ void Clear();
+
+ GameScreen GetGameScreen();
+ }
+}
diff --git a/V3/Screens/IUpdatable.cs b/V3/Screens/IUpdatable.cs
new file mode 100644
index 0000000..61972e7
--- /dev/null
+++ b/V3/Screens/IUpdatable.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// An object that can be updated.
+ /// </summary>
+ public interface IUpdateable
+ {
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ void Update(GameTime gameTime);
+ }
+}
diff --git a/V3/Screens/LoadScreen.cs b/V3/Screens/LoadScreen.cs
new file mode 100644
index 0000000..0e58147
--- /dev/null
+++ b/V3/Screens/LoadScreen.cs
@@ -0,0 +1,124 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using System.Collections.Generic;
+using V3.Data;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the load menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class LoadScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenu mMenu;
+ private readonly ISaveGameManager mSaveGameManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private SelectButton mButtonSaveGame;
+ private Button mButtonLoad;
+ private List<ISaveGame> mSaveGames;
+
+ /// <summary>
+ /// Creates a new load screen.
+ /// </summary>
+ public LoadScreen(ContentManager contentManager, FormMenu menu,
+ ISaveGameManager saveGameManager, MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenu = menu;
+ mSaveGameManager = saveGameManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mButtonLoad = mWidgetFactory.CreateButton("Laden");
+ mButtonSaveGame = mWidgetFactory.CreateSelectButton();
+ mSaveGames = mSaveGameManager.GetSaveGames();
+ foreach (var saveGame in mSaveGames)
+ {
+ mButtonSaveGame.Values.Add(GetSaveGameString(saveGame));
+ }
+ mButtonSaveGame.SelectedIndex = mSaveGames.Count - 1;
+
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Spielstand"));
+ mMenu.Widgets.Add(mButtonSaveGame);
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonLoad);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var backgroundRectangle = new Rectangle((int)mMenu.Position.X,
+ (int)mMenu.Position.Y, (int)mMenu.Size.X, (int)mMenu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonLoad.IsClicked)
+ {
+ var saveGame = mSaveGames[mButtonSaveGame.SelectedIndex];
+ mMenuActions.LoadGame(saveGame);
+ }
+
+ mMenu.Update();
+ }
+
+ private static string GetSaveGameString(ISaveGame saveGame)
+ {
+ return saveGame.Timestamp.ToString("s");
+ }
+ }
+}
diff --git a/V3/Screens/MainScreen.cs b/V3/Screens/MainScreen.cs
new file mode 100644
index 0000000..b7932bf
--- /dev/null
+++ b/V3/Screens/MainScreen.cs
@@ -0,0 +1,195 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the main menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class MainScreen : AbstractScreen, IInitializable
+ {
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IMenu mMenu;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly ContentManager mContentManager;
+
+ private Button mButtonStart;
+ private Button mButtonLoad;
+ private Button mButtonOptions;
+ private Button mButtonTechDemo;
+ private Button mButtonExit;
+ private Button mButtonStatistics;
+ private Button mButtonAchievements;
+
+ private Texture2D mTitleImage;
+ private Texture2D mBackgroundImage;
+ private Vector2 mImagePosition;
+ private Random mRandom;
+ private Vector2 mImageScrollDirection;
+ private Rectangle TitleImageBounds => mTitleImage.Bounds;
+ private Rectangle BackgroundImageBounds => mBackgroundImage.Bounds;
+ private Rectangle ScreenBounds => mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds;
+
+ /// <summary>
+ /// Creates a new main screen.
+ /// </summary>
+ public MainScreen(GraphicsDeviceManager graphicsDeviceManager,
+ VerticalMenu menu, MenuActions menuActions,
+ WidgetFactory widgetFactory, ContentManager contentManager) : base(false, false)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenu = menu;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mContentManager = contentManager;
+ }
+
+ public void Initialize()
+ {
+ mMenuActions.ApplyOptions();
+
+ mButtonStart = mWidgetFactory.CreateButton("Neues Spiel");
+ mButtonLoad = mWidgetFactory.CreateButton("Spiel laden");
+ mButtonLoad.IsEnabled = mMenuActions.CanLoadGame();
+ mButtonOptions = mWidgetFactory.CreateButton("Optionen");
+ mButtonStatistics = mWidgetFactory.CreateButton("Statistiken");
+ mButtonAchievements = mWidgetFactory.CreateButton("Erfolge");
+ mButtonTechDemo = mWidgetFactory.CreateButton("Tech-Demo");
+ mButtonExit = mWidgetFactory.CreateButton("Spiel schließen");
+
+ mMenu.Widgets.Add(mButtonStart);
+ mMenu.Widgets.Add(mButtonLoad);
+ mMenu.Widgets.Add(mButtonOptions);
+ mMenu.Widgets.Add(mButtonStatistics);
+ mMenu.Widgets.Add(mButtonAchievements);
+ mMenu.Widgets.Add(mButtonTechDemo);
+ mMenu.Widgets.Add(mButtonExit);
+
+ mTitleImage = mContentManager.Load<Texture2D>("Menu/Titel");
+ mBackgroundImage = mContentManager.Load<Texture2D>("Menu/mainscreen");
+ mRandom = new Random();
+ mImagePosition = RandomPosition();
+ mImageScrollDirection = RandomScrollDirection();
+ mButtonStart.BackgroundColor = Color.Red;
+ mButtonLoad.BackgroundColor = Color.Red;
+ mButtonOptions.BackgroundColor = Color.Red;
+ mButtonStatistics.BackgroundColor = Color.Red;
+ mButtonAchievements.BackgroundColor = Color.Red;
+ mButtonTechDemo.BackgroundColor = Color.Red;
+ mButtonExit.BackgroundColor = Color.Red;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonStart.IsClicked)
+ mMenuActions.StartNewGame();
+ else if (mButtonLoad.IsClicked)
+ mMenuActions.OpenLoadScreen();
+ else if (mButtonOptions.IsClicked)
+ mMenuActions.OpenOptionsScreen();
+ else if (mButtonTechDemo.IsClicked)
+ mMenuActions.OpenTechDemo();
+ else if (mButtonStatistics.IsClicked)
+ mMenuActions.OpenStatisticsScreen();
+ else if (mButtonAchievements.IsClicked)
+ mMenuActions.OpenAchievementsScreen();
+ else if (mButtonExit.IsClicked)
+ mMenuActions.Exit();
+
+ mMenu.Update();
+ UpdateBackgroundPosition();
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ spriteBatch.Draw(mBackgroundImage, mImagePosition, Color.White);
+ Point titleSize = new Point(ScreenBounds.Width * 9 / 10, ScreenBounds.Width * TitleImageBounds.Height * 9 / 10 / TitleImageBounds.Width);
+ Point titlePosition = new Point(ScreenBounds.Width / 2 - titleSize.X / 2, ScreenBounds.Height / 20);
+ spriteBatch.Draw(mTitleImage,
+ new Rectangle(titlePosition, titleSize),
+ Color.White);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ /// <summary>
+ /// Calculate a random section of the background image to show.
+ /// If the screen is larger than the image, just draw it to the upper left corner.
+ /// </summary>
+ /// <returns>A random Vector2 fitting the image to the screen.</returns>
+ private Vector2 RandomPosition()
+ {
+ Vector2 position;
+ try
+ {
+ var randomX = mRandom.Next(-BackgroundImageBounds.Width + ScreenBounds.Width, 0);
+ var randomY = mRandom.Next(-BackgroundImageBounds.Height + ScreenBounds.Height, 0);
+ position = new Vector2(randomX, randomY);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ position = Vector2.Zero;
+ }
+ return position;
+ }
+
+ private Vector2 RandomScrollDirection()
+ {
+ var imageScrollDirection = new Vector2((float)mRandom.NextDouble(), (float)mRandom.NextDouble());
+ imageScrollDirection.Normalize();
+ switch (mRandom.Next(4))
+ {
+ case 0:
+ return imageScrollDirection;
+ case 1:
+ return new Vector2(imageScrollDirection.X, -imageScrollDirection.Y);
+ case 2:
+ return new Vector2(-imageScrollDirection.X, imageScrollDirection.Y);
+ case 3:
+ return new Vector2(-imageScrollDirection.X, -imageScrollDirection.Y);
+ default:
+ return imageScrollDirection;
+ }
+ }
+
+ private void UpdateBackgroundPosition()
+ {
+ if (mImagePosition.X < 0 && mImagePosition.X > -BackgroundImageBounds.Width + ScreenBounds.Width
+ && mImagePosition.Y < 0 && mImagePosition.Y > -BackgroundImageBounds.Height + ScreenBounds.Height)
+ {
+ mImagePosition += mImageScrollDirection;
+ }
+ else
+ {
+ mImagePosition = RandomPosition();
+ mImageScrollDirection = RandomScrollDirection();
+ }
+ }
+ }
+}
diff --git a/V3/Screens/MenuActions.cs b/V3/Screens/MenuActions.cs
new file mode 100644
index 0000000..6de9e15
--- /dev/null
+++ b/V3/Screens/MenuActions.cs
@@ -0,0 +1,208 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Media;
+using V3.Camera;
+using V3.Data;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Provides common actions for the main and pause menu, like saving and
+ /// loading the game or removing and adding a screen to the screen stack.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class MenuActions
+ {
+ private readonly Game mGame;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IGameStateManager mGameStateManager;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly ISaveGameManager mSaveGameManager;
+ private readonly IScreenManager mScreenManager;
+ private readonly IScreenFactory mScreenFactory;
+ private readonly CameraManager mCameraManager;
+
+ /// <summary>
+ /// Creates a new menu actions instance.
+ /// </summary>
+ public MenuActions(Game game, GraphicsDeviceManager graphicsDeviceManager,
+ IGameStateManager gameStateManager, IOptionsManager optionsManager,
+ ISaveGameManager saveGameManager, IScreenManager screenManager,
+ IScreenFactory screenFactory, CameraManager cameraManager)
+ {
+ mGame = game;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mGameStateManager = gameStateManager;
+ mOptionsManager = optionsManager;
+ mSaveGameManager = saveGameManager;
+ mScreenManager = screenManager;
+ mScreenFactory = screenFactory;
+ mCameraManager = cameraManager;
+ }
+
+ /// <summary>
+ /// Checks whether it is possible to load a game, i. e. whether there
+ /// are save games.
+ /// </summary>
+ /// <returns>true if there are save games to load, false otherwise
+ /// </returns>
+ public bool CanLoadGame()
+ {
+ return mSaveGameManager.GetSaveGames().Count > 0;
+ }
+
+ /// <summary>
+ /// Removes the given screen from the screen manager.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ public void Close(IScreen screen)
+ {
+ // TODO: something like RemoveUntil
+ mScreenManager.RemoveScreen(screen);
+ }
+
+ /// <summary>
+ /// Quits the game.
+ /// </summary>
+ public void Exit()
+ {
+ mGame.Exit();
+ }
+
+ public void LoadGame(ISaveGame saveGame)
+ {
+ mScreenManager.Clear();
+ var gameScreen = mScreenFactory.CreateGameScreen();
+ var gameState = saveGame.GameState;
+ mGameStateManager.LoadGameState(gameState);
+ gameScreen.SetFog(gameState.mFog);
+ gameScreen.SetAiState(gameState.mAiState);
+ mScreenManager.AddScreen(gameScreen);
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ }
+
+ /// <summary>
+ /// Opens the main screen and removes all other screens.
+ /// </summary>
+ public void OpenMainScreen()
+ {
+ mScreenManager.Clear();
+ mScreenManager.AddScreen(mScreenFactory.CreateMainScreen());
+ }
+
+ /// <summary>
+ /// Opens the options screen in the front.
+ /// </summary>
+ public void OpenOptionsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateOptionsScreen());
+ }
+
+ public void OpenLoadScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateLoadScreen());
+ }
+
+ /// <summary>
+ /// Opens the pause screen in the front.
+ /// </summary>
+ public void OpenPauseScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreatePauseScreen());
+ }
+
+ /// <summary>
+ /// Opens the death screen in the front.
+ /// </summary>
+ public void OpenDeathScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateDeathScreen());
+ }
+
+ /// <summary>
+ /// Opens the victory screen in the front.
+ /// </summary>
+ public void OpenVictoryScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateVictoryScreen());
+ }
+
+ /// <summary>
+ /// Opens the statistics screen in the front.
+ /// </summary>
+ public void OpenStatisticsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateStatisticsScreen());
+ }
+
+ /// <summary>
+ /// Opens the achievements screen in the front.
+ /// </summary>
+ public void OpenAchievementsScreen()
+ {
+ mScreenManager.AddScreen(mScreenFactory.CreateAchievementsScreen());
+ }
+
+ /// <summary>
+ /// Opens the tech demo.
+ /// </summary>
+ public void OpenTechDemo()
+ {
+ mScreenManager.Clear();
+ mScreenManager.AddScreen(mScreenFactory.CreaTechdemoScreen());
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ }
+
+ /// <summary>
+ /// Creates a new save game from the current game state.
+ /// </summary>
+ public void SaveGame()
+ {
+ var gameState = mGameStateManager.GetGameState();
+ var gameScreen = mScreenManager.GetGameScreen();
+ if (gameScreen != null)
+ {
+ gameState.mFog = gameScreen.GetFog();
+ gameState.mAiState = gameScreen.GetAiState();
+ }
+ mSaveGameManager.CreateSaveGame(gameState);
+ }
+
+ /// <summary>
+ /// Apply the current graphics options.
+ /// </summary>
+ public void ApplyOptions()
+ {
+ mGraphicsDeviceManager.PreferredBackBufferWidth = mOptionsManager.Options.Resolution.X;
+ mGraphicsDeviceManager.PreferredBackBufferHeight = mOptionsManager.Options.Resolution.Y;
+ mGraphicsDeviceManager.IsFullScreen = mOptionsManager.Options.IsFullScreen;
+ mGraphicsDeviceManager.ApplyChanges();
+
+#if NO_AUDIO
+#else
+ MediaPlayer.Volume = mOptionsManager.Options.GetEffectiveVolume();
+#endif
+ }
+
+ /// <summary>
+ /// Save the current graphics options.
+ /// </summary>
+ public void SaveOptions()
+ {
+ mOptionsManager.SaveOptions();
+ }
+
+ /// <summary>
+ /// Opens the game screen with a new game.
+ /// </summary>
+ public void StartNewGame()
+ {
+ mScreenManager.Clear();
+ var gameScreen = mScreenFactory.CreateGameScreen();
+ gameScreen.CreateInitialPopulation();
+ mScreenManager.AddScreen(gameScreen);
+ mScreenManager.AddScreen(mScreenFactory.CreateHudScreen());
+ if (mCameraManager.GetCamera() is CameraScrolling)
+ mCameraManager.GetCamera().Location = Vector2.Zero;
+ }
+ }
+}
diff --git a/V3/Screens/OptionsScreen.cs b/V3/Screens/OptionsScreen.cs
new file mode 100644
index 0000000..b23e1fe
--- /dev/null
+++ b/V3/Screens/OptionsScreen.cs
@@ -0,0 +1,197 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Camera;
+using V3.Data;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the options menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class OptionsScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenu mMenu;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private SelectButton mButtonSize;
+ private SelectButton mButtonFullscreen;
+ private SelectButton mButtonCamera;
+ private SelectButton mButtonDebug;
+ private SelectButton mButtonMute;
+ private SelectButton mButtonVolume;
+ private Button mButtonApply;
+
+ /// <summary>
+ /// Creates a new options screen.
+ /// </summary>
+ public OptionsScreen(ContentManager contentManager, FormMenu menu,
+ IOptionsManager optionsManager, MenuActions menuActions,
+ WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenu = menu;
+ mOptionsManager = optionsManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mButtonApply = mWidgetFactory.CreateButton("Bestätigen");
+ mButtonSize = mWidgetFactory.CreateSelectButton();
+ foreach (var resolution in Options.Resolutions)
+ {
+ mButtonSize.Values.Add(GetResolutionString(resolution));
+ }
+ mButtonSize.SelectedIndex = Options.Resolutions.IndexOf(mOptionsManager.Options.Resolution);
+ mButtonFullscreen = mWidgetFactory.CreateSelectButton(new[] { "aus", "an" });
+ mButtonFullscreen.SelectedIndex = mOptionsManager.Options.IsFullScreen ? 1 : 0;
+ mButtonCamera = mWidgetFactory.CreateSelectButton();
+ Options.CameraTypes.ForEach(t => mButtonCamera.Values.Add(GetCameraTypeString(t)));
+ mButtonCamera.SelectedIndex = Options.CameraTypes.IndexOf(mOptionsManager.Options.CameraType);
+ mButtonDebug = mWidgetFactory.CreateSelectButton();
+ Options.DebugModes.ForEach(m => mButtonDebug.Values.Add(GetDebugModeString(m)));
+ mButtonDebug.SelectedIndex = Options.DebugModes.IndexOf(mOptionsManager.Options.DebugMode);
+ mButtonMute = mWidgetFactory.CreateSelectButton(new[] { "aus", "an" });
+ mButtonMute.SelectedIndex = mOptionsManager.Options.IsMuted ? 0 : 1;
+ mButtonVolume = mWidgetFactory.CreateSelectButton();
+ foreach (var volume in Options.Volumes)
+ {
+ mButtonVolume.Values.Add(GetVolumeString(volume));
+ }
+ mButtonVolume.SelectedIndex = Options.Volumes.IndexOf(mOptionsManager.Options.Volume);
+
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Auflösung"));
+ mMenu.Widgets.Add(mButtonSize);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Fullscreen"));
+ mMenu.Widgets.Add(mButtonFullscreen);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Kamera"));
+ mMenu.Widgets.Add(mButtonCamera);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Debug"));
+ mMenu.Widgets.Add(mButtonDebug);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Sound"));
+ mMenu.Widgets.Add(mButtonMute);
+ mMenu.Widgets.Add(mWidgetFactory.CreateLabel("Lautstärke"));
+ mMenu.Widgets.Add(mButtonVolume);
+ mMenu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ mMenu.Widgets.Add(mButtonApply);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var backgroundRectangle = new Rectangle((int)mMenu.Position.X,
+ (int)mMenu.Position.Y, (int)mMenu.Size.X, (int)mMenu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonApply.IsClicked)
+ {
+ UpdateOptions();
+ mMenuActions.SaveOptions();
+ mMenuActions.ApplyOptions();
+ }
+
+ mMenu.Update();
+ }
+
+ private void UpdateOptions()
+ {
+ mOptionsManager.Options.IsFullScreen = mButtonFullscreen.SelectedIndex != 0;
+ mOptionsManager.Options.DebugMode = Options.DebugModes[mButtonDebug.SelectedIndex];
+ mOptionsManager.Options.Resolution = Options.Resolutions[mButtonSize.SelectedIndex];
+ mOptionsManager.Options.CameraType = Options.CameraTypes[mButtonCamera.SelectedIndex];
+ mOptionsManager.Options.IsMuted = mButtonMute.SelectedIndex == 0;
+ mOptionsManager.Options.Volume = Options.Volumes[mButtonVolume.SelectedIndex];
+ }
+
+ private static string GetCameraTypeString(CameraType cameraType)
+ {
+ switch (cameraType)
+ {
+ case CameraType.Centered:
+ return "Zentriert";
+ case CameraType.Scrolling:
+ return "Schiebend";
+ default:
+ return "Unkown";
+ }
+ }
+
+ private static string GetDebugModeString(DebugMode debugMode)
+ {
+ switch (debugMode)
+ {
+ case DebugMode.Off:
+ return "Aus";
+ case DebugMode.Fps:
+ return "FPS Zähler";
+ case DebugMode.Full:
+ return "An";
+ default:
+ return "Unknown";
+ }
+ }
+
+ private static string GetResolutionString(Point resolution)
+ {
+ return $"{resolution.X}x{resolution.Y}";
+ }
+
+ private static string GetVolumeString(int volume)
+ {
+ return $"{volume} %";
+ }
+ }
+}
diff --git a/V3/Screens/PauseScreen.cs b/V3/Screens/PauseScreen.cs
new file mode 100644
index 0000000..e7b679f
--- /dev/null
+++ b/V3/Screens/PauseScreen.cs
@@ -0,0 +1,140 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the pause menu.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class PauseScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly IMenu mMenu;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private Button mButtonBack;
+ private Button mButtonSave;
+ private Button mButtonLoad;
+ private Button mButtonOptions;
+ private Button mButtonStatistics;
+ private Button mButtonAchievements;
+ private Button mButtonMain;
+ private Button mButtonExit;
+
+ /// <summary>
+ /// Creates a new pause screen.
+ /// </summary>
+ public PauseScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager, VerticalMenu menu,
+ MenuActions menuActions, WidgetFactory widgetFactory)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenu = menu;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück zum Spiel");
+ mButtonSave = mWidgetFactory.CreateButton("Spiel speichern");
+ mButtonLoad = mWidgetFactory.CreateButton("Spiel laden");
+ mButtonLoad.IsEnabled = mMenuActions.CanLoadGame();
+ mButtonStatistics = mWidgetFactory.CreateButton("Statistiken");
+ mButtonAchievements = mWidgetFactory.CreateButton("Erfolge");
+ mButtonOptions = mWidgetFactory.CreateButton("Optionen");
+ mButtonMain = mWidgetFactory.CreateButton("Hauptmenü");
+ mButtonExit = mWidgetFactory.CreateButton("Spiel schließen");
+
+ mMenu.Widgets.Add(mButtonBack);
+ mMenu.Widgets.Add(mButtonSave);
+ mMenu.Widgets.Add(mButtonLoad);
+ mMenu.Widgets.Add(mButtonOptions);
+ mMenu.Widgets.Add(mButtonStatistics);
+ mMenu.Widgets.Add(mButtonAchievements);
+ mMenu.Widgets.Add(mButtonMain);
+ mMenu.Widgets.Add(mButtonExit);
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ mMenu.HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, mGraphicsDeviceManager.GraphicsDevice.Viewport.Bounds, Color.Black * 0.7f);
+ spriteBatch.End();
+
+ mMenu.Draw(spriteBatch);
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ else if (mButtonSave.IsClicked)
+ {
+ mMenuActions.SaveGame();
+ mMenuActions.Close(this);
+ }
+ else if (mButtonLoad.IsClicked)
+ {
+ mMenuActions.OpenLoadScreen();
+ }
+ else if (mButtonOptions.IsClicked)
+ {
+ mMenuActions.OpenOptionsScreen();
+ }
+ else if (mButtonStatistics.IsClicked)
+ {
+ mMenuActions.OpenStatisticsScreen();
+ }
+ else if (mButtonAchievements.IsClicked)
+ {
+ mMenuActions.OpenAchievementsScreen();
+ }
+ else if (mButtonMain.IsClicked)
+ {
+ mMenuActions.OpenMainScreen();
+ }
+ else if (mButtonExit.IsClicked)
+ {
+ mMenuActions.Exit();
+ }
+
+ mMenu.Update();
+ }
+ }
+}
diff --git a/V3/Screens/ScreenManager.cs b/V3/Screens/ScreenManager.cs
new file mode 100644
index 0000000..97c4e28
--- /dev/null
+++ b/V3/Screens/ScreenManager.cs
@@ -0,0 +1,164 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Media;
+using V3.Input;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// Default implementation of IScreenManager.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ internal sealed class ScreenManager : IScreenManager
+ {
+ private readonly IInputManager mInputManager;
+ private readonly DebugScreen mDebugScreen;
+ private readonly ContentManager mContentManager;
+
+ private readonly Stack<IScreen> mScreens = new Stack<IScreen>();
+
+ /// <summary>
+ /// Creates a new screen manager using the given input manager.
+ /// </summary>
+ public ScreenManager(IInputManager inputManager, DebugScreen debugScreen, ContentManager contentManager)
+ {
+ mInputManager = inputManager;
+ mDebugScreen = debugScreen;
+ mContentManager = contentManager;
+ }
+
+ /// <summary>
+ /// Adds a screen to the foreground.
+ /// </summary>
+ /// <param name="screen">the screen to add in the foreground</param>
+ public void AddScreen(IScreen screen)
+ {
+ mScreens.Push(screen);
+#if NO_AUDIO
+#else
+ if (screen is MainScreen)
+ {
+ MediaPlayer.IsRepeating = true;
+ MediaPlayer.Play(mContentManager.Load<Song>("Sounds/Kosta_T_-_06"));
+ }
+ else if (screen is GameScreen)
+ {
+ MediaPlayer.IsRepeating = true;
+ MediaPlayer.Play(mContentManager.Load<Song>("Sounds/Afraid_to_Go"));
+
+ }
+ //else if (screen is PauseScreen)
+ //{
+ // mAbstractCreature.GetSelf();
+ // mAbstractCreature.mSoundEffectInstance.Stop();
+ // mAbstractCreature.mSoundEffectInstanceFight.Stop();
+ // mAbstractCreature.mSoundEffectInstanceHorse.Stop();
+ // mAbstractCreature.mSoundEffectInstanceKnight.Stop();
+ // mAbstractCreature.mSoundEffectInstanceMeatball.Stop();
+ //}
+#endif
+ }
+
+ /// <summary>
+ /// Removes the given screen if it is on the top of the screen stack.
+ /// </summary>
+ /// <param name="screen">the screen to remove</param>
+ public void RemoveScreen(IScreen screen)
+ {
+ if (mScreens.Count > 0 && screen.Equals(mScreens.Peek()))
+ {
+ mScreens.Pop();
+ }
+ }
+
+ /// <summary>
+ /// Clears the screen stack.
+ /// </summary>
+ public void Clear()
+ {
+ mScreens.Clear();
+ }
+
+ /// <summary>
+ /// Draws the top screen and, if enabled, the lower screens. The draw
+ /// order is from bottom to top, i. e. the lowest enabled screen is
+ /// drawn first, and the top screen is drawn last.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// </param>
+ public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var drawScreens = new Stack<IScreen>();
+ foreach (var screen in mScreens)
+ {
+ drawScreens.Push(screen);
+ if (!screen.DrawLower)
+ break;
+ }
+
+ foreach (var screen in drawScreens)
+ {
+ screen.Draw(gameTime, spriteBatch);
+ }
+ mDebugScreen.Draw(gameTime, spriteBatch);
+ }
+
+ /// <summary>
+ /// Updates the top screen and, if enabled, the lower screens. The
+ /// update order is from top to bottom, i. e. the lowest enabled screen
+ /// is drawn first, and the top screen is drawn last.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public void Update(GameTime gameTime)
+ {
+ mInputManager.Update();
+ HandleInputEvents();
+
+ mDebugScreen.Update(gameTime);
+ var currentScreens = new Stack<IScreen>(new Stack<IScreen>(mScreens));
+ foreach (var screen in currentScreens)
+ {
+ screen.Update(gameTime);
+ if (!screen.UpdateLower)
+ break;
+ }
+ }
+
+ private void HandleInputEvents()
+ {
+ // We need to clone the stack as the input management methods
+ // might want to modify the screen stack. We need two stacks as
+ // each stack reverses the order.
+ var currentScreens = new Stack<IScreen>(new Stack<IScreen>(mScreens));
+ foreach (var keyEvent in mInputManager.KeyEvents)
+ {
+ foreach (var screen in currentScreens)
+ {
+ if (screen.HandleKeyEvent(keyEvent))
+ break;
+ }
+ }
+ foreach (var mouseEvent in mInputManager.MouseEvents)
+ {
+ foreach (var screen in currentScreens)
+ {
+ if (screen.HandleMouseEvent(mouseEvent))
+ break;
+ }
+ }
+ }
+
+ public GameScreen GetGameScreen()
+ {
+ foreach (var screen in mScreens)
+ {
+ if (screen is GameScreen)
+ return (GameScreen) screen;
+ }
+ return null;
+ }
+ }
+}
diff --git a/V3/Screens/StatisticsScreen.cs b/V3/Screens/StatisticsScreen.cs
new file mode 100644
index 0000000..90e0ff4
--- /dev/null
+++ b/V3/Screens/StatisticsScreen.cs
@@ -0,0 +1,186 @@
+using System.Collections.Generic;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Input;
+using V3.Widgets;
+
+namespace V3.Screens
+{
+ /// <summary>
+ /// The screen for the statistics.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class StatisticsScreen : AbstractScreen, IInitializable
+ {
+ private readonly ContentManager mContentManager;
+ private readonly IMenuFactory mMenuFactory;
+ private readonly MenuActions mMenuActions;
+ private readonly WidgetFactory mWidgetFactory;
+ private readonly AchievementsAndStatistics mAchievementsAndStatistics;
+
+ private Button mButtonBack;
+ private SelectButton mSelectMission;
+
+ private List<IMenu> mMenuList = new List<IMenu>();
+ private Texture2D mRectangle;
+
+ public StatisticsScreen(ContentManager contentManager, MenuActions menuActions, WidgetFactory widgetFactory,
+ IMenuFactory menuFactory, AchievementsAndStatistics achievementsAndStatistics)
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mMenuActions = menuActions;
+ mWidgetFactory = widgetFactory;
+ mMenuFactory = menuFactory;
+ mAchievementsAndStatistics = achievementsAndStatistics;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+
+ mButtonBack = mWidgetFactory.CreateButton("Zurück");
+ mSelectMission = mWidgetFactory.CreateSelectButton();
+
+ var menu = mMenuFactory.CreateFormMenu();
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:T}",mAchievementsAndStatistics.mUsedTime)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mKilledCreatures)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mLostServants)));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + string.Format("{0:00000}", mAchievementsAndStatistics.mWalkedDistance) + " m"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ for (var i = 0; i < mMenuList.Count; i++)
+ mSelectMission.Values.Add($"Mission {i + 1}");
+
+ menu = mMenuFactory.CreateFormMenu();
+
+ mMenuList.Add(menu);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Auswahl"));
+ menu.Widgets.Add(mSelectMission);
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Zeit"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "00:00:00"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Getötete Gegner"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("Verlorene Einheiten"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateLabel("zurückgelegte Entfernung"));
+ menu.Widgets.Add(mWidgetFactory.CreateLabel(" " + "000000"));
+
+ menu.Widgets.Add(mWidgetFactory.CreateEmptyWidget());
+ menu.Widgets.Add(mButtonBack);
+
+ mSelectMission.Values.Add("Gesamt");
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down && keyEvent.Key == Keys.Escape)
+ mMenuActions.Close(this);
+
+ return true;
+ }
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ GetCurrentMenu().HandleMouseEvent(mouseEvent);
+ return true;
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ if (mButtonBack.IsClicked)
+ {
+ mMenuActions.Close(this);
+ }
+ GetCurrentMenu().Update();
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ var menu = GetCurrentMenu();
+ var backgroundRectangle = new Rectangle((int)menu.Position.X,
+ (int)menu.Position.Y, (int)menu.Size.X, (int)menu.Size.Y);
+ backgroundRectangle.X -= 30;
+ backgroundRectangle.Y -= 30;
+ backgroundRectangle.Width += 60;
+ backgroundRectangle.Height += 60;
+
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle, backgroundRectangle, Color.LightGray);
+ spriteBatch.End();
+
+ GetCurrentMenu().Draw(spriteBatch);
+ }
+
+ private IMenu GetCurrentMenu()
+ {
+ return mMenuList[mSelectMission.SelectedIndex];
+ }
+ }
+}
diff --git a/V3/Screens/TechdemoScreen.cs b/V3/Screens/TechdemoScreen.cs
new file mode 100644
index 0000000..748c9a1
--- /dev/null
+++ b/V3/Screens/TechdemoScreen.cs
@@ -0,0 +1,325 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using Ninject;
+using V3.Camera;
+using V3.Data;
+using V3.Effects;
+using V3.Input;
+using V3.Map;
+using V3.Objects;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class TechdemoScreen : AbstractScreen, IInitializable
+ {
+ private enum Creatures
+ {
+ Empty, Zombie, Skeleton, Peasant, Knight
+ }
+
+ private Creatures mCreatePerKey;
+
+ private readonly CameraManager mCameraManager;
+ private readonly ContentManager mContentManager;
+ private readonly CreatureFactory mCreatureFactory;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+ private readonly MenuActions mMenuActions;
+ private readonly IOptionsManager mOptionsManager;
+ private readonly IEffectsManager mEffectsManager;
+ private readonly Selection mSelection;
+ private readonly Transformation mTransformation;
+ private readonly IMapManager mMapManager;
+ private readonly IObjectsManager mObjectsManager;
+ private readonly Pathfinder mPathfinder;
+ private readonly Texture2D mOnePixelTexture;
+ // Fields for handling mouse input.
+ private Point mInitialClickPosition;
+ private bool mLeftButtonPressed;
+ private bool mRightButtonPressed;
+ private Vector2 mRightButtonPosition;
+
+ /// <summary>
+ /// Creates a new game screen.
+ /// </summary>
+ public TechdemoScreen(IOptionsManager optionsManager, CameraManager cameraManager,
+ ContentManager contentManager, CreatureFactory creatureFactory,
+ GraphicsDeviceManager graphicsDeviceManager, IMapManager mapManager,
+ MenuActions menuActions, IObjectsManager objectsManager,
+ Pathfinder pathfinder, Selection selection, Transformation transformation,
+ IEffectsManager effectsManager) : base(false, false)
+ {
+ mMapManager = mapManager;
+ mObjectsManager = objectsManager;
+ mCameraManager = cameraManager;
+ mOptionsManager = optionsManager;
+ mContentManager = contentManager;
+ mCreatureFactory = creatureFactory;
+ mEffectsManager = effectsManager;
+ mTransformation = transformation;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ mMenuActions = menuActions;
+ mPathfinder = pathfinder;
+ mSelection = selection;
+ mOnePixelTexture = contentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ }
+
+
+ public override bool HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mLeftButtonPressed)
+ {
+ mLeftButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ else
+ {
+ if (mLeftButtonPressed)
+ {
+ mSelection.Select(mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(),
+ mouseEvent.PositionReleased.GetValueOrDefault() + mCameraManager.GetCamera().Location.ToPoint());
+ mLeftButtonPressed = false;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Pressed)
+ {
+ if (!mRightButtonPressed)
+ {
+ mRightButtonPressed = true;
+ mInitialClickPosition = mouseEvent.PositionPressed;
+ }
+ }
+ if (mouseEvent.MouseButton == MouseButton.Right && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (mouseEvent.PositionReleased != null && mouseEvent.ReleasedOnScreen)
+ {
+ mRightButtonPressed = false;
+ mRightButtonPosition = mouseEvent.PositionReleased.Value.ToVector2() + mCameraManager.GetCamera().Location;
+ mSelection.Move(mouseEvent.PositionReleased.Value.ToVector2()
+ + mCameraManager.GetCamera().Location);
+ }
+ }
+ if (mouseEvent.ReleasedOnScreen)
+ {
+ var position = mouseEvent.PositionPressed.ToVector2() + mCameraManager.GetCamera().Location;
+ switch (mCreatePerKey)
+ {
+ case Creatures.Zombie:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateZombie(position, MovementDirection.S));
+ break;
+ case Creatures.Skeleton:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateSkeleton(position, MovementDirection.S));
+ break;
+ case Creatures.Peasant:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateMalePeasant(position, MovementDirection.S));
+ break;
+ case Creatures.Knight:
+ mObjectsManager.CreateCreature(mCreatureFactory.CreateKnight(position, MovementDirection.S));
+ break;
+ }
+ }
+ return true;
+ }
+
+ public void Initialize()
+ {
+ mMapManager.Load("techdemo");
+ mObjectsManager.Initialize(mMapManager);
+ mCameraManager.Initialize(mMapManager.SizeInPixel);
+ mPathfinder.LoadGrid(mMapManager.GetPathfindingGrid());
+ mTransformation.mSelection = mSelection;
+ mTransformation.LoadArea(mContentManager);
+
+ // Add creatures for testing purposes.
+ var necromancer = mCreatureFactory.CreateNecromancer(new Vector2(1592, 1609), MovementDirection.S);
+ var king = mCreatureFactory.CreateKing(new Vector2(1500, 200), MovementDirection.S);
+ mObjectsManager.CreatePlayerCharacter(necromancer);
+ mObjectsManager.CreateBoss(king);
+ int makeCreatureStrongerModifier = 100;
+ int makePeasantsNotSoMuchStrongerModifier = 10;
+ AddCreature<MalePeasant>(new Point(4), new Point(200, 2000), new Point(2800, 3000), makePeasantsNotSoMuchStrongerModifier);
+ AddCreature<FemalePeasant>(new Point(4), new Point(232, 2032), new Point(2800, 3000), makePeasantsNotSoMuchStrongerModifier);
+ AddCreature<Knight>(new Point(4), new Point(200, 200), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<KingsGuard>(new Point(4), new Point(232, 232), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Zombie>(new Point(3), new Point(264, 264), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Skeleton>(new Point(3), new Point(290, 290), new Point(2800, 1000), makeCreatureStrongerModifier);
+ AddCreature<Zombie>(new Point(4), new Point(264, 2064), new Point(2800, 3000), makeCreatureStrongerModifier);
+
+ mTransformation.ObjectsManager = mObjectsManager;
+ }
+
+ /// <summary>
+ /// Add a generic creature to Techdemo.
+ /// Using pixel coordinates for calculating start and end point of map.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="density">Density on X and Y axis. More creatures when lower.</param>
+ /// <param name="start">Start point for creating the creature batallion.</param>
+ /// <param name="end">At which point to stop creating creatures.</param>
+ /// <param name="makeStronger">Give more HP. Better for testing.</param>
+ private void AddCreature<T>(Point density, Point start, Point end, int makeStronger) where T:ICreature
+ {
+ var type = typeof(T);
+ for (int i = start.Y; i < end.Y; i += 32 * density.Y)
+ {
+ for (int j = start.X; j < end.X; j += 32 * density.X)
+ {
+ Vector2 position = new Vector2(j, i);
+ ICreature creature;
+ if (type == typeof(MalePeasant))
+ {
+ creature = mCreatureFactory.CreateMalePeasant(position, MovementDirection.S);
+ }
+ else if (type == typeof(FemalePeasant))
+ {
+ creature = mCreatureFactory.CreateFemalePeasant(position, MovementDirection.S);
+ }
+ else if (type == typeof(Skeleton))
+ {
+ creature = mCreatureFactory.CreateSkeleton(position, MovementDirection.S);
+ }
+ else if (type == typeof(Zombie))
+ {
+ creature = mCreatureFactory.CreateZombie(position, MovementDirection.S);
+ }
+ else if (type == typeof(Knight))
+ {
+ creature = mCreatureFactory.CreateKnight(position, MovementDirection.S);
+ }
+ else if (type == typeof(SkeletonHorse))
+ {
+ creature = mCreatureFactory.CreateSkeletonHorse(position, MovementDirection.S);
+ }
+ else if (type == typeof(Meatball))
+ {
+ creature = mCreatureFactory.CreateMeatball(position, MovementDirection.S);
+ }
+ else if (type == typeof(KingsGuard))
+ {
+ creature = mCreatureFactory.CreateKingsGuard(position, MovementDirection.S);
+ }
+ else if (type == typeof(SkeletonElite))
+ {
+ creature = mCreatureFactory.CreateSkeletonElite(position, MovementDirection.S);
+ }
+ else
+ {
+ creature = mCreatureFactory.CreateZombie(position, MovementDirection.S);
+ }
+ creature.Empower(makeStronger);
+ mObjectsManager.CreateCreature(creature);
+ }
+ }
+
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ if (keyEvent.KeyState == KeyState.Down)
+ {
+ Creatures createPerKey = Creatures.Empty;
+ switch (keyEvent.Key)
+ {
+ case Keys.Escape:
+ mMenuActions.OpenPauseScreen();
+ break;
+ case Keys.F1:
+ createPerKey = Creatures.Zombie;
+ break;
+ case Keys.F2:
+ createPerKey = Creatures.Skeleton;
+ break;
+ case Keys.F3:
+ createPerKey = Creatures.Peasant;
+ break;
+ case Keys.F4:
+ createPerKey = Creatures.Knight;
+ break;
+ case Keys.F5:
+ Rectangle cameraRectangle = mCameraManager.GetCamera().ScreenRectangle;
+ mEffectsManager.PlayOnce(new SmokeBig(), cameraRectangle.Center, cameraRectangle.Size);
+ mObjectsManager.ExposeTheLiving();
+ break;
+ case Keys.F6:
+ (mObjectsManager.PlayerCharacter as Necromancer)?.ChangeSex();
+ mEffectsManager.PlayOnce(new SmokeSmall(), mObjectsManager.PlayerCharacter.Position.ToPoint(), new Point(256));
+ break;
+ }
+ if (createPerKey == mCreatePerKey)
+ {
+ mCreatePerKey = Creatures.Empty;
+ }
+ else
+ {
+ mCreatePerKey = createPerKey;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Updates the status of this object.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ public override void Update(GameTime gameTime)
+ {
+#if DEBUG
+#else
+ try
+ {
+#endif
+ mObjectsManager.Update(gameTime, mRightButtonPressed, mRightButtonPosition, mCameraManager.GetCamera());
+ mCameraManager.Update(mObjectsManager.PlayerCharacter);
+ mEffectsManager.Update(gameTime);
+
+ // Call for Transformations
+ mTransformation.Transform();
+
+ mSelection.UpdateSelection();
+#if DEBUG
+#else
+ }
+ catch (System.Exception e)
+ {
+ System.Console.WriteLine(e.Message);
+ }
+#endif
+ }
+
+ /// <summary>
+ /// Draws this object using the given sprite batch.
+ /// </summary>
+ /// <param name="gameTime">a snapshot of the game time</param>
+ /// <param name="spriteBatch">the sprite batch to use for drawing
+ /// this object</param>
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ mGraphicsDeviceManager.GraphicsDevice.Clear(Color.Black);
+ spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, mCameraManager.GetCamera().Transform);
+ mMapManager.DrawFloor(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.Draw(spriteBatch, mCameraManager.GetCamera());
+ mTransformation.DrawNecroArea(spriteBatch);
+ mEffectsManager.Draw(spriteBatch);
+ if (mLeftButtonPressed)
+ mSelection.Draw(spriteBatch, mInitialClickPosition + mCameraManager.GetCamera().Location.ToPoint(), Mouse.GetState().Position + mCameraManager.GetCamera().Location.ToPoint());
+ if (mOptionsManager.Options.DebugMode == DebugMode.Full)
+ {
+ mMapManager.DrawPathfindingGrid(spriteBatch, mCameraManager.GetCamera());
+ mObjectsManager.DrawQuadtree(spriteBatch);
+ DrawLastSelection(spriteBatch, mSelection.LastSelection);
+ }
+ // Draws the selection rectangle when left mouse button is pressed.
+ spriteBatch.End();
+ }
+
+ private void DrawLastSelection(SpriteBatch spriteBatch, Rectangle selection)
+ {
+ spriteBatch.Draw(mOnePixelTexture, selection, new Color(Color.Black, 100));
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/Screens/VictoryScreen.cs b/V3/Screens/VictoryScreen.cs
new file mode 100644
index 0000000..ffb2406
--- /dev/null
+++ b/V3/Screens/VictoryScreen.cs
@@ -0,0 +1,76 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Input;
+
+namespace V3.Screens
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class VictoryScreen : AbstractScreen, IInitializable
+ {
+ private static TimeSpan sTotalDelay = TimeSpan.FromSeconds(1);
+
+ private readonly ContentManager mContentManager;
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ private Rectangle mVictoryRectangle;
+ private Vector2 mFontCenter;
+ private Vector2 mCenter;
+ private SpriteFont mVictoryFont;
+ private Texture2D mRectangle;
+
+ private TimeSpan mDelayTimer = sTotalDelay;
+
+ /// <summary>
+ /// Creates a victory screen if the player defeats the boss enemy.
+ /// </summary>
+
+ public VictoryScreen(ContentManager contentManager,
+ GraphicsDeviceManager graphicsDeviceManager
+ )
+ : base(false, true)
+ {
+ mContentManager = contentManager;
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public override bool HandleKeyEvent(IKeyEvent keyEvent)
+ {
+ return false;
+ }
+
+ public void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mVictoryFont = mContentManager.Load<SpriteFont>("Fonts/VictoryFont");
+ }
+
+ public override void Update(GameTime gameTime)
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ if (mDelayTimer > TimeSpan.Zero)
+ mDelayTimer -= gameTime.ElapsedGameTime;
+
+ mCenter = new Vector2(viewport.Width / 2f, viewport.Height / 2f);
+ mFontCenter = mVictoryFont.MeasureString("Die Rache ist euer!") / 2;
+ mVictoryRectangle = new Rectangle(0, (int)mCenter.Y - (int)mFontCenter.Y - 10, viewport.Width, (int)mFontCenter.Y * 2 - 10);
+ }
+
+ public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
+ {
+ float displayRatio = (float)(1 - mDelayTimer.TotalMilliseconds / sTotalDelay.TotalMilliseconds);
+ spriteBatch.Begin();
+ spriteBatch.Draw(mRectangle,
+ mVictoryRectangle,
+ Color.Black * 0.8f * displayRatio);
+
+ spriteBatch.DrawString(mVictoryFont,
+ "Die Rache ist euer!",
+ mCenter,
+ Color.DarkGoldenrod * displayRatio, 0, mFontCenter, 1.0f, SpriteEffects.None, 0.5f);
+ spriteBatch.End();
+ }
+ }
+ }
diff --git a/V3/UpdatesPerSecond.cs b/V3/UpdatesPerSecond.cs
new file mode 100644
index 0000000..e180be1
--- /dev/null
+++ b/V3/UpdatesPerSecond.cs
@@ -0,0 +1,55 @@
+using Microsoft.Xna.Framework;
+
+namespace V3
+{
+ /// <summary>
+ /// A class for telling when to do an update for events with a constant frequency.
+ /// </summary>
+ public sealed class UpdatesPerSecond
+ {
+ private double mTimeBetweenUpdates;
+ private double mTimeSinceLastUpdate;
+
+ /// <summary>
+ /// Initializes class.
+ /// </summary>
+ /// <param name="frequency">How many updates should be done per second.</param>
+ public UpdatesPerSecond(double frequency)
+ {
+ ChangeFrequency(frequency);
+ }
+
+ /// <summary>
+ /// Change update frequency.
+ /// </summary>
+ /// <param name="frequency">How many updates should be done per second.</param>
+ private void ChangeFrequency(double frequency)
+ {
+ mTimeBetweenUpdates = 1d / frequency;
+ }
+
+ /// <summary>
+ /// Tells if it is time to do an update.
+ /// </summary>
+ /// <param name="gameTime">Current game time.</param>
+ /// <returns></returns>
+ public bool IsItTime(GameTime gameTime)
+ {
+ mTimeSinceLastUpdate += gameTime.ElapsedGameTime.TotalSeconds;
+ if (mTimeSinceLastUpdate >= mTimeBetweenUpdates)
+ {
+ Reset();
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Reset internal timer for checking if update should be done.
+ /// </summary>
+ private void Reset()
+ {
+ mTimeSinceLastUpdate = 0;
+ }
+ }
+} \ No newline at end of file
diff --git a/V3/V3.csproj b/V3/V3.csproj
new file mode 100644
index 0000000..56f7461
--- /dev/null
+++ b/V3/V3.csproj
@@ -0,0 +1,498 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{4C475FE4-ECB6-4D25-B7E6-0B773A736A24}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>V3</RootNamespace>
+ <AssemblyName>V3</AssemblyName>
+ <FileAlignment>512</FileAlignment>
+ <MonoGamePlatform>DesktopGL</MonoGamePlatform>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;LINUX</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\</OutputPath>
+ <DefineConstants>TRACE;LINUX NEWMAP</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>Icon.ico</ApplicationIcon>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Pool|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Pool\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;LINUX NO_AUDIO</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>true</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Feature|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Feature\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;LINUX TEST_OBJECTSMANAGER NEW_MUSIC</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>true</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Old Debug|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Old Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;LINUX</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>true</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Larger Map|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Larger Map\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;LINUX NEWMAP</DefineConstants>
+ <DebugType>full</DebugType>
+ <PlatformTarget>x86</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>true</Prefer32Bit>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="AchievementsAndStatistics.cs" />
+ <Compile Include="Bindings.cs" />
+ <Compile Include="AI\ActionState.cs" />
+ <Compile Include="AI\AiState.cs" />
+ <Compile Include="AI\IAction.cs" />
+ <Compile Include="AI\IAiPlayer.cs" />
+ <Compile Include="AI\IStrategy.cs" />
+ <Compile Include="AI\IWorldView.cs" />
+ <Compile Include="AI\Internal\AbstractAction.cs" />
+ <Compile Include="AI\Internal\AiPlayer.cs" />
+ <Compile Include="AI\Internal\AttackStrategy.cs" />
+ <Compile Include="AI\Internal\IActionFactory.cs" />
+ <Compile Include="AI\Internal\MoveAction.cs" />
+ <Compile Include="AI\Internal\SpawnAction.cs" />
+ <Compile Include="AI\Internal\WorldView.cs" />
+ <Compile Include="Camera\CameraCentered.cs" />
+ <Compile Include="Camera\CameraManager.cs" />
+ <Compile Include="Camera\CameraScrolling.cs" />
+ <Compile Include="Camera\CameraType.cs" />
+ <Compile Include="Camera\ICamera.cs" />
+ <Compile Include="Data\DebugMode.cs" />
+ <Compile Include="Data\IGameStateManager.cs" />
+ <Compile Include="Data\Internal\GameStateManager.cs" />
+ <Compile Include="Data\Internal\OptionsManager.cs" />
+ <Compile Include="Data\Internal\PathManager.cs" />
+ <Compile Include="Data\Internal\SaveGame.cs" />
+ <Compile Include="Data\Internal\SaveGameManager.cs" />
+ <Compile Include="Data\IOptionsManager.cs" />
+ <Compile Include="Data\Options.cs" />
+ <Compile Include="Data\ISaveGame.cs" />
+ <Compile Include="Data\ISaveGameManager.cs" />
+ <Compile Include="Effects\AbstractEffect.cs" />
+ <Compile Include="Effects\BloodBang.cs" />
+ <Compile Include="Effects\BloodExplosion.cs" />
+ <Compile Include="Effects\BloodFountain.cs" />
+ <Compile Include="Effects\EffectsManager.cs" />
+ <Compile Include="Effects\Explosion.cs" />
+ <Compile Include="Effects\HorseEffect.cs" />
+ <Compile Include="Effects\IEffect.cs" />
+ <Compile Include="Effects\IEffectsManager.cs" />
+ <Compile Include="Effects\Quake.cs" />
+ <Compile Include="Effects\SmokeBig.cs" />
+ <Compile Include="Effects\SmokeMedium.cs" />
+ <Compile Include="Effects\SmokeSmall.cs" />
+ <Compile Include="Ellipse.cs" />
+ <Compile Include="Faction.cs" />
+ <Compile Include="Input\IInputManager.cs" />
+ <Compile Include="Input\IKeyEvent.cs" />
+ <Compile Include="Input\IMouseEvent.cs" />
+ <Compile Include="Data\IPathManager.cs" />
+ <Compile Include="Input\Internal\InputManager.cs" />
+ <Compile Include="Input\Internal\KeyEvent.cs" />
+ <Compile Include="Input\Internal\MouseEvent.cs" />
+ <Compile Include="Map\AbstractLayer.cs" />
+ <Compile Include="Map\Area.cs" />
+ <Compile Include="Map\Constants.cs" />
+ <Compile Include="Map\FloorLayer.cs" />
+ <Compile Include="Map\FogOfWar.cs" />
+ <Compile Include="Map\IMapManager.cs" />
+ <Compile Include="Map\MapManager.cs" />
+ <Compile Include="Map\ObjectLayer.cs" />
+ <Compile Include="Map\Pathfinder.cs" />
+ <Compile Include="Map\PathfindingGrid.cs" />
+ <Compile Include="Map\SearchNode.cs" />
+ <Compile Include="Map\TiledParser.cs" />
+ <Compile Include="Map\Tileset.cs" />
+ <Compile Include="Node.cs" />
+ <Compile Include="Objects\AbstractBuilding.cs" />
+ <Compile Include="Objects\Arrow.cs" />
+ <Compile Include="Objects\BuildingState.cs" />
+ <Compile Include="Objects\Castle.cs" />
+ <Compile Include="Objects\FemalePeasant.cs" />
+ <Compile Include="Objects\Forge.cs" />
+ <Compile Include="Objects\IBuilding.cs" />
+ <Compile Include="Objects\IObjectsManager.cs" />
+ <Compile Include="Objects\King.cs" />
+ <Compile Include="Objects\KingsGuard.cs" />
+ <Compile Include="Objects\Knight.cs" />
+ <Compile Include="Objects\Meatball.cs" />
+ <Compile Include="Objects\Movement\CountStepsMovement.cs" />
+ <Compile Include="Objects\Movement\IMovable.cs" />
+ <Compile Include="Objects\Movement\PlayerMovement.cs" />
+ <Compile Include="Objects\ObjectsManager.cs" />
+ <Compile Include="Objects\Prince.cs" />
+ <Compile Include="Objects\IdGenerator.cs" />
+ <Compile Include="Objects\Selection.cs" />
+ <Compile Include="Objects\SkeletonElite.cs" />
+ <Compile Include="Objects\SkeletonHorse.cs" />
+ <Compile Include="Objects\Sprite\ArrowSprite.cs" />
+ <Compile Include="Objects\Sprite\BucklerFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\BucklerSprite.cs" />
+ <Compile Include="Objects\Sprite\ChainFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\ChainSprite.cs" />
+ <Compile Include="Objects\Sprite\ClothFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\ClothSprite.cs" />
+ <Compile Include="Objects\Sprite\EquipmentType.cs" />
+ <Compile Include="Objects\Sprite\HeadBaldSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadChainFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadChainSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadPlateFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadPlateSprite.cs" />
+ <Compile Include="Objects\Sprite\HeadSprite.cs" />
+ <Compile Include="Objects\Sprite\KingSprite.cs" />
+ <Compile Include="Objects\Sprite\LongswordFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\LongswordSprite.cs" />
+ <Compile Include="Objects\Sprite\MeatballSprite.cs" />
+ <Compile Include="Objects\Sprite\NecromancerFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\NudeFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\NudeSprite.cs" />
+ <Compile Include="Objects\Sprite\PlateFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\PlateSprite.cs" />
+ <Compile Include="Objects\Sprite\PrinceSprite.cs" />
+ <Compile Include="Objects\Sprite\ShieldFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\ShieldSprite.cs" />
+ <Compile Include="Objects\Sprite\ShortswordFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\ShortswordSprite.cs" />
+ <Compile Include="Objects\Sprite\SkeletonArcherSprite.cs" />
+ <Compile Include="Objects\Sprite\SkeletonEliteSprite.cs" />
+ <Compile Include="Objects\Sprite\SkeletonHorseSprite.cs" />
+ <Compile Include="Objects\Sprite\SkeletonRiderSprite.cs" />
+ <Compile Include="Objects\Sprite\StaffFemaleSprite.cs" />
+ <Compile Include="Objects\Sprite\StaffSprite.cs" />
+ <Compile Include="Objects\Sprite\ZombieWithClubSprite.cs" />
+ <Compile Include="Objects\TextureObject.cs" />
+ <Compile Include="Objects\Transformation.cs" />
+ <Compile Include="Objects\Woodhouse.cs" />
+ <Compile Include="Program.cs" />
+ <Compile Include="Quadtree.cs" />
+ <Compile Include="Screens\AchievementsScreen.cs" />
+ <Compile Include="Screens\DeathScreen.cs" />
+ <Compile Include="Screens\DebugScreen.cs" />
+ <Compile Include="Screens\FpsCounter.cs" />
+ <Compile Include="Screens\HudScreen.cs" />
+ <Compile Include="Screens\IScreenManager.cs" />
+ <Compile Include="Screens\LoadScreen.cs" />
+ <Compile Include="Screens\StatisticsScreen.cs" />
+ <Compile Include="Screens\TechdemoScreen.cs" />
+ <Compile Include="Screens\VictoryScreen.cs" />
+ <Compile Include="UpdatesPerSecond.cs" />
+ <Compile Include="V3Game.cs" />
+ <Compile Include="Data\GameState.cs" />
+ <Compile Include="Input\IMouseEventHandler.cs" />
+ <Compile Include="Input\MouseButton.cs" />
+ <Compile Include="Objects\AbstractCreature.cs" />
+ <Compile Include="Objects\Sprite\AbstractSpriteCreature.cs" />
+ <Compile Include="Objects\CreatureFactory.cs" />
+ <Compile Include="Objects\IBasicCreatureFactory.cs" />
+ <Compile Include="Objects\ICreature.cs" />
+ <Compile Include="Objects\IGameObject.cs" />
+ <Compile Include="Objects\Sprite\ISpriteCreature.cs" />
+ <Compile Include="Objects\Movement.cs" />
+ <Compile Include="Objects\Necromancer.cs" />
+ <Compile Include="Objects\MalePeasant.cs" />
+ <Compile Include="Objects\Sprite\NecromancerSprite.cs" />
+ <Compile Include="Objects\Skeleton.cs" />
+ <Compile Include="Objects\Sprite\SkeletonSprite.cs" />
+ <Compile Include="Objects\Zombie.cs" />
+ <Compile Include="Objects\Sprite\ZombieSprite.cs" />
+ <Compile Include="Screens\AbstractScreen.cs" />
+ <Compile Include="Screens\GameScreen.cs" />
+ <Compile Include="Screens\IDrawable.cs" />
+ <Compile Include="Screens\IScreen.cs" />
+ <Compile Include="Screens\IScreenFactory.cs" />
+ <Compile Include="Screens\IUpdatable.cs" />
+ <Compile Include="Screens\MainScreen.cs" />
+ <Compile Include="Screens\MenuActions.cs" />
+ <Compile Include="Screens\OptionsScreen.cs" />
+ <Compile Include="Screens\PauseScreen.cs" />
+ <Compile Include="Screens\ScreenManager.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Widgets\AbstractMenu.cs" />
+ <Compile Include="Widgets\AbstractTextWidget.cs" />
+ <Compile Include="Widgets\AchievementBox.cs" />
+ <Compile Include="Widgets\Button.cs" />
+ <Compile Include="Widgets\EmptyWidget.cs" />
+ <Compile Include="Widgets\FormMenu.cs" />
+ <Compile Include="Widgets\HorizontalOrientation.cs" />
+ <Compile Include="Widgets\IBasicWidgetFactory.cs" />
+ <Compile Include="Widgets\IClickable.cs" />
+ <Compile Include="Widgets\IImageWidget.cs" />
+ <Compile Include="Widgets\IMenu.cs" />
+ <Compile Include="Widgets\IMenuFactory.cs" />
+ <Compile Include="Widgets\ISelectable.cs" />
+ <Compile Include="Widgets\ITextWidget.cs" />
+ <Compile Include="Widgets\IWidget.cs" />
+ <Compile Include="Widgets\Label.cs" />
+ <Compile Include="Widgets\SelectButton.cs" />
+ <Compile Include="Widgets\VerticalMenu.cs" />
+ <Compile Include="Widgets\WidgetFactory.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Reference Include="Castle.Core, Version=3.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
+ <HintPath>..\packages\Castle.Core.3.2.0\lib\net45\Castle.Core.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Ninject, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
+ <HintPath>..\packages\Ninject.3.2.2.0\lib\net45-full\Ninject.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Ninject.Extensions.Factory, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
+ <HintPath>..\packages\Ninject.Extensions.Factory.3.2.1.0\lib\net45-full\Ninject.Extensions.Factory.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="OpenTK">
+ <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\OpenTK.dll</HintPath>
+ </Reference>
+ <Reference Include="NVorbis">
+ <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\NVorbis.dll</HintPath>
+ </Reference>
+ <Reference Include="MonoGame.Framework">
+ <HintPath>$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CONTROLS.md">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ <EmbeddedResource Include="Icon.ico" />
+ <Content Include="Content\Buttons\Button-01.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-01_Pressed.png" />
+ <Content Include="Content\Buttons\Button-02.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-02_Pressed.png" />
+ <Content Include="Content\Buttons\Button-03.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-03_Pressed.png" />
+ <Content Include="Content\Buttons\Button-04.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-04_Pressed.png" />
+ <Content Include="Content\Buttons\Button-05.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-05_Pressed.png" />
+ <Content Include="Content\Buttons\Button-06.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-06_Pressed.png" />
+ <Content Include="Content\Buttons\Button-07.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Buttons\Button-07_Pressed.png" />
+ <Content Include="Content\Effects\blood_hit_01.png" />
+ <Content Include="Content\Effects\blood_hit_02.png" />
+ <Content Include="Content\Effects\blood_hit_03.png" />
+ <Content Include="Content\Effects\blood_hit_04.png" />
+ <Content Include="Content\Effects\blood_hit_05.png" />
+ <Content Include="Content\Effects\blood_hit_06.png" />
+ <Content Include="Content\Effects\blood_hit_08.png" />
+ <Content Include="Content\Effects\explosion.png" />
+ <Content Include="Content\Effects\particlefx_03.png" />
+ <Content Include="Content\Effects\particlefx_04.png" />
+ <Content Include="Content\Effects\particlefx_05.png" />
+ <Content Include="Content\Effects\quake.png" />
+ <Content Include="Content\Menu\arrow_white.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Content\Menu\mainscreen.jpg" />
+ <Content Include="Content\Menu\Titel.png" />
+ <Content Include="Content\Sounds\Afraid_to_Go.mp3" />
+ <Content Include="Content\Sounds\explode.wav" />
+ <Content Include="Content\Sounds\explodemini.wav" />
+ <Content Include="Content\Sounds\horse.wav" />
+ <Content Include="Content\Sounds\Knight.wav" />
+ <Content Include="Content\Sounds\Kosta_T_-_06.mp3" />
+ <Content Include="Content\Sounds\Monster_Gigante-Doberman-1334685792.wav" />
+ <Content Include="Content\Sounds\Mummy_Zombie-SoundBible.wav" />
+ <Content Include="Content\Sounds\punch.wav" />
+ <Content Include="Content\Sounds\SkeletonHorse.wav" />
+ <Content Include="Content\Sounds\walking.wav" />
+ <Content Include="Content\Sources\horse_paint.png" />
+ <Content Include="Content\Sources\horse_paint_sc.png" />
+ <Content Include="Content\Sources\horse_tack.png" />
+ <Content Include="Content\Sources\king_head.png" />
+ <Content Include="Content\Sources\SkeletonHorse.png" />
+ <Content Include="Content\Sources\SkeletonRider.png" />
+ <Content Include="Content\Sources\skeleton_horse.png" />
+ <Content Include="Content\Sources\the_triumph_of_death.jpg" />
+ <Content Include="Content\Sources\ZombieWithClub.png" />
+ <Content Include="Content\Sprites\arrows.png" />
+ <Content Include="Content\Sprites\buckler.png" />
+ <Content Include="Content\Sprites\buckler_female.png" />
+ <Content Include="Content\Sprites\chain.png" />
+ <Content Include="Content\Sprites\chain_female.png" />
+ <Content Include="Content\Sprites\cloth.png" />
+ <Content Include="Content\Sprites\cloth_female.png" />
+ <Content Include="Content\Sprites\cloud.png" />
+ <Content Include="Content\Sprites\Ellipse.png" />
+ <Content Include="Content\Sprites\fleischklops.png" />
+ <Content Include="Content\Sprites\fog.png" />
+ <Content Include="Content\Sprites\head.png" />
+ <Content Include="Content\Sprites\head_bald.png" />
+ <Content Include="Content\Sprites\head_chain.png" />
+ <Content Include="Content\Sprites\head_chain_female.png" />
+ <Content Include="Content\Sprites\head_female.png" />
+ <Content Include="Content\Sprites\head_plate.png" />
+ <Content Include="Content\Sprites\head_plate_female.png" />
+ <Content Include="Content\Sprites\king.png" />
+ <Content Include="Content\Sprites\longsword.png" />
+ <Content Include="Content\Sprites\longsword_female.png" />
+ <Content Include="Content\Sprites\necromancer.png" />
+ <Content Include="Content\Sprites\necromancer_female.png" />
+ <Content Include="Content\Sprites\nude.png" />
+ <Content Include="Content\Sprites\nude_female.png" />
+ <Content Include="Content\Sprites\plate.png" />
+ <Content Include="Content\Sprites\plate_female.png" />
+ <Content Include="Content\Sprites\prince.png" />
+ <Content Include="Content\Sprites\selection.png" />
+ <Content Include="Content\Sprites\shield.png" />
+ <Content Include="Content\Sprites\shield_female.png" />
+ <Content Include="Content\Sprites\shortsword.png" />
+ <Content Include="Content\Sprites\shortsword_female.png" />
+ <Content Include="Content\Sprites\skeleton.png" />
+ <Content Include="Content\Sprites\skeleton_archer.png" />
+ <Content Include="Content\Sprites\skeleton_elite.png" />
+ <Content Include="Content\Sprites\skeleton_horse.png" />
+ <Content Include="Content\Sprites\skeleton_rider.png" />
+ <Content Include="Content\Sprites\staff.png" />
+ <Content Include="Content\Sprites\staff_female.png" />
+ <Content Include="Content\Sprites\WhiteRectangle.png" />
+ <Content Include="Content\Sprites\zombie.png" />
+ <Content Include="Content\Sprites\zombie_club.png" />
+ <Content Include="Content\Textures\castle.png" />
+ <Content Include="Content\Textures\EmptyPixel.png" />
+ <Content Include="Content\Textures\grassland.png" />
+ <Content Include="Content\Textures\grassland_trees.png" />
+ <Content Include="Content\Textures\grassland_water.png" />
+ <Content Include="Content\Textures\houses_front.png" />
+ <Content Include="Content\Textures\houses_rear.png" />
+ <Content Include="Content\Textures\medieval_building_tiles.png" />
+ <Content Include="Content\Textures\pathfinder.png" />
+ <None Include="CREDITS.md">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ <Content Include="OpenTK.dll.config">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <MonoGameContentReference Include="Content\Content.mgcb" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="ClassDiagram1.cd" />
+ <None Include="Content\Fonts\Blutschrift.ttf" />
+ <None Include="Content\Fonts\DeathFont.spritefont" />
+ <None Include="Content\Fonts\DejaVuSans.ttf" />
+ <None Include="Content\Fonts\grabstein.ttf" />
+ <None Include="Content\Fonts\MenuFont.spritefont" />
+ <None Include="Content\Fonts\Siegesschriftzug.ttf" />
+ <None Include="Content\Fonts\UnitFont.spritefont" />
+ <None Include="Content\Fonts\VictoryFont.spritefont" />
+ <None Include="Content\Maps\map_grassland.tmx">
+ <SubType>Designer</SubType>
+ </None>
+ <None Include="Content\Maps\techdemo.tmx" />
+ <None Include="Content\Maps\work_in_progress.tmx">
+ <SubType>Designer</SubType>
+ </None>
+ <None Include="Content\Sounds\explosion1.ogg" />
+ <None Include="Content\Sounds\impactsplat01.ogg" />
+ <None Include="Content\Sources\castle.xcf" />
+ <None Include="Content\Sources\create_spritesheet.sh" />
+ <None Include="Content\Sources\fleischklops.blend" />
+ <None Include="Content\Sources\fleischklops.xcf" />
+ <None Include="Content\Sources\Horse.blend" />
+ <None Include="Content\Sources\houses_front.xcf" />
+ <None Include="Content\Sources\houses_rear.xcf" />
+ <None Include="Content\Sources\human_construction_set.xcf" />
+ <None Include="Content\Sources\human_construction_set_female.xcf" />
+ <None Include="Content\Sources\king.blend" />
+ <None Include="Content\Sources\necromancer.xcf" />
+ <None Include="Content\Sources\pathfinder.xcf" />
+ <None Include="Content\Sources\prince.blend" />
+ <None Include="Content\Sources\prince.xcf" />
+ <None Include="Content\Sources\selection.xcf" />
+ <None Include="Content\Sources\Skeleton.blend" />
+ <None Include="Content\Sources\SkeletonHorse.blend" />
+ <None Include="Content\Sources\SkeletonRider.blend" />
+ <None Include="Content\Sources\zombie.blend" />
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.5">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGame.Content.Builder.targets" />
+</Project> \ No newline at end of file
diff --git a/V3/V3Game.cs b/V3/V3Game.cs
new file mode 100644
index 0000000..ea7abbf
--- /dev/null
+++ b/V3/V3Game.cs
@@ -0,0 +1,78 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+using V3.Data;
+using V3.Screens;
+
+namespace V3
+{
+ /// <summary>
+ /// This is the main type for your game.
+ /// </summary>
+ public sealed class V3Game : Game
+ {
+ private readonly IKernel mKernel;
+ private SpriteBatch mSpriteBatch;
+ private IScreenManager mScreenManager;
+ private IScreen mMainScreen;
+ //private float mReculate = 1.0f;
+ /// <summary>
+ /// Creates a new V3 game instance.
+ /// </summary>
+ public V3Game()
+ {
+ mKernel = new StandardKernel(new Bindings(this, new GraphicsDeviceManager(this)));
+ IsMouseVisible = true;
+ }
+
+ /// <summary>
+ /// Allows the game to perform any initialization it needs to before starting to run.
+ /// This is where it can query for any required services and load any non-graphic
+ /// related content. Calling base.Initialize will enumerate through any components
+ /// and initialize them as well.
+ /// </summary>
+ protected override void Initialize()
+ {
+ Content.RootDirectory = "Content";
+ mKernel.Get<IPathManager>().CreateMissingDirectories();
+ mScreenManager = mKernel.Get<IScreenManager>();
+ base.Initialize();
+ }
+
+ /// <summary>
+ /// LoadContent will be called once per game and is the place to load
+ /// all of your content.
+ /// </summary>
+ protected override void LoadContent()
+ {
+ // Create a new SpriteBatch, which can be used to draw textures.
+ mSpriteBatch = new SpriteBatch(GraphicsDevice);
+ mMainScreen = mKernel.Get<IScreenFactory>().CreateMainScreen();
+ mScreenManager.AddScreen(mMainScreen);
+
+ }
+
+ /// <summary>
+ /// Allows the game to run logic such as updating the world,
+ /// checking for collisions, gathering input, and playing audio.
+ /// It delegates the update to the screen manager.
+ /// </summary>
+ /// <param name="gameTime">Provides a snapshot of timing values.</param>
+ protected override void Update(GameTime gameTime)
+ {
+ mScreenManager.Update(gameTime);
+ base.Update(gameTime);
+ }
+
+ /// <summary>
+ /// This is called when the game should draw itself. It delegates the
+ /// drawing to the screen manager.
+ /// </summary>
+ /// <param name="gameTime">Provides a snapshot of timing values.</param>
+ protected override void Draw(GameTime gameTime)
+ {
+ mScreenManager.Draw(gameTime, mSpriteBatch);
+ base.Draw(gameTime);
+ }
+ }
+}
diff --git a/V3/Widgets/AbstractMenu.cs b/V3/Widgets/AbstractMenu.cs
new file mode 100644
index 0000000..f179b5e
--- /dev/null
+++ b/V3/Widgets/AbstractMenu.cs
@@ -0,0 +1,96 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using System.Collections.Generic;
+using System.Linq;
+using V3.Input;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// An abstract menu that handles the updating and drawing of widgtes.
+ /// </summary>
+ public abstract class AbstractMenu : IMenu
+ {
+ public List<IWidget> Widgets { get; } = new List<IWidget>();
+ public Vector2 Size { get; private set; }
+ public Vector2 Position { get; private set; }
+
+ private readonly GraphicsDeviceManager mGraphicsDeviceManager;
+
+ protected AbstractMenu(GraphicsDeviceManager graphicsDeviceManager)
+ {
+ mGraphicsDeviceManager = graphicsDeviceManager;
+ }
+
+ public void HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ foreach (var clickable in Widgets.OfType<IClickable>())
+ clickable.HandleMouseEvent(mouseEvent);
+ }
+
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ UpdateWidgetRelativePositions();
+ UpdateWidgetAbsolutePositions();
+ spriteBatch.Begin();
+ Widgets.ForEach(w => w.Draw(spriteBatch));
+ spriteBatch.End();
+ }
+
+ public void Update()
+ {
+ ResetClicked();
+ UpdateMouseSelection();
+ UpdateWidgetSizes();
+ }
+
+ protected abstract void UpdateWidgetSizes();
+
+ protected abstract void UpdateWidgetRelativePositions();
+
+ protected abstract Vector2 GetTotalSize();
+
+ private void UpdateWidgetAbsolutePositions()
+ {
+ var viewport = mGraphicsDeviceManager.GraphicsDevice.Viewport;
+ Size = GetTotalSize();
+ var viewportSize = new Vector2(viewport.Bounds.Width, viewport.Bounds.Height);
+ Position = (viewportSize - Size) / 2;
+ Widgets.ForEach(w => w.Position = w.Position + Position);
+ }
+
+ private void ResetClicked()
+ {
+ foreach (var clickable in Widgets.OfType<IClickable>())
+ clickable.IsClicked = false;
+ }
+
+ private void UpdateMouseSelection()
+ {
+ var position = Mouse.GetState().Position;
+ foreach (var selectable in Widgets.OfType<ISelectable>())
+ selectable.IsSelected = selectable.CheckSelected(position);
+ }
+
+ protected static void MakeWidgetsSameSize(IEnumerable<IWidget> widgets)
+ {
+ var xMax = 0f;
+ var yMax = 0f;
+
+ var widgetsCopy = widgets as IList<IWidget> ?? widgets.ToList();
+ foreach (var widget in widgetsCopy)
+ {
+ var size = widget.GetMinimumSize();
+ if (size.X > xMax)
+ xMax = size.X;
+ if (size.Y > yMax)
+ yMax = size.Y;
+ }
+
+ var sizeMax = new Vector2(xMax, yMax);
+ foreach (var widget in widgetsCopy)
+ widget.Size = sizeMax;
+ }
+ }
+}
diff --git a/V3/Widgets/AbstractTextWidget.cs b/V3/Widgets/AbstractTextWidget.cs
new file mode 100644
index 0000000..1f28742
--- /dev/null
+++ b/V3/Widgets/AbstractTextWidget.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Text;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Ninject;
+
+namespace V3.Widgets
+{
+ public abstract class AbstractTextWidget : ITextWidget, IInitializable
+ {
+ private string mText;
+
+ public Vector2 Position { get; set; }
+
+ public Vector2 Size { get; set; }
+
+ public string Text
+ {
+ get { return mText; }
+ set { mText = ReplaceUmlauteWhenOnUnix(value); }
+ }
+
+ public float PaddingX { get; set; } = 80;
+
+ public float PaddingY { get; set; } = 5;
+
+ public Color Color { get; set; } = Color.Black;
+
+ public HorizontalOrientation HorizontalOrientation { get; set; } = HorizontalOrientation.Center;
+
+ public SpriteFont Font { get; set; }
+
+ private readonly ContentManager mContentManager;
+
+ public AbstractTextWidget(ContentManager contentManager)
+ {
+ mContentManager = contentManager;
+ Text = "";
+ }
+
+ public virtual void Initialize()
+ {
+ Font = mContentManager.Load<SpriteFont>("Fonts/MenuFont");
+ }
+
+ public virtual void Draw(SpriteBatch spriteBatch)
+ {
+ var calculatedSize = Font.MeasureString(Text);
+ var position = Position;
+ switch (HorizontalOrientation)
+ {
+ case HorizontalOrientation.Left:
+ position.X += PaddingX;
+ break;
+ case HorizontalOrientation.Center:
+ position.X += (Size - calculatedSize).X / 2;
+ break;
+ case HorizontalOrientation.Right:
+ position.X += (Size - calculatedSize).X;
+ position.X -= PaddingX;
+ break;
+ }
+ position.Y += (Size - calculatedSize).Y / 2;
+ spriteBatch.DrawString(Font, Text, position, GetColor());
+ }
+
+ public virtual Vector2 GetMinimumSize()
+ {
+ var size = Font.MeasureString(Text);
+ size.X += 2 * PaddingX;
+ size.Y += 2 * PaddingY;
+ return size;
+ }
+
+ public bool CheckSelected(Point position)
+ {
+ var rectangle = new Rectangle((int) Position.X, (int) Position.Y, (int) Size.X, (int) Size.Y);
+ return rectangle.Contains(position);
+ }
+
+ protected virtual Color GetColor()
+ {
+ return Color;
+ }
+
+ /// <summary>
+ /// Test if execution platform is UNIX and replace german Umlaute
+ /// accordingly because a strange ArgumentException is thrown otherwise.
+ /// </summary>
+ /// <param name="originalString">The original input string.</param>
+ /// <returns></returns>
+ private string ReplaceUmlauteWhenOnUnix(string originalString)
+ {
+ // Taken from <http://mono.wikia.com/wiki/Detecting_the_execution_platform>.
+ int p = (int) Environment.OSVersion.Platform;
+ if ((p == 4) || (p == 6) || (p == 128)) // Running on Unix
+ {
+ var sb = new StringBuilder();
+ foreach (char c in originalString)
+ {
+ if (c < 128)
+ {
+ sb.Append(c);
+ }
+ }
+ return sb.ToString();
+ }
+ // NOT running on Unix.
+ return originalString;
+ }
+ }
+}
diff --git a/V3/Widgets/AchievementBox.cs b/V3/Widgets/AchievementBox.cs
new file mode 100644
index 0000000..000894a
--- /dev/null
+++ b/V3/Widgets/AchievementBox.cs
@@ -0,0 +1,91 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Widgets
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class AchievementBox : AbstractTextWidget
+ {
+ public bool IsEnabled { private get; set; }
+ private Color BackgroundColor { get; } = Color.Gray;
+
+ private readonly ContentManager mContentManager;
+ private readonly WidgetFactory mWidgetFactory;
+ private SpriteFont mTitleFont;
+ private SpriteFont mDescriptionFont;
+
+ private Label mButtonTitle;
+ private Label mButtonDescription;
+
+ private Texture2D mRectangle;
+
+ public AchievementBox(ContentManager contentManager, WidgetFactory widgetFactory) : base(contentManager)
+ {
+ mContentManager = contentManager;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public override void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mTitleFont = mContentManager.Load<SpriteFont>("Fonts/MenuFont");
+ mDescriptionFont = mContentManager.Load<SpriteFont>("Fonts/UnitFont");
+
+ mButtonTitle = mWidgetFactory.CreateLabel("");
+ mButtonTitle.PaddingY = 0;
+ mButtonTitle.PaddingX = 10;
+ mButtonTitle.HorizontalOrientation = HorizontalOrientation.Left;
+ mButtonDescription = mWidgetFactory.CreateLabel("");
+ mButtonDescription.PaddingX = 10;
+ mButtonDescription.PaddingY = 0;
+ mButtonDescription.HorizontalOrientation = HorizontalOrientation.Left;
+
+ base.Initialize();
+ }
+
+ public override void Draw(SpriteBatch spriteBatch)
+ {
+ var rectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
+
+ if (IsEnabled)
+ {
+ var borderRectangle = new Rectangle((int)Position.X - 2, (int)Position.Y - 2, (int)Size.X + 4, (int)Size.Y + 4);
+ spriteBatch.Draw(mRectangle, borderRectangle, Color.Gray);
+ }
+
+ mButtonTitle.Color = IsEnabled ? Color.Black : Color.Gray;
+ mButtonDescription.Color = IsEnabled ? Color.Black : Color.Gray;
+
+ spriteBatch.Draw(mRectangle, rectangle, GetBackgroundColor());
+ mButtonTitle.Position = Position + new Vector2(0, 10);
+ mButtonDescription.Position = mButtonTitle.Position + new Vector2(0, mButtonDescription.Size.Y);
+ mButtonDescription.Draw(spriteBatch);
+ mButtonTitle.Draw(spriteBatch);
+ base.Draw(spriteBatch);
+ }
+
+ public override Vector2 GetMinimumSize()
+ {
+ var titleSize = mButtonTitle.GetMinimumSize();
+ var descriptionSize = mButtonDescription.GetMinimumSize();
+
+ return new Vector2(MathHelper.Max(titleSize.X, descriptionSize.X), titleSize.Y + descriptionSize.Y + 20);
+ }
+
+ private Color GetBackgroundColor()
+ {
+ return IsEnabled ? BackgroundColor : Color.LightGray;
+ }
+
+ public void SetText(string title, string description)
+ {
+ mButtonTitle.Text = title;
+ mButtonDescription.Text = description;
+ mButtonTitle.Size = mButtonTitle.GetMinimumSize();
+ mButtonDescription.Size = mButtonDescription.GetMinimumSize();
+ mButtonTitle.Font = mTitleFont;
+ mButtonDescription.Font = mDescriptionFont;
+ }
+ }
+}
diff --git a/V3/Widgets/Button.cs b/V3/Widgets/Button.cs
new file mode 100644
index 0000000..04307d0
--- /dev/null
+++ b/V3/Widgets/Button.cs
@@ -0,0 +1,111 @@
+using Castle.Core.Internal;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using V3.Input;
+
+namespace V3.Widgets
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Button : AbstractTextWidget, IClickable, IImageWidget, ISelectable
+ {
+ public bool IsClicked { get; set; }
+
+ public bool IsEnabled { get; set; } = true;
+
+ public bool IsSelected { get; set; }
+
+ public Color BackgroundColor { private get; set; } = Color.Gray;
+
+ public Texture2D Image { get; set; }
+
+ public string Tooltip { get; set; }
+ public string TooltipTitle { private get; set; }
+
+ public Rectangle Rectangle
+ {
+ get { return new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y); }
+ }
+
+ private readonly ContentManager mContentManager;
+ private readonly WidgetFactory mWidgetFactory;
+
+ private Texture2D mRectangle;
+ private AchievementBox mTooltipBox;
+
+ public Button(ContentManager contentManager, WidgetFactory widgetFactory) : base(contentManager)
+ {
+ mContentManager = contentManager;
+ mWidgetFactory = widgetFactory;
+ }
+
+ public override void Initialize()
+ {
+ mRectangle = mContentManager.Load<Texture2D>("Sprites/WhiteRectangle");
+ mTooltipBox = mWidgetFactory.CreateAchievementBox();
+
+ base.Initialize();
+ }
+
+ public void HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (!IsEnabled)
+ return;
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (!mouseEvent.PositionReleased.HasValue)
+ return;
+ if (Rectangle.Contains(mouseEvent.PositionReleased.Value))
+ IsClicked = true;
+ }
+ }
+
+ public override void Draw(SpriteBatch spriteBatch)
+ {
+ var rectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
+
+ if (IsSelected && IsEnabled)
+ {
+ var borderRectangle = new Rectangle((int)Position.X - 2, (int)Position.Y - 2, (int)Size.X + 4, (int)Size.Y + 4);
+ spriteBatch.Draw(mRectangle, borderRectangle, Color.Red);
+ }
+
+ if (Image == null)
+ {
+ spriteBatch.Draw(mRectangle, rectangle, GetBackgroundColor());
+ }
+ else
+ {
+ spriteBatch.Draw(Image, rectangle, Color.White);
+ if (!IsEnabled)
+ {
+ spriteBatch.Draw(mRectangle, rectangle, Color.Black * 0.7f);
+ }
+ }
+
+ if (IsSelected && !TooltipTitle.IsNullOrEmpty() && !Tooltip.IsNullOrEmpty())
+ {
+ mTooltipBox.SetText(TooltipTitle, Tooltip);
+ mTooltipBox.Size = mTooltipBox.GetMinimumSize();
+ mTooltipBox.Position = Position - new Vector2(0, mTooltipBox.Size.Y);
+ mTooltipBox.IsEnabled = true;
+ if (mTooltipBox.Position.Y < 0)
+ mTooltipBox.Position = Position + new Vector2(0, Size.Y);
+ mTooltipBox.Draw(spriteBatch);
+ }
+
+ base.Draw(spriteBatch);
+ }
+
+ protected override Color GetColor()
+ {
+ return IsEnabled ? Color : Color.Gray;
+ }
+
+ private Color GetBackgroundColor()
+ {
+ return IsEnabled ? BackgroundColor : Color.LightGray;
+ }
+ }
+}
diff --git a/V3/Widgets/EmptyWidget.cs b/V3/Widgets/EmptyWidget.cs
new file mode 100644
index 0000000..d9ad8d8
--- /dev/null
+++ b/V3/Widgets/EmptyWidget.cs
@@ -0,0 +1,41 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A placeholder widget.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class EmptyWidget : IWidget
+ {
+ /// <summary>
+ /// The current position of this widget on the screen.
+ /// </summary>
+ public Vector2 Position { get; set; }
+
+ /// <summary>
+ /// The size of this widget on the screen. The widget should try to
+ /// fill this size. Should not be smaller than the value returned by
+ /// GetMinimumSize().
+ /// </summary>
+ public Vector2 Size { get; set; }
+
+ /// <summary>
+ /// Returns the minimum size this widgets needs.
+ /// </summary>
+ public Vector2 GetMinimumSize()
+ {
+ return new Vector2(0, 0);
+ }
+
+ /// <summary>
+ /// Draws this widget on the given sprite batch. It is assumed that
+ /// spriteBatch.Begin() has already been called.
+ /// </summary>
+ public void Draw(SpriteBatch spriteBatch)
+ {
+ }
+ }
+}
+
diff --git a/V3/Widgets/FormMenu.cs b/V3/Widgets/FormMenu.cs
new file mode 100644
index 0000000..746f8f4
--- /dev/null
+++ b/V3/Widgets/FormMenu.cs
@@ -0,0 +1,75 @@
+using Microsoft.Xna.Framework;
+using System;
+using System.Linq;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A menu that arranges widgets in two columns. All widgets in on column
+ /// are made the same size (the maximum widget size).
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class FormMenu : AbstractMenu
+ {
+ private const float PaddingInnerX = 10;
+ private const float PaddingOuterX = 10;
+ private const float PaddingInnerY = 10;
+ private const float PaddingOuterY = 10;
+
+ public FormMenu(GraphicsDeviceManager graphicsDeviceManager) : base(graphicsDeviceManager)
+ {
+ }
+
+ protected override Vector2 GetTotalSize()
+ {
+ var size = new Vector2(2 * PaddingOuterX, 2 * PaddingOuterY);
+ var maxXLeft = 0f;
+ var maxXRight = 0f;
+ for (var i = 0; i < Widgets.Count; i += 2)
+ {
+ maxXLeft = Math.Max(maxXLeft, Widgets[i].Size.X);
+ if (i + 1 < Widgets.Count)
+ {
+ maxXRight = Math.Max(maxXRight, Widgets[i + 1].Size.X);
+ size.Y += Math.Max(Widgets[i].Size.Y, Widgets[i + 1].Size.Y);
+ }
+ else
+ {
+ size.Y += Widgets[i].Size.Y;
+ }
+ if (i + 2 < Widgets.Count)
+ size.Y += PaddingInnerY;
+ }
+ size.X += maxXLeft + maxXRight + PaddingInnerX;
+ return size;
+ }
+
+ protected override void UpdateWidgetRelativePositions()
+ {
+ var y = PaddingOuterY;
+ for (var i = 0; i < Widgets.Count; i += 2)
+ {
+ var x = PaddingOuterX;
+ Widgets[i].Position = new Vector2(x, y);
+ if (i + 1 < Widgets.Count)
+ {
+ x += Widgets[i].Size.X;
+ x += PaddingInnerX;
+ Widgets[i + 1].Position = new Vector2(x, y);
+ y += Math.Max(Widgets[i].Size.Y, Widgets[i + 1].Size.Y);
+ }
+ else
+ {
+ y += Widgets[i].Size.Y;
+ }
+ y += PaddingInnerY;
+ }
+ }
+
+ protected override void UpdateWidgetSizes()
+ {
+ MakeWidgetsSameSize(Widgets.Where((w, i) => i % 2 == 0));
+ MakeWidgetsSameSize(Widgets.Where((w, i) => i % 2 == 1));
+ }
+ }
+}
diff --git a/V3/Widgets/HorizontalOrientation.cs b/V3/Widgets/HorizontalOrientation.cs
new file mode 100644
index 0000000..489667a
--- /dev/null
+++ b/V3/Widgets/HorizontalOrientation.cs
@@ -0,0 +1,9 @@
+namespace V3.Widgets
+{
+ public enum HorizontalOrientation
+ {
+ Left,
+ Center,
+ Right
+ }
+}
diff --git a/V3/Widgets/IBasicWidgetFactory.cs b/V3/Widgets/IBasicWidgetFactory.cs
new file mode 100644
index 0000000..dd0a28d
--- /dev/null
+++ b/V3/Widgets/IBasicWidgetFactory.cs
@@ -0,0 +1,38 @@
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A widget factory that is automatically implemented by Ninject.
+ /// </summary>
+ public interface IBasicWidgetFactory
+ {
+ /// <summary>
+ /// Creates a new button widget.
+ /// </summary>
+ /// <returns>a new button widget</returns>
+ Button CreateButton();
+
+ /// <summary>
+ /// Creates a new empty widget.
+ /// </summary>
+ /// <returns>a new empty widget</returns>
+ EmptyWidget CreateEmptyWidget();
+
+ /// <summary>
+ /// Creates a new select button widget.
+ /// </summary>
+ /// <returns>a new select button widget</returns>
+ SelectButton CreateSelectButton();
+
+ /// <summary>
+ /// Creates a new label widget.
+ /// </summary>
+ /// <returns>a new label widget</returns>
+ Label CreateLabel();
+
+ /// <summary>
+ /// Creates a new Achievement Box.
+ /// </summary>
+ /// <returns>a new achievement box widget.</returns>
+ AchievementBox CreateAchievementBox();
+ }
+}
diff --git a/V3/Widgets/IClickable.cs b/V3/Widgets/IClickable.cs
new file mode 100644
index 0000000..276c4a1
--- /dev/null
+++ b/V3/Widgets/IClickable.cs
@@ -0,0 +1,14 @@
+using V3.Input;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// An element that can be clicked on.
+ /// </summary>
+ public interface IClickable : IMouseEventHandler
+ {
+ bool IsClicked { get; set; }
+
+ bool IsEnabled { get; set; }
+ }
+}
diff --git a/V3/Widgets/IImageWidget.cs b/V3/Widgets/IImageWidget.cs
new file mode 100644
index 0000000..5a5b368
--- /dev/null
+++ b/V3/Widgets/IImageWidget.cs
@@ -0,0 +1,15 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A widget that displays an image.
+ /// </summary>
+ public interface IImageWidget : IWidget
+ {
+ /// <summary>
+ /// The image to display.
+ /// </summary>
+ Texture2D Image { get; set; }
+ }
+}
diff --git a/V3/Widgets/IMenu.cs b/V3/Widgets/IMenu.cs
new file mode 100644
index 0000000..ad121e0
--- /dev/null
+++ b/V3/Widgets/IMenu.cs
@@ -0,0 +1,33 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using System.Collections.Generic;
+using V3.Input;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A menu that displays a list of widgets.
+ /// </summary>
+ public interface IMenu : IMouseEventHandler
+ {
+ /// <summary>
+ /// The widgets in this menu. The order of the widgets in this list
+ /// is the order in which they are displayed.
+ /// </summary>
+ List<IWidget> Widgets { get; }
+
+ /// <summary>
+ /// The total size of the widgets in this menu.
+ /// </summary>
+ Vector2 Size { get; }
+
+ /// <summary>
+ /// The current position of the widgets.
+ /// </summary>
+ Vector2 Position { get; }
+
+ void Draw(SpriteBatch spriteBatch);
+
+ void Update();
+ }
+}
diff --git a/V3/Widgets/IMenuFactory.cs b/V3/Widgets/IMenuFactory.cs
new file mode 100644
index 0000000..24ed311
--- /dev/null
+++ b/V3/Widgets/IMenuFactory.cs
@@ -0,0 +1,9 @@
+namespace V3.Widgets
+{
+ public interface IMenuFactory
+ {
+ FormMenu CreateFormMenu();
+
+ VerticalMenu CreateVerticalMenu();
+ }
+} \ No newline at end of file
diff --git a/V3/Widgets/ISelectable.cs b/V3/Widgets/ISelectable.cs
new file mode 100644
index 0000000..6eb78dc
--- /dev/null
+++ b/V3/Widgets/ISelectable.cs
@@ -0,0 +1,27 @@
+using Microsoft.Xna.Framework;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// An element that can be selected.
+ /// </summary>
+ public interface ISelectable
+ {
+ bool IsSelected { get; set; }
+
+ /// <summary>
+ /// The tooltip to show if this widget is selected. Can be null or
+ /// empty if no tooltip should be shown.
+ /// </summary>
+ string Tooltip { get; set; }
+
+ /// <summary>
+ /// Checks whether this element is selected by a mouse click at the
+ /// given position.
+ /// </summary>
+ /// <param name="position">the position of the mouse click</param>
+ /// <returns>true if the element is selected by the mouse click at
+ /// that position, otherwise false</returns>
+ bool CheckSelected(Point position);
+ }
+}
diff --git a/V3/Widgets/ITextWidget.cs b/V3/Widgets/ITextWidget.cs
new file mode 100644
index 0000000..e5f8203
--- /dev/null
+++ b/V3/Widgets/ITextWidget.cs
@@ -0,0 +1,43 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A widget that contains some text.
+ /// </summary>
+ public interface ITextWidget : IWidget
+ {
+ /// <summary>
+ /// The text of this widget.
+ /// </summary>
+ string Text { get; set; }
+
+ /// <summary>
+ /// The space that is added before and after the text in the
+ /// horizontal direction.
+ /// </summary>
+ float PaddingX { get; set; }
+
+ /// <summary>
+ /// The space that is added before and after the text in the
+ /// vertical direction.
+ /// </summary>
+ float PaddingY { get; set; }
+
+ /// <summary>
+ /// The horizontal orientation of the text within the size.
+ /// </summary>
+ HorizontalOrientation HorizontalOrientation { get; set; }
+
+ /// <summary>
+ /// The default color of the text.
+ /// </summary>
+ Color Color { get; set; }
+
+ /// <summary>
+ /// The font for text rendering.
+ /// </summary>
+ SpriteFont Font { get; set; }
+ }
+}
diff --git a/V3/Widgets/IWidget.cs b/V3/Widgets/IWidget.cs
new file mode 100644
index 0000000..23466fe
--- /dev/null
+++ b/V3/Widgets/IWidget.cs
@@ -0,0 +1,34 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A simple widget that has a size and a position and that can be drawn.
+ /// </summary>
+ public interface IWidget
+ {
+ /// <summary>
+ /// The current position of this widget on the screen.
+ /// </summary>
+ Vector2 Position { get; set; }
+
+ /// <summary>
+ /// The size of this widget on the screen. The widget should try to
+ /// fill this size. Should not be smaller than the value returned by
+ /// GetMinimumSize().
+ /// </summary>
+ Vector2 Size { get; set; }
+
+ /// <summary>
+ /// Returns the minimum size this widgets needs.
+ /// </summary>
+ Vector2 GetMinimumSize();
+
+ /// <summary>
+ /// Drawst this widget on the given sprite batch. It is assumed that
+ /// spriteBatch.Begin() has already been called.
+ /// </summary>
+ void Draw(SpriteBatch spriteBatch);
+ }
+}
diff --git a/V3/Widgets/Label.cs b/V3/Widgets/Label.cs
new file mode 100644
index 0000000..6b60d4b
--- /dev/null
+++ b/V3/Widgets/Label.cs
@@ -0,0 +1,14 @@
+using Microsoft.Xna.Framework.Content;
+
+namespace V3.Widgets
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Label : AbstractTextWidget
+ {
+ public Label(ContentManager contentManager) : base(contentManager)
+ {
+ HorizontalOrientation = HorizontalOrientation.Left;
+ PaddingX = 20;
+ }
+ }
+}
diff --git a/V3/Widgets/SelectButton.cs b/V3/Widgets/SelectButton.cs
new file mode 100644
index 0000000..9079c6d
--- /dev/null
+++ b/V3/Widgets/SelectButton.cs
@@ -0,0 +1,108 @@
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Content;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using System.Collections.Generic;
+using System.Linq;
+using V3.Input;
+
+namespace V3.Widgets
+{
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class SelectButton : AbstractTextWidget, IClickable
+ {
+ public List<string> Values { get; } = new List<string>();
+
+ public int SelectedIndex { get; set; }
+
+ public bool IsClicked { get; set; }
+
+ public bool IsEnabled { get; set; } = true;
+
+ private readonly ContentManager mContentManager;
+
+ private Texture2D mTriangle;
+ private Rectangle mBoxArrowLeft;
+ private Rectangle mBoxArrowRight;
+
+ public SelectButton(ContentManager contentManager) : base(contentManager)
+ {
+ mContentManager = contentManager;
+ }
+
+ public override void Initialize()
+ {
+ mTriangle = mContentManager.Load<Texture2D>("Menu/arrow_white");
+ base.Initialize();
+ }
+
+ public void HandleMouseEvent(IMouseEvent mouseEvent)
+ {
+ if (mouseEvent.MouseButton == MouseButton.Left && mouseEvent.ButtonState == ButtonState.Released)
+ {
+ if (!mouseEvent.PositionReleased.HasValue)
+ return;
+ var rectangle = new Rectangle((int)Position.X, (int)Position.Y, (int)Size.X, (int)Size.Y);
+ var position = mouseEvent.PositionReleased.Value;
+ if (rectangle.Contains(position))
+ IsClicked = true;
+
+ int change;
+ if (mBoxArrowLeft.Contains(position))
+ change = -1;
+ else if (mBoxArrowRight.Contains(position))
+ change = 1;
+ else
+ return;
+
+ SelectedIndex += change;
+ if (SelectedIndex < 0)
+ SelectedIndex += Values.Count;
+ if (SelectedIndex >= Values.Count)
+ SelectedIndex -= Values.Count;
+ }
+ }
+
+ public override void Draw(SpriteBatch spriteBatch)
+ {
+ UpdateSelection();
+
+ var arrowPadding = 30;
+ var arrowLength = 20;
+ var arrowY = (int)Position.Y + (int)(Size.Y / 2) - arrowLength / 2;
+ mBoxArrowLeft = new Rectangle((int)Position.X + arrowPadding,
+ arrowY, arrowLength, arrowLength);
+ mBoxArrowRight = new Rectangle((int)Position.X + (int)Size.X - arrowPadding,
+ arrowY, arrowLength, arrowLength);
+
+
+ spriteBatch.Draw(mTriangle, mBoxArrowLeft, null, Color.Gray, 0, new Vector2(0, 0), SpriteEffects.FlipHorizontally, 0);
+ spriteBatch.Draw(mTriangle, mBoxArrowRight, Color.Gray);
+
+ base.Draw(spriteBatch);
+ }
+
+ public override Vector2 GetMinimumSize()
+ {
+ Vector2 size;
+ try
+ {
+ size = new Vector2(Values.Max(v => Font.MeasureString(v).X), Font.MeasureString(Text).Y);
+ }
+ catch (ArgumentException)
+ {
+ // Return whatever.
+ size = new Vector2(100, 40);
+ }
+ size.X += 2 * PaddingX;
+ size.Y += 2 * PaddingY;
+ return size;
+ }
+
+ private void UpdateSelection()
+ {
+ Text = Values[SelectedIndex];
+ }
+ }
+}
diff --git a/V3/Widgets/VerticalMenu.cs b/V3/Widgets/VerticalMenu.cs
new file mode 100644
index 0000000..bc18439
--- /dev/null
+++ b/V3/Widgets/VerticalMenu.cs
@@ -0,0 +1,42 @@
+using Microsoft.Xna.Framework;
+using System.Linq;
+
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A simple menu with vertically arranged widgets. All widgets are made
+ /// the same size (the maximum widget size).
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class VerticalMenu : AbstractMenu
+ {
+ private float mPadding = 10;
+
+ public VerticalMenu(GraphicsDeviceManager graphicsDeviceManager) : base(graphicsDeviceManager)
+ {
+ }
+
+ protected override void UpdateWidgetRelativePositions()
+ {
+ var y = 0f;
+ foreach (var widget in Widgets)
+ {
+ widget.Position = new Vector2(0, y);
+ y += widget.Size.Y;
+ y += mPadding;
+ }
+ }
+
+ protected override Vector2 GetTotalSize()
+ {
+ var totalX = Widgets.Max(w => w.Size.X);
+ var totalY = (Widgets.Count - 1) * mPadding + Widgets.Sum(w => w.Size.Y);
+ return new Vector2(totalX, totalY);
+ }
+
+ protected override void UpdateWidgetSizes()
+ {
+ MakeWidgetsSameSize(Widgets);
+ }
+ }
+}
diff --git a/V3/Widgets/WidgetFactory.cs b/V3/Widgets/WidgetFactory.cs
new file mode 100644
index 0000000..35e8e1b
--- /dev/null
+++ b/V3/Widgets/WidgetFactory.cs
@@ -0,0 +1,78 @@
+namespace V3.Widgets
+{
+ /// <summary>
+ /// A widget factory.
+ /// </summary>
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class WidgetFactory
+ {
+ private IBasicWidgetFactory mFactory;
+
+ public WidgetFactory(IBasicWidgetFactory factory)
+ {
+ mFactory = factory;
+ }
+
+ /// <summary>
+ /// Creates a new button with the given text.
+ /// </summary>
+ /// <param name="text">the text of the button</param>
+ /// <returns>the created button</returns>
+ public Button CreateButton(string text)
+ {
+ return CreateTextWidget(mFactory.CreateButton(), text);
+ }
+
+ /// <summary>
+ /// Creates a new empty widget.
+ /// </summary>
+ /// <returns>the created widget</returns>
+ public EmptyWidget CreateEmptyWidget()
+ {
+ return mFactory.CreateEmptyWidget();
+ }
+
+ /// <summary>
+ /// Creates a new select button with the given text options.
+ /// </summary>
+ /// <param name="values">the values of the button</param>
+ /// <returns>the created button</returns>
+ public SelectButton CreateSelectButton(string[] values)
+ {
+ var widget = CreateTextWidget(mFactory.CreateSelectButton(), "");
+ foreach (var val in values)
+ widget.Values.Add(val);
+ return widget;
+ }
+
+ /// <summary>
+ /// Creates a new select button without options.
+ /// </summary>
+ /// <returns>the created button</returns>
+ public SelectButton CreateSelectButton()
+ {
+ return mFactory.CreateSelectButton();
+ }
+
+ /// <summary>
+ /// Creates a new label with the given text.
+ /// </summary>
+ /// <param name="text">the text of the label</param>
+ /// <returns>the created label</returns>
+ public Label CreateLabel(string text)
+ {
+ return CreateTextWidget(mFactory.CreateLabel(), text);
+ }
+
+ private T CreateTextWidget<T>(T widget, string text) where T : ITextWidget
+ {
+ widget.Text = text;
+ return widget;
+ }
+
+ public AchievementBox CreateAchievementBox()
+ {
+ return mFactory.CreateAchievementBox();
+ }
+ }
+}
diff --git a/V3/packages.config b/V3/packages.config
new file mode 100644
index 0000000..8839ef8
--- /dev/null
+++ b/V3/packages.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Castle.Core" version="3.2.0" targetFramework="net45" />
+ <package id="Ninject" version="3.2.2.0" targetFramework="net45" />
+ <package id="Ninject.Extensions.Factory" version="3.2.1.0" targetFramework="net45" />
+</packages> \ No newline at end of file