I just had to bypass Enter() and create my own AddToInventory() method. This is to support containers, such as weapons holding ammo or backpacks holding items. When something enters the mob's contents, I need to see if the mob has a container that might want to hold it.
However, because Enter() is only a promise to enter, not the actual entry, I couldn't use it.
Yet Enter() seems like the right place...I thought about this a bit, and realized that the system I work on in my day job tends to have two methods communicated to delegates:
thisWillHappen()
Asking for permission to do it, like Enter() does.
thisDidHappen()
Letting you know it occurred.
Given that Enter() is cast in stone, perhaps we could have:
Enter()
Same as now -- asks for permission to enter.
DidEnter()
Let's you know that the move occurred.
Then I could check for an available container in the mob's Enter(), and actually move the item into the container when DidEnter() is called for the mob.
ID:138413
Aug 30 2000, 9:28 pm
|
|
In response to Spuzzum
|
|
[edited to add this disclaimer:
Now that I think more about it, I'm pretty sure there's something fundamentally wrong with the code I put below. Move along, nothing to see here.] You can always soft-code an Enter()/Entered() by overwriting object.Move(); Even the commented Spuzzcode perplexes me! I think it's too early in the morning. :) I had a similar concern about Enter sometime in the past, though it was regarding turfs. I came up with something along these lines, but I don't remember if I ever actually tested it or not! turf Enter() // Override to handle side-effects of entry if(CheckEnter()) . = ..() CheckEnter(obj/O) // Override to handle conditions of entry if(O.density) if(src.density) return if(src.loc:density) return var/curContent for(curContent in src) if(curContent:density) return return 1 This (in theory) allows you to separate your entry validation from your special effects... for example, you could have a turf that only allows players of Good alignment (handled by CheckEnter), and heals all their damage (handled by Enter). |
In response to Spuzzum
|
|
On 8/31/00 2:55 am Spuzzum wrote:
You can always soft-code an Enter()/Entered() by overwriting object.Move(); A good idea, but the problem is I'm frequently bypassing Move() for loc setting... Since there are multiple ways to move, it would be good to have the system tell me when a move has occurred. |
In response to Deadron
|
|
On 8/31/00 8:04 am Deadron wrote:
A good idea, but the problem is I'm frequently bypassing Move() for loc setting... Since there are multiple ways to move, it would be good to have the system tell me when a move has occurred. But if you are bypassing Move(), how is it that the object is sometimes being denied entry? When you set the loc explicitely, it should always change, even if this results in density conflicts and what-not. That is why it is safest to use Move(), if possible. I might be missing something, though. |
In response to Tom H.
|
|
[editing a couple of lines for clarity and to remove my overly-obvious comparison of myself to FDR]
On 8/31/00 8:59 am Tom H. wrote: But if you are bypassing Move(), how is it that the object is sometimes being denied entry? When you set the loc explicitely, it should always change, even if this results in density conflicts and what-not. That is why it is safest to use Move(), if possible. I might be missing something, though. Move() is frequently not the appropriate call to use, but that actually doesn't have any applicability to this situation. The original problem in more detail is this: An object is moved to the mob's contents, either by Move() or by changing loc. In my case, the mob may have containers that that object should go into -- say, a bullet goes into a weapon, or an item gets put into a backpack. So the mob needs to know when something has been added to its contents so it can figure out where to put it. But there is no point at which the mob is informed that something has been moved into its contents. Enter() is only a statement of an intent to move in the future. So here is a scenario: An item has been purchased from the Merchant object. The Merchant object creates the item and needs to give it to the mob. It calls mob.Enter(object). mob/Enter(object) sees that an object wants to move to it, checks its inventory and finds that a backpack is available with an empty slot. Now the mob is stuck. Enter() is only a statement of intent to move the item in the future, it is not the actual entry. So if at this point the mob says, great I'll just put this in the backpack: object.Move(backpack) The object is in the backpack...for a split second. Because the Merchant object sees this: permission = mob.Enter(object) if (permission) // Great let's move it to the mob. object.Move(mob) And now the object has been moved out of the backpack and into the mob contents, and the mob doesn't know a thing about it. This is workaroundable, and I've already worked around it, but it would all make a bit more sense if there was a DidEnter() call, so that the mob could do this: Enter(object) // Check whether I have a backpack available .... // I do so go ahead and let this enter. return 1 DidEnter(object) // Okay now put it in the backpack where it belongs. ... In general, it is a good idea in a system to report two events: something wants to happen, something did happen. Most systems do this for things like window closing, for example. The system I work with has something like: boolean windowWillClose(window) // Return 1 if it's okay for window to close, zero if not. // If this document has been changed, first pop up a save panel... .... return 1 windowDidClose(window) // Okay the window closed -- if this was the last window, shut down the app. .... Without the second function, the developer is required to do some acrobatics in their code to do the right thing. |
In response to Deadron
|
|
On 8/31/00 12:33 pm Deadron wrote:
The object is in the backpack...for a split second. Because the Merchant object sees this: This is a little strange to me. Usually there is no need to call any of the predefined procs (like object.Enter()) yourself; a notable exception is Move(), which defined as a proc rather than as a function ("move") because it happens to be called by other client "events" (like North(), etc). This is a regrettable design. To put it another way, the predefined procs are all "events" triggered by the server's internal ticking, rather than explicit code defined by the user. Even Move() is activated in this fashion for the most part, if you consider that it gets called mainly through the various keyboard events like "north" and "south". That is fudgery, though. So, to get back to your example, I don't quite understand the statement: permission = mob.Enter(object) if(permission) object.Move(mob) because object.Move(mob) will internally call mob.Enter(object) as well as the Exit() proc for the loc in which the object currently resides. Only if both of these checks suceed will the move occur. So if you want an all-purpose DidEnter() event, I like Spuzzum's approach of using Move(): obj/Move(loc) if(..()) // will call loc:Enter() loc:DidEnter(src) return 1 else return 0 And then DidEnter() should act just like a built-in event. You'll have to use Move() to change locations, but why wouldn't you? Explicitely setting the loc is dangerous because not only does it bypass the density checking on the entering location, but it doesn't check restrictions on the exiting location either. I have the faintest notion that I'm missing something here, though, so I'd like to hear what you think. |
In response to Tom H.
|
|
On 8/31/00 4:26 pm Tom H. wrote:
This is a little strange to me. Usually there is no need to call any of the predefined procs (like object.Enter()) yourself; a notable exception is Move(), which defined as a proc rather than as a function ("move") because it happens to be called by other client "events" (like North(), etc). This is a regrettable design. I knew changing code on the fly would bite me...I actually just change the loc, rather than using Move() in this case -- but that's "an implementation detail" as we say in the business, and doesn't really impact the question of how an object knows that something has entered its contents. So if you want an all-purpose DidEnter() event, I like Spuzzum's approach of using Move(): Yes this is an approach that will partially do the job (for those cases where Move() is used) -- I just wanted to point out that without doing something like this, there is no way to know that an item has entered the contents through Move(), and that there is in fact no way to know if the item is added using loc. In thinking about that fact, I realized that other systems I've used have a "pattern" to address that, which is the "willDoThis", "didDoThis" thing I talked about, and I thought that might be useful to consider for Byond. The rest of this message has nothing to do with the benefits of a DidEnter() proc... You'll have to use Move() to change locations, but why wouldn't you? Explicitely setting the loc is dangerous because not only does it bypass the density checking on the entering location, but it doesn't check restrictions on the exiting location either. Well, frequently I don't want either of those checks. There are times when I just want to make the move and be confident that it occurred. Most of the time in my game when I have code related to moving, it is to move a mob into the game, or to reincarnate them from null onto the map, and I just want to guarantee that they will arrive at the designated spot. In general I use loc unless I know I want the additional features of Move(). Probably I'm biased in this direction because of some bad early experiences, where I'd move a player onto the map, the Move() would fail, and they would get bounced into limbo. Then I'd end up writing a bunch of code to check if the Move() succeeded, find a random spot nearby, see if they could Move() there, etc. Sometimes this meant they showed up on the other side of a wall they weren't supposed to be able to cross or something... Finally I just got tired of it and decided that when adding a mob to a known spot on the map, I'd just stick them there and avoid the hassle. (Uh oh I'm contributing to that efficiency myth again...) But, in the same way you had to pound on me to not conflate icon movement and icon directions, I beg of you not to mix the concern over whether I'm using Move() or not with the DidEnter() question... |
In response to Deadron
|
|
On 8/31/00 5:41 pm Deadron wrote:
But, in the same way you had to pound on me to not conflate icon movement and icon directions, I beg of you not to mix the concern over whether I'm using Move() or not with the DidEnter() question... Point taken. I will concede that the notation itself is pretty ambigous, since a coder overriding Enter() might well expect that the event will be called on entrance, rather than on entrance-attempt (a subtle distinction). I still think you should use Move(), though. If the moves are failing then the program is probably trying to tell you that you are making a boo-boo (like putting two dense guys at the same location). That, or it would imply some sort of bug on our end. And I'd like to know about that! And even if you don't want to use Move(), you could always code up your own informant move like this: proc/move(obj,loc) obj:loc = loc loc:DidEnter() which is probably what you are doing now to get around these shennanigans. Of course you can argue that this is just as bad as the above since it forces you into using a particular proc to assign location, and that could be prone to error. I'll think about what you've suggested. Despite my babble, I think that this is a good idea. I'm a bit annoyed that the notation is so fixed, though. I wonder if Enter()/Entered() and Exit()/Exited() wouldn't be too confusing. |
obj
Move(loc as loc)
..() //by default, calls src.loc:Exit(), and loc:Enter()
loc:Entered(src)
return ..() //note: Entered() must return 1 for this to work