package net.mostlyoriginal.ns2d.system.active; import com.artemis.Aspect; import com.artemis.ComponentMapper; import com.artemis.Entity; import com.artemis.annotations.Wire; import com.artemis.managers.GroupManager; import com.artemis.managers.TagManager; import com.artemis.systems.EntityProcessingSystem; import com.artemis.utils.ImmutableBag; import com.badlogic.gdx.math.MathUtils; import net.mostlyoriginal.ns2d.component.*; import net.mostlyoriginal.ns2d.util.EntityUtil; /** * @author Daan van Yperen */ @Wire public class SkulkControlSystem extends EntityProcessingSystem { public static final int APPROACH_RANGE = 32; public static final int MIN_LEAP_DISTANCE = 100; private ComponentMapper<WallSensor> wm; private ComponentMapper<Gravity> gm; private ComponentMapper<Physics> ym; private ComponentMapper<Pos> pm; private ComponentMapper<Anim> am; private ComponentMapper<Focus> fm; private ComponentMapper<Weapon> weam; private ComponentMapper<Aim> a2m; private ComponentMapper<Inventory> im; private ComponentMapper<SkulkControlled> com; private TagManager tagManager; private AimSystem aimSystem; private PhysicsSystem physicsSystem; private GroupManager groupManager; public Entity player; public Pos enemyPos; private CombatSystem combatSystem; private ComponentMapper<Buildable> bm; private ComponentMapper<Health> hm; public ImmutableBag<Entity> playerFriends; public SkulkControlSystem() { super(Aspect.getAspectForAll(SkulkControlled.class, WallSensor.class, Anim.class, Physics.class, Focus.class)); } @Override protected void initialize() { super.initialize(); playerFriends = groupManager.getEntities("player-friend"); } @Override protected void begin() { super.begin(); } private Entity determineFocus(Entity skulk) { final Focus focus = fm.get(skulk); float closestDistance = -1; // lose interest in things without health. if (focus.entity != null && !hm.has(focus.entity)) { focus.entity = null; } if (focus.entity != null) { closestDistance = EntityUtil.distance2(skulk, focus.entity); } // check all valid victims. for (int i = 0; playerFriends.size() > i; i++) { final Entity b = playerFriends.get(i); // we don't care about targets without health. if (b == null || !hm.has(b)) continue; final float distance = EntityUtil.distance2(skulk, b); if (closestDistance == -1 || distance < closestDistance) { focus.entity = b; closestDistance = distance; } } return focus.entity; } @Override protected void process(Entity skulk) { SkulkControlled controlled = com.get(skulk); controlled.leapCooldown -= world.delta; Entity focus = determineFocus(skulk); if (focus != null) { stalk(skulk, focus); } else { wander(skulk); } } private void wander(Entity skulk) { final WallSensor sensor = wm.get(skulk); final Physics physics = ym.get(skulk); final Pos skulkPos = pm.get(skulk); float tx = skulkPos.x; float ty = skulkPos.y; aimHeadAtFocus(skulk, null); if (sensor.onVerticalSurface) { ty += 100; // randomly leap SkulkControlled controlled = com.get(skulk); if (controlled.leapCooldown <= 0) { leapTowards(skulk, MathUtils.random(-360, 360), MathUtils.random(100, 600)); } } if (sensor.onHorizontalSurface) { tx += 100; } walkTowards(sensor, physics, tx, ty); } private void stalk(Entity skulk, Entity focus) { enemyPos = pm.get(focus); aimHeadAtFocus(skulk, focus); final WallSensor sensor = wm.get(skulk); final Physics physics = ym.get(skulk); final Pos skulkPos = pm.get(skulk); // 1. crawl along the surface of wherever they are, in the direction of the focus. // 2. Gravity gravity = gm.get(skulk); gravity.enabled = !sensor.onVerticalSurface && !sensor.onHorizontalSurface; float closestEnemyApproach = com.get(skulk).closestEnemyApproach; float enemyDirX = enemyPos.x - skulkPos.x; float enemyDirY = enemyPos.y - skulkPos.y; float enemyDistance = EntityUtil.distance(skulk, focus); boolean tooClose = false; if ( enemyDistance <= closestEnemyApproach * 0.8f ) { // run away! enemyDirX = -enemyDirX; enemyDirY = -enemyDirY; tooClose = true; } else if ( enemyDistance <= closestEnemyApproach ) { // nice distance, we're fine. return; } SkulkControlled controlled = com.get(skulk); if ( !tooClose && com.get(skulk).canLeap && (sensor.onAnySurface() && controlled.leapCooldown <= 0)) { float direction = EntityUtil.angle(skulk, focus) + MathUtils.random(-10f, 10f); leapTowards(skulk, direction, enemyDistance); } else if (sensor.onAnySurface()) { walkTowards(sensor, physics, enemyDirX, enemyDirY); } } private void leapTowards(Entity skulk, float direction, float distance) { // aim and fire! SkulkControlled controlled = com.get(skulk); controlled.leapCooldown = MathUtils.random(1f, 1.5f); physicsSystem.push(skulk, direction, 3 * MathUtils.clamp(distance, 100, 250)); final WallSensor sensor = wm.get(skulk); physicsSystem.push(skulk, sensor.wallAngle - 180, 100); } private void walkTowards(WallSensor sensor, Physics physics, float enemyDirX, float enemyDirY) { float dx = 0; float dy = 0; if (enemyDirX < -APPROACH_RANGE && sensor.onHorizontalSurface) dx = -1; if (enemyDirX > APPROACH_RANGE && sensor.onHorizontalSurface) dx = 1; if (enemyDirY < 0 && sensor.onVerticalSurface) dy = -1; if (enemyDirY > 0 && sensor.onVerticalSurface) dy = 1; physics.vx = dx * 100; if (dy != 0) physics.vy = dy * 100; } private void aimHeadAtFocus(Entity creature, Entity focus) { if (im.has(creature)) { Inventory inventory = im.get(creature); if (inventory.weapon != null && inventory.weapon.isActive()) { Aim aim = a2m.get(inventory.weapon); aim.at = focus; weam.get(inventory.weapon).firing = (focus != null); } } else if ( weam.has(creature)) { Aim aim = a2m.get(creature); aim.at = focus; weam.get(creature).firing = (focus != null); } } }