From 853e60f76e21b5c3b566c6b6e6349d318873a023 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 27 Aug 2010 23:51:12 +1200 Subject: [PATCH] Tech tree changes to support non-buildable prerequisites, capturable superweapons, "hidden tech" for tech buildings, scripted tech unlocks, "build everything" developer toggle. --- OpenRA.Game/Traits/Buildable.cs | 1 + OpenRA.Game/Traits/Player/DeveloperMode.cs | 15 ++- OpenRA.Game/Traits/Player/ProductionQueue.cs | 93 ++++++++++++++----- OpenRA.Game/Traits/Player/TechTreeCache.cs | 30 +++--- OpenRA.Game/Traits/SupportPower.cs | 9 +- .../Delegates/DeveloperModeDelegate.cs | 30 +++--- OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs | 4 +- mods/cnc/chrome/ingame.yaml | 33 ++++--- mods/cnc/structures.yaml | 2 + 9 files changed, 141 insertions(+), 76 deletions(-) diff --git a/OpenRA.Game/Traits/Buildable.cs b/OpenRA.Game/Traits/Buildable.cs index 00fa356c05..35fd571c4c 100755 --- a/OpenRA.Game/Traits/Buildable.cs +++ b/OpenRA.Game/Traits/Buildable.cs @@ -35,6 +35,7 @@ namespace OpenRA.Traits public readonly string[] Owner = { }; public readonly string Queue; + public readonly bool Hidden = false; // todo: UI fluff; doesn't belong here public readonly int BuildPaletteOrder = 9999; diff --git a/OpenRA.Game/Traits/Player/DeveloperMode.cs b/OpenRA.Game/Traits/Player/DeveloperMode.cs index 38b6a89e04..3ebc4647b1 100644 --- a/OpenRA.Game/Traits/Player/DeveloperMode.cs +++ b/OpenRA.Game/Traits/Player/DeveloperMode.cs @@ -25,11 +25,11 @@ namespace OpenRA.Traits public class DeveloperMode : IResolveOrder { DeveloperModeInfo Info; - [Sync] - public bool FastCharge; - public bool FastBuild; - public bool DisableShroud; - public bool PathDebug; + [Sync] public bool FastCharge; + [Sync] public bool AllTech; + [Sync] public bool FastBuild; + [Sync] public bool DisableShroud; + [Sync] public bool PathDebug; public DeveloperMode(DeveloperModeInfo info) { @@ -46,6 +46,11 @@ namespace OpenRA.Traits switch(order.OrderString) { + case "DevEnableTech": + { + AllTech ^= true; + break; + } case "DevFastCharge": { FastCharge ^= true; diff --git a/OpenRA.Game/Traits/Player/ProductionQueue.cs b/OpenRA.Game/Traits/Player/ProductionQueue.cs index 76a52db036..c1e0a6b40a 100644 --- a/OpenRA.Game/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Game/Traits/Player/ProductionQueue.cs @@ -23,39 +23,79 @@ namespace OpenRA.Traits public object Create(ActorInitializer init) { return new ProductionQueue(init.self, this); } } - public class ProductionQueue : IResolveOrder, ITick + public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement { public readonly Actor self; public ProductionQueueInfo Info; - // TODO: sync this - List Producing = new List(); + + // TODO: sync these + // A list of things we are currently building + List Queue = new List(); + + // A list of things we could possibly build, even if our race doesn't normally get it + Dictionary Produceable = new Dictionary(); public ProductionQueue( Actor self, ProductionQueueInfo info ) { this.self = self; this.Info = info; + + var ttc = self.Owner.PlayerActor.Trait(); + foreach (var a in Rules.TechTree.AllBuildables(Info.Type)) + { + var bi = a.Traits.Get(); + // Can our race build this by satisfying normal prereqs? + var buildable = bi.Owner.Contains(self.Owner.Country.Race); + Produceable.Add( a, new ProductionState(){ Visible = buildable && !bi.Hidden } ); + if (buildable) + ttc.Add( a.Name, a.Traits.Get().Prerequisites.ToList(), this ); + } + } + + public void OverrideProduction(ActorInfo type, bool buildable) + { + Produceable[type].Buildable = buildable; + Produceable[type].Sticky = true; + } + + public void PrerequisitesAvailable(string key) + { + var ps = Produceable[ Rules.Info[key] ]; + if (!ps.Sticky) + ps.Buildable = true; + } + + public void PrerequisitesUnavailable(string key) + { + var ps = Produceable[ Rules.Info[key] ]; + if (!ps.Sticky) + ps.Buildable = false; } public ProductionItem CurrentItem() { - return Producing.ElementAtOrDefault(0); + return Queue.ElementAtOrDefault(0); } public IEnumerable AllQueued() { - return Producing; + return Queue; } public IEnumerable AllItems() { - return Rules.TechTree.AllBuildables(Info.Type) - .Where(a => a.Traits.Get().Owner.Contains(self.Owner.Country.Race)) - .OrderBy(a => a.Traits.Get().BuildPaletteOrder); + if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Owner.PlayerActor.Trait().AllTech) + return Produceable.Select(a => a.Key); + + return Produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); } public IEnumerable BuildableItems() { - return Rules.TechTree.BuildableItems(self.Owner, Info.Type).Select(b => Rules.Info[b.ToLowerInvariant()]); + if (Game.LobbyInfo.GlobalSettings.AllowCheats && self.Owner.PlayerActor.Trait().AllTech) + return Produceable.Select(a => a.Key); + + return Produceable.Where(a => a.Value.Buildable).Select(a => a.Key); } public bool CanBuild(ActorInfo actor) @@ -66,13 +106,13 @@ namespace OpenRA.Traits public void Tick( Actor self ) { - while( Producing.Count > 0 && !BuildableItems().Any(b => b.Name == Producing[ 0 ].Item) ) + while( Queue.Count > 0 && !BuildableItems().Any(b => b.Name == Queue[ 0 ].Item) ) { - self.Owner.PlayerActor.Trait().GiveCash(Producing[0].TotalCost - Producing[0].RemainingCost); // refund what's been paid so far. + self.Owner.PlayerActor.Trait().GiveCash(Queue[0].TotalCost - Queue[0].RemainingCost); // refund what's been paid so far. FinishProduction(); } - if( Producing.Count > 0 ) - Producing[ 0 ].Tick( self.Owner ); + if( Queue.Count > 0 ) + Queue[ 0 ].Tick( self.Owner ); } public void ResolveOrder( Actor self, Order order ) @@ -115,8 +155,8 @@ namespace OpenRA.Traits } case "PauseProduction": { - if( Producing.Count > 0 && Producing[0].Item == order.TargetString ) - Producing[0].Paused = ( order.TargetLocation.X != 0 ); + if( Queue.Count > 0 && Queue[0].Item == order.TargetString ) + Queue[0].Paused = ( order.TargetLocation.X != 0 ); break; } case "CancelProduction": @@ -144,17 +184,15 @@ namespace OpenRA.Traits void CancelProduction( string itemName ) { - if (Producing.Count == 0) + if (Queue.Count == 0) return; // Nothing to do here - var lastIndex = Producing.FindLastIndex( a => a.Item == itemName ); + var lastIndex = Queue.FindLastIndex( a => a.Item == itemName ); if (lastIndex > 0) - { - Producing.RemoveAt(lastIndex); - } + Queue.RemoveAt(lastIndex); else if( lastIndex == 0 ) { - var item = Producing[0]; + var item = Queue[0]; self.Owner.PlayerActor.Trait().GiveCash(item.TotalCost - item.RemainingCost); // refund what's been paid so far. FinishProduction(); } @@ -162,13 +200,13 @@ namespace OpenRA.Traits public void FinishProduction() { - if (Producing.Count == 0) return; - Producing.RemoveAt(0); + if (Queue.Count == 0) return; + Queue.RemoveAt(0); } void BeginProduction( ProductionItem item ) { - Producing.Add(item); + Queue.Add(item); } static bool IsDisabledBuilding(Actor a) @@ -213,6 +251,13 @@ namespace OpenRA.Traits } } + public class ProductionState + { + public bool Visible = false; + public bool Buildable = false; + public bool Sticky = false; + } + public class ProductionItem { public readonly string Item; diff --git a/OpenRA.Game/Traits/Player/TechTreeCache.cs b/OpenRA.Game/Traits/Player/TechTreeCache.cs index e90f076c3e..b5f8a089ff 100755 --- a/OpenRA.Game/Traits/Player/TechTreeCache.cs +++ b/OpenRA.Game/Traits/Player/TechTreeCache.cs @@ -21,12 +21,15 @@ namespace OpenRA.Traits { class Watcher { - public readonly List prerequisites; + public readonly string key; + // strings may be either actor type, or "alternate name" key + public readonly List prerequisites; public readonly ITechTreeElement watcher; bool hasPrerequisites; - public Watcher(List prerequisites, ITechTreeElement watcher) + public Watcher(string key, List prerequisites, ITechTreeElement watcher) { + this.key = key; this.prerequisites = prerequisites; this.watcher = watcher; this.hasPrerequisites = false; @@ -34,18 +37,13 @@ namespace OpenRA.Traits public void Tick( Player owner, Cache> buildings ) { - if (owner.Country == null) - return; - - var effectivePrereq = prerequisites.Where( a => a.Traits.Contains() && a.Traits.Get().Owner.Contains( owner.Country.Race ) ); - var nowHasPrerequisites = effectivePrereq.Any() && - effectivePrereq.All( a => buildings[ a.Name ].Any( b => !b.Trait().Disabled ) ); + var nowHasPrerequisites = prerequisites.All( a => buildings[ a ].Any( b => !b.Trait().Disabled ) ); if( nowHasPrerequisites && !hasPrerequisites ) - watcher.Available(); + watcher.PrerequisitesAvailable(key); if( !nowHasPrerequisites && hasPrerequisites ) - watcher.Unavailable(); + watcher.PrerequisitesUnavailable(key); hasPrerequisites = nowHasPrerequisites; } @@ -61,20 +59,20 @@ namespace OpenRA.Traits w.Tick( self.Owner, buildings ); } - public void Add( List prerequisites, ITechTreeElement tte ) + public void Add( string key, List prerequisites, ITechTreeElement tte ) { - watchers.Add( new Watcher( prerequisites, tte ) ); + watchers.Add( new Watcher( key, prerequisites, tte ) ); } - public void Remove( ITechTreeElement tte ) + public void Remove( string key ) { - watchers.RemoveAll( x => x.watcher == tte ); + watchers.RemoveAll( x => x.key == key ); } } interface ITechTreeElement { - void Available(); - void Unavailable(); + void PrerequisitesAvailable(string key); + void PrerequisitesUnavailable(string key); } } diff --git a/OpenRA.Game/Traits/SupportPower.cs b/OpenRA.Game/Traits/SupportPower.cs index 91337babb5..5521e345fe 100644 --- a/OpenRA.Game/Traits/SupportPower.cs +++ b/OpenRA.Game/Traits/SupportPower.cs @@ -59,7 +59,7 @@ namespace OpenRA.Traits Self = self; Owner = self.Owner; - self.Trait().Add( Info.Prerequisites.Select( a => Rules.Info[ a.ToLowerInvariant() ] ).ToList(), this ); + self.Trait().Add( Info.OrderName, Info.Prerequisites.Select( a => a.ToLowerInvariant() ).ToList(), this ); } public void Tick(Actor self) @@ -95,8 +95,7 @@ namespace OpenRA.Traits { 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)); + .Select(a => a.ToLowerInvariant()); if (Info.Prerequisites.Count() == 0) return Owner.PlayerActor.Trait().GetPowerState() == PowerState.Normal; @@ -146,12 +145,12 @@ namespace OpenRA.Traits bool hasPrerequisites; - public void Available() + public void PrerequisitesAvailable(string key) { hasPrerequisites = true; } - public void Unavailable() + public void PrerequisitesUnavailable(string key) { hasPrerequisites = false; } diff --git a/OpenRA.Game/Widgets/Delegates/DeveloperModeDelegate.cs b/OpenRA.Game/Widgets/Delegates/DeveloperModeDelegate.cs index d459639846..1a2c3c3613 100644 --- a/OpenRA.Game/Widgets/Delegates/DeveloperModeDelegate.cs +++ b/OpenRA.Game/Widgets/Delegates/DeveloperModeDelegate.cs @@ -40,51 +40,59 @@ namespace OpenRA.Widgets.Delegates return true; }; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_SHROUD").Checked = + devmodeBG.GetWidget("CHECKBOX_SHROUD").Checked = () => Game.world.LocalPlayer.PlayerActor.Trait().DisableShroud; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_SHROUD").OnMouseDown = mi => + devmodeBG.GetWidget("CHECKBOX_SHROUD").OnMouseDown = mi => { Game.IssueOrder(new Order("DevShroud", Game.world.LocalPlayer.PlayerActor)); return true; }; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_UNITDEBUG").Checked = + devmodeBG.GetWidget("CHECKBOX_UNITDEBUG").Checked = () => Game.Settings.Debug.ShowCollisions; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_UNITDEBUG").OnMouseDown = mi => + devmodeBG.GetWidget("CHECKBOX_UNITDEBUG").OnMouseDown = mi => { Game.IssueOrder(new Order("DevUnitDebug", Game.world.LocalPlayer.PlayerActor)); return true; }; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_PATHDEBUG").Checked = + devmodeBG.GetWidget("CHECKBOX_PATHDEBUG").Checked = () => Game.world.LocalPlayer.PlayerActor.Trait().PathDebug; - devmodeBG.GetWidget("SETTINGS_CHECKBOX_PATHDEBUG").OnMouseDown = mi => + devmodeBG.GetWidget("CHECKBOX_PATHDEBUG").OnMouseDown = mi => { Game.IssueOrder(new Order("DevPathDebug", Game.world.LocalPlayer.PlayerActor)); return true; }; - devmodeBG.GetWidget("SETTINGS_GIVE_CASH").OnMouseUp = mi => + devmodeBG.GetWidget("GIVE_CASH").OnMouseUp = mi => { Game.IssueOrder(new Order("DevGiveCash", Game.world.LocalPlayer.PlayerActor)); return true; }; - devmodeBG.GetWidget("SETTINGS_BUILD_SPEED").Checked = + devmodeBG.GetWidget("INSTANT_BUILD").Checked = () => Game.world.LocalPlayer.PlayerActor.Trait().FastBuild; - devmodeBG.GetWidget("SETTINGS_BUILD_SPEED").OnMouseDown = mi => + devmodeBG.GetWidget("INSTANT_BUILD").OnMouseDown = mi => { Game.IssueOrder(new Order("DevFastBuild", Game.world.LocalPlayer.PlayerActor)); return true; }; - devmodeBG.GetWidget("SETTINGS_CHARGE_TIME").Checked = + devmodeBG.GetWidget("INSTANT_CHARGE").Checked = () => Game.world.LocalPlayer.PlayerActor.Trait().FastCharge; - devmodeBG.GetWidget("SETTINGS_CHARGE_TIME").OnMouseDown = mi => + devmodeBG.GetWidget("INSTANT_CHARGE").OnMouseDown = mi => { Game.IssueOrder(new Order("DevFastCharge", Game.world.LocalPlayer.PlayerActor)); return true; }; + + devmodeBG.GetWidget("ENABLE_TECH").Checked = + () => Game.world.LocalPlayer.PlayerActor.Trait().AllTech; + devmodeBG.GetWidget("ENABLE_TECH").OnMouseDown = mi => + { + Game.IssueOrder(new Order("DevEnableTech", Game.world.LocalPlayer.PlayerActor)); + return true; + }; devModeButton.IsVisible = () => { return Game.LobbyInfo.GlobalSettings.AllowCheats; }; } diff --git a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs index aa37c43548..c824370913 100755 --- a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs +++ b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs @@ -186,8 +186,8 @@ namespace OpenRA.Mods.RA.Widgets // Collect info var x = 0; var y = 0; - var buildableItems = queue.BuildableItems(); - var allBuildables = queue.AllItems(); + var buildableItems = queue.BuildableItems().OrderBy(a => a.Traits.Get().BuildPaletteOrder); + var allBuildables = queue.AllItems().OrderBy(a => a.Traits.Get().BuildPaletteOrder); var overlayBits = new List>(); numActualRows = Math.Max((allBuildables.Count() + Columns - 1) / Columns, Rows); diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index ab8457bbbc..49ab0bbd94 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -204,7 +204,7 @@ Container@ROOT: X:(WINDOW_RIGHT - WIDTH)/2 Y:(WINDOW_BOTTOM - HEIGHT)/2 Width:350 - Height:300 + Height:330 Visible:false Children: Label@LABEL_TITLE: @@ -215,48 +215,55 @@ Container@ROOT: Height:25 Text:Developer Mode Align:Center - Checkbox@SETTINGS_CHECKBOX_SHROUD - Id:SETTINGS_CHECKBOX_SHROUD + Checkbox@CHECKBOX_SHROUD + Id:CHECKBOX_SHROUD X:30 Y:50 Height:20 Width:PARENT_RIGHT - 30 Text:Disable Shroud - Checkbox@SETTINGS_CHECKBOX_UNITDEBUG: - Id:SETTINGS_CHECKBOX_UNITDEBUG + Checkbox@CHECKBOX_UNITDEBUG: + Id:CHECKBOX_UNITDEBUG X:30 Y:80 Width:PARENT_RIGHT - 30 Height:20 Text:Show Occupied Cells - Checkbox@SETTINGS_CHECKBOX_PATHDEBUG: - Id:SETTINGS_CHECKBOX_PATHDEBUG + Checkbox@CHECKBOX_PATHDEBUG: + Id:CHECKBOX_PATHDEBUG X:30 Y:110 Width:PARENT_RIGHT - 30 Height:20 Text:Show Unit Paths - Button@SETTINGS_GIVE_CASH - Id:SETTINGS_GIVE_CASH + Button@GIVE_CASH + Id:GIVE_CASH X:30 Y:140 Width:200 Height:20 Text: Give Cash - Checkbox@SETTINGS_BUILD_SPEED - Id:SETTINGS_BUILD_SPEED + Checkbox@INSTANT_BUILD + Id:INSTANT_BUILD X:30 Y:170 Width:PARENT_RIGHT - 30 Height:20 Text:Instant Build Speed - Checkbox@SETTINGS_CHARGE_TIME - Id:SETTINGS_CHARGE_TIME + Checkbox@INSTANT_CHARGE + Id:INSTANT_CHARGE X:30 Y:200 Width:PARENT_RIGHT - 30 Height:20 Text:Instant Charge Time (Special Powers) + Checkbox@ENABLE_TECH + Id:ENABLE_TECH + X:30 + Y:230 + Width:PARENT_RIGHT - 30 + Height:20 + Text:Build Everything Background@FMVPLAYER: Id:FMVPLAYER Width:WINDOW_RIGHT diff --git a/mods/cnc/structures.yaml b/mods/cnc/structures.yaml index ca1f176005..f6c6767344 100644 --- a/mods/cnc/structures.yaml +++ b/mods/cnc/structures.yaml @@ -1,5 +1,6 @@ FACT: Inherits: ^Building + -Buildable: Valued: Cost: 2000 Tooltip: @@ -83,6 +84,7 @@ PROC.proxy: PROC: Inherits: ^Building + -Buildable: Valued: Cost: 1700 Tooltip: