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:
@@ -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) {
|
||||
|
||||
@@ -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,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user