Last active
July 18, 2022 07:28
-
-
Save y-ack/457dc3d34c93ce325c7c4afb4cb1cc93 to your computer and use it in GitHub Desktop.
i don't think we can 32183P our way out of this one, aifa.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# LAND MAKER ~ DAMAGE AND PUSHBACK [ywy] REV.1 2022/07/18 | |
addresses apply for: landmakrj | |
## terminology: | |
'break' refers to a shot action that hits a piece on a corner, | |
causing them to disappear and send damage. | |
'placing' refers to a shot action that does not break. | |
'building' refers to pieces transformed into houses by a placing | |
shot action. | |
'pushback' refers to the behavior of the pusher retracting as a | |
result of breaking with specific damage output requirements met. | |
not to be confused with | |
'PUSHBACK' refers to an internal pusher state used by both item | |
arrows and 'pushback' as defined above. | |
## damage and pushback | |
### raw damage | |
raw damage for each piece is calculated at break time by $96680, | |
which runs once for each piece cleared, including the shot tile. | |
(larger structures count are a single 'piece') | |
non-building tiles (including items) are worth half a damage, | |
but they are counted separately from building damage, so that | |
leftover half-damage carries over between breaks. | |
single buildings are worth 1 damage, | |
structures are worth | |
`min(pusher_position, 7) * structure_level + structure_value` | |
where pusher position 0 is fully retracted and position 1 looks like | |
https://cdn.discordapp.com/attachments/455404849626873869/957277010512728104/32080.png | |
| | STRUCTURE | VALUE | HEX | | |
|0| bronze | 8 | 8h | | |
|1| silver | 22 | 16h | | |
|2| gold | 38 | 26h | | |
|3| platinum | 54 | 36h | | |
table: damage values by structure level [1] | |
e.g. breaking 2 silvers (structure level=1), at pusher position 2 | |
adds | |
2*1 + 22 | |
+ 2*1 + 22 | |
for 48 raw damage from structures | |
note that 7 push is almost all the way down the board; | |
large structures will not be able to take advantage of it. | |
assuming one did break a gold structure at 5 push, 5*2 == 10 extra raw damage. | |
------------------------------------------------------------------------------- | |
[1] internal tile level assigns nonstructure `0` and bronze starting at 2, | |
but level used in structure damage is `(internal level - 2)` ($96696) | |
[2] pusher row is stored as index into board, div 16 for position here. | |
### damage conversion | |
$AB03E applies received damage | |
raw damage is converted into two types of damage | |
when it is sent to the opponent (represented by glowing orbs), | |
'piece damage' and 'push damage,' | |
but the conversion is affected by margin time. | |
the push damage conversion threshold starts at 9, | |
meaning 9 raw damage is needed for 1 push damage. | |
when the round elapsed time reaches 60 seconds, | |
the push damage threshold decreases by 1, | |
and further decreases every 30 seconds after that, | |
until a minimum push damage threshold of 4 (at 180 seconds) [1] | |
the remainder goes into piece damage, i.e.: | |
push damage += raw damage / push threshold | |
piece damage += raw damage % push threshold | |
in other words, <60s into a match, it takes 9 rawdmg for 1 push; | |
between 60 and 90s, 8 rawdmg is 1 push; and 180s+ is 4 dmg/push | |
note the modulo operator for nuisance tile damage: | |
remainder damage that doesn't go towards push goes into piece. | |
the push threshold influences nuisance tile accumulation! | |
if incoming push damage >= 3, the "DANGER" voice line plays. | |
------------------------------------------------------------------------------- | |
[1] (strictly speaking, this is a modifier that counts up and is subtracted | |
from 9 at damage conversion time, but a view of `(9 - modifier)` as | |
`push threshold` is simpler) | |
### pushback | |
pushback($953A4) is evaluated /whenever damage is sent/. | |
damage is sent on every frame that raw damage is >= 1. | |
(note that **all** breaks will send at least 1 damage; | |
breaking an item (0.5(shot tile)+0.5(item)) | |
or single tile (0.5+0.5) will both evaluate pushback.) | |
internally, pushback accumulates raw damage sent. | |
it can be thought of like an invisible meter. | |
"pusher [position]" refers to the amount of board covered/topmost line | |
- when pusher is at 0, the meter resets. | |
- when pusher is NOT at 0, | |
- the requirement for pushback is `REQ[pusher] + 2` | |
- the total raw damage this frame is added to the total accumulated pushback | |
- if accumulated pushback meter exceeds the requirement: | |
- the threshold is subtracted for the meter | |
- the push state is set to PUSHBACK | |
| | REQ| +2 | | |
|0| 15 | 17 | (ignore this line when calculating) | |
|1| 15 | 17 | | |
|2| 13 | 15 | | |
|3| 9 | 11 | | |
|4| 6 | 8 | | |
|5| 4 | 6 | | |
|6| 2 | 4 | | |
|7| 1 | 3 | | |
|8| 1 | 3 | | |
|9| 1 | 3 | | |
table: pushback meter threshold lookup. | |
note that pusher 0 (and 9?) cannot actually be encountered. | |
- pusher 0 is fully retracted, and the threshold | |
is not considered if damage is sent at this position | |
- pusher 9 is off of the board. | |
for help visualizing pushback meter accumulation and requirements, see scripts for mame and fbneo: | |
https://gist.github.com/y-ack/b17cd4a25b673dcfc7303ad8f5c60770 | |
#### example | |
given a pusher position of 2 and initial accumulated pushback | |
value of 0, a player makes two breaks: 1B+4, then 1S | |
at pusher 2, **15** raw damage is required to achieve pushback. | |
the first break, 1 bronze and 4 house pieces: | |
(2*0 + 8) + (4) = 12 | |
12/15 is insufficient to trigger pushback | |
the second break, 1 silver: | |
(2*1 + 22) = 24 | |
12 previously accumulated + 24 = 36 | |
36/15 is sufficient to trigger pushback, | |
so pushback will occur this turn, and | |
(given that we have no incoming damage) | |
pusher position will be **1** for the next turn. | |
the pushback value vs requirement is now | |
21/17, | |
meaning the next shot that sends damage (any break) will also trigger pushback. | |
#### esoterics | |
fundamentally, the behavior of pushback is | |
"sending enough damage will retreat the pusher." | |
however, there are other subtle behaviors hidden in the already unexplained mechanic: | |
firstly, while every breaking shot will evaluate damage, | |
it is possible for damage to be counted over multiple frames. | |
the most obvious case is with **star items**, where the shot tile and item | |
are counted 40 frames before the tiles eliminated are counted. | |
additionally, it seems that there is some limit to the number of pieces | |
that can be counted while sweeping over the board each frame, | |
such that masses of single tiles send damage over multiple frames. | |
damage being evaluated on multiple frames means | |
pushback can be evaluated multiple times per break, | |
and because there is no check for whether pushback state has already occurred, | |
the pushback accumulator can drain multiple times with no benefit to the player. | |
this bug could be prevented by checking whether the push state is | |
already PUSHBACK before checking the pushback requirement. | |
similarly, because arrow items | |
1) activate on break | |
2) set the same PUSHBACK state, | |
breaking an arrow will drain pushback if possible, and unless there are | |
tiles in the danger zone, this is essentially 'wasted' pushback. | |
the second behavior has to do with pushback evaluation being triggered | |
by sending damage. this is another separation of the actions of | |
shooting a tile without sending damage (placing) and | |
shooting a tile and sending damage (breaking). | |
no matter how much pushback 'meter' is stored up, | |
the pusher cannot be repelled without making a breaking shot. | |
the other side of this is that pushback cannot be /drained/ | |
without breaking tiles. | |
this includes the behavior of resetting when the pusher is fully retracted. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment