Recipe class removed completely

This commit is contained in:
Anuken 2019-01-20 13:49:53 -05:00
parent 750388a425
commit 0478af2564
33 changed files with 285 additions and 471 deletions

View File

@ -416,7 +416,7 @@ mode.attack.descrption = No waves, with the goal to destroy the enemy base.
content.item.name = Items
content.liquid.name = Liquids
content.unit.name = Units
content.recipe.name = Blocks
content.block.name = Blocks
content.mech.name = Mechs
item.copper.name = Copper
item.copper.description = A useful structure material. Used extensively in all types of blocks.

View File

@ -729,6 +729,7 @@ public class Blocks implements ContentList{
launchThreshold = 500;
launchTime = 60f * 10;
launchChunkSize = 100;
alwaysUnlocked = true;
}};
vault = new Vault("vault"){{

View File

@ -1,16 +1,12 @@
package io.anuke.mindustry.content;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.type.Recipe.RecipeVisibility;
import static io.anuke.mindustry.type.Category.*;
public class Recipes implements ContentList{
@Override
public void load(){
/*
//DEBUG
new Recipe(distribution, Blocks.itemSource).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
new Recipe(distribution, Blocks.itemVoid).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
@ -174,5 +170,6 @@ public class Recipes implements ContentList{
new Recipe(liquid, Blocks.mechanicalPump, new ItemStack(Items.copper, 30), new ItemStack(Items.lead, 20));
new Recipe(liquid, Blocks.rotaryPump, new ItemStack(Items.copper, 140), new ItemStack(Items.lead, 100), new ItemStack(Items.silicon, 40), new ItemStack(Items.titanium, 70));
new Recipe(liquid, Blocks.thermalPump, new ItemStack(Items.copper, 160), new ItemStack(Items.lead, 130), new ItemStack(Items.silicon, 60), new ItemStack(Items.titanium, 80), new ItemStack(Items.thorium, 70));
*/
}
}

View File

@ -3,7 +3,6 @@ package io.anuke.mindustry.content;
import io.anuke.arc.collection.Array;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import static io.anuke.mindustry.type.ItemStack.with;
@ -264,7 +263,6 @@ public class TechTree implements ContentList{
public final Array<TechNode> children = new Array<>();
TechNode(Block block, ItemStack[] requirements, Runnable children){
if(block != null && Recipe.getByResult(block) == null) throw new IllegalArgumentException("Block " + block + " does not have a recipe.");
if(context != null){
context.children.add(this);
}
@ -277,9 +275,5 @@ public class TechTree implements ContentList{
children.run();
context = last;
}
public Recipe recipe(){
return Recipe.getByResult(block);
}
}
}

View File

@ -44,7 +44,6 @@ public class ContentLoader{
new Mechs(),
new UnitTypes(),
new Blocks(),
new Recipes(),
new TechTree(),
new Zones(),
@ -185,14 +184,6 @@ public class ContentLoader{
return (Block) getByID(ContentType.block, id);
}
public Array<Recipe> recipes(){
return getBy(ContentType.recipe);
}
public Recipe recipe(int id){
return (Recipe) getByID(ContentType.recipe, id);
}
public Array<Item> items(){
return getBy(ContentType.item);
}

View File

@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
@ -30,8 +29,8 @@ public class Logic implements ApplicationListener{
public Logic(){
Events.on(TileChangeEvent.class, event -> {
if(event.tile.getTeam() == defaultTeam && Recipe.getByResult(event.tile.block()) != null){
handleContent(Recipe.getByResult(event.tile.block()));
if(event.tile.getTeam() == defaultTeam && event.tile.block().isVisible()){
handleContent(event.tile.block());
}
});
}

View File

@ -296,7 +296,7 @@ public class NetServer implements ApplicationListener{
//auto-skip done requests
if(req.breaking && world.tile(req.x, req.y).block() == Blocks.air){
continue;
}else if(!req.breaking && world.tile(req.x, req.y).block() == req.recipe.result && (!req.recipe.result.rotate || world.tile(req.x, req.y).getRotation() == req.rotation)){
}else if(!req.breaking && world.tile(req.x, req.y).block() == req.block && (!req.block.rotate || world.tile(req.x, req.y).getRotation() == req.rotation)){
continue;
}
player.getPlaceQueue().addLast(req);

View File

@ -461,28 +461,28 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
request.y * tilesize + block.offset(),
rad);
}else{
float rad = Mathf.absin(Time.time(), 7f, 1f) - 1.5f + request.recipe.result.size * tilesize / 2f;
float rad = Mathf.absin(Time.time(), 7f, 1f) - 1.5f + request.block.size * tilesize / 2f;
//draw place request
Lines.stroke(1f, Palette.accentBack);
Lines.square(
request.x * tilesize + request.recipe.result.offset(),
request.y * tilesize + request.recipe.result.offset() - 1,
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset() - 1,
rad);
Draw.color();
Draw.rect(request.recipe.result.icon(Icon.full),
request.x * tilesize + request.recipe.result.offset(),
request.y * tilesize + request.recipe.result.offset(), rad*2, rad*2, request.rotation * 90);
Draw.rect(request.block.icon(Icon.full),
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset(), rad*2, rad*2, request.rotation * 90);
Draw.color(Palette.accent);
Lines.square(
request.x * tilesize + request.recipe.result.offset(),
request.y * tilesize + request.recipe.result.offset(),
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset(),
rad);
}
}

View File

@ -26,7 +26,7 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shapes;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Build;
import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile;
@ -81,7 +81,7 @@ public interface BuilderTrait extends Entity{
output.writeInt(Pos.get(request.x, request.y));
output.writeFloat(request.progress);
if(!request.breaking){
output.writeByte(request.recipe.id);
output.writeByte(request.block.id);
output.writeByte(request.rotation);
}
}else{
@ -105,9 +105,9 @@ public interface BuilderTrait extends Entity{
if(type == 1){ //remove
request = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
byte recipe = input.readByte();
byte block = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.recipe(recipe));
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
}
request.progress = progress;
@ -129,7 +129,7 @@ public interface BuilderTrait extends Entity{
* If a place request matching this signature is present, it is removed.
* Otherwise, a new place request is added to the queue.
*/
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
default void replaceBuilding(int x, int y, int rotation, Block block){
for(BuildRequest request : getPlaceQueue()){
if(request.x == x && request.y == y){
clearBuilding();
@ -138,7 +138,7 @@ public interface BuilderTrait extends Entity{
}
}
addBuildRequest(new BuildRequest(x, y, rotation, recipe));
addBuildRequest(new BuildRequest(x, y, rotation, block));
}
/**Clears the placement queue.*/
@ -184,8 +184,8 @@ public interface BuilderTrait extends Entity{
for(BuildRequest request : removal){
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
(!request.breaking &&
(world.tile(request.x, request.y).getRotation() == request.rotation || !request.recipe.result.rotate)
&& world.tile(request.x, request.y).block() == request.recipe.result))){
(world.tile(request.x, request.y).getRotation() == request.rotation || !request.block.rotate)
&& world.tile(request.x, request.y).block() == request.block))){
getPlaceQueue().addLast(request);
}
}
@ -209,8 +209,8 @@ public interface BuilderTrait extends Entity{
}
if(!(tile.block() instanceof BuildBlock)){
if(canCreateBlocks() && !current.breaking && 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);
if(canCreateBlocks() && !current.breaking && Build.validPlace(unit.getTeam(), current.x, current.y, current.block, current.rotation)){
Build.beginPlace(unit.getTeam(), current.x, current.y, current.block, current.rotation);
}else if(canCreateBlocks() && current.breaking && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
}else{
@ -370,18 +370,18 @@ public interface BuilderTrait extends Entity{
/**Class for storing build requests. Can be either a place or remove request.*/
class BuildRequest{
public final int x, y, rotation;
public final Recipe recipe;
public final Block block;
public final boolean breaking;
public float progress;
public boolean initialized;
/**This creates a build request.*/
public BuildRequest(int x, int y, int rotation, Recipe recipe){
public BuildRequest(int x, int y, int rotation, Block block){
this.x = x;
this.y = y;
this.rotation = rotation;
this.recipe = recipe;
this.block = block;
this.breaking = false;
}
@ -390,7 +390,7 @@ public interface BuilderTrait extends Entity{
this.x = x;
this.y = y;
this.rotation = -1;
this.recipe = Recipe.getByResult(world.tile(x, y).block());
this.block = world.tile(x, y).block();
this.breaking = true;
}
@ -400,7 +400,7 @@ public interface BuilderTrait extends Entity{
"x=" + x +
", y=" + y +
", rotation=" + rotation +
", recipe=" + recipe +
", recipe=" + block +
", breaking=" + breaking +
", progress=" + progress +
", initialized=" + initialized +

View File

@ -65,13 +65,13 @@ public class Drone extends FlyingUnit implements BuilderTrait{
if(isBreaking){
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
}else{
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.recipe));
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.block));
}
}
//if it's missing requirements, try and mine them
if(entity.recipe != null){
for(ItemStack stack : entity.recipe.requirements){
if(entity.block != null){
for(ItemStack stack : entity.block.buildRequirements){
if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){
targetItem = stack.item;
getPlaceQueue().clear();

View File

@ -89,7 +89,7 @@ public class OverlayRenderer{
Draw.reset();
//draw selected block bars and info
if(input.recipe == null && !Core.scene.hasMouse()){
if(input.block == null && !Core.scene.hasMouse()){
Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
Tile tile = world.tileWorld(vec.x, vec.y);

View File

@ -41,7 +41,7 @@ public class Palette{
heal = Color.valueOf("98ffa9"),
bar = Color.SLATE,
accent = Color.valueOf("ffd37f"),
locked = Color.valueOf("6b6b6b"),
locked = Color.valueOf("989aa4"),
accentBack = Color.valueOf("d4816b"),
place = Color.valueOf("6335f8"),
remove = Color.valueOf("e55454"),

View File

@ -60,7 +60,7 @@ public class DesktopInput extends InputHandler{
@Override
public boolean isDrawing(){
return mode != none || recipe != null;
return mode != none || block != null;
}
@Override
@ -70,30 +70,30 @@ public class DesktopInput extends InputHandler{
int cursorY = tileY(Core.input.mouseY());
//draw selection(s)
if(mode == placing && recipe != null){
if(mode == placing && block != null){
NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, true, maxLength);
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = selectX + i * Mathf.sign(cursorX - selectX) * Mathf.num(result.isX());
int y = selectY + i * Mathf.sign(cursorY - selectY) * Mathf.num(!result.isX());
if(i + recipe.result.size > result.getLength() && recipe.result.rotate){
Draw.color(!validPlace(x, y, recipe.result, result.rotation) ? Palette.removeBack : Palette.accentBack);
if(i + block.size > result.getLength() && block.rotate){
Draw.color(!validPlace(x, y, block, result.rotation) ? Palette.removeBack : Palette.accentBack);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + recipe.result.offset(),
y * tilesize + recipe.result.offset() - 1,
x * tilesize + block.offset(),
y * tilesize + block.offset() - 1,
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, result.rotation * 90 - 90);
Draw.color(!validPlace(x, y, recipe.result, result.rotation) ? Palette.remove : Palette.accent);
Draw.color(!validPlace(x, y, block, result.rotation) ? Palette.remove : Palette.accent);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + recipe.result.offset(),
y * tilesize + recipe.result.offset(),
x * tilesize + block.offset(),
y * tilesize + block.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, result.rotation * 90 - 90);
}
drawPlace(x, y, recipe.result, result.rotation);
drawPlace(x, y, block, result.rotation);
}
Draw.reset();
@ -119,23 +119,23 @@ public class DesktopInput extends InputHandler{
Draw.color(Palette.remove);
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
}else if(isPlacing()){
if(recipe.result.rotate){
Draw.color(!validPlace(cursorX, cursorY, recipe.result, rotation) ? Palette.removeBack : Palette.accentBack);
if(block.rotate){
Draw.color(!validPlace(cursorX, cursorY, block, rotation) ? Palette.removeBack : Palette.accentBack);
Draw.rect(Core.atlas.find("place-arrow"),
cursorX * tilesize + recipe.result.offset(),
cursorY * tilesize + recipe.result.offset() - 1,
cursorX * tilesize + block.offset(),
cursorY * tilesize + block.offset() - 1,
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
Draw.color(!validPlace(cursorX, cursorY, recipe.result, rotation) ? Palette.remove : Palette.accent);
Draw.color(!validPlace(cursorX, cursorY, block, rotation) ? Palette.remove : Palette.accent);
Draw.rect(Core.atlas.find("place-arrow"),
cursorX * tilesize + recipe.result.offset(),
cursorY * tilesize + recipe.result.offset(),
cursorX * tilesize + block.offset(),
cursorY * tilesize + block.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
}
drawPlace(cursorX, cursorY, recipe.result, rotation);
recipe.result.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, recipe.result, rotation));
drawPlace(cursorX, cursorY, block, rotation);
block.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, block, rotation));
}
Draw.reset();
@ -234,13 +234,13 @@ public class DesktopInput extends InputHandler{
}else if(!ui.chatfrag.chatOpen()){ //if it's out of bounds, shooting is just fine
player.isShooting = true;
}
}else if(Core.input.keyTap(Binding.deselect) && (recipe != null || mode != none || player.isBuilding()) &&
}else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) &&
!(player.getCurrentRequest() != null && player.getCurrentRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
if(recipe == null){
if(block == null){
player.clearBuilding();
}
recipe = null;
block = null;
mode = none;
}else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse()){
//is recalculated because setting the mode to breaking removes potential multiblock cursor offset
@ -252,10 +252,10 @@ public class DesktopInput extends InputHandler{
if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){
if(mode == placing && recipe != null){ //touch up while placing, place everything in selection
if(mode == placing && block != null){ //touch up while placing, place everything in selection
NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, true, maxLength);
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = selectX + i * Mathf.sign(cursorX - selectX) * Mathf.num(result.isX());
int y = selectY + i * Mathf.sign(cursorY - selectY) * Mathf.num(!result.isX());

View File

@ -21,7 +21,6 @@ import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.ValidateException;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.fragments.OverlayFragment;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Build;
@ -41,7 +40,7 @@ public abstract class InputHandler implements InputProcessor{
public final Player player;
public final OverlayFragment frag = new OverlayFragment(this);
public Recipe recipe;
public Block block;
public int rotation;
public boolean droppingItem;
@ -199,7 +198,7 @@ public abstract class InputHandler implements InputProcessor{
if(!consumed && player.isBuilding()){
player.clearBuilding();
recipe = null;
block = null;
return true;
}
@ -245,7 +244,7 @@ public abstract class InputHandler implements InputProcessor{
int tileX(float cursorX){
Vector2 vec = Core.input.mouseWorld(cursorX, 0);
if(selectedBlock()){
vec.sub(recipe.result.offset(), recipe.result.offset());
vec.sub(block.offset(), block.offset());
}
return world.toTile(vec.x);
}
@ -253,7 +252,7 @@ public abstract class InputHandler implements InputProcessor{
int tileY(float cursorY){
Vector2 vec = Core.input.mouseWorld(0, cursorY);
if(selectedBlock()){
vec.sub(recipe.result.offset(), recipe.result.offset());
vec.sub(block.offset(), block.offset());
}
return world.toTile(vec.y);
}
@ -263,7 +262,7 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean isPlacing(){
return recipe != null;
return block != null;
}
public float mouseAngle(float x, float y){
@ -276,7 +275,7 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean canShoot(){
return recipe == null && !Core.scene.hasMouse() && !onConfigurable() && !isDroppingItem();
return block == null && !Core.scene.hasMouse() && !onConfigurable() && !isDroppingItem();
}
public boolean onConfigurable(){
@ -309,8 +308,8 @@ public abstract class InputHandler implements InputProcessor{
}
public void tryPlaceBlock(int x, int y){
if(recipe != null && validPlace(x, y, recipe.result, rotation) && cursorNear()){
placeBlock(x, y, recipe, rotation);
if(block != null && validPlace(x, y, block, rotation) && cursorNear()){
placeBlock(x, y, block, rotation);
}
}
@ -329,8 +328,8 @@ public abstract class InputHandler implements InputProcessor{
return Build.validBreak(player.getTeam(), x, y) && Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
public void placeBlock(int x, int y, Recipe recipe, int rotation){
player.addBuildRequest(new BuildRequest(x, y, rotation, recipe));
public void placeBlock(int x, int y, Block block, int rotation){
player.addBuildRequest(new BuildRequest(x, y, rotation, block));
}
public void breakBlock(int x, int y){

View File

@ -30,7 +30,6 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
@ -73,7 +72,7 @@ public class MobileInput extends InputHandler implements GestureListener{
/** Current place mode. */
private PlaceMode mode = none;
/** Whether no recipe was available when switching to break mode. */
private Recipe lastRecipe;
private Block lastBlock;
/** Last placed request. Used for drawing block overlay. */
private PlaceRequest lastPlaced;
@ -121,8 +120,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(other == null || req.remove) continue;
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
r1.setSize(req.block.size * tilesize);
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
if(r2.overlaps(r1)){
return true;
@ -142,8 +141,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(other == null) continue;
if(!req.remove){
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
r1.setSize(req.block.size * tilesize);
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
if(r2.overlaps(r1)){
return req;
@ -171,8 +170,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(!request.remove){
//draw placing request
float offset = request.recipe.result.offset();
TextureRegion region = request.recipe.result.icon(Icon.full);
float offset = request.block.offset();
TextureRegion region = request.block.icon(Icon.full);
Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f));
Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness);
@ -180,7 +179,7 @@ public class MobileInput extends InputHandler implements GestureListener{
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getWidth() * request.scale * Draw.scl,
region.getHeight() * request.scale * Draw.scl,
request.recipe.result.rotate ? request.rotation * 90 : 0);
request.block.rotate ? request.rotation * 90 : 0);
}else{
float rad = (tile.block().size * tilesize / 2f - 1) * request.scale;
Draw.alpha(0f);
@ -219,8 +218,8 @@ public class MobileInput extends InputHandler implements GestureListener{
table.left().margin(0f).defaults().size(48f);
table.addImageButton("icon-break", "clear-toggle-partial", 16 * 2f, () -> {
mode = mode == breaking ? recipe == null ? none : placing : breaking;
lastRecipe = recipe;
mode = mode == breaking ? block == null ? none : placing : breaking;
lastBlock = block;
if(mode == breaking){
showGuide("deconstruction");
}
@ -230,13 +229,13 @@ public class MobileInput extends InputHandler implements GestureListener{
table.addImageButton("icon-cancel", "clear-partial", 16 * 2f, () -> {
player.clearBuilding();
mode = none;
recipe = null;
}).visible(() -> player.isBuilding() || recipe != null || mode == breaking);
block = null;
}).visible(() -> player.isBuilding() || block != null || mode == breaking);
//rotate button
table.addImageButton("icon-arrow", "clear-partial", 16 * 2f, () -> rotation = Mathf.mod(rotation + 1, 4))
.update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center))
.visible(() -> recipe != null && recipe.result.rotate);
.visible(() -> block != null && block.rotate);
//confirm button
table.addImageButton("icon-check", "clear-partial", 16 * 2f, () -> {
@ -247,10 +246,10 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile != null){
if(!request.remove){
rotation = request.rotation;
Recipe before = recipe;
recipe = request.recipe;
Block before = block;
block = request.block;
tryPlaceBlock(tile.x, tile.y);
recipe = before;
block = before;
}else{
tryBreakBlock(tile.x, tile.y);
}
@ -299,7 +298,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile == null) continue;
if((!request.remove && validPlace(tile.x, tile.y, request.recipe.result, request.rotation))
if((!request.remove && validPlace(tile.x, tile.y, request.block, request.rotation))
|| (request.remove && validBreak(tile.x, tile.y))){
request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f);
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
@ -312,8 +311,8 @@ public class MobileInput extends InputHandler implements GestureListener{
drawRequest(request);
//draw last placed request
if(!request.remove && request == lastPlaced && request.recipe != null){
request.recipe.result.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.recipe.result, rotation));
if(!request.remove && request == lastPlaced && request.block != null){
request.block.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.block, rotation));
}
}
@ -327,32 +326,32 @@ public class MobileInput extends InputHandler implements GestureListener{
int tileY = tileY(Core.input.mouseY());
//draw placing
if(mode == placing && recipe != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tileX, tileY, true, maxLength, lineScale);
if(mode == placing && block != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(block, lineStartX, lineStartY, tileX, tileY, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, true, maxLength);
//go through each cell and draw the block to place if valid
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = lineStartX + i * Mathf.sign(tileX - lineStartX) * Mathf.num(result.isX());
int y = lineStartY + i * Mathf.sign(tileY - lineStartY) * Mathf.num(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
if(!checkOverlapPlacement(x, y, block) && validPlace(x, y, block, result.rotation)){
Draw.color();
TextureRegion region = recipe.result.icon(Icon.full);
TextureRegion region = block.icon(Icon.full);
Draw.rect(region, x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(),
Draw.rect(region, x * tilesize + block.offset(), y * tilesize + block.offset(),
region.getWidth() * lineScale * Draw.scl,
region.getHeight() * lineScale * Draw.scl,
recipe.result.rotate ? result.rotation * 90 : 0);
block.rotate ? result.rotation * 90 : 0);
}else{
Draw.color(Palette.removeBack);
Lines.square(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset() - 1, recipe.result.size * tilesize / 2f);
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f);
Draw.color(Palette.remove);
Lines.square(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), recipe.result.size * tilesize / 2f);
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f);
}
}
@ -441,7 +440,7 @@ public class MobileInput extends InputHandler implements GestureListener{
int tileX = tileX(screenX);
int tileY = tileY(screenY);
if(mode == placing && recipe != null){
if(mode == placing && block != null){
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, true, 100);
@ -449,12 +448,12 @@ public class MobileInput extends InputHandler implements GestureListener{
rotation = result.rotation;
//place blocks on line
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = lineStartX + i * Mathf.sign(tileX - lineStartX) * Mathf.num(result.isX());
int y = lineStartY + i * Mathf.sign(tileY - lineStartY) * Mathf.num(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
PlaceRequest request = new PlaceRequest(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), recipe, result.rotation);
if(!checkOverlapPlacement(x, y, block) && validPlace(x, y, block, result.rotation)){
PlaceRequest request = new PlaceRequest(x * tilesize + block.offset(), y * tilesize + block.offset(), block, result.rotation);
request.scale = 1f;
selection.add(request);
}
@ -517,8 +516,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(mode == breaking){
Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f);
}else if(recipe != null){
Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size);
}else if(block != null){
Effects.effect(Fx.tapBlock, cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block.size);
}
return false;
@ -541,9 +540,9 @@ public class MobileInput extends InputHandler implements GestureListener{
//remove if request present
if(hasRequest(cursor)){
removeRequest(getRequest(cursor));
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, recipe.result, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){
//add to selection queue if it's a valid place position
selection.add(lastPlaced = new PlaceRequest(cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe, rotation));
selection.add(lastPlaced = new PlaceRequest(cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block, rotation));
}else if(mode == breaking && validBreak(cursor.target().x, cursor.target().y) && !hasRequest(cursor.target())){
//add to selection queue if it's a valid BREAK position
cursor = cursor.target();
@ -590,27 +589,27 @@ public class MobileInput extends InputHandler implements GestureListener{
selection.clear();
}
if(lineMode && mode == placing && recipe == null){
if(lineMode && mode == placing && block == null){
lineMode = false;
}
//if there is no mode and there's a recipe, switch to placing
if(recipe != null && mode == none){
if(block != null && mode == none){
mode = placing;
}
if(recipe != null){
if(block != null){
showGuide("construction");
}
if(recipe == null && mode == placing){
if(block == null && mode == placing){
mode = none;
}
//automatically switch to placing after a new recipe is selected
if(lastRecipe != recipe && mode == breaking && recipe != null){
if(lastBlock != block && mode == breaking && block != null){
mode = placing;
lastRecipe = recipe;
lastBlock = block;
}
if(lineMode){
@ -712,7 +711,7 @@ public class MobileInput extends InputHandler implements GestureListener{
class PlaceRequest{
float x, y;
Recipe recipe;
Block block;
int rotation;
boolean remove;
@ -720,10 +719,10 @@ public class MobileInput extends InputHandler implements GestureListener{
float scale;
float redness;
PlaceRequest(float x, float y, Recipe recipe, int rotation){
PlaceRequest(float x, float y, Block block, int rotation){
this.x = x;
this.y = y;
this.recipe = recipe;
this.block = block;
this.rotation = rotation;
this.remove = false;
}
@ -735,7 +734,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}
Tile tile(){
return world.tileWorld(x - (recipe == null ? 0 : recipe.result.offset()), y - (recipe == null ? 0 : recipe.result.offset()));
return world.tileWorld(x - (block == null ? 0 : block.offset()), y - (block == null ? 0 : block.offset()));
}
}
}

View File

@ -172,7 +172,7 @@ public class TypeIO{
buffer.put(request.breaking ? (byte) 1 : 0);
buffer.putInt(Pos.get(request.x, request.y));
if(!request.breaking){
buffer.put(request.recipe.id);
buffer.put(request.block.id);
buffer.put((byte) request.rotation);
}
}
@ -190,9 +190,9 @@ public class TypeIO{
if(type == 1){ //remove
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
byte recipe = buffer.get();
byte block = buffer.get();
byte rotation = buffer.get();
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.recipe(recipe));
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
}
reqs[i] = (currentRequest);
@ -302,16 +302,6 @@ public class TypeIO{
return id == -1 ? null : content.item(id);
}
@WriteClass(Recipe.class)
public static void writeRecipe(ByteBuffer buffer, Recipe recipe){
buffer.put(recipe.id);
}
@ReadClass(Recipe.class)
public static Recipe readRecipe(ByteBuffer buffer){
return content.recipe(buffer.get());
}
@WriteClass(String.class)
public static void writeString(ByteBuffer buffer, String string){
if(string != null){

View File

@ -3,7 +3,6 @@ package io.anuke.mindustry.type;
/**Do not rearrange, ever!*/
public enum ContentType {
item,
recipe,
block,
mech,
bullet,

View File

@ -1,170 +0,0 @@
package io.anuke.mindustry.type;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.collection.OrderedMap;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.ContentStatValue;
import io.anuke.mindustry.world.meta.StatValue;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.*;
public class Recipe extends UnlockableContent{
private static ObjectMap<Block, Recipe> recipeMap = new ObjectMap<>();
private static Array<Recipe> returnArray = new Array<>();
public final Block result;
public final ItemStack[] requirements;
public final Category category;
public final float cost;
public RecipeVisibility visibility = RecipeVisibility.all;
public boolean hidden;
public boolean alwaysUnlocked;
public Recipe(Category category, Block result, ItemStack... requirements){
this.result = result;
this.requirements = requirements;
this.category = category;
Arrays.sort(requirements, (a, b) -> Integer.compare(a.item.id, b.item.id));
float timeToPlace = 0f;
for(ItemStack stack : requirements){
timeToPlace += stack.amount * stack.item.cost;
}
this.cost = timeToPlace;
recipeMap.put(result, this);
}
/**Returns all non-hidden recipes in a category.*/
public static Array<Recipe> getByCategory(Category category){
returnArray.clear();
for(Recipe recipe : content.recipes()){
if(recipe.category == category && recipe.visibility.shown()){
returnArray.add(recipe);
}
}
return returnArray;
}
public static Recipe getByResult(Block block){
return recipeMap.get(block);
}
public Recipe setVisible(RecipeVisibility visibility){
this.visibility = visibility;
return this;
}
public Recipe setHidden(boolean hidden){
this.hidden = hidden;
return this;
}
public Recipe setAlwaysUnlocked(boolean unlocked){
this.alwaysUnlocked = unlocked;
return this;
}
@Override
public boolean alwaysUnlocked(){
return alwaysUnlocked;
}
@Override
public boolean isHidden(){
return !visibility.shown() || hidden;
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayBlock(table, result);
}
@Override
public String localizedName(){
return result.formalName;
}
@Override
public TextureRegion getContentIcon(){
return result.icon(Icon.large);
}
@Override
public void init(){
if(!Core.bundle.has("block." + result.name + ".name")){
Log.err("WARNING: Recipe block '{0}' does not have a formal name defined. Add the following to bundle.properties:", result.name);
Log.err("block.{0}.name={1}", result.name, Strings.capitalize(result.name.replace('-', '_')));
}/*else if(result.fullDescription == null){
Log.err("WARNING: Recipe block '{0}' does not have a description defined.", result.name);
}*/
}
@Override
public String getContentName(){
return result.name;
}
@Override
public ContentType getContentType(){
return ContentType.recipe;
}
@Override
public void onUnlock(){
for(OrderedMap<BlockStat, StatValue> map : result.stats.toMap().values()){
for(StatValue value : map.values()){
if(value instanceof ContentStatValue){
ContentStatValue stat = (ContentStatValue) value;
UnlockableContent[] content = stat.getValueContent();
for(UnlockableContent c : content){
data.unlockContent(c);
}
}
}
}
}
public enum RecipeVisibility{
mobileOnly(true, false),
desktopOnly(false, true),
all(true, true),
sandboxOnly(true, true){
@Override
public boolean usable(){
return state.rules.infiniteResources;
}
};
public final boolean mobile, desktop;
RecipeVisibility(boolean mobile, boolean desktop){
this.mobile = mobile;
this.desktop = desktop;
}
public boolean usable(){
return true;
}
public boolean shown(){
return usable() && ((Vars.mobile && mobile) || (!Vars.mobile && desktop));
}
}
}

View File

@ -24,10 +24,9 @@ import io.anuke.mindustry.content.TechTree;
import io.anuke.mindustry.content.TechTree.TechNode;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.type.Recipe.RecipeVisibility;
import io.anuke.mindustry.ui.TreeLayout;
import io.anuke.mindustry.ui.TreeLayout.TreeNode;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.*;
@ -50,15 +49,15 @@ public class TechTreeDialog extends FloatingDialog{
cont.add(new View()).grow();
{ //debug code; TODO remove
ObjectSet<Recipe> used = new ObjectSet<Recipe>().select(t -> true);
ObjectSet<Block> used = new ObjectSet<Block>().select(t -> true);
for(TechTreeNode node : nodes){
used.add(node.node.recipe());
used.add(node.node.block);
}
Array<Recipe> recipes = content.recipes().select(r -> r.visibility == RecipeVisibility.all && !used.contains(r));
recipes.sort(Structs.comparing(r -> r.cost));
Array<Block> recipes = content.blocks().select(r -> r.isVisible() && !used.contains(r));
recipes.sort(Structs.comparing(r -> r.buildCost));
if(recipes.size > 0){
Log.info("Recipe tree coverage: {0}%", (int)((float)nodes.size / content.recipes().select(r -> r.visibility == RecipeVisibility.all).size * 100));
Log.info("Recipe tree coverage: {0}%", (int)((float)nodes.size / content.blocks().select(Block::isVisible).size * 100));
Log.info("Missing items: ");
recipes.forEach(r -> Log.info(" {0}", r));
}
@ -111,7 +110,7 @@ public class TechTreeDialog extends FloatingDialog{
}
boolean locked(TechNode node){
return !data.isUnlocked(node.recipe());
return !data.isUnlocked(node.block);
}
class TechTreeNode extends TreeNode{
@ -199,7 +198,7 @@ public class TechTreeDialog extends FloatingDialog{
}
void unlock(TechNode node){
data.unlockContent(node.recipe());
data.unlockContent(node.block);
data.removeItems(node.requirements);
showToast(Core.bundle.format("researched", node.block.formalName));
checkNodes(root);
@ -230,7 +229,7 @@ public class TechTreeDialog extends FloatingDialog{
infoTable.margin(0).left().defaults().left();
infoTable.addImageButton("icon-info", "node", 14*2, () -> ui.content.show(node.recipe())).growY().width(50f);
infoTable.addImageButton("icon-info", "node", 14*2, () -> ui.content.show(node.block)).growY().width(50f);
infoTable.add().grow();

View File

@ -22,7 +22,6 @@ import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.type.Category;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
@ -33,6 +32,7 @@ import static io.anuke.mindustry.Vars.*;
public class PlacementFragment extends Fragment{
final int rowWidth = 4;
Array<Block> returnArray = new Array<>();
Category currentCategory = Category.turret;
Block hovered, lastDisplay;
Tile hoverTile;
@ -68,8 +68,8 @@ public class PlacementFragment extends Fragment{
int i = 0;
for(KeyCode key : inputCatGrid){
if(Core.input.keyDown(key)){
input.recipe = Recipe.getByCategory(Category.values()[i]).first();
currentCategory = input.recipe.category;
input.block = getByCategory(Category.values()[i]).first();
currentCategory = input.block.buildCategory;
}
i++;
}
@ -79,19 +79,19 @@ public class PlacementFragment extends Fragment{
if(tile != null){
tile = tile.target();
Recipe tryRecipe = Recipe.getByResult(tile.block());
if(tryRecipe != null && data.isUnlocked(tryRecipe)){
input.recipe = tryRecipe;
currentCategory = input.recipe.category;
Block tryRecipe = tile.block();
if(tryRecipe.isVisible() && data.isUnlocked(tryRecipe)){
input.block = tryRecipe;
currentCategory = input.block.buildCategory;
return true;
}
}
}else{ //select block
int i = 0;
Array<Recipe> recipes = Recipe.getByCategory(currentCategory);
Array<Block> recipes = getByCategory(currentCategory);
for(KeyCode key : inputGrid){
if(Core.input.keyDown(key))
input.recipe = (i < recipes.size && data.isUnlocked(recipes.get(i))) ? recipes.get(i) : null;
input.block = (i < recipes.size && data.isUnlocked(recipes.get(i))) ? recipes.get(i) : null;
i++;
}
}
@ -117,7 +117,7 @@ public class PlacementFragment extends Fragment{
ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
for(Recipe recipe : Recipe.getByCategory(currentCategory)){
for(Block block : getByCategory(currentCategory)){
if(index++ % rowWidth == 0){
blockTable.row();
@ -126,17 +126,17 @@ public class PlacementFragment extends Fragment{
boolean[] unlocked = {false};
ImageButton button = blockTable.addImageButton("icon-locked", "select", 8 * 4, () -> {
if(data.isUnlocked(recipe)){
input.recipe = input.recipe == recipe ? null : recipe;
if(data.isUnlocked(block)){
input.block = input.block == block ? null : block;
}
}).size(46f).group(group).get();
button.update(() -> { //color unplacable things gray
boolean ulock = data.isUnlocked(recipe);
boolean ulock = data.isUnlocked(block);
TileEntity core = players[0].getClosestCore();
Color color = core != null && (core.items.has(recipe.requirements) || state.rules.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
Color color = core != null && (core.items.has(block.buildRequirements) || state.rules.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
button.forEach(elem -> elem.setColor(color));
button.setChecked(input.recipe == recipe);
button.setChecked(input.block == block);
if(ulock == unlocked[0]) return;
unlocked[0] = ulock;
@ -144,13 +144,13 @@ public class PlacementFragment extends Fragment{
if(!ulock){
button.replaceImage(new Image("icon-locked"));
}else{
button.replaceImage(new Image(recipe.result.icon(Icon.medium)));
button.replaceImage(new Image(block.icon(Icon.medium)));
}
});
button.hovered(() -> hovered = recipe.result);
button.hovered(() -> hovered = block);
button.exited(() -> {
if(hovered == recipe.result){
if(hovered == block){
hovered = null;
}
});
@ -176,11 +176,11 @@ public class PlacementFragment extends Fragment{
header.left();
header.add(new Image(lastDisplay.icon(Icon.medium))).size(8 * 4);
header.labelWrap(() ->
!data.isUnlocked(Recipe.getByResult(lastDisplay)) ? Core.bundle.get("blocks.unknown") : lastDisplay.formalName)
!data.isUnlocked(lastDisplay) ? Core.bundle.get("blocks.unknown") : lastDisplay.formalName)
.left().width(190f).padLeft(5);
header.add().growX();
if(data.isUnlocked(Recipe.getByResult(lastDisplay))){
header.addButton("?", "clear-partial", () -> ui.content.show(Recipe.getByResult(lastDisplay)))
if(data.isUnlocked(lastDisplay)){
header.addButton("?", "clear-partial", () -> ui.content.show(lastDisplay))
.size(8 * 5).padTop(-5).padRight(-5).right().grow();
}
}).growX().left();
@ -189,7 +189,7 @@ public class PlacementFragment extends Fragment{
topTable.table(req -> {
req.top().left();
for(ItemStack stack : Recipe.getByResult(lastDisplay).requirements){
for(ItemStack stack : lastDisplay.buildRequirements){
req.table(line -> {
line.left();
line.addImage(stack.item.region).size(8 * 2);
@ -230,7 +230,7 @@ public class PlacementFragment extends Fragment{
ButtonGroup<ImageButton> group = new ButtonGroup<>();
for(Category cat : Category.values()){
if(Recipe.getByCategory(cat).isEmpty()) continue;
if(getByCategory(cat).isEmpty()) continue;
categories.addImageButton("icon-" + cat.name(), "clear-toggle", 16 * 2, () -> {
currentCategory = cat;
@ -248,6 +248,16 @@ public class PlacementFragment extends Fragment{
});
});
}
Array<Block> getByCategory(Category cat){
returnArray.clear();
for(Block block : content.blocks()){
if(block.buildCategory == cat && block.isVisible()){
returnArray.add(block);
}
}
return returnArray;
}
/** Returns the currently displayed block in the top box. */
Block getSelected(){
@ -268,8 +278,8 @@ public class PlacementFragment extends Fragment{
}
//block currently selected
if(control.input(0).recipe != null){
toDisplay = control.input(0).recipe.result;
if(control.input(0).block != null){
toDisplay = control.input(0).block;
}
//block hovered on in build menu

View File

@ -6,6 +6,7 @@ import io.anuke.arc.Graphics.Cursor.SystemCursor;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.EnumSet;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.function.BooleanProvider;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
@ -20,19 +21,26 @@ import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.effect.Puddle;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.graphics.CacheLayer;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Category;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.consumers.ConsumePower;
import io.anuke.mindustry.world.meta.*;
import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.mindustry.world.meta.BlockGroup;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.StatUnit;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.*;
public class Block extends BaseBlock {
public class Block extends BlockStorage{
/** internal name */
public final String name;
/** display name */
@ -77,8 +85,6 @@ public class Block extends BaseBlock {
public boolean instantTransfer = false;
/** The block group. Unless {@link #canReplace} is overriden, blocks in the same group can replace each other. */
public BlockGroup group = BlockGroup.none;
/** List of block stats. */
public BlockStats stats = new BlockStats(this);
/** List of block flags. Used for AI indexing. */
public EnumSet<BlockFlag> flags;
/** Whether the block can be tapped and selected to configure. */
@ -92,6 +98,16 @@ public class Block extends BaseBlock {
/**Whether the overdrive core has any effect on this block.*/
public boolean canOverdrive = true;
/**Cost of constructing this block.*/
public ItemStack[] buildRequirements = new ItemStack[]{};
/**Category in place menu.*/
public Category buildCategory = Category.distribution;
/**Cost of building this block; do not modify directly!*/
public float buildCost;
/**Whether this block is visible and can currently be built.*/
public BooleanProvider buildVisibility = () -> false;
public boolean alwaysUnlocked = false;
protected Array<Tile> tempTiles = new Array<>();
protected TextureRegion[] icons = new TextureRegion[Icon.values().length];
protected TextureRegion[] generatedIcons;
@ -104,16 +120,6 @@ public class Block extends BaseBlock {
this.solid = false;
}
/**Populates the array with all blocks that produce this content.*/
public static void getByProduction(Array<Block> arr, Content result){
arr.clear();
for(Block block : content.blocks()){
if(block.produces.get() == result){
arr.add(block);
}
}
}
public boolean canBreak(Tile tile){
return true;
}
@ -194,6 +200,14 @@ public class Block extends BaseBlock {
public void drawPlace(int x, int y, int rotation, boolean valid){
}
public void draw(Tile tile){
Draw.rect(region, tile.drawx(), tile.drawy(), rotate ? tile.getRotation() * 90 : 0);
}
public void drawShadow(Tile tile){
draw(tile);
}
/** Called after the block is placed by this client. */
public void playerPlaced(Tile tile){
}
@ -221,6 +235,21 @@ public class Block extends BaseBlock {
}
}
@Override
public String localizedName(){
return formalName;
}
@Override
public TextureRegion getContentIcon(){
return icon(Icon.medium);
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayBlock(table, this);
}
@Override
public ContentType getContentType(){
return ContentType.block;
@ -239,6 +268,11 @@ public class Block extends BaseBlock {
health = size * size * 40;
}
buildCost = 0f;
for(ItemStack stack : buildRequirements){
buildCost += stack.amount * stack.item.cost;
}
setStats();
consumes.checkRequired(this);
@ -319,10 +353,6 @@ public class Block extends BaseBlock {
if(hasItems) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items);
}
public String name(){
return name;
}
public boolean isSolidFor(Tile tile){
return false;
}
@ -458,14 +488,6 @@ public class Block extends BaseBlock {
return new TileEntity();
}
public void draw(Tile tile){
Draw.rect(region, tile.drawx(), tile.drawy(), rotate ? tile.getRotation() * 90 : 0);
}
public void drawShadow(Tile tile){
draw(tile);
}
/** Offset for placing and drawing multiblocks. */
public float offset(){
return ((size + 1) % 2) * tilesize / 2f;
@ -475,19 +497,26 @@ public class Block extends BaseBlock {
return size > 1;
}
public Array<Object> getDebugInfo(Tile tile){
return Array.with(
"block", tile.block().name,
"floor", tile.floor().name,
"x", tile.x,
"y", tile.y,
"entity.name", tile.entity.getClass(),
"entity.x", tile.entity.x,
"entity.y", tile.entity.y,
"entity.id", tile.entity.id,
"entity.items.total", hasItems ? tile.entity.items.total() : null,
"entity.graph", tile.entity.power != null && tile.entity.power.graph != null ? tile.entity.power.graph.getID() : null
);
public boolean isVisible(){
return buildVisibility.get() && !isHidden();
}
@Override
public boolean alwaysUnlocked(){
return alwaysUnlocked;
}
protected void requirements(Category cat, ItemStack[] stacks){
requirements(cat, () -> true, stacks);
}
/**Sets up requirements. Use only this method to set up requirements.*/
protected void requirements(Category cat, BooleanProvider visible, ItemStack[] stacks){
this.buildCategory = cat;
this.buildRequirements = stacks;
this.buildVisibility = visible;
Arrays.sort(buildRequirements, (a, b) -> Integer.compare(a.item.id, b.item.id));
}
public enum Icon{

View File

@ -10,15 +10,16 @@ import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.Puddle;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.world.consumers.ConsumeItem;
import io.anuke.mindustry.world.consumers.ConsumeLiquid;
import io.anuke.mindustry.world.consumers.Consumers;
import io.anuke.mindustry.world.meta.BlockStats;
import io.anuke.mindustry.world.meta.Producers;
public abstract class BaseBlock extends MappableContent{
public abstract class BlockStorage extends UnlockableContent{
public boolean hasItems;
public boolean hasLiquids;
public boolean hasPower;
@ -32,6 +33,7 @@ public abstract class BaseBlock extends MappableContent{
public float liquidCapacity = 10f;
public float liquidFlowFactor = 4.9f;
public BlockStats stats = new BlockStats();
public Consumers consumes = new Consumers();
public Producers produces = new Producers();

View File

@ -11,7 +11,6 @@ import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.EventType.BlockBuildBeginEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import static io.anuke.mindustry.Vars.*;
@ -64,8 +63,8 @@ public class Build{
}
/**Places a BuildBlock at this location.*/
public static void beginPlace(Team team, int x, int y, Recipe recipe, int rotation){
if(!validPlace(team, x, y, recipe.result, rotation)){
public static void beginPlace(Team team, int x, int y, Block result, int rotation){
if(!validPlace(team, x, y, result, rotation)){
return;
}
@ -74,13 +73,12 @@ public class Build{
//just in case
if(tile == null) return;
Block result = recipe.result;
Block previous = tile.block();
Block sub = content.getByName(ContentType.block, "build" + result.size);
tile.setBlock(sub, rotation);
tile.<BuildEntity>entity().setConstruct(previous, recipe);
tile.<BuildEntity>entity().setConstruct(previous, result);
tile.setTeam(team);
if(result.isMultiblock()){
@ -107,9 +105,7 @@ public class Build{
/**Returns whether a tile can be placed at this location by this team.*/
public static boolean validPlace(Team team, int x, int y, Block type, int rotation){
Recipe recipe = Recipe.getByResult(type);
if(recipe == null || (!recipe.visibility.usable())){
if(!type.isVisible() || type.isHidden()){
return false;
}

View File

@ -418,7 +418,7 @@ public class Tile implements Position, TargetTrait{
Block block = block();
Block floor = floor();
return floor.name() + ":" + block.name() + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass())) +
return floor.name + ":" + block.name + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass())) +
(link != 0 ? " link=[" + (Pack.leftByte(link) - 8) + ", " + (Pack.rightByte(link) - 8) + "]" : "");
}
}

View File

@ -23,7 +23,6 @@ import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.modules.ItemModule;
@ -74,19 +73,19 @@ public class BuildBlock extends Block{
@Override
public String getDisplayName(Tile tile){
BuildEntity entity = tile.entity();
return Core.bundle.format("block.constructing", entity.recipe == null ? entity.previous.formalName : entity.recipe.result.formalName);
return Core.bundle.format("block.constructing", entity.block == null ? entity.previous.formalName : entity.block.formalName);
}
@Override
public TextureRegion getDisplayIcon(Tile tile){
BuildEntity entity = tile.entity();
return (entity.recipe == null ? entity.previous : entity.recipe.result).icon(Icon.full);
return (entity.block == null ? entity.previous : entity.block).icon(Icon.full);
}
@Override
public boolean isSolidFor(Tile tile){
BuildEntity entity = tile.entity();
return entity == null || (entity.recipe != null && entity.recipe.result.solid) || entity.previous == null || entity.previous.solid;
return entity == null || (entity.block != null && entity.block.solid) || entity.previous == null || entity.previous.solid;
}
@Override
@ -99,9 +98,9 @@ public class BuildBlock extends Block{
BuildEntity entity = tile.entity();
//if the target is constructible, begin constructing
if(entity.recipe != null){
if(entity.block != null){
player.clearBuilding();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.block));
}
}
@ -119,7 +118,7 @@ public class BuildBlock extends Block{
BuildEntity entity = tile.entity();
//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){
if(entity.block != null && entity.previous == entity.block){
return;
}
@ -135,7 +134,7 @@ public class BuildBlock extends Block{
Shaders.blockbuild.color = Palette.accent;
Block target = entity.recipe == null ? entity.previous : entity.recipe.result;
Block target = entity.block == null ? entity.previous : entity.block;
if(target == null) return;
@ -163,7 +162,7 @@ public class BuildBlock extends Block{
* 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 Block block;
public float progress = 0;
public float buildCost;
@ -178,16 +177,16 @@ public class BuildBlock extends Block{
private float[] totalAccumulator;
public void construct(Unit builder, TileEntity core, float amount){
if(recipe == null){
if(block == null){
damage(99999);
return;
}
float maxProgress = checkRequired(core.items, amount, false);
for(int i = 0; i < recipe.requirements.length; i++){
accumulator[i] += Math.min(recipe.requirements[i].amount * maxProgress, recipe.requirements[i].amount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + recipe.requirements[i].amount * maxProgress, recipe.requirements[i].amount);
for(int i = 0; i < block.buildRequirements.length; i++){
accumulator[i] += Math.min(block.buildRequirements[i].amount * maxProgress, block.buildRequirements[i].amount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + block.buildRequirements[i].amount * maxProgress, block.buildRequirements[i].amount);
}
maxProgress = checkRequired(core.items, maxProgress, true);
@ -199,24 +198,23 @@ public class BuildBlock extends Block{
}
if(progress >= 1f || state.rules.infiniteResources){
Call.onConstructFinish(tile, recipe.result, builderID, tile.getRotation(), builder.getTeam());
Call.onConstructFinish(tile, block, builderID, tile.getRotation(), builder.getTeam());
}
}
public void deconstruct(Unit builder, TileEntity core, float amount){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null){
ItemStack[] requirements = recipe.requirements;
if(block != null){
ItemStack[] requirements = block.buildRequirements;
if(requirements.length != accumulator.length || totalAccumulator.length != requirements.length){
setDeconstruct(previous);
}
for(int i = 0; i < requirements.length; i++){
accumulator[i] += Math.min(requirements[i].amount * amount / 2f, requirements[i].amount/2f - totalAccumulator[i]); //add scaled amount progressed to the accumulator
accumulator[i] += Math.min(requirements[i].amount * amount / 2f, requirements[i].amount / 2f - totalAccumulator[i]); //add scaled amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + requirements[i].amount * amount / 2f, requirements[i].amount);
int accumulated = (int) (accumulator[i]); //get amount
int accumulated = (int)(accumulator[i]); //get amount
if(amount > 0 && accumulated > 0){ //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, accumulated, core.tile, builder);
@ -230,21 +228,21 @@ public class BuildBlock extends Block{
progress = Mathf.clamp(progress - amount);
if(progress <= 0 || state.rules.infiniteResources){
Call.onDeconstructFinish(tile, this.recipe == null ? previous : this.recipe.result);
Call.onDeconstructFinish(tile, this.block == null ? previous : this.block);
}
}
private float checkRequired(ItemModule inventory, float amount, boolean remove){
float maxProgress = amount;
for(int i = 0; i < recipe.requirements.length; i++){
for(int i = 0; i < block.buildRequirements.length; i++){
int required = (int) (accumulator[i]); //calculate items that are required now
if(inventory.get(recipe.requirements[i].item) == 0){
if(inventory.get(block.buildRequirements[i].item) == 0){
maxProgress = 0f;
}else if(required > 0){ //if this amount is positive...
//calculate how many items it can actually use
int maxUse = Math.min(required, inventory.get(recipe.requirements[i].item));
int maxUse = Math.min(required, inventory.get(block.buildRequirements[i].item));
//get this as a fraction
float fraction = maxUse / (float) required;
@ -255,7 +253,7 @@ public class BuildBlock extends Block{
//remove stuff that is actually used
if(remove){
inventory.remove(recipe.requirements[i].item, maxUse);
inventory.remove(block.buildRequirements[i].item, maxUse);
}
}
//else, no items are required yet, so just keep going
@ -268,24 +266,24 @@ public class BuildBlock extends Block{
return progress;
}
public void setConstruct(Block previous, Recipe recipe){
this.recipe = recipe;
public void setConstruct(Block previous, Block block){
this.block = block;
this.previous = previous;
this.accumulator = new float[recipe.requirements.length];
this.totalAccumulator = new float[recipe.requirements.length];
this.buildCost = recipe.cost;
this.accumulator = new float[block.buildRequirements.length];
this.totalAccumulator = new float[block.buildRequirements.length];
this.buildCost = block.buildCost;
}
public void setDeconstruct(Block previous){
this.previous = previous;
this.progress = 1f;
if(Recipe.getByResult(previous) != null){
this.recipe = Recipe.getByResult(previous);
this.accumulator = new float[Recipe.getByResult(previous).requirements.length];
this.totalAccumulator = new float[Recipe.getByResult(previous).requirements.length];
this.buildCost = Recipe.getByResult(previous).cost;
if(previous.buildCost > 1f){
this.block = previous;
this.accumulator = new float[previous.buildRequirements.length];
this.totalAccumulator = new float[previous.buildRequirements.length];
this.buildCost = previous.buildCost;
}else{
this.buildCost = 20f; //default no-recipe build cost is 20
this.buildCost = 20f; //default no-requirement build cost is 20
}
}
@ -293,7 +291,7 @@ public class BuildBlock extends Block{
public void write(DataOutput stream) throws IOException{
stream.writeFloat(progress);
stream.writeShort(previous == null ? -1 : previous.id);
stream.writeShort(recipe == null ? -1 : recipe.result.id);
stream.writeShort(block == null ? -1 : block.id);
if(accumulator == null){
stream.writeByte(-1);
@ -323,10 +321,10 @@ public class BuildBlock extends Block{
}
if(pid != -1) previous = content.block(pid);
if(rid != -1) recipe = Recipe.getByResult(content.block(rid));
if(rid != -1) block = content.block(rid);
if(recipe != null){
buildCost = recipe.cost;
if(block != null){
buildCost = block.buildCost;
}else{
buildCost = 20f;
}

View File

@ -31,7 +31,7 @@ public class LiquidBlock extends Block{
@Override
public TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find(name() + "-bottom"), Core.atlas.find(name() + "-top")};
return new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name + "-top")};
}
@Override

View File

@ -1,7 +1,6 @@
package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.LongArray;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
@ -333,17 +332,6 @@ public class Conveyor extends Block{
entity.lastInserted = (byte)(entity.convey.size-1);
}
@Override
public Array<Object> getDebugInfo(Tile tile){
ConveyorEntity entity = tile.entity();
Array<Object> arr = super.getDebugInfo(tile);
arr.addAll(Array.<Object>with(
"clogHeat", entity.clogHeat,
"sleeping", entity.isSleeping()
));
return arr;
}
@Override
public TileEntity newEntity(){
return new ConveyorEntity();

View File

@ -23,7 +23,7 @@ public class LiquidJunction extends LiquidBlock{
@Override
public void draw(Tile tile){
Draw.rect(name(), tile.worldx(), tile.worldy());
Draw.rect(name, tile.worldx(), tile.worldy());
}
@Override

View File

@ -50,7 +50,7 @@ public class GenericCrafter extends Block{
@Override
public void draw(Tile tile){
Draw.rect(name(), tile.drawx(), tile.drawy());
Draw.rect(name, tile.drawx(), tile.drawy());
if(!hasLiquids) return;

View File

@ -47,7 +47,7 @@ public class Pump extends LiquidBlock{
@Override
public void draw(Tile tile){
Draw.rect(name(), tile.drawx(), tile.drawy());
Draw.rect(name, tile.drawx(), tile.drawy());
Draw.color(tile.entity.liquids.current().color);
Draw.alpha(tile.entity.liquids.total() / liquidCapacity);

View File

@ -103,7 +103,7 @@ public class UnitFactory extends Block{
UnitFactoryEntity entity = tile.entity();
TextureRegion region = type.iconRegion;
Draw.rect(name(), tile.drawx(), tile.drawy());
Draw.rect(name, tile.drawx(), tile.drawy());
Shaders.build.region = region;
Shaders.build.progress = entity.buildTime / produceTime;

View File

@ -7,7 +7,6 @@ import io.anuke.arc.util.Log;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.meta.values.*;
import java.util.Locale;
@ -17,13 +16,8 @@ public class BlockStats{
private static final boolean errorWhenMissing = false;
private final OrderedMap<StatCategory, OrderedMap<BlockStat, StatValue>> map = new OrderedMap<>();
private final Block block;
private boolean dirty;
public BlockStats(Block block){
this.block = block;
}
/**Adds a single float value with this stat, formatted to 2 decimal places.*/
public void add(BlockStat stat, float value, StatUnit unit){
add(stat, new NumberValue(value, unit));
@ -73,7 +67,7 @@ public class BlockStats{
}
if(map.containsKey(stat.category) && map.get(stat.category).containsKey(stat)){
throw new RuntimeException("Duplicate stat entry: \"" + stat + "\" in block '" + block.name + "'");
throw new RuntimeException("Duplicate stat entry: \"" + stat + "\" in block.");
}
if(!map.containsKey(stat.category)){
@ -88,7 +82,7 @@ public class BlockStats{
/**Removes a stat, if it exists.*/
public void remove(BlockStat stat){
if(!map.containsKey(stat.category) || !map.get(stat.category).containsKey(stat)){
throw new RuntimeException("No stat entry found: \"" + stat + "\" in block '" + block.name + "'!");
throw new RuntimeException("No stat entry found: \"" + stat + "\" in block.");
}
map.get(stat.category).remove(stat);

View File

@ -22,7 +22,6 @@ import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;