/**
* Programmer: Jacob Scott
* Program Name: BoardStyle
* Description: for wrapping up all board settings
* Date: Jul 29, 2011
*/
package me.desht.chesscraft.chess;
import me.desht.chesscraft.ChessPersistence;
import me.desht.chesscraft.ChessValidate;
import me.desht.chesscraft.DirectoryStructure;
import me.desht.chesscraft.chess.pieces.ChessSet;
import me.desht.chesscraft.chess.pieces.ChessSetFactory;
import me.desht.chesscraft.enums.HighlightStyle;
import me.desht.chesscraft.exceptions.ChessException;
import me.desht.dhutils.*;
import me.desht.dhutils.block.MaterialWithData;
import org.bukkit.configuration.Configuration;
import org.bukkit.material.MaterialData;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class BoardStyle implements Comparable<BoardStyle>, ConfigurationListener {
public static final String DEFAULT_BOARD_STYLE = "standard";
public static final int MIN_HEIGHT = 3, MIN_FRAMEWIDTH = 2, MIN_SQUARESIZE = 1;
public static final int MAX_HEIGHT = 128, MAX_FRAMEWIDTH = 10, MAX_SQUARESIZE = 30;
private static final String WHITE_SQUARE = "white_square";
private static final String BLACK_SQUARE = "black_square";
private static final String FRAME = "frame";
private static final String ENCLOSURE = "enclosure";
private static final String STRUTS = "struts";
private static final String PANEL = "panel";
private static final String HIGHLIGHT_SQUARE = "highlight_square";
private static final String HIGHLIGHT_WHITE_SQUARE = "highlight_white_square";
private static final String HIGHLIGHT_BLACK_SQUARE = "highlight_black_square";
private static final String HIGHLIGHT_STYLE = "highlight_style";
private static final String LIGHT_LEVEL = "light_level";
private static final String PIECE_STYLE = "piece_style";
private static final String HIGHLIGHT_SELECTED = "highlight_selected";
private final boolean isCustom;
private final int frameWidth, squareSize, height;
private final String styleName;
private final AttributeCollection attributes;
/**
* Private constructor. Use {@link #loadStyle(String)} to get new BoardStyle objects.
*
* @param styleName name of this board style
* @param c configuration object which stores this board style
* @param isCustom true if this is a custom-created board style, false if it is a stock style
* @throws ChessException if the board style is any way invalid
*/
private BoardStyle(String styleName, Configuration c, boolean isCustom) throws ChessException {
for (String k : new String[] {
"square_size", "frame_width", "height",
"black_square", "white_square", "frame", "enclosure"}) {
ChessPersistence.requireSection(c, k);
}
this.attributes = new AttributeCollection(this);
registerAttributes();
this.styleName = styleName;
this.isCustom = isCustom;
this.squareSize = c.getInt("square_size");
this.frameWidth = c.getInt("frame_width");
this.height = c.getInt("height");
ChessValidate.isTrue(squareSize > 0, "Invalid square size " + squareSize + " in board style " + styleName);
ChessValidate.isTrue(frameWidth > 1, "Invalid frame width " + frameWidth + " in board style " + styleName);
ChessValidate.isTrue(height > 0, "Invalid height " + height + " in board style " + styleName);
ChessValidate.isTrue(c.contains("lit") || c.contains("light_level"), "Board style must have at least one of 'lit' or 'light_level'");
for (String k : c.getKeys(false)) {
if (attributes.contains(k)) {
try {
attributes.set(k, c.getString(k));
} catch (DHUtilsException e) {
throw new ChessException("Invalid value for '" + k + "' in board style '" + styleName + "': " + e.getMessage());
}
}
}
}
private void registerAttributes() {
attributes.registerAttribute(LIGHT_LEVEL, 15, "Lighting level (0-15) for the board");
attributes.registerAttribute(PIECE_STYLE, "Standard", "Default piece style for the board");
attributes.registerAttribute(WHITE_SQUARE, MaterialWithData.get("wool:white"), "Material for white board square");
attributes.registerAttribute(BLACK_SQUARE, MaterialWithData.get("wool:black"), "Material for black board square");
attributes.registerAttribute(FRAME, MaterialWithData.get("wood"), "Material for outer board frame");
attributes.registerAttribute(ENCLOSURE, MaterialWithData.get("air"), "Material for board enclosure");
attributes.registerAttribute(STRUTS, MaterialWithData.get("wood"), "Material for board edge struts");
attributes.registerAttribute(PANEL, MaterialWithData.get("wood"), "Material for control panel");
attributes.registerAttribute(HIGHLIGHT_SQUARE, MaterialWithData.get("wool:red"), "Material for last-move highlight");
attributes.registerAttribute(HIGHLIGHT_WHITE_SQUARE, MaterialWithData.get("wool:red"), "Material for last-move highlight (white squares)");
attributes.registerAttribute(HIGHLIGHT_BLACK_SQUARE, MaterialWithData.get("wool:red"), "Material for last-move highlight (black squares)");
attributes.registerAttribute(HIGHLIGHT_STYLE, HighlightStyle.CORNERS, "Style of last-move highlighting");
attributes.registerAttribute(HIGHLIGHT_SELECTED, MaterialWithData.get("wool:yellow"), "Material for selected square highlight");
}
public String getName() {
return styleName;
}
public boolean isCustom() {
return isCustom;
}
public String getPieceStyleName() {
return (String) attributes.get(PIECE_STYLE);
}
public int getFrameWidth() {
return frameWidth;
}
public int getHeight() {
return height;
}
public int getSquareSize() {
return squareSize;
}
public int getLightLevel() {
return (Integer) attributes.get(LIGHT_LEVEL);
}
public MaterialData getBlackSquareMaterial() {
return ((MaterialWithData) attributes.get(BLACK_SQUARE)).getMaterialData();
}
public MaterialData getWhiteSquareMaterial() {
return ((MaterialWithData) attributes.get(WHITE_SQUARE)).getMaterialData();
}
public MaterialData getControlPanelMaterial() {
return ((MaterialWithData) attributes.get(PANEL)).getMaterialData();
}
public MaterialData getEnclosureMaterial() {
return ((MaterialWithData) attributes.get(ENCLOSURE)).getMaterialData();
}
public MaterialData getFrameMaterial() {
return ((MaterialWithData) attributes.get(FRAME)).getMaterialData();
}
public HighlightStyle getHighlightStyle() {
return (HighlightStyle) attributes.get(HIGHLIGHT_STYLE);
}
public MaterialData getHighlightMaterial() {
return ((MaterialWithData) attributes.get(HIGHLIGHT_SQUARE)).getMaterialData();
}
public MaterialData getStrutsMaterial() {
return ((MaterialWithData) attributes.get(STRUTS)).getMaterialData();
}
public MaterialData getHighlightMaterial(boolean isWhiteSquare) {
return isWhiteSquare ? getWhiteSquareHighlightMaterial() : getBlackSquareHighlightMaterial();
}
public MaterialData getBlackSquareHighlightMaterial() {
return ((MaterialWithData) attributes.get(HIGHLIGHT_BLACK_SQUARE)).getMaterialData();
}
public MaterialData getWhiteSquareHighlightMaterial() {
return ((MaterialWithData) attributes.get(HIGHLIGHT_BLACK_SQUARE)).getMaterialData();
}
public MaterialData getSelectedHighlightMaterial() {
return ((MaterialWithData) attributes.get(HIGHLIGHT_SELECTED)).getMaterialData();
}
private void validateIsBlock(MaterialWithData mat, String tag) {
ChessValidate.notNull(mat.getBukkitMaterial(), mat + " is not a Bukkit material");
ChessValidate.isTrue(mat.getBukkitMaterial().isBlock(), tag + ": " + mat + " is not a block material!");
}
public void verifyCompatibility(ChessSet pieceStyle) throws ChessException {
// ensure the new chess set actually fits this board
if (pieceStyle.getMaxWidth() > squareSize || pieceStyle.getMaxHeight() > height) {
throw new ChessException("Set '" + pieceStyle.getName() + "' is too large for this board!");
}
}
public BoardStyle saveStyle(String newStyleName) throws ChessException {
File f = DirectoryStructure.getResourceFileForSave(DirectoryStructure.getBoardStyleDirectory(), newStyleName);
// It would be nice to use the configuration API to save this, but I want comments!
try {
BufferedWriter out = new BufferedWriter(new FileWriter(f));
out.write("# Chess board style definition\n\n");
out.write("# NOTE: all materials must be quoted, even if they're just integers, or\n");
out.write("# you will get a java.lang.ClassCastException when the style is loaded.\n\n");
out.write("# width/length of the board squares, in blocks\n");
out.write("square_size: " + getSquareSize() + "\n");
out.write("# width in blocks of the frame surrounding the board\n");
out.write("frame_width: " + getFrameWidth() + "\n");
out.write("# height of the board - number of squares of clear air between board and enclosure roof\n");
out.write("height: " + getHeight() + "\n");
out.write("# material/data for the white squares\n");
out.write("white_square: '" + getWhiteSquareMaterial() + "'\n");
out.write("# material/data for the black squares\n");
out.write("black_square: '" + getBlackSquareMaterial() + "'\n");
out.write("# material/data for the frame\n");
out.write("frame: '" + getFrameMaterial() + "'\n");
out.write("# material/data for the enclosure\n");
out.write("enclosure: '" + getEnclosureMaterial() + "'\n");
out.write("# material/data for the enclosure struts (default: 'enclosure' setting)\n");
out.write("struts: '" + getStrutsMaterial() + "'\n");
out.write("# board lighting level (0-15)\n");
out.write("light_level: " + getLightLevel() + "\n");
out.write("# style of chess set to use (see ../pieces/*.yml)\n");
out.write("# the style chosen must fit within the square_size specified above\n");
out.write("piece_style: " + getPieceStyleName() + "\n");
out.write("# material/data for the control panel (default: 'frame' setting)\n");
out.write("panel: '" + getControlPanelMaterial() + "'\n");
out.write("# highlighting style (one of NONE, CORNERS, EDGES, LINE, CHECKERED)\n");
out.write("highlight_style: " + getHighlightStyle() + "\n");
out.write("# highlighting material (default: glowstone)\n");
out.write("highlight: '" + getHighlightMaterial() + "'\n");
out.write("# highlighting material on white squares (default: 'highlight' setting)\n");
out.write("highlight_white_square: '" + getWhiteSquareHighlightMaterial() + "'\n");
out.write("# highlighting material on black squares (default: 'highlight' setting)\n");
out.write("highlight_black_square: '" + getBlackSquareHighlightMaterial() + "'\n");
out.write("# highlighting material on selected squares\n");
out.write("highlight_selected: '" + getSelectedHighlightMaterial() + "'\n");
out.close();
return loadStyle(newStyleName);
} catch (IOException e) {
throw new ChessException(e.getMessage());
}
}
/**
* Load a new style.
*
* @param styleName Name of the style to load
* @return The new loaded style
* @throws ChessException
*/
public static BoardStyle loadStyle(String styleName) throws ChessException {
if (styleName == null) styleName = DEFAULT_BOARD_STYLE;
try {
File f = DirectoryStructure.getResourceFileForLoad(DirectoryStructure.getBoardStyleDirectory(), styleName);
if (!f.exists()) {
throw new ChessException("No such board style '" + styleName + "'");
}
Configuration c = MiscUtil.loadYamlUTF8(f);
return new BoardStyle(styleName, c, DirectoryStructure.isCustom(f));
} catch (Exception e) {
// e.printStackTrace();
throw new ChessException(e.getMessage());
}
}
/**
* Ensure the given piece style will fit on the given board style.
*
* @param boardStyleName name of the board style
* @param pieceStyleName name of the piece style (null means to use board's default piece style)
* @throws ChessException if the piece style will not fit
*/
public static void verifyCompatibility(String boardStyleName, String pieceStyleName) throws ChessException {
BoardStyle b = BoardStyle.loadStyle(boardStyleName);
ChessSet cs = ChessSetFactory.getChessSet(pieceStyleName.isEmpty() ? b.getPieceStyleName() : pieceStyleName);
b.verifyCompatibility(cs);
}
@Override
public int compareTo(BoardStyle o) {
return getName().compareTo(o.getName());
}
@Override
public Object onConfigurationValidate(ConfigurationManager configurationManager, String key, Object oldVal, Object newVal) {
if (key.equals(LIGHT_LEVEL)) {
int level = (Integer) newVal;
ChessValidate.isTrue(level >= 0 && level <= 15, "Light level must be in range 0-15");
} else if (key.equals(PIECE_STYLE)) {
verifyCompatibility(ChessSetFactory.getChessSet((String) newVal));
} else if (newVal instanceof MaterialWithData) {
validateIsBlock((MaterialWithData) newVal, key);
}
return newVal;
}
@Override
public void onConfigurationChanged(ConfigurationManager configurationManager, String key, Object oldVal, Object newVal) {
// nothing to do here
}
public AttributeCollection getAttributes() {
return attributes;
}
}