Thursday, April 22, 2010

Rogue Survivor Design approach

(You should know about OOP and the design issues behind designing a roguelike game to read this article)

 OOP for Roguelikes
 When designing a game architecture with an OOP (Object Oriented Programming) approach, one naturaly tend to put the data and the logic associated with a game entity in the same place, namely a class.
So you create classes like Item, Actor, Map and the likes and make a one-to-one relation between a game entity and an instance of a class (object). 
One particular Sword entity in the game will likely be an instance of ItemMeleeWeapon, which a hiearchy like ItemMeleeWeapon is ItemWeapon is Item.

Entities Interactions : Assigning responsabilities
Works fine. But then what about entities interaction? Say the player actor wants to open the big door.
Who does it? There are many differents ways and paths to choose. The most common are:
  1. The Actor does it: playerActor.OpenDoor(theBigDoor), which in turns calls theBigDoor.SetState(OPEN) etc...
  2. The Map does it: map.ActorOpenDoor(thePlayer, theBigDoor)...
  3. The Action does it: openDoorAction.Execute()...
Thats works fine too. But to be consistent in your design you have to garantee all actions will follow the same path and logic, otherwise you will end up with anarchic code.

Shared responsabilities = Spaghetti Logic = Bugs and Refactoring Hell
The danger -read: the source of bugs or game logic inconsistencies- resides when one entity (active) initiate an action on another entity (passive) which could trigger a response somewhere else (eg: the door electrocutes the actor) which in turn could trigger another response (eg: the actor roll for electrocution resistance) etc.. and you end up with spaghetti logic. In general this ends up provoking bugs, because somewhere in one of your derived classes you made an assumption about the caller or some property that is broken by the logic chain triggered by the action initiator...
People who take this standard OOP approach tend to search for the "perfect" architecture, refactor their code a lot and most of the time aim for a roguelike engine rather than a game. It works but with great pain.

Game Data separated from Game Logic : Multiple Data, One Controller
In Rogue Survivor I choose since the start to go with a Data-Driven approach with a single Controller.
All the game entities are represented by Data classes. Data classes are "stupid" and only know to maintain themselves. Data classes use inheritance only when requiring additional fields and properties. Eg: ItemWeapon derives from Item because it needs additionnal data to represent a weapon.

All the game logic in centralized in one place, the Game. The RogueGame class has absolute control on everything, is the game master and holds all the game logic.

Actions are Data too, whose job is only to call the RogueGame appropriate methods when checking for legality or executing. Remember, as Data classes they have no game logic in them at all. They are like a dumbed down version of the Command design pattern.

Types of entities (eg: the skeleton actor type, the crowbar item type) are instances of Data classes too, called Models (eg: skeleton actor type is an instance of ActorModel).

AIs are special cases because I consider them as abstract game logic, since the AI logic in itself does nothing in the game world, it just computes stuff and produce an Action command.

The Holy Grail?
Certainly not. In other pet game projects I favor other architectures such as the standard OOP mentionned above.
For Rogue Survivor it just works and is very easy to maintain. All architectures have limitations and flaws, you just have to accept them.

End of post.

Wednesday, April 21, 2010

Alpha gameplay video

Created a youtube account and posted a gameplay video there.
Video : http://www.youtube.com/watch?v=lcIumH9_gWc
Channel: http://www.youtube.com/user/roguedjack

Shows new features like Barricading, Trading, Firearms, Leveling up and Skills.


End of post.

Thursday, April 15, 2010

changelog

Change is good. Let's log it.


Alpha - April 15 2010 build
---------------------------
new Feature - Inflicting HP damage now cause damage to STA too.
new UI      - Quit game asks for a confirmation.
mod Items   - Baseball bat +4a/+3d instead of +3a/+4d.
new Feature - Progressive Day/Night phases, with gradual FoV penalty.
new UI      - Game now announces Day/night phases.
new Feature - Actors with ability CanZombifyKilled now also regen HPs from the damage they inflict on livings.
new Feature - Trading : people chat with each other to exchange items (my item vs yours, deal yes/no). The player has to initiate trade with Ais (as to not get spammed by Ais offers).
new AI      - BehaviorEquipWeapon: equips weapon from inventory if none equiped (used by Civilian AI).
new AI      - BehaviorRestIfTired: wait if tired (used by Civilian AI).
new UI      - Icon for actors who can actually trade with the player, so the player don't waste time with useless trade demands.
mod Data    - changed Item.Quantity from int to short for memory and consistency.
mod AI      - Zombies AI now chase visible enemies first.
fix Rules   - Fixed an awfull typo-induced bug in isAdjacent(). Mistaking a X for a Y gives funny results...
new UI      - When upgrade time, added a popup on top of screen like other modes to make it more noticable.


End of post.

Wednesday, April 14, 2010

Map class: Array & List+Hash

A short presentation about using List+Hash instead of Arrays.
Confirmed programmers can skip this one but I thought it might be quite usefull for novices.
Non-programmers can skip this large nonsense post.

- Good Array

A map is basically a 2D grid of stuff. A 2d grid most natural data structure is a 2d array.
The map is made of tiles. So let's make a 2d array of tiles:

class Map
{
    Tile[,] m_Tiles;  // easy and fast!
}


A tile has various properties, such as the terrain there or if the player visited this tile. These core properties are everywhere on your map, so you want them at every tile.

Your game will access the tiles by position a lot so having an array makes sense and is the most efficient solution.

Entity everywhere on the map + frequent access by position = Array of entity is good.

- Bad Array, quite Good List but not good enough
You have other entities on a map. They have a position on the map too, but all the tiles do not necessary have one of these entities and in fact most of them do not. For instance you never have an actor at each and every tile.
Since most of your map will be void of these entities it makes sense to NOT use an array. That would be a waste of memory and your saved games size will skyrocket.

class Map
{
     Actor[,] m_Actors;  // most of this will be empty! what a waste!
}


That sucks. Image the wasted memory on a large map (yeah even null pointers take memory).

Entity rare on the map = Array of entity is bad.

Ah. Well what about a List then?

class Map
{
     List<Actor> m_Actors;  // ok no waste. happy?
}



Entity rare on the map = List is good.

Hey! But I liked my array! It was so nice to access efficiently to the entity by position! Having to process the whole list of actors to see if there is an actor at a position is awfully inefficient!

Correct. That's why God created Hashtables.

- List+Hash : you are winner!

Ok we have two requierements that seem contradicting:
1 don't waste memory.
2 fast access by position.

List does 1. Array does 2.
Having 1 is nice but we REALLY wants 2, especially if you have a large number of stuff to process in your game.

Hashtables are usefull for this. You can think of them as "hollow" arrays.
- you can access them by index (called key) like an array.
- they hold only the elements you put in them like a list, they have no "holes".

When you think about it, a position (point) on the map is an index. There you have your key.

class Map
{
    List<actor> m_Actors;
    Dictionary<Point, Actor> m_ActorsByPosition;
}

Now you can retrieve actors directly by their position by querying the hashtable:

Actor GetActorAt(Point position)
{
    return m_ActorsByPosition[position];
}

(This little code doesn't handle error cases but you get the idea)
It works great. It looks and behave like an array, with very little overhead and with the benefit of no wasted memory.

We still keep the list, cause we want to be able to write things like: "for all actors in this map, do this". Getting the list of actors from the hashtable doesn't guarantee a stable order, plus you don't know what it does under the hood, it might reallocate a list each time you call it.

Think of the list as the master data structure, and the dictionary/hashtable as the slave or auxiliary data structure : you don't need it, but it proves quite usefull.

- Real life use : AI improvement
Well not quite real life but real game. I use this dual data structure a lot in Rogue Survivor, from Actors to MapObjects and Items stacks.

But I confess I was lazy once and did not use this for the Odor feature and instead kept the basic list alone.
Some actors leave odors behind them and some AIs use this to follow or chase them around.
Worked good enough, until I wanted to improve the Zombies AIs. I thought they were a bit too passive and I wanted them to more actively chase humans by smelling odors at a distance rather than just by standing around.
Implement. Test. Uh oh. With my 100+ Zombies per map performance was fugly.
I forgot about the list and used a profiler to find the problem, if there was one. I was ready to surrender the AI improvement to the god of performance :(
Well, turned out I still had the list and it was doing "find an odor at position X in a big fucking list" a lot.
I promptly repaired this injustice by implenting the companion hashtable and tadda, not only performance improved a lot, but it improved to better levels than before. In the end I had better AI with better performance at the cost of few lines of code.
So trust me, use it :)

End of post.

Tuesday, April 13, 2010

Roberto Bush, Zombie slayer

Nice progress since last blog entry. I'll let the screenies do the talking.

Roberto Bush begins.


Let's go outside for a walk.. Er what about staying inside instead?


Bah! Inside ain't safe either.
 


 Roberto is now stronger. He just needs to find a weapon to make good use of it.

The basic survivor kit : a baseball bat and a medikit. Time to get serious.


 Roberto is an artist.


Roberto meet Gloria Norton, baseball bat wielding blonde.

Roberto soons find out that dead blondes are not as nice as Gloria.


For Roberto, dead people do not die in vain. They might leave usefull items behind.

End of post.

Thursday, April 8, 2010

Shopping with Zombies

Last night Phil Stevens went shopping...
(large pic from 1680x1050 screenshot)


 ...and met some unpleasant customers. 

Basic inventory and item management done. You can grab items from the shop shelves and they stack in your inventory.

Also started to implement Food and Sleep features. You'll need to loot food in place such as shops and find proper places to sleep or you'll get bad things happen to you. I find the need for food to be an annoyance in roguelike games. But here it serves as a motivation for the player to go outside and face danger rather than just holing up in his corner, since the game is all about surviving. Need for Sleep acts like a long term stamina constraint : you can't keep waltzing around zombies and fighting undead all day, you'll have to find a place to rest. Undeads on the other hand don't have these kind of problems at all, plus considering night will have negative effects on the livings, the player will have to strategize his survival schedule.

End of post.

Wednesday, April 7, 2010

Let's begin...

...With a teasing screenshot.
 This thing should be clickable right? The pic is about 1024x768.




Ok so this shows the game in its present state. You can already guess a number of features if you look closely. I'm the little blonde in the middle of the street.

Resizable Game Window & Scalable Graphics

A nice feature is that you can resize the window at will and the graphics will scale properly. The reference size is 1027x768. When testing most of the time I maximize the window on my 1680x1050 desktop for a zooming on the details I'm looking for.
Like this: 1680x1050

End of post.

Hello World!

Hello there. Let me introduce this blog in a short question-answer form.

What is this blog?
It is about me developping my game Rogue Survivor.

What is Rogue Survivor? 
Rogue Survivor is a graphical zombie apocalypse survivor roguelike game.

Status?
It is still in alpha but already playable even though there's no much to do yet. I guess in its present state it could qualify as a 7DRL. As an alpha there are quite a lot of things that could still change from graphics to code and what not but the basic gameplay ideas will not.

Technical stuff?
C# using managed DirectX for rendering in Winforms. No third party library or ressources.

And who are you?
Not a natural English speaker but I think you guessed it ;)

End of post.