(fixed) Combat balance

This forum is for the Lua scriptable clone of DM/CSB called Dungeon Strikes Back by Sophia. Use DSB to build your own highly customised games.

Moderator: Sophia

Forum rules
Please read the Forum rules and policies before posting.
Post Reply
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

(fixed) Combat balance

Post by Joramun »

After a lot of fighting (at low level) I must say that something bother me :
A lot (really a lot) of attacks (whatever the method) miss at low level.
I don't mind them being very low damage, but fighting for ten minutes and dealing zero damage 9 times in 10 is really boring. It's the same for warcry and others "misc" attack. I would prefer higher idle time and higher chances of dealing at least minor damage / short timed fear effect.

I would slightly improve the chance of dealing 1 or 2 damage for every methods. That would be more balanced, since early on, throwing all the inventory in the face of monster seems to be much more efficient than just fighting with weapons or bare hands. And killing a monster by throwing a shirt is just stupid (most clothes should deal no damage at all).

To even this fact, I would also increase the chance of monster to deal a very low damage.
For now, it seems they are also often failing their attacks, and when they succeed, it's generally a major blow...
What Is Your Quest ?
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

Hmm, this could be one of two issues:
  • You have an issue with the DM mechanics. In this case, we can talk about making small changes (especially ones that seem to be what the designers intended, but just didn't do due to program bugs or technical limitations)-- but I'm not sure if I want to change things too drastically.
  • I made a mistake in porting the DM mechanics to DSB. In this case, it's more clear-cut, and I can correct my mistake.
Unfortuantely, I'm not sure which it is.
A cursory examination of the CSBwin code doesn't reveal any huge discrepancies between DSB's algorithm and the one used there, but I'm sure I don't fully understand what's going on.
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

Maybe that's just an impression I had because of reincarnated character.
I have to check but I thought the first levels where quite faster to obtain...

I still think the whole combat doesn't give enough result at low level, even if it is not a bug. So I would go for solution one among your two answers.
What Is Your Quest ?
User avatar
Paul Stevens
CSBwin Guru
Posts: 4318
Joined: Sun Apr 08, 2001 6:00 pm
Location: Madison, Wisconsin, USA

Post by Paul Stevens »

Using CSBwin as a benchmark for party
attacks and party level progression may
not be too wise.

When I first translated the Atari code
to create CSBwin I made quite extensive
use of the Atari emulator. I forget which
one it was but the source code was available
and I could run the emulator using the
Visual C++ debugger and thereby step through
the Atari code. It could have been WinSTon

I modified the emulator to write 'event' files.
Then I modified CSBwin to write 'event' files.
Then I ran the two programs and compared
the 'event' files. If they were different in any
way I fixed CSBwin and tried again. But
things like combat were difficult to test.
So I am afraid that combat did not get tested
as well as (for example) monster actions and
monster attacks.....which required no mouse or
keyboard input. I simply stood still and
got myself killed.

So...It may be that I did not get party attacks
as wll tested as I would have liked.

Here is what I suggest. If you can get a hold
of an emulator and, as far as possible, recreate
the same conditions as a game using CSBwin and
you can demonstrate that there is quite a large
difference in the way they work, then I am 100%
willing to go back and see what might be causing
such differences.
User avatar
beowuuf
Archmastiff
Posts: 20687
Joined: Sat Sep 16, 2000 2:00 pm
Location: Basingstoke, UK

Post by beowuuf »

Just out of interest, what were you trying to attack with the low level characters?

If it was something well armoured, instead of default screamers and mummies of DM, then you might find the misses are due to that intead. And do you gain less experience for ineffective attacks? So it could be the low level model is right aslong as you are attacking low level monsters
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

I used Stamm reincarnated.
Basic enemies like : mummy, screamers, rockpiles, trollins.
I kill them with a few thrown objects (except rockpiles), while in close combat
(even with the axe)
I deal low damage (less than 10) and only once every 10 or 15 attacks at most !

I played PC DM to see for my self, and it goes the other way : reincarnated stamm deals as much as 30 damage once every two or three attacks.

All that with no fighter level.
What Is Your Quest ?
User avatar
beowuuf
Archmastiff
Posts: 20687
Joined: Sat Sep 16, 2000 2:00 pm
Location: Basingstoke, UK

Post by beowuuf »

Yeah, something's wrong - you should indeed be able to, with stamm and his strength, still get decent amounts of damage - as you said 30 is not unexpected :(

And I just thought, usually punching draws blood alot of the time, even if only 1 -2 damage
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

Yes, and punching was not efficient either.
I could kill the first monsters only because of the collision damage... so I just threw a lot of things.
Usually, one or two hits with a thrown axe are enough, but again it's not logical since chopping with an axe should do more damage than throwing it !

It makes the first wasp at level 3 quite deadly !

PaulS: Actually, as far as Conflux is concerned, the party attack balance in CSBWin is ok. I would have to test with original DM in order to get a fair comparison though.

But I think that for something as contrived as the party attack mechanics in DM, the best is to go for a simple (as much as possible), balanced solution.

I'll come up with some ideas on how damage should be dealt with.
(next post...)
What Is Your Quest ?
User avatar
Paul Stevens
CSBwin Guru
Posts: 4318
Joined: Sun Apr 08, 2001 6:00 pm
Location: Madison, Wisconsin, USA

Post by Paul Stevens »

I just started CSBwin DM, reincarnated
STAMM and played through the Mummy and
first six Screamers.

With the Axe, I was getting 25 to 45
damage on about 80% of my 'Chop's.
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

So, what I saw, opening the methods.lua is that :
- failure gives a binary result, so it's almost always 0 or full damage.
- the way success is computed (with luck, quickness, etc.) looks very contrived.
- damage is also a complex result (not to say contrived calculus)
I didn't detail everything :

Code: Select all

HIT IF :
character_quickness - monster_quickness - random(31) > 0
OR
25 % chance
OR
luck_calculus (complex, dependant on the attack method "chance")

DAMAGE =
method_power/32 * [ weapon_power + random(weapon_power/2) ]
- monster_def (= xp_fact + monster_armor + random(31) )
+ weapon_def_reduce
+ weapon_bonus

if DAMAGE < 2
75 % chance to fail ( ! )
else
...more contrived stuff I won't detail ! 
First remark : In DM, the luck stat is not static.
It increase at every failure and decrease at every succes, with boundaries.

It was done because the Atari ST / Amiga had no complex number generator and no way to get complex mathematical function for probability.

I have no problem with that BUT, what I would do is a less binary and contrived way of taking the luck result, that is :

Code: Select all

- Luck Result (function of luck stat, monster and character quickness, method luck etc.) between 0 and 100 % = LR
- 5% > LR, critical failure, increasing the idleness time by a given factor.
- 25 % > LR > 5 %, failure, no damage
- 75 % > LR > 25 %, moderate success, damage factored by LR
- 95 % > LR > 75 %, success, full damage
- LR > 95 %, critical success, damage increased by a given factor
Last edited by Joramun on Sat Nov 17, 2007 5:20 pm, edited 1 time in total.
What Is Your Quest ?
User avatar
Paul Stevens
CSBwin Guru
Posts: 4318
Joined: Sun Apr 08, 2001 6:00 pm
Location: Madison, Wisconsin, USA

Post by Paul Stevens »

Sophia:

If it would help, I can step through
a couple of STAMM's Chop attacks and
write down the intermediate results
so that you can do a comparison with
the DSB results. Something probably
got minimized rather than maximized
or ????.

Edit:
I forgot....CSBwin has an 'Attack Trace'.
Here is the trace from my first Screamer
meeting (37 and 47 damage):

Code: Select all

PAUL attacks from 01(03,0d) to 01(04,0d)
Attack type = 2 = CHOP
Skill number required = 06
Byte20046[attackType=2 ] = 8
staminaCost = Byte20046[attackType=2 ] + Random(1) = 8
experiencedGained = Byte20178[attackType=2 ] = 10
  Enter AttackWithPhysicalForce (attackType=2, skillNumber=6)
  D6W=Byte20002[attackType=2 ]=48
  D7W=Byte19958[attackType=2 ]=48
  Calling PhysicalAttack(character, monster, D5W-1, D6W, D7W, skillNumber)
    Entering PhysicalAttack(char=0,monster,P4=0,P7=48,P8=48,skillNumber=6)
    levelDifficulty = 1
    pi26 word2 &0x40 == 0  or  vorpalOrDisrupt
      Entering function to determine character's Quickness
      quickness = (Dexterity=40) + (random(8)=5) --> 45
      About to compute Maximum load
      Loading effect=(quickness/2)*(load=105)/(maxload=480)-->4
      Subtract loading effect from quickness -->41
      Low Limit = random(8) + 1 --> 8
      High Limit = 100-random(8) --> 97
      Final quickness = 20
    character's quickenss = 20
    required quickness = (pi26.uByte8[4 ]=5) + (random(32)=17) + (levelDifficulty=1) - 16 = 7
    Attack was successful because Character's quickness is great enough
    D7W = Throwing distance=52
    D7W = D7W+random(D7W/2+1) = 77
    D7W = D7W*(P8=48)/32 = 115
    D4W = (i26->uByte8[0 ]=5) + (levelDifficulty=1) + (random(32)=2) = 8
    D7W = D7W + (random(32)=21) - (D4W=8) = 128
    D6W = D7W = 128
    Divide D7W by 2 --> 64
    D0W=random(D7W) --> 28
    D7W += (random(4)=0) + (D0W=28) --> 92
    D7W += (random(D7W)=41)--> 133
    Divide D7W by 4 --> 33
    D7W += (random(4)=3) + 1 --> 37
      Enter DetermineMastery(hero=PAUL, skill=6)
      Extract two flags from skill number.
      flag8000 = skill&0x8000 --> 0
      flag4000 = skill&0x4000 --> 0
      SkillNumber = 6
      Mastery = log2(Experience=18) --> 1
      Mastery of skill 6 is 1
    D0W = ((D7W=37) * (Bits8_11(i26.word16)=0)/16) + 3 --> 3
    AdjustSkills((skill=6), (D0W=3))
    Decrement Stamina by random(4)+4 = 4
    w_2 = DamageMonster() --> 37
    PhysicalAttack returning D7W = 37
  Word20264 = Result of calling PhysicalAttack = 37
  Return 1
PAUL attacks from 01(03,0d) to 01(04,0d)
Attack type = 2 = CHOP
Skill number required = 06
Byte20046[attackType=2 ] = 8
staminaCost = Byte20046[attackType=2 ] + Random(1) = 8
experiencedGained = Byte20178[attackType=2 ] = 10
  Enter AttackWithPhysicalForce (attackType=2, skillNumber=6)
  D6W=Byte20002[attackType=2 ]=48
  D7W=Byte19958[attackType=2 ]=48
  Calling PhysicalAttack(character, monster, D5W-1, D6W, D7W, skillNumber)
    Entering PhysicalAttack(char=0,monster,P4=0,P7=48,P8=48,skillNumber=6)
    levelDifficulty = 1
    pi26 word2 &0x40 == 0  or  vorpalOrDisrupt
      Entering function to determine character's Quickness
      quickness = (Dexterity=40) + (random(8)=3) --> 43
      About to compute Maximum load
      Loading effect=(quickness/2)*(load=105)/(maxload=480)-->4
      Subtract loading effect from quickness -->39
      Low Limit = random(8) + 1 --> 8
      High Limit = 100-random(8) --> 95
      Final quickness = 19
    character's quickenss = 19
    required quickness = (pi26.uByte8[4 ]=5) + (random(32)=13) + (levelDifficulty=1) - 16 = 3
    Attack was successful because Character's quickness is great enough
    D7W = Throwing distance=49
    D7W = D7W+random(D7W/2+1) = 71
    D7W = D7W*(P8=48)/32 = 106
    D4W = (i26->uByte8[0 ]=5) + (levelDifficulty=1) + (random(32)=23) = 29
    D7W = D7W + (random(32)=24) - (D4W=29) = 101
    D6W = D7W = 101
    Divide D7W by 2 --> 50
    D0W=random(D7W) --> 43
    D7W += (random(4)=3) + (D0W=43) --> 96
    D7W += (random(D7W)=84)--> 180
    Divide D7W by 4 --> 45
    D7W += (random(4)=1) + 1 --> 47
      Enter DetermineMastery(hero=PAUL, skill=6)
      Extract two flags from skill number.
      flag8000 = skill&0x8000 --> 0
      flag4000 = skill&0x4000 --> 0
      SkillNumber = 6
      Mastery = log2(Experience=25) --> 1
      Mastery of skill 6 is 1
    D0W = ((D7W=47) * (Bits8_11(i26.word16)=0)/16) + 3 --> 3
    AdjustSkills((skill=6), (D0W=3))
    Decrement Stamina by random(4)+4 = 7
    w_2 = DamageMonster() --> 47
    PhysicalAttack returning D7W = 47
  Word20264 = Result of calling PhysicalAttack = 47
  Return 1
User avatar
Joramun
Mon Master
Posts: 925
Joined: Thu May 25, 2006 7:05 pm
Location: The Universe

Post by Joramun »

I found something peculiar in the "have_luck" function :

Code: Select all

if got_luck < 10 then got_luck = 0 end
I changed it to the more reasonable :

Code: Select all

if got_luck < 10 then got_luck = 10 end
Which gives better results... once in a while.
What Is Your Quest ?
Remy
Craftsman
Posts: 111
Joined: Wed Sep 05, 2007 5:24 pm
Contact:

Post by Remy »

Which gives better results... once in a while
That's not suprising, since Luck is internally ten times the "real world" value, so all you did was change it from "if luck is less than 1, make it 0" to "if luck is less than 1, make it 1".
*Edit* --Actually, I take that back, I was reading it wrong (and discovered what Sophia points out below, that luck is being multiplied by 10 twice).

With Paul's breakdown, I can point out a couple of discrepencies about required_quickness - DSB sets it too high (I don't know whether this is made up for by DSB's other "if-then's to success" though).
CSBwin

Code: Select all

required quickness = (pi26.uByte8[4 ]=5) + (random(32)=17) + (levelDifficulty=1) - 16 = 7 
DSB (methods.lua:134)

Code: Select all

local req_quickness = monster_arch.quickness + dsb_rand(0, 31)
CSBwin's 'pi26.uByte8[4]' is the monster's quickness (I think it is, at least). DSB polls the level difficulty ('local difficulty'), but doesn't use it or subract 16 from the result before returning it.

Also, DSB's calc_quickness is off:
DSB (utils.lua::313: (calc_quickness))

Code: Select all

q = q - (loadval / dsb_get_maxload(who))
CSBwin

Code: Select all

(quickness) = (quickness) - Loading effect=(quickness/2)*(load=105)/(maxload=480)
Load effect should be multiplied by (q / 2) before subtracting from 'q'.

I'm looking into the damage calculations now...
Last edited by Remy on Sat Nov 17, 2007 9:13 pm, edited 2 times in total.
User avatar
Sophia
Concise and Honest
Posts: 4240
Joined: Thu Sep 12, 2002 9:50 pm
Location: Nowhere in particular
Contact:

Post by Sophia »

After some playing around, I found two problems and started getting a lot better hit rates and damage.

- The "damage" value of the weapon (uByte2) was never actually entered into objects.lua. :oops: The formulas made reference to it (base_tpower) but the small values that were in place made it seem more like tweaks than a value important to the calculation-- or something. I actually don't totally remember what I was doing. But, I've fixed this, and the power went up immediately.

- A successful hit accidentally set your luck to an obscenely high value. This would help you, except, sys_update knocks your luck way down if it goes over the max (to simulate the capriciousness of luck)-- in normal play, this would happen rarely. In this case, it was happening every single time you scored a hit, resulting in bad luck rolls all the time. After doing this, the hit rate went up.

Now calc_tpower (the DSB version of CSBwin's DetermineThrowingDistance) will return much higher power levels. I'll need to rebalance object throwing and shooting to compensate-- I kind of winged thrown object damage because it was so paltry in DM-- but things are looking a lot better now. :)

EDIT:

Ok, thanks, Remy.
Two more changes: :D

After the "local req_quickness" stuff, I've added:

Code: Select all

	local req_quickness = monster_arch.quickness + dsb_rand(0, 31)
	req_quickness = req_quickness + difficulty - 16
The first three lines of calc_quickness are now:

Code: Select all

	local q = (dsb_get_stat(who, STAT_DEX) + dsb_rand(0,70)) / 10
	local loadval = ((q/2) * dsb_get_load(who)) / dsb_get_maxload(who)
		
	q = q - loadval
Post Reply