Partial 7.0 merge - API preview

This commit is contained in:
Anuken
2021-06-02 11:08:08 -04:00
parent ea75a357ca
commit 28b235ef07
531 changed files with 12356 additions and 6286 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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),

View File

@@ -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);
}
}
}

View File

@@ -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());

View File

@@ -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);
}
}

View File

@@ -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. **/

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}