package com.aionemu.gameserver.services.event;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aionemu.gameserver.configs.Config;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.TaskId;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.team.TeamMember;
import com.aionemu.gameserver.model.team.TemporaryPlayerTeam;
import com.aionemu.gameserver.model.templates.Guides.GuideTemplate;
import com.aionemu.gameserver.model.templates.QuestTemplate;
import com.aionemu.gameserver.model.templates.event.EventTemplate;
import com.aionemu.gameserver.model.templates.event.InventoryDrop;
import com.aionemu.gameserver.model.templates.quest.QuestCategory;
import com.aionemu.gameserver.model.templates.spawns.Spawn;
import com.aionemu.gameserver.model.templates.spawns.SpawnMap;
import com.aionemu.gameserver.model.templates.spawns.SpawnTemplate;
import com.aionemu.gameserver.network.aion.serverpackets.SM_QUEST_ACTION;
import com.aionemu.gameserver.network.aion.serverpackets.SM_QUEST_ACTION.ActionType;
import com.aionemu.gameserver.questEngine.model.QuestState;
import com.aionemu.gameserver.questEngine.model.QuestStatus;
import com.aionemu.gameserver.services.QuestService;
import com.aionemu.gameserver.services.RespawnService;
import com.aionemu.gameserver.services.item.ItemPacketService.ItemAddType;
import com.aionemu.gameserver.services.item.ItemPacketService.ItemUpdateType;
import com.aionemu.gameserver.services.item.ItemService;
import com.aionemu.gameserver.services.item.ItemService.ItemUpdatePredicate;
import com.aionemu.gameserver.skillengine.model.Effect.ForceType;
import com.aionemu.gameserver.spawnengine.SpawnEngine;
import com.aionemu.gameserver.spawnengine.TemporarySpawnEngine;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.ThreadPoolManager;
import com.aionemu.gameserver.utils.time.ServerTime;
import com.aionemu.gameserver.world.World;
import com.aionemu.gameserver.world.WorldMap;
import com.aionemu.gameserver.world.WorldMapInstance;
/**
* @author Neon
*/
public class Event {
private static final Logger log = LoggerFactory.getLogger(Event.class);
private static final String EFFECT_FORCE_TYPE_PREFIX = "[EVENT] ";
private final EventTemplate eventTemplate;
private final AtomicBoolean started = new AtomicBoolean();
private EventBuffHandler eventBuffHandler;
private Future<?> inventoryDropTask;
private List<Runnable> onEventEndTasks;
public Event(EventTemplate eventTemplate) {
this.eventTemplate = eventTemplate;
}
public EventTemplate getEventTemplate() {
return eventTemplate;
}
public static ForceType getOrCreateEffectForceType(String identifier) {
return ForceType.getInstance(EFFECT_FORCE_TYPE_PREFIX + identifier);
}
public static boolean isEventEffectForceType(ForceType forceType) {
return forceType != null && forceType.getName().startsWith(EFFECT_FORCE_TYPE_PREFIX);
}
public void start() {
if (!started.compareAndSet(false, true))
return;
if (eventTemplate.hasConfigProperties())
Config.load();
if (eventTemplate.getSpawns() != null && eventTemplate.getSpawns().size() > 0) {
for (SpawnMap map : eventTemplate.getSpawns().getTemplates()) {
DataManager.SPAWNS_DATA.addNewSpawnMap(map);
WorldMap worldMap = World.getInstance().getWorldMap(map.getMapId());
for (Spawn spawn : map.getSpawns()) {
spawn.setEventTemplate(eventTemplate);
if (spawn.isCustom())
despawnNonEventSpawns(spawn.getNpcId(), worldMap);
}
}
DataManager.SPAWNS_DATA.afterUnmarshal(null, null);
DataManager.SPAWNS_DATA.clearTemplates();
for (SpawnMap map : eventTemplate.getSpawns().getTemplates()) {
byte difficultId = map.getSpawns().stream().map(Spawn::getDifficultId).filter(d -> d != 0).findFirst().orElse((byte) 0);
World.getInstance().getWorldMap(map.getMapId()).forEach(instance -> SpawnEngine.spawnEventSpawns(instance, difficultId, 0, eventTemplate));
}
}
InventoryDrop inventoryDrop = eventTemplate.getInventoryDrop();
if (inventoryDropTask == null && inventoryDrop != null) {
inventoryDropTask = ThreadPoolManager.getInstance().scheduleAtFixedRate(() -> {
World.getInstance().forEachPlayer(player -> {
if (player.getLevel() >= inventoryDrop.getStartLevel())
// TODO: check the exact type in retail
ItemService.addItem(player, inventoryDrop.getItemId(), inventoryDrop.getCount(), true,
new ItemUpdatePredicate(ItemAddType.ITEM_COLLECT, ItemUpdateType.INC_CASH_ITEM));
});
}, 0, inventoryDrop.getInterval() * 60000);
}
if (eventTemplate.getSurveys() != null) {
for (String survey : eventTemplate.getSurveys()) {
GuideTemplate template = DataManager.GUIDE_HTML_DATA.getTemplateByTitle(survey);
if (template != null)
template.setActivated(true);
}
}
if (eventTemplate.getBuffs() != null)
eventBuffHandler = new EventBuffHandler(eventTemplate.getName(), eventTemplate.getBuffs());
World.getInstance().forEachPlayer(player -> { // simulate login on event start
onPlayerLogin(player);
onEnterMap(player);
});
log.info("Started event: " + eventTemplate.getName());
}
public void stop() {
started.set(false);
if (eventTemplate.hasConfigProperties()) {
Config.load();
}
if (eventTemplate.getSpawns() != null && eventTemplate.getSpawns().size() > 0) {
TemporarySpawnEngine.unregister(eventTemplate);
int[] count = { 0 };
World.getInstance().forEachObject(o -> {
SpawnTemplate spawn = o.getSpawn();
if (spawn != null && eventTemplate.equals(spawn.getEventTemplate())) {
o.getController().delete();
count[0]++;
}
});
count[0] += RespawnService.cancelEventRespawns(eventTemplate);
DataManager.SPAWNS_DATA.removeEventSpawnObjects(eventTemplate);
log.info("Removed " + count[0] + " event spawns (" + eventTemplate.getName() + ")");
}
synchronized (this) {
if (onEventEndTasks != null) {
for (Runnable task : onEventEndTasks) {
try {
task.run();
} catch (Exception e) {
log.error("Could not execute task on end of event " + getEventTemplate().getName(), e);
}
}
onEventEndTasks = null;
}
}
if (inventoryDropTask != null) {
inventoryDropTask.cancel(false);
inventoryDropTask = null;
}
if (eventTemplate.getSurveys() != null) {
for (String survey : eventTemplate.getSurveys()) {
GuideTemplate template = DataManager.GUIDE_HTML_DATA.getTemplateByTitle(survey);
if (template != null)
template.setActivated(false);
}
}
if (eventBuffHandler != null) {
eventBuffHandler.onEventStop();
eventBuffHandler = null;
}
log.info("Stopped event: " + eventTemplate.getName());
}
public ForceType getEffectForceType() {
return eventBuffHandler == null ? null : eventBuffHandler.getEffectForceType();
}
public void onTimeChanged(ZonedDateTime now) {
if (eventBuffHandler != null)
eventBuffHandler.onTimeChanged(now);
}
public void onPlayerLogin(Player player) {
startOrMaintainQuests(player);
if (eventTemplate.getLoginMessage() != null && !eventTemplate.getLoginMessage().isEmpty())
PacketSendUtility.sendMessage(player, eventTemplate.getLoginMessage());
}
public void onEnteredTeam(Player player, TemporaryPlayerTeam<? extends TeamMember<Player>> team) {
if (eventBuffHandler != null)
eventBuffHandler.onEnteredTeam(player, team);
}
public void onLeftTeam(Player player, TemporaryPlayerTeam<? extends TeamMember<Player>> team) {
if (eventBuffHandler != null)
eventBuffHandler.onLeftTeam(player, team);
}
public void onEnterMap(Player player) {
if (eventBuffHandler != null)
eventBuffHandler.onEnterMap(player);
}
public void onPveKill(Player killer, Npc victim) {
if (eventBuffHandler != null)
eventBuffHandler.onPveKill(killer, victim);
}
public void onPvpKill(Player killer, Player victim) {
if (eventBuffHandler != null)
eventBuffHandler.onPvpKill(killer, victim);
}
private void startOrMaintainQuests(Player player) {
for (int startableQuestId : eventTemplate.getStartableQuests()) {
if (isAllowedToStartEventQuest(player, startableQuestId)) {
QuestState qs = player.getQuestStateList().getQuestState(startableQuestId);
if (qs == null) {
qs = new QuestState(startableQuestId, QuestStatus.START);
player.getQuestStateList().addQuest(startableQuestId, qs);
PacketSendUtility.sendPacket(player, new SM_QUEST_ACTION(ActionType.ADD, qs));
} else if (qs.getStatus() != QuestStatus.START && qs.getCompleteCount() > 0 && eventTemplate.getStartDate() != null) {
LocalDateTime completeTime = ServerTime.atDate(qs.getLastCompleteTime()).toLocalDateTime();
if (eventTemplate.getStartDate().isAfter(completeTime)) { // quest was last completed on a previous event, reset & restart it
ActionType actionType = qs.getStatus() == QuestStatus.COMPLETE ? ActionType.ADD : ActionType.UPDATE;
qs.setStatus(QuestStatus.START);
qs.setQuestVar(0);
qs.setCompleteCount(0);
qs.setRewardGroup(null);
PacketSendUtility.sendPacket(player, new SM_QUEST_ACTION(actionType, qs));
}
}
}
}
for (int maintainableQuestId : eventTemplate.getMaintainableQuests()) {
QuestState qs = player.getQuestStateList().getQuestState(maintainableQuestId);
if (qs != null && qs.getCompleteCount() > 0 && isAllowedToStartEventQuest(player, maintainableQuestId)) {
LocalDateTime completeTime = ServerTime.atDate(qs.getLastCompleteTime()).toLocalDateTime();
if (eventTemplate.getStartDate().isAfter(completeTime)) { // quest was last completed on a previous event, reset it
qs.setCompleteCount(0);
}
}
}
}
private boolean isAllowedToStartEventQuest(Player player, int questId) {
QuestTemplate template = DataManager.QUEST_DATA.getQuestById(questId);
if (template.getCategory() != QuestCategory.EVENT)
return false;
if (!QuestService.checkStartConditions(player, questId, false, 0, true, true, false))
return false;
return true;
}
private void despawnNonEventSpawns(int npcId, WorldMap worldMap) {
for (WorldMapInstance worldMapInstance : worldMap) {
worldMapInstance.getNpcs(npcId).forEach(npc -> {
if (npc.getSpawn() != null && !npc.getSpawn().isEventSpawn() && !npc.getController().hasScheduledTask(TaskId.DECAY)) {
if (npc.getController().delete() && !RespawnService.hasRespawnTask(npc))
addOnEventEndTask(new RespawnService.RespawnTask(npc));
}
});
}
}
public boolean addOnEventEndTask(Runnable task) {
if (!started.get())
return false;
synchronized (this) {
if (onEventEndTasks == null)
onEventEndTasks = new ArrayList<>();
onEventEndTasks.add(task);
return true;
}
}
}