package fi.hbp.angr.models; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.BodyDef; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; import com.badlogic.gdx.physics.box2d.Fixture; import com.badlogic.gdx.physics.box2d.QueryCallback; import com.badlogic.gdx.physics.box2d.World; import com.badlogic.gdx.physics.box2d.joints.MouseJoint; import com.badlogic.gdx.physics.box2d.joints.MouseJointDef; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Stage; import fi.hbp.angr.G; /** * Slingshot with mouse controls. */ public abstract class SlingshotActor extends Actor implements InputProcessor { /** * Game stage */ private final Stage stage; /** * Maximum mouse joint acceleration. */ private final float a_max; /** * Applied impulse force. */ private final float F_impulse; /** * Body of the item * @note Body is not initialized by this abstract class */ protected Body body; public static final float touchDst = 10.0f; /** * Slingshot state. */ private boolean slingshotEnabled = true; /* Input processing/Controls */ protected MouseJoint mouseJoint = null; private final Vector3 testPoint = new Vector3(); private final Vector2 startPoint = new Vector2(); /** * Temporary target vector. */ private final Vector2 target = new Vector2(); private final Body groundBody; private Body hitBody = null; /** * Constructor for SlingShotActor. * @param stage game stage. * @param world game (physics) world. * @param a_max maximum mouse joint acceleration. * @param F_impulse maximum force. */ public SlingshotActor(Stage stage, World world, float a_max, float F_impulse) { this.stage = stage; this.a_max = a_max; this.F_impulse = F_impulse; /* We also need an invisible zero size ground body * to which we can connect the mouse joint */ BodyDef bodyDef = new BodyDef(); groundBody = world.createBody(bodyDef); } QueryCallback callback = new QueryCallback() { @Override public boolean reportFixture (Fixture fixture) { /* If the hit point is inside the fixture of the body /* we report it */ Vector2 v = fixture.getBody().getPosition(); if (fixture.testPoint(testPoint.x, testPoint.y) || v.dst(testPoint.x, testPoint.y) < touchDst) { hitBody = fixture.getBody(); if (hitBody.equals(body)) { return false; } } return true; /* Keep going until all bodies in the area are checked. */ } }; @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { if (slingshotEnabled == false) return false; /* Translate the mouse coordinates to world coordinates */ stage.getCamera().unproject(testPoint.set(screenX, screenY, 0)); testPoint.x *= G.WORLD_TO_BOX; testPoint.y *= G.WORLD_TO_BOX; hitBody = null; body.getWorld().QueryAABB(callback, testPoint.x - 0.0001f, testPoint.y - 0.0001f, testPoint.x + 0.0001f, testPoint.y + 0.0001f); if (hitBody == groundBody) hitBody = null; if (hitBody == null) return false; /* Ignore kinematic bodies, they don't work with the mouse joint */ if (hitBody.getType() == BodyType.KinematicBody) return false; if (hitBody.equals(this.body)) { MouseJointDef def = new MouseJointDef(); def.bodyA = groundBody; def.bodyB = hitBody; def.collideConnected = true; def.target.set(testPoint.x, testPoint.y); def.maxForce = a_max * hitBody.getMass(); startPoint.set(testPoint.x, testPoint.y); mouseJoint = (MouseJoint)((body.getWorld()).createJoint(def)); hitBody.setAwake(true); } return false; } @Override public boolean touchDragged(int x, int y, int pointer) { /* If a mouse joint exists we simply update * the target of the joint based on the new * mouse coordinates * */ if (mouseJoint != null) { stage.getCamera().unproject(testPoint.set(x, y, 0)); testPoint.x = MathUtils.clamp(testPoint.x * G.WORLD_TO_BOX, startPoint.x - 3, startPoint.x + 3); testPoint.y = MathUtils.clamp(testPoint.y * G.WORLD_TO_BOX, startPoint.y - 3, startPoint.y + 3); mouseJoint.setTarget(target.set(testPoint.x, testPoint.y)); } return false; } @Override public boolean touchUp(int x, int y, int pointer, int button) { /* If a mouse joint exists we simply destroy it */ if (mouseJoint != null) { body.getWorld().destroyJoint(mouseJoint); mouseJoint = null; float fx = (startPoint.x - testPoint.x) * F_impulse; float fy = (startPoint.y - testPoint.y) * F_impulse; hitBody.applyLinearImpulse(new Vector2(fx, fy), hitBody.getPosition()); slingshotRelease(); } return false; } /** * Optional tasks to be done after releasing slingshot */ protected abstract void slingshotRelease(); @Override public boolean keyDown(int keycode) { return false; } @Override public boolean keyUp(int keycode) { return false; } @Override public boolean keyTyped(char character) { return false; } @Override public boolean mouseMoved(int screenX, int screenY) { return false; } @Override public boolean scrolled(int amount) { return false; } /** * Get slingshot state * @return true = slingshot enabled, false = slingshot disabled */ public boolean getSlingshotState() { return slingshotEnabled; } /** * Set slingshot state * @param state true = slingshot enabled, false = slingshot disabled */ public void setSlingshotState(boolean state) { slingshotEnabled = state; } public Body getBody() { return body; } }