/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server 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 .
*/
package com.l2jserver.gameserver.model.entity;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
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.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.NpcData;
import com.l2jserver.gameserver.datatables.SkillData;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.enums.Team;
import com.l2jserver.gameserver.instancemanager.HandysBlockCheckerManager;
import com.l2jserver.gameserver.model.ArenaParticipantsHolder;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.instance.L2BlockInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.ExBasicActionList;
import com.l2jserver.gameserver.network.serverpackets.ExCubeGameChangePoints;
import com.l2jserver.gameserver.network.serverpackets.ExCubeGameCloseUI;
import com.l2jserver.gameserver.network.serverpackets.ExCubeGameEnd;
import com.l2jserver.gameserver.network.serverpackets.ExCubeGameExtendedChangePoints;
import com.l2jserver.gameserver.network.serverpackets.RelationChanged;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.util.Rnd;
/**
* @author BiggBoss
*/
public final class BlockCheckerEngine
{
protected static final Logger _log = Logger.getLogger(BlockCheckerEngine.class.getName());
// The object which holds all basic members info
protected ArenaParticipantsHolder _holder;
// Maps to hold player of each team and his points
protected FastMap _redTeamPoints = new FastMap<>();
protected FastMap _blueTeamPoints = new FastMap<>();
// The initial points of the event
protected int _redPoints = 15;
protected int _bluePoints = 15;
// Current used arena
protected int _arena = -1;
// All blocks
protected FastList _spawns = new FastList<>();
// Sets if the red team won the event at the end of this (used for packets)
protected boolean _isRedWinner;
// Time when the event starts. Used on packet sending
protected long _startedTime;
// The needed arena coordinates
// Arena X: team1X, team1Y, team2X, team2Y, ArenaCenterX, ArenaCenterY
protected static final int[][] _arenaCoordinates =
{
// Arena 0 - Team 1 XY, Team 2 XY - CENTER XY
{
-58368,
-62745,
-57751,
-62131,
-58053,
-62417
},
// Arena 1 - Team 1 XY, Team 2 XY - CENTER XY
{
-58350,
-63853,
-57756,
-63266,
-58053,
-63551
},
// Arena 2 - Team 1 XY, Team 2 XY - CENTER XY
{
-57194,
-63861,
-56580,
-63249,
-56886,
-63551
},
// Arena 3 - Team 1 XY, Team 2 XY - CENTER XY
{
-57200,
-62727,
-56584,
-62115,
-56850,
-62391
}
};
// Common z coordinate
private static final int _zCoord = -2405;
// List of dropped items in event (for later deletion)
protected FastList _drops = new FastList<>();
// Default arena
private static final byte DEFAULT_ARENA = -1;
// Event is started
protected boolean _isStarted = false;
// Event end
protected ScheduledFuture> _task;
// Preserve from exploit reward by logging out
protected boolean _abnormalEnd = false;
public BlockCheckerEngine(ArenaParticipantsHolder holder, int arena)
{
_holder = holder;
if ((arena > -1) && (arena < 4))
{
_arena = arena;
}
for (L2PcInstance player : holder.getRedPlayers())
{
_redTeamPoints.put(player, 0);
}
for (L2PcInstance player : holder.getBluePlayers())
{
_blueTeamPoints.put(player, 0);
}
}
/**
* Updates the player holder before the event starts to synchronize all info
* @param holder
*/
public void updatePlayersOnStart(ArenaParticipantsHolder holder)
{
_holder = holder;
}
/**
* Returns the current holder object of this object engine
* @return HandysBlockCheckerManager.ArenaParticipantsHolder
*/
public ArenaParticipantsHolder getHolder()
{
return _holder;
}
/**
* Will return the id of the arena used by this event
* @return false;
*/
public int getArena()
{
return _arena;
}
/**
* Returns the time when the event started
* @return long
*/
public long getStarterTime()
{
return _startedTime;
}
/**
* Returns the current red team points
* @return int
*/
public int getRedPoints()
{
synchronized (this)
{
return _redPoints;
}
}
/**
* Returns the current blue team points
* @return int
*/
public int getBluePoints()
{
synchronized (this)
{
return _bluePoints;
}
}
/**
* Returns the player points
* @param player
* @param isRed
* @return int
*/
public int getPlayerPoints(L2PcInstance player, boolean isRed)
{
if (!_redTeamPoints.containsKey(player) && !_blueTeamPoints.containsKey(player))
{
return 0;
}
if (isRed)
{
return _redTeamPoints.get(player);
}
return _blueTeamPoints.get(player);
}
/**
* Increases player points for his teams
* @param player
* @param team
*/
public synchronized void increasePlayerPoints(L2PcInstance player, int team)
{
if (player == null)
{
return;
}
if (team == 0)
{
int points = _redTeamPoints.get(player) + 1;
_redTeamPoints.put(player, points);
_redPoints++;
_bluePoints--;
}
else
{
int points = _blueTeamPoints.get(player) + 1;
_blueTeamPoints.put(player, points);
_bluePoints++;
_redPoints--;
}
}
/**
* Will add a new drop into the list of dropped items
* @param item
*/
public void addNewDrop(L2ItemInstance item)
{
if (item != null)
{
_drops.add(item);
}
}
/**
* Will return true if the event is already started
* @return boolean
*/
public boolean isStarted()
{
return _isStarted;
}
/**
* Will send all packets for the event members with the relation info
* @param plr
*/
protected void broadcastRelationChanged(L2PcInstance plr)
{
for (L2PcInstance p : _holder.getAllPlayers())
{
p.sendPacket(new RelationChanged(plr, plr.getRelation(p), plr.isAutoAttackable(p)));
}
}
/**
* Called when a there is an empty team. The event will end.
*/
public void endEventAbnormally()
{
try
{
synchronized (this)
{
_isStarted = false;
if (_task != null)
{
_task.cancel(true);
}
_abnormalEnd = true;
ThreadPoolManager.getInstance().executeTask(new EndEvent());
if (Config.DEBUG)
{
_log.config("Handys Block Checker Event at arena " + _arena + " ended due lack of players!");
}
}
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Couldnt end Block Checker event at " + _arena, e);
}
}
/**
* This inner class set ups all player and arena parameters to start the event
*/
public class StartEvent implements Runnable
{
// In event used skills
private final Skill _freeze, _transformationRed, _transformationBlue;
// Common and unparametizer packet
private final ExCubeGameCloseUI _closeUserInterface = new ExCubeGameCloseUI();
public StartEvent()
{
// Initialize all used skills
_freeze = SkillData.getInstance().getSkill(6034, 1);
_transformationRed = SkillData.getInstance().getSkill(6035, 1);
_transformationBlue = SkillData.getInstance().getSkill(6036, 1);
}
/**
* Will set up all player parameters and port them to their respective location based on their teams
*/
private void setUpPlayers()
{
// Set current arena as being used
HandysBlockCheckerManager.getInstance().setArenaBeingUsed(_arena);
// Initialize packets avoiding create a new one per player
_redPoints = _spawns.size() / 2;
_bluePoints = _spawns.size() / 2;
final ExCubeGameChangePoints initialPoints = new ExCubeGameChangePoints(300, _bluePoints, _redPoints);
ExCubeGameExtendedChangePoints clientSetUp;
for (L2PcInstance player : _holder.getAllPlayers())
{
if (player == null)
{
continue;
}
// Send the secret client packet set up
boolean isRed = _holder.getRedPlayers().contains(player);
clientSetUp = new ExCubeGameExtendedChangePoints(300, _bluePoints, _redPoints, isRed, player, 0);
player.sendPacket(clientSetUp);
player.sendPacket(ActionFailed.STATIC_PACKET);
// Teleport Player - Array access
// Team 0 * 2 = 0; 0 = 0, 0 + 1 = 1.
// Team 1 * 2 = 2; 2 = 2, 2 + 1 = 3
int tc = _holder.getPlayerTeam(player) * 2;
// Get x and y coordinates
int x = _arenaCoordinates[_arena][tc];
int y = _arenaCoordinates[_arena][tc + 1];
player.teleToLocation(x, y, _zCoord);
// Set the player team
if (isRed)
{
_redTeamPoints.put(player, 0);
player.setTeam(Team.RED);
}
else
{
_blueTeamPoints.put(player, 0);
player.setTeam(Team.BLUE);
}
player.stopAllEffects();
if (player.hasSummon())
{
player.getSummon().unSummon(player);
}
// Give the player start up effects
// Freeze
_freeze.applyEffects(player, player);
// Transformation
if (_holder.getPlayerTeam(player) == 0)
{
_transformationRed.applyEffects(player, player);
}
else
{
_transformationBlue.applyEffects(player, player);
}
// Set the current player arena
player.setBlockCheckerArena((byte) _arena);
player.setInsideZone(ZoneId.PVP, true);
// Send needed packets
player.sendPacket(initialPoints);
player.sendPacket(_closeUserInterface);
// ExBasicActionList
player.sendPacket(ExBasicActionList.STATIC_PACKET);
broadcastRelationChanged(player);
}
}
@Override
public void run()
{
// Wrong arena passed, stop event
if (_arena == -1)
{
_log.severe("Couldnt set up the arena Id for the Block Checker event, cancelling event...");
return;
}
_isStarted = true;
// Spawn the blocks
ThreadPoolManager.getInstance().executeTask(new SpawnRound(16, 1));
// Start up player parameters
setUpPlayers();
// Set the started time
_startedTime = System.currentTimeMillis() + 300000;
}
}
/**
* This class spawns the second round of boxes and schedules the event end
*/
private class SpawnRound implements Runnable
{
int _numOfBoxes;
int _round;
SpawnRound(int numberOfBoxes, int round)
{
_numOfBoxes = numberOfBoxes;
_round = round;
}
@Override
public void run()
{
if (!_isStarted)
{
return;
}
switch (_round)
{
case 1:
// Schedule second spawn round
_task = ThreadPoolManager.getInstance().scheduleGeneral(new SpawnRound(20, 2), 60000);
break;
case 2:
// Schedule third spawn round
_task = ThreadPoolManager.getInstance().scheduleGeneral(new SpawnRound(14, 3), 60000);
break;
case 3:
// Schedule Event End Count Down
_task = ThreadPoolManager.getInstance().scheduleGeneral(new EndEvent(), 180000);
break;
}
// random % 2, if == 0 will spawn a red block
// if != 0, will spawn a blue block
byte random = 2;
// common template
final L2NpcTemplate template = NpcData.getInstance().getTemplate(18672);
// Spawn blocks
try
{
// Creates 50 new blocks
for (int i = 0; i < _numOfBoxes; i++)
{
L2Spawn spawn = new L2Spawn(template);
spawn.setX(_arenaCoordinates[_arena][4] + Rnd.get(-400, 400));
spawn.setY(_arenaCoordinates[_arena][5] + Rnd.get(-400, 400));
spawn.setZ(_zCoord);
spawn.setAmount(1);
spawn.setHeading(1);
spawn.setRespawnDelay(1);
SpawnTable.getInstance().addNewSpawn(spawn, false);
spawn.init();
L2BlockInstance block = (L2BlockInstance) spawn.getLastSpawn();
// switch color
if ((random % 2) == 0)
{
block.setRed(true);
}
else
{
block.setRed(false);
}
block.disableCoreAI(true);
_spawns.add(spawn);
random++;
}
}
catch (Exception e)
{
_log.warning(getClass().getSimpleName() + ": " + e.getMessage());
}
// Spawn the block carrying girl
if ((_round == 1) || (_round == 2))
{
L2NpcTemplate girl = NpcData.getInstance().getTemplate(18676);
try
{
final L2Spawn girlSpawn = new L2Spawn(girl);
girlSpawn.setX(_arenaCoordinates[_arena][4] + Rnd.get(-400, 400));
girlSpawn.setY(_arenaCoordinates[_arena][5] + Rnd.get(-400, 400));
girlSpawn.setZ(_zCoord);
girlSpawn.setAmount(1);
girlSpawn.setHeading(1);
girlSpawn.setRespawnDelay(1);
SpawnTable.getInstance().addNewSpawn(girlSpawn, false);
girlSpawn.init();
// Schedule his deletion after 9 secs of spawn
ThreadPoolManager.getInstance().scheduleGeneral(new CarryingGirlUnspawn(girlSpawn), 9000);
}
catch (Exception e)
{
_log.warning("Couldnt Spawn Block Checker NPCs! Wrong instance type at npc table?");
_log.warning(getClass().getSimpleName() + ": " + e.getMessage());
}
}
_redPoints += _numOfBoxes / 2;
_bluePoints += _numOfBoxes / 2;
int timeLeft = (int) ((getStarterTime() - System.currentTimeMillis()) / 1000);
ExCubeGameChangePoints changePoints = new ExCubeGameChangePoints(timeLeft, getBluePoints(), getRedPoints());
getHolder().broadCastPacketToTeam(changePoints);
}
}
private class CarryingGirlUnspawn implements Runnable
{
private final L2Spawn _spawn;
protected CarryingGirlUnspawn(L2Spawn spawn)
{
_spawn = spawn;
}
@Override
public void run()
{
if (_spawn == null)
{
_log.warning("HBCE: Block Carrying Girl is null");
return;
}
SpawnTable.getInstance().deleteSpawn(_spawn, false);
_spawn.stopRespawn();
_spawn.getLastSpawn().deleteMe();
}
}
/*
* private class CountDown implements Runnable {
* @Override public void run() { _holder.broadCastPacketToTeam(SystemMessage.getSystemMessage(SystemMessageId.BLOCK_CHECKER_ENDS_5)); ThreadPoolManager.getInstance().scheduleGeneral(new EndEvent(), 5000); } }
*/
/**
* This class erase all event parameters on player and port them back near Handy. Also, unspawn blocks, runs a garbage collector and set as free the used arena
*/
protected class EndEvent implements Runnable
{
// Garbage collector and arena free setter
private void clearMe()
{
HandysBlockCheckerManager.getInstance().clearPaticipantQueueByArenaId(_arena);
_holder.clearPlayers();
_blueTeamPoints.clear();
_redTeamPoints.clear();
HandysBlockCheckerManager.getInstance().setArenaFree(_arena);
for (L2Spawn spawn : _spawns)
{
spawn.stopRespawn();
spawn.getLastSpawn().deleteMe();
SpawnTable.getInstance().deleteSpawn(spawn, false);
spawn = null;
}
_spawns.clear();
for (L2ItemInstance item : _drops)
{
// npe
if (item == null)
{
continue;
}
// a player has it, it will be deleted later
if (!item.isVisible() || (item.getOwnerId() != 0))
{
continue;
}
item.decayMe();
L2World.getInstance().removeObject(item);
}
_drops.clear();
}
/**
* Reward players after event. Tie - No Reward
*/
private void rewardPlayers()
{
if (_redPoints == _bluePoints)
{
return;
}
_isRedWinner = _redPoints > _bluePoints ? true : false;
if (_isRedWinner)
{
rewardAsWinner(true);
rewardAsLooser(false);
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.TEAM_C1_WON);
msg.addString("Red Team");
_holder.broadCastPacketToTeam(msg);
}
else if (_bluePoints > _redPoints)
{
rewardAsWinner(false);
rewardAsLooser(true);
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.TEAM_C1_WON);
msg.addString("Blue Team");
_holder.broadCastPacketToTeam(msg);
}
else
{
rewardAsLooser(true);
rewardAsLooser(false);
}
}
/**
* Reward the speicifed team as a winner team 1) Higher score - 8 extra 2) Higher score - 5 extra
* @param isRed
*/
private void rewardAsWinner(boolean isRed)
{
FastMap tempPoints = isRed ? _redTeamPoints : _blueTeamPoints;
// Main give
for (Entry points : tempPoints.entrySet())
{
if (points.getKey() == null)
{
continue;
}
if (points.getValue() >= 10)
{
points.getKey().addItem("Block Checker", 13067, 2, points.getKey(), true);
}
else
{
tempPoints.remove(points.getKey());
}
}
int first = 0, second = 0;
L2PcInstance winner1 = null, winner2 = null;
for (Entry entry : tempPoints.entrySet())
{
L2PcInstance pc = entry.getKey();
int pcPoints = entry.getValue();
if (pcPoints > first)
{
// Move old data
second = first;
winner2 = winner1;
// Set new data
first = pcPoints;
winner1 = pc;
}
else if (pcPoints > second)
{
second = pcPoints;
winner2 = pc;
}
}
if (winner1 != null)
{
winner1.addItem("Block Checker", 13067, 8, winner1, true);
}
if (winner2 != null)
{
winner2.addItem("Block Checker", 13067, 5, winner2, true);
}
}
/**
* Will reward the looser team with the predefined rewards Player got >= 10 points: 2 coins Player got < 10 points: 0 coins
* @param isRed
*/
private void rewardAsLooser(boolean isRed)
{
FastMap tempPoints = isRed ? _redTeamPoints : _blueTeamPoints;
for (Entry entry : tempPoints.entrySet())
{
L2PcInstance player = entry.getKey();
if ((player != null) && (entry.getValue() >= 10))
{
player.addItem("Block Checker", 13067, 2, player, true);
}
}
}
/**
* Telport players back, give status back and send final packet
*/
private void setPlayersBack()
{
final ExCubeGameEnd end = new ExCubeGameEnd(_isRedWinner);
for (L2PcInstance player : _holder.getAllPlayers())
{
if (player == null)
{
continue;
}
player.stopAllEffects();
// Remove team aura
player.setTeam(Team.NONE);
// Set default arena
player.setBlockCheckerArena(DEFAULT_ARENA);
// Remove the event items
PcInventory inv = player.getInventory();
if (inv.getItemByItemId(13787) != null)
{
long count = inv.getInventoryItemCount(13787, 0);
inv.destroyItemByItemId("Handys Block Checker", 13787, count, player, player);
}
if (inv.getItemByItemId(13788) != null)
{
long count = inv.getInventoryItemCount(13788, 0);
inv.destroyItemByItemId("Handys Block Checker", 13788, count, player, player);
}
broadcastRelationChanged(player);
// Teleport Back
player.teleToLocation(-57478, -60367, -2370);
player.setInsideZone(ZoneId.PVP, false);
// Send end packet
player.sendPacket(end);
player.broadcastUserInfo();
}
}
@Override
public void run()
{
if (!_abnormalEnd)
{
rewardPlayers();
}
setPlayersBack();
clearMe();
_isStarted = false;
_abnormalEnd = false;
}
}
}