This problem seems to also go back into the days of Interactive
Fiction. How much interaction? And how do you define that interaction?
Perhaps a list of verbs associated to each object (along with a list of
properties) is the way to go about doing this. I am interested in
knowing how others have solved this problem before.
I guess having flags for material, shape and some other properties is
what you are looking for.
Yes, that is a good start. As you continue to add properties to
objects, it becomes a nightmare to update all the ways you handle the
different objects and properties. I think that a way to define how to
handle the various types of objects is what could be used here.
And again, more problems arise. How do you handle a weapon versus a
piece of armor? They both have a material type, volume, weight, mass,
and different ways to use them. The natural tendency in the roguelike
developer is to make a shield a shield and a sword a sword, though both
objects can inflict pain on the average opponent.
Suppose there were a few more variables per type of item defining the
characteristics when used for:
1. Offense
2. Defense
3. Not (`equipped`/on the person)
4. Idle (on the ground)
This way, each case can be broken down into further scenarios and allow
more generalized variables to come forth, which each type (of object)
would define further. How about that?
That's pretty much all I can think to say. I hope that helps.
For instance, if an attack has a damage type of fire, the material type
(and various other variables) of the target is checked. If the material
type is stone, it doesn't matter if the target is a wall, a golem, or
whatever; the interaction of fire against stone is called. If a player
wants to wield a sock, they're free to, but they're probably isn't
going to be an entry in the map for 'Melee Damage', so the player will
do zero melee damage while wielding it (no entry in map = 0).
For the item data files, I do it something like this:
Name Num Field 1 Num Value 1 Num Field 2 Num Value 2
Dagger Damage 1 Weight 5
Plate Mail Defense 8 Weight 60
Long Bow Range 6 Damage 3
The loader simply uses the 'Num Field *' column values as the indices
in the map, and the 'Num Value *' column values as the appropriate
values. The loader also allows an arbitrary number of columns, so
adding new properties to items is relatively painless.
Can-do.
> Another thing you could do is a have a single command to invoke an
> item's special properties (like 'A' for activate), because scrolls and
> potions tend to do the same thing even if the perceived type is
> different.
Yes, there should be a way to invoke the special properties of an item.
However, the only way that might work is through scripting each
object's specialized action. A basic script could be inherited from the
'Weapon' parent class, which is then specialized further by the 'Sword'
class. The other option is to build a system of states and
variable-checkers that has gone unmentioned yet. This sounds much like
what you have suggested, but again, it has a few problems:
1) Tedious.
2) Reward decreases as number of and magnitude of variation in items
increases.
3) ???
4) PROFIT!!
((In hindsight I wonder if #4 will result in my auto-banning. No doubt
due to the excess spammers found throughout the deepest, darkest,
hairiest parts of usenet..))
> You don't actually need to have the items any different in
> your game.
Let's assume otherwise. Their representation needs to be different
(let's assume so), though it is unimportant how the item is handled by
the engine behind the scenes. To steal your example, there is a scroll
of confusion and a potion of confusion. The potion of confusion is in
some type of a container. The scroll of confusion is paper. You can
actually throw the container of the potion at something. Throwing the
scroll at something is useless and anybody who suggests otherwise
should consider checking into a hospital. Never the less, it is still
possible to throw the scroll. The quality of the effect on whatever it
is thrown at is of importance. In this instance it is zero.
> Just a have a flag that determines what effect water has on the item.
Flagging everything seems to be going in the wrong direction. As I
brought the idea of scripting into this (see above), it can be taken
further. The scripts could be used to specify how the items behave
under certain intentional uses. If a crude AI is to be implemented
later, these functions (determining the behavior) would also produce
reports about the quality of each action per item such that the AI can
select the best option given the situation and other environment
variables.
And now for some poorly timed and poorly placed clarification:
The original intentions were to ignore the presentation of the
system/engine that the players are so familiar with. In its place, the
focus of attention would be at the core functionality of the universe
the game is supposedly representing. Indeed, it only has `meaning` once
the player starts reading and playing. What then is needed here is some
way to classify the relationships between those items/symbols/objects
in order to associate appropriate and applicable behaviors. My
suggestion (see above) is the use of scripted functions per object,
inherited from a hierarchy. Later, the problem of presentation comes
forth.
Interesting way to go about it. I like it so far.
1) In your design, should there be a couple STL maps of their own per
class that inherits from the base class?
2) What's wrong with that solution? Surely it's not perfect. No offense
intended, of course.
I do have a few added for inherited objects. For instance, characters
have an 'equipped' map, with pointers to Items. Walls and such can't
really equip items, so there isn't really any point having that in the
base class. But, for the most part, not much is usually needed.
The base class also includes a few other maps, such as:
- Abilities - a map containing Ability class objects (most things are
handled through abilities)
- Advantages/Drawbacks - similar to Abilities, but of the type
you-have-it-or-you-don't (so they can't be improved)
These allow for a wide variety of terrain, items, and characters.
> 2) What's wrong with that solution? Surely it's not perfect. No offense
> intended, of course.
As with most any solution of this complexity, there are downsides.
Speed could be an issue, since searching for a value in a map is always
going to be slower than taking a value directly from a POD. You also
need to be careful about how you retrieve values from a map. Doing
this:
monster.stats["Health"] -= item.stats["Damage"];
can be somewhat problematic. You first need to ensure that the item
stats map has an entry for "Damage" (using the find() method for maps);
otherwise, an empty entry is added to the map, which can cause minor
issues. You also have to make sure you spell the index properly every
time its used, since they're just string values, and the compiler won't
recognize when they're misspelt (as it would with a misspelt variable).
This can be mitigated with consts/#defines, but its something to keep
in mind.
I'm sure there are others, but these are the ones I can think of off
the top of my head.
This way is something you want to do with complex objects. You
can also build a "partial" class structure like I did basically with
all objects the player can handle the same way (not including terrain).
It's a shame that I didn't know about inheritance and virtual
mechanism back then, when I tried to do the same thing
without OOP. It's certainly possible, but leads to a hopeless
mess in the source code.
You have me already interested in your design. Care to elaborate on
what you mean by `abilities`? We all have our different ways of
approaching this problem and so far yours is the most foreign to me. I
am guessing that the way that you handle these Ability objects is
related to how you make everything `work` and fit in the end.
> Speed could be an issue, since searching for a value in a map is always
> going to be slower than taking a value directly from a POD.
I doubt you mean Ping of Death. What are you reffering to? I am
unfamiliar with the term POD.
> I'm sure there are others, but these are the ones I can think of off
> the top of my head.
Adding columns in a map -- and I'd call it a table due to most of my
solutions involving databases such as MySQL -- where some objects do
not need them is also a problem. Here's what I'm thinking now, if
anybody minds to take a look:
----------------
Structure for an in-game object
| (integer) identification number
| (string) name
Structure for specific attribute types:
| (integer) identification number
| (string) name
| /* possibly */ (string) description or documentation points
| (integer) variable type
Structure for an attribute for the in-game objects:
| (integer) identification number
| (integer) object's identification number
| (integer) attribute's identification number
| (???) value
----------------
This way, there is no field in the map which a specific object would
not fill out. Of course, this asumes that the map does not have objects
with all the same variables. The speed of this database-approach versus
the STL-map needs further analysis.
First, it seems that there would be more calls to access the database
in a version which implements the table structure. On the other hand,
it does not require a database per-say, but rather simply that
structure. That leads to a problem of using some sort of array that has
too many entries to sort through fast enough. The map-approach's
disadvantage seems to be that of having unused variables for an object
sometimes (unless all objects within a particular map are required to
have all variables available be relevant to the object).
Thus this message has gone full circle -- but hopefully some progress
was added to the discussion.
Well, I have a file containing various abilities, which will be used to
describe items, 'spells' (although, I don't really have them in my
roguelike), character's inherent abilities, etc. For instance, there is
a 'Claws' ability, which will probably be used for an animal's basic
attack, a sword, or most other 'sharp' type attack. Their ratings with
the Claws ability will simply be set differently. Heat Vision will be
used for a character's heat vision ability, or to be used for a laser.
Shrinking (whereby a character can reduce their size) would be used for
a character smaller than a normal human, like a halfling, a cat, etc.
> I doubt you mean Ping of Death. What are you reffering to? I am
> unfamiliar with the term POD.
POD stands for Plain Old Data. For the most part, this refers to the
built in data types for a programming language (int, float, bool). If
you store 'hitpoints' in an int variable in some class, its going to be
retrieved much faster than if you try to retrieve a value from a map (a
search of the map is involved, of course).
> Adding columns in a map -- and I'd call it a table due to most of my
> solutions involving databases such as MySQL -- where some objects do
> not need them is also a problem. Here's what I'm thinking now, if
> anybody minds to take a look:
At the program initialization, I load the data from the files, massage
it a bit, and store it. I'm not searching through the files after this
point. The data is stored as a table in the files (a tab-delimited
file), but is stored in maps once loaded.
When the file is loaded, the loader is smart enough to recognize a
blank value, and not add it to the object. So, if the file looked
something like this:
Name Num Field 1 Num Value 1 Num Field 2 Num Value 2
Dagger Min Damage 1 Max Damage 4
Plate Mail Defense 8
Long Bow Range 5
the entry for dagger would have a map that contained two entries (Min
Damage and Max Damage), plate mail would have one map entry (Defense),
and Long Bow would have one (Range). Now, if I needed another numeric
value for Dagger, say Melee Combat Bonus, I would just append two new
columns on the end (Num Field 3 and Num Value 3), and add the necessary
values for the dagger entry. The Plate Mail and Long Bow entries
wouldn't really be affected.
When I want to retrieve some value from an object, say the defense
value, I would do something like:
item->getNumValue("Defense");
which invokes something like:
int Item::getNumValue(const std::string &name)
{
if (this->numValues.find(name) != this->numValues.end())
return this->numValues[name];
return 0;
}
If the item has entry in its map for defense, the value is returned.
Otherwise, a 0 is returned. Plate Mail would return 8, Long Bow and
Dagger would return 0. Objects only have an entry in the map if it is
needed.
When I approached my design, I was concerned mostly with how game
events are managed, and how different objects may be interested in
observing, altering, or participating in those events.
When attacking with a weapon, some effects may be interested in
altering the effect - a fire enchantment would add fire damage. When
attempting to open a chess or disarming a trap, a spell may help the
odds, or worsen them. Guards would always be interested in attacks
that take place.
A more simple system of flags, or accumulators that are added could
work for damage, and special cases could work for the others, but I
wanted something flexable so that an enchantment could have whatever
complex logic it needed to determine if it applied, and by how much.
To handle all of this, I started with abstracting the game events into
Actions. Almost all game-state changing events happen though Actions -
so opening a door, attacking with a weapon, and receiving damage would
all be considered Actions.
All of these actions happen in three phases: Polling, Applying, and
Notifying.
Polling happens first - like someone attempts to swing a weapon.
Interested objects or effects could alter the attributes when swinging
to give it more or less damage. Some actions can be canceled
completely (casting a spell, picking up something, or dropping, etc).
Applying then occurs if the action was successful. The action itself
contains this logic, though the targetted object of the action can
override it.
Notifications then finally happen - any object interested in an action
that was completed will get notified - such as a guard who wants to be
alerted to things taking damage. The participants (the target of an
attack, or something that was just opened) receive a special version so
less routing takes place, and since most objects will only be
interested in actions they participate in.
Objects in the game listen, through an observer style pattern, to these
Actions. Each object registers itself with it's container (another
object, or the module), so that it can be alerted to different phases
of the Action. It uses a naming convention for the registering so that
it's mostly automatic. Since it registers, and fires off events, it's
pretty efficient.
This technique has offered a lot of power, and simplicity once it's up
and running. Adding new actions is very easy and direct, and it is
very simple to make a character "interested" in something taking place.
The biggest drawback I can think of is initial design complexity.
Making all of the objects, and contained objects, register properly,
and have useful parameters passed to them (instead of having to
dynamically cast) has been a challenge. Also, there may be some
limitations I run into if some events require polling to take place in
different types of ordered manners. These are likely to be minor
issues though.
As an added benifit, this also allows attaching Plots to various
objects - the plot then receieves all the notifications it is
interested in for those objects. After these Plot Listeners are
attached, the main plot just filters events and responds accordingly.
For example, when a character is "talked" to, the plot would intercept,
and make it tell the plot-specific text. If it was a
kill-someone-for-me type plot, the vicitim would have it attached as
well. When the vicitim is killed, the plot would be aware of it, and
be able to proceed to the next step. When completed, the plot removes
itself from all objects.
This allow the logic for the plots to be self-contained, and fairly
generic - choosing who and what are involved in the plot now become
simple parameters, which also opens up the possibility for random
plots.
I have the infrastructure for this system mostly completed, and am
still working on polishing, and an actual game. It's done in C# 2.0,
so that's a drawback to some of the portability, but the reflection is
what made it so possible. Lua is another good language for this
though, and I have some of it prototyped in that. The lack of
compile-time checking makes scripting a bit dangerous for a bug-free
app though.
You don't have thousands of items until the day you actually implement
thousands of items. Why to fret about this when you are just starting to
build your game - lets face it: if you need to ask this, then you (like
90% of others) will need to redo huge chunks of your game as you make it.
Instead if you do a framework for this now, you are still likely to redo
much of it, the only exception being that what you are redoing has been
absolutely no use thus far. Code only when you need to. Refactor often.
Optimization is root of all evil. Applies to design as well.
You assume too much. I am not building a game at the moment. (Sorry!)
Otherwise the advice would probably apply. Especially if building a
roguelike in 40 hours -- see the article on [1]. Thanks for the
software engineering advice, though it seems like your sort of advice
is rare. Suspect a debate -- but not with me, not here, not now. ;)
*Premature* optimisation is the root of all evil.
--
Martin Read - my opinions are my own. share them if you wish.
\_\/_/ http://www.chiark.greenend.org.uk/~mpread/dungeonbash/
\ / "tempted white eyes blinded by the night hollow like the towers from the
\/ inside laura's a machine she's burning insane" fields of the nephilim
Do you have a fixed set of flags and accumulators for all Actions, or
can different types of Actions have different flags and accumulators?
It would be easiest to achieve the latter if you access the flags and
accumulators through a small set of methods that accept an argument
to indicate which flag or accumulator the operation is being applied
to.
> Notifications then finally happen - any object interested in an
> action that was completed will get notified - such as a guard who
> wants to be alerted to things taking damage. The participants
> (the target of an attack, or something that was just opened)
> receive a special version so less routing takes place, and since
> most objects will only be interested in actions they participate
> in.
>
> Objects in the game listen, through an observer style pattern, to
> these Actions. Each object registers itself with it's container
> (another object, or the module), so that it can be alerted to
> different phases of the Action. It uses a naming convention for
> the registering so that it's mostly automatic. Since it
> registers, and fires off events, it's pretty efficient.
How do objects get themselves listed as observers for each Action? Is
the observer list inside the Action or inside some game object? How
are the Actions created and how long does each action survive? By
that I mean, when the PC kicks a door, is that the same Action as
every other time he has done that, or is it a fresh Action each time?
> As an added benifit, this also allows attaching Plots to various
> objects - the plot then receieves all the notifications it is
> interested in for those objects. After these Plot Listeners are
> attached, the main plot just filters events and responds
> accordingly.
How does a Plot specify which Actions it is interested in?
> I have the infrastructure for this system mostly completed, and am
> still working on polishing, and an actual game. It's done in C#
> 2.0, so that's a drawback to some of the portability, but the
> reflection is what made it so possible.
Why is reflection important here? Theoretically reflection should be
necessary for only debugging purposes and other language-specific
operations. I think reflection only muddies the design of a roguelike
if used as part of the game logic.
Thanks. So there is common sense in rgrd ;)
> Do you have a fixed set of flags and accumulators for all Actions, or
> can different types of Actions have different flags and accumulators?
> It would be easiest to achieve the latter if you access the flags and
> accumulators through a small set of methods that accept an argument
> to indicate which flag or accumulator the operation is being applied
> to.
>
Actions are all derived from a base class, and can use whatever public
aspects of the objects it deals with. For instance, opening a door
would be something like:
Module.AddAction( new ActionOpenDoor( player, door ) );
first polling would happen, which nothing would likely be interested
in, then the actual action would apply itself, by sending an Open
command to the door, and then notifications would be sent out, which
again, would likely have none.
Most actions would have more vars that are important though - such as
Attacking would have a percent chance of hitting, which various things
may effect - such as spells, or magical items, etc. Some may want to
adjust the applying, instead of the default - such as halving certian
types of attacks, or doubling them when applying.
One other thing - some simple things, like movement, are not actioned.
Mostly because it would be too much overhead, and few things would need
immediate modfication or notifications for these. This seems more
efficent for the creatures that are interested do poll, and watch for
themselves.
> How do objects get themselves listed as observers for each Action? Is
> the observer list inside the Action or inside some game object? How
> are the Actions created and how long does each action survive? By
> that I mean, when the PC kicks a door, is that the same Action as
> every other time he has done that, or is it a fresh Action each time?
The list of actions is held in a singleton, and generated on program
startup using reflection. All of the classes that are derived from
ActionBase are added to the list as names.
Whenever an object is added to a container, it is checked for what
actions it supports. This is done by either reflection to look for
action names (PollDoorOpenAction, OnDoorOpenAction,
WatchDoorOpenAction), or using a cached list that is genereated from
the first reflection. These events are then added as a list of
delegates to the routing object, so it is basically a direct
connection, and very little overhead to call - and very little penalty
if there are none that are subscribing. And yes, a new Action is
created each time. I was considering reusing some of them, or caching
some object for common ones. It would not be that big of a change
really. But that also adds overhead for when multiple creatures in a
round want to make use of the same action. Until it's a problem, I'll
just create new objects.
All this could be done w/out reflection, but it makes the procedure for
adding new actions much more simple.
>
> How does a Plot specify which Actions it is interested in?
Plots work in the same manner - they add an PlotWatcher style object to
the object they are interested in, which gets it registered for those
events through the delegate, and basically that PlotWatcher forwards
any events it receives to the plot. At moment this is done by hand
(OnDoorOpenAction: plot.OnDoorOpenAction(args) ), but it could be made
easier if I find it a hastle. Realistically, most plots will only care
about a limited subset of actions (Being attacked, killed, changes in
the hero's module), and that object can be reused.
> > I have the infrastructure for this system mostly completed, and am
> > still working on polishing, and an actual game. It's done in C#
> > 2.0, so that's a drawback to some of the portability, but the
> > reflection is what made it so possible.
>
> Why is reflection important here? Theoretically reflection should be
> necessary for only debugging purposes and other language-specific
> operations. I think reflection only muddies the design of a roguelike
> if used as part of the game logic.
Reflection has actually made the design much cleaner, with just a itty
bit of startup penalty. Well worth the cost. It means that when I add
a new action, I do not have to do -anything- to get it to route to
objects properly. All I need to do is add the function to handle it.
This could be done without reflection of course - a set of bitflags
could be used to specify what events objects are interested in, along
with an interface for calling those events. That would work just as
well, it would just require setting those flags.
So far, it seems like a very powerful design. I may use a Group AI for
some parts - like towns, just to cutback on the amount of messages
being sent around, but so far I am very happy with the design. It
seems to have very little overhead - I have an SDL test client that's
getting almost the same framerate with and without the game engine
running.
I don't think it will be that much overhead really. I'm using
delegates to signal these events, so if nothing is interested in a
particular action taking place, there is almost no overhead (checking
if a particular array index is NULL). In situations where things are
interested in modifying the results, I think it is less overhead then
the alternatives - looping through items looking for bitflags.
Unless you mean another aspect of it being an issue.
So why not just use a struct?
struct MOB_DEF
{
u8 tile;
const char *name;
u8 movetype;
u8 attack;
DICE hp;
u8 hitdie;
DICE mp;
u8 mpdie;
...
};
This has the note worthy effect of giving you nice O(1) access times in
your source code.
printf("Tridue name is %s\n", glb_mobdef[MOB_TRIDUDE].name);
As an added bonus, all of your structure entries are named, letting the
compiler do a compile time check to ensure you don't mistype "name"
somewhere. (Note also that one does *NOT* use #defines to make the
MOB_TRIDUDE as some have recommended. enums are the right tool for
that job - you can also get some type safety added where
MOB::create(MOB_NAMES name) won't let you call MOB::create(1))
Further, the structure entries are *typed*. You don't have to worry
about using an integer as a string or vice-versa. (My own approach to
this is weaker than it should be as I use u8s rather than enums out of
concern for storage space most roguelike authors don't have)
Traditionally, the counter argument for structures is the awkwardness
of initialization. Not only is it hard to have default values, but it
is very difficult to properly fill out a line that goes:
{"tridude", 0, 32, 1, ATTACK_FOO, ...}
There is, of course, a simple solution to this problem. Use a two pass
system to transform a simple plain text definition of your mobs into
the C++ code to define the structures and fill them out.
An example of my solution to this can be found in You Only Live Once
with the enummaker program. Given a simple definition file, both the
enums, the structure definition, and the structure contents, are all
generated. You may find the program at
http://www.zincland.com/7drl/liveonce.
--
Jeff Lait
(POWDER: http://www.zincland.com/powder)
What is the purpose of a list of class names?
It would probably be very informative if you would provide a rough
sketch of some of your base classes, especially ActionBase and one
for the game objects that observe and respond to actions.
> Whenever an object is added to a container, it is checked for what
> actions it supports. This is done by either reflection to look
> for action names (PollDoorOpenAction, OnDoorOpenAction,
> WatchDoorOpenAction), or using a cached list that is genereated
> from the first reflection.
Are PollDoorOpenAction, OnDoorOpenAction, WatchDoorOpenAction all
subclasses of ActionBase and actions themselves? What do those
actions represent? It seems excessive to have more than one type of
action for opening a door.
Why is this operation performed using names instead of just
dynamically testing for the type of a given action to check if it is
wanted? I understand that this is a preprocessing step before the
game begins, but I do not see what it saves.
> These events are then added as a list
> of delegates to the routing object, so it is basically a direct
> connection, and very little overhead to call - and very little
> penalty if there are none that are subscribing. And yes, a new
> Action is created each time. I was considering reusing some of
> them, or caching some object for common ones. It would not be
> that big of a change really. But that also adds overhead for when
> multiple creatures in a round want to make use of the same action.
> Until it's a problem, I'll just create new objects.
You might want to consider using a factory of some kind instead of
constructing directly. This will allow you greater flexibility in
implementing your action objects, though I'm not sure how that will
interact with your reflection. It will at least allow you to change
the constructor for the actions without changing every occurrence
spread across your plot/monster/door code.
> Reflection has actually made the design much cleaner, with just a
> itty bit of startup penalty. Well worth the cost. It means that
> when I add a new action, I do not have to do -anything- to get it
> to route to objects properly. All I need to do is add the
> function to handle it.
I assume that you also require some changes to the objects to
indicate that they are interested in this new action; correct me if I
am wrong. Given that assumption, I fail to see how reflection is
helping you. What more would you have to do if you were not using
reflection?
> This could be done without reflection of course - a set of
> bitflags could be used to specify what events objects are
> interested in, along with an interface for calling those events.
> That would work just as well, it would just require setting those
> flags.
I would highly recommend not restricting the number and type of
possible actions by using any kind of hard mapping between bits and
actions. It would be better to give observers a method that allows
them to accept or reject an action depending on whether they think it
is important at the time.
In the end you will still have to set something on the objects that
are interested in each new action. What are you setting now? Do you
have a list of the names of the interesting actions for each object
as you seem to suggest above?
> So far, it seems like a very powerful design. I may use a Group
> AI for some parts - like towns, just to cutback on the amount of
> messages being sent around, but so far I am very happy with the
> design. It seems to have very little overhead - I have an SDL
> test client that's getting almost the same framerate with and
> without the game engine running.
In that case I recommend not worrying about efficiency at this stage.
I doubt that passing messages between game objects will be a
bottleneck for you, so you should just focus on making it as
intuitive and powerful as you can. Until you know that it is a
problem, consider doing otherwise to be premature optimization.
ActionBase is rather simple. It looks kind of like this:
class ActionBase
{
virtual void GetParticipants( List<GameEntity> list)
virtual void Execute();
}
Actual actions add more fields:
class DamageAction : ActionBase
{
public int Damage;
public GameEntity Target
override void GetParticipants( List<GameEntity> list) --> Adds
Target
override void Execute() --> Takes points away from Target
}
If target had on some armour:
class Armour : GameEntity
{
public PollDamageAction( ActionBase action)
{
action.Damage = action.Damage / 2;
}
}
The reflection's purpose is to allow the method's NAME to be used to
tell that it is interested in the action. When Armour is registered
with the character, the registration system looks up the Class Type,
and then knows it wants to modify the Poll phase of "DamageAction".
> Are PollDoorOpenAction, OnDoorOpenAction, WatchDoorOpenAction all
> subclasses of ActionBase and actions themselves? What do those
> actions represent? It seems excessive to have more than one type of
> action for opening a door.
As above, there is only one action, but methods can be named with Poll,
On, or Watch, to subscribe to that phase of the action's execution.
Opening a door probably would never need a poll-phase modification, but
there may be times where it could be useful.
> Why is this operation performed using names instead of just
> dynamically testing for the type of a given action to check if it is
> wanted? I understand that this is a preprocessing step before the
> game begins, but I do not see what it saves.
I think it's easier to just name a method to intercept those events.
It could have been implemented the other way, yes. This seemed the
most intuititve from a developing standpoint when adding content. All
and all, that isn't where the power of the approach lies though - i
think the phase system is what is most helpful.
> You might want to consider using a factory of some kind instead of
> constructing directly. This will allow you greater flexibility in
> implementing your action objects, though I'm not sure how that will
> interact with your reflection. It will at least allow you to change
> the constructor for the actions without changing every occurrence
> spread across your plot/monster/door code.
I've been thinking about using a factor actually. Reflection is only
used for the linking to the subscribing objects, so it won't effect it.
It would make testing easier as well.
> In that case I recommend not worrying about efficiency at this stage.
> I doubt that passing messages between game objects will be a
> bottleneck for you, so you should just focus on making it as
> intuitive and powerful as you can. Until you know that it is a
> problem, consider doing otherwise to be premature optimization.
I agree - Its just something I'm keeping an eye on, and trying to be
aware of. All in all it is a fairly simple change, but I'm not going
to bother unless there are enough benifits.