/*
* This file is part of aion-emu <aion-emu.com>.
*
* aion-emu 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.
*
* aion-emu 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 aion-emu. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.gameserver.spawnengine;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import com.aionemu.commons.callbacks.EnhancedObject;
import com.aionemu.gameserver.ai.events.Event;
import com.aionemu.gameserver.controllers.ActionitemController;
import com.aionemu.gameserver.controllers.BindpointController;
import com.aionemu.gameserver.controllers.GatherableController;
import com.aionemu.gameserver.controllers.MonsterController;
import com.aionemu.gameserver.controllers.NpcController;
import com.aionemu.gameserver.controllers.PortalController;
import com.aionemu.gameserver.controllers.PostboxController;
import com.aionemu.gameserver.controllers.ServantController;
import com.aionemu.gameserver.controllers.SummonController;
import com.aionemu.gameserver.controllers.effect.EffectController;
import com.aionemu.gameserver.dataholders.BindPointData;
import com.aionemu.gameserver.dataholders.GatherableData;
import com.aionemu.gameserver.dataholders.NpcData;
import com.aionemu.gameserver.dataholders.NpcSkillData;
import com.aionemu.gameserver.dataholders.SpawnsData;
import com.aionemu.gameserver.dataholders.SummonStatsData;
import com.aionemu.gameserver.dataholders.WorldMapsData;
import com.aionemu.gameserver.model.NpcType;
import com.aionemu.gameserver.model.gameobjects.AionObject;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Gatherable;
import com.aionemu.gameserver.model.gameobjects.Monster;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.Servant;
import com.aionemu.gameserver.model.gameobjects.Summon;
import com.aionemu.gameserver.model.gameobjects.Trap;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.templates.GatherableTemplate;
import com.aionemu.gameserver.model.templates.NpcTemplate;
import com.aionemu.gameserver.model.templates.VisibleObjectTemplate;
import com.aionemu.gameserver.model.templates.WorldMapTemplate;
import com.aionemu.gameserver.model.templates.spawn.SpawnGroup;
import com.aionemu.gameserver.model.templates.spawn.SpawnTemplate;
import com.aionemu.gameserver.model.templates.stats.SummonStatsTemplate;
import com.aionemu.gameserver.utils.gametime.DayTime;
import com.aionemu.gameserver.utils.gametime.GameTime;
import com.aionemu.gameserver.utils.gametime.GameTimeManager;
import com.aionemu.gameserver.utils.gametime.listeners.DayTimeListener;
import com.aionemu.gameserver.utils.idfactory.IDFactory;
import com.aionemu.gameserver.utils.idfactory.IDFactoryAionObject;
import com.aionemu.gameserver.world.KnownList;
import com.aionemu.gameserver.world.StaticObjectKnownList;
import com.aionemu.gameserver.world.World;
import com.google.inject.Inject;
import com.google.inject.Injector;
/**
*
* This class is responsible for NPCs spawn management. Current implementation is temporal and will be replaced in the
* future.
*
* @author Luno
*
* modified by ATracer
*
*/
public class SpawnEngine
{
private static Logger log = Logger.getLogger(SpawnEngine.class);
@Inject
private World world;
@IDFactoryAionObject
@Inject
private IDFactory aionObjectsIDFactory;
@Inject
private SpawnsData spawnsData;
@Inject
private GatherableData gatherableData;
@Inject
private NpcData npcData;
@Inject
private RiftSpawnManager riftSpawnManager;
@Inject
private StaticObjectSpawnManager staticObjectSpawnManager;
@Inject
private WorldMapsData worldMapsData;
@Inject
private BindPointData bindPointData;
@Inject
private NpcSkillData npcSkillData;
@Inject
private SummonStatsData summonStatsData;
private Injector injector;
/** Counter counting number of npc spawns */
private int npcCounter = 0;
/** Counter counting number of gatherable spawns */
private int gatherableCounter = 0;
/**
* @param injector
* the injector to set
*/
public void setInjector(Injector injector)
{
this.injector = injector;
}
/**
* Creates VisibleObject instance and spawns it using given {@link SpawnTemplate} instance.
*
* @param spawn
* @return created and spawned VisibleObject
*/
public VisibleObject spawnObject(SpawnTemplate spawn, int instanceIndex)
{
VisibleObjectTemplate template = null;
int objectId = spawn.getSpawnGroup().getNpcid();
if(objectId > 400000 && objectId < 499999)// gatherable
{
template = gatherableData.getGatherableTemplate(objectId);
if(template == null)
return null;
gatherableCounter++;
}
else
// npc
{
template = npcData.getNpcTemplate(objectId);
if(template == null)
return null;
npcCounter++;
}
if(template instanceof NpcTemplate)
{
NpcType npcType = ((NpcTemplate) template).getNpcType();
Npc npc = null;
switch(npcType)
{
case AGGRESSIVE:
case ATTACKABLE:
npc = new Monster(aionObjectsIDFactory.nextId(), injector.getInstance(MonsterController.class),
spawn, template);
npc.setKnownlist(new KnownList(npc));
break;
case POSTBOX:
npc = new Npc(aionObjectsIDFactory.nextId(), injector.getInstance(PostboxController.class), spawn,
template);
npc.setKnownlist(new StaticObjectKnownList(npc));
break;
case RESURRECT:
BindpointController bindPointController = injector.getInstance(BindpointController.class);
bindPointController.setBindPointTemplate(bindPointData.getBindPointTemplate(objectId));
npc = new Npc(aionObjectsIDFactory.nextId(), bindPointController, spawn, template);
npc.setKnownlist(new StaticObjectKnownList(npc));
break;
case USEITEM:
npc = new Npc(aionObjectsIDFactory.nextId(), injector.getInstance(ActionitemController.class),
spawn, template);
npc.setKnownlist(new StaticObjectKnownList(npc));
break;
case PORTAL:
npc = new Npc(aionObjectsIDFactory.nextId(), injector.getInstance(PortalController.class), spawn,
template);
npc.setKnownlist(new StaticObjectKnownList(npc));
break;
default: // NON_ATTACKABLE
npc = new Npc(aionObjectsIDFactory.nextId(), injector.getInstance(NpcController.class), spawn,
template);
npc.setKnownlist(new StaticObjectKnownList(npc));
}
npc.setNpcSkillList(npcSkillData.getNpcSkillList(template.getTemplateId()));
npc.setEffectController(new EffectController(npc));
npc.getController().onRespawn();
bringIntoWorld(npc, spawn, instanceIndex);
return npc;
}
else if(template instanceof GatherableTemplate)
{
Gatherable gatherable = new Gatherable(spawn, template, aionObjectsIDFactory.nextId(), injector
.getInstance(GatherableController.class));
gatherable.setKnownlist(new StaticObjectKnownList(gatherable));
bringIntoWorld(gatherable, spawn, instanceIndex);
return gatherable;
}
return null;
}
/**
*
* @param spawn
* @param instanceIndex
* @param creator
* @return
*/
public Trap spawnTrap(SpawnTemplate spawn, int instanceIndex, Creature creator, int skillId)
{
int objectId = spawn.getSpawnGroup().getNpcid();
NpcTemplate npcTemplate = npcData.getNpcTemplate(objectId);
Trap trap = new Trap(aionObjectsIDFactory.nextId(), injector.getInstance(NpcController.class), spawn,
npcTemplate);
trap.setKnownlist(new KnownList(trap));
trap.setEffectController(new EffectController(trap));
trap.setCreator(creator);
trap.setSkillId(skillId);
trap.getController().onRespawn();
bringIntoWorld(trap, spawn, instanceIndex);
return trap;
}
/**
*
* @param spawn
* @param instanceIndex
* @param creator
* @param skillId
* @return
*/
public Servant spawnServant(SpawnTemplate spawn, int instanceIndex, Creature creator, int skillId, int hpRatio)
{
int objectId = spawn.getSpawnGroup().getNpcid();
NpcTemplate npcTemplate = npcData.getNpcTemplate(objectId);
Servant servant = new Servant(aionObjectsIDFactory.nextId(), injector.getInstance(ServantController.class), spawn,
npcTemplate);
servant.setKnownlist(new KnownList(servant));
servant.setEffectController(new EffectController(servant));
servant.setCreator(creator);
servant.setSkillId(skillId);
servant.setTarget(creator.getTarget());
servant.setHpRatio(hpRatio);
servant.getController().onRespawn();
bringIntoWorld(servant, spawn, instanceIndex);
return servant;
}
/**
*
* @param creator
* @param npcId
* @return
*/
public Summon spawnSummon(Player creator, int npcId, int skillLvl)
{
float x = creator.getX();
float y = creator.getY();
float z = creator.getZ();
byte heading = creator.getHeading();
int worldId = creator.getWorldId();
int instanceId = creator.getInstanceId();
SpawnTemplate spawn = createSpawnTemplate(worldId, npcId, x, y, z, heading, 0, 0);
NpcTemplate npcTemplate = npcData.getNpcTemplate(npcId);
byte level = (byte) (npcTemplate.getLevel() + skillLvl - 1);
SummonStatsTemplate statsTemplate = summonStatsData.getSummonTemplate(npcId, level);
Summon summon = new Summon(aionObjectsIDFactory.nextId(), injector.getInstance(SummonController.class), spawn,
npcTemplate, statsTemplate);
summon.setLevel(level);
summon.setKnownlist(new KnownList(summon));
summon.setEffectController(new EffectController(summon));
summon.setMaster(creator);
bringIntoWorld(summon, spawn, instanceId);
return summon;
}
/**
*
* @param worldId
* @param objectId
* @param x
* @param y
* @param z
* @param heading
* @param walkerid
* @param randomwalk
* @return
*/
private SpawnTemplate createSpawnTemplate(int worldId, int objectId, float x, float y, float z, byte heading,
int walkerid, int randomwalk)
{
SpawnTemplate spawnTemplate = new SpawnTemplate(x, y, z, heading, walkerid, randomwalk);
SpawnGroup spawnGroup = new SpawnGroup(worldId, objectId, 105, 1);
spawnTemplate.setSpawnGroup(spawnGroup);
spawnGroup.getObjects().add(spawnTemplate);
return spawnTemplate;
}
/**
* Should be used when need to define whether spawn will be deleted after death Using this method spawns will not be
* saved with //save_spawn command
*
* @param worldId
* @param objectId
* @param x
* @param y
* @param z
* @param heading
* @param walkerid
* @param randomwalk
* @param noRespawn
* @return SpawnTemplate
*/
public SpawnTemplate addNewSpawn(int worldId, int instanceId, int objectId, float x, float y, float z,
byte heading, int walkerid, int randomwalk, boolean noRespawn)
{
return this
.addNewSpawn(worldId, instanceId, objectId, x, y, z, heading, walkerid, randomwalk, noRespawn, false);
}
/**
*
* @param worldId
* @param instanceId
* @param objectId
* @param x
* @param y
* @param z
* @param heading
* @param walkerid
* @param randomwalk
* @param noRespawn
* @param isNewSpawn
* @return SpawnTemplate
*/
public SpawnTemplate addNewSpawn(int worldId, int instanceId, int objectId, float x, float y, float z,
byte heading, int walkerid, int randomwalk, boolean noRespawn, boolean isNewSpawn)
{
SpawnTemplate spawnTemplate = createSpawnTemplate(worldId, objectId, x, y, z, heading, walkerid, randomwalk);
if(spawnTemplate == null)
{
log.warn("Object couldn't be spawned");
return null;
}
if(!noRespawn)
{
spawnsData.addNewSpawnGroup(spawnTemplate.getSpawnGroup(), worldId, objectId, isNewSpawn);
}
spawnTemplate.setNoRespawn(noRespawn, instanceId);
return spawnTemplate;
}
private void bringIntoWorld(VisibleObject visibleObject, SpawnTemplate spawn, int instanceIndex)
{
world.storeObject(visibleObject);
world.setPosition(visibleObject, spawn.getWorldId(), instanceIndex, spawn.getX(), spawn.getY(), spawn.getZ(),
spawn.getHeading());
world.spawn(visibleObject);
}
/**
* Spawn all NPC's from templates
*/
public void spawnAll()
{
this.npcCounter = 0;
this.gatherableCounter = 0;
for(WorldMapTemplate worldMapTemplate : worldMapsData)
{
if(worldMapTemplate.isInstance())
continue;
int maxTwin = worldMapTemplate.getTwinCount();
final int mapId = worldMapTemplate.getMapId();
int numberToSpawn = maxTwin > 0 ? maxTwin : 1;
for(int i = 1; i <= numberToSpawn; i++)
{
spawnInstance(mapId, i);
}
}
log.info("Loaded " + npcCounter + " npc spawns");
log.info("Loaded " + gatherableCounter + " gatherable spawns");
riftSpawnManager.startRiftPool();
}
/**
*
* @param worldId
* @param instanceIndex
*/
public void spawnInstance(int worldId, int instanceIndex)
{
List<SpawnGroup> worldSpawns = spawnsData.getSpawnsForWorld(worldId);
if(worldSpawns == null || worldSpawns.size() == 0)
return;
int instanceSpawnCounter = 0;
for(SpawnGroup spawnGroup : worldSpawns)
{
spawnGroup.resetLastSpawnCounter(instanceIndex);
if(spawnGroup.getHandler() == null)
{
int pool = spawnGroup.getPool();
for(int i = 0; i < pool; i++)
{
spawnObject(spawnGroup.getNextAvailableTemplate(instanceIndex), instanceIndex);
instanceSpawnCounter++;
}
}
else
{
switch(spawnGroup.getHandler())
{
case RIFT:
riftSpawnManager.addRiftSpawnGroup(spawnGroup);
break;
case STATIC:
staticObjectSpawnManager.spawnGroup(spawnGroup, instanceIndex);
default:
break;
}
}
}
log.info("Spawned " + worldId + " [" + instanceIndex + "] : " + instanceSpawnCounter);
}
/**
*
* @param dayTime
*/
private void sendDayTimeChangeEvents(DayTime dayTime)
{
Iterator<AionObject> it = world.getObjectsIterator();
while(it.hasNext())
{
AionObject obj = it.next();
if(obj instanceof Npc)
{
((Npc) obj).getAi().handleEvent(Event.DAYTIME_CHANGE);
}
}
}
/**
* Called only once when game server starts
*/
public void addGameTimeHook()
{
((EnhancedObject) GameTimeManager.getGameTime()).addCallback(new DayTimeListener(){
@Override
protected void onDayTimeChange(GameTime gameTime)
{
sendDayTimeChangeEvents(gameTime.getDayTime());
}
});
}
}