From 7881deca304e27f9cb20304c81731433954bec4f Mon Sep 17 00:00:00 2001 From: alzeih Date: Sat, 27 Feb 2010 21:10:22 +1300 Subject: [PATCH] Everything is now OpenRA, not OpenRa --- Makefile | 26 +- OpenRA.FileFormats/AudLoader.cs | 125 ++ OpenRA.FileFormats/Blowfish.cs | 416 ++++++ OpenRA.FileFormats/BlowfishKeyProvider.cs | 538 ++++++++ OpenRA.FileFormats/Cache.cs | 59 + OpenRA.FileFormats/Collections/Set.cs | 76 ++ OpenRA.FileFormats/DisposableAction.cs | 45 + OpenRA.FileFormats/Dune2ShpReader.cs | 156 +++ OpenRA.FileFormats/Exts.cs | 39 + OpenRA.FileFormats/FieldLoader.cs | 119 ++ OpenRA.FileFormats/FileSystem.cs | 138 ++ OpenRA.FileFormats/Folder.cs | 45 + OpenRA.FileFormats/Format2.cs | 46 + OpenRA.FileFormats/Format40.cs | 88 ++ OpenRA.FileFormats/Format80.cs | 135 ++ .../Graphics/IGraphicsDevice.cs | 105 ++ OpenRA.FileFormats/Graphics/Vertex.cs | 38 + OpenRA.FileFormats/IPaletteRemap.cs | 29 + OpenRA.FileFormats/IniFile.cs | 161 +++ OpenRA.FileFormats/Lazy.cs | 56 + OpenRA.FileFormats/Map.cs | 176 +++ OpenRA.FileFormats/MiniYaml.cs | 168 +++ OpenRA.FileFormats/OpenRA.FileFormats.csproj | 106 ++ OpenRA.FileFormats/Package.cs | 166 +++ OpenRA.FileFormats/PackageEntry.cs | 80 ++ OpenRA.FileFormats/Pair.cs | 75 + OpenRA.FileFormats/Palette.cs | 60 + OpenRA.FileFormats/PlayerColorRemap.cs | 50 + OpenRA.FileFormats/PriorityQueue.cs | 113 ++ OpenRA.FileFormats/Properties/AssemblyInfo.cs | 36 + OpenRA.FileFormats/ProtocolVersion.cs | 28 + OpenRA.FileFormats/Session.cs | 84 ++ OpenRA.FileFormats/ShpReader.cs | 177 +++ OpenRA.FileFormats/ShroudPaletteRemap.cs | 48 + OpenRA.FileFormats/SingleColorRemap.cs | 38 + OpenRA.FileFormats/Support/Log.cs | 39 + OpenRA.FileFormats/Support/Stopwatch.cs | 42 + OpenRA.FileFormats/Support/Timer.cs | 35 + OpenRA.FileFormats/Terrain.cs | 67 + OpenRA.FileFormats/TileReference.cs | 45 + OpenRA.FileFormats/TileSet.cs | 109 ++ OpenRA.FileFormats/TreeReference.cs | 40 + OpenRA.FileFormats/Tuple.cs | 39 + OpenRA.FileFormats/TypeDictionary.cs | 115 ++ OpenRA.FileFormats/Walkability.cs | 73 + OpenRA.FileFormats/float2.cs | 113 ++ OpenRA.FileFormats/int2.cs | 67 + OpenRA.Game/Actor.cs | 238 ++++ OpenRA.Game/Chat.cs | 77 ++ OpenRA.Game/Chrome.cs | 1214 +++++++++++++++++ OpenRA.Game/Combat.cs | 114 ++ OpenRA.Game/Controller.cs | 146 ++ OpenRA.Game/Cursor.cs | 36 + OpenRA.Game/Effects/Bullet.cs | 133 ++ OpenRA.Game/Effects/Corpse.cs | 50 + OpenRA.Game/Effects/DelayedAction.cs | 46 + OpenRA.Game/Effects/Explosion.cs | 50 + OpenRA.Game/Effects/FlashTarget.cs | 52 + OpenRA.Game/Effects/IEffect.cs | 31 + OpenRA.Game/Effects/LaserZap.cs | 64 + OpenRA.Game/Effects/Missile.cs | 114 ++ OpenRA.Game/Effects/MoveFlash.cs | 47 + OpenRA.Game/Effects/RepairIndicator.cs | 47 + OpenRA.Game/Effects/Smoke.cs | 50 + OpenRA.Game/Effects/TeslaZap.cs | 104 ++ OpenRA.Game/Exts.cs | 90 ++ OpenRA.Game/Game.cs | 427 ++++++ OpenRA.Game/GameRules/ActorInfo.cs | 139 ++ OpenRA.Game/GameRules/Footprint.cs | 74 + OpenRA.Game/GameRules/GeneralInfo.cs | 112 ++ OpenRA.Game/GameRules/InfoLoader.cs | 51 + OpenRA.Game/GameRules/ProjectileInfo.cs | 45 + OpenRA.Game/GameRules/Rules.cs | 82 ++ OpenRA.Game/GameRules/TechTree.cs | 100 ++ OpenRA.Game/GameRules/UserSettings.cs | 53 + OpenRA.Game/GameRules/VoiceInfo.cs | 74 + OpenRA.Game/GameRules/WarheadInfo.cs | 46 + OpenRA.Game/GameRules/WeaponInfo.cs | 41 + OpenRA.Game/Graphics/Animation.cs | 150 ++ OpenRA.Game/Graphics/ChromeProvider.cs | 97 ++ OpenRA.Game/Graphics/CursorSequence.cs | 61 + OpenRA.Game/Graphics/CursorSheetBuilder.cs | 48 + OpenRA.Game/Graphics/HardwarePalette.cs | 86 ++ OpenRA.Game/Graphics/LineRenderer.cs | 89 ++ OpenRA.Game/Graphics/MappedImage.cs | 51 + OpenRA.Game/Graphics/Minimap.cs | 213 +++ OpenRA.Game/Graphics/OverlayRenderer.cs | 114 ++ OpenRA.Game/Graphics/Renderer.cs | 134 ++ OpenRA.Game/Graphics/Sequence.cs | 70 + OpenRA.Game/Graphics/SequenceProvider.cs | 101 ++ OpenRA.Game/Graphics/Sheet.cs | 72 + OpenRA.Game/Graphics/SheetBuilder.cs | 119 ++ OpenRA.Game/Graphics/Sprite.cs | 71 + OpenRA.Game/Graphics/SpriteFont.cs | 116 ++ OpenRA.Game/Graphics/SpriteRenderer.cs | 91 ++ OpenRA.Game/Graphics/SpriteSheetBuilder.cs | 50 + OpenRA.Game/Graphics/TerrainRenderer.cs | 110 ++ OpenRA.Game/Graphics/Util.cs | 131 ++ OpenRA.Game/Graphics/Viewport.cs | 151 ++ OpenRA.Game/Graphics/WorldRenderer.cs | 315 +++++ OpenRA.Game/MainWindow.cs | 53 + OpenRA.Game/Network/Connection.cs | 142 ++ OpenRA.Game/Network/Order.cs | 182 +++ OpenRA.Game/Network/OrderIO.cs | 78 ++ OpenRA.Game/Network/OrderManager.cs | 158 +++ OpenRA.Game/Network/UnitOrders.cs | 66 + OpenRA.Game/OpenRA.Game.csproj | 306 +++++ OpenRA.Game/OpenRA.ico | Bin 0 -> 270398 bytes OpenRA.Game/Orders/IOrderGenerator.cs | 32 + .../Orders/PlaceBuildingOrderGenerator.cs | 78 ++ OpenRA.Game/Orders/PowerDownOrderGenerator.cs | 61 + OpenRA.Game/Orders/RepairOrderGenerator.cs | 75 + OpenRA.Game/Orders/SellOrderGenerator.cs | 63 + OpenRA.Game/Orders/UnitOrderGenerator.cs | 102 ++ OpenRA.Game/Ore.cs | 207 +++ OpenRA.Game/PackageDownloader.cs | 144 ++ OpenRA.Game/PathFinder.cs | 226 +++ OpenRA.Game/PathSearch.cs | 183 +++ OpenRA.Game/Player.cs | 224 +++ OpenRA.Game/Properties/AssemblyInfo.cs | 37 + OpenRA.Game/Selection.cs | 94 ++ OpenRA.Game/Shroud.cs | 232 ++++ OpenRA.Game/Smudge.cs | 67 + OpenRA.Game/Sound.cs | 281 ++++ OpenRA.Game/Support/PerfHistory.cs | 141 ++ OpenRA.Game/Support/Program.cs | 54 + OpenRA.Game/Support/Settings.cs | 67 + OpenRA.Game/Sync.cs | 125 ++ OpenRA.Game/TerrainCosts.cs | 73 + OpenRA.Game/Traits/AI/AutoHeal.cs | 75 + OpenRA.Game/Traits/AI/AutoTarget.cs | 77 ++ OpenRA.Game/Traits/AI/SelfHealing.cs | 51 + OpenRA.Game/Traits/AI/TakeCover.cs | 64 + OpenRA.Game/Traits/Activities/Attack.cs | 69 + OpenRA.Game/Traits/Activities/CallFunc.cs | 40 + OpenRA.Game/Traits/Activities/DeliverOre.cs | 91 ++ .../Traits/Activities/EnterTransport.cs | 51 + OpenRA.Game/Traits/Activities/Fly.cs | 64 + OpenRA.Game/Traits/Activities/FlyAttack.cs | 69 + OpenRA.Game/Traits/Activities/FlyTimed.cs | 53 + OpenRA.Game/Traits/Activities/Follow.cs | 54 + OpenRA.Game/Traits/Activities/Harvest.cs | 85 ++ OpenRA.Game/Traits/Activities/HeliAttack.cs | 69 + OpenRA.Game/Traits/Activities/HeliFly.cs | 71 + OpenRA.Game/Traits/Activities/HeliLand.cs | 47 + OpenRA.Game/Traits/Activities/HeliReturn.cs | 68 + OpenRA.Game/Traits/Activities/Idle.cs | 30 + OpenRA.Game/Traits/Activities/Land.cs | 68 + OpenRA.Game/Traits/Activities/Move.cs | 286 ++++ OpenRA.Game/Traits/Activities/Rearm.cs | 57 + OpenRA.Game/Traits/Activities/RemoveSelf.cs | 37 + OpenRA.Game/Traits/Activities/Repair.cs | 72 + OpenRA.Game/Traits/Activities/ReturnToBase.cs | 112 ++ OpenRA.Game/Traits/Activities/Sell.cs | 68 + OpenRA.Game/Traits/Activities/Teleport.cs | 43 + .../Traits/Activities/TransformIntoActor.cs | 67 + OpenRA.Game/Traits/Activities/Turn.cs | 53 + OpenRA.Game/Traits/Activities/UndeployMcv.cs | 65 + OpenRA.Game/Traits/Activities/UnloadCargo.cs | 87 ++ OpenRA.Game/Traits/Attack/AttackBase.cs | 254 ++++ OpenRA.Game/Traits/Attack/AttackFrontal.cs | 45 + OpenRA.Game/Traits/Attack/AttackHeli.cs | 41 + OpenRA.Game/Traits/Attack/AttackInfo.cs | 33 + OpenRA.Game/Traits/Attack/AttackOmni.cs | 50 + OpenRA.Game/Traits/Attack/AttackPlane.cs | 42 + OpenRA.Game/Traits/Attack/AttackTurreted.cs | 71 + OpenRA.Game/Traits/Bridge.cs | 175 +++ OpenRA.Game/Traits/Buildable.cs | 37 + OpenRA.Game/Traits/Building.cs | 159 +++ OpenRA.Game/Traits/CanPowerDown.cs | 51 + OpenRA.Game/Traits/Cargo.cs | 111 ++ OpenRA.Game/Traits/Chrome/PowerDownButton.cs | 47 + OpenRA.Game/Traits/Chronoshiftable.cs | 79 ++ OpenRA.Game/Traits/ConstructionYard.cs | 97 ++ OpenRA.Game/Traits/Crate.cs | 100 ++ OpenRA.Game/Traits/CustomSellValue.cs | 32 + OpenRA.Game/Traits/Explodes.cs | 43 + OpenRA.Game/Traits/Fake.cs | 31 + OpenRA.Game/Traits/GeneratesGap.cs | 49 + OpenRA.Game/Traits/Harvester.cs | 115 ++ OpenRA.Game/Traits/Helicopter.cs | 100 ++ OpenRA.Game/Traits/LimitedAmmo.cs | 63 + OpenRA.Game/Traits/Mobile.cs | 145 ++ OpenRA.Game/Traits/Modifiers/BelowUnits.cs | 35 + OpenRA.Game/Traits/Modifiers/Cloak.cs | 82 ++ .../Traits/Modifiers/InvisibleToOthers.cs | 35 + OpenRA.Game/Traits/Modifiers/WithShadow.cs | 41 + OpenRA.Game/Traits/OreRefinery.cs | 65 + OpenRA.Game/Traits/Passenger.cs | 59 + OpenRA.Game/Traits/Plane.cs | 93 ++ OpenRA.Game/Traits/Player/EvaAlerts.cs | 63 + OpenRA.Game/Traits/Player/PlaceBuilding.cs | 60 + OpenRA.Game/Traits/Player/ProductionQueue.cs | 243 ++++ .../Traits/Player/SpawnDefaultUnits.cs | 32 + OpenRA.Game/Traits/Production.cs | 145 ++ OpenRA.Game/Traits/ProductionSurround.cs | 60 + OpenRA.Game/Traits/ProvidesRadar.cs | 55 + OpenRA.Game/Traits/RallyPoint.cs | 69 + OpenRA.Game/Traits/Render/RenderBuilding.cs | 137 ++ .../Traits/Render/RenderBuildingCharge.cs | 44 + .../Traits/Render/RenderBuildingOre.cs | 40 + .../Traits/Render/RenderBuildingTurreted.cs | 56 + .../Traits/Render/RenderBuildingWall.cs | 103 ++ .../Traits/Render/RenderBuildingWarFactory.cs | 89 ++ OpenRA.Game/Traits/Render/RenderInfantry.cs | 96 ++ OpenRA.Game/Traits/Render/RenderSimple.cs | 95 ++ OpenRA.Game/Traits/Render/RenderUnit.cs | 61 + .../Traits/Render/RenderUnitMuzzleFlash.cs | 48 + OpenRA.Game/Traits/Render/RenderUnitReload.cs | 45 + OpenRA.Game/Traits/Render/RenderUnitRotor.cs | 76 ++ .../Traits/Render/RenderUnitSpinner.cs | 47 + .../Traits/Render/RenderUnitTurreted.cs | 67 + OpenRA.Game/Traits/Repairable.cs | 72 + OpenRA.Game/Traits/Reservable.cs | 55 + OpenRA.Game/Traits/SeedsOre.cs | 51 + OpenRA.Game/Traits/Selectable.cs | 31 + OpenRA.Game/Traits/SquishByTank.cs | 56 + OpenRA.Game/Traits/StoresOre.cs | 53 + OpenRA.Game/Traits/Submarine.cs | 85 ++ .../Traits/SupportPowers/ChronoshiftPower.cs | 187 +++ OpenRA.Game/Traits/SupportPowers/NukePower.cs | 106 ++ .../Traits/SupportPowers/SupportPower.cs | 149 ++ OpenRA.Game/Traits/TraitsInterfaces.cs | 143 ++ OpenRA.Game/Traits/TransformsOnDeploy.cs | 84 ++ OpenRA.Game/Traits/Turreted.cs | 48 + OpenRA.Game/Traits/Unit.cs | 49 + OpenRA.Game/Traits/Util.cs | 165 +++ OpenRA.Game/Traits/Wall.cs | 32 + OpenRA.Game/Traits/World/BridgeLoadHook.cs | 88 ++ OpenRA.Game/Traits/World/BuildingInfluence.cs | 71 + .../Traits/World/ChoosePaletteOnSelect.cs | 44 + .../Traits/World/ChronoshiftPaletteEffect.cs | 60 + OpenRA.Game/Traits/World/Country.cs | 34 + OpenRA.Game/Traits/World/CrateSpawner.cs | 79 ++ .../Traits/World/LightPaletteRotator.cs | 45 + OpenRA.Game/Traits/World/OreGrowth.cs | 61 + OpenRA.Game/Traits/World/PaletteFromFile.cs | 45 + OpenRA.Game/Traits/World/PaletteFromRGBA.cs | 52 + OpenRA.Game/Traits/World/PaletteFromRemap.cs | 49 + .../Traits/World/PlayerColorPalette.cs | 47 + OpenRA.Game/Traits/World/ScreenShaker.cs | 54 + OpenRA.Game/Traits/World/ShroudPalette.cs | 41 + OpenRA.Game/Traits/World/UnitInfluence.cs | 113 ++ .../Traits/World/WaterPaletteRotation.cs | 44 + OpenRA.Game/UiOverlay.cs | 79 ++ OpenRA.Game/World.cs | 250 ++++ OpenRA.Game/WorldUtils.cs | 215 +++ OpenRA.Gl/GraphicsDevice.cs | 459 +++++++ OpenRA.Gl/OpenRA.Gl.csproj | 75 + OpenRA.Gl/Properties/AssemblyInfo.cs | 36 + OpenRA.Mods.Aftermath/ChronoshiftDeploy.cs | 111 ++ OpenRA.Mods.Aftermath/DemoTruck.cs | 73 + .../OpenRA.Mods.Aftermath.csproj | 76 ++ .../Orders/SetChronoTankDestination.cs | 62 + .../Properties/AssemblyInfo.cs | 36 + OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj | 78 ++ OpenRA.Mods.Cnc/ProductionAirdrop.cs | 84 ++ OpenRA.Mods.Cnc/Properties/AssemblyInfo.cs | 36 + OpenRA.Mods.Cnc/TiberiumRefinery.cs | 61 + OpenRA.Mods.RA/Activities/CaptureBuilding.cs | 63 + OpenRA.Mods.RA/Activities/Demolish.cs | 46 + OpenRA.Mods.RA/Activities/Infiltrate.cs | 47 + OpenRA.Mods.RA/Activities/LayMine.cs | 40 + OpenRA.Mods.RA/Activities/Steal.cs | 49 + OpenRA.Mods.RA/C4Demolition.cs | 52 + .../Crates/ArmorUpgradeCrateAction.cs | 64 + .../Crates/FirepowerUpgradeCrateAction.cs | 64 + OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs | 56 + .../Crates/SpeedUpgradeCrateAction.cs | 64 + OpenRA.Mods.RA/Effects/CrateEffect.cs | 52 + OpenRA.Mods.RA/Effects/GpsSatellite.cs | 54 + OpenRA.Mods.RA/Effects/InvulnEffect.cs | 50 + OpenRA.Mods.RA/Effects/Parachute.cs | 82 ++ OpenRA.Mods.RA/Effects/SatelliteLaunch.cs | 58 + OpenRA.Mods.RA/EngineerCapture.cs | 56 + OpenRA.Mods.RA/GpsPower.cs | 63 + OpenRA.Mods.RA/InfiltrateForSonarPulse.cs | 34 + OpenRA.Mods.RA/IronCurtainPower.cs | 119 ++ OpenRA.Mods.RA/IronCurtainable.cs | 53 + OpenRA.Mods.RA/Mine.cs | 79 ++ OpenRA.Mods.RA/Minelayer.cs | 62 + OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 104 ++ OpenRA.Mods.RA/ParaDrop.cs | 82 ++ OpenRA.Mods.RA/ParatroopersPower.cs | 92 ++ OpenRA.Mods.RA/Properties/AssemblyInfo.cs | 36 + OpenRA.Mods.RA/RenderSpy.cs | 53 + OpenRA.Mods.RA/RepairableNear.cs | 53 + OpenRA.Mods.RA/RequiresPower.cs | 44 + OpenRA.Mods.RA/SonarPulsePower.cs | 54 + OpenRA.Mods.RA/Spy.cs | 51 + OpenRA.Mods.RA/SpyPlanePower.cs | 86 ++ OpenRA.Mods.RA/Thief.cs | 50 + OpenRA.Server/OpenRA.Server.csproj | 2 +- OpenRA.sln | 67 + SequenceEditor/SequenceEditor.csproj | 2 +- mods/aftermath/mod.yaml | 2 +- mods/cnc/mod.yaml | 4 +- mods/ra/mod.yaml | 2 +- packaging/osx/package.sh | 4 +- 299 files changed, 26919 insertions(+), 21 deletions(-) create mode 100644 OpenRA.FileFormats/AudLoader.cs create mode 100644 OpenRA.FileFormats/Blowfish.cs create mode 100644 OpenRA.FileFormats/BlowfishKeyProvider.cs create mode 100644 OpenRA.FileFormats/Cache.cs create mode 100755 OpenRA.FileFormats/Collections/Set.cs create mode 100644 OpenRA.FileFormats/DisposableAction.cs create mode 100644 OpenRA.FileFormats/Dune2ShpReader.cs create mode 100644 OpenRA.FileFormats/Exts.cs create mode 100644 OpenRA.FileFormats/FieldLoader.cs create mode 100644 OpenRA.FileFormats/FileSystem.cs create mode 100644 OpenRA.FileFormats/Folder.cs create mode 100644 OpenRA.FileFormats/Format2.cs create mode 100644 OpenRA.FileFormats/Format40.cs create mode 100644 OpenRA.FileFormats/Format80.cs create mode 100755 OpenRA.FileFormats/Graphics/IGraphicsDevice.cs create mode 100644 OpenRA.FileFormats/Graphics/Vertex.cs create mode 100644 OpenRA.FileFormats/IPaletteRemap.cs create mode 100644 OpenRA.FileFormats/IniFile.cs create mode 100644 OpenRA.FileFormats/Lazy.cs create mode 100644 OpenRA.FileFormats/Map.cs create mode 100755 OpenRA.FileFormats/MiniYaml.cs create mode 100644 OpenRA.FileFormats/OpenRA.FileFormats.csproj create mode 100644 OpenRA.FileFormats/Package.cs create mode 100644 OpenRA.FileFormats/PackageEntry.cs create mode 100644 OpenRA.FileFormats/Pair.cs create mode 100644 OpenRA.FileFormats/Palette.cs create mode 100644 OpenRA.FileFormats/PlayerColorRemap.cs create mode 100644 OpenRA.FileFormats/PriorityQueue.cs create mode 100644 OpenRA.FileFormats/Properties/AssemblyInfo.cs create mode 100644 OpenRA.FileFormats/ProtocolVersion.cs create mode 100644 OpenRA.FileFormats/Session.cs create mode 100644 OpenRA.FileFormats/ShpReader.cs create mode 100644 OpenRA.FileFormats/ShroudPaletteRemap.cs create mode 100644 OpenRA.FileFormats/SingleColorRemap.cs create mode 100755 OpenRA.FileFormats/Support/Log.cs create mode 100755 OpenRA.FileFormats/Support/Stopwatch.cs create mode 100755 OpenRA.FileFormats/Support/Timer.cs create mode 100644 OpenRA.FileFormats/Terrain.cs create mode 100644 OpenRA.FileFormats/TileReference.cs create mode 100644 OpenRA.FileFormats/TileSet.cs create mode 100644 OpenRA.FileFormats/TreeReference.cs create mode 100644 OpenRA.FileFormats/Tuple.cs create mode 100644 OpenRA.FileFormats/TypeDictionary.cs create mode 100644 OpenRA.FileFormats/Walkability.cs create mode 100644 OpenRA.FileFormats/float2.cs create mode 100644 OpenRA.FileFormats/int2.cs create mode 100755 OpenRA.Game/Actor.cs create mode 100644 OpenRA.Game/Chat.cs create mode 100644 OpenRA.Game/Chrome.cs create mode 100644 OpenRA.Game/Combat.cs create mode 100644 OpenRA.Game/Controller.cs create mode 100644 OpenRA.Game/Cursor.cs create mode 100755 OpenRA.Game/Effects/Bullet.cs create mode 100755 OpenRA.Game/Effects/Corpse.cs create mode 100755 OpenRA.Game/Effects/DelayedAction.cs create mode 100755 OpenRA.Game/Effects/Explosion.cs create mode 100755 OpenRA.Game/Effects/FlashTarget.cs create mode 100755 OpenRA.Game/Effects/IEffect.cs create mode 100644 OpenRA.Game/Effects/LaserZap.cs create mode 100755 OpenRA.Game/Effects/Missile.cs create mode 100755 OpenRA.Game/Effects/MoveFlash.cs create mode 100755 OpenRA.Game/Effects/RepairIndicator.cs create mode 100755 OpenRA.Game/Effects/Smoke.cs create mode 100755 OpenRA.Game/Effects/TeslaZap.cs create mode 100644 OpenRA.Game/Exts.cs create mode 100644 OpenRA.Game/Game.cs create mode 100644 OpenRA.Game/GameRules/ActorInfo.cs create mode 100644 OpenRA.Game/GameRules/Footprint.cs create mode 100644 OpenRA.Game/GameRules/GeneralInfo.cs create mode 100644 OpenRA.Game/GameRules/InfoLoader.cs create mode 100644 OpenRA.Game/GameRules/ProjectileInfo.cs create mode 100755 OpenRA.Game/GameRules/Rules.cs create mode 100755 OpenRA.Game/GameRules/TechTree.cs create mode 100644 OpenRA.Game/GameRules/UserSettings.cs create mode 100644 OpenRA.Game/GameRules/VoiceInfo.cs create mode 100644 OpenRA.Game/GameRules/WarheadInfo.cs create mode 100755 OpenRA.Game/GameRules/WeaponInfo.cs create mode 100644 OpenRA.Game/Graphics/Animation.cs create mode 100644 OpenRA.Game/Graphics/ChromeProvider.cs create mode 100644 OpenRA.Game/Graphics/CursorSequence.cs create mode 100644 OpenRA.Game/Graphics/CursorSheetBuilder.cs create mode 100644 OpenRA.Game/Graphics/HardwarePalette.cs create mode 100644 OpenRA.Game/Graphics/LineRenderer.cs create mode 100644 OpenRA.Game/Graphics/MappedImage.cs create mode 100644 OpenRA.Game/Graphics/Minimap.cs create mode 100755 OpenRA.Game/Graphics/OverlayRenderer.cs create mode 100644 OpenRA.Game/Graphics/Renderer.cs create mode 100644 OpenRA.Game/Graphics/Sequence.cs create mode 100644 OpenRA.Game/Graphics/SequenceProvider.cs create mode 100644 OpenRA.Game/Graphics/Sheet.cs create mode 100644 OpenRA.Game/Graphics/SheetBuilder.cs create mode 100644 OpenRA.Game/Graphics/Sprite.cs create mode 100644 OpenRA.Game/Graphics/SpriteFont.cs create mode 100644 OpenRA.Game/Graphics/SpriteRenderer.cs create mode 100644 OpenRA.Game/Graphics/SpriteSheetBuilder.cs create mode 100644 OpenRA.Game/Graphics/TerrainRenderer.cs create mode 100644 OpenRA.Game/Graphics/Util.cs create mode 100644 OpenRA.Game/Graphics/Viewport.cs create mode 100644 OpenRA.Game/Graphics/WorldRenderer.cs create mode 100755 OpenRA.Game/MainWindow.cs create mode 100755 OpenRA.Game/Network/Connection.cs create mode 100755 OpenRA.Game/Network/Order.cs create mode 100755 OpenRA.Game/Network/OrderIO.cs create mode 100755 OpenRA.Game/Network/OrderManager.cs create mode 100755 OpenRA.Game/Network/UnitOrders.cs create mode 100644 OpenRA.Game/OpenRA.Game.csproj create mode 100644 OpenRA.Game/OpenRA.ico create mode 100644 OpenRA.Game/Orders/IOrderGenerator.cs create mode 100644 OpenRA.Game/Orders/PlaceBuildingOrderGenerator.cs create mode 100644 OpenRA.Game/Orders/PowerDownOrderGenerator.cs create mode 100644 OpenRA.Game/Orders/RepairOrderGenerator.cs create mode 100644 OpenRA.Game/Orders/SellOrderGenerator.cs create mode 100644 OpenRA.Game/Orders/UnitOrderGenerator.cs create mode 100644 OpenRA.Game/Ore.cs create mode 100644 OpenRA.Game/PackageDownloader.cs create mode 100644 OpenRA.Game/PathFinder.cs create mode 100755 OpenRA.Game/PathSearch.cs create mode 100644 OpenRA.Game/Player.cs create mode 100644 OpenRA.Game/Properties/AssemblyInfo.cs create mode 100644 OpenRA.Game/Selection.cs create mode 100644 OpenRA.Game/Shroud.cs create mode 100644 OpenRA.Game/Smudge.cs create mode 100644 OpenRA.Game/Sound.cs create mode 100644 OpenRA.Game/Support/PerfHistory.cs create mode 100644 OpenRA.Game/Support/Program.cs create mode 100644 OpenRA.Game/Support/Settings.cs create mode 100755 OpenRA.Game/Sync.cs create mode 100644 OpenRA.Game/TerrainCosts.cs create mode 100644 OpenRA.Game/Traits/AI/AutoHeal.cs create mode 100644 OpenRA.Game/Traits/AI/AutoTarget.cs create mode 100644 OpenRA.Game/Traits/AI/SelfHealing.cs create mode 100644 OpenRA.Game/Traits/AI/TakeCover.cs create mode 100644 OpenRA.Game/Traits/Activities/Attack.cs create mode 100644 OpenRA.Game/Traits/Activities/CallFunc.cs create mode 100644 OpenRA.Game/Traits/Activities/DeliverOre.cs create mode 100644 OpenRA.Game/Traits/Activities/EnterTransport.cs create mode 100644 OpenRA.Game/Traits/Activities/Fly.cs create mode 100644 OpenRA.Game/Traits/Activities/FlyAttack.cs create mode 100644 OpenRA.Game/Traits/Activities/FlyTimed.cs create mode 100644 OpenRA.Game/Traits/Activities/Follow.cs create mode 100644 OpenRA.Game/Traits/Activities/Harvest.cs create mode 100644 OpenRA.Game/Traits/Activities/HeliAttack.cs create mode 100644 OpenRA.Game/Traits/Activities/HeliFly.cs create mode 100644 OpenRA.Game/Traits/Activities/HeliLand.cs create mode 100644 OpenRA.Game/Traits/Activities/HeliReturn.cs create mode 100644 OpenRA.Game/Traits/Activities/Idle.cs create mode 100644 OpenRA.Game/Traits/Activities/Land.cs create mode 100755 OpenRA.Game/Traits/Activities/Move.cs create mode 100644 OpenRA.Game/Traits/Activities/Rearm.cs create mode 100644 OpenRA.Game/Traits/Activities/RemoveSelf.cs create mode 100644 OpenRA.Game/Traits/Activities/Repair.cs create mode 100644 OpenRA.Game/Traits/Activities/ReturnToBase.cs create mode 100644 OpenRA.Game/Traits/Activities/Sell.cs create mode 100644 OpenRA.Game/Traits/Activities/Teleport.cs create mode 100644 OpenRA.Game/Traits/Activities/TransformIntoActor.cs create mode 100755 OpenRA.Game/Traits/Activities/Turn.cs create mode 100644 OpenRA.Game/Traits/Activities/UndeployMcv.cs create mode 100644 OpenRA.Game/Traits/Activities/UnloadCargo.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackBase.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackFrontal.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackHeli.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackInfo.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackOmni.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackPlane.cs create mode 100644 OpenRA.Game/Traits/Attack/AttackTurreted.cs create mode 100644 OpenRA.Game/Traits/Bridge.cs create mode 100755 OpenRA.Game/Traits/Buildable.cs create mode 100644 OpenRA.Game/Traits/Building.cs create mode 100644 OpenRA.Game/Traits/CanPowerDown.cs create mode 100644 OpenRA.Game/Traits/Cargo.cs create mode 100644 OpenRA.Game/Traits/Chrome/PowerDownButton.cs create mode 100644 OpenRA.Game/Traits/Chronoshiftable.cs create mode 100644 OpenRA.Game/Traits/ConstructionYard.cs create mode 100644 OpenRA.Game/Traits/Crate.cs create mode 100644 OpenRA.Game/Traits/CustomSellValue.cs create mode 100644 OpenRA.Game/Traits/Explodes.cs create mode 100644 OpenRA.Game/Traits/Fake.cs create mode 100644 OpenRA.Game/Traits/GeneratesGap.cs create mode 100644 OpenRA.Game/Traits/Harvester.cs create mode 100644 OpenRA.Game/Traits/Helicopter.cs create mode 100644 OpenRA.Game/Traits/LimitedAmmo.cs create mode 100644 OpenRA.Game/Traits/Mobile.cs create mode 100644 OpenRA.Game/Traits/Modifiers/BelowUnits.cs create mode 100644 OpenRA.Game/Traits/Modifiers/Cloak.cs create mode 100644 OpenRA.Game/Traits/Modifiers/InvisibleToOthers.cs create mode 100644 OpenRA.Game/Traits/Modifiers/WithShadow.cs create mode 100644 OpenRA.Game/Traits/OreRefinery.cs create mode 100644 OpenRA.Game/Traits/Passenger.cs create mode 100644 OpenRA.Game/Traits/Plane.cs create mode 100644 OpenRA.Game/Traits/Player/EvaAlerts.cs create mode 100644 OpenRA.Game/Traits/Player/PlaceBuilding.cs create mode 100644 OpenRA.Game/Traits/Player/ProductionQueue.cs create mode 100644 OpenRA.Game/Traits/Player/SpawnDefaultUnits.cs create mode 100755 OpenRA.Game/Traits/Production.cs create mode 100644 OpenRA.Game/Traits/ProductionSurround.cs create mode 100644 OpenRA.Game/Traits/ProvidesRadar.cs create mode 100644 OpenRA.Game/Traits/RallyPoint.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuilding.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuildingCharge.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuildingOre.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuildingTurreted.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuildingWall.cs create mode 100644 OpenRA.Game/Traits/Render/RenderBuildingWarFactory.cs create mode 100644 OpenRA.Game/Traits/Render/RenderInfantry.cs create mode 100644 OpenRA.Game/Traits/Render/RenderSimple.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnit.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnitMuzzleFlash.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnitReload.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnitRotor.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnitSpinner.cs create mode 100644 OpenRA.Game/Traits/Render/RenderUnitTurreted.cs create mode 100644 OpenRA.Game/Traits/Repairable.cs create mode 100644 OpenRA.Game/Traits/Reservable.cs create mode 100644 OpenRA.Game/Traits/SeedsOre.cs create mode 100755 OpenRA.Game/Traits/Selectable.cs create mode 100644 OpenRA.Game/Traits/SquishByTank.cs create mode 100644 OpenRA.Game/Traits/StoresOre.cs create mode 100644 OpenRA.Game/Traits/Submarine.cs create mode 100644 OpenRA.Game/Traits/SupportPowers/ChronoshiftPower.cs create mode 100644 OpenRA.Game/Traits/SupportPowers/NukePower.cs create mode 100644 OpenRA.Game/Traits/SupportPowers/SupportPower.cs create mode 100644 OpenRA.Game/Traits/TraitsInterfaces.cs create mode 100644 OpenRA.Game/Traits/TransformsOnDeploy.cs create mode 100644 OpenRA.Game/Traits/Turreted.cs create mode 100755 OpenRA.Game/Traits/Unit.cs create mode 100755 OpenRA.Game/Traits/Util.cs create mode 100644 OpenRA.Game/Traits/Wall.cs create mode 100644 OpenRA.Game/Traits/World/BridgeLoadHook.cs create mode 100644 OpenRA.Game/Traits/World/BuildingInfluence.cs create mode 100644 OpenRA.Game/Traits/World/ChoosePaletteOnSelect.cs create mode 100644 OpenRA.Game/Traits/World/ChronoshiftPaletteEffect.cs create mode 100644 OpenRA.Game/Traits/World/Country.cs create mode 100644 OpenRA.Game/Traits/World/CrateSpawner.cs create mode 100644 OpenRA.Game/Traits/World/LightPaletteRotator.cs create mode 100644 OpenRA.Game/Traits/World/OreGrowth.cs create mode 100644 OpenRA.Game/Traits/World/PaletteFromFile.cs create mode 100644 OpenRA.Game/Traits/World/PaletteFromRGBA.cs create mode 100644 OpenRA.Game/Traits/World/PaletteFromRemap.cs create mode 100644 OpenRA.Game/Traits/World/PlayerColorPalette.cs create mode 100644 OpenRA.Game/Traits/World/ScreenShaker.cs create mode 100644 OpenRA.Game/Traits/World/ShroudPalette.cs create mode 100644 OpenRA.Game/Traits/World/UnitInfluence.cs create mode 100644 OpenRA.Game/Traits/World/WaterPaletteRotation.cs create mode 100644 OpenRA.Game/UiOverlay.cs create mode 100644 OpenRA.Game/World.cs create mode 100755 OpenRA.Game/WorldUtils.cs create mode 100644 OpenRA.Gl/GraphicsDevice.cs create mode 100644 OpenRA.Gl/OpenRA.Gl.csproj create mode 100644 OpenRA.Gl/Properties/AssemblyInfo.cs create mode 100644 OpenRA.Mods.Aftermath/ChronoshiftDeploy.cs create mode 100644 OpenRA.Mods.Aftermath/DemoTruck.cs create mode 100644 OpenRA.Mods.Aftermath/OpenRA.Mods.Aftermath.csproj create mode 100644 OpenRA.Mods.Aftermath/Orders/SetChronoTankDestination.cs create mode 100644 OpenRA.Mods.Aftermath/Properties/AssemblyInfo.cs create mode 100644 OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj create mode 100644 OpenRA.Mods.Cnc/ProductionAirdrop.cs create mode 100644 OpenRA.Mods.Cnc/Properties/AssemblyInfo.cs create mode 100644 OpenRA.Mods.Cnc/TiberiumRefinery.cs create mode 100644 OpenRA.Mods.RA/Activities/CaptureBuilding.cs create mode 100644 OpenRA.Mods.RA/Activities/Demolish.cs create mode 100644 OpenRA.Mods.RA/Activities/Infiltrate.cs create mode 100755 OpenRA.Mods.RA/Activities/LayMine.cs create mode 100644 OpenRA.Mods.RA/Activities/Steal.cs create mode 100644 OpenRA.Mods.RA/C4Demolition.cs create mode 100644 OpenRA.Mods.RA/Crates/ArmorUpgradeCrateAction.cs create mode 100644 OpenRA.Mods.RA/Crates/FirepowerUpgradeCrateAction.cs create mode 100644 OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs create mode 100644 OpenRA.Mods.RA/Crates/SpeedUpgradeCrateAction.cs create mode 100644 OpenRA.Mods.RA/Effects/CrateEffect.cs create mode 100644 OpenRA.Mods.RA/Effects/GpsSatellite.cs create mode 100644 OpenRA.Mods.RA/Effects/InvulnEffect.cs create mode 100644 OpenRA.Mods.RA/Effects/Parachute.cs create mode 100644 OpenRA.Mods.RA/Effects/SatelliteLaunch.cs create mode 100644 OpenRA.Mods.RA/EngineerCapture.cs create mode 100644 OpenRA.Mods.RA/GpsPower.cs create mode 100644 OpenRA.Mods.RA/InfiltrateForSonarPulse.cs create mode 100644 OpenRA.Mods.RA/IronCurtainPower.cs create mode 100644 OpenRA.Mods.RA/IronCurtainable.cs create mode 100644 OpenRA.Mods.RA/Mine.cs create mode 100644 OpenRA.Mods.RA/Minelayer.cs create mode 100644 OpenRA.Mods.RA/OpenRA.Mods.RA.csproj create mode 100644 OpenRA.Mods.RA/ParaDrop.cs create mode 100644 OpenRA.Mods.RA/ParatroopersPower.cs create mode 100644 OpenRA.Mods.RA/Properties/AssemblyInfo.cs create mode 100644 OpenRA.Mods.RA/RenderSpy.cs create mode 100644 OpenRA.Mods.RA/RepairableNear.cs create mode 100644 OpenRA.Mods.RA/RequiresPower.cs create mode 100644 OpenRA.Mods.RA/SonarPulsePower.cs create mode 100644 OpenRA.Mods.RA/Spy.cs create mode 100644 OpenRA.Mods.RA/SpyPlanePower.cs create mode 100644 OpenRA.Mods.RA/Thief.cs create mode 100644 OpenRA.sln diff --git a/Makefile b/Makefile index 0418e292da..ebe3b6244a 100644 --- a/Makefile +++ b/Makefile @@ -5,41 +5,41 @@ PROGRAMS =fileformats gl game ra cnc aftermath server seqed COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll -fileformats_SRCS = $(shell find OpenRa.FileFormats/ -iname '*.cs') -fileformats_TARGET = OpenRa.FileFormats.dll +fileformats_SRCS = $(shell find OpenRA.FileFormats/ -iname '*.cs') +fileformats_TARGET = OpenRA.FileFormats.dll fileformats_KIND = library fileformats_LIBS = $(COMMON_LIBS) thirdparty/Tao/Tao.Sdl.dll -gl_SRCS = $(shell find OpenRa.Gl/ -iname '*.cs') -gl_TARGET = OpenRa.Gl.dll +gl_SRCS = $(shell find OpenRA.Gl/ -iname '*.cs') +gl_TARGET = OpenRA.Gl.dll gl_KIND = library gl_DEPS = $(fileformats_TARGET) $(game_TARGET) gl_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll \ thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.Sdl.dll \ $(gl_DEPS) $(game_TARGET) -game_SRCS = $(shell find OpenRa.Game/ -iname '*.cs') -game_TARGET = OpenRa.Game.exe +game_SRCS = $(shell find OpenRA.Game/ -iname '*.cs') +game_TARGET = OpenRA.Game.exe game_KIND = winexe game_DEPS = $(fileformats_TARGET) game_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(game_DEPS) \ thirdparty/Tao/Tao.OpenAl.dll thirdparty/Tao/Tao.FreeType.dll -game_FLAGS = -win32icon:OpenRa.Game/OpenRa.ico +game_FLAGS = -win32icon:OpenRA.Game/OpenRA.ico -ra_SRCS = $(shell find OpenRa.Mods.RA/ -iname '*.cs') -ra_TARGET = mods/ra/OpenRa.Mods.RA.dll +ra_SRCS = $(shell find OpenRA.Mods.RA/ -iname '*.cs') +ra_TARGET = mods/ra/OpenRA.Mods.RA.dll ra_KIND = library ra_DEPS = $(fileformats_TARGET) $(game_TARGET) ra_LIBS = $(COMMON_LIBS) $(ra_DEPS) -cnc_SRCS = $(shell find OpenRa.Mods.Cnc/ -iname '*.cs') -cnc_TARGET = mods/cnc/OpenRa.Mods.Cnc.dll +cnc_SRCS = $(shell find OpenRA.Mods.Cnc/ -iname '*.cs') +cnc_TARGET = mods/cnc/OpenRA.Mods.Cnc.dll cnc_KIND = library cnc_DEPS = $(fileformats_TARGET) $(game_TARGET) cnc_LIBS = $(COMMON_LIBS) $(cnc_DEPS) -aftermath_SRCS = $(shell find OpenRa.Mods.Aftermath/ -iname '*.cs') -aftermath_TARGET = mods/aftermath/OpenRa.Mods.Aftermath.dll +aftermath_SRCS = $(shell find OpenRA.Mods.Aftermath/ -iname '*.cs') +aftermath_TARGET = mods/aftermath/OpenRA.Mods.Aftermath.dll aftermath_KIND = library aftermath_DEPS = $(fileformats_TARGET) $(game_TARGET) aftermath_LIBS = $(COMMON_LIBS) $(aftermath_DEPS) diff --git a/OpenRA.FileFormats/AudLoader.cs b/OpenRA.FileFormats/AudLoader.cs new file mode 100644 index 0000000000..9bc8c84d10 --- /dev/null +++ b/OpenRA.FileFormats/AudLoader.cs @@ -0,0 +1,125 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.IO; + +namespace OpenRA.FileFormats +{ + [Flags] + enum SoundFlags + { + Stereo = 0x1, + _16Bit = 0x2, + } + + enum SoundFormat + { + WestwoodCompressed = 1, + ImaAdpcm = 99, + } + + struct Chunk + { + public int CompressedSize; + public int OutputSize; + + public static Chunk Read(BinaryReader r) + { + Chunk c; + c.CompressedSize = r.ReadUInt16(); + c.OutputSize = r.ReadUInt16(); + if (0xdeaf != r.ReadUInt32()) + throw new InvalidDataException("Chunk header is bogus"); + return c; + } + } + + public static class AudLoader + { + static int[] IndexAdjust = { -1, -1, -1, -1, 2, 4, 6, 8 }; + static int[] StepTable = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, + 17, 19, 21, 23, 25, 28, 31, 34, 37, + 41, 45, 50, 55, 60, 66, 73, 80, 88, + 97, 107, 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, 449, 494, + 544, 598, 658, 724, 796, 876, 963, 1060, 1166, + 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, + 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, + 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; + + static short DecodeSample(byte b, ref int index, ref int current) + { + var sb = (b & 8) != 0; + b &= 7; + + var delta = (StepTable[index] * b) / 4 + StepTable[index] / 8; + if (sb) delta = -delta; + + current += delta; + if (current > short.MaxValue) current = short.MaxValue; + if (current < short.MinValue) current = short.MinValue; + + index += IndexAdjust[b]; + if (index < 0) index = 0; + if (index > 88) index = 88; + + return (short)current; + } + + public static byte[] LoadSound(Stream s) + { + var br = new BinaryReader(s); + /*var sampleRate =*/ br.ReadUInt16(); + var dataSize = br.ReadInt32(); + var outputSize = br.ReadInt32(); + /*var flags = (SoundFlags)*/ br.ReadByte(); + /*var format = (SoundFormat)*/ br.ReadByte(); + + var output = new byte[outputSize]; + var offset = 0; + var index = 0; + var currentSample = 0; + + while (dataSize > 0) + { + var chunk = Chunk.Read(br); + for (int n = 0; n < chunk.CompressedSize; n++) + { + var b = br.ReadByte(); + + var t = DecodeSample(b, ref index, ref currentSample); + output[offset++] = (byte)t; + output[offset++] = (byte)(t >> 8); + + t = DecodeSample((byte)(b >> 4), ref index, ref currentSample); + output[offset++] = (byte)t; + output[offset++] = (byte)(t >> 8); + } + + dataSize -= 8 + chunk.CompressedSize; + } + + return output; + } + } +} diff --git a/OpenRA.FileFormats/Blowfish.cs b/OpenRA.FileFormats/Blowfish.cs new file mode 100644 index 0000000000..72d6b62787 --- /dev/null +++ b/OpenRA.FileFormats/Blowfish.cs @@ -0,0 +1,416 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + class Blowfish + { + public Blowfish(byte[] key) + { + for (int i = 0, j = 0; i < 18; ++i) + { + uint a = key[j++ % key.Length]; + uint b = key[j++ % key.Length]; + uint c = key[j++ % key.Length]; + uint d = key[j++ % key.Length]; + + m_p[i] ^= a << 24 | b << 16 | c << 8 | d; + } + + uint l = 0, r = 0; + + for (int i = 0; i < 18; ) + { + Encrypt(ref l, ref r); + m_p[i++] = l; + m_p[i++] = r; + } + + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 256; ) + { + Encrypt(ref l, ref r); + m_s[i, j++] = l; + m_s[i, j++] = r; + } + } + + public uint[] Encrypt(uint[] data) { return RunCipher(data, Encrypt); } + public uint[] Decrypt(uint[] data) { return RunCipher(data, Decrypt); } + + delegate void CipherFunc( ref uint a, ref uint b ); + + uint[] RunCipher(uint[] data, CipherFunc f) + { + uint[] result = new uint[data.Length]; + + int size = data.Length / 2; + int i = 0; + while (size-- > 0) + { + uint a = SwapBytes(data[i]); + uint b = SwapBytes(data[i+1]); + + f(ref a, ref b); + + result[i++] = SwapBytes(a); + result[i++] = SwapBytes(b); + } + + return result; + } + + void Encrypt(ref uint a, ref uint b) + { + uint _a = a, _b = b; + _a ^= m_p[0]; + + bool x = false; + for( int i = 1; i <= 16; i++, x ^= true) + { + if (x) + Round(ref _a, _b, i); + else + Round(ref _b, _a, i); + } + _b ^= m_p[17]; + + a = _b; + b = _a; + } + + void Decrypt(ref uint a, ref uint b) + { + uint _a = a, _b = b; + _a ^= m_p[17]; + + bool x = false; + for (int i = 16; i >= 1; i--, x ^= true) + { + if (x) + Round(ref _a, _b, i); + else + Round(ref _b, _a, i); + } + _b ^= m_p[0]; + + a = _b; + b = _a; + } + + uint S(uint x, int i) + { + return m_s[i, (x >> ((3 - i) << 3)) & 0xff]; + } + + uint bf_f(uint x) + { + return ((S(x, 0) + S(x, 1)) ^ S(x, 2)) + S(x, 3); + } + + void Round(ref uint a, uint b, int n) + { + a ^= bf_f(b) ^ m_p[n]; + } + + uint SwapBytes(uint i) + { + i = (i << 16) | (i >> 16); + i = ((i << 8) & 0xff00ff00) | ((i >> 8) & 0x00ff00ff); + return i; + } + + uint[] m_p = new uint[] { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + }; + + uint[,] m_s = new uint[,] { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + }, + { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + }, + { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + }, + { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + } + + }; + } +} diff --git a/OpenRA.FileFormats/BlowfishKeyProvider.cs b/OpenRA.FileFormats/BlowfishKeyProvider.cs new file mode 100644 index 0000000000..759af0a08a --- /dev/null +++ b/OpenRA.FileFormats/BlowfishKeyProvider.cs @@ -0,0 +1,538 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; + +namespace OpenRA.FileFormats +{ + /* possibly the fugliest C# i've ever seen. */ + + class BlowfishKeyProvider + { + const string pubkeyStr = "AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V"; + + static sbyte[] char2num = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + class PublicKey + { + public uint[] key1 = new uint[64]; + public uint[] key2 = new uint[64]; + public uint len; + } + PublicKey pubkey = new PublicKey(); + + uint[] glob1 = new uint[64]; + uint glob1_bitlen, glob1_len_x2; + uint[] glob2 = new uint[130]; + uint[] glob1_hi = new uint[4]; + uint[] glob1_hi_inv = new uint[4]; + uint glob1_hi_bitlen; + uint glob1_hi_inv_lo, glob1_hi_inv_hi; + + void init_bignum(uint[] n, uint val, uint len) + { + for (int i = 0; i < len; i++) n[i] = 0; + n[0] = val; + } + + void move_key_to_big(uint[] n, byte[] key, uint klen, uint blen) + { + byte sign; + + if ((key[0] & 0x80) != 0) sign = 0xff; + else sign = 0; + + unsafe + { + fixed (uint* _pn = &n[0]) + { + byte* pn = (byte*)_pn; + uint i = blen * 4; + for (; i > klen; i--) pn[i - 1] = (byte)sign; + for (; i > 0; i--) pn[i - 1] = key[klen - i]; + } + } + } + + void key_to_bignum(uint[] n, byte[] key, uint len) + { + uint keylen; + int i; + + int j = 0; + + if (key[j] != 2) return; + j++; + + if ((key[j] & 0x80) != 0) + { + keylen = 0; + for (i = 0; i < (key[j] & 0x7f); i++) keylen = (keylen << 8) | key[j + i + 1]; + j += (key[j] & 0x7f) + 1; + } + else + { + keylen = key[j]; + j++; + } + if (keylen <= len * 4) + move_key_to_big(n, key.Skip(j).ToArray(), keylen, len); + } + + uint len_bignum(uint[] n, uint len) + { + uint i; + i = len - 1; + while ((i >= 0) && (n[i] == 0)) i--; + return i + 1; + } + + uint bitlen_bignum(uint[] n, uint len) + { + uint ddlen, bitlen, mask; + ddlen = len_bignum(n, len); + if (ddlen == 0) return 0; + bitlen = ddlen * 32; + mask = 0x80000000; + while ((mask & n[ddlen - 1]) == 0) + { + mask >>= 1; + bitlen--; + } + return bitlen; + } + + void init_pubkey() + { + int i = 0; + uint i2, tmp; + byte[] keytmp = new byte[256]; + + init_bignum(pubkey.key2, 0x10001, 64); + + i2 = 0; + while (i < pubkeyStr.Length) + { + tmp = (uint)char2num[pubkeyStr[i++]]; + tmp <<= 6; tmp |= (uint)(byte)char2num[pubkeyStr[i++]]; + tmp <<= 6; tmp |= (uint)(byte)char2num[pubkeyStr[i++]]; + tmp <<= 6; tmp |= (uint)(byte)char2num[pubkeyStr[i++]]; + keytmp[i2++] = (byte)((tmp >> 16) & 0xff); + keytmp[i2++] = (byte)((tmp >> 8) & 0xff); + keytmp[i2++] = (byte)(tmp & 0xff); + } + + key_to_bignum(pubkey.key1, keytmp, 64); + pubkey.len = bitlen_bignum(pubkey.key1, 64) - 1; + } + + uint len_predata() + { + uint a = (pubkey.len - 1) / 8; + return (55 / a + 1) * (a + 1); + } + + int cmp_bignum(uint[] n1, uint[] n2, uint len) + { + + while (len > 0) + { + --len; + if (n1[len] < n2[len]) return -1; + if (n1[len] > n2[len]) return 1; + } + return 0; + } + + void mov_bignum(uint[] dest, uint[] src, uint len) + { + Array.Copy(src, dest, len); + } + + void shr_bignum(uint[] n, int bits, int len) + { + int i; int i2 = bits / 32; + + if (i2 > 0) + { + for (i = 0; i < len - i2; i++) n[i] = n[i + i2]; + for (; i < len; i++) n[i] = 0; + bits = bits % 32; + } + if (bits == 0) return; + for (i = 0; i < len - 1; i++) n[i] = (n[i] >> bits) | (n[i + 1] << (32 - + bits)); + n[i] = n[i] >> bits; + } + + void shl_bignum(uint[] n, int bits, int len) + { + int i, i2; + + i2 = bits / 32; + if (i2 > 0) + { + for (i = len - 1; i > i2; i--) n[i] = n[i - i2]; + for (; i > 0; i--) n[i] = 0; + bits = bits % 32; + } + if (bits == 0) return; + for (i = len - 1; i > 0; i--) n[i] = (n[i] << bits) | (n[i - 1] >> (32 - + bits)); + n[0] <<= bits; + } + + uint sub_bignum(uint[] dest, uint[] src1, uint[] src2, uint carry, int len) + { + uint i1, i2; + + len += len; + unsafe + { + fixed (uint* _ps1 = &src1[0]) + fixed (uint* _ps2 = &src2[0]) + fixed (uint* _pd = &dest[0]) + { + ushort* ps1 = (ushort*)_ps1; + ushort* ps2 = (ushort*)_ps2; + ushort* pd = (ushort*)_pd; + + while (--len != -1) + { + i1 = *ps1++; + i2 = *ps2++; + *pd++ = (ushort)(i1 - i2 - carry); + if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0; + } + } + } + return carry; + } + + unsafe uint sub_bignum(uint* dest, uint* src1, uint* src2, uint carry, int len) + { + uint i1, i2; + + len += len; + + ushort* ps1 = (ushort*)src1; + ushort* ps2 = (ushort*)src2; + ushort* pd = (ushort*)dest; + + while (--len != -1) + { + i1 = *ps1++; + i2 = *ps2++; + *pd++ = (ushort)(i1 - i2 - carry); + if (((i1 - i2 - carry) & 0x10000) != 0) carry = 1; else carry = 0; + + } + return carry; + } + + void inv_bignum(uint[] n1, uint[] n2, uint len) + { + uint[] n_tmp = new uint[64]; + uint n2_bytelen, bit; + int n2_bitlen; + + int j = 0; + + init_bignum(n_tmp, 0, len); + init_bignum(n1, 0, len); + n2_bitlen = (int)bitlen_bignum(n2, len); + bit = ((uint)1) << (n2_bitlen % 32); + j = ((n2_bitlen + 32) / 32) - 1; + n2_bytelen = (uint)((n2_bitlen - 1) / 32) * 4; + n_tmp[n2_bytelen / 4] |= ((uint)1) << ((n2_bitlen - 1) & 0x1f); + + while (n2_bitlen > 0) + { + n2_bitlen--; + shl_bignum(n_tmp, 1, (int)len); + if (cmp_bignum(n_tmp, n2, len) != -1) + { + sub_bignum(n_tmp, n_tmp, n2, 0, (int)len); + n1[j] |= bit; + } + bit >>= 1; + if (bit == 0) + { + j--; + bit = 0x80000000; + } + } + init_bignum(n_tmp, 0, len); + } + + void inc_bignum(uint[] n, uint len) + { + int i = 0; + while ((++n[i] == 0) && (--len > 0)) i++; + } + + void init_two_dw(uint[] n, uint len) + { + mov_bignum(glob1, n, len); + glob1_bitlen = bitlen_bignum(glob1, len); + glob1_len_x2 = (glob1_bitlen + 15) / 16; + mov_bignum(glob1_hi, glob1.Skip((int)len_bignum(glob1, len) - 2).ToArray(), 2); + glob1_hi_bitlen = bitlen_bignum(glob1_hi, 2) - 32; + shr_bignum(glob1_hi, (int)glob1_hi_bitlen, 2); + inv_bignum(glob1_hi_inv, glob1_hi, 2); + shr_bignum(glob1_hi_inv, 1, 2); + glob1_hi_bitlen = (glob1_hi_bitlen + 15) % 16 + 1; + inc_bignum(glob1_hi_inv, 2); + if (bitlen_bignum(glob1_hi_inv, 2) > 32) + { + shr_bignum(glob1_hi_inv, 1, 2); + glob1_hi_bitlen--; + } + glob1_hi_inv_lo = (ushort)glob1_hi_inv[0]; + glob1_hi_inv_hi = (ushort)(glob1_hi_inv[0] >> 16); + } + + unsafe void mul_bignum_word(ushort *pn1, uint[] n2, uint mul, uint len) + { + uint i, tmp; + unsafe + { + fixed (uint* _pn2 = &n2[0]) + { + ushort* pn2 = (ushort*)_pn2; + + tmp = 0; + for (i = 0; i < len; i++) + { + tmp = mul * (*pn2) + (*pn1) + tmp; + *pn1 = (ushort)tmp; + pn1++; + pn2++; + tmp >>= 16; + } + *pn1 += (ushort)tmp; + } + } + } + + void mul_bignum(uint[] dest, uint[] src1, uint[] src2, uint len) + { + uint i; + + unsafe + { + fixed( uint * _psrc2 = &src2[0] ) + fixed(uint* _pdest = &dest[0]) + { + ushort* psrc2 = (ushort*)_psrc2; + ushort* pdest = (ushort*)_pdest; + + init_bignum(dest, 0, len * 2); + for (i = 0; i < len * 2; i++) + mul_bignum_word(pdest++, src1, *psrc2++, len * 2); + } + } + } + + void not_bignum(uint[] n, uint len) + { + uint i; + for (i = 0; i < len; i++) n[i] = ~n[i]; + } + + void neg_bignum(uint[] n, uint len) + { + not_bignum(n, len); + inc_bignum(n, len); + } + + unsafe uint get_mulword(uint* n) + { + ushort* wn = (ushort*)n; + uint i = (uint)((((((((((*(wn - 1) ^ 0xffff) & 0xffff) * glob1_hi_inv_lo + 0x10000) >> 1) + + (((*(wn - 2) ^ 0xffff) * glob1_hi_inv_hi + glob1_hi_inv_hi) >> 1) + 1) + >> 16) + ((((*(wn - 1) ^ 0xffff) & 0xffff) * glob1_hi_inv_hi) >> 1) + + (((*wn ^ 0xffff) * glob1_hi_inv_lo) >> 1) + 1) >> 14) + glob1_hi_inv_hi + * (*wn ^ 0xffff) * 2) >> (int)glob1_hi_bitlen); + if (i > 0xffff) i = 0xffff; + return i & 0xffff; + } + + void dec_bignum(uint[] n, uint len) + { + int i = 0; + while ((--n[i] == 0xffffffff) && (--len > 0)) + i++; + } + + void calc_a_bignum(uint[] n1, uint[] n2, uint[] n3, uint len) + { + uint g2_len_x2, len_diff; + unsafe + { + fixed( uint* g1 = &glob1[0]) + fixed (uint* g2 = &glob2[0]) + { + mul_bignum(glob2, n2, n3, len); + glob2[len * 2] = 0; + g2_len_x2 = len_bignum(glob2, len * 2 + 1) * 2; + if (g2_len_x2 >= glob1_len_x2) + { + inc_bignum(glob2, len * 2 + 1); + neg_bignum(glob2, len * 2 + 1); + len_diff = g2_len_x2 + 1 - glob1_len_x2; + ushort* esi = ((ushort*)g2) + (1 + g2_len_x2 - glob1_len_x2); + ushort* edi = ((ushort*)g2) + (g2_len_x2 + 1); + for (; len_diff != 0; len_diff--) + { + edi--; + uint tmp = get_mulword((uint*)edi); + esi--; + if (tmp > 0) + { + mul_bignum_word(esi, glob1, tmp, 2 * len); + if ((*edi & 0x8000) == 0) + { + if (0 != sub_bignum((uint*)esi, (uint*)esi, g1, 0, (int)len)) (*edi)--; + } + } + } + neg_bignum(glob2, len); + dec_bignum(glob2, len); + } + mov_bignum(n1, glob2, len); + } + } + } + + void clear_tmp_vars(uint len) + { + init_bignum(glob1, 0, len); + init_bignum(glob2, 0, len); + init_bignum(glob1_hi_inv, 0, 4); + init_bignum(glob1_hi, 0, 4); + glob1_bitlen = 0; + glob1_hi_bitlen = 0; + glob1_len_x2 = 0; + glob1_hi_inv_lo = 0; + glob1_hi_inv_hi = 0; + } + + void calc_a_key(uint[] n1, uint[] n2, uint[] n3, uint[] n4, uint len) + { + uint[] n_tmp = new uint[64]; + uint n3_len, n4_len; + int n3_bitlen; + uint bit_mask; + + unsafe + { + fixed (uint* _pn3 = &n3[0]) + { + uint* pn3 = _pn3; + + init_bignum(n1, 1, len); + n4_len = len_bignum(n4, len); + init_two_dw(n4, n4_len); + n3_bitlen = (int)bitlen_bignum(n3, n4_len); + n3_len = (uint)((n3_bitlen + 31) / 32); + bit_mask = (((uint)1) << ((n3_bitlen - 1) % 32)) >> 1; + pn3 += n3_len - 1; + n3_bitlen--; + mov_bignum(n1, n2, n4_len); + while (--n3_bitlen != -1) + { + if (bit_mask == 0) + { + bit_mask = 0x80000000; + pn3--; + } + calc_a_bignum(n_tmp, n1, n1, n4_len); + if ((*pn3 & bit_mask) != 0) + calc_a_bignum(n1, n_tmp, n2, n4_len); + else + mov_bignum(n1, n_tmp, n4_len); + bit_mask >>= 1; + } + init_bignum(n_tmp, 0, n4_len); + clear_tmp_vars(len); + } + } + } + + unsafe void memcpy(byte* dest, byte* src, int len) + { + while (len-- != 0) *dest++ = *src++; + } + + unsafe void process_predata(byte* pre, uint pre_len, byte *buf) + { + uint[] n2 = new uint[64]; + uint[] n3 = new uint[64]; + + uint a = (pubkey.len - 1) / 8; + while (a + 1 <= pre_len) + { + init_bignum(n2, 0, 64); + fixed( uint * pn2 = &n2[0] ) + memcpy((byte *)pn2, pre, (int)a + 1); + calc_a_key(n3, n2, pubkey.key2, pubkey.key1, 64); + + fixed( uint * pn3 = &n3[0] ) + memcpy(buf, (byte *)pn3, (int)a); + + pre_len -= a + 1; + pre += a + 1; + buf += a; + } + } + + public byte[] DecryptKey(byte[] src) + { + init_pubkey(); + byte[] dest = new byte[256]; + + unsafe + { + fixed (byte* pdest = &dest[0]) + fixed (byte* psrc = &src[0]) + process_predata(psrc, len_predata(), pdest); + } + return dest.Take(56).ToArray(); + } + } +} diff --git a/OpenRA.FileFormats/Cache.cs b/OpenRA.FileFormats/Cache.cs new file mode 100644 index 0000000000..92f0e0fa72 --- /dev/null +++ b/OpenRA.FileFormats/Cache.cs @@ -0,0 +1,59 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OpenRA.FileFormats +{ + public class Cache : IEnumerable> + { + Dictionary hax = new Dictionary(); + Func loader; + + public Cache(Func loader) + { + if (loader == null) + throw new ArgumentNullException(); + + this.loader = loader; + } + + public U this[T key] + { + get + { + U result; + if (!hax.TryGetValue(key, out result)) + hax.Add(key, result = loader(key)); + + return result; + } + } + + public IEnumerator> GetEnumerator() { return hax.GetEnumerator(); } + + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + public IEnumerable Keys { get { return hax.Keys; } } + public IEnumerable Values { get { return hax.Values; } } + } +} diff --git a/OpenRA.FileFormats/Collections/Set.cs b/OpenRA.FileFormats/Collections/Set.cs new file mode 100755 index 0000000000..3738562091 --- /dev/null +++ b/OpenRA.FileFormats/Collections/Set.cs @@ -0,0 +1,76 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OpenRA.Collections +{ + public class Set : IEnumerable + { + Dictionary data = new Dictionary(); + + public void Add( T obj ) + { + data.Add( obj, false ); + if( OnAdd != null ) + OnAdd( obj ); + } + + public void Remove( T obj ) + { + data.Remove( obj ); + if( OnRemove != null ) + OnRemove( obj ); + } + + public event Action OnAdd; + public event Action OnRemove; + + public IEnumerator GetEnumerator() + { + return data.Keys.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + public class CachedView : Set + { + public CachedView( Set set, Func include, Func store ) + : this( set, include, x => new[] { store( x ) } ) + { + } + + public CachedView( Set set, Func include, Func> store ) + { + foreach( var t in set ) + if( include( t ) ) + store( t ).Do( x => Add( x ) ); + + set.OnAdd += obj => { if( include( obj ) ) store( obj ).Do( x => Add( x ) ); }; + set.OnRemove += obj => { if( include( obj ) ) store( obj ).Do( x => Remove( x ) ); }; + } + } +} diff --git a/OpenRA.FileFormats/DisposableAction.cs b/OpenRA.FileFormats/DisposableAction.cs new file mode 100644 index 0000000000..ae1836e321 --- /dev/null +++ b/OpenRA.FileFormats/DisposableAction.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA +{ + public class DisposableAction : IDisposable + { + public DisposableAction(Action a) { this.a = a; } + + Action a; + bool disposed; + + public void Dispose() + { + if (disposed) return; + disposed = true; + a(); + GC.SuppressFinalize(this); + } + + ~DisposableAction() + { + Dispose(); + } + } +} diff --git a/OpenRA.FileFormats/Dune2ShpReader.cs b/OpenRA.FileFormats/Dune2ShpReader.cs new file mode 100644 index 0000000000..53060bbdfe --- /dev/null +++ b/OpenRA.FileFormats/Dune2ShpReader.cs @@ -0,0 +1,156 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace OpenRA.FileFormats +{ + public enum Dune2ImageFlags : int + { + F80_F2 = 0, + F2 = 2, + L16_F80_F2_1 = 1, + L16_F80_F2_2 = 3, + Ln_F80_F2 = 5 + } + + public class Dune2ImageHeader + { + public readonly Dune2ImageFlags Flags; + public readonly int Width; + public readonly int Height; + public readonly int Slices; + public readonly int FileSize; + public readonly int DataSize; + + public readonly byte[] LookupTable; + public byte[] Image; + + public Dune2ImageHeader(BinaryReader reader) + { + Flags = (Dune2ImageFlags)reader.ReadUInt16(); + Slices = reader.ReadByte(); + Width = reader.ReadUInt16(); + Height = reader.ReadByte(); + FileSize = reader.ReadUInt16(); + DataSize = reader.ReadUInt16(); + + if (Flags == Dune2ImageFlags.L16_F80_F2_1 || + Flags == Dune2ImageFlags.L16_F80_F2_2 || + Flags == Dune2ImageFlags.Ln_F80_F2) + { + int n = Flags == Dune2ImageFlags.Ln_F80_F2 ? reader.ReadByte() : (byte)16; + LookupTable = new byte[n]; + for (int i = 0; i < n; i++) + LookupTable[i] = reader.ReadByte(); + } + else + { + LookupTable = new byte[256]; + for (int i = 0; i < 256; i++) + LookupTable[i] = (byte)i; + LookupTable[1] = 0x7f; + LookupTable[2] = 0x7e; + LookupTable[3] = 0x7d; + LookupTable[4] = 0x7c; + } + } + + public Size Size + { + get { return new Size(Width, Height); } + } + } + + public class Dune2ShpReader : IEnumerable + { + public readonly int ImageCount; + + List headers = new List(); + + public Dune2ShpReader(Stream stream) + { + BinaryReader reader = new BinaryReader(stream); + + ImageCount = reader.ReadUInt16(); + + //Last offset is pointer to end of file. + uint[] offsets = new uint[ImageCount + 1]; + + uint temp = reader.ReadUInt32(); + + //If fourth byte in file is non-zero, the offsets are two bytes each. + bool twoByteOffsets = (temp & 0xFF0000) > 0; + if (twoByteOffsets) + { + offsets[0] = ((temp & 0xFFFF0000) >> 16) + 2; //Offset does not account for image count bytes + offsets[1] = (temp & 0xFFFF) + 2; + } + else + offsets[0] = temp + 2; + + for (int i = twoByteOffsets ? 2 : 1; i < ImageCount + 1; i++) + offsets[i] = (twoByteOffsets ? reader.ReadUInt16() : reader.ReadUInt32()) + 2; + + for (int i = 0; i < ImageCount; i++) + { + reader.BaseStream.Seek(offsets[i], SeekOrigin.Begin); + Dune2ImageHeader header = new Dune2ImageHeader(reader); + byte[] imgData = reader.ReadBytes(header.FileSize); + header.Image = new byte[header.Height * header.Width]; + + //Decode image data + if (header.Flags != Dune2ImageFlags.F2) + { + byte[] tempData = new byte[header.DataSize]; + Format80.DecodeInto(imgData, tempData); + Format2.DecodeInto(tempData, header.Image); + } + else + Format2.DecodeInto(imgData, header.Image); + + //Lookup values in lookup table + if (header.LookupTable != null) + for (int j = 0; j < header.Image.Length; j++) + header.Image[j] = header.LookupTable[header.Image[j]]; + + headers.Add(header); + } + } + + public Dune2ImageHeader this[int index] + { + get { return headers[index]; } + } + + public IEnumerator GetEnumerator() + { + return headers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/OpenRA.FileFormats/Exts.cs b/OpenRA.FileFormats/Exts.cs new file mode 100644 index 0000000000..992cce920a --- /dev/null +++ b/OpenRA.FileFormats/Exts.cs @@ -0,0 +1,39 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; + +namespace OpenRA +{ + public static class Exts + { + public static string F(this string fmt, params object[] args) + { + return string.Format(fmt, args); + } + + public static void Do( this IEnumerable e, Action fn ) + { + foreach( var ee in e ) + fn( ee ); + } + } +} diff --git a/OpenRA.FileFormats/FieldLoader.cs b/OpenRA.FileFormats/FieldLoader.cs new file mode 100644 index 0000000000..a1db3f89dc --- /dev/null +++ b/OpenRA.FileFormats/FieldLoader.cs @@ -0,0 +1,119 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using System.Reflection; + +namespace OpenRA.FileFormats +{ + public static class FieldLoader + { + public static void Load(object self, IniSection ini) + { + foreach (var x in ini) + { + var field = self.GetType().GetField(x.Key.Trim()); + if (field == null) + throw new NotImplementedException("Missing field `{0}` on `{1}`".F(x.Key.Trim(), self.GetType().Name)); + field.SetValue(self, GetValue(field.FieldType, x.Value.Trim())); + } + } + + public static void Load(object self, MiniYaml my) + { + foreach (var x in my.Nodes) + { + var field = self.GetType().GetField(x.Key.Trim()); + if (field == null) + throw new NotImplementedException("Missing field `{0}` on `{1}`".F(x.Key.Trim(), self.GetType().Name)); + field.SetValue(self, GetValue(field.FieldType, x.Value.Value)); + } + } + + public static object GetValue( Type fieldType, string x ) + { + if (x != null) x = x.Trim(); + if( fieldType == typeof( int ) ) + return int.Parse( x ); + + else if (fieldType == typeof(float)) + return float.Parse(x.Replace("%","")) * (x.Contains( '%' ) ? 0.01f : 1f); + + else if (fieldType == typeof(string)) + return x; + + else if (fieldType.IsEnum) + return Enum.Parse(fieldType, x, true); + + else if (fieldType == typeof(bool)) + return ParseYesNo(x); + + else if (fieldType.IsArray) + { + if (x == null) + return Array.CreateInstance(fieldType.GetElementType(), 0); + + var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length); + for (int i = 0; i < parts.Length; i++) + ret.SetValue(GetValue(fieldType.GetElementType(), parts[i].Trim()), i); + return ret; + } + else if (fieldType == typeof(int2)) + { + var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + return new int2(int.Parse(parts[0]), int.Parse(parts[1])); + } + else + throw new InvalidOperationException("FieldLoader: don't know how to load field of type " + fieldType.ToString()); + } + + static bool ParseYesNo( string p ) + { + p = p.ToLowerInvariant(); + if( p == "yes" ) return true; + if( p == "true" ) return true; + if( p == "no" ) return false; + if( p == "false" ) return false; + throw new InvalidOperationException(); + } + } + + public static class FieldSaver + { + public static MiniYaml Save(object o) + { + return new MiniYaml(null, o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) + .ToDictionary( + f => f.Name, + f => new MiniYaml(FormatValue(o, f)))); + } + + static string FormatValue(object o, FieldInfo f) + { + var v = f.GetValue(o); + return f.FieldType.IsArray + ? string.Join(",", ((Array)v).OfType().Select(a => a.ToString()).ToArray()) + : v.ToString(); + } + } +} diff --git a/OpenRA.FileFormats/FileSystem.cs b/OpenRA.FileFormats/FileSystem.cs new file mode 100644 index 0000000000..0e1d7672e3 --- /dev/null +++ b/OpenRA.FileFormats/FileSystem.cs @@ -0,0 +1,138 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; + +namespace OpenRA.FileFormats +{ + public static class FileSystem + { + static List mountedFolders = new List(); + + static Cache> allFiles = new Cache>( _ => new List() ); + + static void MountInner(IFolder folder) + { + mountedFolders.Add(folder); + + foreach( var hash in folder.AllFileHashes() ) + { + var l = allFiles[hash]; + if( !l.Contains( folder ) ) + l.Add( folder ); + } + } + + public static void Mount(string name) + { + name = name.ToLowerInvariant(); + var optional = name.StartsWith("~"); + if (optional) name = name.Substring(1); + + var a = name.EndsWith(".mix") + ? (Action)(() => FileSystem.MountInner(new Package(name))) + : () => FileSystem.MountInner(new Folder(name)); + + if (optional) + try { a(); } + catch { } + else + a(); + } + + public static void UnmountAll() + { + mountedFolders.Clear(); + allFiles = new Cache>( _ => new List() ); + } + + static Stream GetFromCache( Cache> index, string filename ) + { + foreach( var folder in index[ PackageEntry.HashFilename( filename ) ] ) + { + Stream s = folder.GetContent(filename); + if( s != null ) + return s; + } + return null; + } + + public static Stream Open(string filename) + { + if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 ) + { + var ret = GetFromCache( allFiles, filename ); + if( ret != null ) + return ret; + } + + foreach( IFolder folder in mountedFolders ) + { + Stream s = folder.GetContent(filename); + if( s != null ) + return s; + } + + throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename ); + } + + public static Stream OpenWithExts( string filename, params string[] exts ) + { + if( filename.IndexOfAny( new char[] { '/', '\\' } ) == -1 ) + { + foreach( var ext in exts ) + { + var s = GetFromCache( allFiles, filename + ext ); + if( s != null ) + return s; + } + } + + foreach( var ext in exts ) + { + foreach( IFolder folder in mountedFolders ) + { + Stream s = folder.GetContent( filename + ext ); + if( s != null ) + return s; + } + } + + throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename ); + } + + public static bool Exists(string filename) + { + foreach (var folder in mountedFolders) + { + var s = folder.GetContent(filename); + if (s != null) + { + s.Dispose(); + return true; + } + } + + return false; + } + } +} diff --git a/OpenRA.FileFormats/Folder.cs b/OpenRA.FileFormats/Folder.cs new file mode 100644 index 0000000000..e5e4972ed7 --- /dev/null +++ b/OpenRA.FileFormats/Folder.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.IO; + +namespace OpenRA.FileFormats +{ + public class Folder : IFolder + { + readonly string path; + + public Folder(string path) { this.path = path; } + + public Stream GetContent(string filename) + { + Log.Write( "GetContent from folder: {0}", filename ); + try { return File.OpenRead( Path.Combine( path, filename ) ); } + catch { return null; } + } + + public IEnumerable AllFileHashes() + { + foreach( var filename in Directory.GetFiles( path, "*", SearchOption.TopDirectoryOnly ) ) + yield return PackageEntry.HashFilename( filename ); + } + } +} diff --git a/OpenRA.FileFormats/Format2.cs b/OpenRA.FileFormats/Format2.cs new file mode 100644 index 0000000000..2b6846a231 --- /dev/null +++ b/OpenRA.FileFormats/Format2.cs @@ -0,0 +1,46 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + public static class Format2 + { + public static int DecodeInto(byte[] src, byte[] dest) + { + FastByteReader r = new FastByteReader(src); + + int i = 0; + while (!r.Done()) + { + byte cmd = r.ReadByte(); + if (cmd == 0) + { + byte count = r.ReadByte(); + while (count-- > 0) + dest[i++] = 0; + } + else + dest[i++] = cmd; + } + + return i; + } + } +} diff --git a/OpenRA.FileFormats/Format40.cs b/OpenRA.FileFormats/Format40.cs new file mode 100644 index 0000000000..65073ef1d7 --- /dev/null +++ b/OpenRA.FileFormats/Format40.cs @@ -0,0 +1,88 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + public static class Format40 + { + public static int DecodeInto( byte[] src, byte[] dest ) + { + var ctx = new FastByteReader(src); + int destIndex = 0; + + while( true ) + { + byte i = ctx.ReadByte(); + if( ( i & 0x80 ) == 0 ) + { + int count = i & 0x7F; + if( count == 0 ) + { + // case 6 + count = ctx.ReadByte(); + byte value = ctx.ReadByte(); + for( int end = destIndex + count ; destIndex < end ; destIndex++ ) + dest[ destIndex ] ^= value; + } + else + { + // case 5 + for( int end = destIndex + count ; destIndex < end ; destIndex++ ) + dest[destIndex] ^= ctx.ReadByte(); + } + } + else + { + int count = i & 0x7F; + if( count == 0 ) + { + count = ctx.ReadWord(); + if( count == 0 ) + return destIndex; + + if( ( count & 0x8000 ) == 0 ) + { + // case 2 + destIndex += ( count & 0x7FFF ); + } + else if( ( count & 0x4000 ) == 0 ) + { + // case 3 + for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ ) + dest[destIndex] ^= ctx.ReadByte(); + } + else + { + // case 4 + byte value = ctx.ReadByte(); + for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ ) + dest[ destIndex ] ^= value; + } + } + else + { + // case 1 + destIndex += count; + } + } + } + } + } +} diff --git a/OpenRA.FileFormats/Format80.cs b/OpenRA.FileFormats/Format80.cs new file mode 100644 index 0000000000..7cb1b1f778 --- /dev/null +++ b/OpenRA.FileFormats/Format80.cs @@ -0,0 +1,135 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.FileFormats +{ + class FastByteReader + { + readonly byte[] src; + int offset = 0; + + public FastByteReader(byte[] src) + { + this.src = src; + } + + public bool Done() { return offset >= src.Length; } + public byte ReadByte() { return src[offset++]; } + public int ReadWord() + { + int x = ReadByte(); + return x | (ReadByte() << 8); + } + + public void CopyTo(byte[] dest, int offset, int count) + { + Array.Copy(src, this.offset, dest, offset, count); + this.offset += count; + } + } + + public static class Format80 + { + static void ReplicatePrevious( byte[] dest, int destIndex, int srcIndex, int count ) + { + if( srcIndex > destIndex ) + throw new NotImplementedException( string.Format( "srcIndex > destIndex {0} {1}", srcIndex, destIndex ) ); + + if( destIndex - srcIndex == 1 ) + { + for( int i = 0 ; i < count ; i++ ) + dest[ destIndex + i ] = dest[ destIndex - 1 ]; + } + else + { + for( int i = 0 ; i < count ; i++ ) + dest[ destIndex + i ] = dest[ srcIndex + i ]; + } + } + + public static int DecodeInto( byte[] src, byte[] dest ) + { + var ctx = new FastByteReader(src); + int destIndex = 0; + + while( true ) + { + byte i = ctx.ReadByte(); + if( ( i & 0x80 ) == 0 ) + { + // case 2 + byte secondByte = ctx.ReadByte(); + int count = ( ( i & 0x70 ) >> 4 ) + 3; + int rpos = ( ( i & 0xf ) << 8 ) + secondByte; + + ReplicatePrevious( dest, destIndex, destIndex - rpos, count ); + destIndex += count; + } + else if( ( i & 0x40 ) == 0 ) + { + // case 1 + int count = i & 0x3F; + if( count == 0 ) + return destIndex; + + ctx.CopyTo( dest, destIndex, count ); + destIndex += count; + } + else + { + int count3 = i & 0x3F; + if( count3 == 0x3E ) + { + // case 4 + int count = ctx.ReadWord(); + byte color = ctx.ReadByte(); + + for( int end = destIndex + count ; destIndex < end ; destIndex++ ) + dest[ destIndex ] = color; + } + else if( count3 == 0x3F ) + { + // case 5 + int count = ctx.ReadWord(); + int srcIndex = ctx.ReadWord(); + if( srcIndex >= destIndex ) + throw new NotImplementedException( string.Format( "srcIndex >= destIndex {0} {1}", srcIndex, destIndex ) ); + + for( int end = destIndex + count ; destIndex < end ; destIndex++ ) + dest[ destIndex ] = dest[ srcIndex++ ]; + } + else + { + // case 3 + int count = count3 + 3; + int srcIndex = ctx.ReadWord(); + if( srcIndex >= destIndex ) + throw new NotImplementedException( string.Format( "srcIndex >= destIndex {0} {1}", srcIndex, destIndex ) ); + + for( int end = destIndex + count ; destIndex < end ; destIndex++ ) + dest[ destIndex ] = dest[ srcIndex++ ]; + } + } + } + } + } +} diff --git a/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs new file mode 100755 index 0000000000..dc6a847fcd --- /dev/null +++ b/OpenRA.FileFormats/Graphics/IGraphicsDevice.cs @@ -0,0 +1,105 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.IO; + +namespace OpenRA.FileFormats.Graphics +{ + [AttributeUsage( AttributeTargets.Assembly )] + public class RendererAttribute : Attribute + { + public readonly Type Type; + + public RendererAttribute( Type graphicsDeviceType ) + { + if( !typeof( IGraphicsDevice ).IsAssignableFrom( graphicsDeviceType ) ) + throw new InvalidOperationException( "Incorrect type in RendererAttribute" ); + Type = graphicsDeviceType; + } + } + + public interface IGraphicsDevice + { + IVertexBuffer CreateVertexBuffer( int length ); + IIndexBuffer CreateIndexBuffer( int length ); + ITexture CreateTexture( Bitmap bitmap ); + IShader CreateShader( Stream stream ); + + Size WindowSize { get; } + + void Begin(); + void End(); + void Clear( Color color ); + void Present(); + + void DrawIndexedPrimitives( PrimitiveType type, Range vertexRange, Range indexRange ); + void DrawIndexedPrimitives( PrimitiveType type, int vertexPool, int numPrimitives ); + + void EnableScissor( int left, int top, int width, int height ); + void DisableScissor(); + } + + public interface IVertexBuffer + { + void Bind(); + void SetData( T[] vertices ); + } + + public interface IIndexBuffer + { + void Bind(); + void SetData( ushort[] indices ); + } + + public interface IShader + { + void SetValue( string name, float x, float y ); + void SetValue( string param, ITexture texture ); + void Commit(); + void Render( Action a ); + } + + public interface ITexture + { + void SetData( Bitmap bitmap ); + } + + public interface IFont + { + void DrawText( string text, int2 pos, Color c ); + + int2 Measure( string text ); + } + + public enum PrimitiveType + { + PointList, + LineList, + TriangleList, + } + + public struct Range + { + public readonly T Start, End; + public Range( T start, T end ) { Start = start; End = end; } + } +} diff --git a/OpenRA.FileFormats/Graphics/Vertex.cs b/OpenRA.FileFormats/Graphics/Vertex.cs new file mode 100644 index 0000000000..cd2e8c6a87 --- /dev/null +++ b/OpenRA.FileFormats/Graphics/Vertex.cs @@ -0,0 +1,38 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Runtime.InteropServices; + +namespace OpenRA.FileFormats.Graphics +{ + [StructLayout(LayoutKind.Sequential)] + public struct Vertex + { + public float x, y, z, u, v; + public float p, c; + + public Vertex(float2 xy, float2 uv, float2 pc) + { + this.x = xy.X; this.y = xy.Y; this.z = 0; + this.u = uv.X; this.v = uv.Y; + this.p = pc.X; this.c = pc.Y; + } + } +} diff --git a/OpenRA.FileFormats/IPaletteRemap.cs b/OpenRA.FileFormats/IPaletteRemap.cs new file mode 100644 index 0000000000..953ec19f82 --- /dev/null +++ b/OpenRA.FileFormats/IPaletteRemap.cs @@ -0,0 +1,29 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.FileFormats +{ + public interface IPaletteRemap + { + Color GetRemappedColor(Color original, int index); + } +} diff --git a/OpenRA.FileFormats/IniFile.cs b/OpenRA.FileFormats/IniFile.cs new file mode 100644 index 0000000000..7d850f30ec --- /dev/null +++ b/OpenRA.FileFormats/IniFile.cs @@ -0,0 +1,161 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace OpenRA.FileFormats +{ + public class IniFile + { + Dictionary sections = new Dictionary(); + + public IniFile( Stream s ) + { + Load( s ); + } + + public IniFile( params Stream[] streams ) + { + foreach( var s in streams ) + Load( s ); + } + + public void Load( Stream s ) + { + StreamReader reader = new StreamReader( s ); + IniSection currentSection = null; + + while( !reader.EndOfStream ) + { + string line = reader.ReadLine(); + + if( line.Length == 0 ) continue; + + switch( line[ 0 ] ) + { + case ';': break; + case '[': currentSection = ProcessSection( line ); break; + default: ProcessEntry( line, currentSection ); break; + } + } + } + + Regex sectionPattern = new Regex( @"^\[([^]]*)\]" ); + + IniSection ProcessSection( string line ) + { + Match m = sectionPattern.Match( line ); + if( m == null || !m.Success ) + return null; + string sectionName = m.Groups[ 1 ].Value.ToLowerInvariant(); + + IniSection ret; + if( !sections.TryGetValue( sectionName, out ret ) ) + sections.Add( sectionName, ret = new IniSection( sectionName ) ); + return ret; + } + + bool ProcessEntry( string line, IniSection currentSection ) + { + int comment = line.IndexOf( ';' ); + if( comment >= 0 ) + line = line.Substring( 0, comment ); + + line = line.Trim(); + if( line.Length == 0 ) + return false; + + var key = line; + var value = ""; + int eq = line.IndexOf( '=' ); + if( eq >= 0 ) + { + key = line.Substring( 0, eq ); + value = line.Substring( eq + 1, line.Length - eq - 1 ); + } + + if( currentSection == null ) + throw new InvalidOperationException( "No current INI section" ); + + if( !currentSection.Contains( key ) ) + currentSection.Add( key, value ); + return true; + } + + public IniSection GetSection( string s ) + { + return GetSection( s, false ); + } + + public IniSection GetSection( string s, bool allowFail ) + { + IniSection section; + if( sections.TryGetValue( s.ToLowerInvariant(), out section ) ) + return section; + + if( allowFail ) + return new IniSection( s ); + throw new InvalidOperationException( "Section does not exist in map or rules: " + s ); + } + + public IEnumerable Sections { get { return sections.Values; } } + } + + public class IniSection : IEnumerable> + { + public string Name { get; private set; } + Dictionary values = new Dictionary(); + + public IniSection( string name ) + { + Name = name; + } + + public void Add( string key, string value ) + { + values[key] = value; + } + + public bool Contains( string key ) + { + return values.ContainsKey( key ); + } + + public string GetValue( string key, string defaultValue ) + { + string s; + return values.TryGetValue( key, out s ) ? s : defaultValue; + } + + public IEnumerator> GetEnumerator() + { + return values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/OpenRA.FileFormats/Lazy.cs b/OpenRA.FileFormats/Lazy.cs new file mode 100644 index 0000000000..5497c06570 --- /dev/null +++ b/OpenRA.FileFormats/Lazy.cs @@ -0,0 +1,56 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.FileFormats +{ + public class Lazy + { + Func p; + T value; + + public Lazy(Func p) + { + if (p == null) + throw new ArgumentNullException(); + + this.p = p; + } + + public T Value + { + get + { + if (p == null) + return value; + + value = p(); + p = null; + return value; + } + } + } + + public static class Lazy + { + public static Lazy New(Func p) { return new Lazy(p); } + } +} diff --git a/OpenRA.FileFormats/Map.cs b/OpenRA.FileFormats/Map.cs new file mode 100644 index 0000000000..c3ab61b26d --- /dev/null +++ b/OpenRA.FileFormats/Map.cs @@ -0,0 +1,176 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace OpenRA.FileFormats +{ + public class Map + { + public readonly string Title; + public readonly string Theater; + + public readonly int XOffset; + public readonly int YOffset; + public int2 Offset { get { return new int2( XOffset, YOffset ); } } + + public readonly int Width; + public readonly int Height; + public int2 Size { get { return new int2(Width, Height); } } + + public readonly TileReference[ , ] MapTiles = new TileReference[ 128, 128 ]; + public readonly List Trees = new List(); + + public readonly IEnumerable SpawnPoints; + + static string Truncate( string s, int maxLength ) + { + return s.Length <= maxLength ? s : s.Substring(0,maxLength ); + } + + public string TileSuffix { get { return "." + Truncate(Theater, 3); } } + + public Map(IniFile file) + { + for (int j = 0; j < 128; j++) + for (int i = 0; i < 128; i++) + MapTiles[i, j] = new TileReference(); + + IniSection basic = file.GetSection("Basic"); + Title = basic.GetValue("Name", "(null)"); + + IniSection map = file.GetSection("Map"); + Theater = Truncate(map.GetValue("Theater", "TEMPERATE"), 8); + + XOffset = int.Parse(map.GetValue("X", "0")); + YOffset = int.Parse(map.GetValue("Y", "0")); + + Width = int.Parse(map.GetValue("Width", "0")); + Height = int.Parse(map.GetValue("Height", "0")); + + UnpackTileData(ReadPackedSection(file.GetSection("MapPack"))); + UnpackOverlayData(ReadPackedSection(file.GetSection("OverlayPack"))); + ReadTrees(file); + + SpawnPoints = file.GetSection("Waypoints") + .Select(kv => Pair.New(int.Parse(kv.Key), new int2(int.Parse(kv.Value) % 128, int.Parse(kv.Value) / 128))) + .Where(a => a.First < 8) + .Select(a => a.Second) + .ToArray(); + } + + static MemoryStream ReadPackedSection(IniSection mapPackSection) + { + StringBuilder sb = new StringBuilder(); + for (int i = 1; ; i++) + { + string line = mapPackSection.GetValue(i.ToString(), null); + if (line == null) + break; + + sb.Append(line.Trim()); + } + + byte[] data = Convert.FromBase64String(sb.ToString()); + List chunks = new List(); + BinaryReader reader = new BinaryReader(new MemoryStream(data)); + + try + { + while (true) + { + uint length = reader.ReadUInt32() & 0xdfffffff; + byte[] dest = new byte[8192]; + byte[] src = reader.ReadBytes((int)length); + + /*int actualLength =*/ Format80.DecodeInto(src, dest); + + chunks.Add(dest); + } + } + catch (EndOfStreamException) { } + + MemoryStream ms = new MemoryStream(); + foreach (byte[] chunk in chunks) + ms.Write(chunk, 0, chunk.Length); + + ms.Position = 0; + + return ms; + } + + static byte ReadByte( Stream s ) + { + int ret = s.ReadByte(); + if( ret == -1 ) + throw new NotImplementedException(); + return (byte)ret; + } + + static ushort ReadWord(Stream s) + { + ushort ret = ReadByte(s); + ret |= (ushort)(ReadByte(s) << 8); + + return ret; + } + + void UnpackTileData( MemoryStream ms ) + { + for( int i = 0 ; i < 128 ; i++ ) + for( int j = 0 ; j < 128 ; j++ ) + MapTiles[j, i].tile = ReadWord(ms); + + for( int i = 0 ; i < 128 ; i++ ) + for( int j = 0 ; j < 128 ; j++ ) + { + MapTiles[j, i].image = (byte)ms.ReadByte(); + if( MapTiles[ j, i ].tile == 0xff || MapTiles[ j, i ].tile == 0xffff ) + MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 ); + } + } + + void UnpackOverlayData( MemoryStream ms ) + { + for( int i = 0 ; i < 128 ; i++ ) + for( int j = 0 ; j < 128 ; j++ ) + MapTiles[ j, i ].overlay = ReadByte( ms ); + } + + void ReadTrees( IniFile file ) + { + IniSection terrain = file.GetSection( "TERRAIN", true ); + if( terrain == null ) + return; + + foreach( KeyValuePair kv in terrain ) + Trees.Add( new TreeReference( int.Parse( kv.Key ), kv.Value ) ); + } + + public bool IsInMap(int x, int y) + { + return (x >= XOffset && y >= YOffset && x < XOffset + Width && y < YOffset + Height); + } + } +} diff --git a/OpenRA.FileFormats/MiniYaml.cs b/OpenRA.FileFormats/MiniYaml.cs new file mode 100755 index 0000000000..89f0e5c3a7 --- /dev/null +++ b/OpenRA.FileFormats/MiniYaml.cs @@ -0,0 +1,168 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace OpenRA.FileFormats +{ + using MiniYamlNodes = Dictionary; + + public class MiniYaml + { + public string Value; + public Dictionary Nodes = new Dictionary(); + + public MiniYaml( string value ) : this( value, new Dictionary() ) { } + + public MiniYaml( string value, Dictionary nodes ) + { + Value = value; + Nodes = nodes; + } + + static Dictionary FromLines(string[] lines) + { + var levels = new List>(); + levels.Add(new Dictionary()); + + foreach (var line in lines) + { + var t = line.TrimStart(' ', '\t'); + if (t.Length == 0 || t[0] == '#') + continue; + var level = line.Length - t.Length; + + if (levels.Count <= level) + throw new InvalidOperationException("Bad indent in miniyaml"); + while (levels.Count > level + 1) + levels.RemoveAt(levels.Count - 1); + + var colon = t.IndexOf(':'); + var d = new Dictionary(); + + if (colon == -1) + levels[level].Add(t.Trim(), new MiniYaml(null, d)); + else + { + var value = t.Substring(colon + 1).Trim(); + if (value.Length == 0) + value = null; + levels[level].Add(t.Substring(0, colon).Trim(), new MiniYaml(value, d)); + } + levels.Add(d); + } + return levels[0]; + } + + public static Dictionary FromFile( string path ) + { + return FromLines(File.ReadAllLines( path )); + } + + public static Dictionary FromStream(Stream s) + { + using (var reader = new StreamReader(s)) + return FromString(reader.ReadToEnd()); + } + + public static Dictionary FromString(string text) + { + return FromLines(text.Split('\n')); + } + + public static Dictionary Merge( Dictionary a, Dictionary b ) + { + if( a.Count == 0 ) + return b; + if( b.Count == 0 ) + return a; + + var ret = new Dictionary(); + + var keys = a.Keys.Union( b.Keys ).ToList(); + + var noInherit = keys.Where( x => x.Length > 0 && x[ 0 ] == '-' ).Select( x => x.Substring( 1 ) ).ToList(); + + foreach( var key in keys ) + { + MiniYaml aa, bb; + a.TryGetValue( key, out aa ); + b.TryGetValue( key, out bb ); + + if( key.Length > 0 && key[ 0 ] == '-' ) + continue; + else if( noInherit.Contains( key ) ) + { + if( aa != null ) + ret.Add( key, aa ); + } + else + ret.Add( key, Merge( aa, bb ) ); + } + + return ret; + } + + public static MiniYaml Merge( MiniYaml a, MiniYaml b ) + { + if( a == null ) + return b; + if( b == null ) + return a; + + return new MiniYaml( a.Value ?? b.Value, Merge( a.Nodes, b.Nodes ) ); + } + + public IEnumerable ToLines(string name) + { + yield return name + ": " + Value; + if (Nodes != null) + foreach (var line in Nodes.ToLines(false)) + yield return "\t" + line; + } + } + + public static class MiniYamlExts + { + public static void WriteToFile(this MiniYamlNodes y, string filename) + { + File.WriteAllLines(filename, y.ToLines(true).Select(x => x.TrimEnd()).ToArray()); + } + + public static string WriteToString(this MiniYamlNodes y) + { + return string.Join("\n", y.ToLines(true).Select(x => x.TrimEnd()).ToArray()); + } + + public static IEnumerable ToLines(this MiniYamlNodes y, bool lowest) + { + foreach (var kv in y) + { + foreach (var line in kv.Value.ToLines(kv.Key)) + yield return line; + if (lowest) + yield return ""; + } + } + } +} diff --git a/OpenRA.FileFormats/OpenRA.FileFormats.csproj b/OpenRA.FileFormats/OpenRA.FileFormats.csproj new file mode 100644 index 0000000000..893ee21c9c --- /dev/null +++ b/OpenRA.FileFormats/OpenRA.FileFormats.csproj @@ -0,0 +1,106 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + Library + Properties + OpenRA.FileFormats + OpenRA.FileFormats + + + 2.0 + + + v3.5 + + + true + bin\Debug\ + DEBUG;TRACE + true + full + AnyCPU + prompt + 4 + false + + + bin\Release\ + TRACE + true + true + pdbonly + AnyCPU + prompt + 4 + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenRA.FileFormats/Package.cs b/OpenRA.FileFormats/Package.cs new file mode 100644 index 0000000000..1ffa2c28ef --- /dev/null +++ b/OpenRA.FileFormats/Package.cs @@ -0,0 +1,166 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public interface IFolder + { + Stream GetContent(string filename); + IEnumerable AllFileHashes(); + } + + public class Package : IFolder + { + readonly Dictionary index; + readonly bool isRmix, isEncrypted; + readonly long dataStart; + readonly Stream s; + + public Package(string filename) + { + s = FileSystem.Open(filename); + + BinaryReader reader = new BinaryReader(s); + uint signature = reader.ReadUInt32(); + + isRmix = 0 == (signature & ~(uint)(MixFileFlags.Checksum | MixFileFlags.Encrypted)); + + if (isRmix) + { + isEncrypted = 0 != (signature & (uint)MixFileFlags.Encrypted); + if( isEncrypted ) + { + index = ParseRaHeader( s, out dataStart ).ToDictionary(x => x.Hash); + return; + } + } + else + s.Seek( 0, SeekOrigin.Begin ); + + isEncrypted = false; + index = ParseTdHeader(s, out dataStart).ToDictionary(x => x.Hash); + } + + const long headerStart = 84; + + List ParseRaHeader(Stream s, out long dataStart) + { + BinaryReader reader = new BinaryReader(s); + byte[] keyblock = reader.ReadBytes(80); + byte[] blowfishKey = new BlowfishKeyProvider().DecryptKey(keyblock); + + uint[] h = ReadUints(reader, 2); + + Blowfish fish = new Blowfish(blowfishKey); + MemoryStream ms = Decrypt( h, fish ); + BinaryReader reader2 = new BinaryReader(ms); + + ushort numFiles = reader2.ReadUInt16(); + uint datasize = reader2.ReadUInt32(); + + Console.WriteLine("{0} files, {1} kb", numFiles, datasize >> 10); + + s.Position = headerStart; + reader = new BinaryReader(s); + + int byteCount = 6 + numFiles * PackageEntry.Size; + h = ReadUints( reader, ( byteCount + 3 ) / 4 ); + + ms = Decrypt( h, fish ); + + dataStart = headerStart + byteCount + ( ( ~byteCount + 1 ) & 7 ); + + long ds; + return ParseTdHeader( ms, out ds ); + } + + static MemoryStream Decrypt( uint[] h, Blowfish fish ) + { + uint[] decrypted = fish.Decrypt( h ); + + MemoryStream ms = new MemoryStream(); + BinaryWriter writer = new BinaryWriter( ms ); + foreach( uint t in decrypted ) + writer.Write( t ); + writer.Flush(); + + ms.Position = 0; + return ms; + } + + uint[] ReadUints(BinaryReader r, int count) + { + uint[] ret = new uint[count]; + for (int i = 0; i < ret.Length; i++) + ret[i] = r.ReadUInt32(); + + return ret; + } + + List ParseTdHeader(Stream s, out long dataStart) + { + List items = new List(); + + BinaryReader reader = new BinaryReader(s); + ushort numFiles = reader.ReadUInt16(); + /*uint dataSize = */reader.ReadUInt32(); + + for (int i = 0; i < numFiles; i++) + items.Add(new PackageEntry(reader)); + + dataStart = s.Position; + return items; + } + + public Stream GetContent(uint hash) + { + PackageEntry e; + if (!index.TryGetValue(hash, out e)) + return null; + + s.Seek( dataStart + e.Offset, SeekOrigin.Begin ); + byte[] data = new byte[ e.Length ]; + s.Read( data, 0, (int)e.Length ); + return new MemoryStream(data); + } + + public Stream GetContent(string filename) + { + return GetContent(PackageEntry.HashFilename(filename)); + } + + public IEnumerable AllFileHashes() + { + return index.Keys; + } + } + + [Flags] + enum MixFileFlags : uint + { + Checksum = 0x10000, + Encrypted = 0x20000, + } +} diff --git a/OpenRA.FileFormats/PackageEntry.cs b/OpenRA.FileFormats/PackageEntry.cs new file mode 100644 index 0000000000..b863b1971b --- /dev/null +++ b/OpenRA.FileFormats/PackageEntry.cs @@ -0,0 +1,80 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace OpenRA.FileFormats +{ + public class PackageEntry + { + public readonly uint Hash; + public readonly uint Offset; + public readonly uint Length; + + public PackageEntry(BinaryReader r) + { + Hash = r.ReadUInt32(); + Offset = r.ReadUInt32(); + Length = r.ReadUInt32(); + } + + public override string ToString() + { + string filename; + if (Names.TryGetValue(Hash, out filename)) + return string.Format("{0} - offset 0x{1:x8} - length 0x{2:x8}", filename, Offset, Length); + else + return string.Format("0x{0:x8} - offset 0x{1:x8} - length 0x{2:x8}", Hash, Offset, Length); + } + + public static uint HashFilename(string name) + { + if (name.Length > 12) + name = name.Substring(0, 12); + + name = name.ToUpperInvariant(); + if (name.Length % 4 != 0) + name = name.PadRight(name.Length + (4 - name.Length % 4), '\0'); + + MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(name)); + BinaryReader reader = new BinaryReader(ms); + + int len = name.Length >> 2; + uint result = 0; + + while (len-- != 0) + result = ((result << 1) | (result >> 31)) + reader.ReadUInt32(); + + return result; + } + + static Dictionary Names = new Dictionary(); + + public static void AddStandardName(string s) + { + uint hash = HashFilename(s); + Names.Add(hash, s); + } + + public const int Size = 12; + } +} diff --git a/OpenRA.FileFormats/Pair.cs b/OpenRA.FileFormats/Pair.cs new file mode 100644 index 0000000000..7436a1d583 --- /dev/null +++ b/OpenRA.FileFormats/Pair.cs @@ -0,0 +1,75 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.FileFormats +{ + public struct Pair + { + public T First; + public U Second; + + public Pair(T first, U second) + { + First = first; + Second = second; + } + + static IEqualityComparer tc = EqualityComparer.Default; + static IEqualityComparer uc = EqualityComparer.Default; + + public static bool operator ==(Pair a, Pair b) + { + return tc.Equals(a.First, b.First) && uc.Equals(a.Second, b.Second); + } + + public static bool operator !=(Pair a, Pair b) + { + return !(a == b); + } + + public override bool Equals(object obj) + { + if (!(obj is Pair)) + return false; + + return (Pair)obj == this; + } + + public override int GetHashCode() + { + return First.GetHashCode() ^ Second.GetHashCode(); + } + + public Pair WithFirst(T t) { return new Pair(t, Second); } + public Pair WithSecond(U u) { return new Pair(First, u); } + + public static T AsFirst(Pair p) { return p.First; } + public static U AsSecond(Pair p) { return p.Second; } + + public Pair Swap() { return Pair.New(Second, First); } + } + + public static class Pair + { + public static Pair New(T t, U u) { return new Pair(t, u); } + } +} diff --git a/OpenRA.FileFormats/Palette.cs b/OpenRA.FileFormats/Palette.cs new file mode 100644 index 0000000000..f58ba580c9 --- /dev/null +++ b/OpenRA.FileFormats/Palette.cs @@ -0,0 +1,60 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace OpenRA.FileFormats +{ + public class Palette + { + List colors = new List(); + + public Color GetColor(int index) + { + return colors[index]; + } + + public Palette(Stream s) + { + using (BinaryReader reader = new BinaryReader(s)) + { + for (int i = 0; i < 256; i++) + { + byte r = (byte)(reader.ReadByte() << 2); + byte g = (byte)(reader.ReadByte() << 2); + byte b = (byte)(reader.ReadByte() << 2); + + colors.Add(Color.FromArgb(r, g, b)); + } + } + colors[0] = Color.FromArgb(0, 0, 0, 0); + colors[3] = Color.FromArgb(178, 0, 0, 0); + colors[4] = Color.FromArgb(140, 0, 0, 0); + } + + public Palette(Palette p, IPaletteRemap r) + { + for (int i = 0; i < 256; i++) + colors.Add(r.GetRemappedColor(p.GetColor(i), i)); + } + } +} diff --git a/OpenRA.FileFormats/PlayerColorRemap.cs b/OpenRA.FileFormats/PlayerColorRemap.cs new file mode 100644 index 0000000000..17ff46f203 --- /dev/null +++ b/OpenRA.FileFormats/PlayerColorRemap.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public class PlayerColorRemap : IPaletteRemap + { + Dictionary remapColors; + + public PlayerColorRemap(Stream s) + { + var yaml = MiniYaml.FromStream(s); + remapColors = yaml.ToDictionary( + y => int.Parse(y.Key), + y => ArrayToColor((int[])FieldLoader.GetValue( + typeof(int[]), y.Value.Value.Trim()))); + } + + static Color ArrayToColor(int[] x) { return Color.FromArgb(x[0], x[1], x[2], x[3]); } + + public Color GetRemappedColor(Color original, int index) + { + Color c; + return remapColors.TryGetValue(index, out c) + ? c : original; + } + } +} diff --git a/OpenRA.FileFormats/PriorityQueue.cs b/OpenRA.FileFormats/PriorityQueue.cs new file mode 100644 index 0000000000..db2ad43274 --- /dev/null +++ b/OpenRA.FileFormats/PriorityQueue.cs @@ -0,0 +1,113 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; + +namespace OpenRA.FileFormats +{ + public class PriorityQueue + where T : IComparable + { + List items = new List(); + int level, index; + + public PriorityQueue() + { + items.Add(new T[1]); + } + + public void Add(T item) + { + int addLevel = level; + int addIndex = index; + + while (addLevel >= 1 && Above(addLevel, addIndex).CompareTo(item) > 0) + { + items[addLevel][addIndex] = Above(addLevel, addIndex); + --addLevel; + addIndex >>= 1; + } + + items[addLevel][addIndex] = item; + + if (++index >= (1 << level)) + { + index = 0; + if (items.Count <= ++level) + items.Add(new T[1 << level]); + } + } + + public bool Empty { get { return (level == 0); } } + + T At(int level, int index) { return items[level][index]; } + T Above(int level, int index) { return items[level - 1][index >> 1]; } + + T Last() + { + int lastLevel = level; + int lastIndex = index; + + if (--lastIndex < 0) + lastIndex = (1 << --lastLevel) - 1; + + return At(lastLevel, lastIndex); + } + + public T Pop() + { + if (level == 0 && index == 0) + throw new InvalidOperationException("Attempting to pop empty PriorityQueue"); + + T ret = At(0, 0); + BubbleInto(0, 0, Last()); + if (--index < 0) + index = (1 << --level) - 1; + + return ret; + } + + void BubbleInto(int intoLevel, int intoIndex, T val) + { + int downLevel = intoLevel + 1; + int downIndex = intoIndex << 1; + + if (downLevel > level || (downLevel == level && downIndex >= index)) + { + items[intoLevel][intoIndex] = val; + return; + } + + if (downLevel <= level && downIndex < index - 1 && + At(downLevel, downIndex).CompareTo(At(downLevel, downIndex + 1)) >= 0) + ++downIndex; + + if (val.CompareTo(At(downLevel, downIndex)) <= 0) + { + items[intoLevel][intoIndex] = val; + return; + } + + items[intoLevel][intoIndex] = At(downLevel, downIndex); + BubbleInto(downLevel, downIndex, val); + } + } +} diff --git a/OpenRA.FileFormats/Properties/AssemblyInfo.cs b/OpenRA.FileFormats/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cc37fbbb81 --- /dev/null +++ b/OpenRA.FileFormats/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("OpenRA.FileFormats")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenRA.FileFormats")] +[assembly: AssemblyCopyright("Copyright © 2007,2009,2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenRA.FileFormats/ProtocolVersion.cs b/OpenRA.FileFormats/ProtocolVersion.cs new file mode 100644 index 0000000000..a091347c99 --- /dev/null +++ b/OpenRA.FileFormats/ProtocolVersion.cs @@ -0,0 +1,28 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + public static class ProtocolVersion + { + // you *must* increment this whenever you make an incompatible protocol change + public static readonly int Version = 2; + } +} diff --git a/OpenRA.FileFormats/Session.cs b/OpenRA.FileFormats/Session.cs new file mode 100644 index 0000000000..c28fdc0e65 --- /dev/null +++ b/OpenRA.FileFormats/Session.cs @@ -0,0 +1,84 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public class Session + { + public List Clients = new List(); + public Global GlobalSettings = new Global(); + + public enum ClientState + { + NotReady, + Downloading, + Ready + } + + public class Client + { + public int Index; + public int PaletteIndex; + public string Country; + public int SpawnPoint; + public string Name; + public ClientState State; + } + + public class Global + { + public string Map = "scm12ea.ini"; + public string[] Packages = {}; // filename:sha1 pairs. + public string[] Mods = { "ra" }; // mod names + public int OrderLatency = 3; + } + } + + public class Manifest + { + public readonly string[] Folders = { }; + public readonly string[] Packages = { }; + public readonly string[] LegacyRules = { }; + public readonly string[] Rules = { }; + public readonly string[] Sequences = { }; + public readonly string[] Chrome = { }; + public readonly string[] Assemblies = { }; + + public Manifest(string[] mods) + { + var yaml = mods + .Select(m => MiniYaml.FromFile("mods/" + m + "/mod.yaml")) + .Aggregate(MiniYaml.Merge); + + Folders = YamlList(yaml, "Folders"); + Packages = YamlList(yaml, "Packages"); + LegacyRules = YamlList(yaml, "LegacyRules"); + Rules = YamlList(yaml, "Rules"); + Sequences = YamlList(yaml, "Sequences"); + Chrome = YamlList(yaml, "Chrome"); + Assemblies = YamlList(yaml, "Assemblies"); + } + + static string[] YamlList(Dictionary ys, string key) { return ys[key].Nodes.Keys.ToArray(); } + } +} diff --git a/OpenRA.FileFormats/ShpReader.cs b/OpenRA.FileFormats/ShpReader.cs new file mode 100644 index 0000000000..fc78f42c4a --- /dev/null +++ b/OpenRA.FileFormats/ShpReader.cs @@ -0,0 +1,177 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace OpenRA.FileFormats +{ + public class ImageHeader + { + public uint Offset; + public Format Format; + + public uint RefOffset; + public Format RefFormat; + public ImageHeader RefImage; + + public byte[] Image; + + public ImageHeader( BinaryReader reader ) + { + Offset = reader.ReadUInt32(); + Format = (Format)( Offset >> 24 ); + Offset &= 0xFFFFFF; + + RefOffset = reader.ReadUInt16(); + RefFormat = (Format)reader.ReadUInt16(); + } + } + + public enum Format + { + Format20 = 0x20, + Format40 = 0x40, + Format80 = 0x80, + } + + public class ShpReader : IEnumerable + { + public readonly int ImageCount; + public readonly ushort Width; + public readonly ushort Height; + + public Size Size { get { return new Size(Width, Height); } } + + private readonly List headers = new List(); + + int recurseDepth = 0; + + public ShpReader( Stream stream ) + { + BinaryReader reader = new BinaryReader( stream ); + + ImageCount = reader.ReadUInt16(); + reader.ReadUInt16(); + reader.ReadUInt16(); + Width = reader.ReadUInt16(); + Height = reader.ReadUInt16(); + reader.ReadUInt32(); + + for( int i = 0 ; i < ImageCount ; i++ ) + headers.Add( new ImageHeader( reader ) ); + + new ImageHeader( reader ); // end-of-file header + new ImageHeader( reader ); // all-zeroes header + + Dictionary offsets = new Dictionary(); + foreach( ImageHeader h in headers ) + offsets.Add( h.Offset, h ); + + for( int i = 0 ; i < ImageCount ; i++ ) + { + ImageHeader h = headers[ i ]; + if( h.Format == Format.Format20 ) + h.RefImage = headers[ i - 1 ]; + + else if( h.Format == Format.Format40 ) + { + if( !offsets.TryGetValue( h.RefOffset, out h.RefImage ) ) + throw new InvalidDataException( string.Format( "Reference doesnt point to image data {0}->{1}", h.Offset, h.RefOffset ) ); + } + } + + foreach( ImageHeader h in headers ) + Decompress( stream, h ); + } + + public ImageHeader this[ int index ] + { + get { return headers[ index ]; } + } + + void Decompress( Stream stream, ImageHeader h ) + { + if( recurseDepth > ImageCount ) + throw new InvalidDataException( "Format20/40 headers contain infinite loop" ); + + switch( h.Format ) + { + case Format.Format20: + case Format.Format40: + { + if( h.RefImage.Image == null ) + { + ++recurseDepth; + Decompress( stream, h.RefImage ); + --recurseDepth; + } + + h.Image = CopyImageData( h.RefImage.Image ); + Format40.DecodeInto(ReadCompressedData(stream, h), h.Image); + break; + } + case Format.Format80: + { + byte[] imageBytes = new byte[ Width * Height ]; + Format80.DecodeInto( ReadCompressedData( stream, h ), imageBytes ); + h.Image = imageBytes; + break; + } + default: + throw new InvalidDataException(); + } + } + + static byte[] ReadCompressedData( Stream stream, ImageHeader h ) + { + stream.Position = h.Offset; + // Actually, far too big. There's no length field with the correct length though :( + int compressedLength = (int)( stream.Length - stream.Position ); + + byte[] compressedBytes = new byte[ compressedLength ]; + stream.Read( compressedBytes, 0, compressedLength ); + + //MemoryStream ms = new MemoryStream( compressedBytes ); + return compressedBytes; + } + + private byte[] CopyImageData( byte[] baseImage ) + { + byte[] imageData = new byte[ Width * Height ]; + for( int i = 0 ; i < Width * Height ; i++ ) + imageData[ i ] = baseImage[ i ]; + + return imageData; + } + + public IEnumerator GetEnumerator() + { + return headers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/OpenRA.FileFormats/ShroudPaletteRemap.cs b/OpenRA.FileFormats/ShroudPaletteRemap.cs new file mode 100644 index 0000000000..6f02ef5532 --- /dev/null +++ b/OpenRA.FileFormats/ShroudPaletteRemap.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.FileFormats +{ + public class ShroudPaletteRemap : IPaletteRemap + { + public Color GetRemappedColor(Color original, int index) + { + // false-color version for debug + + //return new[] { + // Color.FromArgb(64,0,0,0), Color.Green, + // Color.Blue, Color.Yellow, + // Color.Green, + // Color.Red, + // Color.Purple, + // Color.Cyan}[index % 8]; + + return new[] { + Color.Transparent, Color.Green, + Color.Blue, Color.Yellow, + Color.Black, + Color.FromArgb(192,0,0,0), + Color.FromArgb(128,0,0,0), + Color.FromArgb(64,0,0,0)}[index % 8]; + } + } +} diff --git a/OpenRA.FileFormats/SingleColorRemap.cs b/OpenRA.FileFormats/SingleColorRemap.cs new file mode 100644 index 0000000000..143c5c0c0b --- /dev/null +++ b/OpenRA.FileFormats/SingleColorRemap.cs @@ -0,0 +1,38 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.FileFormats +{ + public class SingleColorRemap : IPaletteRemap + { + Color c; + public SingleColorRemap(Color c) + { + this.c = c; + } + + public Color GetRemappedColor(Color original, int index) + { + return original.A > 0 ? c : original; + } + } +} diff --git a/OpenRA.FileFormats/Support/Log.cs b/OpenRA.FileFormats/Support/Log.cs new file mode 100755 index 0000000000..4bc5a54051 --- /dev/null +++ b/OpenRA.FileFormats/Support/Log.cs @@ -0,0 +1,39 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.IO; + +namespace OpenRA +{ + public static class Log + { + static StreamWriter writer = File.CreateText("log.txt"); + + static Log() + { + writer.AutoFlush = true; + } + + public static void Write(string format, params object[] args) + { + writer.WriteLine(format, args); + } + } +} diff --git a/OpenRA.FileFormats/Support/Stopwatch.cs b/OpenRA.FileFormats/Support/Stopwatch.cs new file mode 100755 index 0000000000..75f2fd5cd2 --- /dev/null +++ b/OpenRA.FileFormats/Support/Stopwatch.cs @@ -0,0 +1,42 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using Tao.Sdl; +namespace OpenRA.Support +{ + public class Stopwatch + { + int start; + public Stopwatch() + { + Reset(); + } + + public double ElapsedTime() + { + return (1.0/1000.0) * (double)(Sdl.SDL_GetTicks() - start); + } + + public void Reset() + { + start = Sdl.SDL_GetTicks(); + } + } +} diff --git a/OpenRA.FileFormats/Support/Timer.cs b/OpenRA.FileFormats/Support/Timer.cs new file mode 100755 index 0000000000..b76d46d69d --- /dev/null +++ b/OpenRA.FileFormats/Support/Timer.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Support +{ + public static class Timer + { + static Stopwatch sw = new Stopwatch(); + static double lastTime = 0; + + public static void Time( string message ) + { + var time = sw.ElapsedTime(); + Log.Write( message, time - lastTime ); + lastTime = time; + } + } +} diff --git a/OpenRA.FileFormats/Terrain.cs b/OpenRA.FileFormats/Terrain.cs new file mode 100644 index 0000000000..bf321d174f --- /dev/null +++ b/OpenRA.FileFormats/Terrain.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.IO; + +namespace OpenRA.FileFormats +{ + public class Terrain + { + public readonly List TileBitmapBytes = new List(); + + public Terrain( Stream stream ) + { + int Width, Height; + + BinaryReader reader = new BinaryReader( stream ); + Width = reader.ReadUInt16(); + Height = reader.ReadUInt16(); + + if( Width != 24 || Height != 24 ) + throw new InvalidDataException( string.Format( "{0}x{1}", Width, Height ) ); + + /*NumTiles = */reader.ReadUInt16(); + reader.ReadUInt16(); + /*XDim = */reader.ReadUInt16(); + /*YDim = */reader.ReadUInt16(); + /*uint FileSize = */reader.ReadUInt32(); + uint ImgStart = reader.ReadUInt32(); + reader.ReadUInt32(); + reader.ReadUInt32(); + int IndexEnd = reader.ReadInt32(); + reader.ReadUInt32(); + int IndexStart = reader.ReadInt32(); + + stream.Position = IndexStart; + + foreach( byte b in new BinaryReader(stream).ReadBytes(IndexEnd - IndexStart) ) + { + if (b != 255) + { + stream.Position = ImgStart + b * 24 * 24; + TileBitmapBytes.Add(new BinaryReader(stream).ReadBytes(24 * 24)); + } + else + TileBitmapBytes.Add(null); + } + } + } +} diff --git a/OpenRA.FileFormats/TileReference.cs b/OpenRA.FileFormats/TileReference.cs new file mode 100644 index 0000000000..c4ca4d069e --- /dev/null +++ b/OpenRA.FileFormats/TileReference.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + public struct TileReference + { + public ushort tile; + public byte image; + public byte overlay; + public byte smudge; + public byte density; /* used for ore/gems */ + + public override int GetHashCode() { return tile.GetHashCode() ^ image.GetHashCode(); } + + public override bool Equals( object obj ) + { + if( obj == null ) + return false; + + TileReference r = (TileReference)obj; + return ( r.image == image && r.tile == tile ); + } + + public static bool operator ==( TileReference a, TileReference b ) { return a.Equals( b ); } + public static bool operator !=( TileReference a, TileReference b ) { return !a.Equals( b ); } + } +} diff --git a/OpenRA.FileFormats/TileSet.cs b/OpenRA.FileFormats/TileSet.cs new file mode 100644 index 0000000000..905c30263b --- /dev/null +++ b/OpenRA.FileFormats/TileSet.cs @@ -0,0 +1,109 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace OpenRA.FileFormats +{ + public class TileSet + { + public readonly Dictionary tiles = new Dictionary(); + + public readonly Walkability Walkability = new Walkability(); + public readonly Dictionary walk + = new Dictionary(); + + string NextLine( StreamReader reader ) + { + string ret; + do + { + ret = reader.ReadLine(); + if( ret == null ) + return null; + ret = ret.Trim(); + } + while( ret.Length == 0 || ret[ 0 ] == ';' ); + return ret; + } + + public TileSet( string suffix ) + { + Walkability = new Walkability(); + + char tileSetChar = char.ToUpperInvariant( suffix[ 1 ] ); + StreamReader tileIdFile = new StreamReader( FileSystem.Open( "tileSet.til" ) ); + + while( true ) + { + string tileSetStr = NextLine( tileIdFile ); + string countStr = NextLine( tileIdFile ); + string startStr = NextLine( tileIdFile ); + string pattern = NextLine( tileIdFile ); + if( tileSetStr == null || countStr == null || startStr == null || pattern == null ) + break; + + if( tileSetStr.IndexOf( tileSetChar.ToString() ) == -1 ) + continue; + + int count = int.Parse( countStr ); + int start = int.Parse( startStr, NumberStyles.HexNumber ); + for( int i = 0 ; i < count ; i++ ) + { + string tilename = string.Format(pattern, i + 1); + + if (!walk.ContainsKey((ushort)(start + i))) + walk.Add((ushort)(start + i), Walkability.GetWalkability(tilename)); + + using( Stream s = FileSystem.Open( tilename + suffix ) ) + { + if( !tiles.ContainsKey( (ushort)( start + i ) ) ) + tiles.Add( (ushort)( start + i ), new Terrain( s ) ); + } + } + } + + tileIdFile.Close(); + } + + public byte[] GetBytes(TileReference r) + { + Terrain tile; + if( tiles.TryGetValue( r.tile, out tile ) ) + return tile.TileBitmapBytes[ r.image ]; + + byte[] missingTile = new byte[ 24 * 24 ]; + for( int i = 0 ; i < missingTile.Length ; i++ ) + missingTile[ i ] = 0x36; + + return missingTile; + } + + public int GetWalkability(TileReference r) + { + if (r.tile == 0xff || r.tile == 0xffff) + r.image = 0; + + return walk[r.tile].TerrainType[r.image]; + } + } +} diff --git a/OpenRA.FileFormats/TreeReference.cs b/OpenRA.FileFormats/TreeReference.cs new file mode 100644 index 0000000000..24163be6c3 --- /dev/null +++ b/OpenRA.FileFormats/TreeReference.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.FileFormats +{ + public struct TreeReference + { + public readonly int X; + public readonly int Y; + public readonly string Image; + + public TreeReference(int xy, string image) + { + X = xy % 128; + Y = xy / 128; + Image = image; + } + + public Point Location { get { return new Point(X, Y); } } + } +} diff --git a/OpenRA.FileFormats/Tuple.cs b/OpenRA.FileFormats/Tuple.cs new file mode 100644 index 0000000000..ce59ea03be --- /dev/null +++ b/OpenRA.FileFormats/Tuple.cs @@ -0,0 +1,39 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.FileFormats +{ + public class Tuple + { + public A a; + public B b; + public C c; + + public Tuple(A a, B b, C c) { this.a = a; this.b = b; this.c = c; } + } + + public static class Tuple + { + public static Tuple New(A a, B b, C c) + { + return new Tuple(a, b, c); + } + } +} diff --git a/OpenRA.FileFormats/TypeDictionary.cs b/OpenRA.FileFormats/TypeDictionary.cs new file mode 100644 index 0000000000..6a926517b0 --- /dev/null +++ b/OpenRA.FileFormats/TypeDictionary.cs @@ -0,0 +1,115 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public class TypeDictionary + { + Dictionary dataSingular = new Dictionary(); + Dictionary> dataMultiple = new Dictionary>(); + + public void Add( object val ) + { + var t = val.GetType(); + + foreach( var i in t.GetInterfaces() ) + InnerAdd( i, val ); + foreach( var tt in t.BaseTypes() ) + InnerAdd( tt, val ); + } + + void InnerAdd( Type t, object val ) + { + List objs; + object obj; + + if( dataMultiple.TryGetValue( t, out objs ) ) + objs.Add( val ); + else if( dataSingular.TryGetValue( t, out obj ) ) + { + dataSingular.Remove( t ); + dataMultiple.Add( t, new List { obj, val } ); + } + else + dataSingular.Add( t, val ); + } + + public bool Contains() + { + return dataSingular.ContainsKey( typeof( T ) ) || dataMultiple.ContainsKey( typeof( T ) ); + } + + public T Get() + { + if( dataMultiple.ContainsKey( typeof( T ) ) ) + throw new InvalidOperationException( string.Format( "TypeDictionary contains multiple instance of type `{0}`", typeof( T ) ) ); + + object ret; + if( !dataSingular.TryGetValue( typeof( T ), out ret ) ) + throw new InvalidOperationException(string.Format("TypeDictionary does not contain instance of type `{0}`", typeof(T))); + return (T)ret; + } + + public T GetOrDefault() + { + if( dataMultiple.ContainsKey( typeof( T ) ) ) + throw new InvalidOperationException( string.Format( "TypeDictionary contains multiple instance of type `{0}`", typeof( T ) ) ); + + object ret; + if( !dataSingular.TryGetValue( typeof( T ), out ret ) ) + return default( T ); + return (T)ret; + } + + public IEnumerable WithInterface() + { + List objs; + object obj; + + if( dataMultiple.TryGetValue( typeof( T ), out objs ) ) + return objs.Cast(); + else if( dataSingular.TryGetValue( typeof( T ), out obj ) ) + return new T[] { (T)obj }; + else + return new T[ 0 ]; + } + + public IEnumerator GetEnumerator() + { + return WithInterface().GetEnumerator(); + } + } + + static class TypeExts + { + public static IEnumerable BaseTypes( this Type t ) + { + while( t != null ) + { + yield return t; + t = t.BaseType; + } + } + } +} diff --git a/OpenRA.FileFormats/Walkability.cs b/OpenRA.FileFormats/Walkability.cs new file mode 100644 index 0000000000..1d99ecf3d4 --- /dev/null +++ b/OpenRA.FileFormats/Walkability.cs @@ -0,0 +1,73 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.FileFormats +{ + public class TileTemplate + { + public int Index; // not valid for `interior` stuff. only used for bridges. + public string Name; + public int2 Size; + public string Bridge; + public float HP; + public Dictionary TerrainType = new Dictionary(); + } + + public class Walkability + { + Dictionary walkability + = new Dictionary(); + + public Walkability() + { + var file = new IniFile( FileSystem.Open( "templates.ini" ) ); + + foreach (var section in file.Sections) + { + var tile = new TileTemplate + { + Size = new int2( + int.Parse(section.GetValue("width", "0")), + int.Parse(section.GetValue("height", "0"))), + TerrainType = section + .Where(p => p.Key.StartsWith("tiletype")) + .ToDictionary( + p => int.Parse(p.Key.Substring(8)), + p => int.Parse(p.Value)), + Name = section.GetValue("Name", null).ToLowerInvariant(), + Bridge = section.GetValue("bridge", null), + HP = float.Parse(section.GetValue("hp", "0")) + }; + tile.Index = -1; + int.TryParse(section.Name.Substring(3), out tile.Index); + + walkability[tile.Name] = tile; + } + } + + public TileTemplate GetWalkability(string terrainName) + { + return walkability[terrainName]; + } + } +} diff --git a/OpenRA.FileFormats/float2.cs b/OpenRA.FileFormats/float2.cs new file mode 100644 index 0000000000..87a6200eb1 --- /dev/null +++ b/OpenRA.FileFormats/float2.cs @@ -0,0 +1,113 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace OpenRA +{ + [StructLayout(LayoutKind.Sequential)] + public struct float2 + { + public float X, Y; + + public float2(float x, float y) { X = x; Y = y; } + public float2(PointF p) { X = p.X; Y = p.Y; } + public float2(Point p) { X = p.X; Y = p.Y; } + public float2(Size p) { X = p.Width; Y = p.Height; } + public float2(SizeF p) { X = p.Width; Y = p.Height; } + + public PointF ToPointF() { return new PointF(X, Y); } + public SizeF ToSizeF() { return new SizeF(X, Y); } + + public static implicit operator float2(int2 src) { return new float2(src.X, src.Y); } + + public static float2 operator +(float2 a, float2 b) { return new float2(a.X + b.X, a.Y + b.Y); } + public static float2 operator -(float2 a, float2 b) { return new float2(a.X - b.X, a.Y - b.Y); } + + public static float2 operator -(float2 a) { return new float2(-a.X, -a.Y); } + + public static float Lerp(float a, float b, float t) { return a + t * (b - a); } + + public static float2 Lerp(float2 a, float2 b, float t) + { + return new float2( + Lerp(a.X, b.X, t), + Lerp(a.Y, b.Y, t)); + } + + public static float2 Lerp(float2 a, float2 b, float2 t) + { + return new float2( + Lerp(a.X, b.X, t.X), + Lerp(a.Y, b.Y, t.Y)); + } + + public static float2 FromAngle(float a) { return new float2((float)Math.Sin(a), (float)Math.Cos(a)); } + + static float Constrain(float x, float a, float b) { return x < a ? a : x > b ? b : x; } + + public float2 Constrain(float2 min, float2 max) + { + return new float2( + Constrain(X, min.X, max.X), + Constrain(Y, min.Y, max.Y)); + } + + public static float2 operator *(float a, float2 b) { return new float2(a * b.X, a * b.Y); } + public static float2 operator *( float2 a, float2 b ) { return new float2( a.X * b.X, a.Y * b.Y ); } + public static float2 operator /( float2 a, float2 b ) { return new float2( a.X / b.X, a.Y / b.Y ); } + + public static bool operator ==(float2 me, float2 other) { return (me.X == other.X && me.Y == other.Y); } + public static bool operator !=(float2 me, float2 other) { return !(me == other); } + public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } + + public override bool Equals(object obj) + { + if (obj == null) + return false; + + float2 o = (float2)obj; + return o == this; + } + + public static readonly float2 Zero = new float2(0, 0); + + public static bool WithinEpsilon(float2 a, float2 b, float e) + { + float2 d = a - b; + return Math.Abs(d.X) < e && Math.Abs(d.Y) < e; + } + + public float2 Sign() { return new float2(Math.Sign(X), Math.Sign(Y)); } + public static float Dot(float2 a, float2 b) { return a.X * b.X + a.Y * b.Y; } + public float2 Round() { return new float2((float)Math.Round(X), (float)Math.Round(Y)); } + + public override string ToString() { return string.Format("({0},{1})", X, Y); } + public int2 ToInt2() { return new int2((int)X, (int)Y); } + + public static float2 Max(float2 a, float2 b) { return new float2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); } + public static float2 Min(float2 a, float2 b) { return new float2(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); } + + public float LengthSquared { get { return X * X + Y * Y; } } + public float Length { get { return (float)Math.Sqrt(LengthSquared); } } + } +} diff --git a/OpenRA.FileFormats/int2.cs b/OpenRA.FileFormats/int2.cs new file mode 100644 index 0000000000..7567288195 --- /dev/null +++ b/OpenRA.FileFormats/int2.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; + +namespace OpenRA +{ + public struct int2 + { + public int X,Y; + + public int2( int x, int y ) { this.X = x; this.Y = y; } + public int2( Point p ) { X = p.X; Y = p.Y; } + public int2( Size p ) { X = p.Width; Y = p.Height; } + + public static int2 operator +(int2 a, int2 b) { return new int2(a.X + b.X, a.Y + b.Y); } + public static int2 operator -(int2 a, int2 b) { return new int2(a.X - b.X, a.Y - b.Y); } + public static int2 operator *(int a, int2 b) { return new int2(a * b.X, a * b.Y); } + public static int2 operator *(int2 b, int a) { return new int2(a * b.X, a * b.Y); } + + public static bool operator ==(int2 me, int2 other) { return (me.X == other.X && me.Y == other.Y); } + public static bool operator !=(int2 me, int2 other) { return !(me == other); } + + public int2 Sign() { return new int2(Math.Sign(X), Math.Sign(Y)); } + public int2 Abs() { return new int2( Math.Abs( X ), Math.Abs( Y ) ); } + public int LengthSquared { get { return X * X + Y * Y; } } + public int Length { get { return (int)Math.Sqrt(LengthSquared); } } + public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } + + public static int2 Max(int2 a, int2 b) { return new int2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); } + public static int2 Min(int2 a, int2 b) { return new int2(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); } + + public override bool Equals(object obj) + { + if (obj == null) + return false; + + int2 o = (int2)obj; + return o == this; + } + + public static readonly int2 Zero = new int2(0, 0); + public Point ToPoint() { return new Point(X, Y); } + public PointF ToPointF() { return new PointF(X, Y); } + public float2 ToFloat2() { return new float2(X, Y); } + + public override string ToString() { return string.Format("({0},{1})", X, Y); } + } +} diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs new file mode 100755 index 0000000000..f3af5963e8 --- /dev/null +++ b/OpenRA.Game/Actor.cs @@ -0,0 +1,238 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA +{ + public class Actor + { + [Sync] + public readonly TypeDictionary traits = new TypeDictionary(); + public readonly ActorInfo Info; + + public readonly World World; + public readonly uint ActorID; + + [Sync] + public int2 Location; + [Sync] + public Player Owner; + [Sync] + public int Health; + IActivity currentActivity; + + public Actor( World world, string name, int2 location, Player owner ) + { + World = world; + ActorID = world.NextAID(); + Location = location; + CenterLocation = Traits.Util.CenterOfCell(Location); + Owner = owner; + + if (name != null) + { + //Log.Write("Loading {0}",name.ToLowerInvariant()); + Info = Rules.Info[name.ToLowerInvariant()]; + Health = this.GetMaxHP(); + + foreach (var trait in Info.TraitsInConstructOrder()) + traits.Add(trait.Create(this)); + } + } + + public void Tick() + { + while (currentActivity != null) + { + var a = currentActivity; + currentActivity = a.Tick(this) ?? new Idle(); + if (a == currentActivity || currentActivity is Idle) break; + } + } + + public bool IsIdle + { + get { return currentActivity == null || currentActivity is Idle; } + } + + public float2 CenterLocation; + float2 SelectedSize + { + get // todo: inline into GetBounds + { + var si = Info.Traits.GetOrDefault(); + if (si != null && si.Bounds != null) + return new float2(si.Bounds[0], si.Bounds[1]); + + var firstSprite = Render().FirstOrDefault(); + if (firstSprite.Sprite == null) return float2.Zero; + return firstSprite.Sprite.size; + } + } + + public IEnumerable Render() + { + var mods = traits.WithInterface(); + var sprites = traits.WithInterface().SelectMany(x => x.Render(this)); + return mods.Aggregate(sprites, (m, p) => p.ModifyRender(this, m)); + } + + public Order Order( int2 xy, MouseInput mi ) + { + if (Owner != World.LocalPlayer) + return null; + + if (!World.Map.IsInMap(xy.X, xy.Y)) + return null; + + var underCursor = World.FindUnitsAtMouse(mi.Location).FirstOrDefault(); + + if (underCursor != null && !underCursor.traits.Contains()) + underCursor = null; + + return traits.WithInterface() + .Select( x => x.IssueOrder( this, xy, mi, underCursor ) ) + .FirstOrDefault( x => x != null ); + } + + public RectangleF GetBounds(bool useAltitude) + { + var si = Info.Traits.GetOrDefault(); + + var size = SelectedSize; + var loc = CenterLocation - 0.5f * size; + + if (si != null && si.Bounds != null && si.Bounds.Length > 2) + loc += new float2(si.Bounds[2], si.Bounds[3]); + + if (useAltitude) + { + var unit = traits.GetOrDefault(); + if (unit != null) loc -= new float2(0, unit.Altitude); + } + + return new RectangleF(loc.X, loc.Y, size.X, size.Y); + } + + public bool IsDead { get { return Health <= 0; } } + public bool IsInWorld { get; set; } + public bool RemoveOnDeath = true; + + public DamageState GetDamageState() + { + if (Health <= 0) + return DamageState.Dead; + + if (Health < this.GetMaxHP() * Rules.General.ConditionRed) + return DamageState.Quarter; + + if (Health < this.GetMaxHP() * Rules.General.ConditionYellow) + return DamageState.Half; + + if (Health < this.GetMaxHP() * 0.75) + return DamageState.ThreeQuarter; + + return DamageState.Normal; + } + + public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead) + { + if (IsDead) return; /* overkill! don't count extra hits as more kills! */ + + var oldState = GetDamageState(); + + /* apply the damage modifiers, if we have any. */ + damage = (int)traits.WithInterface().Aggregate( + (float)damage, (a, t) => t.GetDamageModifier() * a); + + Health -= damage; + if (Health <= 0) + { + Health = 0; + if (attacker.Owner != null) + attacker.Owner.Kills++; + + if (RemoveOnDeath) + World.AddFrameEndTask(w => w.Remove(this)); + } + + var maxHP = this.GetMaxHP(); + + if (Health > maxHP) Health = maxHP; + + var newState = GetDamageState(); + + foreach (var nd in traits.WithInterface()) + nd.Damaged(this, new AttackInfo + { + Attacker = attacker, + Damage = damage, + DamageState = newState, + DamageStateChanged = newState != oldState, + Warhead = warhead + }); + } + + public void QueueActivity( IActivity nextActivity ) + { + if( currentActivity == null ) + { + currentActivity = nextActivity; + return; + } + var act = currentActivity; + while( act.NextActivity != null ) + { + act = act.NextActivity; + } + act.NextActivity = nextActivity; + } + + public void CancelActivity() + { + if( currentActivity != null ) + currentActivity.Cancel( this ); + } + + // For pathdebug, et al + public IActivity GetCurrentActivity() + { + return currentActivity; + } + + public override int GetHashCode() + { + return (int)ActorID; + } + + public override bool Equals( object obj ) + { + var o = obj as Actor; + return ( o != null && o.ActorID == ActorID ); + } + } +} diff --git a/OpenRA.Game/Chat.cs b/OpenRA.Game/Chat.cs new file mode 100644 index 0000000000..4117faeaf2 --- /dev/null +++ b/OpenRA.Game/Chat.cs @@ -0,0 +1,77 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + class Chat + { + const int logLength = 10; + + public List> recentLines = new List>(); + public string typing = ""; + public bool isChatting = true; + + public void Toggle() + { + if( isChatting && typing.Length > 0 ) + Game.IssueOrder( Order.Chat( typing ) ); + + typing = ""; + if( Game.orderManager.GameStarted ) + isChatting ^= true; + } + + public void Reset() + { + typing = ""; + isChatting = false; + } + + public void TypeChar(char c) + { + if (c == '\b') + { + if (typing.Length > 0) + typing = typing.Remove(typing.Length - 1); + } + else + typing += c; + } + + public void AddLine(Player p, string text) + { + AddLine(p.Color, p.PlayerName, text); + } + + public void AddLine(Color c, string from, string text) + { + Log.Write( "Chat: {0}: {1}", from, text ); + recentLines.Add(Tuple.New(c, from, text)); + var eva = Game.world.LocalPlayer.PlayerActor.Info.Traits.Get(); + Sound.Play(eva.ChatBeep); + while (recentLines.Count > logLength) recentLines.RemoveAt(0); + } + } +} diff --git a/OpenRA.Game/Chrome.cs b/OpenRA.Game/Chrome.cs new file mode 100644 index 0000000000..f3cdf00080 --- /dev/null +++ b/OpenRA.Game/Chrome.cs @@ -0,0 +1,1214 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Orders; +using OpenRA.Support; +using OpenRA.Traits; + +namespace OpenRA +{ + class Chrome : IHandleInput + { + readonly Renderer renderer; + readonly LineRenderer lineRenderer; + readonly SpriteRenderer rgbaRenderer; + readonly SpriteRenderer shpRenderer; + + string chromeCollection; + string radarCollection; + string paletteCollection; + string digitCollection; + + // Special power bin + readonly Dictionary spsprites; + + // Options menu (to be refactored) + bool optionsPressed = false; + + // Buttons + readonly Animation repairButton; + readonly Animation sellButton; + readonly Animation pwrdownButton; + readonly Animation optionsButton; + + // Build Palette tabs + string currentTab = "Building"; + bool paletteOpen = false; + readonly Dictionary tabImageNames; + readonly Dictionary tabSprites; + + // Build Palette + const int paletteColumns = 3; + const int paletteRows = 5; + static float2 paletteOpenOrigin = new float2(Game.viewport.Width - 215, 280); + static float2 paletteClosedOrigin = new float2(Game.viewport.Width - 16, 280); + static float2 paletteOrigin = paletteClosedOrigin; + const int paletteAnimationLength = 7; + int paletteAnimationFrame = 0; + bool paletteAnimating = false; + readonly List>> buttons = new List>>(); + readonly Animation cantBuild; + readonly Animation ready; + readonly Animation clock; + + // Radar + static float2 radarOpenOrigin = new float2(Game.viewport.Width - 215, 29); + static float2 radarClosedOrigin = new float2(Game.viewport.Width - 215, -166); + static float2 radarOrigin = radarClosedOrigin; + float radarMinimapHeight; + const int radarSlideAnimationLength = 15; + const int radarActivateAnimationLength = 5; + int radarAnimationFrame = 0; + bool radarAnimating = false; + bool hasRadar = false; + + // Power bar + static float2 powerOrigin = new float2(42, 205); // Relative to radarOrigin + static Size powerSize = new Size(138,5); + + // mapchooser + Sheet mapChooserSheet; + Sprite mapChooserSprite; + int mapOffset = 0; + + public Chrome(Renderer r) + { + this.renderer = r; + rgbaRenderer = new SpriteRenderer(renderer, true, renderer.RgbaSpriteShader); + lineRenderer = new LineRenderer(renderer); + shpRenderer = new SpriteRenderer(renderer, true, renderer.WorldSpriteShader); + + repairButton = new Animation("repair"); + repairButton.PlayRepeating("normal"); + + sellButton = new Animation("sell"); + sellButton.PlayRepeating("normal"); + + pwrdownButton = new Animation("repair"); + pwrdownButton.PlayRepeating("normal"); + + optionsButton = new Animation("tabs"); + optionsButton.PlayRepeating("left-normal"); + + tabSprites = Rules.Info.Values + .Where(u => u.Traits.Contains()) + .ToDictionary( + u => u.Name, + u => SpriteSheetBuilder.LoadAllSprites(u.Traits.Get().Icon ?? (u.Name + "icon"))[0]); + + spsprites = Rules.Info.Values.SelectMany( u => u.Traits.WithInterface() ) + .ToDictionary( + u => u.Image, + u => SpriteSheetBuilder.LoadAllSprites(u.Image)[0]); + + var groups = Rules.Info.Values.Select( x => x.Category ).Distinct().Where( g => g != null ).ToList(); + + tabImageNames = groups.Select( + (g, i) => Pair.New(g, + OpenRA.Graphics.Util.MakeArray(3, + n => i.ToString()))) + .ToDictionary(a => a.First, a => a.Second); + + cantBuild = new Animation("clock"); + cantBuild.PlayFetchIndex("idle", () => 0); + + ready = new Animation("pips"); + ready.PlayRepeating("ready"); + clock = new Animation("clock"); + + mapChooserSheet = new Sheet(r, new Size(128, 128)); + } + + List visibleTabs = new List(); + + public void Tick(World world) + { + TickPaletteAnimation(); + TickRadarAnimation(); + + visibleTabs.Clear(); + foreach (var q in tabImageNames) + if (!Rules.TechTree.BuildableItems(world.LocalPlayer, q.Key).Any()) + { + CheckDeadTab(world, q.Key); + if (currentTab == q.Key) + currentTab = null; + } + else + visibleTabs.Add(q.Key); + + if (currentTab == null) + currentTab = visibleTabs.FirstOrDefault(); + } + + public void Draw( World world ) + { + DrawDownloadBar(); + + chromeCollection = "chrome-" + world.LocalPlayer.Country.Race; + radarCollection = "radar-" + world.LocalPlayer.Country.Race; + paletteCollection = "palette-" + world.LocalPlayer.Country.Race; + digitCollection = "digits-" + world.LocalPlayer.Country.Race; + + buttons.Clear(); + + renderer.Device.DisableScissor(); + renderer.RegularFont.DrawText( rgbaRenderer, "RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\n".F( + Game.RenderFrame, + Game.orderManager.FrameNumber, + PerfHistory.items["render"].LastValue, + PerfHistory.items["tick_time"].LastValue), + new int2(140, 15), Color.White); + + if (Game.Settings.PerfGraph) + PerfHistory.Render(renderer, world.WorldRenderer.lineRenderer); + + DrawRadar( world ); + DrawPower( world ); + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, chromeCollection, "moneybin"), new float2(Game.viewport.Width - 320, 0), "chrome"); + DrawMoney( world ); + rgbaRenderer.Flush(); + DrawButtons( world ); + + int paletteHeight = DrawBuildPalette(world, currentTab); + DrawSupportPowers( world ); + DrawBuildTabs(world, paletteHeight); + DrawChat(); + DrawOptionsMenu(); + } + + public void DrawDownloadBar() + { + if (PackageDownloader.IsIdle()) + return; + + var r = new Rectangle((Game.viewport.Width - 400) / 2, Game.viewport.Height - 110, 400, 100); + DrawDialogBackground(r, "dialog"); + + DrawCentered("Downloading: {0} (+{1} more)".F( + PackageDownloader.CurrentPackage.Split(':')[0], + PackageDownloader.RemainingPackages), + new int2( Game.viewport.Width /2, Game.viewport.Height - 90), + Color.White); + + DrawDialogBackground(new Rectangle(r.Left + 30, r.Top + 50, r.Width - 60, 20), + "dialog2"); + + var x1 = r.Left + 35; + var x2 = r.Right - 35; + var x = float2.Lerp(x1, x2, PackageDownloader.Fraction); + + for (var y = r.Top + 55; y < r.Top + 65; y++) + lineRenderer.DrawLine( + new float2(x1, y) + Game.viewport.Location, + new float2(x, y) + Game.viewport.Location, + Color.White, Color.White); + + lineRenderer.Flush(); + } + + public void DrawDialog(string text) + { + var w = renderer.BoldFont.Measure(text).X + 120; + var h = 100; + var r = new Rectangle((Game.viewport.Width - w) / 2, (Game.viewport.Height - h) / 2, w, h); + DrawDialogBackground(r, "dialog"); + DrawCentered(text, new int2(Game.viewport.Width / 2, Game.viewport.Height / 2 - 8), Color.White); + + // don't allow clicks through the dialog + AddButton(r, _ => { }); + } + + class MapInfo + { + public readonly string Filename; + public readonly Map Map; + + public MapInfo(string filename) + { + Filename = filename.ToLowerInvariant(); + Map = new Map(new IniFile(FileSystem.Open(Filename))); + } + }; + + Lazy> mapList = Lazy.New( + () => + { + var builtinMaps = new IniFile(FileSystem.Open("missions.pkt")).GetSection("Missions").Select(a => a.Key); + var mapsFolderMaps = Directory.GetFiles("maps/"); + return builtinMaps.Concat(mapsFolderMaps).Select(a => new MapInfo(a)).ToList(); + }); + + bool showMapChooser = false; + MapInfo currentMap; + bool mapPreviewDirty = true; + + void AddUiButton(int2 pos, string text, Action a) + { + var rect = new Rectangle(pos.X - 160 / 2, pos.Y - 4, 160, 24); + DrawDialogBackground( rect, "dialog2"); + DrawCentered(text, new int2(pos.X, pos.Y), Color.White); + AddButton(rect, a); + } + + public void DrawMapChooser() + { + var w = 800; + var h = 600; + var r = new Rectangle( (Game.viewport.Width - w) / 2, (Game.viewport.Height - h) / 2, w, h ); + DrawDialogBackground(r, "dialog"); + DrawCentered("Choose Map", new int2(r.Left + w / 2, r.Top + 20), Color.White); + + DrawDialogBackground(new Rectangle(r.Right - 200 - 160 / 2, + r.Bottom - 50 + 6, 160, 24), "dialog2"); + + AddUiButton(new int2(r.Left + 200, r.Bottom - 40), "OK", + _ => + { + Game.IssueOrder(Order.Chat("/map " + currentMap.Filename)); + showMapChooser = false; + }); + + AddUiButton(new int2(r.Right - 200, r.Bottom - 40), "Cancel", + _ => + { + showMapChooser = false; + }); + + if (mapPreviewDirty) + { + var b = Minimap.RenderTerrainBitmapWithSpawnPoints(currentMap.Map, Game.world.TileSet); // tileset -> hack + mapChooserSheet.Texture.SetData(b); + mapChooserSprite = new Sprite(mapChooserSheet, + Minimap.MakeMinimapBounds(currentMap.Map), TextureChannel.Alpha); + mapPreviewDirty = false; + } + + var mapRect = new Rectangle(r.Right - 280, r.Top + 30, 256, 256); + DrawDialogBackground(mapRect, "dialog2"); + rgbaRenderer.DrawSprite(mapChooserSprite, + new float2(mapRect.Location) + new float2(4, 4), + "chrome", + new float2(mapRect.Size) - new float2(8, 8)); + rgbaRenderer.Flush(); + + var y = r.Top + 50; + + int numListItems = ((r.Bottom - 60 - y ) / 20); + + for(int i = mapOffset; i < numListItems + mapOffset; i++, y += 20){ + var map = mapList.Value.ElementAt(i); + var itemRect = new Rectangle(r.Left + 50, y - 2, r.Width - 340, 20); + if (map == currentMap) + DrawDialogBackground(itemRect, "dialog2"); + + renderer.RegularFont.DrawText(rgbaRenderer, map.Map.Title, new int2(r.Left + 60, y), Color.White); + var closureMap = map; + AddButton(itemRect, _ => { currentMap = closureMap; mapPreviewDirty = true; }); + } + + y = mapRect.Bottom + 20; + DrawCentered("Title: {0}".F(currentMap.Map.Title, currentMap.Map.Height), + new int2(mapRect.Left + mapRect.Width / 2, y), Color.White); + y += 20; + DrawCentered("Size: {0}x{1}".F(currentMap.Map.Width, currentMap.Map.Height), + new int2(mapRect.Left + mapRect.Width / 2, y), Color.White); + y += 20; + DrawCentered("Theater: {0}".F(currentMap.Map.Theater, currentMap.Map.Height), + new int2(mapRect.Left + mapRect.Width / 2, y), Color.White); + y += 20; + DrawCentered("Spawnpoints: {0}".F(currentMap.Map.SpawnPoints.Count()), + new int2(mapRect.Left + mapRect.Width / 2, y), Color.White); + + y += 30; + AddUiButton(new int2(mapRect.Left + mapRect.Width / 2, y), "^", + _ => + { + mapOffset = (mapOffset - 1 < 0) ? 0 : mapOffset - 1; + }); + + y += 30; + AddUiButton(new int2(mapRect.Left + mapRect.Width / 2, y), "\\/", + _ => + { + mapOffset = (mapOffset + 1 > mapList.Value.Count() - numListItems) ? mapOffset : mapOffset + 1; + }); + + AddButton(r, _ => { }); + } + bool PaletteAvailable(int index) { return Game.LobbyInfo.Clients.All(c => c.PaletteIndex != index); } + bool SpawnPointAvailable(int index) { return (index == 0) || Game.LobbyInfo.Clients.All(c => c.SpawnPoint != index); } + + void CyclePalette(bool left) + { + var d = left ? +1 : Player.PlayerColors.Count() - 1; + + var newIndex = ((int)Game.world.LocalPlayer.PaletteIndex + d) % Player.PlayerColors.Count(); + + while (!PaletteAvailable(newIndex) && newIndex != (int)Game.world.LocalPlayer.PaletteIndex) + newIndex = (newIndex + d) % Player.PlayerColors.Count(); + + Game.IssueOrder( + Order.Chat("/pal " + newIndex)); + } + + void CycleRace(bool left) + { + var countries = Game.world.GetCountries(); + var nextCountry = countries.Concat(countries) + .SkipWhile(c => c != Game.world.LocalPlayer.Country) + .Skip(1) + .First(); + + Game.IssueOrder(Order.Chat("/race " + nextCountry.Name)); + } + + void CycleReady(bool left) + { + Game.IssueOrder( + new Order("ToggleReady", Game.world.LocalPlayer.PlayerActor, "") { IsImmediate = true }); + } + + void CycleSpawnPoint(bool left) + { + var d = left ? +1 : Game.world.Map.SpawnPoints.Count(); + + var newIndex = (Game.world.LocalPlayer.SpawnPointIndex + d) % (Game.world.Map.SpawnPoints.Count()+1); + + while (!SpawnPointAvailable(newIndex) && newIndex != (int)Game.world.LocalPlayer.SpawnPointIndex) + newIndex = (newIndex + d) % (Game.world.Map.SpawnPoints.Count()+1); + + Game.IssueOrder( + Order.Chat("/spawn " + newIndex)); + + } + + public void DrawLobby( World world ) + { + buttons.Clear(); + DrawDownloadBar(); + + if (showMapChooser) + { + DrawMapChooser(); + return; + } + + var w = 800; + var h = 600; + var r = new Rectangle( (Game.viewport.Width - w) / 2, (Game.viewport.Height - h) / 2, w, h ); + DrawDialogBackground(r, "dialog"); + DrawCentered("OpenRA Multiplayer Lobby", new int2(r.Left + w / 2, r.Top + 20), Color.White); + + DrawDialogBackground(new Rectangle(r.Right - 324, r.Top + 43, 304, 244),"dialog2"); + var minimapRect = new Rectangle(r.Right - 322, r.Top + 45, 300, 240); + + world.Minimap.Update(); + world.Minimap.Draw(minimapRect, true); + world.Minimap.DrawSpawnPoints(minimapRect); + + if (Game.IsHost) + { + AddUiButton(new int2(r.Right - 100, r.Top + 300), "Change Map", + _ => + { + showMapChooser = true; + currentMap = mapList.Value.Single( + m => m.Filename == Game.LobbyInfo.GlobalSettings.Map.ToLowerInvariant()); + mapPreviewDirty = true; + }); + } + + var f = renderer.BoldFont; + f.DrawText(rgbaRenderer, "Name", new int2(r.Left + 40, r.Top + 50), Color.White); + f.DrawText(rgbaRenderer, "Color", new int2(r.Left + 140, r.Top + 50), Color.White); + f.DrawText(rgbaRenderer, "Faction", new int2(r.Left + 220, r.Top + 50), Color.White); + f.DrawText(rgbaRenderer, "Status", new int2(r.Left + 290, r.Top + 50), Color.White); + f.DrawText(rgbaRenderer, "Spawn", new int2(r.Left + 390, r.Top + 50), Color.White); + + var y = r.Top + 80; + foreach (var client in Game.LobbyInfo.Clients) + { + var isLocalPlayer = client.Index == Game.orderManager.Connection.LocalClientId; + var paletteRect = new Rectangle(r.Left + 130, y - 2, 65, 22); + + if (isLocalPlayer) + { + // todo: name editing + var nameRect = new Rectangle(r.Left + 30, y - 2, 95, 22); + DrawDialogBackground(nameRect, "dialog2"); + + DrawDialogBackground(paletteRect, "dialog2"); + AddButton(paletteRect, CyclePalette); + + var raceRect = new Rectangle(r.Left + 210, y - 2, 65, 22); + DrawDialogBackground(raceRect, "dialog2"); + AddButton(raceRect, CycleRace); + + var readyRect = new Rectangle(r.Left + 280, y - 2, 95, 22); + DrawDialogBackground(readyRect, "dialog2"); + AddButton(readyRect, CycleReady); + + var spawnPointRect = new Rectangle(r.Left + 380, y - 2, 70, 22); + DrawDialogBackground(spawnPointRect, "dialog2"); + AddButton(spawnPointRect, CycleSpawnPoint); + } + + shpRenderer.Flush(); + + f = renderer.RegularFont; + f.DrawText(rgbaRenderer, client.Name, new int2(r.Left + 40, y), Color.White); + lineRenderer.FillRect(RectangleF.FromLTRB(paletteRect.Left + Game.viewport.Location.X + 5, + paletteRect.Top + Game.viewport.Location.Y + 5, + paletteRect.Right + Game.viewport.Location.X - 5, + paletteRect.Bottom+Game.viewport.Location.Y - 5), + Player.PlayerColors[client.PaletteIndex].c); + lineRenderer.Flush(); + f.DrawText(rgbaRenderer, client.Country, new int2(r.Left + 220, y), Color.White); + f.DrawText(rgbaRenderer, client.State.ToString(), new int2(r.Left + 290, y), Color.White); + f.DrawText(rgbaRenderer, (client.SpawnPoint == 0) ? "-" : client.SpawnPoint.ToString(), new int2(r.Left + 410, y), Color.White); + y += 30; + + rgbaRenderer.Flush(); + } + + var typingBox = new Rectangle(r.Left + 20, r.Bottom - 47, r.Width - 40, 27); + var chatBox = new Rectangle(r.Left + 20, r.Bottom - 269, r.Width - 40, 220); + + DrawDialogBackground(typingBox, "dialog2"); + DrawDialogBackground(chatBox, "dialog2"); + + DrawChat(typingBox, chatBox); + + // block clicks `through` the dialog + AddButton(r, _ => { }); + } + + public void TickRadarAnimation() + { + if (!radarAnimating) + return; + + // Increment frame + if (hasRadar) + radarAnimationFrame++; + else + radarAnimationFrame--; + + // Calculate radar bin position + if (radarAnimationFrame <= radarSlideAnimationLength) + radarOrigin = float2.Lerp(radarClosedOrigin, radarOpenOrigin, radarAnimationFrame * 1.0f / radarSlideAnimationLength); + + var eva = Game.world.LocalPlayer.PlayerActor.Info.Traits.Get(); + + // Play radar-on sound at the start of the activate anim (open) + if (radarAnimationFrame == radarSlideAnimationLength && hasRadar) + Sound.Play(eva.RadarUp); + + // Play radar-on sound at the start of the activate anim (close) + if (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength - 1 && !hasRadar) + Sound.Play(eva.RadarDown); + + // Minimap height + if (radarAnimationFrame >= radarSlideAnimationLength) + radarMinimapHeight = float2.Lerp(0, 192, (radarAnimationFrame - radarSlideAnimationLength) * 1.0f / radarActivateAnimationLength); + + // Animation is complete + if ((radarAnimationFrame == 0 && !hasRadar) + || (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength && hasRadar)) + { + radarAnimating = false; + } + } + + void DrawRadar( World world ) + { + var hasNewRadar = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(a => a.Trait.IsActive); + + if (hasNewRadar != hasRadar) + { + radarAnimating = true; + } + + hasRadar = hasNewRadar; + + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, radarCollection, "left"), radarOrigin, "chrome"); + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, radarCollection, "right"), radarOrigin + new float2(201, 0), "chrome"); + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, radarCollection, "bottom"), radarOrigin + new float2(0, 192), "chrome"); + + if (radarAnimating) + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, radarCollection, "bg"), radarOrigin + new float2(9, 0), "chrome"); + + rgbaRenderer.Flush(); + + if (radarAnimationFrame >= radarSlideAnimationLength) + { + RectangleF mapRect = new RectangleF(radarOrigin.X + 9, radarOrigin.Y+(192-radarMinimapHeight)/2, 192, radarMinimapHeight); + world.Minimap.Draw(mapRect, false); + } + } + + void AddButton(RectangleF r, Action b) { buttons.Add(Pair.New(r, b)); } + + void DrawBuildTabs( World world, int paletteHeight) + { + const int tabWidth = 24; + const int tabHeight = 40; + var x = paletteOrigin.X - tabWidth; + var y = paletteOrigin.Y + 9; + + var queue = world.LocalPlayer.PlayerActor.traits.Get(); + + foreach (var q in tabImageNames) + { + var groupName = q.Key; + if (!visibleTabs.Contains(groupName)) + continue; + + string[] tabKeys = { "normal", "ready", "selected" }; + var producing = queue.CurrentItem(groupName); + var index = q.Key == currentTab ? 2 : (producing != null && producing.Done) ? 1 : 0; + var race = world.LocalPlayer.Country.Race; + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer,"tabs-"+tabKeys[index], race+"-"+q.Key), new float2(x, y), "chrome"); + + buttons.Add(Pair.New(new RectangleF(x, y, tabWidth, tabHeight), + (Action)(isLmb => HandleTabClick(groupName)))); + y += tabHeight; + } + + rgbaRenderer.Flush(); + } + + void HandleTabClick(string button) + { + var eva = Game.world.LocalPlayer.PlayerActor.Info.Traits.Get(); + Sound.Play(eva.TabClick); + var wasOpen = paletteOpen; + paletteOpen = (currentTab == button && wasOpen) ? false : true; + currentTab = button; + if (wasOpen != paletteOpen) + paletteAnimating = true; + } + + void CheckDeadTab( World world, string groupName ) + { + var queue = world.LocalPlayer.PlayerActor.traits.Get(); + foreach( var item in queue.AllItems( groupName ) ) + Game.IssueOrder(Order.CancelProduction(world.LocalPlayer, item.Item)); + } + + void DrawMoney( World world ) + { + var moneyDigits = world.LocalPlayer.DisplayCash.ToString(); + var x = Game.viewport.Width - 65; + foreach (var d in moneyDigits.Reverse()) + { + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, digitCollection, (d - '0').ToString()), new float2(x, 6), "chrome"); + x -= 14; + } + } + + float? lastPowerProvidedPos; + float? lastPowerDrainedPos; + + void DrawPower( World world ) + { + // Nothing to draw + if (world.LocalPlayer.PowerProvided == 0 && world.LocalPlayer.PowerDrained == 0) + return; + + // Draw bar horizontally + var barStart = powerOrigin + radarOrigin; + var barEnd = barStart + new float2(powerSize.Width, 0); + + float powerScaleBy = 100; + var maxPower = Math.Max(world.LocalPlayer.PowerProvided, world.LocalPlayer.PowerDrained); + while (maxPower >= powerScaleBy) powerScaleBy *= 2; + + // Current power supply + var powerLevelTemp = barStart.X + (barEnd.X - barStart.X) * (world.LocalPlayer.PowerProvided / powerScaleBy); + lastPowerProvidedPos = float2.Lerp(lastPowerProvidedPos.GetValueOrDefault(powerLevelTemp), powerLevelTemp, .3f); + float2 powerLevel = new float2(lastPowerProvidedPos.Value, barStart.Y); + + var color = Color.LimeGreen; + if (world.LocalPlayer.GetPowerState() == PowerState.Low) + color = Color.Orange; + if (world.LocalPlayer.GetPowerState() == PowerState.Critical) + color = Color.Red; + + var colorDark = Graphics.Util.Lerp(0.25f, color, Color.Black); + for (int i = 0; i < powerSize.Height; i++) + { + color = (i-1 < powerSize.Height/2) ? color : colorDark; + float2 leftOffset = new float2(0,i); + float2 rightOffset = new float2(0,i); + // Indent corners + if ((i == 0 || i == powerSize.Height - 1) && powerLevel.X - barStart.X > 1) + { + leftOffset.X += 1; + rightOffset.X -= 1; + } + lineRenderer.DrawLine(Game.viewport.Location + barStart + leftOffset, Game.viewport.Location + powerLevel + rightOffset, color, color); + } + lineRenderer.Flush(); + + // Power usage indicator + var indicator = ChromeProvider.GetImage(renderer, radarCollection, "power-indicator"); + var powerDrainedTemp = barStart.X + (barEnd.X - barStart.X) * (world.LocalPlayer.PowerDrained / powerScaleBy); + lastPowerDrainedPos = float2.Lerp(lastPowerDrainedPos.GetValueOrDefault(powerDrainedTemp), powerDrainedTemp, .3f); + float2 powerDrainLevel = new float2(lastPowerDrainedPos.Value-indicator.size.X/2, barStart.Y-1); + + rgbaRenderer.DrawSprite(indicator, powerDrainLevel, "chrome"); + rgbaRenderer.Flush(); + } + + const int chromeButtonGap = 2; + + void DrawButtons( World world ) + { + var origin = new int2(Game.viewport.Width - 200, 2); + + foreach (var cb in world.WorldActor.traits.WithInterface()) + { + var button = cb; + var anim = new Animation(cb.Image); + anim.Play(cb.Enabled ? cb.Pressed ? "pressed" : "normal" : "disabled"); + origin.X -= (int)anim.Image.size.X + chromeButtonGap; + shpRenderer.DrawSprite(anim.Image, origin, "chrome"); + AddButton(new RectangleF(origin.X, origin.Y, anim.Image.size.X, anim.Image.size.Y), + _ => { if (button.Enabled) button.OnClick(); }); + } + + //Options + Rectangle optionsRect = new Rectangle(0,0, optionsButton.Image.bounds.Width, + optionsButton.Image.bounds.Height); + + var optionsDrawPos = new float2(optionsRect.Location); + + optionsButton.ReplaceAnim(optionsPressed ? "left-pressed" : "left-normal"); + + AddButton(optionsRect, isLmb => optionsPressed = !optionsPressed); + shpRenderer.DrawSprite(optionsButton.Image, optionsDrawPos, "chrome"); + shpRenderer.Flush(); + + renderer.RegularFont.DrawText(rgbaRenderer, "Options", new int2((int)(optionsButton.Image.size.X - renderer.RegularFont.Measure("Options").X) / 2, -2), Color.White); + } + + void DrawOptionsMenu() + { + if (optionsPressed){ + var width = 500; + var height = 300; + + DrawDialogBackground(new Rectangle((Game.viewport.Width - width)/ 2, (Game.viewport.Height-height) / 2, + width, height), "dialog"); + } + } + + void DrawDialogBackground(Rectangle r, string collection) + { + renderer.Device.EnableScissor(r.Left, r.Top, r.Width, r.Height); + + string[] images = { "border-t", "border-b", "border-l", "border-r", "corner-tl", "corner-tr", "corner-bl", "corner-br", "background" }; + var ss = Graphics.Util.MakeArray(9, n => ChromeProvider.GetImage(renderer, collection,images[n])); + + for( var x = r.Left + (int)ss[2].size.X; x < r.Right - (int)ss[3].size.X; x += (int)ss[8].size.X ) + for( var y = r.Top + (int)ss[0].size.Y; y < r.Bottom - (int)ss[1].size.Y; y += (int)ss[8].size.Y ) + rgbaRenderer.DrawSprite(ss[8], new float2(x, y), "chrome"); + + //draw borders + for (var y = r.Top + (int)ss[0].size.Y; y < r.Bottom - (int)ss[1].size.Y; y += (int)ss[2].size.Y) + { + rgbaRenderer.DrawSprite(ss[2], new float2(r.Left, y), "chrome"); + rgbaRenderer.DrawSprite(ss[3], new float2(r.Right - ss[3].size.X, y), "chrome"); + } + + for (var x = r.Left + (int)ss[2].size.X; x < r.Right - (int)ss[3].size.X; x += (int)ss[0].size.X) + { + rgbaRenderer.DrawSprite(ss[0], new float2(x, r.Top), "chrome"); + rgbaRenderer.DrawSprite(ss[1], new float2(x, r.Bottom - ss[1].size.Y), "chrome"); + } + + rgbaRenderer.DrawSprite(ss[4], new float2(r.Left, r.Top), "chrome"); + rgbaRenderer.DrawSprite(ss[5], new float2(r.Right - ss[5].size.X, r.Top), "chrome"); + rgbaRenderer.DrawSprite(ss[6], new float2(r.Left, r.Bottom - ss[6].size.Y), "chrome"); + rgbaRenderer.DrawSprite(ss[7], new float2(r.Right - ss[7].size.X, r.Bottom - ss[7].size.Y), "chrome"); + rgbaRenderer.Flush(); + + renderer.Device.DisableScissor(); + } + + void DrawChat() + { + var typingArea = new Rectangle(400, Game.viewport.Height - 30, Game.viewport.Width - 420, 30); + var chatLogArea = new Rectangle(400, Game.viewport.Height - 500, Game.viewport.Width - 420, 500 - 40); + + DrawChat(typingArea, chatLogArea); + } + + void DrawChat(Rectangle typingArea, Rectangle chatLogArea) + { + var chatpos = new int2(chatLogArea.X + 10, chatLogArea.Bottom - 6); + + renderer.Device.EnableScissor(typingArea.Left, typingArea.Top, typingArea.Width, typingArea.Height); + if (Game.chat.isChatting) + RenderChatLine(Tuple.New(Color.White, "Chat:", Game.chat.typing), + new int2(typingArea.X + 10, typingArea.Y + 6)); + + rgbaRenderer.Flush(); + renderer.Device.DisableScissor(); + + renderer.Device.EnableScissor(chatLogArea.Left, chatLogArea.Top, chatLogArea.Width, chatLogArea.Height); + foreach (var line in Game.chat.recentLines.AsEnumerable().Reverse()) + { + chatpos.Y -= 20; + RenderChatLine(line, chatpos); + } + + rgbaRenderer.Flush(); + renderer.Device.DisableScissor(); + } + + void RenderChatLine(Tuple line, int2 p) + { + var size = renderer.RegularFont.Measure(line.b); + renderer.RegularFont.DrawText(rgbaRenderer, line.b, p, line.a); + renderer.RegularFont.DrawText(rgbaRenderer, line.c, p + new int2(size.X + 10, 0), Color.White); + } + + void TickPaletteAnimation() + { + if (!paletteAnimating) + return; + + // Increment frame + if (paletteOpen) + paletteAnimationFrame++; + else + paletteAnimationFrame--; + + Log.Write("{0}",paletteAnimationFrame); + + // Calculate palette position + if (paletteAnimationFrame <= paletteAnimationLength) + paletteOrigin = float2.Lerp(paletteClosedOrigin, paletteOpenOrigin, paletteAnimationFrame * 1.0f / paletteAnimationLength); + + var eva = Game.world.LocalPlayer.PlayerActor.Info.Traits.Get(); + + // Play palette-open sound at the start of the activate anim (open) + if (paletteAnimationFrame == 1 && paletteOpen) + Sound.Play(eva.BuildPaletteOpen); + + // Play palette-close sound at the start of the activate anim (close) + if (paletteAnimationFrame == paletteAnimationLength + -1 && !paletteOpen) + Sound.Play(eva.BuildPaletteClose); + + // Animation is complete + if ((paletteAnimationFrame == 0 && !paletteOpen) + || (paletteAnimationFrame == paletteAnimationLength && paletteOpen)) + { + paletteAnimating = false; + } + } + + + // Return an int telling us the y coordinate at the bottom of the palette + int DrawBuildPalette( World world, string queueName ) + { + // Hack + int columns = paletteColumns; + float2 origin = new float2(paletteOrigin.X + 9, paletteOrigin.Y + 9); + + if (queueName == null) return 0; + + var x = 0; + var y = 0; + + var buildableItems = Rules.TechTree.BuildableItems(world.LocalPlayer, queueName).ToArray(); + + var allBuildables = Rules.TechTree.AllBuildables(world.LocalPlayer, queueName) + .Where(a => a.Traits.Contains()) + .Where(a => a.Traits.Get().Owner.Contains(world.LocalPlayer.Country.Race)) + .OrderBy(a => a.Traits.Get().TechLevel); + + var queue = world.LocalPlayer.PlayerActor.traits.Get(); + + var overlayBits = new List>(); + + string tooltipItem = null; + + // Draw the top border + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "top"), new float2(origin.X - 9, origin.Y - 9), "chrome"); + + // Draw the icons + int lasty = -1; + foreach (var item in allBuildables) + { + // Draw the background for this row + if (y != lasty) + { + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "bg-" + (y % 4).ToString()), new float2(origin.X - 9, origin.Y + 48 * y), "chrome"); + rgbaRenderer.Flush(); + lasty = y; + } + + var rect = new RectangleF(origin.X + x * 64, origin.Y + 48 * y, 64, 48); + var drawPos = new float2(rect.Location); + var isBuildingSomething = queue.CurrentItem(queueName) != null; + + shpRenderer.DrawSprite(tabSprites[item.Name], drawPos, "chrome"); + + var firstOfThis = queue.AllItems(queueName).FirstOrDefault(a => a.Item == item.Name); + + if (rect.Contains(lastMousePos.ToPoint())) + tooltipItem = item.Name; + + var overlayPos = drawPos + new float2((64 - ready.Image.size.X) / 2, 2); + + if (firstOfThis != null) + { + clock.PlayFetchIndex( "idle", + () => (firstOfThis.TotalTime - firstOfThis.RemainingTime) + * (clock.CurrentSequence.Length - 1)/ firstOfThis.TotalTime); + clock.Tick(); + shpRenderer.DrawSprite(clock.Image, drawPos, "chrome"); + + if (firstOfThis.Done) + { + ready.Play("ready"); + overlayBits.Add(Pair.New(ready.Image, overlayPos)); + } + else if (firstOfThis.Paused) + { + ready.Play("hold"); + overlayBits.Add(Pair.New(ready.Image, overlayPos)); + } + + var repeats = queue.AllItems(queueName).Count(a => a.Item == item.Name); + if (repeats > 1 || queue.CurrentItem(queueName) != firstOfThis) + { + var offset = -22; + var digits = repeats.ToString(); + foreach (var d in digits) + { + ready.PlayFetchIndex("groups", () => d - '0'); + ready.Tick(); + overlayBits.Add(Pair.New(ready.Image, overlayPos + new float2(offset, 0))); + offset += 6; + } + } + } + else + if (!buildableItems.Contains(item.Name) || isBuildingSomething) + overlayBits.Add(Pair.New(cantBuild.Image, drawPos)); + + var closureItemName = item.Name; + + var eva = world.LocalPlayer.PlayerActor.Info.Traits.Get(); + + AddButton(rect, buildableItems.Contains(item.Name) + ? isLmb => HandleBuildPalette(world, closureItemName, isLmb) + : (Action)(_ => Sound.Play(eva.TabClick))); + + if (++x == columns) { x = 0; y++; } + } + if (x != 0) y++; + + while (y < paletteRows) + { + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "bg-" + (y % 4).ToString()), new float2(origin.X - 9, origin.Y + 48 * y), "chrome"); + y++; + } + + foreach (var ob in overlayBits) + shpRenderer.DrawSprite(ob.First, ob.Second, "chrome"); + + shpRenderer.Flush(); + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "bottom"), new float2(origin.X - 9, origin.Y - 1 + 48 * y), "chrome"); + + // Draw dock + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "dock-top"), new float2(Game.viewport.Width - 14, origin.Y - 23), "chrome"); + for (int i = 0; i < y; i++) + { + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "dock-" + (y % 4).ToString()), new float2(Game.viewport.Width - 14, origin.Y + 48 * i), "chrome"); + } + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, paletteCollection, "dock-bottom"), new float2(Game.viewport.Width - 14, origin.Y - 1 + 48 * y), "chrome"); + rgbaRenderer.Flush(); + + if (tooltipItem != null && paletteOpen) + DrawProductionTooltip(world, tooltipItem, new float2(Game.viewport.Width, origin.Y + y * 48 + 9).ToInt2()/*tooltipPos*/); + + return y*48+9; + } + + void StartProduction( World world, string item ) + { + var eva = world.LocalPlayer.PlayerActor.Info.Traits.Get(); + var unit = Rules.Info[item]; + + Sound.Play(unit.Traits.Contains() ? eva.BuildingSelectAudio : eva.UnitSelectAudio); + Game.IssueOrder(Order.StartProduction(world.LocalPlayer, item)); + } + + void HandleBuildPalette( World world, string item, bool isLmb ) + { + var player = world.LocalPlayer; + var unit = Rules.Info[item]; + var queue = player.PlayerActor.traits.Get(); + var eva = player.PlayerActor.Info.Traits.Get(); + var producing = queue.AllItems(unit.Category).FirstOrDefault( a => a.Item == item ); + + Sound.Play(eva.TabClick); + + if (isLmb) + { + if (producing != null && producing == queue.CurrentItem(unit.Category)) + { + if (producing.Done) + { + if (unit.Traits.Contains()) + Game.controller.orderGenerator = new PlaceBuildingOrderGenerator(player.PlayerActor, item); + return; + } + + if (producing.Paused) + { + Game.IssueOrder(Order.PauseProduction(player, item, false)); + return; + } + } + + StartProduction(world, item); + } + else + { + if (producing != null) + { + // instant cancel of things we havent really started yet, and things that are finished + if (producing.Paused || producing.Done || producing.TotalCost == producing.RemainingCost) + { + Sound.Play(eva.CancelledAudio); + Game.IssueOrder(Order.CancelProduction(player, item)); + } + else + { + Sound.Play(eva.OnHoldAudio); + Game.IssueOrder(Order.PauseProduction(player, item, true)); + } + } + } + } + + int2 lastMousePos; + public bool HandleInput(World world, MouseInput mi) + { + if (mi.Event == MouseInputEvent.Move) + lastMousePos = mi.Location; + + var action = buttons.Where(a => a.First.Contains(mi.Location.ToPoint())) + .Select(a => a.Second).FirstOrDefault(); + + if (action == null) + return false; + + if (mi.Event == MouseInputEvent.Down) + action(mi.Button == MouseButton.Left); + + return true; + } + + public bool HitTest(int2 mousePos) + { + return buttons.Any(a => a.First.Contains(mousePos.ToPoint())); + } + + void DrawRightAligned(string text, int2 pos, Color c) + { + renderer.BoldFont.DrawText(rgbaRenderer, text, pos - new int2(renderer.BoldFont.Measure(text).X, 0), c); + } + + void DrawCentered(string text, int2 pos, Color c) + { + renderer.BoldFont.DrawText(rgbaRenderer, text, pos - new int2(renderer.BoldFont.Measure(text).X / 2, 0), c); + } + + void DrawProductionTooltip(World world, string unit, int2 pos) + { + var tooltipSprite = ChromeProvider.GetImage(renderer, chromeCollection, "tooltip-bg"); + var p = pos.ToFloat2() - new float2(tooltipSprite.size.X, 0); + rgbaRenderer.DrawSprite(tooltipSprite, p, "chrome"); + + + var info = Rules.Info[unit]; + var buildable = info.Traits.Get(); + + renderer.BoldFont.DrawText(rgbaRenderer, buildable.Description, p.ToInt2() + new int2(5, 5), Color.White); + + DrawRightAligned( "${0}".F(buildable.Cost), pos + new int2(-5,5), + world.LocalPlayer.Cash + world.LocalPlayer.Ore >= buildable.Cost ? Color.White : Color.Red); + + var bi = info.Traits.GetOrDefault(); + if (bi != null) + DrawRightAligned("ϟ{0}".F(bi.Power), pos + new int2(-5, 20), + world.LocalPlayer.PowerProvided - world.LocalPlayer.PowerDrained + bi.Power >= 0 + ? Color.White : Color.Red); + + var buildings = Rules.TechTree.GatherBuildings( world.LocalPlayer ); + p += new int2(5, 5); + p += new int2(0, 15); + if (!Rules.TechTree.CanBuild(info, world.LocalPlayer, buildings)) + { + var prereqs = buildable.Prerequisites + .Select( a => Description( a ) ); + renderer.RegularFont.DrawText(rgbaRenderer, "Requires {0}".F(string.Join(", ", prereqs.ToArray())), p.ToInt2(), + Color.White); + } + + if (buildable.LongDesc != null) + { + p += new int2(0, 15); + renderer.RegularFont.DrawText(rgbaRenderer, buildable.LongDesc.Replace( "\\n", "\n" ), p.ToInt2(), Color.White); + } + + rgbaRenderer.Flush(); + } + + static string Description( string a ) + { + if( a[ 0 ] == '@' ) + return "any " + a.Substring( 1 ); + else + return Rules.Info[ a.ToLowerInvariant() ].Traits.Get().Description; + } + + void DrawSupportPowers( World world ) + { + var powers = world.LocalPlayer.PlayerActor.traits.WithInterface(); + var numPowers = powers.Count(p => p.IsAvailable); + + if (numPowers == 0) return; + + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, chromeCollection, "specialbin-top"), new float2(0, 14), "chrome"); + for (var i = 1; i < numPowers; i++) + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, chromeCollection, "specialbin-middle"), new float2(0, 14 + i * 51), "chrome"); + rgbaRenderer.DrawSprite(ChromeProvider.GetImage(renderer, chromeCollection, "specialbin-bottom"), new float2(0, 14 + numPowers * 51), "chrome"); + + rgbaRenderer.Flush(); + + var y = 24; + + SupportPower tooltipItem = null; + int2 tooltipPos = int2.Zero; + + foreach (var sp in powers) + { + var image = spsprites[sp.Info.Image]; + if (sp.IsAvailable) + { + var drawPos = new float2(5, y); + shpRenderer.DrawSprite(image, drawPos, "chrome"); + + clock.PlayFetchIndex("idle", + () => (sp.TotalTime - sp.RemainingTime) + * (clock.CurrentSequence.Length - 1) / sp.TotalTime); + clock.Tick(); + + shpRenderer.DrawSprite(clock.Image, drawPos, "chrome"); + + var rect = new Rectangle(5, y, 64, 48); + if (sp.IsReady) + { + ready.Play("ready"); + shpRenderer.DrawSprite(ready.Image, + drawPos + new float2((64 - ready.Image.size.X) / 2, 2), + "chrome"); + } + + AddButton(rect, HandleSupportPower(sp)); + + if (rect.Contains(lastMousePos.ToPoint())) + { + tooltipItem = sp; + tooltipPos = drawPos.ToInt2() + new int2(72, 0); + } + + y += 51; + } + } + + shpRenderer.Flush(); + + if (tooltipItem != null) + DrawSupportPowerTooltip(world, tooltipItem, tooltipPos); + } + + Action HandleSupportPower(SupportPower sp) + { + return b => { if (b) sp.Activate(); }; + } + + string FormatTime(int ticks) + { + var seconds = ticks / 25; + var minutes = seconds / 60; + + return "{0:D2}:{1:D2}".F(minutes, seconds % 60); + } + + void DrawSupportPowerTooltip(World world, SupportPower sp, int2 pos) + { + var tooltipSprite = ChromeProvider.GetImage(renderer, chromeCollection, "tooltip-bg"); + rgbaRenderer.DrawSprite(tooltipSprite, pos, "chrome"); + rgbaRenderer.Flush(); + + pos += new int2(5, 5); + + renderer.BoldFont.DrawText(rgbaRenderer, sp.Info.Description, pos, Color.White); + + var timer = "Charge Time: {0}".F(FormatTime(sp.RemainingTime)); + DrawRightAligned(timer, pos + new int2((int)tooltipSprite.size.X - 10, 0), Color.White); + + if (sp.Info.LongDesc != null) + { + pos += new int2(0, 25); + renderer.RegularFont.DrawText(rgbaRenderer, sp.Info.LongDesc.Replace("\\n", "\n"), pos, Color.White); + } + + rgbaRenderer.Flush(); + } + + public void SetCurrentTab(string produces) + { + if (!paletteOpen) + paletteAnimating = true; + paletteOpen = true; + currentTab = produces; + } + } +} diff --git a/OpenRA.Game/Combat.cs b/OpenRA.Game/Combat.cs new file mode 100644 index 0000000000..b5ac5513cc --- /dev/null +++ b/OpenRA.Game/Combat.cs @@ -0,0 +1,114 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using OpenRA.Effects; +using OpenRA.GameRules; +using OpenRA.Traits; + +namespace OpenRA +{ + static class Combat /* some utility bits that are shared between various things */ + { + public static void DoImpact(int2 loc, int2 visualLoc, + WeaponInfo weapon, ProjectileInfo projectile, WarheadInfo warhead, Actor firedBy) + { + var world = firedBy.World; + + var targetTile = ((1f / Game.CellSize) * loc.ToFloat2()).ToInt2(); + + var isWater = world.IsWater(targetTile); + var hitWater = world.IsCellBuildable(targetTile, UnitMovementType.Float); + + if (warhead.Explosion != 0) + world.AddFrameEndTask( + w => w.Add(new Explosion(w, visualLoc, warhead.Explosion, hitWater))); + + var impactSound = warhead.ImpactSound; + if (hitWater && warhead.WaterImpactSound != null) + impactSound = warhead.WaterImpactSound; + if (impactSound != null) Sound.Play(impactSound + ".aud"); + + if (!isWater) world.Map.AddSmudge(targetTile, warhead); + if (warhead.Ore) world.Map.DestroyOre(targetTile.X, targetTile.Y); + + var firepowerModifier = firedBy.traits + .WithInterface() + .Select(a => a.GetFirepowerModifier()) + .Product(); + + var maxSpread = GetMaximumSpread(weapon, warhead, firepowerModifier); + var hitActors = world.FindUnitsInCircle(loc, maxSpread); + + foreach (var victim in hitActors) + victim.InflictDamage(firedBy, + (int)GetDamageToInflict(victim, loc, weapon, warhead, firepowerModifier), warhead); + } + + static float GetMaximumSpread(WeaponInfo weapon, WarheadInfo warhead, float modifier) + { + return (int)(warhead.Spread * Math.Log(Math.Abs(weapon.Damage * modifier), 2)); + } + + static float GetDamageToInflict(Actor target, int2 loc, WeaponInfo weapon, WarheadInfo warhead, float modifier) + { + if (!WeaponValidForTarget(weapon, target)) + return 0f; + + var distance = (target.CenterLocation - loc).Length*1/24f; + var rawDamage = weapon.Damage * modifier * (float)Math.Exp(-distance / warhead.Spread); + var multiplier = warhead.EffectivenessAgainst(target.Info.Traits.Get().Armor); + return rawDamage * multiplier; + } + + public static bool WeaponValidForTarget(WeaponInfo weapon, Actor target) + { + var projectile = Rules.ProjectileInfo[weapon.Projectile]; + var warhead = Rules.WarheadInfo[weapon.Warhead]; + var unit = target.traits.GetOrDefault(); + + if (warhead.EffectivenessAgainst(target.Info.Traits.Get().Armor) <= 0) + return false; + + if (target.traits.Contains()) + return projectile.ASW; + + if (unit != null && unit.Altitude > 0) + return projectile.AA; + + if (projectile.UnderWater && !target.Info.Traits.Get().WaterBound) + return false; + + return projectile.AG; + } + + public static bool HasAnyValidWeapons(Actor self, Actor target) + { + var info = self.Info.Traits.Get(); + if (info.PrimaryWeapon != null && + WeaponValidForTarget(self.GetPrimaryWeapon(), target)) return true; + if (info.SecondaryWeapon != null && + WeaponValidForTarget(self.GetSecondaryWeapon(), target)) return true; + + return false; + } + } +} diff --git a/OpenRA.Game/Controller.cs b/OpenRA.Game/Controller.cs new file mode 100644 index 0000000000..bdbd16fc60 --- /dev/null +++ b/OpenRA.Game/Controller.cs @@ -0,0 +1,146 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Orders; +using OpenRA.Traits; + +namespace OpenRA +{ + public class Controller : IHandleInput + { + public IOrderGenerator orderGenerator = new UnitOrderGenerator(); + public Selection selection = new Selection(); + + public void CancelInputMode() { orderGenerator = new UnitOrderGenerator(); } + + public bool ToggleInputMode() where T : IOrderGenerator, new() + { + if (orderGenerator is T) + { + CancelInputMode(); + return false; + } + else + { + orderGenerator = new T(); + return true; + } + } + + void ApplyOrders(World world, float2 xy, MouseInput mi) + { + if (orderGenerator == null) return; + + var orders = orderGenerator.Order(world, xy.ToInt2(), mi).ToArray(); + Game.orderManager.IssueOrders( orders ); + + var voicedActor = orders.Select(o => o.Subject) + .FirstOrDefault(a => a.Owner == world.LocalPlayer && a.traits.Contains()); + + var isMove = orders.Any(o => o.OrderString == "Move"); + var isAttack = orders.Any( o => o.OrderString == "Attack" ); + + if (voicedActor != null) + { + + if(voicedActor.traits.GetOrDefault().CanEnterCell(xy.ToInt2())) + Sound.PlayVoice(isAttack ? "Attack" : "Move", voicedActor); + + if (isMove) + world.Add(new Effects.MoveFlash(world, Game.CellSize * xy)); + } + } + + float2 dragStart, dragEnd; + public bool HandleInput(World world, MouseInput mi) + { + var xy = Game.viewport.ViewToWorld(mi); + + if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down) + { + if (!(orderGenerator is PlaceBuildingOrderGenerator)) + dragStart = dragEnd = xy; + ApplyOrders(world, xy, mi); + } + + if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Move) + dragEnd = xy; + + if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up) + { + if (orderGenerator is UnitOrderGenerator) + { + var newSelection = world.SelectActorsInBox(Game.CellSize * dragStart, Game.CellSize * xy); + selection.Combine(world, newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy); + } + + dragStart = dragEnd = xy; + } + + if (mi.Button == MouseButton.None && mi.Event == MouseInputEvent.Move) + dragStart = dragEnd = xy; + + if (mi.Button == MouseButton.Right && mi.Event == MouseInputEvent.Down) + ApplyOrders(world, xy, mi); + + return true; + } + + public Pair? SelectionBox + { + get + { + if (dragStart == dragEnd) return null; + return Pair.New(Game.CellSize * dragStart, Game.CellSize * dragEnd); + } + } + + public float2 MousePosition { get { return dragEnd; } } + Modifiers modifiers; + + public string ChooseCursor( World world ) + { + int sync = world.SyncHash(); + + try + { + var mi = new MouseInput + { + Location = ( Game.CellSize * MousePosition - Game.viewport.Location ).ToInt2(), + Button = MouseButton.Right, + Modifiers = modifiers + }; + + return orderGenerator.GetCursor( world, MousePosition.ToInt2(), mi ); + } + finally + { + if( sync != world.SyncHash() ) + throw new InvalidOperationException( "Desync in Controller.ChooseCursor" ); + } + } + + public void SetModifiers(Modifiers mods) { modifiers = mods; } + } +} diff --git a/OpenRA.Game/Cursor.cs b/OpenRA.Game/Cursor.cs new file mode 100644 index 0000000000..fb4605d222 --- /dev/null +++ b/OpenRA.Game/Cursor.cs @@ -0,0 +1,36 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA +{ + public class Cursor + { + CursorSequence sequence; + public Cursor(string cursor) + { + sequence = SequenceProvider.GetCursorSequence(cursor); + } + + public Sprite GetSprite(int frame) { return sequence.GetSprite(frame); } + public int2 GetHotspot() { return sequence.Hotspot; } + } +} diff --git a/OpenRA.Game/Effects/Bullet.cs b/OpenRA.Game/Effects/Bullet.cs new file mode 100755 index 0000000000..cc84c643d5 --- /dev/null +++ b/OpenRA.Game/Effects/Bullet.cs @@ -0,0 +1,133 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + public class Bullet : IEffect + { + readonly Player Owner; + readonly Actor FiredBy; + readonly WeaponInfo Weapon; + readonly ProjectileInfo Projectile; + readonly WarheadInfo Warhead; + readonly int2 Src; + readonly int2 Dest; + readonly int2 VisualDest; + readonly int SrcAltitude; + readonly int DestAltitude; + + int t = 0; + Animation anim; + + const int BaseBulletSpeed = 100; /* pixels / 40ms frame */ + + public Bullet(string weapon, Player owner, Actor firedBy, + int2 src, int2 dest, int srcAltitude, int destAltitude) + : this(Rules.WeaponInfo[weapon], owner, firedBy, src, dest, srcAltitude, destAltitude) { } + + /* src, dest are *pixel* coords */ + public Bullet(WeaponInfo weapon, Player owner, Actor firedBy, + int2 src, int2 dest, int srcAltitude, int destAltitude) + { + Owner = owner; + FiredBy = firedBy; + Src = src; + Dest = dest; + SrcAltitude = srcAltitude; + DestAltitude = destAltitude; + VisualDest = Dest + new int2( + firedBy.World.CosmeticRandom.Next(-10, 10), + firedBy.World.CosmeticRandom.Next(-10, 10)); + Weapon = weapon; + Projectile = Rules.ProjectileInfo[Weapon.Projectile]; + Warhead = Rules.WarheadInfo[Weapon.Warhead]; + + if (Projectile.Image != null && Projectile.Image != "none") + { + if (Projectile.Rotates) + anim = new Animation(Projectile.Image, () => Traits.Util.GetFacing((dest - src).ToFloat2(), 0)); + else + anim = new Animation(Projectile.Image); + + anim.PlayRepeating("idle"); + } + } + + int TotalTime() { return (Dest - Src).Length * BaseBulletSpeed / Weapon.Speed; } + + public void Tick( World world ) + { + t += 40; + + if (t > TotalTime()) /* remove finished bullets */ + { + world.AddFrameEndTask(w => w.Remove(this)); + Combat.DoImpact(Dest, VisualDest - new int2( 0, DestAltitude ), + Weapon, Projectile, Warhead, FiredBy); + } + + if (Projectile.Trail != null) + { + var at = (float)t / TotalTime(); + var altitude = float2.Lerp(SrcAltitude, DestAltitude, at); + var pos = float2.Lerp(Src.ToFloat2(), VisualDest.ToFloat2(), at) + - 0.5f * anim.Image.size - new float2(0, altitude); + + var highPos = (Projectile.High || Projectile.Arcing) + ? (pos - new float2(0, (VisualDest - Src).Length * height * 4 * at * (1 - at))) + : pos; + + world.AddFrameEndTask(w => w.Add( + new Smoke(w, highPos.ToInt2(), Projectile.Trail))); + } + } + + const float height = .1f; + + public IEnumerable Render() + { + if (anim != null) + { + var at = (float)t / TotalTime(); + + var altitude = float2.Lerp(SrcAltitude, DestAltitude, at); + var pos = float2.Lerp( Src.ToFloat2(), VisualDest.ToFloat2(), at) + - 0.5f * anim.Image.size - new float2( 0, altitude ); + + if (Projectile.High || Projectile.Arcing) + { + if (Projectile.Shadow) + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, "shadow"); + + var highPos = pos - new float2(0, (VisualDest - Src).Length * height * 4 * at * (1 - at)); + + yield return new Renderable(anim.Image, highPos - .5f * anim.Image.size, Owner.Palette); + } + else + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, Projectile.UnderWater ? "shadow" : Owner.Palette); + } + } + } +} diff --git a/OpenRA.Game/Effects/Corpse.cs b/OpenRA.Game/Effects/Corpse.cs new file mode 100755 index 0000000000..f780c42541 --- /dev/null +++ b/OpenRA.Game/Effects/Corpse.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class Corpse : IEffect + { + readonly Animation anim; + readonly float2 pos; + readonly Player owner; + + public Corpse(Actor fromActor, int death) + { + anim = new Animation(fromActor.traits.GetOrDefault().GetImage(fromActor)); + anim.PlayThen("die{0}".F(death + 1), + () => fromActor.World.AddFrameEndTask(w => w.Remove(this))); + + pos = fromActor.CenterLocation; + owner = fromActor.Owner; + } + + public void Tick( World world ) { anim.Tick(); } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, owner.Palette); + } + } +} diff --git a/OpenRA.Game/Effects/DelayedAction.cs b/OpenRA.Game/Effects/DelayedAction.cs new file mode 100755 index 0000000000..a3e91eea6c --- /dev/null +++ b/OpenRA.Game/Effects/DelayedAction.cs @@ -0,0 +1,46 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + public class DelayedAction : IEffect + { + Action a; + int delay; + + public DelayedAction(int delay, Action a) + { + this.a = a; + this.delay = delay; + } + + public void Tick( World world ) + { + if (--delay <= 0) + world.AddFrameEndTask(w => { w.Remove(this); a(); }); + } + + public IEnumerable Render() { yield break; } + } +} diff --git a/OpenRA.Game/Effects/Explosion.cs b/OpenRA.Game/Effects/Explosion.cs new file mode 100755 index 0000000000..7691396bf9 --- /dev/null +++ b/OpenRA.Game/Effects/Explosion.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + public class Explosion : IEffect + { + Animation anim; + int2 pos; + + public Explosion( World world, int2 pixelPos, int style, bool isWater) + { + this.pos = pixelPos; + var variantSuffix = isWater ? "w" : ""; + anim = new Animation("explosion"); + anim.PlayThen(style.ToString() + variantSuffix, + () => world.AddFrameEndTask(w => w.Remove(this))); + } + + public void Tick( World world ) { anim.Tick(); } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, "effect"); + } + + public Player Owner { get { return null; } } + } +} diff --git a/OpenRA.Game/Effects/FlashTarget.cs b/OpenRA.Game/Effects/FlashTarget.cs new file mode 100755 index 0000000000..d6ee3a934e --- /dev/null +++ b/OpenRA.Game/Effects/FlashTarget.cs @@ -0,0 +1,52 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class FlashTarget : IEffect + { + Actor target; + int remainingTicks = 4; + + public FlashTarget(Actor target) + { + this.target = target; + foreach (var e in target.World.Effects.OfType().Where(a => a.target == target).ToArray()) + target.World.Remove(e); + } + + public void Tick( World world ) + { + if (--remainingTicks == 0) + world.AddFrameEndTask(w => w.Remove(this)); + } + + public IEnumerable Render() + { + if (remainingTicks % 2 == 0) + foreach (var r in target.Render()) + yield return r.WithPalette("highlight"); + } + } +} diff --git a/OpenRA.Game/Effects/IEffect.cs b/OpenRA.Game/Effects/IEffect.cs new file mode 100755 index 0000000000..aef764ddad --- /dev/null +++ b/OpenRA.Game/Effects/IEffect.cs @@ -0,0 +1,31 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + public interface IEffect + { + void Tick( World world ); + IEnumerable Render(); + } +} diff --git a/OpenRA.Game/Effects/LaserZap.cs b/OpenRA.Game/Effects/LaserZap.cs new file mode 100644 index 0000000000..baef036339 --- /dev/null +++ b/OpenRA.Game/Effects/LaserZap.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class LaserZap : IEffect + { + readonly int2 from, to; + readonly int radius; + int timeUntilRemove = 10; // # of frames + int totalTime = 10; + Color color; + + public LaserZap(int2 from, int2 to, int radius, Color color) + { + this.from = from; + this.to = to; + this.color = color; + this.radius = radius; + } + + public void Tick(World world) + { + if (timeUntilRemove <= 0) + world.AddFrameEndTask(w => w.Remove(this)); + --timeUntilRemove; + } + + public IEnumerable Render() + { + int alpha = (int)((1-(float)(totalTime-timeUntilRemove)/totalTime)*255); + Color rc = Color.FromArgb(alpha,color); + + float2 unit = 1.0f/(from - to).Length*(from - to).ToFloat2(); + float2 norm = new float2(-unit.Y, unit.X); + + for (int i = -radius; i < radius; i++) + Game.world.WorldRenderer.lineRenderer.DrawLine(from + i * norm, to + i * norm, rc, rc); + + yield break; + } + } +} diff --git a/OpenRA.Game/Effects/Missile.cs b/OpenRA.Game/Effects/Missile.cs new file mode 100755 index 0000000000..93531dfcef --- /dev/null +++ b/OpenRA.Game/Effects/Missile.cs @@ -0,0 +1,114 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class Missile : IEffect + { + readonly Actor FiredBy; + readonly WeaponInfo Weapon; + readonly ProjectileInfo Projectile; + readonly WarheadInfo Warhead; + float2 Pos; + readonly Actor Target; + readonly Animation anim; + int Facing; + int t; + int Altitude; + + public Missile(WeaponInfo weapon, Player owner, Actor firedBy, + int2 src, Actor target, int altitude, int facing) + { + Weapon = weapon; + Projectile = Rules.ProjectileInfo[Weapon.Projectile]; + Warhead = Rules.WarheadInfo[Weapon.Warhead]; + FiredBy = firedBy; + Target = target; + Pos = src.ToFloat2(); + Altitude = altitude; + Facing = facing; + + if (Projectile.Image != null && Projectile.Image != "none") + { + if (Projectile.Rotates) + anim = new Animation(Projectile.Image, () => Facing); + else + anim = new Animation(Projectile.Image); + + anim.PlayRepeating("idle"); + } + } + + const int MissileCloseEnough = 7; + const float Scale = .2f; + + public void Tick( World world ) + { + t += 40; + + var targetUnit = Target.traits.GetOrDefault(); + var targetAltitude = targetUnit != null ? targetUnit.Altitude : 0; + Altitude += Math.Sign(targetAltitude - Altitude); + + Traits.Util.TickFacing(ref Facing, + Traits.Util.GetFacing(Target.CenterLocation - Pos, Facing), + Projectile.ROT); + + anim.Tick(); + + var dist = Target.CenterLocation - Pos; + if (dist.LengthSquared < MissileCloseEnough * MissileCloseEnough || Target.IsDead) + Explode(world); + + var speed = Scale * Weapon.Speed * ((targetAltitude > 0 && Weapon.TurboBoost) ? 1.5f : 1f); + + var angle = Facing / 128f * Math.PI; + var move = speed * -float2.FromAngle((float)angle); + Pos += move; + + if (Projectile.Trail != null) + world.AddFrameEndTask(w => w.Add( + new Smoke(w, (Pos - 1.5f * move - new int2( 0, Altitude )).ToInt2(), Projectile.Trail))); + + if (Projectile.RangeLimit != 0 && t > Projectile.RangeLimit * 40) + Explode(world); + } + + void Explode(World world) + { + world.AddFrameEndTask(w => w.Remove(this)); + + if (t > Projectile.Arm * 40) /* don't blow up in our launcher's face! */ + Combat.DoImpact(Pos.ToInt2(), Pos.ToInt2(), Weapon, Projectile, Warhead, FiredBy); + return; + } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, Pos - 0.5f * anim.Image.size - new float2(0, Altitude), "effect"); + } + } +} diff --git a/OpenRA.Game/Effects/MoveFlash.cs b/OpenRA.Game/Effects/MoveFlash.cs new file mode 100755 index 0000000000..51723594bf --- /dev/null +++ b/OpenRA.Game/Effects/MoveFlash.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class MoveFlash : IEffect + { + Animation anim = new Animation("moveflsh"); + float2 pos; + + public MoveFlash( World world, float2 pos ) + { + this.pos = pos; + anim.PlayThen( "idle", + () => world.AddFrameEndTask( + w => w.Remove( this ) ) ); + } + + public void Tick( World world ) { anim.Tick(); } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, "shadow"); + } + } +} diff --git a/OpenRA.Game/Effects/RepairIndicator.cs b/OpenRA.Game/Effects/RepairIndicator.cs new file mode 100755 index 0000000000..93bcb0dcda --- /dev/null +++ b/OpenRA.Game/Effects/RepairIndicator.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class RepairIndicator : IEffect + { + int framesLeft = (int)(Rules.General.RepairRate * 25 * 60 / 2); + Actor a; + Animation anim = new Animation("select"); + + public RepairIndicator(Actor a) { this.a = a; anim.PlayRepeating("repair"); } + + public void Tick( World world ) + { + if (--framesLeft == 0 || a.IsDead) + world.AddFrameEndTask(w => w.Remove(this)); + } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, + a.CenterLocation - .5f * anim.Image.size, "chrome"); + } + } +} diff --git a/OpenRA.Game/Effects/Smoke.cs b/OpenRA.Game/Effects/Smoke.cs new file mode 100755 index 0000000000..f67637401d --- /dev/null +++ b/OpenRA.Game/Effects/Smoke.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class Smoke : IEffect + { + readonly int2 pos; + readonly Animation anim; + + public Smoke(World world, int2 pos, string trail) + { + this.pos = pos; + anim = new Animation(trail); + anim.PlayThen("idle", + () => world.AddFrameEndTask(w => w.Remove(this))); + } + + public void Tick( World world ) + { + anim.Tick(); + } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, "effect"); + } + } +} diff --git a/OpenRA.Game/Effects/TeslaZap.cs b/OpenRA.Game/Effects/TeslaZap.cs new file mode 100755 index 0000000000..9df281dbe2 --- /dev/null +++ b/OpenRA.Game/Effects/TeslaZap.cs @@ -0,0 +1,104 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Effects +{ + class TeslaZap : IEffect + { + readonly int2 from, to; + readonly Sequence tesla; + int timeUntilRemove = 2; // # of frames + + public TeslaZap( int2 from, int2 to ) + { + this.from = from; + this.to = to; + this.tesla = SequenceProvider.GetSequence( "litning", "bright" ); + } + + public void Tick( World world ) + { + if( timeUntilRemove <= 0 ) + world.AddFrameEndTask( w => w.Remove( this ) ); + --timeUntilRemove; + } + + public IEnumerable Render() + { + if( from.X < to.X ) + return DrawZap( from, to, tesla ); + else if( from.X > to.X || from.Y > to.Y ) + return DrawZap( to, from, tesla ); + else + return DrawZap( from, to, tesla ); + } + + static IEnumerable DrawZap( int2 from, int2 to, Sequence tesla ) + { + int2 d = to - from; + if( d.X < 8 ) + { + var prev = new int2( 0, 0 ); + var y = d.Y; + while( y >= prev.Y + 8 ) + { + yield return new Renderable( tesla.GetSprite( 2 ), (float2)( from + prev - new int2( 0, 8 ) ), "effect"); + prev.Y += 8; + } + } + else + { + var prev = new int2( 0, 0 ); + for( int i = 1 ; i < d.X ; i += 8 ) + { + var y = i * d.Y / d.X; + if( y <= prev.Y - 8 ) + { + yield return new Renderable(tesla.GetSprite(3), (float2)(from + prev - new int2(8, 16)), "effect"); + prev.Y -= 8; + while( y <= prev.Y - 8 ) + { + yield return new Renderable(tesla.GetSprite(2), (float2)(from + prev - new int2(0, 16)), "effect"); + prev.Y -= 8; + } + } + else if( y >= prev.Y + 8 ) + { + yield return new Renderable(tesla.GetSprite(0), (float2)(from + prev - new int2(8, 8)), "effect"); + prev.Y += 8; + while( y >= prev.Y + 8 ) + { + yield return new Renderable(tesla.GetSprite(2), (float2)(from + prev - new int2(0, 8)), "effect"); + prev.Y += 8; + } + } + else + yield return new Renderable(tesla.GetSprite(1), (float2)(from + prev - new int2(8, 8)), "effect"); + + prev.X += 8; + } + } + } + } +} diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs new file mode 100644 index 0000000000..6da6b1e8b7 --- /dev/null +++ b/OpenRA.Game/Exts.cs @@ -0,0 +1,90 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.GameRules; +using OpenRA.Traits; + +namespace OpenRA +{ + public static class Exts + { + public static bool HasModifier(this Modifiers k, Modifiers mod) + { + return (k & mod) == mod; + } + + public static IEnumerable SymmetricDifference(this IEnumerable xs, IEnumerable ys) + { + // this is probably a shockingly-slow way to do this, but it's concise. + return xs.Except(ys).Concat(ys.Except(xs)); + } + + public static float Product(this IEnumerable xs) + { + return xs.Aggregate(1f, (a, x) => a * x); + } + + public static WeaponInfo GetPrimaryWeapon(this Actor self) + { + var info = self.Info.Traits.GetOrDefault(); + if (info == null) return null; + + var weapon = info.PrimaryWeapon; + if (weapon == null) return null; + + return Rules.WeaponInfo[weapon]; + } + + public static WeaponInfo GetSecondaryWeapon(this Actor self) + { + var info = self.Info.Traits.GetOrDefault(); + if (info == null) return null; + + var weapon = info.SecondaryWeapon; + if (weapon == null) return null; + + return Rules.WeaponInfo[weapon]; + } + + public static int GetMaxHP(this Actor self) + { + var oai = self.Info.Traits.GetOrDefault(); + if (oai == null) return 0; + return oai.HP; + } + + public static V GetOrAdd( this Dictionary d, K k ) + where V : new() + { + return d.GetOrAdd( k, _ => new V() ); + } + + public static V GetOrAdd( this Dictionary d, K k, Func createFn ) + { + V ret; + if( !d.TryGetValue( k, out ret ) ) + d.Add( k, ret = createFn( k ) ); + return ret; + } + } +} diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs new file mode 100644 index 0000000000..c8c9da67d4 --- /dev/null +++ b/OpenRA.Game/Game.cs @@ -0,0 +1,427 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Network; +using OpenRA.Support; +using OpenRA.Traits; +using Timer = OpenRA.Support.Timer; +using System.Runtime.InteropServices; +using System.IO; + +namespace OpenRA +{ + public static class Game + { + public static readonly int CellSize = 24; + + public static World world; + internal static Viewport viewport; + public static Controller controller; + internal static Chrome chrome; + public static UserSettings Settings; + + internal static OrderManager orderManager; + + public static bool skipMakeAnims = true; + + internal static Renderer renderer; + static int2 clientSize; + static string mapName; + internal static Session LobbyInfo = new Session(); + static bool changePending; + + public static void LoadModPackages(Manifest manifest) + { + FileSystem.UnmountAll(); + Timer.Time("reset: {0}"); + + foreach (var dir in manifest.Folders) FileSystem.Mount(dir); + foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg); + + Timer.Time("mount temporary packages: {0}"); + } + + public static void ChangeMap(string mapName) + { + Timer.Time( "----ChangeMap" ); + + var manifest = new Manifest(LobbyInfo.GlobalSettings.Mods); + Timer.Time( "manifest: {0}" ); + + Game.changePending = false; + Game.mapName = mapName; + SheetBuilder.Initialize(renderer); + + LoadModPackages(manifest); + + Rules.LoadRules(mapName, manifest); + Timer.Time( "load rules: {0}" ); + + world = null; // trying to access the old world will NRE, rather than silently doing it wrong. + + Player.ResetPlayerColorList(); + ChromeProvider.Initialize(manifest.Chrome); + + world = new World(); + + Game.world.ActorAdded += a => + { + if (a.Owner != null && a.Info.Traits.Contains()) + a.Owner.Shroud.Explore(a); + }; + Timer.Time( "world: {0}" ); + + SequenceProvider.Initialize(manifest.Sequences); + viewport = new Viewport(clientSize, Game.world.Map.Offset, Game.world.Map.Offset + Game.world.Map.Size, renderer); + Timer.Time( "ChromeProv, SeqProv, viewport: {0}" ); + + skipMakeAnims = true; + foreach (var treeReference in Game.world.Map.Trees) + world.CreateActor(treeReference.Image, new int2(treeReference.Location), null); + Timer.Time( "trees: {0}" ); + + world.LoadMapActors(Rules.AllRules); + skipMakeAnims = false; + Timer.Time( "map actors: {0}" ); + + chrome = new Chrome(renderer); + Timer.Time( "chrome: {0}" ); + + Timer.Time( "----end ChangeMap" ); + chat.AddLine(Color.White, "Debug", "Map change {0} -> {1}".F(Game.mapName, mapName)); + } + + internal static void Initialize(string mapName, Renderer renderer, int2 clientSize, int localPlayer, Controller controller) + { + + Game.renderer = renderer; + Game.clientSize = clientSize; + + // todo + Sound.Initialize(); + PerfHistory.items["render"].hasNormalTick = false; + PerfHistory.items["batches"].hasNormalTick = false; + PerfHistory.items["text"].hasNormalTick = false; + Game.controller = controller; + + ChangeMap(mapName); + + if (Settings.Replay != "") + throw new NotImplementedException(); + else + { + var connection = (string.IsNullOrEmpty(Settings.NetworkHost)) + ? new EchoConnection() + : new NetworkConnection( Settings.NetworkHost, Settings.NetworkPort ); + orderManager = new OrderManager(connection, "replay.rep"); + } + } + + static int lastTime = Environment.TickCount; + + public static void ResetTimer() + { + lastTime = Environment.TickCount; + } + + public static int RenderFrame = 0; + + internal static Chat chat = new Chat(); + + public static void Tick() + { + if (changePending && PackageDownloader.IsIdle()) + { + ChangeMap(LobbyInfo.GlobalSettings.Map); + return; + } + + int t = Environment.TickCount; + int dt = t - lastTime; + if (dt >= Settings.Timestep) + { + using (new PerfSample("tick_time")) + { + lastTime += Settings.Timestep; + chrome.Tick( world ); + + orderManager.TickImmediate( world ); + + if (orderManager.IsReadyForNextFrame) + { + orderManager.Tick(world); + controller.orderGenerator.Tick(world); + controller.selection.Tick(world); + + world.Tick(); + + PerfHistory.Tick(); + } + else + if (orderManager.FrameNumber == 0) + lastTime = Environment.TickCount; + } + } + + using (new PerfSample("render")) + { + ++RenderFrame; + viewport.DrawRegions( world ); + } + + PerfHistory.items["render"].Tick(); + PerfHistory.items["batches"].Tick(); + PerfHistory.items["text"].Tick(); + } + + public static void SyncLobbyInfo(string data) + { + var session = new Session(); + session.GlobalSettings.Mods = Settings.InitialMods; + + var ys = MiniYaml.FromString(data); + foreach (var y in ys) + { + if (y.Key == "GlobalSettings") + { + FieldLoader.Load(session.GlobalSettings, y.Value); + continue; + } + + int index; + if (!int.TryParse(y.Key, out index)) + continue; // not a player. + + var client = new Session.Client(); + FieldLoader.Load(client, y.Value); + session.Clients.Add(client); + + world.players[index].SyncFromLobby(client); + } + + LobbyInfo = session; + + if (Game.orderManager.Connection.ConnectionState == ConnectionState.Connected) + world.SetLocalPlayer(Game.orderManager.Connection.LocalClientId); + + if (Game.orderManager.FramesAhead != LobbyInfo.GlobalSettings.OrderLatency + && !Game.orderManager.GameStarted) + { + Game.orderManager.FramesAhead = LobbyInfo.GlobalSettings.OrderLatency; + Game.chat.AddLine(Color.White, "Server", + "Order lag is now {0} frames.".F(LobbyInfo.GlobalSettings.OrderLatency)); + } + + if (PackageDownloader.SetPackageList(LobbyInfo.GlobalSettings.Packages) + || mapName != LobbyInfo.GlobalSettings.Map) + changePending = true; + } + + public static void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */ + + public static void StartGame() + { + Game.chat.Reset(); + + var taken = LobbyInfo.Clients.Where(c => c.SpawnPoint != 0) + .Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList(); + + var available = world.Map.SpawnPoints.Except(taken).ToList(); + + foreach (var client in LobbyInfo.Clients) + { + var sp = (client.SpawnPoint == 0) + ? ChooseSpawnPoint(available, taken) + : world.Map.SpawnPoints.ElementAt(client.SpawnPoint - 1); + + foreach (var ssu in world.players[client.Index].PlayerActor + .traits.WithInterface()) + ssu.SpawnStartingUnits(world.players[client.Index], sp); + } + + Game.viewport.GoToStartLocation( Game.world.LocalPlayer ); + orderManager.StartGame(); + } + + static int2 ChooseSpawnPoint(List available, List taken) + { + if (available.Count == 0) + throw new InvalidOperationException("No free spawnpoint."); + + var n = taken.Count == 0 + ? world.SharedRandom.Next(available.Count) + : available // pick the most distant spawnpoint from everyone else + .Select((k,i) => Pair.New(k,i)) + .OrderByDescending(a => taken.Sum(t => (t - a.First).LengthSquared)) + .Select(a => a.Second) + .First(); + + var sp = available[n]; + available.RemoveAt(n); + taken.Add(sp); + return sp; + } + + static int2 lastPos; + public static void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e, Modifiers modifierKeys) + { + int sync = Game.world.SyncHash(); + + if (ev == MouseInputEvent.Down) + lastPos = new int2(e.Location); + + if (ev == MouseInputEvent.Move && (e.Button == MouseButtons.Middle || e.Button == (MouseButtons.Left | MouseButtons.Right))) + { + var p = new int2(e.Location); + viewport.Scroll(lastPos - p); + lastPos = p; + } + + Game.viewport.DispatchMouseInput( world, + new MouseInput + { + Button = (MouseButton)(int)e.Button, + Event = ev, + Location = new int2(e.Location), + Modifiers = modifierKeys, + }); + + if( sync != Game.world.SyncHash() ) + throw new InvalidOperationException( "Desync in DispatchMouseInput" ); + } + + public static void HandleKeyDown( KeyEventArgs e ) + { + //int sync = Game.world.SyncHash(); + + + //if( sync != Game.world.SyncHash() ) + // throw new InvalidOperationException( "Desync in OnKeyDown" ); + } + + public static bool IsHost + { + get { return orderManager.Connection.LocalClientId == 0; } + } + + public static void HandleKeyPress( KeyPressEventArgs e, Modifiers modifiers ) + { + int sync = Game.world.SyncHash(); + + if( e.KeyChar == '\r' ) + Game.chat.Toggle(); + else if( Game.chat.isChatting ) + Game.chat.TypeChar( e.KeyChar ); + else + if( e.KeyChar >= '0' && e.KeyChar <= '9' ) + Game.controller.selection.DoControlGroup( world, + e.KeyChar - '0', modifiers ); + + if( sync != Game.world.SyncHash() ) + throw new InvalidOperationException( "Desync in OnKeyPress" ); + } + + public static void HandleModifierKeys(Modifiers mods) + { + controller.SetModifiers(mods); + } + + static Size GetResolution(Settings settings) + { + var desktopResolution = Screen.PrimaryScreen.Bounds.Size; + if (Game.Settings.Width > 0 && Game.Settings.Height > 0) + { + desktopResolution.Width = Game.Settings.Width; + desktopResolution.Height = Game.Settings.Height; + } + return new Size( + desktopResolution.Width, + desktopResolution.Height); + } + + public static void PreInit(Settings settings) + { + while (!Directory.Exists("mods")) + { + var current = Directory.GetCurrentDirectory(); + if (Directory.GetDirectoryRoot(current) == current) + throw new InvalidOperationException("Unable to load MIX files."); + Directory.SetCurrentDirectory(".."); + } + + + LoadUserSettings(settings); + Game.LobbyInfo.GlobalSettings.Mods = Game.Settings.InitialMods; + + // Load the default mod to access required files + Game.LoadModPackages(new Manifest(Game.LobbyInfo.GlobalSettings.Mods)); + + UiOverlay.ShowUnitDebug = Game.Settings.UnitDebug; + WorldRenderer.ShowUnitPaths = Game.Settings.PathDebug; + Renderer.SheetSize = Game.Settings.SheetSize; + + bool windowed = !Game.Settings.Fullscreen; + var resolution = GetResolution(settings); + renderer = new Renderer(resolution, windowed); + resolution = renderer.Resolution; + + var controller = new Controller(); /* a bit of insane input routing */ + + Game.Initialize(Game.Settings.Map, renderer, new int2(resolution), Game.Settings.Player, controller); + + // ShowCursor(false); + Game.ResetTimer(); + } + + static void LoadUserSettings(Settings settings) + { + Game.Settings = new UserSettings(); + var settingsFile = settings.GetValue("settings", "settings.ini"); + FileSystem.Mount("./"); + if (FileSystem.Exists(settingsFile)) + FieldLoader.Load(Game.Settings, + new IniFile(FileSystem.Open(settingsFile)).GetSection("Settings")); + FileSystem.UnmountAll(); + } + + static bool quit; + internal static void Run() + { + while (!quit) + { + Game.Tick(); + Application.DoEvents(); + } + } + + public static void Exit() + { + quit = true; + } + } +} diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs new file mode 100644 index 0000000000..d35890d849 --- /dev/null +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -0,0 +1,139 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.GameRules +{ + public class ActorInfo + { + public readonly string Name; + public readonly string Category; + public readonly TypeDictionary Traits = new TypeDictionary(); + + public ActorInfo( string name, MiniYaml node, Dictionary allUnits ) + { + var mergedNode = MergeWithParent( node, allUnits ).Nodes; + + Name = name; + MiniYaml categoryNode; + if( mergedNode.TryGetValue( "Category", out categoryNode ) ) + Category = categoryNode.Value; + + foreach( var t in mergedNode ) + if( t.Key != "Inherits" && t.Key != "Category" ) + Traits.Add( LoadTraitInfo( t.Key, t.Value ) ); + } + + static MiniYaml GetParent( MiniYaml node, Dictionary allUnits ) + { + MiniYaml inherits; + node.Nodes.TryGetValue( "Inherits", out inherits ); + if( inherits == null || string.IsNullOrEmpty( inherits.Value ) ) + return null; + + MiniYaml parent; + allUnits.TryGetValue( inherits.Value, out parent ); + if( parent == null ) + return null; + + return parent; + } + + static MiniYaml MergeWithParent( MiniYaml node, Dictionary allUnits ) + { + var parent = GetParent( node, allUnits ); + if( parent != null ) + return MiniYaml.Merge( node, MergeWithParent( parent, allUnits ) ); + return node; + } + + static Pair[] ModAssemblies; + public static void LoadModAssemblies(Manifest m) + { + var asms = new List>(); + + // all the core stuff is in this assembly + asms.Add(Pair.New(typeof(ITraitInfo).Assembly, typeof(ITraitInfo).Namespace)); + + // add the mods + foreach (var a in m.Assemblies) + asms.Add(Pair.New(Assembly.LoadFile(Path.GetFullPath(a)), Path.GetFileNameWithoutExtension(a))); + ModAssemblies = asms.ToArray(); + } + + static ITraitInfo LoadTraitInfo(string traitName, MiniYaml my) + { + if (traitName.Contains('@')) + traitName = traitName.Substring(0, traitName.IndexOf('@')); + + foreach (var mod in ModAssemblies) + { + var fullTypeName = mod.Second + "." + traitName + "Info"; + var info = (ITraitInfo)mod.First.CreateInstance(fullTypeName); + if (info == null) continue; + FieldLoader.Load(info, my); + return info; + } + + throw new InvalidOperationException("Cannot locate trait: {0}".F(traitName)); + } + + public IEnumerable TraitsInConstructOrder() + { + var ret = new List(); + var t = Traits.WithInterface().ToList(); + int index = 0; + while( t.Count != 0 ) + { + if( index >= t.Count ) + throw new InvalidOperationException( "Trait prerequisites not satisfied (or prerequisite loop)" ); + + var prereqs = PrerequisitesOf( t[ index ] ); + if( prereqs.Count == 0 || prereqs.All( n => ret.Any( x => x.GetType().IsSubclassOf( n ) ) ) ) + { + ret.Add( t[ index ] ); + t.RemoveAt( index ); + index = 0; + } + else + ++index; + } + + return ret; + } + + static List PrerequisitesOf( ITraitInfo info ) + { + return info + .GetType() + .GetInterfaces() + .Where( t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof( ITraitPrerequisite<> ) ) + .Select( t => t.GetGenericArguments()[ 0 ] ) + .ToList(); + } + } +} diff --git a/OpenRA.Game/GameRules/Footprint.cs b/OpenRA.Game/GameRules/Footprint.cs new file mode 100644 index 0000000000..739c92873d --- /dev/null +++ b/OpenRA.Game/GameRules/Footprint.cs @@ -0,0 +1,74 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.GameRules +{ + static class Footprint + { + public static IEnumerable Tiles( string name, BuildingInfo buildingInfo, int2 topLeft ) + { + var dim = buildingInfo.Dimensions; + + var footprint = buildingInfo.Footprint.Where(x => !char.IsWhiteSpace(x)); + if (buildingInfo.Bib) + { + dim.Y += 1; + footprint = footprint.Concat(new char[dim.X]); + } + + return TilesWhere( name, dim, footprint.ToArray(), a => a != '_' ).Select( t => t + topLeft ); + } + + public static IEnumerable Tiles(Actor a, Traits.Building building) + { + return Tiles( a.Info.Name, a.Info.Traits.Get(), a.Location ); + } + + public static IEnumerable UnpathableTiles( string name, BuildingInfo buildingInfo, int2 position ) + { + var footprint = buildingInfo.Footprint.Where( x => !char.IsWhiteSpace( x ) ).ToArray(); + foreach( var tile in TilesWhere( name, buildingInfo.Dimensions, footprint, a => a == 'x' ) ) + yield return tile + position; + } + + static IEnumerable TilesWhere( string name, int2 dim, char[] footprint, Func cond ) + { + if( footprint.Length != dim.X * dim.Y ) + throw new InvalidOperationException( "Invalid footprint for " + name ); + int index = 0; + + for( int y = 0 ; y < dim.Y ; y++ ) + for( int x = 0 ; x < dim.X ; x++ ) + if( cond( footprint[ index++ ] ) ) + yield return new int2( x, y ); + } + + public static int2 AdjustForBuildingSize( BuildingInfo buildingInfo ) + { + var dim = buildingInfo.Dimensions; + return new int2( dim.X / 2, dim.Y > 1 ? ( dim.Y + 1 ) / 2 : 0 ); + } + } +} diff --git a/OpenRA.Game/GameRules/GeneralInfo.cs b/OpenRA.Game/GameRules/GeneralInfo.cs new file mode 100644 index 0000000000..4a6321e6d8 --- /dev/null +++ b/OpenRA.Game/GameRules/GeneralInfo.cs @@ -0,0 +1,112 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.GameRules +{ + public class GeneralInfo + { + /* Special Weapons */ + public readonly float GapRegenInterval =0; + public readonly int BadgerBombCount = 1; + + /* Chrono Side Effects */ + public readonly float QuakeChance = 0; + public readonly float QuakeDamage = 0; /* percent */ + public readonly float VortexChance = 0; + public readonly int VortexDamage = 0; + public readonly int VortexRange = 0; + public readonly int VortexSpeed = 0; + + /* Repair & Refit */ + public readonly float RefundPercent = 0; + public readonly float ReloadRate = 0; + public readonly float RepairPercent = 0; + public readonly float RepairRate = 0; + public readonly int RepairStep = 0; + public readonly float URepairPercent = 0; + public readonly int URepairStep = 0; + + /* Combat & Damage */ + public readonly float TurboBoost = 1.5f; + public readonly float BallisticScatter = 0; + public readonly float C4Delay = 0; + public readonly float ExpSpread = 0; + public readonly int FireSupress = 0; + public readonly float HomingScatter = 0; + public readonly int MaxDamage = 0; + public readonly int MinDamage = 0; + public readonly bool OreExplosive = false; + public readonly bool PlayerAutoCrush = false; + public readonly bool PlayerReturnFire = false; + public readonly bool PlayerScatter = false; + public readonly bool TreeTargeting = false; + public readonly int Incoming = 0; + + /* Income & Production */ + public readonly float BuildSpeed = 0; + public readonly float BuildupTime = 0; + public readonly int GemValue = 0; + public readonly int GoldValue = 0; + public readonly float OreTruckRate = 0; + public readonly bool SeparateAircraft = true; + public readonly float SurvivorRate = 0; + + /* Audo/Visual Map Controls */ + public readonly bool AllyReveal = true; + public readonly float ConditionRed = 0; + public readonly float ConditionYellow = 0; + public readonly int DropZoneRadius = 0; + public readonly bool EnemyHealth = true; + public readonly int Gravity = 0; + public readonly float IdleActionFrequency = 0; + public readonly float MessageDelay = 0; + public readonly float MovieTime = 0; + public readonly bool NamedCivilians = false; + public readonly float SavourDelay = 0; + public readonly int ShroudRate = 0; + public readonly int SpeakDelay = 0; + public readonly int TimerWarning = 0; + public readonly bool FlashLowPower = false; + + /* Computer & Movement Controls */ + public readonly bool CurleyShuffle = false; + public readonly float BaseBias = 0; + public readonly float BaseDefenseDelay = 0; + public readonly float CloseEnough = 0; + public readonly int DamageDelay = 0; + public readonly int GameSpeeBias = 0; + public readonly int LZScanRadius = 0; + public readonly bool MineAware = false; + public readonly float Stray = 0; + public readonly float SubmergeDelay = 0; + public readonly float SuspendDelay = 0; + public readonly int SuspendPriority = 0; + public readonly float TeamDelay = 0; + + /* Misc */ + [Obsolete] + public readonly bool FineDiffControl = false; + + /* OpenRA-specific */ + public readonly int LowPowerSlowdown = 3; /* build time multiplier */ + } +} diff --git a/OpenRA.Game/GameRules/InfoLoader.cs b/OpenRA.Game/GameRules/InfoLoader.cs new file mode 100644 index 0000000000..e382db71ab --- /dev/null +++ b/OpenRA.Game/GameRules/InfoLoader.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using OpenRA.FileFormats; + +namespace OpenRA.GameRules +{ + public class InfoLoader : IEnumerable> + { + readonly Dictionary infos = new Dictionary(); + + public InfoLoader(params Pair>[] srcs) + { + foreach (var src in srcs) + foreach (var name in Rules.Categories[src.First]) + { + var t = src.Second(name); + FieldLoader.Load(t, Rules.AllRules.GetSection(name)); + infos[name] = t; + } + } + + public T this[string name] + { + get { return infos[name.ToLowerInvariant()]; } + } + + public IEnumerator> GetEnumerator() { return infos.GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return infos.GetEnumerator(); } + } +} diff --git a/OpenRA.Game/GameRules/ProjectileInfo.cs b/OpenRA.Game/GameRules/ProjectileInfo.cs new file mode 100644 index 0000000000..9ae57844d3 --- /dev/null +++ b/OpenRA.Game/GameRules/ProjectileInfo.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.GameRules +{ + public class ProjectileInfo + { + public readonly bool AA = false; + public readonly bool AG = true; + public readonly bool ASW = false; + public readonly bool Arcing = false; + public readonly int Arm = 0; + public readonly bool Degenerates = false; + public readonly bool High = false; + public readonly string Image = null; + public readonly bool Inaccurate = false; + public readonly bool Parachuted = false; + public readonly bool Proximity = false; + public readonly int ROT = 0; + public readonly bool Rotates = false; + public readonly bool Shadow = true; + public readonly bool UnderWater = false; + public readonly int RangeLimit = 0; + + // OpenRA-specific: + public readonly string Trail = null; + } +} diff --git a/OpenRA.Game/GameRules/Rules.cs b/OpenRA.Game/GameRules/Rules.cs new file mode 100755 index 0000000000..4f165c020e --- /dev/null +++ b/OpenRA.Game/GameRules/Rules.cs @@ -0,0 +1,82 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; + +namespace OpenRA +{ + public static class Rules + { + public static IniFile AllRules; + public static Dictionary> Categories = new Dictionary>(); + public static InfoLoader WeaponInfo; + public static InfoLoader WarheadInfo; + public static InfoLoader ProjectileInfo; + public static InfoLoader VoiceInfo; + public static GeneralInfo General; + public static TechTree TechTree; + + public static Dictionary Info; + + public static void LoadRules(string map, Manifest m) + { + var legacyRules = m.LegacyRules.Reverse().ToList(); + legacyRules.Insert(0, map); + AllRules = new IniFile(legacyRules.Select(a => FileSystem.Open(a)).ToArray()); + + General = new GeneralInfo(); + FieldLoader.Load(General, AllRules.GetSection("General")); + + LoadCategories( + "Weapon", + "Warhead", + "Projectile", + "Voice"); + + WeaponInfo = new InfoLoader( + Pair.New>("Weapon", _ => new WeaponInfo())); + WarheadInfo = new InfoLoader( + Pair.New>("Warhead", _ => new WarheadInfo())); + ProjectileInfo = new InfoLoader( + Pair.New>("Projectile", _ => new ProjectileInfo())); + VoiceInfo = new InfoLoader( + Pair.New>("Voice", _ => new VoiceInfo())); + + var yamlRules = m.Rules.Reverse().Select(a => MiniYaml.FromFile(a)).Aggregate(MiniYaml.Merge); + + ActorInfo.LoadModAssemblies(m); + Info = new Dictionary(); + foreach( var kv in yamlRules ) + Info.Add(kv.Key.ToLowerInvariant(), new ActorInfo(kv.Key.ToLowerInvariant(), kv.Value, yamlRules)); + + TechTree = new TechTree(); + } + + static void LoadCategories(params string[] types) + { + foreach (var t in types) + Categories[t] = AllRules.GetSection(t + "Types").Select(x => x.Key.ToLowerInvariant()).ToList(); + } + } +} diff --git a/OpenRA.Game/GameRules/TechTree.cs b/OpenRA.Game/GameRules/TechTree.cs new file mode 100755 index 0000000000..2bb0cfcb5f --- /dev/null +++ b/OpenRA.Game/GameRules/TechTree.cs @@ -0,0 +1,100 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.GameRules +{ + public class TechTree + { + readonly Cache> producesIndex = new Cache>(x => new List()); + + public TechTree() + { + foreach( var info in Rules.Info.Values ) + { + var pi = info.Traits.GetOrDefault(); + if (pi != null) + foreach( var p in pi.Produces ) + producesIndex[ p ].Add( info ); + } + } + + public Cache> GatherBuildings( Player player ) + { + var ret = new Cache>( x => new List() ); + foreach( var b in player.World.Queries.OwnedBy[player].Where( x=>x.Info.Traits.Contains() ) ) + { + ret[ b.Info.Name ].Add( b ); + var buildable = b.Info.Traits.GetOrDefault(); + if( buildable != null ) + foreach( var alt in buildable.AlternateName ) + ret[ alt ].Add( b ); + } + return ret; + } + + public bool CanBuild( ActorInfo info, Player player, Cache> playerBuildings ) + { + var bi = info.Traits.GetOrDefault(); + if( bi == null ) return false; + + if( !bi.Owner.Contains( player.Country.Race ) ) + return false; + + foreach( var p in bi.Prerequisites ) + if( playerBuildings[ p ].Count == 0 ) + return false; + + if( producesIndex[ info.Category ].All( x => playerBuildings[ x.Name ].Count == 0 ) ) + return false; + + return true; + } + + public IEnumerable BuildableItems( Player player, params string[] categories ) + { + var playerBuildings = GatherBuildings( player ); + foreach( var unit in AllBuildables( player, categories ) ) + if( CanBuild( unit, player, playerBuildings ) ) + yield return unit.Name; + } + + public IEnumerable AllBuildables(Player player, params string[] categories) + { + return Rules.Info.Values + .Where( x => x.Name[ 0 ] != '^' ) + .Where( x => categories.Contains( x.Category ) ) + .Where( x => x.Traits.Contains() ); + } + + public IEnumerable UnitBuiltAt( ActorInfo info ) + { + var builtAt = info.Traits.Get().BuiltAt; + if( builtAt.Length != 0 ) + return builtAt.Select( x => Rules.Info[ x.ToLowerInvariant() ] ); + else + return producesIndex[ info.Category ]; + } + } +} diff --git a/OpenRA.Game/GameRules/UserSettings.cs b/OpenRA.Game/GameRules/UserSettings.cs new file mode 100644 index 0000000000..dd5b1d26f8 --- /dev/null +++ b/OpenRA.Game/GameRules/UserSettings.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.GameRules +{ + public class UserSettings + { + // Debug settings + public readonly bool UnitDebug = false; + public readonly bool PathDebug = false; + public readonly bool PerfGraph = true; + + // Window settings + public readonly int Width = 0; + public readonly int Height = 0; + public readonly bool Fullscreen = false; + + // Internal game settings + public readonly int Timestep = 40; + public readonly int SheetSize = 512; + + // External game settings + public readonly string NetworkHost = ""; + public readonly int NetworkPort = 0; + public readonly string Map = "scm12ea.ini"; + public readonly int Player = 1; + public readonly string Replay = ""; + public readonly string PlayerName = ""; + public readonly string[] InitialMods = { "ra" }; + + // Gameplay options + // TODO: These need to die + public readonly bool RepairRequiresConyard = true; + public readonly bool PowerDownBuildings = true; + } +} diff --git a/OpenRA.Game/GameRules/VoiceInfo.cs b/OpenRA.Game/GameRules/VoiceInfo.cs new file mode 100644 index 0000000000..fbe335f44e --- /dev/null +++ b/OpenRA.Game/GameRules/VoiceInfo.cs @@ -0,0 +1,74 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.FileFormats; + +namespace OpenRA.GameRules +{ + public class VoiceInfo + { + public readonly string[] SovietVariants = { ".aud" }; + public readonly string[] AlliedVariants = { ".aud" }; + public readonly string[] Select = { }; + public readonly string[] Move = { }; + public readonly string[] Attack = null; + public readonly string[] Die = { }; + + public readonly Lazy> Pools; + + public VoiceInfo() + { + Pools = Lazy.New(() => + new Dictionary + { + { "Select", new VoicePool(Select) }, + { "Move", new VoicePool(Move) }, + { "Attack", new VoicePool( Attack ?? Move ) }, + { "Die", new VoicePool(Die) }, + }); + } + } + + public class VoicePool + { + readonly string[] clips; + readonly List liveclips = new List(); + + public VoicePool(params string[] clips) + { + this.clips = clips; + } + + public string GetNext() + { + if (liveclips.Count == 0) + liveclips.AddRange(clips); + + if (liveclips.Count == 0) + return null; /* avoid crashing if there's no clips at all */ + + var i = Game.world.CosmeticRandom.Next(liveclips.Count); + var s = liveclips[i]; + liveclips.RemoveAt(i); + return s; + } + } +} diff --git a/OpenRA.Game/GameRules/WarheadInfo.cs b/OpenRA.Game/GameRules/WarheadInfo.cs new file mode 100644 index 0000000000..dc41fd0064 --- /dev/null +++ b/OpenRA.Game/GameRules/WarheadInfo.cs @@ -0,0 +1,46 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.GameRules +{ + public class WarheadInfo + { + public readonly int Spread = 1; + public readonly float[] Verses = { 1, 1, 1, 1, 1 }; + public readonly bool Wall = false; + public readonly bool Wood = false; + public readonly bool Ore = false; + public readonly int Explosion = 0; + public readonly int InfDeath = 0; + public readonly string ImpactSound = null; + public readonly string WaterImpactSound = null; + + public float EffectivenessAgainst(ArmorType at) { return Verses[ (int)at ]; } + } + + public enum ArmorType + { + none = 0, + wood = 1, + light = 2, + heavy = 3, + concrete = 4, + } +} diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs new file mode 100755 index 0000000000..4e893d7d38 --- /dev/null +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.GameRules +{ + public class WeaponInfo + { + public readonly int Burst = 1; + public readonly bool Charges = false; + public readonly int Damage = 0; + public readonly string Projectile = "Invisible"; + public readonly int ROF = 1; // in 1/15 second units. + public readonly float Range = 0; + public readonly string Report = null; + public readonly int Speed = -1; + public readonly bool TurboBoost = false; + public readonly string Warhead = null; + + public readonly bool RenderAsTesla = false; + public readonly bool RenderAsLaser = false; + public readonly bool UsePlayerColor = true; + public readonly int BeamRadius = 1; + } +} diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs new file mode 100644 index 0000000000..910d3219fe --- /dev/null +++ b/OpenRA.Game/Graphics/Animation.cs @@ -0,0 +1,150 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Graphics +{ + public class Animation + { + string name; + public Sequence CurrentSequence { get; private set; } + int frame = 0; + bool backwards = false; + bool tickAlways; + + Func facingFunc; + + public string Name { get { return name; } } + + public Animation( string name ) + : this( name, () => 0 ) + { + } + + public Animation( string name, Func facingFunc ) + { + this.name = name.ToLowerInvariant(); + this.tickFunc = () => { }; + this.facingFunc = facingFunc; + } + + public Sprite Image + { + get + { + return backwards + ? CurrentSequence.GetSprite(CurrentSequence.End - frame - 1, facingFunc()) + : CurrentSequence.GetSprite(frame, facingFunc()); + } + } + + public void Play( string sequenceName ) + { + PlayThen(sequenceName, () => { }); + } + + public void PlayRepeating( string sequenceName ) + { + PlayThen( sequenceName, () => PlayRepeating( CurrentSequence.Name ) ); + } + + public void ReplaceAnim(string sequenceName) + { + CurrentSequence = SequenceProvider.GetSequence(name, sequenceName); + frame %= CurrentSequence.Length; + } + + public void PlayThen( string sequenceName, Action after ) + { + after = after ?? ( () => { } ); + backwards = false; + tickAlways = false; + CurrentSequence = SequenceProvider.GetSequence( name, sequenceName ); + frame = 0; + tickFunc = () => + { + ++frame; + if( frame >= CurrentSequence.Length ) + { + frame = CurrentSequence.Length - 1; + tickFunc = () => { }; + after(); + } + }; + } + + public void PlayBackwardsThen(string sequenceName, Action after) + { + PlayThen(sequenceName, after); + backwards = true; + } + + public void PlayFetchIndex( string sequenceName, Func func ) + { + backwards = false; + tickAlways = true; + CurrentSequence = SequenceProvider.GetSequence( name, sequenceName ); + frame = func(); + tickFunc = () => frame = func(); + } + + int timeUntilNextFrame; + Action tickFunc; + + public void Tick() + { + Tick( 40 ); // tick one frame + } + + public bool HasSequence(string seq) { return SequenceProvider.HasSequence( name, seq ); } + + public void Tick( int t ) + { + if( tickAlways ) + tickFunc(); + else + { + timeUntilNextFrame -= t; + while( timeUntilNextFrame <= 0 ) + { + tickFunc(); + timeUntilNextFrame += 40; // 25 fps == 40 ms + } + } + } + + public void ChangeImage(string newImage) + { + newImage = newImage.ToLowerInvariant(); + + if (name != newImage) + { + name = newImage.ToLowerInvariant(); + ReplaceAnim(CurrentSequence.Name); + } + } + + public Sequence GetSequence( string sequenceName ) + { + return SequenceProvider.GetSequence( name, sequenceName ); + } + } +} diff --git a/OpenRA.Game/Graphics/ChromeProvider.cs b/OpenRA.Game/Graphics/ChromeProvider.cs new file mode 100644 index 0000000000..8f5d18ef30 --- /dev/null +++ b/OpenRA.Game/Graphics/ChromeProvider.cs @@ -0,0 +1,97 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + static class ChromeProvider + { + static Dictionary> collections; + static Dictionary cachedSheets; + static Dictionary> cachedSprites; + + public static void Initialize(params string[] chromeFiles) + { + collections = new Dictionary>(); + cachedSheets = new Dictionary(); + cachedSprites = new Dictionary>(); + + foreach (var f in chromeFiles) + LoadChromeSource(f); + } + + static void LoadChromeSource(string filename) + { + XmlDocument document = new XmlDocument(); + document.Load(FileSystem.Open(filename)); + foreach (XmlElement eCollection in document.SelectNodes("/chrome/collection")) + LoadChromeForCollection(eCollection); + } + + static void LoadChromeForCollection(XmlElement eCollection) + { + string elementName = eCollection.GetAttribute("name"); + string defaultSrc = (eCollection.HasAttribute("src") ? eCollection.GetAttribute("src") : null); + + var images = eCollection.SelectNodes("./image").OfType() + .Select(e => new MappedImage(defaultSrc, e)) + .ToDictionary(s => s.Name); + + collections.Add(elementName, images); + } + + public static Sprite GetImage(Renderer renderer, string collection, string image) + { + // Cached sprite + if (cachedSprites.ContainsKey(collection) && cachedSprites[collection].ContainsKey(image)) + return cachedSprites[collection][image]; + + MappedImage mi; + try { mi = collections[collection][image]; } + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Collection `{0}` does not have an image `{1}`".F(collection, image)); + } + + // Cached sheet + Sheet sheet; + if (cachedSheets.ContainsKey(mi.Src)) + sheet = cachedSheets[mi.Src]; + else + { + sheet = new Sheet(renderer, mi.Src); + cachedSheets.Add(mi.Src, sheet); + } + + // Cache the sprite + if (!cachedSprites.ContainsKey(collection)) + cachedSprites.Add(collection, new Dictionary()); + cachedSprites[collection].Add(image, mi.GetImage(renderer, sheet)); + + return cachedSprites[collection][image]; + } + } +} diff --git a/OpenRA.Game/Graphics/CursorSequence.cs b/OpenRA.Game/Graphics/CursorSequence.cs new file mode 100644 index 0000000000..1b851d7a60 --- /dev/null +++ b/OpenRA.Game/Graphics/CursorSequence.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Xml; + +namespace OpenRA.Graphics +{ + class CursorSequence + { + readonly int start, length; + + public int Start { get { return start; } } + public int End { get { return start + length; } } + public int Length { get { return length; } } + + public readonly int2 Hotspot; + + Sprite[] sprites; + + public CursorSequence(string cursorSrc, XmlElement e) + { + sprites = CursorSheetBuilder.LoadAllSprites(cursorSrc); + + start = int.Parse(e.GetAttribute("start")); + + if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*") + length = sprites.Length - start; + else if (e.HasAttribute("length")) + length = int.Parse(e.GetAttribute("length")); + else if (e.HasAttribute("end")) + length = int.Parse(e.GetAttribute("end")) - start; + else + length = 1; + + int.TryParse( e.GetAttribute("x"), out Hotspot.X ); + int.TryParse( e.GetAttribute("y"), out Hotspot.Y ); + } + + public Sprite GetSprite(int frame) + { + return sprites[(frame % length) + start]; + } + } +} diff --git a/OpenRA.Game/Graphics/CursorSheetBuilder.cs b/OpenRA.Game/Graphics/CursorSheetBuilder.cs new file mode 100644 index 0000000000..3bcdbb496d --- /dev/null +++ b/OpenRA.Game/Graphics/CursorSheetBuilder.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + static class CursorSheetBuilder + { + static Cache cursors = new Cache(LoadCursors); + static readonly string[] exts = { ".shp" }; + + static Sprite[] LoadCursors(string filename) + { + try + { + var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts)); + return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, a.Size)).ToArray(); + } + catch (IndexOutOfRangeException) // This will occur when loading a custom (RA-format) .shp + { + var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts)); + return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray(); + } + } + + public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; } + } +} diff --git a/OpenRA.Game/Graphics/HardwarePalette.cs b/OpenRA.Game/Graphics/HardwarePalette.cs new file mode 100644 index 0000000000..440d284731 --- /dev/null +++ b/OpenRA.Game/Graphics/HardwarePalette.cs @@ -0,0 +1,86 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + class HardwarePalette : Sheet + { + public const int MaxPalettes = 64; + int allocated = 0; + + // We need to store the Palettes themselves for the remap palettes to work + // We should probably try to fix this somehow + static Dictionary palettes; + static Dictionary indices; + public HardwarePalette(Renderer renderer, Map map) + : base(renderer,new Size(256, MaxPalettes)) + { + palettes = new Dictionary(); + indices = new Dictionary(); + } + + public Palette GetPalette(string name) + { + try { return palettes[name]; } + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Palette `{0}` does not exist".F(name)); + } + } + + public int GetPaletteIndex(string name) + { + try { return indices[name]; } + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Palette `{0}` does not exist".F(name)); + } + } + + public int AddPalette(string name, Palette p) + { + palettes.Add(name, p); + indices.Add(name, allocated); + for (int i = 0; i < 256; i++) + { + this[new Point(i, allocated)] = p.GetColor(i); + } + return allocated++; + } + + public void Update(IEnumerable paletteMods) + { + var b = new Bitmap(Bitmap); + foreach (var mod in paletteMods) + mod.AdjustPalette(b); + + Texture.SetData(b); + Game.renderer.PaletteTexture = Texture; + } + } +} diff --git a/OpenRA.Game/Graphics/LineRenderer.cs b/OpenRA.Game/Graphics/LineRenderer.cs new file mode 100644 index 0000000000..bb5ee7d01b --- /dev/null +++ b/OpenRA.Game/Graphics/LineRenderer.cs @@ -0,0 +1,89 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + class LineRenderer + { + Renderer renderer; + IVertexBuffer vertexBuffer; + IIndexBuffer indexBuffer; /* kindof a waste of space, but the GPU likes indexing, oh well */ + + const int linesPerBatch = 1024; + + Vertex[] vertices = new Vertex[ 2 * linesPerBatch ]; + ushort[] indices = new ushort[ 2 * linesPerBatch ]; + int lines = 0; + int nv = 0, ni = 0; + + public LineRenderer( Renderer renderer ) + { + this.renderer = renderer; + vertexBuffer = renderer.Device.CreateVertexBuffer(vertices.Length ); + indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length ); + } + + public void Flush() + { + if( lines > 0 ) + { + renderer.LineShader.Render( () => + { + vertexBuffer.SetData( vertices ); + indexBuffer.SetData( indices ); + renderer.DrawBatch( vertexBuffer, indexBuffer, + nv, ni / 2, null, PrimitiveType.LineList ); + } ); + + nv = 0; ni = 0; + lines = 0; + } + } + + public void DrawLine( float2 start, float2 end, Color startColor, Color endColor ) + { + indices[ ni++ ] = (ushort)nv; + + vertices[ nv++ ] = new Vertex( start, + new float2( startColor.R / 255.0f, startColor.G / 255.0f ), + new float2( startColor.B / 255.0f, startColor.A / 255.0f ) ); + + indices[ ni++ ] = (ushort)nv; + + vertices[ nv++ ] = new Vertex( end, + new float2( endColor.R / 255.0f, endColor.G / 255.0f ), + new float2( endColor.B / 255.0f, endColor.A / 255.0f ) ); + + if( ++lines >= linesPerBatch ) + Flush(); + } + + public void FillRect( RectangleF r, Color color ) + { + for (float y = r.Top; y < r.Bottom; y++) + { + DrawLine(new float2(r.Left, y), new float2(r.Right, y), color, color); + } + } + } +} diff --git a/OpenRA.Game/Graphics/MappedImage.cs b/OpenRA.Game/Graphics/MappedImage.cs new file mode 100644 index 0000000000..a54767ddc8 --- /dev/null +++ b/OpenRA.Game/Graphics/MappedImage.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using System.IO; +using System.Xml; + +namespace OpenRA.Graphics +{ + class MappedImage + { + readonly Rectangle rect; + public readonly string Src; + public readonly string Name; + + public MappedImage(string defaultSrc, XmlElement e) + { + Src = (e.HasAttribute("src")) ? e.GetAttribute("src") : defaultSrc; + Name = e.GetAttribute("name"); + if (Src == null) + throw new InvalidDataException("Image src missing"); + + rect = new Rectangle(int.Parse(e.GetAttribute("x")), + int.Parse(e.GetAttribute("y")), + int.Parse(e.GetAttribute("width")), + int.Parse(e.GetAttribute("height"))); + } + + public Sprite GetImage(Renderer r, Sheet s) + { + return new Sprite(s, rect, TextureChannel.Alpha); + } + } +} diff --git a/OpenRA.Game/Graphics/Minimap.cs b/OpenRA.Game/Graphics/Minimap.cs new file mode 100644 index 0000000000..f9adfa55df --- /dev/null +++ b/OpenRA.Game/Graphics/Minimap.cs @@ -0,0 +1,213 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + class Minimap + { + readonly World world; + Sheet sheet, mapOnlySheet; + SpriteRenderer rgbaRenderer; + LineRenderer lineRenderer; + Sprite sprite, mapOnlySprite; + Bitmap terrain, oreLayer; + Rectangle bounds; + + Sprite ownedSpawnPoint; + Sprite unownedSpawnPoint; + + const int alpha = 230; + + public void Tick() { } + + public Minimap(World world, Renderer r) + { + this.world = world; + sheet = new Sheet(r, new Size(128, 128)); + mapOnlySheet = new Sheet(r, new Size(128, 128)); + + lineRenderer = new LineRenderer(r); + rgbaRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader); + var size = Math.Max(world.Map.Width, world.Map.Height); + var dw = (size - world.Map.Width) / 2; + var dh = (size - world.Map.Height) / 2; + + bounds = new Rectangle(world.Map.Offset.X - dw, world.Map.Offset.Y - dh, size, size); + + sprite = new Sprite(sheet, bounds, TextureChannel.Alpha); + mapOnlySprite = new Sprite(mapOnlySheet, bounds, TextureChannel.Alpha); + + shroudColor = Color.FromArgb(alpha, Color.Black); + + ownedSpawnPoint = ChromeProvider.GetImage(r, "spawnpoints", "owned"); + unownedSpawnPoint = ChromeProvider.GetImage(r, "spawnpoints", "unowned"); + } + + public static Rectangle MakeMinimapBounds(Map m) + { + var size = Math.Max(m.Width, m.Height); + var dw = (size - m.Width) / 2; + var dh = (size - m.Height) / 2; + + return new Rectangle(m.Offset.X - dw, m.Offset.Y - dh, size, size); + } + + static Cache terrainTypeColors = new Cache( + theater => + { + var pal = Game.world.WorldRenderer.GetPalette("terrain"); + return new[] { + theater == "snow" ? 0xe3 :0x1a, + 0x63, 0x2f, 0x1f, 0x14, 0x64, 0x1f, 0x68, 0x6b, 0x6d, 0x88 } + .Select(a => Color.FromArgb(alpha, pal.GetColor(a))).ToArray(); + }); + + static Color shroudColor; + + public void InvalidateOre() { oreLayer = null; } + + public static Bitmap RenderTerrainBitmap(Map map, TileSet tileset) + { + var colors = terrainTypeColors[map.Theater.ToLowerInvariant()]; + var terrain = new Bitmap(128, 128); + for (var y = 0; y < 128; y++) + for (var x = 0; x < 128; x++) + terrain.SetPixel(x, y, map.IsInMap(x, y) + ? colors[tileset.GetWalkability(map.MapTiles[x, y])] + : shroudColor); + return terrain; + } + + public static Bitmap RenderTerrainBitmapWithSpawnPoints(Map map, TileSet tileset) + { + /* todo: do this a bit nicer */ + + var terrain = RenderTerrainBitmap(map, tileset); + foreach (var sp in map.SpawnPoints) + terrain.SetPixel(sp.X, sp.Y, Color.White); + + return terrain; + } + + public void Update() + { + if (terrain == null) + terrain = RenderTerrainBitmap(world.Map, world.TileSet); + + if (oreLayer == null) + { + var colors = terrainTypeColors[world.Map.Theater.ToLowerInvariant()]; + oreLayer = new Bitmap(terrain); + for (var y = world.Map.YOffset; y < world.Map.YOffset + world.Map.Height; y++) + for (var x = world.Map.XOffset; x < world.Map.XOffset + world.Map.Width; x++) + if (world.Map.ContainsResource(new int2(x, y))) + oreLayer.SetPixel(x, y, colors[(int)TerrainMovementType.Ore]); + } + + mapOnlySheet.Texture.SetData(oreLayer); + + if (!world.Queries.OwnedBy[world.LocalPlayer].WithTrait().Any()) + return; + + var bitmap = new Bitmap(oreLayer); + var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), + ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + unsafe + { + var colors = terrainTypeColors[world.Map.Theater.ToLowerInvariant()]; + int* c = (int*)bitmapData.Scan0; + + foreach (var a in world.Queries.WithTrait().Where( a => a.Actor.Owner != null )) + *(c + (a.Actor.Location.Y * bitmapData.Stride >> 2) + a.Actor.Location.X) = + Color.FromArgb(alpha, a.Actor.Owner.Color).ToArgb(); + + for (var y = world.Map.YOffset; y < world.Map.YOffset + world.Map.Height; y++) + for (var x = world.Map.XOffset; x < world.Map.XOffset + world.Map.Width; x++) + { + if (!world.LocalPlayer.Shroud.DisplayOnRadar(x, y)) + { + *(c + (y * bitmapData.Stride >> 2) + x) = shroudColor.ToArgb(); + continue; + } + var b = world.WorldActor.traits.Get().GetBuildingAt(new int2(x, y)); + if (b != null) + *(c + (y * bitmapData.Stride >> 2) + x) = + (b.Owner != null ? Color.FromArgb(alpha, b.Owner.Color) : colors[4]).ToArgb(); + } + } + + bitmap.UnlockBits(bitmapData); + sheet.Texture.SetData(bitmap); + } + + public void Draw(RectangleF rect, bool mapOnly) + { + rgbaRenderer.DrawSprite(mapOnly ? mapOnlySprite : sprite, + new float2(rect.X, rect.Y), "chrome", new float2(rect.Width, rect.Height)); + rgbaRenderer.Flush(); + } + + int2 TransformCellToMinimapPixel(RectangleF viewRect, int2 p) + { + var fx = (float)(p.X - bounds.X) / bounds.Width; + var fy = (float)(p.Y - bounds.Y) / bounds.Height; + + return new int2( + (int)(viewRect.Width * fx + viewRect.Left) - 8, + (int)(viewRect.Height * fy + viewRect.Top) - 8); + } + + public void DrawSpawnPoints(RectangleF rect) + { + var points = world.Map.SpawnPoints + .Select( (sp,i) => Pair.New(sp,world.players.Values.FirstOrDefault( + p => p.SpawnPointIndex == i + 1 ) )) + .ToList(); + + foreach (var p in points) + { + var pos = TransformCellToMinimapPixel(rect, p.First); + + if (p.Second == null) + rgbaRenderer.DrawSprite(unownedSpawnPoint, pos, "chrome"); + else + { + lineRenderer.FillRect(new RectangleF( + Game.viewport.Location.X + pos.X + 2, + Game.viewport.Location.Y + pos.Y + 2, + 12, 12), p.Second.Color); + + rgbaRenderer.DrawSprite(ownedSpawnPoint, pos, "chrome"); + } + } + + lineRenderer.Flush(); + rgbaRenderer.Flush(); + } + } +} diff --git a/OpenRA.Game/Graphics/OverlayRenderer.cs b/OpenRA.Game/Graphics/OverlayRenderer.cs new file mode 100755 index 0000000000..cf56db58b5 --- /dev/null +++ b/OpenRA.Game/Graphics/OverlayRenderer.cs @@ -0,0 +1,114 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + class OverlayRenderer + { + static string[] overlaySpriteNames = + { + "sbag", "cycl", "brik", "fenc", "wood", + "gold01", "gold02", "gold03", "gold04", + "gem01", "gem02", "gem03", "gem04", + "v12", "v13", "v14", "v15", "v16", "v17", "v18", + "fpls", "wcrate", "scrate", "barb", "sbag", + }; + + static string[] smudgeSpriteNames = + { + "bib3", "bib2", "bib1", "sc1", "sc2", "sc3", "sc4", "sc5", "sc6", + "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", + }; + + readonly Sprite[][] overlaySprites; + readonly Sprite[] smudgeSprites; + + SpriteRenderer spriteRenderer; + Map map; + + public OverlayRenderer( Renderer renderer, Map map ) + { + this.spriteRenderer = new SpriteRenderer( renderer, true ); + this.map = map; + + overlaySprites = overlaySpriteNames.Select(f => SpriteSheetBuilder.LoadAllSprites(f)).ToArray(); + smudgeSprites = smudgeSpriteNames.SelectMany(f => SpriteSheetBuilder.LoadAllSprites(f)).ToArray(); + } + + public void Draw() + { + var shroud = Game.world.LocalPlayer.Shroud; + + for (int y = map.YOffset; y < map.YOffset + map.Height; y++) + for (int x = map.XOffset; x < map.XOffset + map.Width; x++) + { + if (!shroud.IsExplored(new int2(x,y))) continue; + + var tr = map.MapTiles[x,y]; + if (tr.smudge != 0 && tr.smudge <= smudgeSprites.Length) + { + var location = new int2(x, y); + spriteRenderer.DrawSprite(smudgeSprites[tr.smudge - 1], + Game.CellSize * (float2)location, "terrain"); + } + + var o = tr.overlay; + if (o < overlaySprites.Length) + { + var location = new int2(x, y); + var sprites = overlaySprites[o]; + var spriteIndex = 0; + if (Ore.overlayIsFence[o]) spriteIndex = NearbyFences(x, y); + else if (Ore.overlayIsOre[o]) spriteIndex = map.MapTiles[x,y].density - 1; + else if (Ore.overlayIsGems[o]) spriteIndex = map.MapTiles[x,y].density - 1; + spriteRenderer.DrawSprite(sprites[spriteIndex], + Game.CellSize * (float2)location, "terrain"); + } + } + + spriteRenderer.Flush(); + } + + bool IsFence( int x, int y ) + { + var o = map.MapTiles[ x, y ].overlay; + if (o < Ore.overlayIsFence.Length) + return Ore.overlayIsFence[o]; + return false; + } + + int NearbyFences( int x, int y ) + { + int ret = 0; + if( IsFence( x, y - 1 ) ) + ret |= 1; + if( IsFence( x + 1, y ) ) + ret |= 2; + if( IsFence( x, y + 1 ) ) + ret |= 4; + if( IsFence( x - 1, y ) ) + ret |= 8; + return ret; + } + } +} diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs new file mode 100644 index 0000000000..6ec5f7fabe --- /dev/null +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -0,0 +1,134 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.Drawing.Text; +using System.Reflection; +using System.Windows.Forms; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; +using OpenRA.Support; +using System.IO; + +namespace OpenRA.Graphics +{ + internal class Renderer + { + internal static int SheetSize; + + readonly IGraphicsDevice device; + + public IShader SpriteShader { get; private set; } /* note: shared shader params */ + public IShader LineShader { get; private set; } + public IShader RgbaSpriteShader { get; private set; } + public IShader WorldSpriteShader { get; private set; } + + public ITexture PaletteTexture; + + public readonly SpriteFont RegularFont, BoldFont; + + public Size Resolution { get { return device.WindowSize; } } + + public Renderer(Size resolution, bool windowed) + { + device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowed, false ); + + SpriteShader = device.CreateShader(FileSystem.Open("shaders/world-shp.fx")); + LineShader = device.CreateShader(FileSystem.Open("shaders/line.fx")); + RgbaSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-rgba.fx")); + WorldSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-shp.fx")); + +// RegularFont = device.CreateFont( "FreeSans.ttf" ); +// BoldFont = device.CreateFont( "FreeSansBold.ttf" ); + + RegularFont = new SpriteFont(this, "FreeSans.ttf", 14); + BoldFont = new SpriteFont(this, "FreeSansBold.ttf", 14); + } + + IGraphicsDevice CreateDevice( Assembly rendererDll, int width, int height, bool windowed, bool vsync ) + { + foreach( RendererAttribute r in rendererDll.GetCustomAttributes( typeof( RendererAttribute ), false ) ) + { + return (IGraphicsDevice)r.Type.GetConstructor( new Type[] { typeof( int ), typeof( int ), typeof( bool ), typeof( bool ) } ) + .Invoke( new object[] { width, height, windowed, vsync } ); + } + throw new NotImplementedException(); + } + + public IGraphicsDevice Device { get { return device; } } + + public void BeginFrame(float2 r1, float2 r2, float2 scroll) + { + device.Begin(); + device.Clear(Color.Black); + + SetShaderParams( SpriteShader, r1, r2, scroll ); + SetShaderParams( LineShader, r1, r2, scroll ); + SetShaderParams( RgbaSpriteShader, r1, r2, scroll ); + SetShaderParams( WorldSpriteShader, r1, r2, scroll ); + } + + private void SetShaderParams( IShader s, float2 r1, float2 r2, float2 scroll ) + { + s.SetValue( "Palette", PaletteTexture ); + s.SetValue( "Scroll", scroll.X, scroll.Y ); + s.SetValue( "r1", r1.X, r1.Y ); + s.SetValue( "r2", r2.X, r2.Y ); + s.Commit(); + } + + public void EndFrame() + { + device.End(); + device.Present(); + } + + public void DrawBatch(IVertexBuffer vertices, IIndexBuffer indices, + Range vertexRange, Range indexRange, ITexture texture, PrimitiveType type, IShader shader) + where T : struct + { + shader.SetValue("DiffuseTexture", texture); + shader.Commit(); + + vertices.Bind(); + indices.Bind(); + + device.DrawIndexedPrimitives(type, vertexRange, indexRange); + + PerfHistory.Increment("batches", 1); + } + + public void DrawBatch(IVertexBuffer vertices, IIndexBuffer indices, + int vertexPool, int numPrimitives, ITexture texture, PrimitiveType type) + where T : struct + { + SpriteShader.SetValue("DiffuseTexture", texture); + SpriteShader.Commit(); + + vertices.Bind(); + indices.Bind(); + + device.DrawIndexedPrimitives(type, vertexPool, numPrimitives); + + PerfHistory.Increment("batches", 1); + } + } +} diff --git a/OpenRA.Game/Graphics/Sequence.cs b/OpenRA.Game/Graphics/Sequence.cs new file mode 100644 index 0000000000..07fbbab816 --- /dev/null +++ b/OpenRA.Game/Graphics/Sequence.cs @@ -0,0 +1,70 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Xml; + +namespace OpenRA.Graphics +{ + public class Sequence + { + readonly Sprite[] sprites; + readonly int start, length, facings; + + public readonly string Name; + public int Start { get { return start; } } + public int End { get { return start + length; } } + public int Length { get { return length; } } + public int Facings { get { return facings; } } + + public Sequence(string unit, XmlElement e) + { + string srcOverride = e.GetAttribute("src"); + Name = e.GetAttribute("name"); + + sprites = SpriteSheetBuilder.LoadAllSprites(string.IsNullOrEmpty(srcOverride) ? unit : srcOverride ); + start = int.Parse(e.GetAttribute("start")); + + if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*") + length = sprites.Length - Start; + else if (e.HasAttribute("length")) + length = int.Parse(e.GetAttribute("length")); + else if (e.HasAttribute("end")) + length = int.Parse(e.GetAttribute("end")) - int.Parse(e.GetAttribute("start")); + else + length = 1; + + if( e.HasAttribute( "facings" ) ) + facings = int.Parse( e.GetAttribute( "facings" ) ); + else + facings = 1; + } + + public Sprite GetSprite( int frame ) + { + return GetSprite( frame, 0 ); + } + + public Sprite GetSprite(int frame, int facing) + { + var f = Traits.Util.QuantizeFacing( facing, facings ); + return sprites[ (f * length) + ( frame % length ) + start ]; + } + } +} diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs new file mode 100644 index 0000000000..e23153074b --- /dev/null +++ b/OpenRA.Game/Graphics/SequenceProvider.cs @@ -0,0 +1,101 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + static class SequenceProvider + { + static Dictionary> units; + static Dictionary cursors; + + public static void Initialize(params string[] sequenceFiles) + { + units = new Dictionary>(); + cursors = new Dictionary(); + + foreach (var f in sequenceFiles) + LoadSequenceSource(f); + } + + static void LoadSequenceSource(string filename) + { + XmlDocument document = new XmlDocument(); + document.Load(FileSystem.Open(filename)); + + foreach (XmlElement eUnit in document.SelectNodes("/sequences/unit")) + LoadSequencesForUnit(eUnit); + + foreach (XmlElement eCursor in document.SelectNodes("/sequences/cursor")) + LoadSequencesForCursor(eCursor); + } + + static void LoadSequencesForCursor(XmlElement eCursor) + { + string cursorSrc = eCursor.GetAttribute("src"); + + foreach (XmlElement eSequence in eCursor.SelectNodes("./sequence")) + cursors.Add(eSequence.GetAttribute("name"), new CursorSequence(cursorSrc, eSequence)); + + Log.Write("* LoadSequencesForCursor() done"); + } + + static void LoadSequencesForUnit(XmlElement eUnit) + { + string unitName = eUnit.GetAttribute("name"); + Log.Write("Loading sequence {0}", unitName); + var sequences = eUnit.SelectNodes("./sequence").OfType() + .Select(e => new Sequence(unitName, e)) + .ToDictionary(s => s.Name); + + units.Add(unitName, sequences); + } + + public static Sequence GetSequence(string unitName, string sequenceName) + { + try { return units[unitName][sequenceName]; } + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName)); + } + } + + public static bool HasSequence(string unit, string seq) + { + return units[unit].ContainsKey(seq); + } + + public static CursorSequence GetCursorSequence(string cursor) + { + try { return cursors[cursor]; } + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Cursor does not have a sequence `{0}`".F(cursor)); + } + } + } +} diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs new file mode 100644 index 0000000000..f0f0934b83 --- /dev/null +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -0,0 +1,72 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + public class Sheet + { + readonly Renderer renderer; + protected readonly Bitmap bitmap; + + ITexture texture; + + internal Sheet(Renderer renderer, Size size) + { + this.renderer = renderer; + this.bitmap = new Bitmap(size.Width, size.Height); + } + + internal Sheet(Renderer renderer, string filename) + { + this.renderer = renderer; + this.bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename)); + } + + void Resolve() + { + texture = renderer.Device.CreateTexture(bitmap); + } + + public ITexture Texture + { + get + { + if (texture == null) + Resolve(); + + return texture; + } + } + + public Size Size { get { return bitmap.Size; } } + + protected Color this[Point p] + { + get { return bitmap.GetPixel(p.X, p.Y); } + set { bitmap.SetPixel(p.X, p.Y, value); } + } + + public Bitmap Bitmap { get { return bitmap; } } // for perf + } +} diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs new file mode 100644 index 0000000000..7462490ee9 --- /dev/null +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -0,0 +1,119 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.Graphics +{ + class SheetBuilder + { + public static SheetBuilder SharedInstance; + public static void Initialize(Renderer r) + { + SharedInstance = new SheetBuilder(r, TextureChannel.Red); + } + + public SheetBuilder(Renderer r, TextureChannel ch) + { + renderer = r; + current = null; + rowHeight = 0; + channel = null; + initialChannel = ch; + } + + public Sprite Add(byte[] src, Size size) + { + Sprite rect = Allocate(size); + Util.FastCopyIntoChannel(rect, src); + return rect; + } + + public Sprite Add(Size size, byte paletteIndex) + { + byte[] data = new byte[size.Width * size.Height]; + for (int i = 0; i < data.Length; i++) + data[i] = paletteIndex; + + return Add(data, size); + } + + Sheet NewSheet() { return new Sheet( renderer, new Size( Renderer.SheetSize, Renderer.SheetSize ) ); } + + Renderer renderer; + Sheet current = null; + int rowHeight = 0; + Point p; + TextureChannel? channel = null; + TextureChannel initialChannel; + + TextureChannel? NextChannel(TextureChannel? t) + { + if (t == null) + return initialChannel; + + switch (t.Value) + { + case TextureChannel.Red: return TextureChannel.Green; + case TextureChannel.Green: return TextureChannel.Blue; + case TextureChannel.Blue: return TextureChannel.Alpha; + case TextureChannel.Alpha: return null; + + default: return null; + } + } + + public Sprite Allocate(Size imageSize) + { + if (current == null) + { + current = NewSheet(); + channel = NextChannel(null); + } + + if (imageSize.Width + p.X > current.Size.Width) + { + p = new Point(0, p.Y + rowHeight); + rowHeight = imageSize.Height; + } + + if (imageSize.Height > rowHeight) + rowHeight = imageSize.Height; + + if (p.Y + imageSize.Height > current.Size.Height) + { + + if (null == (channel = NextChannel(channel))) + { + current = NewSheet(); + channel = NextChannel(channel); + } + + rowHeight = imageSize.Height; + p = new Point(0,0); + } + + Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel.Value); + p.X += imageSize.Width; + + return rect; + } + } +} diff --git a/OpenRA.Game/Graphics/Sprite.cs b/OpenRA.Game/Graphics/Sprite.cs new file mode 100644 index 0000000000..0b9fa7cf4f --- /dev/null +++ b/OpenRA.Game/Graphics/Sprite.cs @@ -0,0 +1,71 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.Graphics +{ + public class Sprite + { + public readonly Rectangle bounds; + public readonly Sheet sheet; + public readonly TextureChannel channel; + public readonly RectangleF uv; + public readonly float2 size; + + readonly float2[] uvhax; + + internal Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel) + { + this.bounds = bounds; + this.sheet = sheet; + this.channel = channel; + + uv = new RectangleF( + (float)(bounds.Left) / sheet.Size.Width, + (float)(bounds.Top) / sheet.Size.Height, + (float)(bounds.Width) / sheet.Size.Width, + (float)(bounds.Height) / sheet.Size.Height); + + uvhax = new float2[] + { + new float2( uv.Left, uv.Top ), + new float2( uv.Right, uv.Top ), + new float2( uv.Left, uv.Bottom ), + new float2( uv.Right, uv.Bottom ), + }; + + this.size = new float2(bounds.Size); + } + + public float2 FastMapTextureCoords( int k ) + { + return uvhax[ k ]; + } + } + + public enum TextureChannel + { + Red = 0, + Green = 1, + Blue = 2, + Alpha = 3, + } +} diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs new file mode 100644 index 0000000000..1977137ac9 --- /dev/null +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -0,0 +1,116 @@ +using System; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using Tao.FreeType; +using System.Runtime.InteropServices; + +namespace OpenRA.Graphics +{ + class SpriteFont + { + int size; + public SpriteFont(Renderer r, string name, int size) + { + this.size = size; + + if (0 != FT.FT_New_Face(library, name, 0, out face)) + throw new InvalidOperationException("FT_New_Face failed"); + + FT.FT_Set_Pixel_Sizes(face, 0, (uint)size); + glyphs = new Cache(CreateGlyph); + + // setup a 1-channel SheetBuilder for our private use + if (builder == null) builder = new SheetBuilder(r, TextureChannel.Alpha); + + // precache glyphs for U+0020 - U+007f + for (var n = (char)0x20; n < (char)0x7f; n++) + if (glyphs[n] == null) + throw new InvalidOperationException(); + } + + public void DrawText(SpriteRenderer r, string text, float2 location, Color c) + { + location.Y += size; // baseline vs top + + var p = location; + foreach (var s in text) + { + if (s == '\n') + { + location.Y += size; + p = location; + continue; + } + + var g = glyphs[s]; + r.DrawSprite(g.Sprite, + new float2( + (int)Math.Round(p.X + g.Offset.X, 0), + p.Y + g.Offset.Y), + "chrome"); + p.X += g.Advance; + } + + r.Flush(); + } + + public int2 Measure(string text) + { + return new int2((int)text.Split( '\n' ).Max( s => s.Sum(a => glyphs[a].Advance)), size); + } + + Cache glyphs; + IntPtr face; + + GlyphInfo CreateGlyph(char c) + { + var index = FT.FT_Get_Char_Index(face, (uint)c); + FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER); + + var _face = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec)); + var _glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(_face.glyph, typeof(FT_GlyphSlotRec)); + + var s = builder.Allocate(new Size(_glyph.metrics.width >> 6, _glyph.metrics.height >> 6)); + + var g = new GlyphInfo + { + Sprite = s, + Advance = _glyph.metrics.horiAdvance / 64f, + Offset = { X = _glyph.bitmap_left, Y = -_glyph.bitmap_top } + }; + + unsafe + { + var p = (byte*)_glyph.bitmap.buffer; + + for (var j = 0; j < s.size.Y; j++) + { + for (var i = 0; i < s.size.X; i++) + if (p[i] != 0) + s.sheet.Bitmap.SetPixel(i + s.bounds.Left, j + s.bounds.Top, + Color.FromArgb(p[i], 0xff, 0xff, 0xff)); + + p += _glyph.bitmap.pitch; + } + } + + return g; + } + + static SpriteFont() + { + FT.FT_Init_FreeType(out library); + } + + static IntPtr library; + static SheetBuilder builder; + } + + class GlyphInfo + { + public float Advance; + public int2 Offset; + public Sprite Sprite; + } +} diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs new file mode 100644 index 0000000000..d3222391b2 --- /dev/null +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -0,0 +1,91 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + class SpriteRenderer + { + IVertexBuffer vertexBuffer; + IIndexBuffer indexBuffer; + Renderer renderer; + IShader shader; + + const int spritesPerBatch = 1024; + + Vertex[] vertices = new Vertex[4 * spritesPerBatch]; + ushort[] indices = new ushort[6 * spritesPerBatch]; + Sheet currentSheet = null; + int sprites = 0; + int nv = 0, ni = 0; + + public SpriteRenderer(Renderer renderer, bool allowAlpha, IShader shader) + { + this.renderer = renderer; + this.shader = shader; + + vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length ); + indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length ); + } + + public SpriteRenderer(Renderer renderer, bool allowAlpha) + : this(renderer, allowAlpha, renderer.SpriteShader) { } + + public void Flush() + { + if (sprites > 0) + { + shader.SetValue( "DiffuseTexture", currentSheet.Texture ); + shader.Render(() => + { + vertexBuffer.SetData(vertices); + indexBuffer.SetData(indices); + renderer.DrawBatch(vertexBuffer, indexBuffer, + new Range(0, nv), + new Range(0, ni), + currentSheet.Texture, PrimitiveType.TriangleList, + shader); + }); + + nv = 0; ni = 0; + currentSheet = null; + sprites = 0; + } + } + + public void DrawSprite(Sprite s, float2 location, string palette) + { + DrawSprite(s, location, palette, s.size); + } + + public void DrawSprite(Sprite s, float2 location, string palette, float2 size) + { + if (s.sheet != currentSheet) + Flush(); + + currentSheet = s.sheet; + Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, Game.world.WorldRenderer.GetPaletteIndex(palette), nv, ni, size); + nv += 4; ni += 6; + if (++sprites >= spritesPerBatch) + Flush(); + } + } +} diff --git a/OpenRA.Game/Graphics/SpriteSheetBuilder.cs b/OpenRA.Game/Graphics/SpriteSheetBuilder.cs new file mode 100644 index 0000000000..e6780c5a0e --- /dev/null +++ b/OpenRA.Game/Graphics/SpriteSheetBuilder.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Graphics +{ + static class SpriteSheetBuilder + { + public static void Initialize( Map map ) + { + exts = new[] { + "." + map.Theater.Substring( 0, 3 ).ToLowerInvariant(), + ".shp", + ".tem", + ".sno", + ".int" }; + sprites = new Cache( LoadSprites ); + } + + static Cache sprites; + static string[] exts; + + static Sprite[] LoadSprites(string filename) + { + var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts)); + return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray(); + } + + public static Sprite[] LoadAllSprites(string filename) { return sprites[filename]; } + } +} diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs new file mode 100644 index 0000000000..17d9e9f490 --- /dev/null +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -0,0 +1,110 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using OpenRA.FileFormats; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + class TerrainRenderer + { + IVertexBuffer vertexBuffer; + IIndexBuffer indexBuffer; + Sheet terrainSheet; + + Renderer renderer; + Map map; + OverlayRenderer overlayRenderer; + + public TerrainRenderer(World world, Renderer renderer, WorldRenderer wr) + { + this.renderer = renderer; + this.map = world.Map; + + Size tileSize = new Size( Game.CellSize, Game.CellSize ); + + var tileMapping = new Cache( + x => SheetBuilder.SharedInstance.Add(world.TileSet.GetBytes(x), tileSize)); + + Vertex[] vertices = new Vertex[4 * map.Height * map.Width]; + ushort[] indices = new ushort[6 * map.Height * map.Width]; + + int nv = 0; + int ni = 0; + for( int j = map.YOffset ; j < map.YOffset + map.Height ; j++ ) + for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ ) + { + Sprite tile = tileMapping[map.MapTiles[i, j]]; + // TODO: The zero below should explicitly refer to the terrain palette, but this code is called + // before the palettes are created + Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni, tile.size); + nv += 4; + ni += 6; + } + + terrainSheet = tileMapping[map.MapTiles[map.XOffset, map.YOffset]].sheet; + + vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length ); + vertexBuffer.SetData( vertices ); + + indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length ); + indexBuffer.SetData( indices ); + + overlayRenderer = new OverlayRenderer( renderer, map ); + } + + public void Draw( Viewport viewport ) + { + int indicesPerRow = map.Width * 6; + int verticesPerRow = map.Width * 4; + + int visibleRows = (int)(viewport.Height / 24.0f + 2); + + int firstRow = (int)((viewport.Location.Y) / 24.0f - map.YOffset); + int lastRow = firstRow + visibleRows; + + if (lastRow < 0 || firstRow > map.Height) + return; + + if (firstRow < 0) firstRow = 0; + if (lastRow > map.Height) lastRow = map.Height; + + if (!Game.world.LocalPlayer.Shroud.HasGPS && Game.world.LocalPlayer.Shroud.bounds.HasValue) + { + var r = Game.world.LocalPlayer.Shroud.bounds.Value; + if (firstRow < r.Top - map.YOffset) + firstRow = r.Top - map.YOffset; + + if (firstRow > r.Bottom - map.YOffset) + firstRow = r.Bottom - map.YOffset; + } + + renderer.SpriteShader.SetValue( "DiffuseTexture", terrainSheet.Texture ); + renderer.SpriteShader.Render(() => + renderer.DrawBatch(vertexBuffer, indexBuffer, + new Range(verticesPerRow * firstRow, verticesPerRow * lastRow), + new Range(indicesPerRow * firstRow, indicesPerRow * lastRow), + terrainSheet.Texture, PrimitiveType.TriangleList, renderer.SpriteShader)); + + overlayRenderer.Draw(); + } + } +} diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs new file mode 100644 index 0000000000..dfd873c2d4 --- /dev/null +++ b/OpenRA.Game/Graphics/Util.cs @@ -0,0 +1,131 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using OpenRA.FileFormats.Graphics; + +namespace OpenRA.Graphics +{ + static class Util + { + public static string[] ReadAllLines(Stream s) + { + List result = new List(); + using (StreamReader reader = new StreamReader(s)) + while(!reader.EndOfStream) + { + var line = reader.ReadLine(); + if( !string.IsNullOrEmpty( line ) && line[0] != '#' ) + result.Add( line ); + } + + return result.ToArray(); + } + + public static T[] MakeArray(int count, Converter f) + { + T[] result = new T[count]; + for (int i = 0; i < count; i++) + result[i] = f(i); + + return result; + } + + static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; + + public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni, float2 size) + { + var attrib = new float2(palette / (float)HardwarePalette.MaxPalettes, channelSelect[(int)r.channel]); + + vertices[nv] = new Vertex(o, + r.FastMapTextureCoords(0), attrib); + vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y), + r.FastMapTextureCoords(1), attrib); + vertices[nv + 2] = new Vertex(new float2(o.X, o.Y + size.Y), + r.FastMapTextureCoords(2), attrib); + vertices[nv + 3] = new Vertex(new float2(o.X + size.X, o.Y + size.Y), + r.FastMapTextureCoords(3), attrib); + + indices[ni] = (ushort)(nv); + indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1); + indices[ni + 2] = indices[ni + 5] = (ushort)(nv + 2); + indices[ni + 4] = (ushort)(nv + 3); + } + + public static void FastCopyIntoChannel(Sprite dest, byte[] src) + { + var bitmap = dest.sheet.Bitmap; + BitmapData bits = null; + uint[] channelMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }; + int[] shifts = { 16, 8, 0, 24 }; + + uint mask = channelMasks[(int)dest.channel]; + int shift = shifts[(int)dest.channel]; + + try + { + bits = bitmap.LockBits(dest.bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + + int width = dest.bounds.Width; + int height = dest.bounds.Height; + + unsafe + { + fixed (byte* srcbase = &src[0]) + { + byte* s = srcbase; + uint* t = (uint*)bits.Scan0.ToPointer(); + int stride = bits.Stride >> 2; + + for (int j = 0; j < height; j++) + { + uint* p = t; + for (int i = 0; i < width; i++, p++) + *p = (*p & ~mask) | ((mask & ((uint)*s++) << shift)); + t += stride; + } + } + } + } + finally + { + bitmap.UnlockBits(bits); + } + } + + public static Color Lerp(float t, Color a, Color b) + { + return Color.FromArgb( + LerpChannel(t, a.A, b.A), + LerpChannel(t, a.R, b.R), + LerpChannel(t, a.G, b.G), + LerpChannel(t, a.B, b.B)); + } + + public static int LerpChannel(float t, int a, int b) + { + return (int)((1 - t) * a + t * b); + } + } +} diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs new file mode 100644 index 0000000000..ba235b448e --- /dev/null +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -0,0 +1,151 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Network; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + interface IHandleInput + { + bool HandleInput(World world, MouseInput mi); + } + + class Viewport + { + readonly float2 screenSize; + float2 scrollPosition; + readonly Renderer renderer; + + public float2 Location { get { return scrollPosition; } } + + public int Width { get { return (int)screenSize.X; } } + public int Height { get { return (int)screenSize.Y; } } + + SpriteRenderer cursorRenderer; + int2 mousePos; + float cursorFrame = 0f; + + public void Scroll(float2 delta) + { + scrollPosition = scrollPosition + delta; + } + + public IEnumerable regions { get { return new IHandleInput[] { Game.chrome, Game.controller }; } } + + public Viewport(float2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer) + { + this.screenSize = screenSize; + this.renderer = renderer; + cursorRenderer = new SpriteRenderer(renderer, true); + + this.scrollPosition = Game.CellSize* mapStart; + } + + public void DrawRegions( World world ) + { + world.WorldRenderer.palette.Update(world.Queries.WithTraitMultiple().Select(t=>t.Trait)); + + float2 r1 = new float2(2, -2) / screenSize; + float2 r2 = new float2(-1, 1); + + renderer.BeginFrame(r1, r2, scrollPosition); + + if( Game.orderManager.GameStarted ) + { + world.WorldRenderer.Draw(); + Game.chrome.Draw( world ); + + + if( Game.orderManager.Connection.ConnectionState == ConnectionState.NotConnected ) + Game.chrome.DrawDialog("Connection lost."); + } + else + { + // what a hack. as soon as we have some real chrome stuff... + + switch( Game.orderManager.Connection.ConnectionState ) + { + case ConnectionState.Connecting: + Game.chrome.DrawDialog("Connecting to {0}:{1}...".F( Game.Settings.NetworkHost, Game.Settings.NetworkPort )); + break; + case ConnectionState.NotConnected: + Game.chrome.DrawDialog("Connection failed."); + break; + case ConnectionState.Connected: + Game.chrome.DrawLobby( world ); + break; + } + } + + var cursorName = Game.chrome.HitTest(mousePos) ? "default" : Game.controller.ChooseCursor( world ); + var c = new Cursor(cursorName); + cursorRenderer.DrawSprite(c.GetSprite((int)cursorFrame), mousePos + Location - c.GetHotspot(), "cursor"); + cursorRenderer.Flush(); + + renderer.EndFrame(); + } + + public void Tick() + { + cursorFrame += 0.5f; + } + + IHandleInput dragRegion = null; + public void DispatchMouseInput(World world, MouseInput mi) + { + if (mi.Event == MouseInputEvent.Move) + mousePos = mi.Location; + + if (dragRegion != null) { + dragRegion.HandleInput( world, mi ); + if (mi.Event == MouseInputEvent.Up) dragRegion = null; + return; + } + + dragRegion = regions.FirstOrDefault(r => r.HandleInput(world, mi)); + if (mi.Event != MouseInputEvent.Down) + dragRegion = null; + } + + public float2 ViewToWorld(MouseInput mi) + { + return (1 / 24.0f) * (new float2(mi.Location.X, mi.Location.Y) + Location); + } + + public void Center(IEnumerable actors) + { + if (!actors.Any()) return; + + var avgPos = (1f / actors.Count()) * actors + .Select(a => a.CenterLocation) + .Aggregate((a, b) => a + b); + + scrollPosition = (avgPos - .5f * new float2(Width, Height)).ToInt2(); + } + + public void GoToStartLocation( Player player ) + { + Center( player.World.Queries.OwnedBy[ player ].WithTrait().Select( a => a.Actor ) ); + } + } +} diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs new file mode 100644 index 0000000000..2f17e32a09 --- /dev/null +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -0,0 +1,315 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.Graphics +{ + public class WorldRenderer + { + readonly World world; + internal readonly TerrainRenderer terrainRenderer; + internal readonly SpriteRenderer spriteRenderer; + internal readonly LineRenderer lineRenderer; + internal readonly UiOverlay uiOverlay; + internal readonly Renderer renderer; + internal readonly HardwarePalette palette; + + public static bool ShowUnitPaths = false; + + internal WorldRenderer(World world, Renderer renderer) + { + this.world = world; + this.renderer = renderer; + + terrainRenderer = new TerrainRenderer(world, renderer, this); + spriteRenderer = new SpriteRenderer(renderer, true); + lineRenderer = new LineRenderer(renderer); + uiOverlay = new UiOverlay(spriteRenderer); + palette = new HardwarePalette(renderer, world.Map); + Log.Write("Created worldrenderer"); + } + + public int GetPaletteIndex(string name) + { + return palette.GetPaletteIndex(name); + } + + public Palette GetPalette(string name) + { + return palette.GetPalette(name); + } + + public void AddPalette(string name, Palette pal) + { + palette.AddPalette(name, pal); + } + + void DrawSpriteList(RectangleF rect, + IEnumerable images) + { + foreach (var image in images) + { + var loc = image.Pos; + + if (loc.X > rect.Right || loc.X < rect.Left - image.Sprite.bounds.Width) + continue; + if (loc.Y > rect.Bottom || loc.Y < rect.Top - image.Sprite.bounds.Height) + continue; + + spriteRenderer.DrawSprite(image.Sprite, loc, image.Palette); + } + } + + class SpriteComparer : IComparer + { + public int Compare(Renderable x, Renderable y) + { + var result = x.ZOffset.CompareTo(y.ZOffset); + if (result == 0) + result = x.Pos.Y.CompareTo(y.Pos.Y); + + return result; + } + } + + Rectangle GetBoundsRect() + { + if (!world.LocalPlayer.Shroud.HasGPS && world.LocalPlayer.Shroud.bounds.HasValue) + { + var r = world.LocalPlayer.Shroud.bounds.Value; + + var left = (int)(Game.CellSize * r.Left - Game.viewport.Location.X); + var top = (int)(Game.CellSize * r.Top - Game.viewport.Location.Y); + var right = left + (int)(Game.CellSize * r.Width); + var bottom = top + (int)(Game.CellSize * r.Height); + + if (left < 0) left = 0; + if (top < 0) top = 0; + if (right > Game.viewport.Width) right = Game.viewport.Width; + if (bottom > Game.viewport.Height) bottom = Game.viewport.Height; + + return new Rectangle(left, top, right - left, bottom - top); + } + else + return new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height); + } + + public void Draw() + { + var bounds = GetBoundsRect(); + renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height); + + terrainRenderer.Draw(Game.viewport); + + var comparer = new SpriteComparer(); + + bounds.Offset((int)Game.viewport.Location.X, (int)Game.viewport.Location.Y); + + var renderables = world.Actors.SelectMany(a => a.Render()) + .OrderBy(r => r, comparer); + + DrawSpriteList(bounds, renderables); + + foreach (var e in world.Effects) + DrawSpriteList(bounds, e.Render()); + + uiOverlay.Draw( world ); + + spriteRenderer.Flush(); + + DrawBandBox(); + + if (Game.controller.orderGenerator != null) + Game.controller.orderGenerator.Render( world ); + + world.LocalPlayer.Shroud.Draw(spriteRenderer); + + lineRenderer.Flush(); + spriteRenderer.Flush(); + } + + void DrawBandBox() + { + var selbox = Game.controller.SelectionBox; + if (selbox == null) return; + + var a = selbox.Value.First; + var b = new float2(selbox.Value.Second.X - a.X, 0); + var c = new float2(0, selbox.Value.Second.Y - a.Y); + + lineRenderer.DrawLine(a, a + b, Color.White, Color.White); + lineRenderer.DrawLine(a + b, a + b + c, Color.White, Color.White); + lineRenderer.DrawLine(a + b + c, a + c, Color.White, Color.White); + lineRenderer.DrawLine(a, a + c, Color.White, Color.White); + + foreach (var u in world.SelectActorsInBox(selbox.Value.First, selbox.Value.Second)) + DrawSelectionBox(u, Color.Yellow, false); + } + + public void DrawSelectionBox(Actor selectedUnit, Color c, bool drawHealthBar) + { + var bounds = selectedUnit.GetBounds(true); + + var xy = new float2(bounds.Left, bounds.Top); + var Xy = new float2(bounds.Right, bounds.Top); + var xY = new float2(bounds.Left, bounds.Bottom); + var XY = new float2(bounds.Right, bounds.Bottom); + + lineRenderer.DrawLine(xy, xy + new float2(4, 0), c, c); + lineRenderer.DrawLine(xy, xy + new float2(0, 4), c, c); + lineRenderer.DrawLine(Xy, Xy + new float2(-4, 0), c, c); + lineRenderer.DrawLine(Xy, Xy + new float2(0, 4), c, c); + + lineRenderer.DrawLine(xY, xY + new float2(4, 0), c, c); + lineRenderer.DrawLine(xY, xY + new float2(0, -4), c, c); + lineRenderer.DrawLine(XY, XY + new float2(-4, 0), c, c); + lineRenderer.DrawLine(XY, XY + new float2(0, -4), c, c); + + if (drawHealthBar) + { + DrawHealthBar(selectedUnit, xy, Xy); + DrawControlGroup(selectedUnit, xy); + + // Only display pips and tags to the owner + if (selectedUnit.Owner == world.LocalPlayer) + { + DrawPips(selectedUnit, xY); + DrawTags(selectedUnit, new float2(.5f * (bounds.Left + bounds.Right ), xy.Y)); + } + } + + if (ShowUnitPaths) + { + var mobile = selectedUnit.traits.GetOrDefault(); + if (mobile != null) + { + var path = mobile.GetCurrentPath(); + var start = selectedUnit.Location; + + foreach (var step in path) + { + lineRenderer.DrawLine( + Game.CellSize * start + new float2(12, 12), + Game.CellSize * step + new float2(12, 12), + Color.Red, Color.Red); + start = step; + } + } + } + } + + void DrawHealthBar(Actor selectedUnit, float2 xy, float2 Xy) + { + var c = Color.Gray; + lineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c); + lineRenderer.DrawLine(Xy + new float2(0, -2), Xy + new float2(0, -4), c, c); + + var healthAmount = (float)selectedUnit.Health / selectedUnit.Info.Traits.Get().HP; + var healthColor = (healthAmount < Rules.General.ConditionRed) ? Color.Red + : (healthAmount < Rules.General.ConditionYellow) ? Color.Yellow + : Color.LimeGreen; + + var healthColor2 = Color.FromArgb( + 255, + healthColor.R / 2, + healthColor.G / 2, + healthColor.B / 2); + + var z = float2.Lerp(xy, Xy, healthAmount); + + lineRenderer.DrawLine(z + new float2(0, -4), Xy + new float2(0, -4), c, c); + lineRenderer.DrawLine(z + new float2(0, -2), Xy + new float2(0, -2), c, c); + + lineRenderer.DrawLine(xy + new float2(0, -3), z + new float2(0, -3), healthColor, healthColor); + lineRenderer.DrawLine(xy + new float2(0, -2), z + new float2(0, -2), healthColor2, healthColor2); + lineRenderer.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), healthColor2, healthColor2); + } + + // depends on the order of pips in TraitsInterfaces.cs! + static readonly string[] pipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray" }; + static readonly string[] tagStrings = { "", "tag-fake", "tag-primary" }; + + void DrawControlGroup(Actor selectedUnit, float2 basePosition) + { + var group = Game.controller.selection.GetControlGroupForActor(selectedUnit); + if (group == null) return; + + var pipImages = new Animation("pips"); + pipImages.PlayFetchIndex("groups", () => (int)group); + pipImages.Tick(); + spriteRenderer.DrawSprite(pipImages.Image, basePosition + new float2(-8, 1), "chrome"); + } + + void DrawPips(Actor selectedUnit, float2 basePosition) + { + // If a mod wants to implement a unit with multiple pip sources, then they are placed on multiple rows + var pipxyBase = basePosition + new float2(-12, -7); // Correct for the offset in the shp file + var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows + + foreach (var pips in selectedUnit.traits.WithInterface()) + { + foreach (var pip in pips.GetPips(selectedUnit)) + { + var pipImages = new Animation("pips"); + pipImages.PlayRepeating(pipStrings[(int)pip]); + spriteRenderer.DrawSprite(pipImages.Image, pipxyBase + pipxyOffset, "chrome"); + pipxyOffset += new float2(4, 0); + + if (pipxyOffset.X+5 > selectedUnit.GetBounds(false).Width) + { + pipxyOffset.X = 0; + pipxyOffset.Y -= 4; + } + } + // Increment row + pipxyOffset.X = 0; + pipxyOffset.Y -= 5; + } + } + + void DrawTags(Actor selectedUnit, float2 basePosition) + { + // If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows + var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file + var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows + + foreach (var tags in selectedUnit.traits.WithInterface()) + { + foreach (var tag in tags.GetTags()) + { + if (tag == TagType.None) + continue; + + var tagImages = new Animation("pips"); + tagImages.PlayRepeating(tagStrings[(int)tag]); + spriteRenderer.DrawSprite(tagImages.Image, tagxyBase + tagxyOffset, "chrome"); + + // Increment row + tagxyOffset.Y += 8; + } + } + } + } +} diff --git a/OpenRA.Game/MainWindow.cs b/OpenRA.Game/MainWindow.cs new file mode 100755 index 0000000000..c3ed7f0033 --- /dev/null +++ b/OpenRA.Game/MainWindow.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Windows.Forms; + +namespace OpenRA +{ + [Flags] + public enum MouseButton + { + None = (int)MouseButtons.None, + Left = (int)MouseButtons.Left, + Right = (int)MouseButtons.Right, + Middle = (int)MouseButtons.Middle, + } + + [Flags] + public enum Modifiers + { + None = (int)Keys.None, + Shift = (int)Keys.Shift, + Alt = (int)Keys.Alt, + Ctrl = (int)Keys.Control, + } + + public struct MouseInput + { + public MouseInputEvent Event; + public int2 Location; + public MouseButton Button; + public Modifiers Modifiers; + } + + public enum MouseInputEvent { Down, Move, Up }; +} diff --git a/OpenRA.Game/Network/Connection.cs b/OpenRA.Game/Network/Connection.cs new file mode 100755 index 0000000000..a6b3533bc0 --- /dev/null +++ b/OpenRA.Game/Network/Connection.cs @@ -0,0 +1,142 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using OpenRA.FileFormats; + +namespace OpenRA.Network +{ + enum ConnectionState + { + NotConnected, + Connecting, + Connected, + } + + interface IConnection + { + int LocalClientId { get; } + ConnectionState ConnectionState { get; } + void Send( byte[] packet ); + void Receive( Action packetFn ); + } + + class EchoConnection : IConnection + { + protected struct ReceivedPacket + { + public int FromClient; + public byte[] Data; + } + protected List receivedPackets = new List(); + + public virtual int LocalClientId + { + get { return 1; } + } + + public virtual ConnectionState ConnectionState + { + get { return ConnectionState.Connected; } + } + + public virtual void Send( byte[] packet ) + { + if( packet.Length == 0 ) + throw new NotImplementedException(); + lock( this ) + receivedPackets.Add( new ReceivedPacket { FromClient = LocalClientId, Data = packet } ); + } + + public virtual void Receive( Action packetFn ) + { + List packets; + lock( this ) + { + packets = receivedPackets; + receivedPackets = new List(); + } + + foreach( var p in packets ) + packetFn( p.FromClient, p.Data ); + } + } + + class NetworkConnection : EchoConnection + { + TcpClient socket; + int clientId; + ConnectionState connectionState = ConnectionState.Connecting; + + public NetworkConnection( string host, int port ) + { + new Thread( _ => + { + try + { + socket = new TcpClient( host, port ); + var reader = new BinaryReader( socket.GetStream() ); + var serverProtocol = reader.ReadInt32(); + + if (ProtocolVersion.Version != serverProtocol) + throw new InvalidOperationException( + "Protocol version mismatch. Server={0} Client={1}" + .F(serverProtocol, ProtocolVersion.Version)); + + clientId = reader.ReadInt32(); + connectionState = ConnectionState.Connected; + + for( ; ; ) + { + var len = reader.ReadInt32(); + var client = reader.ReadInt32(); + var buf = reader.ReadBytes( len ); + if( len == 0 ) + throw new NotImplementedException(); + lock( this ) + receivedPackets.Add( new ReceivedPacket { FromClient = client, Data = buf } ); + } + } + catch( SocketException ) + { + connectionState = ConnectionState.NotConnected; + } + } + ) { IsBackground = true }.Start(); + } + + public override int LocalClientId { get { return clientId; } } + public override ConnectionState ConnectionState { get { return connectionState; } } + + public override void Send( byte[] packet ) + { + base.Send( packet ); + + var ms = new MemoryStream(); + ms.Write( BitConverter.GetBytes( (int)packet.Length ) ); + ms.Write( packet ); + ms.WriteTo( socket.GetStream() ); + } + } +} diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs new file mode 100755 index 0000000000..cf0dad188c --- /dev/null +++ b/OpenRA.Game/Network/Order.cs @@ -0,0 +1,182 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.IO; +using System.Linq; + +namespace OpenRA +{ + public sealed class Order + { + public readonly string OrderString; + public readonly Actor Subject; + public readonly Actor TargetActor; + public readonly int2 TargetLocation; + public readonly string TargetString; + public bool IsImmediate; + + public Player Player { get { return Subject.Owner; } } + + public Order(string orderString, Actor subject, + Actor targetActor, int2 targetLocation, string targetString) + { + this.OrderString = orderString; + this.Subject = subject; + this.TargetActor = targetActor; + this.TargetLocation = targetLocation; + this.TargetString = targetString; + } + + public Order(string orderString, Actor subject) + : this(orderString, subject, null, int2.Zero, null) { } + public Order(string orderString, Actor subject, Actor targetActor) + : this(orderString, subject, targetActor, int2.Zero, null) { } + public Order(string orderString, Actor subject, int2 targetLocation) + : this(orderString, subject, null, targetLocation, null) { } + public Order(string orderString, Actor subject, string targetString) + : this(orderString, subject, null, int2.Zero, targetString) { } + public Order(string orderString, Actor subject, Actor targetActor, int2 targetLocation) + : this(orderString, subject, targetActor, targetLocation, null) { } + public Order(string orderString, Actor subject, Actor targetActor, string targetString) + : this(orderString, subject, targetActor, int2.Zero, targetString) { } + public Order(string orderString, Actor subject, int2 targetLocation, string targetString) + : this(orderString, subject, null, targetLocation, targetString) { } + + public byte[] Serialize() + { + if (IsImmediate) /* chat, whatever */ + { + var ret = new MemoryStream(); + var w = new BinaryWriter(ret); + w.Write((byte)0xfe); + w.Write(OrderString); + w.Write(TargetString); + return ret.ToArray(); + } + + switch (OrderString) + { + // Format: + // u8 : orderID. + // 0xFF: Full serialized order. + // varies: rest of order. + default: + // TODO: specific serializers for specific orders. + { + var ret = new MemoryStream(); + var w = new BinaryWriter(ret); + w.Write( (byte)0xFF ); + w.Write(OrderString); + w.Write(UIntFromActor(Subject)); + w.Write(UIntFromActor(TargetActor)); + w.Write(TargetLocation.X); + w.Write(TargetLocation.Y); + w.Write(TargetString != null); + if (TargetString != null) + w.Write(TargetString); + return ret.ToArray(); + } + } + } + + public static Order Deserialize(World world, BinaryReader r) + { + switch (r.ReadByte()) + { + case 0xFF: + { + var order = r.ReadString(); + var subjectId = r.ReadUInt32(); + var targetActorId = r.ReadUInt32(); + var targetLocation = new int2(r.ReadInt32(), 0); + targetLocation.Y = r.ReadInt32(); + var targetString = null as string; + if (r.ReadBoolean()) + targetString = r.ReadString(); + + Actor subject, targetActor; + if( !TryGetActorFromUInt( world, subjectId, out subject ) || !TryGetActorFromUInt( world, targetActorId, out targetActor ) ) + return null; + + return new Order( order, subject, targetActor, targetLocation, targetString); + } + + case 0xfe: + { + var name = r.ReadString(); + var data = r.ReadString(); + + return new Order( name, null, data ) { IsImmediate = true }; + } + + default: + throw new NotImplementedException(); + } + } + + static uint UIntFromActor(Actor a) + { + if (a == null) return 0xffffffff; + return a.ActorID; + } + + static bool TryGetActorFromUInt(World world, uint aID, out Actor ret ) + { + if( aID == 0xFFFFFFFF ) + { + ret = null; + return true; + } + else + { + foreach( var a in world.Actors.Where( x => x.ActorID == aID ) ) + { + ret = a; + return true; + } + ret = null; + return false; + } + } + + // Named constructors for Orders. + // Now that Orders are resolved by individual Actors, these are weird; you unpack orders manually, but not pack them. + public static Order Chat(string text) + { + return new Order("Chat", null, text) { IsImmediate = true }; + } + + public static Order StartProduction(Player subject, string item) + { + return new Order("StartProduction", subject.PlayerActor, item ); + } + + public static Order PauseProduction(Player subject, string item, bool pause) + { + return new Order("PauseProduction", subject.PlayerActor, new int2( pause ? 1 : 0, 0 ), item); + } + + public static Order CancelProduction(Player subject, string item) + { + return new Order("CancelProduction", subject.PlayerActor, item); + } + } +} diff --git a/OpenRA.Game/Network/OrderIO.cs b/OpenRA.Game/Network/OrderIO.cs new file mode 100755 index 0000000000..6b01dde077 --- /dev/null +++ b/OpenRA.Game/Network/OrderIO.cs @@ -0,0 +1,78 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace OpenRA.Network +{ + static class OrderIO + { + public static void Write(this Stream s, byte[] buf) + { + s.Write(buf, 0, buf.Length); + } + + public static void WriteFrameData(this Stream s, IEnumerable orders, int frameNumber) + { + var bytes = Serialize( orders, frameNumber ); + s.Write( BitConverter.GetBytes( (int)bytes.Length ) ); + s.Write( bytes ); + } + + public static byte[] Serialize( this IEnumerable orders, int frameNumber ) + { + var ms = new MemoryStream(); + ms.Write( BitConverter.GetBytes( frameNumber ) ); + foreach( var o in orders.Select( o => o.Serialize() ) ) + ms.Write( o ); + return ms.ToArray(); + } + + public static List ToOrderList(this byte[] bytes, World world) + { + var ms = new MemoryStream(bytes, 4, bytes.Length - 4); + var reader = new BinaryReader(ms); + var ret = new List(); + while( ms.Position < ms.Length ) + { + var o = Order.Deserialize( world, reader ); + if( o != null ) + ret.Add( o ); + } + return ret; + } + + public static byte[] SerializeSync( this List sync, int frameNumber ) + { + var ms = new MemoryStream(); + using( var writer = new BinaryWriter( ms ) ) + { + writer.Write( frameNumber ); + writer.Write( (byte)0x65 ); + foreach( var s in sync ) + writer.Write( s ); + } + return ms.ToArray(); + } + } +} diff --git a/OpenRA.Game/Network/OrderManager.cs b/OpenRA.Game/Network/OrderManager.cs new file mode 100755 index 0000000000..a55772f15a --- /dev/null +++ b/OpenRA.Game/Network/OrderManager.cs @@ -0,0 +1,158 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Network +{ + class OrderManager + { + int frameNumber = 0; + + public int FramesAhead = 0; + + public bool GameStarted { get { return frameNumber != 0; } } + public IConnection Connection { get; private set; } + + Dictionary> frameClientData = + new Dictionary>(); + List readyForFrames = new List(); + List localOrders = new List(); + + public void StartGame() + { + if (GameStarted) return; + + frameNumber = 1; + for( int i = frameNumber ; i <= FramesAhead ; i++ ) + Connection.Send( new List().Serialize( i ) ); + } + + public int FrameNumber { get { return frameNumber; } } + + public OrderManager( IConnection conn ) + { + Connection = conn; + } + + public OrderManager( IConnection conn, string replayFilename ) + : this( conn ) + { + } + + public void IssueOrders( Order[] orders ) + { + foreach( var order in orders ) + IssueOrder( order ); + } + + public void IssueOrder( Order order ) + { + localOrders.Add( order ); + } + + public void TickImmediate( World world ) + { + var immediateOrders = localOrders.Where( o => o.IsImmediate ).ToList(); + if( immediateOrders.Count != 0 ) + Connection.Send( immediateOrders.Serialize( 0 ) ); + localOrders.RemoveAll( o => o.IsImmediate ); + + var immediatePackets = new List>(); + + Connection.Receive( + ( clientId, packet ) => + { + var frame = BitConverter.ToInt32( packet, 0 ); + if( packet.Length == 5 && packet[ 4 ] == 0xEF ) + readyForFrames.Add( frame ); + else if( packet.Length >= 5 && packet[ 4 ] == 0x65 ) + CheckSync( packet ); + else if( frame == 0 ) + immediatePackets.Add( Pair.New( clientId, packet ) ); + else + frameClientData.GetOrAdd( frame ).Add( clientId, packet ); + } ); + + foreach( var p in immediatePackets ) + foreach( var o in p.Second.ToOrderList( world ) ) + UnitOrders.ProcessOrder( world, p.First, o ); + } + + Dictionary syncForFrame = new Dictionary(); + + void CheckSync( byte[] packet ) + { + var frame = BitConverter.ToInt32( packet, 0 ); + byte[] existingSync; + if( syncForFrame.TryGetValue( frame, out existingSync ) ) + { + if( packet.Length != existingSync.Length ) + OutOfSync( frame ); + else + for( int i = 0 ; i < packet.Length ; i++ ) + if( packet[ i ] != existingSync[ i ] ) + OutOfSync( frame ); + } + else + syncForFrame.Add( frame, packet ); + } + + void OutOfSync( int frame ) + { + throw new InvalidOperationException( "out of sync in frame {0}".F( frame ) ); + } + + public bool IsReadyForNextFrame + { + get { return readyForFrames.Contains( FrameNumber ); } + } + + public void Tick( World world ) + { + if( !IsReadyForNextFrame ) + throw new InvalidOperationException(); + readyForFrames.RemoveAll( f => f <= FrameNumber ); + + Connection.Send( localOrders.Serialize( FrameNumber + FramesAhead ) ); + localOrders.Clear(); + + var frameData = frameClientData[ FrameNumber ]; + var sync = new List(); + sync.Add( world.SyncHash() ); + + foreach( var order in frameData.OrderBy( p => p.Key ).SelectMany( o => o.Value.ToOrderList( world ).Select( a => new { Client = o.Key, Order = a } ) ) ) + { + UnitOrders.ProcessOrder( world, order.Client, order.Order ); + sync.Add( world.SyncHash() ); + } + + var ss = sync.SerializeSync( FrameNumber ); + Connection.Send( ss ); + CheckSync( ss ); + + ++frameNumber; + } + } +} diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs new file mode 100755 index 0000000000..15c9e64019 --- /dev/null +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -0,0 +1,66 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Network +{ + static class UnitOrders + { + public static void ProcessOrder( World world, int clientId, Order order ) + { + switch( order.OrderString ) + { + case "Chat": + { + var player = world.players.Values.Where( p => p.Index == clientId ).Single(); + Game.chat.AddLine(player, order.TargetString); + break; + } + case "StartGame": + { + Game.chat.AddLine(Color.White, "Server", "The game has started."); + Game.StartGame(); + break; + } + case "SyncInfo": + { + Game.SyncLobbyInfo(order.TargetString); + break; + } + case "FileChunk": + { + PackageDownloader.ReceiveChunk(order.TargetString); + break; + } + + default: + { + if( !order.IsImmediate ) + foreach (var t in order.Subject.traits.WithInterface()) + t.ResolveOrder(order.Subject, order); + break; + } + } + } + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj new file mode 100644 index 0000000000..b431953177 --- /dev/null +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -0,0 +1,306 @@ + + + + Debug + x86 + 9.0.30729 + 2.0 + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + WinExe + Properties + OpenRA + OpenRA.Game + + + 2.0 + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + v3.5 + OpenRA.ico + + + true + bin\Debug\ + TRACE;DEBUG;SANITY_CHECKS + true + full + x86 + false + prompt + 4 + false + + + bin\Release\ + TRACE + true + true + pdbonly + x86 + false + prompt + 4 + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + + + + + + \ No newline at end of file diff --git a/OpenRA.Game/OpenRA.ico b/OpenRA.Game/OpenRA.ico new file mode 100644 index 0000000000000000000000000000000000000000..f503a3b7c19d185ee3fd9e19d2f5489eee9b3db9 GIT binary patch literal 270398 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 Jirng`{{u8h4kQ2o literal 0 HcmV?d00001 diff --git a/OpenRA.Game/Orders/IOrderGenerator.cs b/OpenRA.Game/Orders/IOrderGenerator.cs new file mode 100644 index 0000000000..e6259232a1 --- /dev/null +++ b/OpenRA.Game/Orders/IOrderGenerator.cs @@ -0,0 +1,32 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA +{ + public interface IOrderGenerator + { + IEnumerable Order( World world, int2 xy, MouseInput mi ); + void Tick( World world ); + void Render( World world ); + string GetCursor( World world, int2 xy, MouseInput mi ); + } +} diff --git a/OpenRA.Game/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Game/Orders/PlaceBuildingOrderGenerator.cs new file mode 100644 index 0000000000..42dc5884cf --- /dev/null +++ b/OpenRA.Game/Orders/PlaceBuildingOrderGenerator.cs @@ -0,0 +1,78 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.GameRules; +using OpenRA.Traits; + +namespace OpenRA.Orders +{ + class PlaceBuildingOrderGenerator : IOrderGenerator + { + readonly Actor Producer; + readonly string Building; + BuildingInfo BuildingInfo { get { return Rules.Info[ Building ].Traits.Get(); } } + + public PlaceBuildingOrderGenerator(Actor producer, string name) + { + Producer = producer; + Building = name; + } + + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return InnerOrder(world, xy, mi); + } + + IEnumerable InnerOrder(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var topLeft = xy - Footprint.AdjustForBuildingSize( BuildingInfo ); + if (!world.CanPlaceBuilding( Building, BuildingInfo, topLeft, null) + || !world.IsCloseEnoughToBase(Producer.Owner, Building, BuildingInfo, topLeft)) + { + var eva = world.LocalPlayer.PlayerActor.Info.Traits.Get(); + Sound.Play(eva.BuildingCannotPlaceAudio); + yield break; + } + + yield return new Order("PlaceBuilding", Producer.Owner.PlayerActor, topLeft, Building); + } + } + + public void Tick( World world ) + { + var producing = Producer.traits.Get().CurrentItem( Rules.Info[ Building ].Category ); + if (producing == null || producing.Item != Building || producing.RemainingTime != 0) + Game.controller.CancelInputMode(); + } + + public void Render( World world ) + { + world.WorldRenderer.uiOverlay.DrawBuildingGrid( world, Building, BuildingInfo ); + } + + public string GetCursor(World world, int2 xy, MouseInput mi) { return "default"; } + } +} diff --git a/OpenRA.Game/Orders/PowerDownOrderGenerator.cs b/OpenRA.Game/Orders/PowerDownOrderGenerator.cs new file mode 100644 index 0000000000..7bc750e364 --- /dev/null +++ b/OpenRA.Game/Orders/PowerDownOrderGenerator.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Orders +{ + class PowerDownOrderGenerator : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Owner == world.LocalPlayer + && a.traits.Contains()) + .FirstOrDefault(); + + if (underCursor != null) + yield return new Order("PowerDown", underCursor); + } + } + + public void Tick( World world ) { } + public void Render( World world ) { } + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(world, xy, mi).Any() + ? "powerdown" : "powerdown-blocked"; + } + } +} diff --git a/OpenRA.Game/Orders/RepairOrderGenerator.cs b/OpenRA.Game/Orders/RepairOrderGenerator.cs new file mode 100644 index 0000000000..31a8d0b561 --- /dev/null +++ b/OpenRA.Game/Orders/RepairOrderGenerator.cs @@ -0,0 +1,75 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Orders +{ + class RepairOrderGenerator : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Owner == world.LocalPlayer + && a.traits.Contains() + && a.traits.Contains()).FirstOrDefault(); + + var building = underCursor != null ? underCursor.Info.Traits.Get() : null; + + if (building != null && building.Repairable && underCursor.Health < building.HP) + yield return new Order("Repair", underCursor); + } + } + + public void Tick( World world ) + { + if (!Game.Settings.RepairRequiresConyard) + return; + + var hasFact = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(); + + if (!hasFact) + Game.controller.CancelInputMode(); + } + + public void Render( World world ) {} + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(world, xy, mi).Any() + ? "repair" : "repair-blocked"; + } + } +} diff --git a/OpenRA.Game/Orders/SellOrderGenerator.cs b/OpenRA.Game/Orders/SellOrderGenerator.cs new file mode 100644 index 0000000000..1a92bd3346 --- /dev/null +++ b/OpenRA.Game/Orders/SellOrderGenerator.cs @@ -0,0 +1,63 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Orders +{ + class SellOrderGenerator : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Owner == world.LocalPlayer + && a.traits.Contains() + && a.traits.Contains()).FirstOrDefault(); + + var building = underCursor != null ? underCursor.Info.Traits.Get() : null; + + if (building != null && !building.Unsellable) + yield return new Order("Sell", underCursor); + } + } + + public void Tick( World world ) {} + public void Render( World world ) {} + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(world, xy, mi).Any() + ? "sell" : "sell-blocked"; + } + } +} diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs new file mode 100644 index 0000000000..b3252348b5 --- /dev/null +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -0,0 +1,102 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Orders +{ + class UnitOrderGenerator : IOrderGenerator + { + public IEnumerable Order( World world, int2 xy, MouseInput mi ) + { + foreach( var unit in Game.controller.selection.Actors ) + { + var ret = unit.Order( xy, mi ); + if( ret != null ) + yield return ret; + } + } + + public void Tick( World world ) {} + + public void Render( World world ) + { + foreach( var a in Game.controller.selection.Actors ) + world.WorldRenderer.DrawSelectionBox( a, Color.White, true ); + } + + public string GetCursor( World world, int2 xy, MouseInput mi ) + { + return ChooseCursor(world, mi); + } + + string ChooseCursor( World world, MouseInput mi ) + { + var p = Game.controller.MousePosition; + var c = Order(world, p.ToInt2(), mi) + .Select(o => CursorForOrderString(o.OrderString, o.Subject, o.TargetLocation)) + .FirstOrDefault(a => a != null); + + return c ?? + (world.SelectActorsInBox(Game.CellSize * p, + Game.CellSize * p).Any() + ? "select" : "default"); + } + + string CursorForOrderString(string s, Actor a, int2 location) + { + switch (s) + { + case "Attack": return "attack"; + case "Heal": return "heal"; + case "C4": return "c4"; + case "Move": + if (a.traits.GetOrDefault().CanEnterCell(location)) + return "move"; + else + return "move-blocked"; + case "DeployTransform": + var depInfo = a.Info.Traits.Get(); + var transInfo = Rules.Info[depInfo.TransformsInto]; + if (transInfo.Traits.Contains()) + { + var bi = transInfo.Traits.Get(); + if (!a.World.CanPlaceBuilding(depInfo.TransformsInto, bi, a.Location + new int2(depInfo.Offset[0], depInfo.Offset[1]), a)) + return "deploy-blocked"; + } + return "deploy"; + + case "Deploy": return "deploy"; + case "Enter": return "enter"; + case "EnterTransport": return "enter"; + case "Deliver": return "enter"; + case "Infiltrate": return "enter"; + case "Capture": return "capture"; + case "Harvest": return "attackmove"; + case "Steal" : return "enter"; + default: + return null; + } + } + } +} diff --git a/OpenRA.Game/Ore.cs b/OpenRA.Game/Ore.cs new file mode 100644 index 0000000000..0fe76526c3 --- /dev/null +++ b/OpenRA.Game/Ore.cs @@ -0,0 +1,207 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + public static class Ore + { + public static void AddOre(this Map map, int i, int j) + { + if (map.ContainsOre(i, j) && map.MapTiles[i, j].density < 12) + map.MapTiles[i, j].density++; + else if (map.MapTiles[i, j].overlay == 0xff) + { + map.MapTiles[i, j].overlay = ChooseOre(); + map.MapTiles[i, j].density = 1; + } + } + + public static void DestroyOre(this Map map, int i, int j) + { + if (map.ContainsResource(new int2(i, j))) + { + map.MapTiles[i, j].density = 0; + map.MapTiles[i, j].overlay = 0xff; + } + } + + public static bool OreCanSpreadInto(this World world, int i, int j) + { + if (world.WorldActor.traits.Get().GetBuildingAt(new int2(i, j)) != null) + return false; + + return TerrainCosts.Cost(UnitMovementType.Wheel, + world.TileSet.GetWalkability(world.Map.MapTiles[i, j])) + < double.PositiveInfinity; + } + + public static void SpreadOre(this World world, Random r, float chance) + { + var map = world.Map; + + var mini = map.XOffset; var maxi = map.XOffset + map.Width; + var minj = map.YOffset; var maxj = map.YOffset + map.Height; + + /* phase 1: grow into neighboring regions */ + var newOverlay = new byte[128, 128]; + for (int j = minj; j < maxj; j++) + for (int i = mini; i < maxi; i++) + { + newOverlay[i, j] = 0xff; + if (!map.HasOverlay(i, j) + && r.NextDouble() < chance + && map.GetOreDensity(i, j) > 0 + && world.OreCanSpreadInto(i, j)) + newOverlay[i, j] = ChooseOre(); + } + + for (int j = minj; j < maxj; j++) + for (int i = mini; i < maxi; i++) + if (newOverlay[i, j] != 0xff) + map.MapTiles[i, j].overlay = newOverlay[i, j]; + } + + public static void GrowOre(this World world, Random r) + { + var map = world.Map; + + var mini = map.XOffset; var maxi = map.XOffset + map.Width; + var minj = map.YOffset; var maxj = map.YOffset + map.Height; + + /* phase 2: increase density of existing areas */ + var newDensity = new byte[128, 128]; + for (int j = minj; j < maxj; j++) + for (int i = mini; i < maxi; i++) + if (map.ContainsOre(i, j)) newDensity[i, j] = map.GetOreDensity(i, j); + + for (int j = minj; j < maxj; j++) + for (int i = mini; i < maxi; i++) + if (map.MapTiles[i, j].density < newDensity[i, j]) + ++map.MapTiles[i, j].density; + } + + public static void InitOreDensity( this Map map ) + { + for (int j = 0; j < 128; j++) + for (int i = 0; i < 128; i++) + { + if (map.ContainsOre(i, j)) map.MapTiles[i, j].density = map.GetOreDensity(i, j); + if (map.ContainsGem(i, j)) map.MapTiles[i, j].density = map.GetGemDensity(i, j); + } + } + + static byte GetOreDensity(this Map map, int i, int j) + { + int sum = 0; + for (var u = -1; u < 2; u++) + for (var v = -1; v < 2; v++) + if (map.ContainsOre(i + u, j + v)) + ++sum; + sum = (sum * 4 + 2) / 3; + return (byte)sum; + } + + static byte GetGemDensity(this Map map, int i, int j) + { + int sum = 0; + for (var u = -1; u < 2; u++) + for (var v = -1; v < 2; v++) + if (map.ContainsGem(i + u, j + v)) + ++sum; + sum = (sum+2) / 3; /* 3 gem units/tile is full. */ + return (byte)sum; + } + + static bool HasOverlay(this Map map, int i, int j) + { + return map.MapTiles[i, j].overlay < overlayIsOre.Length; + } + + static bool ContainsOre(this Map map, int i, int j) + { + return map.HasOverlay(i, j) && overlayIsOre[map.MapTiles[i, j].overlay]; + } + + static bool ContainsGem(this Map map, int i, int j) + { + return map.HasOverlay(i, j) && overlayIsGems[map.MapTiles[i, j].overlay]; + } + + public static bool ContainsResource(this Map map, int2 p) + { + return map.ContainsGem(p.X, p.Y) || map.ContainsOre(p.X, p.Y); + } + + public static bool Harvest(this Map map, int2 p, out bool isGems) /* harvests one unit if possible */ + { + isGems = map.ContainsGem(p.X, p.Y); + if (map.MapTiles[p.X, p.Y].density == 0) return false; + + if (--map.MapTiles[p.X, p.Y].density == 0) + map.MapTiles[p.X, p.Y].overlay = 0xff; + + return true; + } + + static byte ore = 5; + static byte ChooseOre() + { + if (++ore > 8) ore = 5; + return ore; + } + + public static bool IsOverlaySolid(this Map map, int2 p) + { + var o = map.MapTiles[p.X, p.Y].overlay; + return o < overlayIsFence.Length && overlayIsFence[o]; + } + + public static bool[] overlayIsFence = + { + true, true, true, true, true, + false, false, false, false, + false, false, false, false, + false, false, false, false, false, false, false, + false, false, false, true, true, + }; + + public static bool[] overlayIsOre = + { + false, false, false, false, false, + true, true, true, true, + false, false, false, false, + false, false, false, false, false, false, false, + false, false, false, false, false, + }; + + public static bool[] overlayIsGems = + { + false, false, false, false, false, + false, false, false, false, + true, true, true, true, + false, false, false, false, false, false, false, + false, false, false, false, false, + }; + } +} diff --git a/OpenRA.Game/PackageDownloader.cs b/OpenRA.Game/PackageDownloader.cs new file mode 100644 index 0000000000..1752639808 --- /dev/null +++ b/OpenRA.Game/PackageDownloader.cs @@ -0,0 +1,144 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using OpenRA.FileFormats; + +namespace OpenRA +{ + static class PackageDownloader + { + static string[] allPackages = { }; + static List missingPackages = new List(); + static string currentPackage = null; + static MemoryStream content = null; + + public static string CurrentPackage { get { return currentPackage; } } + public static int RemainingPackages { get { return missingPackages.Count; } } + public static float Fraction { get; private set; } + public static int DownloadedBytes { get { return (int)content.Length; } } + + public static bool SetPackageList(string[] packages) + { + if (!(allPackages.Except(packages).Any() + || packages.Except(allPackages).Any())) + return false; + + allPackages = packages; + missingPackages = allPackages.Where(p => !HavePackage(p)).ToList(); + + if (currentPackage == null || !missingPackages.Contains(currentPackage)) + BeginDownload(); + else + missingPackages.Remove(currentPackage); + + return true; + } + + class Chunk { public int Index = 0; public int Count = 0; public string Data = ""; } + + public static void ReceiveChunk(string data) + { + var c = new Chunk(); + FieldLoader.Load(c, new MiniYaml(null, MiniYaml.FromString(data))); + var bytes = Convert.FromBase64String(c.Data); + content.Write(bytes, 0, bytes.Length); + + Fraction = (float)c.Index / c.Count; + + if (c.Index == c.Count - 1) + EndDownload(); + } + + static void BeginDownload() + { + if (missingPackages.Count == 0) // we're finished downloading resources! + { + currentPackage = null; + return; + } + + currentPackage = missingPackages[0]; + missingPackages.RemoveAt(0); + + content = new MemoryStream(); + + Game.chat.AddLine(Color.White, "Debug", "Requesting package: {0}".F(currentPackage)); + + Game.IssueOrder( + new Order("RequestFile", null, currentPackage) { IsImmediate = true }); + + Fraction = 0f; + } + + static void EndDownload() + { + // commit this data to disk + var parts = currentPackage.Split(':'); + File.WriteAllBytes(parts[0], content.ToArray()); + + if (CalculateSHA1(parts[0]) != parts[1]) + throw new InvalidOperationException("Broken download"); + + Game.chat.AddLine(Color.White, "Debug", "Finished receiving package: {0}".F(currentPackage)); + + currentPackage = null; + + BeginDownload(); + } + + public static bool IsIdle() + { + return currentPackage == null + && missingPackages.Count == 0; + } + + static bool HavePackage(string p) + { + var parts = p.Split(':'); + if (!File.Exists(parts[0])) + { + Game.chat.AddLine(Color.White, "Debug", "Missing package: {0}".F(p)); + return false; + } + + if (CalculateSHA1(parts[0]) != parts[1]) + { + Game.chat.AddLine(Color.White, "Debug", "Bad SHA1 for package; redownloading: {0}".F(p)); + return false; + } + + Game.chat.AddLine(Color.White, "Debug", "Verified package: {0}".F(p)); + return true; + } + + public static string CalculateSHA1(string filename) + { + using (var csp = SHA1.Create()) + return new string(csp.ComputeHash(File.ReadAllBytes(filename)) + .SelectMany(a => a.ToString("x2")).ToArray()); + } + } +} diff --git a/OpenRA.Game/PathFinder.cs b/OpenRA.Game/PathFinder.cs new file mode 100644 index 0000000000..0540c6ea59 --- /dev/null +++ b/OpenRA.Game/PathFinder.cs @@ -0,0 +1,226 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using OpenRA.Support; +using OpenRA.Traits; + +namespace OpenRA +{ + public class PathFinder + { + readonly World world; + float[][,] passableCost = new float[4][,]; + + public PathFinder( World world ) + { + this.world = world; + for (var umt = UnitMovementType.Foot; umt <= UnitMovementType.Float; umt++) + passableCost[(int)umt] = new float[128, 128]; + for( int x = 0 ; x < 128 ; x++ ) + for( int y = 0 ; y < 128 ; y++ ) + for (var umt = UnitMovementType.Foot; umt <= UnitMovementType.Float; umt++ ) + passableCost[(int)umt][ x, y ] = ( world.Map.IsInMap( x, y ) ) + ? (float)TerrainCosts.Cost( umt, world.TileSet.GetWalkability( world.Map.MapTiles[ x, y ] ) ) + : float.PositiveInfinity; + } + + public List FindUnitPath( int2 from, int2 target, UnitMovementType umt ) + { + using (new PerfSample("find_unit_path")) + { + var pb = FindBidiPath( + PathSearch.FromPoint(world, target, from, umt, false).WithCustomBlocker(AvoidUnitsNear(from, 4)), + PathSearch.FromPoint(world, from, target, umt, false).WithCustomBlocker(AvoidUnitsNear(from, 4))); + + CheckSanePath2(pb, from, target); + return pb; + } + } + + public List FindUnitPathToRange( int2 src, int2 target, UnitMovementType umt, int range ) + { + using( new PerfSample( "find_unit_path_multiple_src" ) ) + { + var tilesInRange = world.FindTilesInCircle(target, range) + .Where( t => world.IsCellBuildable( t, umt ) ); + + var path = FindPath( PathSearch.FromPoints( world, tilesInRange, src, umt, false ).WithCustomBlocker(AvoidUnitsNear(src, 4))); + path.Reverse(); + return path; + } + } + + public Func AvoidUnitsNear(int2 p, int dist) + { + return q => + p != q && + ((p - q).LengthSquared < dist * dist) && + (world.WorldActor.traits.Get().GetUnitsAt(q).Any()); + } + + public List FindPath( PathSearch search ) + { + using (new PerfSample("find_path_inner")) + { + while (!search.queue.Empty) + { + var p = search.Expand( world, passableCost ); + PerfHistory.Increment("nodes_expanded", .01); + + if (search.heuristic(p) == 0) + return MakePath(search.cellInfo, p); + } + + // no path exists + return new List(); + } + } + + static List MakePath( CellInfo[ , ] cellInfo, int2 destination ) + { + List ret = new List(); + int2 pathNode = destination; + + while( cellInfo[ pathNode.X, pathNode.Y ].Path != pathNode ) + { + ret.Add( pathNode ); + pathNode = cellInfo[ pathNode.X, pathNode.Y ].Path; + } + + ret.Add(pathNode); + CheckSanePath(ret); + return ret; + } + + + + List FindBidiPath( /* searches from both ends toward each other */ + PathSearch fromSrc, + PathSearch fromDest) + { + while (!fromSrc.queue.Empty && !fromDest.queue.Empty) + { + /* make some progress on the first search */ + var p = fromSrc.Expand( world, passableCost ); + + if (fromDest.cellInfo[p.X, p.Y].Seen && fromDest.cellInfo[p.X, p.Y].MinCost < float.PositiveInfinity) + return MakeBidiPath(fromSrc, fromDest, p); + + /* make some progress on the second search */ + var q = fromDest.Expand( world, passableCost ); + + if (fromSrc.cellInfo[q.X, q.Y].Seen && fromSrc.cellInfo[q.X, q.Y].MinCost < float.PositiveInfinity) + return MakeBidiPath(fromSrc, fromDest, q); + } + + return new List(); + } + + static List MakeBidiPath(PathSearch a, PathSearch b, int2 p) + { + var ca = a.cellInfo; + var cb = b.cellInfo; + + var ret = new List(); + + var q = p; + while (ca[q.X, q.Y].Path != q) + { + ret.Add( q ); + q = ca[ q.X, q.Y ].Path; + } + ret.Add(q); + + ret.Reverse(); + + q = p; + while (cb[q.X, q.Y].Path != q) + { + q = cb[q.X, q.Y].Path; + ret.Add(q); + } + + CheckSanePath( ret ); + return ret; + } + + [Conditional( "SANITY_CHECKS" )] + static void CheckSanePath( List path ) + { + if( path.Count == 0 ) + return; + var prev = path[ 0 ]; + for( int i = 0 ; i < path.Count ; i++ ) + { + var d = path[ i ] - prev; + if( Math.Abs( d.X ) > 1 || Math.Abs( d.Y ) > 1 ) + throw new InvalidOperationException( "(PathFinder) path sanity check failed" ); + prev = path[ i ]; + } + } + + [Conditional("SANITY_CHECKS")] + static void CheckSanePath2(List path, int2 src, int2 dest) + { + if (path.Count == 0) + return; + + if (path[0] != dest) + throw new InvalidOperationException("(PathFinder) sanity check failed: doesn't go to dest"); + if (path[path.Count - 1] != src) + throw new InvalidOperationException("(PathFinder) sanity check failed: doesn't come from src"); + } + } + + public struct CellInfo + { + public float MinCost; + public int2 Path; + public bool Seen; + + public CellInfo( float minCost, int2 path, bool seen ) + { + MinCost = minCost; + Path = path; + Seen = seen; + } + } + + public struct PathDistance : IComparable + { + public float EstTotal; + public int2 Location; + + public PathDistance(float estTotal, int2 location) + { + EstTotal = estTotal; + Location = location; + } + + public int CompareTo(PathDistance other) + { + return Math.Sign(EstTotal - other.EstTotal); + } + } +} diff --git a/OpenRA.Game/PathSearch.cs b/OpenRA.Game/PathSearch.cs new file mode 100755 index 0000000000..24806e1d51 --- /dev/null +++ b/OpenRA.Game/PathSearch.cs @@ -0,0 +1,183 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + public class PathSearch + { + public CellInfo[ , ] cellInfo; + public PriorityQueue queue; + public Func heuristic; + public UnitMovementType umt; + Func customBlock; + public bool checkForBlocked; + public Actor ignoreBuilding; + + public PathSearch() + { + cellInfo = InitCellInfo(); + queue = new PriorityQueue(); + } + + public PathSearch WithCustomBlocker(Func customBlock) + { + this.customBlock = customBlock; + return this; + } + + public PathSearch WithIgnoredBuilding(Actor b) + { + ignoreBuilding = b; + return this; + } + + public int2 Expand( World world, float[][ , ] passableCost ) + { + var p = queue.Pop(); + cellInfo[ p.Location.X, p.Location.Y ].Seen = true; + + var custom2 = world.customTerrain[p.Location.X, p.Location.Y]; + var thisCost = (custom2 != null) + ? custom2.GetCost(p.Location, umt) + : passableCost[(int)umt][p.Location.X, p.Location.Y]; + + if (thisCost == float.PositiveInfinity) + return p.Location; + + foreach( int2 d in directions ) + { + int2 newHere = p.Location + d; + + if (!world.Map.IsInMap(newHere.X, newHere.Y)) continue; + if( cellInfo[ newHere.X, newHere.Y ].Seen ) + continue; + + var custom = world.customTerrain[newHere.X, newHere.Y]; + var costHere = (custom != null) ? custom.GetCost(newHere, umt) : passableCost[(int)umt][newHere.X, newHere.Y]; + + if (costHere == float.PositiveInfinity) + continue; + + if (!world.WorldActor.traits.Get().CanMoveHere(newHere) && + world.WorldActor.traits.Get().GetBuildingAt(newHere) != ignoreBuilding) + continue; + if (world.Map.IsOverlaySolid(newHere)) + continue; + + // Replicate real-ra behavior of not being able to enter a cell if there is a mixture of crushable and uncrushable units + if (checkForBlocked && (world.WorldActor.traits.Get().GetUnitsAt(newHere).Any(a => !world.IsActorPathableToCrush(a, umt)))) + continue; + + if (customBlock != null && customBlock(newHere)) + continue; + + var est = heuristic( newHere ); + if( est == float.PositiveInfinity ) + continue; + + float cellCost = ((d.X * d.Y != 0) ? 1.414213563f : 1.0f) * costHere; + float newCost = cellInfo[ p.Location.X, p.Location.Y ].MinCost + cellCost; + + if( newCost >= cellInfo[ newHere.X, newHere.Y ].MinCost ) + continue; + + cellInfo[ newHere.X, newHere.Y ].Path = p.Location; + cellInfo[ newHere.X, newHere.Y ].MinCost = newCost; + + queue.Add( new PathDistance( newCost + est, newHere ) ); + + } + return p.Location; + } + + static readonly int2[] directions = + { + new int2( -1, -1 ), + new int2( -1, 0 ), + new int2( -1, 1 ), + new int2( 0, -1 ), + new int2( 0, 1 ), + new int2( 1, -1 ), + new int2( 1, 0 ), + new int2( 1, 1 ), + }; + + public void AddInitialCell( World world, int2 location ) + { + if (!world.Map.IsInMap(location.X, location.Y)) + return; + + cellInfo[ location.X, location.Y ] = new CellInfo( 0, location, false ); + queue.Add( new PathDistance( heuristic( location ), location ) ); + } + + public static PathSearch FromPoint( World world, int2 from, int2 target, UnitMovementType umt, bool checkForBlocked ) + { + var search = new PathSearch { + heuristic = DefaultEstimator( target ), + umt = umt, + checkForBlocked = checkForBlocked }; + + search.AddInitialCell( world, from ); + return search; + } + + public static PathSearch FromPoints(World world, IEnumerable froms, int2 target, UnitMovementType umt, bool checkForBlocked) + { + var search = new PathSearch + { + heuristic = DefaultEstimator(target), + umt = umt, + checkForBlocked = checkForBlocked + }; + + foreach (var sl in froms) + search.AddInitialCell(world, sl); + + return search; + } + + static CellInfo[ , ] InitCellInfo() + { + var cellInfo = new CellInfo[ 128, 128 ]; + for( int x = 0 ; x < 128 ; x++ ) + for( int y = 0 ; y < 128 ; y++ ) + cellInfo[ x, y ] = new CellInfo( float.PositiveInfinity, new int2( x, y ), false ); + return cellInfo; + } + + public static Func DefaultEstimator( int2 destination ) + { + return here => + { + int2 d = ( here - destination ).Abs(); + int diag = Math.Min( d.X, d.Y ); + int straight = Math.Abs( d.X - d.Y ); + return 1.5f * diag + straight; + }; + } + } +} diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs new file mode 100644 index 0000000000..a8488ab323 --- /dev/null +++ b/OpenRA.Game/Player.cs @@ -0,0 +1,224 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + public enum PowerState { Normal, Low, Critical }; + + public class Player + { + public Actor PlayerActor; + public int PaletteIndex; + public int Kills; + public string PlayerName; + public string InternalName; + public CountryInfo Country; + public readonly int Index; + public int Cash = 10000; + public int Ore = 0; + public int OreCapacity; + public int DisplayCash = 0; + public int PowerProvided = 0; + public int PowerDrained = 0; + public int SpawnPointIndex = 0; + + public World World { get { return PlayerActor.World; } } + + public static List> PlayerColors = new List>(); + public static void ResetPlayerColorList() + { + // This is unsafe if the mapchange introduces/removes mods that defines new colors + // TODO: ensure that each player's palette index is reassigned appropriately + PlayerColors = new List>(); + } + + public static void RegisterPlayerColor(string palette, string name, Color c) + { + PlayerColors.Add(new Tuple(palette, name, c)); + } + + public Color Color + { + get { return PlayerColors[PaletteIndex].c; } + } + + public string Palette + { + get { return PlayerColors[PaletteIndex].a; } + } + + public Shroud Shroud; + + public Player( World world, int index, Session.Client client ) + { + Shroud = new Shroud(this, world.Map); + this.PlayerActor = world.CreateActor("Player", new int2(int.MaxValue, int.MaxValue), this); + this.Index = index; + this.InternalName = "Multi{0}".F(index); + + this.PaletteIndex = client != null ? client.PaletteIndex : index; + this.PlayerName = client != null ? client.Name : "Player {0}".F(index+1); + this.Country = world.GetCountries().FirstOrDefault( c => client != null && client.Country == c.Name ) + ?? world.GetCountries().First(); + } + + void UpdatePower() + { + var oldBalance = PowerProvided - PowerDrained; + + PowerProvided = 0; + PowerDrained = 0; + + var myBuildings = World.Queries.OwnedBy[this] + .WithTrait(); + + foreach (var a in myBuildings) + { + var p = a.Trait.GetPowerUsage(); + if (p > 0) + PowerProvided += p; + else + PowerDrained -= p; + } + + if (PowerProvided - PowerDrained < 0) + if (PowerProvided - PowerDrained != oldBalance) + GiveAdvice(PlayerActor.Info.Traits.Get().LowPower); + } + + public float GetSiloFullness() + { + return (float)Ore / OreCapacity; + } + + public PowerState GetPowerState() + { + if (PowerProvided >= PowerDrained) return PowerState.Normal; + if (PowerProvided > PowerDrained / 2) return PowerState.Low; + return PowerState.Critical; + } + + void UpdateOreCapacity() + { + OreCapacity = World.Queries.OwnedBy[this] + .Where(a => a.traits.Contains()) + .Select(a => a.Info.Traits.Get()) + .Sum(b => b.Capacity); + } + + void GiveAdvice(string advice) + { + // todo: store the condition or something. + // repeat after Rules.General.SpeakDelay, as long as the condition holds. + Sound.PlayToPlayer(this, advice); + } + + public void GiveCash( int num ) { Cash += num; } + public void GiveOre(int num) + { + Ore += num; + + if (Ore > OreCapacity) + Ore = OreCapacity; // trim off the overflow. + + if (Ore > .8 * OreCapacity) + GiveAdvice(PlayerActor.Info.Traits.Get().SilosNeeded); // silos needed + } + + public bool TakeCash( int num ) + { + if (Cash + Ore < num) return false; + if (Ore <= num) + { + num -= Ore; + Ore = 0; + Cash -= num; + } + else + Ore -= num; + + return true; + } + + const float displayCashFracPerFrame = .07f; + const int displayCashDeltaPerFrame = 37; + + public void Tick() + { + UpdatePower(); + UpdateOreCapacity(); + Shroud.Tick( World ); + + var totalMoney = Cash + Ore; + var diff = Math.Abs(totalMoney - DisplayCash); + var move = Math.Min(Math.Max((int)(diff * displayCashFracPerFrame), + displayCashDeltaPerFrame), diff); + + var eva = PlayerActor.Info.Traits.Get(); + if (DisplayCash < totalMoney) + { + DisplayCash += move; + Sound.PlayToPlayer(this, eva.CashTickUp); + } + else if (DisplayCash > totalMoney) + { + DisplayCash -= move; + Sound.PlayToPlayer(this, eva.CashTickDown); + } + } + + public void SyncFromLobby(Session.Client client) + { + if (PlayerName != client.Name) + { + Game.chat.AddLine(this, "is now known as " + client.Name); + PlayerName = client.Name; + } + + if (string.IsNullOrEmpty(client.Country)) + client.Country = PlayerActor.World.GetCountries().First().Name; + + if (Country.Name != client.Country) + { + Game.chat.AddLine(this, "is now playing {0}".F(client.Country)); + Country = PlayerActor.World.GetCountries().First(c => c.Name == client.Country); + } + + if (PaletteIndex != client.PaletteIndex) + { + PaletteIndex = client.PaletteIndex; + Game.chat.AddLine(this, "has changed color to {0}".F(PlayerColors[client.PaletteIndex].b)); + } + + if (SpawnPointIndex != client.SpawnPoint) + { + SpawnPointIndex = client.SpawnPoint; + Game.chat.AddLine(this, "has changed spawn point to {0}".F(client.SpawnPoint)); + } + } + } +} diff --git a/OpenRA.Game/Properties/AssemblyInfo.cs b/OpenRA.Game/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..35d5841240 --- /dev/null +++ b/OpenRA.Game/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("OpenRA")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenRA")] +[assembly: AssemblyCopyright("Copyright © 2007,2009,2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenRA.Game/Selection.cs b/OpenRA.Game/Selection.cs new file mode 100644 index 0000000000..60c6274613 --- /dev/null +++ b/OpenRA.Game/Selection.cs @@ -0,0 +1,94 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA +{ + public class Selection + { + List actors = new List(); + + public void Combine(World world, IEnumerable newSelection, bool isCombine, bool isClick) + { + var oldSelection = actors.AsEnumerable(); + + if (isClick) + { + var adjNewSelection = newSelection.Take(1); /* todo: select BEST, not FIRST */ + actors = (isCombine ? oldSelection.SymmetricDifference(adjNewSelection) : adjNewSelection).ToList(); + } + else + actors = (isCombine ? oldSelection.Union(newSelection) : newSelection).ToList(); + + var voicedUnit = actors.FirstOrDefault(a => a.traits.Contains() && a.Owner == world.LocalPlayer); + Sound.PlayVoice("Select", voicedUnit); + + foreach (var ns in world.WorldActor.traits.WithInterface()) + ns.SelectionChanged(); + } + + public IEnumerable Actors { get { return actors; } } + public void Clear() { actors = new List(); } + + public void Tick(World world) + { + actors.RemoveAll(a => !a.IsInWorld); + } + + Cache> controlGroups = new Cache>(_ => new List()); + + public void DoControlGroup(World world, int group, Modifiers mods) + { + if (mods.HasModifier(Modifiers.Ctrl)) + { + if (actors.Count == 0) + return; + + controlGroups[group].Clear(); + + for (var i = 0; i < 10; i++) /* all control groups */ + controlGroups[i].RemoveAll(a => actors.Contains(a)); + + controlGroups[group].AddRange(actors); + return; + } + + if (mods.HasModifier(Modifiers.Alt)) + { + Game.viewport.Center(controlGroups[group]); + return; + } + + Combine(world, controlGroups[group], + mods.HasModifier(Modifiers.Shift), false); + } + + public int? GetControlGroupForActor(Actor a) + { + return controlGroups.Where(g => g.Value.Contains(a)) + .Select(g => (int?)g.Key) + .FirstOrDefault(); + } + } +} diff --git a/OpenRA.Game/Shroud.cs b/OpenRA.Game/Shroud.cs new file mode 100644 index 0000000000..7291d3c359 --- /dev/null +++ b/OpenRA.Game/Shroud.cs @@ -0,0 +1,232 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA +{ + public class Shroud + { + bool[,] explored = new bool[128, 128]; + Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow"); + Sprite[,] sprites = new Sprite[128, 128]; + bool dirty = true; + bool hasGPS = false; + Player owner; + Map map; + public Rectangle? bounds; + + public Shroud(Player owner, Map map) { this.owner = owner; this.map = map; } + + int gapOpaqueTicks = (int)(Rules.General.GapRegenInterval * 25 * 60); + int gapTicks; + int[,] gapField = new int[128, 128]; + bool[,] gapActive = new bool[128, 128]; + + public bool HasGPS + { + get { return hasGPS; } + set { hasGPS = value; dirty = true;} + } + + public void Tick( World world ) + { + if (gapTicks > 0) { --gapTicks; return; } + + // Clear active flags + gapActive = new bool[128, 128]; + foreach (var a in world.Queries.WithTrait().Where(a => owner != a.Actor.Owner)) + foreach (var t in a.Trait.GetShroudedTiles()) + { + gapActive[t.X, t.Y] = true; + explored[t.X, t.Y] = false; + dirty = true; + } + + gapTicks = gapOpaqueTicks; + } + + public bool IsExplored(int2 xy) { return IsExplored(xy.X, xy.Y); } + public bool IsExplored(int x, int y) + { + if (gapField[ x, y ] > 0) + return false; + + if (hasGPS) + return true; + + return explored[ x, y ]; + } + + public bool DisplayOnRadar(int x, int y) + { + // Active gap is never shown on radar, even if a unit is in range + if (gapActive[x , y]) + return false; + + return IsExplored(x,y); + } + + Rectangle MakeRect(int2 center, int range) + { + return new Rectangle(center.X - range, center.Y - range, 2 * range + 1, 2 * range + 1); + } + + public void Explore(World w, int2 center, int range) + { + if (range == 0) + return; + + var box = MakeRect(center, range); + bounds = bounds.HasValue ? + Rectangle.Union(bounds.Value, box) : box; + + foreach (var t in w.FindTilesInCircle(center, range)) + { + explored[t.X, t.Y] = true; + gapField[t.X, t.Y] = 0; + } + dirty = true; + } + + public void Explore(Actor a) + { + var sight = a.Info.Traits.Get().Sight; + + // Buildings: explore from each cell in the footprint + if (a.Info.Traits.Contains()) + { + var bi = a.Info.Traits.Get(); + foreach (var t in Footprint.Tiles(a.Info.Name, bi, a.Location)) + Explore(a.World, t, sight); + } + else + { + var mobile = a.traits.GetOrDefault(); + if (mobile != null) + { + Explore(a.World, mobile.fromCell, sight); + Explore(a.World, mobile.toCell, sight); + } + else + Explore(a.World, + (1f / Game.CellSize * a.CenterLocation).ToInt2(), + sight); + } + } + + static readonly byte[][] SpecialShroudTiles = + { + new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + new byte[] { 32, 32, 25, 25, 19, 19, 20, 20 }, + new byte[] { 33, 33, 33, 33, 26, 26, 26, 26, 21, 21, 21, 21, 23, 23, 23, 23 }, + new byte[] { 36, 36, 36, 36, 30, 30, 30, 30 }, + new byte[] { 34, 16, 34, 16, 34, 16, 34, 16, 27, 22, 27, 22, 27, 22, 27, 22 }, + new byte[] { 44 }, + new byte[] { 37, 37, 37, 37, 37, 37, 37, 37, 31, 31, 31, 31, 31, 31, 31, 31 }, + new byte[] { 40 }, + new byte[] { 35, 24, 17, 18 }, + new byte[] { 39, 39, 29, 29 }, + new byte[] { 45 }, + new byte[] { 43 }, + new byte[] { 38, 28 }, + new byte[] { 42 }, + new byte[] { 41 }, + new byte[] { 46 }, + }; + + Sprite ChooseShroud(int i, int j) + { + if( !IsExplored( i, j ) ) return shadowBits[ 0xf ]; + + // bits are for unexploredness: up, right, down, left + var v = 0; + // bits are for unexploredness: TL, TR, BR, BL + var u = 0; + + if( !IsExplored( i, j - 1 ) ) { v |= 1; u |= 3; } + if( !IsExplored( i + 1, j ) ) { v |= 2; u |= 6; } + if( !IsExplored( i, j + 1 ) ) { v |= 4; u |= 12; } + if( !IsExplored( i - 1, j ) ) { v |= 8; u |= 9; } + + var uSides = u; + + if( !IsExplored( i - 1, j - 1 ) ) u |= 1; + if( !IsExplored( i + 1, j - 1 ) ) u |= 2; + if( !IsExplored( i + 1, j + 1 ) ) u |= 4; + if( !IsExplored( i - 1, j + 1 ) ) u |= 8; + + return shadowBits[ SpecialShroudTiles[ u ^ uSides ][ v ] ]; + } + + internal void Draw(SpriteRenderer r) + { + if (dirty) + { + dirty = false; + for (int j = map.YOffset; j < map.YOffset + map.Height; j++) + for (int i = map.XOffset; i < map.XOffset + map.Width; i++) + sprites[i, j] = ChooseShroud(i, j); + } + + var miny = bounds.HasValue ? Math.Max(map.YOffset, bounds.Value.Top) : map.YOffset; + var maxy = bounds.HasValue ? Math.Min(map.YOffset + map.Height, bounds.Value.Bottom) : map.YOffset + map.Height; + + var minx = bounds.HasValue ? Math.Max(map.XOffset, bounds.Value.Left) : map.XOffset; + var maxx = bounds.HasValue ? Math.Min(map.XOffset + map.Width, bounds.Value.Right) : map.XOffset + map.Width; + + for (var j = miny; j < maxy; j++) + { + var starti = minx; + for (var i = minx; i < maxx; i++) + { + if (sprites[i, j] == shadowBits[0x0f]) + continue; + + if (starti != i) + { + r.DrawSprite(sprites[starti,j], + Game.CellSize * new float2(starti, j), + "shroud", + new float2(Game.CellSize * (i - starti), Game.CellSize)); + starti = i+1; + } + + r.DrawSprite(sprites[i, j], + Game.CellSize * new float2(i, j), + "shroud"); + starti = i+1; + } + + if (starti < maxx) + r.DrawSprite(sprites[starti, j], + Game.CellSize * new float2(starti, j), + "shroud", + new float2(Game.CellSize * (maxx - starti), Game.CellSize)); + } + } + } +} diff --git a/OpenRA.Game/Smudge.cs b/OpenRA.Game/Smudge.cs new file mode 100644 index 0000000000..952b18a864 --- /dev/null +++ b/OpenRA.Game/Smudge.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.FileFormats; +using OpenRA.GameRules; + +namespace OpenRA +{ + static class Smudge + { + const int firstScorch = 19; + const int firstCrater = 25; + const int framesPerCrater = 5; + + public static void AddSmudge(this Map map, bool isCrater, int x, int y) + { + var smudge = map.MapTiles[x, y].smudge; + if (smudge == 0) + map.MapTiles[x, y].smudge = (byte) (isCrater + ? (firstCrater + framesPerCrater * ChooseSmudge()) + : (firstScorch + ChooseSmudge())); + + if (smudge < firstCrater || !isCrater) return; /* bib or scorch; don't change */ + + /* deepen the crater */ + var amount = (smudge - firstCrater) % framesPerCrater; + if (amount < framesPerCrater - 1) + map.MapTiles[x, y].smudge++; + } + + public static void AddSmudge(this Map map, int2 targetTile, WarheadInfo warhead) + { + switch (warhead.Explosion) /* todo: push the scorch/crater behavior into data */ + { + case 4: + case 5: + map.AddSmudge(true, targetTile.X, targetTile.Y); + break; + + case 3: + case 6: + map.AddSmudge(false, targetTile.X, targetTile.Y); + break; + } + } + + static int lastSmudge = 0; + static int ChooseSmudge() { lastSmudge = (lastSmudge + 1) % 6; return lastSmudge; } + } +} diff --git a/OpenRA.Game/Sound.cs b/OpenRA.Game/Sound.cs new file mode 100644 index 0000000000..044fcef768 --- /dev/null +++ b/OpenRA.Game/Sound.cs @@ -0,0 +1,281 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.FileFormats; +using OpenRA.Support; +using OpenRA.Traits; +using Tao.OpenAl; + +namespace OpenRA +{ + public static class Sound + { + static ISoundEngine soundEngine; + static Cache sounds; + static ISound music; + + //TODO: read these from somewhere? + static float soundVolume; + static float musicVolume; + // static bool paused; + + static ISoundSource LoadSound(string filename) + { + var data = AudLoader.LoadSound(FileSystem.Open(filename)); + return soundEngine.AddSoundSourceFromMemory(data, 1, 16, 22050); + } + + public static void Initialize() + { + soundEngine = new OpenAlSoundEngine(); + sounds = new Cache(LoadSound); + music = null; + soundVolume = soundEngine.Volume; + musicVolume = soundEngine.Volume; + } + + public static void Play(string name) + { + if (name == "" || name == null) + return; + + var sound = sounds[name]; + // todo: positioning + soundEngine.Play2D(sound, false); + } + + public static void PlayToPlayer(Player player, string name) + { + if( player == player.World.LocalPlayer ) + Play( name ); + } + + public static void PlayMusic(string name) + { + var sound = sounds[name]; + music = soundEngine.Play2D(sound, true); + music.Volume = musicVolume; + } + + //public static bool Paused + //{ + // get { return paused; } + // set { paused = value; soundEngine.SetAllSoundsPaused(paused); } + //} + + public static float Volume + { + get { return soundVolume; } + set + { + soundVolume = value; + soundEngine.Volume = value; + } + } + + public static float MusicVolume + { + get { return musicVolume; } + set + { + musicVolume = value; + if (music != null) + music.Volume = value; + } + } + + //public static void SeekMusic(uint delta) + //{ + // if (music != null) + // { + // music.PlayPosition += delta; + // if (music.PlayPosition < 0 || music.PlayPosition > music.PlayLength) + // music.PlayPosition = 0; + // } + //} + + public static void PlayVoice(string phrase, Actor voicedUnit) + { + if (voicedUnit == null) return; + + var mi = voicedUnit.Info.Traits.GetOrDefault(); + if (mi == null) return; + + var vi = Rules.VoiceInfo[mi.Voice]; + + var clip = vi.Pools.Value[phrase].GetNext(); + if (clip == null) + return; + + if (clip.Contains(".")) /* no variants! */ + { + Play(clip); + return; + } + + // todo: fix this + var variants = (voicedUnit.Owner.Country.Race == "allies") + ? vi.AlliedVariants : vi.SovietVariants; + + var variant = variants[voicedUnit.ActorID % variants.Length]; + + Play(clip + variant); + } + } + + interface ISoundEngine + { + ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate); + ISound Play2D(ISoundSource sound, bool loop); + float Volume { get; set; } + } + + interface ISoundSource {} + interface ISound + { + float Volume { get; set; } + } + + class OpenAlSoundEngine : ISoundEngine + { + float volume = 1f; + Dictionary sourcePool = new Dictionary(); + const int POOL_SIZE = 32; + + public OpenAlSoundEngine() + { + //var str = Alc.alcGetString(IntPtr.Zero, Alc.ALC_DEFAULT_DEVICE_SPECIFIER); + var dev = Alc.alcOpenDevice(null); + if (dev == IntPtr.Zero) + throw new InvalidOperationException("Can't create OpenAL device"); + var ctx = Alc.alcCreateContext(dev, IntPtr.Zero); + if (ctx == IntPtr.Zero) + throw new InvalidOperationException("Can't create OpenAL context"); + Alc.alcMakeContextCurrent(ctx); + + for (var i = 0; i < POOL_SIZE; i++) + { + var source = 0; + Al.alGenSources(1, out source); + if (0 != Al.alGetError()) + throw new InvalidOperationException("failed generating source {0}".F(i)); + sourcePool.Add(source, false); + } + } + + int GetSourceFromPool() + { + foreach (var kvp in sourcePool) + { + if (!kvp.Value) + { + sourcePool[kvp.Key] = true; + return kvp.Key; + } + } + + List freeSources = new List(); + foreach (int key in sourcePool.Keys) + { + int state; + Al.alGetSourcei(key, Al.AL_SOURCE_STATE, out state); + if (state != Al.AL_PLAYING) + freeSources.Add(key); + } + + if (freeSources.Count == 0) + return -1; + + foreach (int i in freeSources) + sourcePool[i] = false; + + sourcePool[freeSources[0]] = true; + + return freeSources[0]; + } + + public ISoundSource AddSoundSourceFromMemory(byte[] data, int channels, int sampleBits, int sampleRate) + { + return new OpenAlSoundSource(data, channels, sampleBits, sampleRate); + } + + public ISound Play2D(ISoundSource sound, bool loop) + { + int source = GetSourceFromPool(); + return new OpenAlSound(source, (sound as OpenAlSoundSource).buffer, loop); + } + + public float Volume + { + get { return volume; } + set { Al.alListenerf(Al.AL_GAIN, volume = value); } + } + } + + class OpenAlSoundSource : ISoundSource + { + public readonly int buffer; + + static int MakeALFormat(int channels, int bits) + { + if (channels == 1) + return bits == 16 ? Al.AL_FORMAT_MONO16 : Al.AL_FORMAT_MONO8; + else + return bits == 16 ? Al.AL_FORMAT_STEREO16 : Al.AL_FORMAT_STEREO8; + } + + public OpenAlSoundSource(byte[] data, int channels, int sampleBits, int sampleRate) + { + Al.alGenBuffers(1, out buffer); + Al.alBufferData(buffer, MakeALFormat(channels, sampleBits), data, data.Length, sampleRate); + } + } + + class OpenAlSound : ISound + { + public readonly int source = -1; + float volume = 1f; + + public OpenAlSound(int source, int buffer, bool looping) + { + if (source == -1) return; + this.source = source; + Al.alSourcef(source, Al.AL_PITCH, 1f); + Al.alSourcef(source, Al.AL_GAIN, 1f); + Al.alSource3f(source, Al.AL_POSITION, 0f, 0f, 0f); + Al.alSource3f(source, Al.AL_VELOCITY, 0f, 0f, 0f); + Al.alSourcei(source, Al.AL_BUFFER, buffer); + Al.alSourcei(source, Al.AL_LOOPING, looping ? Al.AL_TRUE : Al.AL_FALSE); + Al.alSourcePlay(source); + } + + public float Volume + { + get { return volume; } + set + { + if (source != -1) + Al.alSourcef(source, Al.AL_GAIN, volume = value); + } + } + } +} diff --git a/OpenRA.Game/Support/PerfHistory.cs b/OpenRA.Game/Support/PerfHistory.cs new file mode 100644 index 0000000000..0eb8e86587 --- /dev/null +++ b/OpenRA.Game/Support/PerfHistory.cs @@ -0,0 +1,141 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; + +namespace OpenRA.Support +{ + static class PerfHistory + { + static readonly Color[] colors = { Color.Red, Color.Green, Color.Blue, Color.Yellow, Color.Orange, Color.Fuchsia, Color.Lime, Color.LightBlue, Color.White, Color.Black }; + static int nextColor; + + public static Cache items = new Cache( + s => + { + var x = new PerfItem(s, colors[nextColor++]); + if (nextColor >= colors.Length) nextColor = 0; + return x; + }); + + public static void Increment( string item, double x ) + { + items[item].val += x; + } + + public static void Tick() + { + foreach (var item in items.Values) + if (item.hasNormalTick) + item.Tick(); + } + + public static void Render(Renderer r, LineRenderer lr) + { + float2 origin = Game.viewport.Location + new float2(330, Game.viewport.Height - 30); + float2 basis = new float2(-3, -3); + + lr.DrawLine(origin, origin + new float2(100, 0) * basis, Color.White, Color.White); + lr.DrawLine(origin + new float2(100,0) * basis, origin + new float2(100,70) * basis, Color.White, Color.White); + + foreach (var item in items.Values) + { + int n = 0; + item.Samples().Aggregate((a, b) => + { + lr.DrawLine( + origin + new float2(n, (float)a) * basis, + origin + new float2(n+1, (float)b) * basis, + item.c, item.c); + ++n; + return b; + }); + } + + lr.Flush(); + } + } + + class PerfItem + { + public readonly Color c; + public readonly string Name; + public double[] samples = new double[100]; + public double val = 0.0; + int head = 1, tail = 0; + public bool hasNormalTick = true; + + public PerfItem(string name, Color c) + { + Name = name; + this.c = c; + } + + public void Tick() + { + samples[head++] = val; + if (head == samples.Length) head = 0; + if (head == tail && ++tail == samples.Length) tail = 0; + val = 0.0; + } + + public IEnumerable Samples() + { + int n = head; + while (n != tail) + { + --n; + if (n < 0) n = samples.Length - 1; + yield return samples[n]; + } + } + + public double LastValue + { + get + { + int n = head; + if (--n < 0) n = samples.Length - 1; + return samples[n]; + } + } + } + + class PerfSample : IDisposable + { + readonly Stopwatch sw = new Stopwatch(); + readonly string Item; + + public PerfSample(string item) + { + Item = item; + } + + public void Dispose() + { + PerfHistory.Increment(Item, sw.ElapsedTime() * 1000); + } + } +} diff --git a/OpenRA.Game/Support/Program.cs b/OpenRA.Game/Support/Program.cs new file mode 100644 index 0000000000..d8e31513e5 --- /dev/null +++ b/OpenRA.Game/Support/Program.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Diagnostics; + +namespace OpenRA +{ + static class Program + { + [STAThread] + static void Main( string[] args ) + { + if( Debugger.IsAttached ) + { + Run( args ); + return; + } + + try + { + Run( args ); + } + catch( Exception e ) + { + Log.Write( "{0}", e.ToString() ); + throw; + } + } + + static void Run( string[] args ) + { + Game.PreInit( new Settings( args ) ); + Game.Run(); + } + } +} \ No newline at end of file diff --git a/OpenRA.Game/Support/Settings.cs b/OpenRA.Game/Support/Settings.cs new file mode 100644 index 0000000000..92c8019390 --- /dev/null +++ b/OpenRA.Game/Support/Settings.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace OpenRA +{ + public class Settings + { + Dictionary settings = new Dictionary(); + + public Settings(IEnumerable src) + { + Regex regex = new Regex("([^=]+)=(.*)"); + foreach (string s in src) + { + Match m = regex.Match(s); + if (m == null || !m.Success) + continue; + + settings.Add(m.Groups[1].Value, m.Groups[2].Value); + } + } + + public bool Contains(string key) { return settings.ContainsKey(key); } + + public string GetValue(string key, string defaultValue) { return Contains(key) ? settings[key] : defaultValue; } + + public int GetValue(string key, int defaultValue) + { + int result; + + if (!int.TryParse(GetValue(key, defaultValue.ToString()), out result)) + result = defaultValue; + + return result; + } + + public bool GetValue(string key, bool defaultValue) + { + bool result; + + if (!bool.TryParse(GetValue(key, defaultValue.ToString()), out result)) + result = defaultValue; + + return result; + } + } +} diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs new file mode 100755 index 0000000000..1914d2ded5 --- /dev/null +++ b/OpenRA.Game/Sync.cs @@ -0,0 +1,125 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using OpenRA.FileFormats; + +namespace OpenRA +{ + public class SyncAttribute : Attribute { } + + static class Sync + { + static Cache> hashFuncCache = new Cache>( t => GenerateHashFunc( t ) ); + + public static int CalculateSyncHash( object obj ) + { + return hashFuncCache[ obj.GetType() ]( obj ); + } + + public static Func GenerateHashFunc( Type t ) + { + var d = new DynamicMethod( "hash_{0}".F( t.Name ), typeof( int ), new Type[] { typeof( object ) }, t ); + var il = d.GetILGenerator(); + var this_ = il.DeclareLocal( t ).LocalIndex; + il.Emit( OpCodes.Ldarg_0 ); + il.Emit( OpCodes.Castclass, t ); + il.Emit( OpCodes.Stloc, this_ ); + il.Emit( OpCodes.Ldc_I4_0 ); + + const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + foreach( var field in t.GetFields( bf ).Where( x => x.GetCustomAttributes( typeof( SyncAttribute ), true ).Length != 0 ) ) + { + il.Emit( OpCodes.Ldloc, this_ ); + il.Emit( OpCodes.Ldfld, field ); + + if( field.FieldType == typeof( int ) ) + { + il.Emit( OpCodes.Xor ); + } + else if( field.FieldType == typeof( bool ) ) + { + var l = il.DefineLabel(); + il.Emit( OpCodes.Ldc_I4, 0xaaa ); + il.Emit( OpCodes.Brtrue, l ); + il.Emit( OpCodes.Pop ); + il.Emit( OpCodes.Ldc_I4, 0x555 ); + il.MarkLabel( l ); + il.Emit( OpCodes.Xor ); + } + else if( field.FieldType == typeof( int2 ) ) + { + il.EmitCall( OpCodes.Call, ( (Func)hash_int2 ).Method, null ); + il.Emit( OpCodes.Xor ); + } + else if( field.FieldType == typeof( TypeDictionary ) ) + { + il.EmitCall( OpCodes.Call, ( (Func)hash_tdict ).Method, null ); + il.Emit( OpCodes.Xor ); + } + else if( field.FieldType == typeof( Actor ) ) + { + il.EmitCall( OpCodes.Call, ( (Func)hash_actor ).Method, null ); + il.Emit( OpCodes.Xor ); + } + else if( field.FieldType == typeof( Player ) ) + { + il.EmitCall( OpCodes.Call, ( (Func)hash_player ).Method, null ); + il.Emit( OpCodes.Xor ); + } + else + throw new NotImplementedException( "SyncAttribute on unhashable field" ); + } + + il.Emit( OpCodes.Ret ); + return (Func)d.CreateDelegate( typeof( Func ) ); + } + + internal static int hash_int2( int2 i2 ) + { + return ( ( i2.X * 5 ) ^ ( i2.Y * 3 ) ) / 4; + } + + internal static int hash_tdict( TypeDictionary d ) + { + int ret = 0; + foreach( var o in d ) + ret += CalculateSyncHash( o ); + return ret; + } + + internal static int hash_actor( Actor a ) + { + if( a != null ) + return (int)( a.ActorID << 16 ); + return 0; + } + + internal static int hash_player( Player p ) + { + if( p != null ) + return p.Index * 0x567; + return 0; + } + } +} diff --git a/OpenRA.Game/TerrainCosts.cs b/OpenRA.Game/TerrainCosts.cs new file mode 100644 index 0000000000..d325cfc06f --- /dev/null +++ b/OpenRA.Game/TerrainCosts.cs @@ -0,0 +1,73 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA +{ + public enum UnitMovementType : byte + { + Foot = 0, + Track = 1, + Wheel = 2, + Float = 3, + Fly = 4, + } + + enum TerrainMovementType : byte + { + Clear = 0, + Water = 1, + Road = 2, + Rock = 3, + //Tree = 4, + River = 5, + Rough = 6, + Wall = 7, + Beach = 8, + Ore = 9, + Special = 10, + } + + static class TerrainCosts + { + static float[][] costs = Util.MakeArray(4, + a => Util.MakeArray(11, b => float.PositiveInfinity)); + + static TerrainCosts() + { + for( int i = 0 ; i < 11 ; i++ ) + { + if( i == 4 ) continue; + var section = Rules.AllRules.GetSection( ( (TerrainMovementType)i ).ToString() ); + for( int j = 0 ; j < 4 ; j++ ) + { + string val = section.GetValue( ( (UnitMovementType)j ).ToString(), "0%" ); + costs[j][i] = 100f / float.Parse(val.Substring(0, val.Length - 1)); + } + } + } + + public static float Cost( UnitMovementType unitMovementType, int r ) + { + return costs[ (byte)unitMovementType ][ r ]; + } + } +} diff --git a/OpenRA.Game/Traits/AI/AutoHeal.cs b/OpenRA.Game/Traits/AI/AutoHeal.cs new file mode 100644 index 0000000000..34e035e87a --- /dev/null +++ b/OpenRA.Game/Traits/AI/AutoHeal.cs @@ -0,0 +1,75 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class AutoHealInfo : StatelessTraitInfo { } + + class AutoHeal : ITick + { + void AttackTarget(Actor self, Actor target) + { + var attack = self.traits.Get(); + if (target != null) + attack.ResolveOrder(self, new Order("Attack", self, target)); + else + if (self.GetCurrentActivity() is Attack) + self.CancelActivity(); + } + + bool NeedsNewTarget(Actor self) + { + var attack = self.traits.Get(); + var range = Util.GetMaximumRange(self); + + if (attack.target == null) + return true; // he's dead. + if ((attack.target.Location - self.Location).LengthSquared > range * range + 2) + return true; // wandered off faster than we could follow + if (attack.target.Health == attack.target.Info.Traits.Get().HP) + return true; // fully healed + + return false; + } + + public void Tick(Actor self) + { + var range = Util.GetMaximumRange(self); + + if (NeedsNewTarget(self)) + AttackTarget(self, ChooseTarget(self, range)); + } + + Actor ChooseTarget(Actor self, float range) + { + var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range); + + return inRange + .Where(a => a.Owner == self.Owner && a != self) /* todo: one day deal with friendly players */ + .Where(a => Combat.HasAnyValidWeapons(self, a)) + .Where(a => a.Health < a.Info.Traits.Get().HP) + .OrderBy(a => (a.Location - self.Location).LengthSquared) + .FirstOrDefault(); + } + } +} diff --git a/OpenRA.Game/Traits/AI/AutoTarget.cs b/OpenRA.Game/Traits/AI/AutoTarget.cs new file mode 100644 index 0000000000..e8ada3ea56 --- /dev/null +++ b/OpenRA.Game/Traits/AI/AutoTarget.cs @@ -0,0 +1,77 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + class AutoTargetInfo : StatelessTraitInfo { } + + class AutoTarget : ITick, INotifyDamage + { + void AttackTarget(Actor self, Actor target) + { + var attack = self.traits.Get(); + if (target != null) + attack.ResolveOrder(self, new Order("Attack", self, target)); + } + + public void Tick(Actor self) + { + if (!self.IsIdle) return; + + var attack = self.traits.Get(); + var range = Util.GetMaximumRange(self); + + if (attack.target == null || + (attack.target.Location - self.Location).LengthSquared > range * range + 2) + AttackTarget(self, ChooseTarget(self, range)); + } + + Actor ChooseTarget(Actor self, float range) + { + var inRange = self.World.FindUnitsInCircle(self.CenterLocation, Game.CellSize * range); + + return inRange + .Where(a => a.Owner != null && a.Owner != self.Owner) /* todo: one day deal with friendly players */ + .Where(a => Combat.HasAnyValidWeapons(self, a)) + .OrderBy(a => (a.Location - self.Location).LengthSquared) + .FirstOrDefault(); + } + + public void Damaged(Actor self, AttackInfo e) + { + // not a lot we can do about things we can't hurt... although maybe we should automatically run away? + if (!Combat.HasAnyValidWeapons(self, e.Attacker)) + return; + + if (e.Attacker.Owner == self.Owner) + return; // don't retaliate against own units force-firing on us. it's usually not what the player wanted. + + if (e.Damage < 0) + return; // don't retaliate against healers + + var attack = self.traits.Get(); + if (attack.target != null) return; + + AttackTarget(self, e.Attacker); + } + } +} diff --git a/OpenRA.Game/Traits/AI/SelfHealing.cs b/OpenRA.Game/Traits/AI/SelfHealing.cs new file mode 100644 index 0000000000..fb65a9969b --- /dev/null +++ b/OpenRA.Game/Traits/AI/SelfHealing.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class SelfHealingInfo : ITraitInfo + { + public readonly int Step = 5; + public readonly int Ticks = 5; + public readonly float HealIfBelow = .5f; + + public object Create(Actor self) { return new SelfHealing(); } + } + + class SelfHealing : ITick + { + int ticks; + + + public void Tick(Actor self) + { + var info = self.Info.Traits.Get(); + + if ((float)self.Health / self.GetMaxHP() >= info.HealIfBelow) + return; + + if (--ticks <= 0) + { + ticks = info.Ticks; + self.InflictDamage(self, -info.Step, Rules.WarheadInfo["Super"]); + } + } + } +} diff --git a/OpenRA.Game/Traits/AI/TakeCover.cs b/OpenRA.Game/Traits/AI/TakeCover.cs new file mode 100644 index 0000000000..f906225a3d --- /dev/null +++ b/OpenRA.Game/Traits/AI/TakeCover.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class TakeCoverInfo : ITraitInfo + { + public object Create(Actor self) { return new TakeCover(self); } + } + + // infantry prone behavior + class TakeCover : ITick, INotifyDamage, IDamageModifier, ISpeedModifier + { + const int defaultProneTime = 100; /* ticks, =4s */ + const float proneDamage = .5f; + const float proneSpeed = .5f; + + [Sync] + int remainingProneTime = 0; + + public bool IsProne { get { return remainingProneTime > 0; } } + + public TakeCover(Actor self) {} + + public void Damaged(Actor self, AttackInfo e) + { + if (e.Damage > 0) /* fix to allow healing via `damage` */ + remainingProneTime = defaultProneTime; + } + + public void Tick(Actor self) + { + if (IsProne) + --remainingProneTime; + } + + public float GetDamageModifier() + { + return IsProne ? proneDamage : 1f; + } + + public float GetSpeedModifier() + { + return IsProne ? proneSpeed : 1f; + } + } +} diff --git a/OpenRA.Game/Traits/Activities/Attack.cs b/OpenRA.Game/Traits/Activities/Attack.cs new file mode 100644 index 0000000000..817dc29c67 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Attack.cs @@ -0,0 +1,69 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + /* non-turreted attack */ + class Attack : IActivity + { + Actor Target; + int Range; + + public Attack(Actor target, int range) + { + Target = target; + Range = range; + } + + public IActivity NextActivity { get; set; } + + public IActivity Tick( Actor self ) + { + var unit = self.traits.Get(); + + if (Target == null || Target.IsDead) + return NextActivity; + + if ((Target.Location - self.Location).LengthSquared >= Range * Range) + return new Move( Target, Range ) { NextActivity = this }; + + var desiredFacing = Util.GetFacing((Target.Location - self.Location).ToFloat2(), 0); + var renderUnit = self.traits.GetOrDefault(); + var numDirs = (renderUnit != null) + ? renderUnit.anim.CurrentSequence.Facings : 8; + + if (Util.QuantizeFacing(unit.Facing, numDirs) + != Util.QuantizeFacing(desiredFacing, numDirs)) + { + return new Turn( desiredFacing ) { NextActivity = this }; + } + + var attack = self.traits.Get(); + attack.target = Target; + attack.DoAttack(self); + return this; + } + + public void Cancel(Actor self) + { + Target = null; + } + } +} diff --git a/OpenRA.Game/Traits/Activities/CallFunc.cs b/OpenRA.Game/Traits/Activities/CallFunc.cs new file mode 100644 index 0000000000..51e5ffd5bc --- /dev/null +++ b/OpenRA.Game/Traits/Activities/CallFunc.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + public class CallFunc : IActivity + { + public CallFunc(Action a) { this.a = a; } + + Action a; + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (a != null) a(); + return NextActivity; + } + + public void Cancel(Actor self) { a = null; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/DeliverOre.cs b/OpenRA.Game/Traits/Activities/DeliverOre.cs new file mode 100644 index 0000000000..1656c7125e --- /dev/null +++ b/OpenRA.Game/Traits/Activities/DeliverOre.cs @@ -0,0 +1,91 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + public class DeliverOre : IActivity + { + public IActivity NextActivity { get; set; } + + bool isDocking; + Actor refinery; + + public DeliverOre() { } + + public DeliverOre( Actor refinery ) + { + this.refinery = refinery; + } + + public IActivity Tick( Actor self ) + { + var mobile = self.traits.Get(); + + if( NextActivity != null ) + return NextActivity; + + if( refinery != null && refinery.IsDead ) + refinery = null; + + if( refinery == null || self.Location != refinery.Location + refinery.traits.Get().DeliverOffset ) + { + var search = new PathSearch + { + heuristic = PathSearch.DefaultEstimator( self.Location ), + umt = mobile.GetMovementType(), + checkForBlocked = false, + }; + var refineries = self.World.Queries.OwnedBy[self.Owner] + .Where(x => x.traits.Contains()) + .ToList(); + if( refinery != null ) + search.AddInitialCell(self.World, refinery.Location + refinery.traits.Get().DeliverOffset); + else + foreach( var r in refineries ) + search.AddInitialCell(self.World, r.Location + r.traits.Get().DeliverOffset); + + var path = self.World.PathFinder.FindPath( search ); + path.Reverse(); + if( path.Count != 0 ) + { + refinery = refineries.FirstOrDefault(x => x.Location + x.traits.Get().DeliverOffset == path[0]); + return new Move( () => path ) { NextActivity = this }; + } + else + // no refineries reachable? + return this; + } + else if (!isDocking) + { + isDocking = true; + refinery.traits.Get().OnDock(self, this); + } + + return this; + } + + public void Cancel(Actor self) + { + // TODO: allow canceling of deliver orders? + } + } +} diff --git a/OpenRA.Game/Traits/Activities/EnterTransport.cs b/OpenRA.Game/Traits/Activities/EnterTransport.cs new file mode 100644 index 0000000000..38bf1fd3d4 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/EnterTransport.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + class EnterTransport : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + public Actor transport; + + public EnterTransport(Actor self, Actor transport) + { + this.transport = transport; + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + if (transport == null || !transport.IsInWorld) return NextActivity; + + var cargo = transport.traits.Get(); + if (cargo.IsFull(transport)) + return NextActivity; + + cargo.Load(transport, self); + self.World.AddFrameEndTask(w => w.Remove(self)); + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Fly.cs b/OpenRA.Game/Traits/Activities/Fly.cs new file mode 100644 index 0000000000..05d2b61c7e --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Fly.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + public class Fly : IActivity + { + readonly float2 Pos; + bool isCanceled; + + public Fly(float2 pos) { Pos = pos; } + public Fly(int2 pos) { Pos = Util.CenterOfCell(pos); } + + public IActivity NextActivity { get; set; } + + const int CruiseAltitude = 20; + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + + var d = Pos - self.CenterLocation; + if (d.LengthSquared < 50) /* close enough */ + return NextActivity; + + var unit = self.traits.Get(); + + if (unit.Altitude < CruiseAltitude) + ++unit.Altitude; + + var desiredFacing = Util.GetFacing(d, unit.Facing); + if (unit.Altitude == CruiseAltitude) + Util.TickFacing(ref unit.Facing, desiredFacing, self.Info.Traits.Get().ROT); + var speed = .2f * Util.GetEffectiveSpeed(self); + var angle = unit.Facing / 128f * Math.PI; + + self.CenterLocation += speed * -float2.FromAngle((float)angle); + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/FlyAttack.cs b/OpenRA.Game/Traits/Activities/FlyAttack.cs new file mode 100644 index 0000000000..4444d7b8d5 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/FlyAttack.cs @@ -0,0 +1,69 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + public class FlyAttack : IActivity + { + public IActivity NextActivity { get; set; } + Actor Target; + + public FlyAttack(Actor target) { Target = target; } + + public IActivity Tick(Actor self) + { + if (Target == null || Target.IsDead) + return NextActivity; + + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo != null && !limitedAmmo.HasAmmo()) + return NextActivity; + + return Util.SequenceActivities( + new Fly(Target.CenterLocation), + new FlyTimed(50, 20), + this); + } + + public void Cancel(Actor self) { Target = null; NextActivity = null; } + } + + public class FlyCircle : IActivity + { + public IActivity NextActivity { get; set; } + int2 Target; + bool isCanceled; + + public FlyCircle(int2 target) { Target = target; } + + public IActivity Tick(Actor self) + { + if (isCanceled) + return NextActivity; + + return Util.SequenceActivities( + new Fly(Util.CenterOfCell(Target)), + new FlyTimed(50, 20), + this); + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/FlyTimed.cs b/OpenRA.Game/Traits/Activities/FlyTimed.cs new file mode 100644 index 0000000000..5eacbb59ad --- /dev/null +++ b/OpenRA.Game/Traits/Activities/FlyTimed.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + class FlyTimed : IActivity + { + public IActivity NextActivity { get; set; } + int remainingTicks; + int targetAltitude; + + public FlyTimed(int ticks, int targetAltitude) { remainingTicks = ticks; this.targetAltitude = targetAltitude; } + + public IActivity Tick(Actor self) + { + if (remainingTicks == 0) + return NextActivity; + + --remainingTicks; + + var unit = self.traits.Get(); + var speed = .2f * Util.GetEffectiveSpeed(self); + var angle = unit.Facing / 128f * Math.PI; + + self.CenterLocation += speed * -float2.FromAngle((float)angle); + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + + unit.Altitude += Math.Sign(targetAltitude - unit.Altitude); + return this; + } + + public void Cancel(Actor self) { remainingTicks = 0; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Follow.cs b/OpenRA.Game/Traits/Activities/Follow.cs new file mode 100644 index 0000000000..ad04dbee8e --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Follow.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + class Follow : IActivity + { + Actor Target; + int Range; + + public Follow(Actor target, int range) + { + Target = target; + Range = range; + } + + public IActivity NextActivity { get; set; } + + public IActivity Tick( Actor self ) + { + if (Target == null || Target.IsDead) + return NextActivity; + + var inRange = ( Target.Location - self.Location ).LengthSquared < Range * Range; + + if( !inRange ) + return new Move( Target, Range ) { NextActivity = this }; + + return this; + } + + public void Cancel(Actor self) + { + Target = null; + } + } +} diff --git a/OpenRA.Game/Traits/Activities/Harvest.cs b/OpenRA.Game/Traits/Activities/Harvest.cs new file mode 100644 index 0000000000..568be61320 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Harvest.cs @@ -0,0 +1,85 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + public class Harvest : IActivity + { + public IActivity NextActivity { get; set; } + bool isHarvesting = false; + + public IActivity Tick( Actor self ) + { + if( isHarvesting ) return this; + if( NextActivity != null ) return NextActivity; + + var harv = self.traits.Get(); + + if( harv.IsFull ) + return new DeliverOre { NextActivity = NextActivity }; + + if (HarvestThisTile(self)) + return this; + else + { + FindMoreOre(self); + return NextActivity; + } + } + + bool HarvestThisTile(Actor self) + { + var harv = self.traits.Get(); + var renderUnit = self.traits.Get(); /* better have one of these! */ + + var isGem = false; + if (!self.World.Map.ContainsResource(self.Location) || + !self.World.Map.Harvest(self.Location, out isGem)) + return false; + + if (renderUnit.anim.CurrentSequence.Name != "harvest") + { + isHarvesting = true; + renderUnit.PlayCustomAnimation(self, "harvest", () => isHarvesting = false); + } + harv.AcceptResource(isGem); + return true; + } + + void FindMoreOre(Actor self) + { + self.QueueActivity(new Move( + () => + { + var search = new PathSearch + { + heuristic = loc => (self.World.Map.ContainsResource(loc) ? 0 : 1), + umt = UnitMovementType.Wheel, + checkForBlocked = true + }; + search.AddInitialCell(self.World, self.Location); + return self.World.PathFinder.FindPath(search); + })); + self.QueueActivity(new Harvest()); + } + + public void Cancel(Actor self) { } + } +} diff --git a/OpenRA.Game/Traits/Activities/HeliAttack.cs b/OpenRA.Game/Traits/Activities/HeliAttack.cs new file mode 100644 index 0000000000..c7eb9618db --- /dev/null +++ b/OpenRA.Game/Traits/Activities/HeliAttack.cs @@ -0,0 +1,69 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + class HeliAttack : IActivity + { + Actor target; + const int CruiseAltitude = 20; + public HeliAttack( Actor target ) { this.target = target; } + + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (target == null || target.IsDead) + return NextActivity; + + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo != null && !limitedAmmo.HasAmmo()) + return NextActivity; + + var unit = self.traits.Get(); + + if (unit.Altitude != CruiseAltitude) + { + unit.Altitude += Math.Sign(CruiseAltitude - unit.Altitude); + return this; + } + + var range = self.GetPrimaryWeapon().Range - 1; + var dist = target.CenterLocation - self.CenterLocation; + + var desiredFacing = Util.GetFacing(dist, unit.Facing); + Util.TickFacing(ref unit.Facing, desiredFacing, self.Info.Traits.Get().ROT); + + if (!float2.WithinEpsilon(float2.Zero, dist, range * Game.CellSize)) + { + var rawSpeed = .2f * Util.GetEffectiveSpeed(self); + self.CenterLocation += (rawSpeed / dist.Length) * dist; + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + } + + /* todo: maintain seperation wrt other helis */ + return this; + } + + public void Cancel(Actor self) { target = null; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/HeliFly.cs b/OpenRA.Game/Traits/Activities/HeliFly.cs new file mode 100644 index 0000000000..7e30bbf0ce --- /dev/null +++ b/OpenRA.Game/Traits/Activities/HeliFly.cs @@ -0,0 +1,71 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + class HeliFly : IActivity + { + const int CruiseAltitude = 20; + readonly float2 Dest; + public HeliFly(float2 dest) + { + Dest = dest; + } + + public IActivity NextActivity { get; set; } + bool isCanceled; + + public IActivity Tick(Actor self) + { + if (isCanceled) + return NextActivity; + + var unit = self.traits.Get(); + + if (unit.Altitude != CruiseAltitude) + { + unit.Altitude += Math.Sign(CruiseAltitude - unit.Altitude); + return this; + } + + var dist = Dest - self.CenterLocation; + if (float2.WithinEpsilon(float2.Zero, dist, 2)) + { + self.CenterLocation = Dest; + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + return NextActivity; + } + + var desiredFacing = Util.GetFacing(dist, unit.Facing); + Util.TickFacing(ref unit.Facing, desiredFacing, + self.Info.Traits.Get().ROT); + + var rawSpeed = .2f * Util.GetEffectiveSpeed(self); + self.CenterLocation += (rawSpeed / dist.Length) * dist; + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/HeliLand.cs b/OpenRA.Game/Traits/Activities/HeliLand.cs new file mode 100644 index 0000000000..2f5027ee2a --- /dev/null +++ b/OpenRA.Game/Traits/Activities/HeliLand.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + class HeliLand : IActivity + { + public HeliLand(bool requireSpace) { this.requireSpace = requireSpace; } + + bool requireSpace; + bool isCanceled; + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + var unit = self.traits.Get(); + if (unit.Altitude == 0) + return NextActivity; + + if (requireSpace && !self.World.IsCellBuildable(self.Location, UnitMovementType.Foot)) + return this; // fail to land if no space + + --unit.Altitude; + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/HeliReturn.cs b/OpenRA.Game/Traits/Activities/HeliReturn.cs new file mode 100644 index 0000000000..629c230dfb --- /dev/null +++ b/OpenRA.Game/Traits/Activities/HeliReturn.cs @@ -0,0 +1,68 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + class HeliReturn : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + + static Actor ChooseHelipad(Actor self) + { + return self.World.Queries.OwnedBy[self.Owner].FirstOrDefault( + a => a.Info.Name == "hpad" && + !Reservable.IsReserved(a)); + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + var dest = ChooseHelipad(self); + + var initialFacing = self.Info.Traits.Get().InitialFacing; + + if (dest == null) + return Util.SequenceActivities( + new Turn(initialFacing), + new HeliLand(true), + NextActivity); + + var res = dest.traits.GetOrDefault(); + if (res != null) + self.traits.Get().reservation = res.Reserve(self); + + var pi = dest.Info.Traits.GetOrDefault(); + var offset = pi != null ? pi.SpawnOffset : null; + var offsetVec = offset != null ? new float2(offset[0], offset[1]) : float2.Zero; + + return Util.SequenceActivities( + new HeliFly(dest.CenterLocation + offsetVec), + new Turn(initialFacing), + new HeliLand(false), + new Rearm(), + NextActivity); + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Idle.cs b/OpenRA.Game/Traits/Activities/Idle.cs new file mode 100644 index 0000000000..5d287ed7cd --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Idle.cs @@ -0,0 +1,30 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + class Idle : IActivity + { + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) { return NextActivity; } + public void Cancel(Actor self) {} + } +} diff --git a/OpenRA.Game/Traits/Activities/Land.cs b/OpenRA.Game/Traits/Activities/Land.cs new file mode 100644 index 0000000000..77e411b8d3 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Land.cs @@ -0,0 +1,68 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + public class Land : IActivity + { + readonly float2 Pos; + bool isCanceled; + Actor Structure; + + public Land(float2 pos) { Pos = pos; } + public Land(Actor structure) { Structure = structure; Pos = Structure.CenterLocation; } + + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (Structure != null && Structure.IsDead) + { + Structure = null; + isCanceled = true; + } + + if (isCanceled) return NextActivity; + + var d = Pos - self.CenterLocation; + if (d.LengthSquared < 50) /* close enough */ + return NextActivity; + + var unit = self.traits.Get(); + + if (unit.Altitude > 0) + --unit.Altitude; + + var desiredFacing = Util.GetFacing(d, unit.Facing); + Util.TickFacing(ref unit.Facing, desiredFacing, self.Info.Traits.Get().ROT); + var speed = .2f * Util.GetEffectiveSpeed(self); + var angle = unit.Facing / 128f * Math.PI; + + self.CenterLocation += speed * -float2.FromAngle((float)angle); + self.Location = ((1 / 24f) * self.CenterLocation).ToInt2(); + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Move.cs b/OpenRA.Game/Traits/Activities/Move.cs new file mode 100755 index 0000000000..20ab3ddefc --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Move.cs @@ -0,0 +1,286 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + public class Move : IActivity + { + public IActivity NextActivity { get; set; } + + int2? destination; + int nearEnough; + public List path; + Func> getPath; + public Actor ignoreBuilding; + + MovePart move; + + public Move( int2 destination, int nearEnough ) + { + this.getPath = ( self, mobile ) => self.World.PathFinder.FindUnitPath( + self.Location, destination, + mobile.GetMovementType() ); + this.destination = destination; + this.nearEnough = nearEnough; + } + + public Move(int2 destination, Actor ignoreBuilding) + { + this.getPath = (self, mobile) => + self.World.PathFinder.FindPath( + PathSearch.FromPoint( self.World, self.Location, destination, mobile.GetMovementType(), false ) + .WithCustomBlocker( self.World.PathFinder.AvoidUnitsNear( self.Location, 4 )).WithIgnoredBuilding( ignoreBuilding )); + + this.destination = destination; + this.nearEnough = 0; + this.ignoreBuilding = ignoreBuilding; + } + + public Move( Actor target, int range ) + { + this.getPath = ( self, mobile ) => self.World.PathFinder.FindUnitPathToRange( + self.Location, target.Location, + mobile.GetMovementType(), range ); + this.destination = null; + this.nearEnough = range; + } + + public Move(Func> getPath) + { + this.getPath = (_, _2) => getPath(); + this.destination = null; + this.nearEnough = 0; + } + + bool CanEnterCell( int2 c, Actor self ) + { + if (!self.World.WorldActor.traits.Get().CanMoveHere(c) + && self.World.WorldActor.traits.Get().GetBuildingAt(c) != ignoreBuilding) + return false; + + // Cannot enter a cell if any unit inside is uncrushable + // This will need to be updated for multiple-infantry-in-a-cell + return (!self.World.WorldActor.traits.Get().GetUnitsAt(c).Any(a => a != self && !self.World.IsActorCrushableByActor(a, self))); + } + + public IActivity Tick( Actor self ) + { + var unit = self.traits.Get(); + var mobile = self.traits.Get(); + + if( move != null ) + { + move.TickMove( self, mobile, this ); + return this; + } + + if( destination == self.Location ) + return NextActivity; + + if( path == null ) + { + path = getPath( self, mobile ).TakeWhile( a => a != self.Location ).ToList(); + SanityCheckPath( mobile ); + } + + if( path.Count == 0 ) + { + destination = mobile.toCell; + return this; + } + + destination = path[ 0 ]; + + var nextCell = PopPath( self, mobile ); + if( nextCell == null ) + return this; + + int2 dir = nextCell.Value - mobile.fromCell; + var firstFacing = Util.GetFacing( dir, unit.Facing ); + if( firstFacing != unit.Facing ) + { + path.Add( nextCell.Value ); + + return new Turn( firstFacing ) { NextActivity = this }; + } + else + { + mobile.toCell = nextCell.Value; + move = new MoveFirstHalf( + Util.CenterOfCell( mobile.fromCell ), + Util.BetweenCells( mobile.fromCell, mobile.toCell ), + unit.Facing, + unit.Facing, + 0 ); + + move.TickMove( self, mobile, this ); + + return this; + } + } + + [Conditional( "SANITY_CHECKS")] + void SanityCheckPath( Mobile mobile ) + { + if( path.Count == 0 ) + return; + var d = path[path.Count-1] - mobile.toCell; + if( d.LengthSquared > 2 ) + throw new InvalidOperationException( "(Move) Sanity check failed" ); + } + + int2? PopPath( Actor self, Mobile mobile ) + { + if( path.Count == 0 ) return null; + var nextCell = path[ path.Count - 1 ]; + if( !CanEnterCell( nextCell, self ) ) + { + if( ( mobile.toCell - destination.Value ).LengthSquared <= nearEnough ) + { + path.Clear(); + return null; + } + + self.World.WorldActor.traits.Get().Remove( self, mobile ); + var newPath = getPath(self, mobile).TakeWhile(a => a != self.Location).ToList(); + + self.World.WorldActor.traits.Get().Add( self, mobile ); + if (newPath.Count != 0) + path = newPath; + + return null; + } + path.RemoveAt( path.Count - 1 ); + return nextCell; + } + + public void Cancel( Actor self ) + { + path = new List(); + NextActivity = null; + } + + abstract class MovePart + { + public readonly float2 from, to; + public readonly int fromFacing, toFacing; + public int moveFraction; + public readonly int moveFractionTotal; + + public MovePart( float2 from, float2 to, int fromFacing, int toFacing, int startingFraction ) + { + this.from = from; + this.to = to; + this.fromFacing = fromFacing; + this.toFacing = toFacing; + this.moveFraction = startingFraction; + this.moveFractionTotal = (int)( to - from ).Length * ( 25 / 6 ); + } + + public void TickMove( Actor self, Mobile mobile, Move parent ) + { + moveFraction += (int)Util.GetEffectiveSpeed(self); + if( moveFraction >= moveFractionTotal ) + moveFraction = moveFractionTotal; + UpdateCenterLocation( self, mobile ); + if( moveFraction >= moveFractionTotal ) + { + parent.move = OnComplete( self, mobile, parent ); + if( parent.move == null ) + UpdateCenterLocation( self, mobile ); + } + } + + void UpdateCenterLocation( Actor self, Mobile mobile ) + { + var unit = self.traits.Get(); + var frac = (float)moveFraction / moveFractionTotal; + + self.CenterLocation = float2.Lerp( from, to, frac ); + if( moveFraction >= moveFractionTotal ) + unit.Facing = toFacing & 0xFF; + else + unit.Facing = ( fromFacing + ( toFacing - fromFacing ) * moveFraction / moveFractionTotal ) & 0xFF; + } + + protected abstract MovePart OnComplete( Actor self, Mobile mobile, Move parent ); + } + + class MoveFirstHalf : MovePart + { + public MoveFirstHalf( float2 from, float2 to, int fromFacing, int toFacing, int startingFraction ) + : base( from, to, fromFacing, toFacing, startingFraction ) + { + } + + protected override MovePart OnComplete( Actor self, Mobile mobile, Move parent ) + { + var unit = self.traits.Get(); + + var nextCell = parent.PopPath( self, mobile ); + if( nextCell != null ) + { + if( ( nextCell - mobile.toCell ) != ( mobile.toCell - mobile.fromCell ) ) + { + var ret = new MoveFirstHalf( + Util.BetweenCells( mobile.fromCell, mobile.toCell ), + Util.BetweenCells( mobile.toCell, nextCell.Value ), + unit.Facing, + Util.GetNearestFacing( unit.Facing, Util.GetFacing( nextCell.Value - mobile.toCell, unit.Facing ) ), + moveFraction - moveFractionTotal ); + mobile.fromCell = mobile.toCell; + mobile.toCell = nextCell.Value; + return ret; + } + else + parent.path.Add( nextCell.Value ); + } + var ret2 = new MoveSecondHalf( + Util.BetweenCells( mobile.fromCell, mobile.toCell ), + Util.CenterOfCell( mobile.toCell ), + unit.Facing, + unit.Facing, + moveFraction - moveFractionTotal ); + mobile.fromCell = mobile.toCell; + return ret2; + } + } + + class MoveSecondHalf : MovePart + { + public MoveSecondHalf( float2 from, float2 to, int fromFacing, int toFacing, int startingFraction ) + : base( from, to, fromFacing, toFacing, startingFraction ) + { + } + + protected override MovePart OnComplete( Actor self, Mobile mobile, Move parent ) + { + self.CenterLocation = Util.CenterOfCell( mobile.toCell ); + mobile.fromCell = mobile.toCell; + return null; + } + } + } +} diff --git a/OpenRA.Game/Traits/Activities/Rearm.cs b/OpenRA.Game/Traits/Activities/Rearm.cs new file mode 100644 index 0000000000..36494215ba --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Rearm.cs @@ -0,0 +1,57 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + public class Rearm : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + int remainingTicks = ticksPerPip; + + const int ticksPerPip = 25 * 2; + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo == null) return NextActivity; + + if (--remainingTicks == 0) + { + if (!limitedAmmo.GiveAmmo()) return NextActivity; + + var hostBuilding = self.World.FindUnits(self.CenterLocation, self.CenterLocation) + .FirstOrDefault(a => a.traits.Contains()); + + if (hostBuilding != null) + hostBuilding.traits.Get().PlayCustomAnim(hostBuilding, "active"); + + remainingTicks = ticksPerPip; + } + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/RemoveSelf.cs b/OpenRA.Game/Traits/Activities/RemoveSelf.cs new file mode 100644 index 0000000000..fffbbe5f41 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/RemoveSelf.cs @@ -0,0 +1,37 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + public class RemoveSelf : IActivity + { + bool isCanceled; + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + self.World.AddFrameEndTask(w => w.Remove(self)); + return null; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Repair.cs b/OpenRA.Game/Traits/Activities/Repair.cs new file mode 100644 index 0000000000..ec36aff0aa --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Repair.cs @@ -0,0 +1,72 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + public class Repair : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + int remainingTicks; + + public Repair(bool playHostAnim) {} + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + if (remainingTicks == 0) + { + var unitCost = self.Info.Traits.Get().Cost; + var hp = self.Info.Traits.Get().HP; + + var costPerHp = (Rules.General.URepairPercent * unitCost) / hp; + var hpToRepair = Math.Min(Rules.General.URepairStep, hp - self.Health); + var cost = (int)Math.Ceiling(costPerHp * hpToRepair); + if (!self.Owner.TakeCash(cost)) + { + remainingTicks = 1; + return this; + } + + self.InflictDamage(self, -hpToRepair, Rules.WarheadInfo["Super"]); + if (self.Health == hp) + return NextActivity; + + var hostBuilding = self.World.FindUnits(self.CenterLocation, self.CenterLocation) + .FirstOrDefault(a => a.traits.Contains()); + + if (hostBuilding != null) + hostBuilding.traits.Get() + .PlayCustomAnim(hostBuilding, "active"); + + remainingTicks = (int)(Rules.General.RepairRate * 60 * 25); + } + else + --remainingTicks; + + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/ReturnToBase.cs b/OpenRA.Game/Traits/Activities/ReturnToBase.cs new file mode 100644 index 0000000000..6d5befb87e --- /dev/null +++ b/OpenRA.Game/Traits/Activities/ReturnToBase.cs @@ -0,0 +1,112 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + class ReturnToBase : IActivity + { + public IActivity NextActivity { get; set; } + + bool isCanceled; + bool isCalculated; + Actor dest; + + float2 w1, w2, w3; /* tangent points to turn circles */ + float2 landPoint; + + Actor ChooseAirfield(Actor self) + { + var airfield = self.World.Queries.OwnedBy[self.Owner] + .Where(a => a.Info.Name == "afld" + && !Reservable.IsReserved(a)) + .FirstOrDefault(); + + if (airfield == null) + throw new NotImplementedException("nowhere to land; what to do?"); + + return airfield; + } + + void Calculate(Actor self) + { + if (dest == null) dest = ChooseAirfield(self); + var res = dest.traits.GetOrDefault(); + if (res != null) + self.traits.Get().reservation = res.Reserve(self); + + var landPos = dest.CenterLocation; + var unit = self.traits.Get(); + var speed = .2f * Util.GetEffectiveSpeed(self); + var approachStart = landPos - new float2(unit.Altitude * speed, 0); + var turnRadius = (128f / self.Info.Traits.Get().ROT) * speed / (float)Math.PI; + + /* work out the center points */ + var fwd = -float2.FromAngle(unit.Facing / 128f * (float)Math.PI); + var side = new float2(-fwd.Y, fwd.X); /* rotate */ + var sideTowardBase = new[] { side, -side } + .OrderBy(a => float2.Dot(a, self.CenterLocation - approachStart)) + .First(); + + var c1 = self.CenterLocation + turnRadius * sideTowardBase; + var c2 = approachStart + new float2(0, + turnRadius * Math.Sign(self.CenterLocation.Y - approachStart.Y)); // above or below start point + + /* work out tangent points */ + var d = c2 - c1; + var e = (turnRadius / d.Length) * d; + var f = new float2(-e.Y, e.X); /* rotate */ + + /* todo: support internal tangents, too! */ + + if (f.X > 0) f = -f; + + w1 = c1 + f; + w2 = c2 + f; + w3 = approachStart; + landPoint = landPos; + + isCalculated = true; + } + + public ReturnToBase(Actor self, Actor dest) + { + this.dest = dest; + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + if (!isCalculated) + Calculate(self); + + return Util.SequenceActivities( + new Fly(w1), + new Fly(w2), + new Fly(w3), + new Land(landPoint), + NextActivity); + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Sell.cs b/OpenRA.Game/Traits/Activities/Sell.cs new file mode 100644 index 0000000000..159074ce19 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Sell.cs @@ -0,0 +1,68 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + class Sell : IActivity + { + public IActivity NextActivity { get; set; } + + bool started; + + int framesRemaining; + + void DoSell(Actor self) + { + var csv = self.Info.Traits.GetOrDefault(); + var cost = csv != null ? csv.Value : self.Info.Traits.Get().Cost; + var hp = self.Info.Traits.Get().HP; + var refund = Rules.General.RefundPercent * self.Health * cost / hp; + + self.Owner.GiveCash((int)refund); + self.Health = 0; + foreach (var ns in self.traits.WithInterface()) + ns.Sold(self); + self.World.AddFrameEndTask( _ => self.World.Remove( self ) ); + + // todo: give dudes + } + + public IActivity Tick(Actor self) + { + if( !started ) + { + framesRemaining = (self.Info.Traits.Get().HasMakeAnimation) ? self.traits.Get().anim.GetSequence( "make" ).Length : 0; + foreach( var ns in self.traits.WithInterface() ) + ns.Selling( self ); + + started = true; + } + else if( framesRemaining <= 0 ) + DoSell( self ); + + else + --framesRemaining; + + return this; + } + + public void Cancel(Actor self) { /* never gonna give you up.. */ } + } +} diff --git a/OpenRA.Game/Traits/Activities/Teleport.cs b/OpenRA.Game/Traits/Activities/Teleport.cs new file mode 100644 index 0000000000..b9be2715dd --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Teleport.cs @@ -0,0 +1,43 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + public class Teleport : IActivity + { + public IActivity NextActivity { get; set; } + + int2 destination; + + public Teleport(int2 destination) + { + this.destination = destination; + } + + public IActivity Tick(Actor self) + { + var mobile = self.traits.Get(); + mobile.TeleportTo(self, destination); + return NextActivity; + } + + public void Cancel(Actor self) { } + } +} diff --git a/OpenRA.Game/Traits/Activities/TransformIntoActor.cs b/OpenRA.Game/Traits/Activities/TransformIntoActor.cs new file mode 100644 index 0000000000..5f9b193a4a --- /dev/null +++ b/OpenRA.Game/Traits/Activities/TransformIntoActor.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits.Activities +{ + class TransformIntoActor : IActivity + { + string actor = null; + int2 offset; + string[] sounds = null; + bool transferPercentage; + + bool isCanceled; + + public TransformIntoActor(string actor, int2 offset, bool transferHealthPercentage, string[] sounds) + { + this.actor = actor; + this.offset = offset; + this.sounds = sounds; + this.transferPercentage = transferHealthPercentage; + } + + public IActivity NextActivity { get; set; } + + public IActivity Tick( Actor self ) + { + if (isCanceled) return NextActivity; + + self.World.AddFrameEndTask( _ => + { + var oldHP = self.GetMaxHP(); + var newHP = Rules.Info[actor].Traits.Get().HP; + var newHealth = (transferPercentage) ? (int)((float)self.Health/oldHP*newHP) : Math.Min(self.Health, newHP); + + self.Health = 0; + self.World.Remove( self ); + foreach (var s in sounds) + Sound.PlayToPlayer(self.Owner, s); + + var a = self.World.CreateActor( actor, self.Location + offset, self.Owner ); + a.Health = newHealth; + } ); + return this; + } + + public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } + } +} diff --git a/OpenRA.Game/Traits/Activities/Turn.cs b/OpenRA.Game/Traits/Activities/Turn.cs new file mode 100755 index 0000000000..4eac57a8c3 --- /dev/null +++ b/OpenRA.Game/Traits/Activities/Turn.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits.Activities +{ + public class Turn : IActivity + { + public IActivity NextActivity { get; set; } + + int desiredFacing; + + public Turn( int desiredFacing ) + { + this.desiredFacing = desiredFacing; + } + + public IActivity Tick( Actor self ) + { + var unit = self.traits.Get(); + + if( desiredFacing == unit.Facing ) + return NextActivity; + + Util.TickFacing( ref unit.Facing, desiredFacing, self.Info.Traits.Get().ROT ); + return this; + } + + public void Cancel( Actor self ) + { + var unit = self.traits.Get(); + + desiredFacing = unit.Facing; + NextActivity = null; + } + } +} diff --git a/OpenRA.Game/Traits/Activities/UndeployMcv.cs b/OpenRA.Game/Traits/Activities/UndeployMcv.cs new file mode 100644 index 0000000000..6e4255007f --- /dev/null +++ b/OpenRA.Game/Traits/Activities/UndeployMcv.cs @@ -0,0 +1,65 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Traits; + +namespace OpenRA.Traits.Activities +{ + class UndeployMcv : IActivity + { + public IActivity NextActivity { get; set; } + bool started; + + void DoUndeploy(World w,Actor self) + { + self.Health = 0; + foreach (var ns in self.traits.WithInterface()) + ns.Sold(self); + w.Remove(self); + + var mcv = w.CreateActor("mcv", self.Location + new int2(1, 1), self.Owner); + mcv.traits.Get().Facing = 96; + } + + public IActivity Tick(Actor self) + { + if (!started) + { + var rb = self.traits.Get(); + rb.PlayCustomAnimBackwards(self, "make", + () => self.World.AddFrameEndTask(w => DoUndeploy(w,self))); + + foreach (var s in self.Info.Traits.Get().SellSounds) + Sound.PlayToPlayer(self.Owner, s); + + started = true; + } + + return this; + } + + public void Cancel(Actor self) + { + // Cancel can't happen between this being moved to the head of the list, and it being Ticked. + throw new InvalidOperationException("UndeployMcvAction: Cancel() should never occur."); + } + } +} diff --git a/OpenRA.Game/Traits/Activities/UnloadCargo.cs b/OpenRA.Game/Traits/Activities/UnloadCargo.cs new file mode 100644 index 0000000000..f023ecf9eb --- /dev/null +++ b/OpenRA.Game/Traits/Activities/UnloadCargo.cs @@ -0,0 +1,87 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits.Activities +{ + public class UnloadCargo : IActivity + { + public IActivity NextActivity { get; set; } + bool isCanceled; + + int2? ChooseExitTile(Actor self) + { + // is anyone still hogging this tile? + if (self.World.WorldActor.traits.Get().GetUnitsAt(self.Location).Count() > 1) + return null; + + for (var i = -1; i < 2; i++) + for (var j = -1; j < 2; j++) + if ((i != 0 || j != 0) && + self.World.IsCellBuildable(self.Location + new int2(i, j), + UnitMovementType.Foot)) + return self.Location + new int2(i, j); + + return null; + } + + public IActivity Tick(Actor self) + { + if (isCanceled) return NextActivity; + + // if we're a thing that can turn, turn to the + // right facing for the unload animation + var unit = self.traits.GetOrDefault(); + var unloadFacing = self.Info.Traits.Get().UnloadFacing; + if (unit != null && unit.Facing != unloadFacing) + return new Turn(unloadFacing) { NextActivity = this }; + + // todo: handle the BS of open/close sequences, which are inconsistent, + // for reasons that probably make good sense to the westwood guys. + + var cargo = self.traits.Get(); + if (cargo.IsEmpty(self)) + return NextActivity; + + var ru = self.traits.GetOrDefault(); + if (ru != null) + ru.PlayCustomAnimation(self, "unload", null); + + var exitTile = ChooseExitTile(self); + if (exitTile == null) + return this; + + var actor = cargo.Unload(self); + + self.World.AddFrameEndTask(w => + { + w.Add(actor); + actor.traits.Get().TeleportTo(actor, self.Location); + actor.CancelActivity(); + actor.QueueActivity(new Move(exitTile.Value, 0)); + }); + + return this; + } + + public void Cancel(Actor self) { NextActivity = null; isCanceled = true; } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackBase.cs b/OpenRA.Game/Traits/Attack/AttackBase.cs new file mode 100644 index 0000000000..8208980c8b --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackBase.cs @@ -0,0 +1,254 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Effects; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + public class AttackBaseInfo : ITraitInfo + { + public readonly string PrimaryWeapon = null; + public readonly string SecondaryWeapon = null; + public readonly int Recoil = 0; + public readonly int[] PrimaryLocalOffset = { }; + public readonly int[] SecondaryLocalOffset = { }; + public readonly int[] PrimaryOffset = { 0, 0 }; + public readonly int[] SecondaryOffset = null; + public readonly bool MuzzleFlash = false; + public readonly int FireDelay = 0; + + public virtual object Create(Actor self) { return new AttackBase(self); } + } + + class AttackBase : IIssueOrder, IResolveOrder, ITick + { + [Sync] public Actor target; + + // time (in frames) until each weapon can fire again. + [Sync] + protected int primaryFireDelay = 0; + [Sync] + protected int secondaryFireDelay = 0; + + int primaryBurst; + int secondaryBurst; + + public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f; + + public AttackBase(Actor self) + { + var primaryWeapon = self.GetPrimaryWeapon(); + var secondaryWeapon = self.GetSecondaryWeapon(); + + primaryBurst = primaryWeapon != null ? primaryWeapon.Burst : 1; + secondaryBurst = secondaryWeapon != null ? secondaryWeapon.Burst : 1; + } + + protected bool CanAttack(Actor self) + { + return target != null; + } + + public bool IsReloading() + { + return (primaryFireDelay > 0) || (secondaryFireDelay > 0); + } + + List> delayedActions = new List>(); + + public virtual void Tick(Actor self) + { + if (primaryFireDelay > 0) --primaryFireDelay; + if (secondaryFireDelay > 0) --secondaryFireDelay; + + primaryRecoil = Math.Max(0f, primaryRecoil - .2f); + secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f); + + if (target != null && target.IsDead) target = null; /* he's dead, jim. */ + + for (var i = 0; i < delayedActions.Count; i++) + { + var x = delayedActions[i]; + if (--x.First <= 0) + x.Second(); + delayedActions[i] = x; + } + delayedActions.RemoveAll(a => a.First <= 0); + } + + void ScheduleDelayedAction(int t, Action a) + { + if (t > 0) + delayedActions.Add(Pair.New(t, a)); + else + a(); + } + + public void DoAttack(Actor self) + { + var unit = self.traits.GetOrDefault(); + var info = self.Info.Traits.Get(); + + if (info.PrimaryWeapon != null && CheckFire(self, unit, info.PrimaryWeapon, ref primaryFireDelay, + info.PrimaryOffset, ref primaryBurst, info.PrimaryLocalOffset)) + { + secondaryFireDelay = Math.Max(4, secondaryFireDelay); + primaryRecoil = 1; + return; + } + + if (info.SecondaryWeapon != null && CheckFire(self, unit, info.SecondaryWeapon, ref secondaryFireDelay, + info.SecondaryOffset ?? info.PrimaryOffset, ref secondaryBurst, info.SecondaryLocalOffset)) + { + if (info.SecondaryOffset != null) secondaryRecoil = 1; + else primaryRecoil = 1; + return; + } + } + + bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst, int[] localOffset) + { + if (fireDelay > 0) return false; + + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo != null && !limitedAmmo.HasAmmo()) + return false; + + var weapon = Rules.WeaponInfo[weaponName]; + if (weapon.Range * weapon.Range < (target.Location - self.Location).LengthSquared) return false; + + if (!Combat.WeaponValidForTarget(weapon, target)) return false; + + var numOffsets = (localOffset.Length + 2) / 3; + if (numOffsets == 0) numOffsets = 1; + var localOffsetForShot = burst % numOffsets; + var thisLocalOffset = localOffset.Skip(3 * localOffsetForShot).Take(3).ToArray(); + + var fireOffset = new[] { + offset.ElementAtOrDefault(0) + thisLocalOffset.ElementAtOrDefault(0), + offset.ElementAtOrDefault(1) + thisLocalOffset.ElementAtOrDefault(1), + offset.ElementAtOrDefault(2), + offset.ElementAtOrDefault(3) }; + + if (--burst > 0) + fireDelay = 5; + else + { + fireDelay = weapon.ROF; + burst = weapon.Burst; + } + + var firePos = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, fireOffset, 0f).ToInt2(); + var thisTarget = target; // closure. + var destUnit = thisTarget.traits.GetOrDefault(); + var info = self.Info.Traits.Get(); + + ScheduleDelayedAction(info.FireDelay, () => + { + var srcAltitude = unit != null ? unit.Altitude : 0; + var destAltitude = destUnit != null ? destUnit.Altitude : 0; + + if ( weapon.RenderAsLaser ) + { + // TODO: This is a hack; should probably use a particular palette index + Color bc = (weapon.UsePlayerColor) ? Player.PlayerColors[self.Owner.PaletteIndex].c : Color.Red; + self.World.Add(new LaserZap(firePos, thisTarget.CenterLocation.ToInt2(), weapon.BeamRadius, bc)); + } + if( weapon.RenderAsTesla ) + self.World.Add( new TeslaZap( firePos, thisTarget.CenterLocation.ToInt2() ) ); + + if (Rules.ProjectileInfo[weapon.Projectile].ROT != 0) + { + var fireFacing = thisLocalOffset.ElementAtOrDefault(2) + + (self.traits.Contains() ? self.traits.Get().turretFacing : unit.Facing); + + self.World.Add(new Missile(weapon, self.Owner, self, + firePos, thisTarget, srcAltitude, fireFacing)); + } + else + self.World.Add(new Bullet(weapon, self.Owner, self, + firePos, thisTarget.CenterLocation.ToInt2(), srcAltitude, destAltitude)); + + if (!string.IsNullOrEmpty(weapon.Report)) + Sound.Play(weapon.Report + ".aud"); + }); + + foreach (var na in self.traits.WithInterface()) + na.Attacking(self); + + return true; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left || underCursor == null) return null; + if (self == underCursor) return null; + + var isHeal = self.GetPrimaryWeapon().Damage < 0; + var forceFire = mi.Modifiers.HasModifier(Modifiers.Ctrl); + + if (isHeal) + { + if (underCursor.Owner == null) + return null; + if (underCursor.Owner != self.Owner && !forceFire) + return null; + if (underCursor.Health >= underCursor.GetMaxHP()) + return null; // don't allow healing of fully-healed stuff! + } + else + if ((underCursor.Owner == self.Owner || underCursor.Owner == null) && !forceFire) + return null; + + if (!Combat.HasAnyValidWeapons(self, underCursor)) return null; + + return new Order(isHeal ? "Heal" : "Attack", self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Attack" || order.OrderString == "Heal") + { + self.CancelActivity(); + QueueAttack(self, order); + + if (self.Owner == self.World.LocalPlayer) + self.World.AddFrameEndTask(w => w.Add(new FlashTarget(order.TargetActor))); + } + else + target = null; + } + + protected virtual void QueueAttack(Actor self, Order order) + { + const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ + /* todo: choose the appropriate weapon, when only one works against this target */ + var weapon = self.GetPrimaryWeapon() ?? self.GetSecondaryWeapon(); + + self.QueueActivity(new Activities.Attack(order.TargetActor, + Math.Max(0, (int)weapon.Range - RangeTolerance))); + } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackFrontal.cs b/OpenRA.Game/Traits/Attack/AttackFrontal.cs new file mode 100644 index 0000000000..6c9f756ef2 --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackFrontal.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits +{ + abstract class AttackFrontal : AttackBase + { + public AttackFrontal(Actor self, int facingTolerance) + : base(self) { FacingTolerance = facingTolerance; } + + readonly int FacingTolerance; + + public override void Tick(Actor self) + { + base.Tick(self); + + if (target == null) return; + + var unit = self.traits.Get(); + var facingToTarget = Util.GetFacing(target.CenterLocation - self.CenterLocation, unit.Facing); + + if (Math.Abs(facingToTarget - unit.Facing) % 256 < FacingTolerance) + DoAttack(self); + } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackHeli.cs b/OpenRA.Game/Traits/Attack/AttackHeli.cs new file mode 100644 index 0000000000..430aaa8896 --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackHeli.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class AttackHeliInfo : AttackBaseInfo + { + public override object Create(Actor self) { return new AttackHeli(self); } + } + + class AttackHeli : AttackFrontal + { + public AttackHeli(Actor self) : base(self, 20) { } + + protected override void QueueAttack(Actor self, Order order) + { + target = order.TargetActor; + self.QueueActivity(new HeliAttack(order.TargetActor)); + self.QueueActivity(new HeliReturn()); + } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackInfo.cs b/OpenRA.Game/Traits/Attack/AttackInfo.cs new file mode 100644 index 0000000000..90cbdf2877 --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackInfo.cs @@ -0,0 +1,33 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.GameRules; + +namespace OpenRA.Traits +{ + public class AttackInfo + { + public Actor Attacker; + public WarheadInfo Warhead; + public int Damage; + public DamageState DamageState; + public bool DamageStateChanged; + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackOmni.cs b/OpenRA.Game/Traits/Attack/AttackOmni.cs new file mode 100644 index 0000000000..d7d1abfc97 --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackOmni.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class AttackOmniInfo : AttackBaseInfo + { + public override object Create(Actor self) { return new AttackOmni(self); } + } + + class AttackOmni : AttackBase, INotifyBuildComplete + { + bool buildComplete = false; + public void BuildingComplete(Actor self) { buildComplete = true; } + + public AttackOmni(Actor self) : base(self) { } + + public override void Tick(Actor self) + { + base.Tick(self); + + if (!CanAttack(self)) return; + if (self.traits.Contains() && !buildComplete) return; + + DoAttack(self); + } + + protected override void QueueAttack(Actor self, Order order) + { + target = order.TargetActor; + } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackPlane.cs b/OpenRA.Game/Traits/Attack/AttackPlane.cs new file mode 100644 index 0000000000..dd91bd4308 --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackPlane.cs @@ -0,0 +1,42 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class AttackPlaneInfo : AttackBaseInfo + { + public override object Create(Actor self) { return new AttackPlane(self); } + } + + class AttackPlane : AttackFrontal + { + public AttackPlane(Actor self) : base(self, 20) { } + + protected override void QueueAttack(Actor self, Order order) + { + target = order.TargetActor; + self.QueueActivity(new FlyAttack(order.TargetActor)); + self.QueueActivity(new ReturnToBase(self, null)); + self.QueueActivity(new Rearm()); + } + } +} diff --git a/OpenRA.Game/Traits/Attack/AttackTurreted.cs b/OpenRA.Game/Traits/Attack/AttackTurreted.cs new file mode 100644 index 0000000000..31387e459e --- /dev/null +++ b/OpenRA.Game/Traits/Attack/AttackTurreted.cs @@ -0,0 +1,71 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits +{ + class AttackTurretedInfo : AttackBaseInfo + { + public override object Create(Actor self) { return new AttackTurreted( self ); } + } + + class AttackTurreted : AttackBase, INotifyBuildComplete + { + public AttackTurreted(Actor self) : base(self) { } + + public override void Tick(Actor self) + { + base.Tick(self); + + if( !CanAttack( self ) ) return; + + if (self.traits.Contains() && !buildComplete) + return; /* base defenses can't do anything until they finish building !*/ + + var turreted = self.traits.Get(); + turreted.desiredFacing = Util.GetFacing( target.CenterLocation - self.CenterLocation, turreted.turretFacing ); + if( turreted.desiredFacing != turreted.turretFacing ) + return; + + DoAttack( self ); + } + + protected override void QueueAttack( Actor self, Order order ) + { + if (self.traits.Contains() && self.traits.Get().Disabled) + return; + + const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ + /* todo: choose the appropriate weapon, when only one works against this target */ + var weapon = order.Subject.GetPrimaryWeapon() ?? order.Subject.GetSecondaryWeapon(); + + if (self.traits.Contains()) + self.QueueActivity( new Traits.Activities.Follow( order.TargetActor, + Math.Max( 0, (int)weapon.Range - RangeTolerance ) ) ); + + target = order.TargetActor; + + } + + bool buildComplete = false; + public void BuildingComplete(Actor self) { buildComplete = true; } + } +} diff --git a/OpenRA.Game/Traits/Bridge.cs b/OpenRA.Game/Traits/Bridge.cs new file mode 100644 index 0000000000..99dc12c52f --- /dev/null +++ b/OpenRA.Game/Traits/Bridge.cs @@ -0,0 +1,175 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class BridgeInfo : ITraitInfo + { + public readonly bool Long = false; + public readonly bool UseAlternateNames = false; + public readonly int[] NorthOffset = null; + public readonly int[] SouthOffset = null; + public object Create(Actor self) { return new Bridge(self); } + } + + class Bridge : IRender, ICustomTerrain, INotifyDamage + { + Dictionary Tiles; + List> TileSprites = new List>(); + List Templates = new List(); + Actor self; + int state; + + Bridge northNeighbour, southNeighbour; + + public Bridge(Actor self) { this.self = self; self.RemoveOnDeath = false; } + + static string cachedTheater; + static Cache sprites; + + public IEnumerable Render(Actor self) + { + foreach (var t in TileSprites[state]) + yield return new Renderable(t.Value, Game.CellSize * t.Key, "terrain"); + } + + public int StateFromTemplate(TileTemplate t) + { + var info = self.Info.Traits.Get(); + if (info.UseAlternateNames) + { + if (t.Name.EndsWith("d")) return 2; + if (t.Name.EndsWith("h")) return 1; + return 0; + } + else + return t.Name[t.Name.Length - 1] - 'a'; + } + + public string NameFromState(TileTemplate t, int state) + { + var info = self.Info.Traits.Get(); + if (info.UseAlternateNames) + return t.Bridge + new[] { "", "h", "d" }[state]; + else + return t.Bridge + (char)(state + 'a'); + } + + public void SetTiles(World world, TileTemplate template, Dictionary replacedTiles) + { + Tiles = replacedTiles; + state = StateFromTemplate(template); + + foreach (var t in replacedTiles.Keys) + world.customTerrain[t.X, t.Y] = this; + + if (cachedTheater != world.Map.Theater) + { + cachedTheater = world.Map.Theater; + sprites = new Cache( + x => SheetBuilder.SharedInstance.Add(world.TileSet.GetBytes(x), + new Size(Game.CellSize, Game.CellSize))); + } + + var numStates = self.Info.Traits.Get().Long ? 6 : 3; + for (var n = 0; n < numStates; n++) + { + var stateTemplate = world.TileSet.Walkability.GetWalkability(NameFromState(template, n)); + Templates.Add( stateTemplate ); + + TileSprites.Add(replacedTiles.ToDictionary( + a => a.Key, + a => sprites[new TileReference { tile = (ushort)stateTemplate.Index, image = (byte)a.Value }])); + } + + self.Health = (int)(self.GetMaxHP() * template.HP); + } + + Bridge GetNeighbor(World world, int[] offset) + { + if (offset == null) return null; + var pos = self.Location + new int2(offset[0], offset[1]); + if (!world.Map.IsInMap(pos.X, pos.Y)) return null; + return world.customTerrain[pos.X, pos.Y] as Bridge; + } + + public void FinalizeBridges(World world) + { + // go looking for our neighbors, if this is a long bridge. + var info = self.Info.Traits.Get(); + if (info.NorthOffset != null) + northNeighbour = GetNeighbor(world, info.NorthOffset); + if (info.SouthOffset != null) + southNeighbour = GetNeighbor(world, info.SouthOffset); + } + + public float GetCost(int2 p, UnitMovementType umt) + { + // just use the standard walkability from templates.ini. no hackery. + + return TerrainCosts.Cost(umt, + Templates[state].TerrainType[Tiles[p]]); + } + + bool IsIntact(Bridge b) + { + return b != null && b.self.IsInWorld && b.self.Health > 0; + } + + bool IsLong(Bridge b) + { + return b != null && b.self.IsInWorld && b.self.Info.Traits.Get().Long; + } + + void UpdateState() + { + var ds = self.GetDamageState(); + if (!self.Info.Traits.Get().Long) + { + state = (int)ds; + return; + } + + bool waterToSouth = !IsIntact(southNeighbour) && (!IsLong(southNeighbour) || !IsIntact(this)); + bool waterToNorth = !IsIntact(northNeighbour) && (!IsLong(northNeighbour) || !IsIntact(this)); + + if (waterToSouth && waterToNorth) { state = 5; return; } + if (waterToNorth) { state = 4; return; } + if (waterToSouth) { state = 3; return; } + state = (int)ds; + } + + public void Damaged(Actor self, AttackInfo e) + { + if (e.DamageStateChanged) + { + UpdateState(); + if (northNeighbour != null) northNeighbour.UpdateState(); + if (southNeighbour != null) southNeighbour.UpdateState(); + } + } + } +} diff --git a/OpenRA.Game/Traits/Buildable.cs b/OpenRA.Game/Traits/Buildable.cs new file mode 100755 index 0000000000..2da02fd287 --- /dev/null +++ b/OpenRA.Game/Traits/Buildable.cs @@ -0,0 +1,37 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class BuildableInfo : StatelessTraitInfo + { + public readonly int TechLevel = -1; + public readonly string[] Prerequisites = { }; + public readonly string[] BuiltAt = { }; + public readonly string[] Owner = { }; + public readonly int Cost = 0; + public readonly string Description = ""; + public readonly string LongDesc = ""; + public readonly string Icon = null; + public readonly string[] AlternateName = { }; + } + + class Buildable { } +} diff --git a/OpenRA.Game/Traits/Building.cs b/OpenRA.Game/Traits/Building.cs new file mode 100644 index 0000000000..3b07cabac5 --- /dev/null +++ b/OpenRA.Game/Traits/Building.cs @@ -0,0 +1,159 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Effects; +using OpenRA.GameRules; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public class OwnedActorInfo + { + public readonly int HP = 0; + public readonly ArmorType Armor = ArmorType.none; + public readonly bool Crewed = false; // replace with trait? + public readonly int Sight = 0; + public readonly bool WaterBound = false; + } + + public class BuildingInfo : OwnedActorInfo, ITraitInfo + { + public readonly int Power = 0; + public readonly bool BaseNormal = true; + public readonly int Adjacent = 2; + public readonly bool Bib = false; + public readonly bool Capturable = false; + public readonly bool Repairable = true; + public readonly string Footprint = "x"; + public readonly string[] Produces = { }; // does this go somewhere else? + public readonly int2 Dimensions = new int2(1, 1); + public readonly bool Unsellable = false; + + public readonly string[] BuildSounds = {"placbldg.aud", "build5.aud"}; + public readonly string[] SellSounds = {"cashturn.aud"}; + + public object Create(Actor self) { return new Building(self); } + } + + public class Building : INotifyDamage, IResolveOrder, ITick, IRenderModifier + { + readonly Actor self; + public readonly BuildingInfo Info; + [Sync] + bool isRepairing = false; + + public bool Disabled + { + get { return self.traits.WithInterface().Any(t => t.Disabled); } + } + + public Building(Actor self) + { + this.self = self; + Info = self.Info.Traits.Get(); + self.CenterLocation = Game.CellSize + * ((float2)self.Location + .5f * (float2)Info.Dimensions); + } + + public int GetPowerUsage() + { + var modifier = self.traits + .WithInterface() + .Select(t => t.GetPowerModifier()) + .Product(); + + var maxHP = self.Info.Traits.Get().HP; + + if (Info.Power > 0) + return (int)(modifier*(self.Health * Info.Power) / maxHP); + else + return (int)(modifier * Info.Power); + } + + public void Damaged(Actor self, AttackInfo e) + { + if (e.DamageState == DamageState.Dead) + { + ScreenShaker.RegisterShakeEffect(10, self.CenterLocation, 1); + Sound.Play("kaboom22.aud"); + } + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Sell") + { + self.CancelActivity(); + self.QueueActivity(new Sell()); + } + + if (order.OrderString == "Repair") + { + isRepairing = !isRepairing; + } + } + + int remainingTicks; + + public void Tick(Actor self) + { + if (!isRepairing) return; + + if (remainingTicks == 0) + { + var csv = self.Info.Traits.GetOrDefault(); + var buildingValue = csv != null ? csv.Value : self.Info.Traits.Get().Cost; + var maxHP = self.Info.Traits.Get().HP; + var costPerHp = (Rules.General.URepairPercent * buildingValue) / maxHP; + var hpToRepair = Math.Min(Rules.General.URepairStep, maxHP - self.Health); + var cost = (int)Math.Ceiling(costPerHp * hpToRepair); + if (!self.Owner.TakeCash(cost)) + { + remainingTicks = 1; + return; + } + + self.World.AddFrameEndTask(w => w.Add(new RepairIndicator(self))); + self.InflictDamage(self, -hpToRepair, Rules.WarheadInfo["Super"]); + if (self.Health == maxHP) + { + isRepairing = false; + return; + } + remainingTicks = (int)(Rules.General.RepairRate * 60 * 25); + } + else + --remainingTicks; + } + + public IEnumerable ModifyRender(Actor self, IEnumerable r) + { + foreach (var a in r) + { + yield return a; + if (Disabled) + yield return a.WithPalette("disabled"); + } + } + } +} diff --git a/OpenRA.Game/Traits/CanPowerDown.cs b/OpenRA.Game/Traits/CanPowerDown.cs new file mode 100644 index 0000000000..ef2b007d85 --- /dev/null +++ b/OpenRA.Game/Traits/CanPowerDown.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + public class CanPowerDownInfo : ITraitInfo + { + public object Create(Actor self) { return new CanPowerDown(); } + } + + public class CanPowerDown : IDisable, IPowerModifier, IResolveOrder + { + [Sync] + bool IsDisabled = false; + + public bool Disabled + { + get { return IsDisabled; } + set { IsDisabled = value; } + } + + public float GetPowerModifier() { return (IsDisabled) ? 0.0f : 1.0f; } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "PowerDown") + { + IsDisabled = !IsDisabled; + var eva = self.Owner.PlayerActor.Info.Traits.Get(); + Sound.PlayToPlayer(self.Owner, IsDisabled ? eva.EnablePower : eva.DisablePower); + } + } + } +} diff --git a/OpenRA.Game/Traits/Cargo.cs b/OpenRA.Game/Traits/Cargo.cs new file mode 100644 index 0000000000..4ff7606dcf --- /dev/null +++ b/OpenRA.Game/Traits/Cargo.cs @@ -0,0 +1,111 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public class CargoInfo : ITraitInfo + { + public readonly int Passengers = 0; + public readonly UnitMovementType[] PassengerTypes = { }; + public readonly int UnloadFacing = 0; + + public object Create(Actor self) { return new Cargo(self); } + } + + public class Cargo : IPips, IIssueOrder, IResolveOrder + { + List cargo = new List(); + + public Cargo(Actor self) {} + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + // todo: check if there is an unoccupied `land` tile adjacent + if (mi.Button == MouseButton.Right && underCursor == self && cargo.Count > 0) + { + var unit = underCursor.traits.GetOrDefault(); + if (unit != null && unit.Altitude > 0) return null; + + return new Order("Deploy", self); + } + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Deploy") + { + // todo: eject the units + self.CancelActivity(); + self.QueueActivity(new UnloadCargo()); + } + } + + public bool IsFull(Actor self) + { + return cargo.Count == self.Info.Traits.Get().Passengers; + } + + public bool IsEmpty(Actor self) + { + return cargo.Count == 0; + } + + public Actor Unload(Actor self) + { + var a = cargo[0]; + cargo.RemoveAt(0); + return a; + } + + public IEnumerable GetPips( Actor self ) + { + var numPips = self.Info.Traits.Get().Passengers; + for (var i = 0; i < numPips; i++) + if (i >= cargo.Count) + yield return PipType.Transparent; + else + yield return GetPipForPassenger(cargo[i]); + } + + static PipType GetPipForPassenger(Actor a) + { + // probably not actually right yet; fix to match real-ra + + if (a.traits.Contains()) + return PipType.Yellow; + if (!a.traits.Contains()) + return PipType.Yellow; // noncombat [E6,SPY,THF] + + // todo: fix E7 color again. + + return PipType.Green; + } + + public void Load(Actor self, Actor a) + { + cargo.Add(a); + } + } +} diff --git a/OpenRA.Game/Traits/Chrome/PowerDownButton.cs b/OpenRA.Game/Traits/Chrome/PowerDownButton.cs new file mode 100644 index 0000000000..09aa6d94fd --- /dev/null +++ b/OpenRA.Game/Traits/Chrome/PowerDownButton.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; +using OpenRA.Orders; + +namespace OpenRA.Traits +{ + class PowerDownButtonInfo : StatelessTraitInfo { } + + class PowerDownButton : IChromeButton + { + public string Image { get { return "repair"; } } // todo: art + public bool Enabled { get { return true; } } + public bool Pressed { get { return Game.controller.orderGenerator is PowerDownOrderGenerator; } } + public void OnClick() { Game.controller.ToggleInputMode(); } + } + + class SellButtonInfo : StatelessTraitInfo { } + + class SellButton : IChromeButton + { + public string Image { get { return "sell"; } } + public bool Enabled { get { return true; } } + public bool Pressed { get { return Game.controller.orderGenerator is SellOrderGenerator; } } + public void OnClick() { Game.controller.ToggleInputMode(); } + } + + class RepairButtonInfo : StatelessTraitInfo { } + + class RepairButton : IChromeButton + { + public string Image { get { return "repair"; } } // todo: art + public bool Enabled + { + get + { + if (!Game.Settings.RepairRequiresConyard) + return true; + + return Game.world.Queries.OwnedBy[Game.world.LocalPlayer] + .WithTrait().Any(); + } + } + + public bool Pressed { get { return Game.controller.orderGenerator is RepairOrderGenerator; } } + public void OnClick() { Game.controller.ToggleInputMode(); } + } +} diff --git a/OpenRA.Game/Traits/Chronoshiftable.cs b/OpenRA.Game/Traits/Chronoshiftable.cs new file mode 100644 index 0000000000..ff6b84f66e --- /dev/null +++ b/OpenRA.Game/Traits/Chronoshiftable.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class ChronoshiftableInfo : ITraitInfo + { + public object Create(Actor self) { return new Chronoshiftable(self); } + } + + public class Chronoshiftable : ITick + { + // Return-to-sender logic + [Sync] + int2 chronoshiftOrigin; + [Sync] + int chronoshiftReturnTicks = 0; + + public Chronoshiftable(Actor self) { } + + public void Tick(Actor self) + { + if (chronoshiftReturnTicks <= 0) + return; + + if (chronoshiftReturnTicks > 0) + chronoshiftReturnTicks--; + + // Return to original location + if (chronoshiftReturnTicks == 0) + { + self.CancelActivity(); + // Todo: need a new Teleport method that will move to the closest available cell + self.QueueActivity(new Activities.Teleport(chronoshiftOrigin)); + } + } + + public virtual bool Activate(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) + { + /// Set up return-to-sender info + chronoshiftOrigin = self.Location; + chronoshiftReturnTicks = duration; + + // Kill cargo + if (killCargo && self.traits.Contains()) + { + var cargo = self.traits.Get(); + while (!cargo.IsEmpty(self)) + { + chronosphere.Owner.Kills++; + cargo.Unload(self); + } + } + + // Set up the teleport + self.CancelActivity(); + self.QueueActivity(new Activities.Teleport(targetLocation)); + + return true; + } + } +} diff --git a/OpenRA.Game/Traits/ConstructionYard.cs b/OpenRA.Game/Traits/ConstructionYard.cs new file mode 100644 index 0000000000..d2e737fb14 --- /dev/null +++ b/OpenRA.Game/Traits/ConstructionYard.cs @@ -0,0 +1,97 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class ConstructionYardInfo : ITraitInfo + { + public readonly bool AllowUndeploy = true; + + public object Create(Actor self) { return new ConstructionYard(self); } + } + + class ConstructionYard : IIssueOrder, IResolveOrder, IMovement + { + readonly Actor self; + + public ConstructionYard(Actor self) + { + this.self = self; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (!self.Info.Traits.Get().AllowUndeploy) return null; + + if (mi.Button == MouseButton.Left) return null; + + if (underCursor != null) + { + // force-move + if (!mi.Modifiers.HasModifier(Modifiers.Alt)) return null; + if (!self.World.IsActorCrushableByActor(underCursor, self)) return null; + } + if (self.traits.GetOrDefault().CanEnterCell(xy)) + return new Order("Move", self, xy); + else + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Move") + { + self.CancelActivity(); + self.QueueActivity(new UndeployMcv()); + } + } + + // HACK: This should make reference to an MCV actor, and use of its Mobile trait + public UnitMovementType GetMovementType() + { + return UnitMovementType.Wheel; + } + + public bool CanEnterCell(int2 a) + { + if (!self.World.WorldActor.traits.Get().CanMoveHere(a)) return false; + + var crushable = true; + foreach (Actor actor in self.World.WorldActor.traits.Get().GetUnitsAt(a)) + { + if (actor == self) continue; + + if (!self.World.IsActorCrushableByActor(actor, self)) + { + crushable = false; + break; + } + } + + if (!crushable) return false; + + return self.World.Map.IsInMap(a.X, a.Y) && + TerrainCosts.Cost(GetMovementType(), + self.World.TileSet.GetWalkability(self.World.Map.MapTiles[a.X, a.Y])) < double.PositiveInfinity; + } + } +} diff --git a/OpenRA.Game/Traits/Crate.cs b/OpenRA.Game/Traits/Crate.cs new file mode 100644 index 0000000000..c3fbad0246 --- /dev/null +++ b/OpenRA.Game/Traits/Crate.cs @@ -0,0 +1,100 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; + +/* + * Crates left to implement: +Cloak=0,STEALTH2 ; enable cloaking on nearby objects +Darkness=1,EMPULSE ; cloak entire radar map +Explosion=5,NONE,500 ; high explosive baddie (damage per explosion) +HealBase=1,INVUN ; all buildings to full strength +ICBM=1,MISSILE2 ; nuke missile one time shot +Napalm=5,NONE,600 ; fire explosion baddie (damage) +ParaBomb=3,PARABOX ; para-bomb raid one time shot +Reveal=1,EARTH ; reveal entire radar map +Sonar=3,SONARBOX ; one time sonar pulse +Squad=20,NONE ; squad of random infantry +Unit=20,NONE ; vehicle +Invulnerability=3,INVULBOX,1.0 ; invulnerability (duration in minutes) +TimeQuake=3,TQUAKE ; time quake +*/ + +namespace OpenRA.Traits +{ + class CrateInfo : ITraitInfo, ITraitPrerequisite + { + public readonly int Lifetime = 5; // Seconds + public object Create(Actor self) { return new Crate(self); } + } + + class Crate : ICrushable, IOccupySpace, ITick + { + readonly Actor self; + int ticks; + public Crate(Actor self) + { + this.self = self; + self.World.WorldActor.traits.Get().Add(self, this); + + if (self.World.IsWater(self.Location)) + self.traits.Get().anim.PlayRepeating("water"); + } + + public void OnCrush(Actor crusher) + { + var shares = self.traits.WithInterface().Select(a => Pair.New(a, a.SelectionShares)); + var totalShares = shares.Sum(a => a.Second); + var n = self.World.SharedRandom.Next(totalShares); + + self.World.AddFrameEndTask(w => w.Remove(self)); + foreach (var s in shares) + if (n < s.Second) + { + s.First.Activate(crusher); + return; + } + else + n -= s.Second; + } + + public bool IsPathableCrush(UnitMovementType umt, Player player) + { + return true; + } + + public bool IsCrushableBy(UnitMovementType umt, Player player) + { + return true; + } + + public IEnumerable OccupiedCells() { yield return self.Location; } + + public void Tick(Actor self) + { + if (++ticks >= self.Info.Traits.Get().Lifetime * 25) + { + self.World.AddFrameEndTask(w => w.Remove(self)); + } + } + } +} diff --git a/OpenRA.Game/Traits/CustomSellValue.cs b/OpenRA.Game/Traits/CustomSellValue.cs new file mode 100644 index 0000000000..935f90be78 --- /dev/null +++ b/OpenRA.Game/Traits/CustomSellValue.cs @@ -0,0 +1,32 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + // allow a nonstandard sell/repair value to avoid + // buy-sell exploits like c&c's PROC. + + class CustomSellValueInfo : StatelessTraitInfo + { + public readonly int Value = 0; + } + + class CustomSellValue {} +} diff --git a/OpenRA.Game/Traits/Explodes.cs b/OpenRA.Game/Traits/Explodes.cs new file mode 100644 index 0000000000..061ba4d82d --- /dev/null +++ b/OpenRA.Game/Traits/Explodes.cs @@ -0,0 +1,43 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Effects; + +namespace OpenRA.Traits +{ + class ExplodesInfo : StatelessTraitInfo { } + + class Explodes : INotifyDamage + { + public void Damaged(Actor self, AttackInfo e) + { + if (self.IsDead) + { + var unit = self.traits.GetOrDefault(); + var altitude = unit != null ? unit.Altitude : 0; + + self.World.AddFrameEndTask( + w => w.Add(new Bullet("UnitExplode", e.Attacker.Owner, e.Attacker, + self.CenterLocation.ToInt2(), self.CenterLocation.ToInt2(), + altitude, altitude))); + } + } + } +} diff --git a/OpenRA.Game/Traits/Fake.cs b/OpenRA.Game/Traits/Fake.cs new file mode 100644 index 0000000000..050d356fd5 --- /dev/null +++ b/OpenRA.Game/Traits/Fake.cs @@ -0,0 +1,31 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class FakeInfo : StatelessTraitInfo { } + + class Fake : ITags + { + public IEnumerable GetTags() { yield return TagType.Fake; } + } +} diff --git a/OpenRA.Game/Traits/GeneratesGap.cs b/OpenRA.Game/Traits/GeneratesGap.cs new file mode 100644 index 0000000000..3f8df84071 --- /dev/null +++ b/OpenRA.Game/Traits/GeneratesGap.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class GeneratesGapInfo : ITraitInfo + { + public readonly int Range = 10; + public object Create(Actor self) { return new GeneratesGap(self); } + } + + class GeneratesGap + { + Actor self; + public GeneratesGap(Actor self) + { + this.self = self; + } + + public IEnumerable GetShroudedTiles() + { + int range = self.Info.Traits.Get().Range; + + // Gap Generator building; powered down + return (self.traits.Contains() && self.traits.Get().Disabled) + ? new int2[] {} + : self.World.FindTilesInCircle(self.Location, range); + } + } +} diff --git a/OpenRA.Game/Traits/Harvester.cs b/OpenRA.Game/Traits/Harvester.cs new file mode 100644 index 0000000000..e476624178 --- /dev/null +++ b/OpenRA.Game/Traits/Harvester.cs @@ -0,0 +1,115 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class HarvesterInfo : ITraitInfo + { + public readonly int BailCount = 28; + public readonly int PipCount = 7; + + public object Create(Actor self) { return new Harvester(self); } + } + + public class Harvester : IIssueOrder, IResolveOrder, IPips + { + [Sync] + public int oreCarried = 0; /* sum of these must not exceed capacity */ + [Sync] + public int gemsCarried = 0; + + Actor self; + public Harvester(Actor self) + { + this.self = self; + } + + public bool IsFull { get { return oreCarried + gemsCarried == self.Info.Traits.Get().BailCount; } } + public bool IsEmpty { get { return oreCarried == 0 && gemsCarried == 0; } } + + public void AcceptResource(bool isGem) + { + if (isGem) gemsCarried++; + else oreCarried++; + } + + public void Deliver(Actor self, Actor proc) + { + proc.Owner.GiveOre(oreCarried * Rules.General.GoldValue); + proc.Owner.GiveOre(gemsCarried * Rules.General.GemValue); + oreCarried = 0; + gemsCarried = 0; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left) return null; + + if (underCursor != null + && underCursor.Owner == self.Owner + && underCursor.traits.Contains() && !IsEmpty) + return new Order("Deliver", self, underCursor); + + if (underCursor == null && self.World.Map.ContainsResource(xy)) + return new Order("Harvest", self, xy); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Harvest") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetLocation, 0)); + self.QueueActivity(new Harvest()); + } + else if (order.OrderString == "Deliver") + { + self.CancelActivity(); + self.QueueActivity(new DeliverOre(order.TargetActor)); + } + } + + public IEnumerable GetPips(Actor self) + { + int numPips = self.Info.Traits.Get().PipCount; + + for (int i = 0; i < numPips; i++) + { + if (gemsCarried * 1.0f / self.Info.Traits.Get().BailCount > i * 1.0f / numPips) + { + yield return PipType.Red; + continue; + } + + if ((gemsCarried + oreCarried) * 1.0f / self.Info.Traits.Get().BailCount > i * 1.0f / numPips) + { + yield return PipType.Yellow; + continue; + } + yield return PipType.Transparent; + } + } + } +} diff --git a/OpenRA.Game/Traits/Helicopter.cs b/OpenRA.Game/Traits/Helicopter.cs new file mode 100644 index 0000000000..3224a35e9c --- /dev/null +++ b/OpenRA.Game/Traits/Helicopter.cs @@ -0,0 +1,100 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class HelicopterInfo : ITraitInfo + { + public object Create(Actor self) { return new Helicopter(self); } + } + + class Helicopter : IIssueOrder, IResolveOrder, IMovement + { + public IDisposable reservation; + public Helicopter(Actor self) {} + + static bool HeliCanEnter(Actor a) + { + if (a.Info.Name == "hpad") return true; + if (a.Info.Name == "fix") return true; + return false; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left) return null; + + if (underCursor == null) + { + if (self.traits.GetOrDefault().CanEnterCell(xy)) + return new Order("Move", self, xy); + } + + if (HeliCanEnter(underCursor) + && underCursor.Owner == self.Owner + && !Reservable.IsReserved(underCursor)) + return new Order("Enter", self, underCursor); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (reservation != null) + { + reservation.Dispose(); + reservation = null; + } + + if (order.OrderString == "Move") + { + self.CancelActivity(); + self.QueueActivity(new HeliFly(Util.CenterOfCell(order.TargetLocation))); + self.QueueActivity(new Turn(self.Info.Traits.GetOrDefault().InitialFacing)); + self.QueueActivity(new HeliLand(true)); + } + + if (order.OrderString == "Enter") + { + if (Reservable.IsReserved(order.TargetActor)) return; + var res = order.TargetActor.traits.GetOrDefault(); + if (res != null) + reservation = res.Reserve(self); + + var productionInfo = order.TargetActor.Info.Traits.GetOrDefault(); + var offset = productionInfo != null ? productionInfo.SpawnOffset : null; + var offsetVec = offset != null ? new float2(offset[0], offset[1]) : float2.Zero; + + self.CancelActivity(); + self.QueueActivity(new HeliFly(order.TargetActor.CenterLocation + offsetVec)); + self.QueueActivity(new Turn(self.Info.Traits.GetOrDefault().InitialFacing)); + self.QueueActivity(new HeliLand(false)); + self.QueueActivity(order.TargetActor.Info.Name == "hpad" + ? (IActivity)new Rearm() : new Repair(true)); + } + } + + public UnitMovementType GetMovementType() { return UnitMovementType.Fly; } + public bool CanEnterCell(int2 location) { return true; } + } +} diff --git a/OpenRA.Game/Traits/LimitedAmmo.cs b/OpenRA.Game/Traits/LimitedAmmo.cs new file mode 100644 index 0000000000..cff63a57ea --- /dev/null +++ b/OpenRA.Game/Traits/LimitedAmmo.cs @@ -0,0 +1,63 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class LimitedAmmoInfo : ITraitInfo + { + public readonly int Ammo = 0; + public readonly int PipCount = 0; + + public object Create(Actor self) { return new LimitedAmmo(self); } + } + + public class LimitedAmmo : INotifyAttack, IPips + { + [Sync] + int ammo; + Actor self; + + public LimitedAmmo(Actor self) + { + ammo = self.Info.Traits.Get().Ammo; + this.self = self; + } + + public bool HasAmmo() { return ammo > 0; } + public bool GiveAmmo() + { + if (ammo >= self.Info.Traits.Get().Ammo) return false; + ++ammo; + return true; + } + + public void Attacking(Actor self) { --ammo; } + + public IEnumerable GetPips(Actor self) + { + var info = self.Info.Traits.Get(); + var pips = info.PipCount != 0 ? info.PipCount : info.Ammo; + return Graphics.Util.MakeArray(pips, + i => (ammo * pips) / info.Ammo > i ? PipType.Green : PipType.Transparent); + } + } +} diff --git a/OpenRA.Game/Traits/Mobile.cs b/OpenRA.Game/Traits/Mobile.cs new file mode 100644 index 0000000000..71e1c5b7d3 --- /dev/null +++ b/OpenRA.Game/Traits/Mobile.cs @@ -0,0 +1,145 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + public class MobileInfo : ITraitInfo + { + public readonly UnitMovementType MovementType = UnitMovementType.Wheel; + + public object Create(Actor self) { return new Mobile(self); } + } + + public class Mobile : IIssueOrder, IResolveOrder, IOccupySpace, IMovement + { + readonly Actor self; + + [Sync] + int2 __fromCell; + public int2 fromCell + { + get { return __fromCell; } + set { self.World.WorldActor.traits.Get().Remove(self, this); __fromCell = value; self.World.WorldActor.traits.Get().Add(self, this); } + } + public int2 toCell + { + get { return self.Location; } + set + { + if (self.Location != value) + { + self.World.WorldActor.traits.Get().Remove(self, this); + self.Location = value; + self.Owner.Shroud.Explore(self); + } + self.World.WorldActor.traits.Get().Add(self, this); + } + } + + public Mobile(Actor self) + { + this.self = self; + __fromCell = toCell; + self.World.WorldActor.traits.Get().Add(self, this); + } + + public void TeleportTo(Actor self, int2 xy) + { + fromCell = toCell = xy; + self.CenterLocation = Util.CenterOfCell(fromCell); + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left) return null; + + // force-fire should *always* take precedence over move. + if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) return null; + + if (underCursor != null && underCursor.Owner != null) + { + // force-move + if (!mi.Modifiers.HasModifier(Modifiers.Alt)) return null; + if (!self.World.IsActorCrushableByActor(underCursor, self)) return null; + } + + if (Util.GetEffectiveSpeed(self) == 0) return null; /* allow disabling move orders from modifiers */ + if (xy == toCell) return null; + return new Order("Move", self, xy); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Move") + { + if (self.traits.GetOrDefault().CanEnterCell(order.TargetLocation)) + { + self.CancelActivity(); + self.QueueActivity(new Activities.Move(order.TargetLocation, 8)); + } + } + } + + public IEnumerable OccupiedCells() + { + return (fromCell == toCell) + ? new[] { fromCell } + : new[] { fromCell, toCell }; + } + + public UnitMovementType GetMovementType() + { + return self.Info.Traits.Get().MovementType; + } + + public bool CanEnterCell(int2 a) + { + if (!self.World.WorldActor.traits.Get().CanMoveHere(a)) return false; + + var crushable = true; + foreach (Actor actor in self.World.WorldActor.traits.Get().GetUnitsAt(a)) + { + if (actor == self) continue; + + if (!self.World.IsActorCrushableByActor(actor, self)) + { + crushable = false; + break; + } + } + + if (!crushable) return false; + + return self.World.Map.IsInMap(a.X, a.Y) && + TerrainCosts.Cost(GetMovementType(), + self.World.TileSet.GetWalkability(self.World.Map.MapTiles[a.X, a.Y])) < double.PositiveInfinity; + } + + public IEnumerable GetCurrentPath() + { + var move = self.GetCurrentActivity() as Activities.Move; + if (move == null || move.path == null) return new int2[] { }; + return Enumerable.Reverse(move.path); + } + } +} diff --git a/OpenRA.Game/Traits/Modifiers/BelowUnits.cs b/OpenRA.Game/Traits/Modifiers/BelowUnits.cs new file mode 100644 index 0000000000..efa7abd4f5 --- /dev/null +++ b/OpenRA.Game/Traits/Modifiers/BelowUnits.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class BelowUnitsInfo : StatelessTraitInfo { } + + class BelowUnits : IRenderModifier + { + public IEnumerable ModifyRender(Actor self, IEnumerable r) + { + return r.Select(a => a.WithZOffset(-1)); + } + } +} diff --git a/OpenRA.Game/Traits/Modifiers/Cloak.cs b/OpenRA.Game/Traits/Modifiers/Cloak.cs new file mode 100644 index 0000000000..e60c041a43 --- /dev/null +++ b/OpenRA.Game/Traits/Modifiers/Cloak.cs @@ -0,0 +1,82 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class CloakInfo : ITraitInfo + { + public readonly float CloakDelay = 1.2f; // Seconds + public readonly string CloakSound = "ironcur9.aud"; + public readonly string UncloakSound = "ironcur9.aud"; + public object Create(Actor self) { return new Cloak(self); } + } + + class Cloak : IRenderModifier, INotifyAttack, ITick + { + [Sync] + int remainingUncloakTime = 2; /* setup for initial cloak */ + + Actor self; + public Cloak(Actor self) + { + this.self = self; + } + + public void Attacking(Actor self) + { + if (remainingUncloakTime <= 0) + OnCloak(); + + remainingUncloakTime = (int)(self.Info.Traits.Get().CloakDelay * 25); + } + + public IEnumerable + ModifyRender(Actor self, IEnumerable rs) + { + if (remainingUncloakTime > 0) + return rs; + + if (self.Owner == self.World.LocalPlayer) + return rs.Select(a => a.WithPalette("shadow")); + else + return new Renderable[] { }; + } + + public void Tick(Actor self) + { + if (remainingUncloakTime > 0) + if (--remainingUncloakTime <= 0) + OnUncloak(); + } + + void OnCloak() + { + Sound.Play(self.Info.Traits.Get().CloakSound); + } + + void OnUncloak() + { + Sound.Play(self.Info.Traits.Get().UncloakSound); + } + } +} diff --git a/OpenRA.Game/Traits/Modifiers/InvisibleToOthers.cs b/OpenRA.Game/Traits/Modifiers/InvisibleToOthers.cs new file mode 100644 index 0000000000..b2a44fde90 --- /dev/null +++ b/OpenRA.Game/Traits/Modifiers/InvisibleToOthers.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class InvisibleToOthersInfo : StatelessTraitInfo { } + + class InvisibleToOthers : IRenderModifier + { + public IEnumerable ModifyRender(Actor self, IEnumerable r) + { + return self.World.LocalPlayer == self.Owner + ? r : new Renderable[] { }; + } + } +} diff --git a/OpenRA.Game/Traits/Modifiers/WithShadow.cs b/OpenRA.Game/Traits/Modifiers/WithShadow.cs new file mode 100644 index 0000000000..0540b26bfc --- /dev/null +++ b/OpenRA.Game/Traits/Modifiers/WithShadow.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class WithShadowInfo : StatelessTraitInfo {} + + class WithShadow : IRenderModifier + { + public IEnumerable ModifyRender(Actor self, IEnumerable r) + { + var unit = self.traits.Get(); + + var shadowSprites = r.Select(a => a.WithPalette("shadow")); + var flyingSprites = (unit.Altitude <= 0) ? r + : r.Select(a => a.WithPos(a.Pos - new float2(0, unit.Altitude)).WithZOffset(3)); + + return shadowSprites.Concat(flyingSprites); + } + } +} diff --git a/OpenRA.Game/Traits/OreRefinery.cs b/OpenRA.Game/Traits/OreRefinery.cs new file mode 100644 index 0000000000..c118922e93 --- /dev/null +++ b/OpenRA.Game/Traits/OreRefinery.cs @@ -0,0 +1,65 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class OreRefineryInfo : ITraitInfo + { + public object Create(Actor self) { return new OreRefinery(self); } + } + + class OreRefinery : IAcceptOre + { + Actor self; + public OreRefinery(Actor self) + { + this.self = self; + self.World.AddFrameEndTask( + w => + { /* create the free harvester! */ + var harvester = w.CreateActor("harv", self.Location + + new int2(1, 2), self.Owner); + var unit = harvester.traits.Get(); + unit.Facing = 64; + harvester.QueueActivity(new Harvest()); + }); + } + public int2 DeliverOffset { get { return new int2(1, 2); } } + public void OnDock(Actor harv, DeliverOre dockOrder) + { + var unit = harv.traits.Get(); + if (unit.Facing != 64) + harv.QueueActivity(new Turn(64)); + + harv.QueueActivity( new CallFunc( () => { + var renderUnit = harv.traits.Get(); + if (renderUnit.anim.CurrentSequence.Name != "empty") + renderUnit.PlayCustomAnimation(harv, "empty", () => + { + harv.traits.Get().Deliver(harv, self); + harv.QueueActivity(new Harvest()); + }); + } + )); + } + } +} diff --git a/OpenRA.Game/Traits/Passenger.cs b/OpenRA.Game/Traits/Passenger.cs new file mode 100644 index 0000000000..da8ac8f967 --- /dev/null +++ b/OpenRA.Game/Traits/Passenger.cs @@ -0,0 +1,59 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class PassengerInfo : StatelessTraitInfo {} + + class Passenger : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) + return null; + + if (underCursor == null || underCursor.Owner != self.Owner) + return null; + + var cargo = underCursor.traits.GetOrDefault(); + if (cargo == null || cargo.IsFull(underCursor)) + return null; + + var umt = self.traits.Get().GetMovementType(); + if (!underCursor.Info.Traits.Get().PassengerTypes.Contains(umt)) + return null; + + return new Order("EnterTransport", self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "EnterTransport") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor.Location, 1)); + self.QueueActivity(new EnterTransport(self, order.TargetActor)); + } + } + } +} diff --git a/OpenRA.Game/Traits/Plane.cs b/OpenRA.Game/Traits/Plane.cs new file mode 100644 index 0000000000..cb09615e38 --- /dev/null +++ b/OpenRA.Game/Traits/Plane.cs @@ -0,0 +1,93 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class PlaneInfo : ITraitInfo + { + public object Create(Actor self) { return new Plane(self); } + } + + class Plane : IIssueOrder, IResolveOrder, IMovement + { + public IDisposable reservation; + + public Plane(Actor self) {} + + // todo: push into data! + static bool PlaneCanEnter(Actor a) + { + if (a.Info.Name == "afld") return true; + if (a.Info.Name == "fix") return true; + return false; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left) return null; + if (underCursor == null) + { + return new Order("Move", self, xy); + } + if (PlaneCanEnter(underCursor) + && underCursor.Owner == self.Owner + && !Reservable.IsReserved(underCursor)) + return new Order("Enter", self, underCursor); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (reservation != null) + { + reservation.Dispose(); + reservation = null; + } + + if (order.OrderString == "Move") + { + self.CancelActivity(); + self.QueueActivity(new Fly(Util.CenterOfCell(order.TargetLocation))); + self.QueueActivity(new ReturnToBase(self, null)); + self.QueueActivity(new Rearm()); + } + + if (order.OrderString == "Enter") + { + if (Reservable.IsReserved(order.TargetActor)) return; + var res = order.TargetActor.traits.GetOrDefault(); + if (res != null) + reservation = res.Reserve(self); + + self.CancelActivity(); + self.QueueActivity(new ReturnToBase(self, order.TargetActor)); + self.QueueActivity(order.TargetActor.Info.Name == "afld" + ? (IActivity)new Rearm() : new Repair(true)); + } + } + + public UnitMovementType GetMovementType() { return UnitMovementType.Fly; } + public bool CanEnterCell(int2 location) { return true; } + } +} diff --git a/OpenRA.Game/Traits/Player/EvaAlerts.cs b/OpenRA.Game/Traits/Player/EvaAlerts.cs new file mode 100644 index 0000000000..516a68b90e --- /dev/null +++ b/OpenRA.Game/Traits/Player/EvaAlerts.cs @@ -0,0 +1,63 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + + +namespace OpenRA.Traits +{ + class EvaAlertsInfo : StatelessTraitInfo + { + // Sound effects + public readonly string TabClick = "ramenu1.aud"; + + public readonly string ChatBeep = "rabeep1.aud"; + public readonly string RadarUp = "radaron2.aud"; + public readonly string RadarDown = "radardn1.aud"; + + public readonly string CashTickUp = "cashup1.aud"; + public readonly string CashTickDown = "cashdn1.aud"; + + // Build Palette + public readonly string BuildPaletteOpen = "bleep13.aud"; + public readonly string BuildPaletteClose = "bleep13.aud"; + public readonly string BuildingSelectAudio = "abldgin1.aud"; + public readonly string BuildingReadyAudio = "conscmp1.aud"; + public readonly string BuildingCannotPlaceAudio = "nodeply1.aud"; + public readonly string UnitSelectAudio = "train1.aud"; + public readonly string UnitReadyAudio = "unitrdy1.aud"; + public readonly string OnHoldAudio = "onhold1.aud"; + public readonly string CancelledAudio = "cancld1.aud"; + public readonly string ClickAudio = "ramenu1.aud"; + + + // For manual powerup/down in ra-ng + public readonly string DisablePower = "bleep11.aud"; + public readonly string EnablePower = "bleep12.aud"; + + // Eva speech + public readonly string LowPower = "lopower1.aud"; + public readonly string SilosNeeded = "silond1.aud"; + public readonly string UnitLost = "unitlst1.aud"; + public readonly string NavalUnitLost = "navylst1.aud"; + public readonly string PrimaryBuildingSelected = "pribldg1.aud"; + public readonly string CreditsStolen = "credit1.aud"; + } + + class EvaAlerts {} +} diff --git a/OpenRA.Game/Traits/Player/PlaceBuilding.cs b/OpenRA.Game/Traits/Player/PlaceBuilding.cs new file mode 100644 index 0000000000..afd3142bfa --- /dev/null +++ b/OpenRA.Game/Traits/Player/PlaceBuilding.cs @@ -0,0 +1,60 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + class PlaceBuildingInfo : StatelessTraitInfo {} + + class PlaceBuilding : IResolveOrder + { + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "PlaceBuilding" ) + { + self.World.AddFrameEndTask( _ => + { + var queue = self.traits.Get(); + var unit = Rules.Info[ order.TargetString ]; + var producing = queue.CurrentItem(unit.Category); + if( producing == null || producing.Item != order.TargetString || producing.RemainingTime != 0 ) + return; + + var building = self.World.CreateActor( order.TargetString, order.TargetLocation, order.Player ); + + foreach (var s in building.Info.Traits.Get().BuildSounds) + Sound.PlayToPlayer(order.Player, s); + + var facts = self.World.Queries.OwnedBy[self.Owner] + .WithTrait().Select(x => x.Actor); + + var primaryFact = facts.Where(y => y.traits.Get().IsPrimary); + var fact = (primaryFact.Count() > 0) ? primaryFact.FirstOrDefault() : facts.FirstOrDefault(); + + if (fact != null) + fact.traits.Get().PlayCustomAnim(fact, "build"); + + queue.FinishProduction(unit.Category); + } ); + } + } + } +} diff --git a/OpenRA.Game/Traits/Player/ProductionQueue.cs b/OpenRA.Game/Traits/Player/ProductionQueue.cs new file mode 100644 index 0000000000..1a58637d8f --- /dev/null +++ b/OpenRA.Game/Traits/Player/ProductionQueue.cs @@ -0,0 +1,243 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class ProductionQueueInfo : ITraitInfo + { + public object Create(Actor self) { return new ProductionQueue(self); } + } + + class ProductionQueue : IResolveOrder, ITick + { + Actor self; + + public ProductionQueue( Actor self ) + { + this.self = self; + } + + public void Tick( Actor self ) + { + foreach( var p in production ) + if( p.Value.Count > 0 ) + (p.Value)[0].Tick( self.Owner ); + } + + public void ResolveOrder( Actor self, Order order ) + { + switch( order.OrderString ) + { + case "StartProduction": + { + var unit = Rules.Info[ order.TargetString ]; + var ui = unit.Traits.Get(); + var time = ui.Cost + * Rules.General.BuildSpeed /* todo: country-specific build speed bonus */ + * ( 25 * 60 ) /* frames per min */ /* todo: build acceleration, if we do that */ + / 1000; + + time = .08f * time; /* temporary hax so we can build stuff fast for test */ + + if( !Rules.TechTree.BuildableItems( order.Player, unit.Category ).Contains( order.TargetString ) ) + return; /* you can't build that!! */ + + bool hasPlayedSound = false; + + BeginProduction( unit.Category, + new ProductionItem( order.TargetString, (int)time, ui.Cost, + () => self.World.AddFrameEndTask( + _ => + { + var isBuilding = unit.Traits.Contains(); + if( !hasPlayedSound ) + { + var eva = self.Info.Traits.Get(); + Sound.PlayToPlayer( order.Player, isBuilding ? eva.BuildingReadyAudio : eva.UnitReadyAudio ); + hasPlayedSound = true; + } + if( !isBuilding ) + BuildUnit( order.TargetString ); + } ) ) ); + break; + } + case "PauseProduction": + { + var producing = CurrentItem( Rules.Info[ order.TargetString ].Category ); + if( producing != null && producing.Item == order.TargetString ) + producing.Paused = ( order.TargetLocation.X != 0 ); + break; + } + case "CancelProduction": + { + CancelProduction(order.TargetString); + break; + } + } + } + + // Key: Production category. + // TODO: sync this + readonly Cache> production + = new Cache>( _ => new List() ); + + public ProductionItem CurrentItem(string category) + { + return production[category].ElementAtOrDefault(0); + } + + public IEnumerable AllItems(string category) + { + return production[category]; + } + + public void CancelProduction( string itemName ) + { + var category = Rules.Info[itemName].Category; + var queue = production[ category ]; + if (queue.Count == 0) return; + + var lastIndex = queue.FindLastIndex( a => a.Item == itemName ); + if (lastIndex > 0) + { + queue.RemoveAt(lastIndex); + } + else + { + var item = queue[0]; + self.Owner.GiveCash(item.TotalCost - item.RemainingCost); // refund what's been paid so far. + FinishProduction(category); + } + } + + public void FinishProduction( string category ) + { + var queue = production[category]; + if (queue.Count == 0) return; + queue.RemoveAt(0); + } + + public void BeginProduction( string group, ProductionItem item ) + { + production[group].Add(item); + } + + public void BuildUnit( string name ) + { + var newUnitType = Rules.Info[ name ]; + var producerTypes = Rules.TechTree.UnitBuiltAt( newUnitType ); + Actor producer = null; + + // Prioritise primary structure in build order + var primaryProducers = self.World.Queries.OwnedBy[self.Owner] + .WithTrait() + .Where(x => producerTypes.Contains(x.Actor.Info) + && x.Trait.IsPrimary); + + foreach (var p in primaryProducers) + { + // Ignore buildings that are disabled + if (p.Actor.traits.Contains() && p.Actor.traits.Get().Disabled) + continue; + producer = p.Actor; + break; + } + + // TODO: Be smart about disabled buildings. Units in progress should be paused(?) + // Ignore this for now + + // Pick the first available producer + if (producer == null) + { + producer = self.World.Queries.OwnedBy[self.Owner] + .Where( x => producerTypes.Contains( x.Info ) ) + .FirstOrDefault(); + } + + // Something went wrong somewhere... + if( producer == null ) + { + CancelProduction( newUnitType.Category ); + return; + } + + if( producer.traits.WithInterface().Any( p => p.Produce( producer, newUnitType ) ) ) + FinishProduction( newUnitType.Category ); + } + } + + class ProductionItem + { + public readonly string Item; + + public readonly int TotalTime; + public readonly int TotalCost; + public int RemainingTime { get; private set; } + public int RemainingCost { get; private set; } + + public bool Paused = false, Done = false; + public Action OnComplete; + + int slowdown = 0; + + public ProductionItem(string item, int time, int cost, Action onComplete) + { + if (time <= 0) + time = 1; + Item = item; + RemainingTime = TotalTime = time; + RemainingCost = TotalCost = cost; + OnComplete = onComplete; + } + + public void Tick(Player player) + { + if (Done) + { + if (OnComplete != null) OnComplete(); + return; + } + + if (Paused) return; + + if (player.GetPowerState() != PowerState.Normal) + { + if (--slowdown <= 0) + slowdown = Rules.General.LowPowerSlowdown; + else + return; + } + + var costThisFrame = RemainingCost / RemainingTime; + if (costThisFrame != 0 && !player.TakeCash(costThisFrame)) return; + + RemainingCost -= costThisFrame; + RemainingTime -= 1; + if (RemainingTime > 0) return; + + Done = true; + } + } +} diff --git a/OpenRA.Game/Traits/Player/SpawnDefaultUnits.cs b/OpenRA.Game/Traits/Player/SpawnDefaultUnits.cs new file mode 100644 index 0000000000..8337424a9d --- /dev/null +++ b/OpenRA.Game/Traits/Player/SpawnDefaultUnits.cs @@ -0,0 +1,32 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class SpawnDefaultUnitsInfo : StatelessTraitInfo { } + + class SpawnDefaultUnits : IOnGameStart + { + public void SpawnStartingUnits(Player p, int2 sp) + { + p.PlayerActor.World.CreateActor("mcv", sp, p); + } + } +} diff --git a/OpenRA.Game/Traits/Production.cs b/OpenRA.Game/Traits/Production.cs new file mode 100755 index 0000000000..efe9b236e5 --- /dev/null +++ b/OpenRA.Game/Traits/Production.cs @@ -0,0 +1,145 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.GameRules; + +namespace OpenRA.Traits +{ + public class ProductionInfo : ITraitInfo + { + public readonly int[] SpawnOffset = null; + public readonly int[] ProductionOffset = null; + public readonly int[] ExitOffset = null; + public readonly string[] Produces = { }; + + public virtual object Create(Actor self) { return new Production(self); } + } + + public class Production : IIssueOrder, IResolveOrder, IProducer, ITags + { + bool isPrimary = false; + public bool IsPrimary { get { return isPrimary; } } + + public Production( Actor self ) { } + + public virtual int2? CreationLocation( Actor self, ActorInfo producee ) + { + var pos = (1 / 24f * self.CenterLocation).ToInt2(); + var pi = self.Info.Traits.Get(); + if (pi.ProductionOffset != null) + pos += new int2(pi.ProductionOffset[0], pi.ProductionOffset[1]); + return pos; + } + + public virtual int2? ExitLocation(Actor self, ActorInfo producee) + { + var pos = (1 / 24f * self.CenterLocation).ToInt2(); + var pi = self.Info.Traits.Get(); + if (pi.ExitOffset != null) + pos += new int2(pi.ExitOffset[0], pi.ExitOffset[1]); + return pos; + } + + public virtual int CreationFacing( Actor self, Actor newUnit ) + { + return newUnit.Info.Traits.GetOrDefault().InitialFacing; + } + + public virtual bool Produce( Actor self, ActorInfo producee ) + { + var location = CreationLocation( self, producee ); + if( location == null || self.World.WorldActor.traits.Get().GetUnitsAt( location.Value ).Any() ) + return false; + + var newUnit = self.World.CreateActor( producee.Name, location.Value, self.Owner ); + newUnit.traits.Get().Facing = CreationFacing( self, newUnit ); ; + + var pi = self.Info.Traits.Get(); + var rp = self.traits.GetOrDefault(); + if( rp != null || pi.ExitOffset != null) + { + var mobile = newUnit.traits.GetOrDefault(); + if( mobile != null ) + { + if (pi.ExitOffset != null) + newUnit.QueueActivity(new Activities.Move(ExitLocation( self, producee).Value, 1)); + + if (rp != null) + newUnit.QueueActivity( new Activities.Move( rp.rallyPoint, 1 ) ); + } + } + + + if (pi != null && pi.SpawnOffset != null) + newUnit.CenterLocation = self.CenterLocation + + new float2(pi.SpawnOffset[0], pi.SpawnOffset[1]); + + foreach (var t in self.traits.WithInterface()) + t.UnitProduced(self, newUnit); + + return true; + } + + public IEnumerable GetTags() + { + yield return (isPrimary) ? TagType.Primary : TagType.None; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Right && underCursor == self) + return new Order("Deploy", self); + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Deploy") + SetPrimaryProducer(self, !isPrimary); + } + + public void SetPrimaryProducer(Actor self, bool state) + { + if (state == false) + { + isPrimary = false; + return; + } + + // Cancel existing primaries + foreach (var p in self.Info.Traits.Get().Produces) + { + foreach (var b in self.World.Queries.OwnedBy[self.Owner] + .WithTrait() + .Where(x => x.Trait.IsPrimary + && (x.Actor.Info.Traits.Get().Produces.Contains(p)))) + { + b.Trait.SetPrimaryProducer(b.Actor, false); + } + } + isPrimary = true; + + var eva = self.Owner.PlayerActor.Info.Traits.Get(); + Sound.PlayToPlayer(self.Owner,eva.PrimaryBuildingSelected); + } + } +} diff --git a/OpenRA.Game/Traits/ProductionSurround.cs b/OpenRA.Game/Traits/ProductionSurround.cs new file mode 100644 index 0000000000..6a02267b7d --- /dev/null +++ b/OpenRA.Game/Traits/ProductionSurround.cs @@ -0,0 +1,60 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.GameRules; + +namespace OpenRA.Traits +{ + class ProductionSurroundInfo : ProductionInfo + { + public override object Create(Actor self) { return new ProductionSurround(self); } + } + + class ProductionSurround : Production + { + public ProductionSurround(Actor self) : base(self) { } + + static int2? FindAdjacentTile(Actor self, UnitMovementType umt) + { + var tiles = Footprint.Tiles(self, self.traits.Get()); + var min = tiles.Aggregate(int2.Min) - new int2(1, 1); + var max = tiles.Aggregate(int2.Max) + new int2(1, 1); + + for (var j = min.Y; j <= max.Y; j++) + for (var i = min.X; i <= max.X; i++) + if (self.World.IsCellBuildable(new int2(i, j), umt)) + return new int2(i, j); + + return null; + } + + public override int2? CreationLocation(Actor self, ActorInfo producee) + { + return FindAdjacentTile(self, producee.Traits.Get().WaterBound ? + UnitMovementType.Float : UnitMovementType.Wheel); /* hackety hack */ + } + + public override int CreationFacing(Actor self, Actor newUnit) + { + return Util.GetFacing(newUnit.CenterLocation - self.CenterLocation, 128); + } + } +} diff --git a/OpenRA.Game/Traits/ProvidesRadar.cs b/OpenRA.Game/Traits/ProvidesRadar.cs new file mode 100644 index 0000000000..e17656e54c --- /dev/null +++ b/OpenRA.Game/Traits/ProvidesRadar.cs @@ -0,0 +1,55 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + class ProvidesRadarInfo : ITraitInfo + { + public object Create( Actor self ) { return new ProvidesRadar(); } + } + + class ProvidesRadar : ITick + { + public bool IsActive { get; private set; } + + public void Tick(Actor self) { IsActive = UpdateActive(self); } + + bool UpdateActive(Actor self) + { + // Check if powered + var b = self.traits.Get(); + if (b.Disabled) return false; + + var isJammed = self.World.Queries.WithTrait().Any(a => self.Owner != a.Actor.Owner + && (self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get().Range); + + return !isJammed; + } + } + + class JamsRadarInfo : StatelessTraitInfo + { + public readonly int Range = 0; + } + + class JamsRadar { } +} diff --git a/OpenRA.Game/Traits/RallyPoint.cs b/OpenRA.Game/Traits/RallyPoint.cs new file mode 100644 index 0000000000..a932ff4122 --- /dev/null +++ b/OpenRA.Game/Traits/RallyPoint.cs @@ -0,0 +1,69 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RallyPointInfo : ITraitInfo, ITraitPrerequisite + { + public readonly int[] RallyPoint = { 1, 3 }; + + public object Create(Actor self) { return new RallyPoint(self); } + } + + public class RallyPoint : IRender, IIssueOrder, IResolveOrder, ITick + { + [Sync] + public int2 rallyPoint; + public Animation anim; + + public RallyPoint(Actor self) + { + var info = self.Info.Traits.Get(); + rallyPoint = self.Location + new int2(info.RallyPoint[0], info.RallyPoint[1]); + anim = new Animation("flagfly"); + anim.PlayRepeating("idle"); + } + + public IEnumerable Render(Actor self) + { + if (self.Owner == self.World.LocalPlayer && Game.controller.selection.Actors.Contains(self)) + yield return Util.Centered(self, + anim.Image, Util.CenterOfCell(rallyPoint)); + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Left || underCursor != null) return null; + return new Order("SetRallyPoint", self, xy); + } + + public void ResolveOrder( Actor self, Order order ) + { + if( order.OrderString == "SetRallyPoint" ) + rallyPoint = order.TargetLocation; + } + + public void Tick(Actor self) { anim.Tick(); } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuilding.cs b/OpenRA.Game/Traits/Render/RenderBuilding.cs new file mode 100644 index 0000000000..78142dbf25 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuilding.cs @@ -0,0 +1,137 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Effects; + +namespace OpenRA.Traits +{ + public class RenderBuildingInfo : RenderSimpleInfo + { + public readonly bool HasMakeAnimation = true; + public override object Create(Actor self) { return new RenderBuilding(self);} + } + + public class RenderBuilding : RenderSimple, INotifyDamage, INotifySold + { + static readonly int[] bibStarts = { 0, 0, 1, 5, 11 }; + + public RenderBuilding( Actor self ) + : this( self, () => 0 ) + { + } + + public RenderBuilding(Actor self, Func baseFacing) + : base(self, baseFacing) + { + if( Game.skipMakeAnims || !self.Info.Traits.Get().HasMakeAnimation ) + Complete( self ); + else + anim.PlayThen( "make", () => self.World.AddFrameEndTask( _ => Complete( self ) ) ); + + DoBib(self, false); + } + + void Complete( Actor self ) + { + anim.PlayRepeating( GetPrefix(self) + "idle" ); + foreach( var x in self.traits.WithInterface() ) + x.BuildingComplete( self ); + } + + void DoBib(Actor self, bool isRemove) + { + var buildingInfo = self.Info.Traits.Get(); + if (buildingInfo.Bib) + { + var size = buildingInfo.Dimensions.X; + var bibOffset = buildingInfo.Dimensions.Y - 1; + var startIndex = bibStarts[size]; + + for (int i = 0; i < 2 * size; i++) + { + var p = self.Location + new int2(i % size, i / size + bibOffset); + if (isRemove) + { + if (self.World.Map.MapTiles[p.X, p.Y].smudge == (byte)(i + startIndex)) + self.World.Map.MapTiles[ p.X, p.Y ].smudge = 0; + } + else + self.World.Map.MapTiles[p.X, p.Y].smudge = (byte)(i + startIndex); + } + } + } + + protected string GetPrefix(Actor self) + { + return self.GetDamageState() == DamageState.Half ? "damaged-" : ""; + } + + public void PlayCustomAnim(Actor self, string name) + { + anim.PlayThen(GetPrefix(self) + name, + () => anim.PlayRepeating(GetPrefix(self) + "idle")); + } + + public void PlayCustomAnimThen(Actor self, string name, Action a) + { + anim.PlayThen(GetPrefix(self) + name, + () => { anim.PlayRepeating(GetPrefix(self) + "idle"); a(); }); + } + + public void PlayCustomAnimBackwards(Actor self, string name, Action a) + { + anim.PlayBackwardsThen(GetPrefix(self) + name, + () => { anim.PlayRepeating(GetPrefix(self) + "idle"); a(); }); + } + + public virtual void Damaged(Actor self, AttackInfo e) + { + if (!e.DamageStateChanged) + return; + + switch( e.DamageState ) + { + case DamageState.Normal: + anim.ReplaceAnim("idle"); + break; + case DamageState.Half: + anim.ReplaceAnim("damaged-idle"); + Sound.Play("kaboom1.aud"); + break; + case DamageState.Dead: + DoBib(self, true); + self.World.AddFrameEndTask(w => w.Add(new Explosion(w, self.CenterLocation.ToInt2(), 7, false))); + break; + } + } + + public void Selling( Actor self ) + { + if( !Game.skipMakeAnims && self.Info.Traits.Get().HasMakeAnimation ) + anim.PlayBackwardsThen( "make", null ); + + foreach (var s in self.Info.Traits.Get().SellSounds) + Sound.PlayToPlayer(self.Owner, s); + } + + public void Sold(Actor self) { DoBib(self, true); } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuildingCharge.cs b/OpenRA.Game/Traits/Render/RenderBuildingCharge.cs new file mode 100644 index 0000000000..fe3dc9e817 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuildingCharge.cs @@ -0,0 +1,44 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class RenderBuildingChargeInfo : RenderBuildingInfo + { + public readonly string ChargeAudio = "tslachg2.aud"; + public override object Create(Actor self) { return new RenderBuildingCharge(self); } + } + + /* used for tesla */ + class RenderBuildingCharge : RenderBuilding, INotifyAttack + { + public RenderBuildingCharge(Actor self) + : base(self) + { + } + + public void Attacking(Actor self) + { + Sound.Play(self.Info.Traits.Get().ChargeAudio); + anim.PlayThen(GetPrefix(self) + "active", + () => anim.PlayRepeating(GetPrefix(self) + "idle")); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuildingOre.cs b/OpenRA.Game/Traits/Render/RenderBuildingOre.cs new file mode 100644 index 0000000000..b5381cc4c0 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuildingOre.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class RenderBuildingOreInfo : RenderBuildingInfo + { + public override object Create(Actor self) { return new RenderBuildingOre(self); } + } + + class RenderBuildingOre : RenderBuilding, INotifyBuildComplete + { + public RenderBuildingOre(Actor self) + : base(self) + { + } + + public void BuildingComplete( Actor self ) + { + anim.PlayFetchIndex( "idle", () => (int)( 4.9 * self.Owner.GetSiloFullness() ) ); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuildingTurreted.cs b/OpenRA.Game/Traits/Render/RenderBuildingTurreted.cs new file mode 100644 index 0000000000..bb031c1f97 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuildingTurreted.cs @@ -0,0 +1,56 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class RenderBuildingTurretedInfo : RenderBuildingInfo + { + public override object Create(Actor self) { return new RenderBuildingTurreted(self); } + } + + class RenderBuildingTurreted : RenderBuilding, INotifyBuildComplete + { + public RenderBuildingTurreted(Actor self) + : base(self, () => self.traits.Get().turretFacing) + { + } + + public void BuildingComplete( Actor self ) + { + anim.Play( "idle" ); + } + + public override void Damaged(Actor self, AttackInfo e) + { + if (!e.DamageStateChanged) return; + + switch (e.DamageState) + { + case DamageState.Normal: + anim.Play( "idle" ); + break; + case DamageState.Half: + anim.Play( "damaged-idle" ); + Sound.Play("kaboom1.aud"); + break; + } + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuildingWall.cs b/OpenRA.Game/Traits/Render/RenderBuildingWall.cs new file mode 100644 index 0000000000..bf27e730d3 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuildingWall.cs @@ -0,0 +1,103 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + class RenderBuildingWallInfo : RenderBuildingInfo + { + public readonly int DamageStates = 2; + public override object Create(Actor self) { return new RenderBuildingWall(self); } + } + + class RenderBuildingWall : RenderBuilding + { + string seqName; + int damageStates; + Actor self; + + public RenderBuildingWall(Actor self) + : base(self) + { + seqName = "idle"; + this.self = self; + this.damageStates = self.Info.Traits.Get().DamageStates; + } + + public override void Damaged(Actor self, AttackInfo e) + { + if (!e.DamageStateChanged) return; + + switch (e.DamageState) + { + case DamageState.Normal: + seqName = "idle"; + break; + case DamageState.ThreeQuarter: + if (damageStates >= 4) + seqName = "minor-damaged-idle"; + break; + case DamageState.Half: + seqName = "damaged-idle"; + Sound.Play("kaboom1.aud"); + break; + case DamageState.Quarter: + if (damageStates >= 3) + { + seqName = "critical-idle"; + Sound.Play("kaboom1.aud"); + } + break; + } + } + + public override void Tick(Actor self) + { + base.Tick(self); + + // TODO: This only needs updating when a wall is built or destroyed + int index = NearbyWalls( self.Location ); + + anim.PlayFetchIndex(seqName, () => index); + + } + bool IsWall( int x, int y) + { + return self.World.Queries.WithTrait().Any(a => (a.Actor.Info.Name == self.Info.Name && a.Actor.Location.X == x && a.Actor.Location.Y == y)); + } + + int NearbyWalls( int2 xy ) + { + int ret = 0; + + if( IsWall( xy.X, xy.Y - 1 ) ) + ret |= 1; + if( IsWall( xy.X + 1, xy.Y ) ) + ret |= 2; + if( IsWall( xy.X, xy.Y + 1 ) ) + ret |= 4; + if( IsWall( xy.X - 1, xy.Y ) ) + ret |= 8; + return ret; + } + + } +} diff --git a/OpenRA.Game/Traits/Render/RenderBuildingWarFactory.cs b/OpenRA.Game/Traits/Render/RenderBuildingWarFactory.cs new file mode 100644 index 0000000000..053ba3b9a3 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderBuildingWarFactory.cs @@ -0,0 +1,89 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderWarFactoryInfo : ITraitInfo, ITraitPrerequisite + { + public object Create(Actor self) { return new RenderWarFactory(self); } + } + + class RenderWarFactory : INotifyBuildComplete, INotifyDamage, ITick, INotifyProduction, INotifySold + { + public Animation roof; + [Sync] + bool isOpen; + + string GetPrefix(Actor self) + { + return self.GetDamageState() == DamageState.Half ? "damaged-" : ""; + } + + public RenderWarFactory(Actor self) + { + roof = new Animation(self.traits.Get().GetImage(self)); + } + + public void BuildingComplete( Actor self ) + { + roof.Play( GetPrefix(self) + "idle-top" ); + self.traits.Get().anims.Add( "roof", new RenderSimple.AnimationWithOffset( roof ) { ZOffset = 2 } ); + } + + public void Tick(Actor self) + { + if (isOpen && !self.World.WorldActor.traits.Get() + .GetUnitsAt(((1/24f) * self.CenterLocation).ToInt2()).Any()) + { + isOpen = false; + roof.PlayBackwardsThen(GetPrefix(self) + "build-top", () => roof.Play(GetPrefix(self) + "idle-top")); + } + } + + public void Damaged(Actor self, AttackInfo e) + { + if (!e.DamageStateChanged) return; + switch (e.DamageState) + { + case DamageState.Normal: + roof.ReplaceAnim(roof.CurrentSequence.Name.Replace("damaged-","")); + break; + case DamageState.Half: + roof.ReplaceAnim("damaged-" + roof.CurrentSequence.Name); + break; + } + } + + public void UnitProduced(Actor self, Actor other) + { + roof.PlayThen(GetPrefix(self) + "build-top", () => isOpen = true); + } + + public void Selling( Actor self ) + { + self.traits.Get().anims.Remove( "roof" ); + } + + public void Sold( Actor self ) { } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderInfantry.cs b/OpenRA.Game/Traits/Render/RenderInfantry.cs new file mode 100644 index 0000000000..f97107ea6c --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderInfantry.cs @@ -0,0 +1,96 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Effects; + +namespace OpenRA.Traits +{ + public class RenderInfantryInfo : RenderSimpleInfo + { + public override object Create(Actor self) { return new RenderInfantry(self); } + } + + public class RenderInfantry : RenderSimple, INotifyAttack, INotifyDamage + { + public RenderInfantry(Actor self) + : base(self, () => self.traits.Get().Facing) + { + anim.Play("stand"); + } + + bool ChooseMoveAnim(Actor self) + { + if (!(self.GetCurrentActivity() is Activities.Move)) + return false; + + var mobile = self.traits.Get(); + if (float2.WithinEpsilon(self.CenterLocation, Util.CenterOfCell(mobile.toCell), 2)) return false; + + var seq = IsProne(self) ? "crawl" : "run"; + + if (anim.CurrentSequence.Name != seq) + anim.PlayRepeating(seq); + + return true; + } + + bool inAttack = false; + bool IsProne(Actor self) + { + var takeCover = self.traits.GetOrDefault(); + return takeCover != null && takeCover.IsProne; + } + + public void Attacking(Actor self) + { + inAttack = true; + + var seq = IsProne(self) ? "prone-shoot" : "shoot"; + + if (anim.HasSequence(seq)) + anim.PlayThen(seq, () => inAttack = false); + else if (anim.HasSequence("heal")) + anim.PlayThen("heal", () => inAttack = false); + } + + public override void Tick(Actor self) + { + base.Tick(self); + if (inAttack) return; + if (ChooseMoveAnim(self)) return; + + /* todo: idle anims, etc */ + + if (IsProne(self)) + anim.PlayFetchIndex("crawl", () => 0); /* what a hack. */ + else + anim.Play("stand"); + } + + public void Damaged(Actor self, AttackInfo e) + { + if (e.DamageState == DamageState.Dead) + { + Sound.PlayVoice("Die", self); + self.World.AddFrameEndTask(w => w.Add(new Corpse(self, e.Warhead.InfDeath))); + } + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderSimple.cs b/OpenRA.Game/Traits/Render/RenderSimple.cs new file mode 100644 index 0000000000..f4d4793fa8 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderSimple.cs @@ -0,0 +1,95 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + public abstract class RenderSimpleInfo : ITraitInfo + { + public readonly string Image = null; + public readonly string Palette = null; + public abstract object Create(Actor self); + } + + public abstract class RenderSimple : IRender, ITick + { + public Dictionary anims = new Dictionary(); + public Animation anim { get { return anims[""].Animation; } protected set { anims[""].Animation = value; } } + + public string GetImage(Actor self) + { + return self.Info.Traits.Get().Image ?? self.Info.Name; + } + + public RenderSimple(Actor self, Func baseFacing) + { + anims.Add( "", new Animation( GetImage(self), baseFacing ) ); + } + + public virtual IEnumerable Render( Actor self ) + { + var palette = self.Info.Traits.Get().Palette; + foreach( var a in anims.Values ) + if( a.DisableFunc == null || !a.DisableFunc() ) + yield return ( palette == null ) ? a.Image( self ) : a.Image( self ).WithPalette(palette); + } + + public virtual void Tick(Actor self) + { + foreach( var a in anims.Values ) + a.Animation.Tick(); + } + + public class AnimationWithOffset + { + public Animation Animation; + public Func OffsetFunc; + public Func DisableFunc; + public int ZOffset; + + public AnimationWithOffset( Animation a ) + : this( a, null, null ) + { + } + + public AnimationWithOffset( Animation a, Func o, Func d ) + { + this.Animation = a; + this.OffsetFunc = o; + this.DisableFunc = d; + } + + public Renderable Image( Actor self ) + { + var r = Util.Centered( self, Animation.Image, self.CenterLocation + + (OffsetFunc != null ? OffsetFunc() : float2.Zero) ); + return ZOffset != 0 ? r.WithZOffset(ZOffset) : r; + } + + public static implicit operator AnimationWithOffset( Animation a ) + { + return new AnimationWithOffset( a ); + } + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnit.cs b/OpenRA.Game/Traits/Render/RenderUnit.cs new file mode 100644 index 0000000000..c766d0af8a --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnit.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderUnitInfo : RenderSimpleInfo + { + public override object Create(Actor self) { return new RenderUnit(self); } + } + + class RenderUnit : RenderSimple, INotifyDamage + { + public RenderUnit(Actor self) + : base(self, () => self.traits.Get().Facing) + { + anim.Play("idle"); + + anims.Add( "smoke", new AnimationWithOffset( new Animation( "smoke_m" ), null, () => !isSmoking ) ); + } + + public void PlayCustomAnimation(Actor self, string newAnim, Action after) + { + anim.PlayThen(newAnim, () => { anim.Play("idle"); if (after != null) after(); }); + } + + bool isSmoking; + + public void Damaged(Actor self, AttackInfo e) + { + if (e.DamageState != DamageState.Half) return; + if (isSmoking) return; + + isSmoking = true; + var smoke = anims[ "smoke" ].Animation; + smoke.PlayThen( "idle", + () => smoke.PlayThen( "loop", + () => smoke.PlayBackwardsThen( "end", + () => isSmoking = false ) ) ); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnitMuzzleFlash.cs b/OpenRA.Game/Traits/Render/RenderUnitMuzzleFlash.cs new file mode 100644 index 0000000000..00523dba80 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnitMuzzleFlash.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderUnitMuzzleFlashInfo : RenderUnitInfo + { + public override object Create(Actor self) { return new RenderUnitMuzzleFlash(self); } + } + + class RenderUnitMuzzleFlash : RenderUnit + { + public RenderUnitMuzzleFlash(Actor self) + : base(self) + { + var unit = self.traits.Get(); + var attack = self.traits.Get(); + var attackInfo = self.Info.Traits.Get(); + + var muzzleFlash = new Animation(GetImage(self), ()=>unit.Facing); + muzzleFlash.PlayFetchIndex("muzzle", + () => (int)(attack.primaryRecoil * 5.9f)); + anims.Add( "muzzle", new AnimationWithOffset( + muzzleFlash, + () => attackInfo.PrimaryOffset.AbsOffset(), + () => attack.primaryRecoil <= 0 ) ); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnitReload.cs b/OpenRA.Game/Traits/Render/RenderUnitReload.cs new file mode 100644 index 0000000000..87c302a77d --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnitReload.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class RenderUnitReloadInfo : RenderUnitInfo + { + public override object Create(Actor self) { return new RenderUnitReload(self); } + } + + class RenderUnitReload : RenderUnit + { + public RenderUnitReload(Actor self) + : base(self) { } + + public override void Tick(Actor self) + { + var isAttacking = self.GetCurrentActivity() is Activities.Attack; + + var attack = self.traits.GetOrDefault(); + + if (attack != null) + anim.ReplaceAnim((attack.IsReloading() ? "empty-" : "") + + (isAttacking ? "aim" : "idle")); + base.Tick(self); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnitRotor.cs b/OpenRA.Game/Traits/Render/RenderUnitRotor.cs new file mode 100644 index 0000000000..4b87691964 --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnitRotor.cs @@ -0,0 +1,76 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderUnitRotorInfo : RenderUnitInfo + { + public readonly int[] PrimaryOffset = { 0, 0 }; + public readonly int[] SecondaryOffset = null; + + public override object Create(Actor self) { return new RenderUnitRotor(self); } + } + + class RenderUnitRotor : RenderUnit + { + public Animation rotorAnim, secondRotorAnim; + + public RenderUnitRotor( Actor self ) + : base(self) + { + var unit = self.traits.Get(); + var info = self.Info.Traits.Get(); + + rotorAnim = new Animation(GetImage(self)); + rotorAnim.PlayRepeating("rotor"); + anims.Add( "rotor_1", new AnimationWithOffset( + rotorAnim, + () => Util.GetTurretPosition( self, unit, info.PrimaryOffset, 0 ), + null ) { ZOffset = 1 } ); + + if (info.SecondaryOffset == null) return; + + secondRotorAnim = new Animation(GetImage(self)); + secondRotorAnim.PlayRepeating( "rotor2" ); + anims.Add( "rotor_2", new AnimationWithOffset( + secondRotorAnim, + () => Util.GetTurretPosition(self, unit, info.SecondaryOffset, 0), + null) { ZOffset = 1 }); + } + + public override void Tick(Actor self) + { + base.Tick(self); + + var unit = self.traits.Get(); + + var isFlying = unit.Altitude > 0; + + if (isFlying ^ (rotorAnim.CurrentSequence.Name != "rotor")) + return; + + rotorAnim.ReplaceAnim(isFlying ? "rotor" : "slow-rotor"); + if (secondRotorAnim != null) + secondRotorAnim.ReplaceAnim(isFlying ? "rotor2" : "slow-rotor2"); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnitSpinner.cs b/OpenRA.Game/Traits/Render/RenderUnitSpinner.cs new file mode 100644 index 0000000000..9f181354de --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnitSpinner.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderUnitSpinnerInfo : RenderUnitInfo + { + public readonly int[] Offset = { 0, 0 }; + public override object Create(Actor self) { return new RenderUnitSpinner(self); } + } + + class RenderUnitSpinner : RenderUnit + { + public RenderUnitSpinner( Actor self ) + : base(self) + { + var unit = self.traits.Get(); + var info = self.Info.Traits.Get(); + + var spinnerAnim = new Animation( GetImage(self) ); + spinnerAnim.PlayRepeating( "spinner" ); + anims.Add( "spinner", new AnimationWithOffset( + spinnerAnim, + () => Util.GetTurretPosition( self, unit, info.Offset, 0 ), + null ) { ZOffset = 1 } ); + } + } +} diff --git a/OpenRA.Game/Traits/Render/RenderUnitTurreted.cs b/OpenRA.Game/Traits/Render/RenderUnitTurreted.cs new file mode 100644 index 0000000000..af7ec971af --- /dev/null +++ b/OpenRA.Game/Traits/Render/RenderUnitTurreted.cs @@ -0,0 +1,67 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Graphics; + +namespace OpenRA.Traits +{ + class RenderUnitTurretedInfo : RenderUnitInfo + { + public override object Create(Actor self) { return new RenderUnitTurreted(self); } + } + + class RenderUnitTurreted : RenderUnit + { + public RenderUnitTurreted(Actor self) + : base(self) + { + var unit = self.traits.Get(); + var turreted = self.traits.Get(); + var attack = self.traits.GetOrDefault(); + var attackInfo = self.Info.Traits.Get(); + + var turretAnim = new Animation(GetImage(self), () => turreted.turretFacing ); + turretAnim.Play( "turret" ); + + if( attackInfo.PrimaryOffset != null ) + anims.Add("turret_1", new AnimationWithOffset( + turretAnim, + () => Util.GetTurretPosition(self, unit, attackInfo.PrimaryOffset, attack.primaryRecoil), + null) { ZOffset = 1 }); + + if (attackInfo.SecondaryOffset != null) + anims.Add("turret_2", new AnimationWithOffset( + turretAnim, + () => Util.GetTurretPosition(self, unit, attackInfo.SecondaryOffset, attack.secondaryRecoil), + null) { ZOffset = 1 }); + + if( attackInfo.MuzzleFlash ) + { + var muzzleFlash = new Animation( GetImage(self), () => self.traits.Get().turretFacing ); + muzzleFlash.PlayFetchIndex( "muzzle", + () => (int)( attack.primaryRecoil * 5.9f ) ); /* hack: recoil can be 1.0f, but don't overflow into next anim */ + anims.Add( "muzzle_flash", new AnimationWithOffset( + muzzleFlash, + () => Util.GetTurretPosition(self, unit, attackInfo.PrimaryOffset, attack.primaryRecoil), + () => attack.primaryRecoil <= 0 ) ); + } + } + } +} diff --git a/OpenRA.Game/Traits/Repairable.cs b/OpenRA.Game/Traits/Repairable.cs new file mode 100644 index 0000000000..819ef65090 --- /dev/null +++ b/OpenRA.Game/Traits/Repairable.cs @@ -0,0 +1,72 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class RepairableInfo : ITraitInfo + { + public object Create(Actor self) { return new Repairable(self); } + } + + class Repairable : IIssueOrder, IResolveOrder + { + IDisposable reservation; + public Repairable(Actor self) { } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + + if (underCursor.Info.Name == "fix" + && underCursor.Owner == self.Owner + && !Reservable.IsReserved(underCursor)) + return new Order("Enter", self, underCursor); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (reservation != null) + { + reservation.Dispose(); + reservation = null; + } + + if (order.OrderString == "Enter") + { + if (Reservable.IsReserved(order.TargetActor)) + return; + + var res = order.TargetActor.traits.GetOrDefault(); + if (res != null) reservation = res.Reserve(self); + + self.CancelActivity(); + self.QueueActivity(new Move(((1 / 24f) * order.TargetActor.CenterLocation).ToInt2(), order.TargetActor)); + self.QueueActivity(new Rearm()); + self.QueueActivity(new Repair(true)); + } + } + } +} diff --git a/OpenRA.Game/Traits/Reservable.cs b/OpenRA.Game/Traits/Reservable.cs new file mode 100644 index 0000000000..6c52f580c2 --- /dev/null +++ b/OpenRA.Game/Traits/Reservable.cs @@ -0,0 +1,55 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; + +namespace OpenRA.Traits +{ + class ReservableInfo : ITraitInfo + { + public object Create(Actor self) { return new Reservable(self); } + } + + class Reservable : ITick + { + public Reservable(Actor self) { } + Actor reservedFor; + + public void Tick(Actor self) + { + if (reservedFor == null) + return; /* nothing to do */ + + if (reservedFor.IsDead) reservedFor = null; /* not likely to arrive now. */ + } + + public IDisposable Reserve(Actor forActor) + { + reservedFor = forActor; + return new DisposableAction(() => reservedFor = null); + } + + public static bool IsReserved(Actor a) + { + var res = a.traits.GetOrDefault(); + return res != null && res.reservedFor != null; + } + } +} diff --git a/OpenRA.Game/Traits/SeedsOre.cs b/OpenRA.Game/Traits/SeedsOre.cs new file mode 100644 index 0000000000..378b110789 --- /dev/null +++ b/OpenRA.Game/Traits/SeedsOre.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class SeedsOreInfo : ITraitInfo + { + public readonly float Chance = .05f; + public readonly int Interval = 5; + + public object Create(Actor self) { return new SeedsOre(); } + } + + class SeedsOre : ITick + { + int ticks; + + public void Tick(Actor self) + { + if (--ticks <= 0) + { + var info = self.Info.Traits.Get(); + + for (var j = -1; j < 2; j++) + for (var i = -1; i < 2; i++) + if (self.World.SharedRandom.NextDouble() < info.Chance) + if (self.World.OreCanSpreadInto(self.Location.X + i, self.Location.Y + j)) + self.World.Map.AddOre(self.Location.X + i, self.Location.Y + j); + + ticks = info.Interval; + } + } + } +} diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs new file mode 100755 index 0000000000..d836a7502c --- /dev/null +++ b/OpenRA.Game/Traits/Selectable.cs @@ -0,0 +1,31 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + public class SelectableInfo : StatelessTraitInfo + { + public readonly int Priority = 10; + public readonly int[] Bounds = null; + public readonly string Voice = "GenericVoice"; + } + + public class Selectable {} +} diff --git a/OpenRA.Game/Traits/SquishByTank.cs b/OpenRA.Game/Traits/SquishByTank.cs new file mode 100644 index 0000000000..cd18e21a69 --- /dev/null +++ b/OpenRA.Game/Traits/SquishByTank.cs @@ -0,0 +1,56 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class SquishByTankInfo : ITraitInfo + { + public object Create(Actor self) { return new SquishByTank(self); } + } + + class SquishByTank : ICrushable + { + readonly Actor self; + public SquishByTank(Actor self) + { + this.self = self; + } + + public void OnCrush(Actor crusher) + { + self.InflictDamage(crusher, self.Health, Rules.WarheadInfo["Crush"]); + } + + public bool IsPathableCrush(UnitMovementType umt, Player player) + { + return IsCrushableBy(umt, player); + } + + public bool IsCrushableBy(UnitMovementType umt, Player player) + { + if (player == self.Owner) return false; + switch (umt) + { + case UnitMovementType.Track: return true; + default: return false; + } + } + } +} diff --git a/OpenRA.Game/Traits/StoresOre.cs b/OpenRA.Game/Traits/StoresOre.cs new file mode 100644 index 0000000000..77356c176b --- /dev/null +++ b/OpenRA.Game/Traits/StoresOre.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class StoresOreInfo : StatelessTraitInfo + { + public readonly int Pips = 0; + public readonly int Capacity = 0; + } + + class StoresOre : IPips, IAcceptThief + { + public void OnSteal(Actor self, Actor thief) + { + // Steal half the ore the building holds + var toSteal = self.Info.Traits.Get().Capacity / 2; + self.Owner.TakeCash(toSteal); + thief.Owner.GiveCash(toSteal); + + var eva = thief.Owner.PlayerActor.Info.Traits.Get(); + Sound.PlayToPlayer(thief.Owner, eva.CreditsStolen); + } + + public IEnumerable GetPips(Actor self) + { + var numPips = self.Info.Traits.Get().Pips; + + return Graphics.Util.MakeArray( numPips, + i => (self.World.LocalPlayer.GetSiloFullness() > i * 1.0f / numPips) + ? PipType.Yellow : PipType.Transparent ); + } + } +} diff --git a/OpenRA.Game/Traits/Submarine.cs b/OpenRA.Game/Traits/Submarine.cs new file mode 100644 index 0000000000..b4912b51de --- /dev/null +++ b/OpenRA.Game/Traits/Submarine.cs @@ -0,0 +1,85 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class SubmarineInfo : ITraitInfo + { + public readonly float SubmergeDelay = 1.2f; // Seconds + public readonly string SubmergeSound = "subshow1.aud"; + public readonly string SurfaceSound = "subshow1.aud"; + public object Create(Actor self) { return new Submarine(self); } + } + + class Submarine : IRenderModifier, INotifyAttack, ITick, INotifyDamage + { + [Sync] + int remainingSurfaceTime = 2; /* setup for initial dive */ + + Actor self; + public Submarine(Actor self) + { + this.self = self; + } + + void DoSurface() + { + if (remainingSurfaceTime <= 0) + OnSurface(); + + remainingSurfaceTime = (int)(self.Info.Traits.Get().SubmergeDelay * 25); + } + + public void Attacking(Actor self) { DoSurface(); } + public void Damaged(Actor self, AttackInfo e) { DoSurface(); } + + public IEnumerable + ModifyRender(Actor self, IEnumerable rs) + { + if (remainingSurfaceTime > 0) + return rs; + + if (self.Owner == self.World.LocalPlayer) + return rs.Select(a => a.WithPalette("shadow")); + else + return new Renderable[] { }; + } + + public void Tick(Actor self) + { + if (remainingSurfaceTime > 0) + if (--remainingSurfaceTime <= 0) + OnDive(); + } + + void OnSurface() + { + Sound.Play(self.Info.Traits.Get().SurfaceSound); + } + + void OnDive() + { + Sound.Play(self.Info.Traits.Get().SubmergeSound); + } + } +} diff --git a/OpenRA.Game/Traits/SupportPowers/ChronoshiftPower.cs b/OpenRA.Game/Traits/SupportPowers/ChronoshiftPower.cs new file mode 100644 index 0000000000..23d74bf673 --- /dev/null +++ b/OpenRA.Game/Traits/SupportPowers/ChronoshiftPower.cs @@ -0,0 +1,187 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +namespace OpenRA.Traits +{ + class ChronoshiftPowerInfo : SupportPowerInfo + { + public readonly float Duration = 0f; + public readonly bool KillCargo = true; + public override object Create(Actor self) { return new ChronoshiftPower(self,this); } + } + + class ChronoshiftPower : SupportPower, IResolveOrder + { + public ChronoshiftPower(Actor self, ChronoshiftPowerInfo info) : base(self, info) { } + protected override void OnBeginCharging() { Sound.PlayToPlayer(Owner, "chrochr1.aud"); } + protected override void OnFinishCharging() { Sound.PlayToPlayer(Owner, "chrordy1.aud"); } + protected override void OnActivate() + { + Game.controller.orderGenerator = new SelectTarget(); + Sound.Play("slcttgt1.aud"); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "ChronosphereSelect" && self.Owner == self.World.LocalPlayer) + { + Game.controller.orderGenerator = new SelectDestination(order.TargetActor); + } + + if (order.OrderString == "ChronosphereActivate") + { + if (self.Owner == self.World.LocalPlayer) + Game.controller.CancelInputMode(); + + // Cannot chronoshift into unexplored location + if (!self.Owner.Shroud.IsExplored(order.TargetLocation)) + return; + + // Ensure the target cell is valid for the unit + var movement = order.TargetActor.traits.GetOrDefault(); + if (!movement.CanEnterCell(order.TargetLocation)) + return; + + var chronosphere = self.World.Queries + .OwnedBy[self.Owner] + .WithTrait() + .Select(x=>x.Actor).FirstOrDefault(); + + bool success = order.TargetActor.traits.Get().Activate(order.TargetActor, + order.TargetLocation, + (int)((Info as ChronoshiftPowerInfo).Duration * 25 * 60), + (Info as ChronoshiftPowerInfo).KillCargo, + chronosphere); + + if (success) + { + Sound.Play("chrono2.aud"); + + // Trigger screen desaturate effect + foreach (var a in self.World.Queries.WithTrait()) + a.Trait.DoChronoshift(); + + if (chronosphere != null) + chronosphere.traits.Get().PlayCustomAnim(chronosphere, "active"); + } + + FinishActivate(); + } + } + + class SelectTarget : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Owner != null && a.traits.Contains() + && a.traits.Contains()).FirstOrDefault(); + + if (underCursor != null) + yield return new Order("ChronosphereSelect", world.LocalPlayer.PlayerActor, underCursor); + } + + yield break; + } + + public void Tick( World world ) + { + var hasChronosphere = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(); + + if (!hasChronosphere) + Game.controller.CancelInputMode(); + + // TODO: Check if the selected unit is still alive + } + + public void Render( World world ) { } + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(world, xy, mi).Any() + ? "chrono-select" : "move-blocked"; + } + } + + class SelectDestination : IOrderGenerator + { + Actor self; + public SelectDestination(Actor self) { this.self = self; } + + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + { + Game.controller.CancelInputMode(); + yield break; + } + + yield return new Order("ChronosphereActivate", world.LocalPlayer.PlayerActor, self, xy); + } + + public void Tick(World world) + { + var hasChronosphere = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(); + + if (!hasChronosphere) + Game.controller.CancelInputMode(); + + // TODO: Check if the selected unit is still alive + } + + public void Render(World world) + { + world.WorldRenderer.DrawSelectionBox(self, Color.Red, true); + } + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + if (!world.LocalPlayer.Shroud.IsExplored(xy)) + return "move-blocked"; + + var movement = self.traits.GetOrDefault(); + return (movement.CanEnterCell(xy)) ? "chrono-target" : "move-blocked"; + } + } + } + + // tag trait to identify the building + class ChronosphereInfo : StatelessTraitInfo { } + public class Chronosphere { } +} diff --git a/OpenRA.Game/Traits/SupportPowers/NukePower.cs b/OpenRA.Game/Traits/SupportPowers/NukePower.cs new file mode 100644 index 0000000000..5aa75ec439 --- /dev/null +++ b/OpenRA.Game/Traits/SupportPowers/NukePower.cs @@ -0,0 +1,106 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class NukePowerInfo : SupportPowerInfo + { + public override object Create(Actor self) { return new NukePower(self, this); } + } + + class NukePower : SupportPower, IResolveOrder + { + public NukePower(Actor self, NukePowerInfo info) : base(self, info) { } + + protected override void OnBeginCharging() { Sound.PlayToPlayer(Owner, "aprep1.aud"); } + protected override void OnFinishCharging() { Sound.PlayToPlayer(Owner, "aready1.aud"); } + protected override void OnActivate() + { + Game.controller.orderGenerator = new SelectTarget(); + Sound.Play("slcttgt1.aud"); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "NuclearMissile") + { + var silo = self.World.Queries.OwnedBy[self.Owner] + .Where(a => a.traits.Contains()) + .FirstOrDefault(); + if (silo != null) + silo.traits.Get().PlayCustomAnim(silo, "active"); + + Owner.World.AddFrameEndTask(w => + { + // Play to everyone but the current player + if (Owner != Owner.World.LocalPlayer) + Sound.Play("alaunch1.aud"); + + // TODO: FIRE ZE MISSILES + //w.Add(new NukeLaunch(silo)); + }); + + Game.controller.CancelInputMode(); + FinishActivate(); + } + } + + class SelectTarget : IOrderGenerator + { + public SelectTarget() { } + + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + yield return new Order("NuclearMissile", world.LocalPlayer.PlayerActor, xy); + + yield break; + } + + public void Tick(World world) + { + var hasStructure = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(); + + if (!hasStructure) + Game.controller.CancelInputMode(); + } + + public void Render(World world) { } + public string GetCursor(World world, int2 xy, MouseInput mi) { return "nuke"; } + } + } + + // tag trait for the building + class NukeSiloInfo : StatelessTraitInfo { } + class NukeSilo { } +} diff --git a/OpenRA.Game/Traits/SupportPowers/SupportPower.cs b/OpenRA.Game/Traits/SupportPowers/SupportPower.cs new file mode 100644 index 0000000000..8736744033 --- /dev/null +++ b/OpenRA.Game/Traits/SupportPowers/SupportPower.cs @@ -0,0 +1,149 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + public abstract class SupportPowerInfo : ITraitInfo + { + public readonly bool RequiresPower = true; + public readonly bool OneShot = false; + public readonly float ChargeTime = 0; + public readonly string Image = null; + public readonly string Description = ""; + public readonly string LongDesc = ""; + public readonly string[] Prerequisites = { }; + public readonly int TechLevel = -1; + public readonly bool GivenAuto = true; + + public abstract object Create(Actor self); + } + + public class SupportPower : ITick + { + public readonly SupportPowerInfo Info; + public int RemainingTime { get; private set; } + public int TotalTime { get { return (int)(Info.ChargeTime * 60 * 25); } } + public bool IsUsed; + public bool IsAvailable; + public bool IsReady { get { return RemainingTime == 0; } } + public readonly Player Owner; + + bool notifiedCharging; + bool notifiedReady; + + public SupportPower(Actor self, SupportPowerInfo info) + { + Info = info; + RemainingTime = TotalTime; + Owner = self.Owner; + } + + public void Tick(Actor self) + { + if (Info.OneShot && IsUsed) + return; + + var buildings = Rules.TechTree.GatherBuildings(self.Owner); + var effectivePrereq = Info.Prerequisites + .Select(a => a.ToLowerInvariant()) + .Where(a => Rules.Info[a].Traits.Get().Owner.Contains(self.Owner.Country.Race)); + + if (Info.GivenAuto) + { + IsAvailable = Info.TechLevel > -1 + && effectivePrereq.Any() + && effectivePrereq.All(a => buildings[a].Count > 0); + } + + if (IsAvailable && (!Info.RequiresPower || IsPowered())) + { + if (RemainingTime > 0) --RemainingTime; + if (!notifiedCharging) + { + OnBeginCharging(); + notifiedCharging = true; + } + } + + if (RemainingTime == 0 + && !notifiedReady) + { + OnFinishCharging(); + notifiedReady = true; + } + } + + bool IsPowered() + { + var buildings = Rules.TechTree.GatherBuildings(Owner); + var effectivePrereq = Info.Prerequisites + .Select(a => a.ToLowerInvariant()) + .Where(a => Rules.Info[a].Traits.Get().Owner.Contains(Owner.Country.Race)); + + if (Info.Prerequisites.Count() == 0) + return Owner.GetPowerState() == PowerState.Normal; + + return effectivePrereq.Any() && + effectivePrereq.All(a => buildings[a].Any(b => !b.traits.Get().Disabled)); + } + + public void FinishActivate() + { + if (Info.OneShot) + { + IsUsed = true; + IsAvailable = false; + } + RemainingTime = TotalTime; + notifiedReady = false; + notifiedCharging = false; + } + + public void Give(float charge) + { + IsAvailable = true; + IsUsed = false; + RemainingTime = (int)(charge * TotalTime); + } + + protected virtual void OnBeginCharging() { } + protected virtual void OnFinishCharging() { } + protected virtual void OnActivate() { } + + public void Activate() + { + if (!IsAvailable || !IsReady) + { + Sound.Play("briefing.aud"); + return; + } + + if (Info.RequiresPower && !IsPowered()) + { + Sound.Play("nopowr1.aud"); + return; + } + + OnActivate(); + } + } +} diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs new file mode 100644 index 0000000000..e1007137a3 --- /dev/null +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -0,0 +1,143 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public enum DamageState { Normal, ThreeQuarter, Half, Quarter, Dead }; + + // depends on the order of pips in WorldRenderer.cs! + public enum PipType { Transparent, Green, Yellow, Red, Gray }; + public enum TagType { None, Fake, Primary }; + + public interface ITick { void Tick(Actor self); } + public interface IRender { IEnumerable Render(Actor self); } + public interface IIssueOrder { Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor ); } + public interface IResolveOrder { void ResolveOrder(Actor self, Order order); } + + public interface INotifySold { void Selling( Actor self ); void Sold( Actor self ); } + public interface INotifyDamage { void Damaged(Actor self, AttackInfo e); } + public interface INotifyBuildComplete { void BuildingComplete(Actor self); } + public interface INotifyProduction { void UnitProduced(Actor self, Actor other); } + public interface IAcceptOre + { + void OnDock(Actor harv, DeliverOre dockOrder); + int2 DeliverOffset { get; } + } + public interface IAcceptThief { void OnSteal(Actor self, Actor thief); } + public interface IAcceptSpy { void OnInfiltrate(Actor self, Actor spy); } + + public interface ICustomTerrain { float GetCost(int2 p, UnitMovementType umt); } + + public interface IDisable { bool Disabled { get; set; } } + + interface IProducer + { + bool Produce( Actor self, ActorInfo producee ); + void SetPrimaryProducer(Actor self, bool isPrimary); + } + public interface IOccupySpace { IEnumerable OccupiedCells(); } + public interface INotifyAttack { void Attacking(Actor self); } + public interface IRenderModifier { IEnumerable ModifyRender(Actor self, IEnumerable r); } + public interface IDamageModifier { float GetDamageModifier(); } + public interface ISpeedModifier { float GetSpeedModifier(); } + public interface IPowerModifier { float GetPowerModifier(); } + public interface IFirepowerModifier { float GetFirepowerModifier(); } + public interface IPaletteModifier { void AdjustPalette(Bitmap b); } + public interface IPips { IEnumerable GetPips(Actor self); } + public interface ITags { IEnumerable GetTags(); } + public interface IMovement + { + UnitMovementType GetMovementType(); + bool CanEnterCell(int2 location); + } + + public interface ICrushable + { + void OnCrush(Actor crusher); + bool IsCrushableBy(UnitMovementType umt, Player player); + bool IsPathableCrush(UnitMovementType umt, Player player); + } + + public interface ICrateAction + { + int SelectionShares { get; } + void Activate(Actor collector); + } + + public struct Renderable + { + public readonly Sprite Sprite; + public readonly float2 Pos; + public readonly string Palette; + public readonly int ZOffset; + + public Renderable(Sprite sprite, float2 pos, string palette, int zOffset) + { + Sprite = sprite; + Pos = pos; + Palette = palette; + ZOffset = zOffset; + } + + public Renderable(Sprite sprite, float2 pos, string palette) + : this(sprite, pos, palette, 0) { } + + public Renderable WithPalette(string newPalette) { return new Renderable(Sprite, Pos, newPalette, ZOffset); } + public Renderable WithZOffset(int newOffset) { return new Renderable(Sprite, Pos, Palette, newOffset); } + public Renderable WithPos(float2 newPos) { return new Renderable(Sprite, newPos, Palette, ZOffset); } + } + + public interface ITraitInfo { object Create(Actor self); } + + public class StatelessTraitInfo : ITraitInfo + where T : new() + { + static Lazy Instance = Lazy.New(() => new T()); + public object Create(Actor self) { return Instance.Value; } + } + + public interface ITraitPrerequisite { } + + public interface INotifySelection { void SelectionChanged(); } + public interface ILoadWorldHook { void WorldLoaded(World w); } + public interface IOnGameStart { void SpawnStartingUnits(Player p, int2 sp); } + + public interface IActivity + { + IActivity NextActivity { get; set; } + IActivity Tick(Actor self); + void Cancel(Actor self); + } + + public interface IChromeButton + { + string Image { get; } + bool Enabled { get; } + bool Pressed { get; } + void OnClick(); + } +} diff --git a/OpenRA.Game/Traits/TransformsOnDeploy.cs b/OpenRA.Game/Traits/TransformsOnDeploy.cs new file mode 100644 index 0000000000..5c1e62edaa --- /dev/null +++ b/OpenRA.Game/Traits/TransformsOnDeploy.cs @@ -0,0 +1,84 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + class TransformsOnDeployInfo : ITraitInfo + { + public readonly string TransformsInto = null; + public readonly int[] Offset = null; + public readonly int[] DeployDirections = new int[] {96}; + public readonly bool TransferHealthPercentage = true; // Set to false to transfer the absolute health + public readonly string[] TransformSounds = null; + public readonly string[] NoTransformSounds = null; + + public object Create(Actor self) { return new TransformsOnDeploy(self); } + } + + class TransformsOnDeploy : IIssueOrder, IResolveOrder + { + public TransformsOnDeploy(Actor self) { } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Right && self == underCursor) + return new Order("DeployTransform", self); + + return null; + } + + public void ResolveOrder( Actor self, Order order ) + { + if (order.OrderString == "DeployTransform") + { + var info = self.Info.Traits.Get(); + + var transInfo = Rules.Info[info.TransformsInto]; + + if (transInfo.Traits.Contains()) + { + var bi = transInfo.Traits.Get(); + if (!self.World.CanPlaceBuilding(info.TransformsInto, bi, self.Location + new int2(info.Offset[0], info.Offset[1]), self)) + { + foreach (var s in info.NoTransformSounds) + Sound.PlayToPlayer(self.Owner, s); + + return; + } + + } + self.CancelActivity(); + + // Pick the closed deploy direction to turn to + if (self.traits.Contains()) + { + // TODO: Pick the closest deploy direction + var bestDir = info.DeployDirections[0]; + + self.QueueActivity(new Turn(bestDir)); + } + + self.QueueActivity(new TransformIntoActor(info.TransformsInto, new int2(info.Offset[0], info.Offset[1]), info.TransferHealthPercentage, info.TransformSounds)); + } + } + } +} diff --git a/OpenRA.Game/Traits/Turreted.cs b/OpenRA.Game/Traits/Turreted.cs new file mode 100644 index 0000000000..3b0a6699ef --- /dev/null +++ b/OpenRA.Game/Traits/Turreted.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class TurretedInfo : ITraitInfo + { + public readonly int ROT = 255; + public readonly int InitialFacing = 128; + + public object Create(Actor self) { return new Turreted(self); } + } + + class Turreted : ITick + { + [Sync] + public int turretFacing = 0; + public int? desiredFacing; + + public Turreted(Actor self) + { + turretFacing = self.Info.Traits.Get().InitialFacing; + } + + public void Tick( Actor self ) + { + var df = desiredFacing ?? ( self.traits.Contains() ? self.traits.Get().Facing : turretFacing ); + Util.TickFacing(ref turretFacing, df, self.Info.Traits.Get().ROT); + } + } +} diff --git a/OpenRA.Game/Traits/Unit.cs b/OpenRA.Game/Traits/Unit.cs new file mode 100755 index 0000000000..16e0558d4a --- /dev/null +++ b/OpenRA.Game/Traits/Unit.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class UnitInfo : OwnedActorInfo, ITraitInfo + { + public readonly int InitialFacing = 128; + public readonly int ROT = 255; + public readonly int Speed = 1; + + public object Create( Actor self ) { return new Unit( self ); } + } + + public class Unit : INotifyDamage + { + [Sync] + public int Facing; + [Sync] + public int Altitude; + + public Unit( Actor self ) { } + + public void Damaged(Actor self, AttackInfo e) + { + var eva = self.Owner.PlayerActor.Info.Traits.Get(); + if (e.DamageState == DamageState.Dead) + Sound.PlayToPlayer(self.Owner, + self.Info.Traits.Get().WaterBound ? eva.NavalUnitLost : eva.UnitLost); + } + } +} diff --git a/OpenRA.Game/Traits/Util.cs b/OpenRA.Game/Traits/Util.cs new file mode 100755 index 0000000000..6fb76ec1b4 --- /dev/null +++ b/OpenRA.Game/Traits/Util.cs @@ -0,0 +1,165 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public static class Util + { + public static void TickFacing( ref int facing, int desiredFacing, int rot ) + { + var leftTurn = ( facing - desiredFacing ) & 0xFF; + var rightTurn = ( desiredFacing - facing ) & 0xFF; + if( Math.Min( leftTurn, rightTurn ) < rot ) + facing = desiredFacing & 0xFF; + else if( rightTurn < leftTurn ) + facing = ( facing + rot ) & 0xFF; + else + facing = ( facing - rot ) & 0xFF; + } + + static float2[] fvecs = Graphics.Util.MakeArray( 32, + i => -float2.FromAngle( i / 16.0f * (float)Math.PI ) * new float2( 1f, 1.3f ) ); + + public static int GetFacing( float2 d, int currentFacing ) + { + if( float2.WithinEpsilon( d, float2.Zero, 0.001f ) ) + return currentFacing; + + int highest = -1; + float highestDot = -1.0f; + + for( int i = 0 ; i < fvecs.Length ; i++ ) + { + float dot = float2.Dot( fvecs[ i ], d ); + if( dot > highestDot ) + { + highestDot = dot; + highest = i; + } + } + + return highest * 8; + } + + public static int GetNearestFacing( int facing, int desiredFacing ) + { + var turn = desiredFacing - facing; + if( turn > 128 ) + turn -= 256; + if( turn < -128 ) + turn += 256; + + return facing + turn; + } + + public static int QuantizeFacing(int facing, int numFrames) + { + var step = 256 / numFrames; + var a = (facing + step / 2) & 0xff; + return a / step; + } + + static float2 RotateVectorByFacing(float2 v, int facing, float ecc) + { + var angle = (facing / 256f) * (2 * (float)Math.PI); + var sinAngle = (float)Math.Sin(angle); + var cosAngle = (float)Math.Cos(angle); + + return new float2( + (cosAngle * v.X + sinAngle * v.Y), + ecc * (cosAngle * v.Y - sinAngle * v.X)); + } + + static float2 GetRecoil(Actor self, float recoil) + { + var abInfo = self.Info.Traits.GetOrDefault(); + if (abInfo == null || abInfo.Recoil == 0) return float2.Zero; + var rut = self.traits.GetOrDefault(); + if (rut == null) return float2.Zero; + + var facing = self.traits.Get().turretFacing; + var quantizedFacing = QuantizeFacing(facing, rut.anim.CurrentSequence.Facings) * (256 / rut.anim.CurrentSequence.Length); + + return RotateVectorByFacing(new float2(0, recoil * self.Info.Traits.Get().Recoil), quantizedFacing, .7f); + } + + public static float2 CenterOfCell(int2 loc) + { + return new float2(12, 12) + Game.CellSize * (float2)loc; + } + + public static float2 BetweenCells(int2 from, int2 to) + { + return 0.5f * (CenterOfCell(from) + CenterOfCell(to)); + } + + public static float2 GetTurretPosition(Actor self, Unit unit, int[] offset, float recoil) + { + if( unit == null ) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */ + + var ru = self.traits.GetOrDefault(); + var numDirs = (ru != null) ? ru.anim.CurrentSequence.Facings : 8; + var bodyFacing = unit.Facing; + var quantizedFacing = QuantizeFacing(bodyFacing, numDirs) * (256 / numDirs); + + return (RotateVectorByFacing(offset.RelOffset(), quantizedFacing, .7f) + GetRecoil(self, recoil)) + + offset.AbsOffset(); + } + + public static float2 RelOffset(this int[] offset) { return new float2(offset[0], offset[1]); } + public static float2 AbsOffset(this int[] offset) { return new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3)); } + + public static Renderable Centered(Actor self, Sprite s, float2 location) + { + var pal = self.Owner == null ? "player0" : self.Owner.Palette; + var loc = location - 0.5f * s.size; + return new Renderable(s, loc.Round(), pal); + } + + public static float GetEffectiveSpeed(Actor self) + { + var unitInfo = self.Info.Traits.GetOrDefault(); + if( unitInfo == null ) return 0f; + + var modifier = self.traits + .WithInterface() + .Select(t => t.GetSpeedModifier()) + .Product(); + return unitInfo.Speed * modifier; + } + + public static IActivity SequenceActivities(params IActivity[] acts) + { + return acts.Reverse().Aggregate( + (next, a) => { a.NextActivity = next; return a; }); + } + + public static float GetMaximumRange(Actor self) + { + return new[] { self.GetPrimaryWeapon(), self.GetSecondaryWeapon() } + .Where(w => w != null).Max(w => w.Range); + } + } +} diff --git a/OpenRA.Game/Traits/Wall.cs b/OpenRA.Game/Traits/Wall.cs new file mode 100644 index 0000000000..e1f1f8a443 --- /dev/null +++ b/OpenRA.Game/Traits/Wall.cs @@ -0,0 +1,32 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Effects; +using OpenRA.GameRules; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public class WallInfo : StatelessTraitInfo {} + public class Wall {} +} diff --git a/OpenRA.Game/Traits/World/BridgeLoadHook.cs b/OpenRA.Game/Traits/World/BridgeLoadHook.cs new file mode 100644 index 0000000000..1c7687c603 --- /dev/null +++ b/OpenRA.Game/Traits/World/BridgeLoadHook.cs @@ -0,0 +1,88 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Traits +{ + class BridgeLoadHookInfo : StatelessTraitInfo { } + + class BridgeLoadHook : ILoadWorldHook + { + static void MakeBridges(World w) + { + var mini = w.Map.XOffset; var maxi = w.Map.XOffset + w.Map.Width; + var minj = w.Map.YOffset; var maxj = w.Map.YOffset + w.Map.Height; + + for (var j = minj; j < maxj; j++) + for (var i = mini; i < maxi; i++) + if (IsBridge(w, w.Map.MapTiles[i, j].tile)) + ConvertBridgeToActor(w, i, j); + + foreach (var br in w.Queries.WithTraitMultiple()) + br.Trait.FinalizeBridges(w); + } + + static void ConvertBridgeToActor(World w, int i, int j) + { + var tile = w.Map.MapTiles[i, j].tile; + var image = w.Map.MapTiles[i, j].image; + var template = w.TileSet.walk[tile]; + + // base position of the tile + var ni = i - image % template.Size.X; + var nj = j - image / template.Size.X; + + var replacedTiles = new Dictionary(); + for (var y = nj; y < nj + template.Size.Y; y++) + for (var x = ni; x < ni + template.Size.X; x++) + { + var n = (x - ni) + template.Size.X * (y - nj); + if (!template.TerrainType.ContainsKey(n)) continue; + + if (w.Map.IsInMap(x, y)) + if (w.Map.MapTiles[x, y].tile == tile + && w.Map.MapTiles[x, y].image == n) + { + // stash it + replacedTiles[new int2(x, y)] = w.Map.MapTiles[x, y].image; + // remove the tile from the actual map + w.Map.MapTiles[x, y].tile = 0xfffe; + w.Map.MapTiles[x, y].image = 0; + } + } + + if (replacedTiles.Any()) + { + var a = w.CreateActor(template.Bridge, new int2(ni, nj), null); + var br = a.traits.Get(); + br.SetTiles(w, template, replacedTiles); + } + } + + static bool IsBridge(World w, ushort t) + { + return w.TileSet.walk[t].Bridge != null; + } + + public void WorldLoaded(World w) { MakeBridges(w); } + } +} diff --git a/OpenRA.Game/Traits/World/BuildingInfluence.cs b/OpenRA.Game/Traits/World/BuildingInfluence.cs new file mode 100644 index 0000000000..342d35419d --- /dev/null +++ b/OpenRA.Game/Traits/World/BuildingInfluence.cs @@ -0,0 +1,71 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.GameRules; + +namespace OpenRA.Traits +{ + public class BuildingInfluenceInfo : ITraitInfo + { + public object Create( Actor self ) { return new BuildingInfluence( self ); } + } + + public class BuildingInfluence + { + bool[,] blocked = new bool[128, 128]; + Actor[,] influence = new Actor[128, 128]; + + public BuildingInfluence( Actor self ) + { + self.World.ActorAdded += + a => { if (a.traits.Contains()) + ChangeInfluence(a, a.traits.Get(), true); }; + self.World.ActorRemoved += + a => { if (a.traits.Contains()) + ChangeInfluence(a, a.traits.Get(), false); }; + } + + void ChangeInfluence( Actor a, Building building, bool isAdd ) + { + foreach( var u in Footprint.UnpathableTiles( a.Info.Name, a.Info.Traits.Get(), a.Location ) ) + if( IsValid( u ) ) + blocked[ u.X, u.Y ] = isAdd; + + foreach( var u in Footprint.Tiles( a.Info.Name, a.Info.Traits.Get(), a.Location ) ) + if( IsValid( u ) ) + influence[ u.X, u.Y ] = isAdd ? a : null; + } + + bool IsValid(int2 t) + { + return !(t.X < 0 || t.Y < 0 || t.X >= 128 || t.Y >= 128); + } + + public Actor GetBuildingAt(int2 cell) + { + if (!IsValid(cell)) return null; + return influence[cell.X, cell.Y]; + } + + public bool CanMoveHere(int2 cell) + { + return IsValid(cell) && !blocked[cell.X, cell.Y]; + } + }} diff --git a/OpenRA.Game/Traits/World/ChoosePaletteOnSelect.cs b/OpenRA.Game/Traits/World/ChoosePaletteOnSelect.cs new file mode 100644 index 0000000000..152e8b9e95 --- /dev/null +++ b/OpenRA.Game/Traits/World/ChoosePaletteOnSelect.cs @@ -0,0 +1,44 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; + +namespace OpenRA.Traits +{ + class ChoosePaletteOnSelectInfo : StatelessTraitInfo { } + + class ChoosePaletteOnSelect : INotifySelection + { + public void SelectionChanged() + { + var firstItem = Game.controller.selection.Actors.FirstOrDefault( + a => a.World.LocalPlayer == a.Owner && a.traits.Contains()); + + if (firstItem == null) + return; + + var produces = firstItem.Info.Traits.Get().Produces.FirstOrDefault(); + if (produces == null) + return; + + Game.chrome.SetCurrentTab(produces); + } + } +} diff --git a/OpenRA.Game/Traits/World/ChronoshiftPaletteEffect.cs b/OpenRA.Game/Traits/World/ChronoshiftPaletteEffect.cs new file mode 100644 index 0000000000..5b11313128 --- /dev/null +++ b/OpenRA.Game/Traits/World/ChronoshiftPaletteEffect.cs @@ -0,0 +1,60 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.Traits +{ + class ChronoshiftPaletteEffectInfo : StatelessTraitInfo { } + + public class ChronoshiftPaletteEffect : IPaletteModifier, ITick + { + const int chronoEffectLength = 20; + int remainingFrames; + + public void DoChronoshift() + { + remainingFrames = chronoEffectLength; + } + + public void Tick(Actor self) + { + if (remainingFrames > 0) + remainingFrames--; + } + + public void AdjustPalette(Bitmap b) + { + if (remainingFrames == 0) + return; + /* TODO: FIX ME + var frac = (float)remainingFrames / chronoEffectLength; + for( var y = 0; y < (int)PaletteType.Chrome; y++ ) + for (var x = 0; x < 256; x++) + { + var orig = b.GetPixel(x, y); + var lum = (int)(255 * orig.GetBrightness()); + var desat = Color.FromArgb(orig.A, lum, lum, lum); + b.SetPixel(x, y, Graphics.Util.Lerp(frac, orig, desat)); + } + */ + } + } +} diff --git a/OpenRA.Game/Traits/World/Country.cs b/OpenRA.Game/Traits/World/Country.cs new file mode 100644 index 0000000000..f7d6fa850e --- /dev/null +++ b/OpenRA.Game/Traits/World/Country.cs @@ -0,0 +1,34 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + public class CountryInfo : ITraitInfo + { + public readonly string Name = null; + public readonly string Race = null; + + /* todo: icon,... */ + + public object Create(Actor self) { return new CountryInfo(); } + } + + class Country { /* we're only interested in the Info */ } +} diff --git a/OpenRA.Game/Traits/World/CrateSpawner.cs b/OpenRA.Game/Traits/World/CrateSpawner.cs new file mode 100644 index 0000000000..f62376401d --- /dev/null +++ b/OpenRA.Game/Traits/World/CrateSpawner.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; + +namespace OpenRA.Traits +{ + class CrateSpawnerInfo : ITraitInfo + { + public readonly int Minimum = 1; // Minumum number of crates + public readonly int Maximum = 255; // Maximum number of crates + public readonly int SpawnInterval = 180; // Average time (seconds) between crate spawn + public readonly float WaterChance = .2f; // Chance of generating a water crate instead of a land crate + + public object Create(Actor self) { return new CrateSpawner(); } + } + + // assumption: there is always at least one free water cell, and one free land cell. + + class CrateSpawner : ITick + { + List crates = new List(); + int ticks = 0; + + public void Tick(Actor self) + { + if (--ticks <= 0) + { + var info = self.Info.Traits.Get(); + ticks = info.SpawnInterval * 25; // todo: randomize + + crates.RemoveAll(c => !c.IsInWorld); + + var toSpawn = Math.Max(0, info.Minimum - crates.Count) + + (crates.Count < info.Maximum ? 1 : 0); + + for (var n = 0; n < toSpawn; n++) + SpawnCrate(self, info); + } + } + + void SpawnCrate(Actor self, CrateSpawnerInfo info) + { + var inWater = self.World.SharedRandom.NextDouble() < info.WaterChance; + var umt = inWater ? UnitMovementType.Float : UnitMovementType.Wheel; + int count = 0, threshold = 100; + for (; ; ) + { + var p = new int2(self.World.SharedRandom.Next(0, 127), self.World.SharedRandom.Next(0, 127)); + if (self.World.IsCellBuildable(p, umt)) + { + self.World.AddFrameEndTask( + w => crates.Add(w.CreateActor("crate", p, self.Owner))); + break; + } + if (count++ > threshold) + break; + } + } + } +} diff --git a/OpenRA.Game/Traits/World/LightPaletteRotator.cs b/OpenRA.Game/Traits/World/LightPaletteRotator.cs new file mode 100644 index 0000000000..eafcf92cd7 --- /dev/null +++ b/OpenRA.Game/Traits/World/LightPaletteRotator.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.Traits +{ + class LightPaletteRotatorInfo : StatelessTraitInfo { } + class LightPaletteRotator : ITick, IPaletteModifier + { + float t = 0; + public void Tick(Actor self) + { + t += .5f; + } + + public void AdjustPalette(Bitmap b) + { + var rotate = (int)t % 18; + if (rotate > 9) + rotate = 18 - rotate; + + using (var bitmapCopy = new Bitmap(b)) + for (int j = 0; j < 16; j++) + b.SetPixel(0x67, j, b.GetPixel(230+rotate, j)); + } + } +} diff --git a/OpenRA.Game/Traits/World/OreGrowth.cs b/OpenRA.Game/Traits/World/OreGrowth.cs new file mode 100644 index 0000000000..3e96ca88e2 --- /dev/null +++ b/OpenRA.Game/Traits/World/OreGrowth.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +namespace OpenRA.Traits +{ + class OreGrowthInfo : ITraitInfo + { + public readonly float Interval = 1f; + public readonly float Chance = .02f; + public readonly bool Spreads = true; + public readonly bool Grows = true; + + public object Create(Actor self) { return new OreGrowth(); } + } + + class OreGrowth : ITick, ILoadWorldHook + { + int remainingTicks; + + public void Tick(Actor self) + { + if (--remainingTicks <= 0) + { + var info = self.Info.Traits.Get(); + + if (info.Spreads) + Ore.SpreadOre(self.World, + self.World.SharedRandom, + info.Chance); + + if (info.Grows) + Ore.GrowOre(self.World, self.World.SharedRandom); + + self.World.Minimap.InvalidateOre(); + remainingTicks = (int)(info.Interval * 60 * 25); + } + } + + public void WorldLoaded(World w) + { + Ore.InitOreDensity(w.Map); + } + } +} diff --git a/OpenRA.Game/Traits/World/PaletteFromFile.cs b/OpenRA.Game/Traits/World/PaletteFromFile.cs new file mode 100644 index 0000000000..f4d1da0f3f --- /dev/null +++ b/OpenRA.Game/Traits/World/PaletteFromFile.cs @@ -0,0 +1,45 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class PaletteFromFileInfo : ITraitInfo + { + public readonly string Name = null; + public readonly string Theater = null; + public readonly string Filename = null; + public object Create(Actor self) { return new PaletteFromFile(self, this); } + } + + class PaletteFromFile + { + public PaletteFromFile(Actor self, PaletteFromFileInfo info) + { + if (info.Theater == null || + info.Theater.ToLowerInvariant() == self.World.Map.Theater.ToLowerInvariant()) + { + //Log.Write("Loading palette {0} from file {1}", info.Name, info.Filename); + self.World.WorldRenderer.AddPalette(info.Name, new Palette(FileSystem.Open(info.Filename))); + } + } + } +} diff --git a/OpenRA.Game/Traits/World/PaletteFromRGBA.cs b/OpenRA.Game/Traits/World/PaletteFromRGBA.cs new file mode 100644 index 0000000000..72366fc1eb --- /dev/null +++ b/OpenRA.Game/Traits/World/PaletteFromRGBA.cs @@ -0,0 +1,52 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class PaletteFromRGBAInfo : ITraitInfo + { + public readonly string Name = null; + public readonly string Theatre = null; + public readonly int R = 0; + public readonly int G = 0; + public readonly int B = 0; + public readonly int A = 255; + public object Create(Actor self) { return new PaletteFromRGBA(self, this); } + } + + class PaletteFromRGBA + { + public PaletteFromRGBA(Actor self, PaletteFromRGBAInfo info) + { + if (info.Theatre == null || + info.Theatre.ToLowerInvariant() == self.World.Map.Theater.ToLowerInvariant()) + { + Log.Write("Loading palette {0} from RGBA {1} {2} {3} {4}",info.Name,info.R,info.G,info.B,info.A); + // TODO: This shouldn't rely on a base palette + var wr = self.World.WorldRenderer; + var pal = wr.GetPalette("player0"); + wr.AddPalette(info.Name, new Palette(pal, new SingleColorRemap(Color.FromArgb(info.A, info.R, info.G, info.B)))); + } + } + } +} diff --git a/OpenRA.Game/Traits/World/PaletteFromRemap.cs b/OpenRA.Game/Traits/World/PaletteFromRemap.cs new file mode 100644 index 0000000000..371c538abb --- /dev/null +++ b/OpenRA.Game/Traits/World/PaletteFromRemap.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class PaletteFromRemapInfo : ITraitInfo + { + public readonly string Name = null; + public readonly string Theatre = null; + public readonly string BasePalette = null; + public readonly string Remap = null; + public object Create(Actor self) { return new PaletteFromRemap(self, this); } + } + + class PaletteFromRemap + { + public PaletteFromRemap(Actor self, PaletteFromRemapInfo info) + { + if (info.Theatre == null || + info.Theatre.ToLowerInvariant() == self.World.Map.Theater.ToLowerInvariant()) + { + Log.Write("Loading palette {0} from theatre {1} with remap {2}", info.Name, info.BasePalette, info.Remap); + var wr = self.World.WorldRenderer; + var pal = wr.GetPalette(info.BasePalette); + var newpal = (info.Remap == null) ? pal : new Palette(pal, new PlayerColorRemap(FileSystem.Open(info.Remap))); + wr.AddPalette(info.Name, newpal); + } + } + } +} diff --git a/OpenRA.Game/Traits/World/PlayerColorPalette.cs b/OpenRA.Game/Traits/World/PlayerColorPalette.cs new file mode 100644 index 0000000000..ba62c62103 --- /dev/null +++ b/OpenRA.Game/Traits/World/PlayerColorPalette.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class PlayerColorPaletteInfo : ITraitInfo + { + public readonly string Name = null; + public readonly string DisplayName = null; + public readonly string BasePalette = null; + public readonly string Remap = null; + public readonly int[] DisplayColor = null; + public object Create(Actor self) { return new PlayerColorPalette(self, this); } + } + + class PlayerColorPalette + { + public PlayerColorPalette(Actor self, PlayerColorPaletteInfo info) + { + var wr = self.World.WorldRenderer; + var pal = wr.GetPalette(info.BasePalette); + var newpal = (info.Remap == null) ? pal : new Palette(pal, new PlayerColorRemap(FileSystem.Open(info.Remap))); + wr.AddPalette(info.Name, newpal); + Player.RegisterPlayerColor(info.Name, info.DisplayName, Color.FromArgb(info.DisplayColor[0], info.DisplayColor[1], info.DisplayColor[2])); + } + } +} diff --git a/OpenRA.Game/Traits/World/ScreenShaker.cs b/OpenRA.Game/Traits/World/ScreenShaker.cs new file mode 100644 index 0000000000..5cdaf2ca67 --- /dev/null +++ b/OpenRA.Game/Traits/World/ScreenShaker.cs @@ -0,0 +1,54 @@ + +using System; +using System.Linq; +using OpenRA.Traits; +using System.Collections.Generic; +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class ScreenShakerInfo : ITraitInfo + { + public object Create( Actor self ) { return new ScreenShaker(); } + } + + public class ScreenShaker : ITick + { + static int ticks = 0; + static List> shakeEffects = new List>(); + + public void Tick (Actor self) + { + Game.viewport.Scroll(getScrollOffset()); + shakeEffects.RemoveAll(t => t.a == ticks); + ticks++; + } + + public static void RegisterShakeEffect(int time, float2 position, int intensity) + { + shakeEffects.Add(Tuple.New(ticks + time, position, intensity)); + } + + public float2 getScrollOffset() + { + int xFreq = 4; + int yFreq = 5; + + return GetIntensity() * new float2( + (float) Math.Sin((ticks*2*Math.PI)/xFreq) , + (float) Math.Cos((ticks*2*Math.PI)/yFreq)); + } + + public float GetIntensity() + { + var cp = Game.viewport.Location + + .5f * new float2(Game.viewport.Width, Game.viewport.Height); + + var intensity = 24 * 24 * 100 * shakeEffects.Sum( + e => e.c / (e.b - cp).LengthSquared); + + return Math.Min(intensity, 10); + } + + } +} diff --git a/OpenRA.Game/Traits/World/ShroudPalette.cs b/OpenRA.Game/Traits/World/ShroudPalette.cs new file mode 100644 index 0000000000..4c67e07025 --- /dev/null +++ b/OpenRA.Game/Traits/World/ShroudPalette.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.FileFormats; + +namespace OpenRA.Traits +{ + class ShroudPaletteInfo : ITraitInfo + { + public object Create(Actor self) { return new ShroudPalette(self); } + } + + class ShroudPalette + { + public ShroudPalette(Actor self) + { + // TODO: This shouldn't rely on a base palette + var wr = self.World.WorldRenderer; + var pal = wr.GetPalette("terrain"); + + wr.AddPalette("shroud", new Palette(pal, new ShroudPaletteRemap())); + } + } +} diff --git a/OpenRA.Game/Traits/World/UnitInfluence.cs b/OpenRA.Game/Traits/World/UnitInfluence.cs new file mode 100644 index 0000000000..1f9fb559d5 --- /dev/null +++ b/OpenRA.Game/Traits/World/UnitInfluence.cs @@ -0,0 +1,113 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace OpenRA.Traits +{ + public class UnitInfluenceInfo : ITraitInfo + { + public object Create( Actor self ) { return new UnitInfluence( self ); } + } + + public class UnitInfluence : ITick + { + List[,] influence = new List[128, 128]; + + public UnitInfluence( Actor self ) + { + for (int i = 0; i < 128; i++) + for (int j = 0; j < 128; j++) + influence[ i, j ] = new List(); + + self.World.ActorRemoved += a => Remove( a, a.traits.GetOrDefault() ); + } + + public void Tick( Actor self ) + { + // Does this belong here? NO, but it's your mess. + + // Get the crushable actors + foreach (var aa in self.World.Queries.WithTrait()) + { + var a = aa.Actor; + // Are there any units in the same cell that can crush this? + foreach( var ios in a.traits.WithInterface() ) + foreach( var cell in ios.OccupiedCells() ) + { + // There should only be one (counterexample: An infantry and a tank try to pick up a crate at the same time.) + // If there is more than one, do action on the first crusher + var crusher = GetUnitsAt(cell).Where(b => a != b && self.World.IsActorCrushableByActor(a, b)).FirstOrDefault(); + if (crusher != null) + { + Log.Write("{0} crushes {1}", crusher.Info.Name, a.Info.Name); + // Apply the crush action + foreach (var crush in a.traits.WithInterface()) + crush.OnCrush(crusher); + } + } + } + SanityCheck( self ); + } + + [Conditional( "SANITY_CHECKS" )] + void SanityCheck( Actor self ) + { + for( int y = 0 ; y < 128 ; y++ ) + for( int x = 0 ; x < 128 ; x++ ) + if( influence[ x, y ] != null ) + foreach (var a in influence[ x, y ]) + if (!a.traits.Get().OccupiedCells().Contains( new int2( x, y ) ) ) + throw new InvalidOperationException( "UIM: Sanity check failed A" ); + + foreach( var t in self.World.Queries.WithTraitMultiple() ) + foreach( var cell in t.Trait.OccupiedCells() ) + if (!influence[cell.X, cell.Y].Contains(t.Actor)) + throw new InvalidOperationException( "UIM: Sanity check failed B" ); + } + + public IEnumerable GetUnitsAt( int2 a ) + { + return influence[ a.X, a.Y ]; + } + + public void Add( Actor self, IOccupySpace unit ) + { + foreach( var c in unit.OccupiedCells() ) + influence[c.X, c.Y].Add(self); + } + + public void Remove( Actor self, IOccupySpace unit ) + { + if (unit != null) + foreach (var c in unit.OccupiedCells()) + influence[c.X, c.Y].Remove(self); + } + + public void Update(Actor self, IOccupySpace unit) + { + Remove(self, unit); + if (!self.IsDead) Add(self, unit); + } + } +} diff --git a/OpenRA.Game/Traits/World/WaterPaletteRotation.cs b/OpenRA.Game/Traits/World/WaterPaletteRotation.cs new file mode 100644 index 0000000000..d15c015aec --- /dev/null +++ b/OpenRA.Game/Traits/World/WaterPaletteRotation.cs @@ -0,0 +1,44 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; + +namespace OpenRA.Traits +{ + class WaterPaletteRotationInfo : StatelessTraitInfo { } + + class WaterPaletteRotation : ITick, IPaletteModifier + { + float t = 0; + public void Tick(Actor self) + { + t += .25f; + } + + public void AdjustPalette(Bitmap b) + { + var rotate = (int)t % 7; + using (var bitmapCopy = new Bitmap(b)) + for (int j = 0; j < 16; j++) + for (int i = 0; i < 7; i++) + b.SetPixel(0x60 + (rotate + i) % 7, j, bitmapCopy.GetPixel(0x60 + i, j)); + } + } +} diff --git a/OpenRA.Game/UiOverlay.cs b/OpenRA.Game/UiOverlay.cs new file mode 100644 index 0000000000..5308a449f4 --- /dev/null +++ b/OpenRA.Game/UiOverlay.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Drawing; +using System.Linq; +using OpenRA.GameRules; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA +{ + class UiOverlay + { + SpriteRenderer spriteRenderer; + Sprite buildOk, buildBlocked, unitDebug; + + public static bool ShowUnitDebug = false; + + public UiOverlay(SpriteRenderer spriteRenderer) + { + this.spriteRenderer = spriteRenderer; + + buildOk = SynthesizeTile(0x80); + buildBlocked = SynthesizeTile(0xe6); + unitDebug = SynthesizeTile(0x7c); + } + + static Sprite SynthesizeTile(byte paletteIndex) + { + byte[] data = new byte[Game.CellSize * Game.CellSize]; + + for (int i = 0; i < Game.CellSize; i++) + for (int j = 0; j < Game.CellSize; j++) + data[i * Game.CellSize + j] = ((i + j) % 4 < 2) ? (byte)0 : paletteIndex; + + return SheetBuilder.SharedInstance.Add(data, new Size(Game.CellSize, Game.CellSize)); + } + + public void Draw( World world ) + { + if (ShowUnitDebug) + for (var j = 0; j < 128; j++) + for (var i = 0; i < 128; i++) + if (world.WorldActor.traits.Get().GetUnitsAt(new int2(i, j)).Any()) + spriteRenderer.DrawSprite(unitDebug, Game.CellSize * new float2(i, j), "terrain"); + } + + public void DrawBuildingGrid( World world, string name, BuildingInfo bi ) + { + var position = Game.controller.MousePosition.ToInt2(); + var topLeft = position - Footprint.AdjustForBuildingSize( bi ); + var isCloseEnough = world.IsCloseEnoughToBase(world.LocalPlayer, name, bi, topLeft); + + foreach( var t in Footprint.Tiles( name, bi, topLeft ) ) + spriteRenderer.DrawSprite( ( isCloseEnough && world.IsCellBuildable( t, bi.WaterBound + ? UnitMovementType.Float : UnitMovementType.Wheel ) && !world.Map.ContainsResource( t ) ) + ? buildOk : buildBlocked, Game.CellSize * t, "terrain" ); + + spriteRenderer.Flush(); + } + } +} diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs new file mode 100644 index 0000000000..0cb656328b --- /dev/null +++ b/OpenRA.Game/World.cs @@ -0,0 +1,250 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Collections; +using OpenRA.Effects; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Support; +using OpenRA.Traits; + +namespace OpenRA +{ + public class World + { + Set actors = new Set(); + List effects = new List(); + List> frameEndActions = new List>(); + + public Random SharedRandom = new Random(0); // synced + public Random CosmeticRandom = new Random(); // not synced + + public readonly Dictionary players = new Dictionary(); + + int localPlayerIndex; + public Player LocalPlayer + { + get { return players[localPlayerIndex]; } + } + + public void SetLocalPlayer(int index) + { + if (index != localPlayerIndex) + { + localPlayerIndex = index; + Game.viewport.GoToStartLocation(LocalPlayer); + Game.chat.AddLine(LocalPlayer, "is now YOU"); + } + if (!string.IsNullOrEmpty(Game.Settings.PlayerName) && LocalPlayer.PlayerName != Game.Settings.PlayerName) + Game.IssueOrder(Order.Chat("/name " + Game.Settings.PlayerName)); + } + + public readonly Actor WorldActor; + + public readonly PathFinder PathFinder; + + public readonly Map Map; + public readonly TileSet TileSet; + + // for tricky things like bridges. + public readonly ICustomTerrain[,] customTerrain = new ICustomTerrain[128, 128]; + + public readonly WorldRenderer WorldRenderer; + internal readonly Minimap Minimap; + + public World() + { + Timer.Time( "----World.ctor" ); + + Map = new Map( Rules.AllRules ); + Timer.Time( "new Map: {0}" ); + TileSet = new TileSet( Map.TileSuffix ); + SpriteSheetBuilder.Initialize( Map ); + Timer.Time( "Tileset: {0}" ); + + WorldRenderer = new WorldRenderer(this, Game.renderer); + Timer.Time("renderer: {0}"); + + WorldActor = CreateActor("World", new int2(int.MaxValue, int.MaxValue), null); + + for (int i = 0; i < 8; i++) + players[i] = new Player(this, i, Game.LobbyInfo.Clients.FirstOrDefault(a => a.Index == i)); + + Timer.Time( "worldActor, players: {0}" ); + + Queries = new AllQueries( this ); + Timer.Time( "queries: {0}" ); + + foreach (var wlh in WorldActor.traits.WithInterface()) + wlh.WorldLoaded(this); + + PathFinder = new PathFinder(this); + Timer.Time( "hooks, pathing: {0}" ); + + Minimap = new Minimap(this, Game.renderer); + Timer.Time( "minimap: {0}" ); + + Timer.Time( "----end World.ctor" ); + } + + public Actor CreateActor( string name, int2 location, Player owner ) + { + var a = new Actor( this, name, location, owner ); + Add( a ); + return a; + } + + public void Add(Actor a) + { + a.IsInWorld = true; + actors.Add(a); + ActorAdded(a); + } + + public void Remove(Actor a) + { + a.IsInWorld = false; + actors.Remove(a); + ActorRemoved(a); + } + + public void Add(IEffect b) { effects.Add(b); } + public void Remove(IEffect b) { effects.Remove(b); } + + public void AddFrameEndTask( Action a ) { frameEndActions.Add( a ); } + + public event Action ActorAdded = _ => { }; + public event Action ActorRemoved = _ => { }; + + public void Tick() + { + foreach (var a in actors) a.Tick(); + Queries.WithTraitMultiple().Do( x => x.Trait.Tick( x.Actor ) ); + + foreach (var e in effects) e.Tick( this ); + + Game.viewport.Tick(); + + var acts = frameEndActions; + frameEndActions = new List>(); + foreach (var a in acts) a(this); + + Minimap.Update(); + foreach (var player in players.Values) + player.Tick(); + } + + public IEnumerable Actors { get { return actors; } } + public IEnumerable Effects { get { return effects; } } + + uint nextAID = 0; + internal uint NextAID() + { + return nextAID++; + } + + public int SyncHash() + { + using (new PerfSample("synchash")) + { + int ret = 0; + foreach (var a in Actors) + ret += (int)a.ActorID * Sync.CalculateSyncHash(a); + + return ret; + } + } + + public class AllQueries + { + readonly World world; + + public readonly Dictionary OwnedBy = new Dictionary(); + readonly TypeDictionary hasTrait = new TypeDictionary(); + + public AllQueries( World world ) + { + this.world = world; + foreach( var p in world.players.Values ) + { + var player = p; + OwnedBy.Add( player, new OwnedByCachedView( world, world.actors, x => x.Owner == player ) ); + } + } + + public CachedView> WithTrait() + { + return WithTraitInner( world.actors, hasTrait ); + } + + static CachedView> WithTraitInner( Set set, TypeDictionary hasTrait ) + { + var ret = hasTrait.GetOrDefault>>(); + if( ret != null ) + return ret; + ret = new CachedView>( + set, + x => x.traits.Contains(), + x => new TraitPair { Actor = x, Trait = x.traits.Get() } ); + hasTrait.Add( ret ); + return ret; + } + + public CachedView> WithTraitMultiple() + { + var ret = hasTrait.GetOrDefault>>(); + if( ret != null ) + return ret; + ret = new CachedView>( + world.actors, + x => x.traits.Contains(), + x => x.traits.WithInterface().Select( t => new TraitPair { Actor = x, Trait = t } ) ); + hasTrait.Add( ret ); + return ret; + } + + public struct TraitPair + { + public Actor Actor; + public T Trait; + } + + public class OwnedByCachedView : CachedView + { + readonly TypeDictionary hasTrait = new TypeDictionary(); + + public OwnedByCachedView( World world, Set set, Func include ) + : base( set, include, a => a ) + { + } + + public CachedView> WithTrait() + { + return WithTraitInner( this, hasTrait ); + } + } + } + + public readonly AllQueries Queries; + } +} diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs new file mode 100755 index 0000000000..c53801adbd --- /dev/null +++ b/OpenRA.Game/WorldUtils.cs @@ -0,0 +1,215 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.GameRules; +using OpenRA.Traits; + +namespace OpenRA +{ + public static class WorldUtils + { + public static bool IsCellBuildable(this World world, int2 a, UnitMovementType umt) + { + return world.IsCellBuildable(a, umt, null); + } + + public static bool IsCellBuildable(this World world, int2 a, UnitMovementType umt, Actor toIgnore) + { + if (world.WorldActor.traits.Get().GetBuildingAt(a) != null) return false; + if (world.WorldActor.traits.Get().GetUnitsAt(a).Any(b => b != toIgnore)) return false; + + return world.Map.IsInMap(a.X, a.Y) && + TerrainCosts.Cost(umt, + world.TileSet.GetWalkability(world.Map.MapTiles[a.X, a.Y])) < double.PositiveInfinity; + } + + public static bool IsActorCrushableByActor(this World world, Actor a, Actor b) + { + var movement = b.traits.GetOrDefault(); + return movement != null && world.IsActorCrushableByMovementType(a, movement.GetMovementType()); + } + + public static bool IsActorPathableToCrush(this World world, Actor a, UnitMovementType umt) + { + return a != null && + a.traits.WithInterface() + .Any(c => c.IsPathableCrush(umt, a.Owner)); + } + + public static bool IsActorCrushableByMovementType(this World world, Actor a, UnitMovementType umt) + { + return a != null && + a.traits.WithInterface() + .Any(c => c.IsCrushableBy(umt, a.Owner)); + } + + public static bool IsWater(this World world, int2 a) + { + return world.Map.IsInMap(a.X, a.Y) && + TerrainCosts.Cost(UnitMovementType.Float, + world.TileSet.GetWalkability(world.Map.MapTiles[a.X, a.Y])) < double.PositiveInfinity; + } + + public static IEnumerable FindUnitsAtMouse(this World world, int2 mouseLocation) + { + var loc = mouseLocation + Game.viewport.Location; + return FindUnits(world, loc, loc); + } + + public static IEnumerable FindUnits(this World world, float2 a, float2 b) + { + var min = float2.Min(a, b); + var max = float2.Max(a, b); + + var rect = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); + + return world.Actors + .Where(x => x.GetBounds(true).IntersectsWith(rect)); + } + + public static IEnumerable FindUnitsInCircle(this World world, float2 a, float r) + { + var min = a - new float2(r, r); + var max = a + new float2(r, r); + + var rect = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); + + var inBox = world.Actors.Where(x => x.GetBounds(false).IntersectsWith(rect)); + + return inBox.Where(x => (x.CenterLocation - a).LengthSquared < r * r); + } + + public static IEnumerable FindTilesInCircle(this World world, int2 a, int r) + { + var min = a - new int2(r, r); + var max = a + new int2(r, r); + if (min.X < world.Map.XOffset) min.X = world.Map.XOffset; + if (min.Y < world.Map.YOffset) min.Y = world.Map.YOffset; + if (max.X > world.Map.XOffset + world.Map.Width - 1) max.X = world.Map.XOffset + world.Map.Width - 1; + if (max.Y > world.Map.YOffset + world.Map.Height - 1) max.Y = world.Map.YOffset + world.Map.Height - 1; + + for (var j = min.Y; j <= max.Y; j++) + for (var i = min.X; i <= max.X; i++) + if (r * r >= (new int2(i, j) - a).LengthSquared) + yield return new int2(i, j); + } + + public static IEnumerable SelectActorsInBox(this World world, float2 a, float2 b) + { + return world.FindUnits(a, b) + .Where( x => x.traits.Contains() ) + .GroupBy(x => (x.Owner == world.LocalPlayer) ? x.Info.Traits.Get().Priority : 0) + .OrderByDescending(g => g.Key) + .Select( g => g.AsEnumerable() ) + .DefaultIfEmpty( new Actor[] {} ) + .FirstOrDefault(); + } + + public static bool CanPlaceBuilding(this World world, string name, BuildingInfo building, int2 topLeft, Actor toIgnore) + { + return !Footprint.Tiles(name, building, topLeft).Any( + t => !world.Map.IsInMap(t.X, t.Y) || world.Map.ContainsResource(t) || !world.IsCellBuildable(t, + building.WaterBound ? UnitMovementType.Float : UnitMovementType.Wheel, + toIgnore)); + } + + public static bool IsCloseEnoughToBase(this World world, Player p, string buildingName, BuildingInfo bi, int2 topLeft) + { + var buildingMaxBounds = bi.Dimensions; + if( bi.Bib ) + buildingMaxBounds.Y += 1; + + var scanStart = world.ClampToWorld( topLeft - new int2( bi.Adjacent, bi.Adjacent ) ); + var scanEnd = world.ClampToWorld( topLeft + buildingMaxBounds + new int2( bi.Adjacent, bi.Adjacent ) ); + + var nearnessCandidates = new List(); + + for( int y = scanStart.Y ; y < scanEnd.Y ; y++ ) + { + for( int x = scanStart.X ; x < scanEnd.X ; x++ ) + { + var at = world.WorldActor.traits.Get().GetBuildingAt( new int2( x, y ) ); + if( at != null && at.Owner == p ) + nearnessCandidates.Add( new int2( x, y ) ); + } + } + var buildingTiles = Footprint.Tiles( buildingName, bi, topLeft ).ToList(); + return nearnessCandidates + .Any( a => buildingTiles + .Any( b => Math.Abs( a.X - b.X ) <= bi.Adjacent + && Math.Abs( a.Y - b.Y ) <= bi.Adjacent ) ); + } + + static int2 ClampToWorld( this World world, int2 xy ) + { + var mapStart = world.Map.Offset; + var mapEnd = world.Map.Offset + world.Map.Size; + if( xy.X < mapStart.X ) + xy.X = mapStart.X; + if( xy.X > mapEnd.X ) + xy.X = mapEnd.X; + + if( xy.Y < mapStart.Y ) + xy.Y = mapStart.Y; + if( xy.Y > mapEnd.Y ) + xy.Y = mapEnd.Y; + + return xy; + } + + public static void LoadMapActors(this World world, IniFile mapfile) + { + var toLoad = + mapfile.GetSection("STRUCTURES", true) + .Concat(mapfile.GetSection("UNITS", true)); + + foreach (var s in toLoad) + { + //num=owner,type,health,location,facing,... + var parts = s.Value.Split( ',' ); + var loc = int.Parse(parts[3]); + world.CreateActor(parts[1].ToLowerInvariant(), new int2(loc % 128, loc / 128), + world.players.Values.FirstOrDefault(p => p.InternalName == parts[0]) ?? world.players[0]); + } + } + + public static int2 ChooseRandomEdgeCell(this World w) + { + var isX = w.SharedRandom.Next(2) == 0; + var edge = w.SharedRandom.Next(2) == 0; + + return new int2( + isX ? w.SharedRandom.Next(w.Map.XOffset, w.Map.XOffset + w.Map.Width) + : (edge ? w.Map.XOffset : w.Map.XOffset + w.Map.Width), + !isX ? w.SharedRandom.Next(w.Map.YOffset, w.Map.YOffset + w.Map.Height) + : (edge ? w.Map.YOffset : w.Map.YOffset + w.Map.Height)); + } + + public static IEnumerable GetCountries(this World w) + { + return w.WorldActor.Info.Traits.WithInterface(); + } + } +} diff --git a/OpenRA.Gl/GraphicsDevice.cs b/OpenRA.Gl/GraphicsDevice.cs new file mode 100644 index 0000000000..6333c74a48 --- /dev/null +++ b/OpenRA.Gl/GraphicsDevice.cs @@ -0,0 +1,459 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Tao.Cg; +using Tao.OpenGl; +using OpenRA.FileFormats.Graphics; +using Tao.Sdl; + +[assembly: Renderer( typeof( OpenRA.GlRenderer.GraphicsDevice ))] + +namespace OpenRA.GlRenderer +{ + public class GraphicsDevice : IGraphicsDevice + { + Size windowSize; + internal IntPtr cgContext; + internal int vertexProfile, fragmentProfile; + + IntPtr surf; + + public Size WindowSize { get { return windowSize; } } + + internal static void CheckGlError() + { + var n = Gl.glGetError(); + if (n != Gl.GL_NO_ERROR) + throw new InvalidOperationException("GL Error"); + } + + public GraphicsDevice( int width, int height, bool windowed, bool vsync ) + { + Sdl.SDL_Init(Sdl.SDL_INIT_NOPARACHUTE | Sdl.SDL_INIT_VIDEO); + Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DOUBLEBUFFER, 1); + Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_RED_SIZE, 8); + Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_GREEN_SIZE, 8); + Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_BLUE_SIZE, 8); + Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_ALPHA_SIZE, 8); + + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + // pseudo-fullscreen, for sane debugging. + Environment.SetEnvironmentVariable("SDL_VIDEO_WINDOW_POS", "0,0"); + surf = Sdl.SDL_SetVideoMode(width, height, 0, Sdl.SDL_NOFRAME | Sdl.SDL_OPENGL | (windowed ? 0 : Sdl.SDL_FULLSCREEN)); + } + else + { + // OSX doesn't like this, due to quirks of their WM. + surf = Sdl.SDL_SetVideoMode(width, height, 0, Sdl.SDL_OPENGL | (windowed ? 0 : Sdl.SDL_FULLSCREEN)); + } + + Sdl.SDL_WM_SetCaption("OpenRA", "OpenRA"); + Sdl.SDL_ShowCursor(0); + Sdl.SDL_EnableUNICODE( 1 ); + Sdl.SDL_EnableKeyRepeat(Sdl.SDL_DEFAULT_REPEAT_INTERVAL, Sdl.SDL_DEFAULT_REPEAT_DELAY); + + CheckGlError(); + + windowSize = new Size( width, height ); + + cgContext = Cg.cgCreateContext(); + + Cg.cgSetErrorCallback( CgErrorCallback ); + + CgGl.cgGLRegisterStates( cgContext ); + CgGl.cgGLSetManageTextureParameters( cgContext, true ); + vertexProfile = CgGl.cgGLGetLatestProfile( CgGl.CG_GL_VERTEX ); + fragmentProfile = CgGl.cgGLGetLatestProfile( CgGl.CG_GL_FRAGMENT ); + + Console.WriteLine("VP Profile: " + vertexProfile); + Console.WriteLine("FP Profile: " + fragmentProfile); + + Gl.glEnableClientState( Gl.GL_VERTEX_ARRAY ); + CheckGlError(); + Gl.glEnableClientState( Gl.GL_TEXTURE_COORD_ARRAY ); + CheckGlError(); + } + + static Cg.CGerrorCallbackFuncDelegate CgErrorCallback = () => + { + var err = Cg.cgGetError(); + var str = Cg.cgGetErrorString( err ); + throw new InvalidOperationException( + string.Format( "CG Error: {0}: {1}", err, str ) ); + }; + + public void EnableScissor(int left, int top, int width, int height) + { + if( width < 0 ) width = 0; + if( height < 0 ) height = 0; + Gl.glScissor( left, windowSize.Height - ( top + height ), width, height ); + CheckGlError(); + Gl.glEnable(Gl.GL_SCISSOR_TEST); + CheckGlError(); + } + + public void DisableScissor() + { + Gl.glDisable(Gl.GL_SCISSOR_TEST); + CheckGlError(); + } + + public void Begin() { } + public void End() { } + + public void Clear(Color c) + { + Gl.glClearColor(0, 0, 0, 0); + CheckGlError(); + Gl.glClear(Gl.GL_COLOR_BUFFER_BIT); + CheckGlError(); + } + + MouseButtons lastButtonBits = (MouseButtons)0; + + static MouseButtons MakeButton(byte b) + { + return b == Sdl.SDL_BUTTON_LEFT ? MouseButtons.Left + : b == Sdl.SDL_BUTTON_RIGHT ? MouseButtons.Right + : b == Sdl.SDL_BUTTON_MIDDLE ? MouseButtons.Middle + : 0; + } + + static Modifiers MakeModifiers(int raw) + { + return ((raw & Sdl.KMOD_ALT) != 0 ? Modifiers.Alt : 0) + | ((raw & Sdl.KMOD_CTRL) != 0 ? Modifiers.Ctrl : 0) + | ((raw & Sdl.KMOD_SHIFT) != 0 ? Modifiers.Shift : 0); + } + + public void Present() + { + Sdl.SDL_GL_SwapBuffers(); + + var mods = MakeModifiers(Sdl.SDL_GetModState()); + Game.HandleModifierKeys(mods); + + Sdl.SDL_Event e; + while (Sdl.SDL_PollEvent(out e) != 0) + { + switch (e.type) + { + case Sdl.SDL_QUIT: + OpenRA.Game.Exit(); + break; + + case Sdl.SDL_MOUSEBUTTONDOWN: + { + var button = MakeButton(e.button.button); + lastButtonBits |= button; + + Game.DispatchMouseInput(MouseInputEvent.Down, + new MouseEventArgs(button, 1, e.button.x, e.button.y, 0), + mods); + } break; + + case Sdl.SDL_MOUSEBUTTONUP: + { + var button = MakeButton(e.button.button); + lastButtonBits &= ~button; + + Game.DispatchMouseInput(MouseInputEvent.Up, + new MouseEventArgs(button, 1, e.button.x, e.button.y, 0), + mods); + } break; + + case Sdl.SDL_MOUSEMOTION: + { + Game.DispatchMouseInput(MouseInputEvent.Move, + new MouseEventArgs(lastButtonBits, 0, e.motion.x, e.motion.y, 0), + mods); + } break; + + case Sdl.SDL_KEYDOWN: + { + if( e.key.keysym.unicode != 0 ) + Game.HandleKeyPress( new KeyPressEventArgs( (char)e.key.keysym.unicode ), mods ); + + else if( mods != 0 ) + { + var keyName = Sdl.SDL_GetKeyName( e.key.keysym.sym ); + if( keyName.Length == 1 ) + Game.HandleKeyPress( new KeyPressEventArgs( keyName[ 0 ] ), mods ); + else if( keyName == "f4" && ( ( mods & Modifiers.Alt ) != 0 ) ) + OpenRA.Game.Exit(); + } + } break; + + case Sdl.SDL_KEYUP: + { + } break; + } + } + + CheckGlError(); + } + + public void DrawIndexedPrimitives( PrimitiveType pt, Range vertices, Range indices ) + { + Gl.glDrawElements( ModeFromPrimitiveType( pt ), indices.End - indices.Start, Gl.GL_UNSIGNED_SHORT, new IntPtr( indices.Start * 2 ) ); + CheckGlError(); + } + + public void DrawIndexedPrimitives( PrimitiveType pt, int numVerts, int numPrimitives ) + { + Gl.glDrawElements( ModeFromPrimitiveType( pt ), numPrimitives * IndicesPerPrimitive( pt ), Gl.GL_UNSIGNED_SHORT, IntPtr.Zero ); + CheckGlError(); + } + + static int ModeFromPrimitiveType( PrimitiveType pt ) + { + switch( pt ) + { + case PrimitiveType.PointList: return Gl.GL_POINTS; + case PrimitiveType.LineList: return Gl.GL_LINES; + case PrimitiveType.TriangleList: return Gl.GL_TRIANGLES; + } + throw new NotImplementedException(); + } + + static int IndicesPerPrimitive( PrimitiveType pt ) + { + switch( pt ) + { + case PrimitiveType.PointList: return 1; + case PrimitiveType.LineList: return 2; + case PrimitiveType.TriangleList: return 3; + } + throw new NotImplementedException(); + } + + #region IGraphicsDevice Members + + public IVertexBuffer CreateVertexBuffer( int size ) + { + return new VertexBuffer( this, size ); + } + + public IIndexBuffer CreateIndexBuffer( int size ) + { + return new IndexBuffer( this, size ); + } + + public ITexture CreateTexture( Bitmap bitmap ) + { + return new Texture( this, bitmap ); + } + + public IShader CreateShader( Stream stream ) + { + return new Shader( this, stream ); + } + + #endregion + } + + public class VertexBuffer : IVertexBuffer, IDisposable + where T : struct + { + int buffer; + + public VertexBuffer(GraphicsDevice dev, int size) + { + Gl.glGenBuffers(1, out buffer); + GraphicsDevice.CheckGlError(); + } + + public void SetData(T[] data) + { + Bind(); + Gl.glBufferData(Gl.GL_ARRAY_BUFFER, + new IntPtr(Marshal.SizeOf(typeof(T))*data.Length), data, Gl.GL_DYNAMIC_DRAW); + GraphicsDevice.CheckGlError(); + } + + public void Bind() + { + Gl.glBindBuffer(Gl.GL_ARRAY_BUFFER, buffer); + GraphicsDevice.CheckGlError(); + Gl.glVertexPointer(3, Gl.GL_FLOAT, Marshal.SizeOf(typeof(T)), IntPtr.Zero); + GraphicsDevice.CheckGlError(); + Gl.glTexCoordPointer(4, Gl.GL_FLOAT, Marshal.SizeOf(typeof(T)), new IntPtr(12)); + GraphicsDevice.CheckGlError(); + } + + bool disposed; + public void Dispose() + { + if (disposed) return; + GC.SuppressFinalize(this); + Gl.glDeleteBuffers(1, ref buffer); + GraphicsDevice.CheckGlError(); + disposed = true; + } + + //~VertexBuffer() { Dispose(); } + } + + public class IndexBuffer : IIndexBuffer, IDisposable + { + int buffer; + + public IndexBuffer(GraphicsDevice dev, int size) + { + Gl.glGenBuffers(1, out buffer); + GraphicsDevice.CheckGlError(); + } + + public void SetData(ushort[] data) + { + Bind(); + Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, + new IntPtr(2 * data.Length), data, Gl.GL_DYNAMIC_DRAW); + GraphicsDevice.CheckGlError(); + } + + public void Bind() + { + Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, buffer); + GraphicsDevice.CheckGlError(); + } + + bool disposed; + public void Dispose() + { + if (disposed) return; + GC.SuppressFinalize(this); + Gl.glDeleteBuffers(1, ref buffer); + GraphicsDevice.CheckGlError(); + disposed = true; + } + + //~IndexBuffer() { Dispose(); } + } + + public class Shader : IShader + { + IntPtr effect; + IntPtr technique; + GraphicsDevice dev; + + public Shader(GraphicsDevice dev, Stream s) + { + this.dev = dev; + string code; + using (var file = new StreamReader(s)) + code = file.ReadToEnd(); + effect = Cg.cgCreateEffect(dev.cgContext, code, null); + + if (effect == IntPtr.Zero) + { + var err = Cg.cgGetErrorString(Cg.cgGetError()); + var results = Cg.cgGetLastListing(dev.cgContext); + throw new InvalidOperationException( + string.Format("Cg compile failed ({0}):\n{1}", err, results)); + } + + technique = Cg.cgGetFirstTechnique( effect ); + if( technique == IntPtr.Zero ) + throw new InvalidOperationException("No techniques"); + while( Cg.cgValidateTechnique( technique ) == 0 ) + { + technique = Cg.cgGetNextTechnique( technique ); + if( technique == IntPtr.Zero ) + throw new InvalidOperationException("No valid techniques"); + } + } + + public void Render(Action a) + { + CgGl.cgGLEnableProfile(dev.vertexProfile); + CgGl.cgGLEnableProfile(dev.fragmentProfile); + + var pass = Cg.cgGetFirstPass(technique); + while (pass != IntPtr.Zero) + { + Cg.cgSetPassState(pass); + a(); + Cg.cgResetPassState(pass); + pass = Cg.cgGetNextPass(pass); + } + + CgGl.cgGLDisableProfile(dev.fragmentProfile); + CgGl.cgGLDisableProfile(dev.vertexProfile); + } + + public void SetValue(string name, ITexture t) + { + var texture = (Texture)t; + var param = Cg.cgGetNamedEffectParameter( effect, name ); + if( param != IntPtr.Zero && texture != null ) + CgGl.cgGLSetupSampler( param, texture.texture ); + } + + public void SetValue(string name, float x, float y) + { + var param = Cg.cgGetNamedEffectParameter(effect, name); + if( param != IntPtr.Zero ) + CgGl.cgGLSetParameter2f(param, x, y); + } + + public void Commit() { } + } + + public class Texture : ITexture + { + internal int texture; + + public Texture(GraphicsDevice dev, Bitmap bitmap) + { + Gl.glGenTextures(1, out texture); + GraphicsDevice.CheckGlError(); + SetData(bitmap); + } + + public void SetData(Bitmap bitmap) + { + Gl.glBindTexture( Gl.GL_TEXTURE_2D, texture ); + GraphicsDevice.CheckGlError(); + + var bits = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + ImageLockMode.ReadOnly, + PixelFormat.Format32bppArgb); + + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_BASE_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAX_LEVEL, 0); + GraphicsDevice.CheckGlError(); + Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA8, bits.Width, bits.Height, + 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE, bits.Scan0); // todo: weird strides + GraphicsDevice.CheckGlError(); + + bitmap.UnlockBits(bits); + } + } +} diff --git a/OpenRA.Gl/OpenRA.Gl.csproj b/OpenRA.Gl/OpenRA.Gl.csproj new file mode 100644 index 0000000000..af1e386268 --- /dev/null +++ b/OpenRA.Gl/OpenRA.Gl.csproj @@ -0,0 +1,75 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC} + Library + Properties + OpenRA.GlRenderer + OpenRA.Gl + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + + + + False + + + False + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + + + + copy "$(TargetPath)" "$(SolutionDir)" + + \ No newline at end of file diff --git a/OpenRA.Gl/Properties/AssemblyInfo.cs b/OpenRA.Gl/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d8fc8da918 --- /dev/null +++ b/OpenRA.Gl/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("OpenRA.Gl")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("OpenRA.Gl")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[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("5531344c-b25d-4641-bc3c-fe035cc777bd")] + +// 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/OpenRA.Mods.Aftermath/ChronoshiftDeploy.cs b/OpenRA.Mods.Aftermath/ChronoshiftDeploy.cs new file mode 100644 index 0000000000..19f877afe9 --- /dev/null +++ b/OpenRA.Mods.Aftermath/ChronoshiftDeploy.cs @@ -0,0 +1,111 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Mods.Aftermath.Orders; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.Aftermath +{ + class ChronoshiftDeployInfo : ITraitInfo + { + public readonly int ChargeTime = 120; // Seconds + public object Create(Actor self) { return new ChronoshiftDeploy(self); } + } + + class ChronoshiftDeploy : IIssueOrder, IResolveOrder, ITick, IPips + { + // Recharge logic + [Sync] + int chargeTick = 0; // How long until we can chronoshift again? + + public ChronoshiftDeploy(Actor self) { } + + public void Tick(Actor self) + { + if (chargeTick > 0) + chargeTick--; + } + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button == MouseButton.Right && xy == self.Location && chargeTick <= 0) + return new Order("Deploy", self); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Deploy") + { + Game.controller.orderGenerator = new SetChronoTankDestination(self); + return; + } + + var movement = self.traits.GetOrDefault(); + if (order.OrderString == "ChronoshiftSelf" && movement.CanEnterCell(order.TargetLocation)) + { + // Cannot chronoshift into unexplored location + if (!self.Owner.Shroud.IsExplored(order.TargetLocation)) + return; + + Game.controller.CancelInputMode(); + self.CancelActivity(); + self.QueueActivity(new Teleport(order.TargetLocation)); + Sound.Play("chrotnk1.aud"); + chargeTick = 25 * self.Info.Traits.Get().ChargeTime; + + foreach (var a in self.World.Queries.WithTrait()) + a.Trait.DoChronoshift(); + } + } + + // Display 5 pips indicating the current charge status + public IEnumerable GetPips(Actor self) + { + const int numPips = 5; + for (int i = 0; i < numPips; i++) + { + if ((1 - chargeTick * 1.0f / (25 * self.Info.Traits.Get().ChargeTime)) * numPips < i + 1) + { + yield return PipType.Transparent; + continue; + } + + switch (i) + { + case 0: + case 1: + yield return PipType.Red; + break; + case 2: + case 3: + yield return PipType.Yellow; + break; + case 4: + yield return PipType.Green; + break; + } + } + } + } +} diff --git a/OpenRA.Mods.Aftermath/DemoTruck.cs b/OpenRA.Mods.Aftermath/DemoTruck.cs new file mode 100644 index 0000000000..d4876dbad5 --- /dev/null +++ b/OpenRA.Mods.Aftermath/DemoTruck.cs @@ -0,0 +1,73 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.Aftermath +{ + class DemoTruckInfo : ITraitInfo + { + public object Create(Actor self) { return new DemoTruck(self); } + } + + class DemoTruck : Chronoshiftable, INotifyDamage + { + public DemoTruck(Actor self) : base(self) { } + + // Explode on chronoshift + public override bool Activate(Actor self, int2 targetLocation, int duration, bool killCargo, Actor chronosphere) + { + Detonate(self, chronosphere); + return false; + } + + // Fire primary on death + public void Damaged(Actor self, AttackInfo e) + { + if (e.DamageState == DamageState.Dead) + Detonate(self, e.Attacker); + } + + public void Detonate(Actor self, Actor detonatedBy) + { + var unit = self.traits.GetOrDefault(); + var info = self.Info.Traits.Get(); + var altitude = unit != null ? unit.Altitude : 0; + int2 detonateLocation = self.CenterLocation.ToInt2(); + + self.World.AddFrameEndTask( w => + { + // Fire weapon + w.Add(new Bullet(info.PrimaryWeapon, detonatedBy.Owner, detonatedBy, + detonateLocation, detonateLocation, altitude, altitude)); + + var weapon = Rules.WeaponInfo[info.PrimaryWeapon]; + if (!string.IsNullOrEmpty(weapon.Report)) + Sound.Play(weapon.Report + ".aud"); + + // Remove from world + self.Health = 0; + detonatedBy.Owner.Kills++; + w.Remove(self); + } ); + } + } +} diff --git a/OpenRA.Mods.Aftermath/OpenRA.Mods.Aftermath.csproj b/OpenRA.Mods.Aftermath/OpenRA.Mods.Aftermath.csproj new file mode 100644 index 0000000000..3fe535a094 --- /dev/null +++ b/OpenRA.Mods.Aftermath/OpenRA.Mods.Aftermath.csproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331} + Library + Properties + OpenRA.Mods.Aftermath + OpenRA.Mods.Aftermath + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + + + + mkdir "$(SolutionDir)mods/aftermath/" +copy "$(TargetPath)" "$(SolutionDir)mods/aftermath/" + + \ No newline at end of file diff --git a/OpenRA.Mods.Aftermath/Orders/SetChronoTankDestination.cs b/OpenRA.Mods.Aftermath/Orders/SetChronoTankDestination.cs new file mode 100644 index 0000000000..c9e2758b9f --- /dev/null +++ b/OpenRA.Mods.Aftermath/Orders/SetChronoTankDestination.cs @@ -0,0 +1,62 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Traits; + +namespace OpenRA.Mods.Aftermath.Orders +{ + class SetChronoTankDestination : IOrderGenerator + { + public readonly Actor self; + + public SetChronoTankDestination(Actor self) + { + this.self = self; + } + + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + Game.controller.CancelInputMode(); + yield break; + } + + yield return new Order("ChronoshiftSelf", self, xy); + } + + public void Tick( World world ) { } + public void Render( World world ) + { + world.WorldRenderer.DrawSelectionBox(self, Color.White, true); + } + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + if (!world.LocalPlayer.Shroud.IsExplored(xy)) + return "move-blocked"; + + var movement = self.traits.GetOrDefault(); + return (movement.CanEnterCell(xy)) ? "chrono-target" : "move-blocked"; + } + } +} diff --git a/OpenRA.Mods.Aftermath/Properties/AssemblyInfo.cs b/OpenRA.Mods.Aftermath/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8acbbbb613 --- /dev/null +++ b/OpenRA.Mods.Aftermath/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("OpenRA.Mods.Aftermath")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenRA.Mods.Aftermath")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[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("cc21537e-3011-4c80-9068-ad302431e784")] + +// 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/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj new file mode 100644 index 0000000000..774499c592 --- /dev/null +++ b/OpenRA.Mods.Cnc/OpenRA.Mods.Cnc.csproj @@ -0,0 +1,78 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {2881135D-4D62-493E-8F83-5EEE92CCC6BE} + Library + Properties + OpenRA.Mods.Cnc + OpenRA.Mods.Cnc + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E} + OpenRA.Mods.RA + + + + + + mkdir "$(SolutionDir)mods/cnc/" +copy "$(TargetPath)" "$(SolutionDir)mods/cnc/" + + \ No newline at end of file diff --git a/OpenRA.Mods.Cnc/ProductionAirdrop.cs b/OpenRA.Mods.Cnc/ProductionAirdrop.cs new file mode 100644 index 0000000000..0c97976100 --- /dev/null +++ b/OpenRA.Mods.Cnc/ProductionAirdrop.cs @@ -0,0 +1,84 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.GameRules; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.Cnc +{ + public class ProductionAirdropInfo : ProductionInfo + { + public override object Create(Actor self) { return new ProductionAirdrop(self); } + } + + class ProductionAirdrop : Production + { + public ProductionAirdrop(Actor self) : base(self) { } + + public override bool Produce( Actor self, ActorInfo producee ) + { + var owner = self.Owner; + + // Start beyond the edge of the map, to give a finite delay, and ability to land when AFLD is on map edge + var startPos = new int2(owner.World.Map.XOffset + owner.World.Map.Width+15, self.Location.Y); + var endPos = new int2(owner.World.Map.XOffset, self.Location.Y); + var unloadOffset = new int2(1,1); + var exitOffset = new int2(3,1); + + var rp = self.traits.GetOrDefault(); + owner.World.AddFrameEndTask(w => + { + var a = w.CreateActor("C17", startPos, owner); + var cargo = a.traits.Get(); + + var newUnit = new Actor(self.World, producee.Name, new int2(0, 0), self.Owner); + cargo.Load(a, newUnit); + + a.CancelActivity(); + + a.QueueActivity(new Land(self)); + a.QueueActivity(new CallFunc(() => + { + if (self.IsDead) + return; + + var actor = cargo.Unload(self); + self.World.AddFrameEndTask(ww => + { + ww.Add(actor); + actor.traits.Get().TeleportTo(actor, self.Location + unloadOffset); + newUnit.traits.Get().Facing = 192; + actor.CancelActivity(); + actor.QueueActivity(new Move(self.Location + exitOffset, self)); + actor.QueueActivity(new Move(rp.rallyPoint, 0)); + + foreach (var t in self.traits.WithInterface()) + t.UnitProduced(self, actor); + }); + })); + a.QueueActivity(new Fly(endPos)); + a.QueueActivity(new RemoveSelf()); + }); + + return true; + } + } +} diff --git a/OpenRA.Mods.Cnc/Properties/AssemblyInfo.cs b/OpenRA.Mods.Cnc/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e05f9bb228 --- /dev/null +++ b/OpenRA.Mods.Cnc/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("OpenRA.Mods.Cnc")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenRA.Mods.Cnc")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[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("3b31edcf-34e4-4a58-8130-88b15b046d10")] + +// 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/OpenRA.Mods.Cnc/TiberiumRefinery.cs b/OpenRA.Mods.Cnc/TiberiumRefinery.cs new file mode 100644 index 0000000000..500067f61c --- /dev/null +++ b/OpenRA.Mods.Cnc/TiberiumRefinery.cs @@ -0,0 +1,61 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.Cnc +{ + class TiberiumRefineryInfo : ITraitInfo + { + public object Create(Actor self) { return new TiberiumRefinery(self); } + } + + class TiberiumRefinery : IAcceptOre + { + Actor self; + public TiberiumRefinery(Actor self) + { + this.self = self; + self.World.AddFrameEndTask( + w => + { /* create the free harvester! */ + var harvester = w.CreateActor("harv", self.Location + new int2(0, 2), self.Owner); + var unit = harvester.traits.Get(); + unit.Facing = 64; + harvester.QueueActivity(new Harvest()); + }); + } + + public int2 DeliverOffset { get { return new int2(0, 2); } } + public void OnDock(Actor harv, DeliverOre dockOrder) + { + // Todo: need to be careful about cancellation and multiple harvs + harv.QueueActivity(new Move(self.Location + new int2(1,1), self)); + harv.QueueActivity(new Turn(96)); + harv.QueueActivity( new CallFunc( () => + self.traits.Get().PlayCustomAnimThen(self, "active", () => { + harv.traits.Get().Deliver(harv, self); + harv.QueueActivity(new Move(self.Location + DeliverOffset, self)); + harv.QueueActivity(new Harvest()); + }))); + } + } +} diff --git a/OpenRA.Mods.RA/Activities/CaptureBuilding.cs b/OpenRA.Mods.RA/Activities/CaptureBuilding.cs new file mode 100644 index 0000000000..3767610680 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/CaptureBuilding.cs @@ -0,0 +1,63 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA.Activities +{ + class CaptureBuilding : IActivity + { + Actor target; + + public CaptureBuilding(Actor target) { this.target = target; } + + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (target == null || target.IsDead) return NextActivity; + + if (target.Owner == self.Owner) + { + if (target.Health == target.Info.Traits.Get().HP) + return NextActivity; + target.InflictDamage(self, -EngineerCapture.EngineerDamage, Rules.WarheadInfo["Super"]); + } + else + { + if (target.Health - EngineerCapture.EngineerDamage <= 0) + { + target.Owner = self.Owner; + target.InflictDamage(self, target.Health - EngineerCapture.EngineerDamage, Rules.WarheadInfo["Super"]); + } + else + target.InflictDamage(self, EngineerCapture.EngineerDamage, Rules.WarheadInfo["Super"]); + } + + // the engineer is sacrificed. + self.World.AddFrameEndTask(w => w.Remove(self)); + + return NextActivity; + } + + public void Cancel(Actor self) { target = null; NextActivity = null; } + } +} diff --git a/OpenRA.Mods.RA/Activities/Demolish.cs b/OpenRA.Mods.RA/Activities/Demolish.cs new file mode 100644 index 0000000000..6aa0dd4678 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/Demolish.cs @@ -0,0 +1,46 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + class Demolish : IActivity + { + Actor target; + public IActivity NextActivity { get; set; } + + public Demolish( Actor target ) + { + this.target = target; + } + + public IActivity Tick(Actor self) + { + if (target == null || target.IsDead) return NextActivity; + self.World.AddFrameEndTask(w => w.Add(new DelayedAction(25*2, + () => target.InflictDamage(self, target.Health, Rules.WarheadInfo["DemolishWarhead"])))); + return NextActivity; + } + + public void Cancel(Actor self) { target = null; NextActivity = null; } + } +} diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs new file mode 100644 index 0000000000..a48b015aa8 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -0,0 +1,47 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA.Activities +{ + class Infiltrate : IActivity + { + Actor target; + public Infiltrate(Actor target) { this.target = target; } + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (target == null || target.IsDead) return NextActivity; + if (target.Owner == self.Owner) return NextActivity; + + foreach (var t in target.traits.WithInterface()) + t.OnInfiltrate(target, self); + + self.World.AddFrameEndTask(w => w.Remove(self)); + + return NextActivity; + } + + public void Cancel(Actor self) { target = null; NextActivity = null; } + } +} diff --git a/OpenRA.Mods.RA/Activities/LayMine.cs b/OpenRA.Mods.RA/Activities/LayMine.cs new file mode 100755 index 0000000000..feebe6ebd1 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/LayMine.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + class LayMine : IActivity + { + public IActivity NextActivity { get; set; } + + public IActivity Tick( Actor self ) + { + self.World.AddFrameEndTask( + w => w.CreateActor(self.Info.Traits.Get().Mine, self.Location, self.Owner)); + return NextActivity; + } + + public void Cancel( Actor self ) + { + } + } +} diff --git a/OpenRA.Mods.RA/Activities/Steal.cs b/OpenRA.Mods.RA/Activities/Steal.cs new file mode 100644 index 0000000000..c4df427e80 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/Steal.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA.Activities +{ + class Steal : IActivity + { + Actor target; + + public Steal(Actor target) { this.target = target; } + + public IActivity NextActivity { get; set; } + + public IActivity Tick(Actor self) + { + if (target == null || target.IsDead) return NextActivity; + if (target.Owner == self.Owner) return NextActivity; + + foreach (var t in target.traits.WithInterface()) + t.OnSteal(target, self); + + self.World.AddFrameEndTask(w => w.Remove(self)); + + return NextActivity; + } + + public void Cancel(Actor self) { target = null; NextActivity = null; } + } +} diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs new file mode 100644 index 0000000000..8d5a69b181 --- /dev/null +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -0,0 +1,52 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Activities; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class C4DemolitionInfo : StatelessTraitInfo { } + + class C4Demolition : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + if (underCursor.Owner == self.Owner && !mi.Modifiers.HasModifier(Modifiers.Ctrl)) return null; + if (!underCursor.traits.Contains()) return null; + + return new Order("C4", self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "C4") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor, 2)); + self.QueueActivity(new Demolish(order.TargetActor)); + self.QueueActivity(new Move(self.Location, 0)); + } + } + } +} diff --git a/OpenRA.Mods.RA/Crates/ArmorUpgradeCrateAction.cs b/OpenRA.Mods.RA/Crates/ArmorUpgradeCrateAction.cs new file mode 100644 index 0000000000..65fe882c58 --- /dev/null +++ b/OpenRA.Mods.RA/Crates/ArmorUpgradeCrateAction.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class ArmorUpgradeCrateActionInfo : ITraitInfo + { + public float Multiplier = 2.0f; + public int SelectionShares = 10; + public object Create(Actor self) { return new ArmorUpgradeCrateAction(self); } + } + + class ArmorUpgradeCrateAction : ICrateAction + { + Actor self; + public ArmorUpgradeCrateAction(Actor self) + { + this.self = self; + } + + public int SelectionShares + { + get { return self.Info.Traits.Get().SelectionShares; } + } + + public void Activate(Actor collector) + { + Sound.PlayToPlayer(collector.Owner, "armorup1.aud"); + collector.World.AddFrameEndTask(w => + { + var multiplier = self.Info.Traits.Get().Multiplier; + collector.traits.Add(new ArmorUpgrade(multiplier)); + w.Add(new CrateEffect(collector, "armor")); + }); + } + } + + class ArmorUpgrade : IDamageModifier + { + float multiplier; + public ArmorUpgrade(float multiplier) { this.multiplier = 1/multiplier; } + public float GetDamageModifier() { return multiplier; } + } +} diff --git a/OpenRA.Mods.RA/Crates/FirepowerUpgradeCrateAction.cs b/OpenRA.Mods.RA/Crates/FirepowerUpgradeCrateAction.cs new file mode 100644 index 0000000000..e21d586520 --- /dev/null +++ b/OpenRA.Mods.RA/Crates/FirepowerUpgradeCrateAction.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class FirepowerUpgradeCrateActionInfo : ITraitInfo + { + public float Multiplier = 2.0f; + public int SelectionShares = 10; + public object Create(Actor self) { return new FirepowerUpgradeCrateAction(self); } + } + + class FirepowerUpgradeCrateAction : ICrateAction + { + Actor self; + public FirepowerUpgradeCrateAction(Actor self) + { + this.self = self; + } + + public int SelectionShares + { + get { return self.Info.Traits.Get().SelectionShares; } + } + + public void Activate(Actor collector) + { + Sound.PlayToPlayer(collector.Owner, "firepo1.aud"); + collector.World.AddFrameEndTask(w => + { + var multiplier = self.Info.Traits.Get().Multiplier; + collector.traits.Add(new FirepowerUpgrade(multiplier)); + w.Add(new CrateEffect(collector, "fpower")); + }); + } + } + + class FirepowerUpgrade : IFirepowerModifier + { + float multiplier; + public FirepowerUpgrade(float multiplier) { this.multiplier = multiplier; } + public float GetFirepowerModifier() { return multiplier; } + } +} diff --git a/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs b/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs new file mode 100644 index 0000000000..cac401d52b --- /dev/null +++ b/OpenRA.Mods.RA/Crates/GiveCashCrateAction.cs @@ -0,0 +1,56 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class GiveCashCrateActionInfo : ITraitInfo + { + public int Amount = 2000; + public int SelectionShares = 10; + public object Create(Actor self) { return new GiveCashCrateAction(self); } + } + + class GiveCashCrateAction : ICrateAction + { + Actor self; + public GiveCashCrateAction(Actor self) + { + this.self = self; + } + + public int SelectionShares + { + get { return self.Info.Traits.Get().SelectionShares; } + } + + public void Activate(Actor collector) + { + collector.World.AddFrameEndTask(w => + { + var amount = self.Info.Traits.Get().Amount; + collector.Owner.GiveCash(amount); + w.Add(new CrateEffect(collector, "dollar")); + }); + } + } +} diff --git a/OpenRA.Mods.RA/Crates/SpeedUpgradeCrateAction.cs b/OpenRA.Mods.RA/Crates/SpeedUpgradeCrateAction.cs new file mode 100644 index 0000000000..09ff17e19d --- /dev/null +++ b/OpenRA.Mods.RA/Crates/SpeedUpgradeCrateAction.cs @@ -0,0 +1,64 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class SpeedUpgradeCrateActionInfo : ITraitInfo + { + public float Multiplier = 1.7f; + public int SelectionShares = 10; + public object Create(Actor self) { return new SpeedUpgradeCrateAction(self); } + } + + class SpeedUpgradeCrateAction : ICrateAction + { + Actor self; + public SpeedUpgradeCrateAction(Actor self) + { + this.self = self; + } + + public int SelectionShares + { + get { return self.Info.Traits.Get().SelectionShares; } + } + + public void Activate(Actor collector) + { + Sound.PlayToPlayer(collector.Owner, "unitspd1.aud"); + collector.World.AddFrameEndTask(w => + { + var multiplier = self.Info.Traits.Get().Multiplier; + collector.traits.Add(new SpeedUpgrade(multiplier)); + w.Add(new CrateEffect(collector, "speed")); + }); + } + } + + class SpeedUpgrade : ISpeedModifier + { + float multiplier; + public SpeedUpgrade(float multiplier) { this.multiplier = multiplier; } + public float GetSpeedModifier() { return multiplier; } + } +} diff --git a/OpenRA.Mods.RA/Effects/CrateEffect.cs b/OpenRA.Mods.RA/Effects/CrateEffect.cs new file mode 100644 index 0000000000..7a6049a6a7 --- /dev/null +++ b/OpenRA.Mods.RA/Effects/CrateEffect.cs @@ -0,0 +1,52 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Effects +{ + class CrateEffect : IEffect + { + Actor a; + Animation anim = new Animation("crate-effects"); + float2 doorOffset = new float2(-4,0); + + public CrateEffect(Actor a, string seq) + { + this.a = a; + anim.PlayThen(seq, + () => a.World.AddFrameEndTask(w => w.Remove(this))); + } + + public void Tick( World world ) + { + anim.Tick(); + } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image, + a.CenterLocation - .5f * anim.Image.size + doorOffset, "effect"); + } + } +} diff --git a/OpenRA.Mods.RA/Effects/GpsSatellite.cs b/OpenRA.Mods.RA/Effects/GpsSatellite.cs new file mode 100644 index 0000000000..fa9ce588ff --- /dev/null +++ b/OpenRA.Mods.RA/Effects/GpsSatellite.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Effects +{ + class GpsSatellite : IEffect + { + readonly float heightPerTick = 10; + float2 offset; + Animation anim = new Animation("sputnik"); + + public GpsSatellite(float2 offset) + { + this.offset = offset; + anim.PlayRepeating("idle"); + } + + public void Tick( World world ) + { + anim.Tick(); + offset.Y -= heightPerTick; + + if (offset.Y < 0) + world.AddFrameEndTask(w => w.Remove(this)); + } + + public IEnumerable Render() + { + yield return new Renderable(anim.Image,offset, "effect"); + } + } +} diff --git a/OpenRA.Mods.RA/Effects/InvulnEffect.cs b/OpenRA.Mods.RA/Effects/InvulnEffect.cs new file mode 100644 index 0000000000..a255bca624 --- /dev/null +++ b/OpenRA.Mods.RA/Effects/InvulnEffect.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Effects +{ + class InvulnEffect : IEffect + { + Actor a; + IronCurtainable b; + + public InvulnEffect(Actor a) + { + this.a = a; + this.b = a.traits.Get(); + } + + public void Tick( World world ) + { + if (a.IsDead || b.GetDamageModifier() > 0) + world.AddFrameEndTask(w => w.Remove(this)); + } + + public IEnumerable Render() + { + foreach (var r in a.Render()) + yield return r.WithPalette("invuln"); + } + } +} diff --git a/OpenRA.Mods.RA/Effects/Parachute.cs b/OpenRA.Mods.RA/Effects/Parachute.cs new file mode 100644 index 0000000000..162f1a7a2c --- /dev/null +++ b/OpenRA.Mods.RA/Effects/Parachute.cs @@ -0,0 +1,82 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Effects +{ + class Parachute : IEffect + { + readonly Animation anim; + readonly Animation paraAnim; + readonly float2 location; + + readonly Actor cargo; + readonly Player owner; + + float altitude; + const float fallRate = .3f; + + public Parachute(Player owner, string image, float2 location, int altitude, Actor cargo) + { + this.location = location; + this.altitude = altitude; + this.cargo = cargo; + this.owner = owner; + + anim = new Animation(image); + if (anim.HasSequence("idle")) + anim.PlayFetchIndex("idle", () => 0); + else + anim.PlayFetchIndex("stand", () => 0); + anim.Tick(); + + paraAnim = new Animation("parach"); + paraAnim.PlayThen("open", () => paraAnim.PlayRepeating("idle")); + } + + public void Tick(World world) + { + paraAnim.Tick(); + + altitude -= fallRate; + + if (altitude <= 0) + world.AddFrameEndTask(w => + { + w.Remove(this); + w.Add(cargo); + cargo.CancelActivity(); + cargo.traits.Get().TeleportTo(cargo, ((1 / 24f) * location).ToInt2()); + }); + } + + public IEnumerable Render() + { + var pos = location - new float2(0, altitude); + yield return new Renderable(anim.Image, location - .5f * anim.Image.size, "shadow", 0); + yield return new Renderable(anim.Image, pos - .5f * anim.Image.size, owner.Palette, 2); + yield return new Renderable(paraAnim.Image, pos - .5f * paraAnim.Image.size, owner.Palette, 3); + } + } +} diff --git a/OpenRA.Mods.RA/Effects/SatelliteLaunch.cs b/OpenRA.Mods.RA/Effects/SatelliteLaunch.cs new file mode 100644 index 0000000000..4a3eaeda2d --- /dev/null +++ b/OpenRA.Mods.RA/Effects/SatelliteLaunch.cs @@ -0,0 +1,58 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Effects; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Effects +{ + class SatelliteLaunch : IEffect + { + int frame = 0; + Actor a; + Animation doors = new Animation("atek"); + float2 doorOffset = new float2(-4,0); + + public SatelliteLaunch(Actor a) + { + this.a = a; + doors.PlayThen("active", + () => a.World.AddFrameEndTask(w => w.Remove(this))); + } + + public void Tick( World world ) + { + doors.Tick(); + + if (++frame == 19) + { + world.AddFrameEndTask(w => w.Add(new GpsSatellite(a.CenterLocation - .5f * doors.Image.size + doorOffset))); + } + } + + public IEnumerable Render() + { + yield return new Renderable(doors.Image, + a.CenterLocation - .5f * doors.Image.size + doorOffset, "effect"); + } + } +} diff --git a/OpenRA.Mods.RA/EngineerCapture.cs b/OpenRA.Mods.RA/EngineerCapture.cs new file mode 100644 index 0000000000..656e4486bb --- /dev/null +++ b/OpenRA.Mods.RA/EngineerCapture.cs @@ -0,0 +1,56 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Activities; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class EngineerCaptureInfo : StatelessTraitInfo { } + + class EngineerCapture : IIssueOrder, IResolveOrder + { + public const int EngineerDamage = 300; // todo: push into rules, as a weapon + + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + if (!underCursor.traits.Contains()) return null; + + // todo: other bits + if (underCursor.Owner == null) return null; // don't allow capturing of bridges, etc. + + return new Order(underCursor.Health <= EngineerDamage ? "Capture" : "Infiltrate", + self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Infiltrate" || order.OrderString == "Capture") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor, 1)); + self.QueueActivity(new CaptureBuilding(order.TargetActor)); + } + } + } +} diff --git a/OpenRA.Mods.RA/GpsPower.cs b/OpenRA.Mods.RA/GpsPower.cs new file mode 100644 index 0000000000..153d4a03d8 --- /dev/null +++ b/OpenRA.Mods.RA/GpsPower.cs @@ -0,0 +1,63 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.Effects; +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class GpsPowerInfo : SupportPowerInfo + { + public readonly int RevealDelay = 0; + + public override object Create(Actor self) { return new GpsPower(self, this); } + } + + class GpsPower : SupportPower + { + public GpsPower(Actor self, GpsPowerInfo info) : base(self, info) { } + + protected override void OnFinishCharging() + { + var launchSite = Owner.World.Queries.OwnedBy[Owner] + .FirstOrDefault(a => a.traits.Contains()); + + if (launchSite == null) + return; + + Owner.World.AddFrameEndTask(w => + { + Sound.PlayToPlayer(Owner, "satlnch1.aud"); + + w.Add(new SatelliteLaunch(launchSite)); + w.Add(new DelayedAction((Info as GpsPowerInfo).RevealDelay * 25, + () => Owner.Shroud.HasGPS = true)); + }); + + FinishActivate(); + } + } + + // tag trait to identify the building + class GpsLaunchSiteInfo : StatelessTraitInfo { } + class GpsLaunchSite { } +} diff --git a/OpenRA.Mods.RA/InfiltrateForSonarPulse.cs b/OpenRA.Mods.RA/InfiltrateForSonarPulse.cs new file mode 100644 index 0000000000..a9dd26c2db --- /dev/null +++ b/OpenRA.Mods.RA/InfiltrateForSonarPulse.cs @@ -0,0 +1,34 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class InfiltrateForSonarPulseInfo : StatelessTraitInfo { } + + class InfiltrateForSonarPulse : IAcceptSpy + { + public void OnInfiltrate(Actor self, Actor spy) + { + spy.Owner.PlayerActor.traits.Get().Give(1.0f); + } + } +} diff --git a/OpenRA.Mods.RA/IronCurtainPower.cs b/OpenRA.Mods.RA/IronCurtainPower.cs new file mode 100644 index 0000000000..fd739c6204 --- /dev/null +++ b/OpenRA.Mods.RA/IronCurtainPower.cs @@ -0,0 +1,119 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class IronCurtainPowerInfo : SupportPowerInfo + { + public readonly float Duration = 0f; + public override object Create(Actor self) { return new IronCurtainPower(self, this); } + } + + class IronCurtainPower : SupportPower, IResolveOrder + { + public IronCurtainPower(Actor self, IronCurtainPowerInfo info) : base(self, info) { } + + protected override void OnBeginCharging() { Sound.PlayToPlayer(Owner, "ironchg1.aud"); } + protected override void OnFinishCharging() { Sound.PlayToPlayer(Owner, "ironrdy1.aud"); } + protected override void OnActivate() + { + Game.controller.orderGenerator = new SelectTarget(); + Sound.Play("slcttgt1.aud"); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "IronCurtain") + { + if (self.Owner == self.World.LocalPlayer) + Game.controller.CancelInputMode(); + + var curtain = self.World.Queries.WithTrait() + .Where(a => a.Actor.Owner != null) + .FirstOrDefault().Actor; + if (curtain != null) + curtain.traits.Get().PlayCustomAnim(curtain, "active"); + + Sound.Play("ironcur9.aud"); + + order.TargetActor.traits.Get().Activate(order.TargetActor, + (int)((Info as IronCurtainPowerInfo).Duration * 25 * 60)); + + FinishActivate(); + } + } + + class SelectTarget : IOrderGenerator + { + public SelectTarget() { } + + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + Game.controller.CancelInputMode(); + + return OrderInner(world, xy, mi); + } + + IEnumerable OrderInner(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + { + var underCursor = world.FindUnitsAtMouse(mi.Location) + .Where(a => a.Owner != null + && a.traits.Contains() + && a.traits.Contains()).FirstOrDefault(); + + if (underCursor != null) + yield return new Order("IronCurtain", underCursor.Owner.PlayerActor, underCursor); + } + + yield break; + } + + public void Tick(World world) + { + var hasStructure = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(); + + if (!hasStructure) + Game.controller.CancelInputMode(); + } + + public void Render(World world) { } + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + mi.Button = MouseButton.Left; + return OrderInner(world, xy, mi).Any() + ? "ability" : "move-blocked"; + } + } + } + + // tag trait for the building + class IronCurtainInfo : StatelessTraitInfo { } + class IronCurtain { } +} diff --git a/OpenRA.Mods.RA/IronCurtainable.cs b/OpenRA.Mods.RA/IronCurtainable.cs new file mode 100644 index 0000000000..7ab0ade00d --- /dev/null +++ b/OpenRA.Mods.RA/IronCurtainable.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class IronCurtainableInfo : ITraitInfo + { + public object Create(Actor self) { return new IronCurtainable(); } + } + + class IronCurtainable : IDamageModifier, ITick + { + [Sync] + int RemainingTicks = 0; + + public void Tick(Actor self) + { + if (RemainingTicks > 0) + RemainingTicks--; + } + + public float GetDamageModifier() + { + return (RemainingTicks > 0) ? 0.0f : 1.0f; + } + + public void Activate(Actor self, int duration) + { + self.World.AddFrameEndTask(w => w.Add(new InvulnEffect(self))); + RemainingTicks = duration; + } + } +} diff --git a/OpenRA.Mods.RA/Mine.cs b/OpenRA.Mods.RA/Mine.cs new file mode 100644 index 0000000000..0daad0d68b --- /dev/null +++ b/OpenRA.Mods.RA/Mine.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Effects; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class MineInfo : ITraitInfo + { + public readonly int Damage = 0; + public readonly UnitMovementType[] TriggeredBy = { }; + public readonly string Warhead = "ATMine"; + public readonly bool AvoidFriendly = true; + + public object Create(Actor self) { return new Mine(self); } + } + + class Mine : ICrushable, IOccupySpace + { + readonly Actor self; + public Mine(Actor self) + { + this.self = self; + self.World.WorldActor.traits.Get().Add(self, this); + } + + public void OnCrush(Actor crusher) + { + if (crusher.traits.Contains() && crusher.Owner == self.Owner) + return; + + var info = self.Info.Traits.Get(); + var warhead = Rules.WarheadInfo[info.Warhead]; + + self.World.AddFrameEndTask(w => + { + w.Remove(self); + w.Add(new Explosion(w, self.CenterLocation.ToInt2(), warhead.Explosion, false)); + crusher.InflictDamage(crusher, info.Damage, warhead); + }); + } + + public bool IsPathableCrush(UnitMovementType umt, Player player) + { + return !self.Info.Traits.Get().AvoidFriendly || (player != self.Owner); + } + + public bool IsCrushableBy(UnitMovementType umt, Player player) + { + return self.Info.Traits.Get().TriggeredBy.Contains(umt); + } + + public IEnumerable OccupiedCells() { yield return self.Location; } + } + + /* tag trait for stuff that shouldnt trigger mines */ + class MineImmuneInfo : StatelessTraitInfo { } + class MineImmune { } +} diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs new file mode 100644 index 0000000000..1dc1ec0f1c --- /dev/null +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -0,0 +1,62 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Linq; +using OpenRA.Mods.RA.Activities; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class MinelayerInfo : StatelessTraitInfo + { + public readonly string Mine = "minv"; + } + + class Minelayer : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo != null && !limitedAmmo.HasAmmo()) + return null; + + // Ensure that the cell is empty except for the minelayer + if (self.World.WorldActor.traits.Get().GetUnitsAt(xy).Any(a => a != self)) + return null; + + if (mi.Button == MouseButton.Right && underCursor == self) + return new Order("Deploy", self); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Deploy") + { + var limitedAmmo = self.traits.GetOrDefault(); + if (limitedAmmo != null) + limitedAmmo.Attacking(self); + + self.QueueActivity( new LayMine() ); + } + } + } +} diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj new file mode 100644 index 0000000000..796b5cf958 --- /dev/null +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -0,0 +1,104 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E} + Library + Properties + OpenRA.Mods.RA + OpenRA.Mods.RA + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} + OpenRA.FileFormats + + + {0DFB103F-2962-400F-8C6D-E2C28CCBA633} + OpenRA.Game + + + + + + mkdir "$(SolutionDir)mods/ra/" +copy "$(TargetPath)" "$(SolutionDir)mods/ra/" + + \ No newline at end of file diff --git a/OpenRA.Mods.RA/ParaDrop.cs b/OpenRA.Mods.RA/ParaDrop.cs new file mode 100644 index 0000000000..d3527dc00d --- /dev/null +++ b/OpenRA.Mods.RA/ParaDrop.cs @@ -0,0 +1,82 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Mods.RA.Effects; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class ParaDropInfo : ITraitInfo + { + public readonly int LZRange = 4; + public object Create(Actor self) { return new ParaDrop(); } + } + + class ParaDrop : ITick + { + readonly List droppedAt = new List(); + int2 lz; + + public void SetLZ( int2 lz ) + { + this.lz = lz; + droppedAt.Clear(); + } + + public void Tick(Actor self) + { + var r = self.Info.Traits.Get().LZRange; + + if ((self.Location - lz).LengthSquared <= r * r && !droppedAt.Contains(self.Location)) + { + // todo: check is this is a good drop cell. + + // unload a dude here + droppedAt.Add(self.Location); + + var cargo = self.traits.Get(); + if (cargo.IsEmpty(self)) + FinishedDropping(self); + else + { + var a = cargo.Unload(self); + var rs = a.traits.Get(); + + self.World.AddFrameEndTask(w => w.Add( + new Parachute(self.Owner, rs.anim.Name, + self.CenterLocation, + self.traits.Get().Altitude, a))); + + Sound.Play("chute1.aud"); + } + } + } + + void FinishedDropping(Actor self) + { + // this kindof sucks, actually. + self.CancelActivity(); + self.QueueActivity(new Fly(Util.CenterOfCell(self.World.ChooseRandomEdgeCell()))); + self.QueueActivity(new RemoveSelf()); + } + } +} diff --git a/OpenRA.Mods.RA/ParatroopersPower.cs b/OpenRA.Mods.RA/ParatroopersPower.cs new file mode 100644 index 0000000000..f20c4f4425 --- /dev/null +++ b/OpenRA.Mods.RA/ParatroopersPower.cs @@ -0,0 +1,92 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class ParatroopersPowerInfo : SupportPowerInfo + { + public string[] DropItems = { }; + public override object Create(Actor self) { return new ParatroopersPower(self,this); } + } + + class ParatroopersPower : SupportPower, IResolveOrder + { + public ParatroopersPower(Actor self, ParatroopersPowerInfo info) : base(self, info) { } + + protected override void OnActivate() + { + Game.controller.orderGenerator = new SelectTarget(); + Sound.Play("slcttgt1.aud"); + } + + class SelectTarget : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Left) + yield return new Order("ParatroopersActivate", world.LocalPlayer.PlayerActor, xy); + } + + public void Tick(World world) {} + public void Render(World world) {} + + public string GetCursor(World world, int2 xy, MouseInput mi) + { + return "ability"; + } + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "ParatroopersActivate") + { + if (self.Owner == self.World.LocalPlayer) + Game.controller.CancelInputMode(); + + DoParadrop(Owner, order.TargetLocation, + self.Info.Traits.Get().DropItems); + + FinishActivate(); + } + } + + static void DoParadrop(Player owner, int2 p, string[] items) + { + var startPos = owner.World.ChooseRandomEdgeCell(); + owner.World.AddFrameEndTask(w => + { + var a = w.CreateActor("BADR", startPos, owner); + + a.CancelActivity(); + a.QueueActivity(new FlyCircle(p)); + a.traits.Get().SetLZ(p); + + var cargo = a.traits.Get(); + foreach (var i in items) + cargo.Load(a, new Actor(owner.World, i.ToLowerInvariant(), + new int2(0,0), a.Owner)); + }); + } + } +} diff --git a/OpenRA.Mods.RA/Properties/AssemblyInfo.cs b/OpenRA.Mods.RA/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..db550e7faa --- /dev/null +++ b/OpenRA.Mods.RA/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("OpenRA.Mods.RA")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenRA.Mods.RA")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[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("23828b43-3536-4681-bc2f-2eb2e0972354")] + +// 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/OpenRA.Mods.RA/RenderSpy.cs b/OpenRA.Mods.RA/RenderSpy.cs new file mode 100644 index 0000000000..10f120d760 --- /dev/null +++ b/OpenRA.Mods.RA/RenderSpy.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class RenderSpyInfo : RenderInfantryInfo + { + public override object Create(Actor self) { return new RenderSpy(self); } + } + + class RenderSpy : RenderInfantry, IRenderModifier + { + public RenderSpy(Actor self) + : base(self) + { + } + + public IEnumerable ModifyRender(Actor self, IEnumerable r) + { + if (self.Owner == self.World.LocalPlayer) + return r; + + return r.Select(a => a.WithPalette(self.World.LocalPlayer.Palette)); + } + + public override void Tick(Actor self) + { + anim.ChangeImage(self.Owner == self.World.LocalPlayer ? GetImage(self) : "e1"); + base.Tick(self); + } + } +} diff --git a/OpenRA.Mods.RA/RepairableNear.cs b/OpenRA.Mods.RA/RepairableNear.cs new file mode 100644 index 0000000000..81384e8cd3 --- /dev/null +++ b/OpenRA.Mods.RA/RepairableNear.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class RepairableNearInfo : StatelessTraitInfo { } + + class RepairableNear : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + + if (underCursor.Owner == self.Owner && + (underCursor.Info.Name == "spen" || underCursor.Info.Name == "syrd") && + self.Health < self.GetMaxHP()) + return new Order("Enter", self, underCursor); + + return null; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Enter") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor, 1)); + self.QueueActivity(new Repair(false)); + } + } + } +} diff --git a/OpenRA.Mods.RA/RequiresPower.cs b/OpenRA.Mods.RA/RequiresPower.cs new file mode 100644 index 0000000000..64679ce94c --- /dev/null +++ b/OpenRA.Mods.RA/RequiresPower.cs @@ -0,0 +1,44 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class RequiresPowerInfo : ITraitInfo + { + public object Create(Actor self) { return new RequiresPower(self); } + } + + class RequiresPower : IDisable + { + readonly Actor self; + public RequiresPower( Actor self ) + { + this.self = self; + } + + public bool Disabled + { + get { return (self.Owner.GetPowerState() != PowerState.Normal); } + set {} // Cannot explicity set + } + } +} diff --git a/OpenRA.Mods.RA/SonarPulsePower.cs b/OpenRA.Mods.RA/SonarPulsePower.cs new file mode 100644 index 0000000000..6f6c4e47dd --- /dev/null +++ b/OpenRA.Mods.RA/SonarPulsePower.cs @@ -0,0 +1,54 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + public class SonarPulsePowerInfo : SupportPowerInfo + { + public override object Create(Actor self) { return new SonarPulsePower(self, this); } + } + + public class SonarPulsePower : SupportPower, IResolveOrder + { + public SonarPulsePower(Actor self, SonarPulsePowerInfo info) : base(self, info) { } + + protected override void OnBeginCharging() { } + protected override void OnFinishCharging() { Sound.PlayToPlayer(Owner, "pulse1.aud"); } + + protected override void OnActivate() + { + Game.IssueOrder(new Order("SonarPulse", Owner.PlayerActor)); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "SonarPulse") + { + // TODO: Reveal submarines + + // Should this play for all players? + Sound.Play("sonpulse.aud"); + FinishActivate(); + } + } + } +} diff --git a/OpenRA.Mods.RA/Spy.cs b/OpenRA.Mods.RA/Spy.cs new file mode 100644 index 0000000000..b7992e5aef --- /dev/null +++ b/OpenRA.Mods.RA/Spy.cs @@ -0,0 +1,51 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Activities; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class SpyInfo : StatelessTraitInfo { } + + class Spy : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + if (underCursor.Owner == self.Owner) return null; + if (!underCursor.traits.Contains()) return null; + + return new Order("Infiltrate", self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Infiltrate") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor, 1)); + self.QueueActivity(new Infiltrate(order.TargetActor)); + } + } + } +} diff --git a/OpenRA.Mods.RA/SpyPlanePower.cs b/OpenRA.Mods.RA/SpyPlanePower.cs new file mode 100644 index 0000000000..44993f2473 --- /dev/null +++ b/OpenRA.Mods.RA/SpyPlanePower.cs @@ -0,0 +1,86 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class SpyPlanePowerInfo : SupportPowerInfo + { + public readonly int Range = 10; + public override object Create(Actor self) { return new SpyPlanePower(self,this); } + } + + class SpyPlanePower : SupportPower, IResolveOrder + { + public SpyPlanePower(Actor self, SpyPlanePowerInfo info) : base(self, info) { } + + protected override void OnFinishCharging() { Sound.PlayToPlayer(Owner, "spypln1.aud"); } + protected override void OnActivate() + { + Game.controller.orderGenerator = new SelectTarget(); + Sound.Play("slcttgt1.aud"); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "SpyPlane") + { + FinishActivate(); + + if (order.Player == Owner.World.LocalPlayer) + Game.controller.CancelInputMode(); + + var enterCell = self.World.ChooseRandomEdgeCell(); + var exitCell = self.World.ChooseRandomEdgeCell(); + + var plane = self.World.CreateActor("U2", enterCell, self.Owner); + plane.CancelActivity(); + plane.QueueActivity(new Fly(Util.CenterOfCell(order.TargetLocation))); + plane.QueueActivity(new CallFunc( + () => Owner.Shroud.Explore(Owner.World, order.TargetLocation, + (Info as SpyPlanePowerInfo).Range))); + plane.QueueActivity(new Fly(Util.CenterOfCell(exitCell))); + plane.QueueActivity(new RemoveSelf()); + } + } + + class SelectTarget : IOrderGenerator + { + public IEnumerable Order(World world, int2 xy, MouseInput mi) + { + if (mi.Button == MouseButton.Right) + { + Game.controller.CancelInputMode(); + yield break; + } + + yield return new Order("SpyPlane", world.LocalPlayer.PlayerActor, xy); + } + + public void Tick(World world) {} + public void Render(World world) {} + + public string GetCursor(World world, int2 xy, MouseInput mi) { return "ability"; } + } + } +} diff --git a/OpenRA.Mods.RA/Thief.cs b/OpenRA.Mods.RA/Thief.cs new file mode 100644 index 0000000000..6e74749694 --- /dev/null +++ b/OpenRA.Mods.RA/Thief.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using OpenRA.Mods.RA.Activities; +using OpenRA.Traits; +using OpenRA.Traits.Activities; + +namespace OpenRA.Mods.RA +{ + class ThiefInfo : StatelessTraitInfo { } + + class Thief : IIssueOrder, IResolveOrder + { + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) + { + if (mi.Button != MouseButton.Right) return null; + if (underCursor == null) return null; + if (!underCursor.traits.Contains()) return null; + + return new Order("Steal", self, underCursor); + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Steal") + { + self.CancelActivity(); + self.QueueActivity(new Move(order.TargetActor, 1)); + self.QueueActivity(new Steal(order.TargetActor)); + } + } + } +} diff --git a/OpenRA.Server/OpenRA.Server.csproj b/OpenRA.Server/OpenRA.Server.csproj index c1eaa4df1d..d3e07b5e65 100644 --- a/OpenRA.Server/OpenRA.Server.csproj +++ b/OpenRA.Server/OpenRA.Server.csproj @@ -55,7 +55,7 @@ - + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} OpenRA.FileFormats diff --git a/OpenRA.sln b/OpenRA.sln new file mode 100644 index 0000000000..6638a16ca3 --- /dev/null +++ b/OpenRA.sln @@ -0,0 +1,67 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.FileFormats", "OpenRA.FileFormats\OpenRA.FileFormats.csproj", "{BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Game", "OpenRA.Game\OpenRA.Game.csproj", "{0DFB103F-2962-400F-8C6D-E2C28CCBA633}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SequenceEditor", "SequenceEditor\SequenceEditor.csproj", "{230F65CE-A6DE-4235-8B38-13A3D606C7F7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Server", "OpenRA.Server\OpenRA.Server.csproj", "{76F621A1-3D8E-4A99-9F7E-B071EB657817}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Mods.RA", "OpenRA.Mods.RA\OpenRA.Mods.RA.csproj", "{4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Mods.Aftermath", "OpenRA.Mods.Aftermath\OpenRA.Mods.Aftermath.csproj", "{2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Mods.Cnc", "OpenRA.Mods.Cnc\OpenRA.Mods.Cnc.csproj", "{2881135D-4D62-493E-8F83-5EEE92CCC6BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRA.Gl", "OpenRA.Gl\OpenRA.Gl.csproj", "{67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mkremap", "mkremap\mkremap.csproj", "{3A2C18E7-0379-4D72-8A56-B850CB765197}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA}.Release|Any CPU.Build.0 = Release|Any CPU + {0DFB103F-2962-400F-8C6D-E2C28CCBA633}.Debug|Any CPU.ActiveCfg = Debug|x86 + {0DFB103F-2962-400F-8C6D-E2C28CCBA633}.Debug|Any CPU.Build.0 = Debug|x86 + {0DFB103F-2962-400F-8C6D-E2C28CCBA633}.Release|Any CPU.ActiveCfg = Release|x86 + {230F65CE-A6DE-4235-8B38-13A3D606C7F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {230F65CE-A6DE-4235-8B38-13A3D606C7F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {230F65CE-A6DE-4235-8B38-13A3D606C7F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {230F65CE-A6DE-4235-8B38-13A3D606C7F7}.Release|Any CPU.Build.0 = Release|Any CPU + {76F621A1-3D8E-4A99-9F7E-B071EB657817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76F621A1-3D8E-4A99-9F7E-B071EB657817}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76F621A1-3D8E-4A99-9F7E-B071EB657817}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76F621A1-3D8E-4A99-9F7E-B071EB657817}.Release|Any CPU.Build.0 = Release|Any CPU + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E}.Release|Any CPU.Build.0 = Release|Any CPU + {2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E1F8D8B-AEF5-4BCE-A95C-50223A0C7331}.Release|Any CPU.Build.0 = Release|Any CPU + {2881135D-4D62-493E-8F83-5EEE92CCC6BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2881135D-4D62-493E-8F83-5EEE92CCC6BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2881135D-4D62-493E-8F83-5EEE92CCC6BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2881135D-4D62-493E-8F83-5EEE92CCC6BE}.Release|Any CPU.Build.0 = Release|Any CPU + {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67CF1A10-C5F6-48FA-B1A7-FE83BE4CE2CC}.Release|Any CPU.Build.0 = Release|Any CPU + {3A2C18E7-0379-4D72-8A56-B850CB765197}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A2C18E7-0379-4D72-8A56-B850CB765197}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A2C18E7-0379-4D72-8A56-B850CB765197}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A2C18E7-0379-4D72-8A56-B850CB765197}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SequenceEditor/SequenceEditor.csproj b/SequenceEditor/SequenceEditor.csproj index aafb429b0e..e4c94327a4 100644 --- a/SequenceEditor/SequenceEditor.csproj +++ b/SequenceEditor/SequenceEditor.csproj @@ -91,7 +91,7 @@ - + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} OpenRA.FileFormats diff --git a/mods/aftermath/mod.yaml b/mods/aftermath/mod.yaml index 5628cfa208..d031c74377 100644 --- a/mods/aftermath/mod.yaml +++ b/mods/aftermath/mod.yaml @@ -17,4 +17,4 @@ Sequences: mods/aftermath/sequences.xml: Additional aftermath Assemblies: - mods/aftermath/OpenRa.Mods.Aftermath.dll: Traits used \ No newline at end of file + mods/aftermath/OpenRA.Mods.Aftermath.dll: Traits used \ No newline at end of file diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index feebc8e69d..a289a6a453 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -50,8 +50,8 @@ Chrome: mods/cnc/chrome.xml: Assemblies: - mods/cnc/OpenRa.Mods.Cnc.dll: Cnc mod traits - mods/ra/OpenRa.Mods.RA.dll: Red alert mod traits + mods/cnc/OpenRA.Mods.Cnc.dll: Cnc mod traits + mods/ra/OpenRA.Mods.RA.dll: Red alert mod traits Maps: scg01ea.ini: GDI Mission 1 diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index f8b9c86132..b95a3c7375 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -34,4 +34,4 @@ Chrome: mods/ra/chrome.xml: Assemblies: - mods/ra/OpenRa.Mods.RA.dll: Traits used + mods/ra/OpenRA.Mods.RA.dll: Traits used diff --git a/packaging/osx/package.sh b/packaging/osx/package.sh index 82e31215b4..33c78bf8cb 100755 --- a/packaging/osx/package.sh +++ b/packaging/osx/package.sh @@ -55,10 +55,10 @@ export CC="gcc -arch i386 -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/Ma export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/Library/Frameworks/Mono.framework/Versions/Current/lib/pkgconfig/ # Package the server binary -mkbundle --deps --static -z -o openra_server OpenRA.Server.exe OpenRa.FileFormats.dll thirdparty/Tao/Tao.Sdl.dll +mkbundle --deps --static -z -o openra_server OpenRA.Server.exe OpenRA.FileFormats.dll thirdparty/Tao/Tao.Sdl.dll # Package the game binary -mkbundle --deps --static -z -o OpenRA OpenRa.Game.exe OpenRa.Gl.dll OpenRa.FileFormats.dll thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.OpenAl.dll thirdparty/Tao/Tao.FreeType.dll thirdparty/Tao/Tao.Sdl.dll +mkbundle --deps --static -z -o OpenRA OpenRA.Game.exe OpenRA.Gl.dll OpenRA.FileFormats.dll thirdparty/Tao/Tao.Cg.dll thirdparty/Tao/Tao.OpenGl.dll thirdparty/Tao/Tao.OpenAl.dll thirdparty/Tao/Tao.FreeType.dll thirdparty/Tao/Tao.Sdl.dll # Copy game files into our game bundle template cp -R packaging/osx/OpenRA.app .