742 lines
22 KiB
Plaintext
742 lines
22 KiB
Plaintext
|
|
||
|
|
||
|
|
||
|
|
||
|
-- each datum consists of the following:
|
||
|
-- .loaded = this is a stack of rounds loaded in the magazine, each round is int, depending on what kind of round it is
|
||
|
-- .section = for magazines in weapons, this tracks the type of magazine that is loaded
|
||
|
local mags_storage = {}
|
||
|
-- this storage is for vested mags in specific
|
||
|
local carried_mags = {}
|
||
|
|
||
|
-- reverse lookups for magazine properties
|
||
|
local mags_by_basetype = {}
|
||
|
local mags_by_retool_group = {}
|
||
|
local basetypes_by_ammo_type ={}
|
||
|
|
||
|
local loadout_slots = {
|
||
|
small = 2,
|
||
|
medium = 0,
|
||
|
large = 0,
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
function print_dbg(...) magazines.print_dbg(...) end
|
||
|
function print_err(...) magazines.print_err(...) end
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
local function parent_section(sec)
|
||
|
return SYS_GetParam(0, sec, "parent_section", sec) or sec
|
||
|
end
|
||
|
|
||
|
function dump_data(data)
|
||
|
if not data or type(data) ~= "table" then
|
||
|
print_dbg("Mag data unavailable: %s (%s)", data, type(data))
|
||
|
else
|
||
|
local s = ""
|
||
|
for k,v in pairs(data.loaded) do
|
||
|
s = s .. v .. " "
|
||
|
end
|
||
|
print_dbg("Mag section: %s. Rounds loaded: %s. Rounds: %s", data.section, #data.loaded, s)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local look_up_table = ini_file_ex("magazines\\lookups.ltx")
|
||
|
local weapons_lookup = ini_file_ex("magazines\\weapons\\importer.ltx")
|
||
|
local loadout_lookup = ini_file_ex("magazines\\outfitloadouts\\importer.ltx")
|
||
|
|
||
|
-------------------------------
|
||
|
-- SECTION acess functions --
|
||
|
-------------------------------
|
||
|
function get_carried_mags(tbl)
|
||
|
--return carried_mags --efficency v acess control hud won't call this very often so went with acess control
|
||
|
|
||
|
copy_table(tbl, carried_mags )
|
||
|
end
|
||
|
|
||
|
function get_data(id)
|
||
|
tbl = {}
|
||
|
if mags_storage[id] then
|
||
|
copy_table(tbl, mags_storage[id] )
|
||
|
return tbl
|
||
|
end
|
||
|
return mags_storage[id] --could be nil or false want to return either.
|
||
|
end
|
||
|
|
||
|
function set_data(id, data)
|
||
|
mags_storage[id] = data
|
||
|
if carried_mags[id] then
|
||
|
carried_mags[id] = data
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function create_mag_data(id, sec, is_weapon)
|
||
|
local mag_data = {}
|
||
|
mag_data.loaded = {}
|
||
|
mag_data.section = sec
|
||
|
mag_data.is_weapon = is_weapon or false
|
||
|
set_data(id, mag_data)
|
||
|
return mag_data
|
||
|
end
|
||
|
|
||
|
-- loops through mag data and check/validate entries
|
||
|
function clean_data()
|
||
|
for id, data in pairs(mags_storage) do
|
||
|
local se = alife_object(id)
|
||
|
local is_mag = se and SYS_GetParam(1, se:section_name(), "is_mag")
|
||
|
local is_wpn = se and is_supported_weapon(parent_section(se:section_name()))
|
||
|
if not (is_mag or is_wpn) then
|
||
|
set_data(id, nil)
|
||
|
elseif data == false then
|
||
|
set_data(id, {
|
||
|
section = "no_mag",
|
||
|
loaded = {},
|
||
|
is_weapon = true,
|
||
|
})
|
||
|
elseif is_wpn then
|
||
|
print_dbg("Checking wpn %s", se:section_name())
|
||
|
validate_wep(id, wpn_sec)
|
||
|
else
|
||
|
validate_mag(id, se:section_name())
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function get_mag_property(id,key)
|
||
|
--print_dbg("wtf:".. tostring((mags_storage[id] and mags_storage[id][key] or "no dice")))
|
||
|
return mags_storage[id] and mags_storage[id][key]
|
||
|
end
|
||
|
|
||
|
function get_size(id, mag_data)
|
||
|
mag_data = mag_data or get_data(id)
|
||
|
return SYS_GetParam(0, mag_data.section, "mag_size") or "small"
|
||
|
end
|
||
|
|
||
|
function get_total_carried(exact)
|
||
|
local carried = {
|
||
|
["small"] = 0,
|
||
|
["medium"] = 0,
|
||
|
["large"] = 0
|
||
|
}
|
||
|
for id, mag in pairs(carried_mags) do
|
||
|
local size = get_size(id)
|
||
|
carried[size] = carried[size] + 1
|
||
|
end
|
||
|
|
||
|
|
||
|
if not exact then --shift exess up to next size
|
||
|
if carried.small > loadout_slots.small then
|
||
|
carried.medium = carried.medium + carried.small - loadout_slots.small
|
||
|
carried.small = loadout_slots.small
|
||
|
end
|
||
|
if carried.medium > loadout_slots.medium then
|
||
|
carried.large = carried.large + carried.medium - loadout_slots.medium
|
||
|
carried.medium = loadout_slots.medium
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
return carried
|
||
|
end
|
||
|
|
||
|
function get_loadout_size()
|
||
|
local copy = {}
|
||
|
copy_table(copy, loadout_slots)
|
||
|
return copy
|
||
|
end
|
||
|
|
||
|
function get_mags_for_basetype(basetype)
|
||
|
return mags_by_basetype[basetype] and dup_table( mags_by_basetype[basetype])
|
||
|
|
||
|
end
|
||
|
|
||
|
function get_basetypes_by_ammo_type(sec)
|
||
|
--print_dbg("#basetypes_by_ammo_type:%s basetypes_by_ammo_type[%s]:%s",#basetypes_by_ammo_type,sec,basetypes_by_ammo_type[sec] and #basetypes_by_ammo_type[sec])
|
||
|
|
||
|
return basetypes_by_ammo_type[sec] and dup_table( basetypes_by_ammo_type[sec])
|
||
|
end
|
||
|
|
||
|
function get_mags_by_ammo_type(sec)
|
||
|
local t = {}
|
||
|
local basetypes = get_basetypes_by_ammo_type(sec) or {}
|
||
|
for _,v in ipairs(basetypes) do
|
||
|
local mags = get_mags_for_basetype(v) or {}
|
||
|
for __,v2 in ipairs(mags) do
|
||
|
table.insert(t, v2)
|
||
|
end
|
||
|
end
|
||
|
return t
|
||
|
end
|
||
|
|
||
|
-------------------------------
|
||
|
-- SECTION utility functions --
|
||
|
-------------------------------
|
||
|
|
||
|
|
||
|
local function type_correction(val)
|
||
|
if not val then return end
|
||
|
-- print_dbg(type(val))
|
||
|
local id, section, obj, se_obj
|
||
|
if type(val) == "string" then
|
||
|
section = val
|
||
|
elseif type(val) == "number" then
|
||
|
id = val
|
||
|
obj = level.object_by_id(id)
|
||
|
se_obj = alife_object(id)
|
||
|
if obj then
|
||
|
section = obj:section()
|
||
|
elseif se_obj then
|
||
|
section = se_obj:section_name()
|
||
|
else
|
||
|
print_dbg("WTF is:%s any way?", id)
|
||
|
end
|
||
|
elseif type(val.id) == "number" then
|
||
|
id = val.id
|
||
|
se_obj = val
|
||
|
obj = level.object_by_id(id)
|
||
|
section = val:section_name()
|
||
|
elseif type(val.id) == "function" then
|
||
|
id = val:id()
|
||
|
obj = val
|
||
|
se_obj = alife_object(id)
|
||
|
section = val:section()
|
||
|
end
|
||
|
return id, section, obj, se_obj
|
||
|
end
|
||
|
|
||
|
function is_carried_mag(id)
|
||
|
|
||
|
return carried_mags[id] and true or false
|
||
|
end
|
||
|
|
||
|
function toggle_carried_mag(id)
|
||
|
-- print_dbg("tcm1")
|
||
|
if carried_mags[id] then
|
||
|
carried_mags[id] = nil
|
||
|
return false
|
||
|
--print_dbg("tcm2")
|
||
|
elseif room_in_pouch(id) then
|
||
|
carried_mags[id] = mags_storage[id]
|
||
|
return true
|
||
|
--print_dbg("tcm3"..tostring(carried_mags[id] and carried_mags[id].section))
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function update_loadout_slots()
|
||
|
local outfit = db.actor:item_in_slot(7)
|
||
|
local small,medium,large = 0,0,0
|
||
|
if outfit then
|
||
|
small,medium,large = get_loadout_slots(outfit)
|
||
|
|
||
|
--print_dbg("Outfit:%s||S:%s|M:%s|L:%s",outfit:section(), small,medium,large)
|
||
|
else
|
||
|
small,medium,large = get_loadout_slots("o_none")
|
||
|
|
||
|
--print_dbg("Outfit:%s||S:%s|M:%s|L:%s","o_none", small,medium,large)
|
||
|
end
|
||
|
|
||
|
local s,m,l = 0,0,0
|
||
|
local backpack = db.actor:item_in_slot(13)
|
||
|
s,m,l = get_loadout_slots(backpack)
|
||
|
|
||
|
--print_dbg("backpack:%s||S:%s|M:%s|L:%s",backpack and backpack:section(), s,m,l)
|
||
|
|
||
|
small = small + s
|
||
|
medium = medium + m
|
||
|
large = large + l
|
||
|
|
||
|
--print_dbg("Outfit+backpack||S:%s|M:%s|L:%s", small,medium,large)
|
||
|
|
||
|
local ss,mm,ll = 0,0,0
|
||
|
db.actor:iterate_belt( function(owner, obj)
|
||
|
s,m,l = get_loadout_slots(obj)
|
||
|
|
||
|
--print_dbg("belt:%s||S:%s|M:%s|L:%s",obj and obj:section(), s,m,l)
|
||
|
|
||
|
ss = ss + s
|
||
|
mm = mm + m
|
||
|
ll = ll + l
|
||
|
end)
|
||
|
|
||
|
--print_dbg("Belt total||S:%s|M:%s|L:%s", ss,mm,ll)
|
||
|
|
||
|
small = small + ss
|
||
|
medium = medium + mm
|
||
|
large = large + ll
|
||
|
|
||
|
--print_dbg("total slots||S:%s|M:%s|L:%s", small,medium,large)
|
||
|
|
||
|
loadout_slots.small = small
|
||
|
loadout_slots.medium = medium
|
||
|
loadout_slots.large = large
|
||
|
end
|
||
|
|
||
|
function validate_loadout()
|
||
|
update_loadout_slots()
|
||
|
local carried = get_total_carried()
|
||
|
local excess = carried.large - loadout_slots.large
|
||
|
if excess < 1 then -- if they shift arround and don't over fill large all good
|
||
|
--print_dbg("validate_loadout s:%s m:%s l:%s excess:%s", carried.small, carried.medium, carried.large, excess)
|
||
|
return
|
||
|
end
|
||
|
local found = {small = 0, medium = 0, large = 0}
|
||
|
for id, mag in pairs(carried_mags) do --shed exess favoring shifted mags.
|
||
|
local size = get_size(id)
|
||
|
if size then
|
||
|
found[size] = found[size] + 1
|
||
|
if found[size] > loadout_slots[size] then
|
||
|
carried_mags[id] = nil
|
||
|
excess = excess - 1
|
||
|
if excess == 0 then break end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
magazines.inventory_refresh()
|
||
|
end
|
||
|
|
||
|
function room_in_pouch(id)
|
||
|
local s = mags_storage[id] and get_size(id) or false
|
||
|
if not s then return false end
|
||
|
for i, mag in pairs(carried_mags) do -- safety purge of escaped mags
|
||
|
local itm = level.object_by_id(i)
|
||
|
if not (itm and utils_item.in_actor_inv(itm)) then
|
||
|
carried_mags[i] = nil
|
||
|
end
|
||
|
end
|
||
|
carried = get_total_carried()
|
||
|
return carried.large < loadout_slots.large or ((s == "medium" or s == "small") and (carried.medium < loadout_slots.medium)) or (s == "small" and (carried.small < loadout_slots.small))
|
||
|
end
|
||
|
|
||
|
function build_mag_revers_lookups()
|
||
|
local base_types = {}
|
||
|
local function itr(section)
|
||
|
if not is_magazine(section) or section == "tch_mag_base" or SYS_GetParam(1, section, "old_mag", false) then return end
|
||
|
local basetype = SYS_GetParam(0, section, "base_type") or nil
|
||
|
local retool_group = SYS_GetParam(0, section, "retool_group") or nil
|
||
|
local caliber = get_magazine_caliber(section)
|
||
|
if basetype then
|
||
|
if not mags_by_basetype[basetype] then
|
||
|
mags_by_basetype[basetype] = {}
|
||
|
end
|
||
|
mags_by_basetype[basetype][#mags_by_basetype[basetype]+1] = section
|
||
|
end
|
||
|
if retool_group then
|
||
|
if not mags_by_retool_group[retool_group] then
|
||
|
mags_by_retool_group[retool_group] = {}
|
||
|
end
|
||
|
mags_by_retool_group[retool_group][#mags_by_retool_group[retool_group]+1] = section
|
||
|
end
|
||
|
if caliber and basetype and not base_types[basetype] then
|
||
|
base_types[basetype] = true
|
||
|
for i = 1, #caliber do
|
||
|
if not basetypes_by_ammo_type[caliber[i]] then
|
||
|
basetypes_by_ammo_type[caliber[i]] = {}
|
||
|
end
|
||
|
basetypes_by_ammo_type[caliber[i]][#basetypes_by_ammo_type[caliber[i]]+1] = basetype
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
end
|
||
|
ini_sys:section_for_each(itr)
|
||
|
end
|
||
|
|
||
|
|
||
|
-- these functions work equally well given an object id, item section, gameobject or server object.
|
||
|
function is_supported_weapon(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
local is_valid_by_sec = weapons_lookup:section_exist(parent_section(section))
|
||
|
--print_dbg("is_valid_by_sec:%s and obj: %s", is_valid_by_sec, obj and true)
|
||
|
if (not (obj and is_valid_by_sec)) or obj:weapon_in_grenade_mode() then return is_valid_by_sec end
|
||
|
local is_valid = is_valid_by_sec and get_weapon_base_type(id) and true or false
|
||
|
--print_dbg("is_valid:%s and obj: %s", is_valid, obj and true)
|
||
|
|
||
|
-- if it's a valid weapon, bind it to the mag binder
|
||
|
-- HarukaSai: we don't need a binder just for first update, instead we can create data here
|
||
|
if is_valid then
|
||
|
local mag_data = get_data(id)
|
||
|
|
||
|
if not mag_data then
|
||
|
print_dbg("Valid weapon %s has no data, creating default data", obj:section())
|
||
|
|
||
|
local default_mag = weapon_default_magazine(section)
|
||
|
mag_data = create_mag_data(id, default_mag, true)
|
||
|
-- also need to do some things to convert existing ammo
|
||
|
local ammo_max = SYS_GetParam(2, default_mag, "max_mag_size")
|
||
|
local ammo_type = obj:get_ammo_type()
|
||
|
local ammo_map = utils_item.get_ammo(section, id) or SYS_GetParam(2, section, "ammo_mag_size") or 999
|
||
|
print_dbg("Weapon %s uses mags, assigning default mag %s with %s rounds, type is %s", section, default_mag, ammo_max, ammo_map[ammo_type+1])
|
||
|
|
||
|
for i=1,ammo_max do
|
||
|
stack.push(mag_data.loaded, ammo_map[ammo_type+1])
|
||
|
end
|
||
|
|
||
|
set_data(id, mag_data)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return is_valid
|
||
|
end
|
||
|
|
||
|
function is_open_bolt_weapon(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
|
||
|
return look_up_table:r_value(parent_section(section), "open_bolt")
|
||
|
end
|
||
|
|
||
|
function has_loadout_slots(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
local in_list = loadout_lookup:section_exist(parent_section(section))
|
||
|
--print_dbg("has_loadout_slots %s|%s:%s|%s", section,parent_section(section), in_list, IsItem("outfit", section, obj) )
|
||
|
return IsItem("outfit", section, obj) or in_list
|
||
|
end
|
||
|
|
||
|
|
||
|
function get_retool_section(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
local retool_group = SYS_GetParam(0, section, "retool_group") or nil
|
||
|
local retool_section = nil
|
||
|
if retool_group and mags_by_retool_group[retool_group] and #mags_by_retool_group[retool_group] > 1 then --want to return nil of mag has no retool group or is only memeber
|
||
|
for i,v in ipairs(mags_by_retool_group[retool_group]) do
|
||
|
print_dbg("retool section: %s|%s", section, v)
|
||
|
if v == section then
|
||
|
if i < #mags_by_retool_group[retool_group] then
|
||
|
retool_section = mags_by_retool_group[retool_group][i+1]
|
||
|
else
|
||
|
retool_section = mags_by_retool_group[retool_group][1]
|
||
|
end
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return retool_section
|
||
|
|
||
|
end
|
||
|
|
||
|
function get_loadout_slots(val, combine, force_outfit)
|
||
|
if not val then return 0,0,0 end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
|
||
|
local s,m,l = 0,0,0
|
||
|
if look_up_table:section_exist(section) then
|
||
|
s = look_up_table:r_value(section, "mag_limit_small", 2) or s
|
||
|
m = look_up_table:r_value(section, "mag_limit_medium", 2) or m
|
||
|
l = look_up_table:r_value(section, "mag_limit_large", 2) or l
|
||
|
elseif IsOutfit(obj) or force_outfit then
|
||
|
local kind = SYS_GetParam(0, section, "kind")
|
||
|
if look_up_table:section_exist(kind) then
|
||
|
s = look_up_table:r_value(kind, "mag_limit_small", 2) or s
|
||
|
m = look_up_table:r_value(kind, "mag_limit_medium", 2) or m
|
||
|
l = look_up_table:r_value(kind, "mag_limit_large", 2) or l
|
||
|
else
|
||
|
print_dbg("Outfit defaulted: sec:%s kind:%s",section ,kind )
|
||
|
s = look_up_table:r_value("o_none", "mag_limit_small", 2) or s
|
||
|
m = look_up_table:r_value("o_none", "mag_limit_medium", 2) or m
|
||
|
l = look_up_table:r_value("o_none", "mag_limit_large", 2) or l
|
||
|
end
|
||
|
end
|
||
|
if combine then
|
||
|
return {small = s,medium = m, large = l}
|
||
|
else
|
||
|
return s, m, l
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_magazine(val)
|
||
|
if not val then return false end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
return SYS_GetParam(1, section, "is_mag")
|
||
|
end
|
||
|
|
||
|
function get_magazine_base_type(val)
|
||
|
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
|
||
|
return SYS_GetParam(0, section, "base_type") or nil
|
||
|
end
|
||
|
|
||
|
function get_magazine_caliber(val)
|
||
|
return str_explode(look_up_table:r_value(get_magazine_base_type(val) or print_dbg("Missing basetype for: %s",val), "caliber") or print_dbg("Invalid basetyper for: %s",val), ",")
|
||
|
end
|
||
|
|
||
|
-- check if this weapon takes a magazine, and return the base type. false if it does not
|
||
|
|
||
|
function get_weapon_base_type(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
id = obj and id or nil --utils_item.get_ammo will not work if given an id of an offline object. nil id if no gameobject exists
|
||
|
local ammo = utils_item.get_ammo(section, id)[1]
|
||
|
local parent = parent_section(section)
|
||
|
print_dbg("for weapon %s, using ammo %s, bt is %s", parent, ammo, is_supported_weapon(section) and look_up_table:r_value(parent, ammo))
|
||
|
return is_supported_weapon(section) and look_up_table:r_value(parent, ammo) -- base_type is in the ltx based on the first entry in the weapons ammo list.
|
||
|
end
|
||
|
|
||
|
function weapon_default_magazine(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
|
||
|
return look_up_table:r_value(parent_section(section), "default_mag")
|
||
|
end
|
||
|
|
||
|
function weapon_improved_magazine(val)
|
||
|
if not val then return end
|
||
|
local id, section, obj, se_obj = type_correction(val)
|
||
|
|
||
|
return look_up_table:r_value(parent_section(section), "improved_mag")
|
||
|
|
||
|
end
|
||
|
-- check if mag is compatible w. weapon
|
||
|
function is_compatible(weapon, magazine) --both called functions do type correction, added argument order corection
|
||
|
if not is_magazine(magazine) then
|
||
|
local t = magazine
|
||
|
magazine = weapon
|
||
|
weapon = t
|
||
|
end
|
||
|
local weapon_base = get_weapon_base_type(weapon)
|
||
|
local magazine_base = get_magazine_base_type(magazine)
|
||
|
print_dbg("wpn base type is %s, mag base type is %s", weapon_base, magazine_base)
|
||
|
return weapon_base == magazine_base
|
||
|
end
|
||
|
|
||
|
function valid_mag_data(mag_data)
|
||
|
return (mag_data and mag_data.section ~= "no_mag") and mag_data or nil
|
||
|
end
|
||
|
|
||
|
-- get mag data with validation for fake mag items
|
||
|
function get_mag_loaded(id)
|
||
|
return valid_mag_data(get_data(id))
|
||
|
end
|
||
|
|
||
|
function validate_wep(id, sec)
|
||
|
local wpn_obj = level.object_by_id(id)
|
||
|
local mag_data = get_mag_loaded(id)
|
||
|
if not mag_data then return end
|
||
|
if wpn_obj and wpn_obj:weapon_in_grenade_mode() then return end
|
||
|
|
||
|
local w_bt = get_weapon_base_type(id)
|
||
|
local m_bt = get_magazine_base_type(mag_data.section)
|
||
|
if w_bt ~= m_bt then
|
||
|
print_err("(CLEANUP) Incompatible magazine %s (bt %s) found on gun %s (typ %s, bt %s). Granting default magazine.", mag_data.section, m_bt, id, sec, w_bt)
|
||
|
mag_data.section = weapon_default_magazine(wpn_obj)
|
||
|
empty_table(mag_data.loaded)
|
||
|
set_data(id, mag_data)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function validate_mag(id, sec)
|
||
|
local mag_data = get_mag_loaded(id)
|
||
|
if not mag_data then return end
|
||
|
|
||
|
if sec ~= mag_data.section then
|
||
|
print_err("(CLEANUP) Mag section mismatch! Saved type is %s, actual %s for id %s", mag_data.section, sec, id)
|
||
|
mag_data.section = sec
|
||
|
end
|
||
|
-- check for ammo that shouldn't belong in the magazine, and replace with generic ammo
|
||
|
local ammo_map = invert_table(get_magazine_caliber(mag_data.section))
|
||
|
-- remove excess rounds
|
||
|
local ammo_cap = SYS_GetParam(2, mag_data.section, "max_mag_size")
|
||
|
while #mag_data.loaded > ammo_cap do
|
||
|
print_err("(CLEANUP) Loaded magazine %s (%s) has capacity %s, current %s rounds loaded", mag_data.section, id, ammo_cap, #mag_data.loaded)
|
||
|
stack.pop(mag_data.loaded)
|
||
|
end
|
||
|
local ammo_replace = random_key_table(ammo_map)
|
||
|
for k,v in pairs(mag_data.loaded) do
|
||
|
if not ammo_map[v] then
|
||
|
print_err("(CLEANUP) Invalid round %s loaded in mag %s, id %s, replacing", v, mag_data.section, id)
|
||
|
mag_data.loaded[k] = ammo_replace
|
||
|
end
|
||
|
end
|
||
|
set_data(id, mag_data)
|
||
|
end
|
||
|
|
||
|
-- class
|
||
|
function bind(obj)
|
||
|
obj:bind_object(magazine_binder(obj))
|
||
|
end
|
||
|
|
||
|
class "magazine_binder" (object_binder)
|
||
|
|
||
|
function magazine_binder:__init(obj) super(obj)
|
||
|
self.first_update = nil
|
||
|
end
|
||
|
|
||
|
-- global flag used to cap condition at 100 for trade
|
||
|
local freeze = false
|
||
|
-- only update every half second
|
||
|
local update_tick = 500
|
||
|
|
||
|
function magazine_binder:update(delta)
|
||
|
object_binder.update(self, delta)
|
||
|
local tg = time_global()
|
||
|
|
||
|
local obj = self.object
|
||
|
local id = obj:id()
|
||
|
local sec = obj:section()
|
||
|
local mag_data = get_data(id)
|
||
|
|
||
|
if not self.first_update then
|
||
|
self.first_update = true
|
||
|
self.last_update = tg + update_tick
|
||
|
-- associate mag data to empty magazine object
|
||
|
if not mag_data and is_magazine(obj) then
|
||
|
mag_data = create_mag_data(id, sec, false)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if tg < self.last_update then return end
|
||
|
self.last_update = tg + update_tick
|
||
|
|
||
|
-- update mag weight and cond
|
||
|
if is_magazine(obj) then
|
||
|
if freeze then
|
||
|
obj:set_condition(0.999)
|
||
|
else
|
||
|
local mag_weight = SYS_GetParam(2, sec, "inv_weight")
|
||
|
local capacity = SYS_GetParam(2, sec, "max_mag_size")
|
||
|
-- for simplicity we take the weight of each bullet to be the same
|
||
|
local cond = 0
|
||
|
if mag_data and #mag_data.loaded > 0 then
|
||
|
local ammoType = get_magazine_caliber(sec)[1]
|
||
|
local box_size = SYS_GetParam(2, ammoType, "box_size") or 1
|
||
|
local box_weight = SYS_GetParam(2, ammoType, "inv_weight") or 0
|
||
|
local cartridge_weight = box_weight / box_size
|
||
|
cond = #mag_data.loaded / capacity
|
||
|
mag_weight = mag_weight + (#mag_data.loaded * cartridge_weight)
|
||
|
end
|
||
|
set_data(id, mag_data)
|
||
|
obj:set_weight(mag_weight)
|
||
|
obj:set_condition(cond)
|
||
|
end
|
||
|
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function magazine_binder:reload(section)
|
||
|
object_binder.reload(self, section)
|
||
|
end
|
||
|
|
||
|
function magazine_binder:reinit()
|
||
|
object_binder.reinit(self)
|
||
|
end
|
||
|
|
||
|
function magazine_binder:net_spawn(se_abstract)
|
||
|
if not(object_binder.net_spawn(self, se_abstract)) then
|
||
|
return false
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function magazine_binder:net_destroy()
|
||
|
object_binder.net_destroy(self)
|
||
|
end
|
||
|
|
||
|
function magazine_binder:save(stpk)
|
||
|
end
|
||
|
|
||
|
function magazine_binder:load(stpk)
|
||
|
end
|
||
|
-- end class
|
||
|
|
||
|
|
||
|
-------------------------------
|
||
|
-- SECTION inventory highlight --
|
||
|
-------------------------------
|
||
|
|
||
|
-- this gets called _ALOT_ so putting it here where table can be read
|
||
|
|
||
|
local bags = {actor_bag = true,actor_trade_bag= true} --player inv in normal/looting and in the merchant UI.
|
||
|
ready_color = GetARGB(100, 255, 159, 82)
|
||
|
function check_ready(cell)
|
||
|
if bags[cell.container.ID] then
|
||
|
return carried_mags[cell.ID] and ready_color --cell.ID and cell.sec are the object id and section
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function icon_check_ready(cell)
|
||
|
if bags[cell.container.ID] and carried_mags[cell.ID] then
|
||
|
return {texture = "ui_mags_loadout", x = 1, y = 1, w = 15, h = 15}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
-------------------------------
|
||
|
-- SECTION callbacks --
|
||
|
-------------------------------
|
||
|
local function save_state(mdata)
|
||
|
mdata.mags_storage = mags_storage
|
||
|
mdata.carried_mags = carried_mags
|
||
|
end
|
||
|
|
||
|
function load_state(mdata)
|
||
|
mags_storage = mdata.mags_storage or {}
|
||
|
carried_mags = mdata.carried_mags or {}
|
||
|
end
|
||
|
|
||
|
-- attempt to keep mstorage clean
|
||
|
local function on_register(se_obj, typ)
|
||
|
local id = se_obj.id
|
||
|
local sec = se_obj:section_name()
|
||
|
if mags_storage[id] and not (is_magazine(sec) or is_supported_weapon(sec)) then
|
||
|
mags_storage[id] = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function se_item_on_unregister(se_obj, typ)
|
||
|
local id = se_obj.id
|
||
|
mags_storage[id] = nil
|
||
|
carried_mags[id] = nil
|
||
|
end
|
||
|
|
||
|
local past_first_update = false
|
||
|
function actor_on_first_update()
|
||
|
CreateTimeEvent("mag_binder","firstupdatedelay",1,function()
|
||
|
past_first_update = true
|
||
|
validate_loadout()
|
||
|
clean_data()
|
||
|
return true
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
function actor_item_to_slot(obj)
|
||
|
if past_first_update and has_loadout_slots(obj) then
|
||
|
validate_loadout()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function on_trade_opened()
|
||
|
freeze = true
|
||
|
end
|
||
|
|
||
|
function on_trade_closed()
|
||
|
freeze = false
|
||
|
end
|
||
|
|
||
|
|
||
|
function on_game_start()
|
||
|
build_mag_revers_lookups()
|
||
|
RegisterScriptCallback("save_state",save_state)
|
||
|
RegisterScriptCallback("load_state",load_state)
|
||
|
rax_persistent_highlight.register("ready_mag", check_ready) --used like a callback register
|
||
|
rax_icon_layers.register("ready_mag", icon_check_ready) --used like a callback register
|
||
|
RegisterScriptCallback("actor_item_to_slot",actor_item_to_slot)
|
||
|
RegisterScriptCallback("actor_item_to_ruck",actor_item_to_slot)
|
||
|
RegisterScriptCallback("actor_item_to_belt",actor_item_to_slot)
|
||
|
RegisterScriptCallback("actor_on_item_drop",actor_item_to_slot)
|
||
|
RegisterScriptCallback("server_entity_on_unregister",se_item_on_unregister)
|
||
|
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
|
||
|
RegisterScriptCallback("ActorMenu_on_trade_started",on_trade_opened)
|
||
|
RegisterScriptCallback("ActorMenu_on_trade_closed",on_trade_closed)
|
||
|
end
|
||
|
|