ProjectsWhat's NewDownloadsCommunitySupportCompany
Forum Index » S.T.A.L.K.E.R.: Clear Sky Forum » Mod discussion
Need some scripting help...

Posted by/on
Question/AnswerMake Newest Up Sort by Descending
  20:46:24  13 February 2014
profilee-mailreply Message URLTo the Top
Decane
Senior Resident
 

 
On forum: 04/04/2007
 

Message edited by:
Decane
02/14/2014 2:15:24
Messages: 1690
Need some scripting help...

I'm trying to make a stash auto-filler mod that fills all stashes in a given map (with a few exceptions) automatically upon the player's first entry to that map. I have succeeded in this, except that I have the following problem: the stashes are filled again each time I reload a savegame. I can't figure out what I'm doing wrong - I have made the following insertions:

In bind_physic_object.script:

Added this above the function generic_physics_binder:net_spawn(data):
local populated_treasures = {}

Added this inside the function generic_physics_binder:net_spawn(data):
if self.object:clsid() == clsid.inventory_box then
	local num = 0
	for i = 1, #populated_treasures do
		if self.object:id() == populated_treasures[i]:id() then
			num = num + 1
		end
	end
	local box_name = self.object:name()
	if num == 0 and box_name ~= "gar_treasure_quest_digger_reward" then
		treasure_manager.use_box(self.object)
		table.insert(populated_treasures, self.object)
	end
end

In treasure_manager.script:

Modified the function use_box referenced in the code above to populate the relevant stashes; no use writing it here.

Any ideas about what I'm doing wrong? Is the game maybe not saving my populated_treasures table?
  21:17:00  13 February 2014
profilee-mailreply Message URLTo the Top
TKGP
Senior Resident
 

 
On forum: 01/25/2013
 

Message edited by:
TKGP
02/13/2014 21:17:38
Messages: 454

---QUOTATION---
Is the game maybe not saving my populated_treasures table?
---END QUOTATION---


Yes, when you declare a variable in a script like that, it's only temporary for as long as the session lasts. The easiest way to keep track of things across saves is to use an infoportion or for more complex things, xr_logic's pstor functions. There are two of these, xr_logic.pstor_store( [level object], [variable name (string)], [value] ) and xr_logic.pstor_retrieve( [level object], [variable name], [default value (nil if omitted)] ). So, just as a very generic example, you would write something like this:

if not xr_logic.pstor_retrieve( db.actor, "didLogic", false ) then
	xr_logic.pstor_store( db.actor, "didLogic", true )
	--whatever you want to do goes here
end


An infoportion would be fine for a true/false flag like that but with pstor you can save numbers, strings, boolean, whatever. I don't think you can save arrays although you could convert an array to a string first and then unpack it later (AMK does this). Not all objects support it, I would use the actor object if possible. Other NPCs can store values temporarily but it seems to be wiped if you leave the level; maybe this can be changed but I'm not knowledgeable enough on the subject.
Hope that makes sense.
  00:50:48  14 February 2014
profilee-mailreply Message URLTo the Top
Decane
Senior Resident
 

 
On forum: 04/04/2007
Messages: 1690

---QUOTATION---
xr_logic's pstor functions
---END QUOTATION---


Thanks, pstor did the trick.
  08:35:37  17 February 2014
profilee-mailreply Message URLTo the Top
Alundaio
Sad Clown
(Resident)

 

 
On forum: 04/05/2010
 

Message edited by:
Alundaio
02/17/2014 9:25:34
Messages: 2230
I see a few issues with your script that I would like to point out:

1. If you need to remember every single inventory_box that was used you may not want to use Pstor but to use an object's save method, like the one that is in treasure_manager.script. Pstor will save 2 bytes + 1 byte extra, for every single object in that list. I think there is a limit on packet size and if you go over that limit, saves will become corrupted.

2. It's not a good idea to create a reference to a game_object other than within the local scope. Only store game_object IDs. Reference game_objects with a local variable and store their IDs if you want to remember them later (ie. just call level.object_by_id(number) or db.storage[id].object if the need arises.) Instead of putting the game_object into the table, put the id.


I recommend doing something like this for saving table data, in treasure_manager.script:

populated_treasures = {}          -- Not defined locally since we will call it from bind_physic_object.script

function save(thread)

	-- get the size of populated_treasures
	local num = 0
	for k,v in pairs(populated_treasures) do
		num = num + 1
	end
	
	-- write count to packet
	thread:w_u16(num)
	
	-- iterate table and write each object ID to packet
	for id,v in pairs(populated_treasures) do
		thread:w_u16(id)
	end
end


function load(thread)

	local num = thread:r_u16() -- Read table size from packet
	for i=1,num do
		populated_treasures[thread:r_u16()] = true
	end
end




Then in bind_physic_object.script net_spawn:

if (self.object:clsid() == clsid.inventory_box and self.object:name() ~= "gar_treasure_quest_digger_reward") then

	local id = self.object:id()
	if (treasure_manager.populated_treasures[id] == nil) then
		treasure_manager.populated_treasures[id] = true
		treasure_manager.use_box(self.object)
	end
end



As you can see only the object ids are stored into the table and then saved/loaded inside the treasure manager save/load method. If you need to save extra info, like the treasure box being looted you can extend my example to save the boolean value also. This way you can set populated_treasures[id] = false so that new treasure isn't spawned after the actor loots the inventory box. But I'm just guessing here as I don't know the full implementation of what you are doing.
  19:17:53  17 February 2014
profilee-mailreply Message URLTo the Top
Decane
Senior Resident
 

 
On forum: 04/04/2007
 

Message edited by:
Decane
02/17/2014 21:51:27
Messages: 1690
Helpful as always, Alundaio.

I got it working using your method, or at least there do not seem to be any immediately discernible bugs. If you are curious about what I did, I have uploaded my treasure_manager.script to pastebin (http://pastebin.com/1xmmpFvL). I ended up making the following adjustments to bind_physic_objects.script:

function generic_physics_binder:net_spawn(data):
if self.object:clsid() == clsid.inventory_box then
	if self.object:name() ~= ("gar_treasure_quest_digger_reward"
	or "gar_treasure_quest_reward_in_anomaly"
	or "gar_treasure_quest_old_pda") then
		if treasure_manager.populated_stashes[self.object:id()] == nil then
			treasure_manager.use_box(self.object)
		end
	end
end

function generic_physics_binder:use_callback(obj, who):
--		treasure_manager.use_box(obj, who)

EDIT: Removed superfluous variable localizations.
  17:51:19  27 February 2014
profilee-mailreply Message URLTo the Top
Decane
Senior Resident
 

 
On forum: 04/04/2007
Messages: 1690
I have another question. I want to save not only stash data, but also the IDs of NPCs who have been 'checked' for stash coordinates. (By default, CS seems to purge such data when the player moves to another map; I suppose this could be fixed by altering the save() and load() functions in xr_motivator.) To do this, can I extend Alundaio's method as below, or do I need to do something else?

In function save(thread):
	-- Save populated stashes:

	local num = 0
	for k, v in pairs (populated_stashes) do
		num = num + 1
	end

	thread:w_u16(num)

	for id, v in pairs (populated_stashes) do
		thread:w_u16(id)
		thread:w_bool(v)
	end

	-- Save used NPCs:

	num = 0
	for k, v in pairs (used_npcs) do
		num = num + 1
	end

	thread:w_u16(num)

	for id, v in pairs (used_npcs) do
		thread:w_u16(id)
	end

In function load(thread):
	-- Load the saved elements of the populated_stashes table:

	local num, id, val = thread:r_u16()
	for i = 1, num do
		id = thread:r_u16()
		val = thread:r_bool()
		populated_stashes[id] = val
	end

	-- Load the saved elements of the used_npcs table:

	num = thread:r_u16()
	for i = 1, num do
		id = thread:r_u16()
		used_npcs[id] = true
	end

  22:33:22  28 February 2014
profilee-mailreply Message URLTo the Top
Alundaio
Sad Clown
(Resident)

 

 
On forum: 04/05/2010
Messages: 2230
That should work but it might be a good idea to check if the NPC is alive or exists after loading the ID. Otherwise the table might become needlessly huge or if say later another stalker has a similar ID of a long since deleted stalker.

So maybe add this to load:


	local sim = alife()
	local check_npc
	
	num = thread:r_u16()
	for i = 1, num do
		id = thread:r_u16()
		check_npc = sim:object(id)
		if (check_npc and check_npc:clsid() == clsid.script_stalker and check_npc:alive()) then
			used_npcs[id] = true
		end
	end

  00:12:10  1 March 2014
profilee-mailreply Message URLTo the Top
Decane
Senior Resident
 

 
On forum: 04/04/2007
 

Message edited by:
Decane
03/01/2014 0:21:16
Messages: 1690
Thanks for your reply. In xr_motivator, there is the function motivator_binder:update(), in which I made the following alteration:

if bind_stalker.to_delayed_alife_release_objects[self.object:id()] == true then
	local sim, id = alife(), self.object:id()
	sim:release(sim:object(id), true)
	if treasure_manager.used_npcs[id] == true then		-- Decane edit
		table.remove(treasure_manager.used_npcs, id)	-- Decane edit
	end							-- Decane edit
	return
end

The idea is that whenever an NPC is deleted, his ID is simultaneously deleted from the table, if it exists. Would this be a good solution to the table-swelling issue?
  00:24:14  1 March 2014
profilee-mailreply Message URLTo the Top
Alundaio
Sad Clown
(Resident)

 

 
On forum: 04/05/2010
Messages: 2230
Yeah you can do that, too. I think table.remove does strange things on hash tables so might want to use used_npcs[id] = nil. Stick with table.remove on ordered tables (arrays).
 
Each word should be at least 3 characters long.
Search:    
Search conditions:    - spaces as AND    - spaces as OR   
 
Forum Index » S.T.A.L.K.E.R.: Clear Sky Forum » Mod discussion
 

All short dates are in Month-Day-Year format.


 

Copyright © 1995-2019 GSC Game World. All rights reserved.
This site is best viewed in Internet Explorer 4.xx and up and Javascript enabled. Webmaster.
Opera Software products are not supported.
If any problem concerning the site functioning under Opera Software appears apply
to Opera Software technical support service.