diff --git a/CHANGELOG.md b/CHANGELOG.md index 904e6757dd..2544960f3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - New `AEmitter` and `PEmitter` INI and Lua (R/W) property `PlayBurstSound` which denotes whether the BurstSound should play when appropriate. This should not be confused for a trigger - it's just a enable/disable toggle to avoid having to remove and add BurstSound altogether. +- New `GAScripted` Lua script method `IsCompatibleScene(scene)` to allow Activities to generically decide which Scenes are eligible by returning a boolean value. + New `GAScripted` INI enumerating property `AddRequiredArea`, replacing Lua file scanning, to allow Activities to explicitly state which areas are strictly required. + As before, these work in tandem. Both the required areas, if defined, and script conditional method, if defined, must pass for the scene to qualify. + - New `GameActivity` INI properties `TeamNTechSwitchEnabled` which determine whether activity team factions are configurable by the user. This is most useful for communicating what the player is not intended to change, or what inputs would be ignored otherwise. - Allow lua scripts to use LuaJIT's BitOp module (see https://bitop.luajit.org/api.html) @@ -119,6 +123,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - The Signal Hunt activity no longer has a preview image, as it was not formatted correctly and spoiled the interior structure of the cave. +- Removed `GAScripted` Lua script method `SceneTest()` as the new Lua function `IsCompatibleScene(scene)` is more capable. + Removed `GAScripted` C++ functionality that would scan the Lua script file to determine which areas are required. `AddRequiredArea` in the INI should be used instead. + Removed `Scene` Lua function `GetOptionalArea` as it functioned identically to `GetArea` aside from triggering the aforementioned (and now removed) Lua script file scanning. + - Removed `AHuman` property `MaxCrouchRotation`. `CrouchRotAngleTarget` is now used instead. - Deprecated `LimbPath` properties `SlowTravelSpeed`, `NormalTravelSpeed` and `FastTravelSpeed`. For the sake of backwards compatibility they will not crash the game and `NormalTravelSpeed` is a valid synonym for the new `TravelSpeed`. diff --git a/Data/Base.rte/Activities.ini b/Data/Base.rte/Activities.ini index 21449f2310..225897e749 100644 --- a/Data/Base.rte/Activities.ini +++ b/Data/Base.rte/Activities.ini @@ -57,6 +57,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 1 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = Red Brain + AddRequiredArea = Green Brain AddActivity = GAScripted @@ -109,6 +111,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 1 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Attacker + AddRequiredArea = Main Bunker AddActivity = GAScripted @@ -126,6 +130,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 1 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All AddActivity = GAScripted @@ -149,6 +155,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All AddActivity = GAScripted @@ -172,7 +180,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 - + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All AddActivity = GAScripted @@ -197,8 +206,9 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 FogOfWarSwitchEnabled = 0 DeployUnitsSwitchEnabled = 0 + AddRequiredArea = OneManArmyZeroGCompatibilityArea + -// Path to orbit guaranteed by currently being in orbit. AddActivity = GAScripted PresetName = One-Man Army (Diggers, 0-G) Description = Survive with only one unit and no backups in a Zero-G void! The enemy will only use diggers, but the harder the difficulty the less potent weaponry you start out with, and the longer you have to survive. @@ -237,6 +247,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 1 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All AddActivity = GAScripted @@ -253,6 +265,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 1 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All AddActivity = GAScripted @@ -270,6 +284,8 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 FogOfWarSwitchEnabled = 1 DeployUnitsSwitchEnabled = 1 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ All /////////////////////////////////////////////////////////////////////// diff --git a/Data/Base.rte/Activities/BunkerBreach.lua b/Data/Base.rte/Activities/BunkerBreach.lua index 3752870087..7176670b28 100644 --- a/Data/Base.rte/Activities/BunkerBreach.lua +++ b/Data/Base.rte/Activities/BunkerBreach.lua @@ -84,7 +84,7 @@ function BunkerBreach:SetupDefenderBrains() end defenderBrain = self:CreateBrainBot(self.defenderTeam); - defenderBrain.Pos = SceneMan.Scene:GetOptionalArea("Brain"):GetCenterPoint(); + defenderBrain.Pos = SceneMan.Scene:GetArea("Brain"):GetCenterPoint(); MovableMan:AddActor(defenderBrain); else -- Pick the defender brain randomly from among those created by deployments, then delete the others and clean up most of their guards. @@ -99,7 +99,7 @@ function BunkerBreach:SetupDefenderBrains() table.remove(deploymentBrains, brainIndexToChoose); if SceneMan.Scene:HasArea("Brain Chamber") then - self.brainChamber = SceneMan.Scene:GetOptionalArea("Brain Chamber"); + self.brainChamber = SceneMan.Scene:GetArea("Brain Chamber"); end for _, unchosenDeploymentBrain in pairs(deploymentBrains) do unchosenDeploymentBrain.ToDelete = true; @@ -167,7 +167,7 @@ function BunkerBreach:SetupDefenderActors() for _, loadoutName in pairs({"Light", "Heavy", "Sniper", "Engineer", "Mecha", "Turret"}) do if SceneMan.Scene:HasArea(loadoutName .. " Defenders") then hasSpawnAreas = true; - local defenderArea = SceneMan.Scene:GetOptionalArea(loadoutName .. " Defenders"); + local defenderArea = SceneMan.Scene:GetArea(loadoutName .. " Defenders"); if defenderArea ~= nil then for defenderBox in defenderArea.Boxes do local guard; @@ -226,7 +226,7 @@ end function BunkerBreach:SetupDefenderInternalReinforcementAreas() if self.AI.isDefenderTeam then - local internalReinforcementsArea = SceneMan.Scene:GetOptionalArea("Internal Reinforcements"); + local internalReinforcementsArea = SceneMan.Scene:GetArea("Internal Reinforcements"); if internalReinforcementsArea ~= nil then self.AI.internalReinforcementsDoorParticle = CreateMOSRotating("Background Door", "Base.rte"); self.AI.internalReinforcementPositions = {}; @@ -248,7 +248,7 @@ function BunkerBreach:StartActivity(isNewGame) local attackerLZ = SceneMan.Scene:GetArea("LZ Attacker"); self:SetLZArea(self.attackerTeam, attackerLZ); if SceneMan.Scene:HasArea("LZ Defender") then - self:SetLZArea(self.defenderTeam, SceneMan.Scene:GetOptionalArea("LZ Defender")); + self:SetLZArea(self.defenderTeam, SceneMan.Scene:GetArea("LZ Defender")); end self.mainBunkerArea = SceneMan.Scene:GetArea("Main Bunker"); diff --git a/Data/Base.rte/Activities/Siege.lua b/Data/Base.rte/Activities/Siege.lua index ae7b817d6b..d741cab8f6 100644 --- a/Data/Base.rte/Activities/Siege.lua +++ b/Data/Base.rte/Activities/Siege.lua @@ -65,7 +65,7 @@ function Siege:StartActivity() --end if SceneMan.Scene:HasArea("Brain") then - playerBrainsLocation = SceneMan.Scene:GetOptionalArea("Brain"):GetCenterPoint(); + playerBrainsLocation = SceneMan.Scene:GetArea("Brain"):GetCenterPoint(); else -- Look for a brain among actors created by the deployments for actor in MovableMan.AddedActors do @@ -98,7 +98,7 @@ function Siege:StartActivity() end if SceneMan.Scene:HasArea("Brain Chamber") then - self.BrainChamber = SceneMan.Scene:GetOptionalArea("Brain Chamber"); + self.BrainChamber = SceneMan.Scene:GetArea("Brain Chamber"); -- Set all useless actors, i.e. those who should guard brain in the brain chamber but their brain is in another castle -- to delete themselves, because otherwise they are most likely to stand there for the whole battle and waste MOs @@ -112,7 +112,7 @@ function Siege:StartActivity() end if SceneMan.Scene:HasArea("Perimeter") then - self.Perimeter = SceneMan.Scene:GetOptionalArea("Perimeter"); + self.Perimeter = SceneMan.Scene:GetArea("Perimeter"); --print ("Perimeter defined"); end diff --git a/Data/Base.rte/Activities/Utility/DockingHandler.lua b/Data/Base.rte/Activities/Utility/DockingHandler.lua index 18d4a5ec75..a0dd146b7c 100644 --- a/Data/Base.rte/Activities/Utility/DockingHandler.lua +++ b/Data/Base.rte/Activities/Utility/DockingHandler.lua @@ -93,14 +93,14 @@ function DockingHandler:Initialize(activity, newGame, autoAssignUnknownDropships local i = 1; - while SceneMan.Scene:GetOptionalArea("Dropship Dock " .. i) do + while SceneMan.Scene:GetArea("Dropship Dock " .. i) do self.mainTable.activeDSDockTable[i] = {["dockPosition"] = SceneMan.Scene:GetArea("Dropship Dock " .. i).Center, ["activeCraft"] = nil, ["dockingStage"] = nil}; i = i + 1; -- Jump two if needed, the player may want to avoid overlap as per above instructions - if SceneMan.Scene:GetOptionalArea("Dropship Dock " .. i) then - elseif SceneMan.Scene:GetOptionalArea("Dropship Dock " .. i + 1) then + if SceneMan.Scene:GetArea("Dropship Dock " .. i) then + elseif SceneMan.Scene:GetArea("Dropship Dock " .. i + 1) then i = i + 1; end @@ -111,13 +111,13 @@ function DockingHandler:Initialize(activity, newGame, autoAssignUnknownDropships i = 1; - while SceneMan.Scene:GetOptionalArea("Rocket Dock " .. i) do + while SceneMan.Scene:GetArea("Rocket Dock " .. i) do self.mainTable.activeRocketDockTable[i] = {["dockPosition"] = SceneMan.Scene:GetArea("Rocket Dock " .. i).Center, ["activeCraft"] = nil, ["dockingStage"] = nil}; i = i + 1; - if SceneMan.Scene:GetOptionalArea("Rocket Dock " .. i) then - elseif SceneMan.Scene:GetOptionalArea("Rocket Dock " .. i + 1) then + if SceneMan.Scene:GetArea("Rocket Dock " .. i) then + elseif SceneMan.Scene:GetArea("Rocket Dock " .. i + 1) then i = i + 1; end end diff --git a/Data/Base.rte/Activities/Utility/SaveLoadHandler.lua b/Data/Base.rte/Activities/Utility/SaveLoadHandler.lua index 0184101318..c519530205 100644 --- a/Data/Base.rte/Activities/Utility/SaveLoadHandler.lua +++ b/Data/Base.rte/Activities/Utility/SaveLoadHandler.lua @@ -247,7 +247,7 @@ function SaveLoadHandler:ParseTableForAreas(tab) for k, v in pairs(tab) do if type(v) == "string" and string.find(v, "SAVELOADHANDLERAREA_") then local areaName = string.sub(v, 21, -1); - local area = SceneMan.Scene:GetOptionalArea(areaName); + local area = SceneMan.Scene:GetArea(areaName); tab[k] = area; elseif type(v) == "table" then self:ParseTableForAreas(v); diff --git a/Data/Base.rte/Activities/WaveDefense.lua b/Data/Base.rte/Activities/WaveDefense.lua index 3dfcbddeca..ac61109efa 100644 --- a/Data/Base.rte/Activities/WaveDefense.lua +++ b/Data/Base.rte/Activities/WaveDefense.lua @@ -274,7 +274,7 @@ function WaveDefense:UpdateActivity() end -- Reveal the main bunker area for the defender. - local mainBunkerArea = SceneMan.Scene:GetOptionalArea("Main Bunker"); + local mainBunkerArea = SceneMan.Scene:GetArea("Main Bunker"); if mainBunkerArea ~= nil then for mainBunkerBox in mainBunkerArea.Boxes do SceneMan:RevealUnseenBox(mainBunkerBox.Corner.X, mainBunkerBox.Corner.Y, mainBunkerBox.Width, mainBunkerBox.Height, self.playerTeam); diff --git a/Data/Browncoats.rte/Activities/RefineryAssault.lua b/Data/Browncoats.rte/Activities/RefineryAssault.lua index 7fe885c6a3..0d8e1b5fba 100644 --- a/Data/Browncoats.rte/Activities/RefineryAssault.lua +++ b/Data/Browncoats.rte/Activities/RefineryAssault.lua @@ -231,37 +231,37 @@ function RefineryAssault:StartActivity(newGame) self.saveTable.buyDoorTables = {}; self.saveTable.buyDoorTables.All = {}; - local area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_LC1"); + local area = SceneMan.Scene:GetArea("BuyDoorArea_LC1"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_LC2"); + area = SceneMan.Scene:GetArea("BuyDoorArea_LC2"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S3_1"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S3_1"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S3_2"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S3_2"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S3_3"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S3_3"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_1"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_1"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_2"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_2"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_3"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_3"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_4"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_4"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_5"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_5"); self:SetupBuyDoorAreaTable(self, area); - area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_S4_6"); + area = SceneMan.Scene:GetArea("BuyDoorArea_S4_6"); self:SetupBuyDoorAreaTable(self, area); self.buyDoorHandler:ReplaceBuyDoorTable(self.saveTable.buyDoorTables.All); diff --git a/Data/Browncoats.rte/Activities/RefineryAssaultFunctions.lua b/Data/Browncoats.rte/Activities/RefineryAssaultFunctions.lua index f706facd9e..36055fd001 100644 --- a/Data/Browncoats.rte/Activities/RefineryAssaultFunctions.lua +++ b/Data/Browncoats.rte/Activities/RefineryAssaultFunctions.lua @@ -266,7 +266,7 @@ function RefineryAssault:HandleMessage(message, object) self.HUDHandler:SetCameraMinimumAndMaximumX(self.humanTeam, 0, SceneMan.SceneWidth + 9999); self.HUDHandler:RemoveAllObjectives(self.humanTeam); - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceArea").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceArea").Center; self.stage4DoorExploSoundContainer = CreateSoundContainer("Yskely Refinery S4 Doors Explo"); self.stage4DoorExploSoundContainer:Play(pos); self.stage4DoorExploDistSoundContainer = CreateSoundContainer("Yskely Refinery S4 Doors Explo Distant"); @@ -310,7 +310,7 @@ function RefineryAssault:HandleMessage(message, object) true, true); - local taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage4"); + local taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage4"); local task = self.tacticsHandler:AddTask("Patrol Stage 4", self.humanTeam, taskArea, "PatrolArea", 10); local task = self.tacticsHandler:AddTask("Patrol Stage 4", self.aiTeam, taskArea, "PatrolArea", 10); @@ -366,7 +366,7 @@ function RefineryAssault:HandleMessage(message, object) self.saveTable.roninPrisoners = self.deliveryCreationHandler:CreateEliteSquad(5, 5); self.saveTable.roninPrisonerLeader = self.saveTable.roninPrisoners[1]; - local area = SceneMan.Scene:GetOptionalArea("RefineryAssault_RoninPrisonersSpawn"); + local area = SceneMan.Scene:GetArea("RefineryAssault_RoninPrisonersSpawn"); for k, actor in pairs(self.saveTable.roninPrisoners) do actor.HFlipped = true; if self.saveTable.roninPrisonerDoorBroken then @@ -720,19 +720,19 @@ function RefineryAssault:HandleMessage(message, object) self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10BossPostDeath", self.saveTable.finalBossPosition, 1, 3000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_FinalCameraPan1").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_FinalCameraPan1").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10FinalPan1", cameraPos, 0.0015, 10000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_FinalCameraPan2").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_FinalCameraPan2").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10FinalPan2", cameraPos, 0.001, 10000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_FinalCameraPan3").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_FinalCameraPan3").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10FinalPan3", cameraPos, 0.001, 10000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_FinalCameraPan4").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_FinalCameraPan4").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10FinalPan4", cameraPos, 0.001, 15000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_FinalCameraPan5").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_FinalCameraPan5").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S10FinalPan5", cameraPos, 0.0015, 999999, true, true, true); end @@ -983,7 +983,7 @@ function RefineryAssault:SendBuyDoorDelivery(team, task, squadType, specificInde -- check if it's in an area this team owns local areaThisIsIn for i = 1, #self.saveTable.buyDoorTables.teamAreas[team] do - local area = SceneMan.Scene:GetOptionalArea("BuyDoorArea_" .. self.saveTable.buyDoorTables.teamAreas[team][i]); + local area = SceneMan.Scene:GetArea("BuyDoorArea_" .. self.saveTable.buyDoorTables.teamAreas[team][i]); if area:IsInside(taskPos) then areaThisIsIn = area; --print("is inside teamowned area: " .. area.Name); @@ -1018,7 +1018,7 @@ function RefineryAssault:SendBuyDoorDelivery(team, task, squadType, specificInde --print("found closest area to task:"); --print(area); -- actually get the Area - areaThisIsIn = SceneMan.Scene:GetOptionalArea("BuyDoorArea_" .. areaThisIsIn); + areaThisIsIn = SceneMan.Scene:GetArea("BuyDoorArea_" .. areaThisIsIn); else --print("team " .. team .. " doesn't have a backup area"); end @@ -1157,16 +1157,16 @@ function RefineryAssault:SetupFirstStage() self.saveTable.introTimer = Timer(); self.saveTable.introLastRocketSpawnTime = 0; - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_IntroCameraPan1").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_IntroCameraPan1").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S1IntroPan1", cameraPos, 1, 500, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_IntroCameraPan2").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_IntroCameraPan2").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S1IntroPan2", cameraPos, 0.01, 6000, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_IntroCameraPan3").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_IntroCameraPan3").Center; self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S1IntroPan3", cameraPos, 0.01, 3500, true, true, true); - local cameraPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_IntroCameraPan4").Center; + local cameraPos = SceneMan.Scene:GetArea("RefineryAssault_IntroCameraPan4").Center; local introEndFunction = function() local activity = ToGameActivity(ActivityMan:GetActivity()); @@ -1189,14 +1189,14 @@ function RefineryAssault:SetupFirstStage() -- Set up stage 1 enemy actors self.tacticsHandler:AddTask("Sentry", self.aiTeam, Vector(0, 0), "Sentry", 10); - local taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage1"); + local taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage1"); self.tacticsHandler:AddTask("Patrol Stage 1", self.aiTeam, taskArea, "PatrolArea", 5); self:SetupStartingActors(); -- Set up the 2 dock squads - taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage1"); + taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage1"); local task = self.tacticsHandler:AddTask("Search And Destroy", self.humanTeam, taskArea, "PatrolArea", 10); local squad = self:SendDockDelivery(self.humanTeam, task, true, "Elite"); @@ -1210,7 +1210,7 @@ function RefineryAssault:SetupFirstStage() -- Set up player squad and dropship local dropShip, squad = self.deliveryCreationHandler:CreateEliteSquadWithCraft(self.humanTeam, false, 5); - local dropShipPos = SceneMan.Scene:GetOptionalArea("RefineryAssault_HumanBrainSpawn").Center; + local dropShipPos = SceneMan.Scene:GetArea("RefineryAssault_HumanBrainSpawn").Center; dropShip.Team = self.humanTeam; dropShip.Pos = dropShipPos; dropShip.AIMode = Actor.AIMODE_SENTRY; @@ -1290,7 +1290,7 @@ function RefineryAssault:MonitorStage1() if self.saveTable.introTimer:IsPastSimMS(self.saveTable.introLastRocketSpawnTime + 100) then self.saveTable.introLastRocketSpawnTime = self.saveTable.introTimer.ElapsedSimTimeMS; local particle = CreateAEmitter("Particle Rocket Launcher", "Base.rte"); - particle.Pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_IntroRocketSpawns").RandomPoint; + particle.Pos = SceneMan.Scene:GetArea("RefineryAssault_IntroRocketSpawns").RandomPoint; particle.Vel = Vector(math.random(-5, 5), -70); particle.RotAngle = math.pi/2; particle.Team = self.humanTeam; @@ -1314,7 +1314,7 @@ function RefineryAssault:MonitorStage1() self.saveTable.stage1InitialDropshipToReturn = nil; end elseif craft:IsInventoryEmpty() then - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_HumanBrainSpawn").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_HumanBrainSpawn").Center; craft:ClearAIWaypoints(); craft:AddAISceneWaypoint(Vector(pos.X - 300, pos.Y)); craft.DeliveryState = ACraft.LAUNCH; @@ -1370,17 +1370,17 @@ function RefineryAssault:MonitorStage1() -- Task setup - local taskPos = SceneMan.Scene:GetOptionalArea("CaptureArea_RefineryLCHackConsole1").Center; + local taskPos = SceneMan.Scene:GetArea("CaptureArea_RefineryLCHackConsole1").Center; self.tacticsHandler:AddTask("Attack Hack Console 1", self.humanTeam, taskPos, "Attack", 10); self.tacticsHandler:AddTask("Defend Hack Console 1", self.aiTeam, taskPos, "Defend", 10); - taskPos = SceneMan.Scene:GetOptionalArea("CaptureArea_RefineryLCHackConsole2").Center; + taskPos = SceneMan.Scene:GetArea("CaptureArea_RefineryLCHackConsole2").Center; self.tacticsHandler:AddTask("Attack Hack Console 2", self.humanTeam, taskPos, "Attack", 10); self.tacticsHandler:AddTask("Defend Hack Console 2", self.aiTeam, taskPos, "Defend", 10); - local taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage2"); + local taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage2"); local task = self.tacticsHandler:AddTask("Patrol Stage 2", self.humanTeam, taskArea, "PatrolArea", 2); local task = self.tacticsHandler:AddTask("Patrol Stage 2", self.aiTeam, taskArea, "PatrolArea", 4); @@ -1399,7 +1399,7 @@ function RefineryAssault:MonitorStage1() if #self.actorSpawnerReturnedActors > 0 then - local taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage1"); + local taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage1"); local task = self.tacticsHandler:AddTask("Counterattack", self.aiTeam, taskArea, "PatrolArea", 10); self.tacticsHandler:AddSquad(self.aiTeam, self.actorSpawnerReturnedActors, task.Name, true); @@ -1427,7 +1427,7 @@ function RefineryAssault:MonitorStage1() false, true); - local objPos = SceneMan.Scene:GetOptionalArea("CaptureArea_RefineryLCHackConsole1").Center; + local objPos = SceneMan.Scene:GetArea("CaptureArea_RefineryLCHackConsole1").Center; self.HUDHandler:AddObjective(self.humanTeam, "S2HackConsole1", @@ -1439,7 +1439,7 @@ function RefineryAssault:MonitorStage1() true, true); - local objPos = SceneMan.Scene:GetOptionalArea("CaptureArea_RefineryLCHackConsole2").Center; + local objPos = SceneMan.Scene:GetArea("CaptureArea_RefineryLCHackConsole2").Center; self.HUDHandler:AddObjective(self.humanTeam, "S2HackConsole2", @@ -1510,7 +1510,7 @@ function RefineryAssault:MonitorStage2() -- note index access, we get a table back self.saveTable.stage3FacilityOperator = self.deliveryCreationHandler:CreateEliteSquad(self.aiTeam, 1, "Heavy")[1]; self.saveTable.stage3FacilityOperator.Head = CreateAttachable("Browncoat Heavy Alt Head B", "Browncoats.rte"); - local area = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3FacilityOperator"); + local area = SceneMan.Scene:GetArea("RefineryAssault_S3FacilityOperator"); local pos = SceneMan:MovePointToGround(area.Center, 50, 3); self.saveTable.stage3FacilityOperator.Pos = pos; @@ -1526,7 +1526,7 @@ function RefineryAssault:MonitorStage2() -- Task stuff - local taskArea = SceneMan.Scene:GetOptionalArea("TacticsPatrolArea_MissionStage3"); + local taskArea = SceneMan.Scene:GetArea("TacticsPatrolArea_MissionStage3"); local task = self.tacticsHandler:AddTask("Patrol Stage 3", self.humanTeam, taskArea, "PatrolArea", 10); local task = self.tacticsHandler:AddTask("Patrol Stage 3", self.aiTeam, taskArea, "PatrolArea", 10); @@ -1572,7 +1572,7 @@ function RefineryAssault:MonitorStage2() true, true); - local objPos = SceneMan.Scene:GetOptionalArea("CaptureArea_RefineryS3DrillOverloadConsole").Center; + local objPos = SceneMan.Scene:GetArea("CaptureArea_RefineryS3DrillOverloadConsole").Center; self.HUDHandler:AddObjective(self.humanTeam, "S3OverloadDrill", @@ -1662,10 +1662,10 @@ function RefineryAssault:MonitorStage3() self.HUDHandler:RemoveObjective(self.humanTeam, "S3OpenDoors"); -- Reveal fog - local box = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceFogRevealArea").FirstBox; + local box = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceFogRevealArea").FirstBox; SceneMan:RevealUnseenBox(box.Corner.X, box.Corner.Y, box.Width, box.Height, self.humanTeam); - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceArea").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceArea").Center; local soundContainer = CreateSoundContainer("Yskely Refinery Blast Door Alarm", "Browncoats.rte"); soundContainer:Play(pos); @@ -1676,7 +1676,7 @@ function RefineryAssault:MonitorStage3() if not self.stage3ScreenShake then self.stage3ScreenShake = true; - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceArea").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceArea").Center; CameraMan:AddScreenShake(10, pos); end @@ -1690,7 +1690,7 @@ function RefineryAssault:MonitorStage3() ToADoor(self.saveTable.stage3Doors[2]):OpenDoor(); if not self.stage3ScreenShake2 then self.stage3ScreenShake2 = true; - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceArea").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceArea").Center; CameraMan:AddScreenShake(10, pos); end end @@ -1701,7 +1701,7 @@ function RefineryAssault:MonitorStage3() ToADoor(self.saveTable.stage4Door[1]):OpenDoor(); if not self.stage3ScreenShake3 then self.stage3ScreenShake3 = true; - local pos = SceneMan.Scene:GetOptionalArea("RefineryAssault_S3DoorSequenceArea").Center; + local pos = SceneMan.Scene:GetArea("RefineryAssault_S3DoorSequenceArea").Center; CameraMan:AddScreenShake(10, pos); end end @@ -1827,7 +1827,7 @@ function RefineryAssault:MonitorStage5() self.stage6SubcommanderDoor:SendMessage("BuyDoor_CustomTableOrder"); -- Reveal fog - local box = SceneMan.Scene:GetOptionalArea("RefineryAssault_S6SubcommanderViewFogRevealArea").FirstBox; + local box = SceneMan.Scene:GetArea("RefineryAssault_S6SubcommanderViewFogRevealArea").FirstBox; SceneMan:RevealUnseenBox(box.Corner.X, box.Corner.Y, box.Width, box.Height, self.humanTeam); self.HUDHandler:QueueCameraPanEvent(self.humanTeam, "S6SubcommanderView", self.stage6SubcommanderDoor.Pos, 0.05, 5000, true); diff --git a/Data/Missions.rte/Activities/DecisionDay.lua b/Data/Missions.rte/Activities/DecisionDay.lua index 03ec5e4d9e..4513d60fb0 100644 --- a/Data/Missions.rte/Activities/DecisionDay.lua +++ b/Data/Missions.rte/Activities/DecisionDay.lua @@ -176,7 +176,7 @@ function DecisionDay:StartActivity(isNewGame) captureArea = scene:GetArea(bunkerRegionName .. " Capture"), captureDisplayArea = scene:GetArea(bunkerRegionName .. " Capture Display"), captureDisplayScreens = {}, - internalReinforcementsArea = scene:HasArea(bunkerRegionName .. " Internal Reinforcements") and scene:GetOptionalArea(bunkerRegionName .. " Internal Reinforcements") or nil, + internalReinforcementsArea = scene:HasArea(bunkerRegionName .. " Internal Reinforcements") and scene:GetArea(bunkerRegionName .. " Internal Reinforcements") or nil, defenderArea = scene:GetArea(bunkerRegionName .. " Defenders"), ownerTeam = self.aiTeam, hasBeenCapturedAtLeastOnceByHumanTeam = false, @@ -185,11 +185,11 @@ function DecisionDay:StartActivity(isNewGame) aiRegionDefenseTimer = Timer(60000 / self.difficultyRatio, 60000 / self.difficultyRatio), aiRegionAttackTimer = Timer(90000 / self.difficultyRatio), aiRecaptureWeight = bunkerRegionRecaptureWeights[bunkerRegionName] or 0, - fauxdanDisplayArea = scene:HasArea(bunkerRegionName .. " Fauxdan Display") and scene:GetOptionalArea(bunkerRegionName .. " Fauxdan Display") or nil, + fauxdanDisplayArea = scene:HasArea(bunkerRegionName .. " Fauxdan Display") and scene:GetArea(bunkerRegionName .. " Fauxdan Display") or nil, fauxdanDisplayScreens = {}, - shieldedArea = scene:HasArea(bunkerRegionName .. " Shield") and scene:GetOptionalArea(bunkerRegionName .. " Shield") or nil, - brainDoor = scene:HasArea(bunkerRegionName .. " Brain Door") and scene:GetOptionalArea(bunkerRegionName .. " Brain Door") or nil, - brain = scene:HasArea(bunkerRegionName .. " Shield") and scene:GetOptionalArea(bunkerRegionName .. " Brain") or nil, + shieldedArea = scene:HasArea(bunkerRegionName .. " Shield") and scene:GetArea(bunkerRegionName .. " Shield") or nil, + brainDoor = scene:HasArea(bunkerRegionName .. " Brain Door") and scene:GetArea(bunkerRegionName .. " Brain Door") or nil, + brain = scene:HasArea(bunkerRegionName .. " Shield") and scene:GetArea(bunkerRegionName .. " Brain") or nil, }; if bunkerRegionName:find("Vault") then self.bunkerRegions[bunkerRegionName].incomeMultiplier = bunkerRegionName:find("Large") and 2 or (bunkerRegionName:find("Medium") and 1.5 or 1); diff --git a/Data/Missions.rte/MissionActivities.ini b/Data/Missions.rte/MissionActivities.ini index 01bad42124..507cf6de90 100644 --- a/Data/Missions.rte/MissionActivities.ini +++ b/Data/Missions.rte/MissionActivities.ini @@ -23,6 +23,10 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 DeployUnitsSwitchEnabled = 0 FogOfWarSwitchEnabled = 0 + AddRequiredArea = Cave Inside A + AddRequiredArea = Cave Inside B + AddRequiredArea = Console + AddRequiredArea = Pitfall AddActivity = GAScripted @@ -45,6 +49,18 @@ AddActivity = GAScripted DefaultDeployUnits = 1 RequireClearPathToOrbitSwitchEnabled = 0 DeployUnitsSwitchEnabled = 0 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ Team 2 + AddRequiredArea = Cave + AddRequiredArea = Inner Cave + AddRequiredArea = Innermost Cave + AddRequiredArea = Outer Zombie Generator + AddRequiredArea = Outer Bomb Maker + AddRequiredArea = Outer Bomb Pickup + AddRequiredArea = Inner Zombie Generator + AddRequiredArea = Inner Bomb Maker + AddRequiredArea = Inner Bomb Pickup + AddRequiredArea = Control Case AddActivity = GAScripted @@ -69,6 +85,13 @@ AddActivity = GAScripted DefaultDeployUnits = 1 RequireClearPathToOrbitSwitchEnabled = 0 DeployUnitsSwitchEnabled = 0 + AddRequiredArea = LZ Team 1 + AddRequiredArea = LZ Team 2 + AddRequiredArea = Enemy Sneak Spawn 2 + AddRequiredArea = Maginot Bunker + AddRequiredArea = Evacuate Trigger + AddRequiredArea = Rescue Trigger + AddRequiredArea = Rescue Area AddActivity = GAScripted @@ -95,6 +118,11 @@ AddActivity = GAScripted RequireClearPathToOrbitSwitchEnabled = 0 FogOfWarSwitchEnabled = 0 DeployUnitsSwitchEnabled = 0 + AddRequiredArea = Initial Dead Bodies + AddRequiredArea = Initial Extra FOW Reveal + AddRequiredArea = Initial Human FOW Area + AddRequiredArea = Initial Human Spawn + AddRequiredArea = Initial DropShip Spawn AddActivity = GAScripted @@ -118,4 +146,7 @@ AddActivity = GAScripted DefaultFogOfWar = 1 DefaultDeployUnits = 1 RequireClearPathToOrbitSwitchEnabled = 0 - DeployUnitsSwitchEnabled = 0 \ No newline at end of file + DeployUnitsSwitchEnabled = 0 + AddRequiredArea = Dummy Base Alarm + AddRequiredArea = Dummy Factory Invasion + AddRequiredArea = Search Area \ No newline at end of file diff --git a/Source/Activities/GAScripted.cpp b/Source/Activities/GAScripted.cpp index 886fb5f048..bcc7d2c7ee 100644 --- a/Source/Activities/GAScripted.cpp +++ b/Source/Activities/GAScripted.cpp @@ -61,9 +61,6 @@ int GAScripted::Create() { return -1; } - // Scan the script file for any mentions/uses of Areas. - CollectRequiredAreas(); - // If the GAScripted has a OnSave() function, we assume it can be saved by default ReloadScripts(); m_AllowsUserSaving = HasSaveFunction(); @@ -100,6 +97,11 @@ int GAScripted::ReadProperty(const std::string_view& propName, Reader& reader) { MatchProperty("AddPieSlice", { m_PieSlicesToAdd.emplace_back(std::unique_ptr(dynamic_cast(g_PresetMan.ReadReflectedPreset(reader)))); }); + MatchProperty("AddRequiredArea", { + std::string requiredArea; + reader >> requiredArea; + m_RequiredAreas.insert(requiredArea); + }); EndPropertyList; } @@ -117,6 +119,10 @@ int GAScripted::Save(Writer& writer) const { writer.NewPropertyWithValue("AddPieSlice", pieSliceToAdd.get()); } + for (const std::string& requiredArea: m_RequiredAreas) { + writer.NewPropertyWithValue("AddRequiredArea", requiredArea); + } + return 0; } @@ -141,7 +147,6 @@ int GAScripted::ReloadScripts() { } int error = 0; - CollectRequiredAreas(); // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { @@ -187,6 +192,16 @@ bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) { return false; } + // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) + RefreshActivityFunctions(); + + // Call the defined function, but only after first checking if it exists + bool conditionMet = true; + int error = RunLuaConditionalTest("IsCompatibleScene", conditionMet, {pScene}, {}, {}); + if (error < 0 || !conditionMet) { + return false; + } + // Check if all Areas required by this are defined in the Scene for (std::set::iterator itr = m_RequiredAreas.begin(); itr != m_RequiredAreas.end(); ++itr) { // If Area is missing, this Scene is not up to par @@ -195,33 +210,7 @@ bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) { } } - // Temporarily store the scene so the Lua state can access it and check for the necessary Areas etc. - g_LuaMan.GetMasterScriptState().SetTempEntity(pScene); - // Cast the test scene it to a Scene object in Lua - if (g_LuaMan.GetMasterScriptState().RunScriptString("TestScene = ToScene(LuaMan.TempEntity);") < 0) { - return false; - } - - // If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function) - if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) { - // Temporarily store this Activity so the Lua state can access it - g_LuaMan.GetMasterScriptState().SetTempEntity(this); - // Define the var that will hold the script file definitions.. - // it's OK if the script fails, then the scene is still deemed compatible - if (g_LuaMan.GetMasterScriptState().RunScriptString(m_LuaClassName + " = ToGameActivity(LuaMan.TempEntity);") < 0) { - return true; - } - // Load and run the file, defining all the scripted functions of this Activity - if (g_LuaMan.GetMasterScriptState().RunScriptFile(m_ScriptPath) < 0) { - return true; - } - } - - // Call the defined function, but only after first checking if it exists - g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".SceneTest then " + m_LuaClassName + ":SceneTest(); end"); - - // If the test left the Scene pointer still set, it means it passed the test - return g_LuaMan.GetMasterScriptState().GlobalIsDefined("TestScene"); + return true; } void GAScripted::HandleCraftEnteringOrbit(ACraft* orbitedCraft) { @@ -378,60 +367,16 @@ int GAScripted::RunLuaFunction(const std::string& functionName, const std::vecto return error; } -void GAScripted::CollectRequiredAreas() { - // Open the script file so we can check it out - std::ifstream scriptFile = std::ifstream(g_PresetMan.GetFullModulePath(m_ScriptPath.c_str())); - if (!scriptFile.good()) { - return; +int GAScripted::RunLuaConditionalTest(const std::string& functionName, bool& returnParam, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + // Call the defined function, but only after first checking if it exists + auto funcItr = m_ScriptFunctions.find(functionName); + if (funcItr == m_ScriptFunctions.end()) { + return 0; } - // Harvest the required Area:s from the file - m_RequiredAreas.clear(); - - bool blockCommented = false; - - while (!scriptFile.eof()) { - // Go through the script file, line by line - char rawLine[512]; - scriptFile.getline(rawLine, 512); - std::string line = rawLine; - std::string::size_type pos = 0; - std::string::size_type endPos = 0; - std::string::size_type commentPos = std::string::npos; + int error = g_LuaMan.GetMasterScriptState().RunScriptConditionalTestFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, returnParam, functionEntityArguments, functionLiteralArguments, functionObjectArguments); - // Check for block comments - if (!blockCommented && (commentPos = line.find("--[[", 0)) != std::string::npos) { - blockCommented = true; - } - - // Find the end of the block comment - if (blockCommented) { - if ((commentPos = line.find("]]", commentPos == std::string::npos ? 0 : commentPos)) != std::string::npos) { - blockCommented = false; - pos = commentPos; - } - } - - // Process the line as usual - if (!blockCommented) { - // See if this line is commented out anywhere - commentPos = line.find("--", 0); - do { - // Find the beginning of a mentioned Area name - pos = line.find(":GetArea(\"", pos); - if (pos != std::string::npos && pos < commentPos) { - // Move position forward to the actual Area name - pos += 10; - // Find the end of the Area name - endPos = line.find_first_of('"', pos); - // Copy it out and put into the list - if (endPos != std::string::npos) { - m_RequiredAreas.insert(line.substr(pos, endPos - pos)); - } - } - } while (pos != std::string::npos && pos < commentPos); - } - } + return error; } void GAScripted::AddPieSlicesToActiveActorPieMenus() { diff --git a/Source/Activities/GAScripted.h b/Source/Activities/GAScripted.h index 4a106409a7..9c749dad6e 100644 --- a/Source/Activities/GAScripted.h +++ b/Source/Activities/GAScripted.h @@ -23,7 +23,7 @@ namespace RTE { /// Public member variable, method and friend function declarations public: - ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "OnGlobalMessage"); + ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "IsCompatibleScene", "OnGlobalMessage"); // Concrete allocation and cloning definitions EntityAllocation(GAScripted); @@ -132,13 +132,10 @@ namespace RTE { void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override; int RunLuaFunction(const std::string& functionName, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); + int RunLuaConditionalTest(const std::string& functionName, bool& returnParam, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); /// Protected member variable and method declarations protected: - /// Goes through the script file and checks for any mentions and uses of - /// Area:s that are required for this Activity to run in a Scene. - void CollectRequiredAreas(); - /// Does nothing - we do this in script! Just overrides the base behaviour. void InitAIs() override{}; diff --git a/Source/Entities/HDFirearm.cpp b/Source/Entities/HDFirearm.cpp index 9d84428a76..28e3a6a4e7 100644 --- a/Source/Entities/HDFirearm.cpp +++ b/Source/Entities/HDFirearm.cpp @@ -938,7 +938,7 @@ void HDFirearm::Update() { m_FireSound->Play(m_Pos); } if (m_FireEchoSound) { - Scene::Area* noEchoArea = g_SceneMan.GetScene()->GetOptionalArea("IndoorArea"); + Scene::Area* noEchoArea = g_SceneMan.GetScene()->GetArea("IndoorArea"); if (noEchoArea == nullptr || !noEchoArea->IsInside(m_Pos)) { m_FireEchoSound->Play(m_Pos); } diff --git a/Source/Entities/Scene.h b/Source/Entities/Scene.h index d8ccb3ff54..8942c8b1b6 100644 --- a/Source/Entities/Scene.h +++ b/Source/Entities/Scene.h @@ -514,12 +514,6 @@ namespace RTE { /// @return A pointer to the Area asked for, or nullptr if no Area of that name was found. Area* GetArea(const std::string& areaName) { return GetArea(areaName, true); } - /// Gets a specified Area identified by name, showing a Lua warning if it's not found. Ownership is NOT transferred! - /// Using this function will not add the area to the list of required areas which Scenario GUI uses to show compatible areas. - /// @param areaName The name of the Area to try to get. - /// @return A pointer to the Area asked for, or nullptr if no Area of that name was found. - Area* GetOptionalArea(const std::string& areaName) { return GetArea(areaName, false); } - void AddNavigatableArea(const std::string& areaName) { m_NavigatableAreas.push_back(areaName); m_NavigatableAreasUpToDate = false; diff --git a/Source/Lua/LuaBindingsEntities.cpp b/Source/Lua/LuaBindingsEntities.cpp index 2007612c89..0b52c270b9 100644 --- a/Source/Lua/LuaBindingsEntities.cpp +++ b/Source/Lua/LuaBindingsEntities.cpp @@ -1176,7 +1176,6 @@ LuaBindingRegisterFunctionDefinitionForType(EntityLuaBindings, Scene) { .def("SetArea", &Scene::SetArea) .def("HasArea", &Scene::HasArea) .def("GetArea", (Scene::Area * (Scene::*)(const std::string& areaName)) & Scene::GetArea) - .def("GetOptionalArea", &Scene::GetOptionalArea) .def("WithinArea", &Scene::WithinArea) .def("AddNavigatableArea", &Scene::AddNavigatableArea) .def("ClearNavigatableAreas", &Scene::ClearNavigatableAreas) diff --git a/Source/Managers/LuaMan.cpp b/Source/Managers/LuaMan.cpp index bb2616a760..8ecfb38ed2 100644 --- a/Source/Managers/LuaMan.cpp +++ b/Source/Managers/LuaMan.cpp @@ -625,6 +625,82 @@ int LuaStateWrapper::RunScriptFunctionObject(const LuabindObjectWrapper* functio return status; } +int LuaStateWrapper::RunScriptConditionalTestFunctionObject(const LuabindObjectWrapper* functionObject, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, bool& returnParam, const std::vector& functionEntityArguments, const std::vector& functionLiteralArguments, const std::vector& functionObjectArguments) { + int status = 0; + + std::lock_guard lock(m_Mutex); + s_currentLuaState = this; + m_CurrentlyRunningScriptPath = functionObject->GetFilePath(); + + lua_pushcfunction(m_State, &AddFileAndLineToError); + functionObject->GetLuabindObject()->push(m_State); + + int argumentCount = functionEntityArguments.size() + functionLiteralArguments.size() + functionObjectArguments.size(); + if (!selfGlobalTableName.empty() && TableEntryIsDefined(selfGlobalTableName, selfGlobalTableKey)) { + lua_getglobal(m_State, selfGlobalTableName.c_str()); + lua_getfield(m_State, -1, selfGlobalTableKey.c_str()); + lua_remove(m_State, -2); + argumentCount++; + } + + for (const Entity* functionEntityArgument: functionEntityArguments) { + std::unique_ptr downCastEntityAsLuabindObjectWrapper(LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions.at(functionEntityArgument->GetClassName())(const_cast(functionEntityArgument), m_State)); + downCastEntityAsLuabindObjectWrapper->GetLuabindObject()->push(m_State); + } + + for (const std::string_view& functionLiteralArgument: functionLiteralArguments) { + char* stringToDoubleConversionFailed = nullptr; + if (functionLiteralArgument == "nil") { + lua_pushnil(m_State); + } else if (functionLiteralArgument == "true" || functionLiteralArgument == "false") { + lua_pushboolean(m_State, functionLiteralArgument == "true" ? 1 : 0); + } else if (double argumentAsNumber = std::strtod(functionLiteralArgument.data(), &stringToDoubleConversionFailed); !*stringToDoubleConversionFailed) { + lua_pushnumber(m_State, argumentAsNumber); + } else { + lua_pushlstring(m_State, functionLiteralArgument.data(), functionLiteralArgument.size()); + } + } + + for (const LuabindObjectWrapper* functionObjectArgument: functionObjectArguments) { + if (functionObjectArgument->GetLuabindObject()->interpreter() != m_State) { + LuabindObjectWrapper copy = functionObjectArgument->GetCopyForState(*m_State); + copy.GetLuabindObject()->push(m_State); + } else { + functionObjectArgument->GetLuabindObject()->push(m_State); + } + } + + const std::string& path = functionObject->GetFilePath(); + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + { + ZoneScoped; + ZoneName(path.c_str(), path.length()); + + if (lua_pcall(m_State, argumentCount, 1, -argumentCount - 2) > 0) { + m_LastError = lua_tostring(m_State, -1); + lua_pop(m_State, 1); + g_ConsoleMan.PrintString("ERROR: " + m_LastError); + ClearErrors(); + status = -1; + } else { + returnParam = 1 == lua_toboolean(m_State, -1); + lua_pop(m_State, 1); + } + } + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + + // only track time in non-MT scripts, for now + if (&g_LuaMan.GetMasterScriptState() == this) { + m_ScriptTimings[path].m_Time += std::chrono::duration_cast(end - begin).count(); + m_ScriptTimings[path].m_CallCount++; + } + + lua_pop(m_State, 1); + + m_CurrentlyRunningScriptPath = ""; + return status; +} + int LuaStateWrapper::RunScriptFile(const std::string& filePath, bool consoleErrors, bool doInSandboxedEnvironment) { const std::string fullScriptPath = g_PresetMan.GetFullModulePath(filePath); if (fullScriptPath.empty()) { diff --git a/Source/Managers/LuaMan.h b/Source/Managers/LuaMan.h index 6b0997678f..5a11b4b1fc 100644 --- a/Source/Managers/LuaMan.h +++ b/Source/Managers/LuaMan.h @@ -112,6 +112,16 @@ namespace RTE { /// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. int RunScriptFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); + + /// Runs the given Lua function object. The first argument to the function will always be the self object. + /// If either argument list has entries, they will be passed into the function in order, with entity arguments first. + /// @param functionObjectWrapper The LuabindObjectWrapper containing the Lua function to be run. + /// @param selfGlobalTableName The name of the global Lua table that gives access to the self object. + /// @param selfGlobalTableKey The key for this object in the respective global Lua table. + /// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty. + /// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty. + /// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal. + int RunScriptConditionalTestFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, bool& returnParam, const std::vector& functionEntityArguments = std::vector(), const std::vector& functionLiteralArguments = std::vector(), const std::vector& functionObjectArguments = std::vector()); /// Opens and loads a file containing a script and runs it on the state. /// @param filePath The path to the file to load and run. diff --git a/Source/Managers/SceneMan.cpp b/Source/Managers/SceneMan.cpp index bcae4076e3..c034dcc5fd 100644 --- a/Source/Managers/SceneMan.cpp +++ b/Source/Managers/SceneMan.cpp @@ -2240,7 +2240,7 @@ bool SceneMan::OverAltitude(const Vector& point, int threshold, int accuracy) { bool SceneMan::IsPointInNoGravArea(const Vector& point) const { // Todo, instead of a nograv area maybe best to tag certain areas as NoGrav. As otherwise it's tricky to keep track of when things are removed if (m_pCurrentScene) { - Scene::Area* noGravArea = m_pCurrentScene->GetOptionalArea("NoGravityArea"); + Scene::Area* noGravArea = m_pCurrentScene->GetArea("NoGravityArea"); if (noGravArea && noGravArea->IsInside(point)) { return true; }