com.callidusrobotics.object.actor.AbstractActor.java Source code

Java tutorial

Introduction

Here is the source code for com.callidusrobotics.object.actor.AbstractActor.java

Source

/**
 * Copyright (C) 2013 Rusty Gerard
 *
 * 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 com.callidusrobotics.object.actor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import com.callidusrobotics.Message;
import com.callidusrobotics.attribute.Inventoriable;
import com.callidusrobotics.command.Command;
import com.callidusrobotics.locale.Coordinate;
import com.callidusrobotics.locale.MutableCoordinate;
import com.callidusrobotics.locale.Room;
import com.callidusrobotics.object.AbstractObject;
import com.callidusrobotics.object.EquipableData;
import com.callidusrobotics.object.EquipmentSlot;
import com.callidusrobotics.object.InventoriableData;
import com.callidusrobotics.object.Item;
import com.callidusrobotics.object.UseableData;
import com.callidusrobotics.swing.Console;
import com.callidusrobotics.swing.ConsoleGraphic;
import com.callidusrobotics.util.TrueColor;

@SuppressWarnings("PMD.TooManyMethods")
public abstract class AbstractActor extends AbstractObject {
    protected MutableCoordinate lastPosition;
    protected StatBlock statBlock;
    protected boolean npcsAreBarriers = true;
    protected boolean ignoreBarriers = false;
    protected final Map<EquipmentSlot, Item> equipment;
    protected final Random random = new Random();

    protected AbstractActor(final ConsoleGraphic consoleGraphic, final Coordinate position, final String name,
            final String description, final StatBlock statBlock) {
        super(consoleGraphic, position, name, description);

        lastPosition = new MutableCoordinate(position);
        this.statBlock = new StatBlock(statBlock);
        this.equipment = new HashMap<EquipmentSlot, Item>();
    }

    public String getNameFirstPerson() {
        return "The " + StringUtils.lowerCase(name);
    }

    public final boolean hasMoved() {
        return !position.equals(lastPosition);
    }

    @Override
    public String getDescription() {
        final StringBuffer description = new StringBuffer(64);

        description.append(super.getDescription());
        if (!StringUtils.isBlank(description)) {
            description.append(' ');
        }
        description.append("It looks ");

        final StatBlock stats = getCurrentStatBlock();
        final int healthPercent = stats.currentHp * 100 / stats.maxHp;

        if (healthPercent == 100) {
            description.append("unhurt.");
        } else if (healthPercent > 75) {
            description.append("grazed.");
        } else if (healthPercent > 50) {
            description.append("wounded.");
        } else if (healthPercent > 25) {
            description.append("severely wounded.");
        } else {
            description.append("nearly dead.");
        }

        return description.toString();
    }

    @Override
    public void setPosition(final int row, final int col) {
        lastPosition.setRow(position.getRow());
        lastPosition.setCol(position.getCol());

        position.setRow(row);
        position.setCol(col);
    }

    @Override
    public void setPosition(final Coordinate position) {
        lastPosition.setRow(this.position.getRow());
        lastPosition.setCol(this.position.getCol());

        this.position.setRow(position.getRow());
        this.position.setCol(position.getCol());
    }

    @Override
    public void draw(final Console console) {
        console.print(getRow(), getCol(), getConsoleGraphic());
    }

    public int getLastRow() {
        return lastPosition.getRow();
    }

    public int getLastCol() {
        return lastPosition.getCol();
    }

    public Coordinate getLastPosition() {
        return lastPosition;
    }

    public StatBlock getCurrentStatBlock() {
        StatBlock currentStats = new StatBlock(statBlock);

        for (final Item item : equipment.values()) {
            if (item.getStatBlock() != null) {
                currentStats = currentStats.combine(item.getStatBlock());
            }
        }

        if (currentStats.currentHp > currentStats.maxHp) {
            currentStats.currentHp = currentStats.maxHp;
        }

        statBlock.currentHp = currentStats.currentHp;
        return currentStats;
    }

    public void modifyStatBlock(final StatBlock statModifiers) {
        statBlock = statBlock.combine(statModifiers);
        final StatBlock modifiedStats = getCurrentStatBlock();

        if (statBlock.currentHp > modifiedStats.maxHp) {
            statBlock.currentHp = modifiedStats.maxHp;
        }
    }

    public void setCurrentHp(final int currentHp) {
        final StatBlock currentStats = getCurrentStatBlock();
        if (currentStats.maxHp < currentHp) {
            statBlock.setCurrentHp(currentStats.maxHp);
        } else {
            statBlock.setCurrentHp(currentHp);
        }
    }

    public Message attack(final AbstractActor other, final Command command, final Room currentRoom) {
        // Quick and dirty attack system
        final int toHitRoll = 1 + random.nextInt(20);
        final StatBlock attackerStats = getCurrentStatBlock();
        final StatBlock defenderStats = other.getCurrentStatBlock();
        Message message = new Message(command, getNameFirstPerson() + " misses.", getForeground(), TrueColor.BLACK);

        final int damageMean = command == Command.ATTACKMELEE ? getDamage() + attackerStats.getStrength()
                : getDamage();
        final int damageStdev = 1 + damageMean / 4;
        final int damageRoll = (int) (random.nextGaussian() * damageStdev) + damageMean;

        if (toHitRoll == 20) {
            // Automatic hit
            other.setCurrentHp(defenderStats.currentHp - damageRoll);
            message = new Message(command, getNameFirstPerson() + " critically strikes.", TrueColor.BLACK,
                    TrueColor.RED);
        } else if (toHitRoll != 1 && toHitRoll + attackerStats.agility > defenderStats.agility) {
            // Chance to hit
            final int damage = Math.max(0, damageRoll - defenderStats.defense);
            other.setCurrentHp(defenderStats.currentHp - damage);

            if (damage > 0) {
                message = new Message(command, getNameFirstPerson() + " strikes.", getForeground(),
                        TrueColor.BLACK);
            } else {
                message = new Message(command, getNameFirstPerson() + " strikes a glancing blow.", getForeground(),
                        TrueColor.BLACK);
            }
        }

        final Item weapon = this.equipment.get(EquipmentSlot.WEAPON);
        if (weapon != null) {
            weapon.fire(this, currentRoom);
        }

        return message;
    }

    protected int getDamage() {
        final Item weapon = equipment.get(EquipmentSlot.WEAPON);
        if (weapon != null) {
            return weapon.getDamage();
        }

        return 1;
    }

    protected int getRange() {
        final Item weapon = equipment.get(EquipmentSlot.WEAPON);
        if (weapon != null && weapon.getRange() > 0) {
            return weapon.getRange();
        }

        return 0;
    }

    public Item removeEquippedItem(final EquipmentSlot slot) {
        return equipment.remove(slot);
    }

    public Inventoriable getEquippedItem(final EquipmentSlot slot) {
        return equipment.get(slot);
    }

    public boolean equipItemInInventoryToEquipmentSlot(final int index, final EquipmentSlot slot) {
        final Item item = inventory.get(index);

        if (item.isEquipable(slot)) {
            inventory.remove(index);

            if (equipment.containsKey(slot)) {
                addItemToInventory(equipment.get(slot));
            }

            equipment.put(slot, item);
        }

        return item.isEquipable(slot);
    }

    @Override
    public List<Item> removeAllItemsFromInventory() {
        addItemsToInventory(new ArrayList<Item>(equipment.values()));
        equipment.clear();

        return super.removeAllItemsFromInventory();
    }

    protected boolean canMove(final Room currentRoom, final Coordinate offset) {
        if (offset == null) {
            return false;
        }

        final Coordinate desiredPosition = new Coordinate(getRow() + offset.getRow(), getCol() + offset.getCol());
        final boolean isInsideMap = currentRoom.checkCoordinatesRelative(desiredPosition);
        final boolean isBarrier = isInsideMap
                && ((npcsAreBarriers ? currentRoom.isBarrierRelative(desiredPosition) : false)
                        || (ignoreBarriers ? false : currentRoom.getTileRelative(desiredPosition).isBarrier()));
        final boolean onExitTile = currentRoom.getTileRelative(position).getRoomExit() != null;

        return (isInsideMap && !isBarrier) || (!isInsideMap && onExitTile);
    }

    protected boolean move(final Room currentRoom, final Coordinate offset) {
        if (offset == null) {
            return false;
        }

        final int rowOffset = offset.getRow();
        final int colOffset = offset.getCol();
        final Coordinate desiredPosition = new Coordinate(getRow() + rowOffset, getCol() + colOffset);
        final boolean isInsideMap = currentRoom.checkCoordinatesRelative(desiredPosition);

        // Update the last position so hasMoved() will be accurate
        setPosition(position);

        if (!canMove(currentRoom, offset)) {
            return false;
        }

        if (isInsideMap) {
            // Simple movement inside the room with no collision
            setPosition(desiredPosition);
        } else {
            // Player is trying to back out of the room he just entered -- make the GameMediator think we just stepped on the exit tile
            setPosition(getRow() - rowOffset, getCol() - colOffset);
            setPosition(getRow() + rowOffset, getCol() + colOffset);
        }

        return true;
    }

    public MutableCoordinate desiredDirection(final Command command) {
        boolean tryToMove = false;
        int rowOffset = 0;
        int colOffset = 0;

        if (command.toString().contains("NORTH")) {
            rowOffset = -1;
            tryToMove = true;
        }

        if (command.toString().contains("SOUTH")) {
            rowOffset = 1;
            tryToMove = true;
        }

        if (command.toString().contains("EAST")) {
            colOffset = 1;
            tryToMove = true;
        }

        if (command.toString().contains("WEST")) {
            colOffset = -1;
            tryToMove = true;
        }

        if (command == Command.ASCEND || command == Command.DECEND || command == Command.REST) {
            tryToMove = true;
        }

        if (tryToMove) {
            return new MutableCoordinate(rowOffset, colOffset);
        }

        return null;
    }

    public MutableCoordinate desiredPosition(final Command command) {
        final Coordinate position = desiredDirection(command);
        if (position != null) {
            return new MutableCoordinate(getRow() + position.getRow(), getCol() + position.getCol());
        }

        return new MutableCoordinate(this.position);
    }

    public Message processCommand(final Room currentRoom, final Command command) {
        Validate.notNull(currentRoom);
        Validate.notNull(command);

        // Clear the last position so that hasMoved() is up to date
        setPosition(getPosition());

        final Coordinate desiredDirection = desiredDirection(command);
        if (desiredDirection != null) {
            final boolean moved = move(currentRoom, desiredDirection);
            if (moved) {
                return new Message(command, null, null, null);
            }

            return new Message(Command.REST, null, null, null);
        }

        return new Message(Command.UNKNOWN, null, null, null);
    }

    public Item makeCorpse() {
        // TODO: Refactor this to use the item factory?
        final String identifier = StringUtils.replace(name, "\\s+", "_") + "_corpse";
        return new Item(identifier, new ConsoleGraphic('%', getForeground(), getBackground()), 1,
                new InventoriableData(name + " Corpse", "The remains of a " + name + ".", false),
                new UseableData(false, false, -1, null, null, null, null), new EquipableData(0, 0, null, null));
    }
}