Chunky by FelipeFS
Chunky by FelipeFS
GPWiki.org
It is currently Sat Jul 26, 2014 1:03 am

All times are UTC




Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Jan 07, 2014 9:46 pm 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
I'm writing this game in C++ and have a decent understanding of the language itself (to my knowledge, anyways) and am just having a bit of trouble finding out how to go about these Ability and Effect classes and their subclasses. I'm trying to make everything as OO as possible, however straying away from OOP will NOT be that big of a deal for me. Efficiency and readability is what I'm going for!

I was thinking about going one of two ways with the Effect class:

Have the Effect class have static functions which will create the
specified type of Effect, ie:

static Effect Damage(int dmg);
static Effect Burn(int dps, double duration); // dps rounded
etc, etc

and simply have the Effect class handle the Applying with a

switch(_type) // EType
in the

bool Effect::Apply(Entity* target){}
implementation?

OR to have a subclass of Effect for each type of Effect:

class DamageEffect : public Effect
{
yada yada;
};
and have each subclass override the:

bool Apply(Entity* target);
function?

Which one of these would probably be the better way to go? Or is there another way that would be similar?

Also the placement of certain things such as the Enums, and the formatting of my variables/ Function names (for example, instead of the:

static Effect Damage(int dmg);
using:

static Effect damage(int dmg);

I'm not sure if I'm formatting everything "properly" in terms of standards. I've seen it both ways in various places and I'm starting to want to go back to having only Caps with Object names and having variables & functions be lowerCamelCase?


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 11:05 pm 
Grand Optimizer

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 358
Location: Here (where else?)
Hi, welcome to the forum,

Kevinw778 wrote:
I'm writing this game in C++ and have a decent understanding of the language itself (to my knowledge, anyways) and am just having a bit of trouble finding out how to go about these Ability and Effect classes and their subclasses. I'm trying to make everything as OO as possible, however straying away from OOP will NOT be that big of a deal for me. Efficiency and readability is what I'm going for!
Then please let go of the idea "make everything as OO as possible". OOP has its good side, but "everything" is too much imho.

Kevinw778 wrote:
I was thinking about going one of two ways with the Effect class:
I have some mixed feelings about your Effect class, it feels as being too generic. I fail to see how it is useful to have an Effect class hierarchy. Can you explain how it actually affects your Entity?

What I am somewhat afraid of, is that in the end, you're going to have a switch statement (or a bunch of them) that performs "it" for every combination of Effect and Entity. If that's true, you have written a whole lot of code which is basically equivalent to
Code:
class Entity {
   ...
   virtual void Damage(int dmg);
   virtual void Burn(int dps, double duration);
   // etc
  ...
};


And doing damage to some entity is complicated from "entity.Damage(10);" to "DamageEffect e(10); e.Apply(entity);".
(I just picked one of your approaches in the latter example, I think they are mostly equivalent.)


Unless you do more with Effect, or at some points you don't know what effect is happening, I see no reason to make objects of them.


Kevinw778 wrote:
Also the placement of certain things such as the Enums, and the formatting of my variables/ Function names (for example, instead of the:

static Effect Damage(int dmg);
using:

static Effect damage(int dmg);

I'm not sure if I'm formatting everything "properly" in terms of standards. I've seen it both ways in various places and I'm starting to want to go back to having only Caps with Object names and having variables & functions be lowerCamelCase?
There is no standard for it, or rather, there are lots of standards. Just pick one, and stick with it.
(Personally, I like my variables_like_this to make them more distinctive from functions, but that's just me).

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 5:24 am 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
So... Basically just have things like:

Code:
int _dmg;
int _heal;
double _duration; // for dot (burn & poison) / heal over time

bool _stun;
bool _silence;

?
I feel like this MIGHT work? I'd have to have separate durations for the dot/hot & the stun/silence/ other effects like that - no big deal.
But I would HAVE to have all of these member variables for every ability even though not every ability will make use of them.. That bothers me but I guess it's not a big deal.


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 12:36 pm 
Bearer of the Golden Keyboard
User avatar

Joined: Sun Aug 05, 2012 9:32 pm
Posts: 241
I'm not sure if I understand your final statement correctly. You want to create a separate objects for different effects and let all of them have:
Code:
int _dmg;
int _heal;
double _duration; // for dot (burn & poison) / heal over time

bool _stun;
bool _silence;
etc...


inside of them?

By the way, just to add something from me

I don't really see a problem with doing it like this: create an AbstractClass of entityEffect and which will only contain act() or Update(), onAdd(), hasDuration() function function like this
Code:
class entityEffect {
private:
   bool longTerm;
   EffectEnum type;
public:
virtual void onAdd(entity* Ent); // this will happen when you add an effect to an entity
virtual void act(entity* Ent); // this is continual action that happens perhaps on every turn, or every step, wherever you want to call it on the entity
bool isLongTerm(); // returns longTerm, only longTerm effects will be added to the Vector
}


and then have a different object for a different effect and Statuses like you planned originaly (from what i understand?) like this:
Code:
class Effect_heal: public entityEffect {
private:
   bool longTerm;
public:
   Effect_heal(); // initialize duration and type maybe?
   void onAdd(entity* Ent){
   Ent->changeHealth(10); // or -10 for damage
   // maybe play animation on entity if necessary
   }
   void act(entity* Ent){}// act is empty, no need for long term effects here, longTerm = false too.
}

class Effect_stun: public entityEffect {
private:
   EffectEnum type;
   bool longTerm;
public:
   Effect_stun(); // initialize whatever needs to be.
   void onAdd(entity* Ent){
   Ent->addStatus(type); // this will add the status
   }
   void act(entity* Ent){} // empty again, stun does nothing else I think, so longTerm = false anyway;
}

class Effect_blessing: public entityEffect {
private:
   int duration; // this is going to have a continous 5sec effect on the entity
   EffectEnum type;
   bool longTerm;
public:
   Effect_blessing(); // initialize whatever needs to be.
   void onAdd(entity* Ent){
   Ent->addStatus(type); // this will add the blessing status
   }
   void act(entity* Ent){
   if(secondPassed()){
   duration--;
   Ent->changeHealth(3); // heals 3hp/s for 5 seconds
   }
   } // this now causes continous heal on the player
}


There might be some mistakes cause i wrote the whole thing on the wiki now and didn't test it.

the point now is, you can create as many different effects as you need and when an effect is thrown at an entity, the entity will check if it's a long term effect, if yes, add to Vector, if no leave it be. And after that it will call onAdd(this); to perform whatever action it supposed to perform when added to entity. then it will just cycle through the effects Vector and call Act(this); in apropriate time, like every second, every step, every turn, whatever you need it to be.

I forgot about this, but it would also be smart for the effect to remove it self from the vector if it's duration = 0 by calling something like Ent->removeFromVector(&this); Also as you can probably see here, i assume that the vector is of type EntityEffect* so it only stores adresses to different effects and not the effects them selves.

I apologize for every mistake I made in advance xD thanks

Edit: I made a few changes to the code because of the way I used private members in that AbstractClass made no sense, it's not exactly abstract now but I think it's more functional xD anyway, the code is not tested so I wrote it mostly for inspiration :d

_________________
Did you ever wonder, how time works?


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 8:46 pm 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Woah! Much appreciated!
Yeah I wasn't sure if making each effect its own class was an okay idea. Honestly I wasn't 100% sure how the other way would work out (just giving each ability each of the possible member data that Effect objects would have, then set them appropriately when needed), but I feel like that would have become too convoluted.

I will try something like what you posted out and let you know how that goes. Thanks again! :)


Top
 Profile  
 
PostPosted: Thu Jan 09, 2014 11:30 am 
Bearer of the Golden Keyboard
User avatar

Joined: Sun Aug 05, 2012 9:32 pm
Posts: 241
if there are any problems with the code that you have trouble understanding or something you had to fix, please reply so other users can find the solution here :) or if you use a different approach or something let us/me know ^_^

_________________
Did you ever wonder, how time works?


Top
 Profile  
 
PostPosted: Fri Jan 10, 2014 3:07 pm 
Digerati

Joined: Thu Sep 09, 2004 1:17 pm
Posts: 1798
Location: burrowed
I didn't want to post since i feel all has been said but it's an interesting topic anyways :D

I usually have my magiceffects setup in a similar way
The baseclass will only manage the underlying behavior, not apply any effects/damage or anything

Code:
class EntityEffect
{
  Entity attacker, target;
  float duration; // can be set to 0 to oneshot the effect, or set to a time based duration

  EntityEffect(Entity attacker, Entity target, float duration){} // constructor sets the fields and adds the effect to the target Entity

  void Tick(){}  // will count the duration down, and remove it when it goes <= 0
  void Add(Entity target) {}
  void Remove(Entity target) {}
}


Remember, since we have target as local member, we dont need to pass them as argument in the Add/Remove methods, it's just for visualizations sake.

On Add() the Effect will be added to a list of effects in the Entity. The Entity will walk through the list in it's own Tick method.
Side note: since the collection will be modified while working through it (if the duration is <0 in the tick it gets removed) we need to check whether it's been modified and prevent weird behavior. Theres 2 ways that are fairly straight forward to do this.

Code:
for(int i = 0; i < effects.count; i++)
{
  if(!effects[i].Tick()) //Have Effect.Tick return false if it just removed the entity and decrement the incrementor to compensate the now missing element in the next iteration
    i--;
}


Code:
for(int i = effects.count -1; i >= 0; i--)
{
  effects[i].Tick();
}


Since we count backwards in the second loop, we don't have to check or compensate for missing elements since we're leaving that behind us. It is probably the more pretty and straight forward version, but still i mostly use the first way, for whatever reason, i'm not sure :D it's clumsy and requires extra work, so meh, i should change my behavior i suppose.

Lastly a sample for a custom effect

Code:
class Stun: EntityEffect
{
  Stun(){} //calling base constructor stuff etc
  Tick()
  {
    base.Tick();

    if(firstTick) // You can add a bool firstTick or something in the base EntityEffect class aswell to check for intial damage for instance
      target.Damage(DamageType.Whatever, 9001);
    target.RestrictAttack(); //These methods could prevent input calls to the associated actions, or deny the actions directly before they're executed this tick, with these you can implement silence, disarm, entagle etc spells without much change in the underlying code
    target.RestrictMovement();
    target.RestrictCast();
  }
}


Well, so much for that. Hope it's helpful to you :D

_________________
Long pork is people!

wzl's burrow


Top
 Profile  
 
PostPosted: Mon Jan 13, 2014 11:08 pm 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Yeah this has all been very helpful so far!! I'm still in the process of implementing two different ways of going about this (separately, of course! :P)

My main concern at the moment is how to implement the "Tick()" method.

What I have is for an "Effect_Heal" object is...

Code:
void Effect_Heal::Tick(float deltaTime)
{
   int duration = _duration;
   Entity* target = this->GetTarget();

   while (duration > 0)
   {
      if (fmod(deltaTime, 1) == 0)
      {
         Act(target);
         duration--;
      }
   }
}


Is this... even a way of going about it?
The only issue is that the only place I have an sf::Clock instance is in my static ObjectManager instance.. Would there be a practical way of implementing this, you guys think?


Top
 Profile  
 
PostPosted: Mon Jan 13, 2014 11:31 pm 
Digerati

Joined: Thu Sep 09, 2004 1:17 pm
Posts: 1798
Location: burrowed
Ok lets go back a few steps.

Usually you have your game managed in a game loop

Code:
while(running)
{
// game stuff
}


Your game will never exit this loop. If it does, the game will quit. Since you have a delta time in your tick function i'll assume you have an idea about how this works.

Now you have your character class. This aswell has a Tick() or Update() method, which is called through the game loop

Code:
while(running)
{
// update delta time
foreach(character)
{
character.Tick(deltatime);
}
}


Now, in your character class you have your effects array/list/vector or similar.

Code:
class character
{
list<effect> effects;

public void Tick(float deltatime)
{
  foreach(effects)
  {
    effect.Tick(deltatime);
  }
}
}


So you have a call hirarchy like this:
gameloop>character>effect (you can further abstract this however you need it, just so you get the point)

Now comes the interesting part, your effect.Tick(). I'm not sure why you were using a while() loop, this would keep you trapped in your tick method until duration is <= 0. This is not desireable as it defeats the purpose of time based modeling (using delta time)

Code:
public void Tick(float deltatime)
{
  duration -= deltatime;
  if(duration < 0)
    return;

  heal += deltatime * healspersecond;
}


I dont know how familiar you are with time based modeling, so if you need some help there, let us know.

_________________
Long pork is people!

wzl's burrow


Top
 Profile  
 
PostPosted: Tue Jan 14, 2014 7:42 am 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Gaaaahahahah. Yeah I was thinking I'd have to go through a chain of sorts via the update, but I wasn't sure if that was the "right way to go about it".

As for time based modeling... Not at all, really.

I recall working on a Java project that required me to render various things and passed in at some point was the deltaTime and an FPS model of sorts. It was interesting.

But I will try and see what I can do with this reinforced knowledge (thanks again! :) while I await more awesome information to come my way! :D


Top
 Profile  
 
PostPosted: Tue Jan 14, 2014 9:40 am 
Digerati

Joined: Thu Sep 09, 2004 1:17 pm
Posts: 1798
Location: burrowed
Ok then,
The idea is that you handle your logic (move objects etc) by the amount of time that is passed between the current and the last frame, i.e. deltatime.

This is because frames (or ticks) alone itself aren't a good measure of progress (time wise).
Think about it this way: if you move a character 1 pixel per tick, it will move at different speed on different machines, because one might be faster than the other. A lot really old dos games suffer from this, because the devs didn't expect performance to go up at all i guess? :P

So what can we do here?

Code:
float ticktime, oldtime, deltatime = GetTickTime();
while(running)
{
  oldtime = tickTime;
  ticktime = GetTickTime();
  deltatime = ticktime - oldtime;

  character.Tick(deltatime);
}


This is a really basic implementation. GetTickTime() returns the duration your pc is running, or something like that. you store that in ticktime.
oldtime is the time of the last frame (thats why it is assigned before we set ticktime), and deltatime is the difference of the both. (i.e. the time it took the last frame to process, or, how much time passed since we were here the last time)

Important here is that the time is measured in seconds!

The idea now is that your Tick method provides you with deltatime as argument (or through other means like static variables).
Instead of just adding a static amount of pixels to your x value to move your character, you'll multiply by deltatime.

Code:
public void Tick(float deltatime)
{
  if(leftkey)
    x -= movespeedinpixelspersecond * deltatime;
  else if (rightkey)
    x += movespeedinpixelspersecond * deltatime;
}


Your movespeedinpixelspersecond would hold a value of 50, or whatever, and this method basically ensures your character is moving 50pixels per second, regardless on what machine it is running. In theory.

There is a lot more to this topic, and it is crucial to get this right for advanced things like physics or networking, but it is good enough for most simple games.

For further reading go to these places:
http://content.gpwiki.org/index.php/VB: ... delling_IT (gosh this needs an update too :D)
http://gafferongames.com/game-physics/f ... -timestep/

_________________
Long pork is people!

wzl's burrow


Top
 Profile  
 
PostPosted: Thu Feb 06, 2014 11:23 pm 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Alllllrighty I don't know if anyone is still going to be keeping track of this topic since it's been a while since I've posted, but it's worth a shot.

At the moment the structure of the Abilities & Effects are as follows:

Base classes:
Ability, Effect

Subbed Ability:
AreaAbility, SelfAbility, TargetAbility

Subbed Effect:
Effect_Heal, Effect_StatChange, Effect_Stun
-----

The Ability class is meant to hold the information for an Ability's ID, name, description, Type, cost, cooldown, and vector of Effects. It will handle the Acquiring of Targets appropriately depending on the Type of Ability it is (the subclass), as well as handling the Casting, interacting with the Player using the Ability.
The types of Abilities are currently as follows:
Self, Target, MultiTarget, Area, Pathed, and Visible (what is on the player's screen).
---

The Effect class will hold the information for the Effect's Target reference (will be passed the Target of the Ability which it belongs to), whether or not the effect will be "long term" & added to the Target's vector of Effects that will last some duration. (not sure whether or not to include the
Code:
float duration
in the base class or simply in the subclasses that may need it?)
There is a function to handle the Act that will be performed upon the Target, as well as a Tick function that will call Act for some amount of time (duration), as well as an "OnAdd" in case any additional actions need to occur when the Effect is applied to its Target? Not sure if the OnAdd function will be necessary.. Might just be able to use the Act function?
---

Now for the main reason I'm back on this topic!
I've been trying to figure out a relevant system of setting up my assets, including my Abilities & Effects.

From what I gather, things like Abilities are loaded as files (Fireball.abi, DoubleJump.abi, etc) and stored in a global vector of sorts to be accessed when needed? Not 100% sure. Basically I'm looking to make some sort of data files to then parse in C++.

It would be sort of in-depth since I'd have to load data from other files (the Effects) whenever an Ability calls for having an or many Effects as part of the Ability.

For example:
Code:
SelfAbility blessing = new SelfAbility();
heal.AddEffect(new Effect_Heal(20, 5)); // Heal 20 health each second for 5 seconds
heal.AddEffect(new Effect_StatChange(Effect_StatChange::Defense, 5, 5)) // Raise defense by 5, for 5 seconds


I feel like getting stuff like this parsed from a file would be... interesting to say the least.
Any ideas?


Top
 Profile  
 
PostPosted: Fri Feb 07, 2014 6:09 pm 
Grand Optimizer

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 358
Location: Here (where else?)
What part exactly from that example code do you want to load from file?
If it's just a couple of names and numbers, you may have enough with eg an INI file, or JSON.

If you want your own syntax, you'll end up with a lex and yacc generated parser.

If you want to be able to write the example code literally, with the flexibility of a programming language, you'll end up with a full programming language that you have to write, lex and yac is the easy part, then a year or so for the type checking and execution.

If you are thinking about the latter kind of flexibility, an alternative can be to integrate Lua or Python or so into the engine. Personally I wouldn't use Lua, but ymmv.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Sat Feb 08, 2014 4:16 am 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Wow, sorry, I should have posted an example of the types of files that I'd be parsing!

I was hoping to have something(s) like this!:

HolyBlessing.abi:

name: "Holy Blessing"
type: "Self Target"
cost: 20
cooldown: 8
effects: [ {"Heal.eft", 5, 5}, {"IncStat.eft", Def, 5, 5} ]

Something to this ~effect, hehe :D

And I would have a function inside the function loading from the file that separately parses the .eft files?

I'm curious about the whole Lex & Yacc generated parser.. Any links to some tutorial of using said method of parsing files? I googled a bit and found an explanation of how Lex & Yacc can be used to make compilers so...?


Top
 Profile  
 
PostPosted: Sat Feb 08, 2014 11:31 am 
Grand Optimizer

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 358
Location: Here (where else?)
Looks very parsable to me, you may want to add a separator (or terminator) token between the entries (like the ; in many programming languages), unless you're thinking about using the EOL character for that (which makes it a line-based format, one line = one entry).

Sorry, I have no idea about lex/yacc tutorials, I learned it from the Dragon book which explains it all in a few pages, under the assumption that you know about programming C and regular expressions. However, lex and yacc is the defacto standard for parsing everything from simple read-text-line-into-string manipulation up to a language like C, so the Internets are filled with tutorials.
The GNU variants are flex and bison, which may be useful for finding more tutorials :) Their documentation is also quite extensive.

I'd recommend to first do a bit of reading and a tutorial until you get the hang of it somewhat.

For an easy example, a parser I recently wrote (that is quite easy to study) is the sprite encoder in the CorsixTH project https://github.com/CorsixTH/CorsixTH-Graphics
The input (groundtiles.txt) file looks like
Code:
sprite 77 {
    base = "ground_tiles/s77.png";
    top = 0;
    left = 0;
    width = 64;
    height = 32;
}
the parser can handle a bit more than this (you can have recolour layer renumbering as well).

Source code is in the "SpriteEncoder" directory. "parser.y" and "scanner.l" are the parser and scanner definition files, parser.cpp and scanner.cpp are the generated parser and scanner (you can ignore them :) ) "tokens.h" is also generated (it contains the token numbers exchanged from the scanner to the parser). ast.h contains the data-structure created in the parser, where "ScannerData" contains the data part from the scanner to the parser (values of numbers, and names of identifiers).
"encode.cpp" is the main program where "int iRet = yyparse();" is the call to parse the input file, "iter->Check();" checks a loaded item for sanity, and "iter->Write(&out);" generates the output (ie the purpose of the program).

If you have further questions, feel free to ask.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Sun Feb 09, 2014 2:03 am 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
I'm not sure why, but this all seems a bit confusing to me :S
I'm not sure if it's because C is so very unfamiliar to me at this point (it's been a long time, plus when I did use it, it was for basic stuff & a little bit of Networking).

Another thing may be that I'm rather confused as to what Lex/Flex & Yacc/Bison are supposed to be doing.

From my understanding, Lex/Flex generate code & Yacc/Bison generate parsers? Though (and provided I'm correct <---), even knowing this information, I'm still at a bit of a stand still.

One of the things I've worked very little with in C++ is Input/Output, sadly enough - so this could also be one of the reasons I'm rather lost...


Top
 Profile  
 
PostPosted: Sun Feb 09, 2014 10:10 am 
Bearer of the Golden Keyboard
User avatar

Joined: Sun Aug 05, 2012 9:32 pm
Posts: 241
Ok i wanted to write bunch of code on how to parse that, but i got confused what
[ {"Heal.eft", 5, 5}, {"IncStat.eft", Def, 5, 5} ]
is supposed to mean:
[...] - a block of code?
{...} - like a function call?
"something.eft" - is this a path to a file? and if yes, what is/will be in this file? more code to parse?
,5 ,5 - Parameters for the Heal.eft ?
Def, 5, 5 - Parameters for IncStat.eft, but why two fives? increase_Stat Defence by 5 and 5 what?

^^; i am just so damn confused what is this exactly going to achieve, are you planning to store the code part somewhere or are you going to translate it to different code to be easier to read after the file is loaded into your game? Because once the file is writen by a human and loaded to a computer, it no longer needs to be easy to understand and you might as well change those into
myEffectVariable as String -> H,5,5|IS,Def,5,5
actually you know what, you just need to remove all the brackets right now and you get
"Heal.eft",5,5,"IncStat.eft",Def,5,5 and every new "file"/function just starts with " and ignores the second " si the quotation mark acts like a delimeter pretty much, and all of that code can be interpreted using a Split command, perhaps even if you can find the file Heal.eft while parsing this ability, and parse it in a similiar way and then replace "Heal.eft" with an adress to the parsed function, you might get something interesting xD and messy possibly... i dont know :) don't mind this last part, just explain what did you intend to do with all the symbols in the code and I might be able to provide you with a code to parse those perhaps.

peace out

_________________
Did you ever wonder, how time works?


Top
 Profile  
 
PostPosted: Sun Feb 09, 2014 11:41 am 
Grand Optimizer

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 358
Location: Here (where else?)
The basic problem you have to solve is to convert from
Code:
HolyBlessing.abi:

name: "Holy Blessing"
type: "Self Target"
cost: 20
cooldown: 8
effects: [ {"Heal.eft", 5, 5}, {"IncStat.eft", Def, 5, 5} ]
which in memory (or on file) is
Code:
"HolyBlessing.abi:\n\nname: \"Holy Blessing\"\ntype: \"Self Target\"\ncost: 20\ncooldown: 8\neffects: [ {\"Heal.eft\", 5, 5}, {\"IncStat.eft\", Def, 5, 5} ]\n"

to having some data structures in the machine, eg
Code:
struct EffectEntry { string eft_file; bool hasDef; int val1, val2; };
struct Ability { string abi_name; string name, type; int cost, cooldown; list<EffectEntry> effects; };
(Since I have no idea about the meaning of your example, I made assumptions on their type, and internal structure. However, even if you have a totally different runtime structure in mind, the problem does not change.)

So how do you go from a string value to these structures?
There are lots of solutions to this problem. Hazarth is trying to simplify the input text enough to make it easier to write a piece of string manipulation code to read it directly.
flex & bison take a different approach. They split the problem in 2 parts, scanning and parsing.

Scanning is splitting the input string into terminals, elementary symbols of the input. In your input they are
- identifier (for names like "HolyBlessing.abi")
- colon
- white space (\n, ' ', \t)
- comment (if you want to have it)
- keyword "name"
- keyword "type"
- keyword "cost"
- keyword "cooldown"
- keyword "effects"
- string ("\|Holy Blessing\"", or "\"Heal.eft\"", etc)
- number ("20", '8", "5" etc)
- square-bracket-open
- square-bracket-close
- curly-bracket-open
- curly-bracket-close
- comma
ie every kind of "word" in your input. A scanner takes an input file (or string), and splits it in a sequence of elementary symbols. Most such symbols are just a number (like value 1 means 'colon' etc), some other symbols carry additional information (eg the identifier symbol carries its value along).
Thus if you feed a scanner your example input, you get a sequence like
Code:
identifier(HolyBlessing.abi)
colon
name-keyword
colon
string(Holy Blessing)
type-keyword
colon
...
curly-bracket-close
square-bracket-close

I left out all white-space elements for brevity (they are normally discarded anyway).
Of course you can write a scanner manually, it's not even hard to do.
The point is however that this piece of scanner code is mostly the same every time, except for details of the collection of symbols that you want to recognize. Lesk saw this in 1975, and invented lex, a scanner generator.
You give it the list of symbols you want to recognize, and it spits out source code of a scanner in less than a second. You compile the code, and you have the above scanner code implemented faultless (if your list of symbols was ok :) ). If you know what to do, it's less than 15 minutes work.

Ok, we have this sequence of tokens (the common name for instances of the symbols after they ware created by the scanner), now what?
Here comes the parser into play. It takes a list of tokens, as created by a scanner, and decides what belongs together by means of production rules.
A rule is just a list of symbols that should come along from the scanner. If that happens, the rule is said to match, and a piece of code is executed. That code is supposed to combine all information of the list of symbols into a new symbol. That new symbol can be used in other production rules.
An example, say "cost: 20" is something you want to recognize. Then you write a production rule like
Code:
Cost : KEYWORD_COST COLON NUMBER { <code> };
By convention, token names (ie the elementary symbols coming out of the scanner) are written as all uppercase.
This rule says (start reading after the colon), 'when you see the sequence "keyword-cost, colon, number" from the scanner, execute <code>'. The result is then a new symbol called Cost. (there are details here that I left out, namely that you have full control over when a production rule can occur in the input, but that's a bit too much for now).

Obviously, you have to write a production rule for every input that you want to recognize.

As with the scanner, it is possible to write parser code to do this recognition by hand. For simple cases it's quite doable. For more complicated cases, it gets harder, upto quite impossible.
The problem is however still always the same. You have a sequence of input tokens from the scanner and you have to match them against production rules. Some smart people solved this in a generic manner (also in the 70's), and put the result in yacc, a parser generator. (Even nowadays, people are still working on improving parsers and parser generators, ie it's not a completely solved problem. They address much more challenging recognition problems than what yacc can handle though.)
Basically, you give yacc the list of input symbols and the list of production rules you want to recognize, and it spits out source code for a parser also in about a second. Compile the code (together with the source code of the scanner), and you're done.

You point the scanner+parser to a file, and say "result = yyparse();". It reads the file, converts to tokens internally, fires the <code> thingies in the production rules that are matched, and you get the output of the last <code> thingie that fired. No need to do messy string manipulation, or file IO, it's all handled by the generated code.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
PostPosted: Tue Feb 11, 2014 7:29 am 
Level 1 Cleric

Joined: Wed Dec 11, 2013 7:13 pm
Posts: 13
Hazarth wrote:
Ok i wanted to write bunch of code on how to parse that, but i got confused what
[ {"Heal.eft", 5, 5}, {"IncStat.eft", Def, 5, 5} ]
is supposed to mean:
[...] - a block of code?
{...} - like a function call?
"something.eft" - is this a path to a file? and if yes, what is/will be in this file? more code to parse?
,5 ,5 - Parameters for the Heal.eft ?
Def, 5, 5 - Parameters for IncStat.eft, but why two fives? increase_Stat Defence by 5 and 5 what?

^^; i am just so damn confused what is this exactly going to achieve, are you planning to store the code part somewhere or are you going to translate it to different code to be easier to read after the file is loaded into your game? Because once the file is writen by a human and loaded to a computer, it no longer needs to be easy to understand and you might as well change those into
myEffectVariable as String -> H,5,5|IS,Def,5,5
actually you know what, you just need to remove all the brackets right now and you get
"Heal.eft",5,5,"IncStat.eft",Def,5,5 and every new "file"/function just starts with " and ignores the second " si the quotation mark acts like a delimeter pretty much, and all of that code can be interpreted using a Split command, perhaps even if you can find the file Heal.eft while parsing this ability, and parse it in a similiar way and then replace "Heal.eft" with an adress to the parsed function, you might get something interesting xD and messy possibly... i dont know :) don't mind this last part, just explain what did you intend to do with all the symbols in the code and I might be able to provide you with a code to parse those perhaps.

peace out


[ {"Heal.eft", 5, 5}, {"IncStat.eft", Def, 5, 5} ]
was supposed to be a way to organize the information... Though you're completely right, something like

H,5,5 & IS,Def,5,5 would work just fine. Or maybe what I had written but without the []s & {}s, even.

Either way I was looking to be able to parse that information and put them into Objects which could be used in other Objects (Effects being put into their respective Abilities).

Basically I want to be able to have a point where I'm loading all of my files (not sure about the most efficient way to do that, either), and be able to simply read each Ability file and have them be parsed and create each Ability when called for being loaded?

For example, somewhere in the code:

Game.LoadAbilities();
which is defined as..

bool Game::Game::LoadAbilities(std::string filename)
{
forEveryElementIn (Game.GetAbilities(std::string filename)) // folder location where the Ability files are stored
iterator->LoadAbility();
iterator++; //etc, etc
}

LoadAbility would be defined as..

bool Ability::Load(std::string filename) // a reference to where the Effect files are stored will be needed as well
{
After a certain part of the Ability has been read (for example, the cooldown, the "LoadEffects" function would be called on the following lines being read in to Load? This method would parse the line beginning with "effects:" in the example file to create Objects - Effect_Heal with a heal amount of 5, for 5 seconds, Effect_StatChange with a stat of Defense, increased by 5 for 5 seconds, both of which are then added to the previously created SelfAbility Object with a name of "Holy Blessing", type of Self, and cost/cooldown of 20/8 respectively.
}

Those created abilities can then be used throughout the Game by being referenced either by an ID or their name (should... probably include an Ability ID, huh? :D)

Alberth - I'm REALLY glad you were able to clear up what Lex & Yacc are supposed to do & how they do it.. Now the part that I'm curious about is how to have some sort of a skeleton for utilizing Lex & Yacc for my project? The repo you linked me to is a nice concrete example of what you were explaining on this topic - however it's either the language or the plethora of % signs everywhere that are confusing me o_O

Sorry 'bout the nitpicking... C isn't really a language I've worked much with... Any chance you could help a fellow nerd out w/ a C++ version? :D


Top
 Profile  
 
PostPosted: Tue Feb 11, 2014 7:34 pm 
Grand Optimizer

Joined: Sun Oct 16, 2011 3:09 pm
Posts: 358
Location: Here (where else?)
Kevinw778 wrote:
Alberth - I'm REALLY glad you were able to clear up what Lex & Yacc are supposed to do & how they do it.. Now the part that I'm curious about is how to have some sort of a skeleton for utilizing Lex & Yacc for my project? The repo you linked me to is a nice concrete example of what you were explaining on this topic - however it's either the language or the plethora of % signs everywhere that are confusing me o_O
I don't think you're going to understand without reading about it, and doing some experimenting.
The % thingies are mostly explained in tutorials, the others are probably not that interesting at first :)

I don't have a standard setup, I don't write these things often enough for that. I usually follow a pattern of first writing the parser rules without the "{ <code> }" stuff. The parser generator then generates the symbols I added in the production rules, and I use that as starting point for writing the scanner specification. That code usually starts with just { return TOKEN_NUMBER; }. If you then compile the generated source codes, you have a complete parser in the sense that it gives an scanner or parser error if the input is wrong (and nothing if you give it correct input). From there it's a matter of adding the other code (which is usually very straight-forward).

The disadvantage of that approach is that you need to have a complete parser and scanner specification before you can generate both source codes. Before that moment, there is not much you can run.

The other way around works too, it's perfectly allowed to run just the scanner, and print the symbols it finds. The parser however defines the values to use for the input symbols, so you may have to adjust that later.

Either way, the hardest part is writing the production rules. It's not something you can just do, you need to learn that with a some tutorial reading and hands-on experimenting. I can help you when you get stuck, but I won't solve your problem for you.


Quote:
Sorry 'bout the nitpicking... C isn't really a language I've worked much with... Any chance you could help a fellow nerd out w/ a C++ version? :D
Well, every C program is also a C++ program, so problem solved :D

Actually, the CorsixTH sprite encoder program IS not C but a real C++ program. Notice the "new" at line 56, and the include of <cstdio> ?
Besides parsing, the { <code> } is really not doing anything else than filling the "std::set<Sprite> g_oSprites;" set, where the 'spriteSettings' production rules fill objects of type Sprite. It's a bit split up between all the rules, but that's it.
(Unless you peeked in the generated parser.cpp and scanner.cpp files. Really don't do that, it's way too complicated to understand without studying scanner and parser theory first.)

The FreeRCT parser generator file is at http://code.google.com/p/freerct/source ... n/parser.y (scanner generator input file is in the same directory) It's C++11 code, but the global structure is exactly the same as the small program. It's however an order of magnitude bigger, so easier to get completely lost in it.

_________________
My project: Messing about in FreeRCT, dev blog, and IRC #freerct at oftc.net


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 28 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

Powered by phpBB® Forum Software © phpBB Group