Advertisement

Stat-based attack system - should be simple, but need help

Started by September 16, 2010 11:02 PM
17 comments, last by TechnoGoth 14 years, 4 months ago
tommy, I still stand by what I said. Your example needs to be totally rewritten per situation. That's bad programming. Being able to change as little as possible in the code for say, balance issues 9 months down the road in beta, is key to quick bug free production. If you want to spend time hunting down bits of code and making typos, program as per your example.
Quote:
Original post by NateDog
Removing the probability randomness is easy in your code, but I'm still wondering how games handle this without randomness of chooseRandomSingleOpponent() . What's a logical/fair way to line up the units?
This is an AI decision point. Instead of a function chooseRandomSingleOpponant, create a function selectTarget(). A good choice would be to select a target that has the same amount of hitpoints as the combat is expected to deliver most of the time. An even better choice is to select the target that will reduce enemy firepower the most after this round.

[Edited by - AngleWyrm on September 19, 2010 9:39:44 PM]
--"I'm not at home right now, but" = lights on, but no ones home
Advertisement
I like the circle idea its quite an intersting one. Could it be extended to do arc comparisons rather than point comparisons? So a handgun has a damage rating of 30 degress from point 0, while a silk shirt has defense rating of 15 degress from point 350?

In that way a circle could in theory divided into different damage "types".
Actually, a way to implement the system is to use overflow of the computer. Remember that number have a range depending on what you're doing. A short is a 16-bit number, etc. With a certain number range, you can intentionally let overflow to occur. Then, the remainder of the number will determine the damage. You don't need to use the trig functions as they take more CPU cycles.

With byte: 8-bit: 256 different position
With short-int: 16-bit: 65536 different position
With int: 32-bit: 4294967296 different position
With long-int: 18446744073709551616 different position

As you can see, using simple addition and/or subtraction and ignore overflow, you can set up a system that function the same way as your trig function. Also, it is possible to have more than 3 points of extrema.

Allowing overflow is an intentional design to create an RPS system.

Treat the value as unsigned. Set the carry flag. Then do the subtraction with burrow. Next type case the value to signed value. The signed bit will activate within certain range.

Usually HLL do not let you overflow your values (give you an error message) or give you warning.

This method is like the circle method, but it uses the inherit ability of overflow mathematics. This process is also CPU efficient.

MASM
...
MOV AX, ATTACKER
MOV BX, DEFENDER
SETC
SBB AX,BX
MOV RESULT,AX
...


My method takes 5 CPU cycles.

sin + cos = 3.5 cycles
addition = 2 cycle
divisin = 1 clcle
sqrt = 10 cycles
total of Glak's circle method = 16.5 CPU cycles minimum

add 2 cycles to enter and leave the subroutine.

Conclusion, my method may be 3 times faster, but it is a method that is error prone, thus a safe programmer should use Glak's method.


I use QueryPerformanceFrequency(), and the result averages to 8 nanoseconds or about 13 cpu cycles (1.66GHz CPU). Is that reasonable?
I though that the assembly equivalent to accessing unaligned data would be something similar to this order:

  • move
  • mask
  • shift
  • move
  • mask
  • shift
  • or

    So it seems reasonable to say that it takes 14 cycles for unaligned data since we'll have to do the series of instructions once to access and once to assign?
Quote:
Original post by Kylotan
You can assign numerical values to these three but not in a way that preserves the circularity you want.


I'm not sure this is true.

If units do different types of damage, for example, some do "explosive" damage and some do "melee" damage and armor or hp is affected differently by the two different types of damage, then you can easily construct a rock-paper-scissors type system.

Just an off-the-top-of-my-head-example:

Mage: low armor, spell damage (which goes right through armor)
Rogue: medium armor, melee damage (which is mitigated by armor)
Warrior: high armor, less melee damage than rogue (which is mitigated by armor)

It would be fairly easy to tweak these six values (low_armor, med_armor, high_armor, spell_damage, melee_damage, less_melle_damage) so that a rock-paper-scissors system emerged.








So it looks like my system got a lot of discussion. I'll address the responses in order

Tommy,
your system involves a centralized system of if-else statements. It is very explicit and so it makes clear the relationships in your code. That is the upside. There are two serious downsides.

The first is that your system doesn't scale. If you had a game with archers, cavalry, and infantry then you have 6 relationships to deal with: infantry attacking infantry, infantry attacking cavalry, infantry attacking archers, cavalry attacking infantry, etc... and it would be entirely doable. The problem is when you have more than three unit types. The game that I was designing had 60 different unit types, which means that your system would have 60*60-60 relationships = 3540. 3540 is far, far too many. I noticed that you said something troubling: "Add more variables. What about a 4th type, a nail, add more variables. That's programming." No, it isn't programming, it is data entry, which is the opposite of real programming (though we must all do it sometimes).

The second problem is that you have all of this data. How do you maintain it? How do you generate it? How do you communicate it to the players of your game?

TechnoGoth,
could you divide it into different arcs? Yeah probably, but either you are doing something far more complicated or something different. The equation that I supplied is basically the sin function, but distorted so that the max and min appear different places. How would you plug a range into a function that normally takes a single value? I can think of a few ways inspired by fuzzy logic and probability but they wouldn't add anything. Perhaps you meant something completely different, in which case I think that your idea is pretty cool. Are you saying that total armor would be represented by having your armor arc be the full circle? And that say you had a weapon that was the top half of the circle vs an armor that was the bottom half, then the armor would do nothing? If so that is pretty cool.

Platinum_Dragon,
I thought of doing something similar, though I was going to use an if statement to simulate the wrap around. However I wanted a function that was curvy like the sin function.

sooner123,
you are right, a weapon vs armor system is an intuitive approach to generating a counter system. However it is much harder to balance. For example, what if during testing you discover that your Mage vs Rogue matchup needs to be tweaked? You want to make Mages a little stronger vs Rogues so you give them more hp. Well now Mages are also stronger vs Warriors. Ok so you put the hp back to what it was and you then decide to make the value of low armor stronger. Now you realize that all of your units with low armor are stronger. This can go on forever and will require a lot of play testing to get right.

When you finally balance it you will have accomplished a great deed and your game will be awesome. However it will only be after lots of work. When working on my game I determined that it would be too much work and so I decided against it. I implemented my rock-paper-scissors equation and that aspect of combat was working fine. I eventually stopped working on the game because it was just too much for me. I would have quit even sooner if I had attempted to balance 60 units using a combination of armor type, armor amount, attack speed, health,, range, etc..
Advertisement
Quote:
Original post by sooner123
Quote:
Original post by Kylotan
You can assign numerical values to these three but not in a way that preserves the circularity you want.


I'm not sure this is true.

If units do different types of damage, for example, some do "explosive" damage and some do "melee" damage and armor or hp is affected differently by the two different types of damage, then you can easily construct a rock-paper-scissors type system.

That doesn't fit into the original three stats. If you have three parallel sets of ATK/DEF values or ATK/HP values, then sure, an RPS system can emerge by having just one of the 3 parallel systems outweigh the others. You end up with the game theory payoff matrix, essentially. Either you have several ATK scores that resolve simultaneously and differently, or you have a condition that decides what score to go with. Either way it's not encoded in the original stats.
Quote:
Original post by Glak
I came up with an equation for rock-paper-scissors:

sqrt(3.0)*sin(theta)/(2+cos(theta))


You don't really need the sin and cos aspects - they're just wrapping the angle round, really. I suppose it's quite elegant in terms of it being a single equation but I expect there's a simpler one that would yield the same result.

This system works well but obviously the value is no longer in linear units, so you can't easily take a swordsman and make a 'double strength' swordsman. For that, you need another stat.
Quote:
Original post by Glak
I think that your idea is pretty cool. Are you saying that total armor would be represented by having your armor arc be the full circle? And that say you had a weapon that was the top half of the circle vs an armor that was the bottom half, then the armor would do nothing? If so that is pretty cool.


That prety much what I was thinking. Still need to think through how it would all work and fit into a damage equation but I think it'll make an interesting way to visualize and determine combat effectivness.


This topic is closed to new replies.

Advertisement