/*
* Copyright 2012 Benjamin Glatzel <benjamin.glatzel@me.com>
*
* 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 org.terasology.game.modes;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.vecmath.Vector3f;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.spout.api.gamestate.GameState;
import org.terasology.asset.AssetType;
import org.terasology.asset.AssetUri;
import org.terasology.componentSystem.controllers.LocalPlayerSystem;
import org.terasology.components.LocalPlayerComponent;
import org.terasology.entitySystem.ComponentSystem;
import org.terasology.entitySystem.EntityRef;
import org.terasology.entitySystem.PersistableEntityManager;
import org.terasology.entitySystem.persistence.EntityDataJSONFormat;
import org.terasology.entitySystem.persistence.EntityPersisterHelper;
import org.terasology.entitySystem.persistence.EntityPersisterHelperImpl;
import org.terasology.entitySystem.persistence.WorldPersister;
import org.terasology.game.ComponentSystemManager;
import org.terasology.game.CoreRegistry;
import org.terasology.game.TerasologyEngine;
import org.terasology.game.bootstrap.EntitySystemBuilder;
import org.terasology.input.CameraTargetSystem;
import org.terasology.input.InputSystem;
import org.terasology.logic.LocalPlayer;
import org.terasology.logic.manager.AssetManager;
import org.terasology.logic.manager.GUIManager;
import org.terasology.logic.manager.PathManager;
import org.terasology.logic.world.WorldProvider;
import org.terasology.performanceMonitor.PerformanceMonitor;
import org.terasology.protobuf.EntityData;
import org.terasology.rendering.cameras.Camera;
import org.terasology.rendering.gui.menus.UIStatusScreen;
import org.terasology.rendering.physics.BulletPhysicsRenderer;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.utilities.FastRandom;
/**
* Play mode.
*
* @author Benjamin Glatzel <benjamin.glatzel@me.com>
* @author Anton Kireev <adeon.k87@gmail.com>
* @version 0.1
*/
public class StateSinglePlayer extends GameState {
public static final String ENTITY_DATA_FILE = "entity.dat";
private Logger logger = Logger.getLogger(getClass().getName());
private String currentWorldName;
private String currentWorldSeed;
private long currentWorldStartTime;
private TerasologyEngine engine;
private PersistableEntityManager entityManager;
/* RENDERING */
private WorldRenderer worldRenderer;
private ComponentSystemManager componentSystemManager;
private LocalPlayerSystem localPlayerSys;
private CameraTargetSystem cameraTargetSystem;
private InputSystem inputSystem;
/* GAME LOOP */
private boolean pauseGame = false;
public StateSinglePlayer(TerasologyEngine engine) {
this.engine = engine;
}
@Override
public void initialize() {
cacheTextures();
entityManager = new EntitySystemBuilder().build();
componentSystemManager = new ComponentSystemManager();
CoreRegistry.put(ComponentSystemManager.class, componentSystemManager);
localPlayerSys = new LocalPlayerSystem();
componentSystemManager.register(localPlayerSys, "engine:LocalPlayerSystem");
cameraTargetSystem = new CameraTargetSystem();
CoreRegistry.put(CameraTargetSystem.class, cameraTargetSystem);
componentSystemManager.register(cameraTargetSystem, "engine:CameraTargetSystem");
inputSystem = new InputSystem();
CoreRegistry.put(InputSystem.class, inputSystem);
componentSystemManager.register(inputSystem, "engine:InputSystem");
componentSystemManager.loadEngineSystems();
CoreRegistry.put(WorldPersister.class, new WorldPersister(entityManager));
loadPrefabs();
}
private void loadPrefabs() {
EntityPersisterHelper persisterHelper = new EntityPersisterHelperImpl(entityManager);
for (AssetUri prefabURI : AssetManager.list(AssetType.PREFAB)) {
logger.info("Loading prefab " + prefabURI);
try {
InputStream stream = AssetManager.assetStream(prefabURI);
if (stream != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
EntityData.Prefab prefabData = EntityDataJSONFormat.readPrefab(reader);
stream.close();
if (prefabData != null) {
persisterHelper.deserializePrefab(prefabData, prefabURI.getPackage());
}
} else {
logger.severe("Failed to load prefab '" + prefabURI + "'");
}
} catch (IOException e) {
logger.log(Level.WARNING, "Failed to load prefab '" + prefabURI + "'", e);
}
}
}
private void cacheTextures() {
for (AssetUri textureURI : AssetManager.list(AssetType.TEXTURE)) {
AssetManager.load(textureURI);
}
}
@Override
public void loadResources() {
initWorld(currentWorldName, currentWorldSeed, currentWorldStartTime);
}
@Override
public void unloadResources() {
for (ComponentSystem system : componentSystemManager.iterateAll()) {
system.shutdown();
}
GUIManager.getInstance().closeWindows();
try {
CoreRegistry.get(WorldPersister.class).save(new File(PathManager.getInstance().getWorldSavePath(CoreRegistry.get(WorldProvider.class).getTitle()), ENTITY_DATA_FILE), WorldPersister.SaveFormat.Binary);
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to save entities", e);
}
dispose();
entityManager.clear();
}
public void dispose() {
if (worldRenderer != null) {
worldRenderer.dispose();
worldRenderer = null;
}
}
@Override
public void onRender(float delta) {
/* GUI */
updateUserInterface();
if (worldRenderer != null && shouldUpdateWorld()) {
worldRenderer.update(delta);
}
/* TODO: This seems a little off - plus is more of a UI than single player game state concern. Move somewhere
more appropriate? Possibly HUD? */
boolean dead = true;
for (EntityRef entity : entityManager.iteratorEntities(LocalPlayerComponent.class)) {
dead = entity.getComponent(LocalPlayerComponent.class).isDead;
}
if (dead) {
if (GUIManager.getInstance().getWindowById("engine:statusScreen") == null) {
UIStatusScreen statusScreen = GUIManager.getInstance().addWindow(new UIStatusScreen(), "engine:statusScreen");
statusScreen.updateStatus("Sorry! Seems like you have died :-(");
statusScreen.setVisible(true);
}
} else {
GUIManager.getInstance().removeWindow("engine:statusScreen");
}
handleInput(delta);
}
public void handleInput(float delta) {
cameraTargetSystem.update();
inputSystem.update(delta);
// TODO: This should be handled outside of the state, need to fix the screens handling
if (screenHasFocus() || !shouldUpdateWorld()) {
if (Mouse.isGrabbed()) {
Mouse.setGrabbed(false);
Mouse.setCursorPosition(Display.getWidth() / 2, Display.getHeight() / 2);
}
} else {
if (!Mouse.isGrabbed()) {
Mouse.setGrabbed(true);
}
}
}
public void initWorld(String title) {
initWorld(title, null, 0);
}
/**
* Init. a new random world.
*/
public void initWorld(String title, String seed, long time) {
final FastRandom random = new FastRandom();
// Get rid of the old world
if (worldRenderer != null) {
worldRenderer.dispose();
worldRenderer = null;
}
if (seed == null) {
seed = random.randomCharacterString(16);
} else if (seed.isEmpty()) {
seed = random.randomCharacterString(16);
}
logger.log(Level.INFO, "Creating new World with seed \"{0}\"", seed);
// Init. a new world
// TODO
File entityDataFile = new File(PathManager.getInstance().getWorldSavePath(title), ENTITY_DATA_FILE);
entityManager.clear();
if (entityDataFile.exists()) {
try {
CoreRegistry.get(WorldPersister.class).load(entityDataFile, WorldPersister.SaveFormat.Binary);
} catch (IOException e) {
logger.log(Level.SEVERE, "Failed to load entity data", e);
}
}
CoreRegistry.put(WorldRenderer.class, worldRenderer);
CoreRegistry.put(LocalPlayer.class, new LocalPlayer(EntityRef.NULL));
CoreRegistry.put(Camera.class, worldRenderer.getActiveCamera());
CoreRegistry.put(BulletPhysicsRenderer.class, worldRenderer.getBulletRenderer());
for (ComponentSystem system : componentSystemManager.iterateAll()) {
system.initialise();
}
prepareWorld();
}
private Vector3f nextSpawningPoint() {
return new Vector3f(0, 5, 0);
// TODO: Need to generate an X/Z coord, force a chunk relevent and calculate Y
/*
ChunkGeneratorTerrain tGen = ((ChunkGeneratorTerrain) getGeneratorManager().getChunkGenerators().get(0));
FastRandom nRandom = new FastRandom(CoreRegistry.get(Timer.class).getTimeInMs());
for (; ; ) {
int randX = (int) (nRandom.randomDouble() * 128f);
int randZ = (int) (nRandom.randomDouble() * 128f);
for (int y = Chunk.SIZE_Y - 1; y >= 32; y--) {
double dens = tGen.calcDensity(randX + (int) SPAWN_ORIGIN.x, y, randZ + (int) SPAWN_ORIGIN.y);
if (dens >= 0 && y < 64)
return new Vector3d(randX + SPAWN_ORIGIN.x, y, randZ + SPAWN_ORIGIN.y);
else if (dens >= 0 && y >= 64)
break;
}
} */
}
private boolean screenHasFocus() {
return GUIManager.getInstance().getFocusedWindow() != null && GUIManager.getInstance().getFocusedWindow().isModal() && GUIManager.getInstance().getFocusedWindow().isVisible();
}
private boolean shouldUpdateWorld() {
return !pauseGame;
}
// TODO: Maybe should have its own state?
private void prepareWorld() {
// TODO get rid of this
// UILoadingScreen loadingScreen = GUIManager.getInstance().addWindow(new UILoadingScreen(), "engine:loadingScreen");
// Display.update();
//
// int chunksGenerated = 0;
//
// Timer timer = CoreRegistry.get(Timer.class);
// long startTime = timer.getTimeInMs();
//
// Iterator<EntityRef> iterator = entityManager.iteratorEntities(LocalPlayerComponent.class).iterator();
// if (iterator.hasNext()) {
// CoreRegistry.get(LocalPlayer.class).setEntity(iterator.next());
// worldRenderer.setPlayer(CoreRegistry.get(LocalPlayer.class));
// } else {
// // Load spawn zone so player spawn location can be determined
// EntityRef spawnZoneEntity = entityManager.create();
// spawnZoneEntity.addComponent(new LocationComponent(new Vector3f(TeraChunk.SIZE_X / 2, TeraChunk.SIZE_Y / 2, TeraChunk.SIZE_Z / 2)));
// worldRenderer.getChunkProvider().addRegionEntity(spawnZoneEntity, 1);
//
// while (!worldRenderer.getWorldProvider().isBlockActive(new Vector3i(TeraChunk.SIZE_X / 2, TeraChunk.SIZE_Y / 2, TeraChunk.SIZE_Z / 2))) {
// loadingScreen.updateStatus(String.format("Loading spawn area... %.2f%%! :-)", (timer.getTimeInMs() - startTime) / 50.0f));
//
// renderUserInterface();
// updateUserInterface();
// Display.update();
// }
//
// Vector3i spawnPoint = new Vector3i(TeraChunk.SIZE_X / 2, TeraChunk.SIZE_Y, TeraChunk.SIZE_Z / 2);
// while (worldRenderer.getWorldProvider().getBlock(spawnPoint) == BlockManager.getInstance().getAir() && spawnPoint.y > 0) {
// spawnPoint.y--;
// }
//
// PlayerFactory playerFactory = new PlayerFactory(entityManager);
// CoreRegistry.get(LocalPlayer.class).setEntity(playerFactory.newInstance(new Vector3f(spawnPoint.x + 0.5f, spawnPoint.y + 2.0f, spawnPoint.z + 0.5f)));
// worldRenderer.setPlayer(CoreRegistry.get(LocalPlayer.class));
// worldRenderer.getChunkProvider().removeRegionEntity(spawnZoneEntity);
// spawnZoneEntity.destroy();
// }
//
// while (!getWorldRenderer().pregenerateChunks() && timer.getTimeInMs() - startTime < 5000) {
// chunksGenerated++;
//
// loadingScreen.updateStatus(String.format("Fast forwarding world... %.2f%%! :-)", (timer.getTimeInMs() - startTime) / 50.0f));
//
// renderUserInterface();
// updateUserInterface();
// Display.update();
// }
//
// GUIManager.getInstance().removeWindow(loadingScreen);
}
public void render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
if (worldRenderer != null) {
worldRenderer.render();
}
/* UI */
PerformanceMonitor.startActivity("Render and Update UI");
renderUserInterface();
PerformanceMonitor.endActivity();
}
public void renderUserInterface() {
GUIManager.getInstance().render();
}
private void updateUserInterface() {
GUIManager.getInstance().update();
}
public WorldRenderer getWorldRenderer() {
return worldRenderer;
}
public void pause() {
pauseGame = true;
}
public void unpause() {
pauseGame = false;
}
public void togglePauseGame() {
if (pauseGame) {
unpause();
} else {
pause();
}
}
public boolean isGamePaused() {
return pauseGame;
}
}