466 lines
14 KiB
Plaintext
466 lines
14 KiB
Plaintext
|
-- map of exoskeletons to their power supplies
|
||
|
--[[
|
||
|
key: id
|
||
|
value: {
|
||
|
current power
|
||
|
supply: {
|
||
|
name
|
||
|
drain mult
|
||
|
max
|
||
|
}
|
||
|
}
|
||
|
]]
|
||
|
local exo_devices = {}
|
||
|
-- cached properties of exo devices
|
||
|
local cache = {}
|
||
|
local warn = false
|
||
|
local threshold = 25
|
||
|
|
||
|
local gc = game.translate_string
|
||
|
function print_dbg(txt, ...)
|
||
|
if exo_mcm.get_config("debug") then
|
||
|
printf("arti_exo | %s | " .. txt, time_global(), ...)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
function init()
|
||
|
local ini_exo_device = ini_file("items\\settings\\exo_devices.ltx")
|
||
|
|
||
|
-- amper_factor = ini_device:r_float_ex("settings","amper_factor") or 1000
|
||
|
-- map psu props
|
||
|
local n = ini_exo_device:line_count("dev_props")
|
||
|
for i=0,n-1 do
|
||
|
local result, sec, value = ini_exo_device:r_line_ex("dev_props",i,"","")
|
||
|
local vals = str_explode(value, ",")
|
||
|
cache[sec] = {
|
||
|
["name"] = sec,
|
||
|
["drain"] = tonumber(vals[1]) or 1,
|
||
|
["max"] = tonumber(vals[2]) or 100
|
||
|
}
|
||
|
end
|
||
|
n = ini_exo_device:line_count("spec_props")
|
||
|
for i=0,n-1 do
|
||
|
local result, sec, value = ini_exo_device:r_line_ex("spec_props",i,"","")
|
||
|
cache[sec].ability = value
|
||
|
end
|
||
|
-- map exo drain base
|
||
|
n = ini_exo_device:line_count("suit_drain")
|
||
|
for i=0,n-1 do
|
||
|
local result, sec, value = ini_exo_device:r_line_ex("suit_drain",i,"","")
|
||
|
cache[sec] = tonumber(value)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- data management
|
||
|
function get_data(id)
|
||
|
if exo_devices[id] then
|
||
|
return dup_table(exo_devices[id])
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function set_data(id, data)
|
||
|
-- validate the data first
|
||
|
if not data then
|
||
|
print_dbg("No data set for id %s, initializing", id)
|
||
|
data = {}
|
||
|
data.power = 0
|
||
|
end
|
||
|
if data.supply then
|
||
|
local name = data.supply.name
|
||
|
if not cache[name] then
|
||
|
data.supply = nil
|
||
|
end
|
||
|
end
|
||
|
exo_devices[id] = data
|
||
|
end
|
||
|
|
||
|
function init_data(id)
|
||
|
local data = {}
|
||
|
data.power = 0
|
||
|
set_data(id, data)
|
||
|
return data
|
||
|
end
|
||
|
|
||
|
-- helpers
|
||
|
-- check if obj is exo outfit
|
||
|
function is_exo(obj)
|
||
|
local section = obj and obj:section() or false
|
||
|
if section then
|
||
|
return SYS_GetParam(0, section, "repair_type") == "outfit_exo"
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- check if actor is wearing exo
|
||
|
function is_wearing_exo()
|
||
|
local outfit = db.actor:item_in_slot(7)
|
||
|
return is_exo(outfit)
|
||
|
end
|
||
|
|
||
|
-- right-click options
|
||
|
-- remove supply from exo
|
||
|
function check_remove_psu(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then return false end
|
||
|
|
||
|
local id = obj:id()
|
||
|
local data = get_data(id)
|
||
|
if not data or is_empty(data) then return end
|
||
|
if not data.supply then return end
|
||
|
return "st_remove_psu"
|
||
|
end
|
||
|
|
||
|
function remove_psu(obj)
|
||
|
local id = obj:id()
|
||
|
local data = get_data(id)
|
||
|
if not data or is_empty(data) then return end
|
||
|
if not data.supply then return end
|
||
|
if data.power > 100 then data.power = 100 end
|
||
|
alife_create_item(data.supply.name, db.actor)
|
||
|
data.supply = nil
|
||
|
set_data(id, data)
|
||
|
-- adjust_weight(obj)
|
||
|
end
|
||
|
|
||
|
function install_psu(suit, data, psu)
|
||
|
if data.supply ~= nil then
|
||
|
remove_psu(suit)
|
||
|
end
|
||
|
local sec_p = psu:section()
|
||
|
if cache[sec_p] then
|
||
|
|
||
|
data.supply = dup_table(cache[sec_p])
|
||
|
print_dbg("Installing PSU %s with drain %s, capacity %s", data.supply.name, data.supply.drain, data.supply.max)
|
||
|
set_data(suit:id(), data)
|
||
|
-- adjust_weight(suit)
|
||
|
alife_release(psu)
|
||
|
-- play some sound?
|
||
|
xr_sound.set_sound_play(AC_ID,"inv_aam_open")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Update the weight of an exoskeleton factoring in the weight of the PSU
|
||
|
-- function adjust_weight(obj)
|
||
|
-- print_dbg("begin adjust weight for %s", obj:section())
|
||
|
-- if not is_exo(obj) then return end
|
||
|
|
||
|
-- local base_weight = SYS_GetParam(2, obj:section(), "inv_weight")
|
||
|
-- local upgrades = utils_item.get_upgrades_installed(obj)
|
||
|
-- local add_inv_weight = 0
|
||
|
-- for _, upgrade in pairs(upgrades) do
|
||
|
-- local section = ini_sys:r_string_ex(upgrade, "section")
|
||
|
-- local weight_adj = ini_sys:r_string_ex(section,"inv_weight")
|
||
|
-- if weight_adj then
|
||
|
-- local op = string.sub(weight_adj, 1, 1)
|
||
|
-- local val = tonumber(string.sub(weight_adj, 2))
|
||
|
|
||
|
-- add_inv_weight = op == "+" and add_inv_weight + val or add_inv_weight - val
|
||
|
-- end
|
||
|
-- --printf(add_inv_weight)
|
||
|
-- end
|
||
|
|
||
|
-- local data = exo_devices[obj:id()]
|
||
|
-- base_weight = base_weight + add_inv_weight
|
||
|
-- if data and data.supply and data.supply.name then
|
||
|
-- base_weight = base_weight + SYS_GetParam(2, data.supply.name, "inv_weight")
|
||
|
-- end
|
||
|
-- print_dbg("set weight of %s to %s", obj:section(), base_weight)
|
||
|
-- obj:set_weight(base_weight)
|
||
|
-- end
|
||
|
|
||
|
-- charge exo with battery
|
||
|
|
||
|
function menu_battery(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then
|
||
|
return false
|
||
|
end
|
||
|
local suit = db.actor:item_in_slot(7)
|
||
|
if not is_exo(suit) then return end
|
||
|
local id = suit:id()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
local max_power = data.supply and data.supply.max or 100
|
||
|
if data.power < max_power then
|
||
|
return "st_charge_exo"
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
function func_battery(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then
|
||
|
return
|
||
|
end
|
||
|
local suit = db.actor:item_in_slot(7)
|
||
|
if not is_exo(suit) then return end
|
||
|
local id = suit:id()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
charge_exo(obj, suit, data)
|
||
|
end
|
||
|
|
||
|
-- install psu
|
||
|
function check_install_psu(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local suit = db.actor:item_in_slot(7)
|
||
|
if not is_exo(suit) then return false end
|
||
|
local id = suit:id()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
return "st_install_psu"
|
||
|
end
|
||
|
function func_install_psu(obj)
|
||
|
local p = obj:parent()
|
||
|
if not (p and p:id() == AC_ID) then
|
||
|
return
|
||
|
end
|
||
|
local suit = db.actor:item_in_slot(7)
|
||
|
if not is_exo(suit) then return end
|
||
|
local data = get_data(suit:id())
|
||
|
if not data then data = init_data(suit:id()) end
|
||
|
install_psu(suit, data, obj)
|
||
|
end
|
||
|
-- use the battery to charge the suit
|
||
|
function charge_exo(battery, suit, data)
|
||
|
print_dbg("begin charge exo")
|
||
|
local max_restorable = (data.supply and data.supply.max or 100) - data.power
|
||
|
local batt_power = math.ceil(battery:condition() * 100)
|
||
|
local to_restore = math.min(max_restorable, batt_power)
|
||
|
print_dbg("max_restorable is %s, battery power is %s. restoring %s", max_restorable, batt_power, to_restore)
|
||
|
if to_restore > 0 then
|
||
|
data.power = data.power + to_restore
|
||
|
batt_power = batt_power - to_restore
|
||
|
if batt_power == 0 then
|
||
|
alife_release(battery)
|
||
|
else
|
||
|
battery:set_condition(batt_power/100)
|
||
|
end
|
||
|
set_data(suit:id(), data)
|
||
|
actor_effects.play_item_fx("batteries_dead")
|
||
|
utils_obj.play_sound("interface\\inv_batt")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local current_outfit
|
||
|
|
||
|
-- function on_move(obj)
|
||
|
-- if is_exo(obj) then
|
||
|
-- adjust_weight(obj)
|
||
|
-- end
|
||
|
-- end
|
||
|
|
||
|
function actor_on_first_update()
|
||
|
init()
|
||
|
-- local suit = db.actor:item_in_slot(7)
|
||
|
-- if suit then
|
||
|
-- print_dbg("doing one time weight mgmt")
|
||
|
-- actor_item_to_slot(suit)
|
||
|
-- end
|
||
|
end
|
||
|
-- drag battery onto suit, or power device onto suit
|
||
|
local function on_item_drag_dropped(obj_d, obj_o, slot_from, slot_to)
|
||
|
-- Check capability
|
||
|
if not (slot_from == EDDListType.iActorBag and (slot_to == EDDListType.iActorBag or slot_to == EDDListType.iActorSlot)) then
|
||
|
return
|
||
|
end
|
||
|
if ui_inventory.GUI and ui_inventory.GUI.mode == "trade" then return end
|
||
|
|
||
|
local sec_d = obj_d:section() -- battery or device
|
||
|
local sec_o = obj_o:section() -- suit
|
||
|
print_dbg("dragged %s on %s", sec_d, sec_o)
|
||
|
if is_exo(obj_o) then
|
||
|
local id = obj_o:id()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
if sec_d == "batteries_exo" then
|
||
|
-- charge exo
|
||
|
charge_exo(obj_d, obj_o, data)
|
||
|
elseif SYS_GetParam(1, sec_d, "is_psu") then
|
||
|
-- replace the psu
|
||
|
install_psu(obj_o, data, obj_d)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
-- player slowing management
|
||
|
local slow_coef = 0.1
|
||
|
local is_player_slowed = false
|
||
|
local function slow_player()
|
||
|
if not is_player_slowed then
|
||
|
print_dbg("slowing player")
|
||
|
is_player_slowed = true
|
||
|
speed.add_speed("exo_speed", slow_coef, false, true)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function reset_speed()
|
||
|
if is_player_slowed then
|
||
|
print_dbg("restoring speed")
|
||
|
is_player_slowed = false
|
||
|
speed.add_speed("exo_speed", 1, false, true)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local updated = 0
|
||
|
local interval = 400
|
||
|
function actor_on_update()
|
||
|
if time_global() < updated then return end
|
||
|
updated = time_global() + interval
|
||
|
|
||
|
-- update power
|
||
|
if is_wearing_exo() then
|
||
|
manage_drain()
|
||
|
end
|
||
|
|
||
|
-- update speed
|
||
|
if (not is_wearing_exo()) then
|
||
|
reset_speed()
|
||
|
else
|
||
|
if exo_charged() then reset_speed() else slow_player() end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function exo_charged()
|
||
|
local exosuit = db.actor:item_in_slot(7)
|
||
|
|
||
|
if exosuit and is_exo(exosuit) then
|
||
|
local id = exosuit:id()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
return data.power > 0
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
-- modify drain by
|
||
|
local function modify_drain(base_drain, data)
|
||
|
local drain_mult = exo_mcm.get_config("drain") or 1
|
||
|
if data.supply then base_drain = base_drain * data.supply.drain end
|
||
|
if IsMoveState("mcSprint") then
|
||
|
local fast_mult = exo_mcm.get_config("sprint_drain")
|
||
|
return base_drain * fast_mult * drain_mult
|
||
|
elseif IsMoveState("mcAnyMove") then
|
||
|
return base_drain * drain_mult
|
||
|
end
|
||
|
if data.supply and data.supply.ability == "recharge" then return -0.05
|
||
|
else return 0 end
|
||
|
end
|
||
|
|
||
|
-- every tick, update the supply power accordingly
|
||
|
function manage_drain()
|
||
|
local exosuit = db.actor:item_in_slot(7)
|
||
|
if exosuit and is_exo(exosuit) then
|
||
|
local id = exosuit:id()
|
||
|
local sec = exosuit:section()
|
||
|
local data = get_data(id)
|
||
|
if not data then data = init_data(id) end
|
||
|
local base_drain = cache[sec] or 0.01
|
||
|
base_drain = modify_drain(base_drain, data)
|
||
|
data.power = clamp(data.power - base_drain, 0, 400)
|
||
|
set_data(id, data)
|
||
|
|
||
|
if warn and data.power > threshold then
|
||
|
warn = false
|
||
|
end
|
||
|
-- low power warn
|
||
|
if not warn and data.power < threshold then
|
||
|
warn = true
|
||
|
news_manager.send_tip(db.actor, gc("st_low_power"), nil, "swiss_knife", 6000)
|
||
|
end
|
||
|
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
CanRepair = inventory_upgrades.can_repair_item
|
||
|
function inventory_upgrades.can_repair_item( sec, cond, mechanic )
|
||
|
if sec == "batteries_exo" then return false
|
||
|
else return CanRepair(sec, cond, mechanic) end
|
||
|
end
|
||
|
|
||
|
NameCustom = ui_inventory.UIInventory.Name_Custom
|
||
|
-- hijack functions for exos
|
||
|
function ui_inventory.UIInventory:Name_Custom(obj, bag, temp, i)
|
||
|
obj = self:CheckItem(obj,"Name_Custom " .. i)
|
||
|
if i == 3 and is_exo(obj) then
|
||
|
return check_remove_psu(obj)
|
||
|
else
|
||
|
return NameCustom(self, obj, bag, temp, i)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
ActionCustom = ui_inventory.UIInventory.Action_Custom
|
||
|
function ui_inventory.UIInventory:Action_Custom(obj, bag, temp, i)
|
||
|
obj = self:CheckItem(obj,"Action_Custom " .. i)
|
||
|
if i == 3 and is_exo(obj) then
|
||
|
remove_psu(obj)
|
||
|
else
|
||
|
ActionCustom(self, obj, bag, temp, i)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local clr_r = utils_xml.get_color("d_red")
|
||
|
local clr_g = utils_xml.get_color("d_green")
|
||
|
local clr_y = utils_xml.get_color("yellow")
|
||
|
|
||
|
local clr_2 = utils_xml.get_color("ui_gray_1")
|
||
|
original_build_desc_header = ui_item.build_desc_header
|
||
|
function ui_item.build_desc_header(obj, sec, str)
|
||
|
local _str = ""
|
||
|
local _str2 = original_build_desc_header(obj, sec, str)
|
||
|
|
||
|
-- display power + psu
|
||
|
if obj and is_exo(obj) then
|
||
|
local data = get_data(obj:id())
|
||
|
if not data then data = init_data(obj:id()) end
|
||
|
|
||
|
local display_str = ""
|
||
|
local power = string.format("%.2f", data.power)
|
||
|
local max_power = data.supply and data.supply.max or 100
|
||
|
local clr = utils_xml.get_color_con((data.power/max_power) * 100)
|
||
|
display_str = display_str .." " .. clr_y .. gc("st_dot") .. " " .. clr_2 .. gc("st_power") .. " ".. clr .. power .. "/" .. max_power .. "\\n"
|
||
|
local psu = gc("st_equipped_none")
|
||
|
clr = clr_r
|
||
|
if data.supply then
|
||
|
psu = ui_item.get_sec_name(data.supply.name)
|
||
|
clr = clr_g
|
||
|
end
|
||
|
|
||
|
display_str = display_str .. " " .. clr_y .. gc("st_dot") .. " " .. clr_2 .. gc("st_equipped_supply") .. " " .. clr .. psu .. clr_2 .. "\\n \\n"
|
||
|
_str = _str .. display_str
|
||
|
end
|
||
|
_str = _str2 .. _str
|
||
|
|
||
|
return _str
|
||
|
end
|
||
|
|
||
|
local function se_device_on_unregister(se_obj, typ)
|
||
|
local id = se_obj.id
|
||
|
exo_devices[id] = nil
|
||
|
end
|
||
|
|
||
|
local function save_state(mdata)
|
||
|
mdata.exo_devices = exo_devices
|
||
|
end
|
||
|
|
||
|
local function load_state(mdata)
|
||
|
exo_devices = mdata.exo_devices or {}
|
||
|
end
|
||
|
|
||
|
function on_game_start()
|
||
|
RegisterScriptCallback("save_state",save_state)
|
||
|
RegisterScriptCallback("load_state",load_state)
|
||
|
RegisterScriptCallback("ActorMenu_on_item_drag_drop",on_item_drag_dropped)
|
||
|
RegisterScriptCallback("server_entity_on_unregister",se_device_on_unregister)
|
||
|
RegisterScriptCallback("actor_on_update",actor_on_update)
|
||
|
|
||
|
RegisterScriptCallback("actor_item_to_slot",on_move)
|
||
|
RegisterScriptCallback("actor_item_to_ruck",on_move)
|
||
|
RegisterScriptCallback("actor_on_first_update",actor_on_first_update)
|
||
|
end
|