/*******************************************************************************
* Copyright 2015 Maximilian Stark | Dakror <mail@dakror.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.dakror.vloxlands.game;
import java.util.Random;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalShadowLight;
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.graphics.g3d.utils.DepthShaderProvider;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.collision.Ray;
import com.badlogic.gdx.utils.Array;
import de.dakror.vloxlands.Config;
import de.dakror.vloxlands.Vloxlands;
import de.dakror.vloxlands.ai.path.BFS;
import de.dakror.vloxlands.ai.path.node.BFSNode;
import de.dakror.vloxlands.game.entity.Entity;
import de.dakror.vloxlands.game.entity.creature.Creature;
import de.dakror.vloxlands.game.entity.creature.Human;
import de.dakror.vloxlands.game.entity.statics.StaticEntity;
import de.dakror.vloxlands.game.entity.structure.Structure;
import de.dakror.vloxlands.game.entity.structure.Towncenter;
import de.dakror.vloxlands.game.item.Item;
import de.dakror.vloxlands.game.item.ItemStack;
import de.dakror.vloxlands.game.query.VoxelPos;
import de.dakror.vloxlands.game.voxel.Voxel;
import de.dakror.vloxlands.game.world.Chunk;
import de.dakror.vloxlands.game.world.Island;
import de.dakror.vloxlands.game.world.World;
import de.dakror.vloxlands.layer.Layer;
import de.dakror.vloxlands.render.DDirectionalShadowLight;
import de.dakror.vloxlands.render.MeshingThread;
import de.dakror.vloxlands.util.Direction;
import de.dakror.vloxlands.util.event.SelectionListener;
import de.dakror.vloxlands.util.event.VoxelSelection;
import de.dakror.vloxlands.util.math.CustomizableFrustum;
/**
* @author Dakror
*/
@SuppressWarnings("deprecation")
public class Game extends Layer {
public static long seed = (long) (Math.random() * Long.MAX_VALUE);
public static final float velocity = 10;
public static final float rotateSpeed = 0.2f;
public static float pickRayMaxDistance = 150f;
public static final int dayInTicks = 72020; // 1 ingame day = 72020 ticks = 1200s = 20min
public static Game instance;
public static World world;
public static Camera camera;
public static float time = 0.99999999999f;
public Environment env;
public Array<SelectionListener> listeners = new Array<SelectionListener>();
public Environment minimapEnv;
public Camera minimapCamera;
public ModelBatch minimapBatch;
public StaticEntity cursorEntity;
boolean cursorEntityPlacable;
boolean cursorEntityContinousPlacing;
Array<Material> defaultCursorEntityMaterials;
public String activeAction = "";
public Island activeIsland;
public DirectionalShadowLight shadowLight;
DirectionalLight directionalLight;
public CameraInputController controller;
ModelBatch modelBatch;
ModelBatch shadowBatch;
boolean middleDown;
boolean doneLoading;
ModelInstance sky;
int tick;
int ticksForTravel;
int startTick;
public boolean regionSelectionMode = false;
boolean regionSelectionLMB;
public Vector3 hoveredVoxel = new Vector3();
public Vector3 selectedVoxel = new Vector3();
public Vector3 selectionStartVoxel = new Vector3(-1, 0, 0);
Vector3 controllerTarget = new Vector3();
Vector3 cameraPos = new Vector3();
Vector3 target = new Vector3();
Vector3 targetDirection = new Vector3();
Vector3 targetUp = new Vector3();
Vector2 mouseDown = new Vector2();
// -- temp -- //
public final Vector3 tmp = new Vector3();
public final Vector3 tmp1 = new Vector3();
public final Vector3 tmp2 = new Vector3();
public final Vector3 tmp3 = new Vector3();
public final Vector3 tmp4 = new Vector3();
public final Vector3 tmp5 = new Vector3();
public final Vector3 tmp6 = new Vector3();
public final Vector3 tmp7 = new Vector3();
public final Vector3 tmp8 = new Vector3();
public final Matrix4 m4 = new Matrix4();
public final BoundingBox bb = new BoundingBox();
public final BoundingBox bb2 = new BoundingBox();
public final BoundingBox bb3 = new BoundingBox();
@Override
public void show() {
modal = true;
instance = this;
Gdx.app.log("GameLayer.show", "Seed: " + seed + "");
MathUtils.random = new Random(seed);
modelBatch = new ModelBatch(Gdx.files.internal("shader/shader.vs"), Gdx.files.internal("shader/shader.fs"));
minimapBatch = new ModelBatch(Gdx.files.internal("shader/shader.vs"), Gdx.files.internal("shader/shader.fs"));
camera = new PerspectiveCamera(Config.fov, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.near = 0.1f;
camera.far = pickRayMaxDistance;
controller = new CameraInputController(camera) {
private final Vector3 tmpV1 = new Vector3();
private final Vector3 tmpV2 = new Vector3();
@Override
protected boolean process(float deltaX, float deltaY, int button) {
if (button == rotateButton && Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) return false;
if (button == rotateButton) {
tmpV1.set(camera.direction).crs(camera.up).y = 0f;
camera.rotateAround(target, tmpV1.nor(), deltaY * rotateAngle);
float dot = camera.direction.dot(Vector3.Y);
if (dot < -0.95f) camera.rotateAround(target, tmpV1.nor(), -deltaY * rotateAngle);
camera.rotateAround(target, Vector3.Y, deltaX * -rotateAngle);
} else if (button == translateButton) {
camera.translate(tmpV1.set(camera.direction).crs(camera.up).nor().scl(-deltaX * translateUnits));
camera.translate(tmpV2.set(camera.up).scl(-deltaY * translateUnits));
if (translateTarget) target.add(tmpV1).add(tmpV2);
} else if (button == forwardButton) {
camera.translate(tmpV1.set(camera.direction).scl(deltaY * translateUnits));
if (forwardTarget) target.add(tmpV1);
}
if (autoUpdate) camera.update();
return true;
}
@Override
public boolean zoom(float amount) {
if (!alwaysScroll && activateKey != 0 && !activatePressed) return false;
tmpV1.set(camera.direction).scl(amount);
tmpV2.set(camera.position).add(tmpV1);
if (tmpV2.dst(target) > 5) {
camera.translate(tmpV1);
if (scrollTarget) target.add(tmpV1);
if (autoUpdate) camera.update();
return true;
}
return false;
}
};
controller.translateUnits = 20;
controller.rotateLeftKey = -1;
controller.rotateRightKey = -1;
controller.forwardKey = -1;
controller.backwardKey = -1;
controller.translateButton = -1;
controller.rotateButton = Buttons.MIDDLE;
Vloxlands.instance.getMultiplexer().addProcessor(controller);
minimapCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
minimapCamera.near = 0.1f;
minimapCamera.far = pickRayMaxDistance;
minimapEnv = new Environment();
minimapEnv.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.f));
minimapEnv.add(new DirectionalLight().set(1f, 1f, 1f, -0.5f, -0.5f, -0.5f));
minimapEnv.add(new DirectionalLight().set(0.5f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f));
shadowBatch = new ModelBatch(new DepthShaderProvider());
Vloxlands.shapeRenderer = new ShapeRenderer();
new MeshingThread();
env = new Environment();
env.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1.f), new ColorAttribute(ColorAttribute.Fog, 0.5f, 0.8f, 0.85f, 1.f));
env.add(directionalLight = new DirectionalLight().set(0.8f, 0.8f, 0.8f, -0.5f, -0.5f, -0.5f));
env.add((shadowLight = new DDirectionalShadowLight(Config.shadowQuality, 128, 128, camera.near, camera.far)).set(0.6f, 0.6f, 0.6f, 0, -0.5f, time));
env.shadowMap = shadowLight;
// int w = MathUtils.random(1, 5);
// int d = MathUtils.random(1, 5);
world = new World(1, 1);
// world = new World(w, d);
// Gdx.app.log("GameLayer.show", "World size: " + w + "x" + d);
}
public void doneLoading() {
for (Item item : Item.getAll())
item.onLoaded();
focusIsland(world.getIslands()[0], true);
Human human = new Human(Island.SIZE / 2 - 5, Island.SIZE / 4 * 3, Island.SIZE / 2);
activeIsland.addEntity(human, false, false);
human = new Human(Island.SIZE / 2 - 4, Island.SIZE / 4 * 3, Island.SIZE / 2);
activeIsland.addEntity(human, false, false);
Towncenter tc = new Towncenter(Island.SIZE / 2 - 2, Island.SIZE / 4 * 3, Island.SIZE / 2 - 2);
activeIsland.addEntity(tc, false, true);
tc.setBuilt(true);
tc.getInnerInventory().add(new ItemStack(Item.get("AXE"), 5));
tc.getInnerInventory().add(new ItemStack(Item.get("PICKAXE"), 5));
tc.getInnerInventory().add(new ItemStack(Item.get("SHOVEL"), 5));
tc.getInnerInventory().add(new ItemStack(Item.get("HAMMER"), 5));
tc.getInnerInventory().add(new ItemStack(Item.get("HOE"), 5));
tc.getInnerInventory().add(new ItemStack(Item.get("WOODEN_LOG"), 40));
tc.getInnerInventory().add(new ItemStack(Item.get("IRON_INGOT"), 5));
doneLoading = true;
}
public void focusIsland(Island island, boolean initial) {
Vector3 islandCenter = new Vector3(island.pos.x + Island.SIZE / 2, island.pos.y + Island.SIZE / 4 * 3, island.pos.z + Island.SIZE / 2);
activeIsland = island;
selectedVoxel.set(-1, 0, 0);
if (!initial) {
target.set(islandCenter).add(-Island.SIZE / 3, Island.SIZE / 3, -Island.SIZE / 3);
if (target.equals(camera.position)) {
camera.position.set(islandCenter).add(-Island.SIZE / 3, Island.SIZE / 3, -Island.SIZE / 3);
controller.target.set(islandCenter);
camera.lookAt(islandCenter);
controller.update();
camera.update();
return;
}
ticksForTravel = (int) camera.position.dst(target) * Config.getGameSpeed();
Vector3 pos = camera.position.cpy();
Vector3 dir = camera.direction.cpy();
Vector3 up = camera.up.cpy();
camera.position.set(islandCenter).add(-Island.SIZE / 3, Island.SIZE / 3, -Island.SIZE / 3);
controller.target.set(islandCenter);
camera.lookAt(islandCenter);
targetDirection.set(camera.direction);
targetUp.set(camera.up);
camera.position.set(pos);
camera.direction.set(dir);
camera.up.set(up);
startTick = tick;
} else {
camera.position.set(islandCenter).add(-Island.SIZE / 3, Island.SIZE / 3, -Island.SIZE / 3);
controller.target.set(islandCenter);
camera.lookAt(islandCenter);
controller.update();
camera.update();
}
}
@Override
public void render(float delta) {
if (!doneLoading) return;
controller.update();
((PerspectiveCamera) camera).fieldOfView = Config.fov;
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
shadowLight.begin(controller.target, camera.direction);
shadowBatch.begin(shadowLight.getCamera());
world.render(shadowBatch, null);
shadowBatch.end();
shadowLight.end();
Gdx.gl.glClearColor(0.5f, 0.8f, 0.85f, 1);
modelBatch.begin(camera);
world.render(modelBatch, env);
if (!Config.paused) world.update(delta);
// modelBatch.render(sky, env);
if (cursorEntity != null) {
cursorEntity.update(delta);
cursorEntity.render(modelBatch, env, false);
}
modelBatch.end();
if (selectionStartVoxel.x > -1 && selectedVoxel.x > -1) {
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glEnable(GL20.GL_BLEND);
float minX = Math.min(selectionStartVoxel.x, selectedVoxel.x);
float maxX = Math.max(selectionStartVoxel.x, selectedVoxel.x);
float minY = Math.min(selectionStartVoxel.y, selectedVoxel.y);
float maxY = Math.max(selectionStartVoxel.y, selectedVoxel.y);
float minZ = Math.min(selectionStartVoxel.z, selectedVoxel.z);
float maxZ = Math.max(selectionStartVoxel.z, selectedVoxel.z);
Vloxlands.shapeRenderer.begin(ShapeType.Filled);
Vloxlands.shapeRenderer.setProjectionMatrix(camera.combined);
Vloxlands.shapeRenderer.identity();
Vloxlands.shapeRenderer.translate(activeIsland.pos.x + minX, activeIsland.pos.y + minY, activeIsland.pos.z + maxZ + 1.01f);
Vloxlands.shapeRenderer.setColor(0, 1, 0, 0.3f);
Vloxlands.shapeRenderer.box(-0.005f, -0.005f, -0.005f, (maxX - minX) + 1.01f, (maxY - minY) + 1.01f, (maxZ - minZ) + 1.01f);
Vloxlands.shapeRenderer.end();
}
if (Vloxlands.showPathDebug) {
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glEnable(GL20.GL_BLEND);
Vloxlands.shapeRenderer.begin(ShapeType.Filled);
Vloxlands.shapeRenderer.setProjectionMatrix(camera.combined);
for (BFSNode node : BFS.visited) {
Vloxlands.shapeRenderer.identity();
Vloxlands.shapeRenderer.translate(activeIsland.pos.x + node.x, activeIsland.pos.y + node.y, activeIsland.pos.z + node.z + 1.01f);
Vloxlands.shapeRenderer.setColor(1, 1, 1, 0.3f);
Vloxlands.shapeRenderer.box(-0.005f, -0.005f, -0.005f, 1.01f, 1.01f, 1.01f);
}
Vloxlands.shapeRenderer.end();
}
}
@Override
public void tick(int tick) {
this.tick = tick;
if (!Config.paused) {
time -= 0.00002777f;
if (time <= -0.99999999999f) time = 0.99999999999f;
float t = time * MathUtils.PI;
float x = MathUtils.sin(t) * 0.5f;
float z = MathUtils.cos(t);
float light = MathUtils.cos(t - MathUtils.PI / 2) * 0.5f + 0.3f;
shadowLight.set(light - 0.1f, light, light, x, -0.5f, z);
directionalLight.set(light, light, light, x, -0.5f, z);
world.tick(tick);
}
if (cursorEntity != null) cursorEntity.tick(tick);
if (activeIsland != null && startTick > 0) {
camera.position.interpolate(target, (tick - startTick) / (float) (ticksForTravel * Config.getGameSpeed()), Interpolation.linear);
camera.direction.interpolate(targetDirection, (tick - startTick) / (float) (ticksForTravel * Config.getGameSpeed()), Interpolation.linear);
camera.up.interpolate(new Vector3(0, 1, 0), (tick - startTick) / (float) (ticksForTravel * Config.getGameSpeed()), Interpolation.linear);
if (tick >= startTick + ticksForTravel || camera.position.dst(target) < 0.1f) {
Vector3 islandCenter = new Vector3(activeIsland.pos.x + Island.SIZE / 2, activeIsland.pos.y + Island.SIZE / 4 * 3, activeIsland.pos.z + Island.SIZE / 2);
controller.target.set(islandCenter);
camera.position.set(islandCenter).add(-Island.SIZE / 3, Island.SIZE / 3, -Island.SIZE / 3);
camera.lookAt(islandCenter);
startTick = 0;
}
controller.update();
camera.update();
}
}
@Override
public void resize(int width, int height) {
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update();
minimapCamera.viewportWidth = width;
minimapCamera.viewportHeight = height;
minimapCamera.update();
}
public void pickRay(boolean hover, boolean lmb, int x, int y) {
Ray ray = camera.getPickRay(x, y);
if (hover) {
Entity hovered = null;
float distance = 0;
for (Entity e : activeIsland.getEntities()) {
e.hovered = false;
if (!e.isVisible()) continue;
if (!e.inFrustum) continue;
e.getWorldBoundingBox(bb);
if (Intersector.intersectRayBounds(ray, bb, tmp)) {
float dst = ray.origin.dst(tmp);
if (hovered == null || dst < distance) {
hovered = e;
distance = dst;
}
}
}
if (hovered != null) hovered.hovered = true;
} else {
Entity selectedEntity = null;
Chunk selectedChunk = null;
Vector3 selVoxel = new Vector3();
float distance = 0;
for (Entity e : activeIsland.getEntities()) {
e.wasSelected = e.selected;
if (lmb) e.selected = false;
float dst = ray.origin.dst(e.posCache);
if (e.isVisible() && e.inFrustum && e.hovered && (distance == 0 || dst < distance) && dst < pickRayMaxDistance) {
distance = dst;
selectedEntity = e;
}
}
for (Chunk c : activeIsland.getChunks()) {
if (c == null) continue;
if (c.inFrustum && !c.isEmpty()) {
tmp1.set(activeIsland.pos.x + c.pos.x, activeIsland.pos.y + c.pos.y, activeIsland.pos.z + c.pos.z);
tmp2.set(tmp1.cpy().add(Chunk.SIZE, Chunk.SIZE, Chunk.SIZE));
bb.set(tmp1, tmp2);
if (Intersector.intersectRayBounds(ray, bb, null) && c.pickVoxel(ray, tmp5, tmp6)) {
float dst = ray.origin.dst(tmp5);
if ((distance == 0 || dst < distance) && dst <= pickRayMaxDistance) {
distance = dst;
selVoxel.set(tmp6);
selectedChunk = c;
}
}
}
}
if (selectedChunk != null) {
// -- determine selectedVoxelFace -- //
Direction dir = null;
float distanc = 0;
Vector3 is2 = new Vector3();
byte air = Voxel.get("AIR").getId();
for (Direction d : Direction.values()) {
tmp7.set(activeIsland.pos.x + selectedChunk.pos.x + selVoxel.x + d.dir.x, activeIsland.pos.y + selectedChunk.pos.y + selVoxel.y + d.dir.y, activeIsland.pos.z + selectedChunk.pos.z + selVoxel.z + d.dir.z);
tmp8.set(tmp7.cpy().add(1, 1, 1));
bb3.set(tmp7, tmp8);
if (activeIsland.get(selectedChunk.pos.x + selVoxel.x + d.dir.x, selectedChunk.pos.y + selVoxel.y + d.dir.y, selectedChunk.pos.z + selVoxel.z + d.dir.z) != air) continue;
if (Intersector.intersectRayBounds(ray, bb3, is2)) {
float dist = ray.origin.dst(is2);
if (dir == null || dist < distanc) {
distanc = dist;
dir = d;
}
}
}
selectedVoxel.set(selVoxel).add(selectedChunk.pos);
for (SelectionListener sl : listeners)
sl.onVoxelSelection(new VoxelSelection(activeIsland, new VoxelPos(selVoxel.cpy().add(selectedChunk.pos), selectedChunk.get((int) selVoxel.x, (int) selVoxel.y, (int) selVoxel.z)), dir), lmb);
} else if (selectedEntity != null) {
selVoxel.set(-1, 0, 0);
selectedEntity.selected = true;
if (selectedEntity instanceof Structure) {
for (SelectionListener sl : listeners)
sl.onStructureSelection((Structure) selectedEntity, lmb);
} else if (selectedEntity instanceof Creature) {
for (SelectionListener sl : listeners)
sl.onCreatureSelection((Creature) selectedEntity, lmb);
}
} else {
for (SelectionListener sl : listeners)
sl.onNoSelection(lmb);
}
}
}
public Chunk pickVoxelRay(Island island, Vector3 selVoxel, boolean lmb, int x, int y) {
Chunk selectedChunk = null;
Ray ray = camera.getPickRay(x, y);
float distance = 0;
for (Chunk c : island.getChunks()) {
if (c == null) continue;
if (c.inFrustum && !c.isEmpty()) {
tmp1.set(island.pos.x + c.pos.x, island.pos.y + c.pos.y, island.pos.z + c.pos.z);
tmp2.set(tmp1.cpy().add(Chunk.SIZE, Chunk.SIZE, Chunk.SIZE));
bb.set(tmp1, tmp2);
if (Intersector.intersectRayBounds(ray, bb, null) && c.pickVoxel(ray, tmp5, tmp6)) {
float dst = ray.origin.dst(tmp5);
if ((distance == 0 || dst < distance) && dst <= pickRayMaxDistance) {
distance = dst;
selVoxel.set(tmp6).add(c.pos);
selectedChunk = c;
}
}
}
}
return selectedChunk;
}
public void selectionBox(Rectangle rectangle) {
CustomizableFrustum frustum = new CustomizableFrustum(rectangle);
camera.update();
frustum.update(camera.invProjectionView);
Vector3 origin = camera.unproject(new Vector3(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 0), 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
boolean anyEntitySelected = false;
boolean dispatched = false;
for (Entity entity : activeIsland.getEntities()) {
if (entity instanceof StaticEntity) continue;
if (!entity.isVisible()) continue;
entity.wasSelected = entity.selected;
entity.selected = false;
entity.getWorldBoundingBox(bb);
float dst = origin.dst(entity.posCache);
if (entity.inFrustum && frustum.boundsInFrustum(bb) && dst < pickRayMaxDistance) {
entity.selected = true;
anyEntitySelected = true;
if (!dispatched && entity instanceof Creature) {
for (SelectionListener sl : listeners)
sl.onCreatureSelection((Creature) entity, true);
dispatched = true;
}
}
}
if (!anyEntitySelected) {
for (Island i : world.getIslands()) {
if (i == null) continue;
for (Entity e : i.getEntities()) {
if (!(e instanceof StaticEntity)) continue;
e.wasSelected = e.selected;
e.selected = false;
e.getWorldBoundingBox(bb);
float dst = origin.dst(e.posCache);
if (e.inFrustum && frustum.boundsInFrustum(bb) && dst < pickRayMaxDistance) e.selected = true;
}
}
}
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
if (middleDown && Gdx.input.isKeyPressed(Keys.CONTROL_LEFT)) {
float f = 0.1f;
controller.target.y = controllerTarget.y + (screenY - mouseDown.y) * f;
camera.position.y = cameraPos.y + (screenY - mouseDown.y) * f;
camera.update();
controller.update();
}
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
if (regionSelectionMode) pickVoxelRay(activeIsland, selectedVoxel, false, screenX, screenY);
else if (cursorEntity != null) {
pickVoxelRay(activeIsland, hoveredVoxel, false, screenX, screenY);
cursorEntity.getModelInstance().transform.setToTranslation(activeIsland.pos);
cursorEntity.getModelInstance().transform.translate(hoveredVoxel).translate(cursorEntity.getBoundingBox().getDimensions().x <= 1 ? cursorEntity.blockTrn.x : 0, cursorEntity.blockTrn.y, cursorEntity.getBoundingBox().getDimensions().z <= 1 ? cursorEntity.blockTrn.z : 0);
cursorEntity.setIsland(activeIsland);
cursorEntity.updateVoxelPos();
cursorEntityPlacable = cursorEntity.canBePlaced();
if (defaultCursorEntityMaterials == null) {
defaultCursorEntityMaterials = new Array<Material>();
for (Material m : cursorEntity.getModelInstance().materials)
defaultCursorEntityMaterials.add(m.copy());
}
for (int i = 0; i < cursorEntity.getModelInstance().materials.size; i++) {
Material m = cursorEntity.getModelInstance().materials.get(i);
if (!cursorEntityPlacable) {
m.set(new BlendingAttribute(0.8f), ColorAttribute.createDiffuse(Color.RED));
} else {
m.remove(ColorAttribute.Diffuse);
BlendingAttribute ba = (BlendingAttribute) defaultCursorEntityMaterials.get(i).get(BlendingAttribute.Type);
if (ba == null) m.remove(BlendingAttribute.Type);
else m.set(ba);
}
}
} else if (activeIsland != null) pickRay(true, false, screenX, screenY);
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
mouseDown.set(screenX, screenY);
if (button == Buttons.MIDDLE) {
controllerTarget.set(controller.target);
cameraPos.set(camera.position);
middleDown = true;
Gdx.input.setCursorCatched(true);
}
return false;
}
@Override
public boolean tap(float x, float y, int count, int button) {
if (!doneLoading) return false;
if (button != Buttons.MIDDLE) {
if (!regionSelectionMode) {
if (cursorEntity != null) {
if (button == Buttons.LEFT) {
if (cursorEntityPlacable) {
for (int i = 0; i < defaultCursorEntityMaterials.size; i++) {
cursorEntity.getModelInstance().materials.set(i, defaultCursorEntityMaterials.get(i));
}
if (cursorEntity instanceof Structure) ((Structure) cursorEntity).setBuilt(false);
cursorEntity.getModelInstance().transform.translate(-activeIsland.pos.x, -activeIsland.pos.y, -activeIsland.pos.z);
activeIsland.addEntity(cursorEntity, true, false);
cursorEntity.updateVoxelPos();
if (!cursorEntityContinousPlacing) {
cursorEntity = null;
defaultCursorEntityMaterials = null;
cursorEntityPlacable = false;
} else {
cursorEntity = (StaticEntity) Entity.getForId(cursorEntity.getId(), cursorEntity.posCache.x, cursorEntity.posCache.y, cursorEntity.posCache.z);
cursorEntity.setIsland(activeIsland);
cursorEntity.getModelInstance().transform.translate(activeIsland.pos.x, activeIsland.pos.y, activeIsland.pos.z);
cursorEntity.updateVoxelPos();
if (cursorEntity instanceof Structure) ((Structure) cursorEntity).setBuilt(true);
cursorEntity.setVisible(true);
}
}
} else {
cursorEntity = null;
defaultCursorEntityMaterials = null;
cursorEntityPlacable = false;
}
} else {
selectionStartVoxel.set(-1, 0, 0);
pickRay(false, button == Buttons.LEFT, (int) x, (int) y);
}
} else {
if (selectionStartVoxel.x == -1) {
selectedVoxel.set(-1, 0, 0);
pickVoxelRay(activeIsland, selectionStartVoxel, regionSelectionLMB = button == Buttons.LEFT, (int) x, (int) y);
} else if (regionSelectionLMB == (button == Buttons.LEFT)) {
pickVoxelRay(activeIsland, selectedVoxel, button == Buttons.LEFT, (int) x, (int) y);
for (SelectionListener sl : listeners)
sl.onVoxelRangeSelection(activeIsland, selectionStartVoxel, selectedVoxel, regionSelectionLMB);
regionSelectionMode = false;
}
}
}
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if (button == Buttons.MIDDLE) {
middleDown = false;
Gdx.input.setCursorCatched(false);
}
return false;
}
public void addListener(SelectionListener value) {
listeners.insert(0, value);
}
public boolean removeListener(SelectionListener value) {
return listeners.removeValue(value, true);
}
/*
* Only call when @param:action != null
*/
public void action(String action) {
if (action.contains("|region")) {
selectionStartVoxel.set(-1, 0, 0);
selectedVoxel.set(-1, 0, 0);
regionSelectionMode = true;
}
if (action.contains("entity")) {
String[] a = action.split("\\|");
String s = a[0].replace("entity:", "");
Entity e = Entity.getForId((byte) Integer.parseInt(s), 0, 0, 0);
if (e instanceof Structure) {
((Structure) e).setBuilt(true);
((Structure) e).tickRequestsEnabled = false;
}
e.setVisible(true);
cursorEntity = (StaticEntity) e;
cursorEntityContinousPlacing = a.length > 1 && a[1].equals("cont");
}
activeAction = action;
}
}