mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-09 16:45:44 -07:00
Deep wilderness threats now continue at next login if present when logging out
Decreased brawler gloves and PvP gear drop rate from ordinary deep wilderness NPCs (does not apply to revenants or chaos elemental) Brawler gloves and PvP gear drops from ordinary deep wilderness NPCs now require at least 100k high alchemy risk (does not apply to revenants or chaos elemental)
This commit is contained in:
parent
4276ed731d
commit
a0435fb890
8 changed files with 159 additions and 21 deletions
|
|
@ -14,6 +14,7 @@ import core.game.node.item.Item
|
|||
import core.game.system.command.Privilege
|
||||
import core.game.system.timer.PersistTimer
|
||||
import core.game.system.timer.impl.Disease
|
||||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.map.zone.impl.WildernessZone
|
||||
import core.game.world.update.flag.context.Graphics
|
||||
import core.tools.RandomFunction
|
||||
|
|
@ -45,6 +46,7 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
|
|||
var ticksLeft = 0
|
||||
var lastMessage = 0
|
||||
var currentRev: NPC? = null
|
||||
var forceSpawn = false
|
||||
|
||||
override fun run(entity: Entity): Boolean {
|
||||
if (ticksLeft-- <= 0) return false
|
||||
|
|
@ -61,7 +63,8 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
|
|||
else if (ticksLeft >= 500) 1500
|
||||
else 2_000_000
|
||||
|
||||
if ((currentRev == null || DeathTask.isDead(currentRev) || !currentRev!!.isActive) && RandomFunction.roll(rollchance)) {
|
||||
if ((currentRev == null || DeathTask.isDead(currentRev) || !currentRev!!.isActive) && (forceSpawn || RandomFunction.roll(rollchance))) {
|
||||
forceSpawn = false
|
||||
val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel)
|
||||
val npc = NPC.create(type.ids.random(), entity.location)
|
||||
npc.isRespawn = false
|
||||
|
|
@ -80,10 +83,12 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
|
|||
|
||||
override fun save(root: JSONObject, entity: Entity) {
|
||||
root["threat-time-remaining"] = ticksLeft.toString()
|
||||
root["threat-forceSpawn"] = (currentRev != null).toString()
|
||||
}
|
||||
|
||||
override fun parse(root: JSONObject, entity: Entity) {
|
||||
ticksLeft = root["threat-time-remaining"]?.toString()?.toIntOrNull() ?: 0
|
||||
forceSpawn = root["threat-forceSpawn"]?.toString()?.toBoolean() ?: false
|
||||
}
|
||||
|
||||
override fun defineCommands() {
|
||||
|
|
@ -140,4 +145,8 @@ class RevGuardianBehavior : NPCBehavior() {
|
|||
val timer = getOrStartTimer<DWThreatTimer>(target)
|
||||
timer.ticksLeft = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPathfinderOverride(self: NPC): Pathfinder? {
|
||||
return Pathfinder.SMART
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import core.game.node.Node;
|
|||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.impl.WalkingQueue;
|
||||
import core.game.node.entity.npc.NPC;
|
||||
import core.game.node.entity.npc.NPCBehavior;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.system.task.Pulse;
|
||||
import core.game.world.map.Direction;
|
||||
|
|
@ -175,6 +176,11 @@ public abstract class MovementPulse extends Pulse {
|
|||
if (pathfinder == null) {
|
||||
if (mover instanceof Player) {
|
||||
this.pathfinder = Pathfinder.SMART;
|
||||
} else if (mover instanceof NPC) {
|
||||
NPC npc = (NPC)mover;
|
||||
NPCBehavior behavior = npc.behavior;
|
||||
Pathfinder pf = behavior != null ? behavior.getPathfinderOverride(npc) : null;
|
||||
this.pathfinder = pf != null ? pf : Pathfinder.DUMB;
|
||||
} else {
|
||||
this.pathfinder = Pathfinder.DUMB;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public final class Properties {
|
|||
/**
|
||||
* The entity's combat pulse.
|
||||
*/
|
||||
private final CombatPulse combatPulse;
|
||||
private CombatPulse combatPulse;
|
||||
|
||||
/**
|
||||
* If the entity is retaliating.
|
||||
|
|
@ -332,6 +332,14 @@ public final class Properties {
|
|||
return combatPulse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the combatPulse.
|
||||
* @return The void.
|
||||
*/
|
||||
public void setCombatPulse(CombatPulse combatPulse) {
|
||||
this.combatPulse = (CombatPulse)combatPulse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the retaliating.
|
||||
* @return The retaliating.
|
||||
|
|
@ -571,4 +579,4 @@ public final class Properties {
|
|||
this.protectStyle = protectStyle;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import core.game.interaction.MovementPulse;
|
|||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.BattleState;
|
||||
import core.game.node.entity.combat.spell.CombatSpell;
|
||||
import core.game.node.entity.combat.CombatPulse;
|
||||
import core.game.node.entity.combat.CombatStyle;
|
||||
import core.game.node.entity.combat.spell.DefaultCombatSpell;
|
||||
import core.game.node.entity.combat.equipment.WeaponInterface;
|
||||
|
|
@ -187,6 +188,7 @@ public class NPC extends Entity {
|
|||
*/
|
||||
public static NPC create(int id, Location location, Direction direction, Object... objects) {
|
||||
NPC n = AbstractNPC.forId(id);
|
||||
|
||||
if (n != null) {
|
||||
n = ((AbstractNPC) n).construct(id, location, objects);
|
||||
}
|
||||
|
|
@ -229,6 +231,8 @@ public class NPC extends Entity {
|
|||
}
|
||||
}
|
||||
behavior.onCreation(this);
|
||||
// FIXME: hack around MovementPulse's constructor getting run while behavior is null when behavior is set between NPC constructor and init.
|
||||
getProperties().setCombatPulse(new CombatPulse(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import core.game.node.entity.combat.BattleState
|
|||
import core.game.node.entity.combat.CombatStyle
|
||||
import core.game.node.entity.combat.CombatSwingHandler
|
||||
import core.game.world.map.path.ClipMaskSupplier
|
||||
import core.game.world.map.path.Pathfinder
|
||||
|
||||
open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
|
||||
companion object {
|
||||
|
|
@ -134,6 +135,13 @@ open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
|
|||
*/
|
||||
open fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler) : CombatSwingHandler {return original}
|
||||
|
||||
/**
|
||||
* Called by MovementPulse to determine if a non-default Pathfinder should be used (e.g. whether this npc should intelligently walk around obstacles)
|
||||
*/
|
||||
open fun getPathfinderOverride(self: NPC): Pathfinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by pathfinding code to determine the clipping mask supplier this NPC should use. You can use this to ignore water, etc.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package core.game.node.entity.player.link;
|
||||
|
||||
import core.game.container.Container;
|
||||
import core.game.container.ContainerEvent;
|
||||
import core.game.container.ContainerListener;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.item.Item;
|
||||
import core.ServerConstants;
|
||||
import static core.api.ContentAPIKt.*;
|
||||
|
||||
|
|
@ -17,6 +21,35 @@ import static core.game.world.map.zone.impl.WildernessZone.WILDERNESS_PROT_ATTR;
|
|||
* @author Emperor
|
||||
*/
|
||||
public final class SkullManager {
|
||||
public enum SkullIcon {
|
||||
NONE(-1),
|
||||
WHITE(0),
|
||||
RED(1),
|
||||
BH_RED5(2),
|
||||
BH_BLUE4(3),
|
||||
BH_GREEN3(4),
|
||||
BH_GREY2(5),
|
||||
BH_BROWN1(6),
|
||||
SCREAM(7);
|
||||
public final int id;
|
||||
SkullIcon(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
public static SkullIcon forId(int id) {
|
||||
switch(id) {
|
||||
case 0: return SkullIcon.WHITE;
|
||||
case 1: return SkullIcon.RED;
|
||||
case 2: return SkullIcon.BH_RED5;
|
||||
case 3: return SkullIcon.BH_BLUE4;
|
||||
case 4: return SkullIcon.BH_GREEN3;
|
||||
case 5: return SkullIcon.BH_GREY2;
|
||||
case 6: return SkullIcon.BH_BROWN1;
|
||||
case 7: return SkullIcon.SCREAM;
|
||||
default: return SkullIcon.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The player instance.
|
||||
|
|
@ -198,15 +231,74 @@ public final class SkullManager {
|
|||
return skulled || deepWilderness;
|
||||
}
|
||||
|
||||
public boolean isDeepWilderness() {
|
||||
return deepWilderness;
|
||||
}
|
||||
public boolean isDeepWilderness() {
|
||||
return deepWilderness;
|
||||
}
|
||||
|
||||
public void setDeepWilderness (boolean deepWildy) {
|
||||
setSkullIcon(deepWildy ? 1 : skulled ? 0 : -1);
|
||||
setSkullCheckDisabled(deepWildy);
|
||||
deepWilderness = deepWildy;
|
||||
public void setDeepWilderness (boolean deepWildy) {
|
||||
if(deepWildy) {
|
||||
updateDWSkullIcon();
|
||||
} else {
|
||||
removeDWSkullIcon();
|
||||
}
|
||||
setSkullCheckDisabled(deepWildy);
|
||||
deepWilderness = deepWildy;
|
||||
}
|
||||
|
||||
public static final long DEEP_WILD_DROP_RISK_THRESHOLD = 100000;
|
||||
public void updateDWSkullIcon() {
|
||||
if (player.getAttribute("deepwild-value-listener") == null) {
|
||||
ContainerListener listener = new ContainerListener() {
|
||||
@Override
|
||||
public void update(Container c, ContainerEvent event) {
|
||||
refresh(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Container c) {
|
||||
updateDWSkullIcon();
|
||||
}
|
||||
};
|
||||
player.setAttribute("deepwild-value-listener", listener);
|
||||
player.getInventory().getListeners().add(listener);
|
||||
player.getEquipment().getListeners().add(listener);
|
||||
}
|
||||
long value = 0;
|
||||
long maxValue = 0;
|
||||
for (Item item : player.getInventory().toArray()) {
|
||||
if (item != null) {
|
||||
long alchValue = item.getAlchemyValue();
|
||||
value += alchValue;
|
||||
maxValue = Math.max(maxValue, alchValue);
|
||||
}
|
||||
}
|
||||
for (Item item : player.getEquipment().toArray()) {
|
||||
if (item != null) {
|
||||
long alchValue = item.getAlchemyValue();
|
||||
value += alchValue;
|
||||
maxValue = Math.max(maxValue, alchValue);
|
||||
}
|
||||
}
|
||||
// Act as if protect item is always active when calculating risk
|
||||
value -= maxValue;
|
||||
player.setAttribute("deepwild-value-risk", value);
|
||||
SkullIcon skull = SkullIcon.BH_BROWN1;
|
||||
if (value >= DEEP_WILD_DROP_RISK_THRESHOLD) {
|
||||
skull = SkullIcon.RED;
|
||||
}
|
||||
setSkullIcon(skull.id);
|
||||
}
|
||||
|
||||
public void removeDWSkullIcon() {
|
||||
setSkullIcon(skulled ? 0 : -1);
|
||||
ContainerListener listener = player.getAttribute("deepwild-value-listener");
|
||||
if (listener != null) {
|
||||
player.getInventory().getListeners().remove(listener);
|
||||
player.getEquipment().getListeners().remove(listener);
|
||||
}
|
||||
player.removeAttribute("deepwild-value-listener");
|
||||
player.removeAttribute("deepwild-value-risk");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the skulled.
|
||||
|
|
|
|||
|
|
@ -106,6 +106,14 @@ public class Item extends Node{
|
|||
return value * getAmount();
|
||||
}
|
||||
|
||||
public long getAlchemyValue() {
|
||||
long value = 1;
|
||||
if (definition.getAlchemyValue(true) > value) {
|
||||
value = definition.getAlchemyValue(true);
|
||||
}
|
||||
return value * getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of the item.
|
||||
* @return The item copy.
|
||||
|
|
@ -245,4 +253,4 @@ public class Item extends Node{
|
|||
return "Item id=" + getId() + ", name=" + getName() + ", amount=" + amount;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package core.game.world.map.zone.impl;
|
||||
|
||||
import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves;
|
||||
import content.global.skill.summoning.familiar.Familiar;
|
||||
import content.region.wilderness.handlers.DeepWildyThreat;
|
||||
import core.game.component.Component;
|
||||
import core.game.interaction.Option;
|
||||
import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves;
|
||||
import core.game.node.Node;
|
||||
import core.game.node.entity.Entity;
|
||||
import core.game.node.entity.combat.CombatStyle;
|
||||
|
|
@ -12,17 +13,17 @@ import core.game.node.entity.npc.agg.AggressiveBehavior;
|
|||
import core.game.node.entity.npc.agg.AggressiveHandler;
|
||||
import core.game.node.entity.player.Player;
|
||||
import core.game.node.entity.player.info.Rights;
|
||||
import content.global.skill.summoning.familiar.Familiar;
|
||||
import core.game.node.entity.player.link.SkullManager;
|
||||
import core.game.node.item.GroundItemManager;
|
||||
import core.game.node.item.Item;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.map.Location;
|
||||
import core.game.world.map.zone.MapZone;
|
||||
import core.game.world.map.zone.ZoneBorders;
|
||||
import core.game.world.map.zone.ZoneRestriction;
|
||||
import core.game.world.repository.Repository;
|
||||
import core.tools.RandomFunction;
|
||||
import org.rs09.consts.NPCs;
|
||||
import core.game.world.GameWorld;
|
||||
import core.game.world.repository.Repository;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -94,7 +95,9 @@ public final class WildernessZone extends MapZone {
|
|||
if (!(killer instanceof Player)) return;
|
||||
|
||||
boolean isDeepWildy = ((Player) killer).getSkullManager().isDeepWilderness();
|
||||
boolean isValidTarget = e instanceof NPC && (isDeepWildy || e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200);
|
||||
boolean isRevOrCele = e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200;
|
||||
boolean isSufficientRisk = ((Player) killer).getAttribute("deepwild-value-risk", 0L) > SkullManager.DEEP_WILD_DROP_RISK_THRESHOLD;
|
||||
boolean isValidTarget = e instanceof NPC && ((isDeepWildy && isSufficientRisk) || isRevOrCele);
|
||||
|
||||
if (isDeepWildy) {
|
||||
DeepWildyThreat.adjustThreat((Player) killer, 50);
|
||||
|
|
@ -102,13 +105,13 @@ public final class WildernessZone extends MapZone {
|
|||
|
||||
if (!isValidTarget) return;
|
||||
|
||||
int cEleGloveRate = isDeepWildy ? 50 : 150;
|
||||
int normalGloveRate = isDeepWildy ? 100 : 150;
|
||||
|
||||
int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel());
|
||||
if (isDeepWildy)
|
||||
if (isDeepWildy && isRevOrCele)
|
||||
pvpGearRate /= 2;
|
||||
|
||||
int cEleGloveRate = isDeepWildy ? 50 : 150;
|
||||
int normalGloveRate = isDeepWildy && isRevOrCele ? 100 : (int)((1.0/(1.0-Math.pow(1.0 - (1.0/(double)pvpGearRate), 16.0))) * 5.0 / 6.0);
|
||||
|
||||
if (RandomFunction.roll(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? cEleGloveRate : normalGloveRate)) {
|
||||
byte glove = (byte) RandomFunction.random(1, 13);
|
||||
Item reward = new Item(BrawlingGloves.forIndicator(glove).getId());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue