diff --git a/Server/src/main/content/global/ame/RandomEventManager.kt b/Server/src/main/content/global/ame/RandomEventManager.kt index 083dd7bf5..5054b110d 100644 --- a/Server/src/main/content/global/ame/RandomEventManager.kt +++ b/Server/src/main/content/global/ame/RandomEventManager.kt @@ -23,7 +23,7 @@ class RandomEventManager(val player: Player? = null) : LoginListener, EventHook< override fun login(player: Player) { if(player.isArtificial) return - val instance = content.global.ame.RandomEventManager(player) + val instance = RandomEventManager(player) player.hook(Event.Tick, instance) setAttribute(player, "random-manager", instance) instance.rollNextSpawn() @@ -66,7 +66,7 @@ class RandomEventManager(val player: Player? = null) : LoginListener, EventHook< } private fun rollNextSpawn() { - nextSpawn = GameWorld.ticks + RandomFunction.random(content.global.ame.RandomEventManager.Companion.MIN_DELAY_TICKS, content.global.ame.RandomEventManager.Companion.MAX_DELAY_TICKS) + nextSpawn = GameWorld.ticks + RandomFunction.random(MIN_DELAY_TICKS, MAX_DELAY_TICKS) } override fun defineCommands() { @@ -77,16 +77,16 @@ class RandomEventManager(val player: Player? = null) : LoginListener, EventHook< if (target == null) reject(player, "Unable to find player $username!") - content.global.ame.RandomEventManager.Companion.getInstance(target!!)?.fireEvent() + getInstance(target!!)?.fireEvent() } } companion object { - const val AVG_DELAY_TICKS = 6000 // 60 minutes - const val MIN_DELAY_TICKS = content.global.ame.RandomEventManager.Companion.AVG_DELAY_TICKS / 2 - const val MAX_DELAY_TICKS = content.global.ame.RandomEventManager.Companion.MIN_DELAY_TICKS + content.global.ame.RandomEventManager.Companion.AVG_DELAY_TICKS // window of 60 min centered on 60 min (30 to 90 min) + var AVG_DELAY_TICKS = 6000 // 60 minutes + var MIN_DELAY_TICKS = AVG_DELAY_TICKS / 2 + var MAX_DELAY_TICKS = MIN_DELAY_TICKS + AVG_DELAY_TICKS // window of 60 min centered on 60 min (30 to 90 min) - @JvmStatic fun getInstance(player: Player): content.global.ame.RandomEventManager? + @JvmStatic fun getInstance(player: Player): RandomEventManager? { return getAttribute(player, "random-manager", null) } diff --git a/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java index 249b02ce9..8dfb2ef0e 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java @@ -39,14 +39,18 @@ public class BloatedLeechNPC extends Familiar { @Override protected boolean specialMove(FamiliarSpecial special) { - curePoison(owner); + curePoison(owner); + removeTimer(owner, "disease"); for (int i = 0; i < Skills.SKILL_NAME.length; i++) { if (owner.getSkills().getLevel(i) < owner.getSkills().getStaticLevel(i)) { - owner.getSkills().setLevel(i, owner.getSkills().getStaticLevel(i)); + owner.getSkills().updateLevel( + i, + (int) Math.ceil(owner.getSkills().getStaticLevel(i) * 0.2), + owner.getSkills().getStaticLevel(i) + ); } } - owner.getSkills().rechargePrayerPoints(); - getImpactHandler().manualHit(owner, RandomFunction.random(2), HitsplatType.NORMAL); + owner.getImpactHandler().manualHit(owner, RandomFunction.random(1, 5), HitsplatType.NORMAL); return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java index 4a20aed33..2e0f5be84 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java +++ b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java @@ -347,26 +347,26 @@ public abstract class Familiar extends NPC implements Plugin { * Checks if the familiar can execute its special move and does so if able. * @param special The familiar special object. */ - public void executeSpecialMove(FamiliarSpecial special) { + public boolean executeSpecialMove(FamiliarSpecial special) { if (special.getNode() == this) { - return; + return false; } if (specialCost > specialPoints) { owner.getPacketDispatch().sendMessage("Your familiar does not have enough special move points left."); - return; + return false; } SummoningScroll scroll = SummoningScroll.forPouch(pouchId); if (scroll == null) { owner.getPacketDispatch().sendMessage("Invalid scroll for pouch " + pouchId + " - report!"); - return; + return false; } if (!owner.getInventory().contains(scroll.getItemId(), 1)) { owner.getPacketDispatch().sendMessage("You do not have enough scrolls left to do this special move."); - return; + return false; } if (owner.getLocation().getDistance(getLocation()) > 15) { owner.getPacketDispatch().sendMessage("Your familiar is too far away to use that scroll, or it cannot see you."); - return; + return false; } if (specialMove(special)) { setAttribute("special-delay", GameWorld.getTicks() + 3); @@ -376,6 +376,7 @@ public abstract class Familiar extends NPC implements Plugin { updateSpecialPoints(specialCost); owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); } + return true; } /** diff --git a/Server/src/test/kotlin/TestUtils.kt b/Server/src/test/kotlin/TestUtils.kt index a65c69a74..1d994315f 100644 --- a/Server/src/test/kotlin/TestUtils.kt +++ b/Server/src/test/kotlin/TestUtils.kt @@ -1,3 +1,4 @@ +import content.global.ame.RandomEventManager import core.cache.Cache import core.cache.crypto.ISAACCipher import core.cache.crypto.ISAACPair @@ -12,13 +13,18 @@ import org.rs09.consts.Items import core.ServerConstants import core.api.log import core.game.node.Node +import core.game.node.entity.combat.equipment.WeaponInterface import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.SkillBonus import core.game.node.item.GroundItem import core.game.shops.Shop import core.game.shops.ShopItem import core.tools.SystemLogger import core.game.system.config.ConfigParser import core.game.system.config.ServerConfigParser +import core.game.system.timer.TimerRegistry +import core.game.system.timer.impl.Disease +import core.game.system.timer.impl.Poison import core.game.world.GameWorld import core.game.world.map.Location import core.game.world.map.RegionManager @@ -70,9 +76,15 @@ object TestUtils { Cache.init(this::class.java.getResource("cache").path.toString()) ConfigParser().prePlugin() ConfigParser().postPlugin() + registerTimers() } } + fun registerTimers() { //allow timers to be registered for use by tests + TimerRegistry.registerTimer(Poison()) + TimerRegistry.registerTimer(Disease()) + } + fun loadFile(path: String) : URI? { return this::class.java.getResource(path)?.toURI() } @@ -99,7 +111,10 @@ class MockPlayer(name: String) : Player(PlayerDetails(name)), AutoCloseable { var hasInit = false init { this.details.session = MockSession() + this.location = ServerConstants.HOME_LOCATION + this.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_CRUSH) init() + this.setAttribute("tutorial:complete", true) } override fun update() { diff --git a/Server/src/test/kotlin/content/RandomEventManagerTests.kt b/Server/src/test/kotlin/content/RandomEventManagerTests.kt index a6f1cffde..81e700696 100644 --- a/Server/src/test/kotlin/content/RandomEventManagerTests.kt +++ b/Server/src/test/kotlin/content/RandomEventManagerTests.kt @@ -1,6 +1,7 @@ package content import TestUtils +import content.global.ame.RandomEventManager import core.game.node.item.Item import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -9,13 +10,13 @@ import content.global.ame.RandomEventNPC import core.game.world.GameWorld class RandomEventManagerTests { - companion object {init { + init { TestUtils.preTestSetup() - }} + } @Test fun loginShouldEnableManager() { val p = TestUtils.getMockPlayer("Bill") - content.global.ame.RandomEventManager().login(p) + RandomEventManager().login(p) val manager = content.global.ame.RandomEventManager.getInstance(p) Assertions.assertNotNull(manager) Assertions.assertEquals(true, manager!!.enabled) @@ -23,7 +24,7 @@ class RandomEventManagerTests { @Test fun loginShouldSetNextSpawn() { val p = TestUtils.getMockPlayer("Bill") - content.global.ame.RandomEventManager().login(p) + RandomEventManager().login(p) val manager = content.global.ame.RandomEventManager.getInstance(p) Assertions.assertNotNull(manager) Assertions.assertEquals(true, manager!!.nextSpawn > GameWorld.ticks) @@ -32,15 +33,19 @@ class RandomEventManagerTests { @Test fun shouldSpawnRandomEventWithinMAXTICKSGivenNoRestrictions() { val p = TestUtils.getMockPlayer("Bill") p.setAttribute("tutorial:complete", true) //tutorial has to be complete to spawn randoms - content.global.ame.RandomEventManager().login(p) - TestUtils.advanceTicks(content.global.ame.RandomEventManager.MAX_DELAY_TICKS + 5) + RandomEventManager.MIN_DELAY_TICKS = 10 + RandomEventManager.MAX_DELAY_TICKS = 20 + RandomEventManager().login(p) + TestUtils.advanceTicks(RandomEventManager.MAX_DELAY_TICKS + 5) + RandomEventManager.MIN_DELAY_TICKS = 3000 + RandomEventManager.MAX_DELAY_TICKS = 9000 Assertions.assertNotNull(p.getAttribute("re-npc", null)) } @Test fun teleportAndNotePunishmentShouldNotAffectAlreadyNotedItems() { val p = TestUtils.getMockPlayer("Shitforbrains") p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) + RandomEventManager().login(p) p.inventory.add(Item(Items.RAW_SHARK_384, 1000)) content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() @@ -52,7 +57,7 @@ class RandomEventManagerTests { @Test fun teleportAndNotePunishmentShouldNoteNotableUnnotedItems() { val p = TestUtils.getMockPlayer("shitforbrains2") p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) + RandomEventManager().login(p) p.inventory.add(Item(4151, 5)) content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() @@ -65,7 +70,7 @@ class RandomEventManagerTests { @Test fun teleportAndNotePunishmentShouldNotAffectUnnotableItems() { val p = TestUtils.getMockPlayer("shitforbrains3") p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) + RandomEventManager().login(p) p.inventory.add(Item(Items.AIR_RUNE_556, 30)) content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() diff --git a/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt b/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt new file mode 100644 index 000000000..93d585e3f --- /dev/null +++ b/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt @@ -0,0 +1,117 @@ +package content.familiar.special + +import TestUtils +import content.global.skill.summoning.familiar.BloatedLeechNPC +import content.global.skill.summoning.familiar.FamiliarSpecial +import core.ServerConstants +import core.api.* +import core.game.node.entity.skill.Skills +import core.game.system.timer.TimerRegistry +import core.game.system.timer.impl.Disease +import core.game.system.timer.impl.Poison +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class BloodDrainTests { + init { + TestUtils.preTestSetup() + } + @Test fun bloodDrainShouldNotRestorePrayer() { + TestUtils.getMockPlayer("bloodDrainPrayer").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 5.0 + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(5.0, p.skills.prayerPoints) + } + } + + @Test fun bloodDrainShouldDamageOwner() { + TestUtils.getMockPlayer("bloodDrainHealth").use {p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + val npcBaseline = npc.skills.lifepoints + val ownerBaseline = p.skills.lifepoints + + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + TestUtils.advanceTicks(1, false) + + Assertions.assertEquals(npcBaseline, npc.skills.lifepoints) + Assertions.assertEquals(true, p.skills.lifepoints < ownerBaseline) + } + } + + @Test fun bloodDrainShouldNotHeal() { + TestUtils.getMockPlayer("bloodDrainHealth2").use {p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + p.skills.lifepoints = 5 + + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + val ownerBaseline = p.skills.lifepoints + + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + TestUtils.advanceTicks(1, false) + + Assertions.assertEquals(true, p.skills.lifepoints < ownerBaseline) + } + } + + @Test fun bloodDrainShouldOnlyRestorePercentOfStats() { + TestUtils.getMockPlayer("bloodDrainStats").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + p.skills.setStaticLevel(Skills.STRENGTH, 99) + p.skills.setStaticLevel(Skills.FARMING, 99) + p.skills.setLevel(Skills.STRENGTH, 1) + p.skills.setLevel(Skills.FARMING, 1) + + Assertions.assertEquals(true,npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(21, p.skills.getLevel(Skills.STRENGTH)) + Assertions.assertEquals(21, p.skills.getLevel(Skills.FARMING)) + } + } + + @Test fun bloodDrainShouldNotBoostStats() { + TestUtils.getMockPlayer("bloodDrainStats2").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + p.skills.setStaticLevel(Skills.STRENGTH, 99) + p.skills.setStaticLevel(Skills.FARMING, 99) + p.skills.setLevel(Skills.STRENGTH, 98) + p.skills.setLevel(Skills.FARMING, 98) + + Assertions.assertEquals(true,npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(99, p.skills.getLevel(Skills.STRENGTH)) + Assertions.assertEquals(99, p.skills.getLevel(Skills.FARMING)) + } + } + + @Test fun bloodDrainCuresPoisonAndDisease() { + TestUtils.getMockPlayer("bloodDrainAilments").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + applyPoison(p, p, 40) + Assertions.assertNotNull(getOrStartTimer(p, 40)) + Assertions.assertEquals(true, hasTimerActive(p)) + Assertions.assertEquals(true, hasTimerActive(p)) + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(false, hasTimerActive(p)) + Assertions.assertEquals(false, hasTimerActive(p)) + } + } +} \ No newline at end of file