Divergent/mods/Jabbers Soulslike Gamemode/gamedata/scripts/soulslike_scenario_logic_fa...

370 lines
15 KiB
Plaintext
Raw Normal View History

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