package de.fau.cs.mad.fly.res;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetLoaderParameters;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.environment.PointLight;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonReader;
import com.badlogic.gdx.utils.JsonValue;
import de.fau.cs.mad.fly.features.upgrades.types.ChangePointsUpgrade;
import de.fau.cs.mad.fly.features.upgrades.types.ChangeSteeringUpgrade;
import de.fau.cs.mad.fly.features.upgrades.types.ChangeTimeUpgrade;
import de.fau.cs.mad.fly.features.upgrades.types.Collectible;
import de.fau.cs.mad.fly.features.upgrades.types.ResizeGatesUpgrade;
import de.fau.cs.mad.fly.features.upgrades.types.SpeedUpgradeEffect;
import de.fau.cs.mad.fly.features.upgrades.types.TemporarySpeedUpgrade;
import de.fau.cs.mad.fly.game.GameModel;
import de.fau.cs.mad.fly.game.GameObject;
import de.fau.cs.mad.fly.game.object.RotationMover;
import de.fau.cs.mad.fly.game.object.SinusMover;
import de.fau.cs.mad.fly.game.object.SinusRotationMover;
import de.fau.cs.mad.fly.player.gravity.ConstantGravity;
import de.fau.cs.mad.fly.player.gravity.DirectionalGravity;
/**
* Created by danyel on 26/05/14.
*/
public class LevelLoader extends AsynchronousAssetLoader<Level, LevelLoader.LevelParameters> {
private static final String JSON_POSITION = "position";
private static final String MODEL_FOLDER = "models/";
private static final int DEFAULT_GATE_SCORE = 50;
Level level;
private final JsonReader reader;
private final Json auto;
private JsonValue json;
private Map<String, GameModel> models;
private Map<String, String> dependencies;
private Map<String, GameObject> components;
private AssetManager manager;
private FileHandle file;
private LevelParameters parameter;
private Map<String, Environment> environments;
/**
* Constructor, sets the
* {@link com.badlogic.gdx.assets.loaders.FileHandleResolver} to use to
* resolve the file associated with the asset name.
* */
public LevelLoader() {
super(new InternalFileHandleResolver());
reader = new JsonReader();
auto = new Json();
models = new HashMap<String, GameModel>();
components = new HashMap<String, GameObject>();
}
public void loadLevelfromJson() {
parseJson();
for (Map.Entry<String, String> e : dependencies.entrySet()) {
models.put(e.getKey(), dependencyFor(e.getKey()));
}
environments = parseEnvironments();
parseComponents();
Perspective start = auto.fromJson(Perspective.class, json.get("start").toString());
List<GameObject> componentsList = new ArrayList<GameObject>();
componentsList.addAll(components.values());
level = new Level(json.getString("name"), start, componentsList, models, environments);
JsonValue levelClass = json.get("class");
if (levelClass != null) {
level.levelClass = levelClass.asString();
}
level.head.id = json.getInt("id");
level.setLeftTime(json.getInt("time"));
JsonValue gravity = json.get("gravity");
if (gravity != null) {
parseGravity(level, gravity);
}
level.addGateCircuit(parseGates());
level.addCollectibleManager(parseCollectibles());
}
/**
* Parses and adds the gravity to the level.
*
* @param level
* The level to add the gravity to.
* @param e
* The json value of the level.
*/
private void parseGravity(Level level, JsonValue e) {
String type = e.getString("type");
if ("ConstantGravity".equals(type)) {
JsonValue dir = e.get("direction");
ConstantGravity gravity = new ConstantGravity(new Vector3(dir.getFloat(0), dir.getFloat(1), dir.getFloat(2)));
level.setGravity(gravity);
} else if ("DirectionalGravity".equals(type)) {
JsonValue pos = e.get(JSON_POSITION);
float strength = e.getFloat("strength");
DirectionalGravity gravity = new DirectionalGravity(new Vector3(pos.getFloat(0), pos.getFloat(1), pos.getFloat(2)), strength);
level.setGravity(gravity);
}
}
/**
* Parses the gates in the level file and creates a gate circuit.
*
* @return GateCircuit
* @throws LevelLoadingException
*/
private GateCircuit parseGates() {
Map<Integer, GateGoal> gateMap = new HashMap<Integer, GateGoal>();
JsonValue gates = json.get("gates");
if (gates == null) {
return null;
}
// dummy gate goal at the start
GateGoal dummyGate = null;
int len = gates.size;
JsonValue jsonGate;
GateDisplay display;
GateGoal goal;
for (int i = 0; i < len; i++) {
jsonGate = gates.get(i);
JsonValue gateId = jsonGate.get("gateId");
if (gateId != null) {
String ref = jsonGate.getString("ref");
String refHole = jsonGate.getString("refHole");
display = new GateDisplay(models.get(ref));
JsonValue scoreJS = jsonGate.get("score");
int score = DEFAULT_GATE_SCORE;
if (scoreJS != null) {
score = scoreJS.asInt();
}
goal = new GateGoal(gateId.asInt(), models.get(refHole), score, display);
goal.hide();
display.setGoal(goal);
parseTransform(display, jsonGate);
goal.transform = display.transform.cpy();
parseVelocity(display, jsonGate);
if(display.getMover() != null) {
goal.setMover(display.getMover().getCopy(goal));
}
gateMap.put(gateId.asInt(), goal);
} else {
goal = new GateGoal(-1, models.get("hole"), 0, null);
goal.hide();
dummyGate = goal;
}
goal.successors = jsonGate.get("successors").asIntArray();
gateMap.put(goal.getGateId(), goal);
}
if (dummyGate == null) {
Gdx.app.log("LevelLoader.parseGates", "no dummy gate found, create default one");
dummyGate = new GateGoal(-1, models.get("hole"), 0, null);
dummyGate.hide();
int[] dummySuccessors = new int[1];
dummySuccessors[0] = 0;
dummyGate.setSuccessors(dummySuccessors);
}
GateCircuit gateCircuit = new GateCircuit(dummyGate);
gateCircuit.setGates(gateMap);
return gateCircuit;
}
/**
* Parses the additional information of the current game object like the id.
*
* @param o
* The game object.
* @param e
* The json value of the game object.
*/
private void parseInformation(GameObject o, JsonValue e) {
o.setId(e.getString("id"));
JsonValue visible = e.get("visible");
if (visible != null && !visible.asBoolean()) {
o.hide();
}
}
/**
* Parses the transform matrix of the current game object.
*
* @param o
* The game object.
* @param e
* The json value of the game object.
*/
private void parseTransform(GameObject o, JsonValue e) {
JsonValue transform = e.get("transformMatrix");
JsonValue position = e.get(JSON_POSITION);
if (transform != null) {
o.transform.set(transform.asFloatArray());
} else if (position != null) {
Vector3 pos = new Vector3(position.asFloatArray());
JsonValue euler = e.get("euler");
JsonValue quaternion = e.get("quaternion");
if (euler != null) {
Quaternion quat = new Quaternion();
quat.setEulerAngles(euler.getFloat(1), euler.getFloat(0), euler.getFloat(2));
o.transform.set(pos, quat, new Vector3(1.0f, 1.0f, 1.0f));
} else if (quaternion != null) {
Quaternion quat = new Quaternion(quaternion.getFloat(0), quaternion.getFloat(1), quaternion.getFloat(2), quaternion.getFloat(3));
o.transform.set(pos, quat, new Vector3(1.0f, 1.0f, 1.0f));
} else {
o.transform.idt();
o.transform.trn(pos);
}
JsonValue scale = e.get("scale");
if (scale != null) {
o.transform.scale(scale.getFloat(0), scale.getFloat(1), scale.getFloat(2));
}
} else {
o.transform.idt();
}
}
/**
* Parses the velocity information of the current game object. Has to be
* called after parse transform.
*
* @param o
* The game object.
* @param e
* The json value of the game object.
*/
private void parseVelocity(GameObject o, JsonValue e) {
JsonValue sinusX = e.get("sinus_x");
JsonValue sinusY = e.get("sinus_y");
JsonValue sinusZ = e.get("sinus_z");
JsonValue angular = e.get("angular_velocity");
if ((sinusX != null || sinusY != null || sinusZ != null) && angular != null) {
// sin + rot
SinusRotationMover mover = new SinusRotationMover(o);
if (sinusX != null) {
mover.X.set(sinusX.getFloat(0) * 0.01f, sinusX.getFloat(1), sinusX.getFloat(2));
}
if (sinusY != null) {
mover.Y.set(sinusY.getFloat(0) * 0.01f, sinusY.getFloat(1), sinusY.getFloat(2));
}
if (sinusZ != null) {
mover.Z.set(sinusZ.getFloat(0) * 0.01f, sinusZ.getFloat(1), sinusZ.getFloat(2));
}
mover.setRotation(new Vector3(angular.getFloat(0), angular.getFloat(1), angular.getFloat(2)));
o.setMover(mover);
} else if ((sinusX != null || sinusY != null || sinusZ != null) && angular == null) {
// sin
SinusMover mover = new SinusMover(o);
if (sinusX != null) {
mover.X.set(sinusX.getFloat(0) * 0.01f, sinusX.getFloat(1), sinusX.getFloat(2));
}
if (sinusY != null) {
mover.Y.set(sinusY.getFloat(0) * 0.01f, sinusY.getFloat(1), sinusY.getFloat(2));
}
if (sinusZ != null) {
mover.Z.set(sinusZ.getFloat(0) * 0.01f, sinusZ.getFloat(1), sinusZ.getFloat(2));
}
o.setMover(mover);
} else if (angular != null) {
// rot
RotationMover mover = new RotationMover(o);
mover.setRotation(new Vector3(angular.getFloat(0), angular.getFloat(1), angular.getFloat(2)));
o.setMover(mover);
}
}
/**
* Parses the collectibles in the level file.
*
* @return CollectibleManager
*/
private CollectibleManager parseCollectibles() {
List<Collectible> upgradeList = new ArrayList<Collectible>();
JsonValue upgrades = json.get("upgrades");
if (upgrades == null) {
return new CollectibleManager();
}
int len = upgrades.size;
JsonValue jsonUpgrade;
for (int i = 0; i < len; i++) {
jsonUpgrade = upgrades.get(i);
JsonValue upgradeType = jsonUpgrade.get("type");
if (upgradeType != null) {
String type = upgradeType.asString();
Collectible c = null;
String ref = jsonUpgrade.getString("ref");
if ("ChangeTimeUpgrade".equals(type)) {
c = new ChangeTimeUpgrade(models.get(ref), jsonUpgrade.get("time").asInt());
} else if (type.equals("ChangePointsUpgrade")) {
c = new ChangePointsUpgrade(models.get(ref), jsonUpgrade.get("points").asInt());
} else if (type.equals(TemporarySpeedUpgrade.TYPE)) {
float maxSpeedupFactor = jsonUpgrade.get("maxSpeedupFactor").asFloat();
int speedupTimeInMilliSeconds = jsonUpgrade.get("speedupTimeInMilliSeconds").asInt();
int maxSpeedTimeInMilliSeconds = jsonUpgrade.get("maxSpeedTimeInMilliSeconds").asInt();
int slowdownTimeInMilliSeconds = jsonUpgrade.get("slowdownTimeInMilliSeconds").asInt();
SpeedUpgradeEffect effect = new SpeedUpgradeEffect(maxSpeedupFactor, speedupTimeInMilliSeconds, maxSpeedTimeInMilliSeconds, slowdownTimeInMilliSeconds);
c = new TemporarySpeedUpgrade(models.get(ref), effect);
} else if ("ResizeGatesUpgrade".equals(type)) {
JsonValue jsonResizeFactor = jsonUpgrade.get("resizeFactor");
Vector3 scale = new Vector3(jsonResizeFactor.getFloat(0), jsonResizeFactor.getFloat(1), jsonResizeFactor.getFloat(2));
c = new ResizeGatesUpgrade(models.get(ref), scale);
} else if ("ChangeSteeringUpgrade".equals(type)) {
c = new ChangeSteeringUpgrade(models.get(ref), jsonUpgrade.get("roll").asFloat(), jsonUpgrade.get("azimuth").asFloat(), jsonUpgrade.get("duration").asFloat());
}
if (c != null) {
parseInformation(c, jsonUpgrade);
parseTransform(c, jsonUpgrade);
parseVelocity(c, jsonUpgrade);
upgradeList.add(c);
} else {
Gdx.app.log("LevelLoader.parseUpgrades", "Upgrade type not found.");
}
}
}
return new CollectibleManager(upgradeList);
}
/**
* Parses the environments
*/
private Map<String, Environment> parseEnvironments() {
Environment ambientEnvironment = new Environment();
Environment lightingEnvironment = new Environment();
Map<String, Environment> envList = new HashMap<String, Environment>();
JsonValue environments = json.get("environments");
if (environments == null) {
// Set default environments if they aren't specified
ambientEnvironment.set(new ColorAttribute(ColorAttribute.AmbientLight, 1f, 1f, 1f, 1f));
lightingEnvironment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.3f, 0.3f, 0.3f, 1f));
lightingEnvironment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, 0.8f, -0.2f));
envList.put("ambient", ambientEnvironment);
envList.put("lighting", lightingEnvironment);
return envList;
}
JsonValue ambient = environments.get("ambient");
ambientEnvironment.set(new ColorAttribute(ColorAttribute.AmbientLight, ambient.getFloat(0), ambient.getFloat(1), ambient.getFloat(2), ambient.getFloat(3)));
JsonValue lighting = environments.get("lighting");
if (lighting != null) {
ambient = lighting.get("ambientLight");
if (ambient != null) {
lightingEnvironment.set(new ColorAttribute(ColorAttribute.AmbientLight, ambient.getFloat(0), ambient.getFloat(1), ambient.getFloat(2), ambient.getFloat(3)));
}
JsonValue directionalLights = lighting.get("directionalLights");
if (directionalLights != null) {
for (int i = 0; i < directionalLights.size; i++) {
JsonValue colorValue = directionalLights.get(i).get("color");
JsonValue directionValue = directionalLights.get(i).get("direction");
Color color = new Color(colorValue.getFloat(0), colorValue.getFloat(1), colorValue.getFloat(2), colorValue.getFloat(3));
Vector3 direction = new Vector3(directionValue.getFloat(0), directionValue.getFloat(1), directionValue.getFloat(2));
lightingEnvironment.add(new DirectionalLight().set(color, direction));
}
}
JsonValue pointLights = lighting.get("pointLights");
if (pointLights != null) {
for (int i = 0; i < pointLights.size; i++) {
JsonValue colorValue = pointLights.get(i).get("color");
JsonValue positionValue = pointLights.get(i).get(JSON_POSITION);
JsonValue intensityValue = pointLights.get(i).get("intensity");
Color color = new Color(colorValue.getFloat(0), colorValue.getFloat(1), colorValue.getFloat(2), colorValue.getFloat(3));
Vector3 position = new Vector3(positionValue.getFloat(0), positionValue.getFloat(1), positionValue.getFloat(2));
lightingEnvironment.add(new PointLight().set(color, position, intensityValue.asFloat()));
}
}
}
envList.put("ambient", ambientEnvironment);
envList.put("lighting", lightingEnvironment);
return envList;
}
/**
* Parses the json file.
*/
private void parseJson() {
if (json == null) {
json = reader.parse(file);
}
}
/**
* Parses the dependencies.
*/
private void parseDependencies() {
if (dependencies == null) {
parseJson();
dependencies = new HashMap<String, String>();
JsonValue e;
JsonValue jsonDependencies = json.get("dependencies");
int len = jsonDependencies.size;
for (int i = 0; i < len; i++) {
e = jsonDependencies.get(i);
String fileName = MODEL_FOLDER + e.asString() + "/" + e.asString();
dependencies.put(e.name(), fileName);
}
}
}
/**
* Parses the components.
* <p>
* Components are all the decoration stuff in the level.
*/
private void parseComponents() {
parseJson();
components.clear();
GameObject o;
for (JsonValue e : json.get("components")) {
String ref = e.getString("ref");
long timeStart = System.currentTimeMillis();
o = new GameObject(models.get(ref), ref);
Gdx.app.log("loadGameObject", String.valueOf(System.currentTimeMillis() - timeStart));
if (e.has("environment")) {
o.environment = environments.get(e.getString("environment"));
}
parseInformation(o, e);
parseTransform(o, e);
parseVelocity(o, e);
components.put(o.getId(), o);
}
}
private GameModel dependencyFor(String ref) {
return manager.get(dependencies.get(ref), GameModel.class);
}
private void deinit() {
level = null;
json = null;
components.clear();
dependencies = null;
file = null;
parameter = null;
manager = null;
// Models are needed for the features, dont clear them!
// models.clear();
}
@Override
public Level loadSync(AssetManager manager, String fileName, FileHandle file, LevelParameters parameter) {
Gdx.app.log("LevelLoader.loadSync", "LOADING.");
Level l = level;
deinit();
return l;
}
@Override
public void loadAsync(AssetManager manager, String fileName, FileHandle file, LevelParameters parameter) {
Gdx.app.log("LevelLoader.loadAsync", "LOADING.");
this.level = null;
this.manager = manager;
loadLevelfromJson();
}
@Override
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, LevelParameters parameter) {
this.file = file;
this.parameter = parameter;
parseDependencies();
Array<AssetDescriptor> deps = new Array<AssetDescriptor>();
for (String name : dependencies.values()) {
// Gdx.app.log("LevelLoader.getDependencies", name);
deps.add(new AssetDescriptor<GameModel>(name, GameModel.class));
}
return deps;
}
static public class LevelParameters extends AssetLoaderParameters<Level> {
}
}