Download Lua/Tactical/Deployment.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 15.2 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/Lua/Tactical/Deployment.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/Lua/Tactical/Deployment.lua
-
curl -L -o Deployment.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/Lua/Tactical/Deployment.lua
15.2 kB
| GameVar("gv_Deployment", false) | |
| GameVar("gv_DeploymentStarted", false) | |
| GameVar("gv_DeploymentDir", false) | |
| MapVar("gv_Redeployment", false) | |
| MapVar("RedeploymentThread", false) | |
| DefineClass.DeploymentMarker = { | |
| __parents = {"GridMarker"}, | |
| properties = { | |
| { category = "Grid Marker", id = "Type", name = "Type", editor = "dropdownlist", items = {"DeployArea"}, default = "DeployArea", no_edit = true }, | |
| { category = "Marker", id = "Reachable", name = "Reachable only", editor = "bool", default = false, help = "Area of marker includes only tiles reachable from marker position, not the entire rectangle"}, | |
| { category = "Marker", id = "GroundVisuals", name = "Ground Visuals", editor = "bool", default = true, help = "Show ground mesh on the marker area"}, | |
| { category = "Trigger Logic", id = "Trigger", name = "Trigger", editor = "dropdownlist", items = { "always", }, default = "always", no_edit = true}, | |
| { category = "Trigger Logic", id = "TriggerEffects", name = "Effects", editor = "nested_list", base_class = "Effect", default = false, no_edit = true}, | |
| { category = "Deployment", id = "AlternateEntrance", name = "Alternate Entrance", editor = "bool", default = false}, | |
| }, | |
| } | |
| function DeploymentMarker:Init() | |
| self:UpdateVisuals(self.Type, true) | |
| end | |
| function DeploymentMarker:TriggerThreadProc() | |
| end | |
| function DeploymentMarker:IsAreaVisible() | |
| return IsEditorActive() or gv_DeploymentStarted and self:IsMarkerEnabled() | |
| end | |
| local deploy_types = {"Entrance", "Defender", "DefenderPriority", "DeployArea"} | |
| function IsDeployMarker(marker) | |
| return not not table.find(deploy_types, marker.Type) | |
| end | |
| function GetAvailableEntranceMarkers(arrival_dir) | |
| -- Entrance markers are always enabled as mercs can enter from there. | |
| -- There are quest cases in which we might want to disable them though, which is denoted by gv_Deployment | |
| local markers | |
| if gv_Deployment ~= "custom" then | |
| markers = MapGetMarkers("Entrance", g_GoingAboveground and "Underground" or arrival_dir, function(marker) | |
| return marker:IsMarkerEnabled() | |
| end) | |
| else | |
| markers = {} | |
| end | |
| if not g_GoingAboveground then | |
| markers = markers or {} | |
| -- Add alt entrance deployment markers. | |
| local additionalEntrances = MapGetMarkers("DeployArea", arrival_dir, function(marker) | |
| return marker:IsMarkerEnabled() | |
| end) | |
| table.iappend(markers, additionalEntrances) | |
| MapForEach("map", "DeploymentMarker", function (marker, markers) | |
| if not marker.AlternateEntrance and marker:IsMarkerEnabled() then | |
| markers[#markers + 1] = marker | |
| end | |
| end, markers) | |
| end | |
| return markers | |
| end | |
| function GetAvailableDeploymentMarkers(some_unit) | |
| local markers = {} | |
| some_unit = some_unit or SelectedObj | |
| if gv_Deployment == "defend" then | |
| markers = MapGetMarkers("Defender", false, function(m) return m:IsMarkerEnabled() end) | |
| local player_side = NetPlayerSide() | |
| local non_blocked_markers = {} | |
| for _, marker in ipairs(markers) do | |
| local area = marker:GetAreaBox() | |
| local blocked = false | |
| for _, unit in ipairs(g_Units) do | |
| if SideIsEnemy(player_side, unit.team.side) and not unit:IsDead() and area:Point2DInside(unit) then | |
| blocked = true | |
| break | |
| end | |
| end | |
| if not blocked then | |
| table.insert(non_blocked_markers, marker) | |
| end | |
| end | |
| return #non_blocked_markers > 0 and non_blocked_markers or {markers[1]} | |
| elseif some_unit then | |
| markers = GetAvailableEntranceMarkers(some_unit.arrival_dir) | |
| end | |
| return markers | |
| end | |
| function GetEnemyDeploymentMarkers() | |
| local markers = {} | |
| local _, enemy_squads = GetSquadsInSector(gv_CurrentSectorId) | |
| for _, squad in ipairs(enemy_squads) do | |
| local dir = squad.units and squad.units[1] and gv_UnitData[squad.units[1]] and gv_UnitData[squad.units[1]].arrival_dir | |
| if dir then | |
| local available = GetAvailableEntranceMarkers(dir) | |
| for _, marker in ipairs(available) do | |
| table.insert_unique(markers, marker) | |
| end | |
| end | |
| end | |
| return markers | |
| end | |
| function UpdateAvailableDeploymentMarkers() | |
| if gv_DeploymentStarted then | |
| local enemy_markers = GetEnemyDeploymentMarkers() | |
| local available = GetAvailableDeploymentMarkers() | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if IsDeployMarker(marker) then | |
| if not table.find_value(available, marker) then | |
| marker:HideArea() | |
| DeleteBadgesFromTargetOfPreset("DeploymentAreaBadge", marker) | |
| else | |
| if not TargetHasBadgeOfPreset("DeploymentAreaBadge", marker) then | |
| CreateBadgeFromPreset("DeploymentAreaBadge", marker) | |
| end | |
| if not marker:IsAreaShown() then | |
| marker:ShowArea() | |
| end | |
| end | |
| if gv_Deployment == "defend" then | |
| if not table.find_value(enemy_markers, marker) then | |
| DeleteBadgesFromTargetOfPreset("EnemyDeploymentAreaBadge", marker) | |
| else | |
| if not TargetHasBadgeOfPreset("EnemyDeploymentAreaBadge", marker) then | |
| CreateBadgeFromPreset("EnemyDeploymentAreaBadge", marker) | |
| end | |
| end | |
| end | |
| end | |
| end) | |
| else | |
| UpdateEntranceAreasVisibility() | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if marker.Type == "DeployArea" then | |
| marker:HideArea() | |
| end | |
| DeleteBadgesFromTargetOfPreset("DeploymentAreaBadge", marker) | |
| DeleteBadgesFromTargetOfPreset("EnemyDeploymentAreaBadge", marker) | |
| end) | |
| end | |
| end | |
| function IsFirstSquadDeployment(squad_id) -- check all squads if not squad_id is provided | |
| local team = GetCurrentTeam() | |
| if team then | |
| for i, u in ipairs(team.units) do | |
| if (not squad_id or u.Squad == squad_id) and u:IsLocalPlayerControlled() and IsUnitDeployed(u) then | |
| return false | |
| end | |
| end | |
| end | |
| return true | |
| end | |
| function IsDeploymentReady() | |
| local team = GetCurrentTeam() | |
| if team then | |
| for i, u in ipairs(team.units) do | |
| if not IsUnitDeployed(u) then | |
| return false | |
| end | |
| end | |
| end | |
| return true | |
| end | |
| function GetCurrentDeploymentSquadUnits(local_player_controlled_only) | |
| local units = {} | |
| local currentSquad = gv_Squads[g_CurrentSquad] | |
| for i, session_id in ipairs(currentSquad.units) do | |
| local u = g_Units[session_id] | |
| if not local_player_controlled_only or u:IsLocalPlayerControlled() then | |
| units[#units + 1] = u | |
| end | |
| end | |
| return units | |
| end | |
| if FirstLoad then | |
| DeployButtonVisible = true | |
| end | |
| function HideDeployButton() | |
| DeployButtonVisible = false | |
| end | |
| function ShowDeployButton() | |
| DeployButtonVisible = true | |
| end | |
| function ShouldHideDeployButton() | |
| return not DeployButtonVisible | |
| end | |
| function ShowUnitsOnDeployment(bShow, bLclPlayer) | |
| if bShow then | |
| local igi = GetInGameInterfaceModeDlg() | |
| if not IsKindOf(igi, "IModeDeployment") or not igi.units_deployed then | |
| igi = false | |
| end | |
| for _, t in ipairs(g_Teams) do | |
| if t.side == "player1" or t.side == "player2" then -- show only player units, enemies will be handled in exploration VisibilityThread | |
| for i, unit in ipairs(t.units) do | |
| if bLclPlayer == unit:IsLocalPlayerControlled() then | |
| unit:SetVisible(true) | |
| -- Mark unit as deployed. It should be in a valid deploy position due to LocalDeployUnitsOnMarker in StartDeployment | |
| if igi then | |
| igi.units_deployed[unit] = true | |
| igi.cursor_voxel = false -- Force recalc | |
| end | |
| end | |
| end | |
| end | |
| end | |
| else | |
| for _, t in ipairs(g_Teams) do | |
| if t.side == "player1" or t.side == "player2" or t.side == "enemy1" or t.side == "enemy2" then | |
| for i, unit in ipairs(t.units) do | |
| unit:SetVisible(false) | |
| end | |
| end | |
| end | |
| end | |
| ObjModified("DeployUpdated") | |
| ObjModified("UpdateTacticalNotification") | |
| end | |
| function SkipDeployment(mode) | |
| if gv_Deployment then | |
| return false | |
| elseif not mode then | |
| return true | |
| end | |
| if g_TestCombat and g_TestCombat.skip_deployment then | |
| return true | |
| end | |
| -- temp comment out, check SetupDeployOrExploreUI | |
| --[[if mode == "attack" or mode == "defend" then | |
| return false | |
| end]] | |
| local currentSector = gv_Sectors[gv_CurrentSectorId] | |
| local conflict = IsConflictMode(gv_CurrentSectorId) | |
| if not currentSector.enabled_auto_deploy or not conflict then | |
| return true | |
| end | |
| if g_GoingAboveground then | |
| return true | |
| end | |
| return false | |
| end | |
| function SetDeploymentMode(deploy) | |
| local defend_mode = deploy == "defend" or not deploy and gv_Deployment == "defend" | |
| gv_Deployment = deploy | |
| deploy = not not deploy | |
| local update_visuals = {} | |
| if defend_mode then | |
| if deploy then | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if (marker.Type == "Defender" or marker.Type == "DefenderPriority") and marker:IsMarkerEnabled() then | |
| table.insert_unique(g_InteractableAreaMarkers, marker) | |
| update_visuals[#update_visuals + 1] = marker | |
| end | |
| end) | |
| else | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if (marker.Type == "Defender" or marker.Type == "DefenderPriority") and marker:IsMarkerEnabled() then | |
| marker:RemoveFloatTxt() | |
| table.remove_value(g_InteractableAreaMarkers, marker) | |
| update_visuals[#update_visuals + 1] = marker | |
| end | |
| end) | |
| end | |
| else | |
| update_visuals = MapGetMarkers("Entrance", g_GoingAboveground and "Underground" or nil) | |
| table.iappend(update_visuals, MapGetMarkers("DeployArea")) | |
| if deploy then | |
| if not g_GoingAboveground then | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if marker:IsKindOf("DeploymentMarker") then | |
| table.insert_unique(g_InteractableAreaMarkers, marker) | |
| end | |
| end) | |
| end | |
| else | |
| if not g_GoingAboveground then | |
| MapForEachMarker("GridMarker", nil, function(marker) | |
| if marker:IsKindOf("DeploymentMarker") then | |
| marker:RemoveFloatTxt() | |
| table.remove_value(g_InteractableAreaMarkers, marker) | |
| end | |
| end) | |
| end | |
| end | |
| end | |
| for _, marker in ipairs(update_visuals) do | |
| marker.Reachable = true--not deploy | |
| if marker.area_ground_mesh then | |
| marker.area_ground_mesh:UpdateState() | |
| end | |
| marker:UpdateVisuals(deploy and "DeployArea" or marker.Type, "force") | |
| marker:RecalcAreaPositions() | |
| if not marker:IsAreaVisible() then | |
| marker:HideArea() | |
| end | |
| end | |
| if not deploy then | |
| UpdateAvailableDeploymentMarkers() | |
| HideTacticalNotification("deployMode") | |
| Msg("DeploymentModeDone") | |
| end | |
| Msg("DeploymentModeSet", deploy) | |
| end | |
| function TFormat.DeployModeNotif(context_obj) | |
| local non_deployed = 0 | |
| local non_deployed_lcl_player = 0 | |
| local deployed = 0 | |
| local team = GetCurrentTeam() | |
| local totalUnits = 0 | |
| if team then | |
| for i, u in ipairs(team.units) do | |
| if not IsUnitDeployed(u) then | |
| non_deployed = non_deployed + 1 | |
| if u:IsLocalPlayerControlled() then | |
| non_deployed_lcl_player = non_deployed_lcl_player + 1 | |
| end | |
| else | |
| deployed = deployed + 1 | |
| end | |
| end | |
| totalUnits = #team.units | |
| end | |
| local sector = gv_Sectors[gv_CurrentSectorId] | |
| if non_deployed_lcl_player <= 0 and non_deployed > 0 then | |
| local other_player_info = GetOtherNetPlayerInfo() | |
| return T{616675804493, "<other_player> Deploying", other_player = Untranslated(other_player_info and other_player_info.name or "N/A")} | |
| else | |
| return T{673244787391, "Deploy Merc(s) (<deployed>/<total>)", deployed = deployed, total = totalUnits} | |
| end | |
| end | |
| function TFormat.IntelForSector(context_obj) | |
| local sector = gv_Sectors[gv_CurrentSectorId] | |
| if sector and sector.Intel and not sector.intel_discovered then | |
| return T(244800584080, "No Intel for this sector") | |
| end | |
| return false | |
| end | |
| function GetDeploymentAreaRollover(marker) | |
| if marker.DeployRolloverText ~= "" then | |
| return marker.DeployRolloverText | |
| end | |
| if marker.Type == "Entrance" then | |
| if marker:IsInGroup("North") then | |
| return T(147747736813, "North Deployment Zone") | |
| elseif marker:IsInGroup("South") then | |
| return T(565574703512, "South Deployment Zone") | |
| elseif marker:IsInGroup("East") then | |
| return T(189571269539, "East Deployment Zone") | |
| elseif marker:IsInGroup("West") then | |
| return T(998300938139, "West Deployment Zone") | |
| end | |
| end | |
| return T(419061570457, "Deployment Area") | |
| end | |
| OnMsg.CustomInteractableEffectsDone = UpdateAvailableDeploymentMarkers | |
| function IsUnitSeenByAnyDeploymentMarker(unit, markers) | |
| markers = markers or GetAvailableDeploymentMarkers() | |
| -- If the unit can see the marker, we consider it seeing back. | |
| -- This will cause weather effects and such to apply. | |
| -- The code below is a circle x rectangle collision. | |
| local unitSightRadius = unit:GetSightRadius() | |
| local ux, uy = unit:GetPosXYZ() | |
| local half_slabsize = const.SlabSizeX / 2 | |
| for i, m in ipairs(markers) do | |
| local mx, my = m:GetPosXYZ() | |
| local distX = abs(ux - mx) | |
| local markerWidth = m.AreaWidth * half_slabsize | |
| local dx = distX - markerWidth | |
| if dx <= unitSightRadius then | |
| local distY = abs(uy - my) | |
| local markerHeight = m.AreaHeight * half_slabsize | |
| local dy = distY - markerHeight | |
| if dy <= unitSightRadius then | |
| if distX <= markerWidth / 2 or distY <= markerHeight / 2 then | |
| return true | |
| end | |
| -- Pythagoras | |
| if dx * dx + dy * dy <= unitSightRadius * unitSightRadius then | |
| return true | |
| end | |
| end | |
| end | |
| end | |
| return false | |
| end | |
| function IsStuckedMercPos(unit, pos, pfclass, destinations) | |
| if not pfclass then | |
| pfclass = CalcPFClass("player1") | |
| end | |
| if not destinations then | |
| destinations = {} | |
| local markers = GetAvailableEntranceMarkers(unit.arrival_dir) | |
| for i, marker in ipairs(markers) do | |
| local pos = GetPassSlab(marker) | |
| if pos then | |
| table.insert(destinations, pos) | |
| end | |
| end | |
| end | |
| if #destinations == 0 then | |
| return false | |
| end | |
| local has_path, closest_pos = pf.HasPosPath(pos, destinations, pfclass) | |
| if has_path and table.find(destinations, closest_pos) then | |
| return false | |
| end | |
| return true | |
| end | |
| function HasStuckedMercs() | |
| local destinations, dummy | |
| local pfclass = CalcPFClass("player1") | |
| for _, t in ipairs(g_Teams) do | |
| if t.side == "player1" or t.side == "player2" then | |
| for i, unit in ipairs(t.units) do | |
| if unit:IsLocalPlayerControlled() and unit:IsValidPos() and not unit:IsDead() then | |
| if not destinations then | |
| local markers = GetAvailableEntranceMarkers(unit.arrival_dir) | |
| if not markers then | |
| return | |
| end | |
| destinations = {} | |
| for i, marker in ipairs(markers) do | |
| local pos = GetPassSlab(marker) | |
| if pos then | |
| table.insert(destinations, pos) | |
| end | |
| end | |
| end | |
| if #destinations == 0 then | |
| return | |
| end | |
| -- invalidate the path | |
| local start_pos = unit.traverse_tunnel and unit.traverse_tunnel:GetExit() or GetPassSlab(unit) or unit:GetPos() | |
| local pfflags = const.pfmImpassableSource | |
| local has_path, closest_pos = pf.HasPosPath(start_pos, destinations, pfclass, 0, 0, nil, 0, nil, pfflags) | |
| if not has_path or not table.find(destinations, closest_pos) then | |
| return true | |
| end | |
| end | |
| end | |
| end | |
| end | |
| DoneObject(dummy) | |
| return false | |
| end | |
| function RedeploymentCheck() | |
| local redeploy = false | |
| if mapdata.GameLogic and Game and not g_Combat then | |
| if HasStuckedMercs() then | |
| redeploy = true | |
| end | |
| end | |
| gv_Redeployment = redeploy | |
| ObjModified("gv_Redeployment") | |
| end | |
| function RedeploymentCheckDelayed() | |
| if not mapdata.GameLogic or not Game then | |
| return | |
| elseif g_Combat then | |
| return | |
| elseif IsValidThread(RedeploymentThread) then | |
| return | |
| elseif GameState.disable_redeploy_check then | |
| return | |
| end | |
| RedeploymentThread = CreateGameTimeThread(function() | |
| Sleep(2000) | |
| RedeploymentCheck() | |
| end) | |
| end | |
| OnMsg.OnPassabilityChanged = RedeploymentCheckDelayed | |
| OnMsg.CombatEnd = RedeploymentCheckDelayed | |