Damage calculator

#51
One thing that occurred to me.

I was wondering what would happen if you had well over 255 armor. Because since monsters can do at most 255 damage, and you can wear so many pieces of armor, that seems like it would be easy to achieve if the author is not wary of this.

Essentially you should be invincible at this point. So I dawned two pieces of 255 armor. And setup a monster with 255 damage. I wasn't sure what would happen with the armor being more than the damage since, it seems like that would produce negative damage. I wondered if it could possibly be yet another bug.

Well that was not how things played out. It turns out there is another wrinkle in the formula. Basically if the armor is more than the damage, the ATK-DEF part of the equation is skipped: max(0,ATK-DEF)+ATK*ATK/(DEF*2).

And in this case the result of the exponential function was around 63 damage. Again skipping the first term of the equation seems like its totally arbitrary to me. But it does indicate that the original formula has a much larger damage window than square root 0.5 to 2. Whether it works the same way when DEF is less than ATK who is to say.

It's definitely a very piecewise function to say the least. Which is not good for authors.


All and all. I think ATK-max(0,DEF-ATK*ATK/(DEF*2)) is a pretty solid formula. You could probably add some parameters to it to increase its damage window, but I don't think such parameters should be extensions. It's easy enough to setup a custom function. It's much better behaved. But you do need to be cautious about not letting the player wear too much armor.

You could scale the armor in the hit_offset_quantifier extension if you wanted to use the entire range. Or scale up the attacks one. I wouldn't recommend that though unless its just a last ditch desperate attempt, or a test, better to just keep the armor pieces from stacking too high.


EDITED: I think things will be complicated when better hit detection arrives and authors would like to have weapon versus individual piece of armor. The weapons will have to be recalibrated or vice versa, and coverall armor would have to be adjusted to have the stats of a single piece of armor. Not sure how that could all be managed. Probably would just have to make do with custom formulas. It would be easy enough to do.
Reply

#52
I wouldn't be surprised if after all of this work this new default formula is good enough that no one will even care to setup a custom formula. Still its important work.

It's going to take me a while to flesh out all of the details for a demo but the basics are already up and running.

I've decided not to label parameters of extensions for the time being. They are easy enough to lookup. You'd have to lookup the labels anyway.

I've also decided to hold off on passing the affinity number as a parameter unless someone demands that. I think it's an obvious 3rd parameter for the final hit_outcome_quantifier extension. But I don't know if it should be second or third or what for the bonus extensions.

For the bonuses the pc global built in number or whatever--like c is for counters--refers to the player or NPC with the stats in question. Here pc can refer to a monster or NPC. There is also an npc number for the character on the other end, but its not going to be supported until necessary.

pc[0] or just pc is 1 for the player and 0 for NPCs (including enemies) and will be 2 for player character 2 if that is ever a thing.

pc[1] is Strength. And pc[2] is Magic. These are 0 for monsters. 3 is reserved for a Speed stat. And 4 is the weapon. 5 is the helmet I think. And so on. I believe these go in the order of the Equip menu. They are in the order they are in in the save file. For monsters equipment is NaN which will void your formula if you don't work around it.

You can label these yourself. I don't know what to label them. The only obvious labels would be Japanese. So I am letting the end user label them however they want or just use the numbers until I have a better idea.

For now pc is all NaN inside of the hit_outcome_quantifier extension. In other words it is source / target neutral. It will probably stay that way, but if it ever changes pc will refer to the character taking damage.

I don't know if hit_penalty_quantifier extension will make it into the next release or not. It lets you customize poison. Maybe other ailments too.


PS: While I am fairly pleased with this default formula. I am not a fan of the additive bonuses. Has anyone thought about how you'd like to figure the bonuses? Something multiplicative seems better to me. There is probably a website with information about how muscles effect the impact of an instrument out there somewhere...

What is the natural minima and maxima for humans. That would be interesting stuff.
Reply

#53
I understand if you don't want to change SoM's default mechanics, but it sounds like you are changing the default formula so I don't quite get how your new formula is any better than the old.

Quite a few games have used damage=atk-def, as a matter of fact, I'm pretty sure Brigandine does with a slight randomizer and elemental adjustments. My desire for SoM has always been to make it easier for the common fan to make a game. And the problem that everyone mentions about SoM damage is that it's too abstract to adjust without tons of trial and error or calculation. That's just wasted time that would be better spent on other aspects of game development. ‎ 

The bottom line is "how many hits does it take to kill something". No matter how fancy the damage formula is, it always boils down to that same thing. If a player with 29 ATK goes against an enemy with 100 HP, 18 DEF how many hits would it take to kill the enemy using this formula: ATK-max(0,DEF-ATK*ATK/(DEF*2)) ? Good luck without a calculator.

But if the formula is "damage=atk-def", most people can do the math instantly in their head (29-18=11 100/11 = 9.x = 10 hits). Because of that, players can also evaluate equipment in a meaningful way. In KF, if I found a weapon with "56 Slash atk", I had no idea what that meant other than it was better than "55 slash". ‎  With a simple formula, players can weigh the pros and cons of equipment and plan strategically. That adds potential enjoyment to the game.

SoM's default formula IS "damage = 1.5xATK - DEF" as long as stats are balanced. The only point in deviating from that formula as stats grow apart is to restrict players from going into areas where they aren't supposed to be yet - aka "rat in a maze" game design. ‎  If someone wants to make a linear "rat in a maze" game, there are far easier ways of doing it.

I've said all there is to say, and you never change your mind once you've decided so I'm done talking on that particular topic.
Reply

#54
(2013-07-07, 10:41 PM)HwitVlf link Wrote:I understand if you don't want to change SoM's default mechanics, but it sounds like you are changing the default formula so I don't quite get how your new formula is any better than the old.

From reply #49:

Quote:Specifically I think you can start doing damage if your rating is within square root of 0.5 or 70.71% of the defense rating. That's a fairly auspicious number. It shows up in circles. For instance it's how far you can move diagonally when pressing the movement buttons.

Whereas with ATK-DEF you would have to be at 100% of the defense rating before you can deal damage. Which seems unrealistic, though I've no doubt games use this. It's reasonable to suspect that a weapon with a rating just below the armor can do more than 0 damage.

When the weapon and armor are equally matched you deal 55.90% of the weapon's damage. I am not sure if this number is significant (it almost definitely is) but it is the same number that SOM's classical formula yields. So that seems good (edited: I have a feeling this may be 50%. Could be rounding and stat bonuses were making the difference. Still seems like an awful big difference. May be worth revisiting sometime)

I expect when the weapon is 141.42% over the armor you will deal the weapon's maximum damage. This is square root of 2. Or the length of the diagonal of a square enclosing a unit circle. Whereas square root of 0.5 is the width of the square formed by that diagonal clipped at the edge of the circle. ‎  Anyway at this point the armor is no longer effective (edited: for the record, this hunch seems to checkout)


Quote:Quite a few games have used damage=atk-def, as a matter of fact, I'm pretty sure Brigandine does with a slight randomizer and elemental adjustments. My desire for SoM has always been to make it easier for the common fan to make a game. And the problem that everyone mentions about SoM damage is that it's too abstract to adjust without tons of trial and error or calculation. That's just wasted time that would be better spent on other aspects of game development. ‎ 

This new formula is atk-def. It just has a damage window from about 70% to 140%. This models real life expectations better than atk-def. Since if you bang two things together that are approximately the same hardness they damage one another. Whereas atk-def only begins to produce damage when ATK is harder than DEF. Please read posts carefully so I don't have to repeat myself so much.

Quote:The bottom line is "how many hits does it take to kill something". No matter how fancy the damage formula is, it always boils down to that same thing. If a player with 29 ATK goes against an enemy with 100 HP, 18 DEF how many hits would it take to kill the enemy using this formula: ATK-max(0,DEF-ATK*ATK/(DEF*2)) ? Good luck without a calculator.

In that case the ATK overwhelms the defense and does 29 damage. Around 12 ATK it can't penetrate the defense and deals 0 damage. Or 1 using the 0=1 handicap. This is like real life armor. If you just remember that when the two are matched 50% of damage is dealt you'll be fine. The formula is well behaved. Unlike SOM's.

Quote:But if the formula is "damage=atk-def", most people can do the math instantly in their head (29-18=11 100/11 = 9.x = 10 hits). Because of that, players can also evaluate equipment in a meaningful way. In KF, if I found a weapon with "56 Slash atk", I had no idea what that meant other than it was better than "55 slash". ‎  With a simple formula, players can weigh the pros and cons of equipment and plan strategically. That adds potential enjoyment to the game.

Yeah, but it doesn't model anything like real physical dynamics. So its more confusing. And very unforgiving. You can't have parity between the weapons and the armor, since the weapons must always be harder to make the slightest dent.

Quote:SoM's default formula IS "damage = 1.5xATK - DEF" as long as stats are balanced. The only point in deviating from that formula as stats grow apart is to restrict players from going into areas where they aren't supposed to be yet - aka "rat in a maze" game design. ‎  If someone wants to make a linear "rat in a maze" game, there are far easier ways of doing it.

Well that's why this formula. When matched it produces the same results. Plus it isn't a radical departure from SOM's identity. It just shifts its terms around very slightly to produce something that is not so impenetrable, if not ill conceived.

Quote:I've said all there is to say, and you never change your mind once you've decided so I'm done talking on that particular topic.

You'll prefer this formula. I didn't expect it. It just emerged organically. Call it divine providence.


PS: For the record. My concern is always for the player. Both in game. And when they first open up the Ex.ini file out of curiosity. I want them to feel welcome and get sucked in. Just like reading a book. Writing is difficult. Reading should be easy.

EDITED: News Flash. There is also handicap_quantifier to change the behavior of 0=1. There will probably a Player handicap preference too, but it will be a straight scalar. This one is intended for authors.

BTW: Am I the only one that gets freezed out of this site after a couple page reloads? I just expect for this site to go dead for 5 minutes at least once every time I use it. Might want to clean the lent trap on the database.
Reply

#55
Just out of curiosity, can you define "rat in a maze"?

Do you mean for instance you can't go somewhere because it will be Game Over if you get touched?

Or is it you can't dispose of an obstacle? Because if its the latter, say a giant in a doorway with 127 defense. Then with ATK-DEF you are going to being doing 0 damage unless your weapon is at 128. Unless you can make it under the giants legs he may as well be a locked door.

Whereas with this formula if your weapon is at 127 you do 63 damage. And at 89 you do 1 damage. At 177 or more you do 177.

Compare to 127 is 0 damage. 89 is 0 damage. And 177 is 50 damage.

Remember you are stuck between 0 and 255.


PS: Also consider ATK-DEF while being unintuitive also throws out at least half of the range. If you make a monster that has 250 defense. Then the best weapon the player can muster can only possibly do 5 damage plus bonus. Whereas if you hit that monster with a 250 weapon with SOM's formula you do 128 damage, which is what you expect when two equally matched opponents square off. In other words, a tie.
Reply

#56
For the record, the affinity is going to be supplied as a third parameter in case anyone wants it.

The second parameter turned out to be the evasion defense bonus that monsters get. I reckon players will get it too somehow if they are holding up a shield. The second argument for attackers is 0. But if there was a similar mechanic for attackers it will be nonzero. That would have to be an extension. For example how you can do extra damage when falling in Dark Souls.

So in other words, you must incorporate the evasion into hit_offset_quantifier if you set that up. Or else. The bonus won't be in play.


PS: I have no idea what to do about the power gauge yet. Basically it happens before handicap_quantifier and scales down your damage linearly. I don't think it's part of damage per se. So I am not setting up any extensions for it until I have a better idea of what to do. Seems fine the way it is for now.

I think it falls more into a physical simulation category than damage gamification. Whereas gamers we don't want to know how materials are torn apart. We don't want to play a game that cares about that. Because it wouldn't be a game. But how your arm moves is something much simpler, and familiar...

Also I have long concluded that the gauges do not represent power or stamina. They are timing gauges. To the extent that they will represent power will only be to the extent that you use them as an aid to hit the right timing. With the exception of the magic gauge. Sometimes it can represent power draining out. Like air when swimming. Or A magic field waning in strength over time.
Reply

#57
^Actually. Supplying the affinity is really handy. I was thinking about implementing weights for the affinities as an extension. But you don't need that since you can just setup a bank of weights and use that parameter to get at them.

For example. For King's Field I think Fire should deal more damage than Earth. Automatically. So you don't have to think about that when setting up the stats. And likewise Fire should have less defense than Earth. So you'd just setup a number like so:

[Number]
;1 1 1, Fire Wind Water Earth, Holy
hpWeights = 1 1 1, 1 0.9 0.8 0.7, 1
_hpWeights = 1 1 1, 0.7 0.8 0.9 1, 1

Then use those in your bonus formulas.

[Damage]
;Magic stat bonuses
hit_point_quantifier2 = (1_+2pc/5+2_)*hpWeights[3_]
hit_offset_quantifier2 = (1_+2pc/5+2_)*_hpWeights[3_]

In this case you could just compute _hpWeights with (1-hpWeights[3_]). In this case you could also factor in the weights with hit_outcome_quantifier instead in most cases. Wherever its easier to read would usually be best.

This is going to be a big load off my back and help keep the number of extensions to a minimum.
Reply

#58
^Looking at that reminds me that using an additive bonus (+2pc/5) is really broken. Because if it adds, then it should add to 0 stats too. And you basically get a huge bonus just for the weapon having a 1 rating affinity, since that enables the bonus on that affinity.

Multiplication based bonuses has to be the way to go. I'm tempted to even use a different bonus formula by default, since it seems so broken the way it works Movingeyes
Reply

#59
For what it's worth you could hexedit your PRM file to make a monster that inflicts more than one status ailment. It would do them all at once (like the Lovecraftian plant monster in Final Fantasy games) but I can't see any reason why it wouldn't work.
Reply

#60
FYI: I've attached a demo to this post (\<span> site blocked, contact your administrator/bbs2/index.php?topic=142.msg1196#msg1196) but I haven't yet announced it in the thread. I'll do that tomorrow after getting some sleep.

If your numbers have a syntax error you'll get a game over popup box. It's better than nothing for now. The error is not described. But the expression is in the popup's title bar.

Also. I went and changed handicap_quantifier to hit_handicap_quantifier...

The following mentioned extensions are not implemented and odds are good they won't be in the next release.

hit_point_mode
hit_point_model
hit_penalty_quantifier

The damage loop looks like this:

Code:
    DWORD ebp = dst; *out = 0;
    for(esi=0;esi<8;esi++,ebp+=2)
    {
        /*
        0040459E 33 F6            xor         esi,esi
        004045A0 33 C0            xor         eax,eax
        004045A2 8A 44 33 04      mov         al,byte ptr [ebx+esi+4]
        004045A6 84 C0            test        al,al
        004045A8 74 77            je          00404621
        004045AA 0F BF 4D 00      movsx       ecx,word ptr [ebp]
        004045AE 25 FF 00 00 00   and         eax,0FFh
        004045B3 83 FE 03         cmp         esi,3
        004045B6 89 44 24 28      mov         dword ptr [esp+28h],eax
        004045BA DB 44 24 28      fild        dword ptr [esp+28h]
        004045BE 89 4C 24 28      mov         dword ptr [esp+28h],ecx
        004045C2 DB 44 24 28      fild        dword ptr [esp+28h]
        004045C6 D8 44 24 10      fadd        dword ptr [esp+10h]
        004045CA D9 5C 24 28      fstp        dword ptr [esp+28h]
        */
        float offset = *(SHORT*)(ebp);        
        float rating = *(BYTE*)(ebx+esi+4);
        
        if(!rating) continue; //todo: extension to bypass this?
        /*        
        004045CE 7D 0E            jge         004045DE
        004045D0 D8 44 24 14      fadd        dword ptr [esp+14h]
        004045D4 D9 44 24 28      fld         dword ptr [esp+28h]
        004045D8 D8 44 24 1C      fadd        dword ptr [esp+1Ch]
        004045DC EB 0C            jmp         004045EA
        004045DE D8 44 24 18      fadd        dword ptr [esp+18h]
        004045E2 D9 44 24 28      fld         dword ptr [esp+28h]
        004045E6 D8 44 24 20      fadd        dword ptr [esp+20h]
        */        
        if(&hp->hit_point_quantifier)
        {
            SOM::L.pcstatus = srcStatus;

            if(esi<3||!&hp->hit_point_quantifier2)
            {
                rating = hp->hit_point_quantifier(rating,0,esi);
            }
            else rating = hp->hit_point_quantifier2(rating,0,esi);
        }
        else rating+=srcBase[esi>=3];

        if(&hp->hit_offset_quantifier)
        {
            SOM::L.pcstatus = dstStatus;

            if(esi<3||!&hp->hit_offset_quantifier2)
            {
                offset = hp->hit_offset_quantifier(offset,evasion,esi);
            }
            else offset = hp->hit_offset_quantifier2(offset,evasion,esi);
        }
        else offset+=dstBase[esi>=3]+evasion;
        /*
        004045EA D9 C1            fld         st(1)
        004045EC D8 D9            fcomp       st(1)
        004045EE DF E0            fnstsw      ax   
        004045F0 F6 C4 41         test        ah,41h
        004045F3 75 0D            jne         00404602
        004045F5 D9 C1            fld         st(1)
        004045F7 D8 E1            fsub        st,st(1)         
        004045F9 E8 3A B3 04 00   call        0044F938
        004045FE 8B F8            mov         edi,eax
        00404600 EB 02            jmp         00404604
        00404602 33 FF            xor         edi,edi
        */                        Â   
        LONG edi = 0; //max(0,rating-offset);
        /*                                Â 
        00404604 D9 C1            fld         st(1)
        00404606 D8 CA            fmul        st,st(2)
        00404608 D9 C9            fxch        st(1)
        0040460A DC C0            fadd        st(0),st
        0040460C DE F9            fdivp       st(1),st
        //not sure what this does but INF becomes 0//
        0040460E E8 25 B3 04 00   call        0044F938
        00404613 DD D8            fstp        st(0)
        00404615 03 F8            add         edi,eax
        */
        if(&hp->hit_outcome_quantifier)
        {
            SOM::L.pcstatus = 0; //NaN

            *out = //custom formula
            hp->hit_outcome_quantifier(rating,offset,esi);
        }
        else if(!bf||bf->do_fix_damage_calculus)
        {    
            if(offset>0) //default formula
            {
                edi = rating-
                max(0,offset-(rating*rating/(offset*2)));
                if(edi<0) edi = 0;
            }
            else edi = rating;
        }
        else //classic formula with bugs
        {
            edi = max(0,rating-offset);

            rating*=rating; offset+=offset;

            if(offset) edi+=rating/offset;
        }
        /*
        00404617 85 FF            test        edi,edi
        00404619 7E 06            jle         00404621
        0040461B 8B 44 24 30      mov         eax,dword ptr [esp+30h]
        0040461F 01 38            add         dword ptr [eax],edi
        00404621 46               inc         esi
        00404622 83 C5 02         add         ebp,2
        00404625 83 FE 08         cmp         esi,8
        00404628 0F 8C 72 FF FF FF jl          004045A0
        */
        *out+=edi; //why jle and not je???
    }
    /*
    0040462E 8B 43 14         mov         eax,dword ptr [ebx+14h]
    00404631 BF 18 1A 9C 01   mov         edi,19C1A18h
    00404636 3B C7            cmp         eax,edi
    00404638 75 1B            jne         00404655
    */
    DWORD edi = 0x19C1A18;
    if(*(DWORD*)(ebx+0x14)==edi) //0x19C1A18
    {
        //player power gauge
        /*
        0040463A 8B 4C 24 30      mov         ecx,dword ptr [esp+30h]
        0040463E 33 D2            xor         edx,edx
        00404640 66 8B 53 0E      mov         dx,word ptr [ebx+0Eh]
        00404644 B8 59 17 B7 D1   mov         eax,0D1B71759h
        00404649 0F AF 11         imul        edx,dword ptr [ecx]
        0040464C F7 E2            mul         eax,edx
        0040464E C1 EA 0C         shr         edx,0Ch
        00404651 89 11            mov         dword ptr [ecx],edx
        00404653 EB 04            jmp         00404659
        */

        LONG edx = *out**(WORD*)(ebx+0xE);

        edx = (0xD1B71759ULL*edx)>>32; //mul edx
        
        *out = edx>>0xC;
    }
    SOM::L.pcstatus = pcstatus;
    /*
    00404655 8B 4C 24 30      mov         ecx,dword ptr [esp+30h]
    00404659 83 39 00         cmp         dword ptr [ecx],0
    0040465C 75 06            jne         00404664
    0040465E C7 01 01 00 00 00 mov         dword ptr [ecx],1
    */
    if(&hp->hit_handicap_quantifier)
    {
        SOM::L.pcstatus = dstStatus;
        *out = hp->hit_handicap_quantifier(*out);
        SOM::L.pcstatus = pcstatus;
    }
    else if(*out==0) *out = 1;

I would like to change the default bonus if I don't forget. I'd like to hear any suggestions.


EDITED: I don't think I am going to do anymore work with this. The one thing I may do is better error messages for number syntax errors. Especially if anyone has problems. Of course there is a lot of documentation to do. And also. Very important. The damage extensions only work with som_db.exe. So until this release is done, they will only be in effect for project trials.
Reply





Users browsing this thread:
8 Guest(s)