/*
* This file is part of the L2J Mobius project.
*
* 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 .
*/
package com.l2jmobius.gameserver.model.ceremonyofchaos;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.l2jmobius.gameserver.enums.CeremonyOfChaosResult;
import com.l2jmobius.gameserver.instancemanager.CeremonyOfChaosManager;
import com.l2jmobius.gameserver.instancemanager.InstanceManager;
import com.l2jmobius.gameserver.model.L2Party;
import com.l2jmobius.gameserver.model.L2Party.MessageType;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.appearance.PcAppearance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.eventengine.AbstractEvent;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.EventType;
import com.l2jmobius.gameserver.model.events.ListenerRegisterType;
import com.l2jmobius.gameserver.model.events.annotations.RegisterEvent;
import com.l2jmobius.gameserver.model.events.annotations.RegisterType;
import com.l2jmobius.gameserver.model.events.impl.ceremonyofchaos.OnCeremonyOfChaosMatchResult;
import com.l2jmobius.gameserver.model.events.impl.character.OnCreatureDeath;
import com.l2jmobius.gameserver.model.holders.ItemHolder;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
import com.l2jmobius.gameserver.model.instancezone.Instance;
import com.l2jmobius.gameserver.model.instancezone.InstanceTemplate;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.DeleteObject;
import com.l2jmobius.gameserver.network.serverpackets.ExUserInfoAbnormalVisualEffect;
import com.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import com.l2jmobius.gameserver.network.serverpackets.SkillCoolTime;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import com.l2jmobius.gameserver.network.serverpackets.appearance.ExCuriousHouseMemberUpdate;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseEnter;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseLeave;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseMemberList;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseObserveMode;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseRemainTime;
import com.l2jmobius.gameserver.network.serverpackets.ceremonyofchaos.ExCuriousHouseResult;
/**
* @author UnAfraid
*/
public class CeremonyOfChaosEvent extends AbstractEvent
{
private static final Logger LOGGER = Logger.getLogger(CeremonyOfChaosEvent.class.getName());
private final int _id;
private final Instance _instance;
private final Set _monsters = ConcurrentHashMap.newKeySet();
private long _battleStartTime = 0;
public CeremonyOfChaosEvent(int id, InstanceTemplate template)
{
_id = id;
_instance = InstanceManager.getInstance().createInstance(template, null);
if (_instance.getEnterLocations().size() < CeremonyOfChaosManager.getInstance().getMaxPlayersInArena())
{
LOGGER.warning("There are more member slots: " + _instance.getEnterLocations().size() + " then instance entrance positions: " + CeremonyOfChaosManager.getInstance().getMaxPlayersInArena() + "!");
}
}
public int getId()
{
return _id;
}
public int getInstanceId()
{
return _instance.getId();
}
public Instance getInstance()
{
return _instance;
}
public Set getMonsters()
{
return _monsters;
}
public void preparePlayers()
{
final ExCuriousHouseMemberList membersList = new ExCuriousHouseMemberList(_id, CeremonyOfChaosManager.getInstance().getMaxPlayersInArena(), getMembers().values());
final NpcHtmlMessage msg = new NpcHtmlMessage(0);
int index = 0;
for (CeremonyOfChaosMember member : getMembers().values())
{
final L2PcInstance player = member.getPlayer();
if (player.inObserverMode())
{
player.leaveObserverMode();
}
if (player.isInDuel())
{
player.setIsInDuel(0);
}
// Remember player's last location
player.setLastLocation();
// Hide player information
final PcAppearance app = player.getAppearance();
app.setVisibleName("Challenger" + member.getPosition());
app.setVisibleTitle("");
app.setVisibleClanData(0, 0, 0, 0, 0);
// Register the event instance
player.registerOnEvent(this);
// Load the html
msg.setFile(player.getHtmlPrefix(), "data/html/CeremonyOfChaos/started.htm");
// Remove buffs
player.stopAllEffectsExceptThoseThatLastThroughDeath();
// Player shouldn't be able to move and is hidden
player.setIsImmobilized(true);
player.setInvisible(true);
// Same goes for summon
player.getServitors().values().forEach(s ->
{
s.stopAllEffectsExceptThoseThatLastThroughDeath();
s.setInvisible(true);
s.setIsImmobilized(true);
});
if (player.isFlyingMounted())
{
player.untransform();
}
// If player is dead, revive it
if (player.isDead())
{
player.doRevive();
}
// If player is sitting, stand up
if (player.isSitting())
{
player.standUp();
}
// If player in party, leave it
final L2Party party = player.getParty();
if (party != null)
{
party.removePartyMember(player, MessageType.EXPELLED);
}
// Cancel any started action
player.abortAttack();
player.abortCast();
player.stopMove(null);
player.setTarget(null);
// Unsummon pet
final L2Summon pet = player.getPet();
if (pet != null)
{
pet.unSummon(player);
}
// Unsummon agathion
if (player.getAgathionId() > 0)
{
player.setAgathionId(0);
}
// The character’s HP, MP, and CP are fully recovered.
player.setCurrentHp(player.getMaxHp());
player.setCurrentMp(player.getMaxMp());
player.setCurrentCp(player.getMaxCp());
// Skill reuse timers for all skills that have less than 15 minutes of cooldown time are reset.
for (Skill skill : player.getAllSkills())
{
if (skill.getReuseDelay() <= 900000)
{
player.enableSkill(skill);
}
}
player.sendSkillList();
player.sendPacket(new SkillCoolTime(player));
// Apply the Energy of Chaos skill
for (SkillHolder holder : CeremonyOfChaosManager.getInstance().getVariables().getList(CeremonyOfChaosManager.INITIAL_BUFF_KEY, SkillHolder.class))
{
holder.getSkill().activateSkill(player, player);
}
// Send Enter packet
player.sendPacket(ExCuriousHouseEnter.STATIC_PACKET);
// Send all members
player.sendPacket(membersList);
// Send the entrance html
player.sendPacket(msg);
// Send support items to player
for (ItemHolder holder : CeremonyOfChaosManager.getInstance().getRewards(CeremonyOfChaosManager.INITIAL_ITEMS_KEY).calculateDrops())
{
player.addItem("CoC", holder, null, true);
}
// Teleport player to the arena
player.teleToLocation(_instance.getEnterLocations().get(index++), 0, _instance);
}
getTimers().addTimer("match_start_countdown", StatsSet.valueOf("time", 60), 100, null, null);
getTimers().addTimer("teleport_message1", 10000, null, null);
getTimers().addTimer("teleport_message2", 14000, null, null);
getTimers().addTimer("teleport_message3", 18000, null, null);
}
public void startFight()
{
for (CeremonyOfChaosMember member : getMembers().values())
{
final L2PcInstance player = member.getPlayer();
if (player != null)
{
player.sendPacket(SystemMessageId.THE_MATCH_HAS_STARTED_FIGHT);
player.setIsImmobilized(false);
player.setInvisible(false);
player.broadcastInfo();
player.sendPacket(new ExUserInfoAbnormalVisualEffect(player));
player.getServitors().values().forEach(s ->
{
s.setInvisible(false);
s.setIsImmobilized(false);
s.broadcastInfo();
});
}
}
_battleStartTime = System.currentTimeMillis();
getTimers().addRepeatingTimer("update", 1000, null, null);
}
public void stopFight()
{
getMembers().values().stream().filter(p -> p.getLifeTime() == 0).forEach(this::updateLifeTime);
validateWinner();
final List winners = getWinners();
final List members = new ArrayList<>(getMembers().size());
final SystemMessage msg;
if (winners.isEmpty() || (winners.size() > 1))
{
msg = SystemMessage.getSystemMessage(SystemMessageId.THERE_IS_NO_VICTOR_THE_MATCH_ENDS_IN_A_TIE);
}
else
{
msg = SystemMessage.getSystemMessage(SystemMessageId.CONGRATULATIONS_C1_YOU_WIN_THE_MATCH);
msg.addCharName(winners.get(0).getPlayer());
}
for (CeremonyOfChaosMember member : getMembers().values())
{
final L2PcInstance player = member.getPlayer();
if (player != null)
{
// Send winner message
player.sendPacket(msg);
// Send result
player.sendPacket(new ExCuriousHouseResult(member.getResultType(), this));
members.add(member);
}
}
getTimers().cancelTimer("update", null, null);
getTimers().addTimer("match_end_countdown", StatsSet.valueOf("time", 30), 30 * 1000, null, null);
EventDispatcher.getInstance().notifyEvent(new OnCeremonyOfChaosMatchResult(winners, members));
}
private void teleportPlayersOut()
{
for (CeremonyOfChaosMember member : getMembers().values())
{
final L2PcInstance player = member.getPlayer();
if (player != null)
{
// Leaves observer mode
if (player.inObserverMode())
{
player.setObserving(false);
}
// Revive the player
player.doRevive();
// Remove Energy of Chaos
for (SkillHolder holder : CeremonyOfChaosManager.getInstance().getVariables().getList(CeremonyOfChaosManager.INITIAL_BUFF_KEY, SkillHolder.class))
{
player.stopSkillEffects(holder.getSkill());
}
// Apply buffs on players
for (SkillHolder holder : CeremonyOfChaosManager.getInstance().getVariables().getList(CeremonyOfChaosManager.END_BUFFS_KEYH, SkillHolder.class))
{
holder.getSkill().activateSkill(player, player);
}
// Remove quit button
player.sendPacket(ExCuriousHouseLeave.STATIC_PACKET);
// Remove spectator mode
player.setObserving(false);
player.sendPacket(ExCuriousHouseObserveMode.STATIC_DISABLED);
// Teleport player back
player.teleToLocation(player.getLastLocation(), null);
// Restore player information
final PcAppearance app = player.getAppearance();
app.setVisibleName(null);
app.setVisibleTitle(null);
app.setVisibleClanData(-1, -1, -1, -1, -1);
// Remove player from event
player.removeFromEvent(this);
}
}
getMembers().clear();
_instance.destroy();
}
private void updateLifeTime(CeremonyOfChaosMember member)
{
member.setLifeTime(((int) (System.currentTimeMillis() - _battleStartTime) / 1000));
}
public List getWinners()
{
final List winners = new ArrayList<>();
//@formatter:off
final OptionalInt winnerLifeTime = getMembers().values().stream()
.mapToInt(CeremonyOfChaosMember::getLifeTime)
.max();
if(winnerLifeTime.isPresent())
{
getMembers().values().stream()
.sorted(Comparator.comparingLong(CeremonyOfChaosMember::getLifeTime)
.reversed()
.thenComparingInt(CeremonyOfChaosMember::getScore)
.reversed())
.filter(member -> member.getLifeTime() == winnerLifeTime.getAsInt())
.collect(Collectors.toCollection(() -> winners));
}
//@formatter:on
return winners;
}
private void validateWinner()
{
final List winners = getWinners();
winners.forEach(winner -> winner.setResultType(winners.size() > 1 ? CeremonyOfChaosResult.TIE : CeremonyOfChaosResult.WIN));
}
@Override
public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
{
switch (event)
{
case "update":
{
final int time = (int) CeremonyOfChaosManager.getInstance().getScheduler("stopFight").getRemainingTime(TimeUnit.SECONDS);
broadcastPacket(new ExCuriousHouseRemainTime(time));
getMembers().values().forEach(p -> broadcastPacket(new ExCuriousHouseMemberUpdate(p)));
// Validate winner
if (getMembers().values().stream().filter(member -> !member.isDefeated()).count() <= 1)
{
stopFight();
}
break;
}
case "teleport_message1":
{
broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.PROVE_YOUR_ABILITIES));
break;
}
case "teleport_message2":
{
broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_NO_ALLIES_HERE_EVERYONE_IS_AN_ENEMY));
break;
}
case "teleport_message3":
{
broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.IT_WILL_BE_A_LONELY_BATTLE_BUT_I_WISH_YOU_VICTORY));
break;
}
case "match_start_countdown":
{
final int time = params.getInt("time", 0);
final SystemMessage countdown = SystemMessage.getSystemMessage(SystemMessageId.THE_MATCH_WILL_START_IN_S1_SECOND_S);
countdown.addByte(time);
broadcastPacket(countdown);
// Reschedule
if (time == 60)
{
getTimers().addTimer(event, params.set("time", 30), 30 * 1000, null, null);
}
else if ((time == 30) || (time == 20))
{
getTimers().addTimer(event, params.set("time", time - 10), 10 * 1000, null, null);
}
else if (time == 10)
{
getTimers().addTimer(event, params.set("time", 5), 5 * 1000, null, null);
}
else if ((time > 1) && (time <= 5))
{
getTimers().addTimer(event, params.set("time", time - 1), 1000, null, null);
}
break;
}
case "match_end_countdown":
{
final int time = params.getInt("time", 0);
final SystemMessage countdown = SystemMessage.getSystemMessage(SystemMessageId.IN_S1_SECOND_S_YOU_WILL_BE_MOVED_TO_WHERE_YOU_WERE_BEFORE_PARTICIPATING_IN_THE_CEREMONY_OF_CHAOS);
countdown.addByte(time);
broadcastPacket(countdown);
// Reschedule
if ((time == 30) || (time == 20))
{
getTimers().addTimer(event, params.set("time", time - 10), 10 * 1000, null, null);
}
else if ((time > 0) && (time <= 10))
{
getTimers().addTimer(event, params.set("time", time - 1), 1000, null, null);
}
else if (time == 0)
{
teleportPlayersOut();
}
break;
}
}
}
@RegisterEvent(EventType.ON_CREATURE_DEATH)
@RegisterType(ListenerRegisterType.GLOBAL_PLAYERS)
public void onPlayerDeath(OnCreatureDeath event)
{
if (event.getAttacker().isPlayer() && event.getTarget().isPlayer())
{
final L2PcInstance attackerPlayer = event.getAttacker().getActingPlayer();
final L2PcInstance targetPlayer = event.getTarget().getActingPlayer();
final CeremonyOfChaosMember attackerMember = getMembers().get(attackerPlayer.getObjectId());
final CeremonyOfChaosMember targetMember = getMembers().get(targetPlayer.getObjectId());
final DeleteObject deleteObject = new DeleteObject(targetPlayer);
if ((attackerMember != null) && (targetMember != null))
{
attackerMember.incrementScore();
updateLifeTime(targetMember);
// Mark player as defeated
targetMember.setDefeated(true);
// Delete target player
//@formatter:off
getMembers().values().stream()
.filter(member -> member.getObjectId() != targetPlayer.getObjectId())
.map(CeremonyOfChaosMember::getPlayer)
.forEach(deleteObject::sendTo);
//@formatter:on
// Make the target observer
targetPlayer.setObserving(true);
// Make the target spectator
targetPlayer.sendPacket(ExCuriousHouseObserveMode.STATIC_ENABLED);
}
}
}
}