---------------------------------------------------------------- -- Deal with loot injection in stalkers and traders ---------------------------------------------------------------- gc = game.translate_string get_data = magazine_binder.get_data set_data = magazine_binder.set_data get_mag_loaded = magazine_binder.get_mag_loaded prep_weapon = magazines.prep_weapon is_supported_weapon = magazine_binder.is_supported_weapon get_magazine_caliber = magazine_binder.get_magazine_caliber is_magazine = magazine_binder.is_magazine weapon_default_magazine = magazine_binder.weapon_default_magazine weapon_improved_magazine = magazine_binder.weapon_improved_magazine create_mag_data = magazine_binder.create_mag_data print_dbg = magazines.print_dbg get_weapon_base_type = magazine_binder.get_weapon_base_type get_mags_for_basetype = magazine_binder.get_mags_for_basetype valid_mag_data = magazine_binder.valid_mag_data get_config = magazines_mcm.get_config validate_mag = magazine_binder.validate_mag validate_wep = magazine_binder.validate_wep local math_random = math.random local math_floor = math.floor local string_format = string.format local print_table = utils_data.print_table local ini_loadouts = ini_file("items\\settings\\npc_mag_loadouts.ltx") local mag_timer_global = nil local tm = {} -- trader management TraderAuto = trader_autoinject.update function trader_autoinject.update(npc) TraderAuto(npc) CreateTimeEvent("restock_mags" .. npc:id(), "restock_mags" .. npc:id(), 0.01, function() stock_mags(npc) return true end) end -- function to resupply mags based on what weapons are in stock -- formula is: 3 mags for 1st weapon, and 1 extra for each subsequent function stock_mags(npc) local id = npc:id() if trader_autoinject.get_trader_type(npc) ~= trader_autoinject.SUPPLIER then return end print_dbg("Restocking mags for %s", npc:name()) local to_spawn = {} -- collect num. of mags to spawn local function itr_inv(temp, item) local sec = item:section() if not IsAmmo(item) and IsWeapon(item) and is_supported_weapon(item) then local default_mag = weapon_default_magazine(item) local default_capacity = SYS_GetParam(2, default_mag, "max_mag_size") local default_load_delay = SYS_GetParam(2, default_mag, "load_delay") local mags = get_mags_for_basetype(get_weapon_base_type(item)) if mags then print_dbg("Mags [%s]", sec) for _, mag in pairs(mags) do local load_delay = SYS_GetParam(2, mag, "load_delay") local capacity = SYS_GetParam(2, mag, "max_mag_size") if mag ~= default_mag and (capacity > default_capacity or load_delay < default_load_delay) then print_dbg("Mag %s is improved", mag) to_spawn[mag] = to_spawn[mag] and to_spawn[mag] + math_random(0, 1) or math_random(1, 2) else print_dbg("Mag %s is normal", mag) to_spawn[mag] = to_spawn[mag] and to_spawn[mag] + 1 or 3 end end else print_dbg("Weapon has broken basetype [%s]", sec) end end end npc:iterate_inventory(itr_inv, npc) -- spawn them empty trader_autoinject.spawn_items(npc, to_spawn, true) end -- death management local function get_mag_prop(rank, prop) rank = rank or "novice" return ini_loadouts:r_float_ex(rank.."_mag_loadout", prop) end -- called on each created magazine, autofill with the appropriate crap function random_pop_mag(mag_id, mag_sec, ammo_table, rank) local quality = get_config("deathquality") or 1 local amount = math_random(0, get_mag_prop(rank, "mag_fill_max")) amount = clamp(amount * (get_config("deathammo") or 1), 0, 100)/100 local mag_data = get_mag_loaded(mag_id) or create_mag_data(mag_id, mag_sec) empty_table(mag_data.loaded) local to_fill = math_floor(amount * SYS_GetParam(2, mag_sec, "max_mag_size")) -- also pick the appropriate ammo local good_chance = get_mag_prop(rank, "mag_good_chance") * quality -- add validation for existence of a single ammo type (e.g. 9x21 sp10) -- This should really be refactored to categorize bad ammo in the ammo table instead of relying on the index local ammo_to_pick = math_random(#ammo_table) if #ammo_table > 1 then ammo_to_pick = 3*math_floor(math_random(#ammo_table - 1) / 3) + 1 + (math_random(100) < good_chance and 0 or 1) else to_fill = math_floor(to_fill/2) end local ammo_to_use = determine_ammo(ammo_table[ammo_to_pick], mag_sec, rank) print_dbg("Filling mag %s to %s with %s (num %s)", mag_sec, to_fill, ammo_to_use, ammo_to_pick) for i=1,to_fill do stack.push(mag_data.loaded, ammo_to_use) end set_data(mag_id, mag_data) return true end function determine_ammo(ammo_to_pick, mag_sec, rank) return ammo_to_pick end function npc_on_death(npc, who) local rank = ranks.get_obj_rank_name(npc) local found_primary = false local found_secondary = false function itr_inv(temp, item) local sec = item:section() if IsWeapon(nil,item:clsid()) and not npc:marked_dropped(item) and is_supported_weapon(item) then -- spawn mags for one primary, one secondary local is_sidearm = IsPistol(item) if (found_primary and not is_sidearm) or (found_secondary and is_sidearm) then return end -- reduce ammo in loaded weapon local id = item:id() local mag_data = get_data(id) local mags_to_spawn = math_random(0, get_mag_prop(rank, "max_mags")) local mag_sec = weapon_default_magazine(sec) local improved_mag_sec = weapon_improved_magazine(sec) local mag_good_chance = get_mag_prop(rank, "mag_good_chance") local ammo_table = get_magazine_caliber(mag_sec) local quality = get_config("deathquality") or 1 print_dbg("Spawning %s mags for %s", mags_to_spawn, sec) for i=1,mags_to_spawn do local to_create = math_floor(quality * math_random(100)) <= mag_good_chance and improved_mag_sec or mag_sec print_dbg("Creating magazine %s", to_create) local new_mag = alife_create_item(to_create, npc) if new_mag then random_pop_mag(new_mag.id, to_create, ammo_table, rank) end end if is_sidearm then found_secondary = true else found_primary = true end end end npc:iterate_inventory(itr_inv, npc) end SetWepCondition = death_manager.set_weapon_drop_condition function death_manager.set_weapon_drop_condition(npc, itm) SetWepCondition(npc, itm) local death_dropped = se_load_var(npc:id(), npc:name(), "death_dropped") if (death_dropped) then return end local id = itm:id() if not is_supported_weapon(itm) then set_data(id, nil) return end if (is_supported_weapon(itm) and not npc:marked_dropped(itm)) then local id = itm:id() local sec = itm:section() local mag_sec = weapon_default_magazine(sec) local ammo_table = get_magazine_caliber(mag_sec) local rank = ranks.get_obj_rank_name(npc) print_dbg("Resetting mag data for weapon %s (%s)", id, itm:section()) itm:unload_magazine() random_pop_mag(id, mag_sec, ammo_table, rank) prep_weapon(itm, false) end end -- 1.5.2 feature - determine mag cost based on contents function on_get_item_cost(kind, obj, profile, calculated_cost, ret) local id = obj:id() local sec = obj:section() local mag_data = get_mag_loaded(id) local calculated_cost = ret.new_cost or calculated_cost -- in case someone adjusted the price of the weapon if mag_data == nil then return end -- we have mag data - but it can potentially be corrupt. perform validation if needed -- print_dbg("Item %s has mag data", sec) if is_magazine(obj) then validate_mag(id, sec) local cost_base = obj:cost() -- non stalkers, reduce mag cost if profile.mode == 1 then cost_base = cost_base * 0.1 end local info = mags_patches.collect_mag_data(mag_data) for k,v in pairs(info) do local cost_frac = math_floor(SYS_GetParam(2, k, "cost") / SYS_GetParam(2, k, "box_size")) * v -- print_dbg("Mag %s, cost %s, add cost %s from %s of %s", sec, cost_base, cost_frac, v, k) cost_base = cost_base + cost_frac end ret.new_cost = cost_base * profile.discount print_dbg("Final cost of %s is %s, discount %s", sec, ret.new_cost, profile.discount) elseif is_supported_weapon(obj) and mag_data then validate_wep(id, sec) local mag_cost = SYS_GetParam(2, mag_data.section, "cost") * profile.discount if profile.mode == 1 then mag_cost = mag_cost * 0.1 end -- print_dbg("Weapon %s, adding mag %s cost %s", sec, mag_data.section, mag_cost) ret.new_cost = calculated_cost + mag_cost else printf("!!! FRAUD DETECTED !!! Item %s should not have mag data!", sec) set_data(id, nil) end end function on_game_start() if utils_item.on_get_item_cost then RegisterScriptCallback("on_get_item_cost", on_get_item_cost) end RegisterScriptCallback("npc_on_death_callback", npc_on_death) end