Lerk: circling vulture no more!

24

Comments

  • swalkswalk Say hello to my little friend. Join Date: 2011-01-20 Member: 78384Members, Squad Five Blue
    Much clearer, and I completely agree!
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    <!--quoteo(post=1892500:date=Jan 3 2012, 03:26 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 3 2012, 03:26 PM) <a href="index.php?act=findpost&pid=1892500"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Well, then it appears to work as I said. Atleast for 220 damage you end up losing 100 health with vanilla armor and light damage, and for 333.333 damage you lose 100 health with 90 armor and light damage.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Yeah, that's right. I was just making a generalisation (so that you don't have to test each and every attack), which is much more useful for questions of balance.
    So you can look at any health/armour pair versus a DPS/type pair, and easily determine the effectuve time it takes to kill it (assuming perfect aim, or specifying some arbitrary "miss" rate), or versus a raw-damage/type pair, and easily determine the effective health.

    <!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->
    You could probably create an Excel spreadsheet that compares everything to everything else (attacks along the side, targets along the top). You could have the numbers in one embedded sheet, then call the numbers in a second embedded sheet that matches each weapon to victim on a matrix that outputs time taken (and another sheet that can output ammunition, or energy). It would be a fair bit of work, and probably not for us, but if you set it out right you can probably just copy-paste/drag-across for each set of data.
    (I think that time taken to kill is a better metric for evaluating a base-line for balance than raw damage. Obviously there are so many other factors, but I would personally start from something numerically sound.)
    <!--sizec--></span><!--/sizec-->

    <!--quoteo(post=1892500:date=Jan 3 2012, 03:26 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 3 2012, 03:26 PM) <a href="index.php?act=findpost&pid=1892500"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->What file did you find that code in?<!--QuoteEnd--></div><!--QuoteEEnd-->
    LiveScriptActor_Server.lua
    May possibly be called something else now, but I doubt it.

    <!--quoteo(post=1892500:date=Jan 3 2012, 03:26 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 3 2012, 03:26 PM) <a href="index.php?act=findpost&pid=1892500"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Also, only light damage marines have are sentry and pistol, so I don't get why you put light damage by the skulk?<!--QuoteEnd--></div><!--QuoteEEnd-->
    No reason, it was just an example (to illustrate the effect of different damage types). The Excel'd example uses normal damage (unupgraded rifle) against an unupgraded skulk. Also, pistols were heavy damage, last I looked (which was about a year ago).

    Btw, Fluid Core, your analysis of the problem is spot-on, I think.

    However, I would elaborate on this as:

    *Minimum static damage: 10 damage (20)
    Maximum static damage: 18 damage (24)
    *Maximum negative speed damage: -18 damage (0)
    Minimum negative/positive speed damage: 0 damage (0)
    Maximum positive speed damage: 18 damage (0)

    This better illustrates the proposition (since speed damage is just as variable as static damage, if not moreso).

    Actually, to simplify it:

    Static damage: 10~18 (20~24)
    Speed damage: -18~18 (0~0)

    *This is where I think things could get a bit iffy. It is rarely possible to fly directly backwards at maximum speed while simultaneously firing, but given a far enough distance, one would actually, impossibly, produce negative damage.

    The maximum and minimum from my elaboration+simplification leads to:

    Total damage = static damage + speed damage : -8 (20) ~ 36 (24)
    <!--coloro:gray--><span style="color:gray"><!--/coloro--><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->where minimum = flying directly backward at maximum speed and being at or beyond upper threshold (kSpikeMinDamageRange) of distance away from target
    and maximum = flying directly forward at maximum speed and being at 0<b>^</b> distance away from target<!--sizec--></span><!--/sizec--><!--colorc--></span><!--/colorc-->

    As a result, I would instead specify the static minimum and speed maximum as equal to one another, with something along these lines:

    Static damage: 16~20
    Speed damage: -16~16

    Maximum and minimum are:

    Total damage = static damage + speed damage : 0 (20) ~ 36 (24)
    <!--coloro:gray--><span style="color:gray"><!--/coloro--><!--sizeo:1--><span style="font-size:8pt;line-height:100%"><!--/sizeo-->where minimum = flying directly backward at maximum speed and being at or beyond upper threshold (kSpikeMinDamageRange) of distance away from target
    and maximum = flying directly forward at maximum speed and being at 0<b>^</b> distance away from target<!--sizec--></span><!--/sizec--><!--colorc--></span><!--/colorc-->

    <b>^</b>makes me think there should be a lower threshold, since while I don't want minimum damage (zero) to be attainable (and it won't be), I do want maximum damage to be attainable.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    <!--quoteo(post=1892596:date=Jan 4 2012, 05:32 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 4 2012, 05:32 AM) <a href="index.php?act=findpost&pid=1892596"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Btw, Fluid Core, your analysis of the problem is spot-on, I think.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Thanks :)

    I actually tried what happened with negative damage before. You just doesn't deal any damage, so we don't have to worry of any bizzare effects there. Going to take a look at what speed (will look at damage..) I can get while flying backwards before I go to bed.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    Still, there will be a fairly significant <b>region</b> where you will have negative damage (zero damage), while I would prefer a minimum extreme <b>point</b> that is zero, so that you, effectively, always do <b>some</b> damage.

    I've illustrated it here:
    <img src="http://i.imgur.com/OJpnS.png" border="0" class="linked-image" />

    Setting maximum (ergo negative maximum) speed damage to be the same as the minimum static damage, means that only under that one practically unattainable circumstance will you ever get zero damage.

    Also, this is what I'd like to see regarding static damage:
    <img src="http://i.imgur.com/cMciw.png" border="0" class="linked-image" />
    <strike>Perhaps:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->if distToTarget > kSpikeMaxDamageRange
            local damageDistScalar = Clamp(1 - ((distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange)), 0, 1)
    else
            local damageDistScalar = 1
    end<!--c2--></div><!--ec2--></strike>

    Edit: Oh. I just realised:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local damageDistScalar = Clamp(1 - ((distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange)), 0, 1)<!--c2--></div><!--ec2-->
    already does it all in one line, because of the upper clamp (1).
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    Just you wait, there is more to mess it up. I noticed two things during my test.

    You need to include GetViewAngles() after all. For some reason the zAxis if GetCoords() doesn't actually have any y-component. But that's an easy fix.

    What is more interesting is that the speed isn't actually capped at 13. When I flew to the sky in crevice and dived down, I got up to 24 in speed. If I went all the way to the bottom it got up to 30 or so.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    Just... add a clamp?

    Not the most elegant solution, but it is a solution...

    I suppose it's because diving leads to you being affected by gravity directly.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    I guess it could work that way, yes. As for your 16/20 static suggestion. the lerk will be way to efficient at range with 16 damage each spike. This is getting less elegant the more we work on it :P
    Halving movement deduction while moving backwards also work fairly well.

    Another solution is to actually have it scale as if it was kinetic energy that did the damage.

    ((spikespeed+lerkspeed)^2)/(spikespeed^2)

    That's the damage multiple from speed. Then we add a distance scalar and multiply with some constant to get the values in the range we want it to be. This way we can't get nagatiive or even 0 damage unless we move faster then we set the spikes. But then the problem would be to get the damage sufficiently large when moving fast but starting from a distance..
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    I suppose you're right.

    Alternatively, and I know you didn't want to do this initially, you could apply the damageDistScalar over the total of the damage

    so damage = damageDistScalar*(static damage (18) + speed damage (-18~18))

    Edit: I wonder if you could add an airspeed indicator to the HUD, just for fun. :P
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    <!--quoteo(post=1892611:date=Jan 4 2012, 08:00 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 4 2012, 08:00 AM) <a href="index.php?act=findpost&pid=1892611"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Alternatively, and I know you didn't want to do this initially, you could apply the damageDistScalar over the total of the damage<!--QuoteEnd--></div><!--QuoteEEnd-->

    Yes, I've been pondering about such an implementation. But the more I try to make it work, the more I realise that you can't balance the speed and static components at the same time for both short and long distance. One of the distances will end up being broken (much to high or much to low).

    <i>I'll sleep on it...</i>
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    <!--quoteo(post=1892610:date=Jan 4 2012, 02:50 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 4 2012, 02:50 PM) <a href="index.php?act=findpost&pid=1892610"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Another solution is to actually have it scale as if it was kinetic energy that did the damage.

    ((spikespeed+lerkspeed)^2)/(spikespeed^2)<!--QuoteEnd--></div><!--QuoteEEnd-->
    Hey, I made that suggestion earlier, but you didn't comment on it :P

    The problem we're having is that we've got this goal that is a contradiction, right?

    We want to reward flight, so we add speed-based damage.
    We want to encourage close-quarters attacking and discourage sniping from a distance, so we add damage drop-off with distance and have a loose cone of fire.

    But we also want to keep flight from a distance rewarding enough (that is, do enough viable damage from distance).

    I think that the main benefit of a larger cone of fire is that it is obvious to a player that attacking from a distance is inadvisable. It's easy to see that you are doing less damage because you know you are hitting less. On the other hand, damage drop-off is not that obvious to a player since we don't have that kind of feedback. However, it is the cone of fire that is, I think, the primary offender when it comes to trying to reconcile this contradiction; players who attack from a distance are already penalised enough by the cone of fire alone and [I have no idea what I was going to say here].

    I'm wondering if we could play with the cone of fire - have the cone of fire start small and gradually increase to what it is now over a couple seconds. So at the start of the flight, the cone of fire is smaller, therefore making it easier to hit. But this also makes it easier for stationary players to hit. Also, unfortunately that leads to encouraging burst fire, so I don't think that would work at all. Perhaps you could have the cone of fire as a function of speed as well, but that doesn't really... make sense.
    Frankly, if we could drastically reduce the cone of fire, I think it would solve our problems to a large degree - then we just gotta work out how to give the player that feedback: proximity = damage and speed = damage.

    I'm wondering if you could play with the hit indicator (the battlefield style thing), so that the hit indicator is larger or more enhanced when you do more damage - though it would still only communicate the idea to the player with their own trial, error and inference. I would have the colour start off yellow and the indicator be very small, almost unnoticeable when you're doing very little damage; and when you're doing a lot of damage, have the indicator be closer to red and the indicator be closer to two to three times as large. It isn't exactly realistic that you'd have that information... but hell, are hit indicators realistic at all?

    You know what, you said that 16 damage per spike makes lerks too efficient at a distance, but we've gotta consider that they are also under the effect of a somewhat loose cone of fire, so they are penalised by that AND by the fact that they don't get the speed bonus, and that may be enough.

    I mean, look, we've got this:
    Stationary, distant: 16 damage, low hit-rate.
    Stationary, close: 20 damage, high hit-rate.
    Maxspeed, distant: 32 damage (double stationary!), low hit-rate.
    Maxspeed, close: 36 damage (even better), high hit-rate.

    In fact the damage difference between maxspeed at a distance and maxspeed when close is very low, but the reward between maxspeed and being stationary is very high. And I think we can't reward swooping lerks from a distance <b>too much</b>, because then they could just stay at a distance, swoop half-way, retreat, and repeat, couldn't they? I think these numbers, or numbers following the same kind of theme, are good. Actually the only "problem" is that the damage difference between distance and proximity is quite small, but then you also gotta consider that the accuracy/hit-rate would skew the effectiveness considerably.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    Yes, I know you did.
    What the kinetic approach does is that it handles backward movement very well. I suppose we could play with the cone of fire. And you are right about the cone of fire, but I don't think having it vary is a good solution. The cone is twise as large as the rifle. It was even bigger, but I felt like you weren't hitting any spikes berore getting very close to marines then. I think my earlier suggesrion with damage decrease on distance works well. Before you consider backward movement. Something that your numbers handle poorly too. I'm pretty fond of the kinetic way to handle it, will see if I can figure out a good way to keep the contradiction balanced.

    Got three ideas

    <ol type='1'><li>(((spikespeed+lerkspeed)^3)/spikespeed^3)*stationary</li><li>(((spikespeed+lerkspeed)^2)/spikespeed^2+(spikespeed+lerkspeed)/spikespeed-1)*stationary</li><li>((spikespeed+lerkspeed)^2+(spikespeed+lerkspeed))/(spikespeed^2+spikespeed)*stationary</li></ol>

    The division is to normalize the variables to 1 at zero speed. The first and third option never get negative damage, while the second option can reach that.

    All three implementations have very similar curves once you tweak the spike speed. The second option will have lower backward damage then the other two with similar forward speed damage. This is probably a result from that the second curve can get negative at high backwards speed. You can adjust both the spike speed and the stationary damage to reach values that you like. The lower the spike speed, the steeper the slope become. You can quite easily add some distance reduction and balance it anyhow.

    Example for all three options
    Spike speed: 73/60/45
    Stationary maximum damage: 20
    Stationary minimum damage: 15

    Minimum:
    Backward 13: 8.33/5.95/7.65
    Stationary: 15/15/15
    Forward 13: 24.53/25.45/24.80
    Forward 20: 31.01/31.67/31.09

    Maximum:
    Backward 13: 11.10/7.94/10.20
    Stationary: 20/20/20
    Forward 13: 32.70/33.94/33.06
    Forward 20: 41.35/42.22/41.45

    I like the third option most, because there you can view the stationary portion as the mass of the spikes, and the division just converts energy to damage for that particular spike. Then to account for drag we simply reduce the mass of the spike.
    Since all three have very similar results, I suggest we would use the first option however, as I think that may be less calculation intensive.

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local static = 15 + damageDistScalar * 5
            local movement = player:GetVelocity():DotProduct(player:GetViewAngles():GetCoords().zAxis)
            damage = ((73+movement)^3)/(73^3)*static<!--c2--></div><!--ec2-->

    <i>How many times I ended up editing that post...</i>
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    Full code so far.

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Fluid Core: Reduced spread slightly and amount of secondary spikes was reduced
    local kSpread = Math.Radians(6)
    local kNumSpikesOnSecondary = 4<!--c2--></div><!--ec2-->

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->                local damage = kSpikeMinDamage + damageDistScalar * (kSpikeMaxDamage - kSpikeMinDamage)
                    
                    //Yuuki: Make damage increase based on your speed
            //max damage = 20
            //min damage = 15
            local static = 15 + damageDistScalar * 5
            local movement = player:GetVelocity():DotProduct(player:GetViewAngles():GetCoords().zAxis)

                    damage = ((73+movement)^3)/(73^3)*static<!--c2--></div><!--ec2-->

    <b>This is a server-side mod. Just add the lines of code that are missing in the Spikes.lua file (found in ...\ns2\lua\weapons\Alien). Save it like any mod, but replacing the original would probably work fine if you want it permanent for your servers.</b>
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    I like that, mostly. A couple of things though:

    1. I would prefer a lower threshold for the variable static damage range, i.e. set a maximum damage range, so
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local damageDistScalar = Clamp(1 - ((distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange)), 0, 1)<!--c2--></div><!--ec2-->
    (i.e. when distToTarget <= kSpikeMaxDamageRange, damagedistScalar = 1)
    It can be something up close, like kSpikeMaxDamageRange = 1.2; it just makes it so that maximum damage is genuinely attainable.

    2. I don't understand why it is raised to the power of 3.
    Shouldn't it be raised to the power of 2 since:

    (vS = spike speed, vL = lerk speed)

    "static" = KE1 = m.(vS)^2 / 2
    "static plus movement" = KE2 = m.(vS+vL)^2 / 2

    (ratio of "static plus movement" to "static")
    (KE2/KE1) = (vS+vL)^2 / (vS)^2

    KE2 = KE1 * (KE2/KE1)
    "static plus movement" = "static" * (vS+vL)^2 / (vS)^2

    Mathematically sound.

    Example
    <img src="http://i.imgur.com/uaYvN.png" border="0" class="linked-image" />

    <a href="http://www.mediafire.com/?1t492r2q1tt2vds" target="_blank">Excel2010 spreadsheet</a> (mediafire) if you wanna play around with the numbers.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    Quite frankly, it doesn't really matter. I started doing that while I was just comparing with the same (52) spike speed for all and just static damage. With the same values for spikes, I think you got to low damage at speed. But the slopes for all the curves look very similar if you change the spike speed individually. What I got the cubic portion from in the first place, was that I thought that I should include the momentum as well as the kinetic energy, since I figured that component would also effect the damage. Didn't get any good values (for the same reason), so tried multiplying instead. Got better values for the same speed.

    If you just take kinetic energy then a spike speed of 45 gives similar values for all speeds as I showed above. As for the damageDistScalar, that's the official, haven't changed it at all. Not sure how it works either..
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    <!--quoteo(post=1892689:date=Jan 5 2012, 05:59 AM:name=Harimau)--><div class='quotetop'>QUOTE (Harimau @ Jan 5 2012, 05:59 AM) <a href="index.php?act=findpost&pid=1892689"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec--><img src="http://i.imgur.com/uaYvN.png" border="0" class="linked-image" />

    <a href="http://www.mediafire.com/?1t492r2q1tt2vds" target="_blank">Excel2010 spreadsheet</a> (mediafire) if you wanna play around with the numbers.<!--QuoteEnd--></div><!--QuoteEEnd-->

    I made a program and a plot for my TI-84 ;)

    But thanks, might be quicker to check out good values. Oh, and the comparison I did, was between the three different models, in case you missed that, since you had the three different values for one model there. I was evaluating if it mattered which one we choose. As long as we are fine with whatever spike speed (have no other effect then the number), then it doesn't matter much. All can be tweaked to give pretty much the same values for all speeds.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    Well I'm being pedantic but I think it should be raised to the power of 2, and I actually just wrote the proof for it, so now it actually has some kind of basis in reality.
    The idea is that spike velocity and lerk velocity are additive, and mass of the spike is constant.
    We have two situations;
    standing still, we only have the spike speed: KE1, where the velocity is just spike speed: "static" = KE1 = m.(vS)^2 / 2
    moving, we have the added speed: KE2, where the velocity is spike speed plus lerk speed: "static plus movement" = KE2 = m.(vS+vL)^2 / 2
    The ratio between the two is KE2/KE1, the common factors of mass and (1/2) cancel out: ratio = (KE2/KE1) = (vS+vL)^2 / (vS)^2
    And as we know, KE2 = KE1 * KE2/KE1 { i.e. a = b * a/b = a * b/b = a * 1 = a }: "static plus movement" = "static" * (vS+vL)^2 / (vS)^2
    This actually, genuinely, makes sense.

    damageDistScalar is pretty simple.
    distToTarget is just as it sounds, it computes the distance between your origin and your target's origin (or it could be between your 'barrel' and their hitmodel, not sure, you'd have to look).
    There's a SpikeMinDamageRange (i.e. if you exceed this range, you get the constant minimum damage).
    If you are within this range, then the calculation is (1 - distToTarget/SpikeMinDamageRange):

    For values of distToTarget less than SpikeMinDamageRange, the calculation becomes 1 - (0~1) = (1~0)
    For values of distToTarget greater than SpikeMinDamageRange, the calculation becomes 1 - (>1) = (<0)
    but we can't have values of the damage scalar larger than 1 or less than 0, so we put it in a clamp from 0 to 1.
    So when the calculation is less than 0, the output will just be 0; and when the calculation is greater than 1, the output will just be 1.

    e.g. distToTarget/SpikeMinDamageRange = 15/20
    => damageDistScalar = 1 - 15/20 = {0.25} 0.25

    e.g. distToTarget/SpikeMinDamageRange = 21/20
    => damageDistScalar = 1 - 21/20 = {-0.05} 0

    So we have a minimum damage + a "bonus" damage inversely proportional to distance, since it's damage = minimum + scalar * (maximum - minimum)

    What I'm proposing with my code:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local damageDistScalar = Clamp(1 - ((distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange)), 0, 1)<!--c2--></div><!--ec2-->
    is that rather than the range to reach maximum damage be zero (i.e. touching the marine), the range is positive (you get maximum damage within a couple metres for instance)
    just like the following graph:
    <img src="http://i.imgur.com/cMciw.png" border="0" class="linked-image" />
    So what does this mean?

    For values of distToTarget less than SpikeMaxDamageRange, the calculation becomes 1 - (<0) = (>1): clamped to 1
    For values of distToTarget less than SpikeMinDamageRange and greater than SpikeMaxDamageRange, the calculation becomes 1 - (0~1) = (1~0)
    For values of distToTarget greater than SpikeMinDamageRange, the calculation becomes 1 - (>1) = (<0); clamped to 0

    e.g. (distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange) = (1-2)/(20-2)
    => damageDistScalar = 1 - (1-2)/(20-2) = {1.0555556} 1

    e.g. (distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange) = (15-2)/(20-2)
    => damageDistScalar = 1 - (15-2)/(20-2) = {0.277778} 0.277778

    e.g. (distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange) = (21-2)/(20-2)
    => damageDistScalar = 1 - (21-2)/(20-2) = {-0.055556} 0

    You would have to change other parts of the game code (like Balance.lua, and maybe the start of Lerk_Server.lua), just to slot "kSpikeMaxDamageRange" in, since it's a new number. (Alternatively you could just hard-code it in:)
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->        local damageDistScalar = Clamp(1 - ((distToTarget - 2) / (kSpikeMinDamageRange - 2)), 0, 1)<!--c2--></div><!--ec2-->
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    I'm well aware about how kinetic energy is calculated. :)
    Luckily for us, the spike speed have no other effect on the game though, and we can treat the problem as purely mathematical and adjust the formula to get the values we want. Physics is a good pointer though. I suppose I just never took the time to look at clamp, was just happy to not have to code that too. Fairly certain you have the max damage range in balance.lua as kSpikeMaxDamage=2 already, but can't look now.
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    Well, I personally think we should balance the numbers using the physical formula rather than a contrived one. The static damage is just a base damage, so we can effectively ignore that as a starting point, and just play around with the spike speeds to determine a decent damage scalar. So, setting static damage as simply "100%", I've illustrated it here:
    <img src="http://i.imgur.com/dmzHV.png" border="0" class="linked-image" />
    <a href="http://www.mediafire.com/?m9798o20ops7ilt" target="_blank">Excel</a>



    Strange. If the max damage range is in fact already in balance.lua, it doesn't look to actually be implemented.
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    That, is one chart I truly love!

    The backward speeds aren't that important, just nice to know that we take account for them. Looking at the forward speeds, assuming a static damage of 15-20, you'll want a spike speed ranging from about 60 to 30. Any higher then that and you'll snowball way out of line with high places like crevice. The lower range will require to high static damage for decent total damage to be considered.

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->kSpikeMinDamageRange = 16
    kSpikeMaxDamageRange = 2<!--c2--></div><!--ec2-->

    Line 325 and 326 in balance.lua.


    The physical approach sure makes sense, but I can't help but think that it doesn't quite give the appearance that I'd like to see on the scaling. Got a nice idea in my head on the image of the graph, but can't recall what kind of function give that look. Will see if I find something in my mathematics or physics handbook.

    The appearance I'm looking for is a graph of a similar shape such as (x)^(1/3), where the graph approaches a upper and lower limit +/- infinite, respectively, and aren't as steep close to zero. I will keep looking though.

    6 minutes later and I found the shape I was looking for. Will try to make a nice graph to add here too...

    <b>y=tanh(x)=((e^x)+(e^-x))/((e^x)-(e^-x))</b>

    <img src="http://img18.imageshack.us/img18/1778/spikedamage.jpg" border="0" class="linked-image" />

    While not a physical representation, I think it behaves nicely in that high speeds doesn't get extreme values and you can adjust the slope pretty much any way you like it to reach the cap sooner or later, as well as adjusting the actual contribution from speed and not affect how quick it is reached.

    I didn't bother at the time of creation, but to align the curve so that x=0 gives 100% damage, set the function to y=tanh(x/9-0.5493)+1.5
  • YuukiYuuki Join Date: 2010-11-20 Member: 75079Members
    If find this kind of function is easier to work with :

    x^n / (a^n + x^n) (high to low)

    1 / (a^n + x^n) (low to high)

    a is the threshold (where the function change from high to low) and n is the steepness.

    <a href="http://www.wolframalpha.com/input/?i=plot+1+%2F+%282%5E3+%2B+x%5E3%29++between+0+and+6" target="_blank">http://www.wolframalpha.com/input/?i=plot+...between+0+and+6</a>

    Otherwise you got the sigmoid :

    <a href="http://en.wikipedia.org/wiki/Sigmoid_function" target="_blank">http://en.wikipedia.org/wiki/Sigmoid_function</a>
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    <!--quoteo(post=1892817:date=Jan 6 2012, 10:44 AM:name=Yuuki)--><div class='quotetop'>QUOTE (Yuuki @ Jan 6 2012, 10:44 AM) <a href="index.php?act=findpost&pid=1892817"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->x^n / (a^n + x^n) (high to low)

    1 / (a^n + x^n) (low to high)

    Otherwise you got the sigmoid :<!--QuoteEnd--></div><!--QuoteEEnd-->

    Don't you just love wolframalpha? :)

    The problem with the first two functions is that they reach infinite at certain points. The Sigmoid function is just what I was looking for, although the same s-shape is retained for tanh, that may not be in the lua. I would surely hope the natural number (e) is though...
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    By the way, with the physical approach:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->static * (lerkspeed + spikespeed)^2 / (spikespeed)^2
    static * ((lerkspeed + spikespeed)^2 / (spikespeed)^2 * [1]
    static * ((lerkspeed + spikespeed)^2 / (spikespeed)^2 * [(1/spikespeed)^2 / (1/spikespeed)^2]
    static * (lerkspeed/spikespeed + spikespeed/spikespeed)^2 / (spikespeed/spikespeed)^2
    static * (lerkspeed/spikespeed + 1)^2<!--c2--></div><!--ec2-->
    Simplifying it down makes it very simple indeed.

    Also, a graph that may be of interest to you (based on what you described) could be a (1-e^[-x]) graph: it has a zero value and a infinite limit. When x is 0, the value is 0; as x tends to a large number, the value tends to, but does not reach, 1.
    Then you can modify the range/theoretical maximum by multiplying by a scalar: a*(1-e^[-x])
    And you can modify the sensitivity by multiplying the x by a scalar: a*(1-e^[-bx])
    For example:
    <img src="http://i.imgur.com/iTPqg.png" border="0" class="linked-image" />

    Not sure how it'd apply in this case though.

    I have a feeling that what you're trying to accomplish with that shape is increased drag with increased velocity (acting negatively on the spike velocity). Is that right?
    A simple way to approach it is that with increased lerk velocity, you don't get as much increased spike velocity, and that decrease is proportional to your velocity...
    For example,
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// dragfactor = spikespeed/(abs(movement)+spikespeed)       // simplified to:
    dragfactor = 1/(abs(movement)/spikespeed + 1)               // when movement = 0, factor = 1;
                                                                // basically you get "regular drag" when you're at the base spike speed
    damage = static * (dragfactor*movement/spikespeed + 1)^2    // of course, when movement = 0, you end up with just static damage anyway<!--c2--></div><!--ec2-->
    Let's just graph that, using spikespeed = 45 (the mid-point of the range you specified as optimal):
    <img src="http://i.imgur.com/xBQeE.png" border="0" class="linked-image" />

    Originally I wasn't taking the absolute value of the movement (red graph), and I thought backwards movement was getting penalised too much (it was getting penalised even more than the original model), which didn't make a lot of sense: slower combined speed should mean LESS drag, so I took the absolute value of the movement instead (green graph), which actually makes it so that backwards movement is getting penalised even less, which actually does make sense.
    For ease of use and understanding (and maybe a touch of realism), I'm having the spikespeed in dragfactor and damage be the same, but they don't necessarily have to be.

    As a happy accident, the green graph corresponds to the shape that you outlined.

    Edit: Another, possibly more realistic (? I have no idea), way to implement dragfactor could be:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->if movement >= 0
       dragfactor = 1/(movement/spikespeed + 1)
    else
       dragfactor = (movement/spikespeed + 1)
    end<!--c2--></div><!--ec2-->
    The difference is pretty small, though.

    <a href="http://www.mediafire.com/?ze462xb7f6cr40k" target="_blank">Excel</a>

    <!--quoteo(post=1892820:date=Jan 6 2012, 06:44 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 6 2012, 06:44 PM) <a href="index.php?act=findpost&pid=1892820"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Don't you just love wolframalpha? :)<!--QuoteEnd--></div><!--QuoteEEnd-->
    Yes. :)
    I spent a lot of time on it at university just searching the most random things (global gdp divided by the distance from earth to the moon, huh?) when I should have been doing what I actually got on the computer to do: studying.

    Also, regarding extremely high speeds, I think it's balanced to an extent by the fact that you have so little time to actually do the damage, and that speed is not really sustainable.
    So given a constant range, if you're moving twice as fast as "max speed", then with the dragfactor I've applied above (hell, even without), you actually get significantly less than twice as much damage, while you now have half as much time to deal the damage. See? Balanced.
  • swalkswalk Say hello to my little friend. Join Date: 2011-01-20 Member: 78384Members, Squad Five Blue
    Nice to see that you guys are still cranking on this, gotta try it soon :)
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    Yea, if you want a physical approach then drag should definitely be accounted for. I think drag force is usually quadric with speed. If you get increased drag for negative speed (less) then spike speed, then your implementation must be wrong. On second thought, the drag aren't what's causing this, the drag decreases with lower velocity, but the damage is dependant on the total speed too.

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->// dragfactor = spikespeed/(abs(movement)+spikespeed)       // simplified to:
    dragfactor = 1/(abs(movement)/spikespeed + 1)               // when movement = 0, factor = 1;
                                                                // basically you get "regular drag" when you're at the base spike speed
    damage = static * (dragfactor*movement/spikespeed + 1)^2<!--c2--></div><!--ec2-->

    Calling spikespeed for vs and movement for v.

    dragfactor= vs/(v+vs)

    damage = static * ( vs/(v+vs) * v/vs + 1 )^2

    damage = static * ( v/(v+vs) + (v+vs)/(v+vs) )^2

    damage = static * ( (v+v+vs)/(v+vs) )^2

    damage = static * ( (2v+vs)/(v+vs) )^2

    damage = static * ( (2*(v+vs)-vs)/(v+vs) )^2

    damage = static * ( 2*(v+vs)/(v+vs) - vs/(v+vs) )^2

    damage = static * ( 2 - vs/(v+vs) )^2

    Now, let's evaluate 2 - vs/(v+vs). With vs as a positive konstant, we look at 4 different points that will determine how the function changes. We look at v=-infinite, v=-vs from the negative side, v=-vs from the positive side, and finally v=+infinite. The same regions are the interesting ones for the full function.

    v=-infinite: the effect of vs can be ignored, and we approach +2 from above
    v=-vs from negative side: v<vs so vs/(v+vs) will be negative, so the function will approach +infinite
    v=-vs from positive side: v>vs so vs/(v+vs) will be positive, so the function will approach -infinite
    v=+infinite: the effect of vs can be ignored, and we approach +2 from below.

    As you can see, for any reasonable backward speeds, we start of from something above -infinite and from there the function will steadily increase but at a constantly decreasing rate. Now, you may argue that the real function had all this squared. The only changes of appearance is that our negative values become positive. The new global low of the function will be when vs/(v+vs)=2, which happens at v=-vs/2. Again, above this point the function will steadily increase but at a constantly decreasing rate. And it can't really be a realistic physical approach: we get the kinetic energy as +infinite if we move backward as fast as the spikes shoot out from us. And that's hardly a relativistic speed...
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    <!--quoteo(post=1892826:date=Jan 6 2012, 01:01 PM:name=swalk)--><div class='quotetop'>QUOTE (swalk @ Jan 6 2012, 01:01 PM) <a href="index.php?act=findpost&pid=1892826"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Nice to see that you guys are still cranking on this, gotta try it soon :)<!--QuoteEnd--></div><!--QuoteEEnd-->

    You know, these kinds of things are hard to see for yourself... Is the thread becoming annoyingly mathematical?

    <i>I should go study now... I'll check back in a few hours :)</i>


    On topic: As long as the damage approaches an upper limit, so that diving from high still increase your damage, but you will not get into 1-2 shot scenarios, and as such the "working" speed damage can be kept quite high, then I'm happy, regardless of how it's implemented.
  • swalkswalk Say hello to my little friend. Join Date: 2011-01-20 Member: 78384Members, Squad Five Blue
    <!--quoteo(post=1892829:date=Jan 6 2012, 01:12 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 6 2012, 01:12 PM) <a href="index.php?act=findpost&pid=1892829"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->You know, these kinds of things are hard to see for yourself... Is the thread becoming annoyingly mathematical?<!--QuoteEnd--></div><!--QuoteEEnd-->
    At least I couldn't wrap my head around it when I skimmed through it, but I trust that you guys know what you're doing ;)
    Yea I will need some help with testing it ;D
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    <!--quoteo(post=1892827:date=Jan 6 2012, 08:10 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 6 2012, 08:10 PM) <a href="index.php?act=findpost&pid=1892827"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Yea, if you want a physical approach then drag should definitely be accounted for. I think drag force is usually quadric with speed. If you get increased drag for negative speed (less) then spike speed, then your implementation must be wrong.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Might be another case of you reading the post before I finished editing it :P

    Actually, I made two alternate factors, which have it so that drag actually decreases with decreased velocity. Any of these two alternate factors (one uses the absolute-value function, and one uses an if statement) leads to the right behaviour. EDIT: worked out that there's a problem with the second alternate factor: There's a turning point when negative movement is equal to half of spikespeed, so if it gets even more negative than that, damage actually begins to increase again. So far, the "absolute value" factor works best (if not perfect): When movement is negative spikespeed, damage is 25%.

    Btw, the problem with your conclusion is that you haven't really accounted for the absolute value function, so you effectively have to evaluate it twice: when movement is negative, and when movement is positive. But you know, either way, it doesn't actually produce the required "zero" when the velocity is negative spikespeed - only the original version without the dragfactor does that.

    <!--quoteo(post=1892829:date=Jan 6 2012, 08:12 PM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 6 2012, 08:12 PM) <a href="index.php?act=findpost&pid=1892829"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->On topic: As long as the damage approaches an upper limit, so that diving from high still increase your damage, but you will not get into 1-2 shot scenarios, and as such the "working" speed damage can be kept quite high, then I'm happy, regardless of how it's implemented.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Well, when you have lerk speed = spike speed, the original model had the damage = 400%, but with the dragfactor implemented, the damage = 225%.
    And as I mentioned at the end of my last post (one of the last edits):
    <!--quoteo--><div class='quotetop'>QUOTE </div><div class='quotemain'><!--quotec-->Also, regarding extremely high speeds, I think it's balanced to an extent by the fact that you have so little time to actually do the damage, and that speed is not really sustainable.
    So given a constant range, if you're moving twice as fast as "max speed", then with the dragfactor I've applied above (hell, even without), you actually get significantly less than twice as much damage, while you now have half as much time to deal the damage. See? Balanced.<!--QuoteEnd--></div><!--QuoteEEnd-->
    Besides, I kinda like the idea of the Lerk being Death From Above :P
  • YuukiYuuki Join Date: 2010-11-20 Member: 75079Members
    In my experience the exact shape of the function doesn't really matter, specially for something you don't get the feel of, like damage. It's better to favor simplicity in my opinion.

    Anyway, you should come up with some implementations now, so we can test them !
  • Fluid CoreFluid Core Join Date: 2007-12-26 Member: 63260Members, Reinforced - Shadow
    edited January 2012
    Harimau: I was evaluating why you got unexpectedly low damage for negative speeds. Since I reached the conclusion of a minimum of half spike speed, I think that evaluation was sucessful.

    Yuuki & swalk: I have all intentions of testing this, will try to make a server run it once we figured out a good starting version (gint hint gathers).

    I realised why drag behaves strangely too. We considering it reducing the kinetic energy directly, instead of having it act as a countering force, with resulting lower sub-firing speed and consequently lowered kinetic energy. And if you eant ro be really physical, just have the drag scale with distance too. That should solve all unexpected behaviour! :)
  • HarimauHarimau Join Date: 2007-12-24 Member: 63250Members
    edited January 2012
    <!--quoteo(post=1892880:date=Jan 7 2012, 02:53 AM:name=Fluid Core)--><div class='quotetop'>QUOTE (Fluid Core @ Jan 7 2012, 02:53 AM) <a href="index.php?act=findpost&pid=1892880"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->I realised why drag behaves strangely too. We considering it reducing the kinetic energy directly, instead of having it act as a countering force, with resulting lower sub-firing speed and consequently lowered kinetic energy. And if you eant ro be really physical, just have the drag scale with distance too. That should solve all unexpected behaviour! :)<!--QuoteEnd--></div><!--QuoteEEnd-->
    Hahaha, yes, it would, but we were trying to do it all in one step for the sake of "simplicity".

    I'm thinking I should finish this other spreadsheet (hit rate / average damage versus distance) before attempting the "proper method", though. What it does is evaluate the theoretical hit-rate based on the area of overlap between a rectangle and a circle that decreases with distance (if, for example, you fired 1000,000 times, and the calculated hit-rate was 50%, then you would hit around 500,000 times, each hit does x damage, average damage per hit is x * 500,000/1000,000 = x/2, i.e you would do half of the maximum damage; so the average damage is simply the theoretical hit-rate times the actual damage). Applicable to any weapon with spread (rifle, shotgun), but assumes a rectangular hitbox face and firing directly on-center (necessary assumptions for simplicity's sake). It will allow us to better balance damage versus distance, since it will now take into account the effect of the cone of fire.

    Anyway, this is the method I most prefer right now:
    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Fluid Core: Reduced spread slightly and amount of secondary spikes was reduced
    local kSpread = Math.Radians(6)
    local kNumSpikesOnSecondary = 4<!--c2--></div><!--ec2-->

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->local damageDistScalar = Clamp(1 - ((distToTarget - kSpikeMaxDamageRange) / (kSpikeMinDamageRange - kSpikeMaxDamageRange)), 0, 1)
            //need to ensure that the game actually calls kSpikeMaxDamageRange<!--c2--></div><!--ec2-->

    <!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->                local damage = kSpikeMinDamage + damageDistScalar * (kSpikeMaxDamage - kSpikeMinDamage)
                    
                    //Yuuki: Make damage increase based on your speed
            //max damage = 20
            //min damage = 15
            local static = 15 + damageDistScalar * 5
            local movement = player:GetVelocity():DotProduct(player:GetViewAngles():GetCoords().zAxis)
            //spikespeed = 60
            local dragfactor = 1/(math.abs(movement)/60 + 1)       // check if math.abs is the correct function
                    damage = static * (dragfactor*movement/60 + 1)^2<!--c2--></div><!--ec2-->



    Turns out I just can't leave it alone, so here goes:
    <!--coloro:#00FF00--><span style="color:#00FF00"><!--/coloro--><u><b>The Drag Force Approach</b></u>:<!--colorc--></span><!--/colorc-->

    {1} FD = 1/2*rho*v^2*Cd*A
    // all but v^2 are constants, so gather them up:
    FD = constant1*v^2 ...(1)

    // after being released, the only force acting on the spike is the drag force (and negatively), and the starting "release velocity", u, is lerk speed + spike speed

    {2} F = m*a
    // assuming some mass of the spike (another constant, constant2),
    // substitute (1)
    a = F / m = -constant1*v^2 / constant2
    a = CONSTANT1*v^2 ...(2)
    // (CONSTANT1 is negative and has units of "m^-1")

    {3} v^2 = u^2 + 2*a*s
    // substitute (2)
    v^2 = u^2 + 2*CONSTANT1*v^2*s
    (1-2*CONSTANT1*s)*v^2 = u^2
    // (-2*CONSTANT1 is another constant, same units, call it CONSTANT2, a positive constant)
    (1+CONSTANT2*s)*v^2 = u^2
    v^2 = u^2/(1+CONSTANT2*s) ...(3)

    {4} kinetic energy = 1/2*m*v^2 (1/2*m is another constant, constant3)
    kinetic energy = constant3*v^2
    // subtitute (3)
    kinetic energy = constant3*u^2/(1+CONSTANT2*s)
    // damage is proportional to kinetic energy
    damage = constant4*kinetic energy = constant4*constant3*u^2/(1+CONSTANT2*s) = CONSTANT3*u^2/(1+CONSTANT2*s)
    // recall that u (the "release velocity") = lerk speed + spike speed, and that s is the range, or distToTarget, therefore:
    damage = CONSTANT3*(lerkspeed+spikespeed)^2/(1+CONSTANT2*distToTarget) ...(4)
    // (4) <u>This is our final equation.</u>

    // Now, assuming a constant range (and that I haven't slipped up in my maths), we actually end up with

    damage = [CONSTANT3/(1+CONSTANT2*constantrange]*(lerkspeed+spikespeed)^2
    damage = ANOTHERCONSTANT*(lerkspeed+spikespeed)^2

    //which is a quadratic that is fairly similar to what we already had to begin with:

    damage = [static/(spikespeed)^2]*(lerkspeed+spikespeed)^2
    damage = ANOTHERCONSTANT*(lerkspeed+spikespeed)^2

    <!--coloro:red--><span style="color:red"><!--/coloro--><b>Fantastic.</b><!--colorc--></span><!--/colorc-->
Sign In or Register to comment.