1
2
3
4 package spellcast.beings;
5
6 import org.apache.log4j.Logger;
7
8 import spellcast.damage.Damage;
9
10 import spellcast.enchantment.Enchantment;
11
12 import spellcast.model.Id;
13 import spellcast.model.PropertyUpdater;
14
15 import java.beans.PropertyChangeListener;
16 import java.beans.PropertyChangeSupport;
17
18 import java.io.IOException;
19 import java.io.ObjectInputStream;
20 import java.io.Serializable;
21
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.List;
25
26
27 /***
28 * The base class for all <code>Being</code>s in Spellcast.
29 * TODO: add equals() and hashCode()
30 *
31 * @author Barrie Treloar
32 * @version $Revision: 1.1 $
33 */
34 public abstract class Being implements Serializable, PropertyUpdater, IBeing {
35 /*** The property for the being's active value. */
36 public static final String ACTIVE_PROP = "active";
37
38 /*** Use serialVersionUID for interoperability. */
39 private static final long serialVersionUID = 1L;
40
41 /*** Log4J logger. */
42 private Logger logger = Logger.getLogger(Being.class);
43
44 /*** The enchantments that are in effect on this <code>Being</code>. */
45 private List enchantments = new ArrayList();
46
47 /***
48 * Used when a property changes to notify
49 * <code>PropertyChangeListener</code>s of the change. Not all properties
50 * are notified when a change occurs. Each class should document what
51 * properties get updated.
52 */
53 private transient PropertyChangeSupport propertySupport;
54
55 /*** Determines if this <code>Being</code> is active this turn. */
56 private boolean active;
57
58 /*** Determines if this <code>Being</code> is alive. */
59 private boolean alive;
60
61 /*** The <code>Id</code> of this <code>Being</code>. */
62 private Id id;
63
64 /*** The hit points of this <code>Being</code>. */
65 private int hitPoints;
66
67 /***
68 * The Maximum Hit Points for this Being. A creature can never be healed
69 * beyond this value.
70 */
71 private int maxHitPoints;
72
73 /*** The name of this <code>Being</code>. */
74 private String name;
75
76 /*** The <code>Gender</code> of this <code>Being</code>. */
77 private Gender gender;
78
79 /***
80 * Determines if this <code>Being</code> should leave a corpse behind upon
81 * death.
82 */
83 private boolean leavesCorpse;
84
85 private boolean nullable;
86
87 /***
88 * Provided for serialization, do not use as a constructor.
89 */
90 protected Being() {
91 }
92
93 /***
94 * Creates a new BeingImpl object.
95 *
96 * @param anId the<code>Id</code> to use for this <code>Being</code>.
97 * @param aName the name of this<code>Being</code>.
98 * @param aGender the<code>Gender</code> of this <code>Being</code>.
99 * @param theMaxHitPoints the maximum hit points of this<code>Being</code>.
100 */
101 public Being(final Id anId, final String aName, final Gender aGender,
102 final int theMaxHitPoints) {
103 setId(anId);
104 setName(aName);
105 setGender(aGender);
106 setMaxHitPoints(theMaxHitPoints);
107 propertySupport = new PropertyChangeSupport(getId());
108 setAlive(true);
109 setHitPoints(getMaxHitPoints());
110 }
111
112 /***
113 * Necessary in order to recreate the propertySupport object but without
114 * the listeners as they are client or server dependent.
115 *
116 * @param in stream that contains the serialized object
117 *
118 * @throws IOException {@link java.io.ObjectInputStream#readObject see
119 * <code>ObjectInputStream.readObject()</code>}
120 * @throws ClassNotFoundException {@link
121 * java.io.ObjectInputStream#readObject see
122 * <code>ObjectInputStream.readObject()</code>}
123 *
124 * @see Serializable
125 * @see java.io.ObjectInputStream#readObject
126 */
127 private void readObject(final ObjectInputStream in)
128 throws IOException, ClassNotFoundException {
129 in.defaultReadObject();
130 propertySupport = new PropertyChangeSupport(getId());
131 }
132
133 /***
134 * The <code>PropertyChangeSupport</code> object used to notify listeners
135 * of changes to properties.
136 *
137 * @return the <code>PropertyChangeSupport</code>.
138 */
139 protected final PropertyChangeSupport getPropertySupport() {
140 return propertySupport;
141 }
142
143 /***
144 * Adds a <code>PropertyChangeListener</code> to this <code>Being</code>.
145 *
146 * @param listener the <code>PropertyChangeListener</code> to add.
147 */
148 public final void addPropertyChangeListener(
149 final PropertyChangeListener listener) {
150 propertySupport.addPropertyChangeListener(listener);
151 }
152
153 /***
154 * Remove a <code>PropertyChangeListener</code> from this
155 * <code>Being</code>.
156 *
157 * @param listener the <code>PropertyChangeListener</code> to remove.
158 */
159 public final void removePropertyChangeListener(
160 final PropertyChangeListener listener) {
161 propertySupport.removePropertyChangeListener(listener);
162 }
163
164 /***
165 * Add an enchantment to this Being.
166 *
167 * @param anEnchantment the enchantment to add.
168 */
169 public final void addEnchantment(final Enchantment anEnchantment) {
170 enchantments.add(anEnchantment);
171 }
172
173 /***
174 * Remove the specified <code>Enchantment</code> from this
175 * <code>Being</code>. If the enchantment does not exist then nothing is
176 * removed.
177 *
178 * @param enchantment the enchantment to remove. Ignored if the enchantment
179 * is not in effect on this <code>Being</code>.
180 */
181 public final void removeEnchantment(final Enchantment enchantment) {
182 enchantments.remove(enchantment);
183 }
184
185 /***
186 * Return an array of all <code>Enchantment</code>s that are in effect upon
187 * this <code>Being</code>.
188 *
189 * @return an array of all <code>Enchantment</code>s that are in effect
190 * upon this <code>Being</code>.
191 */
192 public final Enchantment[] getEnchantments() {
193 return (Enchantment[]) enchantments.toArray(new Enchantment[0]);
194 }
195
196 /***
197 * Each <code>Enchantment</code> on this <code>Being</code> is notified
198 * that the turn has been completed via <code>turnComplete</code>. If the
199 * <code>Enchantment</code> indicates that it should no longer be in
200 * effect then it is removed.
201 *
202 * @see Enchantment#turnComplete
203 */
204 public final void updateEnchantments() {
205 Iterator iter = enchantments.iterator();
206
207 while (iter.hasNext()) {
208 Enchantment enchantment = (Enchantment) iter.next();
209
210 if (enchantment.turnComplete()) {
211 iter.remove();
212 }
213 }
214 }
215
216 /***
217 * Indicates whether this <code>Being</code> is active this turn.
218 *
219 * @return whether this <code>Being</code> is active this turn.
220 */
221 public final boolean isActive() {
222 return active;
223 }
224
225 /***
226 * Sets this <code>Being</code> as active for this turn.
227 *
228 * @param isActive if this<code>Being</code> is active for this turn, false
229 * otherwise.
230 */
231 public final void setActive(final boolean isActive) {
232 active = isActive;
233 propertySupport.firePropertyChange(ACTIVE_PROP, null,
234 new Boolean(active));
235 }
236
237 /***
238 * Determine if this <code>Being</code> is currently alive. True if alive,
239 * false otherwise.
240 *
241 * @return true if the <code>Being</code> is alive, false otherwise.
242 */
243 public final boolean isAlive() {
244 return alive;
245 }
246
247 /***
248 * Sets whether this <code>Being</code> is currently alive. True if alive,
249 * false otherwise.
250 *
251 * @param isAlive true if alive, false otherwise.
252 */
253 public final void setAlive(final boolean isAlive) {
254 alive = isAlive;
255 }
256
257 /***
258 * Determine if this being should die. TODO this should probably return an
259 * object which indicates: who killed the Being and what killed them.
260 */
261 public final void shouldDie() {
262 if (getHitPoints() == 0) {
263 setAlive(false);
264 }
265 }
266
267 /***
268 * The <code>Id</code> of this <code>Being</code>.
269 *
270 * @return the <code>Id</code> of this <code>Being</code>.
271 */
272 public final Id getId() {
273 return id;
274 }
275
276 /***
277 * Set the <code>Id</code> of this <code>Being</code>.
278 *
279 * @param theId the <code>Id</code> of this <code>Being</code>.
280 */
281 public final void setId(final Id theId) {
282 id = theId;
283 }
284
285 /***
286 * Returns the value of the current Hit Points for this <code>Being</code>.
287 * The current hit points will never exceed max hit points.
288 *
289 * @return the value of the current Hit Points for this <code>Being</code>.
290 */
291 public final int getHitPoints() {
292 return hitPoints;
293 }
294
295 /***
296 * Set the value for the current <code>HitPoints</code> for this Being. Any
297 * values that are negative are converted into 0. Any values that are
298 * greater than <code>getMaxHitPoints</code> are converted into
299 * <code>getMaxHitPoints</code>.
300 *
301 * @param theHitPoints the value to set the Hit Points to. Any negative
302 * values are converted to 0. Any values that are greater
303 * than<code>getMaxHitPoints</code> are converted into
304 * <code>getMaxHitPoints</code>.
305 */
306 public final void setHitPoints(final int theHitPoints) {
307 if (theHitPoints < 0) {
308 hitPoints = 0;
309 } else if (theHitPoints > getMaxHitPoints()) {
310 hitPoints = getMaxHitPoints();
311 } else {
312 hitPoints = theHitPoints;
313 }
314 }
315
316 /***
317 * Returns the value for the Maximum Hit Points for this
318 * <code>Being</code>.
319 *
320 * @return the maximum hit points for this <code>Being</code>.
321 */
322 public final int getMaxHitPoints() {
323 return maxHitPoints;
324 }
325
326 /***
327 * Set the value for the Maximum Hit Points for this <code>Being</code>.
328 * Only positive values are allowed for maxHitPoints. Any invalid values
329 * are silently ignored and the value is set to 0.
330 *
331 * @param theMaxHitPoints the value to set the Maximum Hit Points to. Valid
332 * values are > 0. Invalid values are silently ignored and the
333 * value is set to 0.
334 */
335 public final void setMaxHitPoints(final int theMaxHitPoints) {
336 if (theMaxHitPoints > 0) {
337 maxHitPoints = theMaxHitPoints;
338 } else {
339 maxHitPoints = 0;
340 }
341 }
342
343 /***
344 * The specified damage is dealt to this Being. If there are any
345 * <code>Enchantment</code>s that provide resistance to the
346 * <code>DamageType</code> being dealt then the damage is reduced to zero.
347 *
348 * @param damageDealt the damage to deal to this <code>Being</code>.
349 */
350 public final void takeDamage(final Damage damageDealt) {
351 boolean isResistant = false;
352 Iterator iter = enchantments.iterator();
353 while (iter.hasNext()) {
354 Enchantment theEnchantment = (Enchantment) iter.next();
355 if (theEnchantment.isResistant(damageDealt.getType())) {
356 isResistant = true;
357 break;
358 }
359 }
360 if (!isResistant) {
361 setHitPoints(getHitPoints() - damageDealt.getValue());
362 }
363 }
364
365 /***
366 * The name of the <code>Being</code>.
367 *
368 * @return the name of the <code>Being</code>.
369 */
370 public final String getName() {
371 return name;
372 }
373
374 /***
375 * Sets the name of this <code>Being</code>.
376 *
377 * @param theName the name for this <code>Being</code>.
378 */
379 public final void setName(final String theName) {
380 name = theName;
381 }
382
383 /***
384 * Return the <code>Gender</code> of this <code>Being</code>.
385 *
386 * @return the <code>Gender</code> of this <code>Being</code>.
387 */
388 public final Gender getGender() {
389 return gender;
390 }
391
392 /***
393 * Sets the <code>Gender</code> of this <code>Being</code>.
394 *
395 * @param theGender the <code>Gender</code> of this <code>Being</code>.
396 */
397 public final void setGender(final Gender theGender) {
398 gender = theGender;
399 }
400
401 /***
402 * Indicates whether this <code>Being</code> should leave a corpse when it
403 * dies.
404 *
405 * @return true if a corpse should be created on death, false otherwise.
406 */
407 public final boolean getLeavesCorpse() {
408 return leavesCorpse;
409 }
410
411 /***
412 * This <code>Being</code> will leave a corpse on death if this value is
413 * true, otherwise no corpse will be created.
414 *
415 * @param shouldLeaveCorpse true if a corpse should be created on death,
416 * false otherwise.
417 */
418 public final void setLeavesCorpse(final boolean shouldLeaveCorpse) {
419 leavesCorpse = shouldLeaveCorpse;
420 }
421
422 /***
423 * {@inheritDoc}
424 */
425 public final boolean isNull() {
426 return nullable;
427 }
428
429 /***
430 * Set whether this object is a <code>Nullable</code>.
431 *
432 * @param isNullable true if this object is a Null instance, false
433 * otherwise.
434 */
435 public final void setNull(boolean isNullable) {
436 nullable = isNullable;
437 }
438
439 /***
440 * Return a Null <code>IBeing</code>.
441 *
442 * @return an instance of the Null <code>IBeing</code>.
443 */
444 public static IBeing newNull() {
445 return new NullBeing();
446 }
447
448 }