package fi.hbp.angr.models.actors;
import aurelienribon.bodyeditor.BodyEditorLoader;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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.FixtureDef;
import com.badlogic.gdx.physics.box2d.Joint;
import com.badlogic.gdx.physics.box2d.QueryCallback;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.RevoluteJointDef;
import com.badlogic.gdx.scenes.scene2d.Actor;
import fi.hbp.angr.AssetContainer;
import fi.hbp.angr.G;
import fi.hbp.angr.models.CollisionFilterMasks;
import fi.hbp.angr.models.SlingshotActor;
import fi.hbp.angr.stage.GameStage;
/**
* Hans the player model.
*/
public class Hans extends Actor implements InputProcessor {
/**
* Specific asset container for Hans.
*/
public static class HansAssetContainer {
/** Body assets. */
public AssetContainer acBody = new AssetContainer();
/** Upper part of hand. */
public AssetContainer acHand_u = new AssetContainer();
/** Lower part of hand. */
public AssetContainer acHand_l = new AssetContainer();
}
/** Model data for Hans. */
protected class _ModelData {
/** Body. */
public Body body;
/** Origin coordinates of the body. */
public Vector2 bodyOrigin;
/** Sprite. */
public Sprite sprite;
/** Default angle used on spawn/reset. */
public float defAngle;
}
/** Name of this model. */
private static final String MODEL_NAME = "hans";
/** File path to the body texture. */
private static final String HANS_BODY_TEXTURE = "data/" + MODEL_NAME + "_body.png";
/** File path to the upper part of hand. */
private static final String HANS_HAND_U_TEXTURE = "data/" + MODEL_NAME + "_hand_u.png";
/** File path to the lower part of hand. */
private static final String HANS_HAND_L_TEXTURE = "data/" + MODEL_NAME + "_hand_l.png";
/** Model data for body of Hans. */
private final _ModelData hbody;
/** Model data for upper part of hand. */
private final _ModelData hand_u;
/** Model data for lower part of hand.*/
private final _ModelData hand_l;
/** Model data array. This is used for rendering. */
private final _ModelData[] modelArray;
/* Palm joint related */
/** Object jointed into hand.*/
private Body objPalm;
/** Joint used between hand and hand of Hans.*/
private Joint palmJoint;
/* Input processing/Controls */
/** Game stage. */
private final GameStage stage;
/** Test point for testing contact with mouse. */
private final Vector3 testPoint = new Vector3();
/** Ground body. */
private final Body groundBody;
/** Hit body. */
private Body hitBody = null;
/** true if currently dragging; otherwise false. */
private boolean dragging;
/**
* When this is true the palm joint will break if
* break force is exceeded.
*/
private boolean enableJointBreaking = false;
/**
* Preload static data
*/
public static void preload() {
G.getAssetManager().load(HANS_BODY_TEXTURE, Texture.class);
G.getAssetManager().load(HANS_HAND_U_TEXTURE, Texture.class);
G.getAssetManager().load(HANS_HAND_L_TEXTURE, Texture.class);
}
/**
* Initialize assets of this object
* @param hac storage location for assets of this model.
* @param bel Body editor loader.
*/
public static void initAssets(HansAssetContainer hac, BodyEditorLoader bel) {
/* Textures */
hac.acBody.texture = G.getAssetManager().get(
bel.getImagePath(MODEL_NAME + "_body"),
Texture.class);
hac.acHand_u.texture = G.getAssetManager().get(
bel.getImagePath(MODEL_NAME + "_hand_u"),
Texture.class);
hac.acHand_l.texture = G.getAssetManager().get(
bel.getImagePath(MODEL_NAME + "_hand_l"),
Texture.class);
/* Hans body BodyDef */
hac.acBody.bd = new BodyDef();
hac.acBody.bd.type = BodyType.StaticBody;
hac.acBody.bd.position.set(0, 0);
hac.acBody.bd.active = true;
/* Hans body FixtureDef */
hac.acBody.fd = new FixtureDef();
hac.acBody.fd.density = 4.5f;
hac.acBody.fd.friction = 1.0f;
hac.acBody.fd.restitution = 0.3f;
hac.acBody.fd.filter.categoryBits = CollisionFilterMasks.HANS_BODY;
hac.acBody.fd.filter.maskBits = ~CollisionFilterMasks.ALL;
/* Hand_u BodyDef */
hac.acHand_u.bd = new BodyDef();
hac.acHand_u.bd.type = BodyType.DynamicBody;
hac.acHand_u.bd.active = true;
/* Hand_u FixtureDef */
hac.acHand_u.fd = new FixtureDef();
hac.acHand_u.fd.density = 4.5f;
hac.acHand_u.fd.friction = 1.0f;
hac.acHand_u.fd.restitution = 0.3f;
hac.acHand_u.fd.filter.categoryBits = CollisionFilterMasks.HANS_HAND;
hac.acHand_u.fd.filter.maskBits = ~CollisionFilterMasks.ALL;
/* Hand_l Defs */
hac.acHand_l.bd = hac.acHand_u.bd;
hac.acHand_l.fd = hac.acHand_u.fd;
}
/**
* Class constructor.
* @param stage the game stage.
* @param bel a Body editor loader object.
* @param hac preloaded assets for this class.
* @param x spawn coordinate.
* @param y spawn coordinate.
* @param angle spawn angle.
*/
public Hans(GameStage stage, BodyEditorLoader bel, HansAssetContainer hac, float x, float y, float angle) {
this.stage = stage;
World world = stage.getWorld();
/* Attach body */
hac.acBody.bd.position.set(new Vector2(x * G.WORLD_TO_BOX,
y * G.WORLD_TO_BOX));
hbody = createModelData(world, bel, hac.acBody, "hans_body");
/* Attach hand_u */
hac.acHand_u.bd.position.set(new Vector2((x + 80) * G.WORLD_TO_BOX,
y * G.WORLD_TO_BOX));
hac.acHand_u.bd.angle = -45.0f * MathUtils.degRad;
hand_u = createModelData(world, bel, hac.acHand_u, "hans_hand_u");
hand_u.defAngle = -45.0f * MathUtils.degRad;
/* Attach hand_l */
hac.acHand_l.bd.position.set(new Vector2((x + 50) * G.WORLD_TO_BOX,
y * G.WORLD_TO_BOX));
hand_l = createModelData(world, bel, hac.acHand_l, "hans_hand_l");
hand_l.defAngle = 10.0f * MathUtils.degRad;
/* Create array of models for easier rendering */
modelArray = new _ModelData[]{ hbody, hand_l, hand_u };
/* Create joints */
createHandJoint(world, hbody, new Vector2(0f, 0f),
hand_u, new Vector2(0f, 0f),
-45.0f * MathUtils.degRad); /* ref angle */
createHandJoint(world, hand_u, new Vector2(0.4f, 0f),
hand_l, new Vector2(0f, 0f),
90.0f * MathUtils.degRad); /* ref angle */
/* 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);
}
private _ModelData createModelData(World world, BodyEditorLoader bel, AssetContainer ac, String modelName) {
_ModelData md = new _ModelData();
md.body = world.createBody(ac.bd);
md.body.setUserData(this);
md.sprite = new Sprite(ac.texture);
bel.attachFixture(md.body,
modelName,
ac.fd,
md.sprite.getWidth() * G.WORLD_TO_BOX);
md.bodyOrigin = bel.getOrigin(modelName, md.sprite.getWidth()).cpy();
Vector2 origin = bel.getOrigin(modelName, md.sprite.getWidth()).cpy();
md.sprite.setOrigin(origin.x, origin.y);
md.sprite.setPosition(md.body.getPosition().x * G.BOX_TO_WORLD,
md.body.getPosition().y * G.BOX_TO_WORLD);
md.sprite.setRotation((float)Math.toDegrees(md.body.getAngle()));
return md;
}
private void createHandJoint(World world, _ModelData a, Vector2 anchorA, _ModelData b, Vector2 anchorB, float refAngle) {
RevoluteJointDef jointDef = new RevoluteJointDef();
jointDef.initialize(a.body, b.body, hbody.body.getWorldCenter());
jointDef.lowerAngle = -0.5f * MathUtils.PI; // -90 degrees
jointDef.upperAngle = 0.25f * MathUtils.PI; // 45 degrees
jointDef.referenceAngle = refAngle;
jointDef.enableLimit = true;
jointDef.maxMotorTorque = 0.5f;
jointDef.motorSpeed = 0.0f;
jointDef.enableMotor = true;
jointDef.localAnchorA.set(anchorA);
jointDef.localAnchorB.set(anchorB);
world.createJoint(jointDef);
}
@Override
public void draw(SpriteBatch batch, float parentAlpha) {
for (_ModelData model : modelArray) {
Vector2 pos = model.body.getPosition();
model.sprite.setPosition(pos.x * G.BOX_TO_WORLD - model.bodyOrigin.x,
pos.y * G.BOX_TO_WORLD - model.bodyOrigin.y);
model.sprite.setOrigin(model.bodyOrigin.x, model.bodyOrigin.y);
model.sprite.setRotation((float)(model.body.getAngle() * MathUtils.radiansToDegrees));
model.sprite.draw(batch, parentAlpha);
}
evalJointBreak();
}
/**
* Check if palm joint break force is exceed
*/
private void evalJointBreak() {
if (palmJoint != null) {
Vector2 v = palmJoint.getReactionForce(1.0f);
float len = v.len();
if (len >= 0.55f && enableJointBreaking && !dragging) {
hand_l.body.getWorld().destroyJoint(palmJoint);
palmJoint = null;
enableJointBreaking = false;
}
}
}
/**
* Reset hand position and angle.
*/
public void resetHandPosition() {
Vector2 a;
a =hand_u.body.getPosition();
hand_u.body.setTransform(a, hand_u.defAngle);
a = hand_l.body.getPosition();
hand_l.body.setTransform(a, hand_l.defAngle);
}
@Override
public float getX() {
Vector2 pos = hbody.body.getPosition();
return pos.x * G.BOX_TO_WORLD - hbody.bodyOrigin.x;
}
@Override
public float getY() {
Vector2 pos = hbody.body.getPosition();
return pos.y * G.BOX_TO_WORLD - hbody.bodyOrigin.y;
}
public void setPalmJoint(Body obj) {
this.objPalm = obj;
RevoluteJointDef jointDef = new RevoluteJointDef();
jointDef.initialize(hand_l.body, obj, hand_l.bodyOrigin);
jointDef.enableLimit = false;
jointDef.maxMotorTorque = 0.5f;
jointDef.motorSpeed = 0.0f;
jointDef.enableMotor = true;
jointDef.localAnchorA.set(new Vector2(0.8f, 0.0f));
jointDef.localAnchorB.set(new Vector2(0.0f, 0.0f));
palmJoint = obj.getWorld().createJoint(jointDef);
}
public boolean isPalmJointActive() {
return (objPalm != null);
}
@Override
public boolean keyDown(int arg0) {
return false;
}
@Override
public boolean keyTyped(char arg0) {
return false;
}
@Override
public boolean keyUp(int arg0) {
return false;
}
@Override
public boolean mouseMoved(int arg0, int arg1) {
return false;
}
@Override
public boolean scrolled(int arg0) {
return false;
}
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) < SlingshotActor.touchDst) {
hitBody = fixture.getBody();
if (hitBody.equals(objPalm)) {
return false;
}
}
return true; /* Keep going until all bodies in the are are checked. */
}
};
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (palmJoint == null || objPalm == null)
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;
objPalm.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.objPalm)) {
dragging = true;
}
return false;
}
@Override
public boolean touchDragged(int x, int y, int pointer) {
return false;
}
@Override
public boolean touchUp(int arg0, int arg1, int arg2, int arg3) {
/* Object will be released soon. */
if (dragging == true) {
objPalm = null;
dragging = false;
enableJointBreaking = true;
}
return false;
}
}