Implemented block breaking on Android

This commit is contained in:
Anuken 2018-05-31 16:27:30 -04:00
parent 24e7d755eb
commit 13b0c8880d
10 changed files with 569 additions and 411 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -24,7 +24,7 @@ import io.anuke.ucore.util.OS;
import java.util.Locale;
public class Vars{
public static final boolean testMobile = false;
public static final boolean testMobile = true;
//shorthand for whether or not this is running on android or ios
public static boolean mobile;
public static boolean ios;

View File

@ -61,9 +61,11 @@ public class Control extends Module{
Core.atlas = new Atlas("sprites.atlas");
for(Item item : Item.all()){
item.init();
item.load();
}
db.load();
gdxInput = Gdx.input;
proxy = new InputProxy(Gdx.input){
@ -337,8 +339,14 @@ public class Control extends Module{
input.update();
}
if(Timers.get("timerCheckUnlock", 60)){
//check unlocks every 2 seconds
if(Timers.get("timerCheckUnlock", 120)){
checkUnlockableBlocks();
//save if the db changed
if(db.isDirty()){
db.save();
}
}
if(Inputs.keyTap("pause") && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){

View File

@ -30,7 +30,7 @@ public class Units {
Units.getNearby(rect, unit -> {
if(value[0]) return;
if(unit.hitbox.getRect(unit.x, unit.y).overlaps(rect)){
if(!unit.isFlying() && unit.hitbox.getRect(unit.x, unit.y).overlaps(rect)){
value[0] = true;
}
});

View File

@ -9,7 +9,10 @@ import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Settings;
public class ContentDatabase {
/**Maps unlockable type names to a set of unlocked content.*/
private ObjectMap<String, ObjectSet<String>> unlocked = new ObjectMap<>();
/**Whether unlockables have changed since the last save.*/
private boolean dirty;
/**Returns whether or not this piece of content is unlocked yet.*/
public boolean isUnlocked(Content content){
@ -36,14 +39,24 @@ public class ContentDatabase {
//fire unlock event so other classes can use it
if(ret){
Events.fire(UnlockEvent.class, content);
dirty = true;
}
return ret;
}
//saving/loading currently disabled for testing.
/**Returns whether unlockables have changed since the last save.*/
public boolean isDirty(){
return dirty;
}
private void load(){
/**Clears all unlocked content.*/
public void reset(){
unlocked.clear();
dirty = true;
}
public void load(){
ObjectMap<String, Array<String>> result = Settings.getJson("content-database", ObjectMap.class);
for(Entry<String, Array<String>> entry : result.entries()){
@ -51,9 +64,12 @@ public class ContentDatabase {
set.addAll(entry.value);
unlocked.put(entry.key, set);
}
dirty = false;
}
private void save(){
public void save(){
ObjectMap<String, Array<String>> write = new ObjectMap<>();
for(Entry<String, ObjectSet<String>> entry : unlocked.entries()){
@ -61,6 +77,8 @@ public class ContentDatabase {
}
Settings.putJson("content-database", write);
Settings.save();
dirty = false;
}
}

View File

@ -9,6 +9,7 @@ import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
@ -32,7 +33,7 @@ import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.input.PlaceMode.none;
import static io.anuke.mindustry.input.PlaceMode.*;
public class AndroidInput extends InputHandler implements GestureListener{
private static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
@ -55,7 +56,7 @@ public class AndroidInput extends InputHandler implements GestureListener{
private float lineScale;
/**List of currently selected tiles to place.*/
private Array<PlaceRequest> placement = new Array<>();
private Array<PlaceRequest> selection = new Array<>();
/**Place requests to be removed.*/
private Array<PlaceRequest> removals = new Array<>();
/**Whether or not the player is currently shifting all placed tiles.*/
@ -64,6 +65,8 @@ public class AndroidInput extends InputHandler implements GestureListener{
private boolean lineMode;
/**Current place mode.*/
private PlaceMode mode = none;
/**Whether no recipe was available when switching to break mode.*/
private Recipe lastRecipe;
public AndroidInput(Player player){
super(player);
@ -75,15 +78,15 @@ public class AndroidInput extends InputHandler implements GestureListener{
return getRequest(tile) != null;
}
/**Returns whether this block overlaps any placement requests.*/
/**Returns whether this block overlaps any selection requests.*/
boolean checkOverlapPlacement(int x, int y, Block block){
r2.setSize(block.size * tilesize);
r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset());
for(PlaceRequest req : placement){
for(PlaceRequest req : selection){
Tile other = req.tile();
if(other == null) continue;
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());
@ -95,44 +98,61 @@ public class AndroidInput extends InputHandler implements GestureListener{
return false;
}
/**Returns the placement request that overlaps this tile, or null.*/
/**Returns the selection request that overlaps this tile, or null.*/
PlaceRequest getRequest(Tile tile){
r2.setSize(tilesize);
r2.setCenter(tile.worldx(), tile.worldy());
for(PlaceRequest req : placement){
for(PlaceRequest req : selection){
Tile other = req.tile();
if(other == null) continue;
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
if(!req.remove){
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
if(r2.overlaps(r1)){
return req;
if (r2.overlaps(r1)) {
return req;
}
}else {
r1.setSize(other.block().size * tilesize);
r1.setCenter(other.worldx() + other.block().offset(), other.worldy() + other.block().offset());
if (r2.overlaps(r1)) {
return req;
}
}
}
return null;
}
void removeRequest(PlaceRequest request){
placement.removeValue(request, true);
selection.removeValue(request, true);
removals.add(request);
}
void drawRequest(PlaceRequest request){
Tile tile = request.tile();
float offset = request.recipe.result.offset();
TextureRegion[] regions = request.recipe.result.getBlockIcon();
if(!request.remove) {
//draw placing request
float offset = request.recipe.result.offset();
TextureRegion[] regions = request.recipe.result.getBlockIcon();
Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f));
Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness);
Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f));
Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness);
for(TextureRegion region : regions){
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getRegionWidth() * request.scale, region.getRegionHeight() * request.scale,
request.recipe.result.rotate ? request.rotation * 90 : 0);
for (TextureRegion region : regions) {
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getRegionWidth() * request.scale, region.getRegionHeight() * request.scale,
request.recipe.result.rotate ? request.rotation * 90 : 0);
}
}else{
Draw.color(Palette.remove);
//draw removing request
Lines.poly(tile.drawx(), tile.drawy(), 4, tile.block().size * tilesize/2f * request.scale, 45 + 15);
}
}
@ -143,29 +163,47 @@ public class AndroidInput extends InputHandler implements GestureListener{
new table(){{
abottom().aleft();
new table("pane"){{
margin(5);
defaults().size(60f);
//Add a break button.
new imagebutton("icon-break", "toggle", 16 * 2f, () -> {
mode = mode == breaking ? recipe == null ? none : placing : breaking;
lastRecipe = recipe;
}).update(l -> l.setChecked(mode == breaking));
}}.end();
new table("pane"){{
margin(5);
defaults().size(60f);
//Add a cancel button, which clears the selection.
new imagebutton("icon-cancel", 16 * 2f, () -> recipe = null);
new imagebutton("icon-cancel", 16 * 2f, () -> selection.clear())
.cell.disabled(i -> selection.size == 0);
//Add an accept button, which places everything.
new imagebutton("icon-check", 16 * 2f, () -> {
for (PlaceRequest request : placement) {
for (PlaceRequest request : selection) {
Tile tile = request.tile();
//actually place/break all selected blocks
if (tile != null) {
rotation = request.rotation;
recipe = request.recipe;
tryPlaceBlock(tile.x, tile.y);
if(!request.remove) {
rotation = request.rotation;
recipe = request.recipe;
tryPlaceBlock(tile.x, tile.y);
}else{
tryBreakBlock(tile.x, tile.y);
}
}
}
removals.addAll(placement);
placement.clear();
//move all current requests to removal array to they fade out
removals.addAll(selection);
selection.clear();
selecting = false;
}).cell.disabled(i -> placement.size == 0);
}).cell.disabled(i -> selection.size == 0);
//Add a rotate button
new imagebutton("icon-arrow", 16 * 2f, () -> rotation = Mathf.mod(rotation + 1, 4))
@ -173,8 +211,8 @@ public class AndroidInput extends InputHandler implements GestureListener{
i.getImage().setRotation(rotation * 90);
i.getImage().setOrigin(Align.center);
}).cell.disabled(i -> recipe == null || !recipe.result.rotate);
}}.end();
}}.visible(this::isPlacing).end();
}}.visible(() -> mode != none).end();
}}.visible(() -> !state.is(State.menu)).end();
}
@Override
@ -196,19 +234,21 @@ public class AndroidInput extends InputHandler implements GestureListener{
}
//draw normals
for(PlaceRequest request : placement){
for(PlaceRequest request : selection){
Tile tile = request.tile();
if(tile == null) continue;
if(validPlace(tile.x, tile.y, request.recipe.result, request.rotation)){
if ((!request.remove && validPlace(tile.x, tile.y, request.recipe.result, 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);
}else{
} else {
request.scale = Mathf.lerpDelta(request.scale, 0.5f, 0.1f);
request.redness = Mathf.lerpDelta(request.redness, 1f, 0.2f);
}
drawRequest(request);
}
@ -221,30 +261,57 @@ public class AndroidInput extends InputHandler implements GestureListener{
Tile tile = tileAt(control.gdxInput().getX(), control.gdxInput().getY());
if(tile != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
//draw placing
if(mode == placing) {
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale);
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, maxLength);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
//go through each cell and draw the block to place if valid
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, maxLength);
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
Draw.color();
//go through each cell and draw the block to place if valid
for (int i = 0; i <= result.getLength(); i += recipe.result.size) {
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
TextureRegion[] regions = recipe.result.getBlockIcon();
if (!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)) {
Draw.color();
for(TextureRegion region : regions){
Draw.rect(region, x *tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(),
region.getRegionWidth() * lineScale, region.getRegionHeight() * lineScale, recipe.result.rotate ? result.rotation * 90 : 0);
TextureRegion[] regions = recipe.result.getBlockIcon();
for (TextureRegion region : regions) {
Draw.rect(region, x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(),
region.getRegionWidth() * lineScale, region.getRegionHeight() * lineScale, recipe.result.rotate ? result.rotation * 90 : 0);
}
} else {
Draw.color(Palette.breakInvalid);
Lines.square(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), recipe.result.size * tilesize / 2f);
}
}else{
Draw.color(Palette.breakInvalid);
Lines.square(x*tilesize + recipe.result.offset(), y*tilesize + recipe.result.offset(), recipe.result.size * tilesize/2f);
}
}else if(mode == breaking){
//draw breaking
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, lineStartX, lineStartY, tile.x, tile.y, false, maxLength, 1f);
NormalizeResult dresult = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, false, maxLength);
Draw.color(Palette.remove);
Draw.alpha(0.6f);
Draw.alpha(1f);
for(int x = dresult.x; x <= dresult.x2; x ++){
for(int y = dresult.y; y <= dresult.y2; y ++){
Tile other = world.tile(x, y);
if(other == null || !validBreak(other.x, other.y)) continue;
other = other.target();
Lines.poly(other.drawx(), other.drawy(), 4, other.block().size * tilesize/2f, 45 + 15);
}
}
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
}
}
}
@ -263,10 +330,10 @@ public class AndroidInput extends InputHandler implements GestureListener{
if(cursor == null || ui.hasMouse(screenX, screenY)) return false;
//only begin selecting if the tapped block is a request
selecting = hasRequest(cursor) && isPlacing();
selecting = hasRequest(cursor) && isPlacing() && mode == placing;
//call tap events
if(pointer == 0 && !selecting){
if(pointer == 0 && !selecting && mode == none){
tileTapped(cursor.target());
}
@ -280,22 +347,49 @@ public class AndroidInput extends InputHandler implements GestureListener{
if(lineMode) {
Tile tile = tileAt(screenX, screenY);
if(tile == null) return false;
if (tile == null) return false;
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100);
if(mode == placing) {
rotation = result.rotation;
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100);
//place blocks on line
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
rotation = result.rotation;
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
PlaceRequest request = new PlaceRequest(x * tilesize, y * tilesize, recipe, result.rotation);
request.scale = 1f;
placement.add(request);
//place blocks on line
for (int i = 0; i <= result.getLength(); i += recipe.result.size) {
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
if (!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)) {
PlaceRequest request = new PlaceRequest(x * tilesize, y * tilesize, recipe, result.rotation);
request.scale = 1f;
selection.add(request);
}
}
}else if(mode == breaking){
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, false, maxLength);
//break everything in area
for(int x = 0; x <= Math.abs(result.x2 - result.x); x ++ ){
for(int y = 0; y <= Math.abs(result.y2 - result.y); y ++){
int wx = lineStartX + x * Mathf.sign(tile.x - lineStartX);
int wy = lineStartY + y * Mathf.sign(tile.y - lineStartY);
Tile tar = world.tile(wx, wy);
if(tar == null) continue;
tar = tar.target();
if (!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)) {
PlaceRequest request = new PlaceRequest(tar.worldx(), tar.worldy());
request.scale = 1f;
selection.add(request);
}
}
}
}
@ -306,7 +400,7 @@ public class AndroidInput extends InputHandler implements GestureListener{
@Override
public boolean longPress(float x, float y) {
if(state.is(State.menu) || !isPlacing()) return false;
if(state.is(State.menu) || mode == none) return false;
//get tile on cursor
Tile cursor = tileAt(x, y);
@ -320,7 +414,11 @@ public class AndroidInput extends InputHandler implements GestureListener{
lineStartY = cursor.y;
lineMode = true;
Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size);
if(mode == breaking){
Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f);
}else{
Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size);
}
return false;
}
@ -338,9 +436,12 @@ public class AndroidInput extends InputHandler implements GestureListener{
//remove if request present
if(hasRequest(cursor)) {
removeRequest(getRequest(cursor));
}else if(isPlacing() && validPlace(cursor.x, cursor.y, recipe.result, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
//add to placement queue if it's a valid place position
placement.add(new PlaceRequest(cursor.worldx(), cursor.worldy(), recipe, rotation));
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, recipe.result, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
//add to selection queue if it's a valid place position
selection.add(new PlaceRequest(cursor.worldx(), cursor.worldy(), recipe, rotation));
}else if(mode == breaking && validBreak(cursor.x, cursor.y) && !hasRequest(cursor)){
//add to selection queue if it's a valid BREAK position
selection.add(new PlaceRequest(cursor.worldx(), cursor.worldy()));
}
return false;
@ -350,11 +451,22 @@ public class AndroidInput extends InputHandler implements GestureListener{
public void update(){
//reset state when not placing
if(!isPlacing()){
if(mode == none){
selecting = false;
lineMode = false;
removals.addAll(placement);
placement.clear();
removals.addAll(selection);
selection.clear();
}
//if there is no mode and there's a recipe, switch to placing
if(recipe != null && mode == none){
mode = placing;
}
//automatically switch to placing after a new recipe is selected
if(lastRecipe != recipe && mode == breaking && recipe != null){
mode = placing;
lastRecipe = recipe;
}
if(lineMode){
@ -413,12 +525,14 @@ public class AndroidInput extends InputHandler implements GestureListener{
float dx = deltaX * Core.camera.zoom / Core.cameraScale, dy = deltaY * Core.camera.zoom / Core.cameraScale;
if(selecting){
for(PlaceRequest req : placement){
if(selecting){ //pan all requests
for(PlaceRequest req : selection){
if(req.remove) continue; //don't shift removal requests
req.x += dx;
req.y -= dy;
}
}else{
//pan player
player.x -= dx;
player.y += dy;
player.targetAngle = Mathf.atan2(dx, -dy) + 180f;
@ -481,6 +595,7 @@ public class AndroidInput extends InputHandler implements GestureListener{
float x, y;
Recipe recipe;
int rotation;
boolean remove;
//animation variables
float scale;
@ -491,10 +606,17 @@ public class AndroidInput extends InputHandler implements GestureListener{
this.y = y;
this.recipe = recipe;
this.rotation = rotation;
this.remove = false;
}
PlaceRequest(float x, float y) {
this.x = x;
this.y = y;
this.remove = true;
}
Tile tile(){
return world.tileWorld(x - recipe.result.offset(), y - recipe.result.offset());
return world.tileWorld(x - (recipe == null ? 0 : recipe.result.offset()), y - (recipe == null ? 0 : recipe.result.offset()));
}
}
}

View File

@ -42,7 +42,7 @@ public class Item implements Comparable<Item>, Content{
items.add(this);
}
public void init(){
public void load(){
this.region = Draw.region("item-" + name);
}

View File

@ -99,7 +99,10 @@ public class BlocksFragment implements Fragment{
visible(() -> !state.is(State.menu) && shown);
}}.end().get();
}}.end();
rebuild();
}
/**Rebuilds the whole placement menu, attempting to preserve previous state.*/