Added, Updated and Removed Mods; Updated Readme
This commit is contained in:
@@ -0,0 +1,955 @@
|
||||
|
||||
--[[
|
||||
Jabbers
|
||||
05APR2023
|
||||
Jabbers' Soulslike Anomaly Mod
|
||||
--]]
|
||||
|
||||
local version = "0.28-beta"
|
||||
|
||||
local tools_list = {
|
||||
["itm_basickit"] = true,
|
||||
["itm_advancedkit"] = true,
|
||||
["itm_expertkit"] = true,
|
||||
["itm_drugkit"] = true,
|
||||
["itm_ammokit"] = true,
|
||||
["itm_gunsmith_toolkit"] = true,
|
||||
["itm_artefactskit"] = true,
|
||||
}
|
||||
|
||||
local scenario_logic = nil
|
||||
|
||||
RELATIONS = {
|
||||
FRIENDS = 1000,
|
||||
BUDDIES = 500,
|
||||
NEUTRALS = 0,
|
||||
ENEMIES = -1000
|
||||
}
|
||||
|
||||
SCENARIOS = {
|
||||
Default = 1,
|
||||
RFDetectorStash = 2,
|
||||
HiddenStash = 3,
|
||||
NoLoss = 4,
|
||||
}
|
||||
|
||||
hit_type_to_str = {
|
||||
[hit.light_burn] = "Light Burn",
|
||||
[hit.burn] = "Burn",
|
||||
[hit.strike] = "Strike",
|
||||
[hit.shock] = "Shock",
|
||||
[hit.wound] = "Wound",
|
||||
[hit.radiation] = "Radiation",
|
||||
[hit.telepatic] = "Telepatic",
|
||||
[hit.chemical_burn] = "Chemical Burn",
|
||||
[hit.explosion] = "Explosion",
|
||||
[hit.fire_wound] = "Fire",
|
||||
}
|
||||
|
||||
entity_type = {
|
||||
Stalker = 1,
|
||||
Monster = 2,
|
||||
Anomaly = 3,
|
||||
Self = 4,
|
||||
Other = 4
|
||||
}
|
||||
|
||||
----------------------------------------
|
||||
-- Classes
|
||||
----------------------------------------
|
||||
|
||||
MAX_HIT_POOL_COUNT = 30
|
||||
MAX_HIT_TIME = 30000
|
||||
local hit_queue = soulslike_classes.TimedQueue(MAX_HIT_POOL_COUNT, MAX_HIT_TIME)
|
||||
|
||||
----------------------------------------
|
||||
-- Helpers
|
||||
----------------------------------------
|
||||
|
||||
function try(func, ...)
|
||||
local status, error_or_result = pcall(func, ...)
|
||||
if not status then
|
||||
soulslike.error(error_or_result)
|
||||
return false
|
||||
else
|
||||
return error_or_result
|
||||
end
|
||||
end
|
||||
|
||||
function math.clamp(x, min, max)
|
||||
if x < min then return min end
|
||||
if x > max then return max end
|
||||
return x
|
||||
end
|
||||
|
||||
function table.get_length(T)
|
||||
local count = 0
|
||||
for _ in pairs(T) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
function table.has_value(tab, val)
|
||||
for _, value in ipairs(tab) do
|
||||
if value == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- DEBUG
|
||||
----------------------------------------
|
||||
local function print_table (tbl, indent)
|
||||
if not indent then
|
||||
indent = 0
|
||||
end
|
||||
|
||||
utils_data.debug_write(string.rep(" ", indent) .. "{")
|
||||
|
||||
indent = indent + 2
|
||||
|
||||
if (type(tbl) == "userdata") then
|
||||
utils_data.debug_write("<userdata>,\n")
|
||||
else
|
||||
for k, v in pairs(tbl) do
|
||||
local toprint = string.rep(" ", indent)
|
||||
|
||||
if (type(k) == "number") then
|
||||
toprint = toprint .. "[" .. k .. "] = "
|
||||
elseif (type(k) == "string") then
|
||||
toprint = toprint .. k .. " = "
|
||||
end
|
||||
|
||||
if (type(v) == "number") then
|
||||
utils_data.debug_write(toprint .. v .. ",")
|
||||
elseif (type(v) == "string") then
|
||||
utils_data.debug_write(toprint .. "\"" .. v .. "\",")
|
||||
elseif (type(v) == "table") then
|
||||
utils_data.debug_write(toprint)
|
||||
print_table(v, indent + 2)
|
||||
else
|
||||
utils_data.debug_write(toprint .. "\"" .. tostring(v) .. "\",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils_data.debug_write(string.rep(" ", indent-2) .. "}")
|
||||
end
|
||||
|
||||
local function log(log_type, output)
|
||||
if not output then
|
||||
utils_data.debug_write("[Soulslike] "..log_type..": ".."(nil)")
|
||||
elseif (type(output) == "table") then
|
||||
utils_data.debug_write("[Soulslike] "..log_type..": ")
|
||||
print_table(output)
|
||||
else
|
||||
utils_data.debug_write("[Soulslike] "..log_type..": "..output)
|
||||
end
|
||||
end
|
||||
|
||||
function debug(output)
|
||||
log("DEBUG", output)
|
||||
end
|
||||
|
||||
function info(output)
|
||||
log("INFO ", output)
|
||||
end
|
||||
|
||||
function warn(output)
|
||||
log("WARN ", output)
|
||||
end
|
||||
|
||||
function error(output)
|
||||
log("ERROR", output)
|
||||
end
|
||||
|
||||
function debug_tip(text, delay)
|
||||
debug(text)
|
||||
|
||||
if not soulslike_mcm.show_debug_tips() then
|
||||
return
|
||||
end
|
||||
|
||||
local text = "[Soulslike] "..tostring(text)
|
||||
|
||||
if not db.actor then
|
||||
return
|
||||
end
|
||||
|
||||
local ico = "ui_inGame2_Dengi_otdani"
|
||||
local text_color = utils_xml.get_color("pda_white")
|
||||
|
||||
text = text_color .. text
|
||||
|
||||
if delay == nil then
|
||||
delay = 6000
|
||||
end
|
||||
|
||||
news_manager.send_tip(db.actor, text, nil, ico, delay)
|
||||
end
|
||||
|
||||
|
||||
----------------------------------------
|
||||
-- Globals
|
||||
----------------------------------------
|
||||
|
||||
function _G.IsSoulslikeMode()
|
||||
return not IsHardcoreMode() and axr_main.config and axr_main.config:r_value("character_creation","new_game_soulslike_mode",1) == true or alife_storage_manager.get_state().enable_soulslike_mode == true
|
||||
end
|
||||
|
||||
function _G.IsToolkit(o, s)
|
||||
if not (s) then
|
||||
s = o and o:section()
|
||||
end
|
||||
return tools_list[s]
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Helper Functions
|
||||
----------------------------------------
|
||||
|
||||
function get_soulslike_state()
|
||||
local game_state = alife_storage_manager.get_state()
|
||||
|
||||
if not game_state.soulslike then
|
||||
game_state.soulslike = {
|
||||
created_stashes = {},
|
||||
spawn_location = {
|
||||
level = nil,
|
||||
position = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil,
|
||||
},
|
||||
angle = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil,
|
||||
},
|
||||
level_vertex_id = nil,
|
||||
game_vertex_id = nil,
|
||||
},
|
||||
note_message_data = {},
|
||||
hidden_stashes = {}
|
||||
}
|
||||
end
|
||||
|
||||
-- Backward save compatibility
|
||||
if not game_state.soulslike.note_message_data then
|
||||
game_state.soulslike.note_message_data = {}
|
||||
end
|
||||
|
||||
if not game_state.soulslike.hidden_stashes then
|
||||
game_state.soulslike.hidden_stashes = {}
|
||||
end
|
||||
|
||||
if not game_state.soulslike.created_stashes then
|
||||
game_state.soulslike.created_stashes = {}
|
||||
end
|
||||
|
||||
if not game_state.soulslike.spawn_location then
|
||||
game_state.soulslike.spawn_location = {
|
||||
level = nil,
|
||||
position = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil,
|
||||
},
|
||||
angle = {
|
||||
x = nil,
|
||||
y = nil,
|
||||
z = nil,
|
||||
},
|
||||
level_vertex_id = nil,
|
||||
game_vertex_id = nil,
|
||||
}
|
||||
end
|
||||
|
||||
return game_state.soulslike
|
||||
end
|
||||
|
||||
function set_spawn(show_message)
|
||||
local se_actor = alife():actor()
|
||||
local state = get_soulslike_state()
|
||||
|
||||
state.spawn_location.level = level.name()
|
||||
state.spawn_location.position.x = se_actor.position.x
|
||||
state.spawn_location.position.y = se_actor.position.y
|
||||
state.spawn_location.position.z = se_actor.position.z
|
||||
state.spawn_location.angle.x = se_actor.angle.x
|
||||
state.spawn_location.angle.y = se_actor.angle.y
|
||||
state.spawn_location.angle.z = se_actor.angle.z
|
||||
state.spawn_location.level_vertex_id = se_actor.m_level_vertex_id
|
||||
state.spawn_location.game_vertex_id = se_actor.m_game_vertex_id
|
||||
|
||||
debug("Saved spawn location data:")
|
||||
|
||||
if show_message then
|
||||
local str = game.translate_string("st_soulslike_spawn_location_set")
|
||||
actor_menu.set_msg(1, str, 4)
|
||||
end
|
||||
end
|
||||
|
||||
function find_closest_enemy()
|
||||
local enemy = nil
|
||||
local enemy_dist = 150
|
||||
local sim = alife()
|
||||
local gg = game_graph()
|
||||
local level_name = level.name()
|
||||
|
||||
if not sim then return end
|
||||
if not gg then return end
|
||||
|
||||
for i=1,65534 do
|
||||
local se_obj = sim:object(i)
|
||||
if se_obj and (level_name == sim:level_name(gg:vertex(se_obj.m_game_vertex_id):level_id())) then
|
||||
local cls = se_obj:clsid()
|
||||
local sec = se_obj:section_name()
|
||||
local is_valid = false
|
||||
|
||||
if IsStalker(nil,cls) and string.find(sec,"sim_default_") and se_obj:alive() then
|
||||
local comm = se_obj:community()
|
||||
if (comm ~= "trader") and (comm ~= "zombied") then
|
||||
is_valid = true
|
||||
end
|
||||
elseif IsMonster(nil,cls) then
|
||||
is_valid = true
|
||||
end
|
||||
|
||||
if is_valid then
|
||||
local dist = se_obj.position:distance_to_sqr(db.actor:position())
|
||||
if enemy_dist > dist then
|
||||
if IsStalker(nil,cls) then
|
||||
local comm = se_obj:community()
|
||||
debug("Found enemy "..se_obj:name().." from the "..comm.." community "..tostring(dist).." meters away.")
|
||||
enemy = se_obj
|
||||
elseif IsMonster(nil,cls) then
|
||||
enemy = se_obj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return enemy
|
||||
end
|
||||
|
||||
function find_closest_enemy_mutant()
|
||||
local enemy = nil
|
||||
local enemy_dist = 75
|
||||
local sim = alife()
|
||||
local gg = game_graph()
|
||||
local level_name = level.name()
|
||||
|
||||
if not sim then return end
|
||||
if not gg then return end
|
||||
|
||||
for i=1,65534 do
|
||||
local se_obj = sim:object(i)
|
||||
if se_obj and (level_name == sim:level_name(gg:vertex(se_obj.m_game_vertex_id):level_id())) then
|
||||
local cls = se_obj:clsid()
|
||||
|
||||
if IsMonster(nil,cls) then
|
||||
local dist = se_obj.position:distance_to_sqr(db.actor:position())
|
||||
if enemy_dist > dist then
|
||||
if IsMonster(nil,cls) then
|
||||
enemy = se_obj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return enemy
|
||||
end
|
||||
|
||||
function find_closest_enemy_stalker()
|
||||
local friend = nil
|
||||
local friend_dist = 100000
|
||||
|
||||
for i=1, #db.OnlineStalkers do
|
||||
local id = db.OnlineStalkers[i]
|
||||
local npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
|
||||
|
||||
if npc then
|
||||
local dist = npc:position():distance_to_sqr(db.actor:position())
|
||||
local is_friend = npc:general_goodwill(db.actor) <= RELATIONS.ENEMIES
|
||||
|
||||
if npc:alive() and is_friend and friend_dist > dist and (not get_object_story_id(id)) then
|
||||
local comm = npc:character_community()
|
||||
debug("Found friend "..npc:name().." from the "..comm.." community "..tostring(dist).." meters away.")
|
||||
friend = npc
|
||||
friend_dist = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return friend
|
||||
end
|
||||
|
||||
function find_closest_friendly_stalker()
|
||||
local friend = nil
|
||||
local friend_dist = 100000
|
||||
|
||||
for i=1, #db.OnlineStalkers do
|
||||
local id = db.OnlineStalkers[i]
|
||||
local npc = db.storage[id] and db.storage[id].object or level.object_by_id(id)
|
||||
|
||||
if npc then
|
||||
local dist = npc:position():distance_to_sqr(db.actor:position())
|
||||
local is_friend = npc:general_goodwill(db.actor) >= RELATIONS.ENEMIES
|
||||
|
||||
if npc:alive() and is_friend and friend_dist > dist and (not get_object_story_id(id)) then
|
||||
local comm = npc:character_community()
|
||||
debug("Found friend "..npc:name().." from the "..comm.." community "..tostring(dist).." meters away.")
|
||||
friend = npc
|
||||
friend_dist = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return friend
|
||||
end
|
||||
|
||||
function force_save(type)
|
||||
--if game isn't already paused, then force a pause here
|
||||
local force_pause
|
||||
if not (device():is_paused()) then
|
||||
device():pause(true)
|
||||
force_pause = true
|
||||
end
|
||||
local Y, M, D, h
|
||||
Y, M, D, h = game.get_game_time():get(Y, M, D, h)
|
||||
|
||||
local m = level.get_time_minutes()
|
||||
if m < 10 then
|
||||
m = ("0"..m)
|
||||
end
|
||||
|
||||
local comm = utils_xml.get_special_txt(db.actor:character_community())
|
||||
local map = utils_xml.get_special_txt(level.name())
|
||||
local date = string.format("%d.%d.%d %d-%d", D, M, Y, h, m)
|
||||
local file_name = "soulslike_"..comm.." - "..map.." "..date.." - "..type
|
||||
|
||||
exec_console_cmd("save ".. file_name)
|
||||
|
||||
if (force_pause) then
|
||||
device():pause(false)
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Dream Callbacks
|
||||
----------------------------------------
|
||||
|
||||
function wakeup_callback()
|
||||
debug("wakeup_callback")
|
||||
xr_effects.enable_ui(db.actor, nil)
|
||||
|
||||
exec_console_cmd("snd_volume_music "..tostring(_G.mus_vol))
|
||||
exec_console_cmd("snd_volume_eff "..tostring(_G.amb_vol))
|
||||
|
||||
_G.amb_vol = 0
|
||||
_G.mus_vol = 0
|
||||
|
||||
disable_info("tutorial_sleep")
|
||||
disable_info("actor_is_sleeping")
|
||||
disable_info("sleep_active")
|
||||
|
||||
debug("Looking for scenario logic.")
|
||||
|
||||
if scenario_logic then
|
||||
debug("Completing scenario.")
|
||||
scenario_logic:OnComplete()
|
||||
else
|
||||
error("No logic state")
|
||||
end
|
||||
|
||||
local data = get_soulslike_state()
|
||||
scenario_logic:destroy()
|
||||
scenario_logic = nil
|
||||
data.logic_state = nil
|
||||
end
|
||||
|
||||
function dream_callback()
|
||||
debug("dream_callback")
|
||||
level.add_cam_effector("camera_effects\\sleep.anm", 10, false, "soulslike.wakeup_callback")
|
||||
|
||||
local hours = math.random(6,14)
|
||||
level.change_game_time(0,hours,0)
|
||||
|
||||
db.actor.power = 1
|
||||
|
||||
SendScriptCallback("actor_on_sleep", hours)
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- Game Callbacks
|
||||
----------------------------------------
|
||||
|
||||
local function actor_on_before_death(who, flags)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Pretty sure this fixes arena fights, still need to test
|
||||
if has_alife_info("bar_arena_fight") then
|
||||
debug('Actor was in an arena fight, ignoring death.')
|
||||
return
|
||||
end
|
||||
|
||||
debug('Actor died')
|
||||
game_statistics.increment_statistic("deaths")
|
||||
|
||||
hit_queue:Invalidate()
|
||||
scenario_logic = soulslike_scenario_logic_factory.create_new(hit_queue:Values())
|
||||
|
||||
if scenario_logic then
|
||||
scenario_logic:OnDeath()
|
||||
flags.ret_value = false
|
||||
end
|
||||
|
||||
hit_queue:Clear()
|
||||
end
|
||||
|
||||
local function on_before_save_input(flags, type, text)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
-- No hardcore save setting, allow saving
|
||||
if not soulslike_mcm.is_hardcore_save_enabled() then
|
||||
return
|
||||
end
|
||||
|
||||
-- Hardcore save is enabled, but we still want to save at campfires
|
||||
-- We just return to let the regular saving work.
|
||||
if soulslike_mcm.override_campfire_hardcore_saves() then
|
||||
return
|
||||
end
|
||||
|
||||
-- All other scenarios flow through here and we just disallow saving
|
||||
if not level_weathers.valid_levels[level.name()] then
|
||||
return
|
||||
end
|
||||
|
||||
debug('User tried to save')
|
||||
|
||||
local str = game.translate_string("st_save_only_when_sleeping")
|
||||
actor_menu.set_msg(1, str, 4)
|
||||
exec_console_cmd("main_menu off")
|
||||
flags.ret = true
|
||||
end
|
||||
|
||||
local function try_send_inventory_examined_message(stash_id)
|
||||
local data = get_soulslike_state()
|
||||
local stash_data = data.created_stashes[stash_id]
|
||||
|
||||
if stash_data and not stash_data.examine then
|
||||
local lost_items = stash_data.lost_items
|
||||
|
||||
debug('Stash not yet examined.')
|
||||
debug(lost_items)
|
||||
|
||||
if #lost_items > 0 then
|
||||
|
||||
local msg = "You examine your belongings and find that you were missing the following items: "
|
||||
local item_groups = {}
|
||||
|
||||
for _, sec in pairs(lost_items) do
|
||||
if not item_groups[sec] then
|
||||
item_groups[sec] = {
|
||||
section = sec,
|
||||
count = 1
|
||||
}
|
||||
else
|
||||
item_groups[sec].count = item_groups[sec].count + 1
|
||||
end
|
||||
end
|
||||
|
||||
local item_names = {}
|
||||
|
||||
for _, group in pairs(item_groups) do
|
||||
local inv_name = ui_item.get_sec_name(group.section)
|
||||
if group.count > 1 then
|
||||
table.insert(item_names, tostring(group.count).." x "..inv_name)
|
||||
else
|
||||
table.insert(item_names, inv_name)
|
||||
end
|
||||
end
|
||||
|
||||
msg = msg..table.concat(item_names, ", ")
|
||||
|
||||
local ui_sender = news_manager.tips_icons['default']
|
||||
db.actor:give_game_news("", msg, ui_sender, 0, 20000)
|
||||
|
||||
stash_data.examine = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function actor_on_item_take_from_box(box,obj)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local state = get_soulslike_state()
|
||||
local id = box:id()
|
||||
|
||||
if (box:section() == "inv_backpack") then
|
||||
if (box:is_inv_box_empty()) then
|
||||
hide_hud_inventory()
|
||||
|
||||
local se_obj = alife_object(id)
|
||||
|
||||
if se_obj then
|
||||
alife_release(se_obj)
|
||||
try_send_inventory_examined_message(id)
|
||||
state.created_stashes[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function actor_on_stash_remove(data)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local state = get_soulslike_state()
|
||||
|
||||
if state.created_stashes[data.stash_id] then
|
||||
data.cancel = true
|
||||
try_send_inventory_examined_message(state.stash_id)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_console_execute(name, ...)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
if(name == "save") then
|
||||
debug(name)
|
||||
local extraArgs = {...}
|
||||
|
||||
if extraArgs then
|
||||
local last_save_file_name = table.concat(extraArgs," ")
|
||||
debug(last_save_file_name)
|
||||
if soulslike_mcm.is_hardcore_save_enabled() then
|
||||
last_save_file_name = string.lower(last_save_file_name)
|
||||
|
||||
debug("Don't delete: ".. last_save_file_name)
|
||||
|
||||
local uuid = get_soulslike_state().uuid
|
||||
local fs = getFS()
|
||||
if not fs then return end
|
||||
|
||||
local flist = fs:file_list_open_ex("$game_saves$",bit_or(FS.FS_ListFiles,FS.FS_RootOnly),"*.scoc")
|
||||
local f_cnt = flist:Size()
|
||||
|
||||
for it=0, f_cnt-1 do
|
||||
local file = flist:GetAt(it)
|
||||
local file_name = string.sub(file:NameFull(), 0, (string.len(file:NameFull()) - string.len(".scoc")))
|
||||
|
||||
local scoc_path = fs:update_path('$game_saves$', '')..file_name..".scoc"
|
||||
local scop_path = fs:update_path('$game_saves$', '')..file_name..".scop"
|
||||
local dds_path = fs:update_path('$game_saves$', '')..file_name..".dds"
|
||||
|
||||
local f = io.open(scoc_path,"rb")
|
||||
|
||||
if f then
|
||||
local data = f:read("*all")
|
||||
f:close()
|
||||
|
||||
if (data) then
|
||||
local decoded = alife_storage_manager.decode(data)
|
||||
local d_soulslike = decoded and decoded.soulslike
|
||||
|
||||
if (d_soulslike and (d_soulslike.uuid == uuid)) then
|
||||
debug("/ Soulslike mode | file: "..file_name)
|
||||
file_name = string.lower(file_name)
|
||||
if file_name ~= last_save_file_name then
|
||||
debug("~ Soulslike mode | delete save file: "..file_name)
|
||||
|
||||
local scoc_path_bak = fs:update_path('$game_saves$', '').."soulslike-backup/"..file_name..".scoc"
|
||||
local scop_path_bak = fs:update_path('$game_saves$', '').."soulslike-backup/"..file_name..".scop"
|
||||
local dds_path_bak = fs:update_path('$game_saves$', '').."soulslike-backup/"..file_name..".dds"
|
||||
|
||||
fs:file_copy(scoc_path, scoc_path_bak)
|
||||
fs:file_copy(scop_path, scop_path_bak)
|
||||
fs:file_copy(dds_path, dds_path_bak)
|
||||
|
||||
ui_load_dialog.delete_save_game(file_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function physic_object_on_use_callback(box, who)
|
||||
local data = get_soulslike_state()
|
||||
|
||||
if not IsInvbox(box) or not data.hidden_stashes or not data.hidden_stashes[box:id()] then
|
||||
return
|
||||
end
|
||||
|
||||
local id = box:id()
|
||||
local stash_data = data.hidden_stashes[id]
|
||||
local stash_id = stash_data.stash_id
|
||||
local se_obj = alife_object(stash_id)
|
||||
local stash = level.object_by_id(stash_id)
|
||||
|
||||
if not stash then
|
||||
warn("Not expected, unable to find stash id linked to "..tostring(id)..". Please save and reload near the stash to see if it solves the issue.")
|
||||
return
|
||||
end
|
||||
|
||||
local sim = alife();
|
||||
|
||||
if not sim then return end
|
||||
|
||||
if se_obj and not se_obj.online then
|
||||
se_obj:switch_online()
|
||||
end
|
||||
|
||||
sim:set_switch_online(stash_id,true)
|
||||
sim:set_switch_offline(stash_id,false)
|
||||
|
||||
try_send_inventory_examined_message(stash_id)
|
||||
|
||||
local function transfer_item(temp,item)
|
||||
debug("Transfering item "..item:section())
|
||||
stash:transfer_item(item, box)
|
||||
end
|
||||
|
||||
if stash_data.radio_id then
|
||||
debug("Clearing radio stash "..tostring(stash_data.radio_id))
|
||||
item_radio.clear_stash(stash_data.radio_level, stash_data.radio_id)
|
||||
end
|
||||
|
||||
debug("Transfering items from hidden stash "..tostring(id).." to static stash "..tostring(stash_id))
|
||||
|
||||
|
||||
stash:iterate_inventory_box(transfer_item)
|
||||
alife_release(stash)
|
||||
|
||||
debug("Removing PDA marker "..tostring(id))
|
||||
level.map_remove_object_spot(id , "secondary_task_location")
|
||||
|
||||
data.hidden_stashes[id] = nil
|
||||
end
|
||||
|
||||
local function load_state(data)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local data = get_soulslike_state()
|
||||
|
||||
if not data.logic_state then
|
||||
debug("No logic state")
|
||||
elseif data.logic_state and not data.logic_state.scenario_id then
|
||||
warn("Logic state exists without scenario id")
|
||||
elseif data.logic_state and data.logic_state.scenario_id then
|
||||
-- Reinitialize the last scenario using the saved logic state.
|
||||
scenario_logic = soulslike_scenario_logic_factory.create_by_id(data.logic_state.scenario_id, data.logic_state)
|
||||
end
|
||||
end
|
||||
|
||||
local function save_state(data)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local data = get_soulslike_state()
|
||||
|
||||
if not data.spawn_location.level and level.name() ~= 'fake_start' then
|
||||
set_spawn(false)
|
||||
end
|
||||
|
||||
if scenario_logic then
|
||||
-- Save the logic state for the current scenario so we can restore it on load
|
||||
-- This is for the purposes of ChangeLevel which unloads the scripts and then
|
||||
-- reloads them, so we need to be able to reinitialize the scenario as it was
|
||||
-- before changing levels
|
||||
data.logic_state = scenario_logic.logic_state;
|
||||
end
|
||||
end
|
||||
|
||||
local function on_level_changing()
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
debug("On on_level_changing load.")
|
||||
local data = get_soulslike_state()
|
||||
|
||||
if not data.spawn_location.level and level.name() ~= 'fake_start' then
|
||||
set_spawn(false)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_game_load()
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
debug("On game load.")
|
||||
|
||||
local data = get_soulslike_state()
|
||||
local sim = alife()
|
||||
|
||||
if sim then
|
||||
debug("Updating position and status of hidden stashes.")
|
||||
for box_id, value in pairs(data.hidden_stashes) do
|
||||
local se_hidden_stash = alife_object(value.stash_id)
|
||||
local se_box = alife_object(box_id)
|
||||
|
||||
if not se_hidden_stash then
|
||||
debug("Unable to find hidden stash "..tostring(value.stash_id))
|
||||
elseif not se_box then
|
||||
debug("Unable to find box id "..tostring(box_id))
|
||||
else
|
||||
debug("Moving hidden stash"..tostring(value.stash_id))
|
||||
sim:teleport_object(value.stash_id, se_box.m_game_vertex_id, se_box.m_level_vertex_id, se_box.position)
|
||||
debug("Setting hidden stash online")
|
||||
if not se_box.online then
|
||||
se_box:switch_online()
|
||||
end
|
||||
|
||||
for i=1,65534 do
|
||||
local se_obj = sim:object(i)
|
||||
if (se_obj and se_obj.parent_id == id) then
|
||||
debug("Moving "..se_obj.section.." id:"..tostring(value.stash_id))
|
||||
sim:teleport_object(value.stash_id, se_box.m_game_vertex_id, se_box.m_level_vertex_id, se_box.position)
|
||||
if se_obj then
|
||||
if not se_obj.online then
|
||||
debug("Setting "..se_obj.section.." online")
|
||||
se_obj:switch_online()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Write out an identifier so we can use it later
|
||||
-- to identify other saves with the same ID if the
|
||||
-- user is playing with Hardcore Saves enabled.
|
||||
if not data.uuid then
|
||||
data.uuid = GAME_VERSION .. "_" .. tostring(math.random(100)) .. tostring(math.random()) .. tostring(math.random(1000))
|
||||
end
|
||||
|
||||
debug("Looking for scenario logic.")
|
||||
|
||||
if scenario_logic then
|
||||
debug("Calling scenario respawn.")
|
||||
scenario_logic:OnRespawn()
|
||||
end
|
||||
end
|
||||
|
||||
local function actor_on_sleep(hours)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
soulslike.debug('actor_on_sleep')
|
||||
|
||||
-- Only force save if we aren't running a scenario
|
||||
if not scenario_logic then
|
||||
soulslike.force_save("sleep")
|
||||
end
|
||||
end
|
||||
|
||||
local detour_actor_on_before_hit = nil
|
||||
|
||||
local function actor_on_before_hit(shit, bone_id, flags)
|
||||
if not IsSoulslikeMode() then
|
||||
if detour_actor_on_before_hit then
|
||||
detour_actor_on_before_hit(shit, bone_id, flags)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local damage = shit.power
|
||||
if grok_actor_damage_balancer and grok_actor_damage_balancer.damage then
|
||||
--debug("Using damage from Grok's Damage Balancer")
|
||||
damage = grok_actor_damage_balancer.damage
|
||||
end
|
||||
|
||||
--debug("Player hit:")
|
||||
--debug("shit.type: "..tostring(hit_type_to_str[shit.type]))
|
||||
--debug("shit.power: "..tostring(damage))
|
||||
--debug("shit.impulse: "..tostring(shit.impulse))
|
||||
--debug("shit.draftsman: "..(shit and shit.draftsman:name() or "<nil>"))
|
||||
|
||||
if shit.draftsman then
|
||||
--debug("shit.draftsman: "..(shit and shit.draftsman:name() or "<nil>"))
|
||||
|
||||
end
|
||||
|
||||
local health = db.actor.health
|
||||
local is_fatal = shit.power >= health
|
||||
|
||||
hit_queue:Enqueue({
|
||||
type = shit.type,
|
||||
power = damage,
|
||||
is_fatal = is_fatal,
|
||||
time = time_global(),
|
||||
draftsman_id = shit.draftsman and shit.draftsman:id() or nil,
|
||||
})
|
||||
|
||||
--debug("is_fatal: "..tostring(is_fatal))
|
||||
end
|
||||
|
||||
local function npc_on_net_spawn(npc, se_obj)
|
||||
if not IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local state = get_soulslike_state()
|
||||
if npc and IsStalker(npc) and state.tracked_ambushers and state.tracked_ambushers[npc:id()] then
|
||||
debug("Setting up ambushers "..tostring(npc:id()))
|
||||
local ambusher_state = state.tracked_ambushers[npc:id()]
|
||||
local position = vector():set(ambusher_state.position.x, ambusher_state.position.y, ambusher_state.position.z)
|
||||
|
||||
npc:set_mental_state(ambusher_state.mental_state)
|
||||
npc:set_body_state(ambusher_state.body_state)
|
||||
npc:set_movement_type(ambusher_state.movement_type)
|
||||
npc:set_sight(ambusher_state.sight_type, nil, 0)
|
||||
npc:set_desired_position(position)
|
||||
npc:set_desired_direction()
|
||||
|
||||
if soulslike_mcm.debug_squad_spawns() then
|
||||
level.map_add_object_spot_ser(npc:id(), "secondary_task_location", "DEBUG: Ambush NPC")
|
||||
end
|
||||
|
||||
state.tracked_ambushers[npc:id()] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function on_game_start()
|
||||
debug('Version: '..version)
|
||||
|
||||
RegisterScriptCallback("actor_on_stash_remove", actor_on_stash_remove)
|
||||
RegisterScriptCallback("actor_on_item_take_from_box", actor_on_item_take_from_box)
|
||||
RegisterScriptCallback("actor_on_before_death", actor_on_before_death)
|
||||
RegisterScriptCallback("on_before_save_input", on_before_save_input)
|
||||
RegisterScriptCallback("save_state", save_state)
|
||||
RegisterScriptCallback("load_state", load_state)
|
||||
RegisterScriptCallback("on_level_changing", on_level_changing)
|
||||
RegisterScriptCallback("on_game_load", on_game_load)
|
||||
RegisterScriptCallback("actor_on_sleep", actor_on_sleep)
|
||||
RegisterScriptCallback("on_console_execute", on_console_execute)
|
||||
RegisterScriptCallback("actor_on_before_hit", actor_on_before_hit)
|
||||
RegisterScriptCallback("physic_object_on_use_callback", physic_object_on_use_callback)
|
||||
--RegisterScriptCallback("npc_on_net_spawn", npc_on_net_spawn)
|
||||
end
|
||||
Reference in New Issue
Block a user