268 lines
7.9 KiB
Plaintext
268 lines
7.9 KiB
Plaintext
|
-- Yet Another Campfire Saving
|
||
|
-- Last modified: 2022.12.04
|
||
|
-- https://github.com/Ishmaeel/YetAnotherCampfireSaving
|
||
|
|
||
|
local campfire_saving = true
|
||
|
local campfire_radius = 15
|
||
|
local base_radius = 50
|
||
|
local boss_radius = 10
|
||
|
local token_granted = time_infinite
|
||
|
local token_downtime = 1000 * 60 * 5 -- Five minutes
|
||
|
local token_redeemed = false
|
||
|
local notifications = false
|
||
|
local not_mommy = false
|
||
|
local neutral_bases = false
|
||
|
local enable_friendly_bosses = false
|
||
|
local enable_neutral_bosses = false
|
||
|
|
||
|
local getstr = game.translate_string
|
||
|
local silent = true
|
||
|
|
||
|
bonus_bases = {}
|
||
|
bonus_bases.pri_monolith = { monolith = true }
|
||
|
bonus_bases.pri_a18_smart_terrain = { killer = true }
|
||
|
|
||
|
function require_shelter()
|
||
|
return surge_manager.actor_in_cover()
|
||
|
end
|
||
|
|
||
|
function always_allow()
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function never_allow()
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
the_bawses = {
|
||
|
zat_b18_noah = require_shelter, m_trader = require_shelter, m_lesnik = require_shelter,
|
||
|
trader_monolith_kbo = always_allow, mechanic_monolith_kbo = always_allow
|
||
|
}
|
||
|
|
||
|
function on_game_start()
|
||
|
bind_campfire.check_no_nearby_campfire = check_no_nearby_campfire
|
||
|
|
||
|
RegisterScriptCallback("on_before_save_input", on_before_save_input)
|
||
|
RegisterScriptCallback("on_option_change", on_option_change)
|
||
|
RegisterScriptCallback("actor_on_first_update", actor_on_first_update)
|
||
|
RegisterScriptCallback("on_console_execute", on_console_execute)
|
||
|
RegisterScriptCallback("on_key_release", on_key_release)
|
||
|
|
||
|
on_option_change()
|
||
|
end
|
||
|
|
||
|
function on_before_save_input(flags, typ_, text)
|
||
|
if campfire_saving and level_weathers.valid_levels[level.name()] then
|
||
|
token_redeemed = false
|
||
|
local error_message = nil
|
||
|
|
||
|
local nearby_campfire = bind_campfire.get_nearby_campfire(campfire_radius, true)
|
||
|
|
||
|
if not nearby_campfire then
|
||
|
error_message = strformat(getstr("st_ui_no_save_campfire_base"), text)
|
||
|
elseif not nearby_campfire:is_on() then
|
||
|
error_message = strformat(getstr("st_ui_no_save_campfire_unlit"), text)
|
||
|
end
|
||
|
|
||
|
local within_safe_space = is_within_friendly_base()
|
||
|
|
||
|
if not within_safe_space then
|
||
|
local evaluate_boss_safety = get_friendly_boss_safety_function()
|
||
|
|
||
|
if evaluate_boss_safety then
|
||
|
within_safe_space = evaluate_boss_safety()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if error_message and not within_safe_space then
|
||
|
token_redeemed = redeem_token()
|
||
|
|
||
|
-- always prevent saving.
|
||
|
exec_console_cmd("main_menu off")
|
||
|
flags.ret = true
|
||
|
|
||
|
-- only show message if token is not redeemed.
|
||
|
if not token_redeemed then
|
||
|
actor_menu.set_msg(1, error_message, 4)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function is_within_friendly_base()
|
||
|
local friendly_bases = get_friendly_bases()
|
||
|
|
||
|
for _, smart in pairs(friendly_bases) do
|
||
|
if smart.dist_to_actor < base_radius and surge_manager.actor_in_cover() then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function get_friendly_bases()
|
||
|
local friendly_bases = {}
|
||
|
local community = gameplay_disguise.get_default_comm()
|
||
|
|
||
|
for _, smart in pairs(SIMBOARD.smarts_by_names) do
|
||
|
if ish_campfire_debug then
|
||
|
ish_campfire_debug.draw_map_dot(smart)
|
||
|
end
|
||
|
|
||
|
if smart.is_on_actor_level and smart.props.base > 0 then
|
||
|
-- Base is considered friendly if it's marked for player's faction or "all"
|
||
|
local is_safe = smart.props[community] > 0 or smart.props.all > 0
|
||
|
|
||
|
-- Some bases are not properly marked for factions in vanilla configs, so we have an override table.
|
||
|
is_safe = is_safe or (bonus_bases[smart:name()] and bonus_bases[smart:name()][community])
|
||
|
|
||
|
-- Optional: Other factions may let you use their bases if you have good relations.
|
||
|
is_safe = is_safe or neutral_bases and is_base_neutral(smart, community)
|
||
|
|
||
|
if is_safe then
|
||
|
table.insert(friendly_bases, smart)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return friendly_bases
|
||
|
end
|
||
|
|
||
|
function is_base_neutral(smart, community)
|
||
|
for _, faction in pairs(game_relations.factions_table) do
|
||
|
local influence = smart.props[faction]
|
||
|
if influence > 0 then
|
||
|
local relation = relation_registry.community_relation(faction, community)
|
||
|
local goodwill = relation_registry.community_goodwill(faction, db.actor:id())
|
||
|
if relation + goodwill >= 0 then
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function get_friendly_boss_safety_function()
|
||
|
local boss_safety_function = never_allow
|
||
|
|
||
|
local function iterate_func(obj)
|
||
|
if obj:id() > 0 then
|
||
|
local section = obj:section()
|
||
|
if the_bawses[section] then
|
||
|
local boss_alive = obj:alive()
|
||
|
local boss_relation = game_relations.get_npcs_relation(obj, db.actor)
|
||
|
|
||
|
if d and d.news then
|
||
|
local relationship = game_relations.game_relations_by_num[boss_relation]
|
||
|
d.news("Boss found: %s - %s, %s", section, relationship, boss_alive and "alive" or "dead")
|
||
|
end
|
||
|
|
||
|
if boss_alive and boss_relation == game_object.friend or (enable_neutral_bosses and boss_relation == game_object.neutral) then
|
||
|
boss_safety_function = the_bawses[section]
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if enable_friendly_bosses then
|
||
|
level.iterate_nearest(db.actor:position(), boss_radius, iterate_func)
|
||
|
end
|
||
|
|
||
|
return boss_safety_function
|
||
|
end
|
||
|
|
||
|
function check_no_nearby_campfire()
|
||
|
return false -- Prevent vanilla campfire checking function from interfering.
|
||
|
end
|
||
|
|
||
|
function on_option_change()
|
||
|
if ui_mcm then
|
||
|
campfire_saving = ui_mcm.get("yasc/enableCampfireSaving")
|
||
|
notifications = ui_mcm.get("yasc/enableNotifications")
|
||
|
not_mommy = ui_mcm.get("yasc/isNotMommy")
|
||
|
neutral_bases = ui_mcm.get("yasc/allowNeutralBases")
|
||
|
enable_friendly_bosses = ui_mcm.get("yasc/allowSanctuaries") >= 1
|
||
|
enable_neutral_bosses = ui_mcm.get("yasc/allowSanctuaries") >= 2
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function actor_on_first_update()
|
||
|
extend_token_downtime()
|
||
|
end
|
||
|
|
||
|
function on_console_execute(cmd)
|
||
|
if cmd ~= "save" then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if token_redeemed then
|
||
|
switch_to_rl_mode()
|
||
|
else
|
||
|
extend_token_downtime()
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function on_key_release(key)
|
||
|
if key == DIK_keys.DIK_F5 then
|
||
|
token_redeemed = redeem_token(silent and campfire_saving)
|
||
|
|
||
|
if token_redeemed then
|
||
|
create_emergency_save()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function extend_token_downtime()
|
||
|
token_granted = time_global() + token_downtime
|
||
|
end
|
||
|
|
||
|
function redeem_token(suppress_notification)
|
||
|
local time_remaining = token_granted - time_global()
|
||
|
local has_token = time_remaining <= 0
|
||
|
|
||
|
if has_token and (key_state(DIK_keys.DIK_LSHIFT) ~= 0 or key_state(DIK_keys.DIK_RSHIFT) ~= 0) then
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
if not suppress_notification then
|
||
|
send_notification(time_remaining)
|
||
|
end
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function create_emergency_save()
|
||
|
exec_console_cmd(string.format("save %s - %ssave", (user_name() or "player"), get_parent_type()))
|
||
|
end
|
||
|
|
||
|
function switch_to_rl_mode()
|
||
|
exec_console_cmd("quit")
|
||
|
end
|
||
|
|
||
|
function send_notification(time_remaining)
|
||
|
if notifications then
|
||
|
local parent_type = get_parent_type()
|
||
|
|
||
|
if time_remaining > 0 then
|
||
|
news(getstr("token_pending"), parent_type, time_remaining / 1000)
|
||
|
else
|
||
|
news(getstr("token_granted"), parent_type)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function get_parent_type()
|
||
|
return getstr(string.format("mommy_%s", not not_mommy))
|
||
|
end
|
||
|
|
||
|
function news(message, ...)
|
||
|
message = string.format(message, ...)
|
||
|
if db.actor then
|
||
|
db.actor:give_game_news(getstr("ui_mcm_yacs_title"), message, "ui_inGame2_PD_storonnik_ravnovesiya", 0, 5000, 0)
|
||
|
else
|
||
|
printf(message)
|
||
|
end
|
||
|
end
|