Graves Rewrite
Rewrote graves from scratch using new systems and abundant unit testing Fixed a race condition bug that would prevent classes implementing PersistWorld from properly... persisting Fixed a bug where graves were (still) too generous Fixed a bug where graves would not persist across server reboots Fixed a bug where graves would not recognize a relogged player Fixed numerous other bugs with graves, see the unit tests for complete coverage Grave system now supports quest requirements
This commit is contained in:
@@ -2078,6 +2078,8 @@ fun registerHintIcon(player: Player, location: Location, height: Int) {
|
||||
* @param node the Node to register a hint icon for.
|
||||
*/
|
||||
fun registerHintIcon(player: Player, node: Node) {
|
||||
if (getAttribute(player, "hinticon", null) != null)
|
||||
return
|
||||
setAttribute(player, "hinticon", HintIconManager.registerHintIcon(player, node))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.TickListener
|
||||
import api.clearHintIcon
|
||||
import api.registerHintIcon
|
||||
import api.sendMessage
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.item.GroundItem
|
||||
import core.game.node.item.GroundItemManager
|
||||
import core.game.node.item.Item
|
||||
import core.game.world.map.Location
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.NPCs
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.tools.secondsToTicks
|
||||
import rs09.tools.stringtools.colorize
|
||||
import rs09.tools.ticksToSeconds
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Initializable
|
||||
class Grave : AbstractNPC {
|
||||
lateinit var type: GraveType
|
||||
private val items = ArrayList<GroundItem>()
|
||||
var ownerUsername: String = ""
|
||||
var ownerUid: Int = -1
|
||||
|
||||
var ticksRemaining = -1
|
||||
|
||||
constructor() : super(NPCs.GRAVESTONE_6571, Location.create(0,0,0), false)
|
||||
private constructor(id: Int, location: Location) : super(id, location)
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return Grave(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return GraveType.ids
|
||||
}
|
||||
|
||||
fun configureType(type: GraveType) {
|
||||
this.type = type
|
||||
this.transform(type.npcId)
|
||||
this.ticksRemaining = secondsToTicks(type.durationMinutes * 60)
|
||||
}
|
||||
|
||||
fun initialize(player: Player, location: Location, inventory: Array<Item>) {
|
||||
if (!GraveController.allowGenerate(player))
|
||||
return
|
||||
|
||||
this.ownerUid = player.details.uid
|
||||
this.ownerUsername = player.username
|
||||
this.location = location
|
||||
this.isRespawn = false
|
||||
this.isWalks = false
|
||||
this.isNeverWalks = true
|
||||
|
||||
for (item in inventory) {
|
||||
if (GraveController.shouldRelease(item.id)) {
|
||||
sendMessage(player, "Your ${item.name.lowercase().replace("jar", "")} has escaped.")
|
||||
continue
|
||||
}
|
||||
|
||||
if (GraveController.shouldCrumble(item.id)) {
|
||||
sendMessage(player, "Your ${item.name.lowercase()} has crumbled to dust.")
|
||||
continue
|
||||
}
|
||||
|
||||
val finalItem = GraveController.checkTransform(item)
|
||||
|
||||
val gi = GroundItemManager.create(finalItem, this.location, player)
|
||||
gi.isRemainPrivate = true
|
||||
gi.decayTime = secondsToTicks(type.durationMinutes * 60)
|
||||
this.items.add(gi)
|
||||
}
|
||||
|
||||
if (items.isEmpty()) {
|
||||
clear()
|
||||
return
|
||||
}
|
||||
|
||||
this.init()
|
||||
|
||||
if (GraveController.activeGraves[ownerUid] != null) {
|
||||
val oldGrave = GraveController.activeGraves[ownerUid]
|
||||
oldGrave?.collapse()
|
||||
}
|
||||
|
||||
GraveController.activeGraves[ownerUid] = this
|
||||
sendMessage(player, colorize("%RBecause of your current gravestone, you have ${type.durationMinutes} minutes to get your items back."))
|
||||
}
|
||||
|
||||
fun setupFromJsonParams(playerUid: Int, ticks: Int, location: Location, items: Array<Item>, username: String) {
|
||||
this.ownerUid = playerUid
|
||||
this.ticksRemaining = ticks
|
||||
this.location = location
|
||||
this.isRespawn = false
|
||||
this.isWalks = false
|
||||
this.isNeverWalks = true
|
||||
this.ownerUsername = username
|
||||
|
||||
for (item in items) {
|
||||
val gi = GroundItemManager.create(item, location, playerUid, GameWorld.ticks + ticksRemaining)
|
||||
gi.isRemainPrivate = true
|
||||
this.items.add(gi)
|
||||
}
|
||||
|
||||
this.transform(type.npcId)
|
||||
this.init()
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
//Grave should not do anything else on tick, that is all handled by GraveController.
|
||||
if (Repository.uid_map[ownerUid] != null) {
|
||||
val p = Repository.uid_map[ownerUid] ?: return
|
||||
registerHintIcon(p, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun addTime(ticks: Int) {
|
||||
ticksRemaining += ticks
|
||||
for (gi in items) {
|
||||
gi.decayTime = ticksRemaining
|
||||
}
|
||||
if (ticksRemaining < 30)
|
||||
transform(type.npcId + 2)
|
||||
else if (ticksRemaining < 90)
|
||||
transform(type.npcId + 1)
|
||||
else
|
||||
transform(type.npcId)
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
for (item in items) {
|
||||
GroundItemManager.destroy(item)
|
||||
}
|
||||
clear()
|
||||
GraveController.activeGraves.remove(ownerUid)
|
||||
if (Repository.uid_map[ownerUid] != null) {
|
||||
val p = Repository.uid_map[ownerUid] ?: return
|
||||
clearHintIcon(p)
|
||||
}
|
||||
}
|
||||
|
||||
fun demolish() {
|
||||
val owner = Repository.uid_map[ownerUid] ?: return
|
||||
for (item in items) {
|
||||
if (!item.isRemoved)
|
||||
item.decayTime = secondsToTicks(45)
|
||||
}
|
||||
clear()
|
||||
sendMessage(owner, "It looks like it'll last another ${getFormattedTimeRemaining()}.")
|
||||
sendMessage(owner, "You demolish it anyway.")
|
||||
GraveController.activeGraves.remove(ownerUid)
|
||||
clearHintIcon(owner)
|
||||
}
|
||||
|
||||
fun getItems() : Array<GroundItem> {
|
||||
return this.items.toTypedArray()
|
||||
}
|
||||
|
||||
fun retrieveFormattedText(): String {
|
||||
return type.text
|
||||
.replace("@name", ownerUsername)
|
||||
.replace("@mins", getFormattedTimeRemaining())
|
||||
}
|
||||
|
||||
fun getFormattedTimeRemaining() : String {
|
||||
val seconds = ticksToSeconds(ticksRemaining)
|
||||
val timeQty = if (seconds / 60 > 0) seconds / 60 else seconds
|
||||
val timeUnit = (if (seconds / 60 > 0) "minute" else "second") + if (timeQty > 1) "s" else ""
|
||||
return "$timeQty $timeUnit"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.*
|
||||
import core.game.interaction.Interaction
|
||||
import core.game.node.Node
|
||||
import core.game.node.entity.combat.ImpactHandler
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.info.Rights
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.node.entity.player.link.audio.Audio
|
||||
import core.game.node.entity.skill.Skills
|
||||
import core.game.node.item.Item
|
||||
import core.game.system.task.Pulse
|
||||
import core.game.world.map.Location
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.rs09.consts.Items
|
||||
import rs09.ServerStore
|
||||
import rs09.game.interaction.InteractionListener
|
||||
import rs09.game.system.command.Privilege
|
||||
import rs09.game.world.GameWorld
|
||||
import rs09.game.world.repository.Repository
|
||||
import rs09.tools.secondsToTicks
|
||||
import rs09.tools.stringtools.colorize
|
||||
import rs09.tools.ticksToSeconds
|
||||
import java.util.Map
|
||||
import kotlin.math.min
|
||||
|
||||
class GraveController : PersistWorld, TickListener, InteractionListener, Commands {
|
||||
override fun defineListeners() {
|
||||
on(GraveType.ids, NPC, "read", handler = this::onGraveReadOption)
|
||||
on(GraveType.ids, NPC, "bless", handler = this::onGraveBlessed)
|
||||
on(GraveType.ids, NPC, "repair", handler = this::onGraveRepaired)
|
||||
on(GraveType.ids, NPC, "demolish", handler = this::onGraveDemolished)
|
||||
}
|
||||
|
||||
override fun defineCommands() {
|
||||
define("forcegravedeath", Privilege.ADMIN, "", "Forces a death that should produce a grave.") {player, _ ->
|
||||
player.details.rights = Rights.REGULAR_PLAYER
|
||||
setAttribute(player, "tutorial:complete", true)
|
||||
player.impactHandler.manualHit(player, player.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL)
|
||||
notify(player, "Grave created at ${player.location}")
|
||||
GameWorld.Pulser.submit(object : Pulse(15) {
|
||||
override fun pulse(): Boolean {
|
||||
player.details.rights = Rights.ADMINISTRATOR
|
||||
sendMessage(player, "Rights restored")
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun tick() {
|
||||
for (grave in activeGraves.values.toTypedArray()) {
|
||||
if (grave.ticksRemaining == -1) return
|
||||
|
||||
if (grave.ticksRemaining == secondsToTicks(30) || grave.ticksRemaining == secondsToTicks(90)) {
|
||||
grave.transform(grave.id + 1)
|
||||
}
|
||||
|
||||
if (grave.ticksRemaining == 0) {
|
||||
grave.collapse()
|
||||
}
|
||||
|
||||
grave.ticksRemaining--
|
||||
}
|
||||
}
|
||||
|
||||
private fun onGraveReadOption(player: Player, node: Node) : Boolean {
|
||||
val grave = node as? Grave ?: return false
|
||||
|
||||
var isGraniteBackground = false
|
||||
|
||||
when (grave.type) {
|
||||
in GraveType.SMALL_GS..GraveType.ANGEL_DEATH -> isGraniteBackground = true
|
||||
}
|
||||
|
||||
if (isGraniteBackground)
|
||||
setVarbit(player, 4191, 1)
|
||||
else
|
||||
setVarbit(player, 4191, 0)
|
||||
|
||||
openInterface(player, 266)
|
||||
setInterfaceText(player, grave.retrieveFormattedText(), 266, 23)
|
||||
|
||||
if (player.details.uid == grave.ownerUid) {
|
||||
sendMessage(player, "It looks like it'll survive another ${grave.getFormattedTimeRemaining()}.")
|
||||
sendMessage(player, "Isn't there something a bit odd about reading your own gravestone?")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveBlessed(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (getAttribute(g, "blessed", false)) {
|
||||
sendMessage(player, "This grave has already been blessed.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (player.details.uid == g.ownerUid) {
|
||||
sendMessage(player, "The gods don't seem to approve of people attempting to bless their own gravestones.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (getStatLevel(player, Skills.PRAYER) < 70) {
|
||||
sendMessage(player, "You need a Prayer level of 70 to bless a grave.")
|
||||
return true
|
||||
}
|
||||
|
||||
val blessAmount = min(60, player.skills.prayerPoints.toInt() - 10)
|
||||
|
||||
if (blessAmount <= 0) {
|
||||
sendMessage(player, "You do not have enough prayer points to do that.")
|
||||
return true
|
||||
}
|
||||
|
||||
g.addTime(secondsToTicks(blessAmount * 60))
|
||||
player.skills.prayerPoints -= blessAmount
|
||||
setAttribute(g, "blessed", true)
|
||||
|
||||
playAudio(player, Audio(2674))
|
||||
animate(player, 645)
|
||||
|
||||
val gOwner = Repository.uid_map[g.ownerUid]
|
||||
if (gOwner != null) {
|
||||
sendMessage(gOwner, colorize("%RYour grave has been blessed."))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveRepaired(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (getAttribute(g, "repaired", false)) {
|
||||
sendMessage(player, "This grave has already been repaired.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (getStatLevel(player, Skills.PRAYER) < 2) {
|
||||
sendMessage(player, "You need a Prayer level of 2 to bless a grave.")
|
||||
return true
|
||||
}
|
||||
|
||||
if (player.skills.prayerPoints < 1.0) {
|
||||
sendMessage(player, "You do not have enough prayer points to do that.")
|
||||
return true
|
||||
}
|
||||
|
||||
val restoreAmount = min(5, player.skills.prayerPoints.toInt())
|
||||
g.addTime(secondsToTicks(restoreAmount * 60))
|
||||
player.skills.prayerPoints -= restoreAmount
|
||||
setAttribute(g, "repaired", true)
|
||||
|
||||
playAudio(player, Audio(2674))
|
||||
animate(player, 645)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun onGraveDemolished(player: Player, node: Node) : Boolean {
|
||||
val g = node as? Grave ?: return false
|
||||
|
||||
if (player.details.uid != g.ownerUid) {
|
||||
sendMessage(player, "You cannot demolish someone else's gravestone!")
|
||||
return true
|
||||
}
|
||||
|
||||
g.demolish()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
serializeToServerStore()
|
||||
}
|
||||
|
||||
override fun parse() {
|
||||
deserializeFromServerStore()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val activeGraves = HashMap<Int, Grave>()
|
||||
var childCounter = 0
|
||||
val ATTR_GTYPE = "/save:gravetype"
|
||||
|
||||
@JvmStatic fun produceGrave(type: GraveType): Grave {
|
||||
val g = Grave()
|
||||
g.configureType(type)
|
||||
return g
|
||||
}
|
||||
|
||||
@JvmStatic fun shouldCrumble(item: Int) : Boolean {
|
||||
when (item) {
|
||||
Items.ECTOPHIAL_4251 -> return true
|
||||
in Items.SMALL_POUCH_5509..Items.GIANT_POUCH_5515 -> return true
|
||||
}
|
||||
|
||||
return itemDefinition(item).hasAction("destroy")
|
||||
}
|
||||
|
||||
@JvmStatic fun shouldRelease(item: Int) : Boolean {
|
||||
when (item) {
|
||||
Items.CHINCHOMPA_9976 -> return true
|
||||
Items.CHINCHOMPA_10033 -> return true
|
||||
in Items.BABY_IMPLING_JAR_11238..Items.DRAGON_IMPLING_JAR_11257 -> return itemDefinition(item).isUnnoted
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic fun checkTransform(item: Item) : Item {
|
||||
if (item.hasItemPlugin())
|
||||
return item.plugin.getDeathItem(item)
|
||||
return item
|
||||
}
|
||||
|
||||
@JvmStatic fun allowGenerate(player: Player) : Boolean {
|
||||
if (player.skullManager.isSkulled)
|
||||
return false
|
||||
if (player.skullManager.isWilderness)
|
||||
return false
|
||||
if (player.ironmanManager.mode == IronmanMode.HARDCORE)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic fun getGraveType(player: Player) : GraveType {
|
||||
return GraveType.values()[getAttribute(player, ATTR_GTYPE, 0)]
|
||||
}
|
||||
|
||||
@JvmStatic fun updateGraveType(player: Player, type: GraveType) {
|
||||
setAttribute(player, ATTR_GTYPE, type.ordinal)
|
||||
}
|
||||
|
||||
@JvmStatic fun hasGraveAt(loc: Location) : Boolean {
|
||||
return activeGraves.values.toTypedArray().any { it.location == loc }
|
||||
}
|
||||
|
||||
fun serializeToServerStore() {
|
||||
val archive = ServerStore.getArchive("active-graves")
|
||||
for ((uid,grave) in activeGraves) {
|
||||
val g = JSONObject()
|
||||
g["ticksRemaining"] = grave.ticksRemaining
|
||||
g["location"] = grave.location.toString()
|
||||
g["type"] = grave.type.ordinal
|
||||
g["username"] = grave.ownerUsername
|
||||
val items = JSONArray()
|
||||
for (item in grave.getItems()) {
|
||||
val i = JSONObject()
|
||||
i["id"] = item.id
|
||||
i["amount"] = item.amount
|
||||
i["charge"] = item.charge
|
||||
items.add(i)
|
||||
}
|
||||
g["items"] = items
|
||||
archive["$uid"] = g
|
||||
}
|
||||
}
|
||||
|
||||
fun deserializeFromServerStore() {
|
||||
val archive = ServerStore.getArchive("active-graves")
|
||||
for (entry in archive.entries as Set<Map.Entry<String, JSONObject>>) {
|
||||
val g = entry.value as JSONObject
|
||||
val uid = (entry.key as String).toInt()
|
||||
val type = g["type"].toString().toInt()
|
||||
val ticks = g["ticksRemaining"].toString().toInt()
|
||||
val location = Location.fromString(g["location"].toString())
|
||||
val username = g["username"].toString()
|
||||
|
||||
val items = ArrayList<Item>()
|
||||
val itemsRaw = g["items"] as JSONArray
|
||||
for (itemRaw in itemsRaw) {
|
||||
val item = itemRaw as JSONObject
|
||||
val id = item["id"].toString().toInt()
|
||||
val amount = item["amount"].toString().toInt()
|
||||
val charge = item["charge"].toString().toInt()
|
||||
items.add(Item(id, amount, charge))
|
||||
}
|
||||
|
||||
val grave = produceGrave(GraveType.values()[type])
|
||||
grave.setupFromJsonParams(uid, ticks, location, items.toTypedArray(), username)
|
||||
activeGraves[uid] = grave
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import api.*
|
||||
import core.game.node.item.Item
|
||||
import org.rs09.consts.Components
|
||||
import org.rs09.consts.Items
|
||||
import rs09.game.interaction.InterfaceListener
|
||||
|
||||
class GravePurchaseInterface : InterfaceListener {
|
||||
val BUTTON_CONFIRM = 34
|
||||
val AVAILABLE_GRAVES_BITFIELD = 0xFFF //Enable all graves, authentically the quest-locked ones should be excluded from the bitfield, but y'know.
|
||||
val AVAILABLE_GRAVES_VARBIT = 4191
|
||||
val CURRENT_GRAVE_VARBIT = 4190
|
||||
|
||||
override fun defineInterfaceListeners() {
|
||||
onOpen (Components.GRAVESTONE_SHOP_652) {player, _ ->
|
||||
val userType = GraveController.getGraveType(player).ordinal
|
||||
setVarbit(player, AVAILABLE_GRAVES_VARBIT, AVAILABLE_GRAVES_BITFIELD)
|
||||
setVarbit(player, CURRENT_GRAVE_VARBIT, userType)
|
||||
|
||||
val settings = IfaceSettingsBuilder()
|
||||
.enableAllOptions()
|
||||
.build()
|
||||
player.packetDispatch.sendIfaceSettings(settings, 34, Components.GRAVESTONE_SHOP_652, 0, 13)
|
||||
return@onOpen true
|
||||
}
|
||||
|
||||
on (Components.GRAVESTONE_SHOP_652, BUTTON_CONFIRM) { player, _, _, _, slot, _ ->
|
||||
val selectedType = GraveType.values()[slot]
|
||||
val userType = GraveController.getGraveType(player)
|
||||
val activeGrave = GraveController.activeGraves[player.details.uid]
|
||||
|
||||
if (activeGrave != null){
|
||||
sendDialogue(player, "You cannot change graves while you have a grave active.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType == userType) {
|
||||
sendDialogue(player, "You already have that gravestone!")
|
||||
return@on true
|
||||
}
|
||||
|
||||
val cost = selectedType.cost
|
||||
val requirement = selectedType.requiredQuest
|
||||
|
||||
if (requirement.isNotEmpty() && !isQuestComplete(player, requirement)) {
|
||||
sendDialogue(player, "That gravestone requires completion of $requirement.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType != GraveType.MEM_PLAQUE && amountInInventory(player, Items.COINS_995) < cost) {
|
||||
sendDialogue(player, "You do not have enough coins to afford that gravestone.")
|
||||
return@on true
|
||||
}
|
||||
|
||||
if (selectedType == GraveType.MEM_PLAQUE || removeItem(player, Item(995, cost))) {
|
||||
GraveController.updateGraveType(player, selectedType)
|
||||
sendDialogue(player, "Your grave has been updated.")
|
||||
}
|
||||
|
||||
closeInterface(player)
|
||||
return@on true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package rs09.game.node.entity.player.graves
|
||||
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
enum class GraveType(val npcId: Int, val cost: Int, val durationMinutes: Int, val isMembers: Boolean, val requiredQuest: String = "", val text: String) {
|
||||
MEM_PLAQUE(NPCs.GRAVE_MARKER_6565, 0, 2, false, text = "In memory of @name,<br>who died here."),
|
||||
FLAG(NPCs.GRAVE_MARKER_6568, 50, 2, false, text = MEM_PLAQUE.text),
|
||||
SMALL_GS(NPCs.GRAVESTONE_6571, 500, 2, false, text = "In loving memory of our dear friend @name,<br>who died in this place @mins ago."),
|
||||
ORNATE_GS(NPCs.GRAVESTONE_6574, 5000, 3, false, text = SMALL_GS.text),
|
||||
FONT_OF_LIFE(NPCs.GRAVESTONE_6577, 50000, 4, true, text = "In your travels,<br>pause awhile to remember @name,<br>who passed away at this spot."),
|
||||
STELE(NPCs.STELE_6580, 50000, 4, true, text = FONT_OF_LIFE.text),
|
||||
SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 4, true, text = "@name,<br>an enlightened servant of Saradomin,<br>perished in this place."),
|
||||
ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 4, true, text = "@name,<br>a most bloodthirsty follower of Zamorak,<br>perished in this place."),
|
||||
GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 4, true, text = "@name,<br>who walked with the Balance of Guthix,<br>perished in this place."),
|
||||
BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 4, true, requiredQuest = "Land of the Goblins", text = "@name,<br>a vicious warrior dedicated to Bandos,<br>perished in this place. "),
|
||||
ARMA_SYMBOL(NPCs.ARMADYL_SYMBOL_6595, 50000, 4, true, requiredQuest = "Temple of Ikov", text = "@name,<br>a follower of the Law of Armadyl,<br>perished in this place."),
|
||||
ZARO_SYMBOL(NPCs.MEMORIAL_STONE_6598, 50000, 4, true, requiredQuest = "Desert Treasure", text = "@name,<br>servant of the Unknown Power,<br>perished in this place."),
|
||||
ANGEL_DEATH(NPCs.MEMORIAL_STONE_6601, 500000, 5, true, text = "Ye frail mortals who gaze upon this sight,<br>forget not the fate of @name, once mighty, now<br>surrendered to the inescapable grasp of destiny.<br><i>Requiescat in pace.</i>");
|
||||
|
||||
companion object {
|
||||
val ids = values().fold(ArrayList<Int>()) {list, type ->
|
||||
list.add(type.npcId)
|
||||
list.add(type.npcId + 1)
|
||||
list.add(type.npcId + 2)
|
||||
list
|
||||
}.toIntArray()
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
package rs09.game.node.entity.player.info.login
|
||||
|
||||
import api.PersistPlayer
|
||||
import core.game.interaction.item.brawling_gloves.BrawlingGloves
|
||||
import core.game.node.entity.combat.CombatSpell
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.node.entity.player.link.IronmanMode
|
||||
import core.game.node.entity.player.link.SpellBookManager
|
||||
import core.game.node.entity.player.link.emote.Emotes
|
||||
import core.game.node.entity.player.link.grave.GraveType
|
||||
import core.game.node.entity.player.link.music.MusicEntry
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.simple.JSONArray
|
||||
import org.json.simple.JSONObject
|
||||
import org.json.simple.parser.JSONParser
|
||||
import rs09.ServerConstants
|
||||
import rs09.game.node.entity.player.graves.GraveController
|
||||
import rs09.game.node.entity.player.graves.GraveType
|
||||
import rs09.game.node.entity.skill.farming.CompostBins
|
||||
import rs09.game.node.entity.skill.farming.FarmingPatch
|
||||
import rs09.game.system.SystemLogger
|
||||
@@ -287,8 +285,9 @@ class PlayerSaveParser(val player: Player) {
|
||||
|
||||
fun parseGrave() {
|
||||
saveFile ?: return
|
||||
val graveData = (saveFile!!["grave_type"] as String).toInt()
|
||||
player.graveManager.type = GraveType.values()[graveData]
|
||||
val graveData = (saveFile!!["grave_type"] as? String)?.toInt() ?: return
|
||||
val type = GraveType.values()[graveData]
|
||||
GraveController.updateGraveType(player, type)
|
||||
}
|
||||
|
||||
fun parseAppearance() {
|
||||
|
||||
@@ -39,7 +39,6 @@ class PlayerSaver (val player: Player){
|
||||
saveAppearance(saveFile)
|
||||
saveSpellbook(saveFile)
|
||||
saveVarps(saveFile)
|
||||
saveGraveType(saveFile)
|
||||
saveSavedData(saveFile)
|
||||
saveAutocast(saveFile)
|
||||
saveConfigs(saveFile)
|
||||
@@ -517,10 +516,6 @@ class PlayerSaver (val player: Player){
|
||||
root.put("activityData",activityData)
|
||||
}
|
||||
|
||||
fun saveGraveType(root: JSONObject){
|
||||
root.put("grave_type",player.graveManager.type.ordinal.toString())
|
||||
}
|
||||
|
||||
fun saveSpellbook(root: JSONObject){
|
||||
root.put("spellbook",player.spellBookManager.spellBook.toString())
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import core.game.world.map.RegionManager
|
||||
import core.plugin.CorePluginTypes.StartupPlugin
|
||||
import core.tools.RandomFunction
|
||||
import rs09.ServerConstants
|
||||
import rs09.ServerStore
|
||||
import rs09.auth.AuthProvider
|
||||
import rs09.game.system.Auth
|
||||
import rs09.game.system.SystemLogger
|
||||
@@ -169,7 +170,9 @@ object GameWorld {
|
||||
ConfigParser().prePlugin()
|
||||
ClassScanner.scanClasspath()
|
||||
ClassScanner.loadPureInterfaces()
|
||||
worldPersists.forEach { it.parse() }
|
||||
val s = worldPersists.filterIsInstance<ServerStore>().first()
|
||||
s.parse()
|
||||
worldPersists.filter { it !is ServerStore }.forEach { it.parse() }
|
||||
ClassScanner.loadSideEffectfulPlugins()
|
||||
configParser.postPlugin()
|
||||
startupListeners.forEach { it.startup() }
|
||||
|
||||
@@ -17,7 +17,7 @@ class PulseRunner {
|
||||
fun updateAll() {
|
||||
val pulseCount = pulses.size
|
||||
|
||||
for (i in 0..pulseCount) {
|
||||
for (i in 0 until pulseCount) {
|
||||
val pulse = pulses.take()
|
||||
|
||||
val elapsedTime = measure {
|
||||
|
||||
Reference in New Issue
Block a user