Implemented timer subsystem to eventually replace pulses

Authentic subsystem supports saving/loading arbitrary data and resuming timer countdowns
Lots of documented CAPI functions for working with these new timers
Converted poison to new timers
Converted disease to new timers
Converted farming to new timers (CropGrowth and Compost)
Farming now syncs with 5 minute intervals on realtime clocks (authentic)
Converted seedling growth to new timers
Converted shooting star mining bonus to new timers
Converted entity freezing to new timers
Converted incubation to new timers
Incubation now supports using both incubators (so 2 eggs at once)
Converted miasmic states to the new timers
Converted god spell charged state to new timers
Converted teleblock to new timers
Converted skulled state to new timers
Converted Enhanced Excalibur special attack effect to new timers
Converted passive stat restoration to timers
Converted multicannon firing and decay to a timer
This commit is contained in:
Ceikry
2023-07-29 04:51:06 +00:00
committed by Ryan
parent bda000f220
commit 661390e66f
114 changed files with 1718 additions and 2112 deletions
+157 -32
View File
@@ -25,7 +25,6 @@ import core.game.node.entity.skill.Skills
import content.data.skill.SkillingTool
import content.global.skill.slayer.Tasks
import content.global.skill.summoning.familiar.BurdenBeast
import core.game.node.entity.state.EntityState
import core.game.node.item.GroundItem
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
@@ -74,6 +73,8 @@ import core.game.requirement.*
import core.net.packet.PacketRepository
import core.net.packet.context.MusicContext
import core.net.packet.out.MusicPacket
import core.game.system.timer.*
import core.game.system.timer.impl.*
import java.util.regex.*
import java.io.*
import kotlin.math.*
@@ -963,6 +964,131 @@ fun removeAttributes(entity: Entity, vararg attributes: String) {
for (attribute in attributes) removeAttribute(entity, attribute)
}
/**
* Adds the given timer to the entity's active timers.
* @param entity the entity whose timers are being added to
* @param timer the timer being added
**/
fun registerTimer (entity: Entity, timer: RSTimer?) {
if (timer == null) return
entity.timers.registerTimer (timer)
}
/**
* Used to fetch the existing, active, non-abstract and non-anonymous timer with the given identifier, or start a new timer if none exists and return that.
* @param entity the entity whose timers we're retrieving
* @param identifier the identifier of the timer, refer to the individual timer class for this token.
* @param args various args to pass to the initialization of the timer, if applicable.
* @return Either the existing active timer, or a new timer initialized with the passed args if none exists yet.
**/
fun getOrStartTimer (entity: Entity, identifier: String, vararg args: Any) : RSTimer? {
val existing = getTimer (entity, identifier)
if (existing != null)
return existing
return spawnTimer (identifier, *args).also { registerTimer (entity, it) }
}
/**
* Used to fetch the existing, active, non-abstract and non-anonymous timer with the given type, or start a new timer if none exists and return that.
* @param entity the entity whose timer we're retrieving
* @param T the type of timer we are fetching
* @param args the various args to pass to the initialization of the timer, if applicable.
* @return Either the existing, active timer or a new timer initialized with the passed args if none exists yet.
**/
inline fun <reified T: RSTimer> getOrStartTimer (entity: Entity, vararg args: Any) : T {
val existing = getTimer <T> (entity)
if (existing != null)
return existing
return spawnTimer <T> (*args).also { registerTimer (entity, it) }
}
/**
* Used to fetch a new instance of a registered (see: non-anonymous, non-abstract) timer with the given configuration args
* @param identifier the string identifier for the timer. e.g. poison's is "poison"
* @param args various arbitrary arguments to be passed to the timer's constructor. Refer to the timer in question for what the args are expected to be.
* @return a timer instance configured with your given args, or null if no timer with the given key exists in the registry.
**/
fun spawnTimer (identifier: String, vararg args: Any) : RSTimer? {
return TimerRegistry.getTimerInstance (identifier, *args)
}
/**
* Used to fetch a new instance of a registered (see: non-anonymous, non-abstract) timer with the given configuration args
* @param T the type of the timer you're trying to retrieve. The timer registry will be searched for a timer of this type.
* @param args various arbitrary arguments to be passed to the timer's constructor. Refer to the timer in question for what the args are expected to be.
* @return a timer instance configured with your given args, or null if the timer is not listed in the registry (if this happens, your timer is either abstract or anonymous.)
**/
inline fun <reified T: RSTimer> spawnTimer (vararg args: Any) : T {
return TimerRegistry.getTimerInstance <T> (*args)!!
}
/**
* Used to check if a timer of the given type is registered and active in the entity's timers.
* @param T the type of timer
* @param entity the entity whose timers are being checked
* @return true if there is a timer registered and active with the given type.
**/
inline fun <reified T: RSTimer> hasTimerActive (entity: Entity) : Boolean {
return getTimer<T>(entity) != null
}
/**
* Used to get the active instance of a timer with the given type from the entity's timers.
* @param T the type of timer
* @param entity the entity whose timers we are checking
* @return the active instance of the given type in the entity's timers, or null.
*/
inline fun <reified T: RSTimer> getTimer (entity: Entity) : T? {
return entity.timers.getTimer<T>()
}
/**
* Removes any active timers of the given type from the entity's active timers. This will remove ALL matching instances.
* @param T the type of timer
* @param entity the entity whose timers are being checked
**/
inline fun <reified T: RSTimer> removeTimer (entity: Entity) {
entity.timers.removeTimer<T>()
}
/**
* Used to check if a timer with the given identifier string is registered and active in the entity's timers.
* @param identifier the identifier token assigned to the timer. You'll have to refer to the actual timer class for this string.
* @param entity the entity whose timers are being checked
* @return true if there's a timer with the given identifier active in the entity's timers
**/
fun hasTimerActive (entity: Entity, identifier: String) : Boolean {
return getTimer (entity, identifier) != null
}
/**
* Used to get the active instance of a timer with the given identifier from the entity's timers.
* @param identifier the string identifier of the timer we are looking for. You'll have to refer to the actual timer class for this string.
* @param entity the entity whose timers are being checked
* @return the instance of the active timer if found, null otherwise.
**/
fun getTimer (entity: Entity, identifier: String) : RSTimer? {
return entity.timers.getTimer(identifier)
}
/**
* Removes any active timers with the given identifier from the entity's active timers. This will remove ALL matching instances.
* @param identifier the identifier token assigned to the timer. You'll have to refer to the actual timer class for this string.
* @param entity the entity whose timers are being checked
**/
fun removeTimer (entity: Entity, identifier: String) {
entity.timers.removeTimer(identifier)
}
/**
* Removes the given timer from the entity's active timers. Note this variant will only work with a reference to the same exact timer you want to remove.
* @param entity the entity whose timers we are modifying
* @param timer the timer to remove
**/
fun removeTimer (entity: Entity, timer: RSTimer) {
entity.timers.removeTimer(timer)
}
/**
* Locks the given entity for the given number of ticks
* @param entity the entity to lock
@@ -2492,37 +2618,6 @@ fun requireQuest(player: Player, questName: String, message: String) : Boolean {
}
/**
* Determines whether or not a specified entity has a state
* @param entity the entity whose state we are checking
* @param state the state to check for
* @return whether or not the entity has the provided state
*/
fun hasState(entity: Entity, state: EntityState) : Boolean {
return entity.stateManager.hasState(state)
}
/**
* Adds a state to the entity
* @param entity the entity whose state we are adding
* @param state the state to add
* @param override whether or not it's to override another state
*/
fun addState(entity: Entity, state: EntityState, override: Boolean, vararg args: Any?) {
if(!entity.stateManager.hasState(state)) {
entity.stateManager.register(state, override, *args)
}
}
/**
* Removes a state from the entity
* @param entity the entity whose state we are removing
* @param state the state to remove
*/
fun removeState(entity: Entity, state: EntityState) {
entity.stateManager.remove(state)
}
/**
* Determines whether or not specified node is a player
* @param entity the node whom we are checking
@@ -2690,6 +2785,36 @@ fun isStunned(entity: Entity) : Boolean {
return entity.clocks[Clocks.STUN] >= getWorldTicks()
}
/**
* Applies poison to the target. (In other words, creates and starts a poison timer.)
* @param entity the entity who will be receiving the poison damage.
* @param source the entity to whom credit for the damage should be awarded (the attacker.) You should award credit to the victim if the poison is sourceless (e.g. from a trap or plant or something)
* @param severity the severity of the poison damage. Severity is not a 1:1 representation of damage, rather the formula `floor((severity + 4) / 5)` is used. Severity is decreased by 1 with each application of the poison, and ends when it reaches 0.
* @see To those whe ask "why severity instead of plain damage?" to which the answer is: severity is how it works authentically, and allows for scenarios where, e.g. a poison should hit 6 once, and then drop to 5 immediately.
**/
fun applyPoison (entity: Entity, source: Entity, severity: Int) {
val existingTimer = getTimer<Poison>(entity)
if (existingTimer != null) {
existingTimer.severity = severity
existingTimer.damageSource = source
} else {
registerTimer(entity, spawnTimer<Poison>(source, severity))
}
}
fun isPoisoned (entity: Entity) : Boolean {
return getTimer<Poison>(entity) != null
}
fun curePoison (entity: Entity) {
if (!hasTimerActive<Poison>(entity))
return
removeTimer<Poison>(entity)
if (entity is Player)
sendMessage(entity, "Your poison has been cured.")
}
fun setCurrentScriptState(entity: Entity, state: Int) {
val script = entity.scripts.getActiveScript()
if (script == null) {
+3 -1
View File
@@ -35,4 +35,6 @@ object Event {
@JvmStatic val SummoningPointsRecharged = SummoningPointsRechargeEvent::class.java
@JvmStatic val PrayerPointsRecharged = PrayerPointsRechargeEvent::class.java
@JvmStatic val XpGained = XPGainEvent::class.java
}
@JvmStatic val PrayerActivated = PrayerActivatedEvent::class.java
@JvmStatic val PrayerDeactivated = PrayerDeactivatedEvent::class.java
}
+2 -2
View File
@@ -2,6 +2,6 @@ package core.game.event
import core.game.node.entity.Entity
interface EventHook<T: Event> {
interface EventHook <T: Event> {
fun process(entity: Entity, event: T)
}
}
+4 -1
View File
@@ -7,6 +7,7 @@ import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.SpellBookManager.SpellBook
import core.game.node.entity.player.link.TeleportManager.TeleportType
import core.game.node.entity.player.link.prayer.PrayerType
import core.game.node.item.Item
import core.game.world.map.Location
import content.global.activity.jobs.JobType
@@ -44,4 +45,6 @@ data class VarbitUpdateEvent(val offset: Int, val value: Int) : Event
data class DynamicSkillLevelChangeEvent(val skillId: Int, val oldValue: Int, val newValue: Int): Event
data class SummoningPointsRechargeEvent(val obelisk: Node) : Event
data class PrayerPointsRechargeEvent(val altar: Node) : Event
data class XPGainEvent(val skillId: Int, val amount: Double) : Event
data class XPGainEvent(val skillId: Int, val amount: Double) : Event
data class PrayerActivatedEvent (val type: PrayerType) : Event
data class PrayerDeactivatedEvent (val type: PrayerType) : Event
@@ -15,8 +15,6 @@ import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.TeleportManager;
import core.game.node.entity.skill.Skills;
import core.game.node.entity.state.EntityState;
import core.game.node.entity.state.StateManager;
import core.game.system.task.Pulse;
import org.jetbrains.annotations.NotNull;
import core.game.world.GameWorld;
@@ -30,6 +28,8 @@ import core.game.world.update.flag.context.Graphics;
import core.game.world.update.flag.*;
import core.game.node.entity.combat.CombatSwingHandler;
import core.game.world.update.UpdateMasks;
import core.game.system.timer.TimerManager;
import core.game.system.timer.TimerRegistry;
import java.util.*;
@@ -101,11 +101,6 @@ public abstract class Entity extends Node {
*/
private final ZoneMonitor zoneMonitor = new ZoneMonitor(this);
/**
* The state manager.
*/
private final StateManager stateManager = new StateManager(this);
/**
* The reward locks.
*/
@@ -118,7 +113,7 @@ public abstract class Entity extends Node {
* The mapping of event types to event hooks
*/
private HashMap<Class<?>, ArrayList<EventHook>> hooks = new HashMap<>();
public TimerManager timers = new TimerManager(this);
/**
* If the entity is invisible.
@@ -209,6 +204,7 @@ public abstract class Entity extends Node {
*/
public void init() {
active = true;
TimerRegistry.addAutoTimers (this);
}
/**
@@ -221,6 +217,7 @@ public abstract class Entity extends Node {
Location old = location != null ? location.transform(0, 0, 0) : Location.create(0,0,0);
walkingQueue.update();
scripts.postMovement(!Objects.equals(location, old));
timers.processTimers();
updateMasks.prepare(this);
}
@@ -261,6 +258,8 @@ public abstract class Entity extends Node {
*/
public void fullRestore() {
skills.restore();
timers.removeTimer("poison");
timers.removeTimer("poison:immunity");
}
/**
@@ -279,7 +278,6 @@ public abstract class Entity extends Node {
skills.rechargePrayerPoints();
impactHandler.getImpactQueue().clear();
impactHandler.setDisabledTicks(10);
stateManager.reset();
removeAttribute("combat-time");
face(null);
//Check if it's a Loar shade and transform back into the shadow version.
@@ -916,14 +914,6 @@ public abstract class Entity extends Node {
return zoneMonitor;
}
/**
* Gets the stateManager.
* @return The stateManager.
*/
public StateManager getStateManager() {
return stateManager;
}
/**
* Checks if we have fire resistance.
* @return {@code True} if so.
@@ -937,7 +927,7 @@ public abstract class Entity extends Node {
* @return {@code True} if so.
*/
public boolean isTeleBlocked() {
return stateManager.hasState(EntityState.TELEBLOCK);
return timers.getTimer("teleblock") != null;
}
/**
@@ -11,12 +11,13 @@ import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.skill.Skills
import core.game.node.entity.state.EntityState
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.update.flag.context.Animation
import core.tools.RandomFunction
import core.api.*
import core.game.system.timer.impl.*
/**
* The combat-handling pulse implementation.
@@ -147,7 +148,7 @@ class CombatPulse(
} else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RAPID || (salamander && entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE)) {
speed--
}
if (!magic && entity.stateManager.hasState(EntityState.MIASMIC)) {
if (!magic && hasTimerActive<Miasmic>(entity)) {
speed = (speed * 1.5).toInt()
}
setNextAttack(speed)
@@ -13,7 +13,6 @@ import core.game.node.entity.combat.equipment.WeaponInterface
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.entity.state.EntityState
import core.game.world.map.path.Pathfinder
import core.tools.RandomFunction
import org.rs09.consts.Items
@@ -119,7 +118,7 @@ open class MeleeSwingHandler
damage = 48
}
if (damage > -1 && RandomFunction.random(10) < 4) {
victim.stateManager.register(EntityState.POISONED, false, damage, entity)
applyPoison (victim, entity, damage)
}
}
}
@@ -1,7 +1,7 @@
package core.game.node.entity.combat
import content.global.skill.skillcapeperks.SkillcapePerks
import core.api.log
import core.api.*
import core.game.container.impl.EquipmentContainer
import core.game.node.entity.Entity
import core.game.node.entity.combat.equipment.*
@@ -10,7 +10,6 @@ import core.game.node.entity.impl.Projectile
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.node.entity.state.EntityState
import core.game.node.item.GroundItem
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
@@ -125,7 +124,7 @@ open class RangeSwingHandler
if (state.ammunition != null && entity is Player) {
val damage = state.ammunition.poisonDamage
if (state.estimatedHit > 0 && damage > 8 && RandomFunction.random(10) < 4) {
victim.stateManager.register(EntityState.POISONED, false, damage, entity)
applyPoison (victim, entity, damage)
}
}
super.adjustBattleState(entity, victim, state)
@@ -7,12 +7,13 @@ import core.game.node.entity.combat.ImpactHandler.HitsplatType;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.audio.Audio;
import core.game.node.entity.state.EntityState;
import core.game.world.GameWorld;
import core.game.world.update.flag.context.Graphics;
import core.tools.RandomFunction;
import org.rs09.consts.NPCs;
import static core.api.ContentAPIKt.*;
/**
* Represents a bolt effect.
* @author Vexia
@@ -107,7 +108,7 @@ public enum BoltEffect {
EMERALD(9241, new Graphics(752), new Audio(2919)) {
@Override
public void impact(BattleState state) {
state.getVictim().getStateManager().register(EntityState.POISONED, false, 68, state.getAttacker());
applyPoison(state.getVictim(), state.getAttacker(), 40);
super.impact(state);
}
},
@@ -4,11 +4,12 @@ import core.game.node.Node;
import core.game.node.entity.Entity;
import core.game.node.entity.impl.Animator.Priority;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.EntityState;
import core.game.system.task.NodeTask;
import core.game.world.update.flag.context.Animation;
import core.tools.RandomFunction;
import static core.api.ContentAPIKt.*;
/**
* The fire types.
* @author Emperor
@@ -47,7 +48,7 @@ public enum FireType {
TOXIC_BREATH(new Animation(82, Priority.HIGH), 394, new NodeTask(0) {
@Override
public boolean exec(Node node, Node... n) {
((Entity) node).getStateManager().register(EntityState.POISONED, false, 80, (Entity) n[0]);
applyPoison ((Entity) node, (Entity) n[0], 40);
return true;
}
}),
@@ -58,7 +59,7 @@ public enum FireType {
ICY_BREATH(new Animation(83, Priority.HIGH), 395, new NodeTask(0) {
@Override
public boolean exec(Node node, Node... n) {
((Entity) node).getStateManager().set(EntityState.FROZEN, 7);
registerTimer((Entity) node, spawnTimer ("frozen", 7, true));
return true;
}
});
@@ -112,4 +113,4 @@ public enum FireType {
return task;
}
}
}
@@ -5,9 +5,10 @@ import core.game.node.entity.skill.Skills;
import core.game.node.entity.Entity;
import core.game.node.entity.npc.NPC;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.EntityState;
import core.game.node.item.Item;
import static core.api.ContentAPIKt.*;
/**
* Represents the spell types.
* @author Emperor
@@ -152,7 +153,7 @@ public enum SpellType {
@Override
public int getImpactAmount(Entity e, Entity victim, int base) {
if(!(e instanceof Player)) return 20;
if (e.asPlayer().hasActiveState("godcharge")) {
if (hasTimerActive(e, "magic:spellcharge")) {
Item cape = ((Player) e).getEquipment().getNew(EquipmentContainer.SLOT_CAPE);
if (cape.getId() == 2412 || cape.getId() == 2413 || cape.getId() == 2414) {
return 30;
@@ -252,4 +253,4 @@ public enum SpellType {
return accuracyMod;
}
}
}
@@ -18,7 +18,7 @@ import java.util.Deque;
import java.util.ArrayDeque;
import java.util.ArrayList;
import static core.api.ContentAPIKt.log;
import static core.api.ContentAPIKt.*;
/**
* The walking queue.
@@ -89,6 +89,8 @@ public final class WalkingQueue {
if (isPlayer && updateRegion(entity.getLocation(), true)) {
return;
}
if (hasTimerActive(entity, "frozen"))
return;
Point point = walkingQueue.poll();
boolean drawPath = entity.getAttribute("routedraw", false);
if (point == null) {
@@ -424,6 +424,7 @@ public class NPC extends Entity {
if (isRespawning && respawnTick <= GameWorld.getTicks()) {
behavior.onRespawn(this);
onRespawn();
fullRestore();
isRespawning = false;
}
handleTickActions();
@@ -679,6 +679,8 @@ public class Player extends Entity {
getPrayer().reset();
super.finalizeDeath(killer);
appearance.sync();
timers.removeTimer("poison");
timers.removeTimer("poison:immunity");
if (!getSavedData().getGlobalData().isDeathScreenDisabled()) {
getInterfaceManager().open(new Component(153));
}
@@ -747,7 +749,7 @@ public class Player extends Entity {
@Override
public boolean isPoisonImmune() {
return getAttribute("poison:immunity", -1) > GameWorld.getTicks();
return timers.getTimer("poison:immunity") != null;
}
@Override
@@ -139,7 +139,6 @@ public final class LoginConfiguration {
player.getMusicPlayer().init();
player.updateAppearance();
player.getPlayerFlags().setUpdateSceneGraph(true);
player.getStateManager().init();
player.getPacketDispatch().sendInterfaceConfig(226, 1, true);
if(player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)){
player.getEmoteManager().unlock(Emotes.SAFETY_FIRST);
@@ -299,6 +299,7 @@ class PlayerSaveParser(val player: Player) {
val bOre = coreData["blastOre"] as? JSONArray
val bCoal = coreData["blastCoal"] as? JSONArray
val varpData = coreData["varp"] as? JSONArray
val timerData = coreData["timers"] as? JSONObject
val location = coreData["location"] as String
val bankTabData = coreData["bankTabs"]
if (bankTabData != null) {
@@ -342,6 +343,9 @@ class PlayerSaveParser(val player: Player) {
player.saveVarp[index] = true
}
}
if (timerData != null)
player.timers.parseTimers(timerData)
}
fun parseSkills() {
@@ -20,8 +20,8 @@ import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.lang.Math.ceil
import java.util.*
import javax.script.ScriptEngineManager
import java.util.*
/**
@@ -644,6 +644,7 @@ class PlayerSaver (val player: Player){
val varpData = JSONArray()
for ((index, value) in player.varpMap) {
if (!(player.saveVarp[index] ?: false)) continue
if (value == 0) continue
val varpObj = JSONObject()
varpObj["index"] = index.toString()
@@ -652,6 +653,10 @@ class PlayerSaver (val player: Player){
}
coreData.put("varp", varpData)
val timerData = JSONObject()
player.timers.saveTimers(timerData)
coreData.put("timers", timerData)
root.put("core_data",coreData)
}
}
@@ -147,22 +147,6 @@ public final class Settings {
setVarp(player, 43, attackStyleIndex);
player.getPacketDispatch().sendRunEnergy();
updateChatSettings();
Pulse pulse = player.getAttribute("energy-restore", null);
if (pulse == null || !pulse.isRunning()) {
pulse = new Pulse(50, player) {
@Override
public boolean pulse() {
if (specialEnergy < 100) {
int heal = 100 - specialEnergy;
setSpecialEnergy(specialEnergy + (heal > 10 ? 10 : heal));
}
return false;
}
};
pulse.setTicksPassed(1);
GameWorld.getPulser().submit(pulse);
player.setAttribute("energy-restore", pulse);
}
}
/**
@@ -2,8 +2,8 @@ package core.game.node.entity.player.link;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.EntityState;
import core.ServerConstants;
import static core.api.ContentAPIKt.*;
import java.util.ArrayList;
import java.util.List;
@@ -81,8 +81,8 @@ public final class SkullManager {
return;
}
skullCauses.add(o);
player.clearState("skull");
player.registerState("skull").init();
removeTimer (player, "skulled");
registerTimer (player, spawnTimer("skulled", 2000));
}
/**
@@ -2,12 +2,12 @@ package core.game.node.entity.player.link.prayer;
import core.game.node.entity.player.link.diary.DiaryType;
import core.game.node.entity.skill.SkillBonus;
import core.game.node.entity.skill.SkillRestoration;
import core.game.node.entity.skill.Skills;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.audio.Audio;
import core.game.world.map.zone.ZoneBorders;
import core.tools.StringUtils;
import core.game.event.*;
import java.util.List;
@@ -245,9 +245,11 @@ public enum PrayerType {
&& new ZoneBorders(2732, 3467, 2739, 3471, 0).insideBorder(player)) {
player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 3);
}
player.dispatch (new PrayerActivatedEvent(this));
} else {
player.getPrayer().getActive().remove(this);
findNextIcon(player);
player.dispatch (new PrayerDeactivatedEvent(this));
}
return true;
}
@@ -1,83 +0,0 @@
package core.game.node.entity.skill;
import core.game.container.impl.EquipmentContainer;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.prayer.PrayerType;
import core.game.node.item.Item;
import org.rs09.consts.Items;
import core.game.world.GameWorld;
/**
* Handles the skill restoration data.
* @author Emperor
*/
public final class SkillRestoration {
/**
* The skill index.
*/
private final int skillId;
/**
* Constructs a new {@code SkillRestoration} {@code Object}.
* @param skillId The skill id.
*/
public SkillRestoration(int skillId) {
this.skillId = skillId;
}
/**
* Restores the skill.
* @param entity The entity.
*/
public void restore(Entity entity) {
Skills skills = entity.getSkills();
int max = skills.getStaticLevel(skillId);
int tickDivisor = 1;
if(skillId == Skills.HITPOINTS){
max = skills.getMaximumLifepoints();
if(entity instanceof Player) {
Player player = entity.asPlayer();
if(player.getPrayer().getActive().contains(PrayerType.RAPID_HEAL)) {
tickDivisor *= 2;
}
Item gloves = player.getEquipment().get(EquipmentContainer.SLOT_HANDS);
if(gloves != null && gloves.getId() == Items.REGEN_BRACELET_11133) {
tickDivisor *= 2;
}
}
}
if(skillId == Skills.PRAYER) {
return;
}
if(skillId != Skills.HITPOINTS && skillId != Skills.SUMMONING) {
if(entity instanceof Player) {
if(entity.asPlayer().getPrayer().getActive().contains(PrayerType.RAPID_RESTORE)) {
tickDivisor *= 2;
}
}
}
int ticks = 100 / tickDivisor;
if(GameWorld.getTicks() % ticks == 0){
if(skillId == Skills.HITPOINTS){
if(skills.getLifepoints() >= max){
return;
}
skills.heal(1);
} else {
int current = skills.getLevel(skillId);
skills.updateLevel(skillId,current < max ? 1 : -1,max);
}
}
}
/**
* Gets the skillId.
* @return The skillId.
*/
public int getSkillId() {
return skillId;
}
}
@@ -104,11 +104,6 @@ public final class Skills {
*/
private double experienceGained = 0;
/**
* The restoration pulses.
*/
private final SkillRestoration[] restoration;
/**
* If a lifepoints update should occur.
*/
@@ -135,7 +130,6 @@ public final class Skills {
this.experience = new double[24];
this.staticLevels = new int[24];
this.dynamicLevels = new int[24];
this.restoration = new SkillRestoration[24];
for (int i = 0; i < 24; i++) {
this.staticLevels[i] = 1;
this.dynamicLevels[i] = 1;
@@ -165,15 +159,6 @@ public final class Skills {
*/
public void configure() {
updateCombatLevel();
int max = 24;
if (entity instanceof NPC) {
max = 7;
}
for (int i = 0; i < max; i++) {
if (i != PRAYER && i != SUMMONING && restoration[i] == null) {
configureRestorationPulse(i);
}
}
}
/**
@@ -183,19 +168,6 @@ public final class Skills {
if (lifepoints < 1) {
return;
}
for (int i = 0; i < restoration.length; i++) {
if (restoration[i] != null) {
restoration[i].restore(entity);
}
}
}
/**
* Configures a restoration pulse for the given skill id.
* @param skillId The skill id.
*/
private void configureRestorationPulse(final int skillId) {
restoration[skillId] = new SkillRestoration(skillId);
}
/**
@@ -883,14 +855,6 @@ public final class Skills {
}
}
/**
* Gets the restoration pulses.
* @return The restoration pulse array.
*/
public SkillRestoration[] getRestoration() {
return restoration;
}
/**
* Gets the amount of mastered skills.
* @return The amount of mastered skills.
@@ -1,66 +0,0 @@
package core.game.node.entity.state;
import core.game.node.entity.state.impl.*;
/**
* Represents the statuses.
* @author Emperor
*/
public enum EntityState {
/**
* The entity is poisoned.
*/
POISONED(new PoisonStatePulse(null)),
/**
* The entity is stunned.
*/
STUNNED(new StunStatePulse(null, 0)),
/**
* The entity is frozen.
*/
FROZEN(new FrozenStatePulse(null, 0)),
/**
* The entity is skulled.
*/
SKULLED(new SkullStatePulse(null, 0)),
/**
* The entity is under teleblock.
*/
TELEBLOCK(new TeleblockStatePulse(null, 0, 0)),
/**
* The entity has decreased weapon speeds.
*/
MIASMIC(new MiasmicStatePulse(null, 0)),
/**
* The entity is healing over time
*/
HEALOVERTIME(new HealOverTimePulse(null,0,0,0,0));
/**
* The state pulse used for this state.
*/
private final StatePulse pulse;
/**
* Constructs a new {@code EntityState} {@code Object}.
* @param pulse The state pulse.
*/
private EntityState(StatePulse pulse) {
this.pulse = pulse;
}
/**
* Gets the pulse.
* @return The pulse.
*/
public StatePulse getPulse() {
return pulse;
}
}
@@ -1,138 +0,0 @@
package core.game.node.entity.state;
import core.game.node.entity.Entity;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
/**
* Handles an entity's status (eg. poisoned, stunned, frozen, skulled, ...)
* @author Emperor
*/
public final class StateManager {
/**
* The entity.
*/
private final Entity entity;
/**
* The entity's current states.
*/
private final Map<EntityState, StatePulse> states = new HashMap<>();
/**
* Constructs a new {@code StateManager} {@code Object}.
* @param entity The entity.
*/
public StateManager(Entity entity) {
this.entity = entity;
}
/**
* Initializes the pulses.
*/
public void init() {
for (StatePulse pulse : states.values()) {
pulse.run();
}
}
/**
* Checks if a save is required.
* @return {@code True} if so.
*/
public boolean isSaveRequired() {
return !states.isEmpty();
}
/**
* Registers a state.
* @param state The state.
* @param args The arguments.
*/
public void set(EntityState state, Object... args) {
register(state, true, args);
}
/**
* Registers a state.
* @param state The state.
* @param override If the previous pulse (if any) should be overriden.
* @param args The arguments.
*/
public void register(EntityState state, boolean override, Object... args) {
if (!state.getPulse().canRun(entity)) {
return;
}
StatePulse pulse = states.get(state);
if (pulse != null) {
if (!override) {
return;
}
pulse.stop();
}
pulse = state.getPulse().create(entity, args);
pulse.run();
states.put(state, pulse);
}
/**
* Resets all the states.
*/
public void reset() {
for (StatePulse pulse : states.values()) {
if (pulse.isRunning()) {
pulse.remove();
pulse.stop();
}
}
states.clear();
}
/**
* Resets the state pulse.
* @param state The state.
*/
public void remove(EntityState state) {
StatePulse pulse = states.get(state);
if (pulse != null && pulse.isRunning()) {
pulse.remove();
pulse.stop();
}
states.remove(state);
}
/**
* Checks if the entity has a pulse running for the given state.
* @param state The state.
* @return {@code True} if so.
*/
public boolean hasState(EntityState state) {
StatePulse pulse = states.get(state);
return pulse != null && pulse.isRunning();
}
/**
* Gets the pulse for the given state.
* @param state The state to get the pulse for.
* @return The state pulse.
*/
public StatePulse get(EntityState state) {
return states.get(state);
}
/**
* Gets the entity.
* @return The entity.
*/
public Entity getEntity() {
return entity;
}
public Map<EntityState, StatePulse> getStates() {
return states;
}
}
@@ -28,7 +28,7 @@ class StateRepository : StartupListener{
fun forKey(key: String, player: Player): State?{
val state = states[key]
if(player.hasActiveState(key)){
return null
return states[key]
}
if(state != null){
val clazz = state.newInstance(player)
@@ -1,95 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.StatePulse;
import core.game.world.GameWorld;
import java.nio.ByteBuffer;
/**
* Handles the frozen state pulse.
* @author Emperor
*/
public final class FrozenStatePulse extends StatePulse {
/**
* The amount of ticks immunity lasts.
*/
private static final int IMMUNITY_TICK = 7;
/**
* The message when frozen.
*/
private final String message;
/**
* Constructs a new {@code FrozenStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The ticks to freeze for.
*/
public FrozenStatePulse(Entity entity, String message, int ticks) {
super(entity, ticks);
this.message = message;
}
/**
* Constructs a new {@code FrozenStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The ticks to freeze for.
*/
public FrozenStatePulse(Entity entity, int ticks) {
this(entity, "You have been frozen!", ticks);
}
@Override
public boolean canRun(Entity entity) {
return entity.getAttribute("freeze_immunity", -1) < GameWorld.getTicks();
}
@Override
public void start() {
super.start();
if (entity.getPulseManager().isMovingPulse()) {
entity.getPulseManager().clear();
}
entity.getWalkingQueue().reset();
entity.getLocks().lockMovement(getDelay());
entity.setAttribute("freeze_immunity", GameWorld.getTicks() + getDelay() + IMMUNITY_TICK);
if (entity instanceof Player) {
((Player) entity).getPacketDispatch().sendMessage(message);
}
}
@Override
public StatePulse create(Entity entity, Object... args) {
if (args.length > 1) {
return new FrozenStatePulse(entity, (String) args[1], (Integer) args[0]);
} else {
return new FrozenStatePulse(entity, (Integer) args[0]);
}
}
@Override
public boolean pulse() {
return true;
}
@Override
public boolean isSaveRequired() {
return false;
}
@Override
public void save(ByteBuffer buffer) {
/*
* empty.
*/
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return null;
}
}
@@ -1,86 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.StatePulse;
import core.game.world.update.flag.context.Graphics;
import java.nio.ByteBuffer;
/**
* Method for healing a player a certain amount of HP
* every X amount of ticks for a total of X amount of heals
* over X amount of ticks.
*
* @author phil lips
*/
public class HealOverTimePulse extends StatePulse {
/**The total amount of HP to heal*/
private int totalToHeal;
/**How many ticks to spread the heals over*/
private int ticksTotal;
/**How many times to heal the player during those ticks*/
private int timesToHeal;
private int currentTick;
/**
* The entity.
*/
protected final Entity entity;
/**Constructs a new HealOverTimePulse object
* @param entity the entity to heal over time
* @param ticks the total time to spread the heal over
* @param totalHeal the total amount to heal for
* @param healInc how many times the total amount to heal is divided to*/
public HealOverTimePulse(Entity entity, int ticks, int totalHeal, int healInc, int currentTick){
super(entity,0);
this.entity = entity;
this.ticksTotal = ticks;
this.totalToHeal = totalHeal;
this.timesToHeal = healInc;
this.currentTick = currentTick;
}
@Override
public StatePulse create(Entity entity, Object... args) {
return new HealOverTimePulse(entity, (Integer) args[1],(Integer) args[2],(Integer) args[3],1);
}
/** Checks if it can heal the player every pulse
* the mod is funny math haha :)
*/
@Override
public boolean pulse() {
if(currentTick != 0){
if(currentTick % (ticksTotal / timesToHeal) == 0){
entity.getSkills().heal(totalToHeal / timesToHeal);
}
}
currentTick += 1;
return currentTick > ticksTotal;
}
@Override
public boolean isSaveRequired() {
return true;
}
@Override
public void save(ByteBuffer buffer) {
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return null;
}
}
@@ -1,90 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.StatePulse;
import core.game.world.GameWorld;
import java.nio.ByteBuffer;
/**
* Handles the Miasmic state pulse.
* @author Emperor
*/
public final class MiasmicStatePulse extends StatePulse {
/**
* The amount of ticks immunity lasts.
*/
private static final int IMMUNITY_TICK = 7;
/**
* The message when frozen.
*/
private final String message;
/**
* Constructs a new {@code MiasmicStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The ticks to freeze for.
*/
public MiasmicStatePulse(Entity entity, String message, int ticks) {
super(entity, ticks);
this.message = message;
}
/**
* Constructs a new {@code MiasmicStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The ticks to freeze for.
*/
public MiasmicStatePulse(Entity entity, int ticks) {
this(entity, null, ticks);
}
@Override
public boolean canRun(Entity entity) {
return entity.getAttribute("miasmic_immunity", -1) < GameWorld.getTicks();
}
@Override
public void start() {
super.start();
entity.setAttribute("miasmic_immunity", GameWorld.getTicks() + getDelay() + IMMUNITY_TICK);
if (entity instanceof Player) {
((Player) entity).getPacketDispatch().sendMessage(message);
}
}
@Override
public StatePulse create(Entity entity, Object... args) {
if (args.length > 1) {
return new MiasmicStatePulse(entity, (String) args[1], (Integer) args[0]);
} else {
return new MiasmicStatePulse(entity, (Integer) args[0]);
}
}
@Override
public boolean pulse() {
return true;
}
@Override
public boolean isSaveRequired() {
return false;
}
@Override
public void save(ByteBuffer buffer) {
/*
* empty.
*/
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return null;
}
}
@@ -1,120 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.ImpactHandler;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.StatePulse;
import java.nio.ByteBuffer;
/**
* Handles the poisoned state.
* @author Emperor
*/
public final class PoisonStatePulse extends StatePulse {
/**
* The current amount of poison damage.
*/
private int damage;
/**
* The entity poisoning this entity.
*/
private Entity poisoner;
/**
* Constructs a new {@code PoisonStatePulse} {@code Object}.
* @param entity The entity.
*/
public PoisonStatePulse(Entity entity) {
super(entity, 30);
}
@Override
public boolean canRun(Entity entity) {
return !entity.isPoisonImmune();
}
@Override
public void start() {
super.start();
if (entity instanceof Player) {
((Player) entity).getPacketDispatch().sendMessage("<col=990000>You have been poisoned!</col>");
}
}
@Override
public boolean pulse() {
if (!poisoner.isActive()) {
poisoner = entity;
}
if (damage / 10 > 0) {
entity.getImpactHandler().manualHit(poisoner, damage / 10, ImpactHandler.HitsplatType.POISON);
}
damage -= 2;
if (damage < 10) {
if (entity instanceof Player) {
((Player) entity).getPacketDispatch().sendMessage("The poison has wore off.");
}
return true;
}
return false;
}
@Override
public boolean isSaveRequired() {
return damage > 9;
}
@Override
public void save(ByteBuffer buffer) {
buffer.put((byte) damage);
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return create(entity, buffer.get() & 0xFF, entity);
}
@Override
public StatePulse create(Entity entity, Object... args) {
PoisonStatePulse pulse = new PoisonStatePulse(entity);
pulse.damage = (Integer) args[0];
pulse.poisoner = (Entity) args[1];
return pulse;
}
/**
* Gets the damage.
* @return The damage.
*/
public int getDamage() {
return damage;
}
/**
* Sets the damage.
* @param damage The damage to set.
*/
public void setDamage(int damage) {
this.damage = damage;
}
/**
* Gets the poisoner.
* @return The poisoner.
*/
public Entity getPoisoner() {
return poisoner;
}
/**
* Sets the poisoner.
* @param poisoner The poisoner to set.
*/
public void setPoisoner(Entity poisoner) {
this.poisoner = poisoner;
}
}
@@ -1,70 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.StatePulse;
import java.nio.ByteBuffer;
/**
* Handles the skulled state.
* @author Emperor
*/
public final class SkullStatePulse extends StatePulse {
/**
* The amount of ticks.
*/
public static final int TICKS = 2000;
/**
* Constructs a new {@code SkullStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The amount of ticks.
*/
public SkullStatePulse(Entity entity, int ticks) {
super(entity, ticks);
}
@Override
public void start() {
super.start();
Player player = (Player) entity;
player.getSkullManager().setSkullIcon(0);
player.getSkullManager().setSkulled(true);
}
@Override
public boolean isSaveRequired() {
return getTicksPassed() < getDelay();
}
@Override
public void save(ByteBuffer buffer) {
buffer.putShort((short) (getDelay() - getTicksPassed()));
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return create(entity, buffer.getShort() & 0xFFFF);
}
@Override
public StatePulse create(Entity entity, Object... args) {
int ticks = args.length > 0 ? (Integer) args[0] : TICKS;
return new SkullStatePulse(entity, ticks);
}
@Override
public boolean pulse() {
setTicksPassed(getDelay());
remove();
return true;
}
@Override
public void remove() {
((Player) entity).getSkullManager().reset();
}
}
@@ -1,96 +0,0 @@
package core.game.node.entity.state.impl;
import core.game.node.entity.Entity;
import core.game.node.entity.impl.Animator;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.audio.Audio;
import core.game.node.entity.state.StatePulse;
import core.game.world.update.flag.context.Animation;
import core.game.world.update.flag.context.Graphics;
import java.nio.ByteBuffer;
/**
* Handles the stunned state.
* @author Emperor
*/
public final class StunStatePulse extends StatePulse {
/**
* The stun graphic.
*/
private static final Graphics STUN_GRAPHIC = new Graphics(80, 96);
private static final Audio STUN_AUDIO = new Audio(2727, 1, 0);
private static final Animation STUN_ANIM = new Animation(424, Animator.Priority.VERY_HIGH);
/**
* The stun message.
*/
private String stunMessage;
/**
* Constructs a new {@code FrozenStatePulse} {@code Object}.
* @param entity The entity.
* @param ticks The ticks to freeze for.
* @param stunMessage The stun message.
*/
public StunStatePulse(Entity entity, int ticks, String stunMessage) {
super(entity, ticks);
this.stunMessage = stunMessage;
}
/**
* Constructs a new @{Code StunStatePulse} object.
* @param entity The entity.
* @param ticks The ticks to freeze for.
*/
public StunStatePulse(Entity entity, int ticks) {
this(entity, ticks, "You have been stunned!");
}
@Override
public void start() {
super.start();
entity.getWalkingQueue().reset();
entity.getLocks().lock(getDelay());
entity.graphics(STUN_GRAPHIC);
if (entity instanceof Player) {
entity.asPlayer().getAudioManager().send(STUN_AUDIO);
entity.animate(STUN_ANIM);
((Player) entity).getPacketDispatch().sendMessage(stunMessage);
}
}
@Override
public StatePulse create(Entity entity, Object... args) {
return new StunStatePulse(entity, (Integer) args[0], args.length > 1 ? (String) args[1] : "You have been stunned!");
}
@Override
public boolean pulse() {
if (entity.getAnimator().getGraphics() == STUN_GRAPHIC) {
entity.graphics(Graphics.create(-1));
}
return true;
}
@Override
public boolean isSaveRequired() {
return false;
}
@Override
public void save(ByteBuffer buffer) {
/*
* empty.
*/
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return null;
}
}
@@ -1,105 +0,0 @@
package core.game.node.entity.state.impl;
import core.api.PersistPlayer;
import core.game.node.entity.Entity;
import core.game.node.entity.player.Player;
import core.game.node.entity.state.EntityState;
import core.game.node.entity.state.StatePulse;
import org.jetbrains.annotations.NotNull;
import org.json.simple.JSONObject;
import java.nio.ByteBuffer;
/**
* Handles the teleblock state pulse.
* @author Vexia
*/
public final class TeleblockStatePulse extends StatePulse implements PersistPlayer {
/**
* The ticks needed to pass.
*/
public int ticks;
/**
* The current tick.
*/
public int currentTick;
/**
* Constructs a new {@Code TeleblockStatePulse} {@Code Object}
* @param entity the entity.
* @param ticks the ticks.
* @param currentTick the current tick.
*/
public TeleblockStatePulse(Entity entity, int ticks, int currentTick) {
super(entity, 1);
this.ticks = ticks;
this.currentTick = currentTick;
}
//Required to be instantiated as a ContentListener for PersistPlayer
public TeleblockStatePulse() {
super(null, 0);
}
@Override
public void savePlayer(@NotNull Player player, @NotNull JSONObject save) {
TeleblockStatePulse tbPulse = (TeleblockStatePulse) player.getStateManager().get(EntityState.TELEBLOCK);
if (tbPulse != null && tbPulse.isSaveRequired()) {
JSONObject tbObj = new JSONObject();
tbObj.put("tb-state-current", "" + tbPulse.currentTick);
tbObj.put("tb-state-total", "" + tbPulse.ticks);
save.put("teleblock-state", tbObj);
}
}
@Override
public void parsePlayer(@NotNull Player player, @NotNull JSONObject data) {
if (data.containsKey("teleblock-state")) {
JSONObject tbData = (JSONObject) data.get("teleblock-state");
int currentTick = Integer.parseInt(tbData.get("tb-state-current").toString());
int totalTicks = Integer.parseInt(tbData.get("tb-state-total").toString());
player.getStateManager().set(EntityState.TELEBLOCK, totalTicks, currentTick);
}
}
@Override
public boolean isSaveRequired() {
return currentTick < ticks;
}
@Override
public void save(ByteBuffer buffer) {
buffer.putInt(ticks);
buffer.putInt(currentTick);
}
@Override
public StatePulse parse(Entity entity, ByteBuffer buffer) {
return new TeleblockStatePulse(entity, buffer.getInt(), buffer.getInt());
}
@Override
public void start() {
if (currentTick == 0) {
if (entity instanceof Player) {
entity.asPlayer().getAudioManager().send(203, true);
entity.asPlayer().sendMessage("You have been teleblocked.");
}
}
super.start();
}
@Override
public boolean pulse() {
return ++currentTick >= ticks;
}
@Override
public StatePulse create(Entity entity, Object... args) {
return new TeleblockStatePulse(entity, (int) args[0], args.length > 1 ? (int) args[1] : 0);
}
}
@@ -27,7 +27,6 @@ import content.global.ame.RandomEvents
import content.region.misthalin.draynor.quest.anma.AnmaCutscene
import core.game.ge.GrandExchange
import content.global.handlers.iface.RulesAndInfo
import content.global.skill.farming.FarmingState
import content.minigame.fishingtrawler.TrawlerLoot
import core.game.system.command.CommandMapping
import core.game.system.command.Privilege
@@ -37,6 +36,7 @@ import core.tools.colorize
import java.awt.HeadlessException
import java.awt.Toolkit
import java.awt.datatransfer.StringSelection
import content.global.skill.farming.timers.*
@Initializable
class MiscCommandSet : CommandSet(Privilege.ADMIN){
@@ -534,19 +534,16 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){
}
define("grow", Privilege.ADMIN, "", "Grows all planted crops by 1 stage."){ player, _ ->
val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define
val state = getOrStartTimer <CropGrowth> (player)!!
for(patch in state.getPatches()){
patch.nextGrowth = System.currentTimeMillis()
patch.nextGrowth = System.currentTimeMillis() - 1
}
state.run (player)
}
define("finishbins", Privilege.ADMIN, "", "Finishes any in-progress compost bins."){ player, _ ->
val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define
for(bin in state.getBins()){
bin.finishedTime = System.currentTimeMillis()
}
}
define("testlady", Privilege.ADMIN){ player, _ ->
@@ -7,6 +7,8 @@ const val STATS_LOGS = "logs_chopped"
const val STATS_FISH = "fish_caught"
const val STATS_ROCKS = "rocks_mined"
const val STATS_RC = "essence_crafted"
const val STATS_PK_KILLS = "player_kills"
const val STATS_PK_DEATHS = "player_deaths"
const val STATS_ALKHARID_GATE = "alkharid_gate"
const val FISHING_TRAWLER_GAMES_WON = "fishing-trawler-games"
const val FISHING_TRAWLER_LEAKS_PATCHED = "fishing-trawler-leaks-patched"
@@ -0,0 +1,23 @@
package core.game.system.timer
import core.api.*
import org.json.simple.*
import core.game.node.entity.Entity
import kotlin.reflect.full.createInstance
/**
* A timer implementation with support for saving and loading arbitrary data. See `RSTimer` for more info on timers themselves.
**/
abstract class PersistTimer (runInterval: Int, identifier: String, isSoft: Boolean = false, isAuto: Boolean = false) : RSTimer (runInterval, identifier, isSoft, isAuto) {
open fun save (root: JSONObject, entity: Entity) {
root["ticksLeft"] = (nextExecution - getWorldTicks()).toString()
}
open fun parse (root: JSONObject, entity: Entity) {
runInterval = root["ticksLeft"].toString().toInt()
}
override fun retrieveInstance() : RSTimer {
return this::class.createInstance()
}
}
@@ -0,0 +1,44 @@
package core.game.system.timer
import core.game.node.entity.Entity
import kotlin.reflect.full.createInstance
/**
* Class for the timer feature of the engine. If you have some task which should repeat periodically, such as applying poison damage, etc, use a timer.
* If the `isAuto` value of a timer is set to `true`, then the timer is automatically added to an entity on creation and started. This is separate from the
* default PersistTimer behavior, which automatically starts the timer only if there's saved data for that timer present. In truth, there's very few
* timers that should have isAuto true.
**/
abstract class RSTimer (var runInterval: Int, val identifier: String = "generictimer", val isSoft: Boolean = false, val isAuto: Boolean = false) {
/**
* Executed every time the run interval of the timer elapses.
* Execution will be delayed if this timer has `isSoft` set to false (which 99% of timers should) if the entity has a modal open or is otherwise stalled.
* @return whether the timer should execute again. If false, timer will be unregistered from the entity and stop executing. If true, timer will be scheduled to repeat once the runInterval elapses again.
**/
abstract fun run (entity: Entity) : Boolean
/**
* Called by core code to determine the amount of time between timer scheduling and the initial (first) run.
* Returns the runInterval by default, but cases such as Farming require an override to sync with realtime clocks.
**/
open fun getInitialRunDelay() : Int { return runInterval }
/**
* Called by core code when the timer is first registered. Called after parse on PersistTimers.
**/
open fun onRegister (entity: Entity) {}
var lastExecution: Int = 0
var nextExecution: Int = 0
open fun retrieveInstance() : RSTimer {
return this::class.createInstance()
}
/**
* This is called only when getTimer is called by further up code with arguments, otherwise retrieveInstance() is called if no arguments are passed.
**/
open fun getTimer (vararg args: Any) : RSTimer {
return retrieveInstance()
}
}
@@ -0,0 +1,124 @@
package core.game.system.timer
import core.api.*
import core.tools.*
import java.util.ArrayList
import org.json.simple.JSONObject
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
class TimerManager (val entity: Entity) {
val activeTimers = ArrayList<RSTimer>()
val newTimers = ArrayList<RSTimer>()
val toRemoveTimers = ArrayList<RSTimer>()
fun registerTimer (timer: RSTimer) {
timer.onRegister(entity)
newTimers.add (timer)
}
fun processTimers () {
activeTimers.removeAll(toRemoveTimers)
newTimers.removeAll(toRemoveTimers)
toRemoveTimers.clear()
val canRunNormalTimers = (entity !is Player) || !(entity.asPlayer().hasModalOpen() || entity.scripts.delay > getWorldTicks())
for (timer in activeTimers) {
if (timer.nextExecution > getWorldTicks()) continue
if (!canRunNormalTimers && !timer.isSoft) continue
if (timer.run(entity)) {
timer.nextExecution = getWorldTicks() + timer.runInterval
} else {
timer.nextExecution = Int.MAX_VALUE
toRemoveTimers.add(timer)
}
}
for (timer in newTimers) {
activeTimers.add(timer)
timer.nextExecution = timer.getInitialRunDelay() + getWorldTicks()
}
newTimers.clear()
}
fun clearTimers () {
activeTimers.clear()
newTimers.clear()
toRemoveTimers.clear()
}
fun saveTimers (root: JSONObject) {
for (timer in activeTimers) {
if (timer !is PersistTimer) continue
val obj = JSONObject()
timer.save(obj, entity)
root [timer.identifier] = obj
}
}
fun parseTimers (root: JSONObject) {
for ((identifier, dataObj) in root) {
val data = dataObj as JSONObject
val timer = TimerRegistry.getTimerInstance (identifier.toString()) as? PersistTimer
if (timer == null) {
log (this::class.java, Log.ERR, "Tried to load data for persistent timer identified by $identifier, but no such timer seems to exist.")
continue
}
timer.parse(data, entity)
registerTimer(entity, timer)
}
}
inline fun <reified T: RSTimer> removeTimer () {
for (timer in activeTimers)
if (timer is T)
toRemoveTimers.add(timer)
for (timer in newTimers)
if (timer is T)
toRemoveTimers.add(timer)
}
inline fun <reified T: RSTimer> getTimer () : T? {
var t: T? = null
for (timer in activeTimers)
if (timer is T)
t = timer
for (timer in newTimers)
if (timer is T)
t = timer
if (t == null) return null
if (toRemoveTimers.contains(t))
return null
return t
}
fun getTimer (identifier: String): RSTimer? {
var t: RSTimer? = null
for (timer in activeTimers)
if (timer.identifier == identifier)
t = timer
for (timer in newTimers)
if (timer.identifier == identifier)
t = timer
if (t == null) return null
if (toRemoveTimers.contains(t)) return null
return t
}
fun removeTimer (identifier: String) {
for (timer in activeTimers)
if (timer.identifier == identifier)
toRemoveTimers.add(timer)
for (timer in newTimers)
if (timer.identifier == identifier)
toRemoveTimers.add(timer)
}
fun removeTimer (timer: RSTimer) {
toRemoveTimers.add(timer)
}
}
@@ -0,0 +1,50 @@
package core.game.system.timer
import java.util.*
import core.api.*
import core.tools.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
object TimerRegistry {
val timerMap = HashMap<String, RSTimer>()
val autoTimers = ArrayList<RSTimer>()
fun registerTimer (timer: RSTimer) {
log (this::class.java, Log.WARN, "Registering timer ${timer::class.java.simpleName}")
if (timerMap.containsKey(timer.identifier.lowercase())) {
log (this::class.java, Log.ERR, "Timer identifier ${timer.identifier} already in use by ${timerMap[timer.identifier.lowercase()]!!::class.java.simpleName}! Not loading ${timer::class.java.simpleName}!")
return
}
timerMap[timer.identifier.lowercase()] = timer
if (timer.isAuto) autoTimers.add(timer)
}
fun getTimerInstance (identifier: String, vararg args: Any) : RSTimer? {
var t = timerMap[identifier.lowercase()]
if (args.size > 0)
return t?.getTimer(*args)
else
return t?.retrieveInstance()
}
@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())
}
}
inline fun <reified T> getTimerInstance (vararg args: Any) : T? {
for ((_, inst) in timerMap)
if (inst is T) {
if (args.size > 0)
return inst.getTimer(*args) as? T
else
return inst.retrieveInstance() as? T
}
return null
}
}
@@ -0,0 +1,46 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.world.repository.Repository
import core.game.node.entity.combat.ImpactHandler
import core.tools.RandomFunction
import org.json.simple.*
class Disease : PersistTimer (30, "disease") {
var hitsLeft = 25
override fun save (root: JSONObject, entity: Entity) {
root["hitsLeft"] = hitsLeft.toString()
}
override fun parse (root: JSONObject, entity: Entity) {
hitsLeft = root["hitsLeft"].toString().toInt()
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<Disease>(entity))
removeTimer(entity, this)
else if (entity is Player)
sendMessage(entity, "You have become diseased.")
}
override fun run (entity: Entity) : Boolean {
var damage = RandomFunction.random(1, 5)
entity.impactHandler.manualHit(entity,damage,ImpactHandler.HitsplatType.DISEASE)
var skillId = RandomFunction.random(24)
if(skillId == 3) skillId--
entity.skills.updateLevel(skillId,-damage,0)
if (--hitsLeft == 0 && entity is Player)
sendMessage(entity, "The disease has wore off.")
return hitsLeft > 0
}
override fun getTimer (vararg args: Any) : RSTimer {
val inst = Disease()
inst.hitsLeft = args.getOrNull(0) as? Int ?: 25
return inst
}
}
@@ -0,0 +1,48 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.world.repository.Repository
import org.json.simple.*
class Frozen : PersistTimer (1, "frozen") {
var shouldApplyImmunity = false
override fun save (root: JSONObject, entity: Entity) {
root["ticksLeft"] = (nextExecution - getWorldTicks()).toString()
root["applyImmunity"] = shouldApplyImmunity
}
override fun parse (root: JSONObject, entity: Entity) {
runInterval = root["ticksLeft"].toString().toInt()
shouldApplyImmunity = root["applyImmunity"] as? Boolean ?: false
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<FrozenImmunity>(entity)) {
removeTimer(entity, this)
return
}
if (hasTimerActive<Frozen>(entity)) {
removeTimer(entity, this)
return
}
}
override fun run (entity: Entity) : Boolean {
if (shouldApplyImmunity) {
registerTimer (entity, spawnTimer<FrozenImmunity>(7))
} else (entity as? Player)?.debug ("Can't apply immunity")
return false
}
override fun getTimer (vararg args: Any) : RSTimer {
val inst = Frozen()
println(args)
inst.runInterval = args.getOrNull(0) as? Int ?: 10
inst.shouldApplyImmunity = args.getOrNull(1) as? Boolean ?: false
return inst
}
}
@@ -0,0 +1,38 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.world.repository.Repository
import org.json.simple.*
class FrozenImmunity : PersistTimer (1, "frozen:immunity") {
var ticksRemaining = 0
override fun save (root: JSONObject, entity: Entity) {
root["ticksLeft"] = (nextExecution - getWorldTicks()).toString()
}
override fun parse (root: JSONObject, entity: Entity) {
runInterval = root["ticksRemaining"].toString().toInt()
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<Frozen>(entity)) {
removeTimer<Frozen>(entity)
}
(entity as? Player)?.debug("Applied frozen immunity for $runInterval ticks.")
}
override fun run (entity: Entity) : Boolean {
(entity as? Player)?.debug("Removed frozen immunity")
return false
}
override fun getTimer (vararg args: Any) : RSTimer {
val inst = FrozenImmunity()
inst.runInterval = args.getOrNull(0) as? Int ?: 7
return inst
}
}
@@ -0,0 +1,47 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import org.json.simple.*
import kotlin.math.min
class HealOverTime : PersistTimer (1, "healovertime") {
var healRemaining = 0
var healPerTick = 0
override fun run (entity: Entity) : Boolean {
var amt = min (healRemaining, healPerTick)
healRemaining -= amt
entity.skills.heal(amt)
return healRemaining > 0
}
override fun save (root: JSONObject, entity: Entity) {
super.save (root, entity)
root["healRemaining"] = healRemaining.toString()
root["healPerTick"] = healPerTick.toString()
}
override fun parse (root: JSONObject, entity: Entity) {
super.parse (root, entity)
healRemaining = root["healRemaining"].toString().toInt()
healPerTick = root["healPerTick"].toString().toInt()
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<MiasmicImmunity>(entity))
removeTimer(entity, this)
if (hasTimerActive<Miasmic>(entity))
removeTimer(entity, this)
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = HealOverTime()
t.runInterval = args.getOrNull(0) as? Int ?: 100
t.healRemaining = args.getOrNull(1) as? Int ?: 10
t.healPerTick = args.getOrNull(2) as? Int ?: 2
return t
}
}
@@ -0,0 +1,27 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import org.json.simple.*
class Miasmic : PersistTimer (1, "miasmic") {
override fun run (entity: Entity) : Boolean {
registerTimer (entity, spawnTimer<MiasmicImmunity>(entity, 7))
return false
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<MiasmicImmunity>(entity))
removeTimer(entity, this)
if (hasTimerActive<Miasmic>(entity))
removeTimer(entity, this)
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = Miasmic()
t.runInterval = args.getOrNull(0) as? Int ?: 100
return t
}
}
@@ -0,0 +1,24 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import org.json.simple.*
class MiasmicImmunity : PersistTimer (1, "miasmic:immunity") {
override fun run (entity: Entity) : Boolean {
return false
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<Miasmic>(entity))
removeTimer<Miasmic>(entity)
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = MiasmicImmunity()
t.runInterval = args.getOrNull(0) as? Int ?: 100
return t
}
}
@@ -0,0 +1,79 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.entity.combat.ImpactHandler
import core.game.world.repository.Repository
import org.json.simple.*
/**
* A timer that replicates the behavior of poison mechanics. Runs every 30 ticks.
* If you wish to construct an instance of this Timer, consider using the ContentAPI function `applyPoison` instead.
* If that doesn't suit your needs, then use `Poison.getTimer (source, severity)` and then `registerTimer(entity, timer)`
* Poison mechanics are driven by the `severity` value, which is not a 1:1 representation of damage. Instead the formula `floor((severity + 4) / 5)` is used.
* Every time the damage is applied, the severity decreases by 1. Poison ends when severity reaches 0.
* Example: 30 Severity. Deals 6 damage 5 times, then 5 damage 5 times, and so on.
**/
class Poison : PersistTimer (30, "poison") {
lateinit var damageSource: Entity
var severity = 0
set (value) {
if (value != field - 1 && value % 10 == 8) {//This was Arios's incorrect attempt at replicating severity, convert it to correct values.
(damageSource as? Player)?.debug ("[PoisonTimer] Warning: Converting suspect Arios severity into true severity. If numbers look wrong, this could be why.")
field = (value / 10) * 5
(damageSource as? Player)?.debug ("[PoisonTimer] Warning: New Severity: $field.")
} else field = value
}
override fun save (root: JSONObject, entity: Entity) {
root["source-uid"] = (damageSource as? Player)?.details?.uid ?: -1
root["severity"] = severity.toString()
}
override fun parse (root: JSONObject, entity: Entity) {
val uid = root["source-uid"].toString().toInt()
severity = root["severity"].toString().toInt()
damageSource = Repository.getPlayerByUid (uid) ?: entity
}
override fun onRegister (entity: Entity) {
if (hasTimerActive<PoisonImmunity>(entity)) {
removeTimer<Poison>(entity)
return
}
if (entity is Player) {
sendMessage(entity, "You have been poisoned.")
entity.debug ("[Poison] -> Received for $severity severity.")
}
if (damageSource is Player)
(damageSource as? Player)?.debug ("[Poison] -> Applied for $severity severity.")
}
override fun run (entity: Entity) : Boolean {
entity.impactHandler.manualHit (
damageSource,
getDamageFromSeverity (severity--),
ImpactHandler.HitsplatType.POISON
)
if (severity == 0 && entity is Player)
sendMessage(entity, "The poison has wore off.")
return severity > 0
}
override fun getTimer (vararg args: Any) : RSTimer {
val timer = Poison()
for (arg in args)
println(arg)
timer.damageSource = args[0] as? Entity ?: return timer
timer.severity = args[1] as? Int ?: return timer
return timer
}
private fun getDamageFromSeverity (severity: Int) : Int {
return (severity + 4) / 5
}
}
@@ -0,0 +1,53 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.tools.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.entity.combat.ImpactHandler
import core.game.world.repository.Repository
import core.game.node.entity.player.link.audio.Audio
import org.json.simple.*
/**
* A timer that replicates the behavior of poison immunity mechanics. Runs every tick.
* Will notify the player of various levels of remaining poison immunity, and then remove itself once it has run out.
* This timer is a "soft" timer, meaning it will tick down even while other timers would normally stall (e.g. during entity delays or when the entity has a modal open.)
**/
class PoisonImmunity : PersistTimer (1, "poison:immunity", isSoft = true) {
var ticksRemaining = 0
override fun save (root: JSONObject, entity: Entity) {
root["ticksRemaining"] = ticksRemaining.toString()
}
override fun parse (root: JSONObject, entity: Entity) {
ticksRemaining = root["ticksRemaining"].toString().toInt()
}
override fun onRegister (entity: Entity) {
removeTimer<Poison>(entity)
}
override fun run (entity: Entity) : Boolean {
ticksRemaining--
if (entity is Player && ticksRemaining == secondsToTicks(30)) {
sendMessage(entity, colorize("%RYou have 30 seconds remaining on your poison immunity."))
playAudio(entity, Audio(3120))
}
else if (entity is Player && ticksRemaining == 0) {
sendMessage(entity, colorize("%RYour poison immunity has expired."))
playAudio(entity, Audio(2607))
}
return ticksRemaining > 0
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = PoisonImmunity()
t.ticksRemaining = args.getOrNull(0) as? Int ?: 100
return t
}
}
@@ -0,0 +1,101 @@
package core.game.system.timer.impl
import core.api.*
import core.api.Event
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.entity.player.link.prayer.PrayerType
import core.game.node.entity.skill.Skills
import core.game.system.timer.*
import core.game.event.*
import org.rs09.consts.Items;
class SkillRestore : RSTimer (1, "skillrestore", isAuto = true, isSoft = true) {
val ticksSinceLastRestore = IntArray (25)
val restoreTicks = IntArray (25) { 100 }
override fun run (entity: Entity) : Boolean {
var skills = entity.skills
for (i in 0 until 24) {
if (i == Skills.PRAYER) continue
if (ticksSinceLastRestore[i]++ >= restoreTicks[i]) {
if (i == Skills.HITPOINTS) {
skills.heal (getHealAmount(entity))
} else {
val max = getStatLevel (entity, i)
val current = getDynLevel (entity, i)
if (current != max)
skills.updateLevel (i, if (current < max) 1 else -1, max);
}
ticksSinceLastRestore[i] = 0
}
}
if (entity is Player && ticksSinceLastRestore[24]++ >= 50) {
entity.settings.setSpecialEnergy (kotlin.math.min (100, entity.settings.specialEnergy + 10))
ticksSinceLastRestore[24] = 0
}
return true
}
override fun onRegister (entity: Entity) {
entity.hook (Event.PrayerActivated, PrayerActivatedHook)
entity.hook (Event.PrayerDeactivated, PrayerDeactivatedHook)
(entity as? Player)?.debug("Registered skill restoration timer.")
}
private fun getHealAmount (entity: Entity) : Int {
if (entity !is Player) return 1
val gloves = getItemFromEquipment (entity, EquipmentSlot.HANDS)
if (gloves == null || gloves.id != Items.REGEN_BRACELET_11133)
return 1
else return 2
}
object PrayerActivatedHook : EventHook <PrayerActivatedEvent> {
override fun process (entity: Entity, event: PrayerActivatedEvent) {
val restore = getOrStartTimer <SkillRestore> (entity)
when (event.type) {
PrayerType.RAPID_HEAL -> {
restore.restoreTicks [Skills.HITPOINTS] -= 50
restore.ticksSinceLastRestore [Skills.HITPOINTS] = 0
}
PrayerType.RAPID_RESTORE -> {
for (i in 0 until 24) {
if (i == Skills.HITPOINTS || i == Skills.PRAYER || i == Skills.SUMMONING) continue
restore.restoreTicks [i] -= 50
restore.ticksSinceLastRestore [i] = 0
}
}
else -> {}
}
}
}
object PrayerDeactivatedHook : EventHook <PrayerDeactivatedEvent> {
override fun process (entity: Entity, event: PrayerDeactivatedEvent) {
val restore = getOrStartTimer <SkillRestore> (entity)
when (event.type) {
PrayerType.RAPID_HEAL -> {
restore.restoreTicks [Skills.HITPOINTS] += 50
restore.ticksSinceLastRestore [Skills.HITPOINTS] = 0
}
PrayerType.RAPID_RESTORE -> {
for (i in 0 until 24) {
if (i == Skills.HITPOINTS || i == Skills.PRAYER || i == Skills.SUMMONING) continue
restore.restoreTicks [i] += 50
restore.ticksSinceLastRestore [i] = 0
}
}
else -> {}
}
}
}
}
@@ -0,0 +1,27 @@
package core.game.system.timer.impl
import core.api.*
import core.game.system.timer.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import org.json.simple.*
class Skulled : PersistTimer (1, "skulled") {
override fun onRegister (entity: Entity) {
if (entity !is Player) return
entity.skullManager.setSkullIcon(0)
entity.skullManager.setSkulled(true)
}
override fun run (entity: Entity) : Boolean {
if (entity !is Player) return false
entity.skullManager.reset()
return false
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = Skulled()
t.runInterval = args.getOrNull(0) as? Int ?: 500
return t
}
}
@@ -0,0 +1,24 @@
package core.game.system.timer.impl
import core.game.system.timer.*
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import org.json.simple.*
class Teleblock : PersistTimer (1, "teleblock") {
override fun run (entity: Entity) : Boolean {
return false
}
override fun onRegister (entity: Entity) {
if (entity !is Player) return
sendMessage (entity, "You have been teleblocked.")
}
override fun getTimer (vararg args: Any) : RSTimer {
val t = Teleblock()
t.runInterval = args.getOrNull(0) as? Int ?: 100
return t
}
}
+2 -1
View File
@@ -169,6 +169,7 @@ object GameWorld {
ConfigParser().prePlugin()
ClassScanner.scanClasspath()
ClassScanner.loadPureInterfaces()
ClassScanner.loadTimers()
val s = worldPersists.filterIsInstance<ServerStore>().first()
s.parse()
worldPersists.filter { it !is ServerStore }.forEach { it.parse() }
@@ -216,4 +217,4 @@ object GameWorld {
generateLocation()
} else random_location
}
}
}
@@ -191,6 +191,11 @@ object Repository {
} else playerNames[name.toLowerCase().replace(" ".toRegex(), "_")]
}
@JvmStatic
fun getPlayerByUid(uid: Int) : Player? {
return uid_map[uid]
}
/**
* Gets the renderableNpcs.
* @return The renderableNpcs.
@@ -198,4 +203,4 @@ object Repository {
@JvmStatic
val renderableNpcs: List<NPC>
get() = RENDERABLE_NPCS
}
}
@@ -13,6 +13,7 @@ import core.game.world.map.zone.ZoneBuilder
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import io.github.classgraph.ScanResult
import core.game.system.timer.*
import core.game.bots.PlayerScripts
import core.game.interaction.InteractionListener
import core.game.interaction.InterfaceListener
@@ -73,6 +74,18 @@ object ClassScanner {
}
}
fun loadTimers () {
scanResults.getSubclasses ("core.game.system.timer.RSTimer").filter { !it.isAbstract }.forEach {
try {
val clazz = it.loadClass().newInstance() as RSTimer
TimerRegistry.registerTimer (clazz)
} catch (e: Exception) {
log(this::class.java, Log.ERR, "Error registering timer instance: ${it.simpleName}")
e.printStackTrace()
}
}
}
private fun loadContentInterfacesFrom(scanResults: ScanResult) {
scanResults.getClassesImplementing("core.api.ContentInterface").filter { !it.isAbstract }.forEach {
try {