Adds methods to Stargate for easier logging and less redundancy Loads the language loader in two parts to make it available while loading Adds a translated string to the reload-message Uses String.format to make long messages more readable Makes it possible to get strings directly from the backup language to make debugging easier
348 lines
14 KiB
Java
348 lines
14 KiB
Java
package net.knarcraft.stargate.portal;
|
|
|
|
import net.knarcraft.stargate.Stargate;
|
|
import net.knarcraft.stargate.container.BlockLocation;
|
|
import net.knarcraft.stargate.container.RelativeBlockVector;
|
|
import org.bukkit.Material;
|
|
|
|
import java.io.BufferedWriter;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* A gate describes the physical structure of a stargate
|
|
*
|
|
* <p>While the portal class represents a portal in space, the Gate class represents the physical gate/portal entrance.</p>
|
|
*/
|
|
public class Gate {
|
|
|
|
private final String filename;
|
|
private final GateLayout layout;
|
|
private final Map<Character, Material> characterMaterialMap;
|
|
//Gate materials
|
|
private final Material portalOpenBlock;
|
|
private final Material portalClosedBlock;
|
|
private final Material portalButton;
|
|
//Economy information
|
|
private final int useCost;
|
|
private final int createCost;
|
|
private final int destroyCost;
|
|
private final boolean toOwner;
|
|
|
|
/**
|
|
* Instantiates a new gate
|
|
*
|
|
* @param filename <p>The name of the gate file, including extension</p>
|
|
* @param layout <p>The gate layout defined in the gate file</p>
|
|
* @param characterMaterialMap <p>The material types the different layout characters represent</p>
|
|
* @param portalOpenBlock <p>The material to set the opening to when the portal is open</p>
|
|
* @param portalClosedBlock <p>The material to set the opening to when the portal is closed</p>
|
|
* @param portalButton <p>The material to use for the portal button</p>
|
|
* @param useCost <p>The cost of using a portal with this gate layout (-1 to disable)</p>
|
|
* @param createCost <p>The cost of creating a portal with this gate layout (-1 to disable)</p>
|
|
* @param destroyCost <p>The cost of destroying a portal with this gate layout (-1 to disable)</p>
|
|
* @param toOwner <p>Whether any payment should go to the owner of the gate, as opposed to just disappearing</p>
|
|
*/
|
|
public Gate(String filename, GateLayout layout, Map<Character, Material> characterMaterialMap, Material portalOpenBlock,
|
|
Material portalClosedBlock, Material portalButton, int useCost, int createCost, int destroyCost,
|
|
boolean toOwner) {
|
|
this.filename = filename;
|
|
this.layout = layout;
|
|
this.characterMaterialMap = characterMaterialMap;
|
|
this.portalOpenBlock = portalOpenBlock;
|
|
this.portalClosedBlock = portalClosedBlock;
|
|
this.portalButton = portalButton;
|
|
this.useCost = useCost;
|
|
this.createCost = createCost;
|
|
this.destroyCost = destroyCost;
|
|
this.toOwner = toOwner;
|
|
}
|
|
|
|
/**
|
|
* Gets this gate's layout
|
|
*
|
|
* @return <p>This gate's layout</p>
|
|
*/
|
|
public GateLayout getLayout() {
|
|
return layout;
|
|
}
|
|
|
|
/**
|
|
* Gets the material type used for this gate's control blocks
|
|
*
|
|
* @return <p>The material type used for control blocks</p>
|
|
*/
|
|
public Material getControlBlock() {
|
|
return characterMaterialMap.get(GateHandler.getControlBlockCharacter());
|
|
}
|
|
|
|
/**
|
|
* Gets the filename of this gate's file
|
|
*
|
|
* @return <p>The filename of this gate's file</p>
|
|
*/
|
|
public String getFilename() {
|
|
return filename;
|
|
}
|
|
|
|
/**
|
|
* Gets the block type to use for the opening when a portal using this gate is open
|
|
*
|
|
* @return <p>The block type to use for the opening when open</p>
|
|
*/
|
|
public Material getPortalOpenBlock() {
|
|
return portalOpenBlock;
|
|
}
|
|
|
|
/**
|
|
* Gets the block type to use for the opening when a portal using this gate is closed
|
|
*
|
|
* @return <p>The block type to use for the opening when closed</p>
|
|
*/
|
|
public Material getPortalClosedBlock() {
|
|
return portalClosedBlock;
|
|
}
|
|
|
|
/**
|
|
* Gets the material to use for a portal's button if using this gate type
|
|
*
|
|
* @return <p>The material to use for a portal's button if using this gate type</p>
|
|
*/
|
|
public Material getPortalButton() {
|
|
return portalButton;
|
|
}
|
|
|
|
/**
|
|
* Gets the cost of using a portal with this gate
|
|
*
|
|
* @return <p>The cost of using a portal with this gate</p>
|
|
*/
|
|
public int getUseCost() {
|
|
return useCost < 0 ? Stargate.getEconomyConfig().getDefaultUseCost() : useCost;
|
|
}
|
|
|
|
/**
|
|
* Gets the cost of creating a portal with this gate
|
|
*
|
|
* @return <p>The cost of creating a portal with this gate</p>
|
|
*/
|
|
public Integer getCreateCost() {
|
|
return createCost < 0 ? Stargate.getEconomyConfig().getDefaultCreateCost() : createCost;
|
|
}
|
|
|
|
/**
|
|
* Gets the cost of destroying a portal with this gate
|
|
*
|
|
* @return <p>The cost of destroying a portal with this gate</p>
|
|
*/
|
|
public Integer getDestroyCost() {
|
|
return destroyCost < 0 ? Stargate.getEconomyConfig().getDefaultDestroyCost() : destroyCost;
|
|
}
|
|
|
|
/**
|
|
* Gets whether portal payments go to this portal's owner
|
|
*
|
|
* @return <p>Whether portal payments go to the owner</p>
|
|
*/
|
|
public Boolean getToOwner() {
|
|
return toOwner;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a portal's gate matches this gate type
|
|
*
|
|
* @param topLeft <p>The top-left block of the portal's gate</p>
|
|
* @param yaw <p>The yaw when looking directly outwards</p>
|
|
* @return <p>True if this gate matches the portal</p>
|
|
*/
|
|
public boolean matches(BlockLocation topLeft, double yaw) {
|
|
return matches(topLeft, yaw, false);
|
|
}
|
|
|
|
/**
|
|
* Checks whether a portal's gate matches this gate type
|
|
*
|
|
* <p>If enabling onCreate, opening blocks with materials AIR and WATER will be allowed even if the gate closed
|
|
* material is a different one. If checking and onCreate is not enabled, any inconsistency with opening blocks
|
|
* containing AIR or WATER will cause the gate to not match.</p>
|
|
*
|
|
* @param topLeft <p>The top-left block of the portal's gate</p>
|
|
* @param yaw <p>The yaw when looking directly outwards</p>
|
|
* @param onCreate <p>Whether this is used in the context of creating a new gate</p>
|
|
* @return <p>True if this gate matches the portal</p>
|
|
*/
|
|
public boolean matches(BlockLocation topLeft, double yaw, boolean onCreate) {
|
|
return verifyGateEntrancesMatch(topLeft, yaw, onCreate) && verifyGateBorderMatches(topLeft, yaw);
|
|
}
|
|
|
|
/**
|
|
* Verifies that all border blocks of a portal matches this gate type
|
|
*
|
|
* @param topLeft <p>The top-left block of the portal</p>
|
|
* @param yaw <p>The yaw when looking directly outwards from the portal</p>
|
|
* @return <p>True if all border blocks of the gate match the layout</p>
|
|
*/
|
|
private boolean verifyGateBorderMatches(BlockLocation topLeft, double yaw) {
|
|
Map<Character, Material> characterMaterialMap = new HashMap<>(this.characterMaterialMap);
|
|
for (RelativeBlockVector borderVector : layout.getBorder()) {
|
|
int rowIndex = borderVector.getRight();
|
|
int lineIndex = borderVector.getDown();
|
|
Character key = layout.getLayout()[lineIndex][rowIndex];
|
|
|
|
Material materialInLayout = characterMaterialMap.get(key);
|
|
Material materialAtLocation = topLeft.getRelativeLocation(borderVector, yaw).getType();
|
|
|
|
if (materialInLayout == null) {
|
|
/* This generally should not happen with proper checking, but just in case a material character is not
|
|
* recognized, but still allowed in previous checks, verify the gate as long as all such instances of
|
|
* the character correspond to the same material in the physical gate. All subsequent gates will also
|
|
* need to match the first verified gate. */
|
|
characterMaterialMap.put(key, materialAtLocation);
|
|
Stargate.debug("Gate::Matches", String.format("Missing layout material in %s. Using %s from the" +
|
|
" physical portal.", getFilename(), materialAtLocation));
|
|
} else if (materialAtLocation != materialInLayout) {
|
|
Stargate.debug("Gate::Matches", String.format("Block Type Mismatch: %s != %s",
|
|
materialAtLocation, materialInLayout));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Verifies that all entrances of a portal gate matches this gate type
|
|
*
|
|
* @param topLeft <p>The top-left block of this portal</p>
|
|
* @param yaw <p>The yaw when looking directly outwards</p>
|
|
* @param onCreate <p>Whether this is used in the context of creating a new gate</p>
|
|
* @return <p>Whether this is used in the context of creating a new gate</p>
|
|
*/
|
|
private boolean verifyGateEntrancesMatch(BlockLocation topLeft, double yaw, boolean onCreate) {
|
|
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(topLeft));
|
|
for (RelativeBlockVector entranceVector : layout.getEntrances()) {
|
|
Stargate.debug("verifyGateEntrancesMatch", String.valueOf(entranceVector));
|
|
Material type = topLeft.getRelativeLocation(entranceVector, yaw).getType();
|
|
|
|
//Ignore entrance if it's air or water, and we're creating a new gate
|
|
if (onCreate && (type == Material.AIR || type == Material.WATER)) {
|
|
continue;
|
|
}
|
|
|
|
if (type != portalClosedBlock && type != portalOpenBlock) {
|
|
Stargate.debug("Gate::Matches", "Entrance/Exit Material Mismatch: " + type);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Saves this gate to a file
|
|
*
|
|
* <p>This method will save the gate to its filename in the given folder.</p>
|
|
*
|
|
* @param gateFolder <p>The folder to save the gate file in</p>
|
|
*/
|
|
public void save(String gateFolder) {
|
|
try {
|
|
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(gateFolder + filename));
|
|
|
|
//Save main material names
|
|
writeConfig(bufferedWriter, "portal-open", portalOpenBlock.name());
|
|
writeConfig(bufferedWriter, "portal-closed", portalClosedBlock.name());
|
|
writeConfig(bufferedWriter, "button", portalButton.name());
|
|
|
|
//Save the values necessary for economy
|
|
saveEconomyValues(bufferedWriter);
|
|
|
|
//Store material types to use for frame blocks
|
|
saveFrameBlockTypes(bufferedWriter);
|
|
|
|
bufferedWriter.newLine();
|
|
|
|
//Save the gate layout
|
|
layout.saveLayout(bufferedWriter);
|
|
|
|
bufferedWriter.close();
|
|
} catch (IOException ex) {
|
|
Stargate.logSevere(String.format("Could not save Gate %s - %s", filename, ex.getMessage()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves current economy related values using a buffered writer
|
|
*
|
|
* @param bufferedWriter <p>The buffered writer to write to</p>
|
|
* @throws IOException <p>If unable to write to the buffered writer</p>
|
|
*/
|
|
private void saveEconomyValues(BufferedWriter bufferedWriter) throws IOException {
|
|
//Write use cost if not disabled
|
|
if (useCost != -1) {
|
|
writeConfig(bufferedWriter, "usecost", useCost);
|
|
}
|
|
//Write create cost if not disabled
|
|
if (createCost != -1) {
|
|
writeConfig(bufferedWriter, "createcost", createCost);
|
|
}
|
|
//Write destroy cost if not disabled
|
|
if (destroyCost != -1) {
|
|
writeConfig(bufferedWriter, "destroycost", destroyCost);
|
|
}
|
|
writeConfig(bufferedWriter, "toowner", toOwner);
|
|
}
|
|
|
|
/**
|
|
* Saves the types of blocks used for the gate frame/border using a buffered writer
|
|
*
|
|
* @param bufferedWriter <p>The buffered writer to write to</p>
|
|
* @throws IOException <p>If unable to write to the buffered writer</p>
|
|
*/
|
|
private void saveFrameBlockTypes(BufferedWriter bufferedWriter) throws IOException {
|
|
for (Map.Entry<Character, Material> entry : characterMaterialMap.entrySet()) {
|
|
Character type = entry.getKey();
|
|
Material value = entry.getValue();
|
|
//Skip characters not part of the frame
|
|
if (type.equals(GateHandler.getAnythingCharacter()) ||
|
|
type.equals(GateHandler.getEntranceCharacter()) ||
|
|
type.equals(GateHandler.getExitCharacter())) {
|
|
continue;
|
|
}
|
|
|
|
bufferedWriter.append(type);
|
|
bufferedWriter.append('=');
|
|
if (value != null) {
|
|
bufferedWriter.append(value.toString());
|
|
}
|
|
bufferedWriter.newLine();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes a formatted string to a buffered writer
|
|
*
|
|
* @param bufferedWriter <p>The buffered writer to write the formatted string to</p>
|
|
* @param key <p>The config key to save</p>
|
|
* @param value <p>The config value to save</p>
|
|
* @throws IOException <p>If unable to write to the buffered writer</p>
|
|
*/
|
|
private void writeConfig(BufferedWriter bufferedWriter, String key, Object value) throws IOException {
|
|
//Figure out the correct formatting to use
|
|
String format = "%s=";
|
|
if (value instanceof Boolean) {
|
|
format += "%b";
|
|
} else if (value instanceof Integer) {
|
|
format += "%d";
|
|
} else if (value instanceof String) {
|
|
format += "%s";
|
|
} else {
|
|
throw new IllegalArgumentException("Unrecognized config value type");
|
|
}
|
|
|
|
bufferedWriter.append(String.format(format, key, value));
|
|
bufferedWriter.newLine();
|
|
}
|
|
|
|
}
|