diff --git a/Server/src/main/java/core/game/content/activity/duel/DuelSession.java b/Server/src/main/java/core/game/content/activity/duel/DuelSession.java index 7906a540f..dc7bf43d3 100644 --- a/Server/src/main/java/core/game/content/activity/duel/DuelSession.java +++ b/Server/src/main/java/core/game/content/activity/duel/DuelSession.java @@ -17,6 +17,7 @@ import core.plugin.Plugin; import core.tools.RandomFunction; import kotlin.Unit; import rs09.game.content.global.action.EquipHandler; +import rs09.game.node.entity.player.info.LogType; import rs09.game.node.entity.player.info.PlayerMonitor; import rs09.game.system.config.ItemConfigParser; @@ -311,7 +312,7 @@ public final class DuelSession extends ComponentPlugin { log = log.substring(0, log.length() - 1); } log += "}"; - PlayerMonitor.logMisc(player, "Duel", log); + PlayerMonitor.log(player, LogType.DUEL_INFO, log); } else { player.removeExtension(DuelSession.class); } diff --git a/Server/src/main/java/core/game/content/activity/mta/MTAShop.java b/Server/src/main/java/core/game/content/activity/mta/MTAShop.java index e1e744ea6..de454fd4a 100644 --- a/Server/src/main/java/core/game/content/activity/mta/MTAShop.java +++ b/Server/src/main/java/core/game/content/activity/mta/MTAShop.java @@ -1,5 +1,6 @@ package core.game.content.activity.mta; +import api.ContainerisedItem; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.component.ComponentPlugin; @@ -15,6 +16,8 @@ import rs09.game.world.GameWorld; import java.util.ArrayList; import java.util.List; +import static api.ContentAPIKt.hasAnItem; + /** * Represents the mage training arena shop. * @author Vexia @@ -147,9 +150,11 @@ public class MTAShop { player.sendMessage("You already unlocked that spell."); return; } + ContainerisedItem itemToRemove = null; if (slot >= 1 && slot <= 3) { Item required = ITEMS[slot - 1]; - if (!player.hasItem(new Item(required.getId(), 1)) && !player.hasItem(new Item(6914, 1))) { + itemToRemove = hasAnItem(player, required.getId()); + if (!itemToRemove.exists() && !player.hasItem(new Item(6914, 1))) { player.sendMessage("You don't have the required wand in order to buy this upgrade."); return; } @@ -163,7 +168,8 @@ public class MTAShop { player.getSavedData().getActivityData().setBonesToPeaches(true); player.getDialogueInterpreter().sendDialogue("The guardian teaches you how to use the Bones to Peaches spell!"); } else { - player.getInventory().add(item); + if (itemToRemove == null || itemToRemove.remove()) + player.getInventory().add(item); } for (int i = 0; i < prices.length; i++) { decrementPoints(player, i, prices[i]); diff --git a/Server/src/main/java/core/game/content/consumable/Consumables.java b/Server/src/main/java/core/game/content/consumable/Consumables.java index 7c0d325ab..c14e32161 100644 --- a/Server/src/main/java/core/game/content/consumable/Consumables.java +++ b/Server/src/main/java/core/game/content/consumable/Consumables.java @@ -7,6 +7,7 @@ import core.game.world.update.flag.context.Animation; import core.game.content.consumable.effects.*; import core.game.node.entity.skill.Skills; import rs09.game.content.consumable.effects.KegOfBeerEffect; +import rs09.game.content.consumable.effects.RestoreSummoningSpecial; import java.util.HashMap; @@ -321,7 +322,7 @@ public enum Consumables { HUNTER(new Potion(new int[] {9998, 10000, 10002, 10004}, new SkillEffect(Skills.HUNTER, 3, 0))), RESTORE(new Potion(new int[] {2430, 127, 129, 131}, new RestoreEffect(10, 0.3))), SARA_BREW(new Potion(new int[] {6685, 6687, 6689, 6691}, new MultiEffect(new PercentHeal(2, .15), new SkillEffect(Skills.ATTACK, 0, -0.10), new SkillEffect(Skills.STRENGTH, 0, -0.10), new SkillEffect(Skills.MAGIC, 0, -0.10), new SkillEffect(Skills.RANGE, 0, -0.10), new SkillEffect(Skills.DEFENCE, 2, 0.2)))), - SUMMONING(new Potion(new int[] {12140, 12142, 12144, 12146}, new SummoningEffect(7, 0.25))), + SUMMONING(new Potion(new int[] {12140, 12142, 12144, 12146}, new MultiEffect(new RestoreSummoningSpecial(), new SummoningEffect(7, 0.25)))), COMBAT(new Potion(new int[] {9739, 9741, 9743, 9745}, new MultiEffect(new SkillEffect(Skills.STRENGTH, 3, .1), new SkillEffect(Skills.ATTACK, 3, .1)))), ENERGY(new Potion(new int[] {3008, 3010, 3012, 3014}, new MultiEffect(new EnergyEffect(10), new HealingEffect(3)))), FISHING(new Potion(new int[] {2438, 151, 153, 155}, new SkillEffect(Skills.FISHING, 3, 0))), diff --git a/Server/src/main/java/core/game/node/entity/Entity.java b/Server/src/main/java/core/game/node/entity/Entity.java index cc939e4f2..650198e9c 100644 --- a/Server/src/main/java/core/game/node/entity/Entity.java +++ b/Server/src/main/java/core/game/node/entity/Entity.java @@ -338,6 +338,8 @@ public abstract class Entity extends Node { * @param state The battle state. */ public void onImpact(final Entity entity, BattleState state) { + if (DeathTask.isDead(this)) + state.neutralizeHits(); if (properties.isRetaliating() && !properties.getCombatPulse().isAttacking() && !getLocks().isInteractionLocked() && properties.getCombatPulse().getNextAttack() < GameWorld.getTicks()) { if (!getWalkingQueue().hasPath() && !getPulseManager().isMovingPulse() || (this instanceof NPC)) { properties.getCombatPulse().attack(entity); diff --git a/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java b/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java index ee9126af5..8a9b1b84e 100644 --- a/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java +++ b/Server/src/main/java/core/game/node/entity/combat/ImpactHandler.java @@ -138,6 +138,11 @@ public final class ImpactHandler { * @return The impact object created. */ public Impact handleImpact(Entity source, int hit, final CombatStyle style, final BattleState state, HitsplatType type, boolean secondary) { + if (DeathTask.isDead(this.entity)) { + this.entity.getProperties().getCombatPulse().setVictim(null); + this.entity.getProperties().getCombatPulse().stop(); + return null; + } boolean fam = source instanceof Familiar; if (fam) { source = ((Familiar) source).getOwner(); diff --git a/Server/src/main/java/core/game/node/entity/npc/NPC.java b/Server/src/main/java/core/game/node/entity/npc/NPC.java index 0a60ea68e..204700146 100644 --- a/Server/src/main/java/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/NPC.java @@ -394,6 +394,8 @@ public class NPC extends Entity { return; } if (respawnTick > GameWorld.getTicks()) { + if (respawnTick - GameWorld.getTicks() == 2) + teleport(getProperties().getSpawnLocation()); return; } if (respawnTick == GameWorld.getTicks()) { @@ -522,9 +524,8 @@ public class NPC extends Entity { GlobalKillCounter.incrementKills(p, originalId); } handleDrops(p, killer); - if (!isRespawn()) { + if (!isRespawn()) clear(); - } killer.dispatch(new NPCKillEvent(this)); } diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index 5f303f03c..72e7e677d 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -65,13 +65,12 @@ import proto.management.ClanLeaveNotification; import proto.management.PlayerStatusUpdate; import rs09.GlobalStats; import rs09.ServerConstants; -import rs09.game.Varp; import rs09.game.VarpManager; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.equipment.EquipmentDegrader; import rs09.game.node.entity.player.graves.Grave; -import rs09.game.node.entity.player.graves.GraveType; import rs09.game.node.entity.player.graves.GraveController; +import rs09.game.node.entity.player.info.LogType; import rs09.game.node.entity.player.info.PlayerMonitor; import rs09.game.node.entity.player.info.login.PlayerSaver; import rs09.game.node.entity.skill.runecrafting.PouchManager; @@ -654,7 +653,7 @@ public class Player extends Entity { GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); } if (killer instanceof Player) - PlayerMonitor.logMisc((Player) killer, "PK", "Killed " + name + ", who dropped: " + itemsLost); + PlayerMonitor.log((Player) killer, LogType.PK, "Killed " + name + ", who dropped: " + itemsLost); sendMessage(colorize("%RDue to the circumstances of your death, you do not have a grave.")); } diff --git a/Server/src/main/java/core/game/system/task/NodeTask.java b/Server/src/main/java/core/game/system/task/NodeTask.java index 1780d2fdb..72347b8a8 100644 --- a/Server/src/main/java/core/game/system/task/NodeTask.java +++ b/Server/src/main/java/core/game/system/task/NodeTask.java @@ -103,6 +103,7 @@ public abstract class NodeTask { return NodeTask.this.removeFor(s, node, n); } }; + pulse.start(); return pulse; } diff --git a/Server/src/main/java/core/game/world/map/RegionManager.kt b/Server/src/main/java/core/game/world/map/RegionManager.kt index 2727af5f6..cc5ffc2d8 100644 --- a/Server/src/main/java/core/game/world/map/RegionManager.kt +++ b/Server/src/main/java/core/game/world/map/RegionManager.kt @@ -603,8 +603,17 @@ object RegionManager { val it = npcs.iterator() while (it.hasNext()) { val p = it.next() + if(p.properties.teleportLocation != null && !p.properties.teleportLocation.withinMaxnormDistance(n.location, 1)) { + it.remove() + continue + } + if(p.getAttribute("state:death", false)) { + it.remove() + continue + } if(p.isInvisible()) { it.remove() + continue } if(!p.location.withinMaxnormDistance(n.location, 1)) { it.remove() diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt index 285651477..a382ff85b 100644 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ b/Server/src/main/kotlin/api/ContentAPI.kt @@ -55,6 +55,8 @@ import rs09.game.content.global.shops.Shops import rs09.game.ge.GrandExchangeRecords import rs09.game.interaction.InteractionListeners import rs09.game.interaction.inter.ge.StockMarket +import rs09.game.node.entity.player.info.LogType +import rs09.game.node.entity.player.info.PlayerMonitor import rs09.game.node.entity.skill.slayer.SlayerManager import rs09.game.system.SystemLogger import rs09.game.system.config.ItemConfigParser @@ -164,7 +166,14 @@ fun areAnyEquipped(player: Player, vararg ids: Int): Boolean { } } -data class ContainerisedItem(val container: core.game.container.Container?, val itemId: Int) +class ContainerisedItem(val container: core.game.container.Container?, val itemId: Int) { + fun remove() : Boolean { + return this.container?.remove(this.itemId.asItem()) ?: false + } + fun exists() : Boolean { + return this.container != null && this.itemId > -1 + } +} /** * Check if player has any of the specified item IDs equipped, in inventory, or in banks @@ -239,14 +248,27 @@ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Con * @param container the Container to modify * @return the item that was previously in the slot, or null if none. */ -fun replaceSlot(player: Player, slot: Int, item: Item, container: Container = Container.INVENTORY): Item? { +fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null, container: Container = Container.INVENTORY): Item? { val cont = when (container) { Container.INVENTORY -> player.inventory Container.EQUIPMENT -> player.equipment Container.BANK -> player.bank } - return cont.replace(item, slot) + if (currentItem == null) { + return cont.replace(item, slot) + } + + if (cont.remove(currentItem)) + return cont.replace(item, slot) + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential slot-replacement-based dupe attempt, slot: $slot, item: $item") + val other = when (container) { + Container.INVENTORY -> Container.EQUIPMENT + else -> Container.INVENTORY + } + if (removeItem(player, currentItem, other)) + return cont.replace(item, slot) + return null } /** diff --git a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt index a9a512f4c..86f8892bc 100644 --- a/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt +++ b/Server/src/main/kotlin/rs09/game/content/ame/events/supriseexam/SurpriseExamUtils.kt @@ -1,6 +1,7 @@ package rs09.game.content.ame.events.supriseexam import api.* +import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player import core.game.node.item.GroundItemManager import core.game.node.item.Item @@ -51,7 +52,7 @@ object SurpriseExamUtils { } return true } - }) + }, PulseType.CUSTOM_1) } fun generateInterface(player: Player){ diff --git a/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt b/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt deleted file mode 100644 index aa48b16a8..000000000 --- a/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoning.kt +++ /dev/null @@ -1,13 +0,0 @@ -package rs09.game.content.consumable.effects - -import core.game.node.entity.player.Player -import core.game.content.consumable.ConsumableEffect -import core.game.node.entity.skill.Skills - -class RestoreSummoning(val percent: Double) : ConsumableEffect(){ - override fun activate(p: Player?) { - var amt = (p!!.skills!!.getStaticLevel(Skills.SUMMONING) * percent).toInt() - if(amt + p.skills.getLevel(Skills.SUMMONING) > p.skills.getStaticLevel(Skills.SUMMONING)) amt = p.skills.getStaticLevel(Skills.SUMMONING) - p.skills.getLevel(Skills.SUMMONING) - p.skills.setLevel(Skills.SUMMONING, p.skills.getLevel(Skills.SUMMONING) + amt) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoningSpecial.kt b/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoningSpecial.kt new file mode 100644 index 000000000..491d55a1a --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/consumable/effects/RestoreSummoningSpecial.kt @@ -0,0 +1,11 @@ +package rs09.game.content.consumable.effects + +import core.game.content.consumable.ConsumableEffect +import core.game.node.entity.player.Player + +class RestoreSummoningSpecial : ConsumableEffect(){ + override fun activate(p: Player) { + val f = p.familiarManager.familiar + f?.updateSpecialPoints(-15) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/alkharid/AlKharidShopKeeperDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/alkharid/AlKharidShopKeeperDialogue.kt index b67679281..02bfeb758 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/alkharid/AlKharidShopKeeperDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/alkharid/AlKharidShopKeeperDialogue.kt @@ -14,7 +14,7 @@ import rs09.tools.END_DIALOGUE */ @Initializable -class AlKharidShopKeeperDialogue(player: Player? = null) : DialoguePlugin() { +class AlKharidShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { override fun newInstance(player: Player?): DialoguePlugin { return AlKharidShopKeeperDialogue(player) } diff --git a/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewellery.kt b/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewellery.kt index c5e879f32..c91d84341 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewellery.kt +++ b/Server/src/main/kotlin/rs09/game/content/global/EnchantedJewellery.kt @@ -249,7 +249,7 @@ enum class EnchantedJewellery( private fun replaceJewellery(player: Player, item: Item, nextJewellery: Item, isEquipped: Boolean) { if (isEquipped) { - replaceSlot(player, item.slot, nextJewellery, Container.EQUIPMENT) + replaceSlot(player, item.slot, nextJewellery, item, Container.EQUIPMENT) } else { replaceSlot(player, item.slot, nextJewellery) } diff --git a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt index 32d2f5118..8ec35a606 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt +++ b/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt @@ -6,14 +6,13 @@ import core.game.content.dialogue.FacialExpression import core.game.content.global.GodType import core.game.node.entity.player.Player import core.game.node.entity.player.link.audio.Audio -import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.runecrafting.RunePouch import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items +import rs09.game.node.entity.player.info.LogType import rs09.game.node.entity.player.info.PlayerMonitor import rs09.game.system.SystemLogger import rs09.game.system.config.GroundSpawnLoader @@ -58,7 +57,7 @@ object PickupHandler { } if (item.isActive && player.inventory.add(add)) { if (item.dropper is Player && item.dropper.details.uid != player.details.uid){ - PlayerMonitor.logMisc(item.dropper, "DropTrade", "${getItemName(item.id)} x${item.amount} picked up by ${player.name}.") + PlayerMonitor.log(item.dropper, LogType.DROP_TRADE, "${getItemName(item.id)} x${item.amount} picked up by ${player.name}.") } if (!RegionManager.isTeleportPermitted(item.location)) { player.animate(Animation.create(535)) diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt index 4d0f49d3f..2dd206daf 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt @@ -83,7 +83,9 @@ class ItemQuestRequirementListener : InteractionListener { Items.ZAMORAK_PLATEBODY_2653, Items.SARADOMIN_PLATEBODY_2661, Items.GUTHIX_PLATEBODY_2669, - Items.GREEN_DHIDE_BODY_1135 + Items.GREEN_DHIDE_BODY_1135, + Items.DHIDE_BODYG_7370, //Green (g) + Items.DHIDE_BODY_T_7372 //Green (t) ) private val questCapes = intArrayOf(9813,9814) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt index 58ccb9d39..3c7356293 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/combat/CombatPulse.kt @@ -437,7 +437,7 @@ class CombatPulse( GameWorld.Pulser.submit(object : Pulse(delay - 1, entity, victim) { var impact = false override fun pulse(): Boolean { - if (DeathTask.isDead(victim)) { + if (DeathTask.isDead(victim) || DeathTask.isDead(entity)) { return true } if (impact || getDelay() == 0) { diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/PlayerMonitor.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/PlayerMonitor.kt index 63ecac0c8..d5a6c3880 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/PlayerMonitor.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/info/PlayerMonitor.kt @@ -81,11 +81,11 @@ object PlayerMonitor { dispatch(event) } - @JvmStatic fun logMisc(player: Player, type: String, details: String) { + @JvmStatic fun log(player: Player, type: LogType, details: String) { val event = LogEvent.MiscLog( player.name, player.details.uid, - type, + type.token, details, System.currentTimeMillis() ) @@ -253,4 +253,13 @@ object PlayerMonitor { private const val MISC_LOG_INSERT = "INSERT INTO misc_logs(player,uid,type,details,timestamp) VALUES (?,?,?,?,?);" private const val WEALTH_LOG_INSERT = "INSERT INTO wealth_logs(player,uid,total,diff,timestamp) VALUES (?,?,?,?,?);" +} + +enum class LogType(val token: String) { + DUPE_ALERT("dupe_warning"), + DUEL_INFO("Duel"), + PK("PK"), + DROP_TRADE("DropTrade"), + COMMAND("CommandUsed"), + IP_LOG("login_ip") } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt index dfa79ead6..fc1bc6f6c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/farming/Patch.kt @@ -317,6 +317,16 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl return getCurrentState() - plantable!!.value - plantable!!.stages } + fun getStageGrowthMinutes() : Int { + var minutes = patch.type.stageGrowthTime + if(patch.type == PatchType.FRUIT_TREE && isGrown()) { + // Fruit trees take 160 minutes per stage to grow, but + // restocking their fruit should take 40 minutes per fruit + minutes = 40 + } + return minutes + } + fun isFlowerProtected(): Boolean{ if(patch.type != PatchType.ALLOTMENT) return false diff --git a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt index 02abb37af..1784eae2c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/state/newsys/states/FarmingState.kt @@ -1,5 +1,6 @@ package rs09.game.node.entity.state.newsys.states +import core.Util.clamp import core.game.node.entity.player.Player import core.game.system.task.Pulse import kotlinx.coroutines.GlobalScope @@ -101,20 +102,6 @@ class FarmingState(player: Player? = null) : State(player) { patch.compost = CompostType.values()[compostOrdinal] patch.protectionPaid = protectionPaid patch.setCurrentState(savedState) - if((patch.currentGrowthStage < patch.plantable?.stages ?: 0) && !patch.isWeedy()){ - val startTime = (patch.nextGrowth - TimeUnit.MINUTES.toMillis(patch.patch.type.stageGrowthTime * (patch.currentGrowthStage + 1).toLong())) - var expectedStage = Math.floor((System.currentTimeMillis() - startTime.toDouble()) / TimeUnit.MINUTES.toMillis(patch.patch.type.stageGrowthTime.toLong())).toInt() - SystemLogger.logErr(this::class.java, "$expectedStage $startTime ${System.currentTimeMillis()}") - - if(!patchDiseased && !patchDead) { - if (expectedStage > patch.plantable?.stages ?: 0) { - expectedStage = patch.plantable?.stages ?: 0 - } - for(i in 0 until (expectedStage - patch.currentGrowthStage)){ - patch.update() - } - } - } if((savedState - (patch?.plantable?.value ?: 0)) > patch.currentGrowthStage){ patch.setCurrentState(savedState) @@ -122,6 +109,25 @@ class FarmingState(player: Player? = null) : State(player) { patch.setCurrentState((patch.plantable?.value ?: 0) + patch.currentGrowthStage) } + val type = patch.patch.type + val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE && patch.getFruitOrBerryCount() < 6) + if(shouldPlayCatchup && patch.plantable != null && !patchDead){ + var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0 + if (type == PatchType.BUSH) + stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) + if (type == PatchType.FRUIT_TREE) + stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) + + val nowTime = System.currentTimeMillis() + var simulatedTime = patch.nextGrowth + + while (simulatedTime < nowTime && stagesToSimulate-- > 0 && !patch.isDead) { + val timeToIncrement = TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) + patch.update() + simulatedTime += timeToIncrement + } + } + if(patchMap[fPatch] == null) { patchMap[fPatch] = patch } @@ -148,13 +154,7 @@ class FarmingState(player: Player? = null) : State(player) { if(patch.nextGrowth < System.currentTimeMillis() && !patch.isDead){ patch.update() - var minutes = patch.patch.type.stageGrowthTime.toLong() - if(patch.patch.type == PatchType.FRUIT_TREE && patch.isGrown()) { - // Fruit trees take 160 minutes per stage to grow, but - // restocking their fruit should take 40 minutes per fruit - minutes = 40 - } - patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(minutes) + patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) } } diff --git a/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt b/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt index 48da3cddf..494b466ef 100644 --- a/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt +++ b/Server/src/main/kotlin/rs09/net/packet/PacketProcessor.kt @@ -38,7 +38,6 @@ import core.net.amsc.MSPacketRepository import core.net.packet.PacketRepository import core.net.packet.context.PlayerContext import core.net.packet.out.ClearMinimapFlag -import discord.Discord import org.rs09.consts.Components import proto.management.ClanMessage import proto.management.JoinClanRequest @@ -53,6 +52,7 @@ import rs09.game.interaction.InteractionListeners import rs09.game.interaction.InterfaceListeners import rs09.game.interaction.QCRepository import rs09.game.interaction.inter.ge.StockMarket +import rs09.game.node.entity.player.info.LogType import rs09.game.node.entity.player.info.PlayerMonitor import rs09.game.node.entity.skill.magic.SpellListener import rs09.game.node.entity.skill.magic.SpellListeners @@ -199,7 +199,7 @@ object PacketProcessor { pkt.player.interfaceManager.closeChatbox() } is Packet.Command -> { - PlayerMonitor.logMisc(pkt.player, "CommandUse", pkt.commandLine) + PlayerMonitor.log(pkt.player, LogType.COMMAND, pkt.commandLine) CommandSystem.commandSystem.parse(pkt.player, pkt.commandLine) } is Packet.ChatMessage -> { @@ -404,7 +404,7 @@ object PacketProcessor { if (book != "none") SpellListeners.run(child, type, book, player, target) when (iface) { - 430,192 -> MagicSpell.castSpell(player, SpellBookManager.SpellBook.forInterface(iface), child, target) + 430,192,193 -> MagicSpell.castSpell(player, SpellBookManager.SpellBook.forInterface(iface), child, target) 662 -> { if (player.familiarManager.hasFamiliar()) player.familiarManager.familiar.executeSpecialMove(FamiliarSpecial(target, iface, child, target as? Item)) @@ -664,7 +664,7 @@ object PacketProcessor { player.debug("ID: ${wrapperChild.id}, Option: ${option.name}[${option.index}]") player.debug("Loc: ${scenery.location}, Dir: ${scenery.direction}") if (hasWrapper) { - player.debug("WrapperID: ${scenery.id}, Varbit: ${scenery.definition.configFile.id}") + player.debug("WrapperID: ${scenery.id}, ${scenery.definition.configFile?.let { "Varbit: ${it.id}"} ?: "Varp: ${scenery.definition.configId}"}") } player.debug("------------------------------------------------") diff --git a/Server/src/main/kotlin/rs09/net/packet/in/Login.kt b/Server/src/main/kotlin/rs09/net/packet/in/Login.kt index 080d3e07a..66f26a8ab 100644 --- a/Server/src/main/kotlin/rs09/net/packet/in/Login.kt +++ b/Server/src/main/kotlin/rs09/net/packet/in/Login.kt @@ -19,6 +19,7 @@ import rs09.ServerStore import rs09.ServerStore.Companion.addToList import rs09.ServerStore.Companion.getList import rs09.auth.AuthResponse +import rs09.game.node.entity.player.info.LogType import rs09.game.node.entity.player.info.PlayerMonitor import rs09.game.node.entity.player.info.login.LoginParser import rs09.game.system.SystemLogger @@ -132,7 +133,7 @@ object Login { } val player = Player(details) - PlayerMonitor.logMisc(player, "login_ip", details.ipAddress) + PlayerMonitor.log(player, LogType.IP_LOG, details.ipAddress) if (canBypassAccountLimitCheck(player)) { proceedWithAcceptableLogin(session, player, opcode) } else { diff --git a/Server/src/main/kotlin/rs09/worker/ManagementEvents.kt b/Server/src/main/kotlin/rs09/worker/ManagementEvents.kt index 3173316ec..1ee764f80 100644 --- a/Server/src/main/kotlin/rs09/worker/ManagementEvents.kt +++ b/Server/src/main/kotlin/rs09/worker/ManagementEvents.kt @@ -223,6 +223,7 @@ object ManagementEvents { sendMessage(p, "Error leaving clan. Please relog.") } else { clan.leave(p, true) + p.details.communication.clan = null } }