diff --git a/.gitignore b/.gitignore index 168a3f1d19..3f4542fb21 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ obj # Crap generated by OpenRa sheet-*.png log.txt + +/replay.rep \ No newline at end of file diff --git a/OpenRa.FileFormats/FileSystem.cs b/OpenRa.FileFormats/FileSystem.cs index 4c8b1ab4fc..8a923f8363 100644 --- a/OpenRa.FileFormats/FileSystem.cs +++ b/OpenRa.FileFormats/FileSystem.cs @@ -10,6 +10,27 @@ namespace OpenRa.FileFormats { static List mountedFolders = new List(); + public static void MountDefault( bool useAftermath ) + { + FileSystem.Mount( new Folder( "./" ) ); + if( File.Exists( "main.mix" ) ) + FileSystem.Mount( new Package( "main.mix" ) ); + FileSystem.Mount( new Package( "redalert.mix" ) ); + FileSystem.Mount( new Package( "conquer.mix" ) ); + FileSystem.Mount( new Package( "hires.mix" ) ); + FileSystem.Mount( new Package( "general.mix" ) ); + FileSystem.Mount( new Package( "local.mix" ) ); + FileSystem.Mount( new Package( "sounds.mix" ) ); + FileSystem.Mount( new Package( "speech.mix" ) ); + FileSystem.Mount( new Package( "allies.mix" ) ); + FileSystem.Mount( new Package( "russian.mix" ) ); + if( useAftermath ) + { + FileSystem.Mount( new Package( "expand2.mix" ) ); + FileSystem.Mount( new Package( "hires1.mix" ) ); + } + } + public static void Mount(IFolder folder) { mountedFolders.Add(folder); @@ -24,7 +45,7 @@ namespace OpenRa.FileFormats return s; } - throw new FileNotFoundException("File not found", filename); + throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename ); } public static Stream OpenWithExts( string filename, params string[] exts ) @@ -39,7 +60,7 @@ namespace OpenRa.FileFormats } } - throw new FileNotFoundException( "File not found", filename ); + throw new FileNotFoundException( string.Format( "File not found: {0}", filename ), filename ); } } } diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index d2d2f566ce..1067c774c5 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -46,9 +46,9 @@ namespace OpenRa.Game public static bool skipMakeAnims = true; - public static void Initialize(string mapName, Renderer renderer, int2 clientSize, int localPlayer) + public static void Initialize(string mapName, Renderer renderer, int2 clientSize, int localPlayer, bool useAftermath) { - Rules.LoadRules(mapName); + Rules.LoadRules(mapName, useAftermath); for (int i = 0; i < 8; i++) players.Add(i, diff --git a/OpenRa.Game/GameRules/Rules.cs b/OpenRa.Game/GameRules/Rules.cs index 3eed563b64..8ee2c516e8 100755 --- a/OpenRa.Game/GameRules/Rules.cs +++ b/OpenRa.Game/GameRules/Rules.cs @@ -21,14 +21,24 @@ namespace OpenRa.Game public static Map Map; public static TileSet TileSet; - public static void LoadRules(string mapFileName) + public static void LoadRules(string mapFileName, bool useAftermath) { - AllRules = new IniFile( - FileSystem.Open("session.ini"), - FileSystem.Open(mapFileName), - FileSystem.Open("rules.ini"), - FileSystem.Open("units.ini"), - FileSystem.Open("campaignUnits.ini")); + if( useAftermath ) + AllRules = new IniFile( + FileSystem.Open( "session.ini" ), + FileSystem.Open( mapFileName ), + FileSystem.Open( "aftrmath.ini" ), + FileSystem.Open( "rules.ini" ), + FileSystem.Open( "aftermathUnits.ini" ), + FileSystem.Open( "units.ini" ), + FileSystem.Open( "campaignUnits.ini" ) ); + else + AllRules = new IniFile( + FileSystem.Open("session.ini"), + FileSystem.Open(mapFileName), + FileSystem.Open("rules.ini"), + FileSystem.Open("units.ini"), + FileSystem.Open("campaignUnits.ini")); General = new GeneralInfo(); FieldLoader.Load(General, AllRules.GetSection("General")); diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index 2976ac3ac0..5aebba8217 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -59,12 +59,14 @@ namespace OpenRa.Game.GameRules { public readonly int Passengers = 0; public readonly int Speed = 0; + public readonly bool NoMovingFire = false; public MobileInfo(string name) : base(name) { } } public class InfantryInfo : MobileInfo { + public readonly bool Crushable = true; // also on VehicleInfo, but with a different default public readonly bool C4 = false; public readonly bool FraidyCat = false; public readonly bool Infiltrate = false; @@ -78,7 +80,6 @@ namespace OpenRa.Game.GameRules { public readonly bool Crushable = false; public readonly bool Tracked = false; - public readonly bool NoMovingFire = false; public VehicleInfo(string name) : base(name) { } } diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index 58f48352cc..942c504787 100755 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -5,6 +5,7 @@ using OpenRa.Game.Graphics; using System.Runtime.InteropServices; using OpenRa.Game.Traits; using System.IO; +using System; namespace OpenRa.Game { @@ -26,29 +27,12 @@ namespace OpenRa.Game public MainWindow(Settings settings) { - FileSystem.Mount( new Folder( "../../../../" ) ); - if( File.Exists( "../../../../main.mix" ) ) - FileSystem.Mount(new Package("main.mix")); - FileSystem.Mount(new Package("redalert.mix")); - FileSystem.Mount(new Package("conquer.mix")); - FileSystem.Mount(new Package("hires.mix")); - FileSystem.Mount(new Package("general.mix")); - FileSystem.Mount(new Package("local.mix")); - FileSystem.Mount(new Package("sounds.mix")); - FileSystem.Mount(new Package("speech.mix")); - FileSystem.Mount(new Package("allies.mix")); - FileSystem.Mount(new Package("russian.mix")); - FormBorderStyle = FormBorderStyle.None; BackColor = Color.Black; StartPosition = FormStartPosition.Manual; Location = Point.Empty; Visible = true; - bool windowed = !settings.GetValue("fullscreen", false); - renderer = new Renderer(this, GetResolution(settings), windowed); - SheetBuilder.Initialize(renderer); - UiOverlay.ShowUnitDebug = settings.GetValue("udebug", false); UiOverlay.ShowBuildDebug = settings.GetValue("bdebug", false); WorldRenderer.ShowUnitPaths = settings.GetValue("pathdebug", false); @@ -57,10 +41,26 @@ namespace OpenRa.Game Game.NetworkHost = settings.GetValue( "host", "" ); Game.NetworkPort = int.Parse( settings.GetValue( "port", "0" ) ); + var useAftermath = bool.Parse( settings.GetValue( "aftermath", "false" ) ); + Renderer.SheetSize = int.Parse( settings.GetValue( "sheetsize", "512" ) ); + while( !File.Exists( "redalert.mix" ) ) + { + var current = Directory.GetCurrentDirectory(); + if( Directory.GetDirectoryRoot( current ) == current ) + throw new InvalidOperationException( "Unable to load MIX files." ); + Directory.SetCurrentDirectory( ".." ); + } + + FileSystem.MountDefault( useAftermath ); + + bool windowed = !settings.GetValue( "fullscreen", false ); + renderer = new Renderer( this, GetResolution( settings ), windowed ); + SheetBuilder.Initialize( renderer ); + Game.Initialize(settings.GetValue("map", "scm12ea.ini"), renderer, new int2(ClientSize), - settings.GetValue("player", 1)); + settings.GetValue("player", 1), useAftermath); SequenceProvider.ForcePrecache(); diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index ed3731d607..f30036b979 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -155,6 +155,7 @@ + diff --git a/OpenRa.Game/Traits/RenderUnitSpinner.cs b/OpenRa.Game/Traits/RenderUnitSpinner.cs new file mode 100755 index 0000000000..aa42a18949 --- /dev/null +++ b/OpenRa.Game/Traits/RenderUnitSpinner.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game.Traits +{ + class RenderUnitSpinner : RenderUnit + { + public Animation spinnerAnim; + + public RenderUnitSpinner( Actor self ) + : base(self) + { + spinnerAnim = new Animation( self.unitInfo.Name ); + spinnerAnim.PlayRepeating( "spinner" ); + } + + public override IEnumerable> Render(Actor self) + { + var unit = self.traits.Get(); + + yield return Util.Centered(self, anim.Image, self.CenterLocation); + yield return Util.Centered( self, spinnerAnim.Image, self.CenterLocation + + Util.GetTurretPosition(self, unit, self.unitInfo.PrimaryOffset, 0)); + } + + public override void Tick(Actor self) + { + base.Tick(self); + spinnerAnim.Tick(); + } + } +} diff --git a/OpenRa.Game/Traits/RenderUnitTurreted.cs b/OpenRa.Game/Traits/RenderUnitTurreted.cs index 42354c1a18..548ef646b3 100644 --- a/OpenRa.Game/Traits/RenderUnitTurreted.cs +++ b/OpenRa.Game/Traits/RenderUnitTurreted.cs @@ -15,23 +15,20 @@ namespace OpenRa.Game.Traits public RenderUnitTurreted(Actor self) : base(self) { + self.traits.Get(); turretAnim = new Animation(self.unitInfo.Name); - if (self.traits.Contains()) - { - if (self.unitInfo.MuzzleFlash) - { - var attack = self.traits.WithInterface().First(); - muzzleFlash = new Animation(self.unitInfo.Name); - muzzleFlash.PlayFetchIndex("muzzle", - () => (Util.QuantizeFacing(self.traits.Get().turretFacing,8)) * 6 + (int)(attack.primaryRecoil * 5.9f)); - /* hack: recoil can be 1.0f, but don't overflow into next anim */ - } - turretAnim.PlayFetchIndex("turret", - () => self.traits.Get().turretFacing / 8); + if (self.unitInfo.MuzzleFlash) + { + var attack = self.traits.WithInterface().First(); + muzzleFlash = new Animation(self.unitInfo.Name); + muzzleFlash.PlayFetchIndex("muzzle", + () => (Util.QuantizeFacing(self.traits.Get().turretFacing,8)) * 6 + (int)(attack.primaryRecoil * 5.9f)); + /* hack: recoil can be 1.0f, but don't overflow into next anim */ } - else - turretAnim.PlayRepeating("turret"); /* not really a turret; it's a spinner */ + + turretAnim.PlayFetchIndex("turret", + () => self.traits.Get().turretFacing / 8); } public override IEnumerable> Render(Actor self) diff --git a/SequenceEditor/Program.cs b/SequenceEditor/Program.cs index a81437786a..d8f0f13001 100644 --- a/SequenceEditor/Program.cs +++ b/SequenceEditor/Program.cs @@ -6,6 +6,7 @@ using OpenRa.FileFormats; using System.Xml; using System.Drawing; using System.Drawing.Imaging; +using System.IO; namespace SequenceEditor { @@ -69,11 +70,15 @@ namespace SequenceEditor Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - FileSystem.Mount(new Folder("./")); - var packages = new[] { "redalert", "conquer", "hires", "general", "local", "temperat" }; - - foreach( var p in packages ) - FileSystem.Mount( new Package( p + ".mix" )); + try + { + FileSystem.MountDefault( true ); + } + catch( FileNotFoundException fnf ) + { + if( fnf.FileName != "expand2.mix" ) + throw new InvalidOperationException( "Unable to load MIX files" ); + } Doc = new XmlDocument(); Doc.Load("sequences.xml"); diff --git a/aftermathUnits.ini b/aftermathUnits.ini new file mode 100755 index 0000000000..9eeb2ff4b1 --- /dev/null +++ b/aftermathUnits.ini @@ -0,0 +1,78 @@ +[VehicleTypes] +STNK +TTNK +CTNK +DTRK +QTNK + +[STNK] +Description=Stealth Tank +Traits=Unit, Mobile, RenderUnit +[TTNK] +Description=Tesla Tank +Traits=Unit, Mobile, RenderUnitSpinner +[CTNK] +Description=Chrono Tank +Traits=Unit, Mobile, RenderUnit +[DTRK] +Description=Demo Truck +Traits=Unit, Mobile, RenderUnit +[QTNK] +Description=M.A.D. Tank +Traits=Unit, Mobile, RenderUnit + + + + + +[ShipTypes] +MSUB + +[MSUB] +Description=Missile Submarine +WaterBound=yes +BuiltAt=spen +Traits=Mobile, RenderUnit + + + + + +[InfantryTypes] +SHOK +MECH + +[SHOK] +Description=Tesla Trooper +Traits=InfantrySquad,Mobile +SquadSize=1 + +[MECH] +Description=Mechanic +Traits=InfantrySquad,Mobile +SquadSize=1 + + + + + +[WeaponTypes] +PortaTesla +TTankZap +GoodWrench + +[PortaTesla] + +[TTankZap] + +[GoodWrench] + + + + + +[WarheadTypes] +Mechanical + +[Mechanical] + diff --git a/aftrmath.ini b/aftrmath.ini new file mode 100755 index 0000000000..c07491fa0b --- /dev/null +++ b/aftrmath.ini @@ -0,0 +1,511 @@ +[Aftermath] +MTankDistance=20 +QuakeUnitDamage=45% +QuakeBuildingDamage=40% +QuakeInfantryDamage=0 +QuakeDelay=120 +CarrierLaunchDelay=30 +ChronoTankDuration=2 + +; Removed engineer comments from this file, since we only want +; to use it in multi (Adam) + +[STNK] +Prerequisite=weap,atek +Primary=APTusk +Strength=200 +Armor=heavy +TechLevel=-1 +Sight=5 +Speed=10 +Owner=allies,soviet +Cost=800 +Points=25 +ROT=5 +Tracked=yes +Passengers=1 +Cloakable=yes +SelfHealing=no + +; Carrier +[CARR] +Prerequisite=hpad,atek +Strength=350 +Primary=AirAssault +Armor=heavy +TechLevel=-1 +Sight=6 +Speed=6 +Owner=allies,soviet +Cost=1200 +Points=25 +ROT=7 +Passengers=5 + +; Chrono Tank +[CTNK] +Prerequisite=atek +Primary=APTusk +Strength=350 +Armor=light +TechLevel=12 +Sight=5 +Speed=5 +Owner=allies +Cost=2400 +Points=25 +ROT=5 +Tracked=yes + +; Tesla Tank +[TTNK] +Prerequisite=tsla +Primary=TTankZap +Strength=110 +Armor=light +TechLevel=8 +Sight=7 +Speed=8 +Owner=soviet +Cost=1500 +Points=30 +ROT=5 +Tracked=yes +Crewed=yes +NoMovingFire=yes + +; Demolition Truck +[DTRK] +Prerequisite=mslo +Primary=Democharge +Strength=110 +Armor=light +TechLevel=13 +Sight=3 +Speed=8 +Owner=allies,soviet +Cost=2400 +Points=5 +ROT=5 +Explodes=yes + +; M.A.D. Tank +[QTNK] +Prerequisite=stek +Primary=none +Strength=300 +Armor=heavy +TechLevel=10 +Sight=6 +Speed=3 +Owner=soviet +Cost=2300 +Points=60 +ROT=5 +Tracked=yes +Crewed=no + +; Missile Submarine +[MSUB] +Prerequisite=stek +Primary=SubSCUD +Strength=150 +Armor=light +TechLevel=9 +Sight=6 +Speed=5 +Owner=soviet +Cost=1650 +Points=45 +ROT=7 +Cloakable=yes + +; Shock Trooper +[SHOK] +Prerequisite=tsla +Primary=PortaTesla +Strength=80 +Armor=none +TechLevel=7 +Sight=4 +Speed=3 +Owner=soviet +Cost=900 +Points=15 +Explodes=no +NoMovingFire=yes +Crushable=no + +; field mechanic +[MECH] +Prerequisite=fix +Primary=GoodWrench +Strength=60 +Armor=none +TechLevel=7 +Sight=3 +Speed=4 +Owner=allies +Cost=950 +Points=15 + +; Aircraft carrier weapon +[AirAssault] +Damage=0 +ROF=60 +Range=127 +Projectile=Invisible +Speed=100 +Warhead=Super + +; Portable tesla coil weapon +[PortaTesla] +Damage=45 +ROF=70 +Range=3.5 +Projectile=Invisible +Speed=100 +Warhead=Super +Report=SHKTROP1 +Charges=yes + +; Tesla tank weapon +[TTankZap] +Damage=100 +ROF=120 +Range=7 +Projectile=Invisible +Speed=100 +Warhead=Super +Report=TESLA1 +Charges=yes + +; Mr. Goodwrench mechanic's healing weapon +[GoodWrench] +Damage=-100 +ROF=80 +Range=1.83 +Projectile=Invisible +Speed=100 +Warhead=Mechanical +Report=FIXIT1 + +; Mechanical warhead - doesn't affect infantry (only for mechanic, not weapons) +[Mechanical] +Spread=0 +Verses=100%,100%,100%,100%,100% +InfDeath=0 + +; Submarine's scud missile +[SubSCUD] +Damage=400 +ROF=120 +Range=14 +Projectile=HeatSeeker +Speed=20 +Warhead=HE +Report=MISSILE6 +Burst=2 + +; Chrono tank's missile +[APTusk] +Damage=75 +ROF=80 +Range=5 +Projectile=HeatSeeker +Speed=30 +Warhead=AP +Report=MISSILE6 +Burst=2 + +; Demolition truck charge +[Democharge] +Anim=none +Damage=500 +Speed=100 +Range=1.75 +Projectile=Invisible +Warhead=Nuke +ROF=80 + +; This needs to be re-set from its modified mplayer.ini values +[TurretGun] +Burst=1 +Damage=40 +ROF=50 +Range=6 +Projectile=Cannon +Speed=40 +Warhead=AP +Report=TURRET1 +Anim=GUNFIRE + +[LST] +Strength=350 +Armor=heavy +TechLevel=3 +Sight=6 +Speed=14 +Owner=allies,soviet +Cost=700 +Points=25 +ROT=10 +Passengers=5 +SelfHealing=no + +[FACF] +Image=FACT +Owner=allies +TechLevel=1 +Strength=30 +Cost=50 +Sight=4 +Power=-2 +Points=15 +Bib=yes +Capturable=true +BaseNormal=no +Armor=none + +[WEAF] +Prerequisite=proc +Image=WEAP +Owner=allies +TechLevel=3 +Cost=50 +Strength=30 +Sight=4 +Points=15 +Power=-2 +Bib=yes +Capturable=true +BaseNormal=no +Armor=none + +[DOMF] +Prerequisite=proc +Image=DOME +Strength=30 +Sight=4 +TechLevel=3 +Cost=50 +Owner=allies +Power=-2 +Points=15 +Bib=yes +Capturable=true +BaseNormal=no +Armor=none + +[4TNK] +Prerequisite=weap,stek +Primary=120mm +Secondary=MammothTusk +Strength=600 +Armor=heavy +TechLevel=10 +Sight=6 +Speed=4 +Owner=soviet +Cost=1700 +Points=60 +ROT=5 +Tracked=yes +SelfHealing=yes +Crewed=yes +Explodes=no + +[C2] +Primary=none +Image=C1 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes + +[C3] +Infiltrate=no +Primary=none +Image=C2 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes + +[C4] +Primary=none +Infiltrate=no +Image=C2 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes + +[C5] +Infiltrate=no +Image=C2 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes + +[C6] +Image=C1 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes +Infiltrate=no +Primary=none + +[C9] +Image=C1 +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Fraidycat=yes +Primary=none + +[Catapult] +Animates=no +High=yes +Arcing=yes +Inaccurate=yes +Image=BOMB +Frames=8 +ASW=yes +AG=no +Translucent=yes + +[MISS] +Strength=400 +Points=5 +Armor=wood +Bib=yes +Capturable=true +Owner=allies,soviet + +[BIO] +Strength=600 +Armor=wood +TechLevel=-1 +Sight=4 +Owner= +Cost= +Points=30 +Power=-40 +Bib=yes +Crewed=yes +Capturable=false + +[GNRL] +Primary=Pistol +Strength=80 +Armor=none +TechLevel=-1 +Sight=3 +Speed=5 +Owner=allies,soviet +Cost=0 +Points=15 +Infiltrate=yes +Secondary=none +C4=no +DoubleOwned=no +Burst=1 + +[DOG] +Prerequisite=kenn +Primary=DogJaw +Strength=12 +;Strength=5 +Armor=none +TechLevel=3 +Sight=5 +Speed=4 +Owner=soviet +Cost=200 +Points=5 +IsCanine=yes +GuardRange=7 +SelfHealing=no + +[CHAN] +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Infiltrate=no + +[E3] +Primary=RedEye +Secondary=Dragon +Strength=45 +Armor=none +TechLevel=2 +Sight=4 +Speed=3 +Owner=allies +Cost=300 +Points=10 +DoubleOwned=yes +Infiltrate=no + +[155mm] +Damage=150 +ROF=65 +Range=6 +Projectile=Ballistic +Speed=12 +Warhead=HE +Report=TANK5 +Anim=GUNFIRE +Burst=1 + +[DELPHI] +Infiltrate=no +Primary=Pistol +Strength=25 +Armor=none +TechLevel=-1 +Sight=2 +Speed=5 +Owner=allies,soviet +Cost=10 +Points=1 +Ammo=10 + diff --git a/sequences.xml b/sequences.xml index 84e522d324..ee90cf9a03 100644 --- a/sequences.xml +++ b/sequences.xml @@ -1,4 +1,4 @@ - + - + - + @@ -520,4 +520,20 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/units.ini b/units.ini index 2ba555448d..6e994f3513 100755 --- a/units.ini +++ b/units.ini @@ -33,11 +33,11 @@ Description=Mammoth Tank Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted [MRJ] Description=Radar Jammer -Traits=Unit, Mobile, RenderUnitTurreted +Traits=Unit, Mobile, RenderUnitSpinner PrimaryOffset=0,4,0,-6 [MGG] Description=Mobile Gap Generator -Traits=Unit, Mobile, RenderUnitTurreted +Traits=Unit, Mobile, RenderUnitSpinner PrimaryOffset=0,6,0,-3 [ARTY] Description=Artillery