diff --git a/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt b/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt new file mode 100644 index 000000000..9bc2f593c --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/DeepWildyThreat.kt @@ -0,0 +1,90 @@ +package content.region.wilderness.handlers + +import content.region.wilderness.handlers.revenants.RevenantType +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.DeathTask +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.system.command.Privilege +import core.game.system.timer.PersistTimer +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import core.tools.colorize +import org.json.simple.JSONObject + +object DeepWildyThreat { + @JvmStatic fun getThreat (player: Player) : Int { + return getOrStartTimer(player).ticksLeft + } + + @JvmStatic fun adjustThreat (player: Player, amount: Int) { + val timer = getOrStartTimer(player) + timer.ticksLeft += amount + if (timer.ticksLeft >= 2500 && timer.lastMessage < 2000) { + sendMessage(player, colorize("%RYou sense a great wrath upon you.")) + timer.lastMessage = 2000 + } else if (timer.ticksLeft >= 1000 && timer.lastMessage < 1000) { + sendMessage(player, colorize("%RYou sense watchful eyes upon you.")) + timer.lastMessage = 1000 + } else if (timer.ticksLeft >= 500 && timer.lastMessage < 500) { + sendMessage(player, colorize("%RYou sense a dark presence.")) + timer.lastMessage = 500 + } + } +} + +class DWThreatTimer : PersistTimer(1, "dw-threat"), Commands { + var ticksLeft = 0 + var lastMessage = 0 + 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 { + if (ticksLeft-- <= 0) return false + if (ticksLeft > 3000) ticksLeft = 3000 + if (ticksLeft % 5 != 0) return true + if (entity !is Player) return false + if (!entity.skullManager.isWilderness) return true + + val rollchance = + if (ticksLeft >= 2500) 10 + else if (ticksLeft >= 2000) 200 + else if (ticksLeft >= 1500) 400 + else if (ticksLeft >= 1000) 800 + else if (ticksLeft >= 500) 1500 + else 2_000_000 + + if ((currentRev == null || DeathTask.isDead(currentRev) || !currentRev!!.isActive) && RandomFunction.roll(rollchance)) { + val type = RevenantType.getClosestHigherOrEqual(entity.properties.currentCombatLevel) + val npc = NPC.create(type.ids.random(), entity.location) + npc.isRespawn = false + npc.init() + npc.attack(entity) + Graphics.send(Graphics(86), npc.location) + ticksLeft -= 500 + sendChat(npc, chats.random()) + currentRev = npc + } else if (currentRev != null && !currentRev!!.location.withinDistance(entity.location, 25)) { + poofClear(currentRev!!) + currentRev = null + } + + return true + } + + override fun save(root: JSONObject, entity: Entity) { + root["threat-time-remaining"] = ticksLeft + } + + override fun parse(root: JSONObject, entity: Entity) { + ticksLeft = root.getOrDefault("threat-time-remaining", 3000) as? Int ?: 3000 + } + + override fun defineCommands() { + define("dwthreat", Privilege.ADMIN, "", "") {player, _ -> + val timer = getOrStartTimer(player) + notify(player, "Current Threat: ${timer.ticksLeft}") + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java index fe582d1cf..b2f06aba3 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java @@ -1,5 +1,7 @@ package content.region.wilderness.handlers.revenants; +import core.cache.def.impl.NPCDefinition; + /** * A revenant type. * @author Vexia @@ -58,4 +60,12 @@ public enum RevenantType { public int getMaxHit() { return maxHit; } + + public static RevenantType getClosestHigherOrEqual (int combatLevel) { + for (RevenantType t : values()) { + NPCDefinition def = NPCDefinition.forId(t.ids[0]); + if (def.getCombatLevel() >= combatLevel) return t; + } + return null; + } } diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index 0904ec16d..909123d6d 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -2,6 +2,7 @@ package core.game.node.entity.player; import content.global.handlers.item.equipment.special.SalamanderSwingHandler; import content.global.skill.runecrafting.PouchManager; +import core.api.EquipmentSlot; import core.game.component.Component; import core.game.container.Container; import core.game.container.ContainerType; @@ -34,6 +35,7 @@ import core.game.node.entity.player.link.skillertasks.SkillerTasks; import core.game.node.entity.skill.Skills; import content.global.skill.construction.HouseManager; import content.global.skill.summoning.familiar.FamiliarManager; +import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.system.communication.CommunicationInfo; @@ -613,6 +615,11 @@ public class Player extends Entity { if (this.isArtificial() && killer instanceof NPC) { return; } + if (killer instanceof Player && getWorldTicks() - killer.getAttribute("/save:last-murder-news", 0) >= 500) { + Item wep = getItemFromEquipment((Player) killer, EquipmentSlot.WEAPON); + sendNews(killer.getName() + " has murdered " + getName() + " with " + (wep == null ? "their fists." : (StringUtils.isPlusN(wep.getName()) ? "an " : "a ") + wep.getName())); + killer.setAttribute("/save:last-murder-news", getWorldTicks()); + } getPacketDispatch().sendMessage("Oh dear, you are dead!"); incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS); @@ -653,7 +660,9 @@ public class Player extends Entity { g.initialize(this, location, Arrays.stream(c[1].toArray()).filter(Objects::nonNull).toArray(Item[]::new)); //note: the amount of code required to filter nulls from an array in Java is atrocious. } else { StringBuilder itemsLost = new StringBuilder(); + int coins = 0; for (Item item : c[1].toArray()) { + boolean stayPrivate = false; if (item == null) continue; if (killer instanceof Player) itemsLost.append(getItemName(item.getId())).append("(").append(item.getAmount()).append("), "); @@ -661,8 +670,20 @@ public class Player extends Entity { continue; if (GraveController.shouldRelease(item.getId())) continue; + if (!item.getDefinition().isTradeable()) { + if (killer instanceof Player) { + int value = item.getDefinition().getAlchemyValue(true); + if (getStatLevel(killer, Skills.MAGIC) < 55) value /= 2; + coins += Math.max(0, value - 250); + continue; + } else stayPrivate = true; + } item = GraveController.checkTransform(item); - GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + GroundItem gi = GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + gi.setRemainPrivate(stayPrivate); + } + if (coins > 0) { + GroundItemManager.create(new Item(Items.COINS_995, coins), location, (Player) killer); } if (killer instanceof Player) PlayerMonitor.log((Player) killer, LogType.PK, "Killed " + name + ", who dropped: " + itemsLost); diff --git a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java index 6a0c77a05..fd7211e08 100644 --- a/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java +++ b/Server/src/main/core/game/world/map/zone/impl/WildernessZone.java @@ -1,5 +1,6 @@ package core.game.world.map.zone.impl; +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; @@ -91,10 +92,14 @@ public final class WildernessZone extends MapZone { private void rollWildernessExclusiveLoot(Entity e, Entity killer) { 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); - + + if (isDeepWildy) { + DeepWildyThreat.adjustThreat((Player) killer, 250); + } + if (!isValidTarget) return; int cEleGloveRate = isDeepWildy ? 50 : 150; @@ -109,6 +114,7 @@ public final class WildernessZone extends MapZone { Item reward = new Item(BrawlingGloves.forIndicator(glove).getId()); GroundItemManager.create(reward, e.asNpc().getDropLocation(), killer.asPlayer()); Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); + DeepWildyThreat.adjustThreat((Player) killer, 200); } for (int j : PVP_GEAR) { @@ -122,6 +128,7 @@ public final class WildernessZone extends MapZone { } Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); + DeepWildyThreat.adjustThreat((Player) killer, 1000); } } }