Handling Interactions?

G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

I just added (openable/closeable/smashable) doors to my game. Right now
right-clicking on a door brings up the actions that you can perform on it in
a nice context menu (here's a screenshot -
http://www.stanford.edu/~jjshed/blog/pics/witherwyncontext.png).

The exercise has given me an inkling of a design problem that I'ven't
anticipated. How do I model interactions between game entities?

At the moment, I have an abstract Entity class which I subclass for Items,
Doors, and Critters (the PC is a critter). The get the doors working, I just
added several functions to the Door entity (Open, Close, Smash). Then, when
I need to generate a list of actions that can be taken on a tile, I loop
through all the entities on that tile, check if it's a door, check the
door's state, and generate the appropriate list of actions.

This is kind of messy, and door interactions are pretty simple. So I think I
need some sort of event/message processing system? The thing I am trying to
avoid is having, for example, a Critter entity trying to open a door, call
the Door entity's OpenDoor() function directly. I think all these requests
should be channeled through a message system of some sort. This should make
it easier to manage things like printing out notifications to the text
console...

If I sound confused and unsure, it's because I am. The system I've worked
out as far as the Entities are concerned, I think, is pretty good and has
worked well so far. So maybe someone can help me with the last piece.

PS - I'd also like it to be easy to generate a list of all actions that can
be taken on an Entity - both for pushing this info to the GUI and for
pushing environment data/options to the AI.

--
Blog:
Shedletsky's Bits: A Random Walk Through Manifold Space
http://www.stanford.edu/~jjshed/blog
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At Fri, 9 Sep 2005 09:36:14 -0400,
Shedletsky wrote:

> This is kind of messy, and door interactions are pretty simple. So I think I
> need some sort of event/message processing system? The thing I am trying to
> avoid is having, for example, a Critter entity trying to open a door, call
> the Door entity's OpenDoor() function directly. I think all these requests
> should be channeled through a message system of some sort. This should make
> it easier to manage things like printing out notifications to the text
> console...

Isn't a method call sort of a message passing system already?
Do you want to build another layer of OO language over the one you're
already using?

--
Radomir `The Sheep' Dopieralski @**@_
(nn) 3 Grin
. . . ..v.vVvVVvVvv.v.. .
 

oskar

Distinguished
Apr 8, 2004
31
0
18,530
Archived from groups: rec.games.roguelike.development (More info?)

Shedletsky wrote:

>> Isn't a method call sort of a message passing system already?
>> Do you want to build another layer of OO language over the one you're
>> already using?
>
> It seems like bad decomposition to me if the different Entity subclasses
> need to know of each other's existence (at the level of method
> signatures).
>
> It seems better to me to define an ActionEvent object that contains all
> the information required to perform an action on an Entity (open, close,
> drink, attack, ignite, ect ect). This ActionEvent is then sent to every
> Entity it applies to, and they parse it. If the ActionEvent contains an
> action that the Entity does not respond to, it can just ignore it.
>
> So this way, this way when the player bumps a door I can do something
> like:
>
> Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));

This is a really simple solution I used once:
(In Java notation as that is what I assume you use)

Entity defines the following API:
Vector getActions(Actor actor)
void doAction(String action, Actor actor)

getActions() returns a list of strings, like {"drop", "wield"} for
something
in your inventory, {"open","lock"} for a closed door, {"buy","examine"}
for
an item in a shop, etc... A subclass' getAction() most of the time
returns
its superclass' actions followed by its own...

doAction() is often just multiplexer, calling open(), close(), etc
methods
the action argument matches, else passing it on to the super class
implementation.

Difference from your Map.SendActionEvent(...) example:
- String instead of ActionEvent (simpler)
- Sent directly between entities, not through Map

I found this to work very well for a real-time networked multi-player
roguelike.

Of course, if you plan on using multiple event listeners for various
events
througout the map, it's probably a better idea to dispatch your events
through the Map.

/Oskar
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Shedletsky wrote:
> > Isn't a method call sort of a message passing system already?
> > Do you want to build another layer of OO language over the one you're
> > already using?
>
> It seems like bad decomposition to me if the different Entity subclasses
> need to know of each other's existence (at the level of method signatures).

Well... I'm opposed to having different entity subclasses in the first
place...

In any case, why should the open() method only be present for doors?
Should not all objects that can be targets of open() by the UI have an
open method, which presumeably usually spams: "That is a silly thing to
do"?

> It seems better to me to define an ActionEvent object that contains all the
> information required to perform an action on an Entity (open, close, drink,
> attack, ignite, ect ect). This ActionEvent is then sent to every Entity it
> applies to, and they parse it. If the ActionEvent contains an action that
> the Entity does not respond to, it can just ignore it.

But, if you have these action events, why not just roll them into
methods/interfaces? Why make this second level of abstraction? Is it
in this belief that it would then be easier to add a new verb?

> So this way, this way when the player bumps a door I can do something like:
>
> Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));

How do you know to send OPEN_DOOR and not MELEE_ATTACK? Is it not
because you have already determined it is a door, and thus the logical
action is auto-open?

You thus have:
MOB::bump(direction)
{
if (monster in direction)
Map.SendActionEvent(new ActionEvent(x, y, EventID.MELEE_ATTACK));
else if (tile in direction is door)
Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));
else
Map.SendActionEvent(new ActionEvent(x, y, EventID.WALK));
}

This, I think, also shows a problem with the action event system. You
need to make a general enough signature to fully describe all of your
actions. A melee attack requires knowledge of the attacker, and,
ideally, the victim in case you end up with more than one monster on a
tile.

> Instead of this logic (which is simple in this case because only Door
> entites can have the "open" action applied to them at this point):
>
> ArrayList entities = Map.GetEntities(x, y);
> foreach(Entity e in entities) {
> if(e is Door)
> {
> ((Door)e).OpenDoor();
> }
> }

I don't see you avoid having this logic *somewhere*. You can try and
hide it (by, say, subclassing a "DefaultBump" action which doors react
differently to than creatures). But somewhere you need to take the
actionBump() and decide that the user meant actionOpen. This
*requires* you to determine that the thing is a door, as oppposed to a
monster.

Do also keep in mind that some people, who I will leave nameless, hate
the idea of auto openning doors. It is a lot easier to add a
configuration option to one code path in actionBump() which chooses
what to do, than to try and deal with your class hierarchy hiding this
decision tree.

> Possibly another solution is to model each possible action as an inferace
> (like IOpenable, ICloseable, IFlameable), then have the entities that
> support that action implement the interface.

I do like the idea of using interfaces to approach this sort of
problem. Remember, however, that this isn't all that different from
just defining open() methods for your Entity and having a switch inside
the Entity to handle your different cases.

I think roguelike developers get mislead with the theory that "object
oriented" must mean *physical* objects, so try to build everything
around physical objects. Roguelikes are better organized verb-first
than object-first. You really, REALLY, want all of your "drink"
methods to be in one place. This provides the right axis to factor
code along. I cannot stress enough that it is more important to ensure
all of your eat() code is together than it is to ensure that your
item_moldy_cheese code is all together.

Having ITEM::eat() trigger a giant switch statement is much more
preferrable than having an equal number of ITEM subclasses (or even
ACTIONEAT interface subclasses, though the interface approach at least
can more easily be placed in one file) which override eat() slightly
differently.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

> Isn't a method call sort of a message passing system already?
> Do you want to build another layer of OO language over the one you're
> already using?

It seems like bad decomposition to me if the different Entity subclasses
need to know of each other's existence (at the level of method signatures).

It seems better to me to define an ActionEvent object that contains all the
information required to perform an action on an Entity (open, close, drink,
attack, ignite, ect ect). This ActionEvent is then sent to every Entity it
applies to, and they parse it. If the ActionEvent contains an action that
the Entity does not respond to, it can just ignore it.

So this way, this way when the player bumps a door I can do something like:

Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));

Instead of this logic (which is simple in this case because only Door
entites can have the "open" action applied to them at this point):

ArrayList entities = Map.GetEntities(x, y);
foreach(Entity e in entities) {
if(e is Door)
{
((Door)e).OpenDoor();
}
}

Possibly another solution is to model each possible action as an inferace
(like IOpenable, ICloseable, IFlameable), then have the entities that
support that action implement the interface.

Maybe I'll try gamedev.net to see if anyone there has any ideas.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Jeff Lait wrote:
> Having ITEM::eat() trigger a giant switch statement is much more
> preferrable than having an equal number of ITEM subclasses

Btw, is ITEM::eat(NPC) better than NPC::eat(ITEM)? Or does that
matter?:)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Jeff Lait spoketh thus:

> How do you know to send OPEN_DOOR and not MELEE_ATTACK? Is it not
> because you have already determined it is a door, and thus the logical
> action is auto-open?

Presumably, the monsters in your game do not go around blindly bumping
into things and waiting to see what happens. Much better to have the
monster AI check possible actions and decide what it is going to do,
then send this information.

Monster::MakeMove
{
List1 = GetListOfNearbyEntities()
List2 = GetListOfPossibleActions(List1)
Action = ChooseBestMove(list2)
switch(Action.Type)
{
case OPEN: OpenDoor(Action.x,Action.y,self)
case CLOSE: CloseDoor(Action.x,Action.y,self)
case SMASH: KickDoor(Action.x,Action.y,self)
case ATTACK: DoAttack(Action.x,Action.y,self)
case GRAB: GetItem(Action.x,action.y,self)
else: TakeRandomStep(self)
}
}

Ambiguous bumping only exists for the player. Better to have one bump
function which chooses among discreet actions than a DoorBump(),
MonsterBump(), WallBump() etc. Monsters, of course, dont need to bump
at all, because their AI should decide what they mean to do, not the
bumping code.

Once you know what action is intended and on what, all you need is to
decide where you want to place the function, as Door.Open(opener),
Monster.Open(door), or DoorOpen(x,y,opener). Using the latter option
allows you to, as you suggested, keep your door opening code all in one
place.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At Sat, 10 Sep 2005 11:18:32 -0400,
Shedletsky wrote:

>> Isn't a method call sort of a message passing system already?
>> Do you want to build another layer of OO language over the one you're
>> already using?
> It seems like bad decomposition to me if the different Entity subclasses
> need to know of each other's existence (at the level of method signatures).

You need to know of the existence of an entity if you want to interact
with it.

> It seems better to me to define an ActionEvent object that contains all the
> information required to perform an action on an Entity (open, close, drink,
> attack, ignite, ect ect). This ActionEvent is then sent to every Entity it
> applies to, and they parse it. If the ActionEvent contains an action that
> the Entity does not respond to, it can just ignore it.
>
> So this way, this way when the player bumps a door I can do something like:
>
> Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));
>
> Instead of this logic (which is simple in this case because only Door
> entites can have the "open" action applied to them at this point):
>
> ArrayList entities = Map.GetEntities(x, y);
> foreach(Entity e in entities) {
> if(e is Door)
> {
> ((Door)e).OpenDoor();
> }
> }

Ilike the second one better -- it nicely shows that there's a difference
in behavior when the tile contains door (a special case).
I'd however check for the door after checking that the square you want to
move to is blocked.

> Possibly another solution is to model each possible action as an inferace
> (like IOpenable, ICloseable, IFlameable), then have the entities that
> support that action implement the interface.

:)

> Maybe I'll try gamedev.net to see if anyone there has any ideas.
:)

--
Radomir `The Sheep' Dopieralski @**@_
(nn) 3 Grin
. . . ..v.vVvVVvVvv.v.. .
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At 10 Sep 2005 16:19:29 GMT,
Radomir 'The Sheep' Dopieralski wrote:

> At Sat, 10 Sep 2005 11:18:32 -0400,
> Shedletsky wrote:
>> So this way, this way when the player bumps a door I can do something like:

>> Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));

>> Instead of this logic (which is simple in this case because only Door
>> entites can have the "open" action applied to them at this point):

>> ArrayList entities = Map.GetEntities(x, y);
>> foreach(Entity e in entities) {
>> if(e is Door)
>> {
>> ((Door)e).OpenDoor();
>> }
>> }

> Ilike the second one better -- it nicely shows that there's a difference
> in behavior when the tile contains door (a special case).
> I'd however check for the door after checking that the square you want to
> move to is blocked.

Offcourse it belongs to the UI code, not creature movement code ;)

--
Radomir `The Sheep' Dopieralski @**@_
(--) 3 ..zzZZ
. . . ..v.vVvVVvVvv.v.. .
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

"Krice" <paulkp@mbnet.fi> wrote in message
news:1126383316.355710.49390@g43g2000cwa.googlegroups.com...
> Jeff Lait wrote:
>> Having ITEM::eat() trigger a giant switch statement is much more
>> preferrable than having an equal number of ITEM subclasses
>
> Btw, is ITEM::eat(NPC) better than NPC::eat(ITEM)? Or does that
> matter?:)
>

There is also third option - Rules::eat( NPC, ITEM ).

E.g. in nethack, you need to know details of both NPC and ITEM
instances to know result of different actions.

So I would prefer this third option - keep rules elsewhere.

Of course, this does not mean that eat function needs to be just
one big switch statement, you can add dispatching.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Shedletsky wrote:
[..]
> Map.SendActionEvent(new ActionEvent(x, y, EventID.OPEN_DOOR));
>
> Instead of this logic (which is simple in this case because only Door
> entites can have the "open" action applied to them at this point):
>
> ArrayList entities = Map.GetEntities(x, y);
> foreach(Entity e in entities) {
> if(e is Door)
> {
> ((Door)e).OpenDoor();
> }
> }

You might try reading about the mediator design pattern. It's
basically about objects communicating through a special object
(mediator), so that they don't reference each other directly.

Another good thing is separating the decision making code from
the execution code. I'd use something like this:

ArrayList entities = Map.GetEntities(x, y);
foreach(Entity e in entities) {
if(e is Door)
{
return new Action(Actions.OPEN_DOOR(x,y));
}
}

and somewhere else:

for_all_monsters {
Action x=monster.getAction();
if (x.legal()) x.execute();
}


This way you have common code for performing the PC's
and the monsters' moves. You also have better control
over monsters making illegal moves and other strange things.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

> In any case, why should the open() method only be present for doors?
> Should not all objects that can be targets of open() by the UI have an
> open method, which presumeably usually spams: "That is a silly thing to
> do"?

No. I'm trying to cut down on screen clutter by not presenting options that
are useless. This is the route that a lot of point and click adventure games
(like Quest for Glory) take and it was a huge evolution over having to
pixel-hunt for the right mouse hotspot in the games that came before it.
Presenting useless options is forcing the player to "pixel hunt" (a common
sin in RL UIs, in my opinion).

> Do also keep in mind that some people, who I will leave nameless, hate
> the idea of auto openning doors.

If you have to bump it to open it, it's not really auto-opening, is it? Why
use two keypresses where one will do? I mean, there's no time when you want
to explicitly run into a closed door (though it may confuse the dungeon
denizens).

> I do like the idea of using interfaces to approach this sort of
> problem. Remember, however, that this isn't all that different from
> just defining open() methods for your Entity and having a switch inside
> the Entity to handle your different cases.
>
> I think roguelike developers get mislead with the theory that "object
> oriented" must mean *physical* objects, so try to build everything
> around physical objects. Roguelikes are better organized verb-first
> than object-first. You really, REALLY, want all of your "drink"
> methods to be in one place. This provides the right axis to factor
> code along. I cannot stress enough that it is more important to ensure
> all of your eat() code is together than it is to ensure that your
> item_moldy_cheese code is all together.

Yeah the object oriented-ness is a different way of thinking about code for
me than what I normally am used to. My goal in coming up with a good design
for handling objects and their interactions is to make the system easy to
extend. From this point of view, it makes sense to me to keep all the code
relating to an entity and how it interacts within a single object (if
possible). However, I can see a point where I will have a large number of
different entities, and I will wish that all their action code for a
particular verb were in one place. It seems hard to have it both ways,
though...

> Having ITEM::eat() trigger a giant switch statement is much more
> preferrable than having an equal number of ITEM subclasses (or even
> ACTIONEAT interface subclasses, though the interface approach at least
> can more easily be placed in one file) which override eat() slightly
> differently.

I agree to that. I will probably subclass a Food object off of Item and have
that handle all the food-specific actions within that single object. As
opposed to having a Pineapple object, a Rations object, a Banana object, ect
ect... That would be silly.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

> There is also third option - Rules::eat( NPC, ITEM ).
>
> E.g. in nethack, you need to know details of both NPC and ITEM
> instances to know result of different actions.
>
> So I would prefer this third option - keep rules elsewhere.

But if you do ITEM::eat(NPC) or NPC::eat(ITEM), in both cases you have
pointers to both objects. So I don't see how it is better than
Rules::eat(NPC, ITEM) from this point of view...
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

> This way you have common code for performing the PC's
> and the monsters' moves. You also have better control
> over monsters making illegal moves and other strange things.

Oh. That's something I hadn't thought about. That does seem valuable.
Thanks!
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At Sun, 11 Sep 2005 13:23:31 -0400,
Shedletsky wrote:

>> There is also third option - Rules::eat( NPC, ITEM ).
>>
>> E.g. in nethack, you need to know details of both NPC and ITEM
>> instances to know result of different actions.
>>
>> So I would prefer this third option - keep rules elsewhere.
>
> But if you do ITEM::eat(NPC) or NPC::eat(ITEM), in both cases you have
> pointers to both objects. So I don't see how it is better than
> Rules::eat(NPC, ITEM) from this point of view...

It's clearer. Doesn't introduce assymetry.

If you like OO approach and methaphors, you could have a
GameMaster object, which owns all the level maps, items, etc.
Then the creatures that want to perform an action tell it
to the GameMaster (ie. master.walk(SOUTH)), and he decides
whether the action is possible and what should be it's results.

Note that with this approach it's convenient to separate
beings (the AIs or UIs that act in turns and decide on actions,
each of them having a pointer to the game master) and their
bodies (character sheets and figurines standing on the table,
owned by the game master).

Of course, it's only one of many possible designs. But I think
it nicely separates the game mechanics, with all of it's special
cases in one place, and the AI/UI logic.
--
Radomir `The Sheep' Dopieralski @**@_
(><) 3 Ouch!
. . . ..v.vVvVVvVvv.v.. .
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At Sun, 11 Sep 2005 13:20:19 -0400,
Shedletsky wrote:

>> In any case, why should the open() method only be present for doors?
>> Should not all objects that can be targets of open() by the UI have an
>> open method, which presumeably usually spams: "That is a silly thing to
>> do"?
> No. I'm trying to cut down on screen clutter by not presenting options that
> are useless. This is the route that a lot of point and click adventure games
> (like Quest for Glory) take and it was a huge evolution over having to
> pixel-hunt for the right mouse hotspot in the games that came before it.
> Presenting useless options is forcing the player to "pixel hunt" (a common
> sin in RL UIs, in my opinion).

Not exacly, pixel-hunt is caused by hiding important UI elements from the
user. You're probably talking about trying all the possible combinations
of objects and actions.

Trying all the combinations is surely boring and tiring. But making them
less boting and tiring by automating the process (that is, presenting the
player only those combinations, that actually do something) is not a good
solution in my opinion (at least not in adventure games). Instead, there
should be hints in the game itself on which combinations are useful. This
however cannot be automated. The perfect adventure game wound't allow you
to just 'use an item' without explaining why you think it should work,
and then accept any solution provided it sounds reasonable. ;)

I remember an adventure game where you were supposed to open the window
with a bar of soap...

Anyways, the default open() method for unopenable objects can return
failure without displaying any message -- then your UI code can, depending
on whether an [o]pen action was used or you were just bumping into door,
choose whether to display the message or not.

>> Do also keep in mind that some people, who I will leave nameless, hate
>> the idea of auto openning doors.
> If you have to bump it to open it, it's not really auto-opening, is it? Why
> use two keypresses where one will do? I mean, there's no time when you want
> to explicitly run into a closed door (though it may confuse the dungeon
> denizens).

I can easily imagine a time when you don't want to open that door, but you
pressed one of the 'hjkl' keys (just teasing) too many times.

--
Radomir `The Sheep' Dopieralski @**@_
(..) 3 Bee!
. . . ..v.vVvVVvVvv.v.. .
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

"Shedletsky" <mylastname@stanford.edu> wrote in message
news:dg1p6m$1jj$1@news.Stanford.EDU...
>> There is also third option - Rules::eat( NPC, ITEM ).
>>
>> E.g. in nethack, you need to know details of both NPC and ITEM
>> instances to know result of different actions.
>>
>> So I would prefer this third option - keep rules elsewhere.
>
> But if you do ITEM::eat(NPC) or NPC::eat(ITEM), in both cases you have
> pointers to both objects. So I don't see how it is better than
> Rules::eat(NPC, ITEM) from this point of view...

Yes, you have pointers to both objects. In Rules::eat approach, you
can route all actions through single instance.

(ok, you can get similar result by adding a reference to all ITEM & NPC
instances
or by using global variables).

The only benefit of eat method being part of ITEM / NPC:
- it could be virtual, so derived classes could override
- better encapsulation; some member variables would not be needed to be
exposed
-> still result of action depends on details of the other instance as
well;
most languages do not support double dispatching.

So this Rules::eat approach would close to the following:
- you have a board with pieces (tiles, items, monsters & PC).
- there are attributes related to items & monsters.
-> ITEM, NPC instances
- there is a book of rules - what can be done and what not & what is the
effect of actions.
-> Rules instance(s)
- gamemaster makes sure that those rules are followed.
he also handles actions of monsters & gemeral events in game world.
- player decides actions of PC.

But in the end, it's mostly matter of preference.
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Pekka Nurminen wrote:
> "Krice" <paulkp@mbnet.fi> wrote in message
> news:1126383316.355710.49390@g43g2000cwa.googlegroups.com...
> > Jeff Lait wrote:
> >> Having ITEM::eat() trigger a giant switch statement is much more
> >> preferrable than having an equal number of ITEM subclasses
> >
> > Btw, is ITEM::eat(NPC) better than NPC::eat(ITEM)? Or does that
> > matter?:)

I mostly did everything as NPC::actionFoo(). I think I did the
opposite for actionEat() where NPC::actionEat() triggers
ITEM::actionEat(), but that likely was in a fit of insanity :>

> There is also third option - Rules::eat( NPC, ITEM ).
>
> E.g. in nethack, you need to know details of both NPC and ITEM
> instances to know result of different actions.
>
> So I would prefer this third option - keep rules elsewhere.

This can also be modelled as ::eat(NPC, ITEM). Which is very much
returning us to the C world of Roguelikes, no?

> Of course, this does not mean that eat function needs to be just
> one big switch statement, you can add dispatching.

I much prefer one big switch statement as it makes the ugliness readily
apparent. Ideally you factor away the special cases until you don't
have one big switch statement anymore, but a series of orthogonal
tests.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Seco One wrote:
> Jeff Lait spoketh thus:
>
> > How do you know to send OPEN_DOOR and not MELEE_ATTACK? Is it not
> > because you have already determined it is a door, and thus the logical
> > action is auto-open?
>
> Presumably, the monsters in your game do not go around blindly bumping
> into things and waiting to see what happens.

Of course not.

> Ambiguous bumping only exists for the player. Better to have one bump

Why? I find it a lot easier to write the AI code if I can have my
creatures move with the same code path as the player uses. I don't
need to write open door AI because that is built into the actionBump().

> function which chooses among discreet actions than a DoorBump(),
> MonsterBump(), WallBump() etc. Monsters, of course, dont need to bump
> at all, because their AI should decide what they mean to do, not the
> bumping code.

Bumping code *is* AI. After we develop an effective DWIM for the
player, why should we throw it out when moving our monsters about?
Obviously, we don't use it all of the time. But, when we just want the
monster to "take a random step" isn't actionBump exactly what we want
it to do?

> Once you know what action is intended and on what, all you need is to
> decide where you want to place the function, as Door.Open(opener),
> Monster.Open(door), or DoorOpen(x,y,opener). Using the latter option
> allows you to, as you suggested, keep your door opening code all in one
> place.

I wasn't suggesting the latter option. I was suggesting
Monster.actionOpen(direction). This would then do the appropriate
error checking ("That is not a door!") and so forth, just as if the
player explicitly chose the Open command and gave that as the
direction.

The AI should, IMHO, only affect the world through the same set of
commands that the player uses. This does *NOT* mean dispatching "hjkl"
keypresses. It could, but that's likely more work than necessary. It
means triggering the same methods as the player triggers when the
player presses "hjkl". This lets you put all of your sanity tests in
one location. It lets you ensure that the monster and player are
always capable of performing the same sets of actions. It lets you
write *one* actionBump for both the player and the monsters.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Radomir 'The Sheep' Dopieralski wrote:
> At 12 Sep 2005 06:47:52 -0700,
> Jeff Lait wrote:
>
> > The AI should, IMHO, only affect the world through the same set of
> > commands that the player uses. This does *NOT* mean dispatching "hjkl"
> > keypresses. It could, but that's likely more work than necessary. It
> > means triggering the same methods as the player triggers when the
> > player presses "hjkl".
>
> Why?

Why what? I made two points there :>

My goal here is to ensure there is only one code path for creatures
doing things. Picking items off the floor should use the same code
path regardless of who or why the creature is picking items off the
floor.

> > This lets you put all of your sanity tests in
> > one location. It lets you ensure that the monster and player are
> > always capable of performing the same sets of actions.
>
> Is it necessary?

Is what necessary? Putting all of your sanity tests in one location?
Having monsters and pcs able to do the same things? Neither is
necessary, of course.

However, by having all of your sanity tests in one location you can
have mistakes in the AI look like the creature trying to do something
stupid, rather than in a hard crash.

By having the monsters and PCs able to do the same things, you can
consolidate the PC and monster powers where applicable. Please note
that I am not saying that the *game* should allow monsters and pcs to
do the same things. I'm just saying that the code should allow it, so
the inability for the player to cast the monster spell is because the
player lacks the monster spell, not because the monster spell code
can't take a player as a caster.

The big thing I don't like with traditional C approaches to roguelikes
is that they usually build a huge divider between the monster and the
player. This usually results in two separate functions, "uhitmons" and
"monhitsu" where there should, IMHO, be only one, "monshitsmons".

> > It lets you
> > write *one* actionBump for both the player and the monsters.
>
> Do mosnters need actionBump?

I thought I had explained this?

actionBump is a useful AI tool. It's like having an actionPathfind()
that moves the creature onestep towards a goal position.

For the very reason actionBump is useful to the player, it is useful to
the AI writer. The AI writer doesn't have to say: "Check to see if
there is a door, and I can open it, or if there is a boulder, and I can
push it, or if...", they can use the same DWIM that the player can use.

I think the AI should be dealing with the decision making, not the
mechanics of play. As such, it seems logical to provide the AI the
same set (or an even larger set!) of tools for invoking the mechanics.
The AI writer thus may decide not to use actionBump, but instead use
more specific actionWalk, actionAttack, or what not. OTOH, the AI
writer may want even higher level primitives that embed a series of
tests, much like the actionBump. For example, in POWDER I have a
higher level primitive for a creature to stop itself from turning to
stone. This will check for the possibility of each method of
de-stoning and then apply it if possible. Obviously, this meta
command, unlike actionBump, shouldn't be exposed to the user at any
point as it kind of defeats a portion of the game.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Radomir 'The Sheep' Dopieralski wrote:
> At 12 Sep 2005 08:42:35 -0700,
> Jeff Lait wrote:
>
> > Radomir 'The Sheep' Dopieralski wrote:
> >> At 12 Sep 2005 06:47:52 -0700,
> >> Jeff Lait wrote:
> >> >
> >> > The AI should, IMHO, only affect the world through the same set of
> >> > commands that the player uses. This does *NOT* mean dispatching "hjkl"
> >> > keypresses. It could, but that's likely more work than necessary. It
> >> > means triggering the same methods as the player triggers when the
> >> > player presses "hjkl".
> >> Why?
> > Why what? I made two points there :>
>
> Well, I meant "Why should it use the same set of commands." In my personal
> opinion, only the set of the most primitive, "atom commands" should be
> shared. The higher-level commands, like the discussed "bump", are in fact
> a kind of "sugar" for the UI or AI, making it easier to write
> behavior-calculating functions and preventing mass suicides of users.

I'm not sure how we got ourselves into this argument, as I think we do
both agree on those points :>

I had originally brought up the bump action with respect to players.
It was then asked why monsters should have a bump action. I went on to
justify that, as, in my case, I do use bump for monsters.

I'd still contend some sugar, like actionBump, should be shared. Even
actionPathFind is a good candidate for sharing, as the users of
Darshan's travel patch would agree... However, I'm not that interested
in arguing the merits of one particular bit of sugar over another,
except to say that actionBump has proven useful to me in practice.

> > My goal here is to ensure there is only one code path for creatures
> > doing things. Picking items off the floor should use the same code
> > path regardless of who or why the creature is picking items off the
> > floor.
> >> > This lets you put all of your sanity tests in
> >> > one location. It lets you ensure that the monster and player are
> >> > always capable of performing the same sets of actions.
> >> Is it necessary?
> > Is what necessary? Putting all of your sanity tests in one location?
>
> No, that's pretty obvious for me. ;)
>
> > Having monsters and pcs able to do the same things? Neither is
> > necessary, of course.
>
> This one.
>
> > However, by having all of your sanity tests in one location you can
> > have mistakes in the AI look like the creature trying to do something
> > stupid, rather than in a hard crash.
>
> Or you get the hard crash in the creature's UI code instead of the game
> mechanics code, which is also good because you've got the error where it
> really is.

And in any case you shouldn't be crashing on unexpected input.

> > By having the monsters and PCs able to do the same things, you can
> > consolidate the PC and monster powers where applicable.
>
> I'm not sure I understand what you mean here. It's probably because of
> my poor English. Could you please describe it in other words?
>
> I presume by "consilidate" you mean:
> consolidate v.
> 3: bring together into a single whole or system; "The town and
> county schools are being consolidated"

That is correct. If we have a system for reducing fire damage
according the resistances and vulnerabilities, it is best to use the
same system for monsters and players rather than writing two differen
systems.

> > Please note
> > that I am not saying that the *game* should allow monsters and pcs to
> > do the same things. I'm just saying that the code should allow it, so
> > the inability for the player to cast the monster spell is because the
> > player lacks the monster spell, not because the monster spell code
> > can't take a player as a caster.
>
> Right, you set the rules on the level of game, not the level of engine.
> That's understandable. But why would you want to have the same set of
> rules to choose from for both of them. Game-wise they are totally
> different objects.

I think we see things differently here. I agree with your "Game master
With Token" analogy. However, in my view, monsters and the PC are then
the same objects. The player gets one token to play with (the @) and
the game master gets a whole bunch of tokens (a-zA-Z). The @ and the
B are thus the same objects, so should accept the same sets of
commands.

This also makes polymorph like effects seamless.

The game master may chose to ignore some of its potential actions to
give the @ an advantage. The @ token may lack the attributes to let
the player invoke the abilities the game master invokes on its tokens.

> > The big thing I don't like with traditional C approaches to roguelikes
> > is that they usually build a huge divider between the monster and the
> > player. This usually results in two separate functions, "uhitmons" and
> > "monhitsu" where there should, IMHO, be only one, "monshitsmons".
>
> It allows you to implement totally different mechanics as seen from the
> player's and from the monster's perspective. Separates nicely two totally
> different special cases.

I can't see why I'd want to do that.

Let's say a creature gets hit with fire damage. If it is a PC, we want
to ensure its fire resistance is checked. But what if it is a monster?
Do we check the monsters fire resistance? Do we have "fire
resistance" behave differently for monsters than PCs? If we have it
behave the same, why'd we go to the work of separating the two?

One thing I like about roguelikes is that they can build consistent
worlds. I'm really a simulationist at heart, so like the idea of
discovering new interactions. If that kobold is wearing an amulet of
reflection, it should reflect my magic missile in the same way that if
the roles were reversed.

Overall, a *very* small portion of my combat code differs depending on
whether it is a monster or a PC. This can easily be handled by putting
a:
if (attacker->isAvatar())
check in.

> You don't want to give both monsters and player
> characters equal chances in your game, do you?

Of course not. You have to load the dice on the side of the monsters
for them to have a chance against players :>

> >> > It lets you
> >> > write *one* actionBump for both the player and the monsters.
> >> Do mosnters need actionBump?
> > I thought I had explained this?
>
> Somehow I'm not satisfied with your explanation.
>
> > actionBump is a useful AI tool. It's like having an actionPathfind()
> > that moves the creature onestep towards a goal position.
>
> I agree that it's useful to have a bunch of such "higher level" functions
> handy, both for AI and for UI programming. I just think the sets of
> "useful" functions are different for these two uses, and the actionBump
> in particular is too specialized for UI to be used in AI.

Depends on your implementation of actionBump. YMMV.

This is all off on a tangent anyways. I was trying to point out that
you can't write an actionBump (which you need *at least* for the
player) without first determining whether the target was a door or not.
Thus, there is nothing to be gained by abstractiong ActionOpen so it
can be called blindly as you don't need to call it blindly - you only
need to call it when you know what to call.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Radomir 'The Sheep' Dopieralski wrote:
> At 12 Sep 2005 11:11:37 -0700,
> Jeff Lait wrote:
>
> > Radomir 'The Sheep' Dopieralski wrote:
> >> At 12 Sep 2005 08:42:35 -0700,
> >> Jeff Lait wrote:
> >> > By having the monsters and PCs able to do the same things, you can
> >> > consolidate the PC and monster powers where applicable.
> >> I'm not sure I understand what you mean here. It's probably because of
> >> my poor English. Could you please describe it in other words?
> >>
> >> I presume by "consilidate" you mean:
> >> consolidate v.
> >> 3: bring together into a single whole or system; "The town and
> >> county schools are being consolidated"
> > That is correct. If we have a system for reducing fire damage
> > according the resistances and vulnerabilities, it is best to use the
> > same system for monsters and players rather than writing two differen
> > systems.
>
> Provided you want a single system in the first place.

Even if you don't want a single system, this makes it easier to factor
out the code that *is* similar. Usually there is a fair bit of
sameness.

> >> > Please note
> >> > that I am not saying that the *game* should allow monsters and pcs to
> >> > do the same things. I'm just saying that the code should allow it, so
> >> > the inability for the player to cast the monster spell is because the
> >> > player lacks the monster spell, not because the monster spell code
> >> > can't take a player as a caster.
> >> Right, you set the rules on the level of game, not the level of engine.
> >> That's understandable. But why would you want to have the same set of
> >> rules to choose from for both of them. Game-wise they are totally
> >> different objects.
> > I think we see things differently here. I agree with your "Game master
> > With Token" analogy. However, in my view, monsters and the PC are then
> > the same objects. The player gets one token to play with (the @) and
> > the game master gets a whole bunch of tokens (a-zA-Z). The @ and the
> > B are thus the same objects, so should accept the same sets of
> > commands.
> >
> > This also makes polymorph like effects seamless.
>
> True, it's a great advantage for NetHack-like games trying to be
> "complete" and consistent. But you don't always want such a game.

Agreed. It is a rare day in rgrd when someone says they *don't* want
to make a complete, be-all, game, that encompasses every other game :>

For a 7drl, this strategy is definitely overkill, for example. Or for
any highly asymmetrical game, like Z-day.

> > The game master may chose to ignore some of its potential actions to
> > give the @ an advantage. The @ token may lack the attributes to let
> > the player invoke the abilities the game master invokes on its tokens.
>
> Both systems seem to have the same features available to implement. But
> like with Turing-complete programming languages, the key is the amount
> of work needed to implement given feature.
>
> With the "consistent" approach you are likely to resign from features
> that are not universal and don't translate to form pc's actions to
> monster actions and vice versa wery well.
>
> Take that "time warp" attack mentioned in the monster attack effectcts
> thread. It's supposed to send the player character back in time to the
> moment when he entered given level. Trivial if you've got seed-based level
> generation. But how would you implement it for monsters?

Easy. I'd have time rerolled to when the player first entered the
level. Note that the spell is still PC-centric, but can be cast by
either monster or PC. Indeed, it would be quite frustrating if it was
used as an almost-dead spell by a big baddy.

I'm also quite willing to throw out the consistency for gameplay! The
Identify spell, for example, isn't really castable by monsters (as, for
memory reasons, I only store identification info for the avatar). The
fact remains that *most* design is consistent. If I want a cool effect
that voids consistency, I won't lose sleep about it.

> The consistency is usually pretty expensive for small systems (altrough
> it quickly pays back when the system grows). I think it's easier to make
> an interesting roguelike game if you don't try to write a simulation.

I agree with you. I'm a reformed simulationist though, so please
forgive me the layers of simulation I insist on adding :>

> >> > The big thing I don't like with traditional C approaches to roguelikes
> >> > is that they usually build a huge divider between the monster and the
> >> > player. This usually results in two separate functions, "uhitmons" and
> >> > "monhitsu" where there should, IMHO, be only one, "monshitsmons".
> >>
> >> It allows you to implement totally different mechanics as seen from the
> >> player's and from the monster's perspective. Separates nicely two totally
> >> different special cases.
> >
> > I can't see why I'd want to do that.
> >
> > Let's say a creature gets hit with fire damage. If it is a PC, we want
> > to ensure its fire resistance is checked. But what if it is a monster?
> > Do we check the monsters fire resistance? Do we have "fire
> > resistance" behave differently for monsters than PCs? If we have it
> > behave the same, why'd we go to the work of separating the two?
>
> Stop. You're bending your game rules to your code design (or maybe you
> just chosed the right code design for the rules you envisioned).
> It doesn't have to behave the same.
>
> In fact, if you look at all those "just for fun" tabletop games, you'll
> notice there's rarely any symmetry.

Yes, I'm afraid I'm a simulationist, as I mentioned :>
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

At 12 Sep 2005 06:47:52 -0700,
Jeff Lait wrote:

> The AI should, IMHO, only affect the world through the same set of
> commands that the player uses. This does *NOT* mean dispatching "hjkl"
> keypresses. It could, but that's likely more work than necessary. It
> means triggering the same methods as the player triggers when the
> player presses "hjkl".

Why?

> This lets you put all of your sanity tests in
> one location. It lets you ensure that the monster and player are
> always capable of performing the same sets of actions.

Is it necessary?

> It lets you
> write *one* actionBump for both the player and the monsters.

Do mosnters need actionBump?

--
Radomir `The Sheep' Dopieralski @**@_
(Qq) 3 Sob?
. . . ..v.vVvVVvVvv.v.. .
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

Jeff Lait a écrit :
>>There is also third option - Rules::eat( NPC, ITEM ).
>>
>>E.g. in nethack, you need to know details of both NPC and ITEM
>>instances to know result of different actions.
>>
>>So I would prefer this third option - keep rules elsewhere.
>
>
> This can also be modelled as ::eat(NPC, ITEM). Which is very much
> returning us to the C world of Roguelikes, no?

Well, Rules::eat( NPC, ITEM ) is in fact called like that :
NPC->map->dungeon->planet->universe->rules.eat( NPC, ITEM ):

;)
 
G

Guest

Guest
Archived from groups: rec.games.roguelike.development (More info?)

U¿ytkownik "Pekka Nurminen" <pekka.nurminen@nospam.pp8.inet.fi>
napisa³ w wiadomo¶ci news:siIUe.307$ty5.76@read3.inet.fi...

> Of course, this does not mean that eat function needs to be just
> one big switch statement, you can add dispatching.

How do you get rid ot the switch statement by adding dispatching?

regards,
Filip