mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
This PR partially addresses #3313 by: - Tracking DCS hit events and storing the unit hit point updates in state.json - Adding logic to kill aircraft when the hit points is reduced to 1, as opposed to the DCS logic of hit points to 0. This behavior allows Liberation to track deaths to parked aircraft, which are uncontrolled and seem to have different damage logic in DCS. - Tracking damage to TheaterGroundObjects across turns and killing the unit when the unit's hitpoints reduces to 1 or lower. Intention is to build on this PR by also tracking front line objects and statics (buildings). However, larger refactoring is required and so splitting those into a separate PR.
208 lines
7.8 KiB
Lua
208 lines
7.8 KiB
Lua
-- the state.json file will be updated according to this schedule, and also on each destruction or capture event
|
|
local WRITESTATE_SCHEDULE_IN_SECONDS = 60
|
|
|
|
logger = mist.Logger:new("DCSLiberation", "info")
|
|
logger:info("Check that json.lua is loaded : json = "..tostring(json))
|
|
|
|
crash_events = {} -- killed aircraft will be added via S_EVENT_CRASH event
|
|
dead_events = {} -- killed units will be added via S_EVENT_DEAD event
|
|
unit_lost_events = {} -- killed units will be added via S_EVENT_UNIT_LOST
|
|
kill_events = {} -- killed units will be added via S_EVENT_KILL
|
|
base_capture_events = {}
|
|
destroyed_objects_positions = {} -- will be added via S_EVENT_DEAD event
|
|
killed_ground_units = {} -- keep track of static ground object deaths
|
|
unit_hit_point_updates = {} -- stores updates to unit hit points, triggered by S_EVENT_HIT
|
|
mission_ended = false
|
|
|
|
local function ends_with(str, ending)
|
|
return ending == "" or str:sub(-#ending) == ending
|
|
end
|
|
|
|
local function messageAll(message)
|
|
local msg = {}
|
|
msg.text = message
|
|
msg.displayTime = 25
|
|
msg.msgFor = {coa = {'all'}}
|
|
mist.message.add(msg)
|
|
end
|
|
|
|
function write_state()
|
|
local _debriefing_file_location = debriefing_file_location
|
|
if not debriefing_file_location then
|
|
_debriefing_file_location = "[nil]"
|
|
end
|
|
|
|
local fp = io.open(_debriefing_file_location, 'w')
|
|
local game_state = {
|
|
["crash_events"] = crash_events,
|
|
["dead_events"] = dead_events,
|
|
["base_capture_events"] = base_capture_events,
|
|
["unit_lost_events"] = unit_lost_events,
|
|
["kill_events"] = kill_events,
|
|
["mission_ended"] = mission_ended,
|
|
["destroyed_objects_positions"] = destroyed_objects_positions,
|
|
["killed_ground_units"] = killed_ground_units,
|
|
["unit_hit_point_updates"] = unit_hit_point_updates,
|
|
}
|
|
if not json then
|
|
local message = string.format("Unable to save DCS Liberation state to %s, JSON library is not loaded !", _debriefing_file_location)
|
|
logger:error(message)
|
|
messageAll(message)
|
|
end
|
|
fp:write(json:encode(game_state))
|
|
fp:close()
|
|
end
|
|
|
|
local function canWrite(name)
|
|
local f = io.open(name, "w")
|
|
if f then
|
|
f:close()
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function testDebriefingFilePath(folderPath, folderName, useCurrentStamping)
|
|
if folderPath then
|
|
local filePath = nil
|
|
if not ends_with(folderPath, "\\") then
|
|
folderPath = folderPath .. "\\"
|
|
end
|
|
if useCurrentStamping then
|
|
filePath = string.format("%sstate-%s.json",folderPath, tostring(os.time()))
|
|
else
|
|
filePath = string.format("%sstate.json",folderPath)
|
|
end
|
|
local isOk = canWrite(filePath)
|
|
if isOk then
|
|
logger:info(string.format("The state.json file will be created in %s : (%s)",folderName, filePath))
|
|
return filePath
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function discoverDebriefingFilePath()
|
|
-- establish a search pattern into the following modes
|
|
-- 1. Environment variable LIBERATION_EXPORT_DIR, to support dedicated server hosting
|
|
-- 2. Embedded DCS Liberation dcsLiberation.installPath (set by the app to its install path), to support locally hosted single player
|
|
-- 3. System temporary folder, as set in the TEMP environment variable
|
|
-- 4. Working directory.
|
|
|
|
local useCurrentStamping = nil
|
|
if os then
|
|
useCurrentStamping = os.getenv("LIBERATION_EXPORT_STAMPED_STATE")
|
|
end
|
|
|
|
local installPath = nil
|
|
if dcsLiberation then
|
|
installPath = dcsLiberation.installPath
|
|
end
|
|
|
|
if os then
|
|
local result = nil
|
|
-- try using the LIBERATION_EXPORT_DIR environment variable
|
|
result = testDebriefingFilePath(os.getenv("LIBERATION_EXPORT_DIR"), "LIBERATION_EXPORT_DIR", useCurrentStamping)
|
|
if result then
|
|
return result
|
|
end
|
|
-- no joy ? maybe there is a valid path in the mission ?
|
|
result = testDebriefingFilePath(installPath, "the DCS Liberation install folder", useCurrentStamping)
|
|
if result then
|
|
return result
|
|
end
|
|
-- there's always the possibility of using the system temporary folder
|
|
result = testDebriefingFilePath(os.getenv("TEMP"), "TEMP", useCurrentStamping)
|
|
if result then
|
|
return result
|
|
end
|
|
end
|
|
|
|
-- nothing worked, let's try the last resort folder : current directory.
|
|
if lfs then
|
|
return testDebriefingFilePath(lfs.writedir().."Missions\\", "the working directory", useCurrentStamping)
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
debriefing_file_location = discoverDebriefingFilePath()
|
|
|
|
write_state_error_handling = function()
|
|
local _debriefing_file_location = debriefing_file_location
|
|
if not debriefing_file_location then
|
|
_debriefing_file_location = "[nil]"
|
|
logger:error("Unable to find where to write DCS Liberation state")
|
|
end
|
|
|
|
if pcall(write_state) then
|
|
else
|
|
messageAll("Unable to write DCS Liberation state to ".._debriefing_file_location..
|
|
"\nYou can abort the mission in DCS Liberation.\n"..
|
|
"\n\nPlease fix your setup in DCS Liberation, make sure you are pointing to the right installation directory from the File/Preferences menu. Then after fixing the path restart DCS Liberation, and then restart DCS."..
|
|
"\n\nYou can also try to fix the issue manually by replacing the file <dcs_installation_directory>/Scripts/MissionScripting.lua by the one provided there : <dcs_liberation_folder>/resources/scripts/MissionScripting.lua. And then restart DCS. (This will also have to be done again after each DCS update)"..
|
|
"\n\nIt's not worth playing, the state of the mission will not be recorded.")
|
|
end
|
|
|
|
-- reschedule
|
|
mist.scheduleFunction(write_state_error_handling, {}, timer.getTime() + WRITESTATE_SCHEDULE_IN_SECONDS)
|
|
end
|
|
|
|
function update_hit_points(event)
|
|
local update = {}
|
|
update.name = event.target:getName()
|
|
update.hit_points = event.target:getLife()
|
|
unit_hit_point_updates[#unit_hit_point_updates + 1] = update
|
|
write_state()
|
|
end
|
|
|
|
activeWeapons = {}
|
|
local function onEvent(event)
|
|
if event.id == world.event.S_EVENT_CRASH and event.initiator then
|
|
crash_events[#crash_events + 1] = event.initiator.getName(event.initiator)
|
|
write_state()
|
|
end
|
|
|
|
if event.id == world.event.S_EVENT_UNIT_LOST and event.initiator then
|
|
unit_lost_events[#unit_lost_events + 1] = event.initiator.getName(event.initiator)
|
|
write_state()
|
|
end
|
|
|
|
if event.id == world.event.S_EVENT_KILL and event.target then
|
|
kill_events[#kill_events + 1] = event.target.getName(event.target)
|
|
write_state()
|
|
end
|
|
|
|
if event.id == world.event.S_EVENT_DEAD and event.initiator then
|
|
dead_events[#dead_events + 1] = event.initiator.getName(event.initiator)
|
|
local position = event.initiator.getPosition(event.initiator)
|
|
local destruction = {}
|
|
destruction.x = position.p.x
|
|
destruction.y = position.p.y
|
|
destruction.z = position.p.z
|
|
destruction.type = event.initiator:getTypeName()
|
|
destruction.orientation = mist.getHeading(event.initiator) * 57.3
|
|
destroyed_objects_positions[#destroyed_objects_positions + 1] = destruction
|
|
write_state()
|
|
end
|
|
|
|
if event.id == world.event.S_EVENT_HIT then
|
|
target_category = event.target:getCategory()
|
|
if target_category == Object.Category.UNIT then
|
|
-- check on the health of the target 1 second after as the life value is sometimes not updated
|
|
-- at the time of the event
|
|
timer.scheduleFunction(update_hit_points, event, timer.getTime() + 1)
|
|
end
|
|
end
|
|
|
|
if event.id == world.event.S_EVENT_MISSION_END then
|
|
mission_ended = true
|
|
write_state()
|
|
end
|
|
|
|
end
|
|
|
|
mist.addEventHandler(onEvent)
|
|
|
|
-- create the state.json file and start the scheduling
|
|
write_state_error_handling() |