Corporeal beast authenticity improvements

Dark core will no longer jump to players in safe area
Adjusted maximum attack hits
Protect from magic now blocks the big dart attack by 40%
Now resummons the dark core when it dies
Made the dark core respawn mechanics more authentic
Dark core now drops ashes
This commit is contained in:
Player Name 2024-11-14 11:36:09 +00:00 committed by Ryan
parent b0be48501b
commit 53dc169774
4 changed files with 106 additions and 75 deletions

View file

@ -58878,5 +58878,19 @@
"maxAmount": "1" "maxAmount": "1"
} }
] ]
},
{
"default": [
{
"minAmount": "1",
"weight": "1.0",
"id": "592",
"maxAmount": "1"
}
],
"charm": [],
"ids": "8127",
"description": "Dark energy core",
"main": []
} }
] ]

View file

@ -2,15 +2,11 @@ package content.region.wilderness.handlers;
import content.data.BossKillCounter; import content.data.BossKillCounter;
import core.game.node.entity.Entity; import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.*;
import core.game.node.entity.combat.CombatStyle;
import core.game.node.entity.combat.CombatSwingHandler;
import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.combat.ImpactHandler.HitsplatType;
import core.game.node.entity.combat.MultiSwingHandler;
import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.combat.equipment.SwitchAttack;
import core.game.node.entity.combat.equipment.Weapon; import core.game.node.entity.combat.equipment.Weapon;
import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Projectile;
import core.game.node.entity.npc.AbstractNPC;
import core.game.node.entity.npc.NPC; import core.game.node.entity.npc.NPC;
import core.game.node.entity.npc.NPCBehavior; import core.game.node.entity.npc.NPCBehavior;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
@ -22,7 +18,6 @@ import core.game.world.map.RegionManager;
import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics; import core.game.world.update.flag.context.Graphics;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin;
import core.tools.RandomFunction; import core.tools.RandomFunction;
import org.rs09.consts.NPCs; import org.rs09.consts.NPCs;
@ -32,7 +27,6 @@ import java.util.List;
/** /**
* Handles the Corporeal beast NPC. * Handles the Corporeal beast NPC.
* @author Emperor * @author Emperor
*
*/ */
@Initializable @Initializable
public final class CorporealBeastNPC extends NPCBehavior { public final class CorporealBeastNPC extends NPCBehavior {
@ -41,19 +35,24 @@ public final class CorporealBeastNPC extends NPCBehavior {
* The combat handler. * The combat handler.
*/ */
private final MultiSwingHandler combatHandler = new CombatHandler(); private final MultiSwingHandler combatHandler = new CombatHandler();
/** /**
* The dark energy core NPC. * The dark energy core NPC.
*/ */
public NPC darkEnergyCore; public NPC darkEnergyCore;
/**
* Whether to force a dark core spawn roll on our next swing (only done if we just got hit >= 32 damage).
*/
public boolean forceCoreRoll = false;
/** /**
* Constructs a new {@code CorporealBeastNPC} {@code Object}. * Constructs a new {@code CorporealBeastNPC} {@code Object}.
*/ */
public CorporealBeastNPC() { public CorporealBeastNPC() {
super(new int[]{NPCs.CORPOREAL_BEAST_8133}); super(new int[] { NPCs.CORPOREAL_BEAST_8133 });
} }
@Override @Override
public void onCreation(NPC self) { public void onCreation(NPC self) {
self.configureBossData(); self.configureBossData();
@ -64,27 +63,31 @@ public final class CorporealBeastNPC extends NPCBehavior {
return combatHandler; return combatHandler;
} }
@Override @Override
public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) { public void beforeDamageReceived(NPC self, Entity attacker, BattleState state) {
if(state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) { if (state.getStyle() == CombatStyle.MELEE || state.getStyle() == CombatStyle.RANGE) {
Weapon w = state.getWeapon(); Weapon w = state.getWeapon();
String name = w != null ? w.getName() : ""; String name = w != null ? w.getName() : "";
if(w == null || name.toLowerCase().indexOf("spear") == -1) { if (w == null || name.toLowerCase().indexOf("spear") == -1) {
if(state.getEstimatedHit() > 0) { if (state.getEstimatedHit() > 0) {
state.setEstimatedHit(state.getEstimatedHit()/2); state.setEstimatedHit(state.getEstimatedHit() / 2);
} }
if(state.getSecondaryHit() > 0) { if (state.getSecondaryHit() > 0) {
state.setSecondaryHit(state.getSecondaryHit()/2); state.setSecondaryHit(state.getSecondaryHit() / 2);
} }
} }
} }
if(state.getEstimatedHit() > 100) { if (state.getEstimatedHit() >= 32) {
state.setEstimatedHit(100); CorporealBeastNPC corp = (CorporealBeastNPC) self.behavior;
} corp.forceCoreRoll = true;
if(state.getSecondaryHit() > 100) { }
state.setSecondaryHit(100); if (state.getEstimatedHit() > 100) {
} state.setEstimatedHit(100);
} }
if (state.getSecondaryHit() > 100) {
state.setSecondaryHit(100);
}
}
@Override @Override
public void onDeathFinished(NPC self, Entity killer) { public void onDeathFinished(NPC self, Entity killer) {
@ -94,41 +97,46 @@ public final class CorporealBeastNPC extends NPCBehavior {
darkEnergyCore = null; darkEnergyCore = null;
} }
} }
/** /**
* Handles the Corporeal beast's combat. * Handles the Corporeal beast's combat.
* @author Emperor * @author Emperor
*
*/ */
static class CombatHandler extends MultiSwingHandler { static class CombatHandler extends MultiSwingHandler {
/** /**
* Constructs a new {@code CombatHandler} {@code Object}. * Constructs a new {@code CombatHandler} {@code Object}.
*/ */
public CombatHandler() { public CombatHandler() {
super( super(
//Melee (crush) //Melee (crush)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(52), new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10057)).setMaximumHit(51),
//Melee (slash) //Melee (slash)
new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51), new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), Animation.create(10058)).setMaximumHit(51),
//Magic (drain skill) //Magic (drain skill, blocked by prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1823, 60, 36, 41, 46)).setMaximumHit(55),
//Magic (location based) //Magic (location-based, hits through prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42), new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1824, 60, 36, 41, 46)).setMaximumHit(42),
//Magic (hit through prayer) //Magic (hits through prayer)
new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(66) new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), Animation.create(10410), null, null, Projectile.create(null, null, 1825, 60, 36, 41, 46)).setMaximumHit(65)
); );
} }
@Override @Override
public int swing(Entity entity, Entity victim, BattleState state) { public int swing(Entity entity, Entity victim, BattleState state) {
spawnDarkCore(entity, (CorporealBeastNPC)((NPC) entity).behavior, victim); // If we're below the right HP threshold, roll a chance to spawn the dark core
CorporealBeastNPC corp = (CorporealBeastNPC) ((NPC) entity).behavior;
double thresh = entity.getSkills().getMaximumLifepoints() * (0.3 + (entity.getViewport().getCurrentPlane().getPlayers().size() * 0.05));
if (corp.forceCoreRoll || entity.getSkills().getLifepoints() < thresh) {
rollDarkCore(entity, corp, victim);
corp.forceCoreRoll = false;
}
// If we can stomp, do that for our turn
if (doStompAttack(entity)) { if (doStompAttack(entity)) {
entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed()); entity.getProperties().getCombatPulse().setNextAttack(entity.getProperties().getAttackSpeed());
return -1; return -1;
} }
//Location based attack. // Location-based attack.
if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) { if (super.getNext().getProjectile() != null && super.getNext().getProjectile().getProjectileId() == 1824) {
setCurrent(getNext()); setCurrent(getNext());
CombatStyle style = getCurrent().getStyle(); CombatStyle style = getCurrent().getStyle();
@ -142,18 +150,17 @@ public final class CorporealBeastNPC extends NPCBehavior {
} }
return super.swing(entity, victim, state); return super.swing(entity, victim, state);
} }
/** /**
* Spawns a dark core. * Rolls a 1/8 chance to spawn a dark core.
* @param npc The corporeal beast NPC. * @param npc The corporeal beast NPC.
* @param victim The victim. * @param victim The victim.
*/ */
private void spawnDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) { private void rollDarkCore(Entity corp, final CorporealBeastNPC npc, Entity victim) {
if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive()) { if (npc.darkEnergyCore != null && npc.darkEnergyCore.isActive() && !DeathTask.isDead(npc.darkEnergyCore)) {
return; return;
} }
double max = corp.getSkills().getMaximumLifepoints() * (0.3 + (corp.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); if (!RandomFunction.roll(8)) {
if (corp.getSkills().getLifepoints() > max) {
return; return;
} }
Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3); Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3);
@ -163,8 +170,8 @@ public final class CorporealBeastNPC extends NPCBehavior {
GameWorld.getPulser().submit(new Pulse(2, corp) { GameWorld.getPulser().submit(new Pulse(2, corp) {
@Override @Override
public boolean pulse() { public boolean pulse() {
if (npc.darkEnergyCore == null) if (npc.darkEnergyCore == null)
return true; return true;
npc.darkEnergyCore.init(); npc.darkEnergyCore.init();
return true; return true;
} }
@ -184,6 +191,7 @@ public final class CorporealBeastNPC extends NPCBehavior {
boolean secondStage = false; boolean secondStage = false;
List<Player> players = RegionManager.getLocalPlayers(entity); List<Player> players = RegionManager.getLocalPlayers(entity);
Location[] locations = null; Location[] locations = null;
@Override @Override
public boolean pulse() { public boolean pulse() {
if (!secondStage) { if (!secondStage) {
@ -215,11 +223,14 @@ public final class CorporealBeastNPC extends NPCBehavior {
locations = null; locations = null;
return true; return true;
} }
private void hit(Player p) { private void hit(Player p) {
int max = p.hasProtectionPrayer(CombatStyle.MAGIC) ? 13 : 42;
int hit = 0; int hit = 0;
if (isAccurateImpact(entity, p)) { if (isAccurateImpact(entity, p)) {
hit = RandomFunction.random(max); hit = RandomFunction.random(42);
if (p.hasProtectionPrayer(CombatStyle.MAGIC)) {
hit = (int) (hit * 0.6);
}
} }
p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC); p.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC);
} }
@ -252,7 +263,7 @@ public final class CorporealBeastNPC extends NPCBehavior {
} }
return false; return false;
} }
@Override @Override
public void adjustBattleState(Entity entity, Entity victim, BattleState state) { public void adjustBattleState(Entity entity, Entity victim, BattleState state) {
super.adjustBattleState(entity, victim, state); super.adjustBattleState(entity, victim, state);
@ -262,10 +273,9 @@ public final class CorporealBeastNPC extends NPCBehavior {
int skill = random == 0 ? Skills.PRAYER : random == 1 ? Skills.MAGIC : Skills.SUMMONING; int skill = random == 0 ? Skills.PRAYER : random == 1 ? Skills.MAGIC : Skills.SUMMONING;
int drain = 1 + RandomFunction.random(6); int drain = 1 + RandomFunction.random(6);
if ((skill == Skills.PRAYER ? victim.getSkills().getPrayerPoints() : victim.getSkills().getLevel(skill)) < 1) { if ((skill == Skills.PRAYER ? victim.getSkills().getPrayerPoints() : victim.getSkills().getLevel(skill)) < 1) {
victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL,2); victim.getImpactHandler().manualHit(entity, drain, HitsplatType.NORMAL, 2);
((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!"); ((Player) victim).getPacketDispatch().sendMessage("Your Hitpoints have been slightly drained!");
} } else {
else {
if (skill == Skills.PRAYER) { if (skill == Skills.PRAYER) {
victim.getSkills().decrementPrayerPoints(drain); victim.getSkills().decrementPrayerPoints(drain);
} else { } else {
@ -278,11 +288,13 @@ public final class CorporealBeastNPC extends NPCBehavior {
} }
} }
} }
@Override @Override
protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) {
if (getCurrent().getProjectile() == null || getCurrent().getProjectile().getProjectileId() != 1825) { if (getCurrent().getProjectile() == null || getCurrent().getProjectile().getProjectileId() != 1825) {
hit = (int) entity.getFormattedHit(state, hit); hit = (int) entity.getFormattedHit(state, hit);
} else if (victim.hasProtectionPrayer(CombatStyle.MAGIC)) {
hit = (int) (hit * 0.6);
} }
return formatHit(victim, hit); return formatHit(victim, hit);
} }

View file

@ -9,6 +9,8 @@ import core.game.node.entity.player.Player;
import core.game.system.task.Pulse; import core.game.system.task.Pulse;
import core.game.world.GameWorld; import core.game.world.GameWorld;
import core.game.world.map.Location; import core.game.world.map.Location;
import core.game.world.map.path.Path;
import core.game.world.map.path.Pathfinder;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.tools.RandomFunction; import core.tools.RandomFunction;
@ -16,8 +18,7 @@ import static core.api.ContentAPIKt.*;
/** /**
* Handles the Dark Energy Core NPC. * Handles the Dark Energy Core NPC.
* @author Emperor * @author Emperor, Player Name
*
*/ */
@Initializable @Initializable
public final class DarkEnergyCoreNPC extends AbstractNPC { public final class DarkEnergyCoreNPC extends AbstractNPC {
@ -31,21 +32,22 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
* The amount of ticks. * The amount of ticks.
*/ */
private int ticks = 0; private int ticks = 0;
/** /**
* The amount of failed attacks. * The amount of failed attacks.
*/ */
private int fails = 0; private int fails = 0;
/** /**
* Constructs a new {@code DarkEnergyCoreNPC} {@code Object}. * Constructs a new {@code DarkEnergyCoreNPC} {@code Object}.
*/ */
public DarkEnergyCoreNPC() { public DarkEnergyCoreNPC() {
this(8127, null); this(8127, null);
} }
/** /**
* Constructs a new {@code DarkEnergyCoreNPC} {@code Object}. * Constructs a new {@code DarkEnergyCoreNPC} {@code Object}.
*
* @param id The NPC id. * @param id The NPC id.
* @param location The location. * @param location The location.
*/ */
@ -59,14 +61,15 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
if (objects.length > 0) { if (objects.length > 0) {
core.master = (NPC) objects[0]; core.master = (NPC) objects[0];
} }
core.setRespawn(false);
return core; return core;
} }
@Override @Override
public boolean canStartCombat(Entity victim) { public boolean canStartCombat(Entity victim) {
return false; //No combat needed. return false; //No combat needed.
} }
@Override @Override
public void handleTickActions() { public void handleTickActions() {
ticks++; ticks++;
@ -91,8 +94,11 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
if (jump) { if (jump) {
Entity victim = master.getProperties().getCombatPulse().getVictim(); Entity victim = master.getProperties().getCombatPulse().getVictim();
if (++fails >= 3 && victim != null && victim.getViewport().getCurrentPlane() == getViewport().getCurrentPlane()) { if (++fails >= 3 && victim != null && victim.getViewport().getCurrentPlane() == getViewport().getCurrentPlane()) {
jump(victim.getLocation()); Path path = Pathfinder.find(getLocation(), victim.getLocation(), 1);
fails = 0; if (path.isSuccessful() || !path.isMoveNear()) {
jump(victim.getLocation());
fails = 0;
}
} }
} else { } else {
fails = 0; fails = 0;
@ -119,7 +125,6 @@ public final class DarkEnergyCoreNPC extends AbstractNPC {
@Override @Override
public int[] getIds() { public int[] getIds() {
return new int[] { 8127 }; return new int[]{8127};
} }
} }

View file

@ -8,7 +8,7 @@ import core.tools.RandomFunction
/** /**
* Handles combat swings with switching combat styles. * Handles combat swings with switching combat styles.
* @author Emperor * @author Emperor
* @author Ceirky, Kotlin conversion * @author Ceikry, Kotlin conversion
*/ */
open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttack) : CombatSwingHandler(CombatStyle.RANGE) { open class MultiSwingHandler(meleeDistance: Boolean, vararg attacks: SwitchAttack) : CombatSwingHandler(CombatStyle.RANGE) {
/** /**