Internal refactoring & cleanup

This commit is contained in:
Anuken
2020-09-22 15:50:33 -04:00
parent 0256a475cf
commit f3e08f9cb6
32 changed files with 168 additions and 135 deletions

View File

@@ -74,15 +74,12 @@ public class GroundAI extends AIController{
}*/
}
protected void moveTo(int pathType){
int costType =
unit instanceof Legsc ? Pathfinder.costLegs :
unit instanceof WaterMovec ? Pathfinder.costWater :
Pathfinder.costGround;
protected void moveTo(int pathTarget){
int costType = unit.pathType();
Tile tile = unit.tileOn();
if(tile == null) return;
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathType));
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathTarget));
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;

View File

@@ -14,7 +14,6 @@ public class TeamIndexProcess implements AsyncProcess{
private QuadTree<Unit>[] trees = new QuadTree[Team.all.length];
private int[] counts = new int[Team.all.length];
private int[][] typeCounts = new int[Team.all.length][0];
private int[][] activeCounts = new int[Team.all.length][0];
public QuadTree<Unit> tree(Team team){
if(trees[team.id] == null) trees[team.id] = new QuadTree<>(Vars.world.getQuadBounds(new Rect()));
@@ -30,10 +29,6 @@ public class TeamIndexProcess implements AsyncProcess{
return typeCounts[team.id].length <= type.id ? 0 : typeCounts[team.id][type.id];
}
public int countActive(Team team, UnitType type){
return activeCounts[team.id].length <= type.id ? 0 : activeCounts[team.id][type.id];
}
public void updateCount(Team team, UnitType type, int amount){
counts[team.id] += amount;
if(typeCounts[team.id].length <= type.id){
@@ -42,16 +37,8 @@ public class TeamIndexProcess implements AsyncProcess{
typeCounts[team.id][type.id] += amount;
}
public void updateActiveCount(Team team, UnitType type, int amount){
if(activeCounts[team.id].length <= type.id){
activeCounts[team.id] = new int[Vars.content.units().size];
}
activeCounts[team.id][type.id] += amount;
}
private void count(Unit unit){
updateCount(unit.team, unit.type(), 1);
if(!unit.deactivated) updateActiveCount(unit.team, unit.type(), 1);
if(unit instanceof Payloadc){
((Payloadc)unit).payloads().each(p -> {
@@ -77,7 +64,6 @@ public class TeamIndexProcess implements AsyncProcess{
}
Arrays.fill(typeCounts[team.id], 0);
Arrays.fill(activeCounts[team.id], 0);
}
Arrays.fill(counts, 0);

View File

@@ -1306,7 +1306,6 @@ public class UnitTypes implements ContentList{
health = 280;
accel = 0.4f;
rotateSpeed = 3.3f;
immunities = ObjectSet.with(StatusEffects.wet);
trailLength = 20;
rotateShooting = false;
@@ -1358,7 +1357,6 @@ public class UnitTypes implements ContentList{
armor = 4f;
accel = 0.3f;
rotateSpeed = 2.6f;
immunities = ObjectSet.with(StatusEffects.wet);
rotateShooting = false;
trailLength = 20;
@@ -1400,7 +1398,6 @@ public class UnitTypes implements ContentList{
drag = 0.17f;
hitsize = 16f;
armor = 7f;
immunities = ObjectSet.with(StatusEffects.wet);
rotateShooting = false;
trailLength = 22;
@@ -1495,7 +1492,6 @@ public class UnitTypes implements ContentList{
hitsize = 39f;
accel = 0.2f;
rotateSpeed = 1.3f;
immunities = ObjectSet.with(StatusEffects.wet);
rotateShooting = false;
trailLength = 50;
@@ -1580,10 +1576,9 @@ public class UnitTypes implements ContentList{
armor = 16f;
accel = 0.19f;
rotateSpeed = 0.9f;
immunities = ObjectSet.with(StatusEffects.wet);
rotateShooting = false;
float spawnTime = 60f * 25f;
float spawnTime = 60f * 15f;
abilities.add(new UnitSpawnAbility(flare, spawnTime, 19.25f, -31.75f), new UnitSpawnAbility(flare, spawnTime, -19.25f, -31.75f));

View File

@@ -24,7 +24,7 @@ public class Units{
if(unit != null){
unit.dead = true;
Fx.unitCapKill.at(unit);
Core.app.post(() -> Call.unitDeath(unit.id));
Core.app.post(() -> Call.unitDestroy(unit.id));
}
}
@@ -42,6 +42,21 @@ public class Units{
}
}
//destroys immediately
@Remote(called = Loc.server)
public static void unitDestroy(int uid){
Unit unit = Groups.unit.getByID(uid);
//if there's no unit don't add it later and get it stuck as a ghost
if(netClient != null){
netClient.addRemovedEntity(uid);
}
if(unit != null){
unit.destroy();
}
}
@Remote(called = Loc.server)
public static void unitDespawn(Unit unit){
Fx.unitDespawn.at(unit.x, unit.y, 0, unit);

View File

@@ -4,6 +4,12 @@ import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
//TODO this class is bad for multiple reasons, remove/replace it.
//- effects unreliable
//- not really hitscan but works like it
//- buggy trails
//- looks bad
//- generally unreliable
public class RailBulletType extends BulletType{
public Effect pierceEffect = Fx.hitBulletSmall, updateEffect = Fx.none;
/** Multiplier of damage decreased per health pierced. */

View File

@@ -1,23 +1,18 @@
package mindustry.entities.comp;
import mindustry.annotations.Annotations.*;
import mindustry.entities.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.gen.*;
import static mindustry.Vars.*;
@Component
abstract class ElevationMoveComp implements Velc, Posc, Flyingc, Hitboxc{
@Import float x, y;
@Replace
@Override
public void move(float cx, float cy){
if(isFlying()){
x += cx;
y += cy;
}else{
collisions.move(this, cx, cy);
}
public SolidPred solidity(){
return isFlying() ? null : EntityCollisions::solid;
}
}

View File

@@ -4,16 +4,16 @@ import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.*;
import mindustry.ai.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*;
@Component
abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{
@Import float x, y;
@@ -26,8 +26,14 @@ abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{
@Replace
@Override
public void move(float cx, float cy){
collisions.moveCheck(this, cx, cy, !type.allowLegStep ? EntityCollisions::solid : EntityCollisions::legsSolid);
public SolidPred solidity(){
return !type.allowLegStep ? EntityCollisions::solid : EntityCollisions::legsSolid;
}
@Override
@Replace
public int pathType(){
return Pathfinder.costLegs;
}
@Override

View File

@@ -1,6 +1,7 @@
package mindustry.entities.comp;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@@ -9,11 +10,23 @@ import mindustry.gen.*;
abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, ElevationMovec{
@SyncField(false) @SyncLocal float baseRotation;
transient float walkTime, walkExtension;
transient private boolean walked;
@Override
public void update(){
float len = deltaLen();
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
walkTime += len;
//trigger animation only when walking manually
if(walked){
float len = deltaLen();
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
walkTime += len;
walked = false;
}
}
@Override
public void moveAt(Vec2 vector, float acceleration){
if(!vector.isZero()){
walked = true;
}
}
}

View File

@@ -98,11 +98,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
Unit u = payload.unit;
//can't drop ground units
//TODO bad code, solidity should not be handled this way
if(
((tileOn() == null || tileOn().solid()) && u.elevation < 0.1f && !u.type().allowLegStep) ||
(!floorOn().isLiquid && u instanceof WaterMovec) ||
(u.type().allowLegStep && EntityCollisions.legsSolid(u.tileX(), u.tileY()))){
if(!u.canPassOn()){
return false;
}

View File

@@ -8,6 +8,7 @@ import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.ai.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.ctype.*;
@@ -37,7 +38,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
private UnitController controller;
private UnitType type;
boolean spawnedByCore, deactivated; //TODO remove deactivation boolean
boolean spawnedByCore;
transient Seq<Ability> abilities = new Seq<>(0);
@@ -103,7 +104,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
@Replace
public boolean canDrown(){
return isGrounded() && !hovering && type.canDrown && !(this instanceof WaterMovec);
return isGrounded() && !hovering && type.canDrown;
}
@Override
@@ -149,6 +150,11 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
return type;
}
/** @return pathfinder path type for calculating costs */
public int pathType(){
return Pathfinder.costGround;
}
public void lookAt(float angle){
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta * speedMultiplier());
}
@@ -206,12 +212,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
public void add(){
//check if over unit cap
if(count() > cap() && !spawnedByCore){
deactivated = true;
if(!dead){
Call.unitCapDeath(self());
}
if(count() > cap() && !spawnedByCore && !dead){
Call.unitCapDeath(self());
teamIndex.updateCount(team, type, -1);
}
}
@@ -232,22 +235,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
public void update(){
//activate the unit when possible
if(!net.client() && deactivated && teamIndex.countActive(team, type) < Units.getCap(team)){
teamIndex.updateActiveCount(team, type, 1);
deactivated = false;
}
if(!deactivated){
type.update(self());
type.update(self());
if(abilities.size > 0){
for(Ability a : abilities){
a.update(self());
}
if(abilities.size > 0){
for(Ability a : abilities){
a.update(self());
}
}else if(!dead){
Call.unitCapDeath(self());
}
drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f);
@@ -305,18 +299,20 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
if(floor.damageTaken > 0f){
damageContinuous(floor.damageTaken);
}
}
if(tile.solid()){
if(type.canBoost){
elevation = 1f;
}else if(!net.client()){
kill();
}
//kill entities on tiles that are solid to them
if(tile != null && !canPassOn()){
//boost if possible
if(type.canBoost){
elevation = 1f;
}else if(!net.client()){
kill();
}
}
//AI only updates on the server
if(!net.client() && !dead && !deactivated){
if(!net.client() && !dead){
controller.updateUnit();
}
@@ -325,11 +321,6 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
resetController();
}
//do not control anything when deactivated
if(deactivated){
controlWeapons(false, false);
}
//remove units spawned by the core
if(spawnedByCore && !isPlayer()){
Call.unitDespawn(self());

View File

@@ -3,9 +3,13 @@ package mindustry.entities.comp;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.gen.*;
import static mindustry.Vars.*;
@Component
abstract class VelComp implements Posc{
@Import float x, y;
@@ -22,12 +26,35 @@ abstract class VelComp implements Posc{
vel.scl(Mathf.clamp(1f - drag * Time.delta));
}
/** @return function to use for check solid state. if null, no checking is done. */
@Nullable
SolidPred solidity(){
return null;
}
/** @return whether this entity can move through a location*/
boolean canPass(int tileX, int tileY){
SolidPred s = solidity();
return s == null || !s.solid(tileX, tileY);
}
/** @return whether this entity can exist on its current location*/
boolean canPassOn(){
return canPass(tileX(), tileY());
}
boolean moving(){
return !vel.isZero(0.01f);
}
void move(float cx, float cy){
x += cx;
y += cy;
SolidPred check = solidity();
if(check != null){
collisions.move(self(), cx, cy, check);
}else{
x += cx;
y += cy;
}
}
}

View File

@@ -4,17 +4,17 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.util.*;
import mindustry.ai.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*;
//just a proof of concept
@Component
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
@@ -38,16 +38,8 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
@Override
@Replace
public void lookAt(float angle){
if(onLiquid()){
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta);
}
}
@Override
@Replace
public boolean canShoot(){
return onLiquid();
public int pathType(){
return Pathfinder.costWater;
}
@Override
@@ -74,13 +66,8 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
@Replace
@Override
public void move(float cx, float cy){
if(isGrounded()){
collisions.moveCheck(this, cx, cy, EntityCollisions::waterSolid);
}else{
x += cx;
y += cy;
}
public SolidPred solidity(){
return isFlying() ? null : EntityCollisions::waterSolid;
}
@Replace

View File

@@ -596,7 +596,7 @@ public class DesktopInput extends InputHandler{
}
protected void updateMovement(Unit unit){
boolean omni = !(unit instanceof WaterMovec);
boolean omni = unit.type().omniMovement;
boolean ground = unit.isGrounded();
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type().strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);

View File

@@ -292,7 +292,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}else if(unit == null){ //just clear the unit (is this used?)
player.clearUnit();
//make sure it's AI controlled, so players can't overwrite each other
}else if(unit.isAI() && unit.team == player.team() && !unit.deactivated() && !unit.dead){
}else if(unit.isAI() && unit.team == player.team() && !unit.dead){
if(!net.client()){
player.unit(unit);
}
@@ -365,7 +365,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
if(controlledType != null && player.dead()){
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
if(unit != null){
Call.unitControl(player, unit);
@@ -375,7 +375,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public void checkUnit(){
if(controlledType != null){
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
if(unit == null && controlledType == UnitTypes.block){
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
}
@@ -931,7 +931,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
public @Nullable Unit selectedUnit(){
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, u -> u.isAI() && !u.deactivated());
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, u -> u.isAI());
if(unit != null){
unit.hitbox(Tmp.r1);
Tmp.r1.grow(6f);

View File

@@ -804,7 +804,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(type == null) return;
boolean flying = type.flying;
boolean omni = !(unit instanceof WaterMovec);
boolean omni = unit.type().omniMovement;
boolean legs = unit.isGrounded();
boolean allowHealing = type.canHeal;
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&

View File

@@ -55,6 +55,7 @@ public class UnitType extends UnlockableContent{
public float visualElevation = -1f;
public boolean allowLegStep = false;
public boolean hovering = false;
public boolean omniMovement = true;
public Effect fallEffect = Fx.fallSmoke;
public Effect fallThrusterEffect = Fx.fallSmoke;
public Seq<Ability> abilities = new Seq<>();
@@ -195,13 +196,6 @@ public class UnitType extends UnlockableContent{
}).growX();
table.row();
if(unit.deactivated){
table.table(d -> {
d.left();
d.label(() -> Core.bundle.format("bar.limitreached", unit.count(), unit.cap(), Fonts.getUnicodeStr(name)));
}).left().visible(() -> unit.deactivated);
}
}
@Override
@@ -231,6 +225,15 @@ public class UnitType extends UnlockableContent{
public void init(){
if(constructor == null) throw new IllegalArgumentException("no constructor set up for unit '" + name + "'");
Unit example = constructor.get();
//water preset
if(example instanceof WaterMovec){
canDrown = false;
omniMovement = false;
immunities.add(StatusEffects.wet);
}
singleTarget = weapons.size <= 1;
if(itemCapacity < 0){
@@ -397,10 +400,6 @@ public class UnitType extends UnlockableContent{
unit.trns(-legOffset.x, -legOffset.y);
}
if(unit.deactivated){
drawDeactive(unit);
}
if(unit.abilities.size > 0){
for(Ability a : unit.abilities){
Draw.reset();
@@ -411,16 +410,6 @@ public class UnitType extends UnlockableContent{
}
}
public void drawDeactive(Unit unit){
Draw.color(Color.scarlet);
Draw.alpha(0.8f);
float size = 8f;
Draw.rect(Icon.warning.getRegion(), unit.x, unit.y, size, size);
Draw.reset();
}
public <T extends Unit & Payloadc> void drawPayload(T unit){
if(unit.hasPayload()){
Payload pay = unit.payloads().first();

View File

@@ -17,7 +17,7 @@ import mindustry.world.blocks.production.*;
import static mindustry.Vars.*;
public class PayloadConveyor extends Block{
public float moveTime = 50f;
public float moveTime = 40f, moveForce = 201f;
public @Load("@-top") TextureRegion topRegion;
public @Load("@-edge") TextureRegion edgeRegion;
public Interp interp = Interp.pow5;
@@ -53,6 +53,7 @@ public class PayloadConveyor extends Block{
public class PayloadConveyorBuild extends Building{
public @Nullable Payload item;
public float progress, itemRotation, animation;
public float curInterp, lastInterp;
public @Nullable Building next;
public boolean blocked;
public int step = -1, stepAccepted = -1;
@@ -99,6 +100,10 @@ public class PayloadConveyor extends Block{
public void updateTile(){
if(!enabled) return;
lastInterp = curInterp;
curInterp = fract();
//rollover skip
if(lastInterp > curInterp) lastInterp = 0f;
progress = time() % moveTime;
updatePayload();
@@ -201,6 +206,14 @@ public class PayloadConveyor extends Block{
return Time.time();
}
@Override
public void unitOn(Unit unit){
//calculate derivative of units moved last frame
float delta = (curInterp - lastInterp) * size * tilesize;
Tmp.v1.trns(rotdeg(), delta * moveForce).scl(1f / Math.max(unit.mass(), 201f));
unit.move(Tmp.v1.x, Tmp.v1.y);
}
@Override
public boolean acceptPayload(Building source, Payload payload){
if(source == this){

View File

@@ -8,6 +8,7 @@ import arc.util.*;
import arc.util.io.*;
import mindustry.*;
import mindustry.entities.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.ui.*;
@@ -47,15 +48,16 @@ public class UnitPayload implements Payload{
return false;
}
//naval units need water.
if(unit instanceof WaterMovec){
//check if unit can be dumped here
SolidPred solid = unit.solidity();
if(solid != null){
int tx = unit.tileX(), ty = unit.tileY();
boolean nearEmpty = !EntityCollisions.waterSolid(tx, ty);
boolean nearEmpty = !solid.solid(tx, ty);
for(Point2 p : Geometry.d4){
nearEmpty |= !EntityCollisions.waterSolid(tx + p.x, ty + p.y);
nearEmpty |= !solid.solid(tx + p.x, ty + p.y);
}
//cannot dump on dry land
//cannot dump on solid blocks
if(!nearEmpty) return false;
}