package com.gdxjam.utils;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.PooledEngine;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
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.CircleShape;
import com.badlogic.gdx.physics.box2d.EdgeShape;
import com.badlogic.gdx.physics.box2d.Filter;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.gdxjam.Assets;
import com.gdxjam.behaviors.control.CorvetteControlBehavior;
import com.gdxjam.behaviors.control.CruiserControlBehavior;
import com.gdxjam.behaviors.control.FighterControlBehavior;
import com.gdxjam.components.Components;
import com.gdxjam.components.ControlComponent;
import com.gdxjam.components.DecayComponent;
import com.gdxjam.components.FSMComponent;
import com.gdxjam.components.FactionComponent;
import com.gdxjam.components.FactionComponent.Faction;
import com.gdxjam.components.GunportComponent;
import com.gdxjam.components.HealthComponent;
import com.gdxjam.components.IdentifyingComponent;
//import com.gdxjam.components.IdentifyingComponent;
import com.gdxjam.components.ParalaxComponent;
import com.gdxjam.components.ParticleComponent;
import com.gdxjam.components.PhysicsComponent;
import com.gdxjam.components.ProjectileComponent;
import com.gdxjam.components.ResourceComponent;
import com.gdxjam.components.SpriteComponent;
import com.gdxjam.components.SteerableComponent;
import com.gdxjam.components.SteeringBehaviorComponent;
import com.gdxjam.components.TargetComponent;
import com.gdxjam.systems.ParticleSystem;
import com.gdxjam.systems.ParticleSystem.ParticleType;
import com.gdxjam.systems.PhysicsSystem;
/** @author Torin Wiebelt (Twiebs) Creates Ashley entities using a builder */
public class EntityFactory {
private static final String TAG = "[" + EntityFactory.class.getSimpleName() + "]";
private static PooledEngine engine;
private static PhysicsSystem physicsSystem;
private static EntityBuilder builder = new EntityBuilder();
private static PhysicsBuilder physicsBuilder = new PhysicsBuilder();
public static Entity createShip(Faction faction, Vector2 position) {
Entity entity = builder.createEntity(EntityCategory.UNIT, position).physicsBody(BodyType.DynamicBody).circleCollider(Constants.unitRadius, 1.0f)
.damping(1.5f, 1.0f).steerable(Constants.unitRadius).steeringBehavior().health(100).faction(faction).target()
.sprite(Assets.spacecraft.ships.get(faction.ordinal()), Constants.unitRadius * 2, Constants.unitRadius * 2).getWithoutAdding();
PhysicsComponent physicsComp = Components.PHYSICS.get(entity);
Components.STEERABLE.get(entity).setIndependentFacing(true);
return entity;
}
public static Entity createPlayer(Faction faction, Vector2 position, long count) {
Entity entity = EntityFactory.createShip(faction, position);
builder.entity = entity;
builder.setCollider(faction);
builder.control(faction).uuid(count).addToEngine();
return entity;
}
public static Entity createGun(Vector2 position) {
Entity entity = builder.createEntity(EntityCategory.GUN, position).physicsBody(BodyType.DynamicBody).circleCollider(Constants.unitRadius, 0.5f)
.getWithoutAdding();
return entity;
}
public static Entity createProjectile(Vector2 position, Vector2 velocity, float radius, Faction faction, int damage) {
Entity entity = builder.createEntity(EntityCategory.PROJECTILE, position).physicsBody(BodyType.DynamicBody).circleSensor(radius)
.filter(EntityCategory.PROJECTILE, 0, EntityCategory.UNIT | EntityCategory.RESOURCE | EntityCategory.MOTHERSHIP).faction(faction)
.sprite(Assets.projectile.projectiles.get(faction.ordinal()), radius * 2, radius * 2).getWithoutAdding();
ProjectileComponent projectileComp = engine.createComponent(ProjectileComponent.class).init(damage);
entity.add(projectileComp);
entity.add(engine.createComponent(DecayComponent.class).init(Constants.projectileDecayTime));
PhysicsComponent physicsComp = Components.PHYSICS.get(entity);
physicsComp.getBody().setBullet(true);
physicsComp.getBody().setLinearVelocity(velocity);
physicsComp.getBody().setTransform(position, velocity.angle());
engine.addEntity(entity);
return entity;
}
public static Entity createAsteroid(Vector2 position, float radius) {
Entity entity = builder.createEntity(EntityCategory.RESOURCE, position).physicsBody(BodyType.StaticBody).circleCollider(radius, 50.0f)
.filter(EntityCategory.RESOURCE, 0, EntityCategory.PROJECTILE | EntityCategory.SQUAD | EntityCategory.UNIT)
.resource((int) (Constants.baseAsteroidResourceAmt * radius)).steerable(radius).faction(Faction.NONE)
.sprite(Assets.space.asteroids.random(), radius * 2, radius * 2).addToEngine();
return entity;
}
public static Entity createParticle(Vector2 position, ParticleType type) {
Entity entity = builder.createEntity(EntityCategory.GRAPHICS, position).particle(type).addToEngine();
return entity;
}
public static Entity createBoundry(Vector2 start, Vector2 end) {
Entity entity = builder.createEntity(EntityCategory.WALL, new Vector2(0, 0)).physicsBody(BodyType.StaticBody).getWithoutAdding();
FixtureDef def = new FixtureDef();
EdgeShape edge = new EdgeShape();
edge.set(start, end);
def.shape = edge;
def.filter.categoryBits = EntityCategory.WALL;
Components.PHYSICS.get(entity).getBody().createFixture(edge, 1.0f);
engine.addEntity(entity);
return entity;
}
public static Entity createBackgroundArt(Vector2 position, float width, float height, TextureRegion region, int layer) {
Entity entity = builder.createEntity(EntityCategory.GRAPHICS, position).sprite(region, width, height).getWithoutAdding();
entity.add(engine.createComponent(ParalaxComponent.class).init(position.x, position.y, width, height, layer));
engine.addEntity(entity);
return entity;
}
/**
* Called when the GameManager first initializes a new engine
*
* @param engine
* The engine that the factory will use to create its
* entities
*/
public static void setEngine(PooledEngine engine) {
EntityFactory.engine = engine;
physicsSystem = engine.getSystem(PhysicsSystem.class);
}
/**
* Creates an entity from the engine when first instantiated Exit builder
* by calling addToEngine() or getWithoutAdding()
*/
public static class EntityBuilder {
private static final BodyType DEFAULT_BODY = BodyType.DynamicBody;
public Vector2 position;
public Entity entity;
public EntityBuilder createEntity(int categoryBits, Vector2 position) {
entity = engine.createEntity();
entity.flags = categoryBits;
this.position = position;
return this;
}
public EntityBuilder control(Faction faction) {
control(30, faction);
return this;
}
public EntityBuilder control(float radius, Faction faction) {
if (faction == Faction.FACTION0) {
entity.add(engine.createComponent(ControlComponent.class).init(new FighterControlBehavior(entity, engine, radius)));
}
if (faction == Faction.FACTION1) {
entity.add(engine.createComponent(ControlComponent.class).init(new CruiserControlBehavior(entity, engine, radius)));
}
if (faction == Faction.FACTION2) {
entity.add(engine.createComponent(ControlComponent.class).init(new CorvetteControlBehavior(entity, engine, radius)));
}
return this;
}
public EntityBuilder particle(ParticleType type) {
PooledEffect effect = engine.getSystem(ParticleSystem.class).createEffect(position, type);
entity.add(engine.createComponent(ParticleComponent.class).init(effect));
return this;
}
public PhysicsBuilder buildPhysics(BodyType type) {
return physicsBuilder.reset(type, position, entity);
}
public EntityBuilder physicsBody(BodyType type) {
BodyDef def = new BodyDef();
def.type = type;
def.position.set(position);
Body body = physicsSystem.createBody(def);
body.setUserData(entity);
PhysicsComponent physics = engine.createComponent(PhysicsComponent.class).init(body);
entity.add(physics);
return this;
}
public EntityBuilder damping(float angular, float linear) {
if (Components.PHYSICS.has(entity)) {
PhysicsComponent physics = Components.PHYSICS.get(entity);
physics.getBody().setAngularDamping(angular);
physics.getBody().setLinearDamping(linear);
} else {
Gdx.app.error(TAG, "entity is missing physics component!");
}
return this;
}
public EntityBuilder stateMachine() {
FSMComponent stateMachineComp = engine.createComponent(FSMComponent.class).init(entity);
entity.add(stateMachineComp);
return this;
}
public EntityBuilder steeringBehavior() {
SteeringBehaviorComponent behaviorComp = engine.createComponent(SteeringBehaviorComponent.class);
entity.add(behaviorComp);
return this;
}
public EntityBuilder resource(int amount) {
ResourceComponent resourceComp = engine.createComponent(ResourceComponent.class).init(amount);
entity.add(resourceComp);
return this;
}
public EntityBuilder category(int categoryBits) {
entity.flags = categoryBits;
return this;
}
public EntityBuilder filter(int categoryBits, int groupIndex, int maskBits) {
entity.flags = categoryBits;
Filter filter = new Filter();
filter.categoryBits = (short) categoryBits;
filter.groupIndex = (short) groupIndex;
filter.maskBits = (short) maskBits;
// TODO make EntityBuilder filter beter
Components.PHYSICS.get(entity).getBody().getFixtureList().get(0).setFilterData(filter);
return this;
}
public EntityBuilder target() {
entity.add(engine.createComponent(TargetComponent.class));
return this;
}
public EntityBuilder gunport(Vector2 origin) {
entity.add(engine.createComponent(GunportComponent.class).init(origin));
return this;
}
public EntityBuilder faction(Faction faction) {
entity.add(engine.createComponent(FactionComponent.class).init(faction));
return this;
}
public EntityBuilder steerable(float radius) {
PhysicsComponent physics = Components.PHYSICS.get(entity);
if (physics == null) {
Gdx.app.error(TAG, "cannot create a steerable without physics!");
return this;
}
SteerableComponent steerable = engine.createComponent(SteerableComponent.class).init(physics.getBody(), radius);
entity.add(steerable);
return this;
}
public EntityBuilder setCollider(Faction faction){
if(faction == Faction.FACTION0){
return circleCollider(1, 1);
}
if(faction == Faction.FACTION1 || faction == Faction.FACTION2){
return boxCollider(1, 1, 1);
}
else{return null;}
}
public EntityBuilder circleCollider(float radius, float density) {
CircleShape shape = new CircleShape();
shape.setRadius(radius);
PhysicsComponent physics = Components.PHYSICS.get(entity);
if (physics == null) {
physicsBody(DEFAULT_BODY);
}
FixtureDef def = new FixtureDef();
def.shape = shape;
def.density = density;
def.isSensor = true;
physics.getBody().createFixture(def);
shape = new CircleShape();
shape.setRadius(radius - 2);
shape.setPosition(new Vector2(2, 0));
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
fixture.density = density;
fixture.isSensor = true;
physics.getBody().createFixture(shape, density);
return this;
}
// Working on this not done yet but had to go - Nate
public EntityBuilder polygonCollider(Vector2[] points, float density) {
PolygonShape shape = new PolygonShape(); //
shape.set(points);
PhysicsComponent physics = Components.PHYSICS.get(entity);
if (physics == null) {
physicsBody(DEFAULT_BODY);
}
FixtureDef def = new FixtureDef();
def.shape = shape;
def.density = density;
def.isSensor = true;
physics.getBody().createFixture(def);
shape = new PolygonShape();
shape.set(points);
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
fixture.density = density;
fixture.isSensor = true;
physics.getBody().createFixture(shape, density);
return this;
}
public EntityBuilder boxCollider(float height, float width, float density) {
PolygonShape shape = new PolygonShape(); //
shape.setAsBox(height, width);
PhysicsComponent physics = Components.PHYSICS.get(entity);
if (physics == null) {
physicsBody(DEFAULT_BODY);
}
FixtureDef def = new FixtureDef();
def.shape = shape;
def.density = density;
def.isSensor = true;
physics.getBody().createFixture(def);
shape = new PolygonShape();
shape.setAsBox(height, width);
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
fixture.density = density;
fixture.isSensor = true;
physics.getBody().createFixture(shape, density);
return this;
}
public EntityBuilder circleSensor(float radius) {
CircleShape shape = new CircleShape();
shape.setRadius(radius);
PhysicsComponent physics = Components.PHYSICS.get(entity);
if (physics == null) {
physicsBody(DEFAULT_BODY);
}
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.isSensor = true;
fixtureDef.shape = shape;
physics.getBody().createFixture(fixtureDef);
return this;
}
public EntityBuilder rangeSensor(float range, float arc) {
Body body;
if (Components.PHYSICS.has(entity)) {
body = Components.PHYSICS.get(entity).getBody();
} else {
Gdx.app.error(TAG, "can not add range sensor : entity does not have a physics component!");
return this;
}
Vector2 vertices[] = new Vector2[8];
for (int i = 0; i <= 7; i++) {
vertices[i] = new Vector2(0, 0);
}
for (int i = 0; i < 7; i++) {
float angle = (i / 6.0f * arc * MathUtils.degRad) - (90 * MathUtils.degRad);
vertices[i + 1].set(range * MathUtils.cos(angle), range * MathUtils.sin(angle));
}
PolygonShape poly = new PolygonShape();
poly.set(vertices);
FixtureDef sensorDef = new FixtureDef();
sensorDef.shape = poly;
sensorDef.isSensor = true;
body.createFixture(sensorDef);
poly.dispose();
return this;
}
public EntityBuilder health(int value) {
HealthComponent health = engine.createComponent(HealthComponent.class);
health.max = value;
health.value = value;
entity.add(health);
return this;
}
public EntityBuilder sprite(TextureRegion region, float width, float height) {
SpriteComponent spriteComp = engine.createComponent(SpriteComponent.class).init(region, position.x, position.y, width, height);
entity.add(spriteComp);
return this;
}
public EntityBuilder uuid(long uuid) {
entity.add(engine.createComponent(IdentifyingComponent.class).init(uuid));
return this;
}
public Entity addToEngine() {
engine.addEntity(entity);
return entity;
}
public Entity getWithoutAdding() {
return entity;
}
}
public static class PhysicsBuilder {
private Body body;
public PhysicsBuilder reset(BodyType type, Vector2 position, Entity entity) {
BodyDef def = new BodyDef();
def.type = type;
def.position.set(position);
body = physicsSystem.createBody(def);
body.setUserData(entity);
return this;
}
public EntityBuilder getBody() {
return builder;
}
//
}
}