mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2024-09-27 14:13:11 +02:00
[BLEEDING][BREAKING] Next step on past bounce with pistons.
Steps done: * Add more velocity with less preconditions. * Handle push with the player being just above the block where a slime block is pushed to. * Exception for vdistrel. * Breaking:Move verVelUsed from MovingData to PlayerMoveData. Gameplay issues remaining: * Still too often fall damage is dealt. * Friction envelope gets hidden with past-ground being set too often (vdistsb). * Potential for more edge cases. Missing abuse prevention: * Reinstate invalidation of past entries (currently turned off to progress at all, will need another iteration). * More confined preconditions. * More/better invalidation conditions for velocity set for bounce effects.
This commit is contained in:
parent
cf3ea4a1e2
commit
c9a744a7d3
@ -167,8 +167,6 @@ public class MovingData extends ACheckData {
|
||||
/** Last time the player was actually sprinting. */
|
||||
public long timeSprinting = 0;
|
||||
public double multSprinting = 1.30000002; // Multiplier at the last time sprinting.
|
||||
/** Just used velocity, during processing of moving checks. */
|
||||
public SimpleEntry verVelUsed = null;
|
||||
/** Compatibility entry for bouncing of slime blocks and the like. */
|
||||
public SimpleEntry verticalBounce = null;
|
||||
/** Last used block change id (BlockChangeTracker). */
|
||||
@ -401,7 +399,6 @@ public class MovingData extends ACheckData {
|
||||
insideMediumCount = 0;
|
||||
vehicleConsistency = MoveConsistency.INCONSISTENT;
|
||||
lastFrictionHorizontal = lastFrictionVertical = 0.0;
|
||||
verVelUsed = null;
|
||||
verticalBounce = null;
|
||||
blockChangeRef.valid = false;
|
||||
}
|
||||
@ -940,9 +937,6 @@ public class MovingData extends ACheckData {
|
||||
if (!sfDirty && (horVel.hasActive() || horVel.hasQueued())) {
|
||||
sfDirty = true;
|
||||
}
|
||||
|
||||
// Reset the "just used" velocity.
|
||||
verVelUsed = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -994,7 +988,7 @@ public class MovingData extends ACheckData {
|
||||
public SimpleEntry useVerticalVelocity(final double amount) {
|
||||
final SimpleEntry available = verVel.use(amount, TOL_VVEL);
|
||||
if (available != null) {
|
||||
verVelUsed = available;
|
||||
playerMoves.getCurrentMove().verVelUsed = available;
|
||||
sfDirty = true;
|
||||
// TODO: Consider sfNoLowJump = true;
|
||||
}
|
||||
@ -1009,6 +1003,7 @@ public class MovingData extends ACheckData {
|
||||
* @return
|
||||
*/
|
||||
public SimpleEntry getOrUseVerticalVelocity(final double amount) {
|
||||
final SimpleEntry verVelUsed = playerMoves.getCurrentMove().verVelUsed;
|
||||
if (verVelUsed != null) {
|
||||
if (verVel.matchesEntry(verVelUsed, amount, TOL_VVEL)) {
|
||||
return verVelUsed;
|
||||
|
@ -149,7 +149,7 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
*
|
||||
*/
|
||||
private static final long FLAGS_VELOCITY_BOUNCE_BLOCK = VelocityFlags.ORIGIN_BLOCK_BOUNCE;
|
||||
|
||||
|
||||
private static final long FLAGS_VELOCITY_BOUNCE_BLOCK_MOVE_ASCEND = FLAGS_VELOCITY_BOUNCE_BLOCK
|
||||
| VelocityFlags.SPLIT_ABOVE_0_42 | VelocityFlags.SPLIT_RETAIN_ACTCOUNT | VelocityFlags.ORIGIN_BLOCK_MOVE;
|
||||
|
||||
@ -912,9 +912,9 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final int tick,
|
||||
final MovingData data, final MovingConfig cc) {
|
||||
// TODO: Find more preconditions.
|
||||
// TODO: Might later need to override/adapt just the bounce effect set by the ordinary method.
|
||||
final UUID worldId = from.getWorld().getUID();
|
||||
// Prepare (normal/extra) bounce.
|
||||
// TODO: Might later need to override/adapt just the bounce effect set by the ordinary method.
|
||||
// Typical: a slime block has been there.
|
||||
final BlockChangeEntry entryBelowAny = blockChangeTracker.getBlockChangeEntryMatchFlags(
|
||||
data.blockChangeRef, tick, worldId, to.getBlockX(), to.getBlockY() - 1, to.getBlockZ(),
|
||||
@ -923,7 +923,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
// TODO: Check preconditions for bouncing here at all (!).
|
||||
|
||||
// Check if the/a block below the feet of the player got pushed into the feet of the player.
|
||||
// TODO: Not sure if this can/should be done on ascending.
|
||||
final BlockChangeEntry entryBelowY_POS = entryBelowAny.direction == Direction.Y_POS ? entryBelowAny
|
||||
: blockChangeTracker.getBlockChangeEntryMatchFlags(data.blockChangeRef, tick, worldId,
|
||||
to.getBlockX(), to.getBlockY() - 1, to.getBlockZ(), Direction.Y_POS,
|
||||
@ -938,8 +937,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
return BounceType.STATIC_PAST;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: ADDITIONAL: A slime block has been pushed up [a) block below counts ? b) into the feet of the player].
|
||||
/*
|
||||
* TODO: Can't update span here. If at all, it can be added as side
|
||||
* condition for using the bounce effect. Probably not worth it.
|
||||
@ -951,63 +948,65 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
final Player player, final PlayerLocation from, final PlayerLocation to,
|
||||
final PlayerMoveData thisMove, final PlayerMoveData lastMove, final int tick,
|
||||
final MovingData data, final MovingConfig cc) {
|
||||
// TODO: find more preconditions.
|
||||
// TODO: More preconditions.
|
||||
// TODO: Nail down to more precise side conditions for larger jumps, if possible.
|
||||
final UUID worldId = from.getWorld().getUID();
|
||||
// Possibly a "lost use of slime".
|
||||
// TODO: Might need to cover push up, after ordinary slime bounce.
|
||||
// TODO: Cover push by slime block (center/feet on block).
|
||||
// TODO: Might need to cover push by slime block (center/feet off block).
|
||||
// TODO: Low fall distance cases and where no slime block was underneath at descend have to be included here.
|
||||
// TODO: Note that onPreparedBounceSupport etc. has to be called in here, if needed.
|
||||
|
||||
|
||||
|
||||
// TODO: Work around 0-dist?
|
||||
// TODO: set data.verticalBounce and call data.useVerticalBounce right away (!) -> fall damage !?
|
||||
// TODO: typically up to 1.5 above max level of pushed block ! (typically 2.0 total with 0.0 between).
|
||||
/*
|
||||
* TODO: Other concepts? a) maxCoordinate for SimpleEntry? (alters
|
||||
* method calls to SimpleAxisVelocity) b) use past move tracking and
|
||||
* store more moves (!) and store more context (e.g. bounce entry for
|
||||
* center). c) combine both approaches, possibly use an interface to be
|
||||
* set within SimpleEntry.
|
||||
*/
|
||||
|
||||
// TODO: Adjust amount based on side conditions (center push or off center, distance to block top).
|
||||
double amount = -1.0;
|
||||
final BlockChangeEntry entryBelowY_POS = blockChangeTracker.getBlockChangeEntryMatchFlags(
|
||||
data.blockChangeRef, tick, worldId, from.getBlockX(), from.getBlockY() - 1, from.getBlockZ(),
|
||||
Direction.Y_POS, BlockProperties.F_BOUNCE25);
|
||||
if (
|
||||
// Center push.
|
||||
entryBelowY_POS != null
|
||||
// Off center push (2x 0.5(015) only, (sum below 1.015 ?)).
|
||||
// Off center push.
|
||||
|| thisMove.yDistance < 1.015 && from.matchBlockChangeMatchResultingFlags(blockChangeTracker,
|
||||
data.blockChangeRef, Direction.Y_POS, Math.min(.415, thisMove.yDistance),
|
||||
BlockProperties.F_BOUNCE25)
|
||||
) {
|
||||
// Always allow the double 0.505 move.
|
||||
/*
|
||||
* TODO: May detect the first 0.505 move to match preconditions
|
||||
* better (roughly 2x 0.5, 0.0 -> 1.5, -0.few -> 1.389). One might
|
||||
* also nail down to past y-position further. Also consider
|
||||
* confining to horizontal speed envelope (is that even possible)?
|
||||
*/
|
||||
if (data.debug) {
|
||||
debug(player, "checkPastStateBounceAscend: " + (entryBelowY_POS == null ? "off_center" : "center"));
|
||||
debug(player, "Direct block push with bounce (" + (entryBelowY_POS == null ? "off_center)." : "center)."));
|
||||
}
|
||||
// TODO: Nail down to more precise side conditions for larger jumps, if possible.
|
||||
// TODO: Adjust amount based on side conditions (center push or off center, distance to block top).
|
||||
// TODO: Center push, without being hit by the block (2 below!) -> 0.5 off ground + 1.5 roughly !
|
||||
// TODO: Push up, without being inside the pushed block (0.5 + 0.9x).
|
||||
// TODO: Add two entries, split based on current yDistance?
|
||||
amount = Math.min(Math.max(0.505, 1.0 + (double) from.getBlockY() - from.getY() + 1.515),
|
||||
2.525); // TODO: EXACT MAGIC.
|
||||
if (entryBelowY_POS != null) {
|
||||
data.blockChangeRef.updateSpan(entryBelowY_POS);
|
||||
}
|
||||
}
|
||||
// Center push while being on the top height of the pushed block already (or 0.5 above (!)).
|
||||
if (
|
||||
amount < 0.0
|
||||
// TODO: Not sure about y-Distance.
|
||||
// TODO: MAGIC EVERYWHERE
|
||||
&& lastMove.toIsValid && lastMove.yDistance >= 0.0 && lastMove.yDistance <= 0.505
|
||||
&& from.getY() - (double) from.getBlockY() == lastMove.yDistance // TODO: Margin?
|
||||
) {
|
||||
final BlockChangeEntry entry2BelowY_POS = blockChangeTracker.getBlockChangeEntryMatchFlags(
|
||||
data.blockChangeRef, tick, worldId, from.getBlockX(), from.getBlockY() - 2, from.getBlockZ(),
|
||||
Direction.Y_POS, BlockProperties.F_BOUNCE25);
|
||||
if (entry2BelowY_POS != null
|
||||
// TODO: Does off center push exist with this very case?
|
||||
) {
|
||||
if (data.debug) {
|
||||
debug(player, "Foot position block push with bounce (" + (entry2BelowY_POS == null ? "off_center)." : "center)."));
|
||||
}
|
||||
amount = Math.min(Math.max(0.505, 1.0 + (double) from.getBlockY() - from.getY() + 1.515),
|
||||
2.015 - lastMove.yDistance); // TODO: EXACT MAGIC.
|
||||
if (entryBelowY_POS != null) {
|
||||
data.blockChangeRef.updateSpan(entry2BelowY_POS);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finally add velocity if set.
|
||||
if (amount >= 0.0) {
|
||||
/*
|
||||
* TODO: USE EXISTING velocity with bounce flag set first, then peek
|
||||
* / add. (might while peek -> has bounce flag: remove velocity)
|
||||
*/
|
||||
final double amount = Math.min(Math.max(0.505, 1.0 + (double) from.getBlockY() - from.getY() + 1.515),
|
||||
2.525); // TODO: EXACT MAGIC.
|
||||
if (lastMove.toIsValid && lastMove.yDistance < 0.42 ||
|
||||
data.peekVerticalVelocity(amount, 2, 3) == null) {
|
||||
// (Could skip peek for low distances around 0.5.)
|
||||
if (data.peekVerticalVelocity(amount, 2, 3) == null) {
|
||||
/*
|
||||
* TODO: Concepts for limiting... max amount based on side
|
||||
* conditions such as block height+1.5, max coordinate, max
|
||||
@ -1017,17 +1016,13 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
* past move tracking, or a sub-class of SimpleEntry with better
|
||||
* access signatures including thisMove.
|
||||
*/
|
||||
/*
|
||||
* TODO: Also account for current yDistance here? E.g. Add two
|
||||
* entries, split based on current yDistance?
|
||||
*/
|
||||
final SimpleEntry vel = new SimpleEntry(tick, amount, FLAGS_VELOCITY_BOUNCE_BLOCK_MOVE_ASCEND, 4);
|
||||
data.verticalBounce = vel;
|
||||
data.useVerticalBounce(player);
|
||||
/*
|
||||
* TODO: Update span or not ... [could use a class that extends
|
||||
* SimpleEntry but which also contains a block change id to update
|
||||
* span with, upon using that entry?]
|
||||
*/
|
||||
if (entryBelowY_POS != null) {
|
||||
data.blockChangeRef.updateSpan(entryBelowY_POS);
|
||||
}
|
||||
if (data.debug) {
|
||||
debug(player, "checkPastStateBounceAscend: add velocity: " + vel);
|
||||
}
|
||||
@ -1053,12 +1048,6 @@ public class MovingListener extends CheckListener implements TickListener, IRemo
|
||||
*/
|
||||
private boolean checkBounceEnvelope(final Player player, final PlayerLocation from, final PlayerLocation to,
|
||||
final MovingData data, final MovingConfig cc) {
|
||||
/*
|
||||
* TODO: Likely not conform with getting pushed up, while outside of the
|
||||
* strict envelope. The advantage of detecting that here could be the
|
||||
* invalidation mechanics (also consider passable etc.), otherwise the
|
||||
* move up might miss the already invalidated push up.
|
||||
*/
|
||||
return
|
||||
// 0: Normal envelope (forestall NoFall).
|
||||
(
|
||||
|
@ -15,6 +15,7 @@
|
||||
package fr.neatmonster.nocheatplus.checks.moving.model;
|
||||
|
||||
import fr.neatmonster.nocheatplus.checks.CheckType;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.SimpleEntry;
|
||||
|
||||
/**
|
||||
* Include player specific data for a move.
|
||||
@ -77,6 +78,12 @@ public class PlayerMoveData extends MoveData {
|
||||
*/
|
||||
public boolean mightBeMultipleMoves;
|
||||
|
||||
/**
|
||||
* Just the used vertical velocity. Could be overridden multiple times
|
||||
* during processing of moving checks.
|
||||
*/
|
||||
public SimpleEntry verVelUsed = null;
|
||||
|
||||
@Override
|
||||
protected void resetBase() {
|
||||
// Properties of the player.
|
||||
@ -90,6 +97,7 @@ public class PlayerMoveData extends MoveData {
|
||||
flyCheck = CheckType.UNKNOWN;
|
||||
modelFlying = null;
|
||||
mightBeMultipleMoves = false;
|
||||
verVelUsed = null;
|
||||
// Super class last, because it'll set valid to true in the end.
|
||||
super.resetBase();
|
||||
}
|
||||
|
@ -512,7 +512,9 @@ public class CreativeFly extends Check {
|
||||
return Math.max(maximumHeight - 10.0, world.getMaxHeight());
|
||||
}
|
||||
|
||||
private void outpuDebugMove(final Player player, final double hDistance, final double limitH, final double yDistance, final double limitV, final ModelFlying model, final List<String> tags, final MovingData data) {
|
||||
private void outpuDebugMove(final Player player, final double hDistance, final double limitH,
|
||||
final double yDistance, final double limitV, final ModelFlying model, final List<String> tags,
|
||||
final MovingData data) {
|
||||
final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove();
|
||||
StringBuilder builder = new StringBuilder(350);
|
||||
final String dHDist = lastMove.toIsValid ? " (" + StringUtil.formatDiff(hDistance, lastMove.hDistance) + ")" : "";
|
||||
@ -522,8 +524,8 @@ public class CreativeFly extends Check {
|
||||
if (lastMove.toIsValid) {
|
||||
builder.append(" , fdsq: " + StringUtil.fdec3.format(thisMove.distanceSquared / lastMove.distanceSquared));
|
||||
}
|
||||
if (data.verVelUsed != null) {
|
||||
builder.append(" , vVelUsed: " + data.verVelUsed);
|
||||
if (thisMove.verVelUsed != null) {
|
||||
builder.append(" , vVelUsed: " + thisMove.verVelUsed);
|
||||
}
|
||||
builder.append(" , model: " + model.id);
|
||||
if (!tags.isEmpty()) {
|
||||
|
@ -39,6 +39,7 @@ import fr.neatmonster.nocheatplus.checks.moving.model.LiftOffEnvelope;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.LocationData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.model.PlayerMoveData;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.util.AuxMoving;
|
||||
import fr.neatmonster.nocheatplus.checks.moving.velocity.VelocityFlags;
|
||||
import fr.neatmonster.nocheatplus.checks.workaround.WRPT;
|
||||
import fr.neatmonster.nocheatplus.compat.Bridge1_9;
|
||||
import fr.neatmonster.nocheatplus.compat.BridgeEnchant;
|
||||
@ -410,7 +411,8 @@ public class SurvivalFly extends Check {
|
||||
// Silent set-back.
|
||||
if (data.debug) {
|
||||
tags.add("silentsbcobweb");
|
||||
outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom, yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo);
|
||||
outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom,
|
||||
yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo, thisMove);
|
||||
data.ws.setJustUsedIds(null);
|
||||
}
|
||||
return data.getSetBack(to);
|
||||
@ -456,7 +458,8 @@ public class SurvivalFly extends Check {
|
||||
// Debug output.
|
||||
final int tagsLength;
|
||||
if (data.debug) {
|
||||
outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom, yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo);
|
||||
outputDebug(player, to, data, cc, hDistance, hAllowedDistance, hFreedom,
|
||||
yDistance, vAllowedDistance, fromOnGround, resetFrom, toOnGround, resetTo, thisMove);
|
||||
tagsLength = tags.size();
|
||||
data.ws.setJustUsedIds(null);
|
||||
}
|
||||
@ -578,7 +581,7 @@ public class SurvivalFly extends Check {
|
||||
// Prevent reset if coming from air (purpose of the flag).
|
||||
data.sfLowJump = false;
|
||||
}
|
||||
if (hFreedom <= 0.0 && data.verVelUsed == null) {
|
||||
if (hFreedom <= 0.0 && thisMove.verVelUsed == null) {
|
||||
data.resetVelocityJumpPhase();
|
||||
}
|
||||
}
|
||||
@ -1111,6 +1114,13 @@ public class SurvivalFly extends Check {
|
||||
else if (lastMove.toIsValid && MagicAir.oddJunction(from, to, yDistance, yDistChange, yDistDiffEx, maxJumpGain, resetTo, thisMove, lastMove, data, cc)) {
|
||||
// Several types of odd in-air moves, mostly with gravity near maximum, friction, medium change.
|
||||
}
|
||||
else if (thisMove.yDistance < 1.0 && thisMove.yDistance > 0.9
|
||||
&& lastMove.yDistance >= 1.5 && data.sfJumpPhase <= 2
|
||||
&& lastMove.verVelUsed != null
|
||||
&& (lastMove.verVelUsed.flags & (VelocityFlags.ORIGIN_BLOCK_MOVE | VelocityFlags.ORIGIN_BLOCK_BOUNCE)) != 0) {
|
||||
// Allow too strong decrease.
|
||||
// TODO: Another magic check here? Route most checks through methods anyway?
|
||||
}
|
||||
else {
|
||||
vDistRelVL = true;
|
||||
}
|
||||
@ -1199,7 +1209,8 @@ public class SurvivalFly extends Check {
|
||||
// TODO: move into the in air checking above !?
|
||||
if (!envelopeHack && !resetFrom && !resetTo) {
|
||||
// "On-air" checks (vertical, already use velocity if needed).
|
||||
vDistanceAboveLimit = Math.max(vDistanceAboveLimit, inAirChecks(now, from, to, hDistance, yDistance, lastMove, data, cc));
|
||||
vDistanceAboveLimit = Math.max(vDistanceAboveLimit,
|
||||
inAirChecks(now, from, to, hDistance, yDistance, thisMove, lastMove, data, cc));
|
||||
}
|
||||
|
||||
// Block 'step' with yDistance between step height and minJumpGain (vdistrel and vdistsb should catch the rest).
|
||||
@ -1298,7 +1309,10 @@ public class SurvivalFly extends Check {
|
||||
* @param cc
|
||||
* @return
|
||||
*/
|
||||
private double inAirChecks(final long now, final PlayerLocation from, final PlayerLocation to, final double hDistance, final double yDistance, final PlayerMoveData lastMove, final MovingData data, final MovingConfig cc) {
|
||||
private double inAirChecks(final long now, final PlayerLocation from, final PlayerLocation to,
|
||||
final double hDistance, final double yDistance,
|
||||
final PlayerMoveData thisMove, final PlayerMoveData lastMove,
|
||||
final MovingData data, final MovingConfig cc) {
|
||||
double vDistanceAboveLimit = 0;
|
||||
|
||||
// y direction change detection.
|
||||
@ -1319,7 +1333,7 @@ public class SurvivalFly extends Check {
|
||||
// Allow adding 0.
|
||||
data.vDistAcc.add((float) yDistance);
|
||||
}
|
||||
else if (data.verVelUsed == null) { // Only skip if just used.
|
||||
else if (thisMove.verVelUsed == null) { // Only skip if just used.
|
||||
// Here yDistance can be negative and positive.
|
||||
// if (yDistance != 0.0) {
|
||||
data.vDistAcc.add((float) yDistance);
|
||||
@ -2017,11 +2031,13 @@ public class SurvivalFly extends Check {
|
||||
* @param toOnGround
|
||||
* @param resetTo
|
||||
*/
|
||||
private void outputDebug(final Player player, final PlayerLocation to, final MovingData data, final MovingConfig cc,
|
||||
final double hDistance, final double hAllowedDistance, final double hFreedom, final double yDistance, final double vAllowedDistance,
|
||||
final boolean fromOnGround, final boolean resetFrom, final boolean toOnGround, final boolean resetTo) {
|
||||
private void outputDebug(final Player player, final PlayerLocation to,
|
||||
final MovingData data, final MovingConfig cc,
|
||||
final double hDistance, final double hAllowedDistance, final double hFreedom,
|
||||
final double yDistance, final double vAllowedDistance,
|
||||
final boolean fromOnGround, final boolean resetFrom, final boolean toOnGround, final boolean resetTo,
|
||||
final PlayerMoveData thisMove) {
|
||||
// TODO: Show player name once (!)
|
||||
final PlayerMoveData thisMove = data.playerMoves.getCurrentMove();
|
||||
final PlayerMoveData lastMove = data.playerMoves.getFirstPastMove();
|
||||
final StringBuilder builder = new StringBuilder(500);
|
||||
builder.append(CheckUtils.getLogMessagePrefix(player, type));
|
||||
@ -2035,8 +2051,8 @@ public class SurvivalFly extends Check {
|
||||
if (lastMove.toIsValid) {
|
||||
builder.append(" , fdsq: " + StringUtil.fdec3.format(thisMove.distanceSquared / lastMove.distanceSquared));
|
||||
}
|
||||
if (data.verVelUsed != null) {
|
||||
builder.append(" , vVelUsed: " + data.verVelUsed + " ");
|
||||
if (thisMove.verVelUsed != null) {
|
||||
builder.append(" , vVelUsed: " + thisMove.verVelUsed + " ");
|
||||
}
|
||||
data.addVerticalVelocity(builder);
|
||||
// if (data.horizontalVelocityCounter > 0 || data.horizontalFreedom >= 0.001) {
|
||||
|
Loading…
Reference in New Issue
Block a user