Partial 7.0 merge - API preview
This commit is contained in:
@@ -4,7 +4,6 @@ import arc.graphics.g2d.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -34,7 +33,7 @@ abstract class BlockUnitComp implements Unitc{
|
||||
@Replace
|
||||
@Override
|
||||
public TextureRegion icon(){
|
||||
return tile.block.icon(Cicon.full);
|
||||
return tile.block.fullIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,7 +9,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
|
||||
static final float warpDst = 180f;
|
||||
static final float warpDst = 40f;
|
||||
|
||||
@Import float x, y;
|
||||
@Import Vec2 vel;
|
||||
@@ -17,11 +17,16 @@ abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
|
||||
@Override
|
||||
public void update(){
|
||||
if(!net.client() || isLocal()){
|
||||
|
||||
float dx = 0f, dy = 0f;
|
||||
|
||||
//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;
|
||||
if(x < 0) dx += (-x/warpDst);
|
||||
if(y < 0) dy += (-y/warpDst);
|
||||
if(x > world.unitWidth()) dx -= (x - world.unitWidth())/warpDst;
|
||||
if(y > world.unitHeight()) dy -= (y - world.unitHeight())/warpDst;
|
||||
|
||||
velAddNet(dx, dy);
|
||||
}
|
||||
|
||||
//clamp position if not flying
|
||||
|
||||
@@ -20,6 +20,7 @@ import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.ConstructBlock.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -69,7 +70,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
}
|
||||
}
|
||||
|
||||
Building core = core();
|
||||
var core = core();
|
||||
|
||||
//nothing to build.
|
||||
if(buildPlan() == null) return;
|
||||
@@ -109,7 +110,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
plans.removeFirst();
|
||||
return;
|
||||
}
|
||||
}else if((tile.team() != team && tile.team() != Team.derelict) || (!current.breaking && (cb.cblock != current.block || cb.tile != current.tile()))){
|
||||
}else if((tile.team() != team && tile.team() != Team.derelict) || (!current.breaking && (cb.current != current.block || cb.tile != current.tile()))){
|
||||
plans.removeFirst();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
//region vars and initialization
|
||||
static final float timeToSleep = 60f * 1, timeToUncontrol = 60f * 6;
|
||||
static final ObjectSet<Building> tmpTiles = new ObjectSet<>();
|
||||
static final Seq<Building> tempTileEnts = new Seq<>();
|
||||
static final Seq<Tile> tempTiles = new Seq<>();
|
||||
static final Seq<Building> tempBuilds = new Seq<>();
|
||||
static int sleepingEntities = 0;
|
||||
|
||||
@Import float x, y, health, maxHealth;
|
||||
@@ -57,7 +56,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
transient Tile tile;
|
||||
transient Block block;
|
||||
transient Seq<Building> proximity = new Seq<>(8);
|
||||
transient Seq<Building> proximity = new Seq<>(6);
|
||||
transient boolean updateFlow;
|
||||
transient byte cdump;
|
||||
transient int rotation;
|
||||
@@ -71,6 +70,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
ConsumeModule cons;
|
||||
|
||||
private transient float timeScale = 1f, timeScaleDuration;
|
||||
private transient float dumpAccum;
|
||||
|
||||
private transient @Nullable SoundLoop sound;
|
||||
|
||||
@@ -213,8 +213,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
if(self() instanceof ConstructBuild entity){
|
||||
//update block to reflect the fact that something was being constructed
|
||||
if(entity.cblock != null && entity.cblock.synthetic() && entity.wasConstructing){
|
||||
block = entity.cblock;
|
||||
if(entity.current != null && entity.current.synthetic() && entity.wasConstructing){
|
||||
block = entity.current;
|
||||
overrideConfig = entity.lastConfig;
|
||||
}else{
|
||||
//otherwise this was a deconstruction that was interrupted, don't want to rebuild that
|
||||
@@ -401,6 +401,29 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
//endregion
|
||||
//region handler methods
|
||||
|
||||
/** @return whether the player can select (but not actually control) this building. */
|
||||
public boolean canControlSelect(Player player){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Called when a player control-selects this building - not called for ControlBlock subclasses. */
|
||||
public void onControlSelect(Player player){
|
||||
|
||||
}
|
||||
|
||||
public void acceptPlayerPayload(Player player, Cons<Payload> grabber){
|
||||
Fx.spawn.at(player);
|
||||
var unit = player.unit();
|
||||
player.clearUnit();
|
||||
//player.deathTimer = Player.deathDelay + 1f; //for instant respawn
|
||||
unit.remove();
|
||||
grabber.get(new UnitPayload(unit));
|
||||
Fx.unitDrop.at(unit);
|
||||
if(Vars.net.client()){
|
||||
Vars.netClient.clearRemovedEntity(unit.id);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canUnload(){
|
||||
return block.unloadable;
|
||||
}
|
||||
@@ -467,10 +490,6 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
}
|
||||
|
||||
public void onProximityUpdate(){
|
||||
noSleep();
|
||||
}
|
||||
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return false;
|
||||
}
|
||||
@@ -688,13 +707,27 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
if(Vars.state.rules.sector != null && team == state.rules.defaultTeam) Vars.state.rules.sector.info.handleProduction(item, amount);
|
||||
}
|
||||
|
||||
/** Try dumping any item near the */
|
||||
/** Dumps any item with an accumulator. May dump multiple times per frame. Use with care. */
|
||||
public void dumpAccumulate(){
|
||||
dumpAccumulate(null);
|
||||
}
|
||||
|
||||
/** Dumps any item with an accumulator. May dump multiple times per frame. Use with care. */
|
||||
public void dumpAccumulate(Item item){
|
||||
dumpAccum += delta();
|
||||
while(dumpAccum >= 1f){
|
||||
dump(item);
|
||||
dumpAccum -=1f;
|
||||
}
|
||||
}
|
||||
|
||||
/** Try dumping any item near the building. */
|
||||
public boolean dump(){
|
||||
return dump(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try dumping a specific item near the
|
||||
* Try dumping a specific item near the building.
|
||||
* @param todump Item to dump. Can be null to dump anything.
|
||||
*/
|
||||
public boolean dump(Item todump){
|
||||
@@ -753,20 +786,27 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Called shortly before this building is removed. */
|
||||
public void onProximityRemoved(){
|
||||
if(power != null){
|
||||
powerGraphRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/** in overrides, this does the exact same thing as onProximityUpdate, use that instead */
|
||||
/** Called after this building is created in the world. May be called multiple times, or when adjacent buildings change. */
|
||||
public void onProximityAdded(){
|
||||
if(block.hasPower) updatePowerGraph();
|
||||
if(power != null){
|
||||
updatePowerGraph();
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when anything adjacent to this building is placed/removed, including itself. */
|
||||
public void onProximityUpdate(){
|
||||
noSleep();
|
||||
}
|
||||
|
||||
public void updatePowerGraph(){
|
||||
|
||||
for(Building other : getPowerConnections(tempTileEnts)){
|
||||
for(Building other : getPowerConnections(tempBuilds)){
|
||||
if(other.power != null){
|
||||
other.power.graph.addGraph(power.graph);
|
||||
}
|
||||
@@ -970,7 +1010,12 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
}
|
||||
|
||||
/** Called when the block is destroyed. */
|
||||
/** Called *after* the tile has been removed. */
|
||||
public void afterDestroyed(){
|
||||
|
||||
}
|
||||
|
||||
/** Called when the block is destroyed. The tile is still intact at this stage. */
|
||||
public void onDestroyed(){
|
||||
float explosiveness = block.baseExplosiveness;
|
||||
float flammability = 0f;
|
||||
@@ -1022,7 +1067,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
|
||||
public TextureRegion getDisplayIcon(){
|
||||
return block.icon(Cicon.medium);
|
||||
return block.uiIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1063,7 +1108,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
l.left();
|
||||
for(Item item : content.items()){
|
||||
if(items.hasFlowItem(item)){
|
||||
l.image(item.icon(Cicon.small)).padRight(3f);
|
||||
l.image(item.uiIcon).padRight(3f);
|
||||
l.label(() -> items.getFlowRate(item) < 0 ? "..." : Strings.fixed(items.getFlowRate(item), 1) + ps).color(Color.lightGray);
|
||||
l.row();
|
||||
}
|
||||
@@ -1090,7 +1135,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
Runnable rebuild = () -> {
|
||||
l.clearChildren();
|
||||
l.left();
|
||||
l.image(() -> liquids.current().icon(Cicon.small)).padRight(3f);
|
||||
l.image(() -> liquids.current().uiIcon).padRight(3f);
|
||||
l.label(() -> liquids.getFlowRate() < 0 ? "..." : Strings.fixed(liquids.getFlowRate(), 2) + ps).color(Color.lightGray);
|
||||
};
|
||||
|
||||
@@ -1189,11 +1234,23 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
/** Handle a bullet collision.
|
||||
* @return whether the bullet should be removed. */
|
||||
public boolean collision(Bullet other){
|
||||
damage(other.damage() * other.type().buildingDamageMultiplier);
|
||||
damage(other.team, other.damage() * other.type().buildingDamageMultiplier);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Used to handle damage from splash damage for certain types of blocks. */
|
||||
public void damage(@Nullable Team source, float damage){
|
||||
damage(damage);
|
||||
}
|
||||
|
||||
/** Changes this building's team in a safe manner. */
|
||||
public void changeTeam(Team next){
|
||||
indexer.removeIndex(tile);
|
||||
this.team = next;
|
||||
indexer.addIndex(tile);
|
||||
}
|
||||
|
||||
public boolean canPickup(){
|
||||
return true;
|
||||
}
|
||||
@@ -1366,11 +1423,9 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
@Override
|
||||
public void control(LAccess type, Object p1, double p2, double p3, double p4){
|
||||
//don't execute configure instructions that copy logic building configures; this can cause extreme lag
|
||||
if(type == LAccess.configure && block.logicConfigurable && !(p1 instanceof LogicBuild)){
|
||||
if(type == LAccess.config && block.logicConfigurable && !(p1 instanceof LogicBuild)){
|
||||
//change config only if it's new
|
||||
if(senseObject(LAccess.config) != p1){
|
||||
configured(null, p1);
|
||||
}
|
||||
configured(null, p1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1388,6 +1443,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
onDestroyed();
|
||||
tile.remove();
|
||||
remove();
|
||||
afterDestroyed();
|
||||
}
|
||||
|
||||
@Final
|
||||
@@ -1409,7 +1465,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
}
|
||||
|
||||
if(team == Team.derelict){
|
||||
if(team == Team.derelict || !block.supportsEnv(state.rules.environment)){
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -10,12 +9,10 @@ import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.blocks.defense.Wall.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -31,6 +28,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
BulletType type;
|
||||
float fdata;
|
||||
transient boolean absorbed, hit;
|
||||
transient @Nullable Trail trail;
|
||||
|
||||
@Override
|
||||
public void getCollisions(Cons<QuadTree> consumer){
|
||||
@@ -42,11 +40,6 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBullets(){
|
||||
type.draw(self());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
type.init(self());
|
||||
@@ -54,13 +47,17 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
type.despawned(self());
|
||||
//'despawned' only counts when the bullet is killed externally or reaches the end of life
|
||||
if(!hit){
|
||||
type.despawned(self());
|
||||
}
|
||||
type.removed(self());
|
||||
collided.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float damageMultiplier(){
|
||||
if(owner instanceof Unit) return ((Unit)owner).damageMultiplier() * state.rules.unitDamageMultiplier;
|
||||
if(owner instanceof Unit u) return u.damageMultiplier() * state.rules.unitDamageMultiplier;
|
||||
if(owner instanceof Building) return state.rules.blockDamageMultiplier;
|
||||
|
||||
return 1f;
|
||||
@@ -80,8 +77,8 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
@Replace
|
||||
@Override
|
||||
public boolean collides(Hitboxc other){
|
||||
return type.collides && (other instanceof Teamc && ((Teamc)other).team() != team)
|
||||
&& !(other instanceof Flyingc && !((Flyingc)other).checkTarget(type.collidesAir, type.collidesGround))
|
||||
return type.collides && (other instanceof Teamc t && t.team() != team)
|
||||
&& !(other instanceof Flyingc f && !f.checkTarget(type.collidesAir, type.collidesGround))
|
||||
&& !(type.pierce && collided.contains(other.id())); //prevent multiple collisions
|
||||
}
|
||||
|
||||
@@ -89,24 +86,16 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
@Override
|
||||
public void collision(Hitboxc other, float x, float y){
|
||||
type.hit(self(), x, y);
|
||||
float health = 0f;
|
||||
|
||||
if(other instanceof Healthc h){
|
||||
health = h.health();
|
||||
}
|
||||
|
||||
//must be last.
|
||||
if(!type.pierce){
|
||||
hit = true;
|
||||
remove();
|
||||
}else{
|
||||
collided.add(other.id());
|
||||
}
|
||||
|
||||
type.hitEntity(self(), other, health);
|
||||
|
||||
if(owner instanceof WallBuild && player != null && team == player.team() && other instanceof Unit unit && unit.dead){
|
||||
Events.fire(Trigger.phaseDeflectHit);
|
||||
}
|
||||
type.hitEntity(self(), other, other instanceof Healthc h ? h.health() : 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +119,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
|
||||
if(remove || type.collidesTeam){
|
||||
if(!type.pierceBuilding){
|
||||
hit = true;
|
||||
remove();
|
||||
}else{
|
||||
collided.add(tile.id);
|
||||
@@ -146,13 +136,14 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
}
|
||||
|
||||
if(type.pierceCap != -1 && collided.size >= type.pierceCap){
|
||||
hit = true;
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.bullet);
|
||||
Draw.z(type.layer);
|
||||
|
||||
type.draw(self());
|
||||
type.drawLight(self());
|
||||
|
||||
@@ -83,7 +83,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
clearCommand();
|
||||
units.shuffle();
|
||||
|
||||
float spacing = hitSize * 0.8f;
|
||||
float spacing = hitSize * 0.9f;
|
||||
minFormationSpeed = type.speed;
|
||||
|
||||
controlling.addAll(units);
|
||||
|
||||
@@ -40,6 +40,7 @@ abstract class EntityComp{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Replaced with `this` after code generation. */
|
||||
<T extends Entityc> T self(){
|
||||
return (T)this;
|
||||
}
|
||||
@@ -48,11 +49,6 @@ abstract class EntityComp{
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
<T> T with(Cons<T> cons){
|
||||
cons.get((T)this);
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
@InternalImpl
|
||||
abstract int classId();
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
@@ -9,6 +11,7 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -16,24 +19,30 @@ import static mindustry.Vars.*;
|
||||
|
||||
@EntityDef(value = {Firec.class}, pooled = true)
|
||||
@Component(base = true)
|
||||
abstract class FireComp implements Timedc, Posc, Firec, Syncc{
|
||||
private static final float spreadChance = 0.04f, fireballChance = 0.06f;
|
||||
abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
|
||||
public static final int frames = 40, duration = 90;
|
||||
|
||||
private static final float spreadDelay = 22f, fireballDelay = 40f,
|
||||
ticksPerFrame = (float)duration / frames, warmupDuration = 20f, damageDelay = 40f, tileDamage = 1.8f, unitDamage = 3f;
|
||||
|
||||
public static final TextureRegion[] regions = new TextureRegion[frames];
|
||||
|
||||
@Import float time, lifetime, x, y;
|
||||
|
||||
Tile tile;
|
||||
private transient Block block;
|
||||
private transient float baseFlammability = -1, puddleFlammability;
|
||||
private transient float
|
||||
baseFlammability = -1, puddleFlammability, damageTimer = Mathf.random(40f),
|
||||
spreadTimer = Mathf.random(spreadDelay), fireballTimer = Mathf.random(fireballDelay),
|
||||
warmup = 0f,
|
||||
animation = Mathf.random(frames);
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(Mathf.chance(0.09 * 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));
|
||||
}
|
||||
animation += Time.delta / ticksPerFrame;
|
||||
warmup += Time.delta;
|
||||
animation %= frames;
|
||||
|
||||
if(!headless){
|
||||
control.sound.loop(Sounds.fire, this, 0.07f);
|
||||
@@ -55,46 +64,71 @@ abstract class FireComp implements Timedc, Posc, Firec, Syncc{
|
||||
Building entity = tile.build;
|
||||
boolean damage = entity != null;
|
||||
|
||||
if(baseFlammability < 0 || block != tile.block()){
|
||||
baseFlammability = tile.getFlammability();
|
||||
block = tile.block();
|
||||
}
|
||||
|
||||
float flammability = baseFlammability + puddleFlammability;
|
||||
|
||||
if(!damage && flammability <= 0){
|
||||
time += Time.delta * 8;
|
||||
}
|
||||
|
||||
if(baseFlammability < 0 || block != tile.block()){
|
||||
baseFlammability = tile.build == null ? 0 : tile.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))){
|
||||
if(flammability > 1f && (spreadTimer += Time.delta * Mathf.clamp(flammability / 5f, 0.3f, 2f)) >= spreadDelay){
|
||||
spreadTimer = 0f;
|
||||
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.025 * Time.delta)){
|
||||
if(flammability > 0 && (fireballTimer += Time.delta * Mathf.clamp(flammability / 10f, 0f, 0.5f)) >= fireballDelay){
|
||||
fireballTimer = 0f;
|
||||
Bullets.fireball.createNet(Team.derelict, x, y, Mathf.random(360f), -1f, 1, 1);
|
||||
}
|
||||
|
||||
//apply damage to nearby units & building
|
||||
if((damageTimer += Time.delta) >= damageDelay){
|
||||
damageTimer = 0f;
|
||||
Puddlec p = Puddles.get(tile);
|
||||
puddleFlammability = p != null ? p.getFlammability() / 3f : 0;
|
||||
|
||||
if(damage){
|
||||
entity.damage(1.6f);
|
||||
entity.damage(tileDamage);
|
||||
}
|
||||
Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f,
|
||||
Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, unitDamage,
|
||||
unit -> !unit.isFlying() && !unit.isImmune(StatusEffects.burning),
|
||||
unit -> unit.apply(StatusEffects.burning, 60 * 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(regions[0] == null){
|
||||
for(int i = 0; i < frames; i++){
|
||||
regions[i] = Core.atlas.find("fire" + i);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.alpha(Mathf.clamp(warmup / warmupDuration));
|
||||
Draw.z(Layer.effect);
|
||||
Draw.rect(regions[(int)animation], x, y);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Fx.fireRemove.at(x, y, animation);
|
||||
Fires.remove(tile);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ abstract class LaunchCoreComp implements Drawc, Timedc{
|
||||
|
||||
Draw.z(Layer.weather - 1);
|
||||
|
||||
TextureRegion region = block.icon(Cicon.full);
|
||||
TextureRegion region = block.fullIcon;
|
||||
float rw = region.width * Draw.scl * scale, rh = region.height * Draw.scl * scale;
|
||||
|
||||
Draw.alpha(alpha);
|
||||
|
||||
@@ -83,8 +83,8 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
Tile on = tileOn();
|
||||
|
||||
//clear removed state of unit so it can be synced
|
||||
if(Vars.net.client() && payload instanceof UnitPayload){
|
||||
Vars.netClient.clearRemovedEntity(((UnitPayload)payload).unit.id);
|
||||
if(Vars.net.client() && payload instanceof UnitPayload u){
|
||||
Vars.netClient.clearRemovedEntity(u.unit.id);
|
||||
}
|
||||
|
||||
//drop off payload on an acceptor if possible
|
||||
@@ -106,7 +106,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
Unit u = payload.unit;
|
||||
|
||||
//can't drop ground units
|
||||
if(!u.canPass(tileX(), tileY())){
|
||||
if(!u.canPass(tileX(), tileY()) || Units.count(x, y, u.physicSize(), o -> o.isGrounded()) > 1){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
}
|
||||
|
||||
for(Payload p : payloads){
|
||||
table.image(p.icon(Cicon.small)).size(itemSize).padRight(pad);
|
||||
table.image(p.icon()).size(itemSize).padRight(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import mindustry.gen.*;
|
||||
* Has mass.*/
|
||||
@Component
|
||||
abstract class PhysicsComp implements Velc, Hitboxc, Flyingc{
|
||||
@Import float hitSize;
|
||||
@Import float hitSize, x, y;
|
||||
@Import Vec2 vel;
|
||||
|
||||
transient PhysicRef physref;
|
||||
|
||||
@@ -19,7 +19,6 @@ import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
@@ -33,31 +32,31 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
@Import float x, y;
|
||||
|
||||
@ReadOnly Unit unit = Nulls.unit;
|
||||
transient private Unit lastReadUnit = Nulls.unit;
|
||||
transient @Nullable NetConnection con;
|
||||
|
||||
@ReadOnly Team team = Team.sharded;
|
||||
@SyncLocal boolean typing, shooting, boosting;
|
||||
boolean admin;
|
||||
@SyncLocal float mouseX, mouseY;
|
||||
String name = "noname";
|
||||
boolean admin;
|
||||
String name = "frog";
|
||||
Color color = new Color();
|
||||
|
||||
//locale should not be synced.
|
||||
transient String locale = "en";
|
||||
transient float deathTimer;
|
||||
transient String lastText = "";
|
||||
transient float textFadeTime;
|
||||
transient private Unit lastReadUnit = Nulls.unit;
|
||||
|
||||
public boolean isBuilder(){
|
||||
return unit.canBuild();
|
||||
}
|
||||
|
||||
public @Nullable CoreBuild closestCore(){
|
||||
public @Nullable
|
||||
CoreBuild closestCore(){
|
||||
return state.teams.closestCore(x, y, team);
|
||||
}
|
||||
|
||||
public @Nullable CoreBuild core(){
|
||||
public @Nullable
|
||||
CoreBuild core(){
|
||||
return team.core();
|
||||
}
|
||||
|
||||
@@ -69,7 +68,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
|
||||
public TextureRegion icon(){
|
||||
//display default icon for dead players
|
||||
if(dead()) return core() == null ? UnitTypes.alpha.icon(Cicon.full) : ((CoreBlock)core().block).unitType.icon(Cicon.full);
|
||||
if(dead()) return core() == null ? UnitTypes.alpha.fullIcon : ((CoreBlock)core().block).unitType.fullIcon;
|
||||
|
||||
return unit.icon();
|
||||
}
|
||||
@@ -133,8 +132,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
|
||||
//update some basic state to sync things
|
||||
if(unit.type.canBoost){
|
||||
Tile tile = unit.tileOn();
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, unit.type.riseSpeed);
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, unit.onSolid() || boosting || (unit.isFlying() && !unit.canLand()) ? 1f : 0f, unit.type.riseSpeed);
|
||||
}
|
||||
}else if((core = bestCore()) != null){
|
||||
//have a small delay before death to prevent the camera from jumping around too quickly
|
||||
@@ -266,9 +264,9 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
|
||||
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.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();
|
||||
@@ -294,7 +292,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
sendMessage(text, from, NetClient.colorizeName(from.id(), from.name));
|
||||
}
|
||||
|
||||
void sendMessage(String text, Player from, String fromName){
|
||||
void sendMessage(String text, Player from, String fromName){
|
||||
if(isLocal()){
|
||||
if(ui != null){
|
||||
ui.chatfrag.addMessage(text, fromName);
|
||||
|
||||
@@ -21,10 +21,10 @@ import static mindustry.entities.Puddles.*;
|
||||
@Component(base = true)
|
||||
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(), rect2 = new Rect();
|
||||
private static int seeds;
|
||||
|
||||
@Import int id;
|
||||
@Import float x, y;
|
||||
|
||||
transient float accepting, updateTime, lastRipple;
|
||||
@@ -92,13 +92,13 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
public void draw(){
|
||||
Draw.z(Layer.debris - 1);
|
||||
|
||||
seeds = id();
|
||||
seeds = id;
|
||||
boolean onLiquid = tile.floor().isLiquid;
|
||||
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
|
||||
float smag = onLiquid ? 0.8f : 0f;
|
||||
float sscl = 25f;
|
||||
|
||||
Draw.color(tmp.set(liquid.color).shiftValue(-0.05f));
|
||||
Draw.color(Tmp.c1.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),
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.entities.comp;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -11,10 +12,11 @@ import static mindustry.Vars.*;
|
||||
abstract class ShieldComp implements Healthc, Posc{
|
||||
@Import float health, hitTime, x, y, healthMultiplier;
|
||||
@Import boolean dead;
|
||||
@Import Team team;
|
||||
|
||||
/** Absorbs health damage. */
|
||||
float shield;
|
||||
/** Substracts an amount from damage. */
|
||||
/** Subtracts an amount from damage. */
|
||||
float armor;
|
||||
/** Shield opacity. */
|
||||
transient float shieldAlpha = 0f;
|
||||
@@ -60,7 +62,7 @@ abstract class ShieldComp implements Healthc, Posc{
|
||||
}
|
||||
|
||||
if(hadShields && shield <= 0.0001f){
|
||||
Fx.unitShieldBreak.at(x, y, 0, this);
|
||||
Fx.unitShieldBreak.at(x, y, 0, team.color, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
private Seq<StatusEntry> statuses = new Seq<>();
|
||||
private transient Bits applied = new Bits(content.getBy(ContentType.status).size);
|
||||
|
||||
@ReadOnly transient float speedMultiplier = 1, damageMultiplier = 1, healthMultiplier = 1, reloadMultiplier = 1, buildSpeedMultiplier = 1, dragMultiplier = 1;
|
||||
@ReadOnly transient boolean disarmed = false;
|
||||
//these are considered read-only
|
||||
transient float speedMultiplier = 1, damageMultiplier = 1, healthMultiplier = 1, reloadMultiplier = 1, buildSpeedMultiplier = 1, dragMultiplier = 1;
|
||||
transient boolean disarmed = false;
|
||||
|
||||
@Import UnitType type;
|
||||
|
||||
@@ -46,15 +47,8 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
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(self(), effect, entry.time, duration, StatusEntry.tmp);
|
||||
entry.time = StatusEntry.tmp.time;
|
||||
|
||||
if(StatusEntry.tmp.effect != entry.effect){
|
||||
entry.effect = StatusEntry.tmp.effect;
|
||||
}
|
||||
|
||||
}else if(entry.effect.applyTransition(self(), effect, entry, duration)){ //find reaction
|
||||
//TODO effect may react with multiple other effects
|
||||
//stop looking when one is found
|
||||
return;
|
||||
}
|
||||
@@ -69,6 +63,11 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
}
|
||||
}
|
||||
|
||||
float getDuration(StatusEffect effect){
|
||||
var entry = statuses.find(e -> e.effect == effect);
|
||||
return entry == null ? 0 : entry.time;
|
||||
}
|
||||
|
||||
void clearStatuses(){
|
||||
statuses.clear();
|
||||
}
|
||||
@@ -149,6 +148,10 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
}
|
||||
}
|
||||
|
||||
public Bits statusBits(){
|
||||
return applied;
|
||||
}
|
||||
|
||||
public void draw(){
|
||||
for(StatusEntry e : statuses){
|
||||
e.effect.draw(self());
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -18,17 +19,17 @@ abstract class TeamComp implements Posc{
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Building core(){
|
||||
public CoreBuild core(){
|
||||
return team.core();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Building closestCore(){
|
||||
public CoreBuild closestCore(){
|
||||
return state.teams.closestCore(x, y, team);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Building closestEnemyCore(){
|
||||
public CoreBuild closestEnemyCore(){
|
||||
return state.teams.closestEnemyCore(x, y, team);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,10 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
double flag;
|
||||
|
||||
transient Seq<Ability> abilities = new Seq<>(0);
|
||||
transient float healTime;
|
||||
private transient float resupplyTime = Mathf.random(10f);
|
||||
private transient boolean wasPlayer;
|
||||
private transient float lastHealth;
|
||||
|
||||
public void moveAt(Vec2 vector){
|
||||
moveAt(vector, type.accel);
|
||||
@@ -67,6 +69,16 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
lookAt(x, y);
|
||||
}
|
||||
|
||||
/** @return approx. square size of the physical hitbox for physics */
|
||||
public float physicSize(){
|
||||
return hitSize * 0.7f;
|
||||
}
|
||||
|
||||
/** @return whether there is solid, un-occupied ground under this unit. */
|
||||
public boolean canLand(){
|
||||
return !onSolid() && Units.count(x, y, physicSize(), f -> f != self() && f.isGrounded()) == 0;
|
||||
}
|
||||
|
||||
public boolean inRange(Position other){
|
||||
return within(other, type.range);
|
||||
}
|
||||
@@ -98,7 +110,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return angleTo(buildPlan());
|
||||
}else if(mineTile != null){
|
||||
return angleTo(mineTile);
|
||||
}else if(moving()){
|
||||
}else if(moving() && type.omniMovement){
|
||||
return vel().angle();
|
||||
}
|
||||
return rotation;
|
||||
@@ -114,7 +126,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
if(isBuilding()){
|
||||
return state.rules.infiniteResources ? Float.MAX_VALUE : Math.max(type.clipSize, type.region.width) + buildingRange + tilesize*4f;
|
||||
}
|
||||
return Math.max(type.region.width * 2f, type.clipSize);
|
||||
return type.clipSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,7 +158,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
controller instanceof FormationAI ? ctrlFormation :
|
||||
0;
|
||||
case commanded -> controller instanceof FormationAI && isValid() ? 1 : 0;
|
||||
case payloadCount -> self() instanceof Payloadc pay ? pay.payloads().size : 0;
|
||||
case payloadCount -> ((Object)this) instanceof Payloadc pay ? pay.payloads().size : 0;
|
||||
case size -> hitSize / tilesize;
|
||||
default -> Float.NaN;
|
||||
};
|
||||
@@ -159,7 +171,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
case name -> controller instanceof Player p ? p.name : null;
|
||||
case firstItem -> stack().amount == 0 ? null : item();
|
||||
case controller -> !isValid() ? null : controller instanceof LogicAI log ? log.controller : controller instanceof FormationAI form ? form.leader : this;
|
||||
case payloadType -> self() instanceof Payloadc pay ?
|
||||
case payloadType -> ((Object)this) instanceof Payloadc pay ?
|
||||
(pay.payloads().isEmpty() ? null :
|
||||
pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type :
|
||||
pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null;
|
||||
@@ -313,6 +325,18 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
type.update(self());
|
||||
|
||||
if(health > lastHealth && lastHealth > 0 && healTime <= -1f){
|
||||
healTime = 1f;
|
||||
}
|
||||
healTime -= Time.delta / 20f;
|
||||
lastHealth = health;
|
||||
|
||||
//check if environment is unsupported
|
||||
if(!type.supportsEnv(state.rules.environment) && !dead){
|
||||
Call.unitCapDeath(self());
|
||||
team.data().updateCount(type, -1);
|
||||
}
|
||||
|
||||
if(state.rules.unitAmmo && ammo < type.ammoCapacity - 0.0001f){
|
||||
resupplyTime += Time.delta;
|
||||
|
||||
@@ -336,7 +360,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
float relativeSize = state.rules.dropZoneRadius + hitSize/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));
|
||||
velAddNet(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,7 +391,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
elevation -= type.fallSpeed * Time.delta;
|
||||
|
||||
if(isGrounded()){
|
||||
destroy();
|
||||
Call.unitDestroy(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +438,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
/** @return a preview icon for this unit. */
|
||||
public TextureRegion icon(){
|
||||
return type.icon(Cicon.full);
|
||||
return type.fullIcon;
|
||||
}
|
||||
|
||||
/** Actually destroys the unit, removing it and creating explosions. **/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
@@ -20,7 +21,11 @@ abstract class VelComp implements Posc{
|
||||
@MethodPriority(-1)
|
||||
@Override
|
||||
public void update(){
|
||||
float px = x, py = y;
|
||||
move(vel.x * Time.delta, vel.y * Time.delta);
|
||||
if(Mathf.equal(px, x)) vel.x = 0;
|
||||
if(Mathf.equal(py, y)) vel.y = 0;
|
||||
|
||||
vel.scl(Math.max(1f - drag * Time.delta, 0));
|
||||
}
|
||||
|
||||
@@ -45,6 +50,10 @@ abstract class VelComp implements Posc{
|
||||
return !vel.isZero(0.01f);
|
||||
}
|
||||
|
||||
void move(Vec2 v){
|
||||
move(v.x, v.y);
|
||||
}
|
||||
|
||||
void move(float cx, float cy){
|
||||
SolidPred check = solidity();
|
||||
|
||||
@@ -55,4 +64,20 @@ abstract class VelComp implements Posc{
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
|
||||
void velAddNet(Vec2 v){
|
||||
vel.add(v);
|
||||
if(isRemote()){
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
}
|
||||
}
|
||||
|
||||
void velAddNet(float vx, float vy){
|
||||
vel.add(vx, vy);
|
||||
if(isRemote()){
|
||||
x += vx;
|
||||
y += vy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.audio.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
@Import float x, y, rotation, reloadMultiplier;
|
||||
@Import float x, y;
|
||||
@Import boolean disarmed;
|
||||
@Import Vec2 vel;
|
||||
@Import UnitType type;
|
||||
|
||||
/** temporary weapon sequence number */
|
||||
static int sequenceNum = 0;
|
||||
|
||||
/** weapon mount array, never null */
|
||||
@SyncLocal WeaponMount[] mounts = {};
|
||||
@ReadOnly transient boolean isRotate;
|
||||
@@ -43,7 +33,7 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
void setupWeapons(UnitType def){
|
||||
mounts = new WeaponMount[def.weapons.size];
|
||||
for(int i = 0; i < mounts.length; i++){
|
||||
mounts[i] = new WeaponMount(def.weapons.get(i));
|
||||
mounts[i] = def.weapons.get(i).mountType.get(def.weapons.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +43,10 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
|
||||
void controlWeapons(boolean rotate, boolean shoot){
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.rotate = rotate;
|
||||
mount.shoot = shoot;
|
||||
if(mount.weapon.controllable){
|
||||
mount.rotate = rotate;
|
||||
mount.shoot = shoot;
|
||||
}
|
||||
}
|
||||
isRotate = rotate;
|
||||
isShooting = shoot;
|
||||
@@ -73,8 +65,10 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
y = Tmp.v1.y + this.y;
|
||||
|
||||
for(WeaponMount mount : mounts){
|
||||
mount.aimX = x;
|
||||
mount.aimY = y;
|
||||
if(mount.weapon.controllable){
|
||||
mount.aimX = x;
|
||||
mount.aimY = y;
|
||||
}
|
||||
}
|
||||
|
||||
aimX = x;
|
||||
@@ -88,7 +82,7 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
@Override
|
||||
public void remove(){
|
||||
for(WeaponMount mount : mounts){
|
||||
if(mount.bullet != null){
|
||||
if(mount.bullet != null && mount.bullet.owner == self()){
|
||||
mount.bullet.time = mount.bullet.lifetime - 10f;
|
||||
mount.bullet = null;
|
||||
}
|
||||
@@ -102,136 +96,8 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
/** Update shooting and rotation for this unit. */
|
||||
@Override
|
||||
public void update(){
|
||||
boolean can = canShoot();
|
||||
|
||||
for(WeaponMount mount : mounts){
|
||||
Weapon weapon = mount.weapon;
|
||||
mount.reload = Math.max(mount.reload - Time.delta * reloadMultiplier, 0);
|
||||
|
||||
float weaponRotation = this.rotation - 90 + (weapon.rotate ? mount.rotation : 0);
|
||||
float mountX = this.x + Angles.trnsx(this.rotation - 90, weapon.x, weapon.y),
|
||||
mountY = this.y + Angles.trnsy(this.rotation - 90, weapon.x, weapon.y);
|
||||
float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX, weapon.shootY),
|
||||
shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX, weapon.shootY);
|
||||
float shootAngle = weapon.rotate ? weaponRotation + 90 : Angles.angle(shootX, shootY, mount.aimX, mount.aimY) + (this.rotation - angleTo(mount.aimX, mount.aimY));
|
||||
|
||||
//update continuous state
|
||||
if(weapon.continuous && mount.bullet != null){
|
||||
if(!mount.bullet.isAdded() || mount.bullet.time >= mount.bullet.lifetime || mount.bullet.type != weapon.bullet){
|
||||
mount.bullet = null;
|
||||
}else{
|
||||
mount.bullet.rotation(weaponRotation + 90);
|
||||
mount.bullet.set(shootX, shootY);
|
||||
mount.reload = weapon.reload;
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, mount.bullet.type.recoil));
|
||||
if(weapon.shootSound != Sounds.none && !headless){
|
||||
if(mount.sound == null) mount.sound = new SoundLoop(weapon.shootSound, 1f);
|
||||
mount.sound.update(x, y, true);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//heat decreases when not firing
|
||||
mount.heat = Math.max(mount.heat - Time.delta * reloadMultiplier / mount.weapon.cooldownTime, 0);
|
||||
|
||||
if(mount.sound != null){
|
||||
mount.sound.update(x, y, false);
|
||||
}
|
||||
}
|
||||
|
||||
//flip weapon shoot side for alternating weapons at half reload
|
||||
if(weapon.otherSide != -1 && weapon.alternate && mount.side == weapon.flipSprite &&
|
||||
mount.reload + Time.delta * reloadMultiplier > weapon.reload/2f && mount.reload <= weapon.reload/2f){
|
||||
mounts[weapon.otherSide].side = !mounts[weapon.otherSide].side;
|
||||
mount.side = !mount.side;
|
||||
}
|
||||
|
||||
//rotate if applicable
|
||||
if(weapon.rotate && (mount.rotate || mount.shoot) && can){
|
||||
float axisX = this.x + Angles.trnsx(this.rotation - 90, weapon.x, weapon.y),
|
||||
axisY = this.y + Angles.trnsy(this.rotation - 90, weapon.x, weapon.y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - this.rotation;
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, weapon.rotateSpeed * Time.delta);
|
||||
}else if(!weapon.rotate){
|
||||
mount.rotation = 0;
|
||||
mount.targetRotation = angleTo(mount.aimX, mount.aimY);
|
||||
}
|
||||
|
||||
//shoot if applicable
|
||||
if(mount.shoot && //must be shooting
|
||||
can && //must be able to shoot
|
||||
(ammo > 0 || !state.rules.unitAmmo || team().rules().infiniteAmmo) && //check ammo
|
||||
(!weapon.alternate || mount.side == weapon.flipSprite) &&
|
||||
//TODO checking for velocity this way isn't entirely correct
|
||||
(vel.len() >= mount.weapon.minShootVelocity || (net.active() && !isLocal())) && //check velocity requirements
|
||||
mount.reload <= 0.0001f && //reload has to be 0
|
||||
Angles.within(weapon.rotate ? mount.rotation : this.rotation, mount.targetRotation, mount.weapon.shootCone) //has to be within the cone
|
||||
){
|
||||
shoot(mount, shootX, shootY, mount.aimX, mount.aimY, mountX, mountY, shootAngle, Mathf.sign(weapon.x));
|
||||
|
||||
mount.reload = weapon.reload;
|
||||
|
||||
ammo--;
|
||||
if(ammo < 0) ammo = 0;
|
||||
}
|
||||
mount.weapon.update(self(), mount);
|
||||
}
|
||||
}
|
||||
|
||||
private void shoot(WeaponMount mount, float x, float y, float aimX, float aimY, float mountX, float mountY, float rotation, int side){
|
||||
Weapon weapon = mount.weapon;
|
||||
|
||||
float baseX = this.x, baseY = this.y;
|
||||
boolean delay = weapon.firstShotDelay + weapon.shotDelay > 0f;
|
||||
|
||||
(delay ? weapon.chargeSound : weapon.continuous ? Sounds.none : weapon.shootSound).at(x, y, Mathf.random(weapon.soundPitchMin, weapon.soundPitchMax));
|
||||
|
||||
BulletType ammo = weapon.bullet;
|
||||
float lifeScl = ammo.scaleVelocity ? Mathf.clamp(Mathf.dst(x, y, aimX, aimY) / ammo.range()) : 1f;
|
||||
|
||||
sequenceNum = 0;
|
||||
if(delay){
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> {
|
||||
Time.run(sequenceNum * weapon.shotDelay + weapon.firstShotDelay, () -> {
|
||||
if(!isAdded()) return;
|
||||
mount.bullet = bullet(weapon, x + this.x - baseX, y + this.y - baseY, f + Mathf.range(weapon.inaccuracy), lifeScl);
|
||||
});
|
||||
sequenceNum++;
|
||||
});
|
||||
}else{
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> mount.bullet = bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy), lifeScl));
|
||||
}
|
||||
|
||||
boolean parentize = ammo.keepVelocity;
|
||||
|
||||
if(delay){
|
||||
Time.run(weapon.firstShotDelay, () -> {
|
||||
if(!isAdded()) return;
|
||||
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
|
||||
Effect.shake(weapon.shake, weapon.shake, x, y);
|
||||
mount.heat = 1f;
|
||||
if(!weapon.continuous){
|
||||
weapon.shootSound.at(x, y, Mathf.random(weapon.soundPitchMin, weapon.soundPitchMax));
|
||||
}
|
||||
});
|
||||
}else{
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
|
||||
Effect.shake(weapon.shake, weapon.shake, x, y);
|
||||
mount.heat = 1f;
|
||||
}
|
||||
|
||||
weapon.ejectEffect.at(mountX, mountY, rotation * side);
|
||||
ammo.shootEffect.at(x, y, rotation, parentize ? this : null);
|
||||
ammo.smokeEffect.at(x, y, rotation, parentize ? this : null);
|
||||
apply(weapon.shootStatus, weapon.shootStatusDuration);
|
||||
}
|
||||
|
||||
private Bullet bullet(Weapon weapon, float x, float y, float angle, float lifescl){
|
||||
float xr = Mathf.range(weapon.xRand);
|
||||
|
||||
return weapon.bullet.create(this, team(),
|
||||
x + Angles.trnsx(angle, 0, xr),
|
||||
y + Angles.trnsy(angle, 0, xr),
|
||||
angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd), lifescl);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user