Implemented ground mech boosting
This commit is contained in:
36
core/src/mindustry/entities/comp/BoundedComp.java
Normal file
36
core/src/mindustry/entities/comp/BoundedComp.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
|
||||
static final float warpDst = 180f;
|
||||
|
||||
@Import float x, y;
|
||||
@Import Vec2 vel;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//repel unit out of bounds
|
||||
if(x < 0) vel.x += (-x/warpDst);
|
||||
if(y < 0) vel.y += (-y/warpDst);
|
||||
if(x > world.unitWidth()) vel.x -= (x - world.unitWidth())/warpDst;
|
||||
if(y > world.unitHeight()) vel.y -= (y - world.unitHeight())/warpDst;
|
||||
|
||||
//clamp position if not flying
|
||||
if(isGrounded()){
|
||||
x = Mathf.clamp(x, 0, world.width() * tilesize - tilesize);
|
||||
y = Mathf.clamp(y, 0, world.height() * tilesize - tilesize);
|
||||
}
|
||||
|
||||
//kill when out of bounds
|
||||
if(x < -finalWorldBounds || y < -finalWorldBounds || x >= world.width() * tilesize + finalWorldBounds || y >= world.height() * tilesize + finalWorldBounds){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
251
core/src/mindustry/entities/comp/BuilderComp.java
Normal file
251
core/src/mindustry/entities/comp/BuilderComp.java
Normal file
@@ -0,0 +1,251 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.Queue;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.BuildBlock.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class BuilderComp implements Unitc{
|
||||
static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()};
|
||||
|
||||
@Import float x, y, rotation;
|
||||
|
||||
Queue<BuildRequest> requests = new Queue<>();
|
||||
transient float buildSpeed = 1f;
|
||||
transient boolean building = true;
|
||||
|
||||
@Override
|
||||
public void controller(UnitController next){
|
||||
//reset building state so AI controlled units will always start off building
|
||||
building = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(!building) return;
|
||||
|
||||
float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : buildingRange;
|
||||
|
||||
Iterator<BuildRequest> it = requests.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();
|
||||
}
|
||||
}
|
||||
|
||||
Tilec core = closestCore();
|
||||
|
||||
//nothing to build.
|
||||
if(buildRequest() == null) return;
|
||||
|
||||
//find the next build request
|
||||
if(requests.size > 1){
|
||||
int total = 0;
|
||||
BuildRequest req;
|
||||
while((dst((req = buildRequest()).tile()) > finalPlaceDst || shouldSkip(req, core)) && total < requests.size){
|
||||
requests.removeFirst();
|
||||
requests.addLast(req);
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
BuildRequest current = buildRequest();
|
||||
|
||||
if(dst(current.tile()) > finalPlaceDst) return;
|
||||
|
||||
Tile tile = world.tile(current.x, current.y);
|
||||
|
||||
if(dst(tile) <= finalPlaceDst){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(tile), 0.4f);
|
||||
}
|
||||
|
||||
if(!(tile.block() instanceof BuildBlock)){
|
||||
if(!current.initialized && !current.breaking && Build.validPlace(team(), current.x, current.y, current.block, current.rotation)){
|
||||
boolean hasAll = !Structs.contains(current.block.requirements, i -> !core.items().has(i.item));
|
||||
|
||||
if(hasAll || state.rules.infiniteResources){
|
||||
Build.beginPlace(team(), current.x, current.y, current.block, current.rotation);
|
||||
}else{
|
||||
current.stuck = true;
|
||||
}
|
||||
}else if(!current.initialized && current.breaking && Build.validBreak(team(), current.x, current.y)){
|
||||
Build.beginBreak(team(), current.x, current.y);
|
||||
}else{
|
||||
requests.removeFirst();
|
||||
return;
|
||||
}
|
||||
}else if(tile.team() != team()){
|
||||
requests.removeFirst();
|
||||
return;
|
||||
}
|
||||
|
||||
if(tile.entity instanceof BuildEntity && !current.initialized){
|
||||
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, team(), (Builderc)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(current.breaking){
|
||||
entity.deconstruct(this, core, 1f / entity.buildCost * Time.delta() * buildSpeed * state.rules.buildSpeedMultiplier);
|
||||
}else{
|
||||
if(entity.construct(this, core, 1f / entity.buildCost * Time.delta() * buildSpeed * state.rules.buildSpeedMultiplier, current.hasConfig)){
|
||||
if(current.hasConfig){
|
||||
Call.onTileConfig(null, tile.entity, current.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current.stuck = Mathf.equal(current.progress, entity.progress);
|
||||
current.progress = entity.progress;
|
||||
}
|
||||
|
||||
|
||||
/** Draw all current build requests. Does not draw the beam effect, only the positions. */
|
||||
void drawBuildRequests(){
|
||||
|
||||
for(BuildRequest request : requests){
|
||||
if(request.progress > 0.01f || (buildRequest() == request && request.initialized && (dst(request.x * tilesize, request.y * tilesize) <= buildingRange || state.isEditor()))) continue;
|
||||
|
||||
request.animScale = 1f;
|
||||
if(request.breaking){
|
||||
control.input.drawBreaking(request);
|
||||
}else{
|
||||
request.block.drawRequest(request, control.input.allRequests(),
|
||||
Build.validPlace(team(), request.x, request.y, request.block, request.rotation) || control.input.requestMatches(request));
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
/** @return whether this request should be skipped, in favor of the next one. */
|
||||
boolean shouldSkip(BuildRequest request, @Nullable Tilec core){
|
||||
//requests that you have at least *started* are considered
|
||||
if(state.rules.infiniteResources || request.breaking || core == null) return false;
|
||||
//TODO these are bad criteria
|
||||
return (request.stuck && !core.items().has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items().has(i.item)) && !request.initialized);
|
||||
}
|
||||
|
||||
void removeBuild(int x, int y, boolean breaking){
|
||||
//remove matching request
|
||||
int idx = requests.indexOf(req -> req.breaking == breaking && req.x == x && req.y == y);
|
||||
if(idx != -1){
|
||||
requests.removeIndex(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return whether this builder's place queue contains items. */
|
||||
boolean isBuilding(){
|
||||
return requests.size != 0;
|
||||
}
|
||||
|
||||
/** Clears the placement queue. */
|
||||
void clearBuilding(){
|
||||
requests.clear();
|
||||
}
|
||||
|
||||
/** Add another build requests to the tail of the queue, if it doesn't exist there yet. */
|
||||
void addBuild(BuildRequest place){
|
||||
addBuild(place, true);
|
||||
}
|
||||
|
||||
/** Add another build requests to the queue, if it doesn't exist there yet. */
|
||||
void addBuild(BuildRequest place, boolean tail){
|
||||
BuildRequest replace = null;
|
||||
for(BuildRequest request : requests){
|
||||
if(request.x == place.x && request.y == place.y){
|
||||
replace = request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(replace != null){
|
||||
requests.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){
|
||||
requests.addLast(place);
|
||||
}else{
|
||||
requests.addFirst(place);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the build requests currently active, or the one at the top of the queue.*/
|
||||
@Nullable BuildRequest buildRequest(){
|
||||
return requests.size == 0 ? null : requests.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!isBuilding()) return;
|
||||
|
||||
//TODO check correctness
|
||||
Draw.z(Layer.flyingUnit);
|
||||
|
||||
BuildRequest request = buildRequest();
|
||||
Tile tile = world.tile(request.x, request.y);
|
||||
|
||||
if(dst(tile) > buildingRange && !state.isEditor()){
|
||||
return;
|
||||
}
|
||||
|
||||
int size = request.breaking ? tile.block().size : request.block.size;
|
||||
float tx = request.drawx(), ty = request.drawy();
|
||||
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
float focusLen = 3.8f + Mathf.absin(Time.time(), 1.1f, 0.6f);
|
||||
float px = x + Angles.trnsx(rotation, focusLen);
|
||||
float py = y + Angles.trnsy(rotation, focusLen);
|
||||
|
||||
float sz = Vars.tilesize * size / 2f;
|
||||
float ang = angleTo(tx, ty);
|
||||
|
||||
vecs[0].set(tx - sz, ty - sz);
|
||||
vecs[1].set(tx + sz, ty - sz);
|
||||
vecs[2].set(tx - sz, ty + sz);
|
||||
vecs[3].set(tx + sz, ty + sz);
|
||||
|
||||
Arrays.sort(vecs, Structs.comparingFloat(vec -> -Angles.angleDist(angleTo(vec), ang)));
|
||||
|
||||
float x1 = vecs[0].x, y1 = vecs[0].y,
|
||||
x3 = vecs[1].x, y3 = vecs[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();
|
||||
}
|
||||
}
|
||||
153
core/src/mindustry/entities/comp/BulletComp.java
Normal file
153
core/src/mindustry/entities/comp/BulletComp.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Bulletc.class}, pooled = true, serialize = false)
|
||||
@Component
|
||||
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{
|
||||
@Import Team team;
|
||||
|
||||
IntArray collided = new IntArray(6);
|
||||
Object data;
|
||||
BulletType type;
|
||||
float damage;
|
||||
|
||||
@Override
|
||||
public void getCollisions(Cons<QuadTree> consumer){
|
||||
for(Team team : state.teams.enemiesOf(team)){
|
||||
consumer.get(teamIndex.tree(team));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBullets(){
|
||||
type.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
type.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
type.despawned(this);
|
||||
collided.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float damageMultiplier(){
|
||||
if(owner() instanceof Unitc){
|
||||
return ((Unitc)owner()).damageMultiplier();
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void absorb(){
|
||||
//TODO
|
||||
remove();
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return type.drawSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float damage(){
|
||||
return damage * damageMultiplier();
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public boolean collides(Hitboxc other){
|
||||
return type.collides && (other instanceof Teamc && ((Teamc)other).team() != team())
|
||||
&& !(other instanceof Flyingc && ((((Flyingc)other).isFlying() && !type.collidesAir) || (((Flyingc)other).isGrounded() && !type.collidesGround)))
|
||||
&& !(type.pierce && collided.contains(other.id())); //prevent multiple collisions
|
||||
}
|
||||
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void collision(Hitboxc other, float x, float y){
|
||||
type.hit(this, x, y);
|
||||
|
||||
if(other instanceof Healthc){
|
||||
Healthc h = (Healthc)other;
|
||||
h.damage(damage);
|
||||
}
|
||||
|
||||
if(other instanceof Unitc){
|
||||
Unitc unit = (Unitc)other;
|
||||
unit.vel().add(Tmp.v3.set(other.x(), other.y()).sub(x, y).setLength(type.knockback / unit.mass()));
|
||||
unit.apply(type.status, type.statusDuration);
|
||||
}
|
||||
|
||||
//must be last.
|
||||
if(!type.pierce){
|
||||
remove();
|
||||
}else{
|
||||
collided.add(other.id());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
type.update(this);
|
||||
|
||||
if(type.hitTiles){
|
||||
world.raycastEach(world.toTile(lastX()), world.toTile(lastY()), tileX(), tileY(), (x, y) -> {
|
||||
|
||||
Tilec tile = world.ent(x, y);
|
||||
if(tile == null) return false;
|
||||
|
||||
if(tile.collide(this) && type.collides(this, tile) && !tile.dead() && (type.collidesTeam || tile.team() != team())){
|
||||
if(tile.team() != team()){
|
||||
tile.collision(this);
|
||||
}
|
||||
|
||||
type.hitTile(this, tile);
|
||||
remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.bullet);
|
||||
|
||||
type.draw(this);
|
||||
//TODO refactor
|
||||
Drawf.light(x(), y(), 16f, Pal.powerLight, 0.3f);
|
||||
}
|
||||
|
||||
/** Sets the bullet's rotation in degrees. */
|
||||
@Override
|
||||
public void rotation(float angle){
|
||||
vel().setAngle(angle);
|
||||
}
|
||||
|
||||
/** @return the bullet's rotation. */
|
||||
@Override
|
||||
public float rotation(){
|
||||
float angle = Mathf.atan2(vel().x, vel().y) * Mathf.radiansToDegrees;
|
||||
if(angle < 0) angle += 360;
|
||||
return angle;
|
||||
}
|
||||
}
|
||||
29
core/src/mindustry/entities/comp/ChildComp.java
Normal file
29
core/src/mindustry/entities/comp/ChildComp.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class ChildComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
@Nullable Posc parent;
|
||||
float offsetX, offsetY;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
if(parent != null){
|
||||
offsetX = x - parent.getX();
|
||||
offsetY = y - parent.getY();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(parent != null){
|
||||
x = parent.getX() + offsetX;
|
||||
y = parent.getY() + offsetY;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
core/src/mindustry/entities/comp/CommanderComp.java
Normal file
79
core/src/mindustry/entities/comp/CommanderComp.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/** A unit that can command other units. */
|
||||
@Component
|
||||
abstract class CommanderComp implements Unitc{
|
||||
private static final Array<FormationMember> members = new Array<>();
|
||||
|
||||
@Import float x, y, rotation;
|
||||
|
||||
transient @Nullable Formation formation;
|
||||
transient Array<Unitc> controlling = new Array<>();
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(formation != null){
|
||||
formation.anchor.set(x, y, rotation);
|
||||
formation.updateSlots();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
//make sure to reset command state when the controller is switched
|
||||
@Override
|
||||
public void controller(UnitController next){
|
||||
clearCommand();
|
||||
}
|
||||
|
||||
void command(Formation formation, Array<Unitc> units){
|
||||
clearCommand();
|
||||
|
||||
controlling.addAll(units);
|
||||
for(Unitc unit : units){
|
||||
unit.controller(new FormationAI(this, formation));
|
||||
}
|
||||
this.formation = formation;
|
||||
|
||||
members.clear();
|
||||
for(Unitc u : units){
|
||||
members.add((FormationAI)u.controller());
|
||||
}
|
||||
|
||||
|
||||
//TODO doesn't handle units that don't fit a formation
|
||||
formation.addMembers(members);
|
||||
}
|
||||
|
||||
boolean isCommanding(){
|
||||
return formation != null;
|
||||
}
|
||||
|
||||
void clearCommand(){
|
||||
//reset controlled units
|
||||
for(Unitc unit : controlling){
|
||||
if(unit.controller().isBeingControlled(this)){
|
||||
unit.controller(unit.type().createController());
|
||||
}
|
||||
}
|
||||
|
||||
controlling.clear();
|
||||
formation = null;
|
||||
}
|
||||
}
|
||||
8
core/src/mindustry/entities/comp/DamageComp.java
Normal file
8
core/src/mindustry/entities/comp/DamageComp.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
@Component
|
||||
abstract class DamageComp{
|
||||
abstract float damage();
|
||||
}
|
||||
33
core/src/mindustry/entities/comp/DecalComp.java
Normal file
33
core/src/mindustry/entities/comp/DecalComp.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
@EntityDef(value = {Decalc.class}, pooled = true, serialize = false)
|
||||
@Component
|
||||
abstract class DecalComp implements Drawc, Timedc, Rotc, Posc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
Color color = new Color(1, 1, 1, 1);
|
||||
TextureRegion region;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.scorch);
|
||||
|
||||
Draw.color(color);
|
||||
Draw.alpha(1f - Mathf.curve(fin(), 0.98f));
|
||||
Draw.rect(region, x, y, rotation);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return region.getWidth()*2;
|
||||
}
|
||||
|
||||
}
|
||||
16
core/src/mindustry/entities/comp/DrawComp.java
Normal file
16
core/src/mindustry/entities/comp/DrawComp.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class DrawComp implements Posc{
|
||||
|
||||
float clipSize(){
|
||||
return Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
void draw(){
|
||||
|
||||
}
|
||||
}
|
||||
24
core/src/mindustry/entities/comp/EffectComp.java
Normal file
24
core/src/mindustry/entities/comp/EffectComp.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@EntityDef(value = {Effectc.class, Childc.class}, pooled = true, serialize = false)
|
||||
@Component
|
||||
abstract class EffectComp implements Posc, Drawc, Timedc, Rotc, Childc{
|
||||
Color color = new Color(Color.white);
|
||||
Effect effect;
|
||||
Object data;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
effect.render(id(), color, time(), rotation(), x(), y(), data);
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return effect.size;
|
||||
}
|
||||
}
|
||||
22
core/src/mindustry/entities/comp/ElevationMoveComp.java
Normal file
22
core/src/mindustry/entities/comp/ElevationMoveComp.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
core/src/mindustry/entities/comp/EntityComp.java
Normal file
66
core/src/mindustry/entities/comp/EntityComp.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.player;
|
||||
|
||||
@Component
|
||||
@BaseComponent
|
||||
abstract class EntityComp{
|
||||
private transient boolean added;
|
||||
transient int id = EntityGroup.nextId();
|
||||
|
||||
boolean isAdded(){
|
||||
return added;
|
||||
}
|
||||
|
||||
void update(){}
|
||||
|
||||
void remove(){
|
||||
added = false;
|
||||
}
|
||||
|
||||
void add(){
|
||||
added = true;
|
||||
}
|
||||
|
||||
boolean isLocal(){
|
||||
return ((Object)this) == player || ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).controller() == player;
|
||||
}
|
||||
|
||||
boolean isNull(){
|
||||
return false;
|
||||
}
|
||||
|
||||
<T> T as(Class<T> type){
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
<T> T with(Cons<T> cons){
|
||||
cons.get((T)this);
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@InternalImpl
|
||||
abstract int classId();
|
||||
|
||||
@InternalImpl
|
||||
abstract boolean serialize();
|
||||
|
||||
@MethodPriority(1)
|
||||
void read(Reads read){
|
||||
afterRead();
|
||||
}
|
||||
|
||||
void write(Writes write){
|
||||
|
||||
}
|
||||
|
||||
void afterRead(){
|
||||
|
||||
}
|
||||
}
|
||||
102
core/src/mindustry/entities/comp/FireComp.java
Normal file
102
core/src/mindustry/entities/comp/FireComp.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Firec.class}, pooled = true)
|
||||
@Component
|
||||
abstract class FireComp implements Timedc, Posc, Firec{
|
||||
private static final float spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
|
||||
@Import float time, lifetime, x, y;
|
||||
|
||||
Tile tile;
|
||||
private Block block;
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Fx.fire.at(x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.05 * Time.delta())){
|
||||
Fx.fireSmoke.at(x + Mathf.range(4f), y + Mathf.range(4f));
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.001 * Time.delta())){
|
||||
Sounds.fire.at(this);
|
||||
}
|
||||
|
||||
time = Mathf.clamp(time + Time.delta(), 0, lifetime());
|
||||
|
||||
if(Vars.net.client()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(time >= lifetime() || tile == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
Tilec entity = tile.entity;
|
||||
boolean damage = entity != null;
|
||||
|
||||
float flammability = baseFlammability + puddleFlammability;
|
||||
|
||||
if(!damage && flammability <= 0){
|
||||
time += Time.delta() * 8;
|
||||
}
|
||||
|
||||
if(baseFlammability < 0 || block != tile.block()){
|
||||
baseFlammability = tile.entity == null ? 0 : tile.entity.getFlammability();
|
||||
block = tile.block();
|
||||
}
|
||||
|
||||
if(damage){
|
||||
lifetime += Mathf.clamp(flammability / 8f, 0f, 0.6f) * Time.delta();
|
||||
}
|
||||
|
||||
if(flammability > 1f && Mathf.chance(spreadChance * Time.delta() * Mathf.clamp(flammability / 5f, 0.3f, 2f))){
|
||||
Point2 p = Geometry.d4[Mathf.random(3)];
|
||||
Tile other = world.tile(tile.x + p.x, tile.y + p.y);
|
||||
Fires.create(other);
|
||||
|
||||
if(Mathf.chance(fireballChance * Time.delta() * Mathf.clamp(flammability / 10f))){
|
||||
Bullets.fireball.createNet(Team.derelict, x, y, Mathf.random(360f), -1f, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Puddlec p = Puddles.get(tile);
|
||||
puddleFlammability = p != null ? p.getFlammability() / 3f : 0;
|
||||
|
||||
if(damage){
|
||||
entity.damage(0.4f);
|
||||
}
|
||||
Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f,
|
||||
unit -> !unit.isFlying() && !unit.isImmune(StatusEffects.burning),
|
||||
unit -> unit.apply(StatusEffects.burning, 60 * 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Fires.remove(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
Fires.register(this);
|
||||
}
|
||||
}
|
||||
94
core/src/mindustry/entities/comp/FlyingComp.java
Normal file
94
core/src/mindustry/entities/comp/FlyingComp.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.net;
|
||||
|
||||
@Component
|
||||
abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
|
||||
private static final Vec2 tmp1 = new Vec2(), tmp2 = new Vec2();
|
||||
|
||||
@Import float x, y;
|
||||
@Import Vec2 vel;
|
||||
|
||||
float elevation;
|
||||
private transient boolean wasFlying;
|
||||
transient float drownTime;
|
||||
transient float splashTimer;
|
||||
|
||||
boolean isGrounded(){
|
||||
return elevation < 0.001f;
|
||||
}
|
||||
|
||||
boolean isFlying(){
|
||||
return elevation >= 0.09f;
|
||||
}
|
||||
|
||||
boolean canDrown(){
|
||||
return isGrounded();
|
||||
}
|
||||
|
||||
void landed(){
|
||||
|
||||
}
|
||||
|
||||
void wobble(){
|
||||
x += Mathf.sin(Time.time() + id() * 99, 25f, 0.05f) * Time.delta() * elevation;
|
||||
y += Mathf.cos(Time.time() + id() * 99, 25f, 0.05f) * Time.delta() * elevation;
|
||||
}
|
||||
|
||||
void moveAt(Vec2 vector, float acceleration){
|
||||
Vec2 t = tmp1.set(vector).scl(floorSpeedMultiplier()); //target vector
|
||||
tmp2.set(t).sub(vel).limit(acceleration * vector.len()); //delta vector
|
||||
vel.add(tmp2);
|
||||
}
|
||||
|
||||
float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() ? Blocks.air.asFloor() : floorOn();
|
||||
return on.speedMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Floor floor = floorOn();
|
||||
|
||||
if(isFlying() != wasFlying){
|
||||
if(wasFlying){
|
||||
if(tileOn() != null){
|
||||
Fx.unitLand.at(x, y, floorOn().isLiquid ? 1f : 0.5f, floorOn().mapColor);
|
||||
}
|
||||
}
|
||||
|
||||
wasFlying = isFlying();
|
||||
}
|
||||
|
||||
if(isGrounded() && floor.isLiquid){
|
||||
if((splashTimer += Mathf.dst(deltaX(), deltaY())) >= 7f){
|
||||
floor.walkEffect.at(x, y, 0, floor.mapColor);
|
||||
splashTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(canDrown() && floor.isLiquid && floor.drownTime > 0){
|
||||
drownTime += Time.delta() * 1f / floor.drownTime;
|
||||
drownTime = Mathf.clamp(drownTime);
|
||||
if(Mathf.chance(Time.delta() * 0.05f)){
|
||||
floor.drownUpdateEffect.at(x, y, 0f, floor.mapColor);
|
||||
}
|
||||
|
||||
//TODO is the netClient check necessary?
|
||||
if(drownTime >= 0.999f && !net.client()){
|
||||
kill();
|
||||
//TODO drown event!
|
||||
}
|
||||
}else{
|
||||
drownTime = Mathf.lerpDelta(drownTime, 0f, 0.03f);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
core/src/mindustry/entities/comp/HealthComp.java
Normal file
82
core/src/mindustry/entities/comp/HealthComp.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class HealthComp implements Entityc{
|
||||
static final float hitDuration = 9f;
|
||||
|
||||
float health;
|
||||
transient float hitTime;
|
||||
transient float maxHealth = 1f;
|
||||
transient boolean dead;
|
||||
|
||||
boolean isValid(){
|
||||
return !dead && isAdded();
|
||||
}
|
||||
|
||||
float healthf(){
|
||||
return health / maxHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
hitTime -= Time.delta() / hitDuration;
|
||||
}
|
||||
|
||||
void killed(){
|
||||
//implement by other components
|
||||
}
|
||||
|
||||
void kill(){
|
||||
if(dead) return;
|
||||
|
||||
health = 0;
|
||||
dead = true;
|
||||
killed();
|
||||
remove();
|
||||
}
|
||||
|
||||
void heal(){
|
||||
dead = false;
|
||||
health = maxHealth;
|
||||
}
|
||||
|
||||
boolean damaged(){
|
||||
return health < maxHealth - 0.001f;
|
||||
}
|
||||
|
||||
void damage(float amount){
|
||||
health -= amount;
|
||||
hitTime = 1f;
|
||||
if(health <= 0 && !dead){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
|
||||
void damage(float amount, boolean withEffect){
|
||||
float pre = hitTime;
|
||||
|
||||
damage(amount);
|
||||
|
||||
if(!withEffect){
|
||||
hitTime = pre;
|
||||
}
|
||||
}
|
||||
|
||||
void damageContinuous(float amount){
|
||||
damage(amount * Time.delta(), hitTime <= -20 + hitDuration);
|
||||
}
|
||||
|
||||
void clampHealth(){
|
||||
health = Mathf.clamp(health, 0, maxHealth);
|
||||
}
|
||||
|
||||
void heal(float amount){
|
||||
health += amount;
|
||||
clampHealth();
|
||||
}
|
||||
}
|
||||
64
core/src/mindustry/entities/comp/HitboxComp.java
Normal file
64
core/src/mindustry/entities/comp/HitboxComp.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.geom.QuadTree.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class HitboxComp implements Posc, QuadTreeObject{
|
||||
@Import float x, y;
|
||||
|
||||
transient float lastX, lastY, hitSize;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
updateLastPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
updateLastPosition();
|
||||
}
|
||||
|
||||
void getCollisions(Cons<QuadTree> consumer){
|
||||
|
||||
}
|
||||
|
||||
void updateLastPosition(){
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
|
||||
void collision(Hitboxc other, float x, float y){
|
||||
|
||||
}
|
||||
|
||||
float deltaX(){
|
||||
return x - lastX;
|
||||
}
|
||||
|
||||
float deltaY(){
|
||||
return y - lastY;
|
||||
}
|
||||
|
||||
boolean collides(Hitboxc other){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setCentered(x, y, hitSize, hitSize);
|
||||
}
|
||||
|
||||
public void hitboxTile(Rect rect){
|
||||
float scale = 0.66f;
|
||||
rect.setCentered(x, y, hitSize * scale, hitSize * scale);
|
||||
}
|
||||
}
|
||||
50
core/src/mindustry/entities/comp/ItemsComp.java
Normal file
50
core/src/mindustry/entities/comp/ItemsComp.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
@Component
|
||||
abstract class ItemsComp implements Posc{
|
||||
@ReadOnly ItemStack stack = new ItemStack();
|
||||
transient float itemTime;
|
||||
|
||||
abstract int itemCapacity();
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
stack.amount = Mathf.clamp(stack.amount, 0, itemCapacity());
|
||||
itemTime = Mathf.lerpDelta(itemTime, Mathf.num(hasItem()), 0.05f);
|
||||
}
|
||||
|
||||
Item item(){
|
||||
return stack.item;
|
||||
}
|
||||
|
||||
void clearItem(){
|
||||
stack.amount = 0;
|
||||
}
|
||||
|
||||
boolean acceptsItem(Item item){
|
||||
return !hasItem() || item == stack.item && stack.amount + 1 <= itemCapacity();
|
||||
}
|
||||
|
||||
boolean hasItem(){
|
||||
return stack.amount > 0;
|
||||
}
|
||||
|
||||
void addItem(Item item){
|
||||
addItem(item, 1);
|
||||
}
|
||||
|
||||
void addItem(Item item, int amount){
|
||||
stack.amount = stack.item == item ? stack.amount + amount : amount;
|
||||
stack.item = item;
|
||||
stack.amount = Mathf.clamp(stack.amount, 0, itemCapacity());
|
||||
}
|
||||
|
||||
int maxAccepted(Item item){
|
||||
return stack.item != item && stack.amount > 0 ? 0 : itemCapacity() - stack.amount;
|
||||
}
|
||||
}
|
||||
21
core/src/mindustry/entities/comp/LegsComp.java
Normal file
21
core/src/mindustry/entities/comp/LegsComp.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class LegsComp implements Posc, Flyingc, Hitboxc, Unitc, Legsc, ElevationMovec{
|
||||
@Import float x, y;
|
||||
|
||||
float baseRotation;
|
||||
transient float walkTime;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float len = vel().len();
|
||||
baseRotation = Angles.moveToward(baseRotation, vel().angle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed));
|
||||
walkTime += Time.delta()*len;
|
||||
}
|
||||
}
|
||||
107
core/src/mindustry/entities/comp/MinerComp.java
Normal file
107
core/src/mindustry/entities/comp/MinerComp.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
transient float mineTimer;
|
||||
@Nullable Tile mineTile;
|
||||
|
||||
abstract boolean canMine(Item item);
|
||||
|
||||
abstract float miningSpeed();
|
||||
|
||||
abstract boolean offloadImmediately();
|
||||
|
||||
boolean mining(){
|
||||
return mineTile != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Tilec core = closestCore();
|
||||
|
||||
if(core != null && mineTile != null && mineTile.drop() != null && !acceptsItem(mineTile.drop()) && dst(core) < mineTransferRange){
|
||||
int accepted = core.acceptStack(item(), stack().amount, this);
|
||||
if(accepted > 0){
|
||||
Call.transferItemTo(item(), accepted,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core.tile());
|
||||
clearItem();
|
||||
}
|
||||
}
|
||||
|
||||
if(mineTile == null || core == null || mineTile.block() != Blocks.air || dst(mineTile.worldx(), mineTile.worldy()) > miningRange
|
||||
|| (((Object)this) instanceof Builderc && ((Builderc)(Object)this).isBuilding())
|
||||
|| mineTile.drop() == null || !acceptsItem(mineTile.drop()) || !canMine(mineTile.drop())){
|
||||
mineTile = null;
|
||||
mineTimer = 0f;
|
||||
}else{
|
||||
Item item = mineTile.drop();
|
||||
rotation(Mathf.slerpDelta(rotation(), angleTo(mineTile.worldx(), mineTile.worldy()), 0.4f));
|
||||
mineTimer += Time.delta()*miningSpeed();
|
||||
|
||||
if(mineTimer >= 50f + item.hardness*10f){
|
||||
mineTimer = 0;
|
||||
|
||||
if(dst(core) < mineTransferRange && core.acceptStack(item, 1, this) == 1 && offloadImmediately()){
|
||||
Call.transferItemTo(item, 1,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core.tile());
|
||||
}else if(acceptsItem(item)){
|
||||
//this is clientside, since items are synced anyway
|
||||
InputHandler.transferItemToUnit(item,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.06 * Time.delta())){
|
||||
Fx.pulverizeSmall.at(mineTile.worldx() + Mathf.range(tilesize / 2f), mineTile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!mining()) return;
|
||||
float focusLen = 4f + Mathf.absin(Time.time(), 1.1f, 0.5f);
|
||||
float swingScl = 12f, swingMag = tilesize / 8f;
|
||||
float flashScl = 0.3f;
|
||||
|
||||
float px = x + Angles.trnsx(rotation, focusLen);
|
||||
float py = y + Angles.trnsy(rotation, focusLen);
|
||||
|
||||
float ex = mineTile.worldx() + Mathf.sin(Time.time() + 48, swingScl, swingMag);
|
||||
float ey = mineTile.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);
|
||||
|
||||
//TODO hack?
|
||||
if(isLocal()){
|
||||
Lines.stroke(1f, Pal.accent);
|
||||
Lines.poly(mineTile.worldx(), mineTile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time());
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
9
core/src/mindustry/entities/comp/OwnerComp.java
Normal file
9
core/src/mindustry/entities/comp/OwnerComp.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
class OwnerComp{
|
||||
Entityc owner;
|
||||
}
|
||||
16
core/src/mindustry/entities/comp/PayloadComp.java
Normal file
16
core/src/mindustry/entities/comp/PayloadComp.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
/** An entity that holds a payload. */
|
||||
@Component
|
||||
abstract class PayloadComp{
|
||||
//TODO multiple payloads?
|
||||
@Nullable Payload payload;
|
||||
|
||||
boolean hasPayload(){
|
||||
return payload != null;
|
||||
}
|
||||
}
|
||||
18
core/src/mindustry/entities/comp/PhysicsComp.java
Normal file
18
core/src/mindustry/entities/comp/PhysicsComp.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.async.PhysicsProcess.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/** Affected by physics.
|
||||
* Will bounce off of other objects that are at similar elevations.
|
||||
* Has mass.*/
|
||||
@Component
|
||||
abstract class PhysicsComp implements Velc, Hitboxc, Flyingc{
|
||||
transient PhysicRef physref;
|
||||
transient float mass = 1f;
|
||||
|
||||
public void impulse(float x, float y){
|
||||
vel().add(x / mass, y / mass);
|
||||
}
|
||||
}
|
||||
235
core/src/mindustry/entities/comp/PlayerComp.java
Normal file
235
core/src/mindustry/entities/comp/PlayerComp.java
Normal file
@@ -0,0 +1,235 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Playerc.class}, serialize = false)
|
||||
@Component
|
||||
abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Drawc{
|
||||
static final float deathDelay = 30f;
|
||||
|
||||
@NonNull @ReadOnly Unitc unit = Nulls.unit;
|
||||
|
||||
@ReadOnly Team team = Team.sharded;
|
||||
String name = "noname";
|
||||
@Nullable NetConnection con;
|
||||
boolean admin, typing;
|
||||
Color color = new Color();
|
||||
float mouseX, mouseY;
|
||||
float deathTimer;
|
||||
|
||||
String lastText = "";
|
||||
float textFadeTime;
|
||||
|
||||
public boolean isBuilder(){
|
||||
return unit instanceof Builderc;
|
||||
}
|
||||
|
||||
public boolean isMiner(){
|
||||
return unit instanceof Minerc;
|
||||
}
|
||||
|
||||
public @Nullable CoreEntity closestCore(){
|
||||
return state.teams.closestCore(x(), y(), team);
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
team = state.rules.defaultTeam;
|
||||
admin = typing = false;
|
||||
textFadeTime = 0f;
|
||||
if(!dead()){
|
||||
unit.controller(unit.type().createController());
|
||||
unit = Nulls.unit;
|
||||
}
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(unit.dead()){
|
||||
clearUnit();
|
||||
}
|
||||
|
||||
CoreEntity core = closestCore();
|
||||
|
||||
if(!dead()){
|
||||
x(unit.x());
|
||||
y(unit.y());
|
||||
unit.team(team);
|
||||
deathTimer = 0;
|
||||
}else if(core != null){
|
||||
deathTimer += Time.delta();
|
||||
if(deathTimer >= deathDelay){
|
||||
core.requestSpawn((Playerc)this);
|
||||
}
|
||||
}
|
||||
|
||||
textFadeTime -= Time.delta() / (60 * 5);
|
||||
}
|
||||
|
||||
public void team(Team team){
|
||||
this.team = team;
|
||||
unit.team(team);
|
||||
}
|
||||
|
||||
public void clearUnit(){
|
||||
unit(Nulls.unit);
|
||||
}
|
||||
|
||||
public Unitc unit(){
|
||||
return unit;
|
||||
}
|
||||
|
||||
public Minerc miner(){
|
||||
return !(unit instanceof Minerc) ? Nulls.miner : (Minerc)unit;
|
||||
}
|
||||
|
||||
public Builderc builder(){
|
||||
return !(unit instanceof Builderc) ? Nulls.builder : (Builderc)unit;
|
||||
}
|
||||
|
||||
public void unit(Unitc unit){
|
||||
if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead.");
|
||||
if(this.unit != Nulls.unit){
|
||||
//un-control the old unit
|
||||
this.unit.controller(this.unit.type().createController());
|
||||
}
|
||||
this.unit = unit;
|
||||
if(unit != Nulls.unit){
|
||||
unit.team(team);
|
||||
unit.controller(this);
|
||||
}
|
||||
}
|
||||
|
||||
boolean dead(){
|
||||
return unit.isNull();
|
||||
}
|
||||
|
||||
String uuid(){
|
||||
return con == null ? "[LOCAL]" : con.uuid;
|
||||
}
|
||||
|
||||
String usid(){
|
||||
return con == null ? "[LOCAL]" : con.usid;
|
||||
}
|
||||
|
||||
void kick(KickReason reason){
|
||||
con.kick(reason);
|
||||
}
|
||||
|
||||
void kick(String reason){
|
||||
con.kick(reason);
|
||||
}
|
||||
|
||||
void kick(String reason, int duration){
|
||||
con.kick(reason, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.playerName);
|
||||
float z = Drawf.text();
|
||||
|
||||
BitmapFont font = Fonts.def;
|
||||
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
final float nameHeight = 11;
|
||||
final float textHeight = 15;
|
||||
|
||||
boolean ints = font.usesIntegerPositions();
|
||||
font.setUseIntegerPositions(false);
|
||||
font.getData().setScale(0.25f / Scl.scl(1f));
|
||||
layout.setText(font, name);
|
||||
|
||||
if(!isLocal()){
|
||||
Draw.color(0f, 0f, 0f, 0.3f);
|
||||
Fill.rect(unit.x(), unit.y() + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3);
|
||||
Draw.color();
|
||||
font.setColor(color);
|
||||
font.draw(name, unit.x(), unit.y() + nameHeight, 0, Align.center, false);
|
||||
|
||||
if(admin){
|
||||
float s = 3f;
|
||||
Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f);
|
||||
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1.5f, s, s);
|
||||
Draw.color(color);
|
||||
Draw.rect(Icon.adminSmall.getRegion(), unit.x() + layout.width / 2f + 2 + 1, unit.y() + nameHeight - 1f, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || typing)){
|
||||
String text = textFadeTime <= 0 || lastText == null ? "[LIGHT_GRAY]" + Strings.animated(Time.time(), 4, 15f, ".") : lastText;
|
||||
float width = 100f;
|
||||
float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f);
|
||||
font.setColor(1f, 1f, 1f, textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime);
|
||||
|
||||
layout.setText(font, text, Color.white, width, Align.bottom, true);
|
||||
|
||||
Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime));
|
||||
Fill.rect(unit.x(), unit.y() + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3);
|
||||
font.draw(text, unit.x() - width/2f, unit.y() + textHeight + layout.height, width, Align.center, true);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
Pools.free(layout);
|
||||
font.getData().setScale(1f);
|
||||
font.setColor(Color.white);
|
||||
font.setUseIntegerPositions(ints);
|
||||
|
||||
Draw.z(z);
|
||||
}
|
||||
|
||||
void sendMessage(String text){
|
||||
if(isLocal()){
|
||||
if(ui != null){
|
||||
ui.chatfrag.addMessage(text, null);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con, text, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
void sendMessage(String text, Playerc from){
|
||||
sendMessage(text, from, NetClient.colorizeName(from.id(), from.name()));
|
||||
}
|
||||
|
||||
void sendMessage(String text, Playerc from, String fromName){
|
||||
if(isLocal()){
|
||||
if(ui != null){
|
||||
ui.chatfrag.addMessage(text, fromName);
|
||||
}
|
||||
}else{
|
||||
Call.sendMessage(con, text, fromName, from);
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo getInfo(){
|
||||
if(isLocal()){
|
||||
throw new IllegalArgumentException("Local players cannot be traced and do not have info.");
|
||||
}else{
|
||||
return netServer.admins.getInfo(uuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
58
core/src/mindustry/entities/comp/PosComp.java
Normal file
58
core/src/mindustry/entities/comp/PosComp.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
|
||||
@Component
|
||||
abstract class PosComp implements Position{
|
||||
float x, y;
|
||||
|
||||
void set(float x, float y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
void set(Position pos){
|
||||
set(pos.getX(), pos.getY());
|
||||
}
|
||||
|
||||
void trns(float x, float y){
|
||||
set(this.x + x, this.y + y);
|
||||
}
|
||||
|
||||
int tileX(){
|
||||
return Vars.world.toTile(x);
|
||||
}
|
||||
|
||||
int tileY(){
|
||||
return Vars.world.toTile(y);
|
||||
}
|
||||
|
||||
/** Returns air if this unit is on a non-air top block. */
|
||||
public Floor floorOn(){
|
||||
Tile tile = tileOn();
|
||||
return tile == null || tile.block() != Blocks.air ? (Floor)Blocks.air : tile.floor();
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
Tile tileOn(){
|
||||
return world.tileWorld(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX(){
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(){
|
||||
return y;
|
||||
}
|
||||
}
|
||||
131
core/src/mindustry/entities/comp/PuddleComp.java
Normal file
131
core/src/mindustry/entities/comp/PuddleComp.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.world;
|
||||
import static mindustry.entities.Puddles.maxLiquid;
|
||||
|
||||
@EntityDef(value = {Puddlec.class}, pooled = true)
|
||||
@Component
|
||||
abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
private static final int maxGeneration = 2;
|
||||
private static final Color tmp = new Color();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Rect rect2 = new Rect();
|
||||
private static int seeds;
|
||||
|
||||
@Import float x, y;
|
||||
|
||||
float amount, lastRipple, accepting, updateTime;
|
||||
int generation;
|
||||
Tile tile;
|
||||
Liquid liquid;
|
||||
|
||||
public float getFlammability(){
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//update code
|
||||
float addSpeed = accepting > 0 ? 3f : 0f;
|
||||
|
||||
amount -= Time.delta() * (1f - liquid.viscosity) / (5f + addSpeed);
|
||||
|
||||
amount += accepting;
|
||||
accepting = 0f;
|
||||
|
||||
if(amount >= maxLiquid / 1.5f && generation < maxGeneration){
|
||||
float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Time.delta();
|
||||
for(Point2 point : Geometry.d4){
|
||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
||||
if(other != null && other.block() == Blocks.air){
|
||||
Puddles.deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down Puddlec propagation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if(amount <= 0f){
|
||||
remove();
|
||||
}
|
||||
|
||||
//effects-only code
|
||||
if(amount >= maxLiquid / 2f && updateTime <= 0f){
|
||||
Units.nearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> {
|
||||
if(unit.isGrounded()){
|
||||
unit.hitbox(rect2);
|
||||
if(rect.overlaps(rect2)){
|
||||
unit.apply(liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.vel().len() > 0.1){
|
||||
Fx.ripple.at(unit.x(), unit.y(), liquid.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(liquid.temperature > 0.7f && (tile.entity != null) && Mathf.chance(0.3 * Time.delta())){
|
||||
Fires.create(tile);
|
||||
}
|
||||
|
||||
updateTime = 20f;
|
||||
}
|
||||
|
||||
updateTime -= Time.delta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.debris - 1);
|
||||
|
||||
seeds = id();
|
||||
boolean onLiquid = tile.floor().isLiquid;
|
||||
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
|
||||
float smag = onLiquid ? 0.8f : 0f;
|
||||
float sscl = 20f;
|
||||
|
||||
Draw.color(tmp.set(liquid.color).shiftValue(-0.05f));
|
||||
Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f);
|
||||
Angles.randLenVectors(id(), 3, f * 6f, (ex, ey) -> {
|
||||
Fill.circle(x + ex + Mathf.sin(Time.time() + seeds * 532, sscl, smag),
|
||||
y + ey + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 5f);
|
||||
seeds++;
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
if(liquid.lightColor.a > 0.001f && f > 0){
|
||||
Color color = liquid.lightColor;
|
||||
float opacity = color.a * f;
|
||||
Drawf.light(tile.drawx(), tile.drawy(), 30f * f, color, opacity * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Puddles.remove(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
Puddles.register(this);
|
||||
}
|
||||
}
|
||||
17
core/src/mindustry/entities/comp/RotComp.java
Normal file
17
core/src/mindustry/entities/comp/RotComp.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class RotComp implements Entityc{
|
||||
float rotation;
|
||||
|
||||
void interpolate(){
|
||||
Syncc sync = as(Syncc.class);
|
||||
|
||||
if(sync.interpolator().values.length > 0){
|
||||
rotation = sync.interpolator().values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
50
core/src/mindustry/entities/comp/ShieldComp.java
Normal file
50
core/src/mindustry/entities/comp/ShieldComp.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class ShieldComp implements Healthc, Posc{
|
||||
@Import float health, hitTime;
|
||||
@Import boolean dead;
|
||||
|
||||
/** Absorbs health damage. */
|
||||
float shield;
|
||||
/** Shield opacity. */
|
||||
transient float shieldAlpha = 0f;
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void damage(float amount){
|
||||
hitTime = 1f;
|
||||
|
||||
boolean hadShields = shield > 0.0001f;
|
||||
|
||||
if(hadShields){
|
||||
shieldAlpha = 1f;
|
||||
}
|
||||
|
||||
float shieldDamage = Math.min(shield, amount);
|
||||
shield -= shieldDamage;
|
||||
amount -= shieldDamage;
|
||||
|
||||
if(amount > 0){
|
||||
health -= amount;
|
||||
if(health <= 0 && !dead){
|
||||
kill();
|
||||
}
|
||||
|
||||
if(hadShields && shield <= 0.0001f){
|
||||
Fx.unitShieldBreak.at(x(), y(), 0, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
shieldAlpha -= Time.delta() / 15f;
|
||||
if(shieldAlpha < 0) shieldAlpha = 0f;
|
||||
}
|
||||
}
|
||||
12
core/src/mindustry/entities/comp/ShielderComp.java
Normal file
12
core/src/mindustry/entities/comp/ShielderComp.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class ShielderComp implements Damagec, Teamc, Posc{
|
||||
|
||||
void absorb(){
|
||||
|
||||
}
|
||||
}
|
||||
148
core/src/mindustry/entities/comp/StatusComp.java
Normal file
148
core/src/mindustry/entities/comp/StatusComp.java
Normal file
@@ -0,0 +1,148 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.content;
|
||||
|
||||
@Component
|
||||
abstract class StatusComp implements Posc, Flyingc{
|
||||
private Array<StatusEntry> statuses = new Array<>();
|
||||
private transient Bits applied = new Bits(content.getBy(ContentType.status).size);
|
||||
|
||||
@ReadOnly transient float speedMultiplier, damageMultiplier, armorMultiplier;
|
||||
|
||||
/** @return damage taken based on status armor multipliers */
|
||||
float getShieldDamage(float amount){
|
||||
return amount * Mathf.clamp(1f - armorMultiplier / 100f);
|
||||
}
|
||||
|
||||
/** Apply a status effect for 1 tick (for permanent effects) **/
|
||||
void apply(StatusEffect effect){
|
||||
apply(effect, 1);
|
||||
}
|
||||
|
||||
/** Adds a status effect to this unit. */
|
||||
void apply(StatusEffect effect, float duration){
|
||||
if(effect == StatusEffects.none || effect == null || isImmune(effect)) return; //don't apply empty or immune effects
|
||||
|
||||
if(statuses.size > 0){
|
||||
//check for opposite effects
|
||||
for(int i = 0; i < statuses.size; i ++){
|
||||
StatusEntry entry = statuses.get(i);
|
||||
//extend effect
|
||||
if(entry.effect == effect){
|
||||
entry.time = Math.max(entry.time, duration);
|
||||
return;
|
||||
}else if(entry.effect.reactsWith(effect)){ //find opposite
|
||||
StatusEntry.tmp.effect = entry.effect;
|
||||
entry.effect.getTransition((Unitc)this, effect, entry.time, duration, StatusEntry.tmp);
|
||||
entry.time = StatusEntry.tmp.time;
|
||||
|
||||
if(StatusEntry.tmp.effect != entry.effect){
|
||||
entry.effect = StatusEntry.tmp.effect;
|
||||
}
|
||||
|
||||
//stop looking when one is found
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//otherwise, no opposites found, add direct effect
|
||||
StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new);
|
||||
entry.set(effect, duration);
|
||||
statuses.add(entry);
|
||||
}
|
||||
|
||||
/** Removes a status effect. */
|
||||
void unapply(StatusEffect effect){
|
||||
statuses.remove(e -> {
|
||||
if(e.effect == effect){
|
||||
Pools.free(e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
boolean isBoss(){
|
||||
return hasEffect(StatusEffects.boss);
|
||||
}
|
||||
|
||||
abstract boolean isImmune(StatusEffect effect);
|
||||
|
||||
Color statusColor(){
|
||||
if(statuses.size == 0){
|
||||
return Tmp.c1.set(Color.white);
|
||||
}
|
||||
|
||||
float r = 1f, g = 1f, b = 1f, total = 0f;
|
||||
for(StatusEntry entry : statuses){
|
||||
float intensity = entry.time < 10f ? entry.time/10f : 1f;
|
||||
r += entry.effect.color.r * intensity;
|
||||
g += entry.effect.color.g * intensity;
|
||||
b += entry.effect.color.b * intensity;
|
||||
total += intensity;
|
||||
}
|
||||
float count = statuses.size + total;
|
||||
return Tmp.c1.set(r / count, g / count, b / count, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Floor floor = floorOn();
|
||||
if(isGrounded()){
|
||||
//apply effect
|
||||
apply(floor.status, floor.statusDuration);
|
||||
}
|
||||
|
||||
applied.clear();
|
||||
speedMultiplier = damageMultiplier = armorMultiplier = 1f;
|
||||
|
||||
if(statuses.isEmpty()) return;
|
||||
|
||||
int index = 0;
|
||||
|
||||
while(index < statuses.size){
|
||||
StatusEntry entry = statuses.get(index++);
|
||||
|
||||
entry.time = Math.max(entry.time - Time.delta(), 0);
|
||||
applied.set(entry.effect.id);
|
||||
|
||||
if(entry.time <= 0 && !entry.effect.permanent){
|
||||
Pools.free(entry);
|
||||
index --;
|
||||
statuses.remove(index);
|
||||
}else{
|
||||
speedMultiplier *= entry.effect.speedMultiplier;
|
||||
armorMultiplier *= entry.effect.armorMultiplier;
|
||||
damageMultiplier *= entry.effect.damageMultiplier;
|
||||
//TODO ugly casting
|
||||
entry.effect.update((Unitc)this, entry.time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
for(StatusEntry e : statuses){
|
||||
e.effect.draw((Unitc)this);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasEffect(StatusEffect effect){
|
||||
return applied.get(effect.id);
|
||||
}
|
||||
}
|
||||
37
core/src/mindustry/entities/comp/SyncComp.java
Normal file
37
core/src/mindustry/entities/comp/SyncComp.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.*;
|
||||
|
||||
@Component
|
||||
abstract class SyncComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
transient Interpolator interpolator = new Interpolator();
|
||||
|
||||
void setNet(float x, float y){
|
||||
set(x, y);
|
||||
|
||||
//TODO change interpolator API
|
||||
interpolator.target.set(x, y);
|
||||
interpolator.last.set(x, y);
|
||||
interpolator.pos.set(0, 0);
|
||||
interpolator.updateSpacing = 16;
|
||||
interpolator.lastUpdated = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Vars.net.client() && !isLocal()){
|
||||
interpolate();
|
||||
}
|
||||
}
|
||||
|
||||
void interpolate(){
|
||||
interpolator.update();
|
||||
x = interpolator.pos.x;
|
||||
y = interpolator.pos.y;
|
||||
}
|
||||
}
|
||||
23
core/src/mindustry/entities/comp/TeamComp.java
Normal file
23
core/src/mindustry/entities/comp/TeamComp.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.state;
|
||||
|
||||
@Component
|
||||
abstract class TeamComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
Team team = Team.derelict;
|
||||
|
||||
public @Nullable Tilec closestCore(){
|
||||
return state.teams.closestCore(x, y, team);
|
||||
}
|
||||
|
||||
public @Nullable Tilec closestEnemyCore(){
|
||||
return state.teams.closestEnemyCore(x, y, team);
|
||||
}
|
||||
}
|
||||
1109
core/src/mindustry/entities/comp/TileComp.java
Normal file
1109
core/src/mindustry/entities/comp/TileComp.java
Normal file
File diff suppressed because it is too large
Load Diff
27
core/src/mindustry/entities/comp/TimedComp.java
Normal file
27
core/src/mindustry/entities/comp/TimedComp.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class TimedComp implements Entityc, Scaled{
|
||||
float time, lifetime;
|
||||
|
||||
//called last so pooling and removal happens then.
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void update(){
|
||||
time = Math.min(time + Time.delta(), lifetime);
|
||||
|
||||
if(time >= lifetime){
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float fin(){
|
||||
return time / lifetime;
|
||||
}
|
||||
}
|
||||
13
core/src/mindustry/entities/comp/TimerComp.java
Normal file
13
core/src/mindustry/entities/comp/TimerComp.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
@Component
|
||||
abstract class TimerComp{
|
||||
Interval timer = new Interval(6);
|
||||
|
||||
public boolean timer(int index, float time){
|
||||
return timer.get(index, time);
|
||||
}
|
||||
}
|
||||
9
core/src/mindustry/entities/comp/TrailComp.java
Normal file
9
core/src/mindustry/entities/comp/TrailComp.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
@Component
|
||||
abstract class TrailComp{
|
||||
transient Trail trail = new Trail();
|
||||
}
|
||||
227
core/src/mindustry/entities/comp/UnitComp.java
Normal file
227
core/src/mindustry/entities/comp/UnitComp.java
Normal file
@@ -0,0 +1,227 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{
|
||||
|
||||
@Import float x, y, rotation, elevation, maxHealth;
|
||||
|
||||
private transient UnitController controller;
|
||||
private UnitType type;
|
||||
|
||||
public void moveAt(Vec2 vector){
|
||||
moveAt(vector, type.accel);
|
||||
}
|
||||
|
||||
public void aimLook(Position pos){
|
||||
aim(pos);
|
||||
lookAt(pos);
|
||||
}
|
||||
|
||||
public void aimLook(float x, float y){
|
||||
aim(x, y);
|
||||
lookAt(x, y);
|
||||
}
|
||||
|
||||
public boolean hasWeapons(){
|
||||
return type.hasWeapons();
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return type.region.getWidth() * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int itemCapacity(){
|
||||
return type.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float bounds(){
|
||||
return hitSize() * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controller(UnitController next){
|
||||
this.controller = next;
|
||||
if(controller.unit() != this) controller.unit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitController controller(){
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(UnitType def, UnitController controller){
|
||||
type(type);
|
||||
controller(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(UnitType type){
|
||||
this.type = type;
|
||||
this.maxHealth = type.health;
|
||||
heal();
|
||||
drag(type.drag);
|
||||
hitSize(type.hitsize);
|
||||
controller(type.createController());
|
||||
setupWeapons(type);
|
||||
|
||||
elevation = type.flying ? 1f : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitType type(){
|
||||
return type;
|
||||
}
|
||||
|
||||
public void lookAt(float angle){
|
||||
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta());
|
||||
}
|
||||
|
||||
public void lookAt(Position pos){
|
||||
lookAt(angleTo(pos));
|
||||
}
|
||||
|
||||
public void lookAt(float x, float y){
|
||||
lookAt(angleTo(x, y));
|
||||
}
|
||||
|
||||
public boolean isAI(){
|
||||
return controller instanceof AIController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRead(){
|
||||
//set up type info after reading
|
||||
type(this.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
teamIndex.updateCount(team(), 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
teamIndex.updateCount(team(), -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void landed(){
|
||||
if(type.landShake > 0f){
|
||||
Effects.shake(type.landShake, type.landShake, this);
|
||||
}
|
||||
|
||||
type.landed(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
type.update(this);
|
||||
|
||||
drag(type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f));
|
||||
|
||||
//apply knockback based on spawns
|
||||
if(team() != state.rules.waveTeam){
|
||||
float relativeSize = state.rules.dropZoneRadius + bounds()/2f + 1f;
|
||||
for(Tile spawn : spawner.getSpawns()){
|
||||
if(within(spawn.worldx(), spawn.worldy(), relativeSize)){
|
||||
vel().add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tile tile = tileOn();
|
||||
Floor floor = floorOn();
|
||||
|
||||
if(tile != null && isGrounded()){
|
||||
//unit block update
|
||||
if(tile.entity != null){
|
||||
tile.entity.unitOn(this);
|
||||
}
|
||||
|
||||
//kill when stuck in wall
|
||||
if(tile.solid()){
|
||||
kill();
|
||||
}
|
||||
|
||||
//apply damage
|
||||
if(floor.damageTaken > 0f){
|
||||
damageContinuous(floor.damageTaken);
|
||||
}
|
||||
}
|
||||
|
||||
controller.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmune(StatusEffect effect){
|
||||
return type.immunities.contains(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
type.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer(){
|
||||
return controller instanceof Playerc;
|
||||
}
|
||||
|
||||
public Playerc getPlayer(){
|
||||
return isPlayer() ? (Playerc)controller : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void killed(){
|
||||
float explosiveness = 2f + item().explosiveness * stack().amount;
|
||||
float flammability = item().flammability * stack().amount;
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame);
|
||||
|
||||
Effects.scorch(x, y, (int)(hitSize() / 5));
|
||||
Fx.explosion.at(this);
|
||||
Effects.shake(2f, 2f, this);
|
||||
type.deathSound.at(this);
|
||||
|
||||
Events.fire(new UnitDestroyEvent(this));
|
||||
|
||||
if(explosiveness > 7f && isLocal()){
|
||||
Events.fire(Trigger.suicideBomb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canMine(Item item){
|
||||
return type.drillTier >= item.hardness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float miningSpeed(){
|
||||
return type.mineSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offloadImmediately(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
31
core/src/mindustry/entities/comp/VelComp.java
Normal file
31
core/src/mindustry/entities/comp/VelComp.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@Component
|
||||
abstract class VelComp implements Posc{
|
||||
@Import float x, y;
|
||||
|
||||
final Vec2 vel = new Vec2();
|
||||
transient float drag = 0f;
|
||||
|
||||
//velocity needs to be called first, as it affects delta and lastPosition
|
||||
@MethodPriority(-1)
|
||||
@Override
|
||||
public void update(){
|
||||
move(vel.x * Time.delta(), vel.y * Time.delta());
|
||||
vel.scl(1f - drag * Time.delta());
|
||||
}
|
||||
|
||||
boolean moving(){
|
||||
return !vel.isZero(0.001f);
|
||||
}
|
||||
|
||||
void move(float cx, float cy){
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
41
core/src/mindustry/entities/comp/WaterMoveComp.java
Normal file
41
core/src/mindustry/entities/comp/WaterMoveComp.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
//just a proof of concept
|
||||
@Component
|
||||
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc{
|
||||
@Import float x, y;
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
if(isGrounded()){
|
||||
if(!EntityCollisions.waterSolid(tileX(), tileY())){
|
||||
collisions.move(this, cx, cy, EntityCollisions::waterSolid);
|
||||
}
|
||||
}else{
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public boolean canDrown(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Replace
|
||||
public float floorSpeedMultiplier(){
|
||||
Floor on = isFlying() ? Blocks.air.asFloor() : floorOn();
|
||||
return on.isDeep() ? 1.3f : 1f;
|
||||
}
|
||||
}
|
||||
|
||||
159
core/src/mindustry/entities/comp/WeaponsComp.java
Normal file
159
core/src/mindustry/entities/comp/WeaponsComp.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
@Component
|
||||
abstract class WeaponsComp implements Teamc, Posc, Rotc{
|
||||
@Import float x, y, rotation;
|
||||
|
||||
/** minimum cursor distance from player, fixes 'cross-eyed' shooting */
|
||||
static final float minAimDst = 20f;
|
||||
/** temporary weapon sequence number */
|
||||
static int sequenceNum = 0;
|
||||
|
||||
/** weapon mount array, never null */
|
||||
@ReadOnly transient WeaponMount[] mounts = {};
|
||||
@ReadOnly transient float range, aimX, aimY;
|
||||
@ReadOnly transient boolean isRotate, isShooting;
|
||||
|
||||
void setWeaponRotation(float rotation){
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
boolean inRange(Position other){
|
||||
return within(other, range);
|
||||
}
|
||||
|
||||
void setupWeapons(UnitType def){
|
||||
mounts = new WeaponMount[def.weapons.size];
|
||||
range = 0f;
|
||||
for(int i = 0; i < mounts.length; i++){
|
||||
mounts[i] = new WeaponMount(def.weapons.get(i));
|
||||
range = Math.max(range, def.weapons.get(i).bullet.range());
|
||||
}
|
||||
}
|
||||
|
||||
void controlWeapons(boolean rotate, boolean shoot){
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.rotate = rotate;
|
||||
mount.shoot = shoot;
|
||||
}
|
||||
isRotate = rotate;
|
||||
isShooting = shoot;
|
||||
}
|
||||
|
||||
void aim(Position pos){
|
||||
aim(pos.getX(), pos.getY());
|
||||
}
|
||||
|
||||
/** Aim at something. This will make all mounts point at it. */
|
||||
void aim(float x, float y){
|
||||
Tmp.v1.set(x, y).sub(this.x, this.y);
|
||||
if(Tmp.v1.len() < minAimDst) Tmp.v1.setLength(minAimDst);
|
||||
|
||||
x = Tmp.v1.x + this.x;
|
||||
y = Tmp.v1.y + this.y;
|
||||
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.aimX = x;
|
||||
mount.aimY = y;
|
||||
}
|
||||
|
||||
aimX = x;
|
||||
aimY = y;
|
||||
}
|
||||
|
||||
/** Update shooting and rotation for this unit. */
|
||||
@Override
|
||||
public void update(){
|
||||
for(WeaponMount mount : mounts){
|
||||
Weapon weapon = mount.weapon;
|
||||
mount.reload = Math.max(mount.reload - Time.delta(), 0);
|
||||
|
||||
//rotate if applicable
|
||||
if(weapon.rotate && (mount.rotate || mount.shoot)){
|
||||
float axisXOffset = weapon.mirror ? 0f : weapon.x;
|
||||
float axisX = this.x + Angles.trnsx(rotation, axisXOffset, weapon.y),
|
||||
axisY = this.y + Angles.trnsy(rotation, axisXOffset, weapon.y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - rotation();
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, weapon.rotateSpeed * Time.delta());
|
||||
}else{
|
||||
mount.rotation = 0;
|
||||
mount.targetRotation = angleTo(mount.aimX, mount.aimY);
|
||||
}
|
||||
|
||||
if(mount.shoot){
|
||||
float rotation = this.rotation - 90;
|
||||
|
||||
//shoot if applicable
|
||||
if(mount.reload <= 0.0001f && Angles.within(weapon.rotate ? mount.rotation : this.rotation, mount.targetRotation, mount.weapon.shootCone)){
|
||||
for(int i : (weapon.mirror && !weapon.alternate ? Mathf.signs : Mathf.one)){
|
||||
i *= Mathf.sign(weapon.flipped) * (mount.weapon.mirror ? Mathf.sign(mount.side) : 1);
|
||||
|
||||
//m a t h
|
||||
float weaponRotation = rotation + (weapon.rotate ? mount.rotation : 0);
|
||||
float mountX = this.x + Angles.trnsx(rotation, weapon.x * i, weapon.y),
|
||||
mountY = this.y + Angles.trnsy(rotation, weapon.x * i, weapon.y);
|
||||
float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX * i, weapon.shootY),
|
||||
shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX * i, weapon.shootY);
|
||||
float shootAngle = weapon.rotate ? weaponRotation + 90 : Angles.angle(shootX, shootY, mount.aimX, mount.aimY) + (this.rotation - angleTo(mount.aimX, mount.aimY));
|
||||
|
||||
shoot(weapon, shootX, shootY, shootAngle, -i);
|
||||
}
|
||||
|
||||
if(mount.weapon.mirror) mount.side = !mount.side;
|
||||
mount.reload = weapon.reload;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shoot(Weapon weapon, float x, float y, float rotation, int side){
|
||||
float baseX = this.x, baseY = this.y;
|
||||
|
||||
weapon.shootSound.at(x, y, Mathf.random(0.8f, 1.0f));
|
||||
|
||||
sequenceNum = 0;
|
||||
if(weapon.shotDelay > 0.01f){
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> {
|
||||
Time.run(sequenceNum * weapon.shotDelay, () -> bullet(weapon, x + this.x - baseX, y + this.y - baseY, f + Mathf.range(weapon.inaccuracy)));
|
||||
sequenceNum++;
|
||||
});
|
||||
}else{
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy)));
|
||||
}
|
||||
|
||||
BulletType ammo = weapon.bullet;
|
||||
|
||||
Tmp.v1.trns(rotation + 180f, ammo.recoil);
|
||||
|
||||
if(this instanceof Velc){
|
||||
//TODO apply force?
|
||||
((Velc)this).vel().add(Tmp.v1);
|
||||
}
|
||||
|
||||
Tmp.v1.trns(rotation, 3f);
|
||||
boolean parentize = ammo.keepVelocity;
|
||||
|
||||
Effects.shake(weapon.shake, weapon.shake, x, y);
|
||||
weapon.ejectEffect.at(x, y, rotation * side);
|
||||
ammo.shootEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null);
|
||||
ammo.smokeEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null);
|
||||
}
|
||||
|
||||
private void bullet(Weapon weapon, float x, float y, float angle){
|
||||
Tmp.v1.trns(angle, 3f);
|
||||
weapon.bullet.create(this, team(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user