it is done
This commit is contained in:
13
core/src/mindustry/entities/traits/AbsorbTrait.java
Normal file
13
core/src/mindustry/entities/traits/AbsorbTrait.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
public interface AbsorbTrait extends Entity, TeamTrait, DamageTrait{
|
||||
void absorb();
|
||||
|
||||
default boolean canBeAbsorbed(){
|
||||
return true;
|
||||
}
|
||||
|
||||
default float getShieldDamage(){
|
||||
return damage();
|
||||
}
|
||||
}
|
||||
7
core/src/mindustry/entities/traits/BelowLiquidTrait.java
Normal file
7
core/src/mindustry/entities/traits/BelowLiquidTrait.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
/**
|
||||
* A flag interface for marking an effect as appearing below liquids.
|
||||
*/
|
||||
public interface BelowLiquidTrait{
|
||||
}
|
||||
22
core/src/mindustry/entities/traits/BuilderMinerTrait.java
Normal file
22
core/src/mindustry/entities/traits/BuilderMinerTrait.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
/** A class for gracefully merging mining and building traits.*/
|
||||
public interface BuilderMinerTrait extends MinerTrait, BuilderTrait{
|
||||
|
||||
default void updateMechanics(){
|
||||
updateBuilding();
|
||||
|
||||
//mine only when not building
|
||||
if(buildRequest() == null){
|
||||
updateMining();
|
||||
}
|
||||
}
|
||||
|
||||
default void drawMechanics(){
|
||||
if(isBuilding()){
|
||||
drawBuilding();
|
||||
}else{
|
||||
drawMining();
|
||||
}
|
||||
}
|
||||
}
|
||||
394
core/src/mindustry/entities/traits/BuilderTrait.java
Normal file
394
core/src/mindustry/entities/traits/BuilderTrait.java
Normal file
@@ -0,0 +1,394 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.Queue;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.BuildBlock.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.entities.traits.BuilderTrait.BuildDataStatic.*;
|
||||
|
||||
/** Interface for units that build things.*/
|
||||
public interface BuilderTrait extends Entity, TeamTrait{
|
||||
//these are not instance variables!
|
||||
float placeDistance = 220f;
|
||||
float mineDistance = 70f;
|
||||
|
||||
/** Updates building mechanism for this unit.*/
|
||||
default void updateBuilding(){
|
||||
float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : placeDistance;
|
||||
Unit unit = (Unit)this;
|
||||
|
||||
Iterator<BuildRequest> it = buildQueue().iterator();
|
||||
while(it.hasNext()){
|
||||
BuildRequest req = it.next();
|
||||
Tile tile = world.tile(req.x, req.y);
|
||||
if(tile == null || (req.breaking && tile.block() == Blocks.air) || (!req.breaking && (tile.rotation() == req.rotation || !req.block.rotate) && tile.block() == req.block)){
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
TileEntity core = unit.getClosestCore();
|
||||
|
||||
//nothing to build.
|
||||
if(buildRequest() == null) return;
|
||||
|
||||
//find the next build request
|
||||
if(buildQueue().size > 1){
|
||||
int total = 0;
|
||||
BuildRequest req;
|
||||
while((dst((req = buildRequest()).tile()) > finalPlaceDst || shouldSkip(req, core)) && total < buildQueue().size){
|
||||
buildQueue().removeFirst();
|
||||
buildQueue().addLast(req);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
BuildRequest current = buildRequest();
|
||||
|
||||
if(dst(current.tile()) > finalPlaceDst) return;
|
||||
|
||||
Tile tile = world.tile(current.x, current.y);
|
||||
|
||||
if(!(tile.block() instanceof BuildBlock)){
|
||||
if(!current.initialized && canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){
|
||||
Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation);
|
||||
}else if(!current.initialized && canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
|
||||
Call.beginBreak(getTeam(), current.x, current.y);
|
||||
}else{
|
||||
buildQueue().removeFirst();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.entity instanceof BuildEntity && !current.initialized){
|
||||
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking)));
|
||||
current.initialized = true;
|
||||
}
|
||||
|
||||
//if there is no core to build with or no build entity, stop building!
|
||||
if((core == null && !state.rules.infiniteResources) || !(tile.entity instanceof BuildEntity)){
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise, update it.
|
||||
BuildEntity entity = tile.ent();
|
||||
|
||||
if(entity == null){
|
||||
return;
|
||||
}
|
||||
|
||||
if(unit.dst(tile) <= finalPlaceDst){
|
||||
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
|
||||
}
|
||||
|
||||
if(current.breaking){
|
||||
entity.deconstruct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||
}else{
|
||||
if(entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier, current.hasConfig)){
|
||||
if(current.hasConfig){
|
||||
Call.onTileConfig(null, tile, current.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current.stuck = Mathf.equal(current.progress, entity.progress);
|
||||
current.progress = entity.progress;
|
||||
}
|
||||
|
||||
/** @return whether this request should be skipped, in favor of the next one. */
|
||||
default boolean shouldSkip(BuildRequest request, @Nullable TileEntity core){
|
||||
//requests that you have at least *started* are considered
|
||||
if(state.rules.infiniteResources || request.breaking || !request.initialized || core == null) return false;
|
||||
return request.stuck && !core.items.has(request.block.requirements);
|
||||
}
|
||||
|
||||
/** Returns the queue for storing build requests. */
|
||||
Queue<BuildRequest> buildQueue();
|
||||
|
||||
/** Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all. */
|
||||
float getBuildPower(Tile tile);
|
||||
|
||||
/** Whether this type of builder can begin creating new blocks. */
|
||||
default boolean canCreateBlocks(){
|
||||
return true;
|
||||
}
|
||||
|
||||
default void writeBuilding(DataOutput output) throws IOException{
|
||||
BuildRequest request = buildRequest();
|
||||
|
||||
if(request != null && (request.block != null || request.breaking)){
|
||||
output.writeByte(request.breaking ? 1 : 0);
|
||||
output.writeInt(Pos.get(request.x, request.y));
|
||||
output.writeFloat(request.progress);
|
||||
if(!request.breaking){
|
||||
output.writeShort(request.block.id);
|
||||
output.writeByte(request.rotation);
|
||||
}
|
||||
}else{
|
||||
output.writeByte(-1);
|
||||
}
|
||||
}
|
||||
|
||||
default void readBuilding(DataInput input) throws IOException{
|
||||
readBuilding(input, true);
|
||||
}
|
||||
|
||||
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
|
||||
if(applyChanges) buildQueue().clear();
|
||||
|
||||
byte type = input.readByte();
|
||||
if(type != -1){
|
||||
int position = input.readInt();
|
||||
float progress = input.readFloat();
|
||||
BuildRequest request;
|
||||
|
||||
if(type == 1){ //remove
|
||||
request = new BuildRequest(Pos.x(position), Pos.y(position));
|
||||
}else{ //place
|
||||
short block = input.readShort();
|
||||
byte rotation = input.readByte();
|
||||
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
|
||||
}
|
||||
|
||||
request.progress = progress;
|
||||
|
||||
if(applyChanges){
|
||||
buildQueue().addLast(request);
|
||||
}else if(isBuilding()){
|
||||
BuildRequest last = buildRequest();
|
||||
last.progress = progress;
|
||||
if(last.tile() != null && last.tile().entity instanceof BuildEntity){
|
||||
((BuildEntity)last.tile().entity).progress = progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Return whether this builder's place queue contains items. */
|
||||
default boolean isBuilding(){
|
||||
return buildQueue().size != 0;
|
||||
}
|
||||
|
||||
/** Clears the placement queue. */
|
||||
default void clearBuilding(){
|
||||
buildQueue().clear();
|
||||
}
|
||||
|
||||
/** Add another build requests to the tail of the queue, if it doesn't exist there yet. */
|
||||
default void addBuildRequest(BuildRequest place){
|
||||
addBuildRequest(place, true);
|
||||
}
|
||||
|
||||
/** Add another build requests to the queue, if it doesn't exist there yet. */
|
||||
default void addBuildRequest(BuildRequest place, boolean tail){
|
||||
BuildRequest replace = null;
|
||||
for(BuildRequest request : buildQueue()){
|
||||
if(request.x == place.x && request.y == place.y){
|
||||
replace = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(replace != null){
|
||||
buildQueue().remove(replace);
|
||||
}
|
||||
Tile tile = world.tile(place.x, place.y);
|
||||
if(tile != null && tile.entity instanceof BuildEntity){
|
||||
place.progress = tile.<BuildEntity>ent().progress;
|
||||
}
|
||||
if(tail){
|
||||
buildQueue().addLast(place);
|
||||
}else{
|
||||
buildQueue().addFirst(place);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the build requests currently active, or the one at the top of the queue.
|
||||
* May return null.
|
||||
*/
|
||||
default @Nullable
|
||||
BuildRequest buildRequest(){
|
||||
return buildQueue().size == 0 ? null : buildQueue().first();
|
||||
}
|
||||
|
||||
//due to iOS weirdness, this is apparently required
|
||||
class BuildDataStatic{
|
||||
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
|
||||
}
|
||||
|
||||
/** Draw placement effects for an entity. */
|
||||
default void drawBuilding(){
|
||||
if(!isBuilding()) return;
|
||||
|
||||
Unit unit = (Unit)this;
|
||||
BuildRequest request = buildRequest();
|
||||
Tile tile = world.tile(request.x, request.y);
|
||||
|
||||
if(dst(tile) > placeDistance && !state.isEditor()){
|
||||
return;
|
||||
}
|
||||
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
float focusLen = 3.8f + Mathf.absin(Time.time(), 1.1f, 0.6f);
|
||||
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
|
||||
float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
|
||||
|
||||
float sz = Vars.tilesize * tile.block().size / 2f;
|
||||
float ang = unit.angleTo(tile);
|
||||
|
||||
tmptr[0].set(tile.drawx() - sz, tile.drawy() - sz);
|
||||
tmptr[1].set(tile.drawx() + sz, tile.drawy() - sz);
|
||||
tmptr[2].set(tile.drawx() - sz, tile.drawy() + sz);
|
||||
tmptr[3].set(tile.drawx() + sz, tile.drawy() + sz);
|
||||
|
||||
Arrays.sort(tmptr, (a, b) -> -Float.compare(Angles.angleDist(Angles.angle(unit.x, unit.y, a.x, a.y), ang),
|
||||
Angles.angleDist(Angles.angle(unit.x, unit.y, b.x, b.y), ang)));
|
||||
|
||||
float x1 = tmptr[0].x, y1 = tmptr[0].y,
|
||||
x3 = tmptr[1].x, y3 = tmptr[1].y;
|
||||
|
||||
Draw.alpha(1f);
|
||||
|
||||
Lines.line(px, py, x1, y1);
|
||||
Lines.line(px, py, x3, y3);
|
||||
|
||||
Fill.circle(px, py, 1.6f + Mathf.absin(Time.time(), 0.8f, 1.5f));
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
/** Class for storing build requests. Can be either a place or remove request. */
|
||||
class BuildRequest{
|
||||
/** Position and rotation of this request. */
|
||||
public int x, y, rotation;
|
||||
/** Block being placed. If null, this is a breaking request.*/
|
||||
public @Nullable Block block;
|
||||
/** Whether this is a break request.*/
|
||||
public boolean breaking;
|
||||
/** Whether this request comes with a config int. If yes, any blocks placed with this request will not call playerPlaced.*/
|
||||
public boolean hasConfig;
|
||||
/** Config int. Not used unless hasConfig is true.*/
|
||||
public int config;
|
||||
/** Original position, only used in schematics.*/
|
||||
public int originalX, originalY, originalWidth, originalHeight;
|
||||
|
||||
/** Last progress.*/
|
||||
public float progress;
|
||||
/** Whether construction has started for this request, and other special variables.*/
|
||||
public boolean initialized, worldContext = true, stuck;
|
||||
|
||||
/** Visual scale. Used only for rendering.*/
|
||||
public float animScale = 0f;
|
||||
|
||||
/** This creates a build request. */
|
||||
public BuildRequest(int x, int y, int rotation, Block block){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = rotation;
|
||||
this.block = block;
|
||||
this.breaking = false;
|
||||
}
|
||||
|
||||
/** This creates a remove request. */
|
||||
public BuildRequest(int x, int y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = -1;
|
||||
this.block = world.tile(x, y).block();
|
||||
this.breaking = true;
|
||||
}
|
||||
|
||||
public BuildRequest(){
|
||||
|
||||
}
|
||||
|
||||
public BuildRequest copy(){
|
||||
BuildRequest copy = new BuildRequest();
|
||||
copy.x = x;
|
||||
copy.y = y;
|
||||
copy.rotation = rotation;
|
||||
copy.block = block;
|
||||
copy.breaking = breaking;
|
||||
copy.hasConfig = hasConfig;
|
||||
copy.config = config;
|
||||
copy.originalX = originalX;
|
||||
copy.originalY = originalY;
|
||||
copy.progress = progress;
|
||||
copy.initialized = initialized;
|
||||
copy.animScale = animScale;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public BuildRequest original(int x, int y, int originalWidth, int originalHeight){
|
||||
originalX = x;
|
||||
originalY = y;
|
||||
this.originalWidth = originalWidth;
|
||||
this.originalHeight = originalHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rectangle bounds(Rectangle rect){
|
||||
if(breaking){
|
||||
return rect.set(-100f, -100f, 0f, 0f);
|
||||
}else{
|
||||
return block.bounds(x, y, rect);
|
||||
}
|
||||
}
|
||||
|
||||
public BuildRequest set(int x, int y, int rotation, Block block){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.rotation = rotation;
|
||||
this.block = block;
|
||||
this.breaking = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float drawx(){
|
||||
return x*tilesize + block.offset();
|
||||
}
|
||||
|
||||
public float drawy(){
|
||||
return y*tilesize + block.offset();
|
||||
}
|
||||
|
||||
public BuildRequest configure(int config){
|
||||
this.config = config;
|
||||
this.hasConfig = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable Tile tile(){
|
||||
return world.tile(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "BuildRequest{" +
|
||||
"x=" + x +
|
||||
", y=" + y +
|
||||
", rotation=" + rotation +
|
||||
", recipe=" + block +
|
||||
", breaking=" + breaking +
|
||||
", progress=" + progress +
|
||||
", initialized=" + initialized +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
9
core/src/mindustry/entities/traits/DamageTrait.java
Normal file
9
core/src/mindustry/entities/traits/DamageTrait.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
public interface DamageTrait{
|
||||
float damage();
|
||||
|
||||
default void killed(Entity other){
|
||||
|
||||
}
|
||||
}
|
||||
10
core/src/mindustry/entities/traits/DrawTrait.java
Normal file
10
core/src/mindustry/entities/traits/DrawTrait.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
public interface DrawTrait extends Entity{
|
||||
|
||||
default float drawSize(){
|
||||
return 20f;
|
||||
}
|
||||
|
||||
void draw();
|
||||
}
|
||||
42
core/src/mindustry/entities/traits/Entity.java
Normal file
42
core/src/mindustry/entities/traits/Entity.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import mindustry.entities.EntityGroup;
|
||||
|
||||
public interface Entity extends MoveTrait{
|
||||
|
||||
int getID();
|
||||
|
||||
void resetID(int id);
|
||||
|
||||
default void update(){}
|
||||
|
||||
default void removed(){}
|
||||
|
||||
default void added(){}
|
||||
|
||||
EntityGroup targetGroup();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default void add(){
|
||||
if(targetGroup() != null){
|
||||
targetGroup().add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default void remove(){
|
||||
if(getGroup() != null){
|
||||
getGroup().remove(this);
|
||||
}
|
||||
|
||||
setGroup(null);
|
||||
}
|
||||
|
||||
EntityGroup getGroup();
|
||||
|
||||
void setGroup(EntityGroup group);
|
||||
|
||||
default boolean isAdded(){
|
||||
return getGroup() != null;
|
||||
}
|
||||
}
|
||||
52
core/src/mindustry/entities/traits/HealthTrait.java
Normal file
52
core/src/mindustry/entities/traits/HealthTrait.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.Mathf;
|
||||
|
||||
public interface HealthTrait{
|
||||
|
||||
void health(float health);
|
||||
|
||||
float health();
|
||||
|
||||
float maxHealth();
|
||||
|
||||
boolean isDead();
|
||||
|
||||
void setDead(boolean dead);
|
||||
|
||||
default void onHit(SolidTrait entity){
|
||||
}
|
||||
|
||||
default void onDeath(){
|
||||
}
|
||||
|
||||
default boolean damaged(){
|
||||
return health() < maxHealth() - 0.0001f;
|
||||
}
|
||||
|
||||
default void damage(float amount){
|
||||
health(health() - amount);
|
||||
if(health() <= 0 && !isDead()){
|
||||
onDeath();
|
||||
setDead(true);
|
||||
}
|
||||
}
|
||||
|
||||
default void clampHealth(){
|
||||
health(Mathf.clamp(health(), 0, maxHealth()));
|
||||
}
|
||||
|
||||
default float healthf(){
|
||||
return health() / maxHealth();
|
||||
}
|
||||
|
||||
default void healBy(float amount){
|
||||
health(health() + amount);
|
||||
clampHealth();
|
||||
}
|
||||
|
||||
default void heal(){
|
||||
health(maxHealth());
|
||||
setDead(false);
|
||||
}
|
||||
}
|
||||
5
core/src/mindustry/entities/traits/KillerTrait.java
Normal file
5
core/src/mindustry/entities/traits/KillerTrait.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
public interface KillerTrait{
|
||||
void killed(Entity other);
|
||||
}
|
||||
102
core/src/mindustry/entities/traits/MinerTrait.java
Normal file
102
core/src/mindustry/entities/traits/MinerTrait.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.Time;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.Call;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.Item;
|
||||
import mindustry.world.Tile;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public interface MinerTrait extends Entity{
|
||||
|
||||
/** Returns the range at which this miner can mine blocks.*/
|
||||
default float getMiningRange(){
|
||||
return 70f;
|
||||
}
|
||||
|
||||
default boolean isMining(){
|
||||
return getMineTile() != null;
|
||||
}
|
||||
|
||||
/** Returns the tile this builder is currently mining. */
|
||||
Tile getMineTile();
|
||||
|
||||
/** Sets the tile this builder is currently mining. */
|
||||
void setMineTile(Tile tile);
|
||||
|
||||
/** Returns the mining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc. */
|
||||
float getMinePower();
|
||||
|
||||
/** Returns whether or not this builder can mine a specific item type. */
|
||||
boolean canMine(Item item);
|
||||
|
||||
default void updateMining(){
|
||||
Unit unit = (Unit)this;
|
||||
Tile tile = getMineTile();
|
||||
TileEntity core = unit.getClosestCore();
|
||||
|
||||
if(tile == null || core == null || tile.block() != Blocks.air || dst(tile.worldx(), tile.worldy()) > getMiningRange()
|
||||
|| tile.drop() == null || !unit.acceptsItem(tile.drop()) || !canMine(tile.drop())){
|
||||
setMineTile(null);
|
||||
}else{
|
||||
Item item = tile.drop();
|
||||
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(tile.worldx(), tile.worldy()), 0.4f);
|
||||
|
||||
if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
|
||||
|
||||
if(unit.dst(core) < mineTransferRange && core.tile.block().acceptStack(item, 1, core.tile, unit) == 1){
|
||||
Call.transferItemTo(item, 1,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f), core.tile);
|
||||
}else if(unit.acceptsItem(item)){
|
||||
Call.transferItemToUnit(item,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f),
|
||||
unit);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.06 * Time.delta())){
|
||||
Effects.effect(Fx.pulverizeSmall,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void drawMining(){
|
||||
Unit unit = (Unit)this;
|
||||
Tile tile = getMineTile();
|
||||
|
||||
if(tile == null) return;
|
||||
|
||||
float focusLen = 4f + Mathf.absin(Time.time(), 1.1f, 0.5f);
|
||||
float swingScl = 12f, swingMag = tilesize / 8f;
|
||||
float flashScl = 0.3f;
|
||||
|
||||
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
|
||||
float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
|
||||
|
||||
float ex = tile.worldx() + Mathf.sin(Time.time() + 48, swingScl, swingMag);
|
||||
float ey = tile.worldy() + Mathf.sin(Time.time() + 48, swingScl + 2f, swingMag);
|
||||
|
||||
Draw.color(Color.lightGray, Color.white, 1f - flashScl + Mathf.absin(Time.time(), 0.5f, flashScl));
|
||||
|
||||
Drawf.laser(Core.atlas.find("minelaser"), Core.atlas.find("minelaser-end"), px, py, ex, ey, 0.75f);
|
||||
|
||||
if(unit instanceof Player && ((Player)unit).isLocal){
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
Lines.poly(tile.worldx(), tile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time());
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
20
core/src/mindustry/entities/traits/MoveTrait.java
Normal file
20
core/src/mindustry/entities/traits/MoveTrait.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.geom.Position;
|
||||
|
||||
public interface MoveTrait extends Position{
|
||||
|
||||
void setX(float x);
|
||||
|
||||
void setY(float y);
|
||||
|
||||
default void moveBy(float x, float y){
|
||||
setX(getX() + x);
|
||||
setY(getY() + y);
|
||||
}
|
||||
|
||||
default void set(float x, float y){
|
||||
setX(x);
|
||||
setY(y);
|
||||
}
|
||||
}
|
||||
8
core/src/mindustry/entities/traits/SaveTrait.java
Normal file
8
core/src/mindustry/entities/traits/SaveTrait.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
/**
|
||||
* Marks an entity as serializable.
|
||||
*/
|
||||
public interface SaveTrait extends Entity, TypeTrait, Saveable{
|
||||
byte version();
|
||||
}
|
||||
8
core/src/mindustry/entities/traits/Saveable.java
Normal file
8
core/src/mindustry/entities/traits/Saveable.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface Saveable{
|
||||
void writeSave(DataOutput stream) throws IOException;
|
||||
void readSave(DataInput stream, byte version) throws IOException;
|
||||
}
|
||||
43
core/src/mindustry/entities/traits/ScaleTrait.java
Normal file
43
core/src/mindustry/entities/traits/ScaleTrait.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.Interpolation;
|
||||
|
||||
public interface ScaleTrait{
|
||||
/** 0 to 1. */
|
||||
float fin();
|
||||
|
||||
/** 1 to 0 */
|
||||
default float fout(){
|
||||
return 1f - fin();
|
||||
}
|
||||
|
||||
/** 1 to 0 */
|
||||
default float fout(Interpolation i){
|
||||
return i.apply(fout());
|
||||
}
|
||||
|
||||
/** 1 to 0, ending at the specified margin */
|
||||
default float fout(float margin){
|
||||
float f = fin();
|
||||
if(f >= 1f - margin){
|
||||
return 1f - (f - (1f - margin)) / margin;
|
||||
}else{
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
/** 0 to 1 **/
|
||||
default float fin(Interpolation i){
|
||||
return i.apply(fin());
|
||||
}
|
||||
|
||||
/** 0 to 1 */
|
||||
default float finpow(){
|
||||
return Interpolation.pow3Out.apply(fin());
|
||||
}
|
||||
|
||||
/** 0 to 1 to 0 */
|
||||
default float fslope(){
|
||||
return (0.5f - Math.abs(fin() - 0.5f)) * 2f;
|
||||
}
|
||||
}
|
||||
13
core/src/mindustry/entities/traits/ShooterTrait.java
Normal file
13
core/src/mindustry/entities/traits/ShooterTrait.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.util.Interval;
|
||||
import mindustry.type.Weapon;
|
||||
|
||||
public interface ShooterTrait extends VelocityTrait, TeamTrait{
|
||||
|
||||
Interval getTimer();
|
||||
|
||||
int getShootTimer(boolean left);
|
||||
|
||||
Weapon getWeapon();
|
||||
}
|
||||
38
core/src/mindustry/entities/traits/SolidTrait.java
Normal file
38
core/src/mindustry/entities/traits/SolidTrait.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.math.geom.QuadTree.QuadTreeObject;
|
||||
import mindustry.Vars;
|
||||
|
||||
public interface SolidTrait extends QuadTreeObject, MoveTrait, VelocityTrait, Entity, Position{
|
||||
|
||||
void hitbox(Rectangle rectangle);
|
||||
|
||||
void hitboxTile(Rectangle rectangle);
|
||||
|
||||
Vector2 lastPosition();
|
||||
|
||||
default boolean collidesGrid(int x, int y){
|
||||
return true;
|
||||
}
|
||||
|
||||
default float getDeltaX(){
|
||||
return getX() - lastPosition().x;
|
||||
}
|
||||
|
||||
default float getDeltaY(){
|
||||
return getY() - lastPosition().y;
|
||||
}
|
||||
|
||||
default boolean collides(SolidTrait other){
|
||||
return true;
|
||||
}
|
||||
|
||||
default void collision(SolidTrait other, float x, float y){
|
||||
}
|
||||
|
||||
default void move(float x, float y){
|
||||
Vars.collisions.move(this, x, y);
|
||||
}
|
||||
}
|
||||
18
core/src/mindustry/entities/traits/SpawnerTrait.java
Normal file
18
core/src/mindustry/entities/traits/SpawnerTrait.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.geom.Position;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.world.Tile;
|
||||
|
||||
public interface SpawnerTrait extends TargetTrait, Position{
|
||||
Tile getTile();
|
||||
|
||||
void updateSpawning(Player unit);
|
||||
|
||||
boolean hasUnit(Unit unit);
|
||||
|
||||
@Override
|
||||
default boolean isValid(){
|
||||
return getTile().entity instanceof SpawnerTrait;
|
||||
}
|
||||
}
|
||||
48
core/src/mindustry/entities/traits/SyncTrait.java
Normal file
48
core/src/mindustry/entities/traits/SyncTrait.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import mindustry.net.Interpolator;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface SyncTrait extends Entity, TypeTrait{
|
||||
|
||||
/** Sets the position of this entity and updated the interpolator. */
|
||||
default void setNet(float x, float y){
|
||||
set(x, y);
|
||||
|
||||
if(getInterpolator() != null){
|
||||
getInterpolator().target.set(x, y);
|
||||
getInterpolator().last.set(x, y);
|
||||
getInterpolator().pos.set(0, 0);
|
||||
getInterpolator().updateSpacing = 16;
|
||||
getInterpolator().lastUpdated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Interpolate entity position only. Override if you need to interpolate rotations or other values. */
|
||||
default void interpolate(){
|
||||
if(getInterpolator() == null){
|
||||
throw new RuntimeException("This entity must have an interpolator to interpolate()!");
|
||||
}
|
||||
|
||||
getInterpolator().update();
|
||||
|
||||
setX(getInterpolator().pos.x);
|
||||
setY(getInterpolator().pos.y);
|
||||
}
|
||||
|
||||
/** Return the interpolator used for smoothing the position. Optional. */
|
||||
default Interpolator getInterpolator(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Whether syncing is enabled for this entity; true by default. */
|
||||
default boolean isSyncing(){
|
||||
return true;
|
||||
}
|
||||
|
||||
//Read and write sync data, usually position
|
||||
void write(DataOutput data) throws IOException;
|
||||
|
||||
void read(DataInput data) throws IOException;
|
||||
}
|
||||
35
core/src/mindustry/entities/traits/TargetTrait.java
Normal file
35
core/src/mindustry/entities/traits/TargetTrait.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.geom.Position;
|
||||
import mindustry.game.Team;
|
||||
|
||||
/**
|
||||
* Base interface for targetable entities.
|
||||
*/
|
||||
public interface TargetTrait extends Position, VelocityTrait{
|
||||
|
||||
boolean isDead();
|
||||
|
||||
Team getTeam();
|
||||
|
||||
default float getTargetVelocityX(){
|
||||
if(this instanceof SolidTrait){
|
||||
return ((SolidTrait)this).getDeltaX();
|
||||
}
|
||||
return velocity().x;
|
||||
}
|
||||
|
||||
default float getTargetVelocityY(){
|
||||
if(this instanceof SolidTrait){
|
||||
return ((SolidTrait)this).getDeltaY();
|
||||
}
|
||||
return velocity().y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this entity is a valid target.
|
||||
*/
|
||||
default boolean isValid(){
|
||||
return !isDead();
|
||||
}
|
||||
}
|
||||
7
core/src/mindustry/entities/traits/TeamTrait.java
Normal file
7
core/src/mindustry/entities/traits/TeamTrait.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import mindustry.game.Team;
|
||||
|
||||
public interface TeamTrait extends Entity{
|
||||
Team getTeam();
|
||||
}
|
||||
23
core/src/mindustry/entities/traits/TimeTrait.java
Normal file
23
core/src/mindustry/entities/traits/TimeTrait.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.Mathf;
|
||||
import arc.util.Time;
|
||||
|
||||
public interface TimeTrait extends ScaleTrait, Entity{
|
||||
|
||||
float lifetime();
|
||||
|
||||
void time(float time);
|
||||
|
||||
float time();
|
||||
|
||||
default void updateTime(){
|
||||
time(Mathf.clamp(time() + Time.delta(), 0, lifetime()));
|
||||
|
||||
if(time() >= lifetime()){
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
//fin() is not implemented due to compiler issues with iOS/RoboVM
|
||||
}
|
||||
45
core/src/mindustry/entities/traits/TypeTrait.java
Normal file
45
core/src/mindustry/entities/traits/TypeTrait.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import mindustry.type.TypeID;
|
||||
|
||||
public interface TypeTrait{
|
||||
|
||||
TypeID getTypeID();
|
||||
/*
|
||||
int[] lastRegisteredID = {0};
|
||||
Array<Supplier<? extends TypeTrait>> registeredTypes = new Array<>();
|
||||
ObjectIntMap<Class<? extends TypeTrait>> typeToID = new ObjectIntMap<>();
|
||||
|
||||
/**
|
||||
* Register and return a type ID. The supplier should return a fresh instace of that type.
|
||||
|
||||
static <T extends TypeTrait> void registerType(Class<T> type, Supplier<T> supplier){
|
||||
if(typeToID.get(type, -1) != -1){
|
||||
return; //already registered
|
||||
}
|
||||
|
||||
registeredTypes.add(supplier);
|
||||
int result = lastRegisteredID[0];
|
||||
typeToID.put(type, result);
|
||||
lastRegisteredID[0]++;
|
||||
}
|
||||
|
||||
/**Gets a syncable type by ID.
|
||||
static Supplier<? extends TypeTrait> getTypeByID(int id){
|
||||
if(id == -1){
|
||||
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
|
||||
}
|
||||
return registeredTypes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.
|
||||
* Do not override!
|
||||
|
||||
default int getTypeID(){
|
||||
int id = typeToID.get(getClass(), -1);
|
||||
if(id == -1)
|
||||
throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?");
|
||||
return id;
|
||||
}*/
|
||||
}
|
||||
36
core/src/mindustry/entities/traits/VelocityTrait.java
Normal file
36
core/src/mindustry/entities/traits/VelocityTrait.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.math.geom.Vector2;
|
||||
import arc.util.Time;
|
||||
|
||||
public interface VelocityTrait extends MoveTrait{
|
||||
|
||||
Vector2 velocity();
|
||||
|
||||
default void applyImpulse(float x, float y){
|
||||
velocity().x += x / mass();
|
||||
velocity().y += y / mass();
|
||||
}
|
||||
|
||||
default float maxVelocity(){
|
||||
return Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
default float mass(){
|
||||
return 1f;
|
||||
}
|
||||
|
||||
default float drag(){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
default void updateVelocity(){
|
||||
velocity().scl(1f - drag() * Time.delta());
|
||||
|
||||
if(this instanceof SolidTrait){
|
||||
((SolidTrait)this).move(velocity().x * Time.delta(), velocity().y * Time.delta());
|
||||
}else{
|
||||
moveBy(velocity().x * Time.delta(), velocity().y * Time.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user