package com.pointcliki.dizgruntled.map;
import java.util.TreeSet;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import com.pointcliki.core.EntityQuery;
import com.pointcliki.core.PointClikiGame;
import com.pointcliki.core.ISavable;
import com.pointcliki.dizgruntled.GruntzGame;
import com.pointcliki.dizgruntled.GruntzResourceManager.ToggleInfo;
import com.pointcliki.dizgruntled.Logic;
import com.pointcliki.dizgruntled.LogicFactory;
import com.pointcliki.dizgruntled.logic.Effect;
import com.pointcliki.dizgruntled.rez.MonolithWWD;
import com.pointcliki.dizgruntled.utils.SFX;
import com.pointcliki.event.Dispatcher;
import com.pointcliki.event.IEvent;
import com.pointcliki.grid.GridCoordinate;
import com.pointcliki.grid.GridManager;
import com.pointcliki.grid.GridManager.GridEvent;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* @author Hugheth
* @since alpha 1
*/
public class Map implements ISavable {
/**
* Serial key
*/
private static final long serialVersionUID = 1L;
private MapManager fMapManager;
private int fHeight;
private int fWidth;
private TileSet[] fTilesets;
private byte[][] fTiles;
private String fName;
private String fAuthor;
private String fQuirks;
private String fType;
private String fFog;
private EntityQuery<Logic> fLogics;
private EntityQuery<Logic> fSelected;
private boolean fEditing;
protected MapDispatcher fDispatcher;
private boolean fInitRedPyramids = false;
private boolean fGiantRockBreaking;
public static final String[] WORLDS = new String[] {"Rocky Roadz", "Gruntziclez", "Tropicz", "Sweetz", "Rollerz", "Kitchenz", "Mini Masterz", "Space", "Virtual Reality"};
public static final String[] AREAS = new String[] {"AREA1", "AREA2", "AREA3", "AREA4", "AREA5", "AREA6", "AREA7", "AREA8", "mod::virtualreality"};
/**
* Create a new blank map
* @param world
* @param width
* @param height
*/
public Map(MapManager m, String world, int width, int height) {
fMapManager = m;
fWidth = width;
fHeight = height;
fTilesets = new TileSet[] {GruntzGame.resourceManager().tileset(world), GruntzGame.resourceManager().tileset("Standard")};
fTiles = new byte[2][width * height];
int i = width * height;
while (i-- > 0) fTiles[0][i] = (byte) Math.floor(Math.pow(Math.random(), 4) * 8 + 1);
fName = "New Level";
fAuthor = "author";
fQuirks = "quirks";
fType = "Questz";
fFog = "No Fog";
fLogics = new EntityQuery<Logic>();
fSelected = new EntityQuery<Logic>();
fDispatcher = new MapDispatcher();
}
public Map(MapManager m, JSONObject map) {
fMapManager = m;
String[] size = map.optString("size").split(" ");
fWidth = Integer.parseInt(size[0]);
fHeight = Integer.parseInt(size[1]);
int area = fWidth * fHeight;
boolean valid = true;
String version = map.optString("version");
if (!version.equals("2.5.1") && !version.equals("2.6")) {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setAlwaysOnTop(true);
JOptionPane.showMessageDialog(frame, "Sorry, the map selected isn't compatible with this version of Dizgruntled", "Level Not Compatible", 0);
frame.dispose();
valid = false;
}
fName = map.optString("name");
fAuthor = map.optString("author");
fQuirks = map.optString("quirks");
fType = map.optString("type");
fFog = map.optString("fog");
JSONArray tilesets = map.optJSONArray("tilesets");
fTilesets = new TileSet[tilesets.length()];
for (int i = 0; i < tilesets.length(); i++) fTilesets[i] = GruntzGame.resourceManager().tileset(tilesets.optString(i));
LogicFactory f = new LogicFactory();
fLogics = new EntityQuery<Logic>();
if (valid) {
JSONArray logics = map.optJSONArray("logics");
for (int i = 0; i < logics.length(); i++) fLogics.add(f.createFromJSON(logics.optJSONObject(i)));
}
fTiles = new byte[tilesets.length()][area];
if (valid) {
for (int j = 0; j < fTilesets.length; j++) {
for (int i = 0; i < area; i++) {
try {
fTiles[j] = Base64.decode(map.optJSONArray("tiles").optString(j));
} catch (Base64DecodingException e) {
System.err.println(e.getMessage());
}
}
}
}
fSelected = new EntityQuery<Logic>();
fDispatcher = new MapDispatcher();
}
public Map(MapManager m, MonolithWWD map) {
fMapManager = m;
fWidth = map.width();
fHeight = map.height();
int area = fWidth * fHeight;
fName = "WWD MAP";
fAuthor = "Monolith";
fQuirks = "abc";
fType = "Questz";
fFog = "No Fog";
fTilesets = new TileSet[]{GruntzGame.resourceManager().tileset(WORLDS[map.world() - 1]), GruntzGame.resourceManager().tileset("Standard")};
fTiles = new byte[2][area];
fLogics = new EntityQuery<Logic>();
for (Logic l: map.logics()) fLogics.add(l);
fSelected = new EntityQuery<Logic>();
for (int i = 0; i < area; i++) fTiles[0][i] = fTilesets[0].wapValue(map.tile(i));
for (int i = 0; i < area; i++) fTiles[1][i] = fTilesets[1].wapValue(map.tile(i));
fDispatcher = new MapDispatcher();
}
public MapManager mapManager() {
return fMapManager;
}
public JSONObject exportToJSON() throws JSONException {
JSONObject o = new JSONObject();
JSONArray logics = new JSONArray();
for (Logic l: fLogics) {
JSONObject out = l.exportToJSON();
if (out != null) logics.put(out);
}
o.put("name", fName);
o.put("author", fAuthor);
o.put("size", fWidth + " " + fHeight);
o.put("quirks", fQuirks);
o.put("type", fType);
o.put("fog", fFog);
o.put("version", "2.6");
String[] tilesets = new String[fTilesets.length];
int i = 0;
JSONArray tiles = new JSONArray();
for (TileSet s: fTilesets) {
tilesets[i] = s.name();
tiles.put(Base64.encode(fTiles[i]));
i++;
}
o.put("tilesets", tilesets);
o.put("tiles", tiles);
o.put("logics", logics);
return o;
}
public void placeLogic(Logic log) {
fLogics.add(log);
fDispatcher.update(log);
}
public void selectLogic(Logic log) {
if (log.selected()) return;
if (!PointClikiGame.inputManager().isCtrlPressed()) for (Logic l: fSelected) deselectLogic(l);
fSelected.add(log);
}
public void deselectLogic(Logic logic) {
if (!logic.selected()) return;
logic.deselect();
fSelected.remove(logic);
}
public int tilesetCount() {
return fTilesets.length;
}
public TileSet tileset(int i) {
return fTilesets[i];
}
public int tilesetIndex(TileSet s) {
for (int i = 0; i < fTilesets.length; i++) if (fTilesets[i].equals(s)) return i;
return -1;
}
public int tile(int tileset, int x, int y) {
if (x < 0 || x >= fWidth || y < 0 || y >= fHeight) return -1;
return fTiles[tileset][x + y * fWidth] & 0xFF;
}
public void tile(int tileset, int x, int y, int tileid) {
if (x < 0 || x >= fWidth || y < 0 || y >= fHeight) return;
if (tileset < 0 || tileset >= tilesetCount()) return;
fTiles[tileset][x + y * fWidth] = (byte) tileid;
}
public void tile(TileSet set, int x, int y, int tileid) {
tile(tilesetIndex(set), x, y, tileid);
}
public TreeSet<String> traits(GridCoordinate xy) {
TreeSet<String> tr = new TreeSet<String>();
for (int i = 0; i < fTilesets.length; i++) {
tr.addAll(fTilesets[i].traits(tile(i, xy.x(), xy.y())));
}
return tr;
}
public int tilesetForTrait(GridCoordinate xy, String trait) {
for (int i = 0; i < fTilesets.length; i++) {
if (fTilesets[i].traits(tile(i, xy.x(), xy.y())).contains(trait)) return i;
}
return -1;
}
public EntityQuery<Logic> logics() {
return fLogics;
}
public EntityQuery<Logic> selectedLogics() {
return fSelected;
}
public int width() {
return fWidth;
}
public int height() {
return fHeight;
}
public String name() {
return fName;
}
public String author() {
return fAuthor;
}
public String quirks() {
return fQuirks;
}
public String type() {
return fType;
}
public String fog() {
return fFog;
}
public void name(String n) {
fName = n;
}
public void author(String a) {
fAuthor = a;
}
public void quirks(String q) {
fQuirks = q;
}
public void type(String t) {
fType = t;
}
public void fog(String f) {
fFog = f;
}
public boolean editing() {
return fEditing;
}
public void editing(boolean edit) {
fEditing = true;
}
public boolean isValid(GridCoordinate xy) {
return xy.x() >= 0 && xy.y() >= 0 && xy.x() < fWidth && xy.y() < fHeight;
}
@Override
public ISavable snapshot() throws CloneNotSupportedException {
return null;
}
public void cleanup() {
for (Logic l: logics()) l.cleanup();
fSelected = null;
fLogics = null;
fTiles = null;
fTilesets = null;
}
public MapDispatcher dispatcher() {
return fDispatcher;
}
protected class MapDispatcher extends Dispatcher<IEvent> {
/**
* Serial key
*/
private static final long serialVersionUID = -6130764938267894552L;
public void update(Logic l) {
dispatchEvent("newlogic", new NewLogicEvent(l));
}
public void sfx(SFX s, int layer, GridCoordinate xy) {
dispatchEvent("newsfx", new NewSFXEvent(layer, s, xy));
}
}
public void unregisterLogic(Logic logic) {
fLogics.remove(logic);
fSelected.remove(logic);
}
public void markStatefulTile(GridCoordinate fTile) {
// TODO Mark tile as stateful
}
public void sfx(int layer, String animation, String graphics, String sound, GridCoordinate xy) {
SFX s = new SFX(animation, graphics, sound, xy);
fDispatcher.sfx(s, layer, xy);
}
public boolean tileToggle(GridCoordinate xy) {
boolean found = false;
// Find a tile that will toggle
for (int i = 0; i < fTilesets.length; i++) {
String type = fTilesets[i].type(tile(i, xy.x(), xy.y()));
ToggleInfo info = GruntzGame.resourceManager().toggle(type);
if (info != null && info.tile() != null) {
// Check for giant rock
if (type.startsWith("GROCK") && !fGiantRockBreaking) {
if (type.equals("GROCK")) found = breakGiantRock(xy.subtract(GridCoordinate.NORTH_WEST));
else if (type.equals("GROCK_T")) found = breakGiantRock(xy.subtract(GridCoordinate.WEST));
else if (type.equals("GROCK_R")) found = breakGiantRock(xy.add(new GridCoordinate(-2, -1)));
else if (type.equals("GROCK_B")) found = breakGiantRock(xy.add(new GridCoordinate(-1, -2)));
else if (type.equals("GROCK_L")) found = breakGiantRock(xy.add(GridCoordinate.NORTH));
else if (type.equals("GROCK_TR")) found = breakGiantRock(xy.add(new GridCoordinate(-2, 0)));
else if (type.equals("GROCK_BR")) found = breakGiantRock(xy.add(new GridCoordinate(-2, -2)));
else if (type.equals("GROCK_BL")) found = breakGiantRock(xy.add(new GridCoordinate(0, -2)));
else if (type.equals("GROCK_TL")) found = breakGiantRock(xy);
} else {
int rep = fTilesets[i].tile(info.tile());
if (rep >= 0) tile(i, xy.x(), xy.y(), rep);
found = true;
}
// Do sfx
if (info.graphics("") != null && !fGiantRockBreaking) {
int layer = i;
if (info.layer() >= 0) layer = info.layer();
sfx(layer, info.animation(fTilesets[i].area()), info.graphics(fTilesets[i].area()), info.sound(fTilesets[i].area()), xy);
}
break;
}
}
if (found) mapManager().parent().manager(GridManager.class).dispatcher().dispatch(xy, "tilechange", new GridEvent());
return found;
}
public boolean breakGiantRock(GridCoordinate xy) {
fGiantRockBreaking = true;
boolean found = false;
for (int y = 0; y < 3; y++) {
for (int x = 0; x < 3; x++) {
found = found | tileToggle(xy.add(new GridCoordinate(x, y)));
}
}
fGiantRockBreaking = false;
return found;
}
public void populateRedPyramidLogics(Logic logic) {
if (fInitRedPyramids) return;
fInitRedPyramids = true;
GridManager gm = fMapManager.parent().manager(GridManager.class);
// Iterate through tiles to find red pyramids
for (int y = 0; y < fWidth; y++) {
for (int x = 0; x < fHeight; x++) {
GridCoordinate xy = new GridCoordinate(x, y);
if (traits(xy).contains("redPyramid")) {
// Check whether a logic has been placed here
Effect e = gm.getFirstEntityOfTypeAt(xy, Effect.class);
if (e == null) {
try {
Logic l = new Effect();
JSONObject json = logic.exportToJSON();
json.put("xy", (x * 32) + " " + (y * 32));
l.importFromJSON(json);
placeLogic(l);
} catch (Exception e2) {
System.err.println("Error creating virtual red pyramid logic at " + xy);
System.err.println(e2.getMessage());
}
}
}
}
}
}
public String area(GridCoordinate xy) {
for (int i = 0; i < fTilesets.length; i++) {
if (tile(i, xy.x(), xy.y()) > 0) {
return fTilesets[i].area();
}
}
return null;
}
}