From 395df45a5af63f115e2a25b52567778db3c75275 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 10 Mar 2013 18:13:41 +1300 Subject: [PATCH 1/3] Revert unbuildable MCV and crate changes. --- mods/cnc/rules/system.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 0e9b9cf1d7..e1b237c932 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -260,24 +260,24 @@ CRATE: TerrainTypes: Clear, Rough, Road, Tiberium, BlueTiberium, Beach GiveCashCrateAction: Amount: 1000 - SelectionShares: 20000 + SelectionShares: 20 UseCashTick: yes RevealMapCrateAction: - SelectionShares: 1000 + SelectionShares: 1 Effect: reveal-map ExplodeCrateAction@fire: Weapon: Napalm.Crate - SelectionShares: 5000 + SelectionShares: 5 CloakCrateAction: - SelectionShares: 5000 + SelectionShares: 5 InitialDelay: 15 CloakDelay: 80 CloakSound: trans1.aud UncloakSound: trans1.aud Effect: cloak GiveMcvCrateAction: - SelectionShares: 1 - NoBaseSelectionShares: 9000001 + SelectionShares: 2 + NoBaseSelectionShares: 9001 Unit: mcv RenderSimple: BelowUnits: From 4dc5c4a87146b6286ba4bfd564098ad83d174eb4 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 10 Mar 2013 18:15:25 +1300 Subject: [PATCH 2/3] Fix BuildingInfo formatting. --- OpenRA.Mods.RA/Buildings/Building.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/OpenRA.Mods.RA/Buildings/Building.cs b/OpenRA.Mods.RA/Buildings/Building.cs index 7be8e16994..572f836d0d 100755 --- a/OpenRA.Mods.RA/Buildings/Building.cs +++ b/OpenRA.Mods.RA/Buildings/Building.cs @@ -43,29 +43,29 @@ namespace OpenRA.Mods.RA.Buildings return true; var buildingMaxBounds = (CVec)Dimensions; - if( Rules.Info[ buildingName ].Traits.Contains() ) + if (Rules.Info[buildingName].Traits.Contains()) buildingMaxBounds += new CVec(0, 1); - var scanStart = world.ClampToWorld( topLeft - new CVec( Adjacent, Adjacent ) ); + var scanStart = world.ClampToWorld(topLeft - new CVec(Adjacent, Adjacent)); var scanEnd = world.ClampToWorld(topLeft + buildingMaxBounds + new CVec(Adjacent, Adjacent)); var nearnessCandidates = new List(); var bi = world.WorldActor.Trait(); - for( int y = scanStart.Y ; y < scanEnd.Y ; y++ ) - for( int x = scanStart.X ; x < scanEnd.X ; x++ ) + for (var y = scanStart.Y; y < scanEnd.Y; y++) + for (var x = scanStart.X; x < scanEnd.X; x++) { - var at = bi.GetBuildingAt( new CPos( x, y ) ); - if( at != null && at.Owner.Stances[ p ] == Stance.Ally && at.HasTrait() ) - nearnessCandidates.Add( new CPos( x, y ) ); + var at = bi.GetBuildingAt(new CPos(x, y)); + if (at != null && at.Owner.Stances[p] == Stance.Ally && at.HasTrait()) + nearnessCandidates.Add(new CPos(x, y)); } - var buildingTiles = FootprintUtils.Tiles( buildingName, this, topLeft ).ToList(); + var buildingTiles = FootprintUtils.Tiles(buildingName, this, topLeft).ToList(); return nearnessCandidates - .Any( a => buildingTiles - .Any( b => Math.Abs( a.X - b.X ) <= Adjacent - && Math.Abs( a.Y - b.Y ) <= Adjacent ) ); + .Any(a => buildingTiles + .Any(b => Math.Abs(a.X - b.X) <= Adjacent + && Math.Abs(a.Y - b.Y) <= Adjacent)); } } From 9127d0dcf4cab5b5febe6cb2dcf6b3160e8cf46c Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 10 Mar 2013 18:15:42 +1300 Subject: [PATCH 3/3] Support a maximum building range (Fixes #2156). This also implements support for a per-provider cooldown between placing structures, allowing mods with multiple structure queues to rate-limit placement around a single provider. An initial delay parameter is included to support units that deploy into a base provider and require an initial setup time (e.g. the Surveyor unit from C&C TW). The range and time restrictions are not applied to walls as a balance choice. --- OpenRA.Mods.RA/Buildings/BaseProvider.cs | 88 ++++++++++++++++++++++++ OpenRA.Mods.RA/Buildings/Building.cs | 25 ++++++- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + OpenRA.Mods.RA/Player/PlaceBuilding.cs | 16 +++++ OpenRA.Mods.RA/Render/RenderBuilding.cs | 11 ++- mods/cnc/rules/defaults.yaml | 1 + mods/cnc/rules/structures.yaml | 2 + 7 files changed, 142 insertions(+), 2 deletions(-) create mode 100755 OpenRA.Mods.RA/Buildings/BaseProvider.cs diff --git a/OpenRA.Mods.RA/Buildings/BaseProvider.cs b/OpenRA.Mods.RA/Buildings/BaseProvider.cs new file mode 100755 index 0000000000..8278d74744 --- /dev/null +++ b/OpenRA.Mods.RA/Buildings/BaseProvider.cs @@ -0,0 +1,88 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Drawing; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Buildings +{ + public class BaseProviderInfo : ITraitInfo + { + public readonly float Range = 10; + public readonly int Cooldown = 0; + public readonly int InitialDelay = 0; + + public object Create(ActorInitializer init) { return new BaseProvider(init.self, this); } + } + + public class BaseProvider : ITick, IPreRenderSelection, ISelectionBar + { + public readonly BaseProviderInfo Info; + DeveloperMode devMode; + Actor self; + int total; + int progress; + + public BaseProvider(Actor self, BaseProviderInfo info) + { + Info = info; + this.self = self; + devMode = self.Owner.PlayerActor.Trait(); + progress = total = info.InitialDelay; + } + + public void Tick(Actor self) + { + if (progress > 0) + progress--; + } + + public void BeginCooldown() + { + progress = total = Info.Cooldown; + } + + public bool Ready() + { + return devMode.FastBuild || progress == 0; + } + + // Range circle + public void RenderBeforeWorld(WorldRenderer wr, Actor self) + { + // Visible to player and allies + if (self.World.RenderedPlayer != null && self.Owner.Stances[self.World.RenderedPlayer] != Stance.Ally) + return; + + wr.DrawRangeCircleWithContrast( + Color.FromArgb(128, Ready() ? Color.White : Color.Red), + self.CenterLocation.ToFloat2(), Info.Range, + Color.FromArgb(96, Color.Black), 1); + } + + // Selection bar + public float GetValue() + { + // Visible to player and allies + if (self.World.RenderedPlayer != null && self.Owner.Stances[self.World.RenderedPlayer] != Stance.Ally) + return 0f; + + // Ready or delay disabled + if (progress == 0 || total == 0 || devMode.FastBuild) + return 0f; + + return (float)progress / total; + } + + public Color GetColor() { return Color.Purple; } + } +} diff --git a/OpenRA.Mods.RA/Buildings/Building.cs b/OpenRA.Mods.RA/Buildings/Building.cs index 572f836d0d..f4904e8b82 100755 --- a/OpenRA.Mods.RA/Buildings/Building.cs +++ b/OpenRA.Mods.RA/Buildings/Building.cs @@ -31,17 +31,40 @@ namespace OpenRA.Mods.RA.Buildings [Desc("x means space it blocks, _ is a part that is passable by actors.")] public readonly string Footprint = "x"; public readonly int2 Dimensions = new int2(1, 1); + public readonly bool RequiresBaseProvider = false; public readonly string[] BuildSounds = {"placbldg.aud", "build5.aud"}; public readonly string[] SellSounds = {"cashturn.aud"}; public object Create(ActorInitializer init) { return new Building(init, this); } + public PPos CenterLocation(CPos topLeft) + { + return (PPos)((2 * topLeft.ToInt2() + Dimensions) * Game.CellSize / 2); + } + + bool HasBaseProvider(World world, Player p, CPos topLeft) + { + var center = CenterLocation(topLeft); + foreach (var bp in world.ActorsWithTrait()) + { + if (bp.Actor.Owner.Stances[p] != Stance.Ally || !bp.Trait.Ready()) + continue; + + if (Combat.IsInRange(center, bp.Trait.Info.Range, bp.Actor.CenterLocation)) + return true; + } + return false; + } + public bool IsCloseEnoughToBase(World world, Player p, string buildingName, CPos topLeft) { if (p.PlayerActor.Trait().BuildAnywhere) return true; + if (RequiresBaseProvider && !HasBaseProvider(world, p, topLeft)) + return false; + var buildingMaxBounds = (CVec)Dimensions; if (Rules.Info[buildingName].Traits.Contains()) buildingMaxBounds += new CVec(0, 1); @@ -103,7 +126,7 @@ namespace OpenRA.Mods.RA.Buildings occupiedCells = FootprintUtils.UnpathableTiles( self.Info.Name, Info, TopLeft ) .Select(c => Pair.New(c, SubCell.FullCell)).ToArray(); - pxPosition = (PPos) (( 2 * topLeft.ToInt2() + Info.Dimensions ) * Game.CellSize / 2); + pxPosition = Info.CenterLocation(topLeft); } public int GetPowerUsage() diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index db3abb3163..4ee2c5f1d8 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -422,6 +422,7 @@ + diff --git a/OpenRA.Mods.RA/Player/PlaceBuilding.cs b/OpenRA.Mods.RA/Player/PlaceBuilding.cs index 4405d6a8d8..9adbef1df0 100755 --- a/OpenRA.Mods.RA/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.RA/Player/PlaceBuilding.cs @@ -80,6 +80,22 @@ namespace OpenRA.Mods.RA queue.FinishProduction(); + if (buildingInfo.RequiresBaseProvider) + { + var center = buildingInfo.CenterLocation(order.TargetLocation); + foreach (var bp in w.ActorsWithTrait()) + { + if (bp.Actor.Owner.Stances[self.Owner] != Stance.Ally || !bp.Trait.Ready()) + continue; + + if (Combat.IsInRange(center, bp.Trait.Info.Range, bp.Actor.CenterLocation)) + { + bp.Trait.BeginCooldown(); + break; + } + } + } + if (GetNumBuildables(self.Owner) > prevItems) w.Add(new DelayedAction(10, () => Sound.PlayNotification(order.Player, "Speech", "NewOptions", order.Player.Country.Race))); diff --git a/OpenRA.Mods.RA/Render/RenderBuilding.cs b/OpenRA.Mods.RA/Render/RenderBuilding.cs index ce7711bd7c..f1e969fe16 100755 --- a/OpenRA.Mods.RA/Render/RenderBuilding.cs +++ b/OpenRA.Mods.RA/Render/RenderBuilding.cs @@ -19,7 +19,7 @@ using OpenRA.Mods.RA.Activities; namespace OpenRA.Mods.RA.Render { - public class RenderBuildingInfo : RenderSimpleInfo + public class RenderBuildingInfo : RenderSimpleInfo, Requires, IPlaceBuildingDecoration { public readonly bool HasMakeAnimation = true; public readonly float2 Origin = float2.Zero; @@ -30,6 +30,15 @@ namespace OpenRA.Mods.RA.Render return base.RenderPreview(building, pr) .Select(a => a.WithPos(a.Pos + building.Traits.Get().Origin)); } + + public void Render(WorldRenderer wr, World w, ActorInfo ai, PPos centerLocation) + { + if (!ai.Traits.Get().RequiresBaseProvider) + return; + + foreach (var a in w.ActorsWithTrait()) + a.Trait.RenderBeforeWorld(wr, a.Actor); + } } public class RenderBuilding : RenderSimple, INotifyDamageStateChanged, IRenderModifier diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 9a3ef74237..b78b77373e 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -232,6 +232,7 @@ RepairPercent: 40 RepairStep: 14 Building: + RequiresBaseProvider: true Dimensions: 1,1 Footprint: x BuildSounds: constru2.aud, hvydoor1.aud diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index c85b90e388..38302258fa 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -45,6 +45,8 @@ FACT: ReadyAudio: ConstructionComplete BaseBuilding: ProductionBar: + BaseProvider: + Cooldown: 200 NUKE: Inherits: ^Building