File size: 9,497 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
if FirstLoad then
g_Units = {}
g_Teams = {}
g_CurrentTeam = false
g_CurrentSquad = false
end
function ResetZuluStateGlobals()
local igi = GetInGameInterface()
if igi then igi:Close() end
g_Units = {}
g_Teams = {}
g_CurrentTeam = false
g_CurrentSquad = false
Msg("CurrentSquadChanged")
end
OnMsg.ChangeMap = ResetZuluStateGlobals
OnMsg.NewGame = ResetZuluStateGlobals
OnMsg.NewGameSessionStart = ResetZuluStateGlobals
if FirstLoad then
SideDefs = false
Sides = false
end
DefineClass.CampaignSide = {
__parents = { "PropertyObject", },
properties = {
{ id = "Id", editor = "text", default = false, },
{ id = "DisplayName", name = "Display Name", editor = "text", default = false, translate = true, },
{ id = "Player", editor = "bool", default = false, },
{ id = "Enemy", editor = "bool", default = false, },
},
}
function OnMsg.ClassesBuilt()
SideDefs = {
CampaignSide:new{ Id = "neutral", DisplayName = T(521973101724, "Neutral") },
CampaignSide:new{ Id = "player1", DisplayName = T(222892508302, "Player 1"), Player = true },
CampaignSide:new{ Id = "player2", DisplayName = T(942355779355, "Player 2"), Player = true },
CampaignSide:new{ Id = "enemy1", DisplayName = T(692913892455, "Enemy 1"), Enemy = true },
CampaignSide:new{ Id = "enemy2", DisplayName = T(456135028453, "Enemy 2"), Enemy = true },
CampaignSide:new{ Id = "enemyNeutral", DisplayName = T(607169506860, "Enemy Neutral"), },
CampaignSide:new{ Id = "ally", DisplayName = T(346100175449, "Ally"), },
}
Sides = table.map(SideDefs, "Id")
end
function SetupDummyTeams()
if not g_Teams then g_Teams = {} end
local side_to_team = {}
for _, side in ipairs(SideDefs) do
local team = table.find_value(g_Teams, "side", side.Id)
if not team then
team = CombatTeam:new {
side = side.Id,
control = (side.Id == "player1" or side.Id == "player2") and "UI" or "AI",
team_color = (side.Id == "player1" or side.Id == "player2") and RGB(0, 0, 200) or RGB(200, 0, 0),
}
g_Teams[#g_Teams+1] = team
end
team.units = {}
side_to_team[team.side] = team
end
return side_to_team
end
local function filter_unit(u)
return IsValid(u) and (not u.team or #u.team.units == 0 or not u.team:IsDefeated())
end
function SetupTeamsFromMap(reset_teams)
local units = MapGet("map", "Unit", filter_unit) or {}
local detached_units = MapGet("detached", "Unit", filter_unit) or {}
g_Units = table.union(units, detached_units)
-- Create a team for all campaign sides - you don't know who's gonna show up :)
local side_to_team = SetupDummyTeams()
SuppressTeamUpdate = true
for _, unit in ipairs(units) do
if CheckUniqueSessionId(unit) then
g_Units[unit.session_id] = unit
end
local side = unit:GetSide(reset_teams)
local team = side_to_team[side]
table.insert_unique(team.units, unit)
unit:SetTeam(team)
end
for _, unit in ipairs(g_Units) do
if not unit.team then
unit:SetTeam(side_to_team.neutral)
end
end
-- for player teams only: check for wounded units and start on -1 morale if there are any
for _, team in ipairs(g_Teams) do
if team.player_team then
local wounded
for _, unit in ipairs(team.units) do
wounded = wounded or unit:HasStatusEffect("Wounded")
end
if wounded then
team.morale = -1
end
end
end
-- Is this ever used? Seems like no.
for i, u in ipairs(g_Units) do
assert(not u.Group)
end
SuppressTeamUpdate = false
Msg("TeamsUpdated")
end
function SendUnitToTeam(unit, team)
local hasTeam = not not unit.team
assert(team)
if hasTeam then
table.remove_value(unit.team.units, unit)
end
table.insert(team.units, unit)
unit:SetTeam(team)
AddToGlobalUnits(unit)
Msg("UnitSideChanged", unit, team)
end
function OnMsg.ChangeMapDone(map)
if map ~= "" and mapdata.GameLogic then
g_Units = MapGet("map", "Unit") or {}
end
end
function OnMsg.CloseSatelliteView()
EnsureCurrentSquad()
ObjModified("hud_squads")
local team = GetCurrentTeam()
if team then
for i, u in ipairs(team.units or empty_table) do
ObjModified(u)
end
end
end
-- 1. Ensures that there is a currently selected unit
-- 2. Ensures that the currently selected squad is correct (squad of selected units)
-- Cases in which there might not be a selected unit:
-- 1. Selected units left the sector
-- 2. Squad was destroyed
function EnsureCurrentSquad()
if #(Selection or "") == 0 then
local squadsOnMap, team = GetSquadsOnMap()
local selectedSquadIdx = table.find(squadsOnMap, g_CurrentSquad)
if not selectedSquadIdx and team then
ResetCurrentSquad(team)
selectedSquadIdx = table.find(squadsOnMap, g_CurrentSquad)
end
if selectedSquadIdx then -- first try to select a unit from the current squad
for _, unit in ipairs(g_Units) do
if unit.Squad == g_CurrentSquad and not unit:IsDead() and not unit:IsDowned() and unit:IsLocalPlayerControlled() then
SuppressNextSelectionChangeVR = true
DelayedCall(0, SelectObj, unit) -- this will trigger selection logic which will bring us back here
return
end
end
end
else
-- check if current selection is fine first (only unit selection changed or w/e)
local allFromSelectedSquad = true
for i, u in ipairs(Selection) do
if u.Squad ~= g_CurrentSquad then
allFromSelectedSquad = false
break
end
end
if allFromSelectedSquad then return end
-- Set g_CurrentSquad to be the squad with the most units selected.
local unitsPerSquad = {}
for i, u in ipairs(Selection) do
local squad = u:GetSatelliteSquad()
if squad then
unitsPerSquad[squad.UniqueId] = (unitsPerSquad[squad.UniqueId] or 0) + 1
end
end
local maxCount, maxCountId = 0, 0
for sqId, unitCount in pairs(unitsPerSquad) do
if unitCount > maxCount then
maxCount = unitCount
maxCountId = sqId
end
end
if g_CurrentSquad == maxCountId then return end
g_CurrentSquad = maxCountId
Msg("CurrentSquadChanged")
end
end
function ResetCurrentSquad(currentTeam)
local firstUnit
if Selection and #Selection > 0 then
firstUnit = Selection[1]
else
firstUnit = currentTeam.units[1]
end
local squad = firstUnit and firstUnit:GetSatelliteSquad()
g_CurrentSquad = squad and squad.UniqueId or false
Msg("CurrentSquadChanged")
return firstUnit
end
function CheckUniqueSessionId(unit)
local session_id = unit.session_id
local same_id_unit = g_Units[session_id] and g_Units[session_id] ~= unit
if same_id_unit then
assert(false, string.format("Two units with the same session_id %s?", session_id))
return false
end
return true
end
function AddToGlobalUnits(unit)
if not CheckUniqueSessionId(unit) then
return
end
table.insert_unique(g_Units, unit)
g_Units[unit.session_id] = unit
end
function GetAllPlayerUnitsOnMap()
local team = table.find_value(g_Teams, "side", "player1")
return team and team.units
end
function GetAllPlayerUnitsOnMapSessionId()
local units = GetAllPlayerUnitsOnMap()
return table.map(units, "session_id")
end
function GetCurrentTeam()
return GetPoVTeam()
end
function GetPoVTeam()
if g_Combat then
-- The current team can be an enemy team, return the first allied team instead.
local active_team = g_Teams[g_CurrentTeam or 1]
if active_team and (active_team.control ~= "UI" or not active_team.player_ally) then
for _, team in ipairs(g_Teams) do
if team.control == "UI" and team.player_ally then
return team
end
end
end
return active_team
end
if not Selection or #Selection == 0 then
for _, team in ipairs(g_Teams) do
if team.side == "player1" then
return team
end
end
elseif IsKindOf(Selection[1], "Unit") then
return Selection[1].team
end
end
function WholeTeamSelected()
if g_Combat then return false end -- No multiselect
local team = GetFilteredCurrentTeam()
local unitsCanControl = {}
for i, u in ipairs(team and team.units) do
if u:IsLocalPlayerControlled() then
unitsCanControl[#unitsCanControl + 1] = u
end
end
if #unitsCanControl ~= #Selection then return false end
for i, s in ipairs(Selection) do
if not table.find(unitsCanControl, s) then
return false
end
end
return true
end
function GetFilteredCurrentTeam(team)
team = team or GetCurrentTeam()
-- If there is a current squad filter units from it only.
if team and team.units and g_CurrentSquad then
local teamFiltered = {
DisplayName = false,
side = false,
control = team.control
}
local units = {}
local squad = gv_Squads[g_CurrentSquad]
if not squad then
ResetCurrentSquad(team)
squad = gv_Squads[g_CurrentSquad]
if not squad then return team end
end
teamFiltered.DisplayName = Untranslated(squad.Name)
teamFiltered.side = squad.Side
for i, u in ipairs(squad.units) do
local unit = g_Units[u]
if IsValid(unit) then --and not unit:IsDead() then
units[#units + 1] = unit
end
end
if #units == 0 then
ResetCurrentSquad(team)
return team
end
teamFiltered.units = units
team = teamFiltered
end
return team
end
function GetMapUnitsInSquad(squadId)
local squad = gv_Squads[squadId]
if not squad then return {} end
local units = {}
for i, u in ipairs(squad.units) do
local unitOnMap = g_Units[u]
if unitOnMap then
units[#units + 1] = unitOnMap
end
end
return units
end
function GetCampaignPlayerTeam()
if IsHotSeatGame() or IsCompetitiveGame() then return end
for i, team in ipairs(g_Teams) do
if team.side == "player1" then
return team
end
end
end |