myspace / Lua /Satellite /SatelliteSectorOperations.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
75.2 kB
SectorOperationResoucesBase = {
{
id = "Money", name = T(517301472548, "Money"),
icon = "UI/SectorOperations/T_Icon_Money",
context = function(sector) return Game end,
current = function(sector) return Game.Money end,
-- no <money()> formatting on purpose here, a bitmap $ will be added in the UI
current_txt = function(sector) return T{831649021785, "<money>", money = FormatNumber(Game.Money)} end,
pay = function(sectorId, cost) AddMoney(-cost, "operation") end,
restore = function(merc, cost) AddMoney(cost, "operation") end,
},
}
if FirstLoad then
SectorOperationResouces = false
end
local function lAddInventoryItemAsSectorResource(name, icon, noCheat, bAdditional)
local item = InventoryItemDefs[name]
SectorOperationResouces[#SectorOperationResouces + 1] = {
id = name, name = item.DisplayName,
icon = icon or ("UI/SectorOperations/T_Icon_" .. name),
additional = bAdditional,
context = function(sector) return sector end,
current = function(sector)
if type(sector) == "string" then
sector = gv_Sectors[sector]
end
return GetSectorOperationResource(sector, name)
end,
pay = function(sectorId, cost)
if not noCheat and CheatEnabled("FreeParts") then
cost = 0
end
PaySectorOperationResource(sectorId, name, cost)
end,
restore = function(merc, cost)
if not noCheat and CheatEnabled("FreeParts") then
cost = 0
end
RestoreSectorOperationResource(merc, name, cost)
end,
}
end
function OnMsg.ClassesBuilt()
CreateRealTimeThread(function()
WaitDataLoaded()
SectorOperationResouces = table.copy(SectorOperationResoucesBase)
lAddInventoryItemAsSectorResource("Meds", "UI/SectorOperations/T_Icon_Medicine", false)
lAddInventoryItemAsSectorResource("Parts")
lAddInventoryItemAsSectorResource("FineSteelPipe", "UI/Icons/Upgrades/parts_placeholder", false, "additional")
lAddInventoryItemAsSectorResource("Microchip", "UI/Icons/Upgrades/parts_placeholder", false, "additional")
lAddInventoryItemAsSectorResource("OpticalLens", "UI/Icons/Upgrades/parts_placeholder", false, "additional")
for i, resourceData in ipairs(SectorOperationResouces) do
SectorOperationResouces[resourceData.id] = resourceData
end
end)
end
---
function GetCurrentResourcesContext(operation, sector)
local items = {}
local resources = operation and operation.RequiredResources
if operation and IsCraftOperation(operation.id)then
table.insert_unique(resources,"Parts")
end
if resources then
for _, res in ipairs(resources or empty_table) do
local ts = SectorOperationResouces[res]
if ts then
items[#items + 1] = {resource = res, value = (ts.current_txt or ts.current)(sector), icon = ts.icon, context = ts.context(sector)}
end
end
else
for _, res in ipairs(SectorOperationResouces or empty_table) do
if not res.additional then
items[#items + 1] = {resource = res.id, value = (res.current_txt or res.current)(sector), icon = res.icon, context = res.context(sector)}
end
end
end
return items
end
--todo: batch events
--InterruptSectorOperation + RestoreOperationCost
--can't MercRemoveOperationTreatWounds be the same func as MercSetOperation?
--most events are per merc, and are sent for each merc, rather then iterate themselves
function OperationsSync_ResumeObjModified()
ResumeObjModified("OperationsSync")
end
local thread = false
function OperationsSync_SuspendObjModified()
if IsValidThread(thread) then return end
SuspendObjModified("OperationsSync")
thread = CreateRealTimeThread(function()
while #(SyncEventsQueue or "") > 0 do
WaitNextFrame()
end
thread = false
OperationsSync_ResumeObjModified()
end)
end
function NetSyncEvents.LogOperationStart(operation_id, sector_id, voicelog)
OperationsSync_SuspendObjModified()
if operation_id == "Traveling" or operation_id == "Idle" or operation_id== "Arriving" then
return
end
local operation = SectorOperations[operation_id]
local sector = gv_Sectors[sector_id]
if #operation.Professions >= 2 then
if operation_id == "TrainMercs" then
local m_students = GetOperationProfessionals(sector_id, operation_id,"Student")
local m_teachers = GetOperationProfessionals(sector_id, operation_id,"Teacher")
local solo = not next(m_teachers)
if #m_students >=1 then
local trainers = table.map(m_teachers, "Nick")
local students = table.map(m_students, "Nick")
if voicelog then PlayVoiceResponse(table.rand(not solo and m_teachers or m_students),"ActivityStarted") end
if solo then
CombatLog("short",T{490352745327, "<em><students></em> started training in <SectorName(sector)>", students = ConcatListWithAnd(students), sector = sector})
else
CombatLog("short",T{559221136920, "<em><trainers></em> started training <em><students></em> in <SectorName(sector)>",trainers = ConcatListWithAnd(trainers), students = ConcatListWithAnd(students), sector = sector})
end
end
elseif operation_id == "TreatWounds" then
local m_patients = GetOperationProfessionals(sector_id, operation_id,"Patient")
local m_doctors = GetOperationProfessionals(sector_id, operation_id,"Doctor")
if #m_doctors>=1 and #m_patients>=1 then
local doctors = table.map(m_doctors, "Nick")
local patients = table.map(m_patients, "Nick")
if voicelog then PlayVoiceResponse(table.rand(m_doctors),"ActivityStarted") end
CombatLog("short",T{306176602916, "<em><doctors></em> started treating the wounds of <em><patients></em> in <SectorName(sector)>",doctors = ConcatListWithAnd(doctors), patients = ConcatListWithAnd(patients), sector = sector})
end
end
else
local operationMercs = GetOperationProfessionals(sector_id, operation_id)
local merc_names = table.map(operationMercs, function(o) return o.Nick end)
local msg = operation.log_msg_start and operation.log_msg_start ~= "" and T{operation.log_msg_start, sector = sector, display_name = operation.display_name, mercs = ConcatListWithAnd(merc_names)}
or T{807960240333, "<em><mercs></em> started <em><display_name></em> in <SectorName(sector)>",mercs = ConcatListWithAnd(merc_names), display_name = operation.display_name, sector = sector}
CombatLog("short", msg)
local negotiatorUnits = {}
for _, merc in ipairs(operationMercs) do
if HasPerk(merc, "Negotiator") and InteractionRand(100, "NegotiatorVR") < 50 then
table.insert(negotiatorUnits, merc)
end
end
if voicelog then
if next(negotiatorUnits) then
PlayVoiceResponse(table.rand(negotiatorUnits, InteractionRand(1000000, "RandomNegotiator")), "Negotiator")
elseif next(operationMercs) then
PlayVoiceResponse(table.rand(operationMercs, InteractionRand(1000000, "RandomActivityStartedMerc")), "ActivityStarted")
end
end
end
end
function NetSyncEvents.SetTrainingStat(sector_id, stat)
OperationsSync_SuspendObjModified()
local sector = gv_Sectors[sector_id]
sector.training_stat = stat
ObjModified(sector)
end
function NetSyncEvents.StartOperation(sector_id, operation_id, start_time, training_stat)
OperationsSync_SuspendObjModified()
local sector = gv_Sectors[sector_id]
sector.started_operations = sector.started_operations or {}
sector.started_operations[operation_id] = start_time
if operation_id=="TrainMercs" then
sector.training_stat = training_stat
end
if sector.operations_temp_data and sector.operations_temp_data[operation_id] and sector.operations_temp_data[operation_id].pick_item then
sector.operations_temp_data[operation_id].pick_item = false
end
local mercs = GetOperationProfessionals(sector_id, operation_id)
for _, m in ipairs(mercs) do
Msg("OperationTimeUpdated", m, operation_id)
end
RemoveTimelineEvent("activity-temp")
-- This message will no longer be needed one merc set operation is made
-- to fire only once the operation starts.
Msg("TempOperationStarted", operation_id)
ObjModified(sector)
end
function NetSyncEvents.RestoreOperationCostAndSetOperation(unit_id, refund_amound, operation_id, prof_id, cost, slot, check, all_profs, partial_wounds)
--batch of the two other evs
OperationsSync_SuspendObjModified()
NetSyncEvents.RestoreOperationCost(unit_id, refund_amound)
local prev = gv_UnitData[unit_id]
local prev_op = prev.Operation
SectorOperations[prev_op]:OnMove(prev, true)
NetSyncEvents.MercSetOperation(unit_id, operation_id, prof_id, cost, slot, check, partial_wounds)
end
function NetSyncEvents.RestoreOperationCost(unit_id, cost)
OperationsSync_SuspendObjModified()
local merc = gv_UnitData[unit_id]
if merc then
for _, c in ipairs(cost or empty_table) do
local value = c.value
if CheatEnabled("FreeParts") and c.resource== "Parts" then
value = 0
end
local res_t = SectorOperationResouces[c.resource]
res_t.restore(merc, c.value)
end
end
end
function NetSyncEvents.MercSyncOperationsData(unit_id, tiredness, rest_time, travel_time, travel_timer_start)
OperationsSync_SuspendObjModified()
local merc = gv_UnitData[unit_id]
if merc then
local sector = merc:GetSector()
merc:SetTired(tiredness)
merc.RestTimer = rest_time
merc.TravelTime = travel_time
merc.TravelTimerStart = travel_timer_start
ObjModified(gv_Squads)
ObjModified(sector)
end
end
function NetSyncEvents.MercSetOperationIdle(unit_id,tiredness, rest_time, travel_time, travel_timer_start)
OperationsSync_SuspendObjModified()
local merc = gv_UnitData[unit_id]
if merc then
local sector = merc:GetSector()
merc:SetCurrentOperation("Idle")
merc:SetTired(tiredness)
merc.RestTimer = rest_time
merc.TravelTime = travel_time
merc.TravelTimerStart = travel_timer_start
ObjModified(gv_Squads)
ObjModified(sector)
end
end
function NetSyncEvents.MercSetOperation(unit_id, operation_id, prof_id, cost, slot, check, partial_wounds)
OperationsSync_SuspendObjModified()
local merc = gv_UnitData[unit_id]
if merc then
local sector = merc:GetSector()
PayOperation(cost, merc:GetSector())
if operation_id=="MilitiaTraining" and next(cost) and not sector.militia_training_payed_cost then
sector.militia_training_payed_cost = cost[1].value
end
local prev = SectorOperations[merc.Operation]
merc:SetCurrentOperation(operation_id, slot, prof_id, partial_wounds)
if check then
prev:CheckCompleted(merc, sector)
local mercs = GetOperationProfessionals(sector.Id, prev.id)
if not next(merc) or #mercs<=0 then
if sector.started_operations then
sector.started_operations[prev.id] = false
end
end
end
ObjModified(gv_Squads)
ObjModified(sector)
end
end
function NetSyncEvents.MercRemoveOperationTreatWounds(unit_id, prof_id)
local merc = gv_UnitData[unit_id]
if not merc then return end
OperationsSync_SuspendObjModified()
local sector = merc:GetSector()
if IsPatient(merc) and IsDoctor(merc) then
if prof_id=="Doctor" then
merc:SetCurrentOperation("Idle")
elseif prof_id=="Patient" then
local count = SectorOperationCountPatients(sector.Id, unit_id)
if count>0 then
merc:RemoveOperationProfession("Patient")
merc.OperationProfession = "Doctor"
merc:SetCurrentOperation(merc.Operation)
else
merc:SetCurrentOperation("Idle")
end
end
else
merc:SetCurrentOperation("Idle")
end
ObjModified(gv_Squads)
ObjModified(sector)
end
function NetSyncEvents.InterruptSectorOperation(sector_id, operation, reason)
OperationsSync_SuspendObjModified()
local mercs = GetOperationProfessionals(sector_id, operation)
for _, merc in ipairs(mercs) do
local event_id = GetOperationEventId(merc, operation)
RemoveTimelineEvent(event_id)
merc:SetCurrentOperation("Idle",false, false, false, reason or "interrupted")
end
local sector = gv_Sectors[sector_id]
if sector.started_operations then
sector.started_operations[operation] = false
end
ObjModified(sector)
end
function NetSyncEvents.ChangeSectorOperation(sector_id, operation_id)
OperationsSync_SuspendObjModified()
local mercs = GetOperationProfessionals(sector_id, operation_id)
for _, merc in ipairs(mercs) do
local event_id = GetOperationEventId(merc, operation_id)
RemoveTimelineEvent(event_id)
end
local sector = gv_Sectors[sector_id]
if sector.started_operations then
sector.started_operations[operation_id] = false
end
ObjModified(sector)
local mercs = GetOperationProfessionals(sector_id, operation_id)
local eta = next(mercs) and GetOperationTimeLeft(mercs[1], operation_id) or 0
local timeLeft = eta and Game.CampaignTime + eta
AddTimelineEvent("activity-temp", timeLeft, "operation", { operationId = operation_id, sectorId = sector_id})
end
-- reset operation and cancel prev operation
function SectorOperation_CancelByGame(units,operation_id, already_synced)
local to_cancel_units = {}
for _, unit_id in ipairs(units) do
local unit_data = type(unit_id)== "string" and gv_UnitData[unit_id] or unit_id
if not IsMerc(unit_data) then
return -- enemy squad is moving
end
local prev_operation = unit_data.Operation
if prev_operation~="Idle" and (not operation_id or prev_operation==operation_id) then
to_cancel_units[prev_operation] = to_cancel_units[prev_operation] or {}
table.insert(to_cancel_units[prev_operation], gv_UnitData[unit_data.session_id])
end
end
for operation_id, tbl in sorted_pairs(to_cancel_units) do
local costs = GetOperationCostsProcessed(tbl, operation_id, false, "both", "refund")
for i, unit_data in ipairs(tbl) do
local unit_id = unit_data.session_id
NetSyncEvent("RestoreOperationCost", unit_id, costs[i])
local satview_unit = gv_UnitData[unit_id]
local on_map_unit = g_Units[unit_id]
local map_change = not gv_SatelliteView and on_map_unit
if map_change then
on_map_unit:SyncWithSession("map")
end
satview_unit:SetCurrentOperation("Idle")
SectorOperations[operation_id]:OnMove(satview_unit, already_synced)
if map_change then
on_map_unit:SyncWithSession("session")
end
end
end
RepairItems_RemoveRepairedItems(units, already_synced)
end
function OnMsg.UnitDied(unit)
if not IsMerc(unit) or unit.Operation=="Idle" then return end
SectorOperation_CancelByGame({unit},unit.Operation, true)
end
function NetSyncEvents.ChangeSectorOperationItemsOrder(sector_id, operation_id, sector_items, sector_items_queued )
if not IsCraftOperation(operation_id) then return end
OperationsSync_SuspendObjModified()
local sector = gv_Sectors[sector_id]
local quid, allid = GetCraftOperationListsIds(operation_id)
if allid then
sector[allid] = TableWithItemsFromNet(sector_items)
ObjModified(sector[allid])
end
local tbl = SetCraftOperationQueueTable(sector, operation_id, TableWithItemsFromNet(sector_items_queued))
ObjModified(sector)
ObjModified(tbl)
end
function SectorOperation_CalcCraftResources(sector_id, operation_id)
local sector = gv_Sectors[sector_id]
local craft_table = GetCraftOperationQueueTable(sector, operation_id) or {}
-- calc queued/all resources
local res_items = {}
for _, q_data in pairs(craft_table) do
local recipe = CraftOperationsRecipes[q_data.recipe]
for _, ingrd in ipairs(recipe.Ingredients) do
res_items[ingrd.item] = (res_items[ingrd.item] or 0)+ ingrd.amount
end
end
return res_items
end
function SectorOperation_ValidateRecipeIngredientsAmount(mercs, recipe, res_items, checked_amount_cach)
checked_amount_cach = checked_amount_cach or {}
local res = true
for __, ingrd in ipairs(recipe.Ingredients) do
local amount = ingrd.amount + (res_items[ingrd.item] or 0)
local result
local checked = checked_amount_cach[ingrd.item]
if checked and checked>=amount then
result = true
else
local max
result, max = HasItemInSquad(mercs[1],ingrd.item, amount)
if result then
checked_amount_cach[ingrd.item] = max
end
end
res = res and result
end
return res
end
function SectorOperationValidateItemsToCraft(sector_id, operation_id, merc )
if not IsCraftOperationId(operation_id) then
return
end
local merc = merc or GetOperationProfessionals(sector_id, operation_id)[1]
if not merc then
return
end
local mercs = gv_Squads[merc.Squad].units
-- calc queued resources
local res_items = SectorOperation_CalcCraftResources(sector_id, operation_id)
local id = "g_Recipes"..operation_id
if not rawget(_G,id) then
SectorOperationFillItemsToCraft(sector_id, operation_id, merc)
return
end
local all_to_craft = _G[id] or {}
local checked_amount_cach = {}-- item, amount that is checked sucsessfully,
for _, craft_data in pairs(all_to_craft) do
local recipe = CraftOperationsRecipes[craft_data.recipe]
if recipe.RequiredCrafter and merc.session_id~=recipe.RequiredCrafter then
craft_data.hidden = true
end
local condition = not recipe.QuestConditions or EvalConditionList(recipe.QuestConditions)
craft_data.hidden = craft_data.hidden or not condition
local res = SectorOperation_ValidateRecipeIngredientsAmount(mercs, recipe,res_items, checked_amount_cach)
craft_data.enabled = not not res
end
table.sort(all_to_craft, function(a,b)
if not a or not b then return true end
if a.enabled and not b.enabled then
return true
elseif not a.enabled and b.enabled then
return false
elseif a.item_id<b.item_id then return
true
end
return false
end)
end
function SectorOperations_CraftAdditionalResources(sector_id,operation_id)
local res_table = {}
for recipe_id, recipe in pairs(CraftOperationsRecipes) do
if recipe.CraftOperationId==operation_id or recipe.group=="Ammo" and operation_id=="CraftAmmo" or recipe.group=="Explosives" and operation_id=="CraftExplosives" then
for _, ingr in ipairs(recipe.Ingredients) do
res_table[ingr.item] = (res_table[ingr.item] or 0) + ingr.amount
end
end
end
local needed_res_table = SectorOperation_CalcCraftResources(sector_id,operation_id)
local merc
local mercs = GetOperationProfessionals(sector_id,operation_id)
if next(mercs) then
merc = mercs[1].session_id
else
mercs = GetPlayerMercsInSector(sector_id)
merc = mercs[1]
end
local array = {}
for res, val in pairs(res_table) do
if res~="Money" and res~="Parts" then
local result, amount_found = HasItemInSquad(merc, res, "all")
if amount_found and amount_found>0 then
array[#array + 1] = {res = res, value = val, queued_val = needed_res_table[res], amount_found = amount_found or 0}
end
end
end
table.sortby(array, "res")
return array
end
function SectorOperation_CraftItemTime(sector_id, operation_id, recipe)
local sector = gv_Sectors[sector_id]
local related_stat = SectorOperations[operation_id].related_stat
local mercs = GetOperationProfessionals(sector_id,operation_id)
if not mercs then
return 0
end
local stat = mercs[1][related_stat]
if IsCraftOperation(operation_id) then
local time = CraftOperationsRecipes[recipe].CraftTime
return 3*time*1000/2 - stat*time*1000/100
end
return 0
end
function SectorOperation_CraftTotalTime(sector_id, operation_id)
local sector = gv_Sectors[sector_id]
local s_queued = SectorOperationItems_GetTables(sector_id, operation_id)
local total_time = 0
local related_stat = SectorOperations[operation_id].related_stat
local mercs = GetOperationProfessionals(sector_id,operation_id)
if not next(mercs) then
return
end
local stat = mercs[1][related_stat]
if IsCraftOperation(operation_id) and operation_id~="RepairItems" then
for _, q_item in ipairs(s_queued) do
local time = CraftOperationsRecipes[q_item.recipe].CraftTime
local calced_time = 3*time*1000/2 - stat*time*1000/100
total_time = total_time + calced_time
end
sector.custom_operations = sector.custom_operations or {}
sector.custom_operations[operation_id] = sector.custom_operations[operation_id] or {}
sector.custom_operations[operation_id].total_time = total_time
end
end
function NetSyncEvents.SectorOperationItemsUpdateLists(sector_id,operation_id, sector_items, sector_items_queued)
OperationsSync_SuspendObjModified()
local sector = gv_Sectors[sector_id]
NetSyncEvents.ChangeSectorOperationItemsOrder(sector_id, operation_id, sector_items, sector_items_queued)
local s_queued, s_all = SectorOperationItems_GetTables(sector_id, operation_id)
SectorOperation_CraftTotalTime(sector_id, operation_id)
RecalcOperationETAs(sector, operation_id, "stopped")
ObjModified(sector)
ObjModified(s_queued)
if s_all then ObjModified(s_all) end
--SectorOperation_ItemsUpdateItemLists() --not needed it seems
end
function NetSyncEvents.RecalcOperationETAs(sector_id, operation, stopped)
RecalcOperationETAs(gv_Sectors[sector_id], operation, stopped)
end
function CombineOperationCosts(costs)
local combinedCosts = {}
for _, cost_t in ipairs(costs) do
for i, c in ipairs(cost_t) do
local resource = c.resource
local comb_idx = table.find(combinedCosts, "resource", c.resource)
if comb_idx then
combinedCosts[comb_idx].value = combinedCosts[comb_idx].value + c.value
else
combinedCosts[#combinedCosts + 1] = table.copy(c)
end
end
end
return combinedCosts
end
function GetOperationCostsProcessed(mercs,operation_id, prof_id, both, refund)
local operation = type(operation_id)=="string" and SectorOperations[operation_id] or operation_id
local costs = {}
local min, mcost, merc = false
for idx, m in ipairs(mercs) do
local cost = operation:GetOperationCost(m, prof_id or m.OperationProfession, refund)
if both and operation.id=="TreatWounds" then
local idx = #costs+1
costs[idx] = cost or {}
if m.OperationProfessions and m.OperationProfessions["Doctor"] and m.OperationProfessions["Patient"]then
assert(operation.id=="TreatWounds")
local other = m.OperationProfession=="Patient" and "Doctor" or "Patient"
for _,cost in ipairs(operation:GetOperationCost(m, other)) do
costs[idx] = costs[idx] or {}
table.insert(costs[idx], cost)
end
end
end
if cost[1] and cost[1].min then
if not min or cost[1].value<min then
min, mcost, merc = cost[1].value, cost, m
end
elseif not (both and operation.id=="TreatWounds" ) then
costs[#costs + 1] = cost
end
end
if min then
if refund and operation.id=="MilitiaTraining" then
local sector = merc:GetSector()
mcost[1].value = sector.militia_training_payed_cost or mcost[1].value
end
costs[#costs + 1] = mcost
end
return costs , merc
end
function GetOperationCosts(mercs, operation_id, prof_id, slot, other_free_slots)
local operation = SectorOperations[operation_id]
local names = {}
local combinedCosts = {}
local costs = {}
local errors = {}
--local min, cost, merc = false
costs = GetOperationCostsProcessed(mercs,operation_id, prof_id)
for idx, m in ipairs(mercs) do
names[#names + 1] = m.Nick
local err, context = operation:CanPerformOperation(m, prof_id)
if err and err~= "OperationResourceError" then -- operation cost err is parsed in can pay operation one more time. Does not display mesage twise
table.insert(context, m)
errors[#errors + 1] = T{SatelliteWarnings[err].Body, context, context[1]}
end
end
local combinedCosts = CombineOperationCosts(costs)
local costTexts = {}
for _, cc in ipairs(combinedCosts) do
local resourceId, amount = cc.resource, cc.value
if CheatEnabled("FreeParts") and resourceId=="Parts" then
amount = 0
end
local resourceData = SectorOperationResouces[resourceId]
costTexts[#costTexts + 1] = T{Untranslated(amount) .. string.format("<image %s 1700>", resourceData.icon)}
end
if next(mercs) and not CanPayOperation(combinedCosts, mercs[1]:GetSector()) then
local err, context = "OperationResourceError", {activity = operation.display_name}
if err then
local mercs_text = {}
for i=1,#mercs do
mercs_text[#mercs_text + 1] = mercs[i]:GetDisplayName()
end
errors[#errors + 1] = T{SatelliteWarnings[err].Body, context, DisplayName = table.concat(mercs_text,", ")}
end
end
return combinedCosts, costTexts, names, errors
end
function MercsNetStartOperation(mercs, operation_id, prof_id, cost , slot, other_free_slots, t_wounds_being_treated)
for i, m in ipairs(mercs) do
local slt = i == 1 and slot or other_free_slots and other_free_slots[i-1]
local treated_wounds = t_wounds_being_treated and t_wounds_being_treated[m]
NetSyncEvent("MercSetOperation", m.session_id, operation_id, prof_id, i == 1 and cost, slt or ((slot or 1) + (i-1)), false, treated_wounds)
end
end
function MercsOperationsFillTempDataMercs(mercs, operation_id, prof_id, cost , slot, other_free_slots, t_wounds_being_treated)
local sector = mercs[1]:GetSector()
sector.operations_temp_data = sector.operations_temp_data or {}
if not sector.operations_prev_data or sector.operations_prev_data.operation_id~=operation_id then
sector.operations_prev_data = {}
end
local temp_table = sector.operations_temp_data[operation_id] or {}
for i, m in ipairs(mercs) do
local allProfessions = m.Operation == "TreatWounds" and operation_id == "TreatWounds" and not (m.OperationProfessions[prof_id])
local slot = m.OperationProfession==prof_id and slot or other_free_slots and other_free_slots[i-1] or 1
local treated_wounds = t_wounds_being_treated and t_wounds_being_treated[m] or false
local tt_merc = temp_table[m.session_id] or {}
local prev_operation = m.Operation
if sector.operations_prev_data[m.session_id] then
local data = sector.operations_prev_data[m.session_id]
prev_operation = data and data[1] and data[1].prev_Operation
end
local insert_data = {operation_id, prof_id or false, i == 1 and cost, slot or false, false, treated_wounds or false,
RestTimer = m.RestTimer, TravelTime = m.TravelTime, TravelTimerStart = m.TravelTimerStart, Tiredness = m.Tiredness, prev_Operation = prev_operation}
if next(tt_merc) and allProfessions then
table.insert(tt_merc,insert_data)
else
tt_merc = {insert_data}
end
temp_table[m.session_id] = tt_merc
sector.operations_prev_data[m.session_id] = tt_merc
end
sector.operations_temp_data[operation_id] = temp_table
sector.operations_prev_data.operation_id = operation_id
end
function GetCraftOperationListsIds(operation_id)
if operation_id=="RepairItems" then
return "sector_repair_items_queued", "sector_repair_items"
end
local queued = operation_id == "CraftAmmo" and "sector_craft_ammo_items_queued" or operation_id == "CraftExplosives" and "sector_craft_explosive_items_queued"
if not queued and IsCraftOperationId(operation_id) then
return "sector_"..operation_id.."_items_queued", false, "_custom"
end
return queued, false
end
function SetCraftOperationQueueTable(sector, operation_id, queue)
local qid, _, is_custom = GetCraftOperationListsIds(operation_id)
if is_custom then
sector.custom_operations = sector.custom_operations or {}
sector.custom_operations[operation_id] = sector.custom_operations[operation_id] or {}
sector.custom_operations[operation_id][qid] = queue
return sector.custom_operations[operation_id][qid]
else
sector[qid] = queue
return sector[qid]
end
end
function GetCraftOperationQueueTable(sector, operation_id)
local qid, _, is_custom = GetCraftOperationListsIds(operation_id)
if is_custom then
sector.custom_operations[operation_id] = sector.custom_operations[operation_id] or {}
ObjModified(sector.custom_operations[operation_id][qid])
return sector.custom_operations[operation_id][qid]
else
ObjModified(sector[qid])
return sector[qid]
end
end
function IsCraftOperationId(operation_id)
return CraftOperationIds[operation_id]
end
function IsCraftOperation(operation_id)
return operation_id=="RepairItems" or IsCraftOperationId(operation_id)
end
function MercsOperationsFillTempData(sector, operation_id)
if not IsCraftOperation(operation_id) then
return
end
-- RepairItems, craft
sector.operations_temp_data = sector.operations_temp_data or {}
local temp_table = sector.operations_temp_data[operation_id] or {}
temp_table.all_items = table.copy(SectorOperationItems_GetAllItems(sector.Id, operation_id))
temp_table.queued_items = table.copy(SectorOperationItems_GetItemsQueue(sector.Id, operation_id))
sector.operations_temp_data[operation_id] = temp_table
end
function TryMercsSetPartialTreatWounds(parent, mercs, operation_id, prof_id, slot, other_free_slots)
local sector = mercs[1] and mercs[1]:GetSector()
if prof_id ~= "Patient" or not sector then
return
end
local cost, costTexts, names, errors = GetOperationCosts(mercs, operation_id, prof_id, slot, other_free_slots)
if not cost or not cost[1] or not cost[1].value then
return
end
local t_wounds_being_treated = {}
local res_t = SectorOperationResouces["Meds"]
local all_meds = res_t and res_t.current(sector) or 0
local treatWoundsOperation = SectorOperations["TreatWounds"]
local cost_per_wound = treatWoundsOperation:ResolveValue("MedicalCostPerWound")
for i, m in ipairs(mercs) do
local cost_p = treatWoundsOperation:GetOperationCost(m, "Patient")[1]
if cost_p.value < all_meds then
all_meds = all_meds - cost_p.value
t_wounds_being_treated[m] = PatientGetWoundedStacks(m)
else
local partial = all_meds / cost_per_wound
if partial > 0 then
t_wounds_being_treated[m] = partial
end
break
end
end
for i = #mercs, 1, -1 do
if not t_wounds_being_treated[mercs[i]] then
table.remove(mercs, i)
end
end
if next(mercs) then
local count = 0
local treatWoundsOperation = SectorOperations["TreatWounds"]
for k, v in pairs(t_wounds_being_treated) do
count = count + v
end
cost[1].value = count * treatWoundsOperation:ResolveValue("MedicalCostPerWound")
local dlg = CreateQuestionBox(
terminal.desktop,
T(1000599, "Warning"),
T{887037769776, "You don't have enough Meds to fully heal all mercs. Do you want to spend <meds> Meds to heal <number> wound(s)?",
meds = cost[1].value, number = count },
T(689884995409, "Yes"),
T(782927325160, "No"))
if dlg:Wait()== "ok" then
MercsOperationsFillTempDataMercs(mercs, operation_id, prof_id, cost, slot, other_free_slots, t_wounds_being_treated)
MercsNetStartOperation (mercs, operation_id, prof_id, cost, slot, other_free_slots, t_wounds_being_treated)
end
return true
end
end
function TryMercsSetOperation(parent, mercs, operation_id, prof_id, slot, other_free_slots)
local operation = SectorOperations[operation_id]
local message = ""
local cost, costTexts, names, errors = GetOperationCosts(mercs, operation_id, prof_id, slot, other_free_slots)
local anyErrors = #errors > 0
if anyErrors then
local partial = TryMercsSetPartialTreatWounds(parent, mercs, operation_id, prof_id, slot, other_free_slots)
if not partial then
WaitMessage(parent, T(788367459331, "Error"), table.concat(errors, T(420993559859, "<newline>")), T(325411474155, "OK"))
end
return partial
end
-- move to temp data
-- confirmation
-- set merc operation in one place
MercsOperationsFillTempDataMercs(mercs, operation_id, prof_id, cost , slot, other_free_slots)
MercsNetStartOperation (mercs, operation_id, prof_id, cost, slot, other_free_slots)
return true
end
function SectorOperation_UpdateOnStop(operation, mercs, sector)
local sector = sector or (mercs and mercs[1] and mercs[1]:GetSector())
if not sector then return end
if sector.started_operations and #GetOperationProfessionals(sector.Id, operation.id) == 0 then
sector.started_operations[operation.id] = false
local event_id = GetOperationEventId(mercs[1], operation.id)
RemoveTimelineEvent(event_id)
if sector.operations_temp_data and sector.operations_temp_data [operation.id] then
sector.operations_temp_data [operation.id] = false
end
end
end
function OnMsg.OperationCompleted(operation, mercs, sector)
return SectorOperation_UpdateOnStop(operation, mercs, sector)
end
function GetAvailableMercs(sector, operation, profession)
local mercs = {}
local operation = type(operation)=="string" and SectorOperations[operation] or operation
for _, unit_data in ipairs(GetOperationProfessionals(sector.Id, "Idle")) do
if operation:FilterAvailable(unit_data, profession) then
local idx = unit_data.OperationProfessions and unit_data.OperationProfessions[profession] or (#mercs + 1)
if mercs[idx] then
idx = table.count(mercs) + 1
end
mercs[idx] = unit_data
end
end
-- Special cases where a merc can take multiple professions in one operation.
-- For now this applies to TreatWounds only
if operation.id == "TreatWounds" then
local check_other
if profession == "Patient" then
check_other = "Doctor"
elseif profession == "Doctor" then
check_other = "Patient"
end
for _, unit_data in ipairs(GetOperationProfessionals(sector.Id, "TreatWounds", check_other)) do
if operation:FilterAvailable(unit_data, profession) and (not unit_data.OperationProfessions or not unit_data.OperationProfessions[profession]) then
local idx = (#mercs + 1)
mercs[idx] = unit_data
end
end
end
return mercs
end
function GetBusyMercsForList(sector, operation, profession)
local mercs = {}
for _, unit_data in ipairs(GetOperationProfessionals(sector.Id, operation.id, profession)) do
local idx = unit_data.OperationProfessions and unit_data.OperationProfessions[profession] or (#mercs + 1)
if mercs[idx] then
idx = table.count(mercs) + 1
end
mercs[idx] = unit_data
end
return mercs
end
function GetOperationMercsListContext(sector, mode_param)
local operation = SectorOperations[mode_param.operation]
if mode_param.assign_merc then
local mercs = GetAvailableMercs(sector, operation, mode_param.profession)
local _, merc = next(mercs)
if operation.related_stat or operation.related_stat_2 or merc and operation:GetRelatedStat(merc) then
table.sort(mercs, function(a, b)
if not a then return false end
if not b then return true end
local _, val_a = operation:GetRelatedStat(a)
local _, val_b = operation:GetRelatedStat(b)
return val_a > val_b
end)
end
return {[1] = {mercs = mercs}}
else
local context = {}
for _, prof in ipairs(operation.Professions) do
local id = prof.id
local mercs = GetBusyMercsForList(sector, operation, id)
local sector_slots = operation:GetSectorSlots(id, sector)
local infinite_slots = sector_slots == -1
local all_mercs = #GetPlayerMercsInSector(sector.Id)
if sector_slots == -1 then -- infinite
if all_mercs>#mercs then -- there a re free mercs
mercs[#mercs + 1] = {class = "empty", prof = id}
end
else
for i = 1, (sector_slots or 0) do
mercs[i] = mercs[i] or {class = "empty", prof = id}
end
end
if #mercs <= 0 then
mercs[1] = {class = "empty", prof = id}
end
local occupied_slots = 0
for i=1,#mercs do
mercs[i] = mercs[i] or {class = "empty", prof = id}
if mercs[i].class ~= "empty" then
occupied_slots = occupied_slots + 1
end
end
local free_space = #mercs%6==0 and 0 or 6-(#mercs%6)
for i=1,free_space do
mercs[#mercs + 1] = {class = "free_space",}
end
context[#context + 1] = {
mercs = mercs,
occupied_slots = occupied_slots,
title = prof.display_name_plural_all_caps,
sector_id = sector.Id,
list_as_prof = id,
operation = mode_param.operation,
infinite_slots = infinite_slots,
free_space = free_space,
}
end
if operation.id == "MilitiaTraining" then
-- add militia list
local mercs = {}
local militia_squad_id = sector.militia_squad_id
local militia_squad = militia_squad_id and gv_Squads[militia_squad_id]
local count = {MilitiaRookie = 0,MilitiaVeteran = 0, MilitiaElite = 0} -- count already created and in the squad
for i,unit_id in ipairs(militia_squad and militia_squad.units) do
local class = gv_UnitData[unit_id].class
count[class] = count[class] + 1
end
if count.MilitiaRookie > 0 then
mercs[#mercs + 1] = {class = "MilitiaRookie", def = "MilitiaRookie", prof = "Militia", in_progress = false, click = false, count = count.MilitiaRookie}
end
if count.MilitiaVeteran > 0 then
mercs[#mercs + 1] = {class = "MilitiaVeteran", def = "MilitiaVeteran", prof = "Militia", in_progress = false, click = false, count = count.MilitiaVeteran}
end
if count.MilitiaElite > 0 then
mercs[#mercs +1 ] = {class = "MilitiaElite", def = "MilitiaElite", prof = "Militia", in_progress = false, click = false, count = count.MilitiaElite}
end
local trainers = GetOperationProfessionals(sector.Id, operation.id, "Trainer")
if #trainers>0 then
-- add in progress
local added_MilitiaRookie = 0
local added_MilitiaVeteran = 0
for i = 1, const.Satellite.MilitiaUnitsPerTraining do
if (added_MilitiaRookie + #(militia_squad and militia_squad.units or "")) < sector.MaxMilitia then
added_MilitiaRookie = added_MilitiaRookie + 1
else -- level up
if count.MilitiaRookie<=0 then
break
end
local units_def = table.find_value(mercs, "def", "MilitiaRookie")
if units_def then
units_def.count = units_def.count - 1
if units_def.count<=0 then
table.remove_value(mercs, "def", "MilitiaRookie")
end
end
count.MilitiaRookie = count.MilitiaRookie - 1
count.MilitiaVeteran = count.MilitiaVeteran + 1
added_MilitiaVeteran = added_MilitiaVeteran + 1
end
end
if added_MilitiaRookie>0 then
table.insert(mercs,1,{class = "MilitiaRookie", prof = "Militia", in_progress = true, click = false, count = added_MilitiaRookie})
end
if added_MilitiaVeteran > 0 then
table.insert(mercs,1, {class = "MilitiaVeteran", prev = "MilitiaRookie", prof = "Militia", in_progress = true, click = false, count = added_MilitiaVeteran})
end
end
-- for i=#mercs+1, sector.MaxMilitia do
-- mercs[i] = {class = "empty", prof = "Militia", click = false}
-- end
context[#context + 1] = {mercs = mercs, title = T(977391598484, "Militia"), click = false, operation = mode_param.operation}
end
return context
end
end
function FillTempDataOnOpen(sector, operation_id)
local context = GetOperationMercsListContext(sector, {operation = operation_id})
local operation = SectorOperations[operation_id]
local temp_table = {}
for _, prof in ipairs(operation.Professions) do
local profession = prof.id
local mercs = context[_].mercs
local costs = GatOperationCostsArray(sector.Id,SectorOperations[operation_id])
local idx = 0
for i, merc in ipairs(mercs) do
if merc.class~="empty" and merc.class~="free_space" then
idx = idx+1
temp_table[merc.session_id] = temp_table[merc.session_id] or {}
table.insert(temp_table[merc.session_id],
{operation_id,profession or false, costs[idx+1] or false,merc.OperationProfessions and merc.OperationProfessions[profession] or idx, false, IsPatient(merc) and merc.wounds_being_treated or false,
RestTimer = merc.RestTimer, TravelTime = merc.TravelTime, TravelTimerStart = merc.TravelTimerStart, Tiredness = merc.Tiredness, prev_Operation = operation_id})
end
end
end
sector.operations_temp_data = sector.operations_temp_data or {}
sector.operations_temp_data[operation_id] = temp_table
MercsOperationsFillTempData(sector, operation_id)
end
function GetOperationsInSector(sector_id)
local sector_operations = {}
local sector = gv_Sectors[sector_id]
if not sector then return sector_operations end
if sector.Side == "player1" or sector.Side == "player2" then
ForEachPresetInCampaign("SectorOperation", function(operation)
local id = operation.id
if operation:HasOperation(sector) then
local enabled, rollover = operation:IsEnabled(sector)
if enabled then
local idleling = GetOperationProfessionals(sector.Id, "Idle")
for _, prof in ipairs(operation.Professions) do
local mercs_available = GetAvailableMercs(sector, operation, prof.id)
local mercs_current = GetOperationProfessionals(sector.Id, operation.id)
if #idleling==0 and #mercs_available == 0 and #mercs_current == 0 then
enabled = false
rollover = T{776447291880, "No <name> available", name = prof.display_name}
break
end
end
end
if sector.started_operations and sector.started_operations[id] or
next(GetOperationProfessionals(sector_id, id))
then
rollover = ""
enabled = true
end
sector_operations[#sector_operations + 1] = {operation = operation, enabled = enabled , rollover = rollover, sector = sector_id}
end
end)
end
table.sort(sector_operations, function (a, b)
local operationA = a.operation
local operationB = b.operation
local k1, k2 = operationA.SortKey, operationB.SortKey
if operationA.Custom then k1 = k1 - 100 end
if operationB.Custom then k2 = k2 - 100 end
if k1 ~= k2 then
return k1 < k2
end
return operationA.id < operationB.id
end)
return sector_operations
end
local l_get_sector_operation_resource_amount
function GetSectorOperationResource(sector, item_id)
l_get_sector_operation_resource_amount = 0
--[[ -- sector inventory
local containers = sector.sector_inventory or empty_table
for cidx, container in ipairs(containers) do
if container[2] then -- is opened
local items = container[3] or empty_table
for idx, item in ipairs(items) do
if item.class == item_id then
l_get_sector_operation_resource_amount = l_get_sector_operation_resource_amount + (IsKindOf(item, "InventoryStack") and item.Amount or 1)
end
end
end
end
--]]
-- bags
local squads = GetSquadsInSector(sector.Id)
for _, s in ipairs(squads) do
local bag = GetSquadBag(s.UniqueId)
for i, item in ipairs(bag) do
if item.class == item_id then
l_get_sector_operation_resource_amount = l_get_sector_operation_resource_amount + (IsKindOf(item, "InventoryStack") and item.Amount or 1)
end
end
end
-- all mercs
local mercs = GetPlayerMercsInSector(sector.Id)
for _, id in ipairs(mercs) do
local unit = gv_UnitData[id]
unit:ForEachItemDef(item_id, function(item)
l_get_sector_operation_resource_amount = l_get_sector_operation_resource_amount + (IsKindOf(item, "InventoryStack") and item.Amount or 1)
end)
end
return l_get_sector_operation_resource_amount
end
function NetSyncEvents.PaySectorOperationResource(sector_id, item_id, count)
local left = count --TakeItemFromSectorInventory(sector_id, item_id, count)
if left > 0 then
TakeItemFromMercs(GetPlayerMercsInSector(sector_id), item_id, left)
end
InventoryUIRespawn()
ObjModified(gv_Sectors[sector_id])
ObjModified(sector_id)
end
function NetSyncEvents.RestoreSectorOperationResource(merc_id, item_id, count)
local merc = gv_UnitData[merc_id]
if not merc then return end
if not merc.Squad then return end --merc's contract has expired, he is no longer on the map or in a squad
local left = count
--bag
left = AddItemToSquadBag(merc.Squad, item_id, left)
-- merc
local sector = merc:GetSector()
if left>0 then
merc:ForEachItemDef(item_id, function(item, slot)
if item.Amount < item.MaxStacks then
local add = Min(left, item.MaxStacks - item.Amount)
item.Amount = item.Amount + add
left = left - add
if left == 0 then
return "break"
end
end
end)
end
local restore_to_merc = true
if left > 0 then
local item = PlaceInventoryItem(item_id)
item.Amount = left
left = 0
local pos, reason = merc:AddItem("Inventory", item)
if not pos then
--AddToSectorInventory(sector.Id, {item})
restore_to_merc = false
end
end
if restore_to_merc then
local res = SectorOperationResouces[item_id]
CombatLog("short", T{173792230953, " Restored <count> <resource> to <Nick>.", count = count - left, resource = res.name, merc})
end
InventoryUIRespawn()
ObjModified(sector)
ObjModified(sector.Id)
end
function PaySectorOperationResource(sector_id, item_id, count)
local isSync = IsGameTimeThread()
if isSync then -- PayOperation
NetSyncEvents.PaySectorOperationResource(sector_id, item_id, count)
else -- ModifyWeaponDlg
NetSyncEvent("PaySectorOperationResource", sector_id, item_id, count)
end
end
function RestoreSectorOperationResource(merc, item_id, count)
local isSync = IsGameTimeThread()
if isSync then -- PayOperation
NetSyncEvents.RestoreSectorOperationResource(merc.session_id, item_id, count)
else -- ModifyWeaponDlg
NetSyncEvent("RestoreSectorOperationResource", merc.session_id, item_id, count)
end
end
function CanPayOperation(cost, sector)
for _, c in ipairs(cost or empty_table) do
local value = c.value
if CheatEnabled("FreeParts") and c.resource== "Parts" then
value = 0
end
local res_t = SectorOperationResouces[c.resource]
local total = res_t and res_t.current(sector) or 0
if value and value > total then
return false
end
end
return true
end
function PayOperation(cost, sector)
for _, c in ipairs(cost or empty_table) do
local res_t = SectorOperationResouces[c.resource]
res_t.pay(sector, c.value)
end
end
function GetCustomOperations()
local operations = table.keys(SectorOperations, true)
local custom = {}
for _, ac in ipairs(operations) do
if SectorOperations[ac].Custom then
custom[#custom + 1] = ac
end
end
return custom
end
function GetOperationProfessionals(sector_id, operation, profession, exclude_unit_id)
local profs = {}
local mercs = GetPlayerMercsInSector(sector_id)
for _, id in ipairs(mercs) do
local unit = gv_UnitData[id]
if (not exclude_unit_id or id~=exclude_unit_id )and (not operation or operation == unit.Operation) and (not profession or unit.OperationProfessions and unit.OperationProfessions[profession] or unit.OperationProfession==profession) then
profs[#profs + 1] = unit
end
end
return profs
end
function GetOperationProfessionalsGroupedByProfession(sector_id, operation_id)
local mercs = GetOperationProfessionals(sector_id, operation_id)
if #mercs == 0 then return empty_table end
if not operation_id then
local grouped = {}
for i, m in ipairs(mercs) do
local operation = m.Operation
local profession = m.OperationProfession or ""
if not grouped[operation] then grouped[operation] = {} end
if not grouped[operation][profession] then grouped[operation][profession] = {} end
local arr = grouped[operation][profession]
arr[#arr + 1] = m
end
return grouped
end
local operationPreset = SectorOperations[operation_id]
local professions = operationPreset.Professions
if not professions or #professions == 0 then return mercs end
local grouped = {}
for i, profs in ipairs(professions) do
local id = profs.id
local profsArr = {}
for i, m in ipairs(mercs) do
if m.OperationProfessions and m.OperationProfessions[id] or m.OperationProfession==id then
profsArr[#profsArr + 1] = m
end
end
grouped[id] = profsArr
end
return grouped
end
function GetOperationCostText(cost, img_tag, no_sign, no_name)
local cost_t = {}
for _, c in ipairs(cost or empty_table) do
if c.value >= 0 then
local t = SectorOperationResouces[c.resource]
local display_value = c.value
if not no_sign then
display_value = -display_value
end
local texts = {Untranslated(display_value)}
if not no_name then
texts[#texts+1] = t.name
end
if img_tag and t.icon then
texts[#texts+1] = Untranslated(string.format("<image %s 1700 %d %d %d>", t.icon, GetRGB(GameColors.G)))
end
cost_t[#cost_t + 1] = table.concat(texts, "")
end
end
return table.concat(cost_t, ", ")
end
function GatOperationCostsArray(sector_id, operation)
local operations
if operation == "all" then
operations = GetOperationsInSector(sector_id)
else
operations = {{operation = operation}}
end
local costs = {}
for _, operation_data in ipairs(operations) do
local sector_operation = operation_data.operation
local amercs = GetOperationProfessionals(sector_id, sector_operation.id)
local ocosts = GetOperationCostsProcessed(amercs,sector_operation, false, "both", "refund")
table.iappend(costs, ocosts)
end
return costs
end
function GetActorOperationTimeLeft(merc, operation, profession)
local sector = merc:GetSector()
local operation = SectorOperations[operation]
local progress_per_tick = operation:ProgressPerTick(merc, profession)
if CheatEnabled("FastActivity") then
progress_per_tick = progress_per_tick*100
end
local left_progress = operation:ProgressCompleteThreshold(merc, sector, profession) - operation:ProgressCurrent(merc, sector, profession)
local ticks_left = progress_per_tick==0 and 0 or left_progress / progress_per_tick
if left_progress > 0 then
ticks_left = Max(ticks_left, 1)
end
return ticks_left*const.Satellite.Tick
end
-- treat wounds
function GetPatientHealingTimeLeft(merc, ativity_id)
return GetActorOperationTimeLeft(merc, ativity_id or "TreatWounds", "Patient")
end
function TreatWoundsTimeLeft(context, operation_id)
if context.list_as_prof == "Patient" and (context.force or IsPatient(context.merc)) then
return GetPatientHealingTimeLeft(context.merc, operation_id)
else
local slowest = 0
for _, unit in ipairs(GetOperationProfessionals(context.merc:GetSector().Id, operation_id, "Patient")) do
slowest = Max(slowest, GetPatientHealingTimeLeft(unit, operation_id))
end
return slowest
end
end
function GetHealingBonus(sector, operation_id)
local bonus = 0
local doctors = GetOperationProfessionals(sector.Id, operation_id, "Doctor")
if #doctors>0 then
bonus = 100
local forgiving_mode = IsGameRuleActive("ForgivingMode")
local min_stat_boost = GameRuleDefs.ForgivingMode:ResolveValue("MinStatBoost") or 0
for _, unit in ipairs(doctors) do
local stat = unit.Medical
if forgiving_mode and stat < min_stat_boost then
stat = stat + (min_stat_boost-stat)/2
end
bonus = bonus + stat * 2
end
end
return bonus
end
function GetSumOperationStats(mercs, stat, stat_multiplier)
-- add progress
local forgiving_mode = IsGameRuleActive("ForgivingMode")
local min_stat_boost = GameRuleDefs.ForgivingMode:ResolveValue("MinStatBoost") or 0
local sum_stat = 0
local has_perk = false
for _, m in ipairs( mercs) do
local stat_val = m[stat] or 0
if forgiving_mode and stat_val < min_stat_boost then
stat_val = stat_val + (min_stat_boost-stat_val)/2
end
stat_val = MulDivRound(stat_val, stat_multiplier, 100)
if HasPerk(m, "JackOfAllTrades") then
has_perk = true
end
sum_stat = sum_stat + stat_val
end
if has_perk then
local mod = CharacterEffectDefs.JackOfAllTrades:ResolveValue("activityDurationMod")
sum_stat = sum_stat + MulDivRound(sum_stat, mod, 100)
end
return sum_stat
end
function IsDoctor(merc)
return merc.Operation == "TreatWounds" and merc.OperationProfessions and merc.OperationProfessions["Doctor"]
end
function IsPatient(merc)
return merc and IsOperationHealing(merc.Operation) and (merc.OperationProfessions and merc.OperationProfessions["Patient"])
end
function SectorOperationCountPatients(sector_id, except_unit_id)
local count = 0
for _, unit_data in ipairs(GetOperationProfessionals(sector_id, "TreatWounds")) do
if unit_data.session_id~=except_unit_id and IsPatient(unit_data) then
count = count + 1
end
end
return count
end
function IsOperationHealing(operation_id)
local operationPreset = SectorOperations[operation_id]
if not operationPreset then
assert(false, "No such operation: " .. operation_id)
return false
end
return operationPreset and operationPreset.operation_type and operationPreset.operation_type.Healing
end
function UnitHealPerTick(merc, pertick_progress, heal_wound_threshold, dont_log)
merc.wounds_being_treated = merc.wounds_being_treated>0 and merc.wounds_being_treated or PatientGetWoundedStacks(merc)
if merc.wounds_being_treated >0 then
local progress_per_tick = pertick_progress
if CheatEnabled("FastActivity") then
progress_per_tick = progress_per_tick*100
end
PatientAddHealWoundProgress(merc, progress_per_tick, heal_wound_threshold, dont_log)
end
end
function PatientAddHealWoundProgress(merc, progress, max_progress, dont_log)
if IsGameRuleActive("ForgivingMode") then
-- Boost resting/traveling and R&R heal speed by 25%.
local boost = GameRuleDefs.ForgivingMode:ResolveValue("HealingProgressBoost") or 0
progress = MulDivRound(progress, 100 + boost, 100)
end
merc.heal_wound_progress = merc.heal_wound_progress + progress
local wounds_healed = false
while merc.heal_wound_progress > max_progress do
merc:RemoveStatusEffect("Wounded", 1, merc.Operation)
merc.wounds_being_treated = merc.wounds_being_treated - 1
if merc.wounds_being_treated>0 then
local effect = merc:GetStatusEffect("Wounded")
merc.wounds_being_treated = Min(merc.wounds_being_treated, effect and effect.stacks or 0)
end
merc.heal_wound_progress = merc.heal_wound_progress - max_progress
wounds_healed = true
end
if wounds_healed and not dont_log then
if merc.OperationProfession ~= "Doctor" then
local context = {merc = merc}
if merc.Operation ~= "TreatWounds" or
(merc.Operation == "TreatWounds" and TreatWoundsTimeLeft(context,merc.operation) > 0) then
PlayVoiceResponse(merc, "HealReceivedSatView")
end
end
end
if IsPatientReady(merc) then
if merc.heal_wound_progress > 0 then
merc:SetTired(Min(merc.Tiredness, const.utNormal))
end
merc.heal_wound_progress = 0
merc.wounds_being_treated = 0
elseif wounds_healed and not dont_log then
CombatLog("short", T{394097034872, "<merc_name> was <em>cured of a wound</em>.", merc_name = merc.Nick})
end
end
function IsPatientReady(merc)
return not merc:HasStatusEffect("Wounded") or merc.wounds_being_treated == 0
end
function PatientGetWoundedStacks(merc)
local idx = merc:HasStatusEffect("Wounded")
local effect = idx and merc.StatusEffects[idx]
return effect and effect.stacks or 0
end
function PatientGetWoundsBeingTreated(merc)
return IsPatient(merc) and (merc.wounds_being_treated and merc.wounds_being_treated>0) and merc.wounds_being_treated or PatientGetWoundedStacks(merc)
end
function RecalcOperationETAs(sector,operation, stopped)
local units = GetOperationProfessionals(sector.Id, operation)
local updated
for _, unit_data in ipairs(units) do
local left = GetOperationTimerETA(unit_data) or 0
NetUpdateHash("RecalcOperationETAs", unit_data.session_id, left)
if stopped or (unit_data.OperationInitialETA or 0) < left then -- if the initial ETA was not bigger, the timer will work ok, it will just move faster
if not stopped or IsCraftOperation(operation) then
unit_data.OperationInitialETA = left
updated = true
end
Msg("OperationTimeUpdated", unit_data, operation)
end
end
if not updated and operation=="RepairItems" and next(units) then
Msg("OperationTimeUpdated", units[1], operation)
end
end
function GetUnitStatsComboTranslated(except_stat)
local items = {}
local props = UnitPropertiesStats:GetProperties()
for _, prop in ipairs(props) do
if prop.category == "Stats" and except_stat~=prop.id then
items[#items + 1] = {name = prop.name, value = prop.id}
end
end
return items
end
------------------------------ UI------------------
local tile_size = 72
local tile_size_h = 72
local tile_size_rollover = 146
function SectorOperationItems_ItemsCount(tbl)
local count= 0
for i,itm_data in ipairs(tbl) do
local itm = SectorOperation_FindItemDef(itm_data)
count = count + (itm:IsLargeItem() and 2 or 1)
end
return count
end
function SectorOperationItems_GetTables(sector_id, operation_id)
local sector = gv_Sectors[sector_id]
if IsCraftOperation(operation_id) then
local quid, allid = GetCraftOperationListsIds(operation_id)
local tbl = GetCraftOperationQueueTable(sector, operation_id)
return tbl, operation_id~="RepairItems" and _G["g_Recipes"..operation_id] or sector[allid]
end
end
DefineClass.XOperationItemTile = {
__parents = {"XInventoryTile"},
slot_image = "UI/Icons/Operations/repair_item",
IdNode = true,
MinWidth = tile_size_rollover,
MaxWidth = tile_size_rollover,
MinHeight = tile_size_rollover,
MaxHeight = tile_size_rollover,
}
function XOperationItemTile:Init()
local image = XImage:new({
MinWidth = tile_size,
MaxWidth = tile_size,
MinHeight = tile_size_h,
MaxHeight = tile_size_h,
Id = "idBackImage",
Image = "UI/Inventory/T_Backpack_Slot_Small_Empty.tga",
ImageColor = 0xFFc3bdac,
},
self)
if self.slot_image then
local imgslot = XImage:new({
MinWidth = tile_size,
MaxWidth = tile_size,
MinHeight = tile_size_h,
MaxHeight = tile_size_h,
ImageScale = point(600,600),
Dock = "box",
Id = "idEqSlotImage",
ImageColor = GameColors.A,
Transparency = 110,
},
self)
imgslot:SetImage(self.slot_image)
image:SetImage("UI/Inventory/T_Backpack_Slot_Small.tga")
image:SetImageColor(RGB(255,255,255))
end
local rollover_image = XImage:new({
MinWidth = tile_size_rollover,
MaxWidth = tile_size_rollover,
MinHeight = tile_size_h,
MaxHeight = tile_size_h,
Id = "idRollover",
Image = "UI/Inventory/T_Backpack_Slot_Small_Hover.tga",
ImageColor = 0xFFc3bdac,
Visible = false,
ImageFit = "width",
},
self)
rollover_image:SetVisible(false)
end
function XOperationItemTile:OnSetRollover()
end
DefineClass.XActivityItem = {
__parents = {"XInventoryItem"},
IdNode = true,
}
function XActivityItem:Init()
self.idItemPad:SetImageFit("none")
local item = self:GetContext()
local item_equipimg = XTemplateSpawn("XImage", self.idItemImg)
item_equipimg:SetHAlign("right")
item_equipimg:SetVAlign("bottom")
--item_equipimg:SetImageFit("width")
item_equipimg:SetId("idItemEqImg")
item_equipimg:SetUseClipBox(false)
item_equipimg:SetHandleMouse(false)
--item_equipimg:SetImage(IsEquipSlot(self.slot) and "UI/Icons/Operations/equipped" or "UI/Icons/Operations/backpack")
item_equipimg:SetScaleModifier(point(600,600))
item_equipimg:SetMargins(box(0,0,-15,-15))
local roll_ctrl = self.idRollover
roll_ctrl:SetScaleModifier(point(700,700))
end
function XActivityItem:OnContextUpdate(item,...)
XInventoryItem.OnContextUpdate(self, item,...)
local w, h = item:GetUIWidth(), item:GetUIHeight()
self:SetMinWidth(tile_size*w +( w>1 and 7 or 0))
self:SetMaxWidth(tile_size*w +( w>1 and 7 or 0))
self:SetMinHeight(tile_size*h)
self:SetMaxHeight(tile_size*h)
self:SetGridWidth(w)
self:SetGridHeight(h)
if item.SubIcon and item.SubIcon~= "" then
self.idItemImg.idItemSubImg:SetScaleModifier(point(600,600))
end
local img_mod = rawget(self.idItemImg, "idItemModImg")
if img_mod then
-- img_mod:SetVisible(false)
img_mod:SetScaleModifier(point(550,550))
img_mod:SetMargins(box(-18,-18, 0 , 0))
end
self.idItemImg.idItemEqImg:SetVisible(IsEquipSlot(self.slot) or item and item.owner)
self.idItemImg.idItemEqImg:SetImage(IsEquipSlot(self.slot) and "UI/Icons/Operations/equipped" or "UI/Icons/Operations/backpack")
local itm = rawget(self, "item")
if itm then
self.idText:SetText(T{641971138327, "<style InventoryItemsCountMax><amount></style>", amount = itm.amount})
end
--self.idText:SetVisible(false)
local ammo_type = rawget(self.idItemImg,"idItemAmmoTypeImg")
if ammo_type then
--ammo_type:SetImageScale(point(700,700))
ammo_type:SetMargins(box(-18,-18, 0 , 0))
end
end
function XActivityItem:OnDropEnter(drag_win, pt, drag_source_win)
end
function XActivityItem:OnDropLeave(drag_win, pt, source)
end
-----------------------------for repair itemsactivity----
function TableWithItemsToNet(t)
local ret = {}
for i, inv_slot in ipairs(t or empty_table) do
ret[i] = {}
local rr = ret[i]
for ii, item in ipairs(inv_slot) do
if item then
rr[ii] = item.id
end
end
for k, v in pairs(inv_slot) do
if not rr[k] then
rr[k] = v
end
end
end
return ret
end
function TableWithItemsFromNet(t)
for i, inv_slot in ipairs(t) do
for ii, item_id in ipairs(inv_slot) do
if g_ItemIdToItem[item_id] then
inv_slot[ii] = g_ItemIdToItem[item_id]
end
end
end
return t
end
DefineClass.XDragContextWindow = {
__parents = { "XContentTemplate", "XDragAndDropControl" },
properties = {
{ category = "General", id = "slot_name", name = "Slot Name", editor = "text", default = "", },
{ category = "General", id = "disable_drag", name = "Disable Drag", editor = "bool", default = false, },
},
ClickToDrag = true,
ClickToDrop = true,
}
function XDragContextWindow:OnMouseButtonClick(pos, button)
return XDragAndDropControl.OnMouseButtonClick(self, pos, button)
end
function XDragContextWindow:OnMouseButtonDoubleClick(pos, button)
if button == "L" then
--if not IsMouseViaGamepadActive() then
local ctrl = self.drag_win
if not ctrl then return "break" end
if not ctrl.idItem:GetEnabled() then return "break" end
local operation_id = self.context[1].operation
local dlg = GetDialog(self)
local dlg_context = dlg and dlg.context
local sector = dlg_context
local sector_id = dlg_context.Id
local is_repair = operation_id=="RepairItems"
local search_id = is_repair and "id" or "item_id"
local serch_context = is_repair and ctrl.context.id or ctrl.context.class
local queue, all = SectorOperationItems_GetTables(sector_id,operation_id )
if self.Id=="idAllItems" then
local item, idx = table.find_value(all, search_id, serch_context)
local itm = item and SectorOperationRepairItems_GetItemFromData(item)
local itm_width = itm and itm:IsLargeItem() and 2 or 1
if SectorOperationItems_ItemsCount(queue) + itm_width <= 9 then
if is_repair then
table.remove(all,idx)
end
table.insert(queue,item)
end
else
local item, idx = table.find_value(queue, search_id, serch_context)
table.remove(queue,idx)
if is_repair then
table.insert(all,item)
end
end
self.drag_win:delete()
self.drag_win = false
self:StopDrag()
SectorOperationValidateItemsToCraft(sector_id, operation_id)
NetSyncEvent("SectorOperationItemsUpdateLists", sector_id,operation_id, TableWithItemsToNet(all), TableWithItemsToNet(queue))
SectorOperation_ItemsUpdateItemLists(dlg:ResolveId("node"))
return "break"
---end
end
end
function XDragContextWindow:OnDragStart(pt,button)
if self.disable_drag then return false end
for i, wnd in ipairs(self) do
if wnd:MouseInWindow(pt) and not IsKindOf(wnd.idItem, "XOperationItemTile") and wnd.idItem:GetEnabled() then
return wnd
end
end
return false
end
function XDragContextWindow:OnHoldDown(pt, button)end
function XDragContextWindow:IsDropTarget(drag_win, pt, source)return not self.disable_drag end
function XDragContextWindow:OnDrop(drag_win, pt, drag_source_win)end
function XDragContextWindow:OnDropEnter(drag_win, pt, drag_source_win)end
function XDragContextWindow:OnDropLeave(drag_win, pt, source)end
function XDragContextWindow:OnDragDrop(target, drag_win, drop_res, pt)
if not drag_win or drag_win == target then
return
end
target = target or self
local self_slot = self.slot_name
local target_slot = target.slot_name
local target_wnd = target
for i, wnd in ipairs(target) do
if wnd:MouseInWindow(pt) then
target_wnd = wnd
break
end
end
local operation_id = self.context[1].operation
local is_repair = operation_id=="RepairItems"
local dlg = GetDialog(self)or GetDialog(target_wnd)
local dlg_context = dlg and dlg.context
target_wnd = target_wnd or drag_win
local context = drag_win.context
local target_context = target_wnd:GetContext()
local sector = dlg_context
local sector_id = dlg_context.Id
local self_queue, target_queue
local a_all = SectorOperationItems_GetAllItems(sector_id, operation_id)
local a_queue = SectorOperationItems_GetItemsQueue(sector_id, operation_id)
if self_slot=="ItemsQueue" then
self_queue = a_queue
elseif self_slot=="AllItems" then
self_queue = a_all or {}
end
if target_slot=="ItemsQueue" then
target_queue = a_queue
elseif target_slot=="AllItems" then
target_queue = a_all or {}
end
local cur_idx = is_repair and table.find(self_queue,"id", context.id) or table.find(self_queue,"item_id", context.class)
local target_idx = is_repair and table.find(target_queue, "id", target_context.id) or table.find(target_queue,"item_id", target_context.class)
local itm = self_queue[cur_idx]
local item = itm and SectorOperationRepairItems_GetItemFromData(itm)
local itm_width = is_repair and (item and item:IsLargeItem() and 2 or 1) or 1
if self_slot==target_slot then
if cur_idx then
if target_idx then
target_queue[cur_idx], target_queue[target_idx] = target_queue[target_idx],target_queue[cur_idx]
else
local itm = table.remove(self_queue,cur_idx)
target_queue[#target_queue+1] = itm
end
end
elseif target_slot~="ItemsQueue" or (SectorOperationItems_ItemsCount(target_queue) + itm_width) <= 9 then
local itm
if is_repair or self_slot=="ItemsQueue" then
itm = table.remove(self_queue,cur_idx)
else
itm = table.copy(self_queue[cur_idx])
end
if is_repair or target_slot=="ItemsQueue" then
if not target_idx then
target_queue[#target_queue+1] = itm
else
table.insert(target_queue,target_idx,itm)
end
end
end
local s_queue, s_all = SectorOperationItems_GetTables(sector_id, operation_id)
local all = target_slot=="AllItems" and target_queue or self_slot=="AllItems" and self_queue or s_all
local queued = target_slot=="ItemsQueue" and target_queue or self_slot=="ItemsQueue" and self_queue or s_queue
drag_win:delete()
SectorOperationValidateItemsToCraft(sector_id, operation_id)
NetSyncEvent("SectorOperationItemsUpdateLists", sector_id,operation_id, TableWithItemsToNet(all), TableWithItemsToNet(queued))
local mercs = GetOperationProfessionals(sector_id, operation_id)
local eta = next(mercs) and GetOperationTimeLeft(mercs[1], operation_id) or 0
local timeLeft = eta and Game.CampaignTime + eta
AddTimelineEvent("activity-temp", timeLeft, "operation", { operationId = operation_id, sectorId = sector_id})
self:RespawnContent()
target:RespawnContent()
local node = self:ResolveId("node")
node:OnContextUpdate(node:GetContext())
local node = target:ResolveId("node")
node:OnContextUpdate(node:GetContext())
ObjModified(target_queue)
ObjModified(self_queue)
end
function SectorOperation_StudentStatDiff(sector_id, student, teachers)
local teachers = teachers or GetOperationProfessionals(sector_id, "TrainMercs", "Teacher")
local sector = gv_Sectors[sector_id]
local avg_teachers_stat = table.avg(teachers,sector.training_stat)
if not next(teachers) then
local operation = SectorOperations["TrainMercs"]
avg_teachers_stat = operation:ResolveValue("SoloTrainingStat")
end
local student_stat = student[sector.training_stat]
local diff = avg_teachers_stat-student_stat
if diff<20 then
return 1
elseif diff<40 then
return 2
else
return 3
end
end
function SectorOperation_ItemsCalcRes(sector_id, operation_id)
local queued_items = SectorOperationItems_GetItemsQueue(sector_id, operation_id)
local operation = SectorOperations[operation_id]
local parts = 0
if operation_id=="RepairItems" then
local free_repair = operation:ResolveValue("free_repair")
local restore_condition_per_Part = operation:ResolveValue("restore_condition_per_Part")
local parts_per_step = operation:ResolveValue("parts_per_step")
for _, item_data in ipairs(queued_items) do
local item = SectorOperationRepairItems_GetItemFromData(item_data)
local cur_cond = item and item.Condition or 0
local max_condition = item and item:GetMaxCondition() or 0
local to_repair = max_condition - cur_cond
--use parts
if to_repair > 0 then
if to_repair <= free_repair then
else
local border = 0
while border<max_condition do
local diff = restore_condition_per_Part
border = border + diff
if cur_cond<border and cur_cond+diff>=border then
parts = parts + parts_per_step
cur_cond = cur_cond + diff
if max_condition - cur_cond<=free_repair then
break
end
end
end
end
end
end
end
if IsCraftOperationId(operation_id) then
for _, item_data in ipairs(queued_items) do
local item = CraftOperationsRecipes[item_data.recipe]
for __,ing in ipairs(item.Ingredients) do
if ing.item == "Parts" then
parts = parts + ing.amount
end
end
end
end
return parts
end
function SectorOperation_SquadOnMove(sector_id, newsquads)
local mercs = GetOperationProfessionals(sector_id, "RepairItems")
if #mercs<=0 then return end
local queued = SectorOperationItems_GetItemsQueue(sector_id, "RepairItems")
for i = #queued, 1, -1 do
local item = SectorOperationRepairItems_GetItemFromData(queued[i])
if item and item.owner then
local unit = gv_UnitData[item.owner]
local sqId = unit and unit.Squad
if sqId and table.find(newsquads, sqId) then
table.remove(queued, i)
end
end
end
local all = SectorOperationItems_GetAllItems(sector_id, "RepairItems")
for i = #all, 1, -1 do
local item = SectorOperationRepairItems_GetItemFromData(all[i])
if item and item.owner then
local unit = gv_UnitData[item.owner]
local sqId = unit and unit.Squad
if sqId and table.find(newsquads, sqId) then
table.remove(all, i)
end
end
end
NetSyncEvent("ChangeSectorOperationItemsOrder",sector_id, "RepairItems", TableWithItemsToNet(all), TableWithItemsToNet(queued))
end
local Additionalds = {prev_start_time = true ,all_items = true,queued_items = true,training_stat = true, operation_id = true}
function SectorOperations_IsValidMercId(m_id)
return not Additionalds[m_id]
end
function SectorOperations_DataHasDifference(prev, cur, operation_id, sector)
-- mercs and professions
for m_id, m_data in pairs(prev) do
if SectorOperations_IsValidMercId(m_id) then
if not cur[m_id] then
return true
end
local cur = cur[m_id]
for i, tt_merc in ipairs(m_data) do
local cur = cur[i]
if tt_merc.prev_Operation=="Idle" then
return false
end
for id,tdata in ipairs(tt_merc) do
local idx = id
--if idx>=3 then idx = idx+1 end
if cur[idx]==nil or cur[idx]~=tdata then
return true
end
end
end
end
end
for m_id, m_data in pairs(cur) do
if SectorOperations_IsValidMercId(m_id) then
if not prev[m_id] then
return true
end
end
end
-- repair items
if IsCraftOperation(operation_id) then
if prev.all_items and not sector.sector_repair_items
or sector.sector_repair_items and not prev.all_items
or prev.queued_items and sector.sector_repair_items_queued
or sector.sector_repair_items_queued and not prev.queued_items
then
return true
end
if #(prev.queued_items or empty_table)~=#(sector.sector_repair_items_queued or empty_table) then
return true
end
for i, data in ipairs(prev.queued_items) do
if not table.find(sector.sector_repair_items_queued, "id", data.id) then
return true
end
end
end
-- trainig mercs stat
if prev.training_stat~=sector.training_stat and operation_id == "TrainMercs" then
return true
end
return false
end
function SectorOperations_InterruptCurrent(sector, operation_id, reason)
-- interrupt the last set
local mercs = GetOperationProfessionals(sector.Id, operation_id)
local costs = {}
local costs = GatOperationCostsArray(sector.Id,SectorOperations[operation_id])
RemoveTimelineEvent("activity-temp")
-- id = "sector-activity-randr-"..ud.session_id
for i, merc in ipairs(mercs) do
local event_id = GetOperationEventId(merc, operation_id)
RemoveTimelineEvent(event_id)
NetSyncEvent("RestoreOperationCost", merc.session_id, costs[i])
end
NetSyncEvent("InterruptSectorOperation", sector.Id,operation_id, reason)
sector.operations_temp_data[operation_id] = false
end
function SectorOperations_RestorePrev(host, sector, operation_id, prev_time)
if not sector.operations_prev_data then return end
if sector.operations_prev_data.operation_id~=operation_id then
sector.operations_prev_data = false
return
end
local prev_op = prev_time
local temp = table.copy(sector.operations_prev_data)
if prev_op and operation_id=="TrainMercs" then
sector.training_stat = sector.operations_prev_data.training_stat
end
-- restore prev activity
for m_id, merc_data in pairs(temp) do
if SectorOperations_IsValidMercId(m_id) then
for i,tt_merc_prof in ipairs(merc_data) do
table.remove(tt_merc_prof,3) -- remove cost
local unit_data = gv_UnitData[m_id]
if merc_data[1].prev_Operation=="Idle" then
NetSyncEvent("MercSetOperationIdle",m_id,merc_data[1].Tiredness,merc_data[1].RestTimer, merc_data[1].TravelTime, merc_data[1].TravelTimerStart )
elseif prev_op then
TryMercsSetOperation(host, {unit_data}, table.unpack(tt_merc_prof))
end
end
end
end
if prev_op and IsCraftOperation(operation_id) then
if operation_id==temp.operation_id then
NetSyncEvent("SectorOperationItemsUpdateLists", sector.Id, operation_id, TableWithItemsToNet(temp and temp.all_items), TableWithItemsToNet(temp and temp.queued_items))
end
end -- not sync.. maybe works cuz game is paused
local time = temp.prev_start_time or prev_time or Game.CampaignTime
if prev_op and sector.started_operations and sector.started_operations[operation_id] then
sector.started_operations[operation_id]= time
end
sector.operations_prev_data = false
if prev_op then
NetSyncEvent("StartOperation", sector.Id, operation_id, time,sector.training_stat)
end
end
function SavegameSessionDataFixups.SectorActivityRenameToOperations(data, meta)
if meta and meta.lua_revision > 330550 then return end
for id, sector in pairs(data.gvars.gv_Sectors) do
local started = rawget(sector,"started_activities")
if started then
rawset(sector,"started_activities", nil)
sector.started_operations = started
end
local custom = rawget(sector,"custom_activities")
if custom then
rawset(sector,"custom_activities", nil)
sector.custom_operations = custom
end
end
for _, data in ipairs(data.gvars.gv_Timeline) do
local context = data.context
if data.typ=="activity" and context.activityId then
context.operationId = context.activityId
context.activityId = nil
data.typ = "operation"
end
end
for session_id, unit_data in pairs(data.gvars.gv_UnitData) do
if IsMerc(unit_data) then
local unit = g_Units[session_id]
local activity = rawget(unit_data,"Activity")
if activity then
unit_data.Operation = activity
unit_data.Activity = nil
if unit then
unit.Operation = activity
unit.Activity = nil
end
end
local eta = rawget(unit_data,"ActivityInitialETA")
if eta then
unit_data.OperationInitialETA = eta
unit_data.ActivityInitialETA = nil
if unit then
unit.OperationInitialETA = eta
unit.ActivityInitialETA = nil
end
end
local prof = rawget(unit_data,"ActivityProfession")
if prof then
unit_data.OperationProfession = prof
unit_data.ActivityProfession = nil
if unit then
unit.OperationProfession = prof
unit.ActivityProfession = nil
end
end
local profs = rawget(unit_data,"ActivityProfessions")
if profs then
unit_data.OperationProfessions = profs
unit_data.ActivityProfessions = nil
if unit then
unit.OperationProfessions = profs
unit.ActivityProfessions = nil
end
end
end
end
end