Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Handling Interactions?

38 views
Skip to first unread message

Shedletsky

unread,
Sep 9, 2005, 9:36:14 AM9/9/05
to
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


Radomir 'The Sheep' Dopieralski

unread,
Sep 9, 2005, 11:20:39 AM9/9/05
to
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.. .

Shedletsky

unread,
Sep 10, 2005, 11:18:32 AM9/10/05
to
> 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.


Radomir 'The Sheep' Dopieralski

unread,
Sep 10, 2005, 12:19:29 PM9/10/05
to
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

unread,
Sep 10, 2005, 12:23:09 PM9/10/05
to
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

Oskar

unread,
Sep 10, 2005, 1:37:22 PM9/10/05
to
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

Jeff Lait

unread,
Sep 10, 2005, 1:54:17 PM9/10/05
to
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)

Krice

unread,
Sep 10, 2005, 4:15:16 PM9/10/05
to
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?:)

Seco One

unread,
Sep 10, 2005, 5:28:00 PM9/10/05
to
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.

Pekka Nurminen

unread,
Sep 10, 2005, 5:33:12 PM9/10/05
to

"Krice" <pau...@mbnet.fi> wrote in message
news:1126383316....@g43g2000cwa.googlegroups.com...

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.


Michal Brzozowski

unread,
Sep 11, 2005, 4:01:11 AM9/11/05
to
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.

Shedletsky

unread,
Sep 11, 2005, 1:20:19 PM9/11/05
to
> 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.


Shedletsky

unread,
Sep 11, 2005, 1:23:31 PM9/11/05
to
> 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...


Shedletsky

unread,
Sep 11, 2005, 1:25:46 PM9/11/05
to
> 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!


Radomir 'The Sheep' Dopieralski

unread,
Sep 11, 2005, 2:19:44 PM9/11/05
to
At Sun, 11 Sep 2005 13:23:31 -0400,
Shedletsky wrote:

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!

Radomir 'The Sheep' Dopieralski

unread,
Sep 11, 2005, 2:33:45 PM9/11/05
to
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!

Pekka Nurminen

unread,
Sep 11, 2005, 2:35:01 PM9/11/05
to

"Shedletsky" <mylas...@stanford.edu> wrote in message
news:dg1p6m$1jj$1...@news.Stanford.EDU...

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.


Jeff Lait

unread,
Sep 12, 2005, 9:35:06 AM9/12/05
to
Pekka Nurminen wrote:
> "Krice" <pau...@mbnet.fi> wrote in message
> news:1126383316....@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.

Christophe

unread,
Sep 12, 2005, 9:50:42 AM9/12/05
to
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 ):

;)

Jeff Lait

unread,
Sep 12, 2005, 9:47:52 AM9/12/05
to
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.

Filip Dreger

unread,
Sep 12, 2005, 9:54:01 AM9/12/05
to
Użytkownik "Pekka Nurminen" <pekka.n...@nospam.pp8.inet.fi>
napisał w wiadomości news:siIUe.307$ty5...@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


Radomir 'The Sheep' Dopieralski

unread,
Sep 12, 2005, 10:31:42 AM9/12/05
to
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?

Jeff Lait

unread,
Sep 12, 2005, 11:42:35 AM9/12/05
to
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.

Pekka Nurminen

unread,
Sep 12, 2005, 12:07:41 PM9/12/05
to

"Jeff Lait" <torespon...@hotmail.com> wrote in message
news:1126532106.8...@g44g2000cwa.googlegroups.com...

> Pekka Nurminen wrote:
>> "Krice" <pau...@mbnet.fi> wrote in message
>> news:1126383316....@g43g2000cwa.googlegroups.com...
>> 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?

You can pass through instance (rules::eat or whatever) - especially
if you do not want to use singletons / global variables.

I think it's not same as going back to C world. You can use instances
to represent different effects, scheduling of actions etc.
OOP does not mean that you have to use inheritance everywhere.


Radomir 'The Sheep' Dopieralski

unread,
Sep 12, 2005, 12:11:14 PM9/12/05
to
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:

I'm sorry I'm so dense today, I'll try some more constructive discussion
now ;)

>> > 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.

> 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.

> 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"

> 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.

> 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. You don't want to give both monsters and player
characters equal chances in your game, do you?

>> > 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.

> 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.

But the monsters can be slighly different than the player character, and
thus need different actionBump functions. For example, what if your
monster cannot open doors? Will it be bumping at them endlessly (because
itfigured out it'd be fater to open it than to take any other escape
route)?

It's also a dangerous trap. Suppose you use the actionBump in both your
AI and UI code. You like the way how you can open the door on bump, so
you add another option known from Angband -- disarm traps on bump.
Suddenly there's hardly any trap to try your new UI feature on, because
the monsters have disarmed all of them already...

> 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.

See? Similar technique, but totally different set of needs.

--
Radomir `The Sheep' Dopieralski @**@_

(Xx) 3 ...

Pekka Nurminen

unread,
Sep 12, 2005, 12:13:34 PM9/12/05
to

"Christophe" <chris.c...@free.fr> wrote in message
news:4325863d$0$958$636a...@news.free.fr...

That would be quite against law of demeter. :)

No, it starts from "brain":

rules.eat( NPC, ITEM );

Well, eat method might require some additional parameters.
like if you have cave filled with water and PC cannot eat underwater,
then you need to pass level & position or tile name additionally.


Pekka Nurminen

unread,
Sep 12, 2005, 12:15:58 PM9/12/05
to

"Filip Dreger" <fdr...@amiga.pl> wrote in message
news:dg41e0$ggo$1...@nemesis.news.tpi.pl...
E.g. each case can be represented as an instance.


Jeff Lait

unread,
Sep 12, 2005, 2:11:37 PM9/12/05
to
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.

Radomir 'The Sheep' Dopieralski

unread,
Sep 12, 2005, 2:58:26 PM9/12/05
to
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:
>> > 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 :>

Sorry about that. I took what you've written literally, and thought you're
suggesting to make the monsters use all the player-optimized commands, or
even worse, make the player use the monster-optimized ones (which can
happen if you try to reuse the helper function desperately).

<snip>

>> > 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.

>> > 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.

> 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?

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.

>> > 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.

> 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.

Right design for the right job.

--
Radomir `The Sheep' Dopieralski @**@_

(: ) 3 Snap!

Jeff Lait

unread,
Sep 12, 2005, 3:11:14 PM9/12/05
to
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 :>

Jim Strathmeyer

unread,
Sep 12, 2005, 5:06:16 PM9/12/05
to
Jeff Lait <torespon...@hotmail.com> schrieb:

> Radomir 'The Sheep' Dopieralski wrote:
>> At 12 Sep 2005 11:11:37 -0700, Jeff Lait wrote:
>> > Radomir 'The Sheep' Dopieralski wrote:

Shit, guys, that's four posts in a row each over 100 lines.

Snip and summarize!

--
Jim Strathmeyer

Radomir 'The Sheep' Dopieralski

unread,
Sep 12, 2005, 6:05:14 PM9/12/05
to
At Mon, 12 Sep 2005 16:06:16 -0500,
Jim Strathmeyer wrote:

Sorry, wasn't sure what the other party exactly means, coudn't summarize.

--
Radomir `The Sheep' Dopieralski @**@_

<..> ] 0110110?

Seco One

unread,
Sep 13, 2005, 3:19:46 AM9/13/05
to
Jeff Lait spoketh thus:

> Seco wrote:
> > Ambiguous bumping only exists for the player.
>
> 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().

Will there not also be an explicit open command or is bumping the only
way to open a door? How do you decide if a monster means to close an
open door or move onto the space? Down this path I see redundant 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?

Bumping seems more like UI to me. When the player bumps, the game then
reclassifies his move as an attack / open, etc. It is essentialy a
guess as to what action the player would be most likely to perform in a
given situation, provided for the reason of eliminating an extra
keypress or two. There is no reason to guess at what action a monster
intends to take, because the game decides this anyways.

> 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.
>

How does this make sense? Why give the monster an actionOpen() if it
has a chance of failure? Clearly it is a broken AI system if the
monster is trying to open something that isn't a door. There is no
reason to be displaying messages to computer controlled entities.

Most AI systems I can imagine boil down to this:
1.Check surroundings for things of possible interest (doors,other
monsters etc)
2.Make a list of possible actions
3.Choose one action
4.Act

If the system is well designed the AI will move through each step once
and in order. The error checking you mention would be most useful at
step 2, so that the monster is never even given the option of opening a
wall. Monsters and player alike can (IMO, should) use the same code at
step 4, but the steps 1-3 must be kept seperate. If the system is well
designed, failure is not possible at step 4.

Steps 1,2 and 3 can and often are condensed, but I would say 4 must be
kept seperate.

Bumping will only make the system more confusing, because it conflates
the roles of all four steps.

Jeff Lait

unread,
Sep 13, 2005, 10:20:31 AM9/13/05
to

Seco One wrote:
> Jeff Lait spoketh thus:
> > Seco wrote:
> > > Ambiguous bumping only exists for the player.
> >
> > 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().
>
> Will there not also be an explicit open command or is bumping the only
> way to open a door? How do you decide if a monster means to close an
> open door or move onto the space? Down this path I see redundant code.

As I mentioned to Sheep, I seem to have led the discussion far away
from what I cared about.

I was originally talking about writing bump code *for the player*.
While I still contend it is useful to be able to invoke the bump code
from the AI, it is hardly an important point. YMMV.

> > 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?
>
> Bumping seems more like UI to me. When the player bumps, the game then
> reclassifies his move as an attack / open, etc. It is essentialy a
> guess as to what action the player would be most likely to perform in a
> given situation, provided for the reason of eliminating an extra
> keypress or two.

"Guessing what the player would be most likely to perform in this
situation" is *exactly* AI. If you got a good enough guess, and
applied it to your monsters, you'd likely have AI that is too powerful
for a roguelike.

> There is no reason to guess at what action a monster
> intends to take, because the game decides this anyways.

By the "game", you mean the person writing the AI code. Said person
may not want to explicitly decide which action the monster does. Just
as bumping saves a key press or two for the player, it saves
determining what the proper action is for the AI writer.

Let me try something more concrete... When you are trapped in a pit in
POWDER, you need to Climb Up to get out of the pit. Attempting to walk
in any direction fails as you are in the pit. If one tries to bump in
a direction out of the pit, your bump command gets turned into a climb
up command to first escape the pit.

As the AI writer, I can, whenever I want to move the creatures, first
check to see if they are in a pit, and if so, get them to climb up. In
the long run, this may be the right thing to do. However, because
monsters use the same action bump as the player, I've not had to bother
with that logic in the AI code at all. The creatures properly climb
out of the pit as a result of bump action.

> > 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.
>
> How does this make sense? Why give the monster an actionOpen() if it
> has a chance of failure? Clearly it is a broken AI system if the
> monster is trying to open something that isn't a door. There is no
> reason to be displaying messages to computer controlled entities.

Displaying messages for computer controlled entities is a good way of
discovering where the AI is broken.

I don't see why you point out that it is a broken AI system that lets
monsters open non-existent doors. Of course it is a broken AI system.
Sadly, the probability is that the AI system I write will be, at some
point, broken. Much better to get a "The kobold tries to open empty
space!" than "FATAL ERROR: No such door object!!"

> Most AI systems I can imagine boil down to this:
> 1.Check surroundings for things of possible interest (doors,other
> monsters etc)
> 2.Make a list of possible actions
> 3.Choose one action
> 4.Act

The system I use is rather different.

1. Check self & surroundings for important stuff (PC in sight, am I
turning to stone, etc?)
2. Choose what high level action I want to do. (Attack the PC, stop
myself from turning to stone)
3. Invoke a high-level function that attempts that (AttackPC,
DestoneSelf), which if it returns inability to succeed, go back to step
#2.

The high level functions repeat that process until eventually atomic
move instructions are called. The idea is to keep the list of possible
actions one is trying to disambiguate as small as possible every step.

Note that step #4 is not kept separate - it is directly called by the
final high-level function that has figured out what to do. We could,
theoritically, have the high-level functions return the low-level
function that they were planning on calling, and then invoke it in a
separate big dispatcher or switch statement. But I really don't see
what that gains us.

actionBump is thus just another high level function in the toolbox of
the AI writer.

> If the system is well designed the AI will move through each step once
> and in order. The error checking you mention would be most useful at
> step 2, so that the monster is never even given the option of opening a
> wall. Monsters and player alike can (IMO, should) use the same code at
> step 4, but the steps 1-3 must be kept seperate. If the system is well
> designed, failure is not possible at step 4.

So the player is incapable of attempting anything impossible? If the
player can target a non-door for an open command, then so can the AI.

> Steps 1,2 and 3 can and often are condensed, but I would say 4 must be
> kept seperate.

By "separate", I presume you mean that there should only be one place
where step #4 is invoked?

> Bumping will only make the system more confusing, because it conflates
> the roles of all four steps.

It would under your system. However, in the top-down approach I've
taken, bumping is analogous to a "Destone Self" action. I'm sure you'd
argue adding a Destone Self action is conflating the role of all four
steps. To me, it is encapsulating a hundred lines of AI code into one
meta-action that has a bit more meaning than "drink acid potion".

Krice

unread,
Sep 13, 2005, 12:13:36 PM9/13/05
to
Jeff Lait wrote:
> If the player can target a non-door for an open command, then
> so can the AI.

What could be the reason for that? Fuzzy AI logic?:)
In some point I discovered that some actions clearly belong to
the player and no one else, so there is no need to write AI code
for that stuff, or "bypass" the routine if the npc is not the player.
It's better to have a separate routine for the player, possibly
using atomic code parts shared by all npc's.
One thing is sure. If you don't need to make the source code complex,
then don't do it. Don't use "shared" code for the npc's and the
player, don't put things in the event list if you can manage to
perform a task without it. Keep it simple.

0 new messages