diff --git a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs index 2b7952647d..858abdaf95 100644 --- a/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs +++ b/OpenRA.Mods.Common/Scripting/Global/TriggerGlobal.cs @@ -231,6 +231,16 @@ namespace OpenRA.Mods.Common.Scripting GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnObjectiveFailed, func, Context); } + [Desc("Call a function when this player places a building. " + + "The callback function will be called as func(p: player, placed: actor).")] + public void OnBuildingPlaced(Player player, [ScriptEmmyTypeOverride("fun(p: player, placed: actor)")] LuaFunction func) + { + if (player == null) + throw new NullReferenceException(nameof(player)); + + GetScriptTriggers(player.PlayerActor).RegisterCallback(Trigger.OnBuildingPlaced, func, Context); + } + [Desc("Call a function when this actor is added to the world. " + "The callback function will be called as func(self: actor).")] public void OnAddedToWorld(Actor actor, [ScriptEmmyTypeOverride("fun(self: actor)")] LuaFunction func) diff --git a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs index b5802567e4..deb04fd371 100644 --- a/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs +++ b/OpenRA.Mods.Common/Scripting/ScriptTriggers.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.Common.Scripting { public enum Trigger { - OnIdle, OnDamaged, OnKilled, OnProduction, OnOtherProduction, OnPlayerWon, OnPlayerLost, + OnIdle, OnDamaged, OnKilled, OnProduction, OnOtherProduction, OnBuildingPlaced, OnPlayerWon, OnPlayerLost, OnObjectiveAdded, OnObjectiveCompleted, OnObjectiveFailed, OnCapture, OnInfiltrated, OnAddedToWorld, OnRemovedFromWorld, OnDiscovered, OnPlayerDiscovered, OnPassengerEntered, OnPassengerExited, OnSold, OnTimerExpired @@ -33,7 +33,7 @@ namespace OpenRA.Mods.Common.Scripting public override object Create(ActorInitializer init) { return new ScriptTriggers(init.World, init.Self); } } - public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyOtherProduction, + public sealed class ScriptTriggers : INotifyIdle, INotifyDamage, INotifyKilled, INotifyProduction, INotifyBuildingPlaced, INotifyOtherProduction, INotifyObjectivesUpdated, INotifyCapture, INotifyInfiltrated, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyDiscovered, INotifyActorDisposing, INotifyPassengerEntered, INotifyPassengerExited, INotifySold, INotifyWinStateChanged, INotifyTimeLimit { @@ -280,6 +280,28 @@ namespace OpenRA.Mods.Common.Scripting } } + void INotifyBuildingPlaced.BuildingPlaced(Actor self, Actor building) + { + if (world.Disposing) + return; + + // Run Lua callbacks + foreach (var f in Triggerables(Trigger.OnBuildingPlaced)) + { + try + { + using (var a = self.Owner.ToLuaValue(f.Context)) + using (var b = building.ToLuaValue(f.Context)) + f.Function.Call(a, b).Dispose(); + } + catch (Exception ex) + { + f.Context.FatalError(ex); + return; + } + } + } + void INotifyCapture.OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner, BitSet captureTypes) { if (world.Disposing) diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index e02f149ed0..bf25741da8 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -131,6 +131,13 @@ namespace OpenRA.Mods.Common.Traits foreach (var s in buildingInfo.BuildSounds) Game.Sound.PlayToPlayer(SoundType.World, order.Player, s, placed.CenterPosition); + if (producer.Actor != null) + foreach (var nbp in producer.Actor.TraitsImplementing()) + nbp.BuildingPlaced(producer.Actor, placed); + + foreach (var nbp in self.TraitsImplementing()) + nbp.BuildingPlaced(self, placed); + // Build the connection segments var segmentType = actorInfo.TraitInfo().SegmentType; if (string.IsNullOrEmpty(segmentType)) @@ -141,8 +148,8 @@ namespace OpenRA.Mods.Common.Traits if (t.Cell == targetLocation) continue; - var segment = self.World.Map.Rules.Actors[segmentType]; - var replaceableSegments = segment.TraitInfos() + var segmentInfo = self.World.Map.Rules.Actors[segmentType]; + var replaceableSegments = segmentInfo.TraitInfos() .SelectMany(r => r.ReplaceableTypes) .ToHashSet(); @@ -151,7 +158,7 @@ namespace OpenRA.Mods.Common.Traits if (a.TraitsImplementing().Any(r => !r.IsTraitDisabled && r.Info.Types.Overlaps(replaceableSegments))) self.World.Remove(a); - w.CreateActor(segmentType, + var segment = w.CreateActor(segmentType, [ new LocationInit(t.Cell), new OwnerInit(order.Player), @@ -160,6 +167,13 @@ namespace OpenRA.Mods.Common.Traits new LineBuildParentInit([t.Actor, placed]), new PlaceBuildingInit() ]); + + if (producer.Actor != null) + foreach (var nbp in producer.Actor.TraitsImplementing()) + nbp.BuildingPlaced(producer.Actor, segment); + + foreach (var nbp in self.TraitsImplementing()) + nbp.BuildingPlaced(self, segment); } } else if (os == "PlacePlug") @@ -201,11 +215,14 @@ namespace OpenRA.Mods.Common.Traits foreach (var s in buildingInfo.BuildSounds) Game.Sound.PlayToPlayer(SoundType.World, order.Player, s, building.CenterPosition); - } - if (producer.Actor != null) - foreach (var nbp in producer.Actor.TraitsImplementing()) - nbp.BuildingPlaced(producer.Actor); + if (producer.Actor != null) + foreach (var nbp in producer.Actor.TraitsImplementing()) + nbp.BuildingPlaced(producer.Actor, building); + + foreach (var nbp in self.TraitsImplementing()) + nbp.BuildingPlaced(self, building); + } queue.EndProduction(item); diff --git a/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedAnimation.cs index 153a72b4e2..a9d0a9298c 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedAnimation.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Traits.Render wsb = self.TraitsImplementing().Single(w => w.Info.Name == info.Body); } - void INotifyBuildingPlaced.BuildingPlaced(Actor self) + void INotifyBuildingPlaced.BuildingPlaced(Actor self, Actor building) { if (!IsTraitDisabled) wsb.PlayCustomAnimation(self, Info.Sequence); diff --git a/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedOverlay.cs b/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedOverlay.cs index 19b4cdb9bf..3a957b4c61 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedOverlay.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithBuildingPlacedOverlay.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits.Render overlay.ReplaceAnim(RenderSprites.NormalizeSequence(overlay, e.DamageState, overlay.CurrentSequence.Name)); } - void INotifyBuildingPlaced.BuildingPlaced(Actor self) + void INotifyBuildingPlaced.BuildingPlaced(Actor self, Actor building) { visible = true; overlay.PlayThen(overlay.CurrentSequence.Name, () => visible = false); diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index 005b0aed01..1aa58359c9 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -142,7 +142,7 @@ namespace OpenRA.Mods.Common.Traits public interface INotifyPowerLevelChanged { void PowerLevelChanged(Actor self); } public interface INotifySupportPower { void Charged(Actor self); void Activated(Actor self); } - public interface INotifyBuildingPlaced { void BuildingPlaced(Actor self); } + public interface INotifyBuildingPlaced { void BuildingPlaced(Actor self, Actor building); } public interface INotifyBurstComplete { void FiredBurst(Actor self, in Target target, Armament a); } public interface INotifyChat { bool OnChat(string from, string message); } public interface INotifyProduction { void UnitProduced(Actor self, Actor other, CPos exit); }