feat: scripted unit xp upgrades#7531
Conversation
77aba43 to
571ca74
Compare
1fd21e1 to
4a21c3c
Compare
- This modifies the modrules to disable engine-based xp bonuses. - It does not yet replace xp bonuses with rank-based bonuses. - This replaces the armmav/gunslinger's +range gadget also.
4a21c3c to
e56c4cb
Compare
|
In addition to game-side XP upgrade, we may want to track unit XP ourselves. That depends on what we want out of the finished veterancy system. We currently use engine-default XP. So:
Which can produce odd results:
Though these are mostly minor effects. |
This is barely functional as-is. I don't know that I value including it. - First, we cannot tune many of the values scaled via ownerExpAccWeight: predictSpeedMod, targetMoveError, movingAccuracy, wobble. - Second, BAR balances many units' "accuracy" via predictBoost and leadLimit, making them ultra-accurate against immobile targets with speed-based inaccuracy vs moving ones. - Third, we probably think that ownerExpAccWeight does not scale sprayAngle, because that is what the docs say, but CWeapon::SprayAngleExperience disagrees.
e4c72a4 to
ec84982
Compare
| FireWeapon1() | ||
| { | ||
| start-script ResetFire(); | ||
| start-script ScriptReload(); |
There was a problem hiding this comment.
This (and FireWeapon2 below) calls ScriptReload without a weapon number, and ScriptReload only seems to sleep for weapon 1 or 2 - so wouldn't it skip both and not wait when nothing's passed? armsam passes the number, so I'm guessing cormist just didn't get the same change. The ResetAiming() up in AimWeapon1 looks like it might be the same thing.
| piece flare1, flare2, base, turret, lwheel,rwheel, mlauncher; | ||
|
|
||
| static-var restore_delay, gun_1, fired, aiming; | ||
| static-var reload_time_max, reload_time_1, reload_time_2, gun_1, fired, aiming; |
There was a problem hiding this comment.
From what I can tell these only get set once the unit earns XP (through the SetReloadTime/SetMaxReloadTime calls) - so wouldn't a freshly built unit have them at 0 and end up sleeping for 0 in ScriptReload/ResetAiming/RestoreAfterDelay until its first XP? Might be worth setting them in Create()? (cormist too.)
There was a problem hiding this comment.
Not sure how this will resolve, with #7961 handling attributes being sent to scripts, but veterancies needing it also.
| d[i] = math_round(damages[i] * damageMult) | ||
| end | ||
| spSetUnitWeaponDamages(unitID, weaponNum, d) | ||
| spSetUnitRulesParam(unitID, "veterancy_damages_multiplier", damageMult) |
There was a problem hiding this comment.
This value looks like it's stored per-unit but gets written once for each weapon, so wouldn't whichever weapon is last be the one that sticks? Probably fine for plain damages since every weapon scales the same, but reload_then_damages looks like it can give each weapon a different amount - and the stats panels seem to read this one value for all of them, so I'd expect them to show the wrong number for every weapon except the last.
There was a problem hiding this comment.
True, these will need unique handling per-weapon. Will do.
The gui for handling these stats is in bad shape. I might have been hoping that the contradictions in it become so strong that we have no choice but to start replacing it.
There was a problem hiding this comment.
Yeah no possibility to keep a lot of the gui code. I won't have time for that during daylight-hours, will address it.
| -- Without this, many XP gains may be too small to reach g:UnitExperience. | ||
| -- We still do not capture some updates, e.g. nuclear explosions vs walls. | ||
| -- TODO: Move this into the game setup? Or something? Why in a gadget? | ||
| Spring.SetExperienceGrade(0.01) |
There was a problem hiding this comment.
This runs before the check below that can remove the gadget - so if nothing ends up using veterancy, wouldn't the gadget pull itself out after it's already changed the game-wide XP setting? Might be better after that check (matches your "why in a gadget" note).
There was a problem hiding this comment.
I wanted to make the setting independent. Experience grading is a general behavior that shouldn't depend on this gadget. It doesn't really belong in the file at all, then, but there was no good existing place for it.
I did not know if we would create a gadget for game-side XP tracking, either. If we do, then that is an okay place. It is still odd to include any game configuration in any gadget file imo.
| add = function(unitDef, upgrades) | ||
| -- Shares its scaling customparams with `reload`/`damage`, but does not check `damageScale`: | ||
| local unitReloadScale = getScale(unitDef, "reload", reloadScale) | ||
| local unitDamageScale = getScale(unitDef, "damage", unitReloadScale) |
There was a problem hiding this comment.
Edge case I think I'm seeing: if a unit turns on damage scaling but leaves reload scaling at 0, would a weapon whose reload is longer than its burst actually get the damage bump? The part that raises damage looks like it only runs once the reload's been pulled down to the burst length, which doesn't seem possible with reload scaling off - and the config still seems to get accepted, so it might just quietly do nothing.
There was a problem hiding this comment.
Maybe a downgrade path for reload_then_xyz => xyz would be good for this. I don't mind saying "Configure it correctly" to our unit designers but modders will want something that is more robust.
Not in the "global collectors" sense so it's another mini-pattern in a file that is doomed to be full of mini-patterns. But moving away from the toy code for the demo, we need to get this much work done, and then address how patternless we are. I thought of this earlier as being a "config table", just a name for explicit construction in a declared way, with "mixins", just a name for reused code that is no longer a helper/util but is driving the overall pattern/design. And the real consumer of that data is applyVeterancyEffects, which needs an abstract upgrade to apply. There is still some naming tension there, applyVeterancyEffects instead of applyVeterancyUprade. The organization is now a bit worse. Leaving a note in the commit msg to shame myself into organizing more, I guess.
I wish I had the definitions at the top but Lua is a little hostile to organizing things that way due to lexical scoping. Something to ponder maybe but nbd.
We have a dozen or so engine tickets to split off from this (maybe once we confirm more of the spec for the veterancy rework)


This is step one toward creating new veterancies at rank-up, rather than continuously at each XP gain, by scripting the new wanted effects and adding a measure of support for unit script behaviors. GDD
Work done
veterancy_upgrades = 0) to allow for balance A/B tests without jumping versions.As part of the above:
Future considerations
There are a lot of
TODOs throughout the unit_veterancy_upgrades file. Surfacing a few here:damagesveterancy.damagesveterancy. Maybe unwanted.damagesveterancy to be visible to players.rangeveterancy upgrade does not compensate with increases to TTL, projectile speed, etc.acc_weightveterancy.Testing
Heatrays and the Beamer can gain +damage% instead of -reload%, which works well. There are other units with a constant rate of fire, also, like Razorbacks that could gain a similar bonus.
Damage increase is an interesting tradeoff with reload time decrease:

This is performant (too optimized for the need) and will be even lighter when upgrades are changed to be on rank-up. Here's a thousand units firing heat rays at each other:
