diff --git a/Server/data/configs/npc_configs.json b/Server/data/configs/npc_configs.json index 44c10bdcb..c3048531c 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -33695,6 +33695,7 @@ "attack_level": "255", "attack_speed": "4", "bonuses": "0,0,0,0,0,10,10,10,255,550,0,0,0,0,0", + "can_tolerate": "false", "clue_level": "2", "combat_style": "1", "death_animation": "2856", @@ -33703,7 +33704,7 @@ "lifepoints": "255", "magic_level": "255", "melee_animation": "2855", - "movement_radius": "8", + "movement_radius": "11", "projectile": "475", "range_level": "255", "respawn_delay": "60", @@ -33719,6 +33720,7 @@ "attack_level": "255", "attack_speed": "4", "bonuses": "0,0,0,0,0,255,255,255,255,10,0,0,0,0,0", + "can_tolerate": "false", "clue_level": "2", "combat_style": "2", "death_animation": "2856", @@ -33727,7 +33729,7 @@ "lifepoints": "255", "magic_level": "255", "melee_animation": "2854", - "movement_radius": "8", + "movement_radius": "11", "range_level": "1", "respawn_delay": "60", "safespot": null, @@ -33743,6 +33745,7 @@ "attack_level": "255", "attack_speed": "4", "bonuses": "0,0,0,0,0,255,255,255,10,255,0,0,0,0,0", + "can_tolerate": "false", "clue_level": "2", "death_animation": "2856", "defence_animation": "2852", @@ -33750,7 +33753,7 @@ "lifepoints": "255", "magic_level": "1", "melee_animation": "2853", - "movement_radius": "8", + "movement_radius": "11", "range_level": "255", "respawn_delay": "60", "safespot": null, @@ -33898,7 +33901,9 @@ }, { "id": "2893", - "name": "Suspicious water" + "name": "Suspicious water", + "movement_radius": "22", + "water_npc": "true" }, { "id": "2894", diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index 77ef33f02..ebc7f3f0c 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -157,7 +157,7 @@ }, { "npc_id": "47", - "loc_data": "{2821,3170,0,1,1}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3341,3267,0,1,5}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-" + "loc_data": "{2821,3170,0,1,1}-{3341,3267,0,1,5}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-" }, { "npc_id": "48", @@ -6417,15 +6417,15 @@ }, { "npc_id": "2881", - "loc_data": "{2907,4440,0,1,6}-" + "loc_data": "{2905,4449,0,1,4}-" }, { "npc_id": "2882", - "loc_data": "{2910,4457,0,1,6}-" + "loc_data": "{2911,4452,0,1,6}-" }, { "npc_id": "2883", - "loc_data": "{2923,4443,0,1,4}-" + "loc_data": "{2914,4446,0,1,1}-" }, { "npc_id": "2890", @@ -6433,7 +6433,7 @@ }, { "npc_id": "2894", - "loc_data": "{2908,4435,0,0,1}-{2924,4436,0,0,5}-{2924,4460,0,0,6}-{2908,4463,0,0,3}-{2901,4458,0,0,3}-{2928,4441,0,0,3}-{2929,4449,0,0,6}-{2902,4437,0,0,1}-" + "loc_data": "{2899,4454,0,0,4}-{2899,4442,0,0,4}-{2901,4438,0,0,4}-{2901,4458,0,0,7}-{2904,4462,0,0,6}-{2908,4463,0,0,6}-{2905,4436,0,0,1}-{2909,4435,0,0,1}-{2920,4435,0,0,1}-{2924,4436,0,0,0}-{2927,4439,0,0,3}-{2929,4444,0,0,3}-{2929,4450,0,0,3}-{2927,4454,0,0,3}-{2924,4460,0,0,5}-{2919,4462,0,0,6}-" }, { "npc_id": "2910", diff --git a/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java index 65f5d4e43..6f61a5882 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/DagannothKingNPC.java @@ -45,6 +45,11 @@ public final class DagannothKingNPC extends AbstractNPC { super.init(); } + @Override + public boolean shouldPreventStacking(Entity mover) { + return mover instanceof DagannothKingNPC; + } + @Override public void checkImpact(BattleState state) { CombatStyle style = state.getStyle(); diff --git a/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java index e887f5263..e41e584aa 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java @@ -1,5 +1,6 @@ package content.region.fremennik.waterbirth.handlers; +import core.game.interaction.QueueStrength; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.spell.CombatSpell; @@ -9,9 +10,12 @@ import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.map.Location; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.MultiSwingHandler; +import org.rs09.consts.NPCs; import static core.api.ContentAPIKt.*; @@ -26,6 +30,21 @@ public final class SpinolypNPC extends AbstractNPC { */ private static final CombatSwingHandler SWING_HANDLER = new SpinolpySwingHandler(); + /** Spinolyp NPC transformation. */ + private static final int COMBAT_SPINOLYP = NPCs.SPINOLYP_2894; + private static final int SUBMERGED_SPINOLYP = NPCs.SUSPICIOUS_WATER_2893; + private static final int SUBMERGE_ANIMATION = 2865; + private static final int RESURFACE_ANIMATION = 2864; + private static final int WATER_SPLASH_GFX = 68; + private static final int COMBAT_DURATION = 50; + private static final int COMBAT_DURATION_RANDOM = 8; + private static final int INITIAL_RESURFACE_DELAY = 8; + private static final int INITIAL_RESURFACE_DELAY_RANDOM = 34; + private static final int SUBMERGED_DURATION = 40; + private static final int SUBMERGED_DURATION_RANDOM = 5; + private static final int SUBMERGE_ANIMATION_DELAY = 2; + private static final int ATTACK_DISTANCE = 12; + /** * Constructs a new {@Code SpinolypNPC} {@Code Object} */ @@ -60,6 +79,8 @@ public final class SpinolypNPC extends AbstractNPC { super.getLocks().lockMovement(Integer.MAX_VALUE); setSpell(); getAggressiveHandler().setAllowTolerance(false); + startSubmerged(); + startSubmergeScript(); } /** @@ -71,6 +92,86 @@ public final class SpinolypNPC extends AbstractNPC { getProperties().setAutocastSpell(spell); } + private int getCombatDuration() { + return COMBAT_DURATION + RandomFunction.random(-COMBAT_DURATION_RANDOM, COMBAT_DURATION_RANDOM + 1); + } + + private int getSubmergedDuration() { + return SUBMERGED_DURATION + RandomFunction.random(-SUBMERGED_DURATION_RANDOM, SUBMERGED_DURATION_RANDOM +1); + } + + private int getInitialResurfaceDelay() { + return INITIAL_RESURFACE_DELAY + RandomFunction.random(0, INITIAL_RESURFACE_DELAY_RANDOM +1); + } + + /** Starts Spinolyps in submerged state before the transformation pulse takes over. + * Prevents players getting bombed if they join the server on restart. + */ + private void startSubmerged() { + getProperties().getCombatPulse().stop(); + getWalkingQueue().reset(); + transformWithHpCarryover(SUBMERGED_SPINOLYP); + setAggressive(false); + setAggressiveHandler(null); + setNeverWalks(false); + setWalks(true); + getLocks().unlockMovement(); + resetWalk(); + } + + private void submerge() { + getProperties().getCombatPulse().stop(); + getWalkingQueue().reset(); + setAggressive(false); + setAggressiveHandler(null); + setWalks(false); + setNeverWalks(true); + getLocks().lockMovement(SUBMERGE_ANIMATION_DELAY); + animate(Animation.create(SUBMERGE_ANIMATION)); + } + + private void resurface() { + getProperties().getCombatPulse().stop(); + getWalkingQueue().reset(); + transformWithHpCarryover(COMBAT_SPINOLYP); + getWalkingQueue().reset(); + setWalks(false); + setNeverWalks(true); + getLocks().lockMovement(Integer.MAX_VALUE); + setAggressive(true); + setDefaultBehavior(); + if (getAggressiveHandler() != null) { + getAggressiveHandler().setAllowTolerance(false); + } + setSpell(); + visualize(Animation.create(RESURFACE_ANIMATION), Graphics.create(WATER_SPLASH_GFX)); + } + + private void startSubmergeScript() { + queueScript(this, getInitialResurfaceDelay(), QueueStrength.SOFT, true, (Integer stage) -> { + if (stage == 0) { + resurface(); + return delayScript(this, getCombatDuration()); + } + + if (stage == 1) { + submerge(); + return delayScript(this, SUBMERGE_ANIMATION_DELAY); + } + + if (stage == 2) { + transformWithHpCarryover(SUBMERGED_SPINOLYP); + setNeverWalks(false); + setWalks(true); + getLocks().unlockMovement(); + resetWalk(); + setCurrentScriptState(this, 0); + return delayScript(this, getSubmergedDuration()); + } + return stopExecuting(this); + }); + } + @Override public void sendImpact(BattleState state) { if (state.getEstimatedHit() == 0 && RandomFunction.random(15) < 2) { @@ -83,7 +184,7 @@ public final class SpinolypNPC extends AbstractNPC { @Override public int[] getIds() { - return new int[] { 2894, 2896 }; + return new int[] { COMBAT_SPINOLYP }; } /** @@ -102,11 +203,17 @@ public final class SpinolypNPC extends AbstractNPC { @Override public InteractionType canSwing(Entity entity, Entity victim) { - InteractionType type = super.canSwing(entity, victim); - if (type == InteractionType.MOVE_INTERACT) { - type = InteractionType.NO_INTERACT; + if (!CombatSwingHandler.isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT; } - return type; + + if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, ATTACK_DISTANCE)) && + isAttackable(entity, victim) != InteractionType.NO_INTERACT) { + entity.getWalkingQueue().reset(); + return InteractionType.STILL_INTERACT; + } + + return InteractionType.NO_INTERACT; } @Override @@ -120,7 +227,7 @@ public final class SpinolypNPC extends AbstractNPC { @Override public int getCombatDistance(Entity e, Entity v, int distance) { - return 12; + return ATTACK_DISTANCE; } @Override