Divergent/mods/Mutant Loot Chance_backup/gamedata/scripts/ui_mutant_loot.script

706 lines
21 KiB
Plaintext

--[[
-----------------------------------
Copyright (C) 2012 Alundaio
This program is free software; you can redistribute and/or modify
it under the terms of the Open S.T.A.L.K.E.R. Mod License version 1.0.
-----------------------------------
ponney68
-----------------------------------
Tronex
Last edit: 2019/12/17
Added Loot all button, organized script, inventory cell system, alife spawn handlers
-----------------------------------
ilrathCXV
Last edit: 2024/03/29
MCM for Loot Chances + edits for DLTX Hunting Backpacks
-----------------------------------
-]]
local ini_mutant = ini_file("items\\settings\\mutant_loot.ltx")
local get_config = zzz_cxv_mutant_loot_mcm.get_config
local mcm_defaults = zzz_cxv_mutant_loot_mcm.defaults
local mcm_check = ui_mcm and zzz_cxv_mutant_loot_mcm
is_cxv_enabled = get_config("enable_mod") or mcm_defaults["enable_mod"] or false
-- local sec_kit_hunt = "kit_hunt"
-- local sec_kit_hunt_chance = ini_sys:r_float_ex("kit_hunt","bonus_mutant_part_chance") or 0
local hunting_kits = {
"kit_hunt",
"equ_small_military_hunt_pack",
"equ_military_hunt_pack",
"equ_tourist_hunt_pack",
}
local item_prop_table = { cond_r = {30,70} , cond_ct = "part" , cond_cr = {0.5,0.75,1} }
-- Loot Mutant
local MutantLootDecayTime = ini_mutant:r_float_ex("mutant_loot_mod","decay_time") or 7200
local kind_to_section = {
["SM_KARLIK"] = "karlik",
["SM_PSYSUCKER"] = "psysucker",
["SM_LURKER"] = "lurker"
}
local clsid_to_section = {
[clsid.bloodsucker_s] = "bloodsucker",
[clsid.boar_s] = "boar",
[clsid.burer_s] = "burer",
[clsid.chimera_s] = "chimera",
[clsid.controller_s] = "controller",
[clsid.dog_s] = "dog",
[clsid.flesh_s] = "flesh",
[clsid.gigant_s] = "gigant",
[clsid.poltergeist_s] = "poltergeist",
[clsid.psy_dog_s] = "psy_dog",
[clsid.psy_dog_phantom_s] = "psy_dog",
[clsid.pseudodog_s] = "pseudodog",
[clsid.snork_s] = "snork",
[clsid.tushkano_s] = "tushkano",
[clsid.cat_s] = "cat",
[clsid.fracture_s] = "fracture",
[clsid.zombie_s] = "zombie",
[clsid.crow] = "crow",
[clsid.rat_s] = "rat"
}
local clsdbg_to_section = {
["SM_KARLIK"] = "karlik",
["SM_PSYSUCKER"] = "psysucker",
["SM_LURKER"] = "lurker"
}
local killed_mutant_tbl = { -- ponney68: This table based on "species" of mutants
-- TRX: A-Life Revamp
psysucker = {file="ui\\ui_actor_monsters_pda_3",x="393",y="0",type="small"},
lurker = {file="ui\\ui_actor_monsters_pda_3",x="0",y="0",type="small"},
karlik = {file="ui\\ui_actor_monsters_pda_3",x="0",y="200",type="small"},
snork = {file="ui\\ui_actor_monsters_pda",x="393",y="0",type="small"},
dog = {file="ui\\ui_actor_monsters_pda",x="0",y="800",type="small"},
pseudodog = {file="ui\\ui_actor_monsters_pda",x="393",y="200",type="small"},
psy_dog = {file="ui\\ui_actor_monsters_pda",x="393",y="200",type="small"},
poltergeist = {file="ui\\ui_actor_monsters_pda",x="0",y="400",type="small"},
bloodsucker = {file="ui\\ui_actor_monsters_pda",x="393",y="400",type="human"},
controller = {file="ui\\ui_actor_monsters_pda",x="393",y="800",type="human"},
chimera = {file="ui\\ui_actor_monsters_pda",x="0",y="600",type="large"},
tushkano = {file="ui\\ui_actor_monsters_pda",x="0",y="0",type="small"},
rat = {file="ui\\ui_actor_monsters_pda",x="0",y="0",type="small"},
flesh = {file="ui\\ui_actor_monsters_pda",x="393",y="600",type="large"},
tark = {file="ui\\ui_actor_monsters_pda_2",x="0",y="0",type="human"},
rotan = {file="ui\\ui_actor_monsters_pda",x="0",y="0",type="human"},
burer = {file="ui\\ui_actor_monsters_pda_1",x="0",y="0",type="large"},
boar = {file="ui\\ui_actor_monsters_pda_1",x="393",y="0",type="large"},
giant = {file="ui\\ui_actor_monsters_pda_1",x="0",y="200",type="large"},
cat = {file="ui\\ui_actor_monsters_pda_2",x="0",y="0",type="small"},
fracture = {file="ui\\ui_actor_monsters_pda_2",x="393",y="200",type="human"},
bird = {file="ui\\ui_actor_monsters_pda_2",x="393",y="0",type="small"},
zombie = {file="ui\\ui_actor_monsters_pda_2",x="0",y="200",type="human"},
bloodsucker_arena = {file="ui\\ui_actor_monsters_pda",x="393",y="400",type="human"},
burer_arena = {file="ui\\ui_actor_monsters_pda_1",x="0",y="0",type="large"},
pseudodog_arena = {file="ui\\ui_actor_monsters_pda",x="393",y="200",type="small"},
snork_arena = {file="ui\\ui_actor_monsters_pda",x="393",y="0",type="human"},
}
-- Should specific loot be guaranteed?
mutant_part_guaranteed = {
["i_mutant_part"] = get_config("always_give_parts") or mcm_defaults["always_give_parts"] or false,
["i_mutant_raw"] = get_config("always_give_meat") or mcm_defaults["always_give_meat"] or false,
["i_mutant_belt"] = get_config("always_give_pelt") or mcm_defaults["always_give_pelt"] or false,
}
-- Chance multiplier solely for mutant parts
mutant_part_chance_mult = {
["i_mutant_part"] = get_config("mutant_part_chance_mult") or mcm_defaults["mutant_part_chance_mult"] or 1,
["i_mutant_raw"] = get_config("mutant_meat_chance_mult") or mcm_defaults["mutant_meat_chance_mult"] or 1,
["i_mutant_belt"] = get_config("mutant_pelt_chance_mult") or mcm_defaults["mutant_pelt_chance_mult"] or 1,
}
mutant_other_chance_mult = get_config("mutant_other_chance_mult") or mcm_defaults["mutant_other_chance_mult"] or 1
function get_part_kind(sec)
local kind = sec and ini_sys:r_string_ex(sec,'kind') or nil
return kind
end
function loot_mutant(section, clsid, loot_table, npc, dont_create, victim) -- Prepare mutant loot
npc = npc or db.actor
local clsid = clsid or obj and obj:clsid()
local kind = section and ini_sys:r_string_ex(section,"kind") or "unknown"
if not (clsid) then
return
end
local loot, sec, count, chance
local str_explode = str_explode
local mutant = clsdbg_to_section[kind] or clsid_to_section[clsid]
local was_loot_given = false
local failsafe_loot = get_config("failsafe_loot") or mcm_defaults["failsafe_loot"] or false
if victim:section() == "gigant_jumper" then
mutant = "gigant"
end
local possible_items = utils_data.collect_section(ini_mutant, mutant)
local sim = alife()
local npc_id = npc and npc:id()
local npc_pos = npc and npc:position()
local npc_lvl_id = npc and npc:level_vertex_id()
local npc_game_id = npc and npc:game_vertex_id()
-- Spawn items on NPC if he looted the mutant
for i=1,#possible_items do
loot = str_explode(possible_items[i],",")
if (loot and loot[1] and loot[2]) then
if (not loot[3]) then
loot[3] = 1
end
sec = loot[1]
count = tonumber(loot[2])
if is_cxv_enabled then
part_kind = get_part_kind(sec)
if part_kind and mutant_part_guaranteed[part_kind] == true then
-- printf("[UI Mutant Loot] %s will be guaranteed (%s)", sec, part_kind)
chance = 1
elseif part_kind and mutant_part_chance_mult[part_kind] then
-- printf("[UI Mutant Loot] %s will have its chances increased by %sx (%s)", sec, mutant_part_chance_mult[part_kind], part_kind)
chance = (loot[3] and (loot[3] * mutant_part_chance_mult[part_kind])) or 1
else
-- printf("[UI Mutant Loot] %s will have its chances increased by %sx (%s)", sec, mutant_other_chance_mult, part_kind)
chance = (loot[3] and (loot[3] * mutant_other_chance_mult)) or 1
end
else
printf("[UI Mutant Loot] Mutant Loot MCM not enabled. Defaulting to normal drop chances...")
chance = loot[3] or 1
end
for i=1,count do
if (math.random() <= tonumber(chance)) then
was_loot_given = true
-- In case we don't want to bother with loot table
local se_obj
if (not dont_create) then
se_obj = alife_create_item(sec, npc, item_prop_table)
end
-- Fill loot table if needed
if (loot_table) then
local sec_d, uses = utils_item.get_defined_uses(sec)
if (not loot_table[sec_d]) then
loot_table[sec_d] = {}
end
local c = loot_table[sec_d].count
c = c and (c + 1) or 1
loot_table[sec_d].count = c
if se_obj then
loot_table[sec_d][c] = se_obj.id
end
--printf("loot_mutant")
--[[
if npc and npc:id() ~= AC_ID then
se_obj = alife_create_item(sec, npc, item_prop_table)
end
--]]
end
end
end
end
end
if not was_loot_given and is_cxv_enabled and failsafe_loot then
printf("[UI Mutant Loot] No loot was given. Attempting loot failsafe...")
loot = str_explode(possible_items[math.random(1,#possible_items)],",")
if (loot and loot[1] and loot[2]) then
if (not loot[3]) then
loot[3] = 1
end
sec = loot[1]
count = tonumber(loot[2])
if (not dont_create) then
se_obj = alife_create_item(sec, npc, item_prop_table)
end
if (loot_table) then
local sec_d, uses = utils_item.get_defined_uses(sec)
if (not loot_table[sec_d]) then
loot_table[sec_d] = {}
end
local c = loot_table[sec_d].count
c = c and (c + 1) or 1
loot_table[sec_d].count = c
if se_obj then
loot_table[sec_d][c] = se_obj.id
end
end
end
end
-- Unlock relevant mutant article in guide.
if mutant and npc and (npc:id() == AC_ID) then
SendScriptCallback("actor_on_interaction", "mutants", nil, mutant)
end
SendScriptCallback("monster_on_loot_init",victim,loot_table)
end
----------------------------------------------------------------------
GUI = nil -- instance, don't touch
function start(obj, for_bug1, for_bug2)
if (not obj) then
printf("!ERROR ui_mutant_loot | no game object passed!")
return
end
if (not GUI) then
GUI = UIMutantLoot()
end
if (GUI) and (not GUI:IsShown()) then
local can_show = GUI:Reset(obj, for_bug1, for_bug2)
if can_show then
GUI:ShowDialog(true)
Register_UI("UIMutantLoot","ui_mutant_loot")
end
end
end
----------------------------------------------------------------------
-- CALLBACKS
----------------------------------------------------------------------
local function monster_on_actor_use_callback(obj,who) -- Open mutant loot UI
-- Return if mutant is already looted
local looted = se_load_var(obj:id(),obj:name(),"looted")
if (looted) then
return
end
-- This is important so NPCs don't try to loot the corpse the player is looting
if (obj:clsid() == clsid.crow) then
save_var(obj, "looted", true)
else
se_save_var(obj:id(),obj:name(),"looted",true)
end
xr_corpse_detection.set_valuable_loot(obj:id(),false)
-- if mutant corpse is lefted for long time, body is decayed
local st = db.storage[obj:id()]
if (st and st.death_time and game.get_game_time():diffSec(st.death_time) > MutantLootDecayTime) then
actor_menu.set_msg(1, game.translate_string("st_body_decayed"),4)
-- Start the Mutant Loot UI
else
start(obj, obj:id(), obj:section(), obj:clsid())
end
end
function monster_on_loot_init(obj,t)
-- t['conserva'] = {
-- count = 3
-- }
-- utils_data.print_table(t,obj and obj:name() or "no_obj")
end
function on_option_change(mcm)
if mcm then
is_cxv_enabled = get_config("enable_mod") or mcm_defaults["enable_mod"] or false
mutant_part_guaranteed = {
["i_mutant_part"] = get_config("always_give_parts") or mcm_defaults["always_give_parts"] or false,
["i_mutant_raw"] = get_config("always_give_meat") or mcm_defaults["always_give_meat"] or false,
["i_mutant_belt"] = get_config("always_give_pelt") or mcm_defaults["always_give_pelt"] or false,
}
-- Chance multiplier solely for mutant parts
mutant_part_chance_mult = {
["i_mutant_part"] = get_config("mutant_part_chance_mult") or mcm_defaults["mutant_part_chance_mult"] or 1,
["i_mutant_raw"] = get_config("mutant_meat_chance_mult") or mcm_defaults["mutant_meat_chance_mult"] or 1,
["i_mutant_belt"] = get_config("mutant_pelt_chance_mult") or mcm_defaults["mutant_pelt_chance_mult"] or 1,
}
mutant_other_chance_mult = get_config("mutant_other_chance_mult") or mcm_defaults["mutant_other_chance_mult"] or 1
end
end
function on_game_start()
RegisterScriptCallback("monster_on_actor_use_callback",monster_on_actor_use_callback)
RegisterScriptCallback("monster_on_loot_init",monster_on_loot_init)
RegisterScriptCallback("on_option_change",on_option_change)
on_option_change(mcm_check)
end
----------------------------------------------------------------------
-- UI
----------------------------------------------------------------------
class "UIMutantLoot" (CUIScriptWnd)
function UIMutantLoot:__init() super()
self:InitControls()
self:InitCallBacks()
end
function UIMutantLoot:__finalize()
end
function UIMutantLoot:InitControls()
self:SetWndRect (Frect():set(0,0,1024,768))
self:SetAutoDelete(true)
self.xml = CScriptXmlInit()
self.xml:ParseFile ("ui_mutant_loot.xml")
local xml = self.xml
self.dialog = xml:InitStatic("mutant_loot:background",self)
-- Mutant image
self.image = self.xml:InitStatic("mutant_loot:image",self.dialog)
-- Loot
self.frame = xml:InitStatic("mutant_loot:frame",self.dialog)
self.CC = utils_ui.UICellContainer("loot", self, nil, "mutant_loot:cont_loot", self.dialog)
self.CC.showcase = true
-- self.CC.can_select = true
self.CC.disable_drag = true
self.CC.disable_stack = true
self.CC:SetGridSpecs(35, 2)
self.item_info = utils_ui.UIInfoItem(self, 1000)
-- Button Loot one
self.btn_loot_one = xml:Init3tButton("mutant_loot:btn_loot",self.dialog)
self:Register(self.btn_loot_one, "button_loot")
-- Button Loot all
self.btn_loot_all = xml:Init3tButton("mutant_loot:btn_loot_all",self.dialog)
self:Register(self.btn_loot_all, "button_loot_all")
-- Button Cancel
self.btn_cancel = xml:Init3tButton("mutant_loot:btn_cancel",self.dialog)
self:Register(self.btn_cancel, "button_cancel")
end
function UIMutantLoot:InitCallBacks()
self:AddCallback("button_loot",ui_events.BUTTON_CLICKED,self.OnButton_LootSelected,self)
self:AddCallback("button_loot_all",ui_events.BUTTON_CLICKED,self.OnButton_LootAll,self)
self:AddCallback("button_cancel",ui_events.BUTTON_CLICKED,self.Close,self)
end
function UIMutantLoot:Reset(obj, for_bug1, for_bug2)
local function is_number(var)
local function lets_try(var)
var = tonumber(var)
return (var > 0) or (var < 0) or var or -var
end
if pcall(function() lets_try(var) end) then
return true
else
return false
end
end
if not (is_number(obj)) then
self.section = obj:section()
self.clsid = obj:clsid()
self.id = obj:id()
self.obj = obj
else
self.id = obj
self.section=for_bug1
self.clsid = for_bug2
self.obj = nil
end
self:SetMutantImage()
return self:FillList()
end
function UIMutantLoot:Update()
CUIScriptWnd.Update(self)
-- Highlight selected items
for idx,ci in pairs(self.CC.cell) do
if (not ci:IsCursorOverWindow()) then
if ci.flags.selected then
ci:Highlight(true,"green")
else
ci:Highlight(false)
end
end
end
-- Updating item info box and item cell containers
local found_cell = self.CC:Update(self.item_info)
if (not found_cell) then
self.item_info:Update()
end
end
-- Utility
function UIMutantLoot:SetMutantImage()
local mutant_id = game.translate_string(ini_sys:r_string_ex(self.section,"species") or "")
local kind = ini_sys:r_string_ex(self.section,"kind") or "unknown"
mutant_id = kind_to_section[kind] or mutant_id
--printf("-MUTANT:"..mutant_id)
local mutant_f = "ui\\ui_actor_monsters_pda_1"
local mutant_x = 0
local mutant_y = 0
mutant_f = tostring(killed_mutant_tbl[mutant_id].file)
mutant_x = tostring(killed_mutant_tbl[mutant_id].x)
mutant_y = tostring(killed_mutant_tbl[mutant_id].y)
local x1 = mutant_x
local y1 = mutant_y
local mutant_width = 393
local mutant_height = 200
local x2 = x1 + mutant_width
local y2 = y1 + mutant_height
self.image:InitTexture(tostring(mutant_f))
self.image:SetTextureRect(Frect():set(x1,y1,x2,y2))
self.image:SetStretchTexture(true)
end
function UIMutantLoot:Loot(loot_all)
local obj_mutant = level.object_by_id(self.id)
if (not obj_mutant) then
self:Close()
return
end
local is_looted
local sim = alife()
local bonus_part_chance = 0
-- Checking in case player has Hunting Backpacks Expanded
local is_huntkit
local needs_equipped_hk = ui_options.get("gameplay/general/need_equipped_hkit")
if needs_equipped_hk then
local backpack = db.actor:item_in_slot(13)
if backpack then
backpack_sec = backpack:section()
bonus_part_chance = ini_sys:r_float_ex(backpack_sec,"bonus_mutant_part_chance") or 0
if bonus_part_chance > 0 then
is_huntkit = true
end
end
end
-- Keeping the "Hunting Kit Equipped" option functional
if not is_huntkit and not needs_equipped_hk then
-- Checking for all types of Hunting Kits
for i=1,#hunting_kits do
if db.actor:object(hunting_kits[i]) then
-- Checking in case a backpack with higher part chance is found (in case people add their own backpacks to the list)
local temp_chance = ini_sys:r_float_ex(hunting_kits[i],"bonus_mutant_part_chance") or 0
if temp_chance > bonus_part_chance then
bonus_part_chance = temp_chance
-- printf("[UI Mutant Loot] New bonus mutant part chance (%s%) from %s", bonus_part_chance, hunting_kits[i])
end
end
end
end
--if (needs_equipped_hk and (backpack and (backpack:section() == sec_kit_hunt))) or (not needs_equipped_hk and db.actor:object(sec_kit_hunt)) then
-- is_huntkit = true
--end
-- Spawn selected items, clean from loot table
if loot_all then
local tbl = self.loot -- temp
for sec,t in pairs(tbl) do
for i=1,t.count do
is_looted = true
item_knife.degradate()
alife_create_item(sec, db.actor, item_prop_table)
if is_huntkit and (math.random(100) < bonus_part_chance) then
alife_create_item(sec, db.actor, item_prop_table)
end
self.loot[sec].count = self.loot[sec].count - 1
if (self.loot[sec].count == 0) then
self.loot[sec] = nil
end
end
end
else
for idx,ci in pairs(self.CC.cell) do
if ci.flags.selected then
local sec = ci.section
is_looted = true
item_knife.degradate()
alife_create_item(sec, db.actor, item_prop_table)
if is_huntkit and (math.random(100) < bonus_part_chance) then
alife_create_item(sec, db.actor, item_prop_table)
end
self.loot[sec].count = self.loot[sec].count - 1
if self.loot[sec].count == 0 then
self.loot[sec] = nil
end
end
end
end
-- If no item is looted, don't proceed
if (not is_looted) then
return
end
-- Animation boost if player has Hunter Kit or Well Dressed Achievement
if (actor_effects) then
local boost = (game_achievements.has_achievement("well_dressed") and 1 or 0) + (is_huntkit and 1 or 0)
if (boost == 2) then
actor_effects.play_item_fx("mutant_looting_boost_2")
elseif (boost == 1) then
actor_effects.play_item_fx("mutant_looting_boost_1")
else
actor_effects.play_item_fx("mutant_looting")
end
end
xr_sound.set_sound_play(AC_ID,"inv_mutant_loot_animal")
-- Increat field dressings stat
game_statistics.increment_statistic("field_dressings")
-- Mutant post-state
save_var(obj_mutant,"loot",self.loot)
local is_more_loot = not is_empty(self.loot)
-- Refill loot list if there's loot left
if ((not actor_effects.is_animations_on()) and is_more_loot) then
self:FillList()
else
self:Close()
end
end
function UIMutantLoot:FillList()
--developed by Dimeyne, copied by Wafel
self.loot = load_var(self.obj,"loot",nil)
if not self.loot then
self.loot = {}
loot_mutant(self.section, self.clsid, self.loot, nil, true, self.obj)
save_var(self.obj,"loot",self.loot)
end
local is_there_loot
local inv = {}
for sec,t in pairs(self.loot) do
for i=1,t.count do
inv[#inv + 1] = sec
end
is_there_loot = true
end
if (self.obj:clsid() ~= clsid.crow) and load_var(self.obj,"looted",nil) then
is_there_loot = false
end
if is_there_loot then
self:ShowDialog(true)
self.CC:Reinit(inv)
return true
else
actor_menu.set_msg(1, "st_body_useless",3)
end
end
function UIMutantLoot:SetMutantState(is_more_loot, obj_mutant)
obj_mutant = obj_mutant or level.object_by_id(self.id)
if (is_more_loot == nil) then
is_more_loot = not is_empty(self.loot)
end
-- We set mutant state to looted or not if there's loot left, so other NPCs can decide what to do with the corpse
if obj_mutant then
if is_more_loot then
--save_var(obj_mutant,"looted",false)
se_save_var(obj_mutant:id(),obj_mutant:name(),"looted",false)
xr_corpse_detection.set_valuable_loot(self.id,true)
else
--save_var(obj_mutant,"looted",true)
se_save_var(obj_mutant:id(),obj_mutant:name(),"looted",true)
xr_corpse_detection.set_valuable_loot(self.id,false)
end
else
printe("!ERROR ui_mutant_loot | can't retrieve online object of mutant [%s](%s)", self.section, self.id)
end
end
-- Callbacks
function UIMutantLoot:On_CC_Mouse1(cont, idx)
local ci = self.CC.cell[idx]
if (not ci) then
return
end
if (not ci.flags.selected) then
ci.flags.selected = true
else
ci.flags.selected = nil
end
end
function UIMutantLoot:OnButton_LootSelected()
self:Loot(false)
end
function UIMutantLoot:OnButton_LootAll()
self:Loot(true)
end
function UIMutantLoot:Close()
self:SetMutantState()
self:HideDialog()
Unregister_UI("UIMutantLoot")
end
function UIMutantLoot:OnKeyboard(dik, keyboard_action)
local res = CUIScriptWnd.OnKeyboard(self,dik,keyboard_action)
if (res == false) then
self.CC:OnKeyboard(dik, keyboard_action)
if (dik == DIK_keys.DIK_RETURN) then
self:OnButton_LootAll()
elseif (dik == DIK_keys.DIK_ESCAPE) then
self:Close()
end
end
return res
end