Skip to main content
GameDev.net gamedev.net
🔒 Locked

RPG Item class

Started by BeaverusIV Jun 15, 2008 at 9:46 PM 16 replies 4.3k views
Original Post
BeaverusIV
BeaverusIV
I am recoding my RPG (for the third time) that I have been making on and off for 8 years now (I started before I knew c++). This time I decided instead of starting with getting a map displayed, then character, then moving around I would start with the battle system as it is something I haven never got around to doing and might give me some more insight on a better engine structure for my game. Currently it goes like this: BattleEngine class handles whos turn it is, asking them for an action and doing that action, then doling out exp/items at the end of the battle. There is the Beastiary & Party classes that handle the Character class, they both basically load a character (savegame or monster) from xml and is used to be scalable (so I can have more than one character on each side of the fight). What I am implementing now is an item system so the characters can equip something. My question is how have people gone about this.... I want my game to be very item-oriented ala Diablo II but for now I'll leave random item generation alone and stick to helm with 5 fire resistance etc. My Character class has private members like health, dhealth (delta health, i.e. how much health skills/items add onto or decrease your base health), strength, dStrength, fire_resistance, dFire_resistance, etc, etc. and I was thinking of having the Character class load in the items and parse their modifiers, adding them to the 'd' members of Character to be used in damage calculations, but then I realised if I did this I wouldn't really need an item class so much as a function to open the itemstats.xml, grab the item's modifiers and return them to the Character class... to me this doesn't sound right, I want to have the items loaded so you can view them in your inventory without having to relaod an xml everytime. The other way was to have the item class and load each item into its repspective Character memeber (Head, Torso, Ring1, Ring2, etc.) and then parse the modifiers everytime you need to claculate damage, but that seems inefficient to me, haivng to loop through all the items every round (Even item in the inventory can effect the player ala Diablo II charms). I was just looking for some advice on someone who has done this before on which way to go or wether there is a different approach I can't see. Thanks for reading this long as(probably illegible) post, first time posting here, but long time reader.
Splinter of Chaos
Splinter of Chaos
I think you're right that it's not good to run through the xml doc every time you use an item. I suggest loading items to memory (when needed, or if there aren't many, all at once) and keeping it somewhere it'll stay until no longer needed or until the game shuts down (whichever you feel is best for your game). But, I don't just mean in battle scope, I mean, what's in your inventory (in-game, in-battle) on the field, or in battle.

I also don't really like your delta-statistic system, but I never really thought about it. What I don't like about it is how it adds more variables to the equation (twice as many, in fact). Especially since how much fire-damage an attack should cause shouldn't need to be recalculated every time you attack. Maybe stat, dstat, and tstat (t for total). You might even have only dstat and tstat with the t being implicit.

Ever thought of having an object deal with how much damage an attack does? The result could be recalculated and you'd only ever need to recalculate when you change equipment, and it could keep track of the modifiers so when you attack using that object, it could figure the deductions based on defenses on its own.
BeaverusIV
BeaverusIV
They are there mainly so when temporary stat buffs/debuffs wear off it comes out of the delta and means things that use the base stat to calculate percentages (i.e. helm gives +15% strength should only work on base strength, not the extra strength from the +10% torso you have on), I evolved the delta stat idea into having a stat class where you overload operators so you would have a Statistics member in Character and when you load an item it would return a Statistic you could '+' onto the Character member. Oh, and yes, the total was implicit (return health + dHealth; )

I think I'll have the items instantiate at Character creation and have it cache the damage values/resistance values in the Character class, and if I need the original character's values I could reload from xml, right?
Splinter of Chaos
Splinter of Chaos
"if I need the original character's values I could reload from xml, right?"

sure.

I guess what I'm curious about now is: How do you calculate the delta stat? How often?

But back to the items: I don't really know that there's a best solution. If you have to look into the xml file every frame and the frame rate doesn't dip, you're code is redundant, but you can get away with it. If you load every item, all at once, you only have to look into the xml file once, but you've offended your RAM; not that that matters if you're not already using a bunch of RAM. The rout of loading at character initialization seems like the middle-ground to me.

So, what I'm trying to say is, I don't know that what you're doing, or what you're going to do, is bad because I don't know where your game is or is going. Never having had a problem with RAM, myself, I'd just load all the items, but you might've had a different experience.
BeaverusIV
BeaverusIV
No RAM probelms for me, and this is game is more a personal project than 'Hey world! Heres my awesome game!' so I can get away with being a RAM whore (If it ends up that way).

At the moment all it is is a commandline turn-by-turn fight (Press 1 for attack, 2 for items, 3 for skills, 4 for character screen, 5 for run away kinda thing). ofc in the end it'll get a gui and be integrated into RPG, but for now I like the autonomy (means I'm not influenced by previous game's structure).

The delta stats are calculated at Character intialisation and item equipping, so not very often... this will be later extended to being called when a spell wears off/battle is over as well.

e.g.:
You start off with dStrength = 0; (nothing calculated yet) Strength = 50; (from xml file).
You load up the helm, it has +15% strength (50 * 0.15 = 7.5 = 7) so dStrength = 7.
You load up the Armour it has + 4 strength, so dStrength is now 11.
Load the Gloves and they have +50% strength, so dStrength = 36 (25 + 11).
Boots have -10 strength, so dStrength is now 26.
if you call getStrength() it'll: return 50 + 26;

Make sense and answer your curiosities?
TheTroll
TheTroll
Do your calculations when you add or remove an item. That is the only time that it is going to change. Other then that just store them as a modifier on the character.

theTroll
BeaverusIV
BeaverusIV
But I don;t like the idea of opening the xml everytime they view the inventory, so I'll load them into a class, too, its just they won't be used much. Inelegant, but it'll work.
BUnzaga
BUnzaga
I am also working on a rpg game right now.

The way I am doing it is the way Splinter of Chaos suggested. At the start I load an 'item catalog' file which is basically a text file which stores everything about the items. As an example, here is one item:

Goblin Cleaver
right
An old cleaver which still has an edge.
C:\Documents and Settings\BUnzaga\My Documents\DX Dungeon\Items\cleaver.png
C:\Documents and Settings\BUnzaga\My Documents\DX Dungeon\Items\goblinCleaver.dxmesh
2.0
250
5
0.00
C:\Documents and Settings\BUnzaga\My Documents\DX Dungeon\Item Scripts\testClick.is

At runtime I parse all that into an array of GameObject.

GameObject(name, position, description, bmpLoc, meshLoc, weight, range, damage, sellPrice, script)
{
//assign the signature info to the objects fields
}

The next thing I did was create an Item Controller. This handles the methods for loading the items giving them unique names, etc.

So as an example, as part of my map loading script, I have it load items and position them in the map:

0
1
new Vector(10,0,10)
-1

0 is the item position in the catalogue (ie: itemList[0]). 1 is the number of itmes (for coins and arrows and such this will be larger than 1). Place it on the map at the Vector, and the -1 means it belongs to the 'map'.

The Item Controller also handles things such as monsters dropping loot, or the player picking up items, dropping items, etc.

Once the player picks up the item though, the item is now handled by the Character Inventory Controller.

From the item data you can see above, the second line is the string 'right'. This means this object can be used in the right hand.

The line third from the bottom is the integer '5' this means this object adds +5 to the players damage.

So you can see the item itself is responsable for damage checks, etc.
BUnzaga
BUnzaga
As far as when to check the users stats, just do it when an effect is applied, or removed, or when initializing the character like The Troll said.

Here is some psuedo code to get you on your way.

onInit()
baseStr = character.getStat(strength)
itemStr = equippedGear.getStat(strength)
currentStr = baseStr+itemStr

character.getStat(statType)
return stat[statType]

equippedGear.getStat(statType)
var amount = 0
for(var i1=0;i1{
amount+=equippedGear[i1].statBonus[statType]
}
return amount;



When you add or remove an item, the equippedGear needs to be recalculated. If you level up or something your base stats need to be adjusted.

Perhaps you might even want a third calculation for spell effects...

currentStr = baseStr+itemStr+effectStr

Then when you use your stats for combat, you just reference currentStr.
BeaverusIV
BeaverusIV
Ok, so I finished it a few days ago (Thanks for the input anyway). Here is what my items go through:

I have items.xml


Short Sword
WEAPON1H

70 //these ids are defined in mods.h


I load the character from hero.xml
...

0
1
....

So the character init() calls Item::create(1) which loads the data into an Item and sends back a Statistics which has all the deltas from the item.

The deltas include from spells etc and are recalculated when a base stat is changed or items are equipped/unequipped. spell duration running out tells the delta how much to increase/decrease to stop spell effect.

And thats what I'm currently doing; designing spells so I can get a feel of what they do/how to code them in.
BUnzaga
BUnzaga
Let me know when you get to abilities. :/ I haven't done a lot of research on them, but they seem a bit abstract for me to get my head around.
BeaverusIV
BeaverusIV
Yeah looks a bit daunting to me, I have 8 classes and I want 30-40 skills for each (including upgrades), but I'm starting off with about 5 in each, trying to make them as varied as possible so I can code every type in at once. Still going over how they can fit in. My battle system is text-based at the moment so 'rendering' for me right now is "You hurl a fireball at them for 30 damage!" lol, I hope I can turn the function that does that into one that gets the animation going easy enough.
Rasm
Rasm
Hi BeaverusIV,
I'm currently working on a text based RPG called Wrath Lands II. You can check out the project and source at SourceForge. It's still a work in progress but the concept is very similar to what you are trying to accomplish and what BUnzaga suggested.

UML Diagram for WL2.

To do abilities (or in my case Skills or Spells), you create the base class with a type (for derived classes tracking),a magnitude (how powerful the ability is), and its requirements to use such as mana, stamina, soul stones, etc and then an abstract function Cast which contains a caster pointer and target pointer (for obvious spell manipulation reasons). You then derive from the base Spell/Skill class and override the Cast function with whatever functionality you want for a particular subset of skills/spells. Since your using XML to create your database of abilites/skills/spells, you can use the type variable in the base class as a way to determine which child classes cast function to use (basically setting the logic of the spell).

Since it sounds like you are making your game graphical, you would need to add additional variables and functions to the base class. One variable would be an index to the animation that plays while the spell is in motion, another value for an index to the animation that plays when the spell processes the effect (such as a fireball exploding on impact), and finally the speed at which the process takes (based on your game framerate). You would also need to add a OnImpact function to the base class which processes the actual effect (use Cast to check requirements and drain mana/stamina). You then need to implement a Controller class and an instance class.

The instance class will be created at runtime when your spell/skill is used. It contains information such as location of use, direction of use, time of use (for animation updates), current state of spell (moving/causing effect), and of course an index to the spell type in your master list. If you're doing turnbased combat (final fantasy style) you'll probably want to include a pointer to the caster of the ability/spell and a pointer to the intended target since its unlikely the target or caster will move and collision calculations during update wont be necessary.

You then have a controller class which creates/updates/deletes your instance class. When create is called, a new instance class is created which should call the overriden Cast function which should take mana/health/stamina or fizzle if not available, or backfire etc. When update is called, the animation frame and location is updated, and then checked to see if the spell struck the target(s). If so, then call OnImpact(Target*) which should subtract health, instant kill, add an effect to the target such as poison, spawn another spell, etc.

General advice: Keep things modular. Static data such as requirements to use and behavior stay defined in the implementation class. Data that will change at runtime such as location, direction, targets, and animation frame go inside an instance class which are updated and controlled by a controller class.

I'm sure I left some things out accidentally but you should get the idea. Cheers!
Most of our obstacles would melt away if, instead of cowering before them, we should make up our minds to walk boldly through them.- Orison Swett Marden
BeaverusIV
BeaverusIV
Thanks for the input, sounds about what I was going to do. Makes it easier to tackle when its laid out, though.
FippyDarkpaw
FippyDarkpaw
You can handle Skills and Monsters in an RPG in much the same way as you handled Items, with a simple list of archetypes that is loaded from file at game start.

- ItemsDB: vector/array of all item stats
- MonsterDB: vector/array of all monster stats

Vectors/arrays automatically gives each item an ID - which is their index in the vector. When an item spawns, copy the item from the ItemDB to the new item. When a monster spawns, copy the stats from the MonsterDB to the new monster.

For Classes/Skills and even status effects you can handle in much the same way.

- ClassDB: vector/array of classes
- PowersDB: vector/array of powers
- StatusEffectsDB: vector/array of status effects

Each class has some stats and a list of what Powers it possesses.
Each power has some stats and a list of what StatusEffects it induces.
Etc...
BeaverusIV
BeaverusIV
Sounds interesting. My skills are going to be summoning, mind control, kidnapping, all sorts of things, I'm trying not be held down by my previous game experiences, but its very hard to come up with something other than 'fireball' for destructive magic, lol.
scjohnno
scjohnno
The Prototype pattern can be used in this situation. Load up all of the items at the start, and create an instance of Item (or Weapon, or Armor, or whatever) for each one, storing them by some identifier such as a name. Then, when someone wants to create a BronzeSword, you fetch the instance that corresponds to BronzeSword, and clone it (via a virtual clone() method). This clone can then be modified or destroyed, and you'll still have that original prototype for later use.
BUnzaga
BUnzaga
I suppose it wouldn't hurt to say that my first step was to create 'editors' for my items. I am also planning to do the same for characters, spells, quests, etc.

In my item editor, it is basically a GUI that I can use to create my item catalog file, so I don't have to do it all by hand. I can create a new item catalog, load one, or save one. This makes it a hell of a lot easier, plus it adds more of a visual to something that is abstract.

Then in my game I just load the file and parse the values into the object array.

The same can be done with characters, maps, spells, etc.

This goes along with what Rasm and everyone since has said. To add a new goblin, you would just use something like entity[n] = new npcList[7] or castSpell(new spellList[5]);

They load, take care of themselves, and then are unloaded.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.