Bugfixes / Removed tile unit multi-mining

This commit is contained in:
Anuken 2020-10-25 11:22:10 -04:00
parent b2b0082c7d
commit 81dae580a4
23 changed files with 111 additions and 57 deletions

View File

@ -312,6 +312,28 @@ public class BlockIndexer{
return null;
}
/** Find the closest ore block relative to a position. */
public Tile findClosestOre(Unit unit, Item item){
if(!(unit instanceof Minerc miner)) return null;
TileArray arr = getOrePositions(item);
arr.tiles.sort(t -> t.dst2(unit.x, unit.y));
for(Tile tile : arr.tiles){
for(int x = Math.max(0, tile.x - quadrantSize / 2); x < tile.x + quadrantSize / 2 && x < world.width(); x++){
for(int y = Math.max(0, tile.y - quadrantSize / 2); y < tile.y + quadrantSize / 2 && y < world.height(); y++){
Tile res = world.tile(x, y);
if(res.drop() == item && miner.validMine(res, false)){
return res;
}
}
}
}
return null;
}
/** @return extra unit cap of a team. This is added onto the base value. */
public int getExtraUnits(Team team){
return unitCaps[team.id];
@ -457,8 +479,8 @@ public class BlockIndexer{
}
public static class TileArray implements Iterable<Tile>{
private Seq<Tile> tiles = new Seq<>(false, 16);
private IntSet contained = new IntSet();
Seq<Tile> tiles = new Seq<>(false, 16);
IntSet contained = new IntSet();
public void add(Tile tile){
if(contained.add(tile.pos())){

View File

@ -44,8 +44,8 @@ public class LogicAI extends AIController{
@Override
protected void updateMovement(){
if(itemTimer > 0) itemTimer -= Time.delta;
if(payTimer > 0) payTimer -= Time.delta;
if(itemTimer >= 0) itemTimer -= Time.delta;
if(payTimer >= 0) payTimer -= Time.delta;
if(targetTimer > 0f){
targetTimer -= Time.delta;

View File

@ -39,8 +39,8 @@ public class MinerAI extends AIController{
if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
mining = false;
}else{
if(retarget() && targetItem != null){
ore = indexer.findClosestOre(unit.x, unit.y, targetItem);
if(timer.get(timerTarget, 60) && targetItem != null){
ore = indexer.findClosestOre(unit, targetItem);
}
if(ore != null){

View File

@ -17,6 +17,7 @@ public class SectorPresets implements ContentList{
groundZero = new SectorPreset("groundZero", serpulo, 15){{
alwaysUnlocked = true;
addStartingItems = true;
captureWave = 10;
difficulty = 1;
}};

View File

@ -264,7 +264,6 @@ public class Control implements ApplicationListener, Loadable{
slot.load();
slot.setAutosave(true);
state.rules.sector = sector;
state.secinfo = state.rules.sector.info;
//if there is no base, simulate a new game and place the right loadout at the spawn position
if(state.rules.defaultTeam.cores().isEmpty()){
@ -282,6 +281,9 @@ public class Control implements ApplicationListener, Loadable{
//reset wave so things are more fair
state.wave = 1;
//reset win wave??
state.rules.winWave = sector.preset != null ? sector.preset.captureWave : 40;
//kill all units, since they should be dead anwyay
Groups.unit.clear();
Groups.fire.clear();
@ -312,8 +314,8 @@ public class Control implements ApplicationListener, Loadable{
world.loadSector(sector);
state.rules.sector = sector;
//assign origin when launching
state.secinfo.origin = origin;
state.secinfo.destination = origin;
sector.info.origin = origin;
sector.info.destination = origin;
logic.play();
control.saves.saveSector(sector);
Events.fire(Trigger.newGame);

View File

@ -26,8 +26,6 @@ public class GameState{
public GameStats stats = new GameStats();
/** Global attributes of the environment, calculated by weather. */
public Attributes envAttrs = new Attributes();
/** Sector information. Only valid in the campaign. */
public SectorInfo secinfo = new SectorInfo();
/** Team data. Gets reset every new game. */
public Teams teams = new Teams();
/** Number of enemies in the game; only used clientside in servers. */

View File

@ -55,15 +55,14 @@ public class Logic implements ApplicationListener{
}
});
Events.on(LaunchItemEvent.class, e -> state.secinfo.handleItemExport(e.stack));
//when loading a 'damaged' sector, propagate the damage
Events.on(SaveLoadEvent.class, e -> {
if(state.isCampaign()){
state.secinfo.write();
SectorInfo info = state.rules.sector.info;
info.write();
//how much wave time has passed
int wavesPassed = state.secinfo.wavesPassed;
int wavesPassed = info.wavesPassed;
//wave has passed, remove all enemies, they are assumed to be dead
if(wavesPassed > 0){
@ -84,10 +83,10 @@ public class Logic implements ApplicationListener{
}
//reset values
state.secinfo.damage = 0f;
state.secinfo.wavesPassed = 0;
state.secinfo.hasCore = true;
state.secinfo.secondsPassed = 0;
info.damage = 0f;
info.wavesPassed = 0;
info.hasCore = true;
info.secondsPassed = 0;
state.rules.sector.saveInfo();
}
@ -273,7 +272,7 @@ public class Logic implements ApplicationListener{
state.teams.updateTeamStats();
if(state.isCampaign()){
state.secinfo.update();
state.rules.sector.info.update();
}
if(state.isCampaign()){

View File

@ -331,6 +331,7 @@ public class World{
state.rules.weather.add(new WeatherEntry(Weathers.sporestorm));
}
Log.info("saving resources as @ -> @ / @", sector.info.resources, content.asArray(), sector.info.hashCode());
sector.info.resources = content.asArray();
sector.info.resources.sort(Structs.comps(Structs.comparing(Content::getContentType), Structs.comparingInt(c -> c.id)));
sector.saveInfo();

View File

@ -32,12 +32,16 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
}
boolean mining(){
return mineTile != null && !(((Object)this) instanceof Builderc && ((Builderc)(Object)this).activelyBuilding());
return mineTile != null && !(((Object)this) instanceof Builderc b && b.activelyBuilding());
}
public boolean validMine(Tile tile, boolean checkDst){
return !(tile == null || tile.block() != Blocks.air || (!within(tile.worldx(), tile.worldy(), miningRange) && checkDst)
|| tile.drop() == null || !canMine(tile.drop())) && state.teams.canMine(self(), tile);
}
public boolean validMine(Tile tile){
return !(tile == null || tile.block() != Blocks.air || !within(tile.worldx(), tile.worldy(), miningRange)
|| tile.drop() == null || !canMine(tile.drop()));
return validMine(tile, true);
}
@Override
@ -58,6 +62,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
mineTile = null;
mineTimer = 0f;
}else if(mining()){
state.teams.registerMined(mineTile, self());
Item item = mineTile.drop();
lookAt(angleTo(mineTile.worldx(), mineTile.worldy()));
mineTimer += Time.delta *type.mineSpeed;
@ -84,8 +89,6 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
mineTimer = 0f;
}
}
}
}

View File

@ -160,7 +160,7 @@ public class AIController implements UnitController{
}
protected boolean retarget(){
return timer.get(timerTarget, 30);
return timer.get(timerTarget, 40);
}
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){

View File

@ -156,7 +156,6 @@ public class SectorInfo{
damage = 0;
if(state.rules.sector != null){
state.rules.sector.info = this;
state.rules.sector.saveInfo();
}

View File

@ -11,6 +11,7 @@ import mindustry.content.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.payloads.*;
import mindustry.world.blocks.storage.CoreBlock.*;
@ -26,6 +27,8 @@ public class Teams{
public Seq<TeamData> active = new Seq<>();
/** Teams with block or unit presence. */
public Seq<TeamData> present = new Seq<>(TeamData.class);
/** Ores currently being mined. */
private IntMap<Unit> mined = new IntMap<>();
public Teams(){
active.add(get(Team.crux));
@ -142,7 +145,20 @@ public class Teams{
}
}
/** @return whether this ore is taken. */
public boolean canMine(Unit unit, Tile tile){
if(tile == null) return false;
Unit u = mined.get(tile.pos());
return u == unit || u == null;
}
public void registerMined(Tile tile, Unit unit){
if(tile == null || unit == null) return;
mined.put(tile.pos(), unit);
}
public void updateTeamStats(){
mined.clear();
present.clear();
for(Team team : Team.all){

View File

@ -945,10 +945,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
boolean canMine(Tile tile){
return !Core.scene.hasMouse()
&& tile.drop() != null && player.miner().canMine(tile.drop())
&& tile.drop() != null
&& player.miner().validMine(tile)
&& !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null)
&& player.unit().acceptsItem(tile.drop())
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= miningRange;
&& tile.block() == Blocks.air;
}
/** Returns the tile at the specified MOUSE coordinates. */

View File

@ -72,7 +72,7 @@ public abstract class SaveVersion extends SaveFileReader{
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{
//prepare campaign data for writing
if(state.isCampaign()){
state.secinfo.prepare();
state.rules.sector.info.prepare();
state.rules.sector.saveInfo();
}
@ -110,11 +110,6 @@ public abstract class SaveVersion extends SaveFileReader{
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
lastReadBuild = map.getInt("build", -1);
//load in sector info
if(state.rules.sector != null){
state.secinfo = state.rules.sector.info;
}
if(!headless){
Tmp.v1.tryFromString(map.get("viewpos"));
Core.camera.position.set(Tmp.v1);

View File

@ -246,7 +246,7 @@ public class LExecutor{
switch(locate){
case ore -> {
if(exec.obj(ore) instanceof Item item){
res = indexer.findClosestOre(unit.x, unit.y, item);
res = indexer.findClosestOre(unit, item);
}
}
case building -> {
@ -466,9 +466,8 @@ public class LExecutor{
if(ai.itemTimer > 0) return;
Building build = exec.building(p1);
int amount = exec.numi(p2);
int dropped = Math.min(unit.stack.amount, amount);
if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange)){
int dropped = Math.min(unit.stack.amount, exec.numi(p2));
if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange + build.block.size * tilesize/2f)){
int accepted = build.acceptStack(unit.item(), dropped, unit);
if(accepted > 0){
Call.transferItemTo(unit, unit.item(), accepted, unit.x, unit.y, build);
@ -482,7 +481,7 @@ public class LExecutor{
Building build = exec.building(p1);
int amount = exec.numi(p3);
if(build != null && build.items != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange)){
if(build != null && build.items != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange + build.block.size * tilesize/2f)){
int taken = Math.min(build.items.get(item), Math.min(amount, unit.maxAccepted(item)));
if(taken > 0){

View File

@ -82,13 +82,13 @@ public class SectorDamage{
/** Applies wave damage based on sector parameters. */
public static void applyCalculatedDamage(){
//calculate base damage fraction
float damage = getDamage(state.secinfo);
float damage = getDamage(state.rules.sector.info);
//scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier)
float scaled = Mathf.pow(damage, 1.5f);
//apply damage to units
float unitDamage = damage * state.secinfo.sumHealth;
float unitDamage = damage * state.rules.sector.info.sumHealth;
Tile spawn = spawner.getFirstSpawn();
//damage only units near the spawn point
@ -114,7 +114,7 @@ public class SectorDamage{
}
}
if(state.secinfo.wavesPassed > 0){
if(state.rules.sector.info.wavesPassed > 0){
//simply remove each block in the spawner range if a wave passed
for(Tile spawner : spawner.getSpawns()){
spawner.circle((int)(state.rules.dropZoneRadius / tilesize), tile -> {

View File

@ -14,9 +14,11 @@ import static mindustry.Vars.*;
public class FileMapGenerator implements WorldGenerator{
public final Map map;
public final SectorPreset preset;
public FileMapGenerator(String mapName){
public FileMapGenerator(String mapName, SectorPreset preset){
this.map = maps != null ? maps.loadInternalMap(mapName) : null;
this.preset = preset;
}
@Override
@ -56,6 +58,10 @@ public class FileMapGenerator implements WorldGenerator{
if(tile.isCenter() && tile.block() instanceof CoreBlock && tile.team() == state.rules.defaultTeam && !anyCores){
Schematics.placeLaunchLoadout(tile.x, tile.y);
anyCores = true;
if(preset.addStartingItems){
tile.build.items.add(state.rules.loadout);
}
}
}

View File

@ -17,10 +17,11 @@ public class SectorPreset extends UnlockableContent{
public Cons<Rules> rules = rules -> rules.winWave = captureWave;
/** Difficulty, 0-10. */
public float difficulty;
public boolean addStartingItems = false;
public SectorPreset(String name, Planet planet, int sector){
super(name);
this.generator = new FileMapGenerator(name);
this.generator = new FileMapGenerator(name, this);
this.planet = planet;
sector %= planet.sectors.size;
this.sector = planet.sectors.get(sector);

View File

@ -49,6 +49,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
shouldPause = true;
addCloseListener();
buttons.defaults().size(200f, 56f).pad(2);
buttons.button("@back", Icon.left, this::hide);
buttons.button("@techtree", Icon.tree, () -> ui.research.show());
@ -380,6 +382,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
stable.row();
}
Log.info("sector resources = @ / @", sector.info.resources, sector.info.hashCode());
if(sector.save != null && sector.info.resources.any()){
stable.add("@sectors.resources").row();
stable.table(t -> {

View File

@ -368,9 +368,9 @@ public class HudFragment extends Fragment{
c.clearChildren();
for(Item item : content.items()){
if(state.secinfo.getExport(item) >= 1){
if(state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1){
c.image(item.icon(Cicon.small));
c.label(() -> (int)state.secinfo.getExport(item) + " /s").color(Color.lightGray);
c.label(() -> (int)state.rules.sector.info.getExport(item) + " /s").color(Color.lightGray);
c.row();
}
}
@ -379,7 +379,7 @@ public class HudFragment extends Fragment{
c.update(() -> {
boolean wrong = false;
for(Item item : content.items()){
boolean has = state.secinfo.getExport(item) >= 1;
boolean has = state.rules.sector != null && state.rules.sector.info.getExport(item) >= 1;
if(used.get(item.id) != has){
used.set(item.id, has);
wrong = true;
@ -389,7 +389,7 @@ public class HudFragment extends Fragment{
rebuild.run();
}
});
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.secinfo.getExport(i) > 0));
}).visible(() -> state.isCampaign() && content.items().contains(i -> state.rules.sector != null && state.rules.sector.info.getExport(i) > 0));
});
blockfrag.build(parent);

View File

@ -126,7 +126,6 @@ public class Build{
for(int dy = 0; dy < type.size; dy++){
int wx = dx + offsetx + tile.x, wy = dy + offsety + tile.y;
Tile check = world.tile(wx, wy);
if(
@ -136,6 +135,10 @@ public class Build{
!check.interactable(team) || //cannot interact
!check.floor().placeableOn || //solid wall
!((type.canReplace(check.block()) || //can replace type
//controversial change: allow rebuilding damaged blocks
//this could be buggy and abusable, so I'm not enabling it yet
//note that this requires a change in BuilderComp as well
//(type == check.block() && check.centerX() == x && check.centerY() == y && check.build != null && check.build.health < check.build.maxHealth - 0.0001f) ||
(check.block instanceof ConstructBlock && check.<ConstructBuild>bc().cblock == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
type.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(check.block.bounds(check.centerX(), check.centerY(), Tmp.r2))) || //no replacement
(type.requiresWater && check.floor().liquidDrop != Liquids.water) //requires water but none found

View File

@ -119,7 +119,7 @@ public class LaunchPad extends Block{
table.row();
table.label(() -> {
Sector dest = state.secinfo.getRealDestination();
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
return Core.bundle.format("launch.destination",
dest == null ? Core.bundle.get("sectors.nonelaunch") :
@ -135,7 +135,11 @@ public class LaunchPad extends Block{
}
table.button(Icon.upOpen, Styles.clearTransi, () -> {
ui.planet.showSelect(state.rules.sector, other -> state.secinfo.destination = other);
ui.planet.showSelect(state.rules.sector, other -> {
if(state.isCampaign()){
state.rules.sector.info.destination = other;
}
});
deselect();
}).size(40f);
}
@ -208,7 +212,7 @@ public class LaunchPad extends Block{
public void remove(){
if(!state.isCampaign()) return;
Sector destsec = state.secinfo.getRealDestination();
Sector destsec = state.rules.sector.info.getRealDestination();
//actually launch the items upon removal
if(team() == state.rules.defaultTeam){
@ -219,7 +223,7 @@ public class LaunchPad extends Block{
dest.add(stack);
//update export
state.secinfo.handleItemExport(stack);
state.rules.sector.info.handleItemExport(stack);
Events.fire(new LaunchItemEvent(stack));
}

View File

@ -339,15 +339,15 @@ public class CoreBlock extends StorageBlock{
public void itemTaken(Item item){
if(state.isCampaign() && team == state.rules.defaultTeam){
//update item taken amount
state.secinfo.handleCoreItem(item, -1);
state.rules.sector.info.handleCoreItem(item, -1);
}
}
@Override
public void handleItem(Building source, Item item){
if(net.server() || !net.active()){
if(team == state.rules.defaultTeam){
state.secinfo.handleCoreItem(item, 1);
if(team == state.rules.defaultTeam && state.isCampaign()){
state.rules.sector.info.handleCoreItem(item, 1);
}
if(items.get(item) >= getMaximumAccepted(item)){