L2JMobius
Public Development => Bug Reports => Topic started by: 8floorgm on January 20, 2026, 02:20:30 AM
-
source
L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java
currently l2j use skilltime as skill animation length time
======================
if (!skill.isToggle())
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
=====================
and skilltime = hittime + cooltime,
======================
// Get the Base Casting Time of the Skills.
int skillTime = (skill.getHitTime() + skill.getCoolTime());
======================
but there are problem here
=================================
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
=================================
this packet not send the full skill animation time but the skill animation from start to skill "hit" point.
for example: skill animation <Start-------------skilltime----------------Hit!> if hit point at the end of skill animation or close to the end of animation , it is fine.
( because of _skillCast2 = ThreadPool.schedule(mut, Math.max(0, skillTime - 400)); some time skill hit animation cut)
but some skill animation (expecially grandboss) : <<start<-----------skilltime------------------>Hit!---------------------end>>
the skill animtion after Hit will be cut. because this broadcastSkillPacket only show: <<start-----------skilltime------------------Hit!
the simply way to fix this problem is change skillTime to skill.getHitTime, you will find all boss animation show prefectly, (with correct cooltime)
==================================
if (!skill.isToggle())
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skill.getHitTime, reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
==================================
this change match the broadcastSkillPacket skill hittime animation. and skilltime still valid, the rest of cooltime will truely for skill cooldown, you will see the rest of animation continue,
>>start>---------hittime--->Hit!>-------cooltime--->end>> this is the right logic,
but this fix not fix the skill actual damage occurs after skilltime, wait for l2j engineer to fix it.
-
source
L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java
currently l2j use skilltime as skill animation length time
======================
if (!skill.isToggle())
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
=====================
and skilltime = hittime + cooltime,
======================
// Get the Base Casting Time of the Skills.
int skillTime = (skill.getHitTime() + skill.getCoolTime());
======================
but there are problem here
=================================
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
=================================
this packet not send the full skill animation time but the skill animation from start to skill "hit" point.
for example: skill animation <Start-------------skilltime----------------Hit!> if hit point at the end of skill animation or close to the end of animation , it is fine.
( because of _skillCast2 = ThreadPool.schedule(mut, Math.max(0, skillTime - 400)); some time skill hit animation cut)
but some skill animation (expecially grandboss) : <<start<-----------skilltime------------------>Hit!---------------------end>>
the skill animtion after Hit will be cut. because this broadcastSkillPacket only show: <<start-----------skilltime------------------Hit!
the simply way to fix this problem is change skillTime to skill.getHitTime, you will find all boss animation show prefectly, (with correct cooltime)
==================================
if (!skill.isToggle())
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skill.getHitTime, reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
==================================
this change match the broadcastSkillPacket skill hittime animation. and skilltime still valid, the rest of cooltime will truely for skill cooldown, you will see the rest of animation continue,
>>start>---------hittime--->Hit!>-------cooltime--->end>> this is the right logic,
but this fix not fix the skill actual damage occurs after skilltime, wait for l2j engineer to fix it.
Can we get a patch to test this?
-
Patch for anyone interested.
Index: java/org/l2jmobius/gameserver/model/actor/Creature.java
===================================================================
--- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 19475)
+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy)
@@ -1941,7 +1941,7 @@
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
- broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
+ broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skill.getHitTime(), reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
-
completed fix
--- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java
+++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java
@@ -671,7 +671,7 @@ public abstract class Creature extends WorldObject
final WorldObject target = ((intention == Intention.ATTACK) || (intention == Intention.FOLLOW)) ? _target : null;
if (target != null)
{
- if (target != this)
+ if ((target != this) && !isOnGeodataPath(move))
{
broadcastPacket(new MoveToPawn(this, target, getAI().getClientMovingToPawnOffset()));
}
@@ -1943,18 +1943,24 @@ public abstract class Creature extends WorldObject
// Get the Base Casting Time of the Skills.
int skillTime = (skill.getHitTime() + skill.getCoolTime());
+ int animationTime = skill.getHitTime(); // Use only hitTime for animation
+ int hitTime = skill.getHitTime(); // Calculate hitTime separately for damage timing
if (!skill.isChanneling() || (skill.getChannelingSkillId() == 0))
{
// Calculate the Casting Time of the "Non-Static" Skills (with caster PAtk/MAtkSpd).
if (!skill.isStatic())
{
skillTime = Formulas.calcAtkSpd(this, skill, skillTime);
+ animationTime = Formulas.calcAtkSpd(this, skill, animationTime);
+ hitTime = Formulas.calcAtkSpd(this, skill, hitTime);
}
// Calculate the Casting Time of Magic Skills (reduced in 40% if using SPS/BSPS)
if (skill.isMagic() && (isChargedShot(ShotType.SPIRITSHOTS) || isChargedShot(ShotType.BLESSED_SPIRITSHOTS)))
{
skillTime = (int) (0.6 * skillTime);
+ animationTime = (int) (0.6 * animationTime);
+ hitTime = (int) (0.6 * hitTime);
}
}
@@ -1970,6 +1976,9 @@ public abstract class Creature extends WorldObject
skillTime = 500;
}
+ // Calculate coolTime after all adjustments
+ int coolTime = skillTime - hitTime;
+
// queue herbs and potions
if (_isCastingSimultaneouslyNow && simultaneously)
{
@@ -2100,7 +2109,7 @@ public abstract class Creature extends WorldObject
{
// Send a Server->Client packet MagicSkillUser with target, displayId, level, skillTime, reuseDelay
// to the Creature AND to all Player in the _KnownPlayers of the Creature
- broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), skillTime, reuseDelay), target);
+ broadcastSkillPacket(new MagicSkillUse(this, target, skill.getDisplayId(), skill.getDisplayLevel(), animationTime, reuseDelay), target);
broadcastSkillPacket(new MagicSkillLaunched(this, skill.getDisplayId(), skill.getDisplayLevel(), targets), targets);
}
@@ -2140,6 +2149,7 @@ public abstract class Creature extends WorldObject
}
final MagicUseTask mut = new MagicUseTask(this, targets, skill, skillTime, simultaneously);
+ mut.setCoolTime(coolTime);
// launch the magic in skillTime milliseconds
if (skillTime > 0)
@@ -2164,9 +2174,9 @@ public abstract class Creature extends WorldObject
_skillCast2 = null;
}
- // Create a task MagicUseTask to launch the MagicSkill at the end of the casting time (skillTime)
+ // Create a task MagicUseTask to launch the MagicSkill at hitTime (for damage) instead of skillTime
// For client animation reasons (party buffs especially) 400 ms before!
- _skillCast2 = ThreadPool.schedule(mut, Math.max(0, skillTime - 400));
+ _skillCast2 = ThreadPool.schedule(mut, Math.max(0, hitTime - 400));
}
else
{
@@ -2177,9 +2187,9 @@ public abstract class Creature extends WorldObject
_skillCast = null;
}
- // Create a task MagicUseTask to launch the MagicSkill at the end of the casting time (skillTime)
+ // Create a task MagicUseTask to launch the MagicSkill at hitTime (for damage) instead of skillTime
// For client animation reasons (party buffs especially) 400 ms before!
- _skillCast = ThreadPool.schedule(mut, Math.max(0, skillTime - 400));
+ _skillCast = ThreadPool.schedule(mut, Math.max(0, hitTime - 400));
}
}
else
@@ -6061,13 +6071,34 @@ public abstract class Creature extends WorldObject
LOGGER.log(Level.WARNING, "", e);
}
+ // Clear casting state immediately after damage if creature is dead
+ // This allows death processing to proceed normally
+ if (isAlikeDead())
+ {
+ if (!mut.isSimultaneous())
+ {
+ _skillCast = null;
+ _castInterruptTime = 0;
+ setCastingNow(false);
+ }
+ else
+ {
+ _skillCast2 = null;
+ setCastingSimultaneouslyNow(false);
+ }
+ // Don't schedule finalizer if dead, death processing will handle cleanup
+ return;
+ }
+
if (mut.getSkillTime() > 0)
{
mut.setCount(mut.getCount() + 1);
}
mut.setPhase(3);
- if (mut.getSkillTime() == 0)
+ // Schedule finalizer at the end of skillTime (after coolTime from hitTime)
+ int finalizerDelay = mut.getCoolTime();
+ if (finalizerDelay <= 0)
{
onMagicFinalizer(mut);
}
@@ -6075,11 +6106,11 @@ public abstract class Creature extends WorldObject
{
if (mut.isSimultaneous())
{
- _skillCast2 = ThreadPool.schedule(mut, 0);
+ _skillCast2 = ThreadPool.schedule(mut, finalizerDelay);
}
else
{
- _skillCast = ThreadPool.schedule(mut, 0);
+ _skillCast = ThreadPool.schedule(mut, finalizerDelay);
}
}
}
@@ -6094,7 +6125,7 @@ public abstract class Creature extends WorldObject
return;
}
- // Cleanup
+ // Cleanup - ensure casting state is cleared even if creature is dead
_skillCast = null;
_castInterruptTime = 0;
-
this is compiled GameServer.jar base on high five version,
https://drive.google.com/file/d/1v9ydGWEeG8fgBmXrGLJcyaSIiqvO5_DD/view?usp=drive_link
place to libs/ folder
-
this is compiled GameServer.jar base on high five version,
https://drive.google.com/file/d/1v9ydGWEeG8fgBmXrGLJcyaSIiqvO5_DD/view?usp=drive_link
place to libs/ folder
When testing the hurricane skill, the visual effect behaves inconsistently. a duplicate of the effect that is supposed to travel toward and hit the target sometimes remains attached to the caster’s body instead of moving forward.
In some instances, the projectile effect that should reach the enemy is not rendered at all. only a spinning visual effect remains visible in front of the caster. The character’s casting speed is 1.653, which is below the expected threshold for abnormal visual behavior.