All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bladecoder.engine.model.Scene Maven / Gradle / Ivy

There is a newer version: 4.3.1
Show newest version
/*******************************************************************************
 * Copyright 2014 Rafael Garcia Moreno.
 * 
 * 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 com.bladecoder.engine.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Polygon;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Json.Serializable;
import com.badlogic.gdx.utils.JsonValue;
import com.bladecoder.engine.actions.SceneActorRef;
import com.bladecoder.engine.assets.AssetConsumer;
import com.bladecoder.engine.assets.EngineAssetManager;
import com.bladecoder.engine.polygonalpathfinder.NavNodePolygonal;
import com.bladecoder.engine.polygonalpathfinder.PolygonalNavGraph;
import com.bladecoder.engine.util.EngineLogger;
import com.bladecoder.engine.util.SerializationHelper;
import com.bladecoder.engine.util.SerializationHelper.Mode;

public class Scene implements Serializable, AssetConsumer {

	public static final Color ACTOR_BBOX_COLOR = new Color(0.2f, 0.2f, 0.8f, 1f);
	public static final Color WALKZONE_COLOR = Color.GREEN;
	public static final Color OBSTACLE_COLOR = Color.RED;
	public static final Color ANCHOR_COLOR = Color.RED;
	public static final float ANCHOR_RADIUS = 14f;

	public static final String VAR_PLAYER = "$PLAYER";

	/**
	 * All actors in the scene
	 */
	private HashMap actors = new HashMap();

	/**
	 * BaseActor layers
	 */
	private List layers = new ArrayList();

	private SceneCamera camera = new SceneCamera();

	private Array background;
	private String backgroundAtlas;
	private String backgroundRegionId;

	/** For polygonal PathFinding */
	private PolygonalNavGraph polygonalNavGraph;

	/**
	 * depth vector. X: the actor 'y' position for a 0.0 scale, Y: the actor 'y'
	 * position for a 1.0 scale.
	 */
	private Vector2 depthVector;

	private String player;

	/** The actor the camera will follow */
	private SpriteActor followActor;

	/**
	 * Music for the scene.
	 */
	private MusicDesc musicDesc;

	private Vector2 sceneSize;

	private String id;

	/** internal state. Can be used for actions to maintain a state machine */
	private String state;

	private VerbManager verbs = new VerbManager();

	public Scene() {
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getState() {
		return state;
	}

	public void setState(String s) {
		state = s;
	}

	public List getLayers() {
		return layers;
	}

	public SceneLayer getLayer(String name) {
		for (SceneLayer l : layers) {
			if (name.equals(l.getName()))
				return l;
		}

		return null;
	}

	public void addLayer(SceneLayer layer) {
		layers.add(layer);
	}

	public MusicDesc getMusicDesc() {
		return musicDesc;
	}

	public void setMusicDesc(MusicDesc musicDesc) {
		this.musicDesc = musicDesc;
	}

	public float getFakeDepthScale(float y) {
		if (depthVector == null)
			return 1.0f;

		float worldScale = EngineAssetManager.getInstance().getScale();

		return Math.max(0, (y - depthVector.x * worldScale) / ((depthVector.y - depthVector.x) * worldScale));
	}

	public VerbManager getVerbManager() {
		return verbs;
	}

	public Verb getVerb(String id) {
		return verbs.getVerb(id, state, null);
	}

	public void runVerb(String id) {
		verbs.runVerb(id, state, null);
	}

	public void update(float delta) {
		// We draw the elements in order: from top to bottom.
		// so we need to order the array list
		for (SceneLayer layer : layers)
			layer.update();

		for (BaseActor a : actors.values()) {
			a.update(delta);
		}

		camera.update(delta);

		if (followActor != null) {
			camera.updatePos(followActor);
		}
	}

	public void draw(SpriteBatch batch) {

		if (background != null) {
			batch.disableBlending();
			batch.setProjectionMatrix(camera.calculateParallaxMatrix(1, 1));
			batch.begin();

			float x = 0;

			for (AtlasRegion tile : background) {
				batch.draw(tile, x, 0f);
				x += tile.getRegionWidth();
			}

			batch.end();
			batch.enableBlending();
		}

		// draw layers from bottom to top
		for (int i = layers.size() - 1; i >= 0; i--) {
			SceneLayer layer = layers.get(i);

			batch.setProjectionMatrix(camera.calculateParallaxMatrix(layer.getParallaxMultiplier(), 1));
			batch.begin();
			layer.draw(batch);
			batch.end();
		}
	}

	public void drawBBoxLines(ShapeRenderer renderer) {
		// renderer.begin(ShapeType.Rectangle);
		renderer.begin(ShapeType.Line);

		for (BaseActor a : actors.values()) {
			Polygon p = a.getBBox();

			if (p == null) {
				EngineLogger.error("ERROR DRAWING BBOX FOR: " + a.getId());
			}

			if (a instanceof ObstacleActor) {
				renderer.setColor(OBSTACLE_COLOR);
				renderer.polygon(p.getTransformedVertices());
			} else if (a instanceof AnchorActor) {
				renderer.setColor(Scene.ANCHOR_COLOR);
				renderer.line(p.getX() - Scene.ANCHOR_RADIUS, p.getY(), p.getX() + Scene.ANCHOR_RADIUS, p.getY());
				renderer.line(p.getX(), p.getY() - Scene.ANCHOR_RADIUS, p.getX(), p.getY() + Scene.ANCHOR_RADIUS);
			} else {
				renderer.setColor(ACTOR_BBOX_COLOR);
				renderer.polygon(p.getTransformedVertices());
			}

			// Rectangle r = a.getBBox().getBoundingRectangle();
			// renderer.rect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
		}

		if (polygonalNavGraph != null) {
			renderer.setColor(WALKZONE_COLOR);
			renderer.polygon(polygonalNavGraph.getWalkZone().getTransformedVertices());

			// DRAW LINEs OF SIGHT
			renderer.setColor(Color.WHITE);
			ArrayList nodes = polygonalNavGraph.getGraphNodes();
			for (NavNodePolygonal n : nodes) {
				for (NavNodePolygonal n2 : n.neighbors) {
					renderer.line(n.x, n.y, n2.x, n2.y);
				}
			}
		}

		renderer.end();
	}

	public BaseActor getActor(String id, boolean searchInventory) {

		if (VAR_PLAYER.equals(id))
			return actors.get(player);

		BaseActor a = actors.get(id);

		if (a == null && searchInventory) {
			a = World.getInstance().getInventory().get(id);
			
			// Search the uiActors
			if(a == null)
				a = World.getInstance().getUIActors().get(id);
		}

		return a;
	}

	public Map getActors() {
		return actors;
	}

	public void addActor(BaseActor actor) {
		actors.put(actor.getId(), actor);
		actor.setScene(this);

		if (actor instanceof InteractiveActor) {
			InteractiveActor ia = (InteractiveActor) actor;

			SceneLayer layer = getLayer(ia.getLayer());

			if (layer == null) { // fallback for compatibility
				layer = new SceneLayer();
				layer.setName(ia.getLayer());
				layers.add(layer);
			}

			layer.add(ia);
		}
	}

	public void setBackground(String bgAtlas, String bgId, String lightMapAtlas, String lightMapId) {
		this.backgroundAtlas = bgAtlas;
		this.backgroundRegionId = bgId;
	}

	/**
	 * Returns the Interactive actor at the position. The actor must have the
	 * interaction property enabled.
	 */
	public InteractiveActor getInteractiveActorAt(float x, float y) {

		for (SceneLayer layer : layers) {

			if (!layer.isVisible())
				continue;

			// Obtain actors in reverse (close to camera)
			for (int i = layer.getActors().size() - 1; i >= 0; i--) {
				BaseActor a = layer.getActors().get(i);

				if (a instanceof InteractiveActor && ((InteractiveActor) a).canInteract() && a.hit(x, y)) {
					return (InteractiveActor) a;
				}
			}
		}

		return null;
	}

	private Rectangle tmpToleranceRect = new Rectangle();

	/**
	 * Obtains the actor at (x,y) with TOLERANCE.
	 * 
	 * Creates a square with size = TOLERANCE and checks:
	 * 
	 * 1. if some vertex from the TOLERANCE square is inside an actor bbox. 
	 * 2. if some actor of the actor vertexes is inside the TOLERANCE square.
	 */
	public InteractiveActor getInteractiveActorAt(float x, float y, float tolerance) {
		if (tolerance <= 0) {
			return getInteractiveActorAt(x, y);
		}

		List layers = getLayers();

		tmpToleranceRect.x = x - tolerance / 2;
		tmpToleranceRect.y = y - tolerance / 2;
		tmpToleranceRect.width = tolerance;
		tmpToleranceRect.height = tolerance;

		for (SceneLayer layer : layers) {

			if (!layer.isVisible())
				continue;

			// Obtain actors in reverse (close to camera)
			for (int l = layer.getActors().size() - 1; l >= 0; l--) {
				BaseActor a = layer.getActors().get(l);

				if (a instanceof InteractiveActor && ((InteractiveActor) a).canInteract()) {

					if (a.hit(x, y) || a.hit(tmpToleranceRect.x, tmpToleranceRect.y)
							|| a.hit(tmpToleranceRect.x + tmpToleranceRect.width, tmpToleranceRect.y)
							|| a.hit(tmpToleranceRect.x, tmpToleranceRect.y + tmpToleranceRect.height)
							|| a.hit(tmpToleranceRect.x + tmpToleranceRect.width,
									tmpToleranceRect.y + tmpToleranceRect.height))
						return (InteractiveActor) a;

					float[] verts = a.getBBox().getTransformedVertices();
					for (int i = 0; i < verts.length; i += 2) {
						float vx = verts[i];
						float vy = verts[i + 1];

						if (tmpToleranceRect.contains(vx, vy))
							return (InteractiveActor) a;
					}
				}
			}
			
		}

		return null;
	}

	/**
	 * Returns the actor at the position. Including not interactive actors.
	 */
	public BaseActor getActorAt(float x, float y) {

		// 1. Search for ANCHOR Actors
		for (BaseActor a : actors.values()) {
			if (a instanceof AnchorActor) {
				float dst = Vector2.dst(x, y, a.getX(), a.getY());

				if (dst < ANCHOR_RADIUS)
					return a;
			}
		}

		// 2. Search for INTERACTIVE Actors
		for (SceneLayer layer : layers) {

			if (!layer.isVisible())
				continue;

			// Obtain actors in reverse (close to camera)
			for (int i = layer.getActors().size() - 1; i >= 0; i--) {
				BaseActor a = layer.getActors().get(i);

				if (a.hit(x, y)) {
					return a;
				}
			}
		}

		// 3. Search for OBSTACLE actors
		for (BaseActor a : actors.values()) {
			if (a instanceof ObstacleActor && a.hit(x, y)) {
				return a;
			}
		}

		return null;
	}

	public void setPlayer(CharacterActor a) {
		if (a != null) {
			player = a.getId();
			a.setInteraction(false);
		} else {
			player = null;
		}
	}

	public CharacterActor getPlayer() {
		return (CharacterActor) actors.get(player);
	}

	public Vector2 getDepthVector() {
		return depthVector;
	}

	public void setDepthVector(Vector2 v) {
		depthVector = v;
	}

	public String getBackgroundAtlas() {
		return backgroundAtlas;
	}

	public void setBackgroundAtlas(String backgroundAtlas) {
		this.backgroundAtlas = backgroundAtlas;
	}

	public String getBackgroundRegionId() {
		return backgroundRegionId;
	}

	public void setBackgroundRegionId(String backgroundRegionId) {
		this.backgroundRegionId = backgroundRegionId;
	}

	public void removeActor(BaseActor a) {

		if (player != null && a.getId().equals(player)) {
			player = null;
		}

		BaseActor r = actors.remove(a.getId());

		if (r == null) {
			EngineLogger.error("Removing actor from scene: Actor not found");
			return;
		}

		if (a instanceof InteractiveActor) {
			InteractiveActor ia = (InteractiveActor) a;
			SceneLayer layer = getLayer(ia.getLayer());
			layer.getActors().remove(ia);
		}

		if (a instanceof ObstacleActor && polygonalNavGraph != null)
			polygonalNavGraph.removeDinamicObstacle(a.getBBox());

		a.setScene(null);

	}

	public Array getBackground() {
		return background;
	}

	public SceneCamera getCamera() {
		return camera;
	}

	public void resetCamera(float worldWidth, float worldHeight) {
		camera.create(worldWidth, worldHeight);

		if (getPlayer() != null)
			setCameraFollowActor(getPlayer());
	}

	public void setCameraFollowActor(SpriteActor a) {
		followActor = a;

		if (a != null)
			camera.updatePos(a);
	}

	public SpriteActor getCameraFollowActor() {
		return followActor;
	}

	@Override
	public void loadAssets() {

		if (backgroundAtlas != null && !backgroundAtlas.isEmpty()) {
			EngineAssetManager.getInstance().loadAtlas(backgroundAtlas);
		}

		for (BaseActor a : actors.values()) {
			if (a instanceof AssetConsumer)
				((AssetConsumer) a).loadAssets();
		}

		// CALC WALK GRAPH
		if (polygonalNavGraph != null) {
			polygonalNavGraph.createInitialGraph(actors.values());
		}
	}

	@Override
	public void retrieveAssets() {

		// RETRIEVE BACKGROUND
		if (backgroundAtlas != null && !backgroundAtlas.isEmpty()) {
			background = EngineAssetManager.getInstance().getRegions(backgroundAtlas, backgroundRegionId);

			int width = 0;

			for (int i = 0; i < background.size; i++) {
				width += background.get(i).getRegionWidth();
			}

			int height = background.get(0).getRegionHeight();

			// Sets the scrolling dimensions. It must be done here because
			// the background must be loaded to calculate the bbox
			if (sceneSize == null)
				camera.setScrollingDimensions(width, height);

			// if(followActor != null)
			// camera.updatePos(followActor);
		}

		if (sceneSize != null)
			camera.setScrollingDimensions(sceneSize.x, sceneSize.y);

		// RETRIEVE ACTORS
		for (BaseActor a : actors.values()) {
			if (a instanceof AssetConsumer)
				((AssetConsumer) a).retrieveAssets();
		}
	}

	@Override
	public void dispose() {

		if (backgroundAtlas != null && !backgroundAtlas.isEmpty()) {
			EngineAssetManager.getInstance().disposeAtlas(backgroundAtlas);
		}

		// orderedActors.clear();

		for (BaseActor a : actors.values()) {
			if (a instanceof AssetConsumer)
				((AssetConsumer) a).dispose();
		}
	}

	public Vector2 getSceneSize() {
		return sceneSize;
	}

	public void setSceneSize(Vector2 sceneSize) {
		this.sceneSize = sceneSize;
	}

	public void orderLayersByZIndex() {
		for (SceneLayer l : layers) {
			l.orderByZIndex();
		}
	}

	public PolygonalNavGraph getPolygonalNavGraph() {
		return polygonalNavGraph;
	}

	public void setPolygonalNavGraph(PolygonalNavGraph polygonalNavGraph) {
		this.polygonalNavGraph = polygonalNavGraph;
	}

	@Override
	public void write(Json json) {
		if (SerializationHelper.getInstance().getMode() == Mode.MODEL) {

			json.writeValue("id", id);
			json.writeValue("layers", layers, layers.getClass(), SceneLayer.class);

			json.writeValue("actors", actors);

			if (backgroundAtlas != null) {
				json.writeValue("backgroundAtlas", backgroundAtlas);
				json.writeValue("backgroundRegionId", backgroundRegionId);
			}

			json.writeValue("musicDesc", musicDesc);

			if (depthVector != null)
				json.writeValue("depthVector", depthVector);

			if (polygonalNavGraph != null)
				json.writeValue("polygonalNavGraph", polygonalNavGraph);

			if (sceneSize != null)
				json.writeValue("sceneSize", sceneSize);

		} else {
			SceneActorRef actorRef;

			json.writeObjectStart("actors");
			for (BaseActor a : actors.values()) {
				actorRef = new SceneActorRef(a.getInitScene(), a.getId());
				json.writeValue(actorRef.toString(), a);
			}
			json.writeObjectEnd();

			json.writeValue("camera", camera);

			if (followActor != null)
				json.writeValue("followActor", followActor.getId());
		}

		verbs.write(json);

		if (state != null)
			json.writeValue("state", state);

		if (player != null)
			json.writeValue("player", player);
	}

	@SuppressWarnings("unchecked")
	@Override
	public void read(Json json, JsonValue jsonData) {
		if (SerializationHelper.getInstance().getMode() == Mode.MODEL) {

			id = json.readValue("id", String.class, jsonData);
			layers = json.readValue("layers", ArrayList.class, SceneLayer.class, jsonData);
			actors = json.readValue("actors", HashMap.class, BaseActor.class, jsonData);

			for (BaseActor actor : actors.values()) {
				actor.setScene(this);
				actor.setInitScene(id);

				if (actor instanceof InteractiveActor) {
					InteractiveActor ia = (InteractiveActor) actor;

					SceneLayer layer = getLayer(ia.getLayer());
					layer.add(ia);
				}
			}

			orderLayersByZIndex();

			backgroundAtlas = json.readValue("backgroundAtlas", String.class, jsonData);
			backgroundRegionId = json.readValue("backgroundRegionId", String.class, jsonData);

			musicDesc = json.readValue("musicDesc", MusicDesc.class, jsonData);

			depthVector = json.readValue("depthVector", Vector2.class, jsonData);

			polygonalNavGraph = json.readValue("polygonalNavGraph", PolygonalNavGraph.class, jsonData);

			sceneSize = json.readValue("sceneSize", Vector2.class, jsonData);

		} else {
			JsonValue jsonValueActors = jsonData.get("actors");
			SceneActorRef actorRef;

			// GET ACTORS FROM HIS INIT SCENE AND MOVE IT TO THE LOADING SCENE.
			for (int i = 0; i < jsonValueActors.size; i++) {
				JsonValue jsonValueAct = jsonValueActors.get(i);
				actorRef = new SceneActorRef(jsonValueAct.name);
				Scene sourceScn = World.getInstance().getScene(actorRef.getSceneId());

				if (sourceScn != this) {
					BaseActor actor = sourceScn.getActor(actorRef.getActorId(), false);
					sourceScn.removeActor(actor);
					addActor(actor);
				}
			}

			// READ ACTOR STATE.
			// The state must be retrieved after getting actors from his init
			// scene to restore verb cb properly.
			for (int i = 0; i < jsonValueActors.size; i++) {
				JsonValue jsonValueAct = jsonValueActors.get(i);
				actorRef = new SceneActorRef(jsonValueAct.name);

				BaseActor actor = getActor(actorRef.getActorId(), false);

				if (actor != null)
					actor.read(json, jsonValueAct);
				else
					EngineLogger.debug("Actor not found: " + actorRef);
			}

			orderLayersByZIndex();

			camera = json.readValue("camera", SceneCamera.class, jsonData);
			String followActorId = json.readValue("followActor", String.class, jsonData);

			setCameraFollowActor((SpriteActor) actors.get(followActorId));
		}

		verbs.read(json, jsonData);
		state = json.readValue("state", String.class, jsonData);
		player = json.readValue("player", String.class, jsonData);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy