mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-01 18:50:27 +03:00
Refactored shield provider interface
This commit is contained in:
@@ -16,6 +16,7 @@ import mindustry.gen.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -30,22 +31,22 @@ public class BlockIndexer{
|
||||
|
||||
private int quadWidth, quadHeight;
|
||||
|
||||
//TODO: refactor to be field of state, and also refactor such that data is stored in teams when appropriate
|
||||
/** Stores all ore quadrants on the map. Maps ID to qX to qY to a list of tiles with that ore. */
|
||||
private IntSeq[][][] ores, wallOres;
|
||||
/** Stores all damaged tile entities by team. */
|
||||
private Seq<Building>[] damagedTiles = new Seq[Team.all.length];
|
||||
/** All ores present on the map - can be wall or floor. */
|
||||
private Seq<Item> allPresentOres = new Seq<>();
|
||||
/** All ores available on this map. */
|
||||
private ObjectIntMap<Item> allOres = new ObjectIntMap<>(), allWallOres = new ObjectIntMap<>();
|
||||
/** Stores teams that are present here as tiles. */
|
||||
private Seq<Team> activeTeams = new Seq<>(Team.class);
|
||||
//TODO: move to TeamData
|
||||
/** Maps teams to a map of flagged tiles by flag. */
|
||||
private Seq<Building>[][] flagMap = new Seq[Team.all.length][BlockFlag.all.length];
|
||||
/** Counts whether a certain floor is present in the world upon load. */
|
||||
private boolean[] blocksPresent;
|
||||
/** Array used for returning and reusing. */
|
||||
private Seq<Building> breturnArray = new Seq<>(Building.class);
|
||||
private Seq<Building> breturn = new Seq<>(Building.class);
|
||||
/** Maps block flag to a list of floor tiles that have it. */
|
||||
private Seq<Tile>[] floorMap;
|
||||
|
||||
@@ -66,7 +67,6 @@ public class BlockIndexer{
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
damagedTiles = new Seq[Team.all.length];
|
||||
flagMap = new Seq[Team.all.length][BlockFlag.all.length];
|
||||
floorMap = new Seq[BlockFlag.all.length];
|
||||
activeTeams = new Seq<>(Team.class);
|
||||
@@ -214,9 +214,13 @@ public class BlockIndexer{
|
||||
data.turretTree.remove(build);
|
||||
}
|
||||
|
||||
if(data.shieldTree != null && build instanceof ShieldProvider){
|
||||
data.shieldTree.remove(build);
|
||||
}
|
||||
|
||||
//unregister damaged buildings if applicable
|
||||
if(build.wasDamaged && damagedTiles[team.id] != null){
|
||||
damagedTiles[team.id].remove(build);
|
||||
if(build.wasDamaged){
|
||||
data.damagedBuildings.remove(build);
|
||||
}
|
||||
|
||||
build.wasDamaged = false;
|
||||
@@ -292,14 +296,9 @@ public class BlockIndexer{
|
||||
|
||||
/** Returns all damaged tiles by team. */
|
||||
public Seq<Building> getDamaged(Team team){
|
||||
if(damagedTiles[team.id] == null){
|
||||
return damagedTiles[team.id] = new Seq<>(false);
|
||||
}
|
||||
|
||||
var tiles = damagedTiles[team.id];
|
||||
tiles.removeAll(b -> !b.damaged());
|
||||
|
||||
return tiles;
|
||||
var buildings = team.data().damagedBuildings;
|
||||
buildings.removeAll(b -> !b.damaged());
|
||||
return buildings;
|
||||
}
|
||||
|
||||
/** Get all allied blocks with a flag. */
|
||||
@@ -329,24 +328,24 @@ public class BlockIndexer{
|
||||
});
|
||||
return returnBool;
|
||||
}else{
|
||||
breturnArray.clear();
|
||||
breturn.clear();
|
||||
|
||||
var buildings = team.data().buildingTree;
|
||||
if(buildings == null) return false;
|
||||
buildings.intersect(wx - range, wy - range, range*2f, range*2f, b -> {
|
||||
if(b.within(wx, wy, range + b.hitSize() / 2f) && pred.get(b) && !b.block.privileged){
|
||||
breturnArray.add(b);
|
||||
breturn.add(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int size = breturnArray.size;
|
||||
var items = breturnArray.items;
|
||||
int size = breturn.size;
|
||||
var items = breturn.items;
|
||||
for(int i = 0; i < size; i++){
|
||||
cons.get(items[i]);
|
||||
items[i] = null;
|
||||
}
|
||||
breturnArray.size = 0;
|
||||
breturn.size = 0;
|
||||
|
||||
return size > 0;
|
||||
}
|
||||
@@ -355,30 +354,47 @@ public class BlockIndexer{
|
||||
public boolean eachBlock(Team team, Rect rect, Boolf<Building> pred, Cons<Building> cons){
|
||||
if(team == null) return false;
|
||||
|
||||
breturnArray.clear();
|
||||
breturn.clear();
|
||||
|
||||
var buildings = team.data().buildingTree;
|
||||
if(buildings == null) return false;
|
||||
buildings.intersect(rect, b -> {
|
||||
if(pred.get(b) && !b.block.privileged){
|
||||
breturnArray.add(b);
|
||||
breturn.add(b);
|
||||
}
|
||||
});
|
||||
|
||||
int size = breturnArray.size;
|
||||
var items = breturnArray.items;
|
||||
int size = breturn.size;
|
||||
var items = breturn.items;
|
||||
for(int i = 0; i < size; i++){
|
||||
cons.get(items[i]);
|
||||
items[i] = null;
|
||||
}
|
||||
breturnArray.size = 0;
|
||||
breturn.size = 0;
|
||||
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
/** @return all enemy shield blocks in a rectangle (bottom left corner origin). */
|
||||
public Seq<Building> getEnemyShields(Team team, float x, float y, float width, float height){
|
||||
breturn.clear();
|
||||
var all = state.teams.present;
|
||||
for(int i = 0; i < all.size; i++){
|
||||
Team enemy = all.items[i].team;
|
||||
if(enemy == team) continue;
|
||||
|
||||
var data = enemy.data();
|
||||
if(data.shieldTree != null){
|
||||
data.shieldTree.intersect(x, y, width, height, breturn);
|
||||
}
|
||||
}
|
||||
|
||||
return breturn;
|
||||
}
|
||||
|
||||
/** Get all enemy blocks with a flag. */
|
||||
public Seq<Building> getEnemy(Team team, BlockFlag type){
|
||||
breturnArray.clear();
|
||||
breturn.clear();
|
||||
Seq<TeamData> data = state.teams.present;
|
||||
//when team data is not initialized, scan through every team. this is terrible
|
||||
if(data.isEmpty()){
|
||||
@@ -386,7 +402,7 @@ public class BlockIndexer{
|
||||
if(enemy == team || (enemy == Team.derelict && !state.rules.coreCapture)) continue;
|
||||
var set = getFlagged(enemy)[type.ordinal()];
|
||||
if(set != null){
|
||||
breturnArray.addAll(set);
|
||||
breturn.addAll(set);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@@ -395,28 +411,26 @@ public class BlockIndexer{
|
||||
if(enemy == team || (enemy == Team.derelict && !state.rules.coreCapture)) continue;
|
||||
var set = getFlagged(enemy)[type.ordinal()];
|
||||
if(set != null){
|
||||
breturnArray.addAll(set);
|
||||
breturn.addAll(set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return breturnArray;
|
||||
return breturn;
|
||||
}
|
||||
|
||||
public void notifyHealthChanged(Building build){
|
||||
boolean damaged = build.damaged();
|
||||
|
||||
if(build.wasDamaged != damaged){
|
||||
if(damagedTiles[build.team.id] == null){
|
||||
damagedTiles[build.team.id] = new Seq<>(false);
|
||||
}
|
||||
var list = build.team.data().damagedBuildings;
|
||||
|
||||
if(damaged){
|
||||
//is now damaged, add to array
|
||||
damagedTiles[build.team.id].add(build);
|
||||
list.add(build);
|
||||
}else{
|
||||
//no longer damaged, remove
|
||||
damagedTiles[build.team.id].remove(build);
|
||||
list.remove(build);
|
||||
}
|
||||
|
||||
build.wasDamaged = damaged;
|
||||
@@ -424,16 +438,16 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
public void allBuildings(float x, float y, float range, Cons<Building> cons){
|
||||
breturnArray.clear();
|
||||
breturn.clear();
|
||||
for(int i = 0; i < activeTeams.size; i++){
|
||||
Team team = activeTeams.items[i];
|
||||
var buildings = team.data().buildingTree;
|
||||
if(buildings == null) continue;
|
||||
buildings.intersect(x - range, y - range, range*2f, range*2f, breturnArray);
|
||||
buildings.intersect(x - range, y - range, range*2f, range*2f, breturn);
|
||||
}
|
||||
|
||||
var items = breturnArray.items;
|
||||
int size = breturnArray.size;
|
||||
var items = breturn.items;
|
||||
int size = breturn.size;
|
||||
for(int i = 0; i < size; i++){
|
||||
var b = items[i];
|
||||
if(b != null && b.within(x, y, range + b.hitSize()/2f)){
|
||||
@@ -441,7 +455,7 @@ public class BlockIndexer{
|
||||
}
|
||||
items[i] = null;
|
||||
}
|
||||
breturnArray.size = 0;
|
||||
breturn.size = 0;
|
||||
}
|
||||
|
||||
public Building findEnemyTile(Team team, float x, float y, float range, BuildingPriorityf priority, Boolf<Building> pred){
|
||||
@@ -492,11 +506,11 @@ public class BlockIndexer{
|
||||
var buildings = team.data().buildingTree;
|
||||
if(buildings == null) return null;
|
||||
|
||||
breturnArray.clear();
|
||||
buildings.intersect(rect.setCentered(x, y, range * 2f), breturnArray);
|
||||
breturn.clear();
|
||||
buildings.intersect(rect.setCentered(x, y, range * 2f), breturn);
|
||||
|
||||
for(int i = 0; i < breturnArray.size; i++){
|
||||
var next = breturnArray.items[i];
|
||||
for(int i = 0; i < breturn.size; i++){
|
||||
var next = breturn.items[i];
|
||||
|
||||
if(!pred.get(next) || (next.team != source && !next.block.targetable)) continue;
|
||||
|
||||
@@ -622,6 +636,14 @@ public class BlockIndexer{
|
||||
data.turretTree.insert(tile.build);
|
||||
}
|
||||
|
||||
if(tile.build instanceof ShieldProvider){
|
||||
if(data.shieldTree == null){
|
||||
data.shieldTree = new ShieldQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
}
|
||||
|
||||
data.shieldTree.insert(tile.build);
|
||||
}
|
||||
|
||||
notifyHealthChanged(tile.build);
|
||||
}
|
||||
|
||||
@@ -652,4 +674,21 @@ public class BlockIndexer{
|
||||
return new TurretQuadtree(rect);
|
||||
}
|
||||
}
|
||||
|
||||
static class ShieldQuadtree<T extends Building & ShieldProvider> extends QuadTree<T>{
|
||||
|
||||
public ShieldQuadtree(Rect bounds){
|
||||
super(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(T build){
|
||||
tmp.setCentered(build.x, build.y, build.getShieldBounds());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QuadTree<T> newChild(Rect rect){
|
||||
return new ShieldQuadtree<>(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public class Damage{
|
||||
int f = i;
|
||||
Time.run(i * 2f, () -> {
|
||||
var shields = ignoreTeam == null ? null : indexer.getEnemy(ignoreTeam, BlockFlag.shield);
|
||||
if(shields == null || shields.isEmpty() || !shields.contains(b -> b instanceof ExplosionShield s && s.absorbExplosion(x, y, damagePerWave))){
|
||||
if(shields == null || shields.isEmpty() || !shields.contains(b -> b instanceof ShieldProvider s && s.absorbExplosion(x, y, damagePerWave))){
|
||||
damage(ignoreTeam, x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), damagePerWave, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,8 @@ import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static java.lang.Float.NaN;
|
||||
import static java.lang.Float.*;
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.logic.GlobalVars.*;
|
||||
|
||||
@@ -922,9 +921,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
//if this unit crash landed (was flying), damage stuff in a radius
|
||||
if(type.flying && !spawnedByCore && type.createWreck && state.rules.unitCrashDamage(team) > 0){
|
||||
var shields = indexer.getEnemy(team, BlockFlag.shield);
|
||||
var shields = indexer.getEnemyShields(team, x, y, 0f, 0f);
|
||||
float crashDamage = Mathf.pow(hitSize, 0.75f) * type.crashDamageMultiplier * 2.5f * state.rules.unitCrashDamage(team);
|
||||
if(shields.isEmpty() || !shields.contains(b -> b instanceof ExplosionShield s && s.absorbExplosion(x, y, crashDamage))){
|
||||
if(shields.isEmpty() || !shields.contains(b -> b instanceof ShieldProvider s && s.absorbExplosion(x, y, crashDamage))){
|
||||
Damage.damage(team, x, y, Mathf.pow(hitSize, 0.94f) * 1.25f, crashDamage, true, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,8 @@ public class Teams{
|
||||
public @Nullable QuadTree<Building> buildingTree;
|
||||
/** Turrets by range. Null if not active. */
|
||||
public @Nullable QuadTree<Building> turretTree;
|
||||
/** ShieldProvider instances. */
|
||||
public @Nullable QuadTree<Building> shieldTree;
|
||||
/** Quadtree for units of this team. Do not access directly. */
|
||||
public @Nullable QuadTree<Unit> unitTree;
|
||||
/** Current unit cap. Do not modify externally. */
|
||||
@@ -313,6 +315,8 @@ public class Teams{
|
||||
public Seq<Building> buildings = new Seq<>(false);
|
||||
/** Units of this team by type. Updated each frame. */
|
||||
public @Nullable Seq<Unit>[] unitsByType;
|
||||
/** Stores all damaged buildings. Do not access directly. */
|
||||
public Seq<Building> damagedBuildings = new Seq<>();
|
||||
|
||||
public TeamData(Team team){
|
||||
this.team = team;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package mindustry.mod;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
/** Generated class. Maps simple class names to concrete classes. For use in JSON mods. */
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ClassMap{
|
||||
public static final ObjectMap<String, Class<?>> classes = new ObjectMap<>();
|
||||
|
||||
|
||||
static{
|
||||
classes.put("AssemblerAI", mindustry.ai.types.AssemblerAI.class);
|
||||
classes.put("BoostAI", mindustry.ai.types.BoostAI.class);
|
||||
@@ -151,7 +153,7 @@ public class ClassMap{
|
||||
classes.put("ConstructBlock", mindustry.world.blocks.ConstructBlock.class);
|
||||
classes.put("ConstructBuild", mindustry.world.blocks.ConstructBlock.ConstructBuild.class);
|
||||
classes.put("ControlBlock", mindustry.world.blocks.ControlBlock.class);
|
||||
classes.put("ExplosionShield", mindustry.world.blocks.ExplosionShield.class);
|
||||
classes.put("ExplosionShield", ShieldProvider.class);
|
||||
classes.put("ItemSelection", mindustry.world.blocks.ItemSelection.class);
|
||||
classes.put("LaunchAnimator", mindustry.world.blocks.LaunchAnimator.class);
|
||||
classes.put("RotBlock", mindustry.world.blocks.RotBlock.class);
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
package mindustry.world.blocks;
|
||||
|
||||
//TODO: horrible API design, but I'm not sure of a better way to do this right now. please don't use this class
|
||||
public interface ExplosionShield{
|
||||
public interface ShieldProvider{
|
||||
/** @return whether the shield was able to absorb the explosion; this should apply damage to the shield if true is returned. */
|
||||
boolean absorbExplosion(float x, float y, float damage);
|
||||
/** @return maximum (not current size!) half-size of the shield square bounding box */
|
||||
float getShieldBounds();
|
||||
}
|
||||
@@ -162,7 +162,7 @@ public class ForceProjector extends Block{
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
public class ForceBuild extends Building implements Ranged, ExplosionShield{
|
||||
public class ForceBuild extends Building implements Ranged, ShieldProvider{
|
||||
public boolean broken = true;
|
||||
public float buildup, radscl, hit, warmup, phaseHeat;
|
||||
|
||||
@@ -277,6 +277,11 @@ public class ForceProjector extends Block{
|
||||
return absorb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShieldBounds(){
|
||||
return radius + phaseRadiusBoost;
|
||||
}
|
||||
|
||||
public float realRadius(){
|
||||
return (radius + phaseHeat * phaseRadiusBoost) * radscl;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user