/*
* Copyright (C) 2016 eccentric_nz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package me.eccentric_nz.TARDIS.ARS;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import me.eccentric_nz.TARDIS.JSON.JSONArray;
import me.eccentric_nz.TARDIS.TARDIS;
import me.eccentric_nz.TARDIS.advanced.TARDISCircuitChecker;
import me.eccentric_nz.TARDIS.advanced.TARDISCircuitDamager;
import me.eccentric_nz.TARDIS.database.QueryFactory;
import me.eccentric_nz.TARDIS.database.ResultSetARS;
import me.eccentric_nz.TARDIS.database.ResultSetCondenser;
import me.eccentric_nz.TARDIS.database.ResultSetPlayerPrefs;
import me.eccentric_nz.TARDIS.database.ResultSetTardis;
import me.eccentric_nz.TARDIS.database.ResultSetTravellers;
import me.eccentric_nz.TARDIS.enumeration.DIFFICULTY;
import me.eccentric_nz.TARDIS.enumeration.DISK_CIRCUIT;
import me.eccentric_nz.TARDIS.rooms.TARDISWalls.Pair;
import me.eccentric_nz.TARDIS.utility.TARDISMessage;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
/**
* The architectural reconfiguration system is a component of the Doctor's
* TARDIS in the shape of a tree that, according to the Eleventh Doctor,
* "reconstructs the particles according to your needs." It is basically "a
* machine that makes machines," perhaps somewhat like a 3D printer. It is,
* according to Gregor Van Baalen's scanner, "more valuable than the total sum
* of any currency.
*
* @author eccentric_nz
*/
public class TARDISARSMethods {
public final TARDIS plugin;
public final HashMap<UUID, Integer> scroll_start = new HashMap<UUID, Integer>();
public final HashMap<UUID, Integer> selected_slot = new HashMap<UUID, Integer>();
public final HashMap<UUID, TARDISARSSaveData> save_map_data = new HashMap<UUID, TARDISARSSaveData>();
public final HashMap<UUID, TARDISARSMapData> map_data = new HashMap<UUID, TARDISARSMapData>();
public final String[] levels = new String[]{"Bottom level", "Main level", "Top level"};
public final List<Material> consoleBlocks = Arrays.asList(Material.IRON_BLOCK, Material.GOLD_BLOCK, Material.DIAMOND_BLOCK, Material.EMERALD_BLOCK, Material.REDSTONE_BLOCK, Material.COAL_BLOCK, Material.QUARTZ_BLOCK, Material.LAPIS_BLOCK, Material.BOOKSHELF, Material.STAINED_CLAY, Material.DRAGON_EGG, Material.PRISMARINE);
public final HashMap<UUID, Integer> ids = new HashMap<UUID, Integer>();
public final List<UUID> hasLoadedMap = new ArrayList<UUID>();
public TARDISARSMethods(TARDIS plugin) {
this.plugin = plugin;
}
/**
* Saves the current ARS data to the database.
*
* @param uuid the UUID of the player who is using the ARS GUI
*/
public void saveAll(UUID uuid) {
TARDISARSMapData md = map_data.get(uuid);
JSONArray json = new JSONArray(md.getData());
HashMap<String, Object> set = new HashMap<String, Object>();
set.put("ars_x_east", md.getE());
set.put("ars_z_south", md.getS());
set.put("ars_y_layer", md.getY());
set.put("json", json.toString());
HashMap<String, Object> wherea = new HashMap<String, Object>();
wherea.put("ars_id", md.getId());
new QueryFactory(plugin).doUpdate("ars", set, wherea);
}
/**
* Saves the current ARS data to the database.
*
* @param uuid the UUID of the player who is using the ARS GUI
*/
public void revert(UUID uuid) {
TARDISARSSaveData sd = save_map_data.get(uuid);
JSONArray json = new JSONArray(sd.getData());
final HashMap<String, Object> set = new HashMap<String, Object>();
set.put("json", json.toString());
final HashMap<String, Object> wherea = new HashMap<String, Object>();
wherea.put("ars_id", sd.getId());
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
public void run() {
new QueryFactory(plugin).doUpdate("ars", set, wherea);
}
}, 6L);
}
/**
* Converts the JSON data stored in the database to a 3D array.
*
* @param js the JSON from the database
* @return a 3D array of ints
*/
public static int[][][] getGridFromJSON(String js) {
int[][][] grid = new int[3][9][9];
JSONArray json = new JSONArray(js);
for (int y = 0; y < 3; y++) {
JSONArray jsonx = json.getJSONArray(y);
for (int x = 0; x < 9; x++) {
JSONArray jsonz = jsonx.getJSONArray(x);
for (int z = 0; z < 9; z++) {
if (jsonz.getInt(z) == 46) {
grid[y][x][z] = 1;
} else {
grid[y][x][z] = jsonz.getInt(z);
}
}
}
}
return grid;
}
/**
* Gets a 5x5 2D slice from a 3D array
*
* @param layer the level to to get
* @param x the x position of the slice
* @param z the z position of the slice
* @return a slice of the larger array
*/
public int[][] sliceGrid(int[][] layer, int x, int z) {
int[][] slice = new int[5][5];
int indexx = 0, indexz = 0;
for (int xx = x; xx < (x + 5); xx++) {
for (int zz = z; zz < (z + 5); zz++) {
slice[indexx][indexz] = layer[xx][zz];
indexz++;
}
indexz = 0;
indexx++;
}
return slice;
}
/**
* Sets an ItemStack to the specified inventory slot updating the display
* name and removing any lore.
*
* @param inv the inventory to update
* @param slot the slot number to update
* @param id the item id to set the item stack to
* @param room the room type associated with the id
* @param uuid the player using the GUI
* @param update whether to update the grid display
*/
@SuppressWarnings("deprecation")
public void setSlot(Inventory inv, int slot, int id, String room, UUID uuid, boolean update) {
ItemStack is = new ItemStack(id, 1);
ItemMeta im = is.getItemMeta();
im.setDisplayName(room);
if (!room.equals("Empty slot")) {
String config_path = TARDISARS.ARSFor(room).getActualName();
List<String> lore = Arrays.asList("Cost: " + plugin.getRoomsConfig().getInt("rooms." + config_path + ".cost"));
im.setLore(lore);
} else {
im.setLore(null);
}
is.setItemMeta(im);
inv.setItem(slot, is);
if (update) {
updateGrid(uuid, slot, id);
}
}
/**
* Sets an ItemStack to the specified inventory slot.
*
* @param inv the inventory to update
* @param slot the slot number to update
* @param is the item stack to set
* @param uuid the player using the GUI
* @param update whether to update the grid display
*/
@SuppressWarnings("deprecation")
public void setSlot(Inventory inv, int slot, ItemStack is, UUID uuid, boolean update) {
inv.setItem(slot, is);
int id = is.getTypeId();
if (update) {
updateGrid(uuid, slot, id);
}
}
/**
* Get the coordinates of the clicked slot in relation to the ARS map.
*
* @param slot the slot that was clicked
* @param md an instance of the TARDISARSMapData class from which to
* retrieve the map offset
* @return an array of ints
*/
public int[] getCoords(int slot, TARDISARSMapData md) {
int[] coords = new int[2];
if (slot <= 8) {
coords[0] = (slot - 4) + md.getE();
coords[1] = md.getS();
}
if (slot > 8 && slot <= 17) {
coords[0] = (slot - 13) + md.getE();
coords[1] = md.getS() + 1;
}
if (slot > 17 && slot <= 26) {
coords[0] = (slot - 22) + md.getE();
coords[1] = md.getS() + 2;
}
if (slot > 26 && slot <= 35) {
coords[0] = (slot - 31) + md.getE();
coords[1] = md.getS() + 3;
}
if (slot > 35 && slot <= 44) {
coords[0] = (slot - 40) + md.getE();
coords[1] = md.getS() + 4;
}
return coords;
}
/**
* Saves the current map to the TARDISARSMapData instance associated with
* the player using the GUI.
*
* @param uuid the UUID of the player using the GUI
* @param slot the slot that was clicked
* @param id the type id of the block in the slot
*/
public void updateGrid(UUID uuid, int slot, int id) {
TARDISARSMapData md = map_data.get(uuid);
int[][][] grid = md.getData();
int yy = md.getY();
int[] coords = getCoords(slot, md);
int newx = coords[0];
int newz = coords[1];
if (id == 24) {
if (yy < 2) {
grid[yy + 1][newx][newz] = id;
}
} else if (id == 48) {
if (yy > 0) {
grid[yy - 1][newx][newz] = id;
}
}
grid[yy][newx][newz] = id;
md.setData(grid);
map_data.put(uuid, md);
}
/**
* Sets the lore of the ItemStack in the specified slot.
*
* @param inv the inventory to update
* @param slot the slot to update
* @param str the lore to set
*/
public void setLore(Inventory inv, int slot, String str) {
List<String> lore = (str != null) ? Arrays.asList(str) : null;
ItemStack is = inv.getItem(slot);
ItemMeta im = is.getItemMeta();
im.setLore(lore);
is.setItemMeta(im);
}
/**
* Switches the indicator block for the map level.
*
* @param inv the inventory to update
* @param slot the slot to update
* @param uuid the UUID of the player using the GUI
*/
public void switchLevel(Inventory inv, int slot, UUID uuid) {
TARDISARSMapData md = map_data.get(uuid);
for (int i = 27; i < 30; i++) {
byte data = 0;
if (i == slot) {
data = 4;
md.setY(i - 27);
map_data.put(uuid, md);
}
ItemStack is = new ItemStack(Material.WOOL, 1, data);
ItemMeta im = is.getItemMeta();
im.setDisplayName(levels[i - 27]);
is.setItemMeta(im);
setSlot(inv, i, is, uuid, false);
}
}
/**
* Closes the inventory.
*
* @param p the player using the GUI
*/
public void close(final Player p) {
final UUID uuid = p.getUniqueId();
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
public void run() {
if (scroll_start.containsKey(uuid)) {
scroll_start.remove(uuid);
}
if (selected_slot.containsKey(uuid)) {
selected_slot.remove(uuid);
}
if (hasLoadedMap.contains(uuid)) {
hasLoadedMap.remove(uuid);
}
if (map_data.containsKey(uuid)) {
if (playerIsOwner(p.getUniqueId().toString(), ids.get(uuid))) {
saveAll(uuid);
TARDISARSProcessor tap = new TARDISARSProcessor(plugin, ids.get(uuid));
boolean changed = tap.compare3DArray(save_map_data.get(uuid).getData(), map_data.get(uuid).getData());
if (changed && tap.checkCosts(tap.getChanged(), tap.getJettison())) {
TARDISMessage.send(p, "ARS_START");
// do all jettisons first
if (tap.getJettison().size() > 0) {
TARDISMessage.send(p, "ROOM_JETT", String.format("%d", tap.getJettison().size()));
long del = 5L;
for (Map.Entry<TARDISARSJettison, ARS> map : tap.getJettison().entrySet()) {
TARDISARSJettisonRunnable jr = new TARDISARSJettisonRunnable(plugin, map.getKey(), map.getValue(), ids.get(uuid), p);
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, jr, del);
del += 5L;
}
}
// one every 40 seconds at default room_speed
long period = 2400L * (Math.round(20 / plugin.getConfig().getDouble("growth.room_speed")));
long delay = 20L;
for (Map.Entry<TARDISARSSlot, ARS> map : tap.getChanged().entrySet()) {
TARDISARSRunnable ar = new TARDISARSRunnable(plugin, map.getKey(), map.getValue(), p, ids.get(uuid));
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, ar, delay);
delay += period;
}
// damage the circuit if configured
if (plugin.getConfig().getBoolean("circuits.damage") && !plugin.getDifficulty().equals(DIFFICULTY.EASY) && plugin.getConfig().getInt("circuits.uses.ars") > 0) {
// get the id of the TARDIS this player is in
int id = plugin.getTardisAPI().getIdOfTARDISPlayerIsIn(uuid);
TARDISCircuitChecker tcc = new TARDISCircuitChecker(plugin, id);
tcc.getCircuits();
// decrement uses
int uses_left = tcc.getArsUses();
new TARDISCircuitDamager(plugin, DISK_CIRCUIT.ARS, uses_left, id, p).damage();
}
} else {
// reset map to the previous version
revert(uuid);
if (tap.getError().equals("ARS_LIMIT")) {
TARDISMessage.send(p, tap.getError(), plugin.getConfig().getString("growth.ars_limit"));
} else {
TARDISMessage.send(p, tap.getError());
}
}
} else {
TARDISMessage.send(p, "ROOM_ONLY_TL");
revert(uuid);
}
map_data.remove(uuid);
save_map_data.remove(uuid);
ids.remove(uuid);
}
p.closeInventory();
}
}, 1L);
}
/**
* Loads the map from the database ready for use in the GUI.
*
* @param inv the inventory to load the map into
* @param uuid the UUID of the player using the GUI
*/
public void loadMap(Inventory inv, UUID uuid) {
if (inv.getItem(10).getItemMeta().hasLore()) {
setLore(inv, 10, "Map already loaded!");
return;
}
setLore(inv, 10, "Loading...");
HashMap<String, Object> where = new HashMap<String, Object>();
where.put("tardis_id", ids.get(uuid));
ResultSetARS rs = new ResultSetARS(plugin, where);
if (rs.resultSet()) {
TARDISARSSaveData sd = new TARDISARSSaveData();
TARDISARSMapData md = new TARDISARSMapData();
int[][][] json = getGridFromJSON(rs.getJson());
int[][][] json2 = getGridFromJSON(rs.getJson());
sd.setData(json);
sd.setId(rs.getId());
md.setData(json2);
md.setE(rs.getEast());
md.setS(rs.getSouth());
md.setY(rs.getLayer());
md.setId(rs.getId());
save_map_data.put(uuid, sd);
map_data.put(uuid, md);
setMap(rs.getLayer(), rs.getEast(), rs.getSouth(), uuid, inv);
saveAll(uuid);
hasLoadedMap.add(uuid);
setLore(inv, 10, "Map LOADED");
switchLevel(inv, (27 + rs.getLayer()), uuid);
}
}
public void setMap(int ul, int ue, int us, UUID uuid, Inventory inv) {
TARDISARSMapData data = map_data.get(uuid);
int[][][] grid = data.getData();
int[][] layer = grid[ul];
int[][] map = sliceGrid(layer, ue, us);
int indexx = 0, indexz = 0;
for (int i = 4; i < 9; i++) {
for (int j = 0; j < 5; j++) {
int slot = i + (j * 9);
int id = map[indexx][indexz];
String name = TARDISARS.ARSFor(id).getDescriptiveName();
setSlot(inv, slot, id, name, uuid, false);
indexz++;
}
indexz = 0;
indexx++;
}
}
/**
* Move the map to a new position.
*
* @param uuid the UUID of the player using the GUI
* @param inv the inventory to update
* @param slot the slot number to update
*/
public void moveMap(UUID uuid, Inventory inv, int slot) {
if (map_data.containsKey(uuid)) {
TARDISARSMapData md = map_data.get(uuid);
int ue, us;
switch (slot) {
case 1:
ue = md.getE();
us = ((md.getS() + 1) < 5) ? md.getS() + 1 : md.getS();
break;
case 9:
ue = ((md.getE() + 1) < 5) ? md.getE() + 1 : md.getE();
us = md.getS();
break;
case 11:
ue = ((md.getE() - 1) >= 0) ? md.getE() - 1 : md.getE();
us = md.getS();
break;
default:
ue = md.getE();
us = ((md.getS() - 1) >= 0) ? md.getS() - 1 : md.getS();
break;
}
setMap(md.getY(), ue, us, uuid, inv);
setLore(inv, slot, null);
md.setE(ue);
md.setS(us);
map_data.put(uuid, md);
} else {
setLore(inv, slot, "Load map data first!");
}
}
/**
* Checks whether a player has condensed the required blocks to grow the
* room.
*
* @param uuid the UUID of the player to check for
* @param room the room to check
* @param id the TARDIS id
* @return true or false
*/
public boolean hasCondensables(String uuid, String room, int id) {
boolean hasRequired = true;
HashMap<String, Integer> roomBlocks = plugin.getBuildKeeper().getRoomBlockCounts().get(room);
String wall = "ORANGE_WOOL";
String floor = "LIGHT_GREY_WOOL";
HashMap<String, Object> wherepp = new HashMap<String, Object>();
boolean hasPrefs = false;
wherepp.put("uuid", uuid);
ResultSetPlayerPrefs rsp = new ResultSetPlayerPrefs(plugin, wherepp);
if (rsp.resultSet()) {
hasPrefs = true;
wall = rsp.getWall();
floor = rsp.getFloor();
}
HashMap<String, Integer> item_counts = new HashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : roomBlocks.entrySet()) {
String[] block_data = entry.getKey().split(":");
String bid = block_data[0];
String mat;
String bkey;
if (hasPrefs && block_data.length == 2 && (block_data[1].equals("1") || block_data[1].equals("8"))) {
mat = (block_data[1].equals("1")) ? wall : floor;
Pair iddata = plugin.getTardisWalls().blocks.get(mat);
bkey = iddata.getType().toString();
} else {
bkey = bid;
}
int tmp = Math.round((entry.getValue() / 100.0F) * plugin.getConfig().getInt("growth.rooms_condenser_percent"));
int required = (tmp > 0) ? tmp : 1;
if (item_counts.containsKey(bkey)) {
item_counts.put(bkey, (item_counts.get(bkey) + required));
} else {
item_counts.put(bkey, required);
}
}
for (Map.Entry<String, Integer> map : item_counts.entrySet()) {
HashMap<String, Object> wherec = new HashMap<String, Object>();
wherec.put("tardis_id", id);
wherec.put("block_data", map.getKey());
ResultSetCondenser rsc = new ResultSetCondenser(plugin, wherec);
if (rsc.resultSet()) {
if (rsc.getBlock_count() < map.getValue()) {
hasRequired = false;
}
} else {
hasRequired = false;
}
}
return hasRequired;
}
public int getTardisId(String uuid) {
int id = 0;
HashMap<String, Object> where = new HashMap<String, Object>();
where.put("uuid", uuid);
ResultSetTravellers rs = new ResultSetTravellers(plugin, where, false);
if (rs.resultSet()) {
id = rs.getTardis_id();
}
return id;
}
public boolean hasRenderer(UUID uuid) {
HashMap<String, Object> where = new HashMap<String, Object>();
where.put("tardis_id", ids.get(uuid));
ResultSetTardis rs = new ResultSetTardis(plugin, where, "", false, 0);
if (rs.resultSet()) {
return !rs.getTardis().getRenderer().isEmpty();
}
return false;
}
public boolean checkSlotForConsole(Inventory inv, int slot) {
Material m = inv.getItem(slot).getType();
return (consoleBlocks.contains(m));
}
public boolean playerIsOwner(String uuid, int id) {
HashMap<String, Object> where = new HashMap<String, Object>();
where.put("tardis_id", id);
where.put("uuid", uuid);
ResultSetTardis rs = new ResultSetTardis(plugin, where, "", false, 0);
return rs.resultSet();
}
}