Add OnBuildingPlaced lua trigger.

This commit is contained in:
Paul Chote
2025-11-08 11:04:03 +00:00
committed by Gustas Kažukauskas
parent eb550bbbbb
commit 3177de7b2c
6 changed files with 61 additions and 12 deletions

View File

@@ -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)

View File

@@ -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<CaptureType> captureTypes)
{
if (world.Disposing)

View File

@@ -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<INotifyBuildingPlaced>())
nbp.BuildingPlaced(producer.Actor, placed);
foreach (var nbp in self.TraitsImplementing<INotifyBuildingPlaced>())
nbp.BuildingPlaced(self, placed);
// Build the connection segments
var segmentType = actorInfo.TraitInfo<LineBuildInfo>().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<ReplacementInfo>()
var segmentInfo = self.World.Map.Rules.Actors[segmentType];
var replaceableSegments = segmentInfo.TraitInfos<ReplacementInfo>()
.SelectMany(r => r.ReplaceableTypes)
.ToHashSet();
@@ -151,7 +158,7 @@ namespace OpenRA.Mods.Common.Traits
if (a.TraitsImplementing<Replaceable>().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<INotifyBuildingPlaced>())
nbp.BuildingPlaced(producer.Actor, segment);
foreach (var nbp in self.TraitsImplementing<INotifyBuildingPlaced>())
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<INotifyBuildingPlaced>())
nbp.BuildingPlaced(producer.Actor);
if (producer.Actor != null)
foreach (var nbp in producer.Actor.TraitsImplementing<INotifyBuildingPlaced>())
nbp.BuildingPlaced(producer.Actor, building);
foreach (var nbp in self.TraitsImplementing<INotifyBuildingPlaced>())
nbp.BuildingPlaced(self, building);
}
queue.EndProduction(item);

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.Common.Traits.Render
wsb = self.TraitsImplementing<WithSpriteBody>().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);

View File

@@ -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);

View File

@@ -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); }