util.Utils.java Source code

Java tutorial

Introduction

Here is the source code for util.Utils.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FileTextureData;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.tiles.StaticTiledMapTile;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.fadeOut;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.moveTo;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.removeActor;
import static com.badlogic.gdx.scenes.scene2d.actions.Actions.sequence;
import exodus.BaseScreen;
import exodus.CombatScreen;
import exodus.Constants;
import exodus.Context;
import exodus.Exodus;
import exodus.Party.PartyMember;
import exodus.Sound;
import exodus.Sounds;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import objects.BaseMap;
import objects.Creature;
import objects.Drawable;
import objects.Moongate;
import objects.Person;
import objects.Portal;
import objects.ProjectileActor;
import objects.Tile;
import objects.TileSet;
import org.apache.commons.io.IOUtils;

/**
 *
 * @author Paul
 */
public class Utils implements Constants {

    public static Random rand = new XORShiftRandom();

    public static String properCase(String s) {
        return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
    }

    //This gives you a random number in between low (inclusive) and high (exclusive)
    public static int getRandomBetween(int low, int high) {
        return rand.nextInt(high - low) + low;
    }

    public static Object loadXml(String fname, Class<?> clazz) throws Exception {
        InputStream is = Utils.class.getResourceAsStream("/" + fname);
        JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        return jaxbUnmarshaller.unmarshal(is);
    }

    public static int adjustValueMax(int v, int val, int max) {
        v += val;
        if (v > max) {
            v = max;
        }
        return v;
    }

    public static int adjustValueMin(int v, int val, int min) {
        v += val;
        if (v < min) {
            v = min;
        }
        return v;
    }

    public static int adjustValue(int v, int val, int max, int min) {
        v += val;
        if (v > max) {
            v = max;
        }
        if (v < min) {
            v = min;
        }
        return v;
    }

    public static void setMapTiles(BaseMap map, TileSet ts) throws Exception {

        String fname = map.getFname().toLowerCase();
        if (fname == null || fname.isEmpty()) {
            return;
        }

        if (fname.endsWith("tmx")) {
            setTilesFromTMX(map, Maps.get(map.getId()), fname, ts);
        } else {

            InputStream is = ClassLoader.class.getResourceAsStream("/assets/data/" + fname);
            byte[] bytes = IOUtils.toByteArray(is);

            Tile[] tiles = new Tile[map.getWidth() * map.getHeight()];

            if (map.getType() == MapType.world || map.getType() == MapType.city) {

                int pos = 0;
                for (int y = 0; y < map.getHeight(); y++) {
                    for (int x = 0; x < map.getWidth(); x++) {
                        int index = (bytes[pos] & 0xff) / 4;
                        pos++;
                        Tile tile = ts.getTileByIndex(index);
                        if (tile == null) {
                            System.out.println(
                                    "Tile index cannot be found: " + index + " using index 37 for black space.");
                            tile = ts.getTileByIndex(37);
                        }

                        tiles[x + y * map.getWidth()] = tile;
                    }
                }

                //doors
                for (int y = 0; y < map.getHeight(); y++) {
                    for (int x = 0; x < map.getWidth(); x++) {
                        Tile tile = tiles[x + y * map.getWidth()];
                        Tile left = x > 0 ? tiles[(x - 1) + y * map.getWidth()] : null;
                        Tile right = x < map.getWidth() - 1 ? tiles[(x + 1) + y * map.getWidth()] : null;
                        if (tile.getIndex() == 46 && (left != null && left.getRule() != TileRule.signs)
                                && (right != null && right.getRule() != TileRule.signs)) {
                            tiles[x + y * map.getWidth()] = ts.getTileByName("locked_door");
                        }
                    }
                }

                //ambrosia doors
                if (map.getId() == Maps.AMBROSIA.getId()) {
                    tiles[34 + 5 * map.getWidth()] = ts.getTileByName("locked_door");
                    tiles[35 + 5 * map.getWidth()] = ts.getTileByName("locked_door");
                    tiles[36 + 5 * map.getWidth()] = ts.getTileByName("locked_door");
                }

                if (map.getType() == MapType.city) {
                    setPeople(map, tiles, bytes, ts);
                }

                if (map.getType() == MapType.world) {
                    //set a moongate tile to grass here
                    tiles[15 + 29 * map.getWidth()] = ts.getTileByIndex(1);
                }

            } else if (map.getType() == MapType.combat) {

                int pos = 0x40;
                for (int y = 0; y < map.getHeight(); y++) {
                    for (int x = 0; x < map.getWidth(); x++) {
                        int index = bytes[pos] & 0xff;
                        pos++;
                        Tile tile = ts.getTileByIndex(index);
                        if (tile == null) {
                            //System.err.printf("%S Combat Tile index cannot be found: %d using index 127 for black space (%d, %d)\n", map, index, x, y);
                            tile = ts.getTileByIndex(127);
                        }
                        tiles[x + y * map.getWidth()] = tile;
                    }
                }
            } else if (map.getType() == MapType.shrine) {
                int pos = 0;
                for (int y = 0; y < map.getHeight(); y++) {
                    for (int x = 0; x < map.getWidth(); x++) {
                        int index = bytes[pos] & 0xff;
                        pos++;
                        Tile tile = ts.getTileByIndex(index);
                        if (tile == null) {
                            System.out.println(
                                    "Tile index cannot be found: " + index + " using index 127 for black space.");
                            tile = ts.getTileByIndex(127);
                        }
                        if (tile.getIndex() == 31) { //avatar position
                            tile = ts.getTileByIndex(4);
                        }
                        tiles[x + y * map.getWidth()] = tile;
                    }
                }
            }

            map.setTiles(tiles);

        }

    }

    public static void setPeople(BaseMap map, Tile[] tiles, byte[] bytes, TileSet ts) {
        List<Person> people = new ArrayList<>();

        int pos = 0x1180;
        for (int x = 0; x < 32; x++) {
            int index = (bytes[pos] & 0xff) / 4;
            pos++;
            Tile tile = ts.getTileByIndex(index);
            if (tile == null) {
                System.out.println("Tile index cannot be found: " + index + " using index 37 for black space.");
                tile = ts.getTileByIndex(37);
            }
            Person p = new Person();
            p.setTile(tile);
            people.add(p);
        }

        pos = 0x11C0;
        for (int x = 0; x < 32; x++) {
            int dx = bytes[pos] & 0xff;
            pos++;
            people.get(x).setStart_x(dx);
            people.get(x).setX(dx);
        }

        pos = 0x11E0;
        for (int x = 0; x < 32; x++) {
            int dy = bytes[pos] & 0xff;
            pos++;
            people.get(x).setStart_y(dy);
            people.get(x).setY(dy);
        }

        pos = 0x1200;
        for (int x = 0; x < 32; x++) {
            int dialog = bytes[pos] & 0x0f;
            int m = (bytes[pos] >> 4) & 0x0f;
            ObjectMovementBehavior move = ObjectMovementBehavior.FIXED;
            if (m == 4) {
                move = ObjectMovementBehavior.WANDER;
            }
            if (m == 8) {
                move = ObjectMovementBehavior.FOLLOW_AVATAR;
            }
            if (m == 12) {
                move = ObjectMovementBehavior.ATTACK_AVATAR;
            }
            pos++;
            people.get(x).setMovement(move);
            people.get(x).setDialogId(dialog);
        }

        pos = 0x11A0;
        for (int x = 0; x < 32; x++) {
            int index = (bytes[pos] & 0xff) / 4;
            pos++;
            Tile tile = ts.getTileByIndex(index);
            if (tile == null) {
                System.out.println("Tile index cannot be found: " + index + " using index 37 for black space.");
                tile = ts.getTileByIndex(37);
            }
            int dx = people.get(x).getX();
            int dy = people.get(x).getY();

            tiles[dx + dy * map.getWidth()] = tile;
        }

        pos = 0x1000;
        int[] textOffsets = new int[8];
        for (int x = 0; x < 8; x++) {
            textOffsets[x] = (bytes[pos] & 0xff) + 0x1000;
            pos += 2;
        }

        String[] texts = new String[8];
        for (int x = 0; x < 8; x++) {
            int os = textOffsets[x];
            byte[] b = new byte[64];
            int c = 0;
            while (true) {
                b[c] = bytes[os];
                if (b[c] == 0) {
                    break;
                }
                os++;
                c++;
            }
            texts[x] = new String(b).trim();
            texts[x] = texts[x].replaceAll("[<>]", "");
            texts[x] = texts[x].replaceAll("[\n\r]", " ");

        }

        for (int x = 0; x < 8; x++) {
            for (Person per : people) {
                if (per.getDialogId() == x + 1) {
                    per.setConversation(texts[x]);
                }
            }
        }

        map.setPeople(people);
    }

    public static void setTilesFromTMX(BaseMap map, Maps id, String tmxFile, TileSet ts) {

        Tile[] tiles = new Tile[map.getWidth() * map.getHeight()];

        FileHandleResolver resolver = new Constants.ClasspathResolver();
        TmxMapLoader loader = new TmxMapLoader(resolver);
        TiledMap tm = loader.load("assets/tmx/" + tmxFile);

        TiledMapTileLayer ml = (TiledMapTileLayer) tm.getLayers().get(map.getId() + "-map");
        if (ml != null) {
            FileHandle f = resolver.resolve("assets/graphics/latest-atlas.txt");
            TextureAtlas.TextureAtlasData atlas = new TextureAtlas.TextureAtlasData(f, f.parent(), false);
            int png_grid_width = 24;
            Tile[] mapTileIds = new Tile[png_grid_width * Constants.tilePixelWidth + 1];
            for (TextureAtlas.TextureAtlasData.Region r : atlas.getRegions()) {
                int x = r.left / r.width;
                int y = r.top / r.height;
                int i = x + (y * png_grid_width) + 1;
                mapTileIds[i] = ts.getTileByName(r.name);
                if (mapTileIds[i] == null) {
                    //System.out.printf("no tile found: %s %d\n",r.name,i);
                }
            }

            for (int y = 0; y < map.getHeight(); y++) {
                for (int x = 0; x < map.getWidth(); x++) {
                    StaticTiledMapTile tr = (StaticTiledMapTile) ml.getCell(x, map.getWidth() - 1 - y).getTile();
                    Tile tile = mapTileIds[tr.getId()];
                    if (tile == null) {
                        System.out.printf("no tile found: %d %d %d\n", x, y, tr.getId());
                    }
                    tiles[x + (y * map.getWidth())] = tile;
                }
            }
        }

        map.setTiles(tiles);

        MapLayer objectsLayer = tm.getLayers().get("portals");
        if (objectsLayer != null) {
            Iterator<MapObject> iter = objectsLayer.getObjects().iterator();
            while (iter.hasNext()) {
                MapObject obj = iter.next();
                Portal p = map.getPortal(Maps.valueOf(obj.getName()).getId());
                Iterator<String> keys = obj.getProperties().getKeys();
                while (keys.hasNext()) {
                    String key = keys.next();
                    String value = obj.getProperties().get(key).toString();
                    if (key.equals("x")) {
                        p.setX(new Integer(value));
                    } else if (key.equals("y")) {
                        p.setY(new Integer(value));
                    }
                }
            }
        }

        objectsLayer = tm.getLayers().get("moongates");
        if (objectsLayer != null) {
            Iterator<MapObject> iter = objectsLayer.getObjects().iterator();
            while (iter.hasNext()) {
                MapObject obj = iter.next();
                Moongate m = map.getMoongate(obj.getName());
                Iterator<String> keys = obj.getProperties().getKeys();
                while (keys.hasNext()) {
                    String key = keys.next();
                    String value = obj.getProperties().get(key).toString();
                    if (key.equals("x")) {
                        m.setX(new Integer(value));
                    } else if (key.equals("y")) {
                        m.setY(new Integer(value));
                    }
                }
            }
        }

    }

    public static Direction getPath(MapBorderBehavior borderbehavior, int width, int height, int toX, int toY,
            int validMovesMask, boolean towards, int fromX, int fromY) {
        /* find the directions that lead [to/away from] our target */
        int directionsToObject = towards
                ? getRelativeDirection(borderbehavior, width, height, toX, toY, fromX, fromY)
                : ~getRelativeDirection(borderbehavior, width, height, toX, toY, fromX, fromY);

        /* make sure we eliminate impossible options */
        directionsToObject &= validMovesMask;

        /* get the new direction to move */
        if (directionsToObject > 0) {
            return Direction.getRandomValidDirection(directionsToObject);
        } /* there are no valid directions that lead to our target, just move wherever we can! */ else {
            return null;//Direction.getRandomValidDirection(validMovesMask);
        }
    }

    /**
     * Finds the movement distance (not using diagonals) from point a to point b
     * on a map, taking into account map boundaries and such.
     */
    public static int movementDistance(MapBorderBehavior borderbehavior, int width, int height, int fromX,
            int fromY, int toX, int toY) {
        int dirmask = 0;
        ;
        int dist = 0;

        /* get the direction(s) to the coordinates */
        dirmask = getRelativeDirection(borderbehavior, width, height, toX, toY, fromX, fromY);

        if (borderbehavior == MapBorderBehavior.wrap) {
            if (Math.abs(fromX - toX) > Math.abs(fromX + width - toX)) {
                fromX += width;
            } else if (Math.abs(fromX - toX) > Math.abs(fromX - width - toX)) {
                fromX -= width;
            }

            if (Math.abs(fromY - toY) > Math.abs(fromY + width - toY)) {
                fromY += height;
            } else if (Math.abs(fromY - toY) > Math.abs(fromY - width - toY)) {
                fromY -= height;
            }
        }

        while (fromX != toX || fromY != toY) {

            if (fromX != toX) {
                if (Direction.isDirInMask(Direction.WEST, dirmask)) {
                    fromX--;
                } else {
                    fromX++;
                }
                dist++;
            }
            if (fromY != toY) {
                if (Direction.isDirInMask(Direction.NORTH, dirmask)) {
                    fromY--;
                } else {
                    fromY++;
                }
                dist++;
            }

        }

        return dist;
    }

    /**
     * Returns a mask of directions that indicate where one point is relative to
     * another. For instance, if the object at (x, y) is northeast of (c.x,
     * c.y), then this function returns (MASK_DIR(DIR_NORTH) |
     * MASK_DIR(DIR_EAST)) This function also takes into account map boundaries
     * and adjusts itself accordingly.
     */
    public static int getRelativeDirection(MapBorderBehavior borderbehavior, int width, int height, int toX,
            int toY, int fromX, int fromY) {
        int dx = 0, dy = 0;
        int dirmask = 0;

        /* adjust our coordinates to find the closest path */
        if (borderbehavior == MapBorderBehavior.wrap) {

            if (Math.abs(fromX - toX) > Math.abs(fromX + width - toX)) {
                fromX += width;
            } else if (Math.abs(fromX - toX) > Math.abs(fromX - width - toX)) {
                fromX -= width;
            }

            if (Math.abs(fromY - toY) > Math.abs(fromY + width - toY)) {
                fromY += height;
            } else if (Math.abs(fromY - toY) > Math.abs(fromY - width - toY)) {
                fromY -= height;
            }

            dx = fromX - toX;
            dy = fromY - toY;
        } else {
            dx = fromX - toX;
            dy = fromY - toY;
        }

        /* add x directions that lead towards to_x to the mask */
        if (dx < 0) {
            dirmask |= Direction.EAST.getMask();
        } else if (dx > 0) {
            dirmask |= Direction.WEST.getMask();
        }

        /* add y directions that lead towards to_y to the mask */
        if (dy < 0) {
            dirmask |= Direction.SOUTH.getMask();
        } else if (dy > 0) {
            dirmask |= Direction.NORTH.getMask();
        }

        /* return the result */
        return dirmask;
    }

    public static AttackVector enemyfireCannon(Context context, List<Drawable> objects, BaseMap combatMap,
            Direction dir, int startX, int startY, int avatarX, int avatarY) throws PartyDeathException {

        List<AttackVector> path = Utils.getDirectionalActionPath(combatMap, dir.getMask(), startX, startY, 1, 4,
                true, false, true);

        AttackVector target = null;
        int distance = 1;
        for (AttackVector v : path) {
            AttackResult res = fireAt(context, objects, combatMap, v, false, avatarX, avatarY);
            target = v;
            target.result = res;
            target.distance = distance;
            if (res != AttackResult.NONE) {
                break;
            }
            distance++;
        }

        return target;
    }

    public static AttackVector avatarfireCannon(Context context, List<Drawable> objects, BaseMap combatMap,
            Direction dir, int startX, int startY) {

        List<AttackVector> path = Utils.getDirectionalActionPath(combatMap, dir.getMask(), startX, startY, 1, 4,
                true, true, true);
        AttackVector target = null;
        try {
            int distance = 1;
            for (AttackVector v : path) {
                AttackResult res = fireAt(context, objects, combatMap, v, true, 0, 0);
                target = v;
                target.result = res;
                target.distance = distance;
                if (res != AttackResult.NONE) {
                    break;
                }
                distance++;
            }
        } catch (PartyDeathException e) {
            //not happening
        }

        return target;
    }

    public static List<AttackVector> getDirectionalActionPath(BaseMap combatMap, int dirmask, int x, int y,
            int minDistance, int maxDistance, boolean weaponCanAttackThroughObjects, boolean checkForCreatures,
            boolean isCannonBall) {

        List<AttackVector> path = new ArrayList<>();

        /*
         * try every tile in the given direction, up to the given range.
         * Stop when the the range is exceeded, or the action is blocked.
         */
        int nx = x;
        int ny = y;

        for (int distance = minDistance; distance <= maxDistance; distance++) {

            /* make sure our action isn't taking us off the map */
            if (nx > combatMap.getWidth() - 1 || nx < 0 || ny > combatMap.getHeight() - 1 || ny < 0) {
                break;
            }

            boolean blocked = combatMap.isTileBlockedForRangedAttack(nx, ny, checkForCreatures);

            Tile tile = combatMap.getTile(nx, ny);

            boolean canAttackOverSolid = (tile != null && tile.getRule() != null && weaponCanAttackThroughObjects);

            if (!blocked || canAttackOverSolid || isCannonBall) {
                path.add(new AttackVector(nx, ny));
            } else {
                path.add(new AttackVector(nx, ny));
                break;
            }

            if (Direction.isDirInMask(Direction.NORTH, dirmask)) {
                ny--;
            }
            if (Direction.isDirInMask(Direction.SOUTH, dirmask)) {
                ny++;
            }
            if (Direction.isDirInMask(Direction.EAST, dirmask)) {
                nx++;
            }
            if (Direction.isDirInMask(Direction.WEST, dirmask)) {
                nx--;
            }

        }

        return path;
    }

    public static void animateCannonFire(final BaseScreen screen, final Stage stage, final BaseMap map,
            final AttackVector av, final int sx, final int sy, final boolean avatarAttack) {

        Sounds.play(Sound.CANNON);

        final ProjectileActor p = new ProjectileActor(screen, Color.WHITE, sx, sy, av.result);

        Vector3 d = screen.getMapPixelCoords(av.x, av.y);

        p.addAction(sequence(moveTo(d.x, d.y, av.distance * .1f), Actions.run(new Runnable() {
            public void run() {
                switch (p.res) {
                case HIT:
                    p.resultTexture = Exodus.hitTile;
                    map.removeCreature(av.impactedCreature);
                    if (av.impactedDrawable != null && av.impactedDrawable.getShipHull() <= 0) {
                        av.impactedDrawable.remove();
                    }
                    break;
                case MISS:
                    p.resultTexture = Exodus.missTile;
                    break;
                }

                if (avatarAttack) {
                    Vector3 v = screen.getCurrentMapCoords();
                    screen.finishTurn((int) v.x, (int) v.y);
                }

            }
        }), Actions.fadeOut(.3f), removeActor(p)));

        stage.addActor(p);
    }

    private static AttackResult fireAt(Context context, List<Drawable> objects, BaseMap combatMap,
            AttackVector target, boolean avatarAttack, int avatarX, int avatarY) throws PartyDeathException {

        AttackResult res = AttackResult.NONE;

        //check for ship
        Drawable ship = null;
        for (Drawable d : objects) {
            if (d.getTile().getName().equals("ship") && d.getCx() == target.x && d.getCy() == target.y) {
                ship = d;
            }
        }

        if (ship != null) {
            ship.damageShip(-1, 10);
            target.impactedDrawable = ship;
            return AttackResult.HIT;
        }

        if (avatarAttack) {

            Creature creature = null;
            for (Creature c : combatMap.getCreatures()) {
                if (c.currentX == target.x && c.currentY == target.y) {
                    creature = c;
                    break;
                }
            }

            if (creature == null) {
                return res;
            }

            if (rand.nextInt(4) == 0) {
                res = AttackResult.HIT;
                target.impactedCreature = creature;
            } else {
                res = AttackResult.MISS;
            }

        } else if (target.x == avatarX && target.y == avatarY) {

            if (context.getTransport() == Transport.SHIP) {
                context.damageShip(-1, 10);
            } else {
                //context.getParty().damageParty(10, 25);
            }

            res = AttackResult.HIT;
        }

        return res;
    }

    public static AttackResult attackHit(Creature attacker, PartyMember defender) {
        int attackValue = rand.nextInt(256) + attacker.getAttackBonus();
        int defenseValue = defender.getDefense();
        return attackValue > defenseValue ? AttackResult.HIT : AttackResult.MISS;
    }

    private static boolean attackHit(PartyMember attacker, Creature defender) {
        int attackValue = rand.nextInt(256) + attacker.getAttackBonus();
        int defenseValue = defender.getDefense();
        return attackValue > defenseValue;
    }

    public static boolean dealDamage(PartyMember attacker, Creature defender, int damage) {
        int xp = defender.getExp();
        if (!damageCreature(defender, damage, true)) {
            attacker.awardXP(xp);
            return false;
        }
        return true;
    }

    public static boolean dealDamage(Creature attacker, PartyMember defender) throws PartyDeathException {
        int damage = attacker.getDamage();
        return defender.applyDamage(damage, true);
    }

    public static boolean damageCreature(Creature cr, int damage, boolean byplayer) {

        if (cr.getTile() != CreatureType.lord_british) {
            cr.setHP(Utils.adjustValueMin(cr.getHP(), -damage, 0));
        }

        switch (cr.getDamageStatus()) {

        case DEAD:
            if (byplayer) {
                Exodus.hud.add(String.format("%s Killed! Exp. %d", cr.getName(), cr.getExp()));
            } else {
                Exodus.hud.add(String.format("%s Killed!", cr.getName()));
            }
            return false;
        case FLEEING:
            Exodus.hud.add(String.format("%s Fleeing!", cr.getName()));
            break;

        case CRITICAL:
            Exodus.hud.add(String.format("%s Critical!", cr.getName()));
            break;

        case HEAVILYWOUNDED:
            Exodus.hud.add(String.format("%s Heavily Wounded!", cr.getName()));
            break;

        case LIGHTLYWOUNDED:
            Exodus.hud.add(String.format("%s Lightly Wounded!", cr.getName()));
            break;

        case BARELYWOUNDED:
            Exodus.hud.add(String.format("%s Barely Wounded!", cr.getName()));
            break;
        case FINE:
            break;
        default:
            break;
        }

        return true;
    }

    /**
     * using diagonals computes distance, used with finding nearest party member
     */
    public static int distance(MapBorderBehavior borderbehavior, int width, int height, int fromX, int fromY,
            int toX, int toY) {
        int dist = movementDistance(borderbehavior, width, height, fromX, fromY, toX, toY);

        if (dist <= 0) {
            return dist;
        }

        /* calculate how many fewer movements there would have been */
        dist -= Math.abs(fromX - toX) < Math.abs(fromY - toY) ? Math.abs(fromX - toX) : Math.abs(fromY - toY);

        return dist;
    }

    public static void animateAttack(Stage stage, final CombatScreen scr, PartyMember attacker, Direction dir,
            int x, int y, int range) {

        final AttackVector av = Utils.attack(scr.combatMap, attacker, dir, x, y, range);

        boolean magicHit = attacker.getPlayer().weapon.getWeapon().getHittile().equals("magic_flash");

        final ProjectileActor p = new ProjectileActor(scr, magicHit ? Color.CYAN : Color.RED, x, y, av.result);

        Vector3 v = scr.getMapPixelCoords(av.x, av.y);

        final TextureRegion hitTile = (magicHit ? Exodus.magicHitTile : Exodus.hitTile);

        p.addAction(sequence(moveTo(v.x, v.y, av.distance * .1f), new Action() {
            @Override
            public boolean act(float delta) {
                switch (p.res) {
                case HIT:
                    p.resultTexture = hitTile;
                    break;
                case MISS:
                    p.resultTexture = Exodus.missTile;
                    break;
                }

                scr.finishPlayerTurn();

                return true;
            }
        }, fadeOut(.2f), removeActor(p)));

        stage.addActor(p);
    }

    private static AttackVector attack(BaseMap combatMap, PartyMember attacker, Direction dir, int x, int y,
            int range) {

        WeaponType wt = attacker.getPlayer().weapon;
        boolean weaponCanAttackThroughObjects = wt.getWeapon().getAttackthroughobjects();

        List<AttackVector> path = Utils.getDirectionalActionPath(combatMap, dir.getMask(), x, y, 1, range,
                weaponCanAttackThroughObjects, true, false);

        AttackVector target = null;
        boolean foundTarget = false;
        int distance = 1;
        for (AttackVector v : path) {
            AttackResult res = attackAt(combatMap, v, attacker, range, distance);
            target = v;
            target.result = res;
            target.distance = distance;
            if (res != AttackResult.NONE) {
                foundTarget = true;
                break;
            }
            distance++;
        }

        if (wt.getWeapon().getLose() || (wt.getWeapon().getLosewhenranged() && (!foundTarget || distance > 1))) {
            if (attacker.loseWeapon() == WeaponType.NONE) {
                Exodus.hud.add("Last One!");
            }
        }

        return target;
    }

    public static void animateMagicAttack(Stage stage, final CombatScreen scr, PartyMember attacker, Direction dir,
            int x, int y, Spell spell, int minDamage, int maxDamage) {

        final AttackVector av = Utils.castSpellAttack(scr.combatMap, attacker, dir, x, y, minDamage, maxDamage,
                spell);

        Color color = Color.BLUE;
        switch (spell) {
        case FULGAR:
            color = Color.RED;
            break;
        case DECORP:
            color = Color.VIOLET;
            break;
        }

        final ProjectileActor p = new ProjectileActor(scr, color, x, y, av.result);

        Vector3 v = scr.getMapPixelCoords(av.x, av.y);

        p.addAction(sequence(moveTo(v.x, v.y, av.distance * .1f), new Action() {
            public boolean act(float delta) {

                switch (p.res) {
                case HIT:
                    p.resultTexture = Exodus.magicHitTile;
                    break;
                case MISS:
                    p.resultTexture = Exodus.missTile;
                    break;
                }

                //scr.replaceTile(av.leaveTileName, av.x, av.y);
                scr.finishPlayerTurn();

                return true;
            }
        }, fadeOut(.2f), removeActor(p)));

        stage.addActor(p);
    }

    private static AttackVector castSpellAttack(BaseMap combatMap, PartyMember attacker, Direction dir, int x,
            int y, int minDamage, int maxDamage, Spell spell) {

        List<AttackVector> path = Utils.getDirectionalActionPath(combatMap, dir.getMask(), x, y, 1, 11, true, true,
                false);

        AttackVector target = null;
        int distance = 1;
        for (AttackVector v : path) {
            AttackResult res = castAt(combatMap, v, attacker, minDamage, maxDamage, spell);
            target = v;
            target.result = res;
            target.distance = distance;
            if (res != AttackResult.NONE) {
                break;
            }
            distance++;
        }

        return target;
    }

    private static AttackResult castAt(BaseMap combatMap, AttackVector target, PartyMember attacker, int minDamage,
            int maxDamage, Spell spell) {

        AttackResult res = AttackResult.NONE;
        Creature creature = null;
        for (Creature c : combatMap.getCreatures()) {
            if (c.currentX == target.x && c.currentY == target.y) {
                creature = c;
                break;
            }
        }

        if (creature == null) {
            return res;
        }

        if (spell == Spell.FULGAR) {
            if ("fire".equals(creature.getResists())) {
                Sounds.play(Sound.EVADE);
                Exodus.hud.add("Resisted!\n");
                return AttackResult.MISS;
            }
        }

        Sounds.play(Sound.NPC_STRUCK);

        int attackDamage = ((minDamage >= 0) && (minDamage < maxDamage))
                ? rand.nextInt((maxDamage + 1) - minDamage) + minDamage
                : maxDamage;

        dealDamage(attacker, creature, attackDamage);

        return AttackResult.HIT;
    }

    private static AttackResult attackAt(BaseMap combatMap, AttackVector target, PartyMember attacker, int range,
            int distance) {
        AttackResult res = AttackResult.NONE;
        Creature creature = null;
        for (Creature c : combatMap.getCreatures()) {
            if (c.currentX == target.x && c.currentY == target.y) {
                creature = c;
                break;
            }
        }

        WeaponType wt = attacker.getPlayer().weapon;
        boolean wrongRange = (wt.getWeapon().getAbsolute_range() > 0 && (distance != range));

        if (creature == null || wrongRange) {
            return res;
        }

        if (!attackHit(attacker, creature)) {
            Exodus.hud.add("Missed!\n");
            res = AttackResult.MISS;
        } else {
            Sounds.play(Sound.NPC_STRUCK);
            dealDamage(attacker, creature, attacker.getDamage());
            res = AttackResult.HIT;
        }

        return res;
    }

    //was used for TMX type dungeon maps
    public static Texture peerGem(TiledMapTileLayer layer, String[] ids, TextureAtlas atlas, int cx, int cy)
            throws Exception {
        FileTextureData d = (FileTextureData) (atlas.getRegions().first().getTexture().getTextureData());
        InputStream is = ClassLoader.class
                .getResourceAsStream("/assets/graphics/" + d.getFileHandle().file().getName());
        BufferedImage sheet = ImageIO.read(is);
        BufferedImage canvas = new BufferedImage(32 * layer.getWidth(), 32 * layer.getHeight(),
                BufferedImage.TYPE_INT_ARGB);

        for (int y = 0; y < layer.getHeight(); y++) {
            for (int x = 0; x < layer.getWidth(); x++) {
                String val = ids[layer.getCell(x, layer.getHeight() - y - 1).getTile().getId()];
                DungeonTile tile = DungeonTile.getTileByName(val);
                if (tile == null) {
                    val = "brick_floor";
                }
                if (x == cx && y == cy) {
                    val = "avatar";
                }
                TextureAtlas.AtlasRegion ar = (TextureAtlas.AtlasRegion) atlas.findRegion(val);
                BufferedImage sub = sheet.getSubimage(ar.getRegionX(), ar.getRegionY(), 32, 32);
                canvas.getGraphics().drawImage(sub, x * 32, y * 32, 32, 32, null);
            }
        }

        java.awt.Image tmp = canvas.getScaledInstance(20 * 32, 20 * 32, Image.SCALE_AREA_AVERAGING);
        BufferedImage scaledCanvas = new BufferedImage(20 * 32, 20 * 32, BufferedImage.TYPE_INT_ARGB);
        scaledCanvas.getGraphics().drawImage(tmp, 0, 0, null);

        Pixmap p = Utils.createPixmap(scaledCanvas.getWidth(), scaledCanvas.getHeight(), scaledCanvas, 0, 0);

        Texture t = new Texture(p);
        p.dispose();

        return t;
    }

    //used for telescope viewing or in towns
    public static Texture peerGem(Maps map, TextureAtlas atlas) throws Exception {

        Texture t = null;

        if (map.getMap().getType() == MapType.city) {
            FileTextureData d = (FileTextureData) (atlas.getRegions().first().getTexture().getTextureData());
            InputStream is = ClassLoader.class
                    .getResourceAsStream("/assets/graphics/" + d.getFileHandle().file().getName());
            BufferedImage sheet = ImageIO.read(is);
            BufferedImage canvas = new BufferedImage(64 * 32, 64 * 32, BufferedImage.TYPE_INT_ARGB);

            for (int y = 0; y < 64; y++) {
                for (int x = 0; x < 64; x++) {
                    Tile ct = map.getMap().getTile(x, y);
                    TextureAtlas.AtlasRegion ar = (TextureAtlas.AtlasRegion) atlas.findRegion(ct.getName());
                    BufferedImage sub = sheet.getSubimage(ar.getRegionX(), ar.getRegionY(), 32, 32);
                    canvas.getGraphics().drawImage(sub, x * 32, y * 32, 32, 32, null);

                    Person cr = map.getMap().getPersonAt(x, y);
                    if (cr != null) {
                        canvas.getGraphics().fillRect(x * 32, y * 32, 32, 32);
                    }

                    Drawable obj = map.getMap().getObjectAt(x, y);
                    if (obj != null) {
                        canvas.getGraphics().fillRect(x * 32, y * 32, 32, 32);
                    }
                }
            }

            java.awt.Image tmp = canvas.getScaledInstance(20 * 32, 20 * 32, Image.SCALE_AREA_AVERAGING);
            BufferedImage scaledCanvas = new BufferedImage(20 * 32, 20 * 32, BufferedImage.TYPE_INT_ARGB);
            scaledCanvas.getGraphics().drawImage(tmp, 0, 0, null);

            Pixmap p = createPixmap(Exodus.SCREEN_WIDTH, Exodus.SCREEN_HEIGHT, scaledCanvas,
                    (Exodus.SCREEN_WIDTH - scaledCanvas.getWidth()) / 2,
                    (Exodus.SCREEN_HEIGHT - scaledCanvas.getHeight()) / 2);

            t = new Texture(p);
            p.dispose();

        } else if (map.getMap().getType() == MapType.dungeon) {
            //NO OP not needed since I added the minimap already on the HUD
        }

        return t;

    }

    //used for view gem on the world map only
    public static Texture peerGem(BaseMap worldMap, int avatarX, int avatarY, TextureAtlas atlas) throws Exception {
        FileTextureData d = (FileTextureData) (atlas.getRegions().first().getTexture().getTextureData());
        InputStream is = ClassLoader.class
                .getResourceAsStream("/assets/graphics/" + d.getFileHandle().file().getName());
        BufferedImage sheet = ImageIO.read(is);
        BufferedImage canvas = new BufferedImage(32 * 64, 32 * 64, BufferedImage.TYPE_INT_ARGB);

        int startX = avatarX - 32;
        int startY = avatarY - 32;
        int endX = avatarX + 32;
        int endY = avatarY + 32;
        int indexX = 0;
        int indexY = 0;
        for (int y = startY; y < endY; y++) {
            for (int x = startX; x < endX; x++) {
                int cx = x;
                if (x < 0) {
                    cx = 256 + x;
                } else if (x >= 256) {
                    cx = x - 256;
                }
                int cy = y;
                if (y < 0) {
                    cy = 256 + y;
                } else if (y >= 256) {
                    cy = y - 256;
                }
                Tile ct = worldMap.getTile(cx, cy);
                TextureAtlas.AtlasRegion ar = (TextureAtlas.AtlasRegion) atlas.findRegion(ct.getName());
                BufferedImage sub = sheet.getSubimage(ar.getRegionX(), ar.getRegionY(), 32, 32);
                canvas.getGraphics().drawImage(sub, indexX * 32, indexY * 32, 32, 32, null);

                Creature cr = worldMap.getCreatureAt(cx, cy);
                if (cr != null) {
                    canvas.getGraphics().fillRect(indexX * 32, indexY * 32, 32, 32);
                }

                Drawable obj = worldMap.getObjectAt(cx, cy);
                if (obj != null) {
                    canvas.getGraphics().fillRect(indexX * 32, indexY * 32, 32, 32);
                }

                indexX++;
            }
            indexX = 0;
            indexY++;
        }

        //add avatar in the middle
        canvas.getGraphics().fillRect((32 * 64) / 2, (32 * 64) / 2, 32, 32);

        java.awt.Image tmp = canvas.getScaledInstance(20 * 32, 20 * 32, Image.SCALE_AREA_AVERAGING);
        BufferedImage scaledCanvas = new BufferedImage(20 * 32, 20 * 32, BufferedImage.TYPE_INT_ARGB);
        scaledCanvas.getGraphics().drawImage(tmp, 0, 0, null);

        Pixmap p = createPixmap(Exodus.SCREEN_WIDTH, Exodus.SCREEN_HEIGHT, scaledCanvas,
                (Exodus.SCREEN_WIDTH - scaledCanvas.getWidth()) / 2,
                (Exodus.SCREEN_HEIGHT - scaledCanvas.getHeight()) / 2);

        Texture t = new Texture(p);
        p.dispose();
        return t;

    }

    public static Pixmap createPixmap(int width, int height, BufferedImage image, int sx, int sy) {

        int imgWidth = image.getWidth();
        int imgHeight = image.getHeight();

        Pixmap pix = new Pixmap(width, height, Pixmap.Format.RGBA8888);
        pix.setColor(0f, 0f, 0f, 1f);
        pix.fillRectangle(0, 0, width, height);

        int[] pixels = image.getRGB(0, 0, imgWidth, imgHeight, null, 0, width);

        for (int x = 0; x < imgWidth; x++) {
            for (int y = 0; y < imgHeight; y++) {
                int pixel = pixels[y * width + x];
                pix.drawPixel(sx + x, sy + y, getRGBA(pixel));
            }
        }

        return pix;
    }

    private static int getRGBA(int rgb) {
        int a = rgb >> 24;
        a &= 0x000000ff;
        int rest = rgb & 0x00ffffff;
        rest <<= 8;
        rest |= a;
        return rest;
    }

    public static Texture fillRectangle(int width, int height, Color color, float alpha) {
        Pixmap pix = new Pixmap(width, height, Pixmap.Format.RGBA8888);
        pix.setColor(color.r, color.g, color.b, alpha);
        pix.fillRectangle(0, 0, width, height);
        Texture t = new Texture(pix);
        pix.dispose();
        return t;
    }

}