Added Android automatic shooting

This commit is contained in:
Anuken
2018-06-01 18:23:45 -04:00
parent 1bc4b16d38
commit be8632d94b
23 changed files with 416 additions and 167 deletions
@@ -30,6 +30,11 @@ public interface BlockBuilder {
/**Returns the queue for storing build requests.*/
Queue<BuildRequest> getPlaceQueue();
/**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){
return getPlaceQueue().size != 0;
}
/**If a place request matching this signature is present, it is removed.
* Otherwise, a new place request is added to the queue.*/
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
+140 -57
View File
@@ -7,6 +7,7 @@ import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pools;
import com.badlogic.gdx.utils.Queue;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.content.Weapons;
import io.anuke.mindustry.content.fx.ExplosionFx;
@@ -14,16 +15,14 @@ import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.effect.DamageArea;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Trail;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.Floor;
import io.anuke.mindustry.world.blocks.types.storage.CoreBlock.CoreEntity;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
@@ -38,10 +37,14 @@ import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.*;
public class Player extends Unit implements BlockBuilder {
private static final float speed = 1.1f;
private static final float walkSpeed = 1.1f;
private static final float flySpeed = 0.4f;
private static final float flyMaxSpeed = 3f;
private static final float dashSpeed = 1.8f;
private static final Vector2 movement = new Vector2();
//region instance variables, constructor
public String name = "name";
public String uuid;
public boolean isAdmin;
@@ -51,17 +54,16 @@ public class Player extends Unit implements BlockBuilder {
public Weapon weapon = Weapons.blaster;
public Mech mech = Mechs.standard;
public float targetAngle = 0f;
public boolean dashing = false;
public int clientid = -1;
public int playerIndex = 0;
public boolean isLocal = false;
public Timer timer = new Timer(4);
public Targetable target;
private boolean respawning;
private float walktime;
private Queue<BuildRequest> placeQueue = new Queue<>();
private Trail trail = new Trail(16);
public Player(){
hitbox.setSize(5);
@@ -71,11 +73,25 @@ public class Player extends Unit implements BlockBuilder {
heal();
}
//endregion
//region unit and event overrides, utility methods
@Override
public float getArmor() {
return 0f;
}
@Override
public boolean acceptsAmmo(Item item) {
return weapon.getAmmoType(item) != null && inventory.canAcceptAmmo(weapon.getAmmoType(item));
}
@Override
public void addAmmo(Item item) {
inventory.addAmmo(weapon.getAmmoType(item));
}
@Override
public void onRemoteShoot(BulletType type, float x, float y, float rotation, short data) {
Weapon weapon = Upgrade.getByID(Bits.getLeftByte(data));
@@ -131,14 +147,33 @@ public class Player extends Unit implements BlockBuilder {
}
@Override
public void onRemoteDeath(){
public void onRemoteDeath() {
dead = true;
respawning = false;
Effects.effect(ExplosionFx.explosion, this);
Effects.shake(4f, 5f, this);
Effects.sound("die", this);
}
@Override
public Player set(float x, float y){
this.x = x;
this.y = y;
if(isFlying() && isLocal){
Core.camera.position.set(x, y, 0f);
}
return this;
}
@Override
public Player add(){
return add(playerGroup);
}
//endregion
//region draw methods
@Override
public void drawSmooth(){
if((debug && (!showPlayer || !showUI)) || dead) return;
@@ -219,6 +254,8 @@ public class Player extends Unit implements BlockBuilder {
if(!isShooting() && getCurrentRequest() != null && !dead) {
drawBuilding(this);
}
trail.draw(Palette.lighterOrange, Palette.lightishOrange, 5f);
}
public void drawName(){
@@ -284,7 +321,11 @@ public class Player extends Unit implements BlockBuilder {
Draw.reset();
}
//endregion
//region update methods
@Override
public void update(){
hitTime = Math.max(0f, hitTime - Timers.delta());
@@ -309,6 +350,11 @@ public class Player extends Unit implements BlockBuilder {
updateMech();
}
float wobblyness = 0.6f;
trail.update(x + Angles.trnsx(rotation + 180f, 6f) + Mathf.range(wobblyness),
y + Angles.trnsy(rotation + 180f, 6f) + Mathf.range(wobblyness));
if(!isShooting()) {
updateBuilding(this);
}
@@ -317,26 +363,6 @@ public class Player extends Unit implements BlockBuilder {
y = Mathf.clamp(y, 0, world.height() * tilesize);
}
/**Resets all values of the player.*/
public void reset(){
weapon = Weapons.blaster;
team = Team.blue;
inventory.clear();
upgrades.clear();
placeQueue.clear();
add();
heal();
}
public boolean isShooting(){
return control.input(playerIndex).canShoot() && control.input(playerIndex).isShooting() && inventory.hasAmmo();
}
public Queue<BuildRequest> getPlaceQueue(){
return placeQueue;
}
protected void updateMech(){
Tile tile = world.tileWorld(x, y);
@@ -347,9 +373,7 @@ public class Player extends Unit implements BlockBuilder {
if(ui.chatfrag.chatOpen()) return;
dashing = Inputs.keyDown("dash");
float speed = dashing ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.speed ;
float speed = Inputs.keyDown("dash") ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.walkSpeed;
float carrySlowdown = 0.3f;
@@ -370,8 +394,12 @@ public class Player extends Unit implements BlockBuilder {
boolean shooting = isShooting();
if(shooting){
weapon.update(this, true);
weapon.update(this, false);
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
float vx = vec.x, vy = vec.y;
weapon.update(this, true, vx, vy);
weapon.update(this, false, vx, vy);
}
movement.limit(speed);
@@ -396,32 +424,83 @@ public class Player extends Unit implements BlockBuilder {
}
protected void updateFlying(){
rotation = Mathf.slerpDelta(rotation, targetAngle, 0.1f);
}
@Override
public Player set(float x, float y){
this.x = x;
this.y = y;
if(mobile && !isLocal){
Core.camera.position.set(x, y, 0f);
if(Units.invalidateTarget(target, this)){
target = null;
}
float targetX = Core.camera.position.x, targetY = Core.camera.position.y;
float attractDst = 15f;
movement.set(targetX - x, targetY - y).limit(flySpeed);
movement.setAngle(Mathf.slerpDelta(movement.angle(), velocity.angle(), 0.05f));
if(distanceTo(targetX, targetY) < attractDst){
movement.setZero();
}
velocity.add(movement);
updateVelocityStatus(0.1f, flyMaxSpeed);
//hovering effect
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.08f);
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.08f);
if(velocity.len() <= 0.2f){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 1f);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), velocity.len()/10f);
}
//update shooting if not building and there's ammo left
if(!isBuilding() && inventory.hasAmmo()){
//autofire: mobile only!
if(mobile) {
if (target == null) {
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else {
//rotate toward and shoot the target
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
Vector2 intercept =
Predict.intercept(x, y, target.getX(), target.getY(), target.getVelocity().x - velocity.x, target.getVelocity().y - velocity.y, inventory.getAmmo().bullet.speed);
weapon.update(this, true, intercept.x, intercept.y);
weapon.update(this, false, intercept.x, intercept.y);
}
}else if(isShooting()){ //desktop shooting, TODO
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
float vx = vec.x, vy = vec.y;
weapon.update(this, true, vx, vy);
weapon.update(this, false, vx, vy);
}
}
return this;
}
@Override
public boolean acceptsAmmo(Item item) {
return weapon.getAmmoType(item) != null && inventory.canAcceptAmmo(weapon.getAmmoType(item));
//endregion
//region utility methods
/**Resets all values of the player.*/
public void reset(){
weapon = Weapons.blaster;
team = Team.blue;
inventory.clear();
upgrades.clear();
placeQueue.clear();
add();
heal();
}
@Override
public void addAmmo(Item item) {
inventory.addAmmo(weapon.getAmmoType(item));
public boolean isShooting(){
return control.input(playerIndex).canShoot() && control.input(playerIndex).isShooting() && inventory.hasAmmo();
}
@Override
public Player add(){
return add(playerGroup);
public Queue<BuildRequest> getPlaceQueue(){
return placeQueue;
}
@Override
@@ -429,6 +508,10 @@ public class Player extends Unit implements BlockBuilder {
return "Player{" + id + ", mech=" + mech.name + ", local=" + isLocal + ", " + x + ", " + y + "}\n";
}
//endregion
//region read and write methods
@Override
public void writeSave(DataOutputStream stream) throws IOException {
stream.writeBoolean(isLocal);
@@ -502,7 +585,6 @@ public class Player extends Unit implements BlockBuilder {
data.putFloat(rotation);
data.putFloat(baseRotation);
data.putShort((short)health);
data.put((byte)(dashing ? 1 : 0));
}
@Override
@@ -515,8 +597,9 @@ public class Player extends Unit implements BlockBuilder {
byte dashing = data.get();
this.health = health;
this.dashing = dashing == 1;
interpolator.read(this.x, this.y, x, y, rot, baseRot, time);
}
//endregion
}
@@ -8,8 +8,15 @@ public class Predict {
private static Vector2 vec = new Vector2();
private static Vector2 vresult = new Vector2();
/**Returns resulting predicted vector.
* Don't call from multiple threads.*/
/**Calculates of intercept of a stationary and moving target. Do not call from multiple threads!
* @param srcx X of shooter
* @param srcy Y of shooter
* @param dstx X of target
* @param dsty Y of target
* @param dstvx X velocity of target (subtract shooter X velocity if needed)
* @param dstvy Y velocity of target (subtract shooter Y velocity if needed)
* @param v speed of bullet
* @return the intercept location*/
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v) {
float tx = dstx - srcx,
ty = dsty - srcy,
@@ -0,0 +1,18 @@
package io.anuke.mindustry.entities;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.game.Team;
import io.anuke.ucore.util.Position;
/**Base interface for targetable entities.*/
public interface Targetable extends Position{
boolean isDead();
Team getTeam();
Vector2 getVelocity();
/**Whether this entity is a valid target.*/
default boolean isValid(){
return !isDead();
}
}
@@ -1,7 +1,9 @@
package io.anuke.mindustry.entities;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.mindustry.world.Block;
@@ -23,19 +25,19 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.tileGroup;
import static io.anuke.mindustry.Vars.world;
public class TileEntity extends Entity{
public class TileEntity extends Entity implements Targetable{
public static final float timeToSleep = 60f*4; //4 seconds to fall asleep
public static int sleepingEntities = 0;
public Tile tile;
public Timer timer;
public float health;
public boolean dead = false;
public PowerModule power;
public InventoryModule items;
public LiquidModule liquids;
private boolean dead = false;
private boolean sleeping;
private float sleepTime;
@@ -82,7 +84,11 @@ public class TileEntity extends Entity{
sleepingEntities --;
}
}
public boolean isDead() {
return dead;
}
public void write(DataOutputStream stream) throws IOException{}
public void read(DataInputStream stream) throws IOException{}
@@ -128,7 +134,17 @@ public class TileEntity extends Entity{
NetEvents.handleBlockDamaged(this);
}
}
@Override
public Team getTeam() {
return tile.getTeam();
}
@Override
public Vector2 getVelocity() {
return Vector2.Zero;
}
@Override
public void update(){
synchronized (Tile.tileSetLock) {
@@ -21,7 +21,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world;
public abstract class Unit extends SyncEntity implements SerializableEntity {
public abstract class Unit extends SyncEntity implements SerializableEntity, Targetable {
/**total duration of hit flash effect*/
public static final float hitDuration = 9f;
@@ -175,6 +175,11 @@ public abstract class Unit extends SyncEntity implements SerializableEntity {
}
}
@Override
public Vector2 getVelocity() {
return velocity;
}
public void drawUnder(){}
public void drawOver(){}
@@ -53,6 +53,11 @@ public class UnitInventory {
}
}
/**Returns ammo range, or MAX_VALUE if this inventory has no ammo.*/
public float getAmmoRange(){
return hasAmmo() ? getAmmo().getRange() : Float.MAX_VALUE;
}
public AmmoType getAmmo() {
return ammos.size == 0 ? null : ammos.peek().type;
}
@@ -20,6 +20,36 @@ import static io.anuke.mindustry.Vars.*;
public class Units {
private static Rectangle rect = new Rectangle();
/**Validates a target.
* @param target The target to validate
* @param team The team of the thing doing tha targeting
* @param x The X position of the thing doign the targeting
* @param y The Y position of the thing doign the targeting
* @param range The maximum distance from the target X/Y the targeter can be for it to be valid
* @return whether the target is invalid
*/
public static boolean invalidateTarget(Targetable target, Team team, float x, float y, float range){
if(target == null){
return false;
}
if(range != Float.MAX_VALUE && target.distanceTo(x, y) > range){
return false;
}
return target.getTeam() == team || !target.isValid();
}
/**See {@link #invalidateTarget(Targetable, Team, float, float, float)}*/
public static boolean invalidateTarget(Targetable target, Team team, float x, float y){
return invalidateTarget(target, team, x, y, Float.MAX_VALUE);
}
/**See {@link #invalidateTarget(Targetable, Team, float, float, float)}*/
public static boolean invalidateTarget(Targetable target, Unit targeter){
return invalidateTarget(target, targeter.team, targeter.x, targeter.y, targeter.inventory.getAmmoRange());
}
/**Returns whether there are any entities on this tile.*/
public static boolean anyEntities(Tile tile){
Block type = tile.block();
@@ -95,6 +125,16 @@ public class Units {
}
}
/**Returns the closest target enemy. First, units are checked, then tile entities.*/
public static Targetable getClosestTarget(Team team, float x, float y, float range){
Unit unit = getClosestEnemy(team, x, y, range, u -> true);
if(unit != null){
return unit;
}else{
return findEnemyTile(team, x, y, range, tile -> true);
}
}
/**Returns the closest enemy of this team. Filter by predicate.*/
public static Unit getClosestEnemy(Team team, float x, float y, float range, Predicate<Unit> predicate){
Unit[] result = {null};
@@ -103,7 +143,7 @@ public class Units {
rect.setSize(range*2f).setCenter(x, y);
getNearbyEnemies(team, rect, e -> {
if (!predicate.test(e))
if (e.isDead() || !predicate.test(e))
return;
float dist = Vector2.dst(e.x, e.y, x, y);
@@ -95,7 +95,7 @@ public class Bullet extends BulletEntity<BulletType>{
if (tile == null) return false;
tile = tile.target();
if (tile.entity != null && tile.entity.collide(this) && !tile.entity.dead && tile.entity.tile.getTeam() != team) {
if (tile.entity != null && tile.entity.collide(this) && !tile.entity.isDead() && tile.entity.tile.getTeam() != team) {
tile.entity.collision(this);
remove();
type.hit(this);
@@ -1,15 +1,15 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.Targetable;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.BlockFlag;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer;
@@ -26,7 +26,7 @@ public class BaseUnit extends Unit{
public Timer timer = new Timer(5);
public float walkTime = 0f;
public StateMachine state = new StateMachine();
public Entity target;
public Targetable target;
public BaseUnit(UnitType type, Team team){
this.type = type;
@@ -1,6 +1,5 @@
package io.anuke.mindustry.entities.units;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.type.AmmoType;
@@ -59,7 +58,7 @@ public class FlyingUnitType extends UnitType {
}
protected void circle(BaseUnit unit, float circleLength){
vec.set(unit.target.x - unit.x, unit.target.y - unit.y);
vec.set(unit.target.getX() - unit.x, unit.target.getY() - unit.y);
if(vec.len() < circleLength){
vec.rotate((circleLength-vec.len())/circleLength * 180f);
@@ -71,7 +70,7 @@ public class FlyingUnitType extends UnitType {
}
protected void attack(BaseUnit unit, float circleLength){
vec.set(unit.target.x - unit.x, unit.target.y - unit.y);
vec.set(unit.target.getX() - unit.x, unit.target.getY() - unit.y);
float ang = unit.angleTo(unit.target);
float diff = Angles.angleDist(ang, unit.rotation);
@@ -113,8 +112,7 @@ public class FlyingUnitType extends UnitType {
}
public void update(BaseUnit unit) {
if(unit.target != null && (unit.target instanceof TileEntity &&
(((TileEntity)unit.target).tile.getTeam() == unit.team || !((TileEntity)unit.target).tile.breakable()))){
if(Units.invalidateTarget(unit.target, unit.team, unit.x, unit.y)){
unit.target = null;
}
@@ -89,8 +89,7 @@ public abstract class GroundUnitType extends UnitType{
public void updateTargeting(BaseUnit unit) {
super.updateTargeting(unit);
if(unit.target != null && unit.inventory.hasAmmo() &&
!(unit.target instanceof TileEntity) && unit.target.distanceTo(unit) > unit.inventory.getAmmo().getRange()){
if(Units.invalidateTarget(unit.target, unit)){
unit.target = null;
}
}
@@ -69,7 +69,7 @@ public class Drone extends FlyingUnitType {
Shapes.laser("beam", "beam-end",
unit.x + Angles.trnsx(unit.rotation, len),
unit.y + Angles.trnsy(unit.rotation, len),
unit.target.x, unit.target.y);
unit.target.getX(), unit.target.getY());
Draw.color();
}
}