Function calls : from_inv_slot / to_inv_slot / after_...

This forum is for the Lua scriptable clone of DM/CSB called Dungeon Strikes Back by Sophia. Use DSB to build your own highly customised games.

Moderator: Sophia

Forum rules
Please read the Forum rules and policies before posting.
Post Reply
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Function calls : from_inv_slot / to_inv_slot / after_...

Post by Joramun »

As discussed with Sophia, those functions get problematic
if someone tries to make a custom inventory with different
slot number, or to make "unpredicted" behavior, like an item
that would call a function once put into a chest.

I don't really understand when exactly "after_to_l_hand" and "after_from_l_hand" are called also...

EDIT: there can also be problems with the "fit_/slot/" properties for custom inventories...
What Is Your Quest ?
Remy
Craftsman
Posts: 111
Joined: Wed Sep 05, 2007 5:24 pm
Contact:

Post by Remy »

I don't really understand when exactly "after_to_l_hand" and "after_from_l_hand" are called also...
Well, to_l_hand is called before the obj is popped from the mouse cursor, after_to_l_hand is called after. Both, however, occur after the DSB Engine does a "fit_[slot]" check (so if that check fails, neither is called).
So, after the player clicks a slot (with an obj in the cursor), DSB checks "fit_", then calls "to_", then pops the obj to the slot, then calls "after_to_".
That's basically the sequence (slot "r_hand" has some further actions, which may or may not be relevant).

Really, I'd have to see how sys_inventory actually handles "fit_" checks under the new system before making assumptions about what's left internally, handled by the DSB Engine alone. Depending on how/from where/why it gets called, the solution could be there. Obviously, it would have to be aware of the slot the player is trying to put something in, and if it is, then it could cover calling "to_" functions.
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Under the new system, the only thing called by the engine for all slots is sys_inventory. All that fit/to/from/etc. stuff is handled from there. Well, and the core engine takes care of the actual putting the instance into the slot, but that's just housekeeping. Currently, slot 0 (by default, "r_hand") has some hardcoded extra features, like triggering subrenderers when the inventory screen is up.

But why tell when I can show! It's not that long of a function:

Code: Select all

function sys_inventory(who, slot, pickup, putdown, when, force)
	local from_word
	if (when) then from_word = "after_from_"
	else from_word = "from_" end

	local to_word
	if (when) then to_word = "after_to_"
	else to_word = "to_" end
	
	local slotname = inventory_info[slot].name
	
	if (not when) then
	    local fit = false
	    
		if (force or not putdown) then
		    fit = true
		else
		    local arch = dsb_find_arch(putdown)
		    
		    if (slot <= INV_L_HAND) then fit = true
		    elseif (arch.no_fit_inventory) then fit = false
		    elseif (slot >= INV_PACK) then fit = true
		    else
		        fit = arch["fit_" .. slotname]
		        if (slot == INV_QUIVER and not fit) then
		            fit = arch.fit_sheath
				end
		    end
		end
		
		if (not fit) then return false end
	end
	
	if (pickup) then
	    local arch = dsb_find_arch(pickup)
		if (arch[from_word .. "anywhere"]) then
		    if (arch[from_word .. "anywhere"](arch, pickup, who)) then
		        return false
			end
		end
		if (arch[from_word .. slotname]) then
		    if (arch[from_word .. slotname](arch, pickup, who)) then
		        return false
			end
		end
	end
	
	if (putdown) then
	    local arch = dsb_find_arch(putdown)
		if (arch[to_word .. "anywhere"]) then
		    if (arch[to_word .. "anywhere"](arch, putdown, who)) then
		        return false
			end
		end
		if (arch[to_word .. slotname]) then
		    if (arch[to_word .. slotname](arch, putdown, who)) then
		        return false
			end
		end
	end
	
	return true
end
As for chests, that's already handled. When an objzone is clicked, the objzone_check method is called for that arch. For a chest, this checks fit_chest. (For convenience and logic, anything with fit_pouch set is also assumed to fit in a chest)
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

And so this function is called whenever an item is taken to/from any inventory slot...

Is it also called when an item is spawned in a character's inventory ?
What Is Your Quest ?
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Yes it is.

In that case, the return value is ignored-- the instance is going into the inventory no matter. In those cases, the force parameter is set to true.
Remy
Craftsman
Posts: 111
Joined: Wed Sep 05, 2007 5:24 pm
Contact:

Post by Remy »

n that case, the return value is ignored-- the instance is going into the inventory no matter. In those cases, the force parameter is set to true.
Perhaps I'm reading it wrong, but if 'force' is supposed to work no matter what, shouldn't the second half of the function - after all the "fit" stuff is done - return "force" in the places where it returns 'false'? Namely, if an event handler exists, and that event handler returns something other than 'false' or 'nil'. I assume it's done this way because most handlers will return 'nil', but gives the designer an oppurtunity to cancel a move conditionally.
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Your understanding of the function is right, and that would be a good change to ensure that the "proper" return value is always returned.

In practice, it makes no difference, because if force is set, the return value is ignored anyway. The function can return true, false, nil, or 3.14159, it doesn't matter. (Hence, it's being "forced")

It's simply there to inform the function the move is always going to succeed, so that it can carry out whatever it needs to do, even if it would normally fail. In this case, force tells it to not even bother with any of the fit_ values, because the inst will go into the inventory anyway.
Remy
Craftsman
Posts: 111
Joined: Wed Sep 05, 2007 5:24 pm
Contact:

Post by Remy »

Okay, well, then I have another question: if two insts are being swapped (one going from MOUSE_HAND to an inventory slot, and one going the other way), does sys_inventory get called just once for both, with both arguments - 'pickup' and 'putdown' - set?
'Cause if that's the case, I think there needs to be caveat - that 'to_[slot]' functions should be used for conditional checks and that 'after_to_[slot]' functions for any actual changes to a champion or the dungeon.
If both arch's have event functions defined, and the 'putdown' inst follows through with its handler, but the 'pickup' one hits a conditional and returns 'true', then the move will be cancelled; but 'putdown' will have already executed it's handler, which is bad.
Now, I don't think this needs to be enforced in the code - because it's too limiting for run-of-the-mill dungeons that will never exploit the ability to cancel moves this way - but it's something to keep in mind.

Either that, or do away with the exploit, but I really can't see another way to do the kinds of things you can do with it available.

(Actually, whether or not both insts are checked at the same time is irrelevant in this case. I imagine both are - since otherwise there will be a moment when either the MOUSE_HAND or an inventory slot will have two items - but the end result of a cancellation is the same.)
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Those are very good observations. :)

I've come up with a couple of ways to remedy this. First of all, I've added the following lines:

Code: Select all

if (type(fit) == "function") then
	fit = fit(arch, putdown, who)
end
What this does is it allows the fit_* fields in the arch to be functions as well, giving the ability to dynamically cancel the move before anything's actually happened.

Now, this means that the fit_* functions will cancel on a false. The other ones would cancel on a true... and it just got too confusing. So I changed the function call to this:

Code: Select all

if (arch[from_word .. "anywhere"](arch, pickup, who) ~= nil)
Now, if it returns anything at all, that will be taken as failure. False, true, whatever. Chances are if everything went according to plan you're not going to have a return statement at all. (and in Lua, that's taken as a return of nil)

Finally, I changed the second set of functions (the ones operating on putdown, after pickup has already succeeded) to ignore the return value. Might as well not even invite the troublesome situation. :)
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

...I thought that false == nil in LUA...

So the function sys_inventory is called once "before" and once "after" the swap of items (or between NULL and an item) :
- "fit_" can interrupt the swap.
- "to_" & "from_" applies if 'fit' is true, and can also interrupt the swap
( it's ok if I want some item to be unmovable from the inventory )
- 'after_" applies after the swap...

I agree it's better to hide the swap in between as a "core function", but somehow the current design makes me uncomfortable.
It's possible in a "normal" dungeon to make weird things with the "to_" and "from_" functions,
that should normally be used only for checking special cases of "fit_" (like a torch going into a hand).
The use of the "after_" functions seems ok, even though the return value of "after_from" can impede the execution of "after_to"...

And about the dsb_spawn calling sys_inventory :
what happens if the spawn fails ?
(e.g. if the inventory slot is taken already)
What Is Your Quest ?
Remy
Craftsman
Posts: 111
Joined: Wed Sep 05, 2007 5:24 pm
Contact:

Post by Remy »

...I thought that false == nil in LUA...
Not quite. Lua will accept 'nil' as 'false', but 'false' is never 'nil'. 'nil' is the absense of a value, 'false' is a value - it can never mean the absense of itself.
- "fit_" can interrupt the swap.
- "to_" & "from_" applies if 'fit' is true, and can also interrupt the swap
Again, not quite. At least, not anymore. 'to_*' can no longer interrupt swaps - to stop an inst from going to a slot, it must be handled in a 'fit_*'. (Which is actually rather brilliant, and much better than the idea I had when I said "enforced in code").
It's possible in a "normal" dungeon to make weird things with the "to_" and "from_" functions,
I'm wondering how you classify a dungeon as "normal" - if it's doing wierd things in 'to_*' and 'from_*' functions, and you don't call that "normal", then, obviously, the dungeon isn't "normal".
It can only do wierd things if the designer specifically returns values with event handlers - in other words, the designer has to make an active choice.
This applies to 'after_*' functions as well - they shouldn't be returning anything, since returning values here does absolutely nothing - the swap has already taken place, it can't be stopped at this point. If the designer makes them return values, he's trying to do something he likely shouldn't be.
what happens if the spawn fails ?
The inst is never created, so even if sys_inventory is called, both 'pickup' and 'putdown' are 'nil', and no event functions will be executed.
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Joramun wrote:...I thought that false == nil in LUA...
Nil evalulates to false when it's taken as a boolean, but other than that, they're distinct values.

As an aside, one of the few things that actually annoys me about Lua is that the number zero is not evalulated in this way.

Code: Select all

num = 0
if (num) then
	-- This code will execute
else
	-- This code won't!
end
Joramun wrote:It's possible in a "normal" dungeon to make weird things with the "to_" and "from_" functions,
that should normally be used only for checking special cases of "fit_" (like a torch going into a hand).
"Normal" and "weird" are pretty subjective when we're talking about custom dungeons, aren't they? ;)

My motivation behind allowing the from_* to return a value that can be acted upon was actually exactly what you said was weird-- to embed a check in there. In the current code, there's no opposite to "fit." This is why I made from_* execute first, so it can also forbid taking something out of the hand. For example, say you want to create a cursed sword that won't let you take it out of your weapon hand. That code would have to be in from_r_hand.
Joramun wrote:The use of the "after_" functions seems ok, even though the return value of "after_from" can impede the execution of "after_to"...
Well, yes. Realistically, though, after_* shouldn't return anything, because the return value won't do anything. If it returns something, I'd say that's a flaw in the dungeon designer's code. You're right about that situation though-- I'm going to leave it in for the bizarre circumstances I haven't thought of where some twisted mind might find it useful.
So, in other words, if Zyx ever gets into DSB editing, we'll find out what it's for. ;)
RemyR wrote:If the designer makes them return values, he's trying to do something he likely shouldn't be.
Or something delightfully twisted us mere mortals cannot yet grasp. :D
(Ok, so I'm being optimistic!)
RemyR wrote:The inst is never created, so even if sys_inventory is called, both 'pickup' and 'putdown' are 'nil', and no event functions will be executed.
It's never called.
If the slot you try to dsb_spawn or dsb_move into is full, the function returns nil and does nothing else significant.
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

You'll soon see what's a weird dungeon.
This is the second item in my list of projects in DSB.

Anyway, my brain is too small to figure out what the last version of the sys_inventory function must look like now, I was only talking about the one above. So don't worry about my remarks on "to_*" "from_*" etc.
What Is Your Quest ?
Post Reply