1 /*************************************************************************** 2 * (C) Copyright 2019 - Arianne * 3 *************************************************************************** 4 *************************************************************************** 5 * * 6 * This program is free software; you can redistribute it and/or modify * 7 * it under the terms of the GNU General Public License as published by * 8 * the Free Software Foundation; either version 2 of the License, or * 9 * (at your option) any later version. * 10 * * 11 ***************************************************************************/ 12 package games.stendhal.server.entity.npc; 13 14 import java.awt.Point; 15 import java.awt.Rectangle; 16 import java.awt.geom.Rectangle2D; 17 import java.util.Collections; 18 import java.util.List; 19 20 import games.stendhal.common.Direction; 21 import games.stendhal.server.core.pathfinder.EntityGuide; 22 import games.stendhal.server.core.pathfinder.FixedPath; 23 import games.stendhal.server.core.pathfinder.Node; 24 import games.stendhal.server.entity.CollisionAction; 25 26 public abstract class PassiveNPC extends NPC { 27 28 private boolean teleports = false; 29 30 PassiveNPC()31 public PassiveNPC() { 32 put("title_type", "npc"); 33 34 baseSpeed = 0.2; 35 createPath(); 36 setSize(1, 1); 37 38 // NOTE: sub-classes must call updateModifiedAttributes() 39 } 40 41 /** 42 * Create path for the NPC. Sub classes can implement this method. 43 */ createPath()44 protected void createPath() { 45 } 46 47 /** 48 * Changed the entity's path to walk in the opposite direction 49 */ reversePath()50 public void reversePath() { 51 final EntityGuide guide = getGuide(); 52 if (!usesRandomPath() && guide.path.isLoop()) { 53 List<Node> reverseNodes = guide.path.getNodeList(); 54 55 // Sets the position for the reversed path 56 int reversePosition = (guide.path.getNodeList().size() - 1) - guide.getPreviousPosition(); 57 58 Collections.reverse(reverseNodes); 59 setPath(new FixedPath(reverseNodes, guide.path.isLoop()), reversePosition); 60 } 61 else { 62 stop(); 63 } 64 } 65 66 /** 67 * Retrieves the coordinates of the next position on the entity's path or `null`. 68 */ getPosFront()69 private Point getPosFront() { 70 if (hasPath()) { 71 final Node next = getGuide().nextNode(); 72 final Rectangle2D area = new Rectangle.Double(); 73 area.setRect(next.getX(), next.getY(), 1, 1); 74 final Direction dir = getDirectionToward(area); 75 76 return new Point(getX() + dir.getdx(), getY() + dir.getdy()); 77 } 78 79 return null; 80 } 81 82 /** 83 * Retrieves the coordinates of the previous position on the entity's path or `null`. 84 */ getPosBehind()85 private Point getPosBehind() { 86 if (hasPath()) { 87 final Node prev = getGuide().prevNode(); 88 final Rectangle2D area = new Rectangle.Double(); 89 area.setRect(prev.getX(), prev.getY(), 1, 1); 90 final Direction dir = getDirectionToward(area); 91 92 return new Point(getX() + dir.getdx(), getY() + dir.getdy()); 93 } 94 95 return null; 96 } 97 98 /** 99 * Checks if entity can move. 100 */ pathIsBlocked()101 private boolean pathIsBlocked() { 102 if (!usesRandomPath()) { 103 /* for entities with fixed path we check collision of adjacent nodes 104 * on entity's path only 105 */ 106 return !canMoveTo(getPosFront()) && !canMoveTo(getPosBehind()); 107 } 108 109 /* for entities with random path we check collision of all adjacent nodes */ 110 for (final Node node: getAdjacentNodes()) { 111 if (canMoveTo(node.getX(), node.getY())) { 112 return false; 113 } 114 } 115 116 return true; 117 } 118 119 /** 120 * Plan a new path to the old destination. 121 */ 122 @Override reroute()123 public void reroute() { 124 if (getPath().isLoop()) { 125 // If entity cannot be rerouted use reversePath 126 reversePath(); 127 } else { 128 super.reroute(); 129 } 130 } 131 132 @Override onMoved(final int oldX, final int oldY, final int newX, final int newY)133 protected void onMoved(final int oldX, final int oldY, final int newX, final int newY) { 134 super.onMoved(oldX, oldY, newX, newY); 135 } 136 137 @Override handleObjectCollision()138 protected void handleObjectCollision() { 139 if (pathIsBlocked()) { 140 stop(); 141 return; 142 } 143 144 CollisionAction action = getCollisionAction(); 145 146 if (usesRandomPath()) { 147 setRandomPathFrom(getX(), getY(), getMovementRange() / 2); 148 } else if (action == CollisionAction.REVERSE) { 149 reversePath(); 150 } else if (action == CollisionAction.REROUTE) { 151 reroute(); 152 } else { 153 stop(); 154 } 155 } 156 157 @Override handleSimpleCollision(final int nx, final int ny)158 protected void handleSimpleCollision(final int nx, final int ny) { 159 CollisionAction action = getCollisionAction(); 160 if (!ignoresCollision()) { 161 if (usesRandomPath()) { 162 setRandomPathFrom(getX(), getY(), getMovementRange() / 2); 163 } else if (action == CollisionAction.REROUTE) { 164 reroute(); 165 } else { 166 stop(); 167 } 168 } 169 } 170 setTeleportsFlag(final boolean teleports)171 public void setTeleportsFlag(final boolean teleports) { 172 this.teleports = teleports; 173 } 174 isTeleporter()175 public boolean isTeleporter() { 176 return teleports; 177 } 178 } 179