236 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
		
		
			
		
	
	
			236 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| 
								 | 
							
								----------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								-- 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
							 |