Coatl boss implementation.

Thanks to notorionn.
This commit is contained in:
MobiusDevelopment
2024-12-22 01:52:19 +02:00
parent d05249e512
commit 0ac7172388
32 changed files with 7417 additions and 1682 deletions

View File

@@ -0,0 +1,129 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package ai.areas.Gludio;
import org.l2jmobius.commons.threads.ThreadPool;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Player;
import ai.AbstractNpcAI;
/**
* @author Notorion
*/
public class LizardmanBarracksXPParty extends AbstractNpcAI
{
// Monsters Lizardman Barracks
private static final int[] MONSTER_IDS =
{
23834,
23835,
23836,
23837,
23838,
23839
};
// Distance radius for XP bonus Party
private static final int BONUS_RADIUS = 1500;
// Maximum XP Bonus (Level 126)
private static final double MAX_BONUS_PERCENTAGE = 0.25; // Bonus 25%
private LizardmanBarracksXPParty()
{
addKillId(MONSTER_IDS);
}
@Override
public String onKill(Npc npc, Player killer, boolean isSummon)
{
if (npc.isMonster() && contains(MONSTER_IDS, npc.getId()))
{
final org.l2jmobius.gameserver.model.Party party = killer.getParty();
if (party != null)
{
for (Player member : party.getMembers())
{
if (killer.isInsideRadius3D(member, BONUS_RADIUS) && (member.getLevel() >= 117) && (member.getLevel() <= 131))
{
final double bonusPercentage = calculateBonusPercentage(member.getLevel());
final long bonusXp = (long) (npc.getExpReward(member.getLevel()) * bonusPercentage);
// Adiciona a experiência total ao jogador após 1 segundo
ThreadPool.schedule(() ->
{
member.addExpAndSp(bonusXp, 0);
}, 1000); // 1000 milissegundos = 1 segundo
}
}
}
}
return super.onKill(npc, killer, isSummon);
}
private double calculateBonusPercentage(int level)
{
if ((level < 117) || (level > 131))
{
return 0; // No bonus for out of range levels
}
// Sets the percentage proportional to the level
switch (level)
{
case 117:
return MAX_BONUS_PERCENTAGE * 0.1;
case 118:
case 119:
case 120:
return MAX_BONUS_PERCENTAGE * 0.2;
case 121:
return MAX_BONUS_PERCENTAGE * 0.4;
case 122:
return MAX_BONUS_PERCENTAGE * 0.6;
case 123:
return MAX_BONUS_PERCENTAGE * 0.97;
case 124:
case 125:
case 126:
case 127:
case 128:
case 129:
case 130:
case 131:
return MAX_BONUS_PERCENTAGE;
default:
return 0;
}
}
private static boolean contains(int[] array, int value)
{
for (int i : array)
{
if (i == value)
{
return true;
}
}
return false;
}
public static void main(String[] args)
{
new LizardmanBarracksXPParty();
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package ai.areas.Gludio;
import org.l2jmobius.commons.threads.ThreadPool;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Player;
import ai.AbstractNpcAI;
/**
* @author Notorion
*/
public class LizardmanTempleXPParty extends AbstractNpcAI
{
// Monsters Lizardman Temple
private static final int[] MONSTER_IDS =
{
24687,
24688,
24689,
24690,
24691,
24692,
24693,
24694
};
// Distance radius for XP bonus Party
private static final int BONUS_RADIUS = 1500;
// Maximum XP Bonus (Level 128)
private static final double MAX_BONUS_PERCENTAGE = 0.25; // Bonus 25%
private LizardmanTempleXPParty()
{
addKillId(MONSTER_IDS);
}
@Override
public String onKill(Npc npc, Player killer, boolean isSummon)
{
if (npc.isMonster() && contains(MONSTER_IDS, npc.getId()))
{
final org.l2jmobius.gameserver.model.Party party = killer.getParty();
if (party != null)
{
for (Player member : party.getMembers())
{
if (killer.isInsideRadius3D(member, BONUS_RADIUS) && (member.getLevel() >= 118) && (member.getLevel() <= 131))
{
final double bonusPercentage = calculateBonusPercentage(member.getLevel());
final long bonusXp = (long) (npc.getExpReward(member.getLevel()) * bonusPercentage);
// Adiciona a experiência total ao jogador após 1 segundo
ThreadPool.schedule(() ->
{
member.addExpAndSp(bonusXp, 0);
}, 1000); // 1000 milissegundos = 1 segundo
}
}
}
}
return super.onKill(npc, killer, isSummon);
}
private double calculateBonusPercentage(int level)
{
if ((level < 118) || (level > 131))
{
return 0; // No bonus for out of range levels
}
// Sets the percentage proportional to the level
switch (level)
{
case 118:
return MAX_BONUS_PERCENTAGE * 0.1;
case 119:
return MAX_BONUS_PERCENTAGE * 0.2;
case 120:
return MAX_BONUS_PERCENTAGE * 0.3;
case 121:
return MAX_BONUS_PERCENTAGE * 0.4;
case 122:
return MAX_BONUS_PERCENTAGE * 0.5;
case 123:
return MAX_BONUS_PERCENTAGE * 0.6;
case 124:
case 125:
return MAX_BONUS_PERCENTAGE * 0.8;
case 126:
return MAX_BONUS_PERCENTAGE * 0.9;
case 127:
return MAX_BONUS_PERCENTAGE * 0.95;
case 128:
case 129:
case 130:
case 131:
return MAX_BONUS_PERCENTAGE;
default:
return 0;
}
}
private static boolean contains(int[] array, int value)
{
for (int i : array)
{
if (i == value)
{
return true;
}
}
return false;
}
public static void main(String[] args)
{
new LizardmanTempleXPParty();
}
}

View File

@@ -0,0 +1,902 @@
/*
* Copyright (c) 2013 L2jMobius
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package ai.bosses.Coatl;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.l2jmobius.commons.threads.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.SpawnTable;
import org.l2jmobius.gameserver.data.xml.SkillData;
import org.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.SkillHolder;
import org.l2jmobius.gameserver.model.skill.BuffInfo;
import org.l2jmobius.gameserver.model.skill.Skill;
import org.l2jmobius.gameserver.model.skill.SkillCaster;
import org.l2jmobius.gameserver.model.zone.type.ArenaZone;
import ai.AbstractNpcAI;
/**
* @author Notorion
*/
public class Coatl extends AbstractNpcAI
{
// NPCs
private static final int MAIN_BOSS_ID = 29408; // Coatl RaidBoss
private static final int INVISIBLE_COATL_ID = 29413; // Coatl invisible
private static final int FLAME_TOTEM_ID = 29409;
private static final int KASHA_TOTEM_ID = 29412;
private static final int EARTH_TOTEM_ID = 29411;
private static final int WATER_TOTEM_ID = 29410;
private static final int[] TOTEM_IDS =
{
WATER_TOTEM_ID,
KASHA_TOTEM_ID,
EARTH_TOTEM_ID,
FLAME_TOTEM_ID
};
// Locations
private static final Location FLAME_TOTEM_LOCATION = new Location(-83613, 209856, -5248, 371);
private static final Location KASHA_TOTEM_LOCATION = new Location(-84417, 210668, -5254, 16503);
private static final Location EARTH_TOTEM_LOCATION = new Location(-85226, 209857, -5258, 32689);
private static final Location WATER_TOTEM_LOCATION = new Location(-84415, 209044, -5258, 49298);
private static final Location INVISIBLE_COATL_LOCATION = new Location(-84416, 209860, -5254);
private static final Location SPAWN_LOCATION = new Location(-84416, 209860, -5254);
// private static final Location GLUDIO_LOCATION_OUTSIDE_ARENA = new Location(-12847, 121707, -2969); // Optional, teleport for attackers from outside
// private static final Location GLUDIO_LOCATION_PLAYER_EXIT = new Location(-14575, 121425, -3011); // Optional, Teleport for those who left the arena
// Skills
// private static final SkillHolder KILL_PLAYERS_SKILL = new SkillHolder(34796, 1);
private static final SkillHolder WATER_TOTEM_SKILL = new SkillHolder(34807, 1);
// private static final SkillHolder FLAME_TOTEM_SKILL = new SkillHolder(34806, 1);
private static final SkillHolder EARTH_TOTEM_SKILL = new SkillHolder(34808, 1);
private static final SkillHolder KASHA_TOTEM_SKILL = new SkillHolder(34809, 1);
private static final int FLAME_TOTEM_SKILL_ID = 34806;
// private static final SkillHolder TOTEMS_SKILL = new SkillHolder(34810, 1);
private static final SkillHolder FLAME_EXPLOSION_SKILL = new SkillHolder(34805, 1);
private static final SkillHolder EARTH_EXPLOSION_SKILL = new SkillHolder(34811, 1);
private static final SkillHolder KASHA_EXPLOSION_SKILL = new SkillHolder(34812, 1);
private static final SkillHolder WATER_EXPLOSION_SKILL = new SkillHolder(34813, 1);
private static final SkillHolder SKILL_WATER = new SkillHolder(34807, 1);
private static final SkillHolder SKILL_KASHA = new SkillHolder(34809, 1);
private static final SkillHolder SKILL_EARTH = new SkillHolder(34808, 1);
private static final SkillHolder SKILL_FLAME = new SkillHolder(34806, 1);
private static final SkillHolder SKILL_1 = new SkillHolder(34789, 1);
private static final SkillHolder SKILL_2 = new SkillHolder(34790, 1);
private static final SkillHolder SKILL_3 = new SkillHolder(34791, 1);
private static final SkillHolder SKILL_4 = new SkillHolder(34792, 1);
private static final SkillHolder LIMIT_BARRIER = new SkillHolder(29515, 1);
// Misc
private static final ArenaZone ARENA_ZONE = ZoneManager.getInstance().getZoneByName("Coatls_Lair", ArenaZone.class);
private static final int HIT_COUNT = 2000; // 2000 hits to break the barrier.
private static final int BARRIER_DURATION_MILLIS = 600000; // 10 minutes barrier duration.
private static final int HIT_COUNT_RENEW = 500; // 500 hits in 20 seconds to continue without the barrier.
private static final int RENEW_DURATION_MILLIS = 20000; // 20 seconds of Coatl vulnerability, requires 500 hits in 20 seconds.
private final Set<Integer> _involvedPlayers = new HashSet<>();
private final Map<Creature, Integer> _aggroList = new ConcurrentHashMap<>();
private final Map<Npc, Integer> _coatlHits = new ConcurrentHashMap<>();
private final Map<String, Object[]> _timerParameters = new HashMap<>();
private final Queue<Runnable> _specialMechanicsQueue = new LinkedList<>();
private boolean _specialMechanicsActive = false;
private boolean _vulnerablePhase = false;
private boolean _barrierActivated = false;
private boolean _hp76Triggered = false;
private boolean _hp70Triggered = false;
private boolean _hp50Triggered = false;
private boolean _hp40Triggered = false;
private boolean _hp30Triggered = false;
private boolean _hp10Triggered = false;
private static Npc _spawnedMainBoss;
private static Npc _invisibleCoatl;
private static Npc _flameTotem;
private static Npc _kashaTotem;
private static Npc _earthTotem;
private static Npc _waterTotem;
private Coatl()
{
addAttackId(MAIN_BOSS_ID);
addKillId(MAIN_BOSS_ID);
final long currentTime = System.currentTimeMillis();
final Calendar calendar = Calendar.getInstance();
// Spawn time to 21:00 Monday.
calendar.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
calendar.set(Calendar.HOUR_OF_DAY, 21);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
// Spawn Coatl
if ((currentTime > calendar.getTimeInMillis()) && (SpawnTable.getInstance().getAnySpawn(MAIN_BOSS_ID) == null) && GlobalVariablesManager.getInstance().getBoolean("COATL_ALIVE", true))
{
spawnCoatl();
}
if (calendar.getTimeInMillis() < currentTime)
{
calendar.add(Calendar.WEEK_OF_YEAR, 1);
}
ThreadPool.scheduleAtFixedRate(this::spawnCoatl, calendar.getTimeInMillis() - currentTime, 604800000); // 604800000 milissegundos = 1 week
startQuestTimer("check_arena", 10000, null, null, true);
}
private void spawnCoatl()
{
_spawnedMainBoss = addSpawn(MAIN_BOSS_ID, SPAWN_LOCATION);
GlobalVariablesManager.getInstance().set("COATL_ALIVE", true);
// Spawn.
_invisibleCoatl = addSpawn(INVISIBLE_COATL_ID, INVISIBLE_COATL_LOCATION);
_flameTotem = addSpawn(FLAME_TOTEM_ID, FLAME_TOTEM_LOCATION);
_kashaTotem = addSpawn(KASHA_TOTEM_ID, KASHA_TOTEM_LOCATION);
_earthTotem = addSpawn(EARTH_TOTEM_ID, EARTH_TOTEM_LOCATION);
_waterTotem = addSpawn(WATER_TOTEM_ID, WATER_TOTEM_LOCATION);
// startQuestTimer("dispel_boss_buffs", 250, null, null, true);
}
@Override
public String onEvent(String event, Npc npc, Player player)
{
switch (event)
{
case "check_arena":
{
if (_spawnedMainBoss != null)
{
checkPlayersInArena();
checkBossHP();
}
break;
}
case "activate_barrier":
{
_barrierActivated = true;
LIMIT_BARRIER.getSkill().applyEffects(_spawnedMainBoss, _spawnedMainBoss);
npc.setInvul(true);
_vulnerablePhase = false;
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
_coatlHits.put(npc, 0);
break;
}
case "remove_barrier":
{
_barrierActivated = false;
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
_coatlHits.put(npc, 0);
break;
}
case "castWaterTotemSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(WATER_TOTEM_SKILL.getSkill());
}
break;
}
case "castKashaTotemSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(KASHA_TOTEM_SKILL.getSkill());
}
break;
}
case "castEarthTotemSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(EARTH_TOTEM_SKILL.getSkill());
}
break;
}
case "castFlameTotemSkill":
{
if (npc != null)
{
npc.setTarget(npc);
final Skill flameTotemSkill = SkillData.getInstance().getSkill(FLAME_TOTEM_SKILL_ID, 1);
if (flameTotemSkill != null)
{
npc.doCast(flameTotemSkill);
}
}
break;
}
case "dispel_boss_buffs":
{
dispelBossBuffs(_spawnedMainBoss);
break;
}
case "waterTotemMechanicsEnd":
{
final Object[] parameters = _timerParameters.get("waterTotemMechanicsEnd");
if (parameters != null)
{
final int waterBuffId = (int) parameters[0];
final int effectSkillId = (int) parameters[1];
cancelQuestTimer("coatl_common_skills", _spawnedMainBoss, null);
killPlayersInArena(waterBuffId);
castSkillOnTotems(SkillData.getInstance().getSkill(effectSkillId, 1));
cancelTotemTimers();
}
break;
}
case "kashaTotemMechanicsEnd":
{
final Object[] parameters = _timerParameters.get("kashaTotemMechanicsEnd");
if (parameters != null)
{
final int kashaBuffId = (int) parameters[0];
final int effectSkillId = (int) parameters[1];
cancelQuestTimer("coatl_common_skills", _spawnedMainBoss, null);
killPlayersInArena(kashaBuffId);
castSkillOnTotems(SkillData.getInstance().getSkill(effectSkillId, 1));
cancelTotemTimers();
}
break;
}
case "earthTotemMechanicsEnd":
{
final Object[] parameters = _timerParameters.get("earthTotemMechanicsEnd");
if (parameters != null)
{
final int earthBuffId = (int) parameters[0];
final int effectSkillId = (int) parameters[1];
cancelQuestTimer("coatl_common_skills", _spawnedMainBoss, null);
killPlayersInArena(earthBuffId);
castSkillOnTotems(SkillData.getInstance().getSkill(effectSkillId, 1));
cancelTotemTimers();
}
break;
}
case "flameTotemMechanicsEnd":
{
final Object[] parameters = _timerParameters.get("flameTotemMechanicsEnd");
if (parameters != null)
{
final int flameBuffId = (int) parameters[0];
final int effectSkillId = (int) parameters[1];
cancelQuestTimer("coatl_common_skills", _spawnedMainBoss, null);
killPlayersInArena(flameBuffId);
castSkillOnTotems(SkillData.getInstance().getSkill(effectSkillId, 1));
cancelTotemTimers();
}
break;
}
case "castWaterExplosionSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(WATER_EXPLOSION_SKILL.getSkill());
}
break;
}
case "castKashaExplosionSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(KASHA_EXPLOSION_SKILL.getSkill());
}
break;
}
case "castEarthExplosionSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(EARTH_EXPLOSION_SKILL.getSkill());
}
break;
}
case "castFlameExplosionSkill":
{
if (npc != null)
{
npc.setTarget(npc);
npc.doCast(FLAME_EXPLOSION_SKILL.getSkill());
}
break;
}
}
return super.onEvent(event, npc, player);
}
private void addAggro(Creature attacker, int damage)
{
if ((attacker == null) || attacker.isDead())
{
return;
}
final int newAggroVal = damage + getRandom(3000);
final int aggroVal = _aggroList.getOrDefault(attacker, 0) + 1000;
if (aggroVal < newAggroVal)
{
_aggroList.put(attacker, newAggroVal);
}
}
private void manageSkills(Npc npc)
{
if (npc.isCastingNow())
{
return;
}
// Optional.
// if (npc.isCastingNow() || npc.isCoreAIDisabled() || !npc.isInCombat())
// {
// return;
// }
_aggroList.forEach((attacker, aggro) ->
{
if ((attacker == null) || attacker.isDead() || (npc.calculateDistance3D(attacker) > 3000))
{
_aggroList.remove(attacker);
}
});
final Creature topAttacker = _aggroList.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
if ((topAttacker != null) && !_specialMechanicsActive)
{
SkillHolder skillToCast = null;
final int randomSkill = getRandom(100);
if (randomSkill < 49)
{
skillToCast = SKILL_4;
}
else if (randomSkill < 50)
{
skillToCast = SKILL_1;
}
else if (randomSkill < 70)
{
skillToCast = SKILL_2;
}
else
{
skillToCast = SKILL_3;
}
if (SkillCaster.checkUseConditions(npc, skillToCast.getSkill()))
{
npc.doCast(skillToCast.getSkill());
}
}
}
private void cancelTotemTimers()
{
cancelQuestTimer("castWaterTotemSkill", _waterTotem, null);
cancelQuestTimer("castKashaTotemSkill", _kashaTotem, null);
cancelQuestTimer("castEarthTotemSkill", _earthTotem, null);
cancelQuestTimer("castFlameTotemSkill", _flameTotem, null);
}
private void killPlayersInArena(int buffId)
{
for (Creature creature : ARENA_ZONE.getCharactersInside())
{
if (!creature.isPlayer())
{
continue;
}
boolean hasBuff = false;
for (BuffInfo effect : creature.getEffectList().getEffects())
{
if (effect.getSkill().getId() == buffId)
{
hasBuff = true;
break;
}
}
if (!hasBuff)
{
creature.doDie(_spawnedMainBoss);
}
}
}
public void castSkillOnTotems(Skill skill)
{
if (_waterTotem != null)
{
_waterTotem.setTarget(_waterTotem);
_waterTotem.doCast(skill);
}
if (_kashaTotem != null)
{
_kashaTotem.setTarget(_kashaTotem);
_kashaTotem.doCast(skill);
}
if (_earthTotem != null)
{
_earthTotem.setTarget(_earthTotem);
_earthTotem.doCast(skill);
}
if (_flameTotem != null)
{
_flameTotem.setTarget(_flameTotem);
_flameTotem.doCast(skill);
}
}
private void checkPlayersInArena()
{
for (Creature creature : ARENA_ZONE.getCharactersInside())
{
if (!creature.isPlayer())
{
continue;
}
if (ARENA_ZONE.isInsideZone(creature))
{
if (!_involvedPlayers.contains(creature.getObjectId()))
{
_involvedPlayers.add(creature.getObjectId());
}
}
// else if (involvedPlayers.contains(creature.getObjectId()))
// {
// creature.teleToLocation(GLUDIO_LOCATION_PLAYER_EXIT, false);
// involvedPlayers.remove(creature.getObjectId());
// }
// if (!ARENA_ZONE.isInsideZone(player) && (creature.getTarget() == _spawnedMainBoss))
// {
// creature.teleToLocation(GLUDIO_LOCATION_OUTSIDE_ARENA, false);
// }
if ((_spawnedMainBoss != null) && !ARENA_ZONE.isInsideZone(_spawnedMainBoss))
{
// Teleporting Coatl to spawn location if leaving arena. Officially Coatl runs to spawn.
_spawnedMainBoss.stopMove(null);
_spawnedMainBoss.setXYZ(SPAWN_LOCATION.getX(), SPAWN_LOCATION.getY(), SPAWN_LOCATION.getZ());
// Restores HP to 100%
_spawnedMainBoss.setCurrentHp(_spawnedMainBoss.getMaxHp());
}
}
}
@Override
public String onAttack(Npc npc, Player attacker, int damage, boolean isSummon)
{
if (!_barrierActivated)
{
_barrierActivated = true;
LIMIT_BARRIER.getSkill().applyEffects(_spawnedMainBoss, _spawnedMainBoss);
npc.setInvul(true);
startQuestTimer("remove_barrier", BARRIER_DURATION_MILLIS, npc, null);
_coatlHits.put(npc, 0);
}
if (_vulnerablePhase)
{
final int hits = _coatlHits.getOrDefault(npc, 0) + 1;
_coatlHits.put(npc, hits);
if (hits >= HIT_COUNT_RENEW)
{
cancelQuestTimer("activate_barrier", npc, null);
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
_coatlHits.put(npc, 0);
}
}
else
{
final int hits = _coatlHits.getOrDefault(npc, 0) + 1;
_coatlHits.put(npc, hits);
if (hits >= HIT_COUNT)
{
npc.stopSkillEffects(LIMIT_BARRIER.getSkill());
npc.setInvul(false);
cancelQuestTimer("remove_barrier", npc, null);
_vulnerablePhase = true;
startQuestTimer("activate_barrier", RENEW_DURATION_MILLIS, npc, null);
_coatlHits.put(npc, 0);
}
}
checkBossHP();
addAggro(attacker, damage);
manageSkills(npc);
// if (!ARENA_ZONE.isInsideZone(attacker))
// {
// attacker.teleToLocation(GLUDIO_LOCATION_OUTSIDE_ARENA, false);
// }
manageSkills(npc);
return super.onAttack(npc, attacker, damage, isSummon);
}
private void checkBossHP()
{
if (_spawnedMainBoss != null)
{
manageSkills(_spawnedMainBoss);
final double currentHP = _spawnedMainBoss.getCurrentHp();
final long maxHP = _spawnedMainBoss.getMaxHp(); // Corrigido para long
final int currentHPPercentage = (int) ((currentHP / maxHP) * 100);
if (currentHPPercentage <= 0)
{
onNpcDeath(_spawnedMainBoss);
return;
}
if ((currentHPPercentage <= 76) && !_hp76Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp76Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
else if ((currentHPPercentage <= 70) && !_hp70Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp70Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
else if ((currentHPPercentage <= 50) && !_hp50Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp50Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
else if ((currentHPPercentage <= 40) && !_hp40Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp40Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
else if ((currentHPPercentage <= 30) && !_hp30Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp30Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
else if ((currentHPPercentage <= 10) && !_hp10Triggered && !_spawnedMainBoss.isCastingNow())
{
_hp10Triggered = true;
_specialMechanicsQueue.offer(this::startRandomTotemMechanics);
processSpecialMechanicsQueue();
}
}
}
private void processSpecialMechanicsQueue()
{
if (!_specialMechanicsActive && !_specialMechanicsQueue.isEmpty())
{
_specialMechanicsActive = true;
final Runnable mechanic = _specialMechanicsQueue.poll();
mechanic.run();
}
}
private void dispelBossBuffs(Npc boss)
{
if ((boss != null) && !boss.isDead())
{
boss.stopSkillEffects(SKILL_WATER.getSkill());
boss.stopSkillEffects(SKILL_KASHA.getSkill());
boss.stopSkillEffects(SKILL_EARTH.getSkill());
boss.stopSkillEffects(SKILL_FLAME.getSkill());
boss.stopSkillEffects(null, 46797);
}
}
private void start78PercentMechanics()
{
if (_waterTotem != null)
{
_waterTotem.setTarget(_waterTotem);
_waterTotem.doCast(WATER_TOTEM_SKILL.getSkill());
startQuestTimer("castWaterTotemSkill", 3100, _waterTotem, null, true);
}
if (_kashaTotem != null)
{
_kashaTotem.setTarget(_kashaTotem);
_kashaTotem.doCast(KASHA_TOTEM_SKILL.getSkill());
startQuestTimer("castKashaTotemSkill", 3100, _kashaTotem, null, true);
}
if (_earthTotem != null)
{
_earthTotem.setTarget(_earthTotem);
_earthTotem.doCast(EARTH_TOTEM_SKILL.getSkill());
startQuestTimer("castEarthTotemSkill", 3100, _earthTotem, null, true);
}
if (_flameTotem != null)
{
_flameTotem.setTarget(_flameTotem);
Skill flameTotemSkill = SkillData.getInstance().getSkill(FLAME_TOTEM_SKILL_ID, 1);
if (flameTotemSkill != null)
{
_flameTotem.doCast(flameTotemSkill);
startQuestTimer("castFlameTotemSkill", 3100, _flameTotem, null, true);
}
}
}
private void startRandomTotemMechanics()
{
final int totemIndex = Rnd.get(TOTEM_IDS.length);
switch (totemIndex)
{
case 0: // Water
{
startWaterTotemMechanics();
break;
}
case 1: // Kasha
{
startKashaTotemMechanics();
break;
}
case 2: // Earth
{
startEarthTotemMechanics();
break;
}
case 3: // Flame
{
startFlameTotemMechanics();
break;
}
}
_specialMechanicsActive = false;
processSpecialMechanicsQueue();
startQuestTimer("dispel_boss_buffs", 250, null, null, true);
}
private void startWaterTotemMechanics()
{
final int bossMainCastSkillId = 34800;
final int coatlInvisibleSkillId = 34796;
final int waterBuffId = 34807;
final int effectSkillId = 34810;
final Skill kashaProtection = SkillData.getInstance().getSkill(34816, 1);
if (kashaProtection != null)
{
kashaProtection.applyEffects(_spawnedMainBoss, _spawnedMainBoss);
}
Skill skill = SkillData.getInstance().getSkill(bossMainCastSkillId, 1);
if (!_spawnedMainBoss.isSkillDisabled(skill))
{
_spawnedMainBoss.setTarget(_spawnedMainBoss);
_spawnedMainBoss.doCast(skill);
}
if (_invisibleCoatl != null)
{
skill = SkillData.getInstance().getSkill(coatlInvisibleSkillId, 1);
if (!_invisibleCoatl.isSkillDisabled(skill))
{
_invisibleCoatl.setTarget(_invisibleCoatl);
_invisibleCoatl.doCast(skill);
}
}
start78PercentMechanics();
_timerParameters.put("waterTotemMechanicsEnd", new Object[]
{
waterBuffId,
effectSkillId
});
startQuestTimer("waterTotemMechanicsEnd", 9000L, _spawnedMainBoss, null);
startQuestTimer("castWaterExplosionSkill", 9000L, _invisibleCoatl, null);
}
private void startKashaTotemMechanics()
{
final int bossMainCastSkillId = 34799;
final int coatlInvisibleSkillId = 34795;
final int kashaBuffId = 34809;
final int effectSkillId = 34810;
final Skill kashaProtection = SkillData.getInstance().getSkill(34816, 1);
if (kashaProtection != null)
{
kashaProtection.applyEffects(_spawnedMainBoss, _spawnedMainBoss);
}
Skill skill = SkillData.getInstance().getSkill(bossMainCastSkillId, 1);
if (!_spawnedMainBoss.isSkillDisabled(skill))
{
_spawnedMainBoss.setTarget(_spawnedMainBoss);
_spawnedMainBoss.doCast(skill);
}
if (_invisibleCoatl != null)
{
skill = SkillData.getInstance().getSkill(coatlInvisibleSkillId, 1);
if (!_invisibleCoatl.isSkillDisabled(skill))
{
_invisibleCoatl.setTarget(_invisibleCoatl);
_invisibleCoatl.doCast(skill);
}
}
start78PercentMechanics();
_timerParameters.put("kashaTotemMechanicsEnd", new Object[]
{
kashaBuffId,
effectSkillId
});
startQuestTimer("kashaTotemMechanicsEnd", 9000L, _spawnedMainBoss, null);
startQuestTimer("castKashaExplosionSkill", 9000L, _invisibleCoatl, null);
}
private void startEarthTotemMechanics()
{
final int bossMainCastSkillId = 34798;
final int coatlInvisibleSkillId = 34794;
final int earthBuffId = 34808;
final int effectSkillId = 34810;
final Skill kashaProtection = SkillData.getInstance().getSkill(34816, 1);
if (kashaProtection != null)
{
kashaProtection.applyEffects(_spawnedMainBoss, _spawnedMainBoss);
}
Skill skill = SkillData.getInstance().getSkill(bossMainCastSkillId, 1);
if (!_spawnedMainBoss.isSkillDisabled(skill))
{
_spawnedMainBoss.setTarget(_spawnedMainBoss);
_spawnedMainBoss.doCast(skill);
}
if (_invisibleCoatl != null)
{
skill = SkillData.getInstance().getSkill(coatlInvisibleSkillId, 1);
if (!_invisibleCoatl.isSkillDisabled(skill))
{
_invisibleCoatl.setTarget(_invisibleCoatl);
_invisibleCoatl.doCast(skill);
}
}
start78PercentMechanics();
_timerParameters.put("earthTotemMechanicsEnd", new Object[]
{
earthBuffId,
effectSkillId
});
startQuestTimer("earthTotemMechanicsEnd", 9000L, _spawnedMainBoss, null);
startQuestTimer("castEarthExplosionSkill", 9000L, _invisibleCoatl, null);
}
private void startFlameTotemMechanics()
{
final int bossMainCastSkillId = 34797;
final int coatlInvisibleSkillId = 34793;
final int flameBuffId = 34806;
final int effectSkillId = 34810;
final Skill kashaProtection = SkillData.getInstance().getSkill(34816, 1);
if (kashaProtection != null)
{
kashaProtection.applyEffects(_spawnedMainBoss, _spawnedMainBoss);
}
Skill skill = SkillData.getInstance().getSkill(bossMainCastSkillId, 1);
if (!_spawnedMainBoss.isSkillDisabled(skill))
{
_spawnedMainBoss.setTarget(_spawnedMainBoss);
_spawnedMainBoss.doCast(skill);
}
if (_invisibleCoatl != null)
{
skill = SkillData.getInstance().getSkill(coatlInvisibleSkillId, 1);
if (!_invisibleCoatl.isSkillDisabled(skill))
{
_invisibleCoatl.setTarget(_invisibleCoatl);
_invisibleCoatl.doCast(skill);
}
}
start78PercentMechanics();
_timerParameters.put("flameTotemMechanicsEnd", new Object[]
{
flameBuffId,
effectSkillId
});
startQuestTimer("flameTotemMechanicsEnd", 9000L, _spawnedMainBoss, null);
startQuestTimer("castFlameExplosionSkill", 9000L, _invisibleCoatl, null);
}
@Override
public String onKill(Npc npc, Player killer, boolean isSummon)
{
cancelQuestTimers("check_arena");
cancelQuestTimer("remove_barrier", npc, null);
_spawnedMainBoss = null;
GlobalVariablesManager.getInstance().set("COATL_ALIVE", false);
_invisibleCoatl.deleteMe();
_flameTotem.deleteMe();
_kashaTotem.deleteMe();
_earthTotem.deleteMe();
_waterTotem.deleteMe();
_invisibleCoatl = null;
_flameTotem = null;
_kashaTotem = null;
_earthTotem = null;
_waterTotem = null;
cancelQuestTimers("dispel_boss_buffs");
return super.onKill(npc, killer, isSummon);
}
public void onNpcDeath(Npc npc)
{
if (npc == _spawnedMainBoss)
{
cancelQuestTimers("check_arena");
cancelQuestTimer("remove_barrier", npc, null);
_spawnedMainBoss = null;
_involvedPlayers.clear();
_invisibleCoatl.setInvul(true);
}
}
public boolean isCoatlActive()
{
return _spawnedMainBoss != null;
}
public static void main(String[] args)
{
new Coatl();
}
}