/*
* 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 3 of the License, or (at your option) any later
* version.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.model.entity;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastList;
import javolution.util.FastMap;
import com.l2jserver.Config;
import com.l2jserver.gameserver.cache.HtmCache;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.CharInfo;
import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
import com.l2jserver.gameserver.network.serverpackets.UserInfo;
import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
import com.l2jserver.gameserver.util.PlayerEventStatus;
import com.l2jserver.util.ValueSortMap;
/**
* @since $Revision: 1.3.4.1 $ $Date: 2005/03/27 15:29:32 $
* This ancient thingie got reworked by Nik at $Date: 2011/05/17 21:51:39 $
* Yeah, for 6 years no one bothered reworking this buggy event engine.
*/
public class L2Event
{
protected static final Logger _log = Logger.getLogger(L2Event.class.getName());
public static EventState eventState = EventState.OFF;
public static String _eventName = "";
public static String _eventCreator = "";
public static String _eventInfo = "";
public static int _teamsNumber = 0;
public static final Map<Integer, String> _teamNames = new FastMap<Integer, String>();
public static final List<L2PcInstance> _registeredPlayers = new FastList<L2PcInstance>();
public static final Map<Integer, FastList<L2PcInstance>> _teams = new FastMap<Integer, FastList<L2PcInstance>>();
public static int _npcId = 0;
//public static final List<L2Npc> _npcs = new FastList<L2Npc>();
private static final Map<L2PcInstance, PlayerEventStatus> _connectionLossData = new FastMap<L2PcInstance, PlayerEventStatus>();
public enum EventState
{
OFF, // Not running
STANDBY, // Waiting for participants to register
ON // Registration is over and the event has started.
}
/**
*
* @param player
* @return The team ID where the player is in, or -1 if player is null or team not found.
*/
public static int getPlayerTeamId(L2PcInstance player)
{
if (player == null)
return -1;
for (Entry<Integer, FastList<L2PcInstance>> team : _teams.entrySet())
{
if (team.getValue().contains(player))
return team.getKey();
}
return -1;
}
public static List<L2PcInstance> getTopNKillers(int n)
{
Map<L2PcInstance, Integer> tmp = new FastMap<L2PcInstance, Integer>();
for (FastList<L2PcInstance> teamList : _teams.values())
{
for (L2PcInstance player : teamList)
{
if (player.getEventStatus() == null)
continue;
tmp.put(player, player.getEventStatus().kills.size());
}
}
ValueSortMap.sortMapByValue(tmp, false);
// If the map size is less than "n", n will be as much as the map size
if (tmp.size() <= n)
{
List<L2PcInstance> toReturn = new FastList<L2PcInstance>();
toReturn.addAll(tmp.keySet());
return toReturn;
}
else
{
List<L2PcInstance> toReturn = new FastList<L2PcInstance>();
toReturn.addAll(tmp.keySet());
return toReturn.subList(1, n);
}
}
public static void showEventHtml(L2PcInstance player, String objectid)
{//TODO: work on this
if (eventState == EventState.STANDBY)
{
try
{
final String htmContent;
NpcHtmlMessage html = new NpcHtmlMessage(5);
if (_registeredPlayers.contains(player))
htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/mods/EventEngine/Participating.htm");
else
htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/mods/EventEngine/Participation.htm");
if (htmContent != null)
html.setHtml(htmContent);
html.replace("%objectId%", objectid); // Yeah, we need this.
html.replace("%eventName%", _eventName);
html.replace("%eventCreator%", _eventCreator);
html.replace("%eventInfo%", _eventInfo);
player.sendPacket(html);
}
catch (Exception e)
{
_log.log(Level.WARNING, "Exception on showEventHtml(): " + e.getMessage(), e);
}
}
}
/**
* Spawns an event participation NPC near the player.
* The npc id used to spawning is L2Event._npcId
*/
public static void spawnEventNpc(L2PcInstance target)
{
L2NpcTemplate template = NpcTable.getInstance().getTemplate(_npcId);
try
{
L2Spawn spawn = new L2Spawn(template);
spawn.setLocx(target.getX() + 50);
spawn.setLocy(target.getY() + 50);
spawn.setLocz(target.getZ());
spawn.setAmount(1);
spawn.setHeading(target.getHeading());
SpawnTable.getInstance().addNewSpawn(spawn, false);
spawn.init();
spawn.getLastSpawn().setCurrentHp(999999999);
spawn.getLastSpawn().setTitle(_eventName);
spawn.getLastSpawn().isEventMob = true;
//spawn.getLastSpawn().decayMe();
//spawn.getLastSpawn().spawnMe(spawn.getLastSpawn().getX(), spawn.getLastSpawn().getY(), spawn.getLastSpawn().getZ());
spawn.getLastSpawn().broadcastPacket(new MagicSkillUse(spawn.getLastSpawn(), spawn.getLastSpawn(), 1034, 1, 1, 1));
//_npcs.add(spawn.getLastSpawn());
}
catch (Exception e)
{
_log.log(Level.WARNING, "Exception on spawn(): " + e.getMessage(), e);
}
}
public static void unspawnEventNpcs()
{
//Its a little rough, but for sure it will remove every damn event NPC.
for (L2Spawn spawn : SpawnTable.getInstance().getSpawnTable())
{
if (spawn.getLastSpawn() != null && spawn.getLastSpawn().isEventMob)
{
spawn.getLastSpawn().deleteMe();
spawn.stopRespawn();
SpawnTable.getInstance().deleteSpawn(spawn, false);
}
}
//for (L2Npc npc : _npcs)
// npc.deleteMe();
}
/**
*
* @return False: If player is null, his event status is null or the event state is off.
* True: if the player is inside the _registeredPlayers list while the event state is STANDBY.
* If the event state is ON, it will check if the player is inside in one of the teams.
*/
public static boolean isParticipant(L2PcInstance player)
{
if (player == null || player.getEventStatus() == null)
return false;
switch (eventState)
{
case OFF:
return false;
case STANDBY:
return _registeredPlayers.contains(player);
case ON:
for (FastList<L2PcInstance> teamList : _teams.values())
{
if (teamList.contains(player))
return true;
}
}
return false;
}
/**
*
* Adds the player to the list of participants.
* If the event state is NOT STANDBY, the player wont be registered.
*/
public static void registerPlayer(L2PcInstance player)
{
if (eventState != EventState.STANDBY)
{
player.sendMessage("The registration period for this event is over.");
return;
}
if (AntiFeedManager.getInstance().tryAddPlayer(AntiFeedManager.L2EVENT_ID, player, Config.L2JMOD_DUALBOX_CHECK_MAX_L2EVENT_PARTICIPANTS_PER_IP))
_registeredPlayers.add(player);
else
{
player.sendMessage("You have reached the maximum allowed participants per IP.");
return;
}
}
/**
*
* Removes the player from the participating players and the teams and restores
* his init stats before he registered at the event (loc, pvp, pk, title etc)
*/
public static void removeAndResetPlayer(L2PcInstance player)
{
try
{
if (isParticipant(player))
{
if (player.isDead())
{
player.restoreExp(100.0);
player.doRevive();
player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
player.setCurrentCp(player.getMaxCp());
}
player.getPoly().setPolyInfo(null, "1");
player.decayMe();
player.spawnMe(player.getX(), player.getY(), player.getZ());
CharInfo info1 = new CharInfo(player);
player.broadcastPacket(info1);
UserInfo info2 = new UserInfo(player);
player.sendPacket(info2);
player.broadcastPacket(new ExBrExtraUserInfo(player));
player.stopTransformation(true);
}
if (player.getEventStatus() != null)
player.getEventStatus().restoreInits();
player.setEventStatus(null);
_registeredPlayers.remove(player);
int teamId = getPlayerTeamId(player);
if (_teams.containsKey(teamId))
_teams.get(teamId).remove(player);
}
catch (Exception e)
{
_log.log(Level.WARNING, "Error at unregisterAndResetPlayer in the event:" + e.getMessage(), e);
}
}
/**
*
* The player's event status will be saved at _connectionLossData
*/
public static void savePlayerEventStatus(L2PcInstance player)
{
_connectionLossData.put(player, player.getEventStatus());
}
/**
*
* If _connectionLossData contains the player, it will restore the player's event status.
* Also it will remove the player from the _connectionLossData.
*/
public static void restorePlayerEventStatus(L2PcInstance player)
{
if (_connectionLossData.containsKey(player))
{
player.setEventStatus(_connectionLossData.get(player));
_connectionLossData.remove(player);
}
}
/**
* If the event is ON or STANDBY, it will not start.
* Sets the event state to STANDBY and spawns registration NPCs
* @return a string with information if the event participation has been successfully started or not.
*/
public static String startEventParticipation()
{
try
{
switch (eventState)
{
case ON:
return "Cannot start event, it is already on.";
case STANDBY:
return "Cannot start event, it is on standby mode.";
case OFF: // Event is off, so no problem turning it on.
eventState = EventState.STANDBY;
break;
}
// Register the event at AntiFeedManager and clean it for just in case if the event is already registered.
AntiFeedManager.getInstance().registerEvent(AntiFeedManager.L2EVENT_ID);
AntiFeedManager.getInstance().clear(AntiFeedManager.TVT_ID);
// Just in case
unspawnEventNpcs();
_registeredPlayers.clear();
//_npcs.clear();
if (NpcTable.getInstance().getTemplate(_npcId) == null)
return "Cannot start event, invalid npc id.";
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("data/events/" + _eventName)));
BufferedReader inbr = new BufferedReader(new InputStreamReader(in));
_eventCreator = inbr.readLine();
_eventInfo = inbr.readLine();
List<L2PcInstance> temp = new FastList<L2PcInstance>();
for (L2PcInstance player : L2World.getInstance().getAllPlayers().values())
{
if (!player.isOnline()) // Offline shops?
continue;
if (!temp.contains(player))
{
spawnEventNpc(player);
temp.add(player);
}
for (L2PcInstance playertemp : player.getKnownList().getKnownPlayers().values())
{
if ((Math.abs(playertemp.getX() - player.getX()) < 1000) && (Math.abs(playertemp.getY() - player.getY()) < 1000) && (Math.abs(playertemp.getZ() - player.getZ()) < 1000))
temp.add(playertemp);
}
}
}
catch (Exception e)
{
e.printStackTrace();
return "Cannot start event participation, an error has occured.";
}
return "The event participation has been successfully started.";
}
/**
* If the event is ON or OFF, it will not start.
* Sets the event state to ON, creates the teams,
* adds the registered players ordered by level at the teams
* and adds a new event status to the players.
* @return a string with information if the event has been successfully started or not.
*/
public static String startEvent()
{
try
{
switch (eventState)
{
case ON:
return "Cannot start event, it is already on.";
case STANDBY:
eventState = EventState.ON;
break;
case OFF: // Event is off, so no problem turning it on.
return "Cannot start event, it is off. Participation start is required.";
}
// Clean the things we will use, just in case.
unspawnEventNpcs();
_teams.clear();
_connectionLossData.clear();
// Insert empty lists at _teams.
for (int i = 0; i < _teamsNumber; i++)
_teams.put(i + 1, new FastList<L2PcInstance>());
int i = 0;
while (!_registeredPlayers.isEmpty())
{
//Get the player with the biggest level
int max = 0;
L2PcInstance biggestLvlPlayer = null;
for (L2PcInstance player : _registeredPlayers)
{
if (player == null)
continue;
if (max < player.getLevel())
{
max = player.getLevel();
biggestLvlPlayer = player;
}
}
if (biggestLvlPlayer == null)
continue;
_registeredPlayers.remove(biggestLvlPlayer);
_teams.get(i + 1).add(biggestLvlPlayer);
biggestLvlPlayer.setEventStatus();
i = (i + 1) % _teamsNumber;
}
}
catch (Exception e)
{
e.printStackTrace();
return "Cannot start event, an error has occured.";
}
return "The event has been successfully started.";
}
/**
* If the event state is OFF, it will not finish.
* Sets the event state to OFF, unregisters and resets the players,
* unspawns and clers the event NPCs, clears the teams, registered players,
* connection loss data, sets the teams number to 0, sets the event name to empty.
* @return a string with information if the event has been successfully stopped or not.
*/
public static String finishEvent()
{
switch (eventState)
{
case OFF:
return "Cannot finish event, it is already off.";
case STANDBY:
for (L2PcInstance player : _registeredPlayers)
removeAndResetPlayer(player);
unspawnEventNpcs();
//_npcs.clear();
_registeredPlayers.clear();
_teams.clear();
_connectionLossData.clear();
_teamsNumber = 0;
_eventName = "";
eventState = EventState.OFF;
return "The event has been stopped at STANDBY mode, all players unregistered and all event npcs unspawned.";
case ON:
for (FastList<L2PcInstance> teamList : _teams.values())
{
for (L2PcInstance player : teamList)
removeAndResetPlayer(player);
}
eventState = EventState.OFF;
AntiFeedManager.getInstance().clear(AntiFeedManager.TVT_ID);
unspawnEventNpcs(); // Just in case
//_npcs.clear();
_registeredPlayers.clear();
_teams.clear();
_connectionLossData.clear();
_teamsNumber = 0;
_eventName = "";
_npcId = 0;
_eventCreator = "";
_eventInfo = "";
return "The event has been stopped, all players unregistered and all event npcs unspawned.";
}
return "The event has been successfully finished.";
}
}