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
|
||||
@@ -0,0 +1,74 @@
|
||||
class "TimedQueue"
|
||||
|
||||
function TimedQueue:__init(max_count, expiriation_ms)
|
||||
self.data = {}
|
||||
self.first = 0
|
||||
self.last = -1
|
||||
self.max_count = max_count
|
||||
self.expiriation_ms = expiriation_ms
|
||||
end
|
||||
|
||||
function TimedQueue:Clear()
|
||||
self.data = {}
|
||||
self.first = 0
|
||||
self.last = -1
|
||||
--soulslike.debug("TimedQueue:Clear")
|
||||
end
|
||||
|
||||
function TimedQueue:Enqueue(value)
|
||||
value.__cached_time = time_global()
|
||||
self.last = self.last + 1
|
||||
self.data[self.last] = value
|
||||
--soulslike.debug("TimedQueue:Enqueue")
|
||||
end
|
||||
|
||||
function TimedQueue:Dequeue()
|
||||
local result
|
||||
if (self.first > self.last) then
|
||||
result = nil
|
||||
else
|
||||
result = self.data[self.first]
|
||||
self.data[self.first] = nil
|
||||
self.first = self.first + 1
|
||||
end
|
||||
--soulslike.debug("TimedQueue:Dequeue")
|
||||
return result
|
||||
end
|
||||
|
||||
function TimedQueue:Peek()
|
||||
local result
|
||||
if (self.first > self.last) then
|
||||
result = nil
|
||||
else
|
||||
result = self.data[self.first]
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
function TimedQueue:Values()
|
||||
--soulslike.debug("TimedQueue:Values")
|
||||
--soulslike.debug(self.data)
|
||||
local values = {}
|
||||
for _,v in pairs(self.data) do
|
||||
if v then
|
||||
table.insert(values, v)
|
||||
end
|
||||
end
|
||||
return values
|
||||
end
|
||||
|
||||
function TimedQueue:Invalidate()
|
||||
while true do
|
||||
local next = self:Peek()
|
||||
if next == nil then
|
||||
break
|
||||
end
|
||||
if next.__cached_time + self.expiriation_ms < time_global() then
|
||||
--soulslike.debug("TimedQueue:Dequeue_time")
|
||||
self:Dequeue()
|
||||
else
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,644 @@
|
||||
--[[
|
||||
Jabbers
|
||||
05APR2023
|
||||
Jabbers' Soulslike Anomaly Mod
|
||||
--]]
|
||||
|
||||
|
||||
function on_mcm_load()
|
||||
local op = {
|
||||
id = "soulslike",
|
||||
gr = { }
|
||||
}
|
||||
|
||||
local general = {
|
||||
id = "general",
|
||||
text = "ui_mcm_soulslike_general",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_general_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_general_description",
|
||||
clr = {255, 255 ,255 ,255},
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_general_disclaimer",
|
||||
clr = {255, 255 ,255 ,0},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, general)
|
||||
|
||||
local character = {
|
||||
id = "character",
|
||||
text = "ui_mcm_soulslike_character",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_character_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "health_loss_on_respawn",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0.0,
|
||||
max = 0.75,
|
||||
step = 0.01,
|
||||
def = 0.75
|
||||
},
|
||||
{
|
||||
id = "rank_loss_percent",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0,
|
||||
max = 0.75,
|
||||
step = 0.01,
|
||||
def = 0.02
|
||||
},
|
||||
{
|
||||
id = "rep_loss_percent",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0,
|
||||
max = 0.75,
|
||||
step = 0.01,
|
||||
def = 0.05
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, character)
|
||||
|
||||
local items = {
|
||||
id = "items",
|
||||
text = "ui_mcm_soulslike_items",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_items_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "keep_equipped_items_on_death",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_items_scalar_description",
|
||||
clr = {255, 255 ,255 ,255},
|
||||
},
|
||||
{
|
||||
id = "item_loss_scalar",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0,
|
||||
max = 0.75,
|
||||
step = 0.01,
|
||||
def = 0.20
|
||||
},
|
||||
{
|
||||
id = "item_condition_loss_scalar",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0,
|
||||
max = 0.25,
|
||||
step = 0.01,
|
||||
def = 0.05
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_items_allow_desc",
|
||||
clr = {255, 255 ,255 ,255},
|
||||
},
|
||||
{
|
||||
id = "allow_weapon_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_outfit_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_headgear_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_artifact_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_tool_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, items)
|
||||
|
||||
local scenarios = {
|
||||
id = "scenarios",
|
||||
text = "ui_mcm_soulslike_scenarios",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_scenarios_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_scenarios_description",
|
||||
clr = {255, 255 ,255 ,255},
|
||||
},
|
||||
{
|
||||
id = "rf_detector_scenario_weight",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.01,
|
||||
def = 0.10
|
||||
},
|
||||
{
|
||||
id = "hidden_stash_scenario_weight",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.01,
|
||||
def = 0.10
|
||||
},
|
||||
{
|
||||
id = "looter_npcs_marked",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, scenarios)
|
||||
|
||||
local ambush = {
|
||||
id = "ambush",
|
||||
text = "ui_mcm_soulslike_ambush",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_ambush_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_ambush_description",
|
||||
clr = {255, 255 ,255 ,255},
|
||||
},
|
||||
{
|
||||
id = "mutant_ambush_chance",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.01,
|
||||
def = 0.25
|
||||
},
|
||||
{
|
||||
id = "allow_boar_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_flesh_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_dogs_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_cats_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_snorks_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_bloodsucker_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_burer_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_chimera_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_controller_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "stalker_ambush_chance",
|
||||
type = "track",
|
||||
val = 2,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.01,
|
||||
def = 0.25
|
||||
},
|
||||
{
|
||||
id = "allow_stalker_novice_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_stalker_advanced_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_stalker_veteran_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
},
|
||||
{
|
||||
id = "allow_stalker_sniper_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = true
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, ambush)
|
||||
|
||||
local hardcore = {
|
||||
id = "hardcore",
|
||||
text = "ui_mcm_soulslike_hardcore",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_hardcore_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "desc_mcm",
|
||||
type = "desc",
|
||||
text = "ui_mcm_soulslike_hardcore_save_description",
|
||||
clr = {255, 255 ,255 ,0},
|
||||
},
|
||||
{
|
||||
id = "is_enabled",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "override_campfire_hardcore_saves",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, hardcore)
|
||||
|
||||
local debug = {
|
||||
id = "debug",
|
||||
text = "ui_mcm_soulslike_debug",
|
||||
sh = true,
|
||||
gr = {
|
||||
{
|
||||
id = "title",
|
||||
type = "slide",
|
||||
link = "ui_options_slider_gameplay_diff",
|
||||
text = "ui_mcm_soulslike_debug_title",
|
||||
size = {512,50},
|
||||
spacing = 20
|
||||
},
|
||||
{
|
||||
id = "is_enabled",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "enable_tips",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_hidden_stashes",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_squad_spawns",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_item_loss",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_the_tarkov_looter",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_the_noloss_scenario",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_the_default_scenario",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_the_rf_scenario",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_the_hidden_stash_scenario",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
},
|
||||
{
|
||||
id = "debug_always_spawn_ambush",
|
||||
type = "check",
|
||||
val = 1,
|
||||
def = false
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
table.insert(op.gr, debug)
|
||||
|
||||
return op
|
||||
end
|
||||
|
||||
local function get_config(key, default_when_nil)
|
||||
local opt = ui_mcm and ui_mcm.get("soulslike/"..key)
|
||||
if opt == nil then
|
||||
opt = default_when_nil
|
||||
end
|
||||
return opt
|
||||
end
|
||||
|
||||
-- Hardcore
|
||||
function is_hardcore_save_enabled()
|
||||
return get_config("hardcore/is_enabled", false)
|
||||
end
|
||||
|
||||
function override_campfire_hardcore_saves()
|
||||
return get_config("hardcore/override_campfire_hardcore_saves", false)
|
||||
end
|
||||
|
||||
-- Scenarios
|
||||
function are_looter_npcs_marked()
|
||||
return get_config("scenarios/looter_npcs_marked", false)
|
||||
end
|
||||
|
||||
function rf_detector_scenario_weight()
|
||||
return get_config("scenarios/rf_detector_scenario_weight", 0.10)
|
||||
end
|
||||
|
||||
function hidden_stash_scenario_weight()
|
||||
return get_config("scenarios/hidden_stash_scenario_weight", 0.10)
|
||||
end
|
||||
|
||||
function scattered_stash_scenario_weight()
|
||||
return get_config("scenarios/scattered_stash_scenario_weight", 0.02)
|
||||
end
|
||||
|
||||
function nearby_dead_stalker_scenario_weight()
|
||||
return get_config("scenarios/nearby_dead_stalker_scenario_weight", 0.10)
|
||||
end
|
||||
|
||||
-- Items
|
||||
function get_item_loss_scalar()
|
||||
return get_config("items/item_loss_scalar", 0.2)
|
||||
end
|
||||
|
||||
function get_item_condition_loss_percent()
|
||||
return get_config("items/item_condition_loss_scalar", 0.05)
|
||||
end
|
||||
|
||||
function allow_toolkit_loss()
|
||||
return get_config("items/allow_tool_loss", true)
|
||||
end
|
||||
|
||||
function allow_artifact_loss()
|
||||
return get_config("items/allow_artifact_loss", true)
|
||||
end
|
||||
|
||||
function allow_headgear_loss()
|
||||
return get_config("items/allow_headgear_loss", true)
|
||||
end
|
||||
|
||||
function allow_outfit_loss()
|
||||
return get_config("items/allow_outfit_loss", true)
|
||||
end
|
||||
|
||||
function allow_weapon_loss()
|
||||
return get_config("items/allow_weapon_loss", true)
|
||||
end
|
||||
|
||||
function get_keep_equipped_items_on_death()
|
||||
return get_config("items/keep_equipped_items_on_death", false)
|
||||
end
|
||||
|
||||
-- Ambush
|
||||
function mutant_ambush_chance()
|
||||
return get_config("ambush/mutant_ambush_chance", 0.5)
|
||||
end
|
||||
|
||||
function stalker_ambush_chance()
|
||||
return get_config("ambush/stalker_ambush_chance", 0.5)
|
||||
end
|
||||
|
||||
function allow_boar_ambush()
|
||||
return get_config("ambush/allow_boar_ambush", true)
|
||||
end
|
||||
function allow_flesh_ambush()
|
||||
return get_config("ambush/allow_flesh_ambush", true)
|
||||
end
|
||||
function allow_dogs_ambush()
|
||||
return get_config("ambush/allow_dogs_ambush", true)
|
||||
end
|
||||
function allow_cats_ambush()
|
||||
return get_config("ambush/allow_cats_ambush", true)
|
||||
end
|
||||
function allow_snorks_ambush()
|
||||
return get_config("ambush/allow_snorks_ambush", true)
|
||||
end
|
||||
function allow_bloodsucker_ambush()
|
||||
return get_config("ambush/allow_bloodsucker_ambush", true)
|
||||
end
|
||||
function allow_burer_ambush()
|
||||
return get_config("ambush/allow_burer_ambush", true)
|
||||
end
|
||||
function allow_chimera_ambush()
|
||||
return get_config("ambush/allow_chimera_ambush", true)
|
||||
end
|
||||
function allow_controller_ambush()
|
||||
return get_config("ambush/allow_controller_ambush", true)
|
||||
end
|
||||
function allow_stalker_novice_ambush()
|
||||
return get_config("ambush/allow_stalker_novice_ambush", true)
|
||||
end
|
||||
function allow_stalker_advanced_ambush()
|
||||
return get_config("ambush/allow_stalker_advanced_ambush", true)
|
||||
end
|
||||
function allow_stalker_veteran_ambush()
|
||||
return get_config("ambush/allow_stalker_veteran_ambush", true)
|
||||
end
|
||||
function allow_stalker_sniper_ambush()
|
||||
return get_config("ambush/allow_stalker_sniper_ambush", true)
|
||||
end
|
||||
|
||||
-- Character
|
||||
function get_health_loss_percent()
|
||||
return get_config("character/health_loss_on_respawn", 0.75)
|
||||
end
|
||||
|
||||
function rank_loss_percent()
|
||||
return get_config("character/rank_loss_percent", 0.05)
|
||||
end
|
||||
|
||||
function rep_loss_percent()
|
||||
return get_config("character/rep_loss_percent", 0.02)
|
||||
end
|
||||
|
||||
-- Debug
|
||||
function is_debug_enabled()
|
||||
return get_config("debug/is_enabled", false)
|
||||
end
|
||||
|
||||
function show_debug_tips()
|
||||
return get_config("debug/enable_tips", false)
|
||||
end
|
||||
|
||||
function debug_hidden_stashes()
|
||||
return is_debug_enabled() and get_config("debug/debug_hidden_stashes", false)
|
||||
end
|
||||
|
||||
function debug_squad_spawns()
|
||||
return is_debug_enabled() and get_config("debug/debug_squad_spawns", false)
|
||||
end
|
||||
|
||||
function debug_item_loss()
|
||||
return is_debug_enabled() and get_config("debug/debug_item_loss", false)
|
||||
end
|
||||
|
||||
function debug_remove_default_scenario()
|
||||
return is_debug_enabled() and get_config("debug/debug_remove_default_scenario", false)
|
||||
end
|
||||
|
||||
function debug_the_tarkov_looter()
|
||||
return is_debug_enabled() and get_config("debug/debug_the_tarkov_looter", false)
|
||||
end
|
||||
|
||||
function debug_the_noloss_scenario()
|
||||
return is_debug_enabled() and get_config("debug/debug_the_noloss_scenario", false)
|
||||
end
|
||||
|
||||
function debug_the_default_scenario()
|
||||
return is_debug_enabled() and get_config("debug/debug_the_default_scenario", false)
|
||||
end
|
||||
|
||||
function debug_the_rf_scenario()
|
||||
return is_debug_enabled() and get_config("debug/debug_the_rf_scenario", false)
|
||||
end
|
||||
|
||||
function debug_the_hidden_stash_scenario()
|
||||
return is_debug_enabled() and get_config("debug/debug_the_hidden_stash_scenario", false)
|
||||
end
|
||||
|
||||
function debug_always_spawn_ambush()
|
||||
return is_debug_enabled() and get_config("debug/debug_always_spawn_ambush", false)
|
||||
end
|
||||
@@ -0,0 +1,183 @@
|
||||
local function rescuer_initial_msg_factory()
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_initial_1",
|
||||
"st_soulslike_rescuer_initial_2",
|
||||
"st_soulslike_rescuer_initial_3",
|
||||
"st_soulslike_rescuer_initial_4",
|
||||
"st_soulslike_rescuer_initial_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_eat_drink_msg_factory()
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_food_1",
|
||||
"st_soulslike_rescuer_food_2",
|
||||
"st_soulslike_rescuer_food_3",
|
||||
"st_soulslike_rescuer_food_4",
|
||||
"st_soulslike_rescuer_food_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_pda_marker_msg_factory()
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_pda_1",
|
||||
"st_soulslike_rescuer_pda_2",
|
||||
"st_soulslike_rescuer_pda_3",
|
||||
"st_soulslike_rescuer_pda_4",
|
||||
"st_soulslike_rescuer_pda_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_used_items_msg_factory()
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_used_items_1",
|
||||
"st_soulslike_rescuer_used_items_2",
|
||||
"st_soulslike_rescuer_used_items_3",
|
||||
"st_soulslike_rescuer_used_items_4",
|
||||
"st_soulslike_rescuer_used_items_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_stay_safe_msg_factory()
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_stay_safe_1",
|
||||
"st_soulslike_rescuer_stay_safe_2",
|
||||
"st_soulslike_rescuer_stay_safe_3",
|
||||
"st_soulslike_rescuer_stay_safe_4",
|
||||
"st_soulslike_rescuer_stay_safe_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_moster_looter_msg_factory(enemy_name, enemy_comm)
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_monster_looter_1",
|
||||
"st_soulslike_rescuer_monster_looter_2",
|
||||
"st_soulslike_rescuer_monster_looter_3",
|
||||
"st_soulslike_rescuer_monster_looter_4",
|
||||
"st_soulslike_rescuer_monster_looter_5",
|
||||
}
|
||||
|
||||
return game.translate_string(messages[math.random(#messages)])
|
||||
end
|
||||
|
||||
local function rescuer_enemy_looter_msg_factory(enemy_name, enemy_comm)
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_enemy_looter_1",
|
||||
"st_soulslike_rescuer_enemy_looter_2",
|
||||
"st_soulslike_rescuer_enemy_looter_3",
|
||||
"st_soulslike_rescuer_enemy_looter_4",
|
||||
"st_soulslike_rescuer_enemy_looter_5",
|
||||
}
|
||||
|
||||
return strformat(game.translate_string(messages[math.random(#messages)]), enemy_comm, enemy_name)
|
||||
end
|
||||
|
||||
local function rescuer_enemy_looter_tarkov_looter_msg_factory(enemy_name, enemy_comm)
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_enemy_tarkov_looter_1",
|
||||
"st_soulslike_rescuer_enemy_tarkov_looter_2",
|
||||
"st_soulslike_rescuer_enemy_tarkov_looter_3",
|
||||
"st_soulslike_rescuer_enemy_tarkov_looter_4",
|
||||
"st_soulslike_rescuer_enemy_tarkov_looter_5",
|
||||
}
|
||||
|
||||
return strformat(game.translate_string(messages[math.random(#messages)]), enemy_comm, enemy_name)
|
||||
end
|
||||
|
||||
local function rescuer_enemy_radio_freq_msg_factory(freq)
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_radio_freq_1",
|
||||
"st_soulslike_rescuer_radio_freq_2",
|
||||
"st_soulslike_rescuer_radio_freq_3",
|
||||
"st_soulslike_rescuer_radio_freq_4",
|
||||
"st_soulslike_rescuer_radio_freq_5",
|
||||
}
|
||||
|
||||
return strformat(game.translate_string(messages[math.random(#messages)]), freq)
|
||||
end
|
||||
|
||||
local function rescuer_hidden_stash_msg_factory(freq)
|
||||
local messages = {
|
||||
"st_soulslike_hidden_stash_1",
|
||||
"st_soulslike_hidden_stash_2",
|
||||
"st_soulslike_hidden_stash_3",
|
||||
"st_soulslike_hidden_stash_4",
|
||||
"st_soulslike_hidden_stash_5",
|
||||
}
|
||||
|
||||
return strformat(game.translate_string(messages[math.random(#messages)]), freq)
|
||||
end
|
||||
|
||||
local function recuer_indoor_msg_factory(level_name)
|
||||
local messages = {
|
||||
"st_soulslike_rescuer_indoor_1",
|
||||
"st_soulslike_rescuer_indoor_2",
|
||||
"st_soulslike_rescuer_indoor_3",
|
||||
"st_soulslike_rescuer_indoor_4",
|
||||
"st_soulslike_rescuer_indoor_5",
|
||||
}
|
||||
|
||||
return strformat(game.translate_string(messages[math.random(#messages)]), level_name)
|
||||
end
|
||||
|
||||
function create(rescuer, params)
|
||||
if rescuer then
|
||||
local msg = params.player_died_indoor
|
||||
and recuer_indoor_msg_factory(params.level_name)
|
||||
or rescuer_initial_msg_factory()
|
||||
|
||||
local looter = params.enemy
|
||||
|
||||
if params.gave_food_or_water then
|
||||
msg = msg.." "..rescuer_eat_drink_msg_factory()
|
||||
end
|
||||
if looter then
|
||||
if looter.tarkov_experience then
|
||||
--TODO: Make message factory for this
|
||||
msg = msg.." "..rescuer_enemy_looter_tarkov_looter_msg_factory(looter.name, looter.community)
|
||||
else
|
||||
msg = msg.." "..rescuer_enemy_looter_msg_factory(looter.name, looter.community)
|
||||
end
|
||||
end
|
||||
if params.has_pda_marker then
|
||||
msg = msg.." "..rescuer_pda_marker_msg_factory()
|
||||
end
|
||||
if params.has_stash_pda_marker then
|
||||
msg = msg.." "..rescuer_hidden_stash_msg_factory()
|
||||
end
|
||||
if params.radio_freq then
|
||||
msg = msg.." "..rescuer_enemy_radio_freq_msg_factory(params.radio_freq)
|
||||
end
|
||||
if params.items_were_lost then
|
||||
if params.looter_type == soulslike.entity_type.Monster then
|
||||
msg = msg.." "..rescuer_moster_looter_msg_factory()
|
||||
else
|
||||
msg = msg.." "..rescuer_used_items_msg_factory()
|
||||
end
|
||||
end
|
||||
msg = msg.." "..rescuer_stay_safe_msg_factory()
|
||||
return msg
|
||||
else
|
||||
local msg = game.translate_string("st_soulslike_rescuer_initial_generic")
|
||||
if params.gave_food_or_water then
|
||||
msg = msg.." "..game.translate_string("st_soulslike_rescuer_food_generic")
|
||||
end
|
||||
if params.has_pda_marker or params.has_stash_pda_marker or params.has_multi_stash_pda_marker then
|
||||
msg = msg.." " ..game.translate_string("st_soulslike_rescuer_pda_generic")
|
||||
end
|
||||
if params.radio_freq then
|
||||
msg = msg.." "..game.translate_string(strformat("st_soulslike_rescuer_radio_generic", params.radio_freq))
|
||||
end
|
||||
return msg
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
function menu_read(obj) -- return "read" name
|
||||
local p = obj:parent()
|
||||
if not (p and p:id() == AC_ID) then return end
|
||||
|
||||
return game.translate_string("st_item_read")
|
||||
end
|
||||
|
||||
function func_radio_freq(obj)
|
||||
local sec = obj:section()
|
||||
|
||||
if (not IsItem("letter",sec)) then
|
||||
return
|
||||
end
|
||||
|
||||
local id = obj:id()
|
||||
local data = soulslike.get_soulslike_state()
|
||||
|
||||
if data.note_message_data and data.note_message_data[id] then
|
||||
local note_data = data.note_message_data[id]
|
||||
local freq = note_data.freq;
|
||||
local level_name = utils_xml.get_special_txt(note_data.level_name)
|
||||
local msg = strformat(game.translate_string("st_soulslike_rescuers_radio_frequency_note_message"), freq, level_name)
|
||||
|
||||
actor_menu.set_msg(2, msg, 10)
|
||||
|
||||
hide_hud_inventory()
|
||||
end
|
||||
end
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
|
||||
|
||||
local function compute_fatal_hit_info(hits)
|
||||
local fatal_hit = nil
|
||||
local agg_hit = {}
|
||||
local max_time = -1
|
||||
|
||||
soulslike.debug("Calculating fatal hit:")
|
||||
soulslike.debug(hits)
|
||||
|
||||
-- Find the final blow
|
||||
for _, value in ipairs(hits) do
|
||||
if value.is_fatal then
|
||||
fatal_hit = value
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for _, value in ipairs(hits) do
|
||||
max_time = math.max(max_time, value.time)
|
||||
end
|
||||
|
||||
local min_time = soulslike.MAX_HIT_TIME
|
||||
|
||||
-- Convert to an aggregation of damage if there is a well defined draftsman_type
|
||||
for _, hit_data in ipairs(hits) do
|
||||
hit_data.draftsman = hit_data.draftsman_id and level.object_by_id(hit_data.draftsman_id) or nil
|
||||
local power_weight = (hit_data.time - min_time) / soulslike.MAX_HIT_TIME
|
||||
local draftsman_type = soulslike.entity_type.Other
|
||||
if hit_data.draftsman then
|
||||
if hit_data.draftsman:id() == AC_ID then
|
||||
draftsman_type = soulslike.entity_type.Self
|
||||
elseif IsStalker(hit_data.draftsman) then
|
||||
draftsman_type = soulslike.entity_type.Stalker
|
||||
elseif IsMonster(hit_data.draftsman) then
|
||||
draftsman_type = soulslike.entity_type.Monster
|
||||
elseif IsAnomaly(hit_data.draftsman) then
|
||||
draftsman_type = soulslike.entity_type.Anomaly
|
||||
end
|
||||
end
|
||||
if not agg_hit[draftsman_type] then
|
||||
agg_hit[draftsman_type] = {
|
||||
draftsman_type = draftsman_type
|
||||
}
|
||||
end
|
||||
local data = agg_hit[draftsman_type]
|
||||
data.power = (data.power or 0) + hit_data.power * (power_weight * (hit_data.is_fatal and 2 or 1))
|
||||
end
|
||||
|
||||
soulslike.debug("Agg Hit Info:")
|
||||
soulslike.debug(agg_hit)
|
||||
|
||||
local result = nil
|
||||
|
||||
for _, value in pairs(agg_hit) do
|
||||
if not result then
|
||||
result = value
|
||||
elseif result.power < value.power then
|
||||
result = value
|
||||
end
|
||||
end
|
||||
|
||||
if result then
|
||||
result.fatal_hit = fatal_hit
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local function find_backpack()
|
||||
soulslike.debug("Looking for player backpack")
|
||||
local has_backpack = false
|
||||
local function find_backpack(_, item)
|
||||
local sec = item:section()
|
||||
has_backpack = SYS_GetParam(0, sec, "kind") == "i_backpack"
|
||||
if has_backpack then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
db.actor:iterate_inventory(find_backpack)
|
||||
|
||||
if has_backpack then
|
||||
soulslike.debug("Player has a backpack")
|
||||
else
|
||||
soulslike.debug("Player does not have a backpack")
|
||||
end
|
||||
return has_backpack
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- Factory
|
||||
---------------------------------------------------------
|
||||
|
||||
function create_by_id(scenario_id, state)
|
||||
local scenario = nil
|
||||
|
||||
soulslike.debug_tip("Creating scenario "..tostring(scenario_id))
|
||||
|
||||
if scenario_id == soulslike.SCENARIOS.Default then
|
||||
scenario = soulslike_scenarios.DefaultSoulslikeScenarioLogic(state)
|
||||
end
|
||||
|
||||
if scenario_id == soulslike.SCENARIOS.RFDetectorStash then
|
||||
scenario = soulslike_scenarios.RFDetectorSoulslikeScenarioLogic(state)
|
||||
end
|
||||
|
||||
if scenario_id == soulslike.SCENARIOS.HiddenStash then
|
||||
scenario = soulslike_scenarios.HiddenStashSoulslikeScenarioLogic(state)
|
||||
end
|
||||
|
||||
if scenario_id == soulslike.SCENARIOS.NoLoss then
|
||||
scenario = soulslike_scenarios.NoLossSoulslikeScenarioLogic(state)
|
||||
end
|
||||
|
||||
if scenario == nil then
|
||||
soulslike.debug("Unrecognized Scenario Id: "..tostring(scenario_id))
|
||||
scenario = soulslike_scenarios.DefaultSoulslikeScenarioLogic(state)
|
||||
end
|
||||
|
||||
return scenario
|
||||
end
|
||||
|
||||
function create_new(hits)
|
||||
local hit = compute_fatal_hit_info(hits)
|
||||
|
||||
if hit then
|
||||
soulslike.debug("Fatal Hit Type: "..tostring(hit.draftsman_type))
|
||||
end
|
||||
-- We want to be able to debug each scenario using the
|
||||
-- scenario weight to max, and in this case we want to
|
||||
-- be able to remove the default scenario so its always
|
||||
-- chosen.
|
||||
|
||||
local has_backpack = find_backpack()
|
||||
local player_is_indoor = not level_weathers.valid_levels[level.name()]
|
||||
local rescuer = nil
|
||||
local looter = nil
|
||||
local looter_type = nil
|
||||
local scenario_loot_scalar = 1.0
|
||||
local available_scenarios = {}
|
||||
local game_state = soulslike.get_soulslike_state()
|
||||
local spawn_position = vector():set(game_state.spawn_location.position.x, game_state.spawn_location.position.y, game_state.spawn_location.position.z)
|
||||
local distance_from_spawn = spawn_position:distance_to_sqr(db.actor:position())
|
||||
|
||||
if soulslike_mcm.debug_the_noloss_scenario() then
|
||||
soulslike.debug("Debugging noloss scenario.")
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
scenario_loot_scalar = 0
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 1.0})
|
||||
elseif soulslike_mcm.debug_the_default_scenario() then
|
||||
soulslike.debug("Debugging default scenario.")
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 1
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
elseif soulslike_mcm.debug_the_rf_scenario() then
|
||||
soulslike.debug("Debugging rf detector scenario.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 1
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = 1.0})
|
||||
elseif soulslike_mcm.debug_the_rf_scenario() then
|
||||
soulslike.debug("Debugging hidden stash scenario.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 1
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = 1.0})
|
||||
elseif distance_from_spawn <= 50 then
|
||||
soulslike.debug("Player was killed "..tostring(distance_from_spawn).." meters their spawn point.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
scenario_loot_scalar = 0
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 1.0})
|
||||
elseif distance_from_spawn <= 200 then
|
||||
soulslike.debug("Player was killed "..tostring(distance_from_spawn).." meters their spawn point.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = (distance_from_spawn - 50) / 150
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 1.0})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = scenario_loot_scalar})
|
||||
elseif player_is_indoor then
|
||||
soulslike.debug("Player died indoor, oof.")
|
||||
-- TODO: Need to figure out this scenario later, not sure what to do....
|
||||
-- It will be very difficult to get your gear back, prob annoyingly frustrating...
|
||||
-- For now, no loss scenario
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 1.0})
|
||||
elseif hit and hit.draftsman_type == soulslike.entity_type.Self then
|
||||
soulslike.debug("Player killed themself. haha idiot.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 0.75
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
end
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
elseif hit and
|
||||
hit.draftsman_type == soulslike.entity_type.Stalker and
|
||||
hit.fatal_hit and
|
||||
hit.fatal_hit.draftsman:general_goodwill(db.actor) >= soulslike.RELATIONS.NEUTRALS then
|
||||
soulslike.debug("Friendly/Neutral Stalker killed the player.")
|
||||
|
||||
rescuer = hit.draftsman
|
||||
looter = nil
|
||||
scenario_loot_scalar = 0.0
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 1.0})
|
||||
elseif hit and
|
||||
hit.draftsman_type == soulslike.entity_type.Stalker then
|
||||
soulslike.debug("Enemy Stalker killed the player.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy_stalker()
|
||||
scenario_loot_scalar = 1.0
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
end
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
elseif hit and
|
||||
hit.draftsman_type == soulslike.entity_type.Monster then
|
||||
soulslike.debug("Mutant killed the player.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy_mutant()
|
||||
scenario_loot_scalar = 1.0
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
end
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
elseif hit and
|
||||
hit.draftsman_type == soulslike.entity_type.Anomaly then
|
||||
soulslike.debug("Player died to an anomaly.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 0.25
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
else
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
end
|
||||
elseif hit and
|
||||
hit.draftsman_type == soulslike.entity_type.Other then
|
||||
soulslike.debug("Player died to damage over time of some sort.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 0.25
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
else
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
end
|
||||
else
|
||||
soulslike.warn("I have no idea how the player died.")
|
||||
|
||||
rescuer = soulslike.find_closest_friendly_stalker()
|
||||
looter = soulslike.find_closest_enemy()
|
||||
scenario_loot_scalar = 0.25
|
||||
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.NoLoss, min = 0, max = 0, weight = 0.05})
|
||||
|
||||
if has_backpack then
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.Default, min = 0, max = 0, weight = 1.0})
|
||||
else
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.RFDetectorStash, min = 0, max = 0, weight = soulslike_mcm.rf_detector_scenario_weight()})
|
||||
table.insert(available_scenarios, {id = soulslike.SCENARIOS.HiddenStash, min = 0, max = 0, weight = soulslike_mcm.hidden_stash_scenario_weight()})
|
||||
end
|
||||
end
|
||||
|
||||
if looter and IsStalker(looter) then
|
||||
looter_type = soulslike.entity_type.Stalker
|
||||
elseif looter and IsMonster(looter) then
|
||||
looter_type = soulslike.entity_type.Monster
|
||||
end
|
||||
|
||||
local is_in_water = load_var(db.actor,"grw_in_water") == true
|
||||
|
||||
if is_in_water then
|
||||
soulslike.debug("Actor died in the water, reducing loot scalar.")
|
||||
scenario_loot_scalar = scenario_loot_scalar * 0.5
|
||||
end
|
||||
|
||||
if #available_scenarios == 0 then
|
||||
soulslike.error("No scenario chosen because all scenario weights are at 0 and remove default was enabled.")
|
||||
return nil
|
||||
end
|
||||
|
||||
local weight_sum = 0
|
||||
for _, value in pairs(available_scenarios) do
|
||||
weight_sum = weight_sum + value.weight
|
||||
end
|
||||
|
||||
local min = 0
|
||||
for _, value in pairs(available_scenarios) do
|
||||
value.min = min + 1
|
||||
value.max = math.floor(min + (100 * (value.weight / weight_sum)))
|
||||
min = value.max
|
||||
end
|
||||
|
||||
local roll = math.random(1, 100)
|
||||
local scenario_id = nil
|
||||
for id, value in pairs(available_scenarios) do
|
||||
if(roll >= value.min and roll <= value.max) then
|
||||
scenario_id = value.id
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if scenario_id == nil then
|
||||
soulslike.debug("ERROR: Unable to determine scenario from roll: "..tostring(roll))
|
||||
soulslike.debug(available_scenarios)
|
||||
scenario_id = soulslike.SCENARIOS.NoLoss
|
||||
end
|
||||
|
||||
local scenario_logic = create_by_id(scenario_id)
|
||||
local level_name = level.name()
|
||||
|
||||
scenario_logic:SetLevelName(game.translate_string(level_name))
|
||||
scenario_logic:SetLootScalar(scenario_loot_scalar)
|
||||
scenario_logic:SetIsInDoor(player_is_indoor)
|
||||
scenario_logic:SetIsInWater(is_in_water)
|
||||
scenario_logic:SetRescuer(rescuer)
|
||||
|
||||
if looter then
|
||||
scenario_logic:SetLooter(looter)
|
||||
scenario_logic:SetLooterType(looter_type)
|
||||
end
|
||||
|
||||
if hit and hit.fatal_hit then
|
||||
scenario_logic:SetKillerType(hit.draftsman_type)
|
||||
scenario_logic:SetKiller(hit.fatal_hit.draftsman)
|
||||
scenario_logic:SetFatalHitType(hit.fatal_hit.type)
|
||||
end
|
||||
|
||||
return scenario_logic
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
local _InitControls = ui_sleep_dialog.UISleep.InitControls
|
||||
local _InitCallbacks = ui_sleep_dialog.UISleep.InitCallbacks
|
||||
local _TestAndShow = ui_sleep_dialog.UISleep.TestAndShow
|
||||
local _OnButtonSleep = ui_sleep_dialog.UISleep.OnButtonSleep
|
||||
|
||||
function ui_sleep_dialog.UISleep.OnButtonSleep(self)
|
||||
if not soulslike.IsSoulslikeMode() then
|
||||
_OnButtonSleep(self)
|
||||
return
|
||||
end
|
||||
local bleeding = db.actor.bleeding > 0
|
||||
local radiation = db.actor.radiation > 0
|
||||
|
||||
-- Prevent sleep if bleeding and/or iradiated.
|
||||
if (bleeding or radiation) then
|
||||
if (bleeding and radiation) then
|
||||
actor_menu.set_msg(1, game.translate_string("st_sleep_bleeding_irradiated"),5)
|
||||
elseif (bleeding) then
|
||||
actor_menu.set_msg(1, game.translate_string("st_sleep_bleeding"),4)
|
||||
elseif (radiation) then
|
||||
actor_menu.set_msg(1, game.translate_string("st_sleep_irradiated"),4)
|
||||
end
|
||||
disable_info("sleep_active")
|
||||
if self:IsShown() then
|
||||
self:HideDialog()
|
||||
Unregister_UI("UISleep")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if actor is inside a safe zone
|
||||
local actor_hide = GetEvent("current_safe_cover") and true or false
|
||||
|
||||
-- Check if actor is inside a tent
|
||||
if (not actor_hide) then
|
||||
actor_hide = item_tent.get_nearby_tent(1.5)
|
||||
end
|
||||
|
||||
-- If all is no, dont sleep
|
||||
if (not actor_hide) then
|
||||
actor_menu.set_msg(1, game.translate_string("st_cant_sleep_find_shelter_mlr"),4)
|
||||
disable_info("sleep_active")
|
||||
if self:IsShown() then
|
||||
self:HideDialog()
|
||||
Unregister_UI("UISleep")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
_OnButtonSleep(self)
|
||||
end
|
||||
|
||||
function ui_sleep_dialog.UISleep.TestAndShow(self, force)
|
||||
if not soulslike.IsSoulslikeMode() then
|
||||
_TestAndShow(self, force)
|
||||
return
|
||||
end
|
||||
|
||||
if force then
|
||||
_TestAndShow(self, force)
|
||||
else
|
||||
-- Detour and show the UI so we can let the user
|
||||
-- set their spawn point. Move the sleep check code
|
||||
-- to the Sleep button click instead
|
||||
self:Initialize()
|
||||
self:ShowDialog(true)
|
||||
Register_UI("UISleep","ui_sleep_dialog")
|
||||
end
|
||||
end
|
||||
|
||||
function ui_sleep_dialog.UISleep.InitControls(self)
|
||||
_InitControls(self)
|
||||
|
||||
if not soulslike.IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local xml = CScriptXmlInit()
|
||||
xml:ParseFile("soulslike_ui_sleep_dialog.xml")
|
||||
|
||||
local function move(ctrl, deltaX, deltaY)
|
||||
local pos = ctrl:GetWndPos()
|
||||
ctrl:SetWndPos(vector2():set( pos.x + deltaX ,pos.y + deltaY ))
|
||||
end
|
||||
|
||||
move(self.btn_sleep, 52, 0)
|
||||
move(self.btn_cancel, 52, 0)
|
||||
|
||||
self.btn_set_spawn = xml:Init3tButton("btn_set_spawn", self.back)
|
||||
self:Register(self.btn_set_spawn, "btn_set_spawn")
|
||||
|
||||
if not utils_xml.is_widescreen() then
|
||||
move(self.btn_set_spawn, 40, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function ui_sleep_dialog.UISleep.InitCallbacks(self)
|
||||
_InitCallbacks(self)
|
||||
|
||||
if not soulslike.IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
self:AddCallback("btn_set_spawn", ui_events.BUTTON_CLICKED, self.OnButtonSetSpawn, self)
|
||||
end
|
||||
|
||||
function ui_sleep_dialog.UISleep:OnButtonSetSpawn()
|
||||
soulslike.set_spawn(true);
|
||||
end
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
local _Main_Controls = ui_mm_faction_select.UINewGame.Main_Controls
|
||||
local _Main_CallBacks = ui_mm_faction_select.UINewGame.Main_CallBacks
|
||||
local _OnStartGame = ui_mm_faction_select.UINewGame.OnStartGame
|
||||
local _OnCheckSetAzazel = ui_mm_faction_select.UINewGame.OnCheckSetAzazel
|
||||
|
||||
function ui_mm_faction_select.UINewGame.Main_Controls(self, rand)
|
||||
_Main_Controls(self, rand)
|
||||
|
||||
local function move(ctrl, deltaX, deltaY)
|
||||
local pos = ctrl:GetWndPos()
|
||||
ctrl:SetWndPos(vector2():set( pos.x + deltaX ,pos.y + deltaY ))
|
||||
end
|
||||
|
||||
if efp_utils_mcm then
|
||||
move(self.ck_azazel_mode_cap, 0, -10)
|
||||
move(self.ck_azazel_mode, 0, -10)
|
||||
else
|
||||
move(self.ck_story_cap, 0, -10)
|
||||
move(self.ck_story, 0, -10)
|
||||
move(self.ck_azazel_mode_cap, 0, -15)
|
||||
move(self.ck_azazel_mode, 0, -15)
|
||||
move(self.ck_survival_cap, 0, -20)
|
||||
move(self.ck_survival, 0, -20)
|
||||
end
|
||||
|
||||
local xml = CScriptXmlInit()
|
||||
xml:ParseFile ("soulslike_ui_mm_gamemode.xml")
|
||||
|
||||
self.ck_soulslike_mode_cap = xml:InitStatic("main_dialog:options:cap_check_soulslike_mode",self.templ_options)
|
||||
self.ck_soulslike_mode = xml:InitCheck("main_dialog:options:check_soulslike_mode", self.templ_options)
|
||||
self:Register(self.ck_soulslike_mode,"check_soulslike_mode")
|
||||
|
||||
if efp_utils_mcm then
|
||||
move(self.ck_soulslike_mode_cap, 0, 65)
|
||||
move(self.ck_soulslike_mode, 0, 65)
|
||||
end
|
||||
|
||||
self.ck_states["ck_soulslike_mode"] = false
|
||||
end
|
||||
|
||||
function ui_mm_faction_select.UINewGame.Main_CallBacks(self)
|
||||
_Main_CallBacks(self)
|
||||
|
||||
self:AddCallback("check_soulslike_mode", ui_events.BUTTON_CLICKED, self.OnCheckSetSoulslike, self)
|
||||
self:AddCallback("check_hardcore", ui_events.BUTTON_CLICKED, self.OnCheckHardcore, self)
|
||||
end
|
||||
|
||||
|
||||
function ui_mm_faction_select.UINewGame.OnStartGame(self)
|
||||
if (not self.access) then
|
||||
return
|
||||
end
|
||||
|
||||
local character_name = self.character_name:GetText()
|
||||
|
||||
if axr_main and axr_main.config and character_name and character_name ~= "" then
|
||||
local is_soulslike_mode = self.ck_soulslike_mode and self.ck_soulslike_mode:GetCheck() and true or nil
|
||||
printf('[Soulslike] OnStartGame:is_soulslike_mode='..tostring(is_soulslike_mode))
|
||||
axr_main.config:w_value("character_creation", "new_game_soulslike_mode", is_soulslike_mode)
|
||||
axr_main.config:save()
|
||||
end
|
||||
|
||||
_OnStartGame(self)
|
||||
end
|
||||
|
||||
function ui_mm_faction_select.UINewGame.OnCheckSetAzazel(self)
|
||||
_OnCheckSetAzazel(self)
|
||||
|
||||
if (not self.access) then
|
||||
return
|
||||
end
|
||||
|
||||
self.ck_soulslike_mode:SetCheck(false)
|
||||
self.ck_states["ck_soulslike_mode"] = false
|
||||
end
|
||||
|
||||
function ui_mm_faction_select.UINewGame:OnCheckHardcore()
|
||||
if (not self.access) then
|
||||
return
|
||||
end
|
||||
|
||||
self.ck_soulslike_mode:SetCheck(false)
|
||||
self.ck_states["ck_soulslike_mode"] = false
|
||||
end
|
||||
|
||||
function ui_mm_faction_select.UINewGame:OnCheckSetSoulslike()
|
||||
if (not self.access) then
|
||||
return
|
||||
end
|
||||
|
||||
self.ck_azazel_mode:SetCheck(false)
|
||||
self.ck_states["ck_azazel_mode"] = false
|
||||
|
||||
self.ck_hardcore:SetCheck(false)
|
||||
self.ck_states["ck_hardcore"] = false
|
||||
|
||||
self:LoadMap()
|
||||
end
|
||||
|
||||
local function on_game_load(binder)
|
||||
local config = axr_main.config
|
||||
|
||||
if not (config) then
|
||||
printf('[Soulslike] on_game_load:no config')
|
||||
return
|
||||
end
|
||||
|
||||
local need_save
|
||||
|
||||
-- Gameplay Options
|
||||
if (USE_MARSHAL) then
|
||||
if (config:r_value("character_creation","new_game_soulslike_mode", 1) == true) then
|
||||
printf('[Soulslike] on_game_load:enable_soulslike_mode=true')
|
||||
alife_storage_manager.get_state().enable_soulslike_mode = true
|
||||
config:w_value("character_creation","new_game_soulslike_mode")
|
||||
need_save = true
|
||||
end
|
||||
end
|
||||
|
||||
if (need_save) then
|
||||
config:save()
|
||||
end
|
||||
end
|
||||
|
||||
local function actor_on_first_update()
|
||||
if not soulslike.IsSoulslikeMode() then
|
||||
return
|
||||
end
|
||||
|
||||
local state = alife_storage_manager.get_state()
|
||||
|
||||
if not state.soulslike_mode_initial_spawn_set and level.name() ~= 'fake_start' then
|
||||
printf('Initial spawn point set to '..level.name())
|
||||
soulslike.set_spawn(false)
|
||||
state.soulslike_mode_initial_spawn_set = true
|
||||
end
|
||||
end
|
||||
|
||||
function on_game_start()
|
||||
RegisterScriptCallback("on_game_load",on_game_load)
|
||||
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
|
||||
end
|
||||
Reference in New Issue
Block a user