Rewriting item use routines. I've always had problems with these, because there are couple of ways to implement it. You can first of all do everything in Creature class and check player specific stuff by checking out if the Creature is the player. Then you can do the opposite and create routines for creatures and the player, or you can do any mix of these.
Checking out the player in Creature class is I guess considered to be bad style, but whatever gets the job done is good enough. If there are lots of special stuff for player then it could be easiest to create some kind of basic routines in Creature class and use them in player's class. Sometimes the "linear" flow of the program logic can make it somewhat pesky.
Another issue to think is how clever the artificial intelligence will be. If for example you make creatures eat food items it will change the gameplay a lot, because finding unused food becomes quite difficult and may even result to situation where all food items in the level are eaten before the player can even reach them. In this case the AI is too clever, but in theory you could fix this by creating another type of food distribution, possibly placing food items in more secure places like containers which can't be opened by simpler creatures like animals.
One good example of impossible "mirroring" of functions for creatures is the food clock. It simply doesn't work for creatures, because the player could in theory just wait for all enemies to die in hunger! Yet even this feature could be fixed by making simple monsters harvest their food from nature and give more clever monsters food items and/or possibility to fetch food from somewhere.
Wednesday, 30 September 2015
Sunday, 27 September 2015
Game objects as message actors
Last time I was talking about replacing simple string pointers to game object pointers and that's what I did. Now inside the message routine certain glyphs are replaced with object names. There is some uncertain stuff going on with how message routines are called. For now I'm using function overloading which is something I try to always use, but there may be later confusion with routines that have let's say one game object instance as a pointer. At the moment it always has to be something called an action object which simply means stuff creatures use like throwing an object at someone.
When refactoring the player's routines I realized that some of them are going to be moved into Creature class, because the artificial intelligence of creatures will be improved. Creatures are able to pick up and use them. It's a big step from the current engine, but I try to keep it really simple. The good thing with Teemu is that it's a goofy game so there is no need for serious AI code.
Another big refactoring part will be the map routines. As with Kaduria I'm planning to change it to "tile based" implementation where a tile object contains everything: the ground type, objects (items, creature, etc.) and other variables. The current implementation has several layers of maps containing that same stuff, so it's again a big change, but hopefully doesn't require too much work.
Even the Message refactoring itself is quite difficult, because it's not just simple search and replace. Most routines with messages are something that need to be changed so as the actor in the routine can be either player or some other object (usually a monster). As an example, there is a part where (this is probably not a big spoiler) poison arrows start to fly when you trigger a trap. Before the "arrow" was simply a piece of text, but since the object is passed to the message routine I had to make the routine create actual poison arrows (or darts). You can even pick them up if you need poison darts.
When refactoring the player's routines I realized that some of them are going to be moved into Creature class, because the artificial intelligence of creatures will be improved. Creatures are able to pick up and use them. It's a big step from the current engine, but I try to keep it really simple. The good thing with Teemu is that it's a goofy game so there is no need for serious AI code.
Another big refactoring part will be the map routines. As with Kaduria I'm planning to change it to "tile based" implementation where a tile object contains everything: the ground type, objects (items, creature, etc.) and other variables. The current implementation has several layers of maps containing that same stuff, so it's again a big change, but hopefully doesn't require too much work.
Even the Message refactoring itself is quite difficult, because it's not just simple search and replace. Most routines with messages are something that need to be changed so as the actor in the routine can be either player or some other object (usually a monster). As an example, there is a part where (this is probably not a big spoiler) poison arrows start to fly when you trigger a trap. Before the "arrow" was simply a piece of text, but since the object is passed to the message routine I had to make the routine create actual poison arrows (or darts). You can even pick them up if you need poison darts.
Friday, 25 September 2015
The you
Message pools are divided roughly for the player and other creatures which most commonly trigger a message. The problem with the second noun of a message is that it can be a creature or player, but when the name is constructed from the € and replaced with some name, it is "the you" if the target is the player. You could add "the" with creatures and only "you" with player.
How you do that is another problem. It's good that I haven't got far in the refactoring process, because I think the second noun could always be a game object. It means that rather than passing the noun as a string you should pass it as a game object. Then you can check in the actual text process routine for the type of the game object. The object name can be also a named object. It means mostly creatures who have a job, something like "Darren the shopkeeper".
The second noun could be indeed the target. The first noun is some game object and it should be also passed as a game object, since it can be that shopkeeper. If I wanted to go deep I could create a verb object that chooses from forms between for example "hit" and "hits" for player or npc, but let's not go in there. But it would likely mean that only one message pool would be required for both player and non-player characters.
How you do that is another problem. It's good that I haven't got far in the refactoring process, because I think the second noun could always be a game object. It means that rather than passing the noun as a string you should pass it as a game object. Then you can check in the actual text process routine for the type of the game object. The object name can be also a named object. It means mostly creatures who have a job, something like "Darren the shopkeeper".
The second noun could be indeed the target. The first noun is some game object and it should be also passed as a game object, since it can be that shopkeeper. If I wanted to go deep I could create a verb object that chooses from forms between for example "hit" and "hits" for player or npc, but let's not go in there. But it would likely mean that only one message pool would be required for both player and non-player characters.
Monday, 21 September 2015
Bad naming
I'm facing a big refactoring job with game messages, but it's worse than that. I was a bit careless about naming some things and now it's going to give me some extra work.
What I have already changed was message.cpp (and .h) to gamemess.cpp. The reason behind this is that you should always name the source file as closely as possible to class name which here is Game_Message. What happened in this case is that I wanted to create an atomic class for messages, called Message. Yes, it would be confusing to use another file name for that class.
Much bigger problem is that I'm using a global instance of Game_Message called msg. Yet another big mistake, because now I want to create a namespace for message ids and it's going to be msg. Not only that, I'm using a function called Message in Game_Message which should have been overloaded version of Write rather than Message, because for one I need Message for the new class and it would look really stupid to write message->Message().
Or maybe it would be better to preserve Message as the internal function for messages in game object classes and give the class some other name, something like Message_Data? The new Message class is for pointers in message data, so when you use a message id the location of text is retrieved from that class instance which also contains alternative versions for messages.
What I have already changed was message.cpp (and .h) to gamemess.cpp. The reason behind this is that you should always name the source file as closely as possible to class name which here is Game_Message. What happened in this case is that I wanted to create an atomic class for messages, called Message. Yes, it would be confusing to use another file name for that class.
Much bigger problem is that I'm using a global instance of Game_Message called msg. Yet another big mistake, because now I want to create a namespace for message ids and it's going to be msg. Not only that, I'm using a function called Message in Game_Message which should have been overloaded version of Write rather than Message, because for one I need Message for the new class and it would look really stupid to write message->Message().
Or maybe it would be better to preserve Message as the internal function for messages in game object classes and give the class some other name, something like Message_Data? The new Message class is for pointers in message data, so when you use a message id the location of text is retrieved from that class instance which also contains alternative versions for messages.
Sunday, 20 September 2015
Inherit problem
T_Bag was ready for GO_Container to inherit from, but what blocked it was the way game objects are saved with a template routine which is creating only the default constructor object. Now I'm sure it's a bad way to do it, because what happens is that when you load the object all pointer composite classes are re-created in the Load()-routine. I guess it's not that bad, but still it would be more appropriate to always create the whole object with the constructor.
It's certainly not impossible or even hard to fix, because all you have to do is save the type of object and then write routines for each object type with specific extra information about the constructor parameters. In this case the container (GO_Container) has to know what kind of T_Bag class it has during the construction, in fact before it by passing the container type to GO_Container's constructor. This information has to be saved and loaded outside the class of course, because you can't create the instance before knowing what kind of class it is.
The way I solved the wrapper function problem for now was indeed making the container component accessible to the player and level classes, and using class->component->Do_Something(); notation.
Other than that I've pretty much cleaned the simple game object classes. The base class has some message related routines which need fixing, but it requires the new message routine not yet ready. The main focus is now in Item and then Creature classes. The problem with those is not only the size and increased compexity but the fact that they are often more connected to the role-playing system. When I was programming the previous versions it was quite easy, because there wasn't any role-playing system worth mentioning. The game has now a HP value and some damage amounts which are totally not in balance at all. The easiest way to kill enemies is just throw something at them which they can't do in return.
I have to be more clever with the RPG system than before and try to keep things really simple. Luckily there is no magic or other difficult issues, only damage with various ways and HP. The new RPG system is probably going to be similar to that, but more balanced and slightly more complex with armour protection.
It's certainly not impossible or even hard to fix, because all you have to do is save the type of object and then write routines for each object type with specific extra information about the constructor parameters. In this case the container (GO_Container) has to know what kind of T_Bag class it has during the construction, in fact before it by passing the container type to GO_Container's constructor. This information has to be saved and loaded outside the class of course, because you can't create the instance before knowing what kind of class it is.
The way I solved the wrapper function problem for now was indeed making the container component accessible to the player and level classes, and using class->component->Do_Something(); notation.
Other than that I've pretty much cleaned the simple game object classes. The base class has some message related routines which need fixing, but it requires the new message routine not yet ready. The main focus is now in Item and then Creature classes. The problem with those is not only the size and increased compexity but the fact that they are often more connected to the role-playing system. When I was programming the previous versions it was quite easy, because there wasn't any role-playing system worth mentioning. The game has now a HP value and some damage amounts which are totally not in balance at all. The easiest way to kill enemies is just throw something at them which they can't do in return.
I have to be more clever with the RPG system than before and try to keep things really simple. Luckily there is no magic or other difficult issues, only damage with various ways and HP. The new RPG system is probably going to be similar to that, but more balanced and slightly more complex with armour protection.
Thursday, 17 September 2015
Kingdom of Refactoria
So I had an idea to inherit container functionality to GO_Container which confusingly is a game object type (such as boxes you can move), not the container class itself. We have been told that you should use composite type classes which are instances of classes inside another class. However sometimes it creates a number of wrapper functions, especially when you use the composite class from outside the host class. One way to reduce that is expose the composite to outside, so you can use notation class->composite->Do_Something(); but then again, why not use inheritance?
I started by looking at the container class. In Teemu it's called T_Bag. I know, I know... It's an "abstract" class which is just a list of items which then can be accessed, removed and put into the container. I found something fishy about Get_Slot() function that took the item type as parameter. The function itself wasn't a problem but the way it was used in Player class (I think player's class was Player in Teemu, not sure).
There was a variable called 'weapon' in the Player class. I noticed that it was the type of the item and that item was retrieved from the container with Get_Slot(). I think my original reasoning was that items are always stacked so there will always be only one item type in a slot position. But I think it has changed or will change in the near future. It means that when you try to access the weapon item it can be another item with same item type!
I'm embarrassed to mention, but first I made a mistake and changed the 'weapon' to weapon_slot which pointed to a slot in the inventory. It's perfectly valid way to access the inventory and the only way to access items when you select an item from inventory. Then again if you use the slot to store the current weapon it will be a disaster, since you can throw items from the inventory and change the internal slot positions. I wasn't really thinking this, because I only remembered you can't drop items, although it also will change soon.
After realizing this I changed weapon_slot to weapon_item which is simply a handle or pointer to the item in the inventory. The solution was simple in theory, but required changes in Wield_Item and Wear_Item routines among other routines. This time I also wrote routines to handle simple operations with member functions and not just directly using weapon_item variable as I had done with 'weapon'. Even when you are using member variables it's sometimes good to limit the places where the variable is set or changed and "hide" it behind a member function. Not only it's easier to refactor but also less likely to create bugs.
As a refactoring experience this one was actually not that difficult. It seems like I didn't make much progress, but in fact now T_Bag is ready for experimenting as a part of inheritance structure of GO_Container and I have fixed that dubious weapon handling. To make things bit more complex T_Bag will also be a composite class in Player, because the player has two inventories, one for regular items and one for tools, but I think it should work.
I started by looking at the container class. In Teemu it's called T_Bag. I know, I know... It's an "abstract" class which is just a list of items which then can be accessed, removed and put into the container. I found something fishy about Get_Slot() function that took the item type as parameter. The function itself wasn't a problem but the way it was used in Player class (I think player's class was Player in Teemu, not sure).
There was a variable called 'weapon' in the Player class. I noticed that it was the type of the item and that item was retrieved from the container with Get_Slot(). I think my original reasoning was that items are always stacked so there will always be only one item type in a slot position. But I think it has changed or will change in the near future. It means that when you try to access the weapon item it can be another item with same item type!
I'm embarrassed to mention, but first I made a mistake and changed the 'weapon' to weapon_slot which pointed to a slot in the inventory. It's perfectly valid way to access the inventory and the only way to access items when you select an item from inventory. Then again if you use the slot to store the current weapon it will be a disaster, since you can throw items from the inventory and change the internal slot positions. I wasn't really thinking this, because I only remembered you can't drop items, although it also will change soon.
After realizing this I changed weapon_slot to weapon_item which is simply a handle or pointer to the item in the inventory. The solution was simple in theory, but required changes in Wield_Item and Wear_Item routines among other routines. This time I also wrote routines to handle simple operations with member functions and not just directly using weapon_item variable as I had done with 'weapon'. Even when you are using member variables it's sometimes good to limit the places where the variable is set or changed and "hide" it behind a member function. Not only it's easier to refactor but also less likely to create bugs.
As a refactoring experience this one was actually not that difficult. It seems like I didn't make much progress, but in fact now T_Bag is ready for experimenting as a part of inheritance structure of GO_Container and I have fixed that dubious weapon handling. To make things bit more complex T_Bag will also be a composite class in Player, because the player has two inventories, one for regular items and one for tools, but I think it should work.
Wednesday, 16 September 2015
The game object engine of Teemu
The way game object system is implemented in Teemu is similar to Kaduria where all game objects have a base class and derived classes for main object types (item, creature, door, etc.). The difference is that in Teemu the base class is a type-less class, without information about the game object's type. In Kaduria the base class knows the type of the object.
These two ways to implement the base class are not that different actually. The "pure abstract" version in Teemu is kind of what you should do when creating an abstract base class (aka ABC). When the base class knows the type it's possible to write functions in the base class that work with all object types, without having to write multiple virtual functions.
The current base class in Teemu has only the location of the object and a flag telling is the object destroyed. These two values are indeed abstract to the derived classes and for example the location doesn't need to know anything about the game world in the base class context. In derived classes you need to make decisions where the object can be placed and handle location according to that.
What I haven't explored is the possibility to use intermediate classes between the base and derived classes. These classes could include functions like moving the object and the container ability for an object.
There has been more development done in last two days than in this year combined. Not a joke. I've been checking out the game object system in general and found out the "usual suspects" which are creature and item classes. They always seem to get big and complex. I've also tried to refactor so called data type classes related to game objects and reduce the amount of getters. Each game object class has a static data type class with information about the object's name, frame, color and other functions. The more you can put functionality in those classes the less you need in elsewhere, but the static nature restricts some functions, which is what it's supposed to do. The reason for data type classes is that it's easier to handle static data that way and also it's possible to find out the basic data of the game object without creating an actual game object. This last one can also become a subtle source of bugs, because the type is decoupled from the actual object which can result to mismatch of types. It's possible to avoid that by watching out any external handling of game object data and bringing them back to the object by handling the situation through game object's member function.
The intermediate classes could be a good way to reduce the complexity of the creature and item classes and that's the next thing I'm going to try.
These two ways to implement the base class are not that different actually. The "pure abstract" version in Teemu is kind of what you should do when creating an abstract base class (aka ABC). When the base class knows the type it's possible to write functions in the base class that work with all object types, without having to write multiple virtual functions.
The current base class in Teemu has only the location of the object and a flag telling is the object destroyed. These two values are indeed abstract to the derived classes and for example the location doesn't need to know anything about the game world in the base class context. In derived classes you need to make decisions where the object can be placed and handle location according to that.
What I haven't explored is the possibility to use intermediate classes between the base and derived classes. These classes could include functions like moving the object and the container ability for an object.
There has been more development done in last two days than in this year combined. Not a joke. I've been checking out the game object system in general and found out the "usual suspects" which are creature and item classes. They always seem to get big and complex. I've also tried to refactor so called data type classes related to game objects and reduce the amount of getters. Each game object class has a static data type class with information about the object's name, frame, color and other functions. The more you can put functionality in those classes the less you need in elsewhere, but the static nature restricts some functions, which is what it's supposed to do. The reason for data type classes is that it's easier to handle static data that way and also it's possible to find out the basic data of the game object without creating an actual game object. This last one can also become a subtle source of bugs, because the type is decoupled from the actual object which can result to mismatch of types. It's possible to avoid that by watching out any external handling of game object data and bringing them back to the object by handling the situation through game object's member function.
The intermediate classes could be a good way to reduce the complexity of the creature and item classes and that's the next thing I'm going to try.
Tuesday, 15 September 2015
Teemu news
With new confidence from experiences in Kaduria about handling messages in a new way I'm trying to apply that to Teemu. The last time I touched Teemu's source code was in March this year. Yes. It's been a long time.
The way I'm approaching the development of Teemu is through refactoring. It seems to be a better option than just continue with the current source code structure. The changes are quite minor I hope, but an important step to get past the writer's block.
The good thing about Teemu is that it's way simpler than Kaduria. It's so much easier to program. The bad thing is that it's still a roguelike-ish project and is going to be more roguelike with the new features. So it's easier, but only in the context of roguelikes.
My plan for allocating time for each of my projects could be something like a week for each project at a time? I don't know, because it never seems to go according to the plan. But it would be nice to get Teemu 1.3 ready and also the next version of Brick Atelier to get them out of the way, possibly giving space for entirely new side projects.
The way I'm approaching the development of Teemu is through refactoring. It seems to be a better option than just continue with the current source code structure. The changes are quite minor I hope, but an important step to get past the writer's block.
The good thing about Teemu is that it's way simpler than Kaduria. It's so much easier to program. The bad thing is that it's still a roguelike-ish project and is going to be more roguelike with the new features. So it's easier, but only in the context of roguelikes.
My plan for allocating time for each of my projects could be something like a week for each project at a time? I don't know, because it never seems to go according to the plan. But it would be nice to get Teemu 1.3 ready and also the next version of Brick Atelier to get them out of the way, possibly giving space for entirely new side projects.
Subscribe to:
Posts (Atom)