Random event manager rewrite

Time until next random event now persists across logins
Events will no longer spawn while the player has an interface like the bank open
Replaced the slurry of random event commands with a singular ::revent admin command
Random events now support being associated with multiple skills
Random event selection is now more robust
Players will no longer get skill-based random events if more than 2.5 minutes have passed since training the skill
This commit is contained in:
Ceikry
2023-08-14 12:56:35 +00:00
committed by Ryan
parent bb21ad533b
commit a3e6df109d
26 changed files with 538 additions and 285 deletions
@@ -23,6 +23,7 @@ import org.rs09.consts.Components
import core.ServerConstants
import core.api.Event
import core.api.utils.PlayerCamera
import core.game.system.timer.impl.AntiMacro
import core.tools.SystemLogger
import core.game.world.GameWorld
import core.tools.Log
@@ -238,7 +239,7 @@ abstract class Cutscene(val player: Player) {
player.lock()
player.hook(Event.SelfDeath, CUTSCENE_DEATH_HOOK)
player.logoutListeners["cutscene"] = {player -> player.location = exitLocation; player.getCutscene()?.end() }
content.global.ame.RandomEventManager.getInstance(player)!!.enabled = false
AntiMacro.pause(player)
}
/**
@@ -275,7 +276,7 @@ abstract class Cutscene(val player: Player) {
clearNPCs()
player.unhook(CUTSCENE_DEATH_HOOK)
player.logoutListeners.remove("cutscene")
content.global.ame.RandomEventManager.getInstance(player)?.enabled = true
AntiMacro.unpause(player)
PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0))
try {
endActions?.invoke()
@@ -105,7 +105,7 @@ public abstract class Entity extends Node {
* The reward locks.
*/
private final ActionLocks locks = new ActionLocks();
public final ScriptProcessor scripts = new ScriptProcessor(this);
public ScriptProcessor scripts = new ScriptProcessor(this);
public final int[] clocks = new int[10];
@@ -219,7 +219,7 @@ public class Player extends Entity {
/**
* The quest repository.
*/
private QuestRepository questRepository = new QuestRepository(this);
public QuestRepository questRepository = new QuestRepository(this);
/**
* The prayer manager.
@@ -239,7 +239,7 @@ public class Player extends Entity {
/**
* The saved data.
*/
private SavedData savedData = new SavedData(this);
public SavedData savedData = new SavedData(this);
/**
* The request manager.
@@ -39,7 +39,7 @@ class PlayerSaveParser(val player: Player) {
var saveFile: JSONObject? = null
var read = true
init {
fun parse() {
val JSON = File(ServerConstants.PLAYER_SAVE_PATH + player.name + ".json")
if(JSON.exists())
{
@@ -49,36 +49,38 @@ class PlayerSaveParser(val player: Player) {
if (read) {
saveFile = parser.parse(reader) as JSONObject
}
if (read) {
parseData()
}
}
fun parse() {
if (read) {
parseCore()
parseAttributes()
parseSkills()
parseSettings()
parseQuests()
parseAppearance()
parseGrave()
parseVarps()
parseStates()
parseSpellbook()
parseSavedData()
parseAutocastSpell()
parseFarming()
parseConfigs()
parseMonitor()
parseMusic()
parseFamiliars()
parseBankPin()
parseHouse()
parseIronman()
parseEmoteManager()
parseStatistics()
parseAchievements()
parsePouches()
parsePouches()
}
fun parseData() {
parseCore()
parseAttributes()
parseSkills()
parseSettings()
parseQuests()
parseAppearance()
parseGrave()
parseVarps()
parseStates()
parseSpellbook()
parseSavedData()
parseAutocastSpell()
parseFarming()
parseConfigs()
parseMonitor()
parseMusic()
parseFamiliars()
parseBankPin()
parseHouse()
parseIronman()
parseEmoteManager()
parseStatistics()
parseAchievements()
parsePouches()
parsePouches()
}
fun runContentHooks()
@@ -34,7 +34,7 @@ class PlayerSaver (val player: Player){
companion object {
val contentHooks = ArrayList<PersistPlayer>()
}
private fun populate(): JSONObject {
fun populate(): JSONObject {
val saveFile = JSONObject()
saveCoreData(saveFile)
saveSkills(saveFile)
@@ -26,6 +26,7 @@ import org.rs09.consts.Items;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import static core.api.ContentAPIKt.getWorldTicks;
import static java.lang.Math.floor;
import static java.lang.Math.max;
@@ -119,7 +120,8 @@ public final class Skills {
*/
private int skillMilestone;
public int lastTrainedSkill = -1;
public int lastTrainedSkill = -1;
public int lastXpGain = 0;
/**
* Constructs a new {@code Skills} {@code Object}.
@@ -271,7 +273,8 @@ public final class Skills {
lastUpdateXp = this.experience.clone();
lastUpdate = GameWorld.getTicks();
}
lastTrainedSkill = slot;
lastTrainedSkill = slot;
lastXpGain = getWorldTicks();
}
/**
@@ -546,26 +546,6 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){
define("finishbins", Privilege.ADMIN, "", "Finishes any in-progress compost bins."){ player, _ ->
}
define("testlady", Privilege.ADMIN){ player, _ ->
content.global.ame.RandomEventManager.getInstance(player)!!.event = RandomEvents.RIVER_TROLL.npc.create(player)
content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init()
}
define("freak", Privilege.ADMIN){ player, _ ->
content.global.ame.RandomEventManager.getInstance(player)!!.event = RandomEvents.FREAKY_FORESTER.npc.create(player)
content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init()
}
define("bob", Privilege.ADMIN){ player, _ ->
content.global.ame.RandomEventManager.getInstance(player)!!.event = RandomEvents.EVIL_BOB.npc.create(player)
content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init()
}
define("revent", Privilege.ADMIN){ player, _ ->
println(player.pulseManager.current)
content.global.ame.RandomEventManager.getInstance(player)!!.fireEvent()
}
define("addcredits", Privilege.ADMIN){ player, _ ->
player.details.credits += 100
}
@@ -56,6 +56,12 @@ class TimerManager (val entity: Entity) {
timer.save(obj, entity)
root [timer.identifier] = obj
}
for (timer in newTimers) {
if (timer !is PersistTimer) continue
val obj = JSONObject()
timer.save(obj, entity)
root [timer.identifier] = obj
}
}
fun parseTimers (root: JSONObject) {
@@ -30,7 +30,6 @@ object TimerRegistry {
@JvmStatic
fun addAutoTimers (entity: Entity) {
(entity as? Player)?.debug ("Adding auto timers...")
for (timer in autoTimers) {
if (!hasTimerActive (entity, timer.identifier))
registerTimer (entity, timer.retrieveInstance())
@@ -0,0 +1,174 @@
package core.game.system.timer.impl
import content.global.ame.RandomEventNPC
import content.global.ame.RandomEvents
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.command.Privilege
import core.game.system.timer.PersistTimer
import core.game.world.map.zone.ZoneRestriction
import core.game.world.repository.Repository
import core.tools.RandomFunction
import org.json.simple.JSONObject
class AntiMacro : PersistTimer(0, "antimacro", isAuto = true), Commands {
var paused = false
var nextRandom: RandomEvents? = null
override fun run(entity: Entity): Boolean {
if (entity !is Player) return false
setNextExecution()
if (!canSpawn(entity)) return true
val eventSelection = rollEventPool(entity)
val eventNpc = eventSelection.npc.create(entity, eventSelection.loot, eventSelection.type)
if (eventNpc.spawnLocation == null) {
entity.debug("[AntiMacro] Attempted to spawn random, but spawnLoc was null.")
return true
}
eventNpc.init()
setAttribute(entity, EVENT_NPC, eventNpc)
entity.debug("[AntiMacro] Fired ${eventSelection.name}.")
return true
}
override fun onRegister(entity: Entity) {
if (entity !is Player)
entity.timers.removeTimer(this)
if (runInterval == 0)
setNextExecution()
}
override fun save(root: JSONObject, entity: Entity) {
root["ticksRemaining"] = (nextExecution - getWorldTicks()).toString()
}
override fun parse(root: JSONObject, entity: Entity) {
runInterval = (root["ticksRemaining"]?.toString()?.toIntOrNull() ?: 0)
nextExecution = getWorldTicks() + runInterval
}
private fun canSpawn(entity: Entity) : Boolean {
if (entity.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS))
return false
val current = getAttribute<RandomEventNPC?>(entity, EVENT_NPC, null)
if (current != null)
return false
if (paused)
return false
return true
}
private fun setNextExecution() {
runInterval = RandomFunction.random(MIN_DELAY_TICKS, MAX_DELAY_TICKS + 1)
nextExecution = getWorldTicks() + runInterval
}
private fun rollEventPool(entity: Entity) : RandomEvents {
if (nextRandom != null) {
val result = nextRandom!!
nextRandom = null
return result
}
val skillBasedRandom = RandomEvents.getSkillBasedRandomEvent(entity.skills.lastTrainedSkill)
val normalRandom = RandomEvents.getNonSkillRandom()
val roll = RandomFunction.random(100)
if (roll >= 65 && skillBasedRandom != null && getWorldTicks() - entity.skills.lastXpGain < 250)
return skillBasedRandom
return normalRandom
}
override fun defineCommands() {
define("revent", Privilege.ADMIN, "::revent [-p] <lt>player name<gt> [-e <lt>event name<gt>]", "Spawns a random event for the target player.<br>Optional -e parameter to pass a specific event.") {player, args ->
if (args.size == 1) {
val possible = RandomEvents.values()
for (event in possible) {
notify(player, event.name.lowercase())
}
return@define
}
val arg = parseCommandArgs(args.joinToString(" "))
val target = Repository.getPlayerByName(arg.targetPlayer)
if (target == null)
reject(player, "Unable to find user ${arg.targetPlayer}.")
forceEvent(target!!, arg.targetEvent)
}
}
data class CommandArgs (val targetPlayer: String, val targetEvent: RandomEvents?)
companion object {
const val EVENT_NPC = "re-npc"
const val MIN_DELAY_TICKS = 3000
const val MAX_DELAY_TICKS = 9000
fun terminateEventNpc (player: Player) {
getEventNpc(player)?.terminate()
}
fun rollEventLoot (player: Player) : ArrayList<Item> {
return getEventNpc(player)?.loot?.roll(player) ?: ArrayList()
}
fun getEventNpc (player: Player) : RandomEventNPC? {
return getAttribute<RandomEventNPC?>(player, EVENT_NPC, null)
}
fun pause (player: Player) {
val timer = getTimer<AntiMacro>(player) ?: return
timer.paused = true
}
fun unpause (player: Player) {
val timer = getTimer<AntiMacro>(player) ?: return
timer.paused = false
}
fun forceEvent (player: Player, event: RandomEvents? = null) {
val timer = getTimer<AntiMacro>(player) ?: return
timer.nextExecution = getWorldTicks()
timer.nextRandom = event
}
fun parseCommandArgs (args: String, commandName: String = "revent") : CommandArgs {
val tokens = args.split(" ")
val modeTokens = arrayOf("-p", "-e")
var userString = ""
var eventString = ""
var lastMode = "-p"
for (token in tokens) {
when (token) {
commandName -> continue
in modeTokens -> lastMode = token
else -> when (lastMode) {
"-p" -> userString += "$token "
"-e" -> eventString += "$token "
}
}
}
val username = userString.trim().lowercase().replace(" ", "_")
val eventName = eventString.trim().uppercase().replace(" ", "_")
var event: RandomEvents? = null
try { event = RandomEvents.valueOf(eventName) } catch (_: Exception) {}
return CommandArgs(username, event)
}
}
}