Divergent/mods/Hideout Furniture/gamedata/scripts/weapon_showcase.script

271 lines
11 KiB
Plaintext
Raw Normal View History

2024-03-17 20:18:03 -04:00
weapon_displays = {}
weapon_display_slots = {}
function on_game_start()
RegisterScriptCallback("ActorMenu_on_item_after_move", ActorMenu_on_item_after_move)
RegisterScriptCallback("ActorMenu_on_item_before_move", ActorMenu_on_item_before_move)
RegisterScriptCallback("actor_on_item_before_pickup", actor_on_item_before_pickup)
RegisterScriptCallback("game_object_on_net_spawn", game_object_on_net_spawn)
RegisterScriptCallback("save_state", save_state)
RegisterScriptCallback("load_state", load_state)
-- Patch the displayed items into world items, so that npc's don't pick them up
local base_is_world_item = game_setup.is_world_item
function game_setup.is_world_item(id)
for case_id, case_guns in pairs(weapon_displays) do
for item_id, world_id in pairs(case_guns) do
if id == world_id then
return true
end
end
end
return base_is_world_item(id)
end
end
valid_actor_bags = {
[EDDListType.iActorBag] = true,
[EDDListType.iActorSlot] = true
}
valid_stash = {}
valid_display_kind = {}
-- probably a bad idea to mix display metadata with pos/rot offsets in seperate sections, but in the same LTX
local ini_displays = ini_file("items\\settings\\hideout_furniture\\displays\\displays.ltx")
ini_displays:section_for_each(function(section)
local slots = ini_displays:r_float_ex(section, "slots")
if not slots then return end -- exit early if there is no number of slots
valid_stash[section] = slots
local valid_classes = ini_displays:r_list(section, "valid_classes")
valid_display_kind[section] = {}
for i, class in pairs(valid_classes) do
valid_display_kind[section][class] = true
end
end)
---@param se_obj game_object
function game_object_on_net_spawn(se_obj)
for case_id, case_guns in pairs(weapon_displays) do
for item_id, world_id in pairs(case_guns) do
if se_obj:id() == world_id then
CreateTimeEvent("collector","freeze_" .. se_obj:id(), 0, freeze_item, se_obj:id(), item_id)
return
end
end
end
end
function ActorMenu_on_item_before_move(flags, case_id, item, mode, bag_from)
if not (case_id and item and mode == "loot") then return end
local case_obj = get_object_by_id(case_id)
local case_sec = case_obj:section()
if case_obj and case_sec and valid_stash[case_sec] and valid_actor_bags[bag_from] then
local sec = item:section()
local kind = SYS_GetParam(0, sec, "kind")
local case_slot_count = valid_stash[case_sec]
if weapon_displays[case_id] and size_table(weapon_displays[case_id]) >= case_slot_count or not(kind and valid_display_kind[case_sec][kind]) then
flags.ret_value = false
end
end
end
function ActorMenu_on_item_after_move(case_id, item, mode, bag_from)
if not (case_id and item and mode == "loot") then return end
local case_obj = get_object_by_id(case_id)
local case_sec = case_obj:section()
if case_obj and case_sec and valid_stash[case_sec] then
local sec = item:section()
local kind = SYS_GetParam(0, sec, "kind")
local item_id = item:id()
if valid_actor_bags[bag_from] and kind and valid_display_kind[case_sec][kind] then
if weapon_displays[case_id] and weapon_displays[case_id][item_id] then
RemoveTimeEvent("collector","remove_" .. weapon_displays[case_id][item_id])
else
if not weapon_displays[case_id] then
weapon_displays[case_id] = {}
weapon_display_slots[case_id] = {}
end
local ini_wd = ini_file("items\\settings\\hideout_furniture\\displays\\displays.ltx")
local se_case = alife_object(case_id)
local pos = vector():set(case_obj:position())
local case_slot_count = valid_stash[case_sec]
local gun_number = get_first_open_slot(weapon_display_slots[case_id], case_slot_count)
local gun_section = ini_sys:r_string_ex(sec,"parent_section") or sec
-- Rotation --
-- Pull slot angle for stash
local angle = vector():set(se_case.angle)
local angle_q = aol_rotation.Quaternion(angle)
local angle_str = ini_wd:r_string_ex(case_sec .. "_angles",gun_number)
-- Pull angle for gun slot
local case_slot_angle = vector():set(string_to_vector(angle_str)) -- get specific angle for gun slot
case_slot_angle = case_slot_angle:mul(0.017453292519943295769236907684886) -- convert to radian
-- Pull angle for gun
local gun_adjust_str = ini_wd:r_string_ex("gun_angle_offsets",gun_section) or "0,0,0"
local gun_adjust_angle = string_to_vector(gun_adjust_str)
gun_adjust_angle = gun_adjust_angle:mul(0.017453292519943295769236907684886)
local angle_offset = case_slot_angle:add(gun_adjust_angle)
local angle_offset_q = aol_rotation.Quaternion(angle_offset)
local q_rotation = angle_offset_q:multiply(angle_q)
-- Location --
local pos_offset = vector():set(0,0,0)
-- Add position offset (weapon-specific, in vanilla)
local position_offset_str = ini_sys:r_string_ex(sec, "position")
if position_offset_str then
local vec = string_to_vector(position_offset_str)
vec.x = 0 -- ignore x axis offset
pos_offset:add(vec)
end
-- Pull position adjustment for gun (weapon-display-specific)
local ini_gun_offsets = ini_file("items\\settings\\hideout_furniture\\item_offsets\\items.ltx")
local pos_gun_adjust = ini_gun_offsets:r_string_ex("gun_position_offsets",gun_section)
if pos_gun_adjust then
-- Rotate offset to align with orientation of stash
pos_offset:add(string_to_vector(pos_gun_adjust))
end
-- Rotate position offset by unique gun rotation offset
pos_offset = angle_offset_q:rotate_vector(pos_offset)
-- Pull slot positions for stash
local slot_pos_str = ini_wd:r_string_ex(case_sec .. "_positions",gun_number)
local slot_pos = vector():set(string_to_vector(slot_pos_str))
-- pos_offset = aol_rotation.rotate_vector_by_euler(pos_offset, se_case.angle)
pos_offset:add(slot_pos)
pos_offset = angle_q:rotate_vector(pos_offset)
pos = pos:add(pos_offset)
-- Create object at pos
local world_se_obj = alife_create_item(sec, { pos, db.actor:level_vertex_id(), db.actor:game_vertex_id() })
-- Rotate object
world_se_obj.angle = q_rotation:to_euler_angles()
-- world_se_obj.angle = angle
-- Set invisible to AI (prevent pickup)
local data = utils_stpk.get_weapon_data(world_se_obj)
local remove_flags = 128
local flag_mask = bit_not(remove_flags)
data.object_flags = bit_and(data.object_flags, flag_mask)
utils_stpk.set_weapon_data(data, world_se_obj)
release_item_manager.unmark_item(world_se_obj.id)
weapon_displays[case_id][item_id] = world_se_obj.id
weapon_display_slots[case_id][gun_number] = world_se_obj.id
end
elseif weapon_displays[case_id] and weapon_displays[case_id][item_id] then
CreateTimeEvent("collector","remove_" .. weapon_displays[case_id][item_id], 0, remove_displayed_item, item_id, weapon_displays[case_id][item_id], case_id)
end
end
end
function string_to_vector(string)
local t = str_explode(string,",")
for k, str in pairs(t) do
t[k] = string.gsub(str, "%s+", "") -- remove whitespaces
t[k] = tonumber(t[k])
--printf(str)
end
return vector():set(t[1], t[2], t[3])
end
function get_first_open_slot(t, max_slots)
for i=1, max_slots do
if not t[i] then
return i
end
end
end
function actor_on_item_before_pickup(obj, flags)
for case_id, case_guns in pairs(weapon_displays) do
for item_id, world_id in pairs(case_guns) do
if obj:id() == world_id then
--printf("NO PICKUP")
flags.ret_value = false
end
end
end
end
function freeze_item(id, item_id)
local obj = level.object_by_id(id)
local phys = obj and obj:get_physics_shell()
if phys then
-- Show silencer/launcher if stashed item has the attachment
if utils_item.has_attached_silencer(get_object_by_id(item_id)) then
obj:set_bone_visible("wpn_silencer", true, true)
end
if utils_item.has_attached_gl(get_object_by_id(item_id)) then
obj:set_bone_visible("wpn_launcher", true, true)
end
phys:freeze()
return true
end
return false
end
function remove_displayed_item(item_id, world_item_id, case_id)
local obj = alife_object(world_item_id)
if obj then
alife_release(obj) -- USE safe_release_manager.release(se_obj) ??????
weapon_displays[case_id][item_id] = nil
for slot, id in pairs(weapon_display_slots[case_id]) do
if id == world_item_id then
weapon_display_slots[case_id][slot] = nil
break
end
end
if size_table(weapon_displays[case_id]) == 0 then
weapon_displays[case_id] = nil
weapon_display_slots[case_id] = nil
end
RemoveTimeEvent("collector","freeze_" .. world_item_id)
--printf("DISPLAYED ITEM REMOVED")
return true
end
--printf("TRYING TO REMOVE")
return false
end
function vector_rotate_y_radian(v, angle)
--angle = angle * 0.017453292519943295769236907684886
local c = math.cos (angle)
local s = math.sin (angle)
return vector():set(v.x * c - v.z * s, v.y, v.x * s + v.z * c)
end
function angle_to_radian(angle)
return angle * 0.017453292519943295769236907684886
end
function save_state(m_data)
m_data.weapon_displays = weapon_displays
m_data.weapon_display_slots = weapon_display_slots
end
function load_state(m_data)
weapon_displays = m_data.weapon_displays or {}
weapon_display_slots = m_data.weapon_display_slots or {}
end