/* $Id: CreatureRespawnPoint.java,v 1.28 2010/11/28 21:56:24 martinfuchs Exp $ */
/***************************************************************************
* (C) Copyright 2003 - Marauroa *
***************************************************************************
***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
package games.stendhal.server.entity.mapstuff.spawner;
import games.stendhal.common.Rand;
import games.stendhal.server.core.engine.SingletonRepository;
import games.stendhal.server.core.engine.StendhalRPZone;
import games.stendhal.server.core.events.TurnListener;
import games.stendhal.server.core.rp.StendhalRPAction;
import games.stendhal.server.entity.creature.Creature;
import java.util.LinkedList;
import java.util.List;
import java.util.Observer;
import org.apache.log4j.Logger;
/**
* RespawnPoints are points at which creatures can appear. Several creatures can
* be spawned, until a maximum has been reached (note that this maximum is
* usually 1); then the RespawnPoint will stop spawning creatures until at least
* one of the creatures has died. It will then continue to spawn creatures. A
* certain time must pass between respawning creatures; this respawn time is
* usually dependent of the type of the creatures that are spawned.
*
* Each respawn point can only spawn one type of creature. The Prototype design
* pattern is used; the <i>prototypeCreature</i> will be copied to create new
* creatures.
*/
public class CreatureRespawnPoint implements TurnListener {
/** longest possible respawn time in turns. half a year - should be longer than the
* server is up in one phase */
private static final int MAX_RESPAWN_TIME = 200 * 60 * 24 * 30 * 6;
/** minimum respawn time in turns. about 10s */
private static final int MIN_RESPAWN_TIME = 33;
/** the logger instance. */
private static final Logger logger = Logger.getLogger(CreatureRespawnPoint.class);
protected final StendhalRPZone zone;
private LinkedList<Observer> observers = new LinkedList<Observer>();
protected final int x;
protected final int y;
/**
* The number of creatures spawned here that can exist at the same time.
*/
private final int maximum;
/**
* This is the prototype; it will be copied to create new creatures that
* will be spawned here.
*/
protected Creature prototypeCreature;
/** All creatures that were spawned here and that are still alive. */
protected final List<Creature> creatures;
/**
* Stores if this respawn point is currently waiting for a creature to
* respawn.
*/
protected boolean respawning;
/**
* How long it takes to respawn a creature. This defaults to the creature's
* default respawn time. It is in turns.
*/
private int respawnTime;
/**
* Creates a new RespawnPoint.
*
* @param zone
* @param x
* @param y
* @param creature
* The prototype creature
* @param maximum
* The number of creatures spawned here that can exist at the
* same time
*/
public CreatureRespawnPoint(final StendhalRPZone zone, final int x, final int y,
final Creature creature, final int maximum) {
this.zone = zone;
this.x = x;
this.y = y;
this.prototypeCreature = creature;
this.maximum = maximum;
this.respawnTime = creature.getRespawnTime();
this.creatures = new LinkedList<Creature>();
respawning = true;
// don't respawn in next turn!
SingletonRepository.getTurnNotifier().notifyInTurns(calculateNextRespawnTurn(), this);
}
/**
* Creates a new RespawnPoint.
*
* @param zone
* @param x
* @param y
* @param creature
* The prototype creature
* @param maximum
* The number of creatures spawned here that can exist at the
* same time
*/
public CreatureRespawnPoint(StendhalRPZone zone, int x,
int y, Creature creature, int maximum, final Observer observer) {
this(zone, x, y, creature, maximum);
this.observers.add(observer);
}
public Creature getPrototypeCreature() {
return prototypeCreature;
}
/**
* Sets the time it takes to respawn a creature. Note that this value
* defaults to the creature's default respawn time.
* @param respawnTime the middled delay between spawns in turns
*/
public void setRespawnTime(final int respawnTime) {
this.respawnTime = respawnTime;
}
/**
* Notifies this respawn point about the death of a creature that was
* spawned here.
*
* @param dead
* The creature that has died
*/
public void notifyDead(final Creature dead) {
if (!respawning) {
// start respawning a new creature
respawning = true;
SingletonRepository.getTurnNotifier().notifyInTurns(
calculateNextRespawnTurn(), this);
}
creatures.remove(dead);
}
/**
* Is called when a new creature is ready to pop up.
*
* @see games.stendhal.server.core.events.TurnListener#onTurnReached(int)
*/
public void onTurnReached(final int currentTurn) {
respawn();
// Is this all or should we spawn more creatures?
if (creatures.size() == maximum) {
respawning = false;
} else {
SingletonRepository.getTurnNotifier().notifyInTurns(
calculateNextRespawnTurn(), this);
}
}
/**
* Calculates a randomized respawn time.
* @return the amount of turns calculated
*/
protected int calculateNextRespawnTurn() {
final int time = Rand.randExponential(respawnTime);
// limit between MAX_ and MIN_
return Math.max(Math.min(time, MAX_RESPAWN_TIME), MIN_RESPAWN_TIME);
}
/**
* Checks how many creatures which were spawned here are currently alive.
*
* @return amount of living creatures
*/
public int size() {
return creatures.size();
}
/**
* function returns X coord of this respawn point
* @return - x coord
*/
public int getX() {
return this.x;
}
/**
* function returns Y coord of this respawn point
* @return - y coord
*/
public int getY() {
return this.y;
}
/**
*
*/
public void setPrototypeCreature(final Creature creature) {
this.prototypeCreature = creature;
}
/**
* add observer to observers list
* @param observer - observer to add
*/
public void addObserver(final Observer observer) {
observers.add(observer);
}
/**
* remove observer from list
* @param observer - observer to remove
*/
public void removeObserver(final Observer observer) {
observers.remove(observer);
}
/**
* return zone where respawn point placed
* @return - zone where respawn point placed
*/
public StendhalRPZone getZone() {
return this.zone;
}
/**
* Pops up a new creature.
*/
protected void respawn() {
try {
// clone the prototype creature
final Creature newentity = prototypeCreature.getNewInstance();
// A bit of randomization to make Joan and Snaketails a bit happier.
// :)
newentity.setAtk(Rand.randGaussian(newentity.getAtk(),
newentity.getAtk() / 10));
newentity.setDef(Rand.randGaussian(newentity.getDef(),
newentity.getDef() / 10));
newentity.registerObjectsForNotification(observers);
if (StendhalRPAction.placeat(zone, newentity, x, y)) {
newentity.init();
newentity.setRespawnPoint(this);
creatures.add(newentity);
} else {
// Could not place the creature anywhere.
// Treat it like it just had died.
notifyDead(newentity);
logger.warn("Could not respawn " + newentity.getName() + " near "
+ zone.getName() + " " + x + " " + y);
}
} catch (final Exception e) {
logger.error("error respawning entity " + prototypeCreature, e);
}
}
}