Author Topic: Handling deaths/corpses  (Read 12764 times)

loom_weaver

  • Newcomer
  • Posts: 23
  • Karma: +0/-0
    • View Profile
Handling deaths/corpses
« on: May 26, 2011, 04:16:42 PM »
Hello everyone,

It's my first post at the Temple.  I'm pretty excited to find these forums.

Anyway, I'm in the middle of developing my roguelike and I'm trying to determine the best way to handle a monster's death.  I have a Mobile class and an Item class.  The two possibilities are:
1. If a Mobile dies, remove the instance from the GameState and create an Item (%) representing the corpse.
2. If a Mobile dies, flip a field i.e. Mobile.dead = true

The former makes drawing, game loop processing, and corpse manipulation a bit easier.  However, I can see some advantages with the latter once I add resurrection and monster inventories.  Do you have any recommendations?

purestrain

  • Rogueliker
  • ***
  • Posts: 172
  • Karma: +0/-0
    • View Profile
Re: Handling deaths/corpses
« Reply #1 on: May 27, 2011, 06:35:00 AM »
Hi,

my recommendation: remove the distinction between mobile/item. That makes everything a lot easier. If you can't do this i would propose the second one...

Are you able to pick up a monster like an item und put it in your bag? No? Maybe with workarounds and duplicated code? Then rethink my recommendation ;-)

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #2 on: May 27, 2011, 07:34:58 AM »
This should be quite simple. If the engine supports only items in inventory (and other uses for items) and you need corpses as items, then it's obvious they need to be items.

I've planned to change big corpses as moveable items (in Kaduria, it's an item type you can't pick up, but you can possibly push them to another location and loot etc.), because it's better for semi-realistic gameplay.

Z

  • Rogueliker
  • ***
  • Posts: 905
  • Karma: +0/-0
    • View Profile
    • Z's Roguelike Stuff
Re: Handling deaths/corpses
« Reply #3 on: May 28, 2011, 04:46:03 PM »
For me, problems like this are an argument that the traditional object oriented programming (like in Java/C++) is not suited that well for roguelike programming. Objects that change classes during gameplay, objects that share properties of several classes, objects that change properties of other objects... and so on.

TSMI

  • Rogueliker
  • ***
  • Posts: 65
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #4 on: May 30, 2011, 11:15:15 PM »
For me, problems like this are an argument that the traditional object oriented programming (like in Java/C++) is not suited that well for roguelike programming. Objects that change classes during gameplay, objects that share properties of several classes, objects that change properties of other objects... and so on.


In what world are the bastardised hybrids of java and C++ traditional OOP languages? :P They're both C-likes with a limited OO layer tacked on.

Anyway, even in those languages I don't think the above are much issues.

Quote
Objects that change classes during gameplay

Instead of changing the class of an object (which is impossible), I think it'd be better to haeve a method in monster, "die", that returns an item object (ie a corpse). You can then call the destructor itself or wait for the GC to pick it up.

Quote
objects that share properties of several classes

In java it'd be better to turn "several classes" into "several interfaces" then there isn't much issue (but here I admit my ignorance of C++)

Quote
objects that change properties of other objects

Not seeing a problem here either...what's so bad about having a method of an object operate on another object? no worse than having a function operate on a struct/record surely?

Z

  • Rogueliker
  • ***
  • Posts: 905
  • Karma: +0/-0
    • View Profile
    • Z's Roguelike Stuff
Re: Handling deaths/corpses
« Reply #5 on: May 31, 2011, 06:31:42 PM »
In what world are the bastardised hybrids of java and C++ traditional OOP languages? :P They're both C-likes with a limited OO layer tacked on.
The same applies to other class-based OO languages, like Simula or SmallTalk.

Of course all can be solved, but naive OO does not provide tools to do that.

Quote
Instead of changing the class of an object (which is impossible), I think it'd be better to haeve a method in monster, "die", that returns an item object (ie a corpse). You can then call the destructor itself or wait for the GC to pick it up.

Now suppose you want to have a Resurrection or Animate Dead spell, and the properties of the animated corpse to be based on the properties of the original dead monster. Then you should copy all the properties of the original monster to the corpse, and after that, properties of the corpse should be copied to the new monster again. Too much duplication for my taste. Also all pointers/links to the old object become invalid instead of pointing to the new one (for some reason IVAN creates a new object when you fix or break a weapon, which makes you lose all the experience with the given weapon if it is broken and fixed, which is weird). Monster death is one of the cases when an object changes into something else, you can also have a polymorph spell or something like that.

IVAN is object oriented, but I do not like the design of it (as it is both object oriented and data driven, which means that both the C++ source and the script defining objects contain definitions of the classes... not nice IMO).

Quote
Quote
objects that share properties of several classes

In java it'd be better to turn "several classes" into "several interfaces" then there isn't much issue (but here I admit my ignorance of C++)

So let's take a typical example: we have a Creature class, and the following derived classes:

Humanoid (can wield weapons and wear armor)
Equine (can be saddled and wear horseshoes)
Bird (can fly)

Now, you also want to have Centaurs, Angels, and Pegasi. They share properties of several of these classes. AFAIK Java interfaces do not help here, since they just inform the compiler that a given class of objects implements some methods, but it is impossible to share the implementation, or to know whether a given Creature object is flying (or maybe it is possible, but then I think we are using the interface feature not in the way it was designed for).

C++ is a bit better in this, since you have virtual base classes and multiple inheritance, which can be successfully used for this purpose. But these features are rather counter-intuitive (that's why they did not make it into Java), and I still expect some problems when you are e.g. trying to save a file.

Quote
objects that change properties of other objects

Let's assume you have somehow successfully managed to include Humanoids, Equines, and Birds in your game. Birds can fly and Humanoids and Equines cannot. Now you want to add winged armors and horseshoes of flying, which allow Humanoids and Equines to fly too. You also want to add the Spell of Gravity which disables the birds' fly ability.

The problem is that in roguelikes objects do not form a strict hierarchy of classes, with properties depending on the class of the given object, but rather may be changed by the environment. (Of course not all roguelikes.)

TSMI

  • Rogueliker
  • ***
  • Posts: 65
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #6 on: June 01, 2011, 01:25:29 AM »
In what world are the bastardised hybrids of java and C++ traditional OOP languages? :P They're both C-likes with a limited OO layer tacked on.
The same applies to other class-based OO languages, like Simula or SmallTalk.

Of course all can be solved, but naive OO does not provide tools to do that.

Quote
Instead of changing the class of an object (which is impossible), I think it'd be better to haeve a method in monster, "die", that returns an item object (ie a corpse). You can then call the destructor itself or wait for the GC to pick it up.

Now suppose you want to have a Resurrection or Animate Dead spell, and the properties of the animated corpse to be based on the properties of the original dead monster. Then you should copy all the properties of the original monster to the corpse, and after that, properties of the corpse should be copied to the new monster again. Too much duplication for my taste. Also all pointers/links to the old object become invalid instead of pointing to the new one (for some reason IVAN creates a new object when you fix or break a weapon, which makes you lose all the experience with the given weapon if it is broken and fixed, which is weird). Monster death is one of the cases when an object changes into something else, you can also have a polymorph spell or something like that.

IVAN is object oriented, but I do not like the design of it (as it is both object oriented and data driven, which means that both the C++ source and the script defining objects contain definitions of the classes... not nice IMO).

Quote
Quote
objects that share properties of several classes

In java it'd be better to turn "several classes" into "several interfaces" then there isn't much issue (but here I admit my ignorance of C++)

So let's take a typical example: we have a Creature class, and the following derived classes:

Humanoid (can wield weapons and wear armor)
Equine (can be saddled and wear horseshoes)
Bird (can fly)

Now, you also want to have Centaurs, Angels, and Pegasi. They share properties of several of these classes. AFAIK Java interfaces do not help here, since they just inform the compiler that a given class of objects implements some methods, but it is impossible to share the implementation, or to know whether a given Creature object is flying (or maybe it is possible, but then I think we are using the interface feature not in the way it was designed for).

C++ is a bit better in this, since you have virtual base classes and multiple inheritance, which can be successfully used for this purpose. But these features are rather counter-intuitive (that's why they did not make it into Java), and I still expect some problems when you are e.g. trying to save a file.

Quote
objects that change properties of other objects

Let's assume you have somehow successfully managed to include Humanoids, Equines, and Birds in your game. Birds can fly and Humanoids and Equines cannot. Now you want to add winged armors and horseshoes of flying, which allow Humanoids and Equines to fly too. You also want to add the Spell of Gravity which disables the birds' fly ability.

The problem is that in roguelikes objects do not form a strict hierarchy of classes, with properties depending on the class of the given object, but rather may be changed by the environment. (Of course not all roguelikes.)


Those are some good points...I won't try and defend java anymore since I don't know it well and it doesn't seem to click with how I think.

My OO language of choice is ruby, in which mixins will solve a lot of those problems (or at least make them easier to deal with) - modules can be used like "filled in interfaces" which is very handy.

I'm curious, do you have a preferred paradigm when doing these things? Or are you simply pointing out that class hierarchies aren't the silver-bullet?

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #7 on: June 01, 2011, 06:54:46 AM »
C++ is a bit better in this, since you have virtual base classes and multiple inheritance, which can be successfully used for this purpose. But these features are rather counter-intuitive

If you want to go that way, why not create a tree of inheritance that doesn't require multiple inheritance? Anyway, inheritance like that usually becomes kind of complex so why not just handle it with data-driven design.

Z

  • Rogueliker
  • ***
  • Posts: 905
  • Karma: +0/-0
    • View Profile
    • Z's Roguelike Stuff
Re: Handling deaths/corpses
« Reply #8 on: June 01, 2011, 05:36:51 PM »
My OO language of choice is ruby, in which mixins will solve a lot of those problems (or at least make them easier to deal with) - modules can be used like "filled in interfaces" which is very handy.

I'm curious, do you have a preferred paradigm when doing these things? Or are you simply pointing out that class hierarchies aren't the silver-bullet?

Actually yes; my big roguelike is written in a different paradigm. It has evolved from very simple things like rings changing wearers' abilities, but now it is used for everything except the basic stuff and more complicated algorithms; maybe mixins (or something) are a similar idea, but I do not know enough about them to judge (I have actually started like 12 years ago and did not know much about programming languages). Since it has evolved from very simple things and has not been really designed, there are also a lot of bad things about my implementation (some features feel very low level). Also, it is obviously much less effective than something like C++ (I don't know how it compares with script languages). It is not a silver bullet either, it is just putting some things in other places, some other things become harder to read because of this. But still, programming is different and cool. It's nice that I get saving for free (which is usually a pain in C++).

I won't describe the idea in detail, but the basic idea is that objects X can change the properties of other objects Y in a way that is not predicted by the implementation of Y themselves. This is a bit inspired by collectible card games. For example, a Winter Orb card from MtG can have a rule "A player may not untap more than one land [card] during the untap phase of each of his or her turns."; the general rules of MtG just say that there are players, turns, cards, and untap phase, and that you untap all your cards during your untap phase; you cannot predict what in particular will be changed by the cards when you are reading the general rules. The same should happen in a roguelike: objects have scripted abilities which change properties of other objects, like a Ring of Strength changes the value returned by wearer's GetStrength method. And all the implementation of this should be in the Ring of Strength itself, we don't want to change the creature's GetStrength method itself by considering a special case of wearing a ring of strength. Thus, items/monsters/spells are defined in a single place in the script file (much better than in the data-driven/object oriented hybrids like IVAN).

The basic class-like unit is a methplex. Classes of monsters, items, spells, etc. are all methplices. A methplex is a collection of methods, but it can also do some augmentation (applying some methplex with some parameters to some object). It can also be refreshed, which means that all descendant methplices arising from augmentation are killed and augmentation is performed again, this happens when e.g. you wear or remove the ring of strength.

Although designed for magical items, many things can be expressed in this way. For example, FOV works by modifying "visible" and "notifyWatchers" methods of the cells which are in sight.

Krice: IMO you are right, data driven approach is usually better for roguelikes than object oriented. My approach seems to combine good properties of the two (although I lose something in the process too).

TSMI

  • Rogueliker
  • ***
  • Posts: 65
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #9 on: June 02, 2011, 01:44:49 AM »
C++ is a bit better in this, since you have virtual base classes and multiple inheritance, which can be successfully used for this purpose. But these features are rather counter-intuitive

If you want to go that way, why not create a tree of inheritance that doesn't require multiple inheritance? Anyway, inheritance like that usually becomes kind of complex so why not just handle it with data-driven design.

I've seen you mention data-driven design a lot. Would you care to give an example of contrasting it with C++ style OOP? (Yeah I could google but I want something roguelike specific)

TSMI

  • Rogueliker
  • ***
  • Posts: 65
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #10 on: June 02, 2011, 01:54:25 AM »
The basic class-like unit is a methplex. Classes of monsters, items, spells, etc. are all methplices. A methplex is a collection of methods, but it can also do some augmentation (applying some methplex with some parameters to some object). It can also be refreshed, which means that all descendant methplices arising from augmentation are killed and augmentation is performed again, this happens when e.g. you wear or remove the ring of strength.

This sounds some what similar to using mixins in ruby. In ruby a module is something between a C++ namespace and a class... on a basic level it's just a convenient way to group related methods, but they can also have state and instance variables, and if you "mixin" a module into a class, you automatically get all the methods a module has.

Though your idea is a bit beyond me:D

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #11 on: June 02, 2011, 08:19:50 AM »
I've seen you mention data-driven design a lot. Would you care to give an example of contrasting it with C++ style OOP?

This example is from Teemu's current version. Instead of using inheritance for monsters I'm using one class with a monster type which determines how the monster is acting:

Code: [Select]
class GO_Creature : public Game_Object
{
private:
Game_Creature_Type ctype;
...

Game_Creature_Type is a class that contains static data for monsters. I like to use verbose public interface, it makes the code easy to read:

Code: [Select]
class Game_Creature_Type
{
private:
int ot;
Game_Creature_Family family;

public:
Game_Creature_Type() { }
Game_Creature_Type(int t);
Game_Creature_Type& operator=(const Game_Creature_Type& r)
{
if(this == &r) return *this;
ot=r.ot;
family=r.family;
return *this;
}
int Get() const{ return ot; }

int Get_Family() const;
int Get_Create_Type() const;
char *Get_Name(int amt);
char *Get_Name();
int Get_Max_Hit_Points() const;
int Get_Basic_Mood() const;
int Get_Prefix() const;
int Get_Score() const;
int Get_Light_Size();
int Get_Default_Flymode();
int Get_Size() const;
int Get_Bottled_Item() const;

bool Is_Pirate() const;
bool Is_Created_On_Water();
bool Is_Friend_Of_Grassman();
bool Is_Water_Hater();
bool Is_Light_Hater() const;
bool Is_Damaged_By_Light() const;

int Get_Attack_Type() const;
int Get_Size_Modifier();
int Get_Damage_Type() const;
int Get_Moving_Speed() const;
int Get_Attack_Response_Mood();

//gui
int Get_Glowing_Eye_Type() const;
void Display(int x, int y);
void Rage_Message();

//player only routines
int Grab(std::vector<Game_Event*> &v_event_list);
};

Usually my data type classes don't have other than the type itself, but this one has family class which determines some of the monster's behaviour based on its family.

TSMI

  • Rogueliker
  • ***
  • Posts: 65
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #12 on: June 02, 2011, 09:58:41 AM »
That looks very similar to a mixin.

http://en.wikipedia.org/wiki/Mixin

I wouldn't have thought of doing it that way in C++, it's surprisingly clean.

Pteriforever

  • Newcomer
  • Posts: 39
  • Karma: +0/-0
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #13 on: June 10, 2011, 10:00:20 AM »
I was thinking of handling corpses by adding a special inventory slot that's set when the creature's created -- Nothing for some, corpses for others -- that doesn't effect and usually isn't affected by anything else until the creature dies, at which point it's dropped just like any other inventory slot.

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: Handling deaths/corpses
« Reply #14 on: June 10, 2011, 04:24:03 PM »
Nothing for some, corpses for others -- that doesn't effect and usually isn't affected by anything else until the creature dies, at which point it's dropped just like any other inventory slot.

You saying the monster is carrying his own corpse? That's hilarious. But if it works then it's ok I guess.