diff --git a/OpenRA.Mods.RA/Activities/Hunt.cs b/OpenRA.Mods.RA/Activities/Hunt.cs new file mode 100644 index 0000000000..863de0cd80 --- /dev/null +++ b/OpenRA.Mods.RA/Activities/Hunt.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 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.Collections.Generic; +using System.Linq; +using OpenRA.Mods.RA.Buildings; +using OpenRA.Mods.RA.Move; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Activities +{ + public class Hunt : Activity + { + readonly IEnumerable targets; + + public Hunt(Actor self) + { + var attack = self.Trait(); + targets = self.World.Actors.Where(a => self != a && !a.IsDead() && a.IsInWorld && a.AppearsHostileTo(self) + && a.HasTrait() && attack.HasAnyValidWeapons(Target.FromActor(a))); + } + + public override Activity Tick(Actor self) + { + if (IsCanceled) + return NextActivity; + + var target = targets.ClosestTo(self); + if (target == null) + return this; + + return Util.SequenceActivities( + new AttackMove.AttackMoveActivity(self, new Move.Move(target.Location, WRange.FromCells(2))), + new Wait(25), + this); + } + } + + public class HuntableInfo : TraitInfo { } + public class Huntable { } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 0ae49ce679..eba63a2dde 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -81,6 +81,7 @@ + @@ -338,6 +339,7 @@ + diff --git a/OpenRA.Mods.RA/Scripting/LuaScriptEvents.cs b/OpenRA.Mods.RA/Scripting/LuaScriptEvents.cs new file mode 100644 index 0000000000..a9a361b5be --- /dev/null +++ b/OpenRA.Mods.RA/Scripting/LuaScriptEvents.cs @@ -0,0 +1,40 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 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.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA.Scripting +{ + public class LuaScriptEventsInfo : TraitInfo { } + + public class LuaScriptEvents : INotifyKilled, INotifyAddedToWorld, INotifyRemovedFromWorld + { + public event Action OnKilled = (self, e) => { }; + public event Action OnAddedToWorld = self => { }; + public event Action OnRemovedFromWorld = self => { }; + + public void Killed(Actor self, AttackInfo e) + { + OnKilled(self, e); + } + + public void AddedToWorld(Actor self) + { + OnAddedToWorld(self); + } + + public void RemovedFromWorld(Actor self) + { + OnRemovedFromWorld(self); + } + } +} diff --git a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs index 43f449bb9d..db5c240a14 100644 --- a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs +++ b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs @@ -8,12 +8,16 @@ */ #endregion -using System; -using System.Linq; using LuaInterface; using OpenRA.Effects; using OpenRA.FileFormats; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Air; +using OpenRA.Mods.RA.Missions; +using OpenRA.Scripting; using OpenRA.Traits; +using System; +using System.Linq; using WorldRenderer = OpenRA.Graphics.WorldRenderer; namespace OpenRA.Mods.RA.Scripting @@ -42,7 +46,7 @@ namespace OpenRA.Mods.RA.Scripting AddMapActorGlobals(); context.Lua["World"] = w; context.Lua["WorldRenderer"] = wr; - context.RegisterObject(this, "_OpenRA", false); + context.RegisterObject(this, "Internal", false); context.RegisterType(typeof(WVec), "WVec", true); context.RegisterType(typeof(WPos), "WPos", true); context.RegisterType(typeof(CPos), "CPos", true); @@ -161,5 +165,101 @@ namespace OpenRA.Mods.RA.Scripting { world.AddFrameEndTask(w => w.Add(new DelayedAction((int)delay, func))); } + + [LuaGlobal] + public void PlaySpeechNotification(Player player, string notification) + { + Sound.PlayNotification(player, "Speech", notification, player != null ? player.Country.Race : null); + } + + [LuaGlobal] + public void PlaySoundNotification(Player player, string notification) + { + Sound.PlayNotification(player, "Sounds", notification, player != null ? player.Country.Race : null); + } + + [LuaGlobal] + public void WaitFor(Actor actor, Func func) + { + actor.QueueActivity(new WaitFor(func)); + } + + [LuaGlobal] + public void CallFunc(Actor actor, Action func) + { + actor.QueueActivity(new CallFunc(func)); + } + + [LuaGlobal] + public int GetFacing(object vec, double currentFacing) + { + if (vec is CVec) + return Util.GetFacing((CVec)vec, (int)currentFacing); + if (vec is WVec) + return Util.GetFacing((WVec)vec, (int)currentFacing); + throw new ArgumentException("Unsupported vector type: {0}".F(vec.GetType())); + } + + [LuaGlobal] + public WRange GetWRangeFromCells(double cells) + { + return WRange.FromCells((int)cells); + } + + [LuaGlobal] + public void SetWinState(Player player, string winState) + { + player.WinState = Enum.Parse(winState); + } + + [LuaGlobal] + public void PlayRandomMusic() + { + MissionUtils.PlayMissionMusic(); + } + + [LuaGlobal] + public bool IsDead(Actor actor) + { + return actor.IsDead(); + } + + [LuaGlobal] + public void PlayMovieFullscreen(string movie, Action onComplete) + { + Media.PlayFMVFullscreen(world, movie, onComplete); + } + + [LuaGlobal] + public void FlyToPos(Actor actor, WPos pos) + { + actor.QueueActivity(Fly.ToPos(pos)); + } + + [LuaGlobal] + public void FlyAttackActor(Actor actor, Actor targetActor) + { + actor.QueueActivity(new FlyAttack(Target.FromActor(targetActor))); + } + + [LuaGlobal] + public void FlyAttackCell(Actor actor, CPos location) + { + actor.QueueActivity(new FlyAttack(Target.FromCell(location))); + } + + [LuaGlobal] + public void SetUnitStance(Actor actor, string stance) + { + var at = actor.TraitOrDefault(); + if (at != null) + at.stance = Enum.Parse(stance); + } + + [LuaGlobal] + public bool RequiredUnitsAreDestroyed(Player player) + { + return world.ActorsWithTrait().All(p => p.Actor.Owner != player); + } } } diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 88f84b4832..f00675d58a 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -163,3 +163,12 @@ Fonts: TinyBold: Font:FreeSansBold.ttf Size:10 + +LuaScripts: + mods/ra/lua/utils.lua + mods/ra/lua/openra.lua + mods/ra/lua/map.lua + mods/ra/lua/actor.lua + mods/ra/lua/team.lua + mods/ra/lua/media.lua + mods/ra/lua/mission.lua diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index 8d2b0626be..42c27ad7fb 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -40,6 +40,8 @@ CloakDelay: 90 CloakSound: trans1.aud UncloakSound: trans1.aud + Huntable: + LuaScriptEvents: ^Tank: AppearsOnRadar: @@ -86,6 +88,8 @@ CloakDelay: 90 CloakSound: trans1.aud UncloakSound: trans1.aud + Huntable: + LuaScriptEvents: ^Helicopter: AppearsOnRadar: @@ -113,6 +117,8 @@ CombatDebugOverlay: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Infantry: AppearsOnRadar: @@ -170,6 +176,8 @@ DamageCooldown: 125 RequiresTech: InfantryHealing UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^CivInfantry: Inherits: ^Infantry @@ -247,6 +255,8 @@ AttackMove: AttackFrontal: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Plane: AppearsOnRadar: @@ -263,6 +273,8 @@ ActorLostNotification: CombatDebugOverlay: BodyOrientation: + Huntable: + LuaScriptEvents: ^Ship: AppearsOnRadar: @@ -286,6 +298,8 @@ Guardable: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Building: AppearsOnRadar: @@ -337,6 +351,8 @@ FrozenUnderFog: UpdatesPlayerStatistics: EngineerRepairable: + Huntable: + LuaScriptEvents: ^CivBuilding: Inherits: ^Building @@ -372,6 +388,7 @@ BodyOrientation: FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^TechBuilding: Inherits: ^CivBuilding @@ -407,6 +424,7 @@ Palette: terrain FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^Wall: AppearsOnRadar: @@ -437,6 +455,7 @@ Guardable: BodyOrientation: FrozenUnderFog: + LuaScriptEvents: ^Tree: Tooltip: @@ -460,6 +479,7 @@ BodyOrientation: FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^Rock: Tooltip: @@ -477,6 +497,7 @@ BodyOrientation: FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^Husk: Health: @@ -499,6 +520,7 @@ # AllowAllies: true # AllowNeutral: true # AllowEnemies: true + LuaScriptEvents: ^HelicopterHusk: Inherits: ^Husk @@ -524,3 +546,4 @@ DamagedSound: xplos.aud DestroyedSound: xplobig4.aud BodyOrientation: + LuaScriptEvents: diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 02eaf01e8c..7fff8c53f6 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -144,3 +144,12 @@ Fonts: TinyBold: Font:FreeSansBold.ttf Size:10 + +LuaScripts: + mods/ra/lua/utils.lua + mods/ra/lua/openra.lua + mods/ra/lua/map.lua + mods/ra/lua/actor.lua + mods/ra/lua/team.lua + mods/ra/lua/media.lua + mods/ra/lua/mission.lua diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 0afb310914..aed8639c40 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -35,6 +35,8 @@ RenderUnit: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Tank: AppearsOnRadar: @@ -73,6 +75,8 @@ RenderUnit: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Husk: Health: @@ -90,6 +94,7 @@ Tooltip: Name: Destroyed Tank BodyOrientation: + LuaScriptEvents: ^TowerHusk: Health: @@ -108,6 +113,7 @@ ProximityCaptor: Types:Husk BodyOrientation: + LuaScriptEvents: ^AircraftHusk: Inherits: ^Husk @@ -167,6 +173,8 @@ Guardable: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Plane: AppearsOnRadar: @@ -188,6 +196,8 @@ CombatDebugOverlay: BodyOrientation: UpdatesPlayerStatistics: + Huntable: + LuaScriptEvents: ^Helicopter: Inherits: ^Plane @@ -236,4 +246,6 @@ Range: 3 BodyOrientation: UpdatesPlayerStatistics: - WithCrumbleOverlay: \ No newline at end of file + WithCrumbleOverlay: + Huntable: + LuaScriptEvents: \ No newline at end of file diff --git a/mods/ra/lua/actor.lua b/mods/ra/lua/actor.lua new file mode 100644 index 0000000000..904b6a5be4 --- /dev/null +++ b/mods/ra/lua/actor.lua @@ -0,0 +1,147 @@ +Actor = { } + +Actor.Create = function(name, init) + if name == nil then error("No actor name specified", 2) end + if init.Owner == nil then error("No actor owner specified", 2) end + local td = OpenRA.New("TypeDictionary") + local addToWorld = true + for key, value in pairs(init) do + if key == "AddToWorld" then + addToWorld = value + else + td:Add(OpenRA.New(key .. "Init", { value })) + end + end + return World:CreateActor(addToWorld, name, td) +end + +Actor.Turn = function(actor, facing) + actor:QueueActivity(OpenRA.New("Turn", { { facing, "Int32" } })) +end + +Actor.Move = function(actor, location) + Actor.MoveNear(actor, location, 0) +end + +Actor.MoveNear = function(actor, location, nearEnough) + actor:QueueActivity(OpenRA.New("Move", { location, Map.GetWRangeFromCells(nearEnough) })) +end + +Actor.HeliFly = function(actor, position) + actor:QueueActivity(OpenRA.New("HeliFly", { position })) +end + +Actor.HeliLand = function(actor, requireSpace) + actor:QueueActivity(OpenRA.New("HeliLand", { requireSpace })) +end + +Actor.Fly = function(actor, position) + Internal.FlyToPos(actor, position) +end + +Actor.FlyAttackActor = function(actor, targetActor) + Internal.FlyAttackActor(actor, targetActor) +end + +Actor.FlyAttackCell = function(actor, location) + Internal.FlyAttackCell(actor, location) +end + +Actor.FlyOffMap = function(actor) + actor:QueueActivity(OpenRA.New("FlyOffMap")) +end + +Actor.Hunt = function(actor) + actor:QueueActivity(OpenRA.New("Hunt", { actor })) +end + +Actor.UnloadCargo = function(actor, unloadAll) + actor:QueueActivity(OpenRA.New("UnloadCargo", { unloadAll })) +end + +Actor.Harvest = function(actor) + actor:QueueActivity(OpenRA.New("FindResources")) +end + +Actor.Scatter = function(actor) + local mobile = Actor.Trait(actor, "Mobile") + mobile:Nudge(actor, actor, true) +end + +Actor.Wait = function(actor, period) + actor:QueueActivity(OpenRA.New("Wait", { { period, "Int32" } })) +end + +Actor.WaitFor = function(actor, func) + Internal.WaitFor(actor, func) +end + +Actor.CallFunc = function(actor, func) + Internal.CallFunc(actor, func) +end + +Actor.DeployTransform = function(actor) + Actor.CallFunc(actor, function() + Actor.Trait(actor, "Transforms"):DeployTransform() + end) +end + +Actor.RemoveSelf = function(actor) + actor:QueueActivity(OpenRA.New("RemoveSelf")) +end + +Actor.Stop = function(actor) + actor:CancelActivity() +end + +Actor.IsDead = function(actor) + return Internal.IsDead(actor) +end + +Actor.IsInWorld = function(actor) + return actor.IsInWorld +end + +Actor.Owner = function(actor) + return actor.Owner +end + +Actor.SetStance = function(actor, stance) + Internal.SetUnitStance(actor, stance) +end + +Actor.OnKilled = function(actor, eh) + Actor.Trait(actor, "LuaScriptEvents").OnKilled:Add(eh) +end + +Actor.OnAddedToWorld = function(actor, eh) + Actor.Trait(actor, "LuaScriptEvents").OnAddedToWorld:Add(eh) +end + +Actor.OnRemovedFromWorld = function(actor, eh) + Actor.Trait(actor, "LuaScriptEvents").OnRemovedFromWorld:Add(eh) +end + +Actor.HasTrait = function(actor, className) + return Internal.HasTrait(actor, className) +end + +Actor.TraitOrDefault = function(actor, className) + return Internal.TraitOrDefault(actor, className) +end + +Actor.Trait = function(actor, className) + return Internal.Trait(actor, className) +end + +Actor.HasTraitInfo = function(actorType, className) + return Internal.HasTraitInfo(actorType, className) +end + +Actor.TraitInfoOrDefault = function(actorType, className) + return Internal.TraitInfoOrDefault(actorType, className) +end + +Actor.TraitInfo = function(actorType, className) + return Internal.TraitInfo(actorType, className) +end \ No newline at end of file diff --git a/mods/ra/lua/map.lua b/mods/ra/lua/map.lua new file mode 100644 index 0000000000..39ed982234 --- /dev/null +++ b/mods/ra/lua/map.lua @@ -0,0 +1,9 @@ +Map = { } + +Map.GetFacing = function(vec, currentFacing) + return Internal.GetFacing(vec, currentFacing) +end + +Map.GetWRangeFromCells = function(cells) + return Internal.GetWRangeFromCells(cells) +end \ No newline at end of file diff --git a/mods/ra/lua/media.lua b/mods/ra/lua/media.lua new file mode 100644 index 0000000000..7400551f46 --- /dev/null +++ b/mods/ra/lua/media.lua @@ -0,0 +1,20 @@ +Media = { } + +Media.PlaySpeechNotification = function(notification, player) + Internal.PlaySpeechNotification(player, notification) +end + +Media.PlaySoundNotification = function(notification, player) + Internal.PlaySoundNotification(player, notification) +end + +Media.PlayRandomMusic = function() + Internal.PlayRandomMusic() +end + +Media.PlayMovieFullscreen = function(movie, onComplete) + if onComplete == nil then + onComplete = function() end + end + Internal.PlayMovieFullscreen(movie, onComplete) +end \ No newline at end of file diff --git a/mods/ra/lua/mission.lua b/mods/ra/lua/mission.lua new file mode 100644 index 0000000000..ed26c90e0a --- /dev/null +++ b/mods/ra/lua/mission.lua @@ -0,0 +1,119 @@ +Mission = { } + +Mission.PerformHelicopterInsertion = function(owner, helicopterName, passengerNames, enterPosition, unloadPosition, exitPosition) + local facing = { Map.GetFacing(WPos.op_Subtraction(unloadPosition, enterPosition), 0), "Int32" } + local altitude = { Actor.TraitInfo(helicopterName, "AircraftInfo").CruiseAltitude, "Int32" } + local heli = Actor.Create(helicopterName, { Owner = owner, CenterPosition = enterPosition, Facing = facing, Altitude = altitude }) + local cargo = Actor.Trait(heli, "Cargo") + local passengers = { } + for i, passengerName in ipairs(passengerNames) do + local passenger = Actor.Create(passengerName, { AddToWorld = false, Owner = owner }) + cargo:Load(heli, passenger) + passengers[i] = passenger + end + Actor.HeliFly(heli, unloadPosition) + Actor.Turn(heli, 0) + Actor.HeliLand(heli, true) + Actor.UnloadCargo(heli, true) + Actor.Wait(heli, 125) + Actor.HeliFly(heli, exitPosition) + Actor.RemoveSelf(heli) + return heli, passengers +end + +Mission.PerformHelicopterExtraction = function(owner, helicopterName, passengers, enterPosition, loadPosition, exitPosition) + local facing = { Map.GetFacing(WPos.op_Subtraction(loadPosition, enterPosition), 0), "Int32" } + local altitude = { Actor.TraitInfo(helicopterName, "AircraftInfo").CruiseAltitude, "Int32" } + local heli = Actor.Create(helicopterName, { Owner = owner, CenterPosition = enterPosition, Facing = facing, Altitude = altitude }) + local cargo = Actor.Trait(heli, "Cargo") + Actor.HeliFly(heli, loadPosition) + Actor.Turn(heli, 0) + Actor.HeliLand(heli, true) + Actor.WaitFor(heli, function() + for i, passenger in ipairs(passengers) do + if not cargo.Passengers:Contains(passenger) then + return false + end + end + return true + end) + Actor.Wait(heli, 125) + Actor.HeliFly(heli, exitPosition) + Actor.RemoveSelf(heli) + return heli +end + +Mission.Reinforce = function(owner, reinforcementNames, enterLocation, rallyPointLocation, interval, onCreateFunc) + local facing = { Map.GetFacing(CPos.op_Subtraction(rallyPointLocation, enterLocation), 0), "Int32" } + local ret = { } + for i = 1, #reinforcementNames do + local reinforcement = Actor.Create(reinforcementNames[i], { AddToWorld = false, Owner = owner, Location = enterLocation, Facing = facing }) + table.insert(ret, reinforcement) + OpenRA.RunAfterDelay((i - 1) * interval, function() + World:Add(reinforcement) + Actor.MoveNear(reinforcement, rallyPointLocation, 2) + if onCreateFunc ~= nil then + onCreateFunc(reinforcement) + end + end) + end + return ret +end + +Mission.Parabomb = function(owner, planeName, enterLocation, bombLocation) + local facing = { Map.GetFacing(CPos.op_Subtraction(bombLocation, enterLocation), 0), "Int32" } + local altitude = { Actor.TraitInfo(planeName, "AircraftInfo").CruiseAltitude, "Int32" } + local plane = Actor.Create(planeName, { Location = enterLocation, Owner = owner, Facing = facing, Altitude = altitude }) + Actor.Trait(plane, "AttackBomber"):SetTarget(bombLocation.CenterPosition) + Actor.Fly(plane, bombLocation.CenterPosition) + Actor.FlyOffMap(plane) + Actor.RemoveSelf(plane) +end + +Mission.Paradrop = function(owner, planeName, passengerNames, enterLocation, dropLocation) + local facing = { Map.GetFacing(CPos.op_Subtraction(dropLocation, enterLocation), 0), "Int32" } + local altitude = { Actor.TraitInfo(planeName, "AircraftInfo").CruiseAltitude, "Int32" } + local plane = Actor.Create(planeName, { Location = enterLocation, Owner = owner, Facing = facing, Altitude = altitude }) + Actor.FlyAttackCell(plane, dropLocation) + Actor.Trait(plane, "ParaDrop"):SetLZ(dropLocation) + local cargo = Actor.Trait(plane, "Cargo") + for i, passengerName in ipairs(passengerNames) do + cargo:Load(plane, Actor.Create(passengerName, { AddToWorld = false, Owner = owner })) + end +end + +Mission.MissionOver = function(winners, losers, setWinStates) + World:SetLocalPauseState(true) + World:set_PauseStateLocked(true) + if winners then + for i, player in ipairs(winners) do + Media.PlaySpeechNotification("Win", player) + if setWinStates then + OpenRA.SetWinState(player, "Won") + end + end + end + if losers then + for i, player in ipairs(losers) do + Media.PlaySpeechNotification("Lose", player) + if setWinStates then + OpenRA.SetWinState(player, "Lost") + end + end + end + Mission.MissionIsOver = true +end + +Mission.GetGroundAttackersOf = function(player) + return Utils.EnumerableWhere(World.Actors, function(actor) + return not Actor.IsDead(actor) and Actor.IsInWorld(actor) and Actor.Owner(actor) == player and Actor.HasTrait(actor, "AttackBase") and Actor.HasTrait(actor, "Mobile") + end) +end + +Mission.TickTakeOre = function(player) + OpenRA.TakeOre(player, 0.01 * OpenRA.GetOreCapacity(player) / 25) +end + +Mission.RequiredUnitsAreDestroyed = function(player) + return Internal.RequiredUnitsAreDestroyed(player) +end \ No newline at end of file diff --git a/mods/ra/lua/openra.lua b/mods/ra/lua/openra.lua new file mode 100644 index 0000000000..ae746d2db4 --- /dev/null +++ b/mods/ra/lua/openra.lua @@ -0,0 +1,69 @@ +OpenRA = { } + +OpenRA.New = function(className, args) + return Internal.New(className, args) +end + +OpenRA.RunAfterDelay = function(delay, func) + Internal.RunAfterDelay(delay, func) +end + +OpenRA.Debug = function(obj) + Internal.Debug(obj) +end + +OpenRA.SetViewportCenterPosition = function(position) + WorldRenderer.Viewport:Center(position) +end + +OpenRA.GetViewportCenterPosition = function() + return WorldRenderer.Viewport.CenterPosition +end + +OpenRA.GetDifficulty = function() + return World.LobbyInfo.GlobalSettings.Difficulty +end + +OpenRA.IsSinglePlayer = function() + return World.LobbyInfo:get_IsSinglePlayer() +end + +OpenRA.GetPlayer = function(internalName) + return Utils.EnumerableFirstOrNil(World.Players, function(p) return p.InternalName == internalName end) +end + +OpenRA.SetWinState = function(player, winState) + Internal.SetWinState(player, winState) +end + +OpenRA.TakeOre = function(player, amount) + Actor.Trait(player.PlayerActor, "PlayerResources"):TakeOre(amount) +end + +OpenRA.TakeCash = function(player, amount) + Actor.Trait(player.PlayerActor, "PlayerResources"):TakeCash(amount) +end + +OpenRA.GiveOre = function(player, amount) + Actor.Trait(player.PlayerActor, "PlayerResources"):GiveOre(amount) +end + +OpenRA.GiveCash = function(player, amount) + Actor.Trait(player.PlayerActor, "PlayerResources"):GiveCash(amount) +end + +OpenRA.CanGiveOre = function(player, amount) + return Actor.Trait(player.PlayerActor, "PlayerResources"):CanGiveOre(amount) +end + +OpenRA.GetOreCapacity = function(player) + return Actor.Trait(player.PlayerActor, "PlayerResources").OreCapacity +end + +OpenRA.GetOre = function(player) + return Actor.Trait(player.PlayerActor, "PlayerResources").Ore +end + +OpenRA.GetCash = function(player) + return Actor.Trait(player.PlayerActor, "PlayerResources").Cash +end \ No newline at end of file diff --git a/mods/ra/lua/team.lua b/mods/ra/lua/team.lua new file mode 100644 index 0000000000..f77a0733ed --- /dev/null +++ b/mods/ra/lua/team.lua @@ -0,0 +1,61 @@ +Team = { } + +Team.Create = function(actors) + local team = { } + team.Actors = actors + team.OnAllKilled = { } + team.OnAnyKilled = { } + team.OnAllRemovedFromWorld = { } + team.OnAnyRemovedFromWorld = { } + Team.AddActorEventHandlers(team) + return team +end + +Team.AddActorEventHandlers = function(team) + for i, actor in ipairs(team.Actors) do + + Actor.OnKilled(actor, function() + Team.InvokeHandlers(team.OnAnyKilled) + if Team.AllAreDead(team) then Team.InvokeHandlers(team.OnAllKilled) end + end) + + Actor.OnRemovedFromWorld(actor, function() + Team.InvokeHandlers(team.OnAnyRemovedFromWorld) + if not Team.AnyAreInWorld(team) then Team.InvokeHandlers(team.OnAllRemovedFromWorld) end + end) + end +end + +Team.InvokeHandlers = function(event) + for i, handler in ipairs(event) do + handler() + end +end + +Team.AllAreDead = function(team) + return Utils.All(team.Actors, Actor.IsDead) +end + +Team.AnyAreDead = function(team) + return Utils.Any(team.Actors, Actor.IsDead) +end + +Team.AllAreInWorld = function(team) + return Utils.All(team.Actors, Actor.IsInWorld) +end + +Team.AnyAreInWorld = function(team) + return Utils.Any(team.Actors, Actor.IsInWorld) +end + +Team.AddEventHandler = function(event, func) + table.insert(event, func) +end + +Team.Contains = function(team, actor) + return Utils.Any(team.Actors, function(a) return a == actor end) +end + +Team.Do = function(team, func) + Utils.ForEach(team.Actors, func) +end \ No newline at end of file diff --git a/mods/ra/lua/utils.lua b/mods/ra/lua/utils.lua new file mode 100644 index 0000000000..9a486d88dc --- /dev/null +++ b/mods/ra/lua/utils.lua @@ -0,0 +1,67 @@ +Utils = { } + +Utils.Enumerate = function(netEnumerable) + local enum = netEnumerable:GetEnumerator() + return function() + if enum:MoveNext() then + return enum:get_Current() + end + end +end + +Utils.EnumerableFirstOrNil = function(netEnumerable, func) + for item in Utils.Enumerate(netEnumerable) do + if func(item) then + return item + end + end + return nil +end + +Utils.EnumerableWhere = function(netEnumerable, func) + local ret = { } + for item in Utils.Enumerate(netEnumerable) do + if func(item) then + table.insert(ret, item) + end + end + return ret +end + +Utils.Where = function(array, func) + local ret = { } + for i, item in ipairs(array) do + if func(item) then + table.insert(ret, item) + end + end + return ret +end + +Utils.All = function(array, func) + for i, item in ipairs(array) do + if not func(item) then + return false + end + end + return true +end + +Utils.Any = function(array, func) + for i, item in ipairs(array) do + if func(item) then + return true + end + end + return false +end + +Utils.ForEach = function(array, func) + for i, item in ipairs(array) do + func(item) + end +end + +Utils.TableToArray = function(luaTable) + return Internal.TableToArray(luaTable) +end \ No newline at end of file diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index d694d00fd3..e36a9a7edb 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -164,3 +164,12 @@ Fonts: TinyBold: Font:FreeSansBold.ttf Size:10 + +LuaScripts: + mods/ra/lua/utils.lua + mods/ra/lua/openra.lua + mods/ra/lua/map.lua + mods/ra/lua/actor.lua + mods/ra/lua/team.lua + mods/ra/lua/media.lua + mods/ra/lua/mission.lua diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 9366e89531..87f214d0c2 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -42,6 +42,8 @@ SuccessRate: 20 EjectOnGround: yes EjectInAir: no + Huntable: + LuaScriptEvents: ^Tank: AppearsOnRadar: @@ -87,6 +89,8 @@ SuccessRate: 20 EjectOnGround: yes EjectInAir: no + Huntable: + LuaScriptEvents: ^Infantry: AppearsOnRadar: @@ -142,6 +146,8 @@ HealIfBelow: 1 DamageCooldown: 125 RequiresTech: InfantryHealing + Huntable: + LuaScriptEvents: ^Ship: AppearsOnRadar: @@ -174,6 +180,8 @@ Guard: Guardable: BodyOrientation: + Huntable: + LuaScriptEvents: ^Plane: AppearsOnRadar: @@ -203,6 +211,8 @@ UpdatesPlayerStatistics: CombatDebugOverlay: BodyOrientation: + Huntable: + LuaScriptEvents: ^Helicopter: Inherits: ^Plane @@ -252,6 +262,8 @@ FrozenUnderFog: GpsDot: String:Structure + Huntable: + LuaScriptEvents: ^Wall: AppearsOnRadar: @@ -287,6 +299,7 @@ Guardable: BodyOrientation: FrozenUnderFog: + LuaScriptEvents: ^TechBuilding: Inherits: ^Building @@ -391,6 +404,7 @@ BodyOrientation: FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^Husk: Husk: @@ -408,6 +422,7 @@ BelowUnits: BodyOrientation: Chronoshiftable: + LuaScriptEvents: ^HelicopterHusk: Inherits: ^Husk @@ -442,6 +457,7 @@ Types:Bridge AutoTargetIgnore: BodyOrientation: + LuaScriptEvents: #Temperate Terrain Expansion ^SVBridge: @@ -459,6 +475,7 @@ Types:Bridge AutoTargetIgnore: BodyOrientation: + LuaScriptEvents: ^SHBridge: Tooltip: @@ -475,6 +492,7 @@ Types:Bridge AutoTargetIgnore: BodyOrientation: + LuaScriptEvents: ^STDBridge: Tooltip: @@ -491,6 +509,7 @@ Types:Bridge AutoTargetIgnore: BodyOrientation: + LuaScriptEvents: #Desert Terrain Expansion: ^Rock: @@ -512,6 +531,7 @@ BodyOrientation: FrozenUnderFog: StartsRevealed: true + LuaScriptEvents: ^DesertCivBuilding: Inherits: ^CivBuilding diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 63adc87e73..0030ff7b92 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -185,3 +185,12 @@ Fonts: TinyBold: Font:FreeSansBold.ttf Size:10 + +LuaScripts: + mods/ra/lua/utils.lua + mods/ra/lua/openra.lua + mods/ra/lua/map.lua + mods/ra/lua/actor.lua + mods/ra/lua/team.lua + mods/ra/lua/media.lua + mods/ra/lua/mission.lua diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 3ef39c5683..9c692cc530 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -36,6 +36,8 @@ Guardable: Range: 3 BodyOrientation: + Huntable: + LuaScriptEvents: ^Infantry: AppearsOnRadar: @@ -80,6 +82,8 @@ Guard: Guardable: BodyOrientation: + Huntable: + LuaScriptEvents: ^CivilianInfantry: Inherits: ^Infantry @@ -145,6 +149,8 @@ Guard: Guardable: BodyOrientation: + Huntable: + LuaScriptEvents: ^Wall: AppearsOnRadar: @@ -175,6 +181,7 @@ Sellable: UpdatesPlayerStatistics: BodyOrientation: + LuaScriptEvents: ^Helicopter: AppearsOnRadar: @@ -197,4 +204,6 @@ DrawLineToTarget: ActorLostNotification: CombatDebugOverlay: - BodyOrientation: \ No newline at end of file + BodyOrientation: + Huntable: + LuaScriptEvents: \ No newline at end of file