Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » RPG Combat

This thread is locked; no one can reply to it. rss feed Print
 1   2 
RPG Combat
spellcaster
Member #1,493
September 2001
avatar

I'm currently trying to find an easy way to do combat in an RPG.

My current idea is like this:
You have attributes, like strength, decterity, iq, etc. An average attribute would be 25, with something like 50 really good, 75 legendary and everything higher than 90 would be god like.

Every action (attacks, spells, etc) are skills.
Skills have an upper limit of 20 and are based on an attribute.

Club fighting could be a strength based skill.
Defense is either strength based or dexterity based, depending whether he has a shield or not.
Armor gives you a bonus.

Say, Gargle the Dwarf fights againts StoneStick the Troll.

Gargle uses his club:
Club: 5/20 (25%), Str 45 -> Total 45 * 0.25 = 8.5

StoneStick has no defense weapon, so he has to dodge:
Dodge: 2/20 (1%), Dex 18, Armor (stone skin): 10 -> Total 28 * 0.01 = 2.8

Both roll a d100 now.

Gargle rolls a 20
StoneStick rolls a 55.

We multiply the rolled number by the factor:
Gargle: 8.5 * 20 = 170
StoneStick: 2.8 * 55 = 154

So Gargle hits. The damage inflicted is based on the weapon, his club does 4-10 points of damage.

Ok, comments on that system? How are you guys handling combat normally? (Mandrake?)

I'm just not sure if the idea of making the attacks dice based is a good one. I mean, with some bad luck the player could miss all the time..
which would be really frustrating.

Maybe rolling a d10 or d20 might be better? Help?

--
There are no stupid questions, but there are a lot of inquisitive idiots.

23yrold3yrold
Member #1,134
March 2001
avatar

Sounds good. You could read up on AD&D rules and steal a few ideas ;) Mind you, it sounds like you already have.

--
Software Development == Church Development
Step 1. Build it.
Step 2. Pray.

spellcaster
Member #1,493
September 2001
avatar

Hm... is this close to D&D? I only know the 3rd edition, and I think my system is different.

In fact, I'm even considering to allow instant-hits (as long as you don't goof up)...

But then I'm not sure how armor would be used, and how to determine the damage... hints? help?

--
There are no stupid questions, but there are a lot of inquisitive idiots.

nonnus29
Member #2,606
August 2002
avatar

I really couldn't follow the logic in your system. First you calculate attributes and bonuses to come up with a multiplier then multiply that by the percentage rolled by the d100. Seems to be almost complete chance since the d100 outwieghs everything.

An alternative form of dice rolling that alot of paper and pencil rpgs use is to assign a number of six sided dice depending on 1. amount of skill with an item/spell 2. character attributes (str/dex/int etc) 3. dice for any bonuses (ie character had a blessing placed on him/her). Then you would roll your fist full of sixers and pray. Variations of this theme were used by: Shadowrun (FASA), StarWars RPG, Space: 1889, Risk(!), and TMNT (I think). EDIT Warhammer 40k too.

Korval
Member #1,538
September 2001
avatar

First question right off the bat: is this a real-time fighting system (where deciding whether you hit/miss is based on whether your sword model/sprite contacts the enemy) or a non-real-time system (where deciding whether your hit/miss is based on a random number)?

If it is the first, then you task is a bit easier: you only have to worry about damage stats (unless you want to get into weapon/recovery speed, where a 2-handed sword is swung slower than a 1-handed sword. If you touch that, then you need to do some damage-over-time balancing, but I digress...).

But, in either case, you do need to do some long-range planning now. First, decide what you maximum level is going to be. In videogames, players expect to level-up fairly often, so having a large level range is a good idea. This means, btw, that the difference between two consecutive levels is not that great.

I would suggest that you have 50 levels or more. Now, there's two ways of going about raising levels.

One way is the common D&D method. Your character is a level-N character. Level N for that character's class(es) provides the following bonuses/detriments in combat. In this case, the player's basic stats don't increase in level, but the player does do more damage with their weapon of choice or gain new abilities.

The other way is that each stat has a particular level. When the player gains XP, he can use those points to buy a level of experience for a particular stat. That way, if you've got a swordsman, you will want to spend time leveling up stats that help his sword skill, not things that make him a better wizard.

The D&D method has several advantages:

1) Easy to balance. Because there is just one number that changes (though potions or the like may change a particular stat, stat changes are rare), it is easy to alter the system. Since the user has little control over it, the user can't find that perfect combination that makes a Level 5 character as powerful as a Level 8 character. You are in full control.

2) Less micromanagement. The user doesn't really have to do anything more than kill stuff. The game will automatically improve their character when he level's up.

But, on the other hand, the second method has its advantages too:

1) Good for a non-class-based (or flexible-class) system. If I want a system where a character doesn't have a particular "class", then I would want to use something like this so that the user can customize the benifits of gaining experience for his character.

2) More micromanagement. Some people like micromanagement (I, personally, prefer just dealing with armor, weapons, and baubles without having to also balance stats). I would suggest, however, that, if you go this route, keep the party small. The total party, not just the number you can bring to a battle simultaneously.

Once you have made those important decisions, you now have one more to make: just how much is a lot of damage, anyway?

The Tarasque, one of the most feared creatures in all of D&D, has less than 200 Hp. A level 10 fighter with 80 Hp is either a fluke of lucky rolling or gets a large constitution bonus (which, ultimately, is the same thing).

On the other hand, in the average Final Fantasy game, 80 Hp is nothing past the middle of the game. The common monster at that point is lobbing 300-400 Hp blows, with powerful boss attacks reacing 1500 or more. The last boss will, likely, have tens, if not hundreds, of thousands of Hp.

One of the reasons for this is obvious: FF is a videogame, while D&D is a board game. In a videogame, you have a large level spread. You, also, have a lot of ground to cover with monsters that are steadily increasing in difficulty. The easiest way to make monsters tougher is to give them more Hp. Of course, this means that your party needs to do more damage per level now.

The way that D&D gets away with relatively low Hp is that they use other means to balance tougher enemies. AC, giving the enemy multiple attacks, making the enemy do more damage to you.

Here's the math of the entire problem. There is a ratio of the following things:

Hp taken by the party
Hp done to a group of monsters

  1. of Hp that the party has (modified for cure spells/items)

  2. of Hp that the enemies have (modified for cure spells/items)

Now, to complicate matters, the damage is not a simple question of, "how much damage does an enemy take from an attack?", but the average damage over time (DOT). If a sword does 10 damage every second, and another sword does 5 damage every .25 seconds, the 5 damage sword has a greater DOT, and ultimately, greater damage output.

Therefore, to lower the DOT done by a particular attack, you can make it do less damage outright, or make it hit fewer times. If the 10 damage sword from above hit 90% of the time, and the 5 damage one hit only 10%, then the 10 damage sword would have a greater DOT.

D&D tends to balance enemies by giving them a few more Hp, better armor, and more powerful attacks (that hit more often). A party, to defeat these enemies, will need to have a bit more Hp (a level or 2 worth of Hit Dice), more DOT (better THAC0), and better armor (lower's enemy's DOT).

You have to find level equations and so forth that keep this ratio in balance for the entire game. If it goes too far out of balance, then either the game is too easy or too hard.

Also, consider at this point whether or not you want it to be class-based or classless. If it is "classless", how classless is it? 3rd edition D&D has some pretty simple rules for multiclassing, which makes for some customizability in your character.

Now that we have thought through all of that, we come to the really hard work: designing the actual system.

If you're using a single level-based system (like both D&D and FF), you need to determine your min/max levels and min/max stats. Then, you need to decide on an equation that converts these stats, plus the stats of the weapon in question, into damage. It should, also, take into account the armor rating of the character being hit.

Note that D&D does not use armor to lessen the blow of attacks. Armor is only used to deflect attacks. The idea is that the armor either prevented the attack or the full attack pierced it. FF, by contrast, factors the armor into the overall damage done by an attack and, typically, uses just a Dexterity-like stat to avoid the damage. Using this method, it is possible to get a hit that ultimately does no damage.

Your equation may, like FF's, want to take into account elemental affinities of the attacking weapon and the defending creature.

The D&D equation (for a basic fighter) is simple: weapon damage + strength bonus/penalty (if any). FF's equations are, typically, much more complex. Typically, you will want some kind of random jitter to the output of the equation.

If you are going to have to-hit rolls (non-realtime systems), then you have to have an additional equation.

BTW, you should create stats as you need them; don't create them beforehand. If you don't have a dodge-roll, then you shouldn't have Dex/Agility unless they factor into something else (like ranged attacks).

Here's an example (off the top of my head) of both to-hit and damage from a table-top RPG I abandoned. This, btw, is a stat level-based system, so there is no character level:

Character stats:
Strength
Agility
Natural Defense
etc... (don't remember the rest, but they don't matter for melee).

To-Hit roll:
Strength Level + Weapon Attack Level vs. Agility Level + Armor Dodge Bonus

The characters both roll (D100) on the resulting levels. The results are computed by looking up that level's entry on a chart and seeing what color it is (there were 6 possible colors). Higher levels had higher probabilities for rolling the higher colors. If the attacker's color is greater than or equal to the defender's color, then the attack hits.

Damage:
(Strength Level + Weapon Attack Level) - (Natural Defense Level + Armor Defense Level)

The resulting of this is looked up in a different table. This table turns a level into a Hp damage amount. The defender suffers this much Hp in damage.

In this system, the color chart and the damage chart are the balancing factors (along with the power of weapons and the species-based caps on Strength Level).

I could have, easily enough, added a "finess" stat that was used in place of Strength, so that high strength (and therefore, higher to-hit changes) didn't mean high damage. But, then again, given the color chart method of determining hits, I found that attackers didn't miss much unless the levels between them were very large.

Once, I designed a combat system on the presumption that all attacks hit unless the defender avoids them. This makes some reasonable amount of sense. So, the attacker didn't even roll on the to-hit; only the defender did. Granted, this roll took into account the appropriate stats on the attacker, but it required only one roll. Think D&D's THAC0, only the defender does the rolling.

Ultio
Member #1,336
April 2001

Why not come up with a completely new system, some system that doesn't seem so damn turn based, unless that's what you're going for. How about a system where both player and enemy decide what action they are going to take at the SAME time, and then they are executed.

For instance, if they both attack, they can both take damage from eachother - in the same "round". Also, if one decides to attack and the other decides to parry, you would get the scenario you have already pointed out. Also, if they both decide to parry, nothing happens. Of course, this would be kind of wacky when you have a battle of 3v3, as one player could be swamped by all 3 of the other enemy at once - but wouldn't this be a totally new spin on things? (If you've ever played DragonSeeds for playstation you'll know vaguely what I'm trying to say. The system in the game is really cool. You have to anticipate what the enemy is going to do and base your actions off of what you THINK is going to happen. Once false move can screw you up real bad.)

To tell you the truth, I'm kind of sick of the repetitive battles that you see in every RPG. Why are monsters stupid? I've never seen a game where the monster realizes that, say, you've been using the same magic spell over and over and over again. Why not cast an anti-magic shell on yourself? Or, how about, the monster is almost dead. Why not heal yourself? Heh. Of course, this might lengthen battles a little too much - but what's the point if you're going to automatically win the battle anyways? Heck. You might as well gain your exp from walking around, and just fight the bosses.

[rant]Waiting for the annoying enemy to cast that spell that takes so damn long is annoying, too. Faster battles by far is way better than fancy looking spells that just get annoying after seeing it for the Nth time. [/rant]

Hm. This post was probably not helpful at all. Oh well. Good luck!

---
"It's nice knowing that someday, the software I write will just be pirated by millions of people." :-/
http://loomsoft.net | Zep's Dreamland | Allegro Newbie Tutorials

spellcaster
Member #1,493
September 2001
avatar

Quote:

I really couldn't follow the logic in your system. First you calculate attributes and bonuses to come up with a multiplier then multiply that by the percentage rolled by the d100. Seems to be almost complete chance since the d100 outwieghs everything.

The idea is that the d100 roll is modified by the attack or defence rating. Ín the above example, the Dwarf is 4.7 times more likely to win the contest than the Troll... but you still have some element of luck.

Quote:

An alternative form of dice rolling that alot of paper and pencil rpgs use is to assign a number of six sided dice depending on 1. amount of skill with an item/spell 2.


Yes, a dice pool system would be an option.
Main problem here is to come up with a system that is not too close to any of those you mention. Oh, "Vampires of the old world" is using a pool system as well.

Hm..

--
There are no stupid questions, but there are a lot of inquisitive idiots.

nonnus29
Member #2,606
August 2002
avatar

Korval he said:

Quote:

I'm currently trying to find an easy way to do combat in an RPG.

:P

EDIT: Impressive post nonetheless...

Korval
Member #1,538
September 2001
avatar

Quote:

Korval he said:

Quote:

I'm currently trying to find an easy way to do combat in an RPG.

Anything worth doing is worth doing well, and nothing is ever easy.

dudaskank
Member #561
July 2000
avatar

And what about the Pokémon fighting style?

Ok, not so complex / impressive like the others mentioned, but I like it!

  • choose the attack of each pokémon

  • Determine the first pokémon to attack
  • if(pkmn1.speed > pkmn2.speed)
       first = pkmn1;
       second = pkmn2;
    else
       first = pkmn2;
       second = pkmn1;
    attack(first, second);
    attack(second, first);
    

  • Determine if hit or no based on attack chance of hit

  • Each attack have different powers (i.e. tackle = 20, explosion = 150) and type (normal, fire), calc the damage based on strenght or special stats and type of the two pkmn
  • 1void attack(POKEMON p1, p2)
    2{
    3 int damage;
    4 // hit return truee if hit the enemy
    5 if(!hit(p1.attack))
    6 return;
    7 switch(p1.attack.type) {
    8 // phisical
    9 case PHISICAL :
    10 // I don't have any idea how this is calculated
    11 damage = (p1.attack.power * p1.strenght) - (p1.attack.power * p2.defense);
    12 break;
    13 // special attack
    14 case SPECIAL :
    15 // I don't have any idea how this is calculated too
    16 damage = (p1.attack.power * p1.special) - (p1.attack.power * p2.special);
    17 break;
    18 }
    19 if(critucal_hit())
    20 damage *= 1.5;
    21 if(super_efetive(p1, p2)
    22 damage *= 1.5;
    23 if(not_very_effective(p1, p2)
    24 damage /= 1.5;
    25 if(damage < 1)
    26 damage = 1;
    27 p2.hp -= damage;
    28}

    Hope it helps you in something...

    ^__^

    Toque a balada do amor inabalável, eterna love song de nós dois
    Eduardo "Dudaskank"
    [ Home Page (ptbr) | Blog (ptbr) | Tetris 1.1 (ptbr) | Resta Um (ptbr) | MJpgAlleg 2.3 ]

    Inphernic
    Member #1,111
    March 2001

    Valkyrie Profile's battle system was pretty great. It was both turn-based and real-time - you could toggle characters to attack at any time (if it was your turn), creating some massive havoc if all of your 4 characters were attacking at once (and one hit added to the combo counter, which allowed super attacks when full).

    Also if the enemy ended up flying because of someone's attack, and then one of your characters came with a "land" attack, he/she couldn't hit the enemy.

    Timing and knowing your characters' attacks was everything.

    Quoting Working Designs : "For all the ways that I loved Valkyrie Profile is was the combat that was the real tour-de-force. Battles begin as frenetic swirls of sprites and sounds and button mashing that eventually sort themselves into a rhythmic sort of combat-song. The bangs and crashes and swipes and smashes frame the screamed lyrics of the Einherjar - lines like "My life is strewn with corpses" and "This is the law of heaven." It's almost physical and at the same time sublime, especially the moment you realize that the chaos on the screen is perfectly under your control."

    The whole game reeks of mad leetness. :D :D :D Needless to say, I <3 VP.

    Star Ocean 1 & 2 had a similar system in a more real-time form.

    spellcaster
    Member #1,493
    September 2001
    avatar

    Inphernic: Sounds good... do you have some more information on the mechanics?

    dudaskrank: I'd like to have something like pokemon, but the problem is: how do you determine if an attack is a success?

    Say you have a ATK and DEF rating.
    If you simply compare if ATK >= DEF to determine if you hit, you have no chance at all to hit if the DEF value is higher than your ATK... which is not that good. You should always have a small chance to hit.
    Maybe with a dice pool?
    If you have as many dice as points in your ATK and DEF? So, for each point you roll a D10. At the end you compare the sum of the ATK and DEF values.
    That could work.
    Armor could now simply add points to the DEF value. And the damage inflicted could be simply a formula based on the STR and weapon data.
    Hm... and if the rolls are really good, you could give him a damage bonus...

    Yes, I think I like this.

    Any other suggestions?

    --
    There are no stupid questions, but there are a lot of inquisitive idiots.

    dudaskank
    Member #561
    July 2000
    avatar

    In Pokémon, each attack have your own success rate:

    I.e. a tackle, thunderbolt and surf attacks is 100% hit chance, but other like thunder, blizzard, fire blast, fissure and others are less than 100%.

    But remember you can alter this rate using some attacks like flash and sand attack (decreasing attack rate)

    So, to calc if the pokemon1 hit the other, do like this:

    int hit(PKMN_ATTACK attack)
    {
       int n;
       // 0 to 100
       n = rand() % 101;
       if(attack.hit_chance <= n)
          return 1;
       else
          return 0;
    }
    

    ^__^

    Toque a balada do amor inabalável, eterna love song de nós dois
    Eduardo "Dudaskank"
    [ Home Page (ptbr) | Blog (ptbr) | Tetris 1.1 (ptbr) | Resta Um (ptbr) | MJpgAlleg 2.3 ]

    Derezo
    Member #1,666
    April 2001
    avatar

    Well, I'm doing it like so right now, in my MUD:

        diceroll = rand()%100;
    
        if ( diceroll == 0 || ( diceroll != 99 && diceroll < (victim->agi/10)-(ch->agi/10)))
        {
      // miss.
      return 0;
        }
    

    For a regular hit. :)
    It's dependant on the aggressor's agility (ch) and the victim's agility. Agility ranges from 0 to 255.
    Skills would be the same, and currently spell success rate is 100%.. ::)

    I haven't tested this system a lot, but it seems to work rather well in terms of hit/miss calculations.

    As for defense.. I use that in determining the damage dealt.. rather than the success rate. If you're strapped down in armor, they'll still be able to hit you just as easily.. it just might not cause any/much damage :P

    "He who controls the stuffing controls the Universe"

    Korval
    Member #1,538
    September 2001
    avatar

    Quote:

    diceroll = rand()%100;

    I don't think that's going to give the proper behavior. If RAND_MAX (implementation dependent) is not divisible by 100, then your results will be skewed. Don't take this probability skewing lightly; it will ultimately effect how things work out.

    Back to the actual question.

    OK, so you're looking for to-hit rolls. Step one is to think about what you want.

    There are 2 sides to the equation: the attacker and the defender. The attacker has some computation that determine's his attack to-hit (ATH). The defender has a computation that determine's his defender evade (DE).

    Now, we need to decide what these numbers mean. Since they are fairly abstract at this point, we only have the relationships between them.

    Let's take a simple, and very important, relationship:

    ATH == DE.

    In this case, what should happen? Does this mean that the attacker should win the majority of the time, the defender should win the majority of the time, or that it should be a coin toss?

    I would suggest that it should be a coin toss. Literally 50-50 percentage if they are equal.

    Now, another relationship:

    ATH >= 2 * DE

    The attacker has, at least, twice the value of the defender. I would suggest, depending on what you want, that this means the attacker should will a proponderance of the time. Possibley 75-80% of the hits.

    Next:

    ATH >= 4 * DE

    I would suggest that, in this case, the attacker automatically hits (roll anyway for critical fumbles/hits if you want them).

    The probability relationship seems relatively simple. The probability is based on the following number:

    ATH / DE = ProbValue;

    The easiest way to implement this in code is to use a bunch of if-then statements on the value of ProbValue, and interpolate the probability between two ProbValue numbers. Take the following:

    1int iToHitNumber;
    2 
    3if(ProbValue < 0.25f)
    4{
    5 iToHitNumber = 0;
    6}
    7else if(0.25f <= ProbValue && ProbValue < 0.5f)
    8{
    9 float fNormDiff;
    10 fNormDiff = (ProbValue - 0.25f) * 4.0f;
    11 iToHitNumber = (int)(25.0f * fNormDiff);
    12}
    13else if(0.5f <= ProbValue && ProbValue < 1.0f)
    14{
    15 float fNormDiff;
    16 fNormDiff = (ProbValue - 0.5f) * 2.0f;
    17 iToHitNumber = (int)(25.0f * fNormDiff) + 25;
    18}
    19else if(1.0f <= ProbValue && ProbValue < 2.0f)
    20{
    21 float fNormDiff;
    22 fNormDiff = (ProbValue - 1.0f);
    23 iToHitNumber = (int)(25.0f * fNormDiff) + 50;
    24}
    25else if(2.0f < ProbValue && ProbValue < 4.0f)
    26{
    27 float fNormDiff;
    28 fNormDiff = (ProbValue - 2.0f) / 2.0f;
    29 iToHitNumber = (int)(25.0f * fNormDiff) + 75;
    30}
    31else
    32{
    33 iToHitNumber = 100;
    34}

    iToHitNumber is then tested against a D100 roll. if(iToHitNumber < roll), then you hit. Otherwise, you miss.

    BTW, note that, if you're paying attention and thinking about code, you can see classes and object naturally develop from the above. You can see you'll need a class that can generate ATH and DE for a given type of attack. You will, also, need a mediator class that will take ATH and DE, do the required probability roll, and get the results.

    The good thing about this method is that it is open-ended. ATH and DE are both floats and are computed in a completely unimportant way. There are no limits placed on ATH or DE; they could be between 0 and 10, or they could be any arbiturary number. This way, all you need to think about in terms of balancing this method is what factors into ATH and choosing good monster DE's so that you get the results you're looking for. If you want lots of hitting on both sides, each side's ATH should be at least 3x the expected DE of the other side. If you like lots of misses (you should make up for this by making monsters die in a very few hits), keep ATH close to DE, or let DE slip a little higher.

    dudaskank
    Member #561
    July 2000
    avatar

    Quote:

    else if(0.25f <= ProbValue && ProbValue < 0.5f)

    The first part (o.25 <= ProbValue) don't need to be tested, since if the value is minor than 0.25, only the first if is entered, so:

    if(ProbValue < 0.5)

    Will do the work, and the same to all the others if's

    Quote:

    I don't think that's going to give the proper behavior. If RAND_MAX (implementation dependent) is not divisible by 100, then your results will be skewed

    Well, I don't know what is "skewed" (I'm Brazilian) :P, but, what matters if RAND_MAN is not divisble by 100? It's used only to get a random number between 0 and 99. (and in my code I pu rand() % 101, but maybe the correct is 100)

    ???

    Toque a balada do amor inabalável, eterna love song de nós dois
    Eduardo "Dudaskank"
    [ Home Page (ptbr) | Blog (ptbr) | Tetris 1.1 (ptbr) | Resta Um (ptbr) | MJpgAlleg 2.3 ]

    spellcaster
    Member #1,493
    September 2001
    avatar

    Guess I'm using something like this:

    resultATK = rollDice() - attacker.attack;
    if resultATK >= 0 {
         resultDEF = rollDice() - defender.defense;
         if (resultDEF < 0 || resultATK < resultDEF) {
              /* attack succesful */
         } else {
              /* defense succesful */
         }
    }
    

    This seems to be the most simple solution :)
    And I want to have something very simple...

    --
    There are no stupid questions, but there are a lot of inquisitive idiots.

    Korval
    Member #1,538
    September 2001
    avatar

    Quote:

    Well, I don't know what is "skewed" (I'm Brazilian) , but, what matters if RAND_MAN is not divisble by 100? It's used only to get a random number between 0 and 99. (and in my code I pu rand() % 101, but maybe the correct is 100)

    Well, if you went and actually rolled 2D10 several million times (limit as number of rolls approaches infinity), you should hit each number the same number of times. In short, the probability of rolling a 4 is the same as rolling an 86 or a 34, or any other values from 0 to 99.

    Keeping that in mind, let's say RAND_MAX is 150. It probably isn't, but let's say it is for the sake of argument.

    What is the probability, using rand() % 100, of rolling a 66? The only way rand() % 100 == 66 is if rand() == 66. So, the probability (assuming a perfect rand()), is 1/150.

    What is the probability of rolling 8? Well, if rand() == 8, then rand()%100 == 8. But, if rand() == 108, then rand()%100 == 8 too. Therefore, the probability is 2/150.

    Your probabilities are now no longer correct. Given this example, it is twice as likely to come up with a number less than 50 than it is to come up with one greater than 50.

    The correct way to do this is to check to see if rand() >= (RAND_MAX - RAND_MAX % 100). If it is, then redo the rand() until it is. BTW, good coding practice says that this should be made its own function.

    This way, your probabilities will be correct, to the degree that rand() is probabilistically accurate.

    Quote:

    Guess I'm using something like this:

    Several problems/concerns:

    1) Why should a negative "resultATK" be an automatic failure? It is entirely possible that, through a very poor roll on the defender's part, that "resultATK" will be higher than "resultDEF".

    2) Right now, if "resultATK" is negative, then nothing happens. Presumably, "defense successful" should be the result.

    3) Both "attacker.attack" and "defender.defense" should be function calls, not variable accesses. Preferably, they should be function calls with an argument that tells them the kind of attack (melee, magic, etc).

    4) This doesn't address the other half of your querry; that is, dealing damage. I have a number of ideas on the subject, but you've made it perfectly clear that explaining them would be a waste of my time, so I shall refrain. I'm sure you can find something... simple to do.

    spellcaster
    Member #1,493
    September 2001
    avatar

    Quote:

    1) Why should a negative "resultATK" be an automatic failure? It is entirely possible that, through a very poor roll on the defender's part, that "resultATK" will be higher than "resultDEF".

    Because that means that the attacker spoiled his attack role. He managed to roll a number larger than his skill... so it's a failure. No need for the defender to defend... the attacker will miss the target by a mile.

    Quote:

    2) Right now, if "resultATK" is negative, then nothing happens. Presumably, "defense successful" should be the result.

    Displaying "Miss" or something would do the job :)

    Quote:

    3) Both "attacker.attack" and "defender.defense" should be function calls, not variable accesses. Preferably, they should be function calls with an argument that tells them the kind of attack (melee, magic, etc).

    Agreed. This was more meant to be pseudo code. But you won't use ATK and DEF for magic ;)

    Quote:

    4) This doesn't address the other half of your querry; that is, dealing damage

    Damage will be based on the "quality" of the result (so how much larger resultATK is than resultDEF) and a weapon modifier.

    Quote:

    . I have a number of ideas on the subject, but you've made it perfectly clear that explaining them would be a waste of my time,

    I'm always open for new idea :)
    My bookshelf is full of RPG systems (gurps, d&d, palladium, shadowrun, ars magica, rolemaster, vampires of the old world, and some lesser known ones) and source books.

    If you have good ideas, let's discuss them ;)
    I might have found the system I'm going to use for that project, butu it doesn't mean we can discuss other systems...

    --
    There are no stupid questions, but there are a lot of inquisitive idiots.

    dudaskank
    Member #561
    July 2000
    avatar

    if (resultDEF < 0 || resultATK < resultDEF)
       /* attack succesful */
    

    ???

    Is it correctly, or is:

    if (resultDEF < 0 || resultDEF < resultATK)
       /* attack succesful */
    

    Korval: Ahh... I get it!

    Tell me if my new d_100() will be better:

    int d_100(void)
    {
       int d;
       int max;
    
       max = 100;
       d = max * (rand() / RAND_MAX);
       return d;
    }
    

    If the probability of each result is correct now, only changing the max to other value to make a D20 or d10 or d1981 :P

    ;D

    Toque a balada do amor inabalável, eterna love song de nós dois
    Eduardo "Dudaskank"
    [ Home Page (ptbr) | Blog (ptbr) | Tetris 1.1 (ptbr) | Resta Um (ptbr) | MJpgAlleg 2.3 ]

    Kitty Cat
    Member #2,815
    October 2002
    avatar

    almost, but not quite:

    /* removed unnecessary variables, casted rand() and RAND_MAX to float(result would always be 0 otherwise), which, after being multiplied by 100(max), recasts to an int */
    int d_100(void)
    {
    return ((float)rand() / (float)RAND_MAX) * 100.0;
    }

    --
    "Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

    spellcaster
    Member #1,493
    September 2001
    avatar

    dudaskrnak, you're right. It's a typo :)

    Regarding the d_100 routine...

    int roll_dice(int kindOfDice) {
        int dice   = 0;
        int result = 0;
        int count  = 0;
        int max    = 2;
    
        result = dice = (rand() % kindOfDice) +1;
        count++;
        while (dice == kindOfDice && count < max) {
            dice = (rand() % kindOfDice) +1;
            result += dice;
            count++;
        }
    }
    

    This routined calcs "adjusted" dice. So if you roll the maximum value, you can roll again (up to two times, in that example).

    --
    There are no stupid questions, but there are a lot of inquisitive idiots.

    dudaskank
    Member #561
    July 2000
    avatar

    Chris: Yes, some optimizations will be good in my little function, I know ;D

    spellcaster: But... this is the same thing I've done before... the probability is different for each result, like korval wrote...

    Quote:

    This routined calcs "adjusted" dice. So if you roll the maximum value, you can roll again (up to two times, in that example).

    Yes, great idea too!

    ^__^

    Toque a balada do amor inabalável, eterna love song de nós dois
    Eduardo "Dudaskank"
    [ Home Page (ptbr) | Blog (ptbr) | Tetris 1.1 (ptbr) | Resta Um (ptbr) | MJpgAlleg 2.3 ]

    spellcaster
    Member #1,493
    September 2001
    avatar

    Quote:

    spellcaster: But... this is the same thing I've done before... the probability is different for each result, like korval wrote...

    Why do you think so?

    1#include <stdio.h>
    2#include <stdlib.h>
    3#include <limits.h>
    4 
    5#define INSANE
    6 
    7#ifdef INSANE
    8#define MAX_TEST ((unsigned int)((~0)-1))
    9#else
    10#define MAX_TEST 100000
    11#endif
    12 
    13int main(int ragc, char** argv) {
    14
    15
    16 unsigned int a;
    17 unsigned int min;
    18 unsigned int max;
    19 float avg;
    20
    21 int value;
    22 int minIndex, maxIndex;
    23 int count[100];
    24
    25 for (a=0; a < 100; a++) {
    26 count[a] = 0;
    27 }
    28 printf("Doing %u rolls...\n", MAX_TEST);
    29 fflush(stdout);
    30
    31 for (a=0; a < MAX_TEST; a++) {
    32 value = rand()%100;
    33 count[value]++;
    34 }
    35
    36 min = MAX_TEST;
    37 max = 0;
    38 avg = 0;
    39 maxIndex = minIndex = 0;
    40 for (a=0; a < 100; a++) {
    41 if (count[a] > max) {
    42 max = count[a];
    43 maxIndex = a;
    44 }
    45 if (count[a] < min) {
    46 min = count[a];
    47 minIndex = a;
    48 }
    49 avg += count[a];
    50 }
    51 avg = avg / 100.0;
    52 printf("Number most often rolled: %5i (%5i times == %2.2f per cent)\n",
    53 maxIndex, max, max*100.0 / (float) MAX_TEST);
    54 printf("Number least often rolled: %5i (%5i times == %2.2f per cent)\n",
    55 minIndex, min, min*100.0 / (float) MAX_TEST);
    56 printf("In average every number got rolled: %3.2f times. That's %3.2f percent\n",
    57 avg, avg*100.0 / (float) MAX_TEST);
    58
    59 return 0;
    60}

    Result:

    Quote:

    Doing 4294967294 rolls...
    Number most often rolled: 1 (42991616 times)
    Number least often rolled: 68 (42860544 times)
    In average every number got rolled: 42949672 times. That's 1.00 percent

    So, since you have 100 numbers you'd expect every number to be rolled around 1 per cent of time :)

    If you try this code, please be patient... it'll take some time.
    Take the #define INSANE out if you want a faster result..

    So, what's the problem with using that algo?
    ::)

    But korval is right. If your upper limit gets close to rand max, you'll get problems.
    But even if you roll d20000 you won't have a problem. ;)

    --
    There are no stupid questions, but there are a lot of inquisitive idiots.

    Korval
    Member #1,538
    September 2001
    avatar

    Quote:

    But even if you roll d20000 you won't have a problem.

    Well, unless your RAND_MAX is 0x7FFF (32767), of course. VC++'s RAND_MAX is.

    BTW, you're cheating. By your equation, the percent can't be anything but 1%. Observe:

    Before the divide by 100, "ave" will guarenteeably equal MAX_TEST. Just look at the code: for each MAX_TEST number of times, some entry in count[] is being incremented. Add all of these up, and you get MAX_TEST. Therefore, the percentage is computed as follows:

    ((MAX_TEST / 100) * 100) / MAX_TEST. This is precisely 1.0f, regardless of which element we choose to increment in count[].

    The correct analysis would be to take the count[] variable and do some statistical analysis techniques on them. I don't remember most of my statistical analysis from chemistry, but there are ways to determine how far off your probabilities are from the true amount.

    Besides, you haven't even attempted to answer my logical argument above. Just because you can write code that looks like it isn't broken doesn't mean that it isn't. Trust me; just do it the right way.

     1   2 


    Go to: