package com.deftwun.zombiecopter.components;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.Entity;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Filter;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Joint;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Json.Serializable;
import com.badlogic.gdx.utils.JsonValue;
import com.badlogic.gdx.utils.Logger;
import com.badlogic.gdx.utils.Pool.Poolable;
import com.deftwun.zombiecopter.App;
import com.deftwun.zombiecopter.box2dJson.BodyModel;
import com.deftwun.zombiecopter.box2dJson.JointModel;
import com.deftwun.zombiecopter.box2dJson.PhysicsScene;
import com.deftwun.zombiecopter.box2dJson.PhysicsSceneModel;
/*
This class contains more logic than most components.
Its built around a 'PhysicsScene' which is basically a container for
Bodies, fixtures, and joints.
*/
public class PhysicsComponent extends Component implements Serializable, Poolable{
public Entity ownerEntity = null;
public String collisionEffect = "";
private PhysicsScene scene = new PhysicsScene(App.engine.systems.physics.world);
private float collisionNormal;
private Logger logger = new Logger("PhysicsComponent",Logger.INFO);
public PhysicsComponent(){
collisionEffect = "";
collisionNormal = 0;
ownerEntity = null;
scene.setPhysicsSceneListener(App.engine.systems.physics);
}
//Add existing body
public String addBody(String name,Body b){
logger.debug("Add body");
b.setUserData(this);
return scene.addBody(name,b);
}
//Add Existing body
public String addBody(Body b){
logger.debug("Add body");
b.setUserData(this);
return scene.addBody(b);
}
//Create and add body using a BodyModel. A unique name is automatically generated and returned
public String addBody(BodyModel bm){
logger.debug("Add body model");
Body b = scene.createBody(bm);
b.setUserData(this);
return bm.name;
}
public String addJoint(JointModel jm){
logger.debug("Add joint model");
logger.error("addJoint(JointModel jm) may not function correctly");
scene.createJoint(jm);
return jm.name;
}
public void addJoint(Joint j) {
scene.addJoint(j);
}
//Set the primary body
public void setPrimaryBody(String name){
scene.setPrimaryBody(name);
}
//Get the primary body
public Body getPrimaryBody(){
return scene.getPrimaryBody();
}
//Get the name of primary body
public String getPrimaryBodyName(){
return scene.getName(getPrimaryBody());
}
//Get body by name
public Body getBody(String name){
return scene.getBody(name);
}
//Get an array of all attached bodies
public Array<Body> getBodies(){
return scene.getBodies();
}
//Get a specific joint
public Joint getJoint(String name){
return scene.getJoint(name);
}
//Get all joints
public Array<Joint> getJoints(){
return scene.getJoints();
}
//Get fixture by name
public Fixture getFixture(String name){
return scene.getFixture(name);
}
//Applies filter to ALL fixtures that have been added so far
public void setFilter(Filter filter){
for (Fixture f : scene.getFixtures()){
f.setFilterData(filter);
}
}
//Applies filter to ALL fixtures that have been added so far
public void setFilter(short category, short mask){
for (Fixture f : scene.getFixtures()){
Filter filter = f.getFilterData();
filter.categoryBits = category;
filter.maskBits = mask;
f.setFilterData(filter);
}
}
//Get the position of the primary body
//TODO maybe sum the body AABB's together and return the center of that?
public Vector2 getPosition(){
if (scene.getPrimaryBody() != null) return scene.getPrimaryBody().getWorldCenter();
return new Vector2();
}
//Set the position of the primary body. All other bodies are moved relative to that
public void setPosition(Vector2 position) {
if (scene.getPrimaryBody() == null) return;
Vector2 deltaP = new Vector2(position).sub(scene.getPrimaryBody().getWorldCenter());
for (Body b : scene.getBodies()){
b.setTransform(b.getWorldCenter().add(deltaP), b.getAngle());
}
}
//Set the rotation of the primary body. All other bodies are rotated relative to that. (NOT YET!)
// TODO: OTHER BODIES ARE NOT ALTERED
public void setRotation(float angleDegrees){
if (scene.getPrimaryBody() != null){
scene.getPrimaryBody().setTransform(scene.getPrimaryBody().getWorldCenter().x,scene.getPrimaryBody().getWorldCenter().y,(float) Math.toRadians(angleDegrees));
}
}
//Get the rotation of the primary body
public float getRotation(){
if (scene.getPrimaryBody() != null)
return (float) Math.toDegrees(scene.getPrimaryBody().getAngle());
return 0;
}
//Set velocity of all bodies
public void setLinearVelocity(Vector2 vel){
for (Body b : scene.getBodies()){
b.setLinearVelocity(vel);
}
}
//Get velocity of primary body
public Vector2 getLinearVelocity(){
if (scene.getPrimaryBody() != null){
return scene.getPrimaryBody().getLinearVelocity();
}
return new Vector2();
}
//Get the combined mass of all bodies
public float getMass(){
float mass = 0;
for (Body b : scene.getBodies())
mass += b.getMass();
return mass;
}
//Destroy everything
public void destroy(){
logger.debug("Destroy Everything");
scene.destroy();
collisionNormal = 0;
}
//Destroy specific body
public void destroyBody(String name){
logger.debug("Destroy body: " + name);
scene.destroyBody(name);
}
//Destroy specific fixture
public void destroyFixture(String name){
logger.debug("Destroy fixture: " + name);
scene.destroyFixture(name);
}
//Destroy Joint
public void destroyJoint(String name){
logger.debug("Destroy Joint: " + name);
scene.destroyJoint(name);
}
//Get the total impact force that has been applied since last update
public float getCollisionNormal(){
return collisionNormal;
}
//Used by physics system during BeginContact
public void addCollisionNormal(float normal) {
collisionNormal += normal;
}
//Used by physics system during update
public void clearCollisionNormal(){
collisionNormal = 0;
}
//Json Write
@Override
public void write(Json json) {
logger.debug("Serializing..");
PhysicsConfig config = new PhysicsConfig();
config.collisionEffect = collisionEffect;
config.sceneModel = scene.toSceneModel();
json.writeFields(config);
}
//Json read
@Override
public void read(Json json, JsonValue jsonData) {
logger.debug("Deserializing");
PhysicsConfig config = json.fromJson(PhysicsConfig.class, jsonData.toString());
collisionEffect = config.collisionEffect;
this.buildFromModel(config.sceneModel);
}
//Pooled component reset
@Override
public void reset() {
collisionEffect = "";
collisionNormal = 0;
ownerEntity = null;
scene.setPhysicsSceneListener(App.engine.systems.physics);
this.destroy();
}
//Create everything from a SceneModel. (SceneModels are easily serialized)
private void buildFromModel(PhysicsSceneModel physicsModel){
logger.debug("Build from physics scene model");
scene.createFromModel(physicsModel);
for (Body b : scene.getBodies()){
b.setUserData(this);
}
}
//Used internally for serialization / deserialization
private static class PhysicsConfig{
protected String collisionEffect = "";
protected PhysicsSceneModel sceneModel;
}
}