/* * 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); } } } }