Misc bugfixes / Unified build/break block system

This commit is contained in:
Anuken
2018-07-04 23:52:21 -04:00
parent cc92edfa58
commit 4a32706c5a
23 changed files with 746 additions and 895 deletions

View File

@@ -32,7 +32,6 @@ public class Blocks extends BlockList implements ContentList{
for(int i = 1; i <= 6; i ++){
new BuildBlock("build" + i);
new BreakBlock("break" + i);
}
space = new Floor("space") {{

View File

@@ -27,9 +27,10 @@ public class PowerBlocks extends BlockList implements ContentList {
}};
turbineGenerator = new TurbineGenerator("turbine-generator") {{
powerOutput = 0.25f;
powerOutput = 0.28f;
powerCapacity = 40f;
itemDuration = 30f;
auxLiquidUse = 0.05f;
size = 2;
}};

View File

@@ -191,6 +191,10 @@ public class NetServer extends Module{
player.setMineTile(packet.mining);
player.isBoosting = packet.boosting;
player.isShooting = packet.shooting;
player.getPlaceQueue().clear();
if(packet.currentRequest != null){
player.getPlaceQueue().addLast(packet.currentRequest);
}
vector.set(packet.x - player.getInterpolator().target.x, packet.y - player.getInterpolator().target.y);

View File

@@ -245,7 +245,9 @@ public class Renderer extends RendererModule{
}
batch.begin();
EntityDraw.setClip(false);
drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
EntityDraw.setClip(true);
batch.end();
}

View File

@@ -100,6 +100,10 @@ public class World extends Module{
return tiles[0].length;
}
public int toPacked(int x, int y){
return x + y *width();
}
public Tile tile(int packed){
return tiles == null ? null : tile(packed % width(), packed / width());
}

View File

@@ -735,6 +735,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
buffer.writeByte(mech.id);
buffer.writeBoolean(isBoosting);
buffer.writeInt(mining == null ? -1 : mining.packedPosition());
writeBuilding(buffer);
}
@Override
@@ -748,6 +750,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
mech = Upgrade.getByID(buffer.readByte());
boolean boosting = buffer.readBoolean();
int mine = buffer.readInt();
readBuilding(buffer, !isLocal);
interpolator.read(lastx, lasty, x, y, time, rotation);
rotation = lastrot;

View File

@@ -8,32 +8,36 @@ import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Build;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BreakBlock;
import io.anuke.mindustry.world.blocks.BreakBlock.BreakEntity;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
/**Interface for units that build, break or mine things.*/
public interface BuilderTrait {
public interface BuilderTrait extends Entity{
//these are not instance variables!
Translator[] tmptr = {new Translator(), new Translator(), new Translator(), new Translator()};
float placeDistance = 140f;
@@ -54,6 +58,49 @@ public interface BuilderTrait {
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
float getBuildPower(Tile tile);
default void writeBuilding(DataOutput output) throws IOException{
BuildRequest request = getCurrentRequest();
if(request != null){
output.writeByte(request.remove ? 1 : 0);
output.writeInt(world.toPacked(request.x, request.y));
if(!request.remove){
output.writeByte(request.recipe.id);
output.writeByte(request.rotation);
}
}else{
output.writeByte(-1);
}
}
default void readBuilding(DataInput input) throws IOException{
readBuilding(input, true);
}
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
synchronized (getPlaceQueue()) {
if(applyChanges) getPlaceQueue().clear();
byte type = input.readByte();
if (type != -1) {
int position = input.readInt();
BuildRequest request;
if (type == 1) { //remove
request = new BuildRequest(position % world.width(), position / world.width());
} else { //place
byte recipe = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
}
if(applyChanges){
getPlaceQueue().addLast(request);
}
}
}
}
/**Return whether this builder's place queue contains items.*/
default boolean isBuilding(){
return getPlaceQueue().size != 0;
@@ -77,11 +124,7 @@ public interface BuilderTrait {
/**Clears the placement queue.*/
default void clearBuilding(){
if(this instanceof Player) {
CallBlocks.onBuildDeselect((Player) this);
}else{
getPlaceQueue().clear();
}
getPlaceQueue().clear();
}
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
@@ -119,75 +162,38 @@ public interface BuilderTrait {
setMineTile(null);
}
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
Tile tile = world.tile(current.x, current.y);
if(unit.distanceTo(tile) > placeDistance || //out of range, skip it
(current.lastEntity != null && current.lastEntity.isDead())) { //build/destroy request has died, skip it
getPlaceQueue().removeFirst();
}else if(current.remove){
if (!(tile.block() instanceof BreakBlock)) { //check if haven't started placing
if(Build.validBreak(unit.getTeam(), current.x, current.y)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.breakBlock((Player)unit, unit.getTeam(), current.x, current.y);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
if (!(tile.block() instanceof BuildBlock)) {
if(!current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)) {
Build.beginPlace(unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
}else if(current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
}else{
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BreakEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core, unit, 1f / entity.breakTime * Timers.delta() * getBuildPower(tile));
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
}
}else{
if (!(tile.block() instanceof BuildBlock)) { //check if haven't started placing
if(Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)){
//if it's valid, place it
if(!current.requested && unit instanceof Player){
CallBlocks.placeBlock((Player)unit, unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
current.requested = true;
}
}else{
//otherwise, skip it
getPlaceQueue().removeFirst();
}
}else{
TileEntity core = unit.getClosestCore();
//if there is no core to build with, stop building!
if(core == null){
return;
}
//otherwise, update it.
BuildEntity entity = tile.entity();
current.lastEntity = entity;
entity.addProgress(core.items, 1f / entity.recipe.cost * Timers.delta() * getBuildPower(tile));
if(unit instanceof Player){
entity.lastBuilder = (Player)unit;
}
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
getCurrentRequest().progress = entity.progress();
getPlaceQueue().removeFirst();
return;
}
}
//otherwise, update it.
BuildEntity entity = tile.entity();
//deconstructing is 2x as fast
if(current.remove){
entity.deconstruct(unit, core, 2f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}else{
entity.construct(unit, core, 1f / entity.buildCost * Timers.delta() * getBuildPower(tile));
}
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
current.progress = entity.progress();
}
/**Do not call directly.*/
@@ -302,9 +308,6 @@ public interface BuilderTrait {
public final Recipe recipe;
public final boolean remove;
public boolean requested;
public TileEntity lastEntity;
public float progress;
/**This creates a build request.*/

View File

@@ -18,7 +18,6 @@ import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
@@ -206,26 +205,19 @@ public class Drone extends FlyingUnit implements BuilderTrait {
public void write(DataOutput data) throws IOException {
super.write(data);
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
data.writeInt(placeQueue.size == 0 ? -1 : world.tile(placeQueue.last().x, placeQueue.last().y).packedPosition());
data.writeByte(placeQueue.size == 0 ? -1 : placeQueue.last().recipe.id);
writeBuilding(data);
}
@Override
public void read(DataInput data, long time) throws IOException {
super.read(data, time);
int mined = data.readInt();
int pp = data.readInt();
byte rid = data.readByte();
readBuilding(data);
if(mined != -1){
mineTile = world.tile(mined);
}
if(pp != -1){
Tile tile = world.tile(pp);
placeQueue.clear();
placeQueue.addLast(new BuildRequest(tile.x, tile.y, tile.getRotation(), Recipe.getByID(rid)));
}
}
public final UnitState

View File

@@ -144,6 +144,16 @@ public class TypeIO {
return world.tile(buffer.getInt());
}
@WriteClass(Block.class)
public static void writeBlock(ByteBuffer buffer, Block block){
buffer.put((byte)block.id);
}
@ReadClass(Block.class)
public static Block readBlock(ByteBuffer buffer){
return Block.getByID(buffer.get());
}
@WriteClass(KickReason.class)
public static void writeKick(ByteBuffer buffer, KickReason reason){
buffer.put((byte)reason.ordinal());

View File

@@ -5,7 +5,9 @@ import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.io.IOUtils;
import io.anuke.ucore.util.Mathf;
@@ -122,6 +124,7 @@ public class Packets {
public float x, y, pointerX, pointerY, rotation, baseRotation, xv, yv;
public Tile mining;
public boolean boosting, shooting;
public BuildRequest currentRequest;
@Override
public void write(ByteBuffer buffer) {
@@ -145,6 +148,19 @@ public class Packets {
buffer.putShort((short)(player.baseRotation*2));
buffer.putInt(player.getMineTile() == null ? -1 : player.getMineTile().packedPosition());
BuildRequest request = player.getCurrentRequest();
if(request != null){
buffer.put(request.remove ? (byte)1 : 0);
buffer.putInt(world.toPacked(request.x, request.y));
if(!request.remove){
buffer.put((byte)request.recipe.id);
buffer.put((byte)request.rotation);
}
}else{
buffer.put((byte)-1);
}
}
@Override
@@ -164,6 +180,21 @@ public class Packets {
rotation = buffer.getShort()/2f;
baseRotation = buffer.getShort()/2f;
mining = world.tile(buffer.getInt());
byte type = buffer.get();
if (type != -1) {
int position = buffer.getInt();
if (type == 1) { //remove
currentRequest = new BuildRequest(position % world.width(), position / world.width());
} else { //place
byte recipe = buffer.get();
byte rotation = buffer.get();
currentRequest = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
}
}else{
currentRequest = null;
}
}
}

View File

@@ -1,20 +1,11 @@
package io.anuke.mindustry.world;
import com.badlogic.gdx.math.Rectangle;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.EventType.BlockBuildEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.blocks.BreakBlock;
import io.anuke.mindustry.world.blocks.BreakBlock.BreakEntity;
import io.anuke.mindustry.world.blocks.BuildBlock;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Events;
import io.anuke.ucore.entities.Entities;
@@ -26,15 +17,10 @@ public class Build {
private static final Rectangle hitrect = new Rectangle();
/**Returns block type that was broken, or null if unsuccesful.*/
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void breakBlock(Player player, Team team, int x, int y){
if(Net.server()){
if(!validBreak(team, x, y)){
return;
}
team = player.getTeam();
//throw new ValidateException(player, "An invalid block has been broken.");
//@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void beginBreak(Team team, int x, int y){
if(!validBreak(team, x, y)){
return;
}
Tile tile = world.tile(x, y);
@@ -46,59 +32,38 @@ public class Build {
Block previous = tile.block();
//remote players only
if(player != null && !player.isLocal){
player.getPlaceQueue().clear();
player.getPlaceQueue().addFirst(new BuildRequest(x, y));
}
Block sub = Block.getByName("build" + previous.size);
Block sub = Block.getByName("break" + previous.size);
tile.setBlock(sub);
tile.<BuildEntity>entity().setDeconstruct(previous);
tile.setTeam(team);
if(previous instanceof BuildBlock){
BuildEntity build = tile.entity();
if (previous.isMultiblock()) {
int offsetx = -(previous.size - 1) / 2;
int offsety = -(previous.size - 1) / 2;
tile.setBlock(sub);
tile.setTeam(team);
BreakEntity breake = tile.entity();
breake.set(build.recipe.result);
breake.progress = 1.0 - build.progress;
}else {
tile.setBlock(sub);
tile.<BreakEntity>entity().set(previous);
tile.setTeam(team);
if (previous.isMultiblock()) {
int offsetx = -(previous.size - 1) / 2;
int offsety = -(previous.size - 1) / 2;
for (int dx = 0; dx < previous.size; dx++) {
for (int dy = 0; dy < previous.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
for (int dx = 0; dx < previous.size; dx++) {
for (int dy = 0; dy < previous.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
}
}
}
}
}
/**Places a BuildBlock at this location. Call validPlace first.*/
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void placeBlock(Player player, Team team, int x, int y, Recipe recipe, int rotation){
if(Net.server()){
if(!validPlace(team, x, y, recipe.result, rotation)){
return;
}
team = player.getTeam();
//throw new ValidateException(player, "An invalid block has been placed.");
/**Places a BuildBlock at this location.*/
//@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void beginPlace(Team team, int x, int y, Recipe recipe, int rotation){
if(!validPlace(team, x, y, recipe.result, rotation)){
return;
}
Tile tile = world.tile(x, y);
@@ -109,51 +74,33 @@ public class Build {
Block result = recipe.result;
Block previous = tile.block();
//remote players only
if(player != null && !player.isLocal){
player.getPlaceQueue().clear();
player.getPlaceQueue().addFirst(new BuildRequest(x, y, rotation, recipe));
}
Block sub = Block.getByName("build" + result.size);
if(previous instanceof BreakBlock){
BreakEntity breake = tile.entity();
tile.setBlock(sub, rotation);
tile.<BuildEntity>entity().setConstruct(previous, recipe);
tile.setTeam(team);
tile.setBlock(sub);
tile.setTeam(team);
if (result.isMultiblock()) {
int offsetx = -(result.size - 1) / 2;
int offsety = -(result.size - 1) / 2;
BuildEntity build = tile.entity();
build.set(breake.previous, recipe);
build.progress = 1.0 - breake.progress;
}else{
tile.setBlock(sub, rotation);
tile.<BuildEntity>entity().set(previous, recipe);
tile.setTeam(team);
if (result.isMultiblock()) {
int offsetx = -(result.size - 1) / 2;
int offsety = -(result.size - 1) / 2;
for (int dx = 0; dx < result.size; dx++) {
for (int dy = 0; dy < result.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
for (int dx = 0; dx < result.size; dx++) {
for (int dy = 0; dy < result.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if (!(worldx == x && worldy == y)) {
Tile toplace = world.tile(worldx, worldy);
if (toplace != null) {
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
}
}
}
}
Team fteam = team;
threads.runDelay(() -> Events.fire(BlockBuildEvent.class, fteam, tile));
threads.runDelay(() -> Events.fire(BlockBuildEvent.class, team, tile));
}
/**Returns whether a tile can be placed at this location by this team.*/
@@ -226,7 +173,7 @@ public class Build {
public static boolean validBreak(Team team, int x, int y) {
Tile tile = world.tile(x, y);
return tile != null && !tile.block().unbreakable && !(tile.target().block() instanceof BreakBlock)
return tile != null && !tile.block().unbreakable
&& (!tile.isLinked() || !tile.getLinked().block().unbreakable) && tile.breakable() && (tile.getTeam() == Team.none || tile.getTeam() == team);
}
}

View File

@@ -1,211 +0,0 @@
package io.anuke.mindustry.world.blocks;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.CursorType;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.graphics.Draw;
import static io.anuke.mindustry.Vars.world;
public class BreakBlock extends Block {
private static final float decaySpeedScl = 6f;
public BreakBlock(String name) {
super(name);
solidifes = true;
update = true;
size = Integer.parseInt(name.charAt(name.length()-1) + "");
health = 1;
layer = Layer.placement;
consumesTap = true;
}
@Override
public boolean isSolidFor(Tile tile) {
BreakEntity entity = tile.entity();
return entity.previous == null || entity.previous.solid;
}
@Override
public CursorType getCursor(Tile tile) {
return CursorType.hand;
}
@Override
public void tapped(Tile tile, Player player) {
CallBlocks.onBreakSelect(player, tile);
}
@Override
public void setBars(){
bars.replace(new BlockBar(BarType.health, true, tile -> (float)tile.<BreakEntity>entity().progress));
}
@Override
public void onDestroyed(Tile tile){
Effects.effect(ExplosionFx.blockExplosionSmoke, tile);
if(!tile.floor().solid && !tile.floor().isLiquid){
RubbleDecal.create(tile.drawx(), tile.drawy(), size);
}
}
@Override
public void afterDestroyed(Tile tile, TileEntity e){
BreakEntity entity = (BreakEntity)e;
if(entity != null && entity.previous != null && entity.previous.synthetic()){
tile.setBlock(entity.previous);
}
}
@Override
public void draw(Tile tile){
}
@Override
public void drawLayer(Tile tile) {
BreakEntity entity = tile.entity();
Shaders.blockbuild.color = Palette.remove;
if(entity.previous == null) return;
for(TextureRegion region : entity.previous.getBlockIcon()){
Shaders.blockbuild.region = region;
Shaders.blockbuild.progress = (float)(1f-entity.progress); //progress reversed
Shaders.blockbuild.apply();
Draw.rect(region, tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
Graphics.flush();
}
}
@Override
public void drawShadow(Tile tile) {
BreakEntity entity = tile.entity();
if(entity.previous instanceof BreakBlock || entity.previous == null || entity.previous.shadowRegion == null){
return;
}
entity.previous.drawShadow(tile);
}
@Override
public void update(Tile tile) {
BreakEntity entity = tile.entity();
if(entity.progress >= 1f){
CallBlocks.onBreakFinish(tile);
}else if(entity.progress < 0f){
CallBlocks.onBreakDeath(tile);
}
}
@Override
public TileEntity getEntity() {
return new BreakEntity();
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBreakDeath(Tile tile){
BreakEntity entity = tile.entity();
Team team = tile.getTeam();
tile.setBlock(entity.previous);
tile.setTeam(team);
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBreakFinish(Tile tile){
if(tile.entity instanceof BreakEntity){
BreakEntity entity = tile.entity();
if(entity.previous != null){
Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), entity.previous.size);
}
}
world.removeBlock(tile);
}
@Remote(called = Loc.both, targets = Loc.both, in = In.blocks, forward = true)
public static void onBreakSelect(Player player, Tile tile){
if(player == null || !(tile.entity instanceof BreakEntity)) return;
player.getPlaceQueue().clear();
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
}
public class BreakEntity extends TileEntity{
private double[] accumulator;
public double progress = 0;
public Block previous;
public float breakTime;
public void addProgress(TileEntity core, Unit unit, double add){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null) {
ItemStack[] requirements = recipe.requirements;
for (int i = 0; i < requirements.length; i++) {
accumulator[i] += requirements[i].amount * add / 2f; //add scaled amount progressed to the accumulator
int amount = (int) (accumulator[i]); //get amount
if (amount > 0) { //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, amount, core.tile, unit);
core.tile.block().handleStack(requirements[i].item, amount, core.tile, unit);
accumulator[i] -= accepting;
}
}
}
progress += add;
if(progress > 1.0001f){
progress = 1.0001f;
}
}
public float progress(){
return (float)progress;
}
public void set(Block previous){
this.previous = previous;
if(Recipe.getByResult(previous) != null){
this.accumulator = new double[Recipe.getByResult(previous).requirements.length];
this.breakTime = Recipe.getByResult(previous).cost;
}else{
this.breakTime = 20f;
}
}
}
}

View File

@@ -7,6 +7,7 @@ import io.anuke.mindustry.content.fx.ExplosionFx;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.Team;
@@ -16,6 +17,7 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.CursorType;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.BarType;
import io.anuke.mindustry.world.Block;
@@ -25,16 +27,15 @@ import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import static io.anuke.mindustry.Vars.threads;
import static io.anuke.mindustry.Vars.*;
public class BuildBlock extends Block {
private static final float decaySpeedScl = 6f;
public BuildBlock(String name) {
super(name);
update = true;
@@ -48,7 +49,7 @@ public class BuildBlock extends Block {
@Override
public boolean isSolidFor(Tile tile) {
BuildEntity entity = tile.entity();
return entity == null || entity.recipe ==null || entity.recipe.result.solid || entity.previous.solid;
return entity == null || entity.recipe == null || entity.recipe.result.solid || entity.previous.solid;
}
@Override
@@ -58,7 +59,13 @@ public class BuildBlock extends Block {
@Override
public void tapped(Tile tile, Player player) {
CallBlocks.onBuildSelect(player, tile);
BuildEntity entity = tile.entity();
//if the target is constructible, begin constructing
if(entity.recipe != null){
player.clearBuilding();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
}
}
@Override
@@ -79,7 +86,7 @@ public class BuildBlock extends Block {
public void afterDestroyed(Tile tile, TileEntity e){
BuildEntity entity = (BuildEntity)e;
if(entity.previous.synthetic()){
if(entity.previous != null && entity.previous.synthetic()){
tile.setBlock(entity.previous);
}
}
@@ -88,10 +95,13 @@ public class BuildBlock extends Block {
public void draw(Tile tile){
BuildEntity entity = tile.entity();
if(entity.previous != null && entity.previous.synthetic()) {
for (TextureRegion region : entity.previous.getBlockIcon()) {
Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0);
}
//When breaking, don't draw the previous block... since it's the thing you were breaking
if(entity.recipe != null && entity.previous == entity.recipe.result){
return;
}
for (TextureRegion region : entity.previous.getBlockIcon()) {
Draw.rect(region, tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
}
}
@@ -101,12 +111,16 @@ public class BuildBlock extends Block {
Shaders.blockbuild.color = Palette.accent;
for(TextureRegion region : entity.recipe.result.getBlockIcon()){
Block target = entity.recipe == null ? entity.previous : entity.recipe.result;
if(target == null) return;
for(TextureRegion region : target.getBlockIcon()){
Shaders.blockbuild.region = region;
Shaders.blockbuild.progress = (float)entity.progress;
Shaders.blockbuild.apply();
Draw.rect(region, tile.drawx(), tile.drawy(), entity.recipe.result.rotate ? tile.getRotation() * 90 : 0);
Draw.rect(region, tile.drawx(), tile.drawy(), target.rotate ? tile.getRotation() * 90 : 0);
Graphics.flush();
}
@@ -116,26 +130,16 @@ public class BuildBlock extends Block {
public void drawShadow(Tile tile) {
BuildEntity entity = tile.entity();
if(entity.recipe != null && entity.recipe.result != null){
if(entity.recipe != null){
entity.recipe.result.drawShadow(tile);
}else if(entity.previous != null){
entity.previous.drawShadow(tile);
}
}
@Override
public void update(Tile tile) {
BuildEntity entity = tile.entity();
if(entity.progress >= 1f){
CallBlocks.onBuildFinish(tile, entity.lastBuilder);
}else if(entity.progress < 0f){
CallBlocks.onBuildDeath(tile);
}
if(!entity.updated && entity.recipe != null){
entity.progress -= 1f/entity.recipe.cost/decaySpeedScl;
}
entity.updated = false;
}
@Override
@@ -144,77 +148,85 @@ public class BuildBlock extends Block {
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBuildDeath(Tile tile){
if(tile.entity == null) return;
tile.entity.damage(tile.entity.health + 1);
public static void onDeconstructFinish(Tile tile, Block block){
Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), block.size);
world.removeBlock(tile);
}
@Remote(called = Loc.server, in = In.blocks)
public static void onBuildFinish(Tile tile, Player lastBuilder){
if(tile.entity == null || !(tile.entity instanceof BuildEntity)) return;
BuildEntity entity = tile.entity();
public static void onConstructFinish(Tile tile, Block block, int builderID){
Team team = tile.getTeam();
tile.setBlock(entity.recipe.result);
tile.setBlock(block);
tile.setTeam(team);
Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), entity.recipe.result.size);
Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), block.size);
//last builder was this local client player, call placed()
if(lastBuilder != null && lastBuilder.isLocal){
if(!headless && builderID == players[0].id){
//this is run delayed, since if this is called on the server, all clients need to recieve the onBuildFinish()
//event first before they can recieve the placed() event modification results
threads.runDelay(() -> tile.block().placed(tile));
}
}
@Remote(called = Loc.both, targets = Loc.both, in = In.blocks, forward = true)
public static void onBuildSelect(Player player, Tile tile){
if(player == null || !(tile.entity instanceof BuildEntity)) return;
BuildEntity entity = tile.entity();
player.getPlaceQueue().clear();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
}
@Remote(called = Loc.server, targets = Loc.both, in = In.blocks, forward = true)
public static void onBuildDeselect(Player player){
if(player == null) return;
player.getPlaceQueue().clear();
}
public class BuildEntity extends TileEntity{
/**The recipe of the block that is being constructed.
* If there is no recipe for this block, as is the case with rocks, 'previous' is used.*/
public Recipe recipe;
public double progress = 0;
public double lastProgress;
public double buildCost;
/**The block that used to be here.
* If a non-recipe block is being deconstructed, this is the block that is being deconstructed.*/
public Block previous;
public Player lastBuilder;
private double[] accumulator;
private boolean updated;
public void addProgress(InventoryModule inventory, double amount){
double maxProgress = checkRequired(inventory, amount);
public void construct(Unit builder, TileEntity core, double amount){
double maxProgress = checkRequired(core.items, amount);
for (int i = 0; i < recipe.requirements.length; i++) {
accumulator[i] += recipe.requirements[i].amount*maxProgress; //add min amount progressed to the accumulator
}
maxProgress = checkRequired(inventory, maxProgress);
maxProgress = checkRequired(core.items, maxProgress);
progress += maxProgress;
progress = Mathf.clamp(progress + maxProgress);
lastProgress = maxProgress;
updated = true;
if(progress > 1.0001f){
progress = 1.0001f;
if(progress >= 1f){
CallBlocks.onConstructFinish(tile, recipe.result, builder.getID());
}
}
public double checkRequired(InventoryModule inventory, double amount){
public void deconstruct(Unit builder, TileEntity core, double amount){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null) {
ItemStack[] requirements = recipe.requirements;
for (int i = 0; i < requirements.length; i++) {
accumulator[i] += requirements[i].amount * amount / 2f; //add scaled amount progressed to the accumulator
int accumulated = (int) (accumulator[i]); //get amount
if (amount > 0) { //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, accumulated, core.tile, builder);
core.tile.block().handleStack(requirements[i].item, accumulated, core.tile, builder);
accumulator[i] -= accepting;
}
}
}
progress = Mathf.clamp(progress - amount);
if(progress <= 0){
CallBlocks.onDeconstructFinish(tile, recipe == null ? previous : recipe.result);
}
}
private double checkRequired(InventoryModule inventory, double amount){
double maxProgress = amount;
for(int i = 0; i < recipe.requirements.length; i ++){
@@ -243,11 +255,23 @@ public class BuildBlock extends Block {
return (float)progress;
}
public void set(Block previous, Recipe recipe){
updated = true;
public void setConstruct(Block previous, Recipe recipe){
this.recipe = recipe;
this.previous = previous;
this.accumulator = new double[recipe.requirements.length];
this.buildCost = recipe.cost;
}
public void setDeconstruct(Block previous){
this.previous = previous;
this.progress = 1f;
if(Recipe.getByResult(previous) != null){
this.recipe = Recipe.getByResult(previous);
this.accumulator = new double[Recipe.getByResult(previous).requirements.length];
this.buildCost = Recipe.getByResult(previous).cost;
}else{
this.buildCost = 20f; //default no-recipe build cost is 20
}
}
@Override

View File

@@ -110,6 +110,7 @@ public abstract class Turret extends Block{
stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(BlockStat.reload, 60f/reload, StatUnit.seconds);
stats.add(BlockStat.shots, shots, StatUnit.none);
stats.add(BlockStat.targetsAir, targetAir);
}
@Override

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.world.blocks.power;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.type.Item;
@@ -29,6 +30,7 @@ public abstract class ItemGenerator extends PowerGenerator {
protected Effect generateEffect = BlockFx.generatespark, explodeEffect =
BlockFx.generatespark;
protected Color heatColor = Color.valueOf("ff9b59");
protected TextureRegion topRegion;
public ItemGenerator(String name) {
super(name);
@@ -36,6 +38,12 @@ public abstract class ItemGenerator extends PowerGenerator {
hasItems = true;
}
@Override
public void load() {
super.load();
topRegion = Draw.region(name + "-top");
}
@Override
public void setStats() {
super.setStats();
@@ -61,7 +69,7 @@ public abstract class ItemGenerator extends PowerGenerator {
float alpha = (entity.items.totalItems() > 0 ? 1f : Mathf.clamp(entity.generateTime));
alpha = alpha * 0.7f + Mathf.absin(Timers.time(), 12f, 0.3f) * alpha;
Draw.alpha(alpha);
Draw.rect(name + "-top", tile.worldx(), tile.worldy());
Draw.rect(topRegion, tile.drawx(), tile.drawy());
Draw.reset();
}
}

View File

@@ -51,8 +51,11 @@ public class PowerDistributor extends PowerBlock {
}
protected boolean shouldDistribute(Tile tile, Tile other) {
other = other.target();
//only generators can distribute to other generators
return (!(other.block() instanceof PowerGenerator) || tile.block() instanceof PowerGenerator)
&& other.entity != null
&& other.block().hasPower
&& other.entity.power.amount / other.block().powerCapacity < tile.entity.power.amount / powerCapacity;
}

View File

@@ -38,7 +38,8 @@ public enum BlockStat {
shots(StatCategory.shooting),
reload(StatCategory.shooting),
powerShot(StatCategory.shooting),
targetsAir(StatCategory.shooting)
,
;

View File

@@ -5,10 +5,7 @@ import com.badlogic.gdx.utils.OrderedMap;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.world.meta.values.ItemValue;
import io.anuke.mindustry.world.meta.values.LiquidValue;
import io.anuke.mindustry.world.meta.values.NumberValue;
import io.anuke.mindustry.world.meta.values.StringValue;
import io.anuke.mindustry.world.meta.values.*;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Log;
@@ -24,6 +21,11 @@ public class BlockStats {
add(stat, new NumberValue(value, unit));
}
/**Adds a single y/n boolean value.*/
public void add(BlockStat stat, boolean value){
add(stat, new BooleanValue(value));
}
/**Adds an item value.*/
public void add(BlockStat stat, Item item){
add(stat, new ItemValue(new ItemStack(item, 1)));

View File

@@ -0,0 +1,17 @@
package io.anuke.mindustry.world.meta.values;
import io.anuke.mindustry.world.meta.StatValue;
import io.anuke.ucore.scene.ui.layout.Table;
public class BooleanValue implements StatValue {
private final boolean value;
public BooleanValue(boolean value) {
this.value = value;
}
@Override
public void display(Table table) {
table.add(!value ? "$text.no" : "$text.yes");
}
}