
agents.andySloane.MarioState Maven / Gradle / Ivy
package agents.andySloane;
public final class MarioState extends SpriteState {
// FIXME: try to minimize the sizes of these fields as much as possible; we
// allocate a huge number of MarioState objects.
public int jumpTime = 0, invulnerableTime = 0, hurt = 0;
public boolean big = true, // mario is big
dead = false, // yep
hurtThisStep = false, // auch
fire = true, // mario can throw fireballs
wasOnGround = false, // previous frame (used for stomping logic)
mayJump = true, // yep
sliding = false; // sliding down the side of a wall
public float xJumpSpeed = 0, yJumpSpeed = 0; // can we get rid of this somehow?
public static int ACT_SPEED = 1;
public static int ACT_RIGHT = 2;
public static int ACT_LEFT = 4;
public static int ACT_JUMP = 8;
public static int[] jumpstep_table = {0, 1, 2, 4, 7};
// fields used by the search stuff
public float g, cost; // heuristic costs
public int root_action;
public WorldState ws;
// these two are only needed for showing the paths taken
public int action;
public MarioState pred; // predecessor
public MarioState(float _x, float _y, float _xa, float _ya) {
x = _x;
y = _y;
xa = _xa;
ya = _ya;
}
public float height() {
return big ? 24 : 12;
}
public void print() {
System.out.printf("g=%d a:%d x:(%f,%f) v:(%f,%f) %s%s%s cost=%f\n", (int) g, action, x, y, xa, ya,
onGround ? "G" : "g", mayJump ? "J" : "j", sliding ? "S" : "s", cost);
}
public MarioState clone() {
MarioState n = new MarioState(x, y, xa, ya);
n.facing = facing;
n.jumpTime = jumpTime;
n.big = big;
n.fire = fire;
n.dead = dead;
n.hurt = hurt;
hurtThisStep = false;
n.onGround = onGround;
n.wasOnGround = onGround;
n.mayJump = mayJump;
n.sliding = sliding;
n.xJumpSpeed = xJumpSpeed;
n.yJumpSpeed = yJumpSpeed;
n.root_action = root_action;
n.invulnerableTime = invulnerableTime;
n.ws = ws;
return n;
}
public MarioState next(int action, WorldState ws) {
MarioState n = clone();
n.action = action;
n.pred = this;
n.g = g + Tunables.GIncrement;
n.ws = ws;
n.move(action);
if (!hurtThisStep) {
n.ws = n.ws.step();
n.ws = n.ws.interact(n, false);
}
return n;
}
public void move(int action) {
boolean ducking = false; // just... we aren't doing this
float sideWaysSpeed = (action & ACT_SPEED) != 0 ? 1.2f : 0.6f;
// System.out.println("move: sidewaysspeed = " + sideWaysSpeed);
// System.out.println(String.format("move: xy=%5.1f,%5.1f", x, y));
if (invulnerableTime > 0)
invulnerableTime--;
if (xa > 2)
facing = 1;
else if (xa < -2)
facing = -1;
// jumping logic
// note: jumpTime ranges from 7 downto 0 during a standard upward jump
// acceleration phase, after which the regular falling code takes over
// jumpTime ranges from -6 upto 0 in the rare case of walljumps
if ((action & ACT_JUMP) != 0 || (jumpTime < 0 && !onGround && !sliding)) {
if (jumpTime < 0) { // post-walljump
xa = xJumpSpeed;
ya = -jumpTime * yJumpSpeed;
jumpTime++;
} else if (onGround && mayJump) { // initial jump phase
xJumpSpeed = 0;
yJumpSpeed = -1.9f;
jumpTime = 7;
ya = jumpTime * yJumpSpeed;
onGround = false;
sliding = false;
} else if (sliding && mayJump) { // walljump?
xJumpSpeed = -facing * 6.0f;
yJumpSpeed = -2.0f;
jumpTime = -6;
xa = xJumpSpeed;
ya = -jumpTime * yJumpSpeed;
onGround = false;
sliding = false;
facing = -facing;
} else if (jumpTime > 0) { // post-peak downward jump trajectory
// apparently this acts different than falling?
xa = xa + xJumpSpeed;
ya = jumpTime * yJumpSpeed;
jumpTime = jumpTime - 1;
}
} else {
jumpTime = 0;
}
// ducking code elided
if ((action & ACT_LEFT) != 0 && !ducking) {
if (facing == 1)
sliding = false;
xa -= sideWaysSpeed;
if (jumpTime >= 0)
facing = -1;
}
if ((action & ACT_RIGHT) != 0 && !ducking) {
if (facing == -1)
sliding = false;
xa += sideWaysSpeed;
if (jumpTime >= 0)
facing = 1;
}
if (((action & ACT_LEFT) == 0 && (action & ACT_RIGHT) == 0) || ducking || ya < 0 || onGround) {
sliding = false;
}
// if ((action&ACT_SPEED) != 0 && canShoot && fire && world.fireballsOnScreen<2)
// {
// // in theory: add fireball to our state
// }
// canShoot = (action&ACT_SPEED) == 0;
mayJump = (onGround || sliding) && (action & ACT_JUMP) == 0;
if (Math.abs(xa) < 0.5f) {
xa = 0;
}
// friction while sliding against a wall
if (sliding)
ya = ya * 0.5f;
// System.out.println("move: (xa,ya)1 = " + xa + "," + ya);
onGround = false;
move(xa, 0);
// System.out.println("move: (x,y,xa,ya)2 = " + x + "," + y + "," + xa + "," +
// ya);
move(0, ya);
// System.out.println("move: (x,y,xa,ya)3 = " + x + "," + y + "," + xa + "," +
// ya);
// world.level.height hardcoded as 15
if (y > 15 * 16 + 16) {
dead = true;
}
if (x < 0) {
x = 0;
xa = 0;
}
// System.out.println("move: (x,y,xa,ya)4 = " + x + "," + y + "," + xa + "," +
// ya);
// maximum speed is DAMPING_X/(1-DAMPING_X) * impulse_per_step
// impulse_per_step = 0.6 if walking, 1.2 if running
// max speed = 4.85 walking, 9.7 running
xa *= DAMPING_X;
ya *= DAMPING_Y;
// falling?
if (!onGround)
ya += 3;
// System.out.println("move: (xa,ya)5 = " + xa + "," + ya);
}
private boolean move(float xa, float ya) {
while (xa > 8) {
if (!move(8, 0))
return false;
xa -= 8;
}
while (xa < -8) {
if (!move(-8, 0))
return false;
xa += 8;
}
while (ya > 8) {
if (!move(0, 8))
return false;
ya -= 8;
}
while (ya < -8) {
if (!move(0, -8))
return false;
ya += 8;
}
boolean collide = false;
int width = 4;
int height = big ? 24 : 12;
if (ya > 0) {
if (isBlocking(x + xa - width, y + ya, xa, 0))
collide = true;
else if (isBlocking(x + xa + width, y + ya, xa, 0))
collide = true;
else if (isBlocking(x + xa - width, y + ya + 1, xa, ya))
collide = true;
else if (isBlocking(x + xa + width, y + ya + 1, xa, ya))
collide = true;
}
if (ya < 0) {
if (isBlocking(x + xa, y + ya - height, xa, ya))
collide = true;
else if (collide || isBlocking(x + xa - width, y + ya - height, xa, ya))
collide = true;
else if (collide || isBlocking(x + xa + width, y + ya - height, xa, ya))
collide = true;
}
if (xa > 0) {
sliding = true;
if (isBlocking(x + xa + width, y + ya - height, xa, ya))
collide = true;
else
sliding = false;
if (isBlocking(x + xa + width, y + ya - height / 2, xa, ya))
collide = true;
else
sliding = false;
if (isBlocking(x + xa + width, y + ya, xa, ya))
collide = true;
else
sliding = false;
}
if (xa < 0) {
sliding = true;
if (isBlocking(x + xa - width, y + ya - height, xa, ya))
collide = true;
else
sliding = false;
if (isBlocking(x + xa - width, y + ya - height / 2, xa, ya))
collide = true;
else
sliding = false;
if (isBlocking(x + xa - width, y + ya, xa, ya))
collide = true;
else
sliding = false;
}
if (collide) {
if (xa < 0) {
x = (int) ((x - width) / 16) * 16 + width;
this.xa = 0;
} else if (xa > 0) {
x = (int) ((x + width) / 16 + 1) * 16 - width - 1;
this.xa = 0;
}
if (ya < 0) {
y = (int) ((y - height) / 16) * 16 + height;
jumpTime = 0;
this.ya = 0;
} else if (ya > 0) {
y = (int) ((y - 1) / 16 + 1) * 16 - 1;
onGround = true;
}
return false;
} else {
x += xa;
y += ya;
return true;
}
}
private boolean isBlocking(float _x, float _y, float xa, float ya) {
int x = (int) (_x / 16); // block's quantized pos
int y = (int) (_y / 16);
int Mx = (int) (this.x / 16); // mario's quantized pos
int My = (int) (this.y / 16);
if (x == Mx && y == My)
return false;
boolean blocking = ws.isBlocking(x, y, xa, ya);
int block = ws.getBlock(x, y);
if (block == 34) { // coin
// yay for crazy side effects: pick up coin
ws = ws.removeTile(x, y);
return false;
}
if (blocking && ya < 0)
ws = ws.bump(x, y, big);
return blocking;
}
public void stomp(SpriteState enemy) {
float targetY = enemy.y - enemy.height() / 2;
move(0, targetY - y);
xJumpSpeed = 0;
yJumpSpeed = -1.9f;
jumpTime = 8;
ya = jumpTime * yJumpSpeed;
onGround = false;
sliding = false;
invulnerableTime = 1;
}
public void getHurt() {
if (invulnerableTime > 0)
return;
if (big) {
if (fire) {
fire = false;
} else {
big = false;
}
invulnerableTime = 32;
} else {
dead = true;
}
hurt++;
hurtThisStep = true;
}
public int marioMode() {
return ((big) ? 1 : 0) + ((fire) ? 1 : 0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy