it never ends
This commit is contained in:
@@ -1,19 +1,17 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -31,8 +29,8 @@ public class Damage{
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
|
||||
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
|
||||
int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20);
|
||||
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3,
|
||||
x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
//TODO uncomment
|
||||
//Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), branches + Mathf.range(2)));
|
||||
}
|
||||
|
||||
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
|
||||
@@ -68,7 +66,8 @@ public class Damage{
|
||||
float cy = y + Mathf.range(range);
|
||||
Tile tile = world.tileWorld(cx, cy);
|
||||
if(tile != null){
|
||||
Fire.create(tile);
|
||||
//TODO uncomment
|
||||
//Fire.create(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +233,7 @@ public class Damage{
|
||||
if(scaledDamage <= 0 || tile == null) continue;
|
||||
|
||||
//apply damage to entity if needed
|
||||
if(tile.entity != null && tile.getTeam() != team){
|
||||
if(tile.entity != null && tile.team() != team){
|
||||
int health = (int)tile.entity.health();
|
||||
if(tile.entity.health() > 0){
|
||||
tile.entity.damage(scaledDamage);
|
||||
@@ -257,7 +256,7 @@ public class Damage{
|
||||
for(int dx = -trad; dx <= trad; dx++){
|
||||
for(int dy = -trad; dy <= trad; dy++){
|
||||
Tile tile = world.tile(Math.round(x / tilesize) + dx, Math.round(y / tilesize) + dy);
|
||||
if(tile != null && tile.entity != null && (team == null ||team.isEnemy(tile.getTeam())) && Mathf.dst(dx, dy) <= trad){
|
||||
if(tile != null && tile.entity != null && (team == null ||team.isEnemy(tile.team())) && Mathf.dst(dx, dy) <= trad){
|
||||
tile.entity.damage(damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,10 @@ public class Effect{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public <T> T data(){
|
||||
return (T)data;
|
||||
}
|
||||
|
||||
public void scaled(float lifetime, Cons<EffectContainer> cons){
|
||||
if(innerContainer == null) innerContainer = new EffectContainer();
|
||||
if(time <= lifetime){
|
||||
|
||||
@@ -5,16 +5,19 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Represents a group of a certain type of entity.*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityGroup<T extends Entityc>{
|
||||
public class EntityGroup<T extends Entityc> implements Iterable<T>{
|
||||
private final Array<T> array = new Array<>(false, 32);
|
||||
private final Array<T> intersectArray = new Array<>();
|
||||
private final Rect intersectRect = new Rect();
|
||||
private IntMap<T> map;
|
||||
private QuadTree tree;
|
||||
private boolean clearing;
|
||||
|
||||
private int index;
|
||||
|
||||
@@ -44,6 +47,13 @@ public class EntityGroup<T extends Entityc>{
|
||||
}
|
||||
}
|
||||
|
||||
public void each(Boolf<T> filter, Cons<T> cons){
|
||||
T[] items = array.items;
|
||||
for(index = 0; index < array.size; index++){
|
||||
if(filter.get(items[index])) cons.get(items[index]);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean useTree(){
|
||||
return map != null;
|
||||
}
|
||||
@@ -101,6 +111,10 @@ public class EntityGroup<T extends Entityc>{
|
||||
return array.size;
|
||||
}
|
||||
|
||||
public boolean contains(Boolf<T> pred){
|
||||
return array.contains(pred);
|
||||
}
|
||||
|
||||
public int count(Boolf<T> pred){
|
||||
return array.count(pred);
|
||||
}
|
||||
@@ -110,11 +124,12 @@ public class EntityGroup<T extends Entityc>{
|
||||
array.add(type);
|
||||
|
||||
if(mappingEnabled()){
|
||||
map.put(type.getId(), type);
|
||||
map.put(type.id(), type);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(T type){
|
||||
if(clearing) return;
|
||||
if(type == null) throw new RuntimeException("Cannot remove a null entity!");
|
||||
int idx = array.indexOf(type, true);
|
||||
if(idx != -1){
|
||||
@@ -128,12 +143,22 @@ public class EntityGroup<T extends Entityc>{
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
clearing = true;
|
||||
|
||||
array.each(Entityc::remove);
|
||||
array.clear();
|
||||
if(map != null)
|
||||
map.clear();
|
||||
|
||||
clearing = false;
|
||||
}
|
||||
|
||||
public T find(Boolf<T> pred){
|
||||
return array.find(pred);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator(){
|
||||
return array.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
86
core/src/mindustry/entities/Lightning.java
Normal file
86
core/src/mindustry/entities/Lightning.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO move into a different class
|
||||
public class Lightning{
|
||||
private static final Rand random = new Rand();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Array<Unitc> entities = new Array<>();
|
||||
private static final IntSet hit = new IntSet();
|
||||
private static final int maxChain = 8;
|
||||
private static final float hitRange = 30f;
|
||||
private static boolean bhit = false;
|
||||
private static int lastSeed = 0;
|
||||
|
||||
/** Create a lighting branch at a location. Use Team.none to damage everyone. */
|
||||
public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){
|
||||
createLightingInternal(lastSeed++, team, color, damage, x, y, targetAngle, length);
|
||||
}
|
||||
|
||||
//TODO remote method
|
||||
//@Remote(called = Loc.server, unreliable = true)
|
||||
private static void createLightingInternal(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
|
||||
random.setSeed(seed);
|
||||
hit.clear();
|
||||
|
||||
Array<Vec2> lines = new Array<>();
|
||||
bhit = false;
|
||||
|
||||
for(int i = 0; i < length / 2; i++){
|
||||
Bullets.damageLightning.create(null, team, x, y, 0f, damage, 1f, 1f, null);
|
||||
lines.add(new Vec2(x + Mathf.range(3f), y + Mathf.range(3f)));
|
||||
|
||||
if(lines.size > 1){
|
||||
bhit = false;
|
||||
Vec2 from = lines.get(lines.size - 2);
|
||||
Vec2 to = lines.get(lines.size - 1);
|
||||
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
|
||||
|
||||
Tile tile = world.ltile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
bhit = true;
|
||||
//snap it instead of removing
|
||||
lines.get(lines.size -1).set(wx * tilesize, wy * tilesize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(bhit) break;
|
||||
}
|
||||
|
||||
rect.setSize(hitRange).setCenter(x, y);
|
||||
entities.clear();
|
||||
if(hit.size < maxChain){
|
||||
Units.nearbyEnemies(team, rect, u -> {
|
||||
if(!hit.contains(u.id())){
|
||||
entities.add(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Unitc furthest = Geometry.findFurthest(x, y, entities);
|
||||
|
||||
if(furthest != null){
|
||||
hit.add(furthest.id());
|
||||
x = furthest.x();
|
||||
y = furthest.y();
|
||||
}else{
|
||||
rotation += random.range(20f);
|
||||
x += Angles.trnsx(rotation, hitRange / 2f);
|
||||
y += Angles.trnsy(rotation, hitRange / 2f);
|
||||
}
|
||||
}
|
||||
|
||||
Fx.lightning.at(x, y, rotation, color, lines);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package mindustry.entities;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/**
|
||||
* Class for predicting shoot angles based on velocities of targets.
|
||||
*/
|
||||
@@ -52,8 +54,8 @@ public class Predict{
|
||||
/**
|
||||
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
||||
*/
|
||||
public static Vec2 intercept(Teamc src, Teamc dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/(2f*Time.delta()), dst.getTargetVelocityY() - src.getTargetVelocityY()/(2f*Time.delta()), v);
|
||||
public static Vec2 intercept(Hitboxc src, Hitboxc dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.deltaX() - src.deltaX()/(2f*Time.delta()), dst.deltaY() - src.deltaX()/(2f*Time.delta()), v);
|
||||
}
|
||||
|
||||
private static Vec2 quad(float a, float b, float c){
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
@@ -41,8 +39,8 @@ public class Units{
|
||||
}
|
||||
|
||||
/** See {@link #invalidateTarget(Teamc, Team, float, float, float)} */
|
||||
public static boolean invalidateTarget(Teamc target, Unitc targeter){
|
||||
return invalidateTarget(target, targeter.team(), targeter.x, targeter.y, targeter.getWeapon().bullet.range());
|
||||
public static boolean invalidateTarget(Teamc target, Unitc targeter, float range){
|
||||
return invalidateTarget(target, targeter.team(), targeter.x(), targeter.y(), range);
|
||||
}
|
||||
|
||||
/** Returns whether there are any entities on this tile. */
|
||||
@@ -56,7 +54,7 @@ public class Units{
|
||||
|
||||
nearby(x, y, width, height, unit -> {
|
||||
if(boolResult) return;
|
||||
if(!unit.isFlying()){
|
||||
if(unit.isGrounded()){
|
||||
unit.hitbox(hitrect);
|
||||
|
||||
if(hitrect.overlaps(x, y, width, height)){
|
||||
@@ -118,7 +116,7 @@ public class Units{
|
||||
nearbyEnemies(team, x - range, y - range, range*2f, range*2f, e -> {
|
||||
if(e.dead() || !predicate.get(e)) return;
|
||||
|
||||
float dst2 = Mathf.dst2(e.x, e.y, x, y);
|
||||
float dst2 = e.dst2(x, y);
|
||||
if(dst2 < range*range && (result == null || dst2 < cdist)){
|
||||
result = e;
|
||||
cdist = dst2;
|
||||
@@ -136,7 +134,7 @@ public class Units{
|
||||
nearby(team, x, y, range, e -> {
|
||||
if(!predicate.get(e)) return;
|
||||
|
||||
float dist = Mathf.dst2(e.x, e.y, x, y);
|
||||
float dist = e.dst2(x, y);
|
||||
if(result == null || dist < cdist){
|
||||
result = e;
|
||||
cdist = dist;
|
||||
@@ -149,7 +147,7 @@ public class Units{
|
||||
/** Iterates over all units in a rectangle. */
|
||||
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x, y, width, height, u -> {
|
||||
if(u.getTeam() == team){
|
||||
if(u.team() == team){
|
||||
cons.get(u);
|
||||
}
|
||||
});
|
||||
@@ -158,7 +156,7 @@ public class Units{
|
||||
/** Iterates over all units in a circle around this position. */
|
||||
public static void nearby(Team team, float x, float y, float radius, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> {
|
||||
if(unit.getTeam() == team && unit.withinDst(x, y, radius)){
|
||||
if(unit.team() == team && unit.withinDst(x, y, radius)){
|
||||
cons.get(unit);
|
||||
}
|
||||
});
|
||||
@@ -177,7 +175,7 @@ public class Units{
|
||||
/** Iterates over all units that are enemies of this team. */
|
||||
public static void nearbyEnemies(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
|
||||
Groups.unit.intersect(x, y, width, height, u -> {
|
||||
if(team.isEnemy(u.getTeam())){
|
||||
if(team.isEnemy(u.team())){
|
||||
cons.get(u);
|
||||
}
|
||||
});
|
||||
@@ -188,13 +186,4 @@ public class Units{
|
||||
nearbyEnemies(team, rect.x, rect.y, rect.width, rect.height, cons);
|
||||
}
|
||||
|
||||
/** Iterates over all units. */
|
||||
public static void all(Cons<Unitc> cons){
|
||||
Groups.unit.all().each(cons);
|
||||
}
|
||||
|
||||
public static void each(Team team, Cons<Unitc> cons){
|
||||
Groups.unit.all().each(t -> t.getTeam() == team, cons);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class ArtilleryBulletType extends BasicBulletType{
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
|
||||
if(b.timer.get(0, 3 + b.fslope() * 2f)){
|
||||
if(b.timer(0, 3 + b.fslope() * 2f)){
|
||||
trailEffect.at(b.x(), b.y(), b.fslope() * 4f, backColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package mindustry.entities.bullet;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.math.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -109,7 +109,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void hit(Bulletc b, float x, float y){
|
||||
hitEffect.at(x, y, b.getRotation());
|
||||
hitEffect.at(x, y, b.rotation());
|
||||
hitSound.at(b);
|
||||
|
||||
Effects.shake(hitShake, hitShake, b);
|
||||
@@ -132,7 +132,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void despawned(Bulletc b){
|
||||
despawnEffect.at(b.getX(), b.getY(), b.getRotation());
|
||||
despawnEffect.at(b.getX(), b.getY(), b.rotation());
|
||||
hitSound.at(b);
|
||||
|
||||
if(fragBullet != null || splashDamageRadius > 0){
|
||||
@@ -140,7 +140,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
for(int i = 0; i < lightining; i++){
|
||||
Lightning.createLighting(Lightning.nextSeed(), b.team(), Pal.surge, damage, b.getX(), b.getY(), Mathf.random(360f), lightningLength);
|
||||
Lightning.create(b.team(), Pal.surge, damage, b.getX(), b.getY(), Mathf.random(360f), lightningLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,20 +148,20 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void init(Bulletc b){
|
||||
if(killShooter && b.getOwner() instanceof Healthc){
|
||||
((Healthc)b.getOwner()).kill();
|
||||
if(killShooter && b.owner() instanceof Healthc){
|
||||
((Healthc)b.owner()).kill();
|
||||
}
|
||||
|
||||
if(instantDisappear){
|
||||
b.setTime(lifetime);
|
||||
b.time(lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Bulletc b){
|
||||
if(homingPower > 0.0001f){
|
||||
Teamc target = Units.closestTarget(b.team(), b.getX(), b.getY(), homingRange, e -> !e.isFlying() || collidesAir);
|
||||
Teamc target = Units.closestTarget(b.team(), b.getX(), b.getY(), homingRange, e -> e.isGrounded() || collidesAir);
|
||||
if(target != null){
|
||||
b.vel().setAngle(Mathf.slerpDelta(b.getRotation(), b.angleTo(target), 0.08f));
|
||||
b.vel().setAngle(Mathf.slerpDelta(b.rotation(), b.angleTo(target), 0.08f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,23 +182,24 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle, float velocityScl){
|
||||
return create(owner, team, x, y, angle, velocityScl, 1f, null);
|
||||
return create(owner, team, x, y, angle, -1, velocityScl, 1f, null);
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl){
|
||||
return create(owner, team, x, y, angle, velocityScl, lifetimeScl, null);
|
||||
return create(owner, team, x, y, angle, -1, velocityScl, lifetimeScl, null);
|
||||
}
|
||||
|
||||
public Bulletc create(Bulletc parent, float x, float y, float angle){
|
||||
return create(parent.getOwner(), parent.team(), x, y, angle);
|
||||
return create(parent.owner(), parent.team(), x, y, angle);
|
||||
}
|
||||
|
||||
public Bulletc create(Bulletc parent, float x, float y, float angle, float velocityScl){
|
||||
return create(parent.getOwner(), parent.team(), x, y, angle, velocityScl);
|
||||
return create(parent.owner(), parent.team(), x, y, angle, velocityScl);
|
||||
}
|
||||
|
||||
public Bulletc create(Entityc owner, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl, Object data){
|
||||
public Bulletc create(@Nullable Entityc owner, Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl, Object data){
|
||||
|
||||
//TODO assign type damage is damage <0, else assign provided damage
|
||||
|
||||
//TODO implement
|
||||
return null;
|
||||
@@ -223,12 +224,12 @@ public abstract class BulletType extends Content{
|
||||
return bullet;*/
|
||||
}
|
||||
|
||||
public void createNet(Team team, float x, float y, float angle, float velocityScl, float lifetimeScl){
|
||||
Call.createBullet(this, team, x, y, angle, velocityScl, lifetimeScl);
|
||||
public void createNet(Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl){
|
||||
Call.createBullet(this, team, x, y, damage, angle, velocityScl, lifetimeScl);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void createBullet(BulletType type, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl){
|
||||
type.create(null, team, x, y, angle, velocityScl, lifetimeScl, null);
|
||||
public static void createBullet(BulletType type, Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl){
|
||||
type.create(null, team, x, y, angle, damage, velocityScl, lifetimeScl, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,16 +26,16 @@ public class FlakBulletType extends BasicBulletType{
|
||||
@Override
|
||||
public void update(Bulletc b){
|
||||
super.update(b);
|
||||
if(b.getData() instanceof Integer) return;
|
||||
if(b.data() instanceof Integer) return;
|
||||
|
||||
if(b.timer.get(2, 6)){
|
||||
if(b.timer(2, 6)){
|
||||
Units.nearbyEnemies(b.team(), rect.setSize(explodeRange * 2f).setCenter(b.x(), b.y()), unit -> {
|
||||
if(b.getData() instanceof Float) return;
|
||||
if(b.data() instanceof Float) return;
|
||||
|
||||
if(unit.dst(b) < explodeRange){
|
||||
b.setData(0);
|
||||
b.data(0);
|
||||
Time.run(5f, () -> {
|
||||
if(b.getData() instanceof Integer){
|
||||
if(b.data() instanceof Integer){
|
||||
b.time(b.lifetime());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ public class HealBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public boolean collides(Bulletc b, Tile tile){
|
||||
return tile.getTeam() != b.team() || tile.entity.healthf() < 1f;
|
||||
return tile.team() != b.team() || tile.entity.healthf() < 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,9 +47,9 @@ public class HealBulletType extends BulletType{
|
||||
super.hit(b);
|
||||
tile = tile.link();
|
||||
|
||||
if(tile.entity != null && tile.getTeam() == b.team() && !(tile.block() instanceof BuildBlock)){
|
||||
if(tile.entity != null && tile.team() == b.team() && !(tile.block() instanceof BuildBlock)){
|
||||
Fx.healBlockFull.at(tile.drawx(), tile.drawy(), tile.block().size, Pal.heal);
|
||||
tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth());
|
||||
tile.entity.heal(healPercent / 100f * tile.entity.maxHealth());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class LaserBulletType extends BulletType{
|
||||
Lines.stroke((cwidth *= lengthFalloff) * b.fout());
|
||||
Lines.lineAngle(b.x(), b.y(), b.rotation(), baseLen, CapStyle.none);
|
||||
Tmp.v1.trns(b.rotation(), baseLen);
|
||||
Drawf.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, Lines.getStroke() * 1.22f, cwidth * 2f + width / 2f, b.rotation());
|
||||
Drawf.tri(b.x() + Tmp.v1.x, b.y() + Tmp.v1.y, Lines.getStroke() * 1.22f, cwidth * 2f + width / 2f, b.rotation());
|
||||
|
||||
Fill.circle(b.x(), b.y(), 1f * cwidth * b.fout());
|
||||
for(int i : Mathf.signs){
|
||||
|
||||
@@ -2,7 +2,7 @@ package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
@@ -66,7 +65,7 @@ public class LiquidBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void hit(Bulletc b, float hitx, float hity){
|
||||
hitEffect.at(liquid.color, hitx, hity);
|
||||
hitEffect.at(hitx, hity, liquid.color);
|
||||
Puddle.deposit(world.tileWorld(hitx, hity), liquid, puddleSize);
|
||||
|
||||
if(liquid.temperature <= 0.5f && liquid.flammability < 0.3f){
|
||||
|
||||
@@ -38,17 +38,17 @@ public class MassDriverBolt extends BulletType{
|
||||
@Override
|
||||
public void update(Bulletc b){
|
||||
//data MUST be an instance of DriverBulletData
|
||||
if(!(b.getData() instanceof DriverBulletData)){
|
||||
if(!(b.data() instanceof DriverBulletData)){
|
||||
hit(b);
|
||||
return;
|
||||
}
|
||||
|
||||
float hitDst = 7f;
|
||||
|
||||
DriverBulletData data = (DriverBulletData)b.getData();
|
||||
DriverBulletData data = (DriverBulletData)b.data();
|
||||
|
||||
//if the target is dead, just keep flying until the bullet explodes
|
||||
if(data.to.isDead()){
|
||||
if(data.to.dead()){
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class MassDriverBolt extends BulletType{
|
||||
if(Angles.near(angleTo, baseAngle, 2f)){
|
||||
intersect = true;
|
||||
//snap bullet position back; this is used for low-FPS situations
|
||||
b.set(data.to.x + Angles.trnsx(baseAngle, hitDst), data.to.y + Angles.trnsy(baseAngle, hitDst));
|
||||
b.set(data.to.x() + Angles.trnsx(baseAngle, hitDst), data.to.y() + Angles.trnsy(baseAngle, hitDst));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,9 +85,9 @@ public class MassDriverBolt extends BulletType{
|
||||
public void despawned(Bulletc b){
|
||||
super.despawned(b);
|
||||
|
||||
if(!(b.getData() instanceof DriverBulletData)) return;
|
||||
if(!(b.data() instanceof DriverBulletData)) return;
|
||||
|
||||
DriverBulletData data = (DriverBulletData)b.getData();
|
||||
DriverBulletData data = (DriverBulletData)b.data();
|
||||
|
||||
for(int i = 0; i < data.items.length; i++){
|
||||
int amountDropped = Mathf.random(0, data.items[i]);
|
||||
|
||||
@@ -34,7 +34,7 @@ public class MissileBulletType extends BasicBulletType{
|
||||
}
|
||||
|
||||
if(weaveMag > 0){
|
||||
b.vel().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag) * Time.delta());
|
||||
b.vel().rotate(Mathf.sin(Time.time() + b.id() * 442, weaveScale, weaveMag) * Time.delta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
@@ -177,21 +177,12 @@ public class EntityComps{
|
||||
}
|
||||
|
||||
@Component
|
||||
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc{
|
||||
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{
|
||||
private float lifeScl;
|
||||
|
||||
Object data;
|
||||
BulletType type;
|
||||
Interval timer = new Interval(6);
|
||||
|
||||
public boolean timer(int index, float time){
|
||||
return timer.get(index, time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDamage(){
|
||||
return type.damage;
|
||||
}
|
||||
float damage;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
@@ -258,8 +249,8 @@ public class EntityComps{
|
||||
Tile tile = world.ltile(x, y);
|
||||
if(tile == null) return false;
|
||||
|
||||
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.dead() && (type.collidesTeam || tile.getTeam() != team())){
|
||||
if(tile.getTeam() != team()){
|
||||
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.dead() && (type.collidesTeam || tile.team() != team())){
|
||||
if(tile.team() != team()){
|
||||
tile.entity.collision(this);
|
||||
}
|
||||
|
||||
@@ -295,6 +286,15 @@ public class EntityComps{
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
abstract class TimerComp{
|
||||
@ReadOnly Interval timer = new Interval(6);
|
||||
|
||||
public boolean timer(int index, float time){
|
||||
return timer.get(index, time);
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
abstract class DamageComp{
|
||||
abstract float damage();
|
||||
@@ -590,7 +590,7 @@ public class EntityComps{
|
||||
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
|
||||
|
||||
if(other == null) continue;
|
||||
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
|
||||
if(other.entity == null || !(other.interactable(tile.team()))) continue;
|
||||
|
||||
//add this tile to proximity of nearby tiles
|
||||
if(!other.entity.proximity().contains(tile, true)){
|
||||
@@ -671,7 +671,7 @@ public class EntityComps{
|
||||
}
|
||||
|
||||
@Component
|
||||
abstract class PlayerComp implements UnitController, Entityc, Syncc{
|
||||
abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc{
|
||||
@Nullable Unitc unit;
|
||||
|
||||
@ReadOnly Team team = Team.sharded;
|
||||
@@ -684,6 +684,19 @@ public class EntityComps{
|
||||
@Nullable String lastText;
|
||||
float textFadeTime;
|
||||
|
||||
public @Nullable Tilec closestCore(){
|
||||
return state.teams.closestCore(x(), y(), team);
|
||||
}
|
||||
|
||||
public void reset(){
|
||||
team = state.rules.defaultTeam;
|
||||
admin = typing = false;
|
||||
lastText = null;
|
||||
textFadeTime = 0f;
|
||||
unit.controller(unit.type().createController());
|
||||
unit = null;
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(unit != null){
|
||||
x(unit.x());
|
||||
@@ -704,12 +717,16 @@ public class EntityComps{
|
||||
unit.team(team);
|
||||
}
|
||||
|
||||
boolean dead(){
|
||||
return unit == null;
|
||||
}
|
||||
|
||||
String uuid(){
|
||||
return con == null ? "AAAAAAAA" : con.uuid;
|
||||
return con == null ? "[LOCAL]" : con.uuid;
|
||||
}
|
||||
|
||||
String usid(){
|
||||
return con == null ? "AAAAAAAA" : con.usid;
|
||||
return con == null ? "[LOCAL]" : con.usid;
|
||||
}
|
||||
|
||||
void kick(KickReason reason){
|
||||
@@ -801,7 +818,7 @@ public class EntityComps{
|
||||
}
|
||||
|
||||
@Component
|
||||
abstract class TeamComp implements Posc, Healthc{
|
||||
abstract class TeamComp implements Posc{
|
||||
transient float x, y;
|
||||
|
||||
Team team = Team.sharded;
|
||||
@@ -944,8 +961,7 @@ public class EntityComps{
|
||||
|
||||
private void bullet(Weapon weapon, float x, float y, float angle){
|
||||
Tmp.v1.trns(angle, 3f);
|
||||
//TODO create the bullet
|
||||
//Bullet.create(weapon.bullet, this, getTeam(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd));
|
||||
weapon.bullet.create(this, team(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1202,7 +1218,7 @@ public class EntityComps{
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core.tile());
|
||||
}else if(acceptsItem(item)){
|
||||
//this is clientside, since items are synced anyway
|
||||
ItemTransfer.transferItemToUnit(item,
|
||||
InputHandler.transferItemToUnit(item,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f),
|
||||
this);
|
||||
@@ -1480,6 +1496,16 @@ public class EntityComps{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
@@ -1591,8 +1617,7 @@ public class EntityComps{
|
||||
return;
|
||||
}else if(entry.effect.reactsWith(effect)){ //find opposite
|
||||
StatusEntry.tmp.effect = entry.effect;
|
||||
//TODO unit cannot be null here
|
||||
entry.effect.getTransition((mindustry.gen.Unitc)this, effect, entry.time, duration, StatusEntry.tmp);
|
||||
entry.effect.getTransition((Unitc)this, effect, entry.time, duration, StatusEntry.tmp);
|
||||
entry.time = StatusEntry.tmp.time;
|
||||
|
||||
if(StatusEntry.tmp.effect != entry.effect){
|
||||
@@ -1611,6 +1636,10 @@ public class EntityComps{
|
||||
statuses.add(entry);
|
||||
}
|
||||
|
||||
boolean isBoss(){
|
||||
return hasEffect(StatusEffects.boss);
|
||||
}
|
||||
|
||||
boolean isImmune(StatusEffect effect){
|
||||
return false;
|
||||
}
|
||||
@@ -1714,7 +1743,7 @@ public class EntityComps{
|
||||
}
|
||||
|
||||
boolean isLocal(){
|
||||
return this instanceof Unitc && ((Unitc)this).controller() == player || this == player;
|
||||
return ((Object)this) == player || ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).controller() == player;
|
||||
}
|
||||
|
||||
<T> T as(Class<T> type){
|
||||
|
||||
@@ -5,12 +5,17 @@ import mindustry.entities.def.EntityComps.*;
|
||||
|
||||
public class EntityGroupDefs{
|
||||
|
||||
@GroupDef(EntityComp.class)
|
||||
void all(){
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(PlayerComp.class)
|
||||
void player(){
|
||||
|
||||
}
|
||||
|
||||
@GroupDef(UnitComp.class)
|
||||
@GroupDef(value = UnitComp.class, spatial = true)
|
||||
void unit(){
|
||||
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
|
||||
private static final IntMap<Fire> map = new IntMap<>();
|
||||
private static final float baseLifetime = 1000f, spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
|
||||
private int loadedPosition = -1;
|
||||
private Tile tile;
|
||||
private Block block;
|
||||
private float baseFlammability = -1, puddleFlammability;
|
||||
private float lifetime;
|
||||
|
||||
/** Deserialization use only! */
|
||||
public Fire(){
|
||||
}
|
||||
|
||||
@Remote
|
||||
public static void onRemoveFire(int fid){
|
||||
fireGroup.removeByID(fid);
|
||||
}
|
||||
|
||||
/** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */
|
||||
public static void create(Tile tile){
|
||||
if(net.client() || tile == null) return; //not clientside.
|
||||
|
||||
Fire fire = map.get(tile.pos());
|
||||
|
||||
if(fire == null){
|
||||
fire = new Fire();
|
||||
fire.tile = tile;
|
||||
fire.lifetime = baseLifetime;
|
||||
fire.set(tile.worldx(), tile.worldy());
|
||||
fire.add();
|
||||
map.put(tile.pos(), fire);
|
||||
}else{
|
||||
fire.lifetime = baseLifetime;
|
||||
fire.time = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean has(int x, int y){
|
||||
if(!Structs.inBounds(x, y, world.width(), world.height()) || !map.containsKey(Pos.get(x, y))){
|
||||
return false;
|
||||
}
|
||||
Fire fire = map.get(Pos.get(x, y));
|
||||
return fire.isAdded() && fire.fin() < 1f && fire.tile != null && fire.tile.x == x && fire.tile.y == y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.
|
||||
*/
|
||||
public static void extinguish(Tile tile, float intensity){
|
||||
if(tile != null && map.containsKey(tile.pos())){
|
||||
Fire fire = map.get(tile.pos());
|
||||
fire.time += intensity * Time.delta();
|
||||
if(fire.time >= fire.lifetime()){
|
||||
Events.fire(Trigger.fireExtinguish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return TypeIDs.fire;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
@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());
|
||||
map.put(tile.pos(), this);
|
||||
|
||||
if(net.client()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(time >= lifetime() || tile == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
Tilec entity = tile.link().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.block().getFlammability(tile);
|
||||
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);
|
||||
create(other);
|
||||
|
||||
if(Mathf.chance(fireballChance * Time.delta() * Mathf.clamp(flammability / 10f))){
|
||||
Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta())){
|
||||
Puddle p = Puddle.getPuddle(tile);
|
||||
if(p != null){
|
||||
puddleFlammability = p.getFlammability() / 3f;
|
||||
}else{
|
||||
puddleFlammability = 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.applyEffect(StatusEffects.burning, 60 * 5));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.pos());
|
||||
stream.writeFloat(lifetime);
|
||||
stream.writeFloat(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.lifetime = stream.readFloat();
|
||||
this.time = stream.readFloat();
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeInt(tile.pos());
|
||||
data.writeFloat(lifetime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
int pos = data.readInt();
|
||||
this.lifetime = data.readFloat();
|
||||
|
||||
x = Pos.x(pos) * tilesize;
|
||||
y = Pos.y(pos) * tilesize;
|
||||
tile = world.tile(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
baseFlammability = -1;
|
||||
puddleFlammability = 0f;
|
||||
incrementID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
set(tile.worldx(), tile.worldy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(tile != null){
|
||||
Call.onRemoveFire(id);
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return fireGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.effectGroup;
|
||||
|
||||
public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
private Vec2 from = new Vec2();
|
||||
private Vec2 current = new Vec2();
|
||||
private Vec2 tovec = new Vec2();
|
||||
private Item item;
|
||||
private float seed;
|
||||
private Position to;
|
||||
private Runnable done;
|
||||
|
||||
public ItemTransfer(){
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemEffect(Item item, float x, float y, Itemsc to){
|
||||
if(to == null) return;
|
||||
create(item, x, y, to, () -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemToUnit(Item item, float x, float y, Itemsc to){
|
||||
if(to == null) return;
|
||||
create(item, x, y, to, () -> to.addItem(item));
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
|
||||
if(tile == null || tile.entity == null || tile.entity.items() == null) return;
|
||||
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
|
||||
Time.run(i * 3, () -> create(item, x, y, tile, () -> {}));
|
||||
}
|
||||
tile.entity.items().add(item, amount);
|
||||
}
|
||||
|
||||
public static void create(Item item, float fromx, float fromy, Position to, Runnable done){
|
||||
ItemTransfer tr = Pools.obtain(ItemTransfer.class, ItemTransfer::new);
|
||||
tr.item = item;
|
||||
tr.from.set(fromx, fromy);
|
||||
tr.to = to;
|
||||
tr.done = done;
|
||||
tr.seed = Mathf.range(1f);
|
||||
tr.add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return 60;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
item = null;
|
||||
to = null;
|
||||
done = null;
|
||||
from.setZero();
|
||||
current.setZero();
|
||||
tovec.setZero();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(done != null){
|
||||
done.run();
|
||||
}
|
||||
Pools.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(to == null){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
super.update();
|
||||
current.set(from).interpolate(tovec.set(to.getX(), to.getY()), fin(), Interpolation.pow3);
|
||||
current.add(tovec.set(to.getX(), to.getY()).sub(from).nor().rotate90(1).scl(seed * fslope() * 10f));
|
||||
set(current.x, current.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(fslope() * 2f, Pal.accent);
|
||||
|
||||
Lines.circle(x, y, fslope() * 2f);
|
||||
|
||||
Draw.color(item.color);
|
||||
Fill.circle(x, y, fslope() * 1.5f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return effectGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO lightning should be an effect with custom logic + bullet with custom init logic
|
||||
public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
|
||||
public static final float lifetime = 10f;
|
||||
|
||||
private static final Rand random = new Rand();
|
||||
private static final Rect rect = new Rect();
|
||||
private static final Array<Unitc> entities = new Array<>();
|
||||
private static final IntSet hit = new IntSet();
|
||||
private static final int maxChain = 8;
|
||||
private static final float hitRange = 30f;
|
||||
private static int lastSeed = 0;
|
||||
|
||||
private Array<Vec2> lines = new Array<>();
|
||||
private Color color = Pal.lancerLaser;
|
||||
|
||||
/** For pooling use only. Do not call directly! */
|
||||
public Lightning(){
|
||||
}
|
||||
|
||||
/** Create a lighting branch at a location. Use Team.none to damage everyone. */
|
||||
public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){
|
||||
Call.createLighting(nextSeed(), team, color, damage, x, y, targetAngle, length);
|
||||
}
|
||||
|
||||
public static int nextSeed(){
|
||||
return lastSeed++;
|
||||
}
|
||||
|
||||
/** Do not invoke! */
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void createLighting(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
|
||||
|
||||
Lightning l = Pools.obtain(Lightning.class, Lightning::new);
|
||||
Float dmg = damage;
|
||||
|
||||
l.x = x;
|
||||
l.y = y;
|
||||
l.color = color;
|
||||
l.add();
|
||||
|
||||
random.setSeed(seed);
|
||||
hit.clear();
|
||||
|
||||
boolean[] bhit = {false};
|
||||
|
||||
for(int i = 0; i < length / 2; i++){
|
||||
Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg);
|
||||
l.lines.add(new Vec2(x + Mathf.range(3f), y + Mathf.range(3f)));
|
||||
|
||||
if(l.lines.size > 1){
|
||||
bhit[0] = false;
|
||||
Position from = l.lines.get(l.lines.size - 2);
|
||||
Position to = l.lines.get(l.lines.size - 1);
|
||||
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
|
||||
|
||||
Tile tile = world.ltile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
bhit[0] = true;
|
||||
//snap it instead of removing
|
||||
l.lines.get(l.lines.size -1).set(wx * tilesize, wy * tilesize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if(bhit[0]) break;
|
||||
}
|
||||
|
||||
rect.setSize(hitRange).setCenter(x, y);
|
||||
entities.clear();
|
||||
if(hit.size < maxChain){
|
||||
Units.nearbyEnemies(team, rect, u -> {
|
||||
if(!hit.contains(u.getID())){
|
||||
entities.add(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Unitc furthest = Geometry.findFurthest(x, y, entities);
|
||||
|
||||
if(furthest != null){
|
||||
hit.add(furthest.getID());
|
||||
x = furthest.x;
|
||||
y = furthest.y;
|
||||
}else{
|
||||
rotation += random.range(20f);
|
||||
|
||||
x += Angles.trnsx(rotation, hitRange / 2f);
|
||||
y += Angles.trnsy(rotation, hitRange / 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float lifetime(){
|
||||
return lifetime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
super.reset();
|
||||
color = Pal.lancerLaser;
|
||||
lines.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
super.removed();
|
||||
Pools.free(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Lines.stroke(3f * fout());
|
||||
Draw.color(color, Color.white, fin());
|
||||
Lines.beginLine();
|
||||
|
||||
Lines.linePoint(x, y);
|
||||
for(Position p : lines){
|
||||
Lines.linePoint(p.getX(), p.getY());
|
||||
}
|
||||
Lines.endLine();
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Position p : lines){
|
||||
Fill.square(p.getX(), p.getY(), (5f - (float)i++ / lines.size * 2f) * fout(), 45);
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return bulletGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
package mindustry.entities.effect;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.Pool.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait{
|
||||
private static final IntMap<Puddle> map = new IntMap<>();
|
||||
private static final float maxLiquid = 70f;
|
||||
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;
|
||||
|
||||
private int loadedPosition = -1;
|
||||
|
||||
private float updateTime;
|
||||
private float lastRipple;
|
||||
private Tile tile;
|
||||
private Liquid liquid;
|
||||
private float amount, targetAmount;
|
||||
private float accepting;
|
||||
private byte generation;
|
||||
|
||||
/** Deserialization use only! */
|
||||
public Puddle(){
|
||||
}
|
||||
|
||||
/** Deposists a puddle between tile and source. */
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
deposit(tile, source, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Deposists a puddle at a tile. */
|
||||
public static void deposit(Tile tile, Liquid liquid, float amount){
|
||||
deposit(tile, tile, liquid, amount, 0);
|
||||
}
|
||||
|
||||
/** Returns the puddle on the specified tile. May return null. */
|
||||
public static Puddle getPuddle(Tile tile){
|
||||
return map.get(tile.pos());
|
||||
}
|
||||
|
||||
private static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){
|
||||
if(tile == null) return;
|
||||
|
||||
if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){
|
||||
reactPuddle(tile.floor().liquidDrop, liquid, amount, tile,
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
|
||||
if(generation == 0 && p != null && p.lastRipple <= Time.time() - 40f){
|
||||
Fx.ripple.at(tile.floor().liquidDrop.color,
|
||||
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
p.lastRipple = Time.time();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
if(p == null){
|
||||
if(net.client()) return; //not clientside.
|
||||
|
||||
Puddle puddle = Pools.obtain(Puddle.class, Puddle::new);
|
||||
puddle.tile = tile;
|
||||
puddle.liquid = liquid;
|
||||
puddle.amount = amount;
|
||||
puddle.generation = (byte)generation;
|
||||
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
puddle.add();
|
||||
map.put(tile.pos(), puddle);
|
||||
}else if(p.liquid == liquid){
|
||||
p.accepting = Math.max(amount, p.accepting);
|
||||
|
||||
if(generation == 0 && p.lastRipple <= Time.time() - 40f && p.amount >= maxLiquid / 2f){
|
||||
Fx.ripple.at(p.liquid.color, (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
p.lastRipple = Time.time();
|
||||
}
|
||||
}else{
|
||||
p.amount += reactPuddle(p.liquid, liquid, amount, p.tile, p.x, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the first liquid can 'stay' on the second one.
|
||||
* Currently, the only place where this can happen is oil on water.
|
||||
*/
|
||||
private static boolean canStayOn(Liquid liquid, Liquid other){
|
||||
return liquid == Liquids.oil && other == Liquids.water;
|
||||
}
|
||||
|
||||
/** Reacts two liquids together at a location. */
|
||||
private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){
|
||||
if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
|
||||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
|
||||
Fire.create(tile);
|
||||
if(Mathf.chance(0.006 * amount)){
|
||||
Call.createBullet(Bullets.fireball, Team.derelict, x, y, Mathf.random(360f), 1f, 1f);
|
||||
}
|
||||
}else if(dest.temperature > 0.7f && liquid.temperature < 0.55f){ //cold liquid poured onto hot puddle
|
||||
if(Mathf.chance(0.5f * amount)){
|
||||
Fx.steam.at(x, y);
|
||||
}
|
||||
return -0.1f * amount;
|
||||
}else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold puddle
|
||||
if(Mathf.chance(0.8f * amount)){
|
||||
Fx.steam.at(x, y);
|
||||
}
|
||||
return -0.4f * amount;
|
||||
}
|
||||
return 0f;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void onPuddleRemoved(int puddleid){
|
||||
puddleGroup.removeByID(puddleid);
|
||||
}
|
||||
|
||||
public float getFlammability(){
|
||||
return liquid.flammability * amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return TypeIDs.puddle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setCenter(x, y).setSize(tilesize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitboxTile(Rect rect){
|
||||
rect.setCenter(x, y).setSize(0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
//no updating happens clientside
|
||||
if(net.client()){
|
||||
amount = Mathf.lerpDelta(amount, targetAmount, 0.15f);
|
||||
}else{
|
||||
//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){
|
||||
deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down puddle propagation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if(amount <= 0f){
|
||||
Call.onPuddleRemoved(getID());
|
||||
}
|
||||
}
|
||||
|
||||
//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.isFlying()) return;
|
||||
|
||||
unit.hitbox(rect2);
|
||||
if(!rect.overlaps(rect2)) return;
|
||||
|
||||
unit.applyEffect(liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.vel().len() > 0.1){
|
||||
Fx.ripple.at(liquid.color, unit.x, unit.y);
|
||||
}
|
||||
});
|
||||
|
||||
if(liquid.temperature > 0.7f && (tile.link().entity != null) && Mathf.chance(0.3 * Time.delta())){
|
||||
Fire.create(tile);
|
||||
}
|
||||
|
||||
updateTime = 20f;
|
||||
}
|
||||
|
||||
updateTime -= Time.delta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
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;
|
||||
renderer.lights.add(tile.drawx(), tile.drawy(), 30f * f, color, opacity * 0.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drawSize(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
stream.writeInt(tile.pos());
|
||||
stream.writeFloat(x);
|
||||
stream.writeFloat(y);
|
||||
stream.writeByte(liquid.id);
|
||||
stream.writeFloat(amount);
|
||||
stream.writeByte(generation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSave(DataInput stream, byte version) throws IOException{
|
||||
this.loadedPosition = stream.readInt();
|
||||
this.x = stream.readFloat();
|
||||
this.y = stream.readFloat();
|
||||
this.liquid = content.liquid(stream.readByte());
|
||||
this.amount = stream.readFloat();
|
||||
this.generation = stream.readByte();
|
||||
add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
loadedPosition = -1;
|
||||
tile = null;
|
||||
liquid = null;
|
||||
amount = 0;
|
||||
generation = 0;
|
||||
accepting = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void added(){
|
||||
if(loadedPosition != -1){
|
||||
map.put(loadedPosition, this);
|
||||
tile = world.tile(loadedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
if(tile != null){
|
||||
map.remove(tile.pos());
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutput data) throws IOException{
|
||||
data.writeFloat(x);
|
||||
data.writeFloat(y);
|
||||
data.writeByte(liquid.id);
|
||||
data.writeShort((short)(amount * 4));
|
||||
data.writeInt(tile.pos());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(DataInput data) throws IOException{
|
||||
x = data.readFloat();
|
||||
y = data.readFloat();
|
||||
liquid = content.liquid(data.readByte());
|
||||
targetAmount = data.readShort() / 4f;
|
||||
int pos = data.readInt();
|
||||
tile = world.tile(pos);
|
||||
|
||||
map.put(pos, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return puddleGroup;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,17 @@
|
||||
package mindustry.entities.units;
|
||||
|
||||
public class AIController extends UnitController{
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class AIController implements UnitController{
|
||||
protected Unitc unit;
|
||||
|
||||
@Override
|
||||
public void unit(Unitc unit){
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unitc unit(){
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user