package com.corosus.game.system;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector4f;
import com.artemis.Aspect;
import com.artemis.Component;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.systems.IntervalEntityProcessingSystem;
import com.corosus.game.Cst;
import com.corosus.game.GameSettings;
import com.corosus.game.Game_AI_TestBed;
import com.corosus.game.Level;
import com.corosus.game.Logger;
import com.corosus.game.client.assets.GameAssetManager;
import com.corosus.game.component.EntityData;
import com.corosus.game.component.Health;
import com.corosus.game.component.PhysicsData;
import com.corosus.game.component.Position;
import com.corosus.game.component.ProfileData;
import com.corosus.game.component.ProjectileData;
import com.corosus.game.component.Velocity;
import com.corosus.game.component.WeaponData;
import com.corosus.game.component.WeaponData.Weapon;
import com.corosus.game.component.WeaponData.WeaponLocation;
import com.corosus.game.entity.ActionRoutine;
import com.corosus.game.entity.EnumEntityType;
import com.corosus.game.factory.EntityFactory;
import com.corosus.game.util.MathUtil;
import com.corosus.game.util.VecUtil;
public class SpriteSim extends IntervalEntityProcessingSystem {
private ComponentMapper<Position> mapPos;
private ComponentMapper<Health> mapHealth;
private ComponentMapper<Velocity> mapVelocity;
private ComponentMapper<EntityData> mapData;
private ComponentMapper<ProfileData> mapProfile;
private ComponentMapper<PhysicsData> mapPhysics;
private ComponentMapper<ProjectileData> mapProjectile;
private ComponentMapper<WeaponData> mapWeapons;
public class CollisionEntry {
public int entIDA;
public int entIDB;
public CollisionEntry(int entIDA, int entIDB) {
this.entIDA = entIDA;
this.entIDB = entIDB;
}
}
public SpriteSim(float interval) {
super(Aspect.one(Position.class, Health.class, Velocity.class,
EntityData.class, ProfileData.class, PhysicsData.class,
ProjectileData.class), interval);
}
@Override
protected void initialize() {
super.initialize();
}
@Override
protected void process(Entity e) {
//Position pos = mapPos.get(e);
Health health = mapHealth.get(e);
Position pos = mapPos.get(e);
Velocity motion = mapVelocity.get(e);
EntityData data = mapData.get(e);
ProfileData profileData = mapProfile.get(e);
PhysicsData physics = mapPhysics.get(e);
//ProjectileData projData = mapProjectile.get(e);
Level level = Game_AI_TestBed.instance().getLevel(data.levelID);
if (physics.needInit) {
physics.needInit = false;
//need to make enemy projectiles not able to hurt enemies
//teams?
short categoryBits = 0;
if (data.type == EnumEntityType.SPRITE) {
if (data.team == 0) {
categoryBits = PhysicsData.COLLIDE_TEAM0_SPRITE;
} else if (data.team == 1) {
categoryBits = PhysicsData.COLLIDE_TEAM1_SPRITE;
} else {
Logger.dbg("MISSING TEAM COLLIDE DATA");
}
} else if (data.type == EnumEntityType.PROJECTILE) {
if (data.team == 0) {
categoryBits = PhysicsData.COLLIDE_TEAM0_PROJECTILE;
} else if (data.team == 1) {
categoryBits = PhysicsData.COLLIDE_TEAM1_SPRITE;
} else {
Logger.dbg("MISSING TEAM COLLIDE DATA");
}
}
short maskBits = 0;
if (data.type == EnumEntityType.SPRITE) {
if (data.team == 0) {
maskBits = (short) (PhysicsData.COLLIDE_TEAM1_PROJECTILE | PhysicsData.COLLIDE_TEAM0_SPRITE | PhysicsData.COLLIDE_TEAM1_SPRITE);
} else if (data.team == 1) {
maskBits = (short) (PhysicsData.COLLIDE_TEAM0_PROJECTILE | PhysicsData.COLLIDE_TEAM1_SPRITE | PhysicsData.COLLIDE_TEAM0_SPRITE);
} else {
Logger.dbg("MISSING TEAM COLLIDE DATA");
}
} else if (data.type == EnumEntityType.PROJECTILE) {
if (data.team == 0) {
maskBits = PhysicsData.COLLIDE_TEAM1_SPRITE;
//make projectiles also collide with eachother
//maskBits = (short) (PhysicsData.COLLIDE_TEAM1_SPRITE | PhysicsData.COLLIDE_TEAM1_PROJECTILE);
} else if (data.team == 1) {
maskBits = PhysicsData.COLLIDE_TEAM0_SPRITE;
} else {
Logger.dbg("MISSING TEAM COLLIDE DATA");
}
}
//TODO: refine use of CCD on fast moving projectiles only, determine best based on smallest sprite and prj size / speed
physics.initPhysics(data.levelID, e.getId(), pos.x, pos.y, data.sizeDiameter, categoryBits, maskBits, data.type == EnumEntityType.PROJECTILE);
}
List<Component> listComponents = new ArrayList<Component>();
listComponents.add(motion);
for (int i = 0; i < profileData.listRoutines.size(); i++) {
ActionRoutine action = profileData.listRoutines.get(i);
action.tick(listComponents);
if (action.isActive()) {
}/* else {
action.dispose();
profileData.listRoutines.remove(i--);
}*/
}
Random rand = new Random();
//Logger.dbg("data.type: " + data.type + " pos: " + pos.x + ", " + pos.y);
if (data.type == EnumEntityType.SPRITE) {
WeaponData weapons = mapWeapons.get(e);
if (data.aiControlled) {
data.getAgent().tick();
/*if (weapons.hasPrimaryWeapon()) {
Weapon weapon = weapons.getActivePrimary();
float distRand = 1F;
EntityFactory.getEntity(weapon.projectileType).prepareFromData(data.levelID, pos.x, pos.y, data.team, rand.nextFloat()-rand.nextFloat(), rand.nextFloat()-rand.nextFloat());
}*/
//TEMP DUMMY AI
profileData.moveSpeed = 5;
/*motion.x = rand.nextInt(2)-rand.nextInt(2);
motion.y = rand.nextInt(2)-rand.nextInt(2);*/
Entity player = level.getPlayerEntity();
if (player != null) {
Position posPlayer = mapPos.get(player);
float distPlayer = VecUtil.getDist(new Vector2f(pos.x, pos.y), new Vector2f(posPlayer.x, posPlayer.y));
boolean chase = false;
boolean omnipotent = false;
if (omnipotent || (distPlayer < 800 && VecUtil.canSee(data.levelID, pos.toVec(), posPlayer.toVec()))) {
chase = true;
data.getAgent().getBlackboard().setTargetID(player.getId());
}
if (data.getAgent().getBlackboard().getTargetID() != -1) {
data.getAgent().moveTo(posPlayer.toVec());
} else {
int distRand = Cst.TILESIZE * 30;
int tryCount = 10;
if (rand.nextInt(30) == 0) {
for (int i = 0; i < tryCount; i++) {
Vector2f tryPos = new Vector2f(rand.nextInt(distRand)-rand.nextInt(distRand), rand.nextInt(distRand)-rand.nextInt(distRand));
tryPos = new Vector2f(pos.x + tryPos.x, pos.y + tryPos.y);
if (level.isPassable((int)tryPos.x, (int)tryPos.y)) {
data.getAgent().moveTo(tryPos);
break;
}
}
}
}
if (chase) {
Vector2f targVec = VecUtil.getTargetVector(pos.x, pos.y, posPlayer.x, posPlayer.y);
/*if (distPlayer > 64) {
motion.x = targVec.x * profileData.moveSpeed;
motion.y = targVec.y * profileData.moveSpeed;
} else {
motion.x = 0;
motion.y = 0;
}*/
if (weapons.hasPrimaryWeapon()) {
Weapon weapon = weapons.getActivePrimary();
if (weapon.canFire()) {
//float speed = proj.moveSpeed * 4F;
float vecX = targVec.x;
float vecY = targVec.y;
weapon.fire();
GameAssetManager.instance().getSound("shoot").play(GameSettings.vol);
EntityFactory.getEntity(weapon.projectileType).prepareFromData(data.levelID, pos.x + vecX * 2, pos.y + vecY * 2, data.team, vecX, vecY);
}
}
/*if (level.getGameTime() % 2 == 0) {
if (rand.nextInt(5) == 0) {
float speed = profileData.moveSpeed * 4F;
float vecX = targVec.x * speed;
float vecY = targVec.y * speed;
EntityFactory.getEntity(SpawnableTypes.PRJ_PULSE).prepareFromData(pos.x + vecX * 2, pos.y + vecY * 2, data.team, vecX, vecY);
}
}*/
} else {
/*motion.x = rand.nextInt(distRand)-rand.nextInt(distRand);
motion.y = rand.nextInt(distRand)-rand.nextInt(distRand);*/
}
}
//move to AI target position
if (data.getAgent().getBlackboard().getPosTarget() != null) {
float distToTarg = VecUtil.getDist(new Vector2f(pos.x, pos.y), data.getAgent().getBlackboard().getPosTarget());
if (distToTarg > Cst.TILESIZE / 32) {
Vector2f targVec = VecUtil.getTargetVector(new Vector2f(pos.x, pos.y), data.getAgent().getBlackboard().getPosTarget());
motion.x = targVec.x * profileData.moveSpeed;
motion.y = targVec.y * profileData.moveSpeed;
}
}
//make rotationYaw be aimed at motion
double angle = Math.toDegrees(Math.atan2(motion.y, motion.x));
if (angle < 0) angle += 360;
pos.rotationYaw = (float) angle;
} else {
//non AI player stuff...
}
//TODO: is this proper ecs design? maybe we should have a subsystem for weapon logic? or just a separate system?
//process weapons - cooldowns all weapons
for (WeaponLocation weapLoc : weapons.listWeaponLocations) {
for (Weapon weapon : weapLoc.listWeapons) {
if (weapon.ticksCooldownCur > 0) {
weapon.ticksCooldownCur--;
}
}
}
/*float drag = 0.15F;
motion.x *= drag;
motion.y *= drag;*/
} else if (data.type == EnumEntityType.PROJECTILE) {
if (health.lifeTime > 100) {
//TODO: RELOCATE TO PROPER CLEANUP METHOD
killEntity(data.levelID, e);
}
}
//physics
pos.prevX = pos.x;
pos.prevY = pos.y;
//process specific entity collision before applying motion to pos
//List<CollisionEntry> listCollisions = lookupEntIDToCollisionList.get(e.getId());
//if (listCollisions != null) {
boolean spreadOutNPCs = true;
if (spreadOutNPCs) {
Vector2f pushVec = new Vector2f();
float maxAxisSpeed = 3;
float maxPushForce = 3;
for (int entry : physics.listEntitiesCollidingWith) {
int entIDTarget = entry;
/*if (entry.entIDA == e.getId()) {
entIDTarget = entry.entIDB;
} else {
entIDTarget = entry.entIDA;
}*/
Entity entHit = level.getWorld().getEntity(entIDTarget);
if (entHit != null) {
EntityData dataHit = mapData.get(entHit);
Position posB = mapPos.get(entHit);
if (dataHit != null && data.type == EnumEntityType.SPRITE) {
if (dataHit.type == EnumEntityType.SPRITE) {
Vector2f futurePos = new Vector2f(pos.x + motion.x, pos.y + motion.y);
Vector2f pushA = VecUtil.getTargetVector(pos.toVec(), posB.toVec());
//Vector2f pushB = VecUtil.getTargetVector(posB.toVec(), pos.toVec());
float pushDistA = VecUtil.getDist(pos.toVec(), posB.toVec());
//float pushDistB = VecUtil.getDist(posB.toVec(), pos.toVec());
float cusionSize = Cst.COLLIDESIZE_SPRITE + 1;
float pushForce = 8.5F / (cusionSize / (cusionSize - pushDistA));
if (pushForce > maxPushForce) {
pushForce = maxPushForce;
}
try {
//player?
if (data.getAgent() == null || dataHit.getAgent() == null) continue;
//float distA = VecUtil.getDist(pos.toVec(), data.getAgent().getBlackboard().getPosTarget());
//float distB = VecUtil.getDist(posB.toVec(), dataHit.getAgent().getBlackboard().getPosTarget());
float distNow = VecUtil.getDist(pos.toVec(), posB.toVec());
float distFuture = VecUtil.getDist(futurePos, posB.toVec());
if (Float.isNaN(pushA.x)) {
pushVec.x += rand.nextFloat() * pushForce;
} else {
pushVec.x += -pushA.x * pushForce;
}
if (Float.isNaN(pushA.y)) {
pushVec.y += rand.nextFloat() * pushForce;
} else {
pushVec.y += -pushA.y * pushForce;
}
float speedReduce = (rand.nextFloat() * 0.5F) + 0.5F;
speedReduce = (rand.nextFloat() * 1F);
speedReduce = 0.001F;
float speedIncrease = 0.8F;
//speedReduce = 1F;
//motion.x += pushVec.x;
//motion.y += pushVec.y;
//Logger.dbg("processing collision, distA: " + distA + ", distB: " + distB);
/*if (e.getId() > entIDTarget && level.getPlayerEntity().getId() != e.getId()) {
motion.x *= speedReduce;
motion.y *= speedReduce;
break;
}*/
if (distFuture < distNow) {
/*motion.x *= speedReduce;
motion.y *= speedReduce;
break;*/
} else {
/*motion.x /= speedIncrease;
motion.y /= speedIncrease;
break;*/
}
/*if (distA < distB) {
motion.x /= speedReduce;
motion.y /= speedReduce;
break;
} else if (distA > distB) {
motion.x *= speedReduce;
motion.y *= speedReduce;
break;
}*//* else if (e.getId() > entIDTarget && level.getPlayerEntity().getId() != e.getId()) {
motion.x *= speedReduce;
motion.y *= speedReduce;
break;
}*/
} catch (Exception e2) {
e2.printStackTrace();
}
} else if (dataHit.type == EnumEntityType.PROJECTILE) {
ProjectileData prjData = mapProjectile.get(entHit);
Health prjHealth = mapHealth.get(entHit);
health.hp -= prjData.prjDamage;
//healthB.hp -= projDataA.prjDamage;
prjHealth.hp = 0;
GameAssetManager.instance().getSound("hit")
.play(GameSettings.vol);
}
}
}
}
if (pushVec.x > maxAxisSpeed) {
pushVec.x = maxAxisSpeed;
} else if (pushVec.x < -maxAxisSpeed) {
pushVec.x = -maxAxisSpeed;
}
if (pushVec.y > maxAxisSpeed) {
pushVec.y = maxAxisSpeed;
} else if (pushVec.y < -maxAxisSpeed) {
pushVec.y = -maxAxisSpeed;
}
if (pushVec != null) {
motion.x += pushVec.x;
motion.y += pushVec.y;
//break;
}
}
//}
/*motion.x *= 0.98F;
motion.y *= 0.98F;*/
//temp code \\
/*float maxAxisSpeed = 7;
if (motion.x > maxAxisSpeed) {
motion.x = maxAxisSpeed;
} else if (motion.x < -maxAxisSpeed) {
motion.x = -maxAxisSpeed;
}
if (motion.y > maxAxisSpeed) {
motion.y = maxAxisSpeed;
} else if (motion.y < -maxAxisSpeed) {
motion.y = -maxAxisSpeed;
}*/
//temp code //
int fPosX = (int) (pos.x + motion.x);
int fPosY = (int) (pos.y + motion.y);
//int fTileX = MathUtil.floorF((float)fPosX / (float)Cst.TILESIZE);
//int fTileY = MathUtil.floorF((float)fPosY / (float)Cst.TILESIZE);
Vector4f vec = level.getCellBorder(fPosX, (int) pos.y);
//Logger.dbg("x: " + fPosX + " - y: " + fPosY + " vs " + vec + " passable: " + level.isPassable(fPosX, fPosY));
//Logger.dbg("real pos: " + fPosX + " - " + fPosY + " tile pos: " + fTileX + " - " + fTileY);
boolean collide = false;
boolean bounce = false;
if (!level.isPassable(fPosX, (int) pos.y) && motion.x < 0 && fPosX < vec.x + Cst.TILESIZE) {
//System.out.println("adjust -x!");
fPosX = (int) (vec.x + Cst.TILESIZE) + 1;
if (bounce) {
motion.x *= -1;
} else {
motion.x = 0;
}
collide = true;
}
vec = level.getCellBorder(fPosX, (int) pos.y);
if (!level.isPassable(fPosX, (int) pos.y) && motion.x > 0 && fPosX > vec.x) {
//System.out.println("adjust +x!");
fPosX = (int) (vec.x) - 1;
if (!collide) {
if (bounce) {
motion.x *= -1;
} else {
motion.x = 0;
}
}
collide = true;
}
vec = level.getCellBorder((int) pos.x, fPosY);
if (!level.isPassable((int) pos.x, fPosY) && motion.y < 0 && fPosY < vec.y + Cst.TILESIZE) {
//System.out.println("adjust -y!");
fPosY = (int) (vec.y + Cst.TILESIZE) + 1;
if (!collide) {
if (bounce) {
motion.y *= -1;
} else {
motion.y = 0;
}
}
collide = true;
}
vec = level.getCellBorder((int) pos.x, fPosY);
if (!level.isPassable((int) pos.x, fPosY) && motion.y > 0 && fPosY > vec.y) {
//System.out.println("adjust +y!");
fPosY = (int) (vec.y) - 1;
if (!collide) {
if (bounce) {
motion.y *= -1;
} else {
motion.y = 0;
}
}
collide = true;
}
//left
/*if (fPosX < vec.x + Cst.TILESIZE) {
System.out.println("adjust!");
fPosX = (int) (vec.x + Cst.TILESIZE) + 1;
} else if (fPosX > vec.x) {
System.out.println("adjust2!");
fPosX = (int) (vec.x) - 1;
}*/
if (collide && data.type == EnumEntityType.PROJECTILE) {
health.hp = 0;
}
vec = level.getCellBorder(fPosX, fPosY);
if (!level.isPassable(fPosX, fPosY)) {
//up
/*if (fPosY > vec.y + Cst.TILESIZE) {
System.out.println("adjust!");
fPosY = (int) (vec.y + Cst.TILESIZE);
}*/
}
pos.x = fPosX;
pos.y = fPosY;
health.lifeTime++;
//possibly relocate this code to physicswrapper system
if (physics.body != null) {
physics.body.setTransform(pos.x, pos.y, 0);
physics.body.applyForceToCenter(0, 0, true);
}
/*if (pos.x < 0) {
pos.setPos(0, pos.y);
}
if (pos.x > level.getLevelSizeX()) {
pos.setPos(level.getLevelSizeX(), pos.y);
}
if (pos.y < 0) {
pos.setPos(pos.x, 0);
}
if (pos.y > level.getLevelSizeY()) {
pos.setPos(pos.x, level.getLevelSizeY());
}*/
/*if (pos.x < 0 || pos.x > level.getLevelSizeX() || pos.y < 0 || pos.y > level.getLevelSizeY()) {
//System.out.println("killed out of bound entity");
level.killEntity(e);
}*/
if (health.isDead()) {
//System.out.println("killed entity");
killEntity(data.levelID, e);
}
}
public void killEntity(int levelID, Entity e) {
Level level = Game_AI_TestBed.instance().getLevel(levelID);
if (e.getId() == level.getPlayerEntity().getId()) {
level.respawnPlayer();
} else {
PhysicsData phys = mapPhysics.get(e);
// phys.dispose(levelID);
PhysicsWrapper.pendRemoveBody(phys);
level.killEntity(e);
}
}
@Override
protected void processSystem() {
super.processSystem();
}
public void triggerCollisionEvent(int entIDA, int entIDB) {
addToLookup(entIDA, entIDB);
addToLookup(entIDB, entIDA);
}
public void addToLookup(int id, int idHit) {
// TODO: WE ARE ASSUMING LEVEL 0 HERE, FIX!!!
Level level = Game_AI_TestBed.instance().getLevel(0);
Entity entA = level.getWorld().getEntity(id);
Entity entB = level.getWorld().getEntity(idHit);
if (entA != null && entB != null) {
PhysicsData dataA = entA.getComponent(PhysicsData.class);
PhysicsData dataB = entB.getComponent(PhysicsData.class);
if (dataA != null) dataA.addCollision(idHit);
if (dataB != null) dataB.addCollision(id);
}
}
public void removeFromLookup(int id, int idHit) {
// TODO: WE ARE ASSUMING LEVEL 0 HERE, FIX!!!
Level level = Game_AI_TestBed.instance().getLevel(0);
Entity entA = level.getWorld().getEntity(id);
Entity entB = level.getWorld().getEntity(idHit);
if (entA != null && entB != null) {
PhysicsData dataA = entA.getComponent(PhysicsData.class);
PhysicsData dataB = entB.getComponent(PhysicsData.class);
if (dataA != null) dataA.removeCollision(idHit);
if (dataB != null) dataB.removeCollision(id);
}
}
public void triggerCollisionEndEvent(int entIDA, int entIDB) {
removeFromLookup(entIDA, entIDB);
removeFromLookup(entIDB, entIDA);
}
}