Java tutorial
/******************************************************************************* * This file is part of AnathemaRL. * * AnathemaRL 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. * * AnathemaRL 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 AnathemaRL. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.anathema_roguelike.characters; import java.util.Collection; import java.util.LinkedList; import java.util.Optional; import com.anathema_roguelike.characters.abilities.Ability; import com.anathema_roguelike.characters.abilities.AbilitySet; import com.anathema_roguelike.characters.actions.Action; import com.anathema_roguelike.characters.ai.AIPathFinder; import com.anathema_roguelike.characters.ai.Faction; import com.anathema_roguelike.characters.attacks.BasicAttackAbility; import com.anathema_roguelike.characters.classes.CharacterClass; import com.anathema_roguelike.characters.events.MoveEvent; import com.anathema_roguelike.characters.events.ResourceChangedEvent; import com.anathema_roguelike.characters.events.TurnEvent; import com.anathema_roguelike.characters.inventory.Inventory; import com.anathema_roguelike.characters.inventory.PrimaryWeapon; import com.anathema_roguelike.environment.Direction; import com.anathema_roguelike.environment.Environment; import com.anathema_roguelike.environment.Location; import com.anathema_roguelike.environment.Point; import com.anathema_roguelike.environment.features.Doorway; import com.anathema_roguelike.environment.terrain.grounds.Stairs; import com.anathema_roguelike.main.Config; import com.anathema_roguelike.main.Entity; import com.anathema_roguelike.main.Game; import com.anathema_roguelike.main.display.BufferMask; import com.anathema_roguelike.main.display.Color; import com.anathema_roguelike.main.display.VisualRepresentation; import com.anathema_roguelike.main.ui.messages.Message; import com.anathema_roguelike.main.utilities.pathfinding.Path; import com.anathema_roguelike.stats.HasStats; import com.anathema_roguelike.stats.StatSet; import com.anathema_roguelike.stats.characterstats.CharacterStat; import com.anathema_roguelike.stats.characterstats.CharacterStatSet; import com.anathema_roguelike.stats.characterstats.attributes.Attribute; import com.anathema_roguelike.stats.characterstats.resources.BoundedResource; import com.anathema_roguelike.stats.characterstats.resources.CurrentHealth; import com.anathema_roguelike.stats.characterstats.resources.Resource; import com.anathema_roguelike.stats.characterstats.secondarystats.LightEmission; import com.anathema_roguelike.stats.characterstats.secondarystats.detection.Visibility; import com.anathema_roguelike.stats.characterstats.secondarystats.detection.VisibilityLevel; import com.anathema_roguelike.stats.effects.Effect; import com.anathema_roguelike.stats.effects.HasEffect; import com.anathema_roguelike.stimuli.PercievedStimulus; import com.anathema_roguelike.stimuli.Sight; import com.anathema_roguelike.stimuli.Stimulus; import com.anathema_roguelike.stimuli.StimulusEvent; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; public abstract class Character extends Entity implements HasStats<Character, CharacterStat> { private int faction; private int level = 1; private CharacterClass charClass; private boolean actionRemaining = false; private long turn = 0; private AbilitySet abilities = new AbilitySet(); private EventBus eventBus = new EventBus(); private Inventory inventory = new Inventory(this, eventBus); private boolean alive = true; private double facing = Direction.UP; BufferMask currentVisibility; private CharacterStatSet stats = new CharacterStatSet(this, eventBus); private LinkedList<PercievedStimulus> percievedStimuli = new LinkedList<>(); public abstract void onDeath(); protected abstract void onTurn(); public Character(Optional<VisualRepresentation> representation) { super(representation); Game.getInstance().getEventBus().register(this); eventBus.register(this); new BasicAttackAbility().grant(this); } public void takeTurn() { setActionRemaining(true); pruneStimuli(); onTurn(); } public EventBus getEventBus() { return eventBus; } public void registerHandler(Object obj) { eventBus.register(obj); } public void generateStimulus(Stimulus stimulus) { Game.getInstance().getEventBus().post(new StimulusEvent(stimulus)); } public void unregisterHandler(Object obj) { eventBus.unregister(obj); } public void postEvent(Object obj) { eventBus.post(obj); } public void levelUp() { level++; getStat(CurrentHealth.class).reset(); } @Override public StatSet<Character, CharacterStat> getStatSet() { return stats; } public boolean getActionRemaining() { return actionRemaining; } public void setActionRemaining(boolean actionRemaining) { this.actionRemaining = actionRemaining; } public Inventory getInventory() { return inventory; } public int getFaction() { return faction; } public CharacterClass getCharClass() { return charClass; } public int getLevel() { return level; } public <T extends Ability> Iterable<T> getAbilities(Class<T> superclass) { return abilities.get(superclass); } public <T extends Ability> Iterable<T> getAbilities(Class<T> superclass, Predicate<T> predicate) { return abilities.get(superclass, predicate); } public void addAbility(Ability ability) { abilities.add(ability); } public void removeAbility(Ability ability) { abilities.remove(ability); } public boolean hasAbility(Class<? extends Ability> ability) { return !Iterables.isEmpty(getAbilities(ability)); } protected void setFaction(int faction) { this.faction = faction; } public void setClass(CharacterClass charClass) { this.charClass = charClass; } public void basicAttack(Point target) { getAbilities(BasicAttackAbility.class).iterator().next().activate(target); } @Subscribe public void handleResourceChangedEvent(ResourceChangedEvent event) { //TODO not sure if i need to do anything in this case but figure its a good thing to have an event for } @Subscribe public void handleTurnEvent(TurnEvent e) { turn++; } public long getTurn() { return turn; } public boolean moveCharacterBy(int x, int y) { return moveCharacterTo(new Point(getX() + x, getY() + y), false); } public boolean move(int direction) { return moveCharacterTo(Direction.offset(getPosition(), direction), false); } public void takeAction(Action action) { action.take(this); } public boolean moveCharacterTo(Point point, boolean teleport) { Environment level = Game.getInstance().getState().getEnvironment(getDepth()); if (!level.isPassable(point)) { return false; } else if (level.getLocation(point).getTerrain() instanceof Doorway && this instanceof Player) { Doorway door = (Doorway) level.getLocation(point).getTerrain(); door.open(); return true; } else { if (teleport) { Collection<Character> characters = level.getEntitiesAt(point, Character.class); if (characters.size() > 0) { return false; } else { getEnvironment().moveEntityTo(this, point); return true; } } else { AIPathFinder pathfinder = new AIPathFinder(this); Path path = pathfinder.getPath(getPosition(), point); //shouldnt try to move into the space they are already in path.remove(0); for (Point p : path) { Collection<Character> characters = level.getEntitiesAt(p, Character.class); if (characters.size() > 0) { Character character = level.getEntitiesAt(p, Character.class).iterator().next(); if (!Faction.friendly(character, this)) { basicAttack(character.getPosition()); } return false; } //need to decide how/if to not pay cost when the character cant actually move getEnvironment().getEventBus().post(new MoveEvent(this, p)); generateStimulus(new Sight(p, 100, this)); getEnvironment().moveEntityTo(this, p); } return true; } } } public boolean moveCharacterTo(Point point) { return moveCharacterTo(point, false); } public boolean isAlive() { return alive; } public void kill() { alive = false; } public int getPrimaryWeaponDamage() { return inventory.getSlot(PrimaryWeapon.class).getEquippedItem().getWeaponDamage(); } public int getResourceMax(Class<? extends BoundedResource> resource) { return getStat(resource).getMaximum(); } public void setAbilityScore(Class<? extends Attribute> ability, int amount) { stats.getStat(ability).setScore(amount); } public void setResource(Character initiator, HasEffect<? extends Effect<Character, ?>> source, Class<? extends Resource> stat, int amount) { stats.getStat(stat).set(initiator, source, amount); } public void modifyResource(Character initiator, HasEffect<? extends Effect<Character, ?>> source, Class<? extends Resource> stat, int amount) { Resource resource = stats.getStat(stat); resource.modify(initiator, source, amount); } public boolean takeStairs(int direction) { Environment level = Game.getInstance().getState().getEnvironment(getDepth()); int zOffset = 0; int newStairDirection = 0; if (level.getLocation(getPosition()).getTerrain() instanceof Stairs) { Stairs stairs = (Stairs) level.getLocation(getPosition()).getTerrain(); if (stairs.takeStairs(direction)) { if (direction == Direction.UP) { zOffset = -1; newStairDirection = Direction.DOWN; } else if (direction == Direction.DOWN) { zOffset = 1; newStairDirection = Direction.UP; } if ((getDepth() + zOffset) < 0 || (getDepth() + zOffset) >= Config.DUNGEON_DEPTH) { Game.getInstance().getUserInterface().addMessage(new Message("No!", Color.ERROR)); return false; } Environment newLevel = Game.getInstance().getState().getEnvironment(getDepth() + zOffset); Point stairsPosition = newLevel.getStairs(newStairDirection).getPosition(); setDepth(getDepth() + zOffset); level.removeEntity(this); newLevel.addEntity(this, stairsPosition); return true; } } return false; } public Environment getEnvironment() { return Game.getInstance().getState().getEnvironment(getDepth()); } public VisibilityLevel getVisibilityOf(Character character) { if (character == null) { return VisibilityLevel.IMPERCEPTIBLE; } if (getCurrentVisibility().get(character.getX(), character.getY())) { if (getPosition().isAdjacentTo(character.getPosition())) { return VisibilityLevel.EXPOSED; } return character.getStat(Visibility.class).getVisibilityLevel(); } else { return VisibilityLevel.IMPERCEPTIBLE; } } @Override public boolean isVisibleTo(Character character) { return character.getVisibilityOf(this).ordinal() >= 3; } public BufferMask getCurrentVisibility() { if (currentVisibility == null) { computeVisibility(); } return currentVisibility; } public void computeVisibility() { getEnvironment().getLightLevels().recomputeLightLevels(); currentVisibility = getEnvironment().getLitFOVProcessor().computeLitFOVMask(this); } public double getFacing() { return facing; } public void setFacing(double facing) { this.facing = facing; } @Override public double getLightEmission() { return getStatAmount(LightEmission.class); } @Subscribe public void percieveStimulus(StimulusEvent e) { Stimulus stimulus = e.getStimulus(); if (stimulus.getSource().isPresent() && Faction.friendly(this, stimulus.getSource().get())) { return; } Optional<PercievedStimulus> percieved = e.getPercievedStimulus(this); percieved.ifPresent(p -> percievedStimuli.add(p)); } protected void pruneStimuli() { percievedStimuli.removeIf(s -> s.getMagnitude() <= 0 || s.getPosition().equals(getPosition())); } public LinkedList<PercievedStimulus> getPercievedStimuli() { return percievedStimuli; } public Location getLocation() { return getEnvironment().getLocation(getPosition()); } }