From bb8d4a41e31baecc6a9ab2f1ed7243922d000bba Mon Sep 17 00:00:00 2001 From: Matt Low Date: Wed, 21 Nov 2018 15:38:32 +0400 Subject: [PATCH] Add hidden (press F12 at main menu) level editor mode Saves a level.json in the external storage --- .../brickbuster/layout/LevelJsonTemplate.java | 23 ++ .../com/me/brickbuster/state/EditorState.java | 229 ++++++++++++++++++ .../com/me/brickbuster/state/MenuState.java | 6 + 3 files changed, 258 insertions(+) create mode 100644 core/src/com/me/brickbuster/layout/LevelJsonTemplate.java create mode 100644 core/src/com/me/brickbuster/state/EditorState.java diff --git a/core/src/com/me/brickbuster/layout/LevelJsonTemplate.java b/core/src/com/me/brickbuster/layout/LevelJsonTemplate.java new file mode 100644 index 0000000..0b12931 --- /dev/null +++ b/core/src/com/me/brickbuster/layout/LevelJsonTemplate.java @@ -0,0 +1,23 @@ +package com.me.brickbuster.layout; + +import com.me.brickbuster.entity.BrickShape; +import com.me.brickbuster.entity.BrickType; +import com.me.brickbuster.entity.powerup.PowerUpType; + +import java.util.List; + +public class LevelJsonTemplate { + + public String backgroundFile; + public String borderFile; + public List bricks; + + public static class BrickJsonTemplate { + public int col; + public int row; + public BrickType type; + public BrickShape.Shape shape; + public PowerUpType powerUp; + } + +} diff --git a/core/src/com/me/brickbuster/state/EditorState.java b/core/src/com/me/brickbuster/state/EditorState.java new file mode 100644 index 0000000..0499c76 --- /dev/null +++ b/core/src/com/me/brickbuster/state/EditorState.java @@ -0,0 +1,229 @@ +package com.me.brickbuster.state; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.InputAdapter; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer; +import com.badlogic.gdx.physics.box2d.World; +import com.badlogic.gdx.utils.Json; +import com.me.brickbuster.BrickBuster; +import com.me.brickbuster.entity.Brick; +import com.me.brickbuster.entity.BrickShape; +import com.me.brickbuster.entity.BrickType; +import com.me.brickbuster.entity.powerup.PowerUpType; +import com.me.brickbuster.layout.LevelJsonTemplate; +import com.me.brickbuster.physics.Box2dContactListener; + +import java.util.ArrayList; + +public class EditorState extends FieldState { + + public static final long BLINK_INTERVAL = 250l; + public static final int COLUMNS = 11; + public static final int ROWS = 33; + + private Brick placeHolder; + private Brick[][] bricks = new Brick[COLUMNS][ROWS]; + + private int col = 0; + private int row = 0; + + private PowerUpType curPowerUpType; + private BrickShape.Shape curShape; + private BrickType curType; + + private Box2DDebugRenderer debugRenderer; + + private long lastBlink = System.currentTimeMillis(); + private boolean blink = false; + + public EditorState(BrickBuster game) { + super(game); + } + + @Override + public void setup() { + world = new World(new Vector2(), false); + world.setContactListener(new Box2dContactListener()); + + curPowerUpType = null; + curShape = BrickShape.Shape.RECTANGLE; + curType = BrickType.STANDARD_10; + + updatePlaceHolder(); + + Gdx.input.setInputProcessor(new InputProcessor()); + + debugRenderer = new Box2DDebugRenderer(); + } + + @Override + public void render() { + for (int col = 0; col < bricks.length; col++) { + for (int row = 0; row < bricks[col].length; row++) { + if (col == this.col && row == this.row && !blink) { + continue; + } + Brick brick = bricks[col][row]; + if (brick != null) { + brick.render(game.sr); + } + } + } + + if (!blink) { + placeHolder.render(game.sr); + } + + debugRenderer.render(world, game.cam.combined.cpy().scl(PlayState.PIXEL_PER_METER)); + + game.sb.begin(); + game.font.setColor(Color.GRAY); + game.font.draw(game.sb, String.format("Current powerup: %s", curPowerUpType), 10, 230); + game.font.draw(game.sb, "WASD: Move brick", 10, 170); + game.font.draw(game.sb, "ENTER: Set brick BACKSPACE: Delete brick F12: Save", 10, 110); + game.font.draw(game.sb, "UP/DOWN: type LEFT/RIGHT: shape SPACE: powerup", 10, 55); + game.sb.end(); + } + + @Override + public void update(float dt) { + long now = System.currentTimeMillis(); + if (now - lastBlink > BLINK_INTERVAL) { + blink = !blink; + lastBlink = now; + } + } + + private void updatePlaceHolder() { + if (placeHolder != null) { + world.destroyBody(placeHolder.getBody()); + } + placeHolder = createBrick(); + } + + private Brick createBrick() { + return new Brick(this, curType, curShape, curPowerUpType, getBrickX(), getBrickY()); + } + + private float getBrickX() { + return PlayState.EDGE_PADDING + Brick.BRICK_WIDTH/2 + col * Brick.BRICK_WIDTH; + } + + private float getBrickY() { + return PlayState.BOARD_HEIGHT - (PlayState.EDGE_PADDING + Brick.BRICK_HEIGHT/2 + row * Brick.BRICK_HEIGHT); + } + + private void saveToFile() { + Json json = new Json(); + FileHandle file = Gdx.files.external("level.json"); + + LevelJsonTemplate level = new LevelJsonTemplate(); + level.bricks = new ArrayList(); + + for (int col = 0; col < bricks.length; col++) { + for (int row = 0; row < bricks[col].length; row++) { + Brick brick = bricks[col][row]; + if (brick == null) { + continue; + } + LevelJsonTemplate.BrickJsonTemplate brickJson = new LevelJsonTemplate.BrickJsonTemplate(); + brickJson.col = col; + brickJson.row = row; + brickJson.type = brick.getType(); + brickJson.shape = brick.getShape(); + brickJson.powerUp = brick.getPowerUpType(); + level.bricks.add(brickJson); + } + } + + file.writeString(json.prettyPrint(level), false); + System.out.println("level.json saved to " + file.file().getAbsolutePath()); + } + + private final class InputProcessor extends InputAdapter { + + @Override + public boolean keyDown(int keycode) { + int next = -1; + switch (keycode) { + case Input.Keys.UP: + next = curType.ordinal() == BrickType.values().length-1? 0 : curType.ordinal()+1; + curType = BrickType.values()[next]; + updatePlaceHolder(); + break; + case Input.Keys.DOWN: + next = curType.ordinal() == 0? BrickType.values().length-1 : curType.ordinal()-1; + curType = BrickType.values()[next]; + updatePlaceHolder(); + break; + case Input.Keys.LEFT: + next = curShape.ordinal() == 0? BrickShape.Shape.values().length-1 : curShape.ordinal()-1; + curShape = BrickShape.Shape.values()[next]; + updatePlaceHolder(); + break; + case Input.Keys.RIGHT: + next = curShape.ordinal() == BrickShape.Shape.values().length-1? 0 : curShape.ordinal()+1; + curShape = BrickShape.Shape.values()[next]; + updatePlaceHolder(); + break; + case Input.Keys.W: + if (row > 0) { + row--; + placeHolder.setY(getBrickY()); + } + break; + case Input.Keys.A: + if (col > 0) { + col--; + placeHolder.setX(getBrickX()); + } + break; + case Input.Keys.S: + if (row < ROWS-1) { + row++; + placeHolder.setY(getBrickY()); + } + break; + case Input.Keys.D: + if (col < COLUMNS-1) { + col++; + placeHolder.setX(getBrickX()); + } + break; + case Input.Keys.SPACE: + next = curPowerUpType == null? 0 : curPowerUpType.ordinal()+1; + if (next >= curPowerUpType.values().length) { + curPowerUpType = null; + } else { + curPowerUpType = PowerUpType.values()[next]; + } + break; + case Input.Keys.ENTER: + if (bricks[col][row] != null) { + world.destroyBody(bricks[col][row].getBody()); + bricks[col][row].dispose(); + } + bricks[col][row] = createBrick(); + break; + case Input.Keys.BACKSPACE: + if (bricks[col][row] != null) { + world.destroyBody(bricks[col][row].getBody()); + bricks[col][row].dispose(); + bricks[col][row] = null; + } + break; + case Input.Keys.F12: + saveToFile(); + break; + default: + return false; + } + return true; + } + } + +} diff --git a/core/src/com/me/brickbuster/state/MenuState.java b/core/src/com/me/brickbuster/state/MenuState.java index 09c6708..ec79ba1 100644 --- a/core/src/com/me/brickbuster/state/MenuState.java +++ b/core/src/com/me/brickbuster/state/MenuState.java @@ -1,6 +1,7 @@ package com.me.brickbuster.state; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.math.Vector3; import com.me.brickbuster.BrickBuster; @@ -39,6 +40,11 @@ public class MenuState extends State { dispose(); } } + + if(Gdx.input.isKeyJustPressed(Input.Keys.F12)) { + game.setScreen(new EditorState(game)); + dispose(); + } } @Override