package toritools.scripting;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.VolatileImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import toritools.controls.KeyHolder;
import toritools.debug.Debug;
import toritools.entity.Entity;
import toritools.entity.Level;
import toritools.entrypoint.Binary;
import toritools.map.ToriMapIO;
import toritools.map.VariableCase;
import toritools.math.Vector2;
/**
* Construction yard for various things that help users out in writing entity
* scripts.
*
* @author toriscope
*
*/
public class ScriptUtils {
private static VariableCase profileVariables = new VariableCase();
private final static String PROFILE = "profile.save";
private static KeyHolder keyHolder = new KeyHolder();
private static boolean debugMode = false;
private static Level level;
private static Level newLevel;
public static Random random = new Random();
public static KeyHolder getKeyHolder() {
return keyHolder;
}
public static void setKeyHolder(final KeyHolder keyHolder) {
ScriptUtils.keyHolder = keyHolder;
}
public static String getVar(final String key) {
return profileVariables.getVar(key);
}
public static void setVar(final String key, final String value) {
profileVariables.setVar(key, value);
}
public static void saveProfileVariables(final String prefix) throws IOException {
ToriMapIO.writeMap(new File(prefix + "_" + PROFILE), profileVariables.getVariables());
}
public static void loadProfileVariables(final String prefix) throws FileNotFoundException {
profileVariables = new VariableCase(ToriMapIO.readMap(new File(prefix + "_" + PROFILE)));
}
/**
* Represents the 8 directions. Use the getDirection method to grab an enum
* easily.
*
* @author toriscope
*
*/
public static enum Direction {
UP, DOWN, LEFT, RIGHT, UP_RIGHT, UP_LEFT, DOWN_LEFT, DOWN_RIGHT, CENTER;
/**
* Find the proper enum for the direction.
*
* @param dir
* direction in radians.
* @return the proper enum.
*/
public static Direction findEnum(float dir) {
dir = (float) Math.toDegrees(dir) % 360;
if (dir >= 337.5 || dir < 22.5)
return Direction.RIGHT;
if (dir >= 22.5 && dir < 67.5)
return Direction.UP_RIGHT;
if (dir >= 67.5 && dir < 112.5)
return Direction.UP;
if (dir >= 112.5 && dir < 157.5)
return Direction.UP_LEFT;
if (dir >= 157.5 && dir < 202.5)
return Direction.LEFT;
if (dir >= 202.5 && dir < 247.5)
return Direction.DOWN_LEFT;
if (dir >= 247.5 && dir < 292.5)
return Direction.DOWN;
return Direction.DOWN_RIGHT;
}
}
public static boolean isColliding(final Entity a, final Entity b) {
// left of
if (a.getPos().x + a.getDim().x <= b.getPos().x) {
return false;
}
// below
else if (a.getPos().y + a.getDim().y <= b.getPos().y) {
return false;
}
// right
else if (b.getPos().x + b.getDim().x <= a.getPos().x) {
return false;
}
// above
else if (b.getPos().y + b.getDim().y <= a.getPos().y) {
return false;
}
return true;
}
public static boolean isPointWithin(final Entity a, final Vector2 point) {
return point.x > a.getPos().x && point.x < a.getPos().x + a.getDim().x && point.y > a.getPos().y
&& point.y < a.getPos().y + a.getDim().y;
}
public static boolean isCollidingRad(final Entity a, final Entity b) {
return Vector2.dist(a.getPos().add(a.getDim().scale(.5f)), b.getPos().add(b.getDim().scale(.5f))) < a.getDim().x
/ 2 + b.getDim().x / 2;
}
public static boolean isColliding(final Entity a, final List<Entity> b) {
for (Entity e : b) {
if (e != a && isColliding(e, a))
return true;
}
return false;
}
public static Vector2 moveOut(final Entity self, final boolean disregardOutOfView, final Entity entity) {
Vector2 delta = new Vector2();
if (self != entity) {
if (!(disregardOutOfView && !entity.isInView()) && isColliding(entity, self)) {
self.setPos(self.getPos().add(delta = findBestVectorOut(self, entity).scale(1.1f)));
}
}
return delta;
}
public static Vector2 moveOut(final Entity self, final boolean disregardOutOfView, final List<Entity> entities) {
Vector2 delta = new Vector2();
for (Entity entity : entities)
if (self != entity && !(disregardOutOfView && !entity.isInView()) && isColliding(entity, self)) {
self.setPos(self.getPos().add(delta = findBestVectorOut(self, entity).scale(1.1f)));
}
return delta;
}
public static Vector2 findBestVectorOut(final Entity toMove, final Entity noMove) {
Vector2 test;
Vector2 best = new Vector2(0, noMove.getPos().y - (toMove.getPos().y + toMove.getDim().y));
test = new Vector2(0, (toMove.getPos().y - (noMove.getPos().y + noMove.getDim().y)) * -1);
if (test.mag() < best.mag())
best = test;
test = new Vector2(noMove.getPos().x - (toMove.getPos().x + toMove.getDim().x), 0);
if (test.mag() < best.mag())
best = test;
test = new Vector2((toMove.getPos().x - (noMove.getPos().x + noMove.getDim().x)) * -1, 0);
if (test.mag() < best.mag())
best = test;
return best;
}
public static boolean isDebugMode() {
return debugMode;
}
public static void setDebugMode(boolean debugMode) {
ScriptUtils.debugMode = debugMode;
}
public static Level getCurrentLevel() {
return level;
}
/**
* Queue a level switch. If there is no current level, then it is switched
* to automatically.
*
* @param newLevel
* the level to switch to.
*/
public static void queueLevelSwitch(final Level newLevel) {
ScriptUtils.newLevel = newLevel;
Debug.print("New level queued.");
}
public static boolean isLevelQueued() {
return newLevel != null;
}
/**
* Move to the queued level.
*/
public static void moveToQueuedLevel() {
Debug.print("Moving to the queued level.");
keyHolder.clearKeys();
level = newLevel;
newLevel = null;
}
/*
* Image Caching.
*/
private static HashMap<File, Image> imageCache = new HashMap<File, Image>();
/**
* Fetch an image based on the file index. If it is invalid, or is not yet
* cached, it will be cached and stored in video memory.
*
* @param imageIndex
* @return
*/
public static Image fetchImage(final File imageIndex) {
Image i = imageCache.get(imageIndex);
if (i == null
|| (i instanceof VolatileImage && !(((VolatileImage) i).validate(Binary.gc) == VolatileImage.IMAGE_OK))) {
imageCache.put(imageIndex, loadImage(imageIndex));
}
return imageCache.get(imageIndex);
}
private static Image loadImage(final File imageIndex) {
Image image = null;
try {
image = (new ImageIcon(ClassLoader.getSystemResource(imageIndex.getPath().replace("\\", "/")))).getImage();
} catch (Exception e) {
try {
image = ImageIO.read(imageIndex);
} catch (IOException e1) {
e1.printStackTrace();
System.err.println("Can't find resouce " + imageIndex.toString());
System.exit(0);
}
}
VolatileImage i = Binary.gc.createCompatibleVolatileImage(image.getWidth(null), image.getHeight(null),
VolatileImage.TRANSLUCENT);
i.validate(Binary.gc);
Graphics2D g = (Graphics2D) i.getGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
g.fillRect(0, 0, i.getWidth(), i.getHeight());
i.getGraphics().drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null);
i.getGraphics().dispose();
return i;
}
/**
* Get the amount of cached images.
*/
public static int cachedImageAmount() {
return imageCache.size();
}
/**
* Clear the image cache.
*/
public static void clearImageCache() {
for (Entry<File, Image> s : imageCache.entrySet()) {
if (s.getValue() instanceof VolatileImage) {
((VolatileImage) s.getValue()).flush();
}
}
imageCache.clear();
}
}