Deep wilderness threat balancing

Killing the hostile event now removes all built up threat
Hostile event can now follow the player up/down stairs, through cave entrances, etc.
Hostile event now applies disease status for 25 hits when killed
Reduced the threat gained by killing NPCs by 5x
Increased the threat gained by brawlers drops by 3.75x
PvP gear drops now award maximum threat (30 minutes to return to zero)
In summary, the hostile event will trigger less often but will be more dangerous
This commit is contained in:
Ceikry 2023-08-11 00:09:07 +00:00 committed by Ryan
parent 1be9335815
commit 6a3e91f13c
7 changed files with 84 additions and 21 deletions

View file

@ -1,13 +1,19 @@
package content.region.wilderness.handlers package content.region.wilderness.handlers
import content.region.wilderness.handlers.revenants.RevenantController
import content.region.wilderness.handlers.revenants.RevenantNPC
import content.region.wilderness.handlers.revenants.RevenantType import content.region.wilderness.handlers.revenants.RevenantType
import core.api.* import core.api.*
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.combat.DeathTask import core.game.node.entity.combat.DeathTask
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.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.command.Privilege import core.game.system.command.Privilege
import core.game.system.timer.PersistTimer import core.game.system.timer.PersistTimer
import core.game.system.timer.impl.Disease
import core.game.world.map.zone.impl.WildernessZone
import core.game.world.update.flag.context.Graphics import core.game.world.update.flag.context.Graphics
import core.tools.RandomFunction import core.tools.RandomFunction
import core.tools.colorize import core.tools.colorize
@ -38,7 +44,6 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
var ticksLeft = 0 var ticksLeft = 0
var lastMessage = 0 var lastMessage = 0
var currentRev: NPC? = null var currentRev: NPC? = null
var chats = arrayOf("Leave this place!", "Suffer!", "Death to you!", "Flee, coward!", "Leave my resting place!", "Let me rest in peace!", "You belong to me!")
override fun run(entity: Entity): Boolean { override fun run(entity: Entity): Boolean {
if (ticksLeft-- <= 0) return false if (ticksLeft-- <= 0) return false
@ -59,13 +64,12 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel) val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel)
val npc = NPC.create(type.ids.random(), entity.location) val npc = NPC.create(type.ids.random(), entity.location)
npc.isRespawn = false npc.isRespawn = false
npc.behavior = RevGuardianBehavior()
npc.init() npc.init()
npc.attack(entity) npc.setAttribute("dw-threat-target", entity)
Graphics.send(Graphics(86), npc.location) RevenantController.unregisterRevenant(npc as RevenantNPC, false)
ticksLeft -= 500
sendChat(npc, chats.random())
currentRev = npc currentRev = npc
} else if (currentRev != null && !currentRev!!.location.withinDistance(entity.location, 25)) { } else if (currentRev != null && !currentRev!!.location.withinDistance(entity.location, 25) && currentRev!!.properties.teleportLocation == null) {
poofClear(currentRev!!) poofClear(currentRev!!)
currentRev = null currentRev = null
} }
@ -74,11 +78,11 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
} }
override fun save(root: JSONObject, entity: Entity) { override fun save(root: JSONObject, entity: Entity) {
root["threat-time-remaining"] = ticksLeft root["threat-time-remaining"] = ticksLeft.toString()
} }
override fun parse(root: JSONObject, entity: Entity) { override fun parse(root: JSONObject, entity: Entity) {
ticksLeft = root.getOrDefault("threat-time-remaining", 3000) as? Int ?: 3000 ticksLeft = root["threat-time-remaining"]?.toString()?.toIntOrNull() ?: 0
} }
override fun defineCommands() { override fun defineCommands() {
@ -88,3 +92,41 @@ class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands {
} }
} }
} }
class RevGuardianBehavior : NPCBehavior() {
val deathMessages = arrayOf("Curses upon thee!", "Rot in blight!", "Suffer my wrath!", "Nevermore!", "May ye be undone!")
var chats = arrayOf("Leave this place!", "Suffer!", "Death to thee!", "Flee, coward!", "Leave my resting place!", "Let me rest in peace!", "Thou belongeth to me!")
override fun tick(self: NPC): Boolean {
val target = getAttribute<Player?>(self, "dw-threat-target", null) ?: return true
if (!target.isActive) {
self.clear()
return true
}
if (target.properties.teleportLocation != null && self.properties.teleportLocation == null) {
if (WildernessZone.isInZone(target.properties.teleportLocation))
self.properties.teleportLocation = target.properties.teleportLocation
}
sendChat(self, "ticking")
self.attack(target)
return true
}
override fun onCreation(self: NPC) {
Graphics.send(Graphics(86), self.location)
sendChat(self, chats.random())
}
override fun onDeathStarted(self: NPC, killer: Entity) {
val target = getAttribute<Player?>(self, "dw-threat-target", null) ?: return
sendChat(self, deathMessages.random())
val disease = getOrStartTimer<Disease>(target, 25)
disease.hitsLeft = 25
}
override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList<Item>) {
val target = getAttribute<Player?>(self, "dw-threat-target", null) ?: return
val timer = getOrStartTimer<DWThreatTimer>(target)
timer.ticksLeft = 0
}
}

View file

@ -35,10 +35,11 @@ class RevenantController : TickListener, Commands {
Repository.RENDERABLE_NPCS.add(revenantNPC) Repository.RENDERABLE_NPCS.add(revenantNPC)
} }
@JvmStatic fun unregisterRevenant(revenantNPC: RevenantNPC) { @JvmStatic fun unregisterRevenant(revenantNPC: RevenantNPC, removeRender: Boolean = true) {
trackedRevenants.remove(revenantNPC) trackedRevenants.remove(revenantNPC)
taskTimeRemaining.remove(revenantNPC) taskTimeRemaining.remove(revenantNPC)
currentTask.remove(revenantNPC) currentTask.remove(revenantNPC)
if (removeRender)
Repository.RENDERABLE_NPCS.remove(revenantNPC) Repository.RENDERABLE_NPCS.remove(revenantNPC)
} }

View file

@ -99,7 +99,7 @@ public class RevenantNPC extends AbstractNPC {
@Override @Override
public void clear() { public void clear() {
super.clear(); super.clear();
RevenantController.unregisterRevenant(this); RevenantController.unregisterRevenant(this, true);
} }
@Override @Override
@ -125,6 +125,7 @@ public class RevenantNPC extends AbstractNPC {
} }
setAttribute("eat-delay", GameWorld.getTicks() + 6); setAttribute("eat-delay", GameWorld.getTicks() + 6);
} }
behavior.tick(this);
if (aggressiveHandler != null && aggressiveHandler.selectTarget()) { if (aggressiveHandler != null && aggressiveHandler.selectTarget()) {
return; return;
} }

View file

@ -2,6 +2,9 @@ package content.region.wilderness.handlers.revenants;
import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.NPCDefinition;
import java.util.ArrayList;
import java.util.List;
/** /**
* A revenant type. * A revenant type.
* @author Vexia * @author Vexia
@ -66,6 +69,14 @@ public enum RevenantType {
NPCDefinition def = NPCDefinition.forId(t.ids[0]); NPCDefinition def = NPCDefinition.forId(t.ids[0]);
if (def.getCombatLevel() >= combatLevel) return t; if (def.getCombatLevel() >= combatLevel) return t;
} }
return null; return RevenantType.DRAGON;
}
public static List<Integer> getAllIds() {
ArrayList<Integer> ids = new ArrayList<>();
for (RevenantType t : values()) {
for (int id : t.ids) ids.add(id);
}
return ids;
} }
} }

View file

@ -40,7 +40,6 @@ class Frozen : PersistTimer (1, "frozen") {
override fun getTimer (vararg args: Any) : RSTimer { override fun getTimer (vararg args: Any) : RSTimer {
val inst = Frozen() val inst = Frozen()
println(args)
inst.runInterval = args.getOrNull(0) as? Int ?: 10 inst.runInterval = args.getOrNull(0) as? Int ?: 10
inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false
return inst return inst

View file

@ -97,13 +97,13 @@ public final class WildernessZone extends MapZone {
boolean isValidTarget = e instanceof NPC && (isDeepWildy || e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200); boolean isValidTarget = e instanceof NPC && (isDeepWildy || e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200);
if (isDeepWildy) { if (isDeepWildy) {
DeepWildyThreat.adjustThreat((Player) killer, 250); DeepWildyThreat.adjustThreat((Player) killer, 50);
} }
if (!isValidTarget) return; if (!isValidTarget) return;
int cEleGloveRate = isDeepWildy ? 50 : 150; int cEleGloveRate = isDeepWildy ? 50 : 150;
int normalGloveRate = isDeepWildy ? 75 : 150; int normalGloveRate = isDeepWildy ? 100 : 150;
int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel()); int pvpGearRate = getNewDropRate(e.asNpc().getDefinition().getCombatLevel());
if (isDeepWildy) if (isDeepWildy)
@ -114,7 +114,8 @@ public final class WildernessZone extends MapZone {
Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); Item reward = new Item(BrawlingGloves.forIndicator(glove).getId());
GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer());
Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!");
DeepWildyThreat.adjustThreat((Player) killer, 200); if (isDeepWildy)
DeepWildyThreat.adjustThreat((Player) killer, 750);
} }
for (int j : PVP_GEAR) { for (int j : PVP_GEAR) {
@ -128,7 +129,8 @@ public final class WildernessZone extends MapZone {
} }
Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!");
GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer());
DeepWildyThreat.adjustThreat((Player) killer, 1000); if (isDeepWildy)
DeepWildyThreat.adjustThreat((Player) killer, 3000);
} }
} }
} }
@ -307,6 +309,13 @@ public final class WildernessZone extends MapZone {
return false; return false;
} }
public static boolean isInZone (Location l) {
for (ZoneBorders z : INSTANCE.borders) {
if (z.insideBorder(l)) return true;
}
return false;
}
/** /**
* The wilderness level. * The wilderness level.
* @return the level. * @return the level.

View file

@ -34,13 +34,13 @@ class UpdateSequence
npcList = Repository.renderableNpcs npcList = Repository.renderableNpcs
lobbyList!!.map{ PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(it)) } lobbyList!!.map{ PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(it)) }
var playerTickStart = System.currentTimeMillis()
renderablePlayers.forEach(Player::tick)
Grafana.playerTickTime = (System.currentTimeMillis() - playerTickStart).toInt()
var npcTickStart = System.currentTimeMillis() var npcTickStart = System.currentTimeMillis()
npcList!!.forEach(NPC::tick) npcList!!.forEach(NPC::tick)
Grafana.npcTickTime = (System.currentTimeMillis() - npcTickStart).toInt() Grafana.npcTickTime = (System.currentTimeMillis() - npcTickStart).toInt()
var playerTickStart = System.currentTimeMillis()
renderablePlayers.forEach(Player::tick)
Grafana.playerTickTime = (System.currentTimeMillis() - playerTickStart).toInt()
} }
/** /**