myspace / Lua /Tactical /TestCombat.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
17.3 kB
DefineClass.TestTeamDef = {
__parents = { "PropertyObject" },
properties = {
{ id = "mercs", name = "Mercs", editor = "string_list", default = empty_table, item_default = "", items = function() return table.keys2(UnitDataDefs, "sorted", "") end },
{ id = "side", name = "Side", editor = "dropdownlist", default = false, items = function() return Sides end },
{ id = "ai_control", name = "AI", editor = "bool", default = false },
{ id = "team_color", name = "Team Color", editor = "color", default = RGB(100, 100, 100) },
{ id = "team_name", name = "Team Name", editor = "text", default = "", translate = true },
--{ id = "spawn_marker_group", name = "Spawn Marker Group", editor = "combo", items = GridMarkerGroupsCombo(), default = "" },
{ id = "spawn_marker_group", name = "Spawn Marker Group", editor = "text", default = "" },
},
}
function TestTeamDef:GetEditorView()
return Untranslated("<team_name> ( <side> ):")..Untranslated(ValueToLuaCode(self.mercs))
end
function InitTestCombat(combat, combat_params, time_of_day)
if g_Combat ~= combat and IsValid(g_Combat) then
DoneObject(g_Combat)
end
g_Combat = combat or Combat:new()
combat_params = combat_params or {
TestTeamDef:new{
mercs = { "Barry", "Ivan", "Buns" },
team_color = RGB(0, 0, 200),
ai_control = false,
},
TestTeamDef:new{
mercs = { "Grizzly", "Grunty", "Gus" },
team_color = RGB(200, 0, 0),
ai_control = true,
},
}
if not HasGameSession() then
NewGameSession()
end
-- generate units from merc defs
g_Units = {}
g_Teams = {}
g_SquadsArray = {}
--gv_UnitData = {}
gv_Squads = {}
gv_NextSquadUniqueId = 1
gv_CurrentSectorId = gv_CurrentSectorId or "A1"
gv_Sectors[gv_CurrentSectorId].Map = GetMapName()
local seed = 0 -- tests should run consistently in order to be reproducible, so no AsyncRand here
g_Combat.time_of_day = time_of_day or "day"
Game.game_type = false
if table.find(combat_params, "side", "player2") then
Game.game_type = (not netGamePlayers or #netGamePlayers < 2) and "HotSeat" or "Competitive"
end
for i, team_def in ipairs(combat_params) do
local combat_team = CombatTeam:new{
control = team_def.ai_control and "AI" or "UI",
side = team_def.side or (team_def.ai_control or team_def:HasMember("enemy")) and "enemy1" or "player1",
team_color = team_def.team_color,
spawn_marker_group = team_def.spawn_marker_group,
dbgSquadIdx = i
}
combat_team.DisplayName = (team_def.team_name ~= "") and team_def.team_name or T{676161962248, "Team #<i>", i = i}
local unit_ids = {}
g_Teams[#g_Teams + 1] = combat_team
for _, merc in ipairs(team_def.mercs) do
local session_id = netInGame and string.format("%s_%s", merc, team_def.side) or merc
local combat_unit = SpawnUnit(merc, session_id)
unit_ids[#unit_ids+1] = session_id
SendUnitToTeam(combat_unit, combat_team)
end
CreateNewSatelliteSquad({
Name = _InternalTranslate(combat_team.DisplayName),
UniqueId = i,
Side = combat_team.side,
units = unit_ids,
CurrentSector = gv_CurrentSectorId,
})
end
local combat_center = point(terrain.GetMapSize())/2
for _, team in ipairs(g_Teams) do
local markers, center, positions, angle, marker
if team.spawn_marker_group ~= "" then
markers = MapGetMarkers("Entrance", team.spawn_marker_group)
if markers and #markers > 0 then
marker, positions, angle = GetRandomSpawnMarkerPositions(markers, #team.units)
assert(#positions == #team.units)
else
markers = MapGetMarkers("Position", team.spawn_marker_group)
if markers and #markers > 0 then
positions = {}
for i = 1, #markers do
positions[i] = markers[i]:GetPos()
angle = angle or markers[i]:GetAngle()
end
end
end
center = #markers > 0 and AveragePoint(markers)
end
local team_center = center or #team.units > 0 and GetRandomTerrainVoxelPosAroundCenter(team.units[1], combat_center, 15*guim)
markers = markers or empty_table
positions = positions or empty_table
for i, unit in ipairs(team.units) do
if #positions > 0 then
local idx = 1 + unit:Random(#positions)
local pos = positions[idx]
table.remove(positions, idx)
unit:SetPos(pos)
unit:SetAngle(angle)
else
local unit_pos = GetRandomTerrainVoxelPosAroundCenter(unit, team_center, 5*guim)
unit:SetPos(unit_pos)
unit:Face(combat_center)
end
local sessionId = unit.session_id
local unitData = CreateUnitData(unit.unitdatadef_id, sessionId, seed)
unitData.Squad = team.dbgSquadIdx
unitData.already_spawned_on_map = true
gv_UnitData[sessionId] = unitData
local squad = gv_Squads[team.dbgSquadIdx]
squad.units[#squad.units + 1] = sessionId
unit.Squad = squad.UniqueId
if team.side == "player1" or team.side == "player2" then
unit.ControlledBy = team.side == "player1" and 1 or 2
unitData.ControlledBy = team.side == "player1" and 1 or 2
end
end
end
MapForEach("map", "Unit", function(u)
CreateUnitData(u.unitdatadef_id, u.session_id, 0)
end)
ViewPos(combat_center)
end
function DbgStartCombat(map, combat_params, time_of_day)
local in_combat = not not g_Combat
DbgStopCombat()
if in_combat then
CloseAllDialogs()
end
if not HasGameSession() then
NewGameSession()
end
if g_Units and next(g_Units) then
NetSyncEvent("ExplorationStartCombat")
return
end
if map and (map ~= GetMapName() or in_combat) then
CreateRealTimeThread(function(map, combat_params, time_of_day)
ChangeMap(map)
DbgStartCombat(map, combat_params, time_of_day)
end, map, combat_params, time_of_day)
return
end
CreateGameTimeThread(function()
-- link dlg combat to satellite sector
local dbg_sector = "A1"
gv_Sectors[dbg_sector].Map = map
gv_CurrentSectorId = dbg_sector
gv_ActiveCombat = dbg_sector
g_Combat = Combat:new{test_combat = true}
InitTestCombat(g_Combat, combat_params, time_of_day)
SetupTeamsFromMap()
g_Combat:Start()
ShowInGameInterface(true, false, { Mode = "IModeCombatMovement" })
end)
end
function DbgStopCombat()
if g_Combat then
g_Combat:End()
DoneObject(g_Combat)
g_Combat = false
end
end
-- new test combat, simulating real game
function GetTestCombatSquadOptions()
local squads = table.keys(EnemySquadDefs, true)
table.insert(squads, 1, "CurrentPlayerSquad")
return squads
end
if FirstLoad then
g_TestCombat = false
g_TriggerEnemySpawners = false
g_DisableEnemySpawners = false
g_TestExploration = false
end
function OnMsg.NewGameSessionStart()
g_TestExploration = false
g_TestCombat = false
g_TriggerEnemySpawners = false
g_DisableEnemySpawners = false
end
function TestCombatTest(def, obj, prop_id)
return TestCombatEnterSector(def, def.map)
end
function TestCombatEnterSector(def, map)
if not def then
print("Invalid test combat def given", tostring(def))
return
end
if GetMap() == "" then
ChangeMap("__Empty")
CloseAllDialogs()
end
-- we only want player squads from the old game session, all other data is initialized
local old_unit_data = gv_UnitData
local old_squads = gv_Squads
local old_squads_arr = g_SquadsArray
local old_next_id = gv_NextSquadUniqueId
local old_playersquads = g_PlayerSquads
local old_pandmsquads = g_PlayerAndMilitiaSquads
local old_militiasquads = g_MilitiaSquads
local old_enemysquads = g_EnemySquads
local old_sectorData = gv_Sectors
NewGameSession(nil, {difficulty = Game and Game.game_difficulty or "Normal"})
local sector_id = def.sector_id
local _, enemy = GetSquadsInSector(sector_id)
local enemiesCopied = table.copy(enemy)
for _, squad in ipairs(enemiesCopied) do
RemoveSquad(squad)
end
gv_Sectors = next(old_sectorData) and old_sectorData or gv_Sectors
gv_UnitData = old_unit_data or {}
gv_Squads = old_squads or {}
g_SquadsArray = old_squads_arr or {}
g_PlayerSquads = old_playersquads or {}
g_PlayerAndMilitiaSquads = old_pandmsquads or {}
g_MilitiaSquads = old_militiasquads or {}
g_EnemySquads = old_enemysquads or {}
gv_NextSquadUniqueId = old_next_id or 1
g_TestCombat = def
g_TriggerEnemySpawners = def.trigger_enemy_spawners
g_DisableEnemySpawners = def.disable_enemy_spawners
Game.CampaignTime = CalculateTimeFromTimeOfDay(def.TimeOfDay) or 0
Game.CampaignTimeStart = Game.CampaignTime
if map and type(map) == "string" then
map = map
else
map = def.map
end
if map then
sector_id = sector_id or "A1"
gv_Sectors[sector_id].Map = map
end
if def.reveal_intel then
DiscoverIntelForSector(sector_id, "suppress notification")
end
for _, squad in ipairs(g_EnemySquads) do
if squad.CurrentSector==sector_id then
RemoveSquad(squad)
end
end
local squad_to_spawn_markers = {}
local game_spawn_logic = true
local current_squad_def
-- process test combat def squads
local player_squads = GetPlayerMercSquads()
if not next(player_squads) then
local campaign = GetCurrentCampaignPreset()
local id = CreateNewSatelliteSquad({Side = "player1", CurrentSector = sector_id, Name = Presets.SquadName.Player[1].Name}, GetTestCampaignSquad(), 14, 1234567)
player_squads = {gv_Squads[id]}
end
game_spawn_logic = def.player_role
for _, squad_def in ipairs(def.squads) do
local tier = squad_def.tier
local spawn_on_marker = squad_def.spawn_location == "On Marker"
if squad_def.squad_type == "NPC" then
local squad_id = GenerateEnemySquad(squad_def.npc_squad_id, sector_id, "TestCombat")
local squad = gv_Squads[squad_id]
TierUpSquad(squad, tier)
if spawn_on_marker then
squad_to_spawn_markers[squad.UniqueId] = {squad_def.spawn_marker_type or nil, squad_def.spawn_marker_group or nil}
end
squad.Side = squad_def.side
local ally = SideIsAlly(squad_def.side, "player1")
if (def.player_role == "attack" and ally) or (def.player_role == "defend" and not ally) then
for _, session_id in ipairs(squad.units) do
local unit = gv_UnitData[session_id]
unit.arrival_dir = def.attacker_dir
unit.already_spawned_on_map = false
end
end
else -- player squad
if squad_def.squad_type == "Custom" then
for _, squad in ipairs(player_squads) do
RemoveSquad(squad)
end
local id = CreateNewSatelliteSquad({Side = "player1", CurrentSector = sector_id, Name = Presets.SquadName.Player[1].Name}, squad_def.Mercs, 14, 1234567)
local squad = gv_Squads[id]
player_squads = { squad }
if spawn_on_marker then
squad_to_spawn_markers[squad.UniqueId] = {squad_def.spawn_marker_type or nil, squad_def.spawn_marker_group or nil}
end
end
for _, squad in ipairs(player_squads) do
TierUpSquad(squad, tier)
squad.CurrentSector = sector_id
if spawn_on_marker then
squad_to_spawn_markers[squad.UniqueId] = {squad_def.spawn_marker_type or nil, squad_def.spawn_marker_group or nil}
end
for _, session_id in ipairs(squad.units) do
local merc = gv_UnitData[session_id]
merc.arrival_dir = not spawn_on_marker and def.attacker_dir
merc.already_spawned_on_map = false
merc.statGainingPoints = 20
end
end
end
end
CloseDialog(GetPreGameMainMenu())
CreateRealTimeThread( function()
WaitMsg("PostNewMapLoaded")
def:OnMapLoaded()
end )
CreateRealTimeThread( function()
WaitMsg("CombatStart")
def:OnCombatStart()
end )
EnterConflict(gv_Sectors[sector_id], nil, game_spawn_logic or "attack")
LocalLoadSector(sector_id, game_spawn_logic, squad_to_spawn_markers)
end
function OnMsg.CanSaveGameQuery(query)
query.test_combat = g_TestCombat or nil
end
function IgnoreSpawnEnemyConditions(obj)
if obj:IsKindOf("UnitMarker") then
for _, group in ipairs(g_TriggerEnemySpawners or empty_table) do
if obj:IsInGroup(group) then
return true
end
end
end
end
function ForceDisableSpawnEnemy(obj)
if obj:IsKindOf("UnitMarker") then
for _, group in ipairs(g_DisableEnemySpawners or empty_table) do
if obj:IsInGroup(group) then
return true
end
end
end
end
function TriggerEnemySpawnersCombo(combat_map)
local groups = table.copy(GridMarkerGroupsCombo())
local group_used = {}
for _, group in ipairs(groups) do
group_used[group] = true
end
local map_markers = g_DebugMarkersInfo and g_DebugMarkersInfo[combat_map]
if map_markers then
for _, marker in ipairs(map_markers) do
if marker.Groups then
for _, group in ipairs(marker.Groups) do
group_used[group] = true
end
end
end
end
local groups_unique = table.keys2(group_used, true)
table.insert(groups_unique, 1, "")
return groups_unique
end
function GetCheatsTestCombatPresets()
local items = {}
for _, presets in ipairs(Presets.TestCombat) do
for _, preset in ipairs(presets) do
if preset.show_in_cheats then
items[#items + 1] = preset
end
end
end
table.sort(items, function(a, b) return a.SortKey < b.SortKey end)
return items
end
function PlaceBloodDecalDummy(appearance_name, weapon_name)
local pos = GetTerrainCursor()
local dummy_unit = PlaceObject("AppearanceObject")
dummy_unit:ApplyAppearance(appearance_name or "Buns")
dummy_unit:SetPos(pos)
local unit = dummy_unit
SetpieceSetStance.SetupActorWeapon(dummy_unit, weapon_name or "AUG")
-- Please keep decals at a later slot...
local dec1 = PlaceObject("DecSkBloodSplatter_01")
unit:Attach(dec1, unit:GetSpotBeginIndex("Shoulderl"), true)
dec1:SetAttachAxis(point(0,4096, 0))
dec1:SetAttachAngle(60*90)
dec1:SetAttachOffset(140, 0, 0)
local dec2 = PlaceObject("DecSkBloodSplatter_01")
unit:Attach(dec2, unit:GetSpotBeginIndex("Kneel"), true)
dec2:SetAttachAxis(point(0,4096, 0))
dec2:SetAttachAngle(60*90 + 180 * 60)
dec2:SetAttachOffset(-40, 0, 0)
local dec3 = PlaceObject("DecSkBloodSplatter_01")
unit:Attach(dec3, unit:GetSpotBeginIndex("Weaponr"), true)
dec3:SetAttachAxis(point(4096,0, 0))
dec3:SetAttachAngle(60*90 + 90 * 90)
end
function AttachBloodDecal(unit, spot)
local dec = PlaceObject("DecSkBloodSplatter_01")
unit:Attach(dec, unit:GetSpotBeginIndex(spot), true)
if spot == "Shoulderl" then
dec:SetAttachAxis(point(0,4096, 0))
dec:SetAttachAngle(60*90)
dec:SetAttachOffset(140, 0, 0)
elseif spot == "Kneel" then
dec:SetAttachAxis(point(0,4096, 0))
dec:SetAttachAngle(60*90 + 180 * 60)
dec:SetAttachOffset(-40, 0, 0)
elseif spot == "Weaponr" then
dec:SetAttachAxis(point(4096,0, 0))
dec:SetAttachAngle(60*90 + 90 * 90)
end
end
function TierUpSquad(squad, tier)
if not squad or not squad.units then return end
if tier == 1 then return end -- base tier no change needed
for _, id in ipairs(squad.units) do
local unit = gv_UnitData[id]
-- stats
local levelsToGain = 2 * (tier-1)
local statsToGain = 3 * levelsToGain
local nXPTable = #XPTable
unit.Experience = GetXPTable(Min(unit:GetLevel() + levelsToGain, nXPTable))
local statProps = UnitPropertiesStats:GetProperties()
for i = 1, statsToGain do
local roll = InteractionRand(#statProps, "TestCombatTierUp") + 1
local stat = statProps[roll].id
local id = string.format("StatGain-%s-%s-%d", stat, unit.session_id, GetPreciseTicks())
local mod = unit:AddModifier(id, stat, false, 1)
--GainStat(unit, stat)
end
-- perks
local perksToGain = levelsToGain
local availablePerks = PresetArray(CharacterEffectCompositeDef)
availablePerks = table.ifilter(availablePerks, function(i, perk)
return perk.object_class == "Perk" and table.find({"Bronze", "Silver", "Gold"}, perk.Tier) and not HasPerk(unit, perk.id)
end)
table.sort(availablePerks, function(a, b)
if unit[a.Stat] == unit[b.Stat] then
if a.Tier == "Gold" and (b.Tier == "Silver" or b.Tier == "Bronze") then
return true
elseif a.Tier == "Silver" and b.Tier == "Bronze" then
return true
else
return false
end
else
return unit[a.Stat] > unit[b.Stat]
end
end)
for _, perk in ipairs(availablePerks) do
if perksToGain <= 0 then break end
unit:AddStatusEffect(perk.id)
perksToGain = perksToGain - 1
end
-- items
local equipedItems, slots = unit:GetHandheldItems()
for i, item in ipairs(equipedItems) do
if IsKindOf(item, "Firearm") then
local weaponType = InventoryItemDefs[item.class].object_class
weaponType = g_Classes[weaponType].WeaponType
local weapons = GetWeaponsByType(weaponType)
table.sortby_field(weapons, "Cost")
local weapon
if tier == 2 and #weapons >= 2 then
weapon = weapons[#weapons-1]
elseif tier >= 3 then
weapon = weapons[#weapons]
else
break
end
local weaponObj = PlaceInventoryItem(weapon.id)
unit:RemoveItem(slots[i], item)
unit:AddItem(slots[i], weaponObj)
local ammos = GetAmmosWithCaliber(weapon.Caliber)
local ammoKey = table.find(ammos, "colorStyle" , "AmmoBasicColor")
if not ammoKey then
ammoKey = 1
end
local ammo = PlaceInventoryItem(ammos[ammoKey].id)
ammo.Amount = ammo.MaxStacks
unit:AddItem("Inventory", ammo)
end
end
end
end
function TestCombatStartFromAltShortcut(shortcut)
local testCombat
for _, presets in ipairs(Presets.TestCombat) do
local foundTestIndex = table.find(presets, "Alt_Shortcut", shortcut) or false
if foundTestIndex then
testCombat = presets[foundTestIndex]
TestCombatEnterSector(testCombat, testCombat.map)
return
end
end
print("No test combat found at shortcut: ", shortcut)
end