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