diff --git a/data/area/misthalin/clan_cup_plaque.ifaces.toml b/data/area/misthalin/clan_cup_plaque.ifaces.toml
new file mode 100644
index 0000000000..c1f7f327db
--- /dev/null
+++ b/data/area/misthalin/clan_cup_plaque.ifaces.toml
@@ -0,0 +1,22 @@
+[clan_cup_interface]
+id = 205
+
+[.combined_winners_button]
+id = 49
+
+[.skilling_winners_button]
+id = 53
+
+[.combat_winners_button]
+id = 57
+
+[.current_winners_button]
+id = 61
+
+[.sub_text]
+id = 64
+
+[.main_text]
+id = 65
+
+
diff --git a/data/area/misthalin/lumbridge/freds_farm/ram.anims.toml b/data/area/misthalin/lumbridge/freds_farm/ram.anims.toml
index a01ac502bd..02eba0a3e6 100644
--- a/data/area/misthalin/lumbridge/freds_farm/ram.anims.toml
+++ b/data/area/misthalin/lumbridge/freds_farm/ram.anims.toml
@@ -1,5 +1,6 @@
[ram_death]
id = 5336
+ticks = 2
[ram_defend]
id = 5337
diff --git a/data/area/misthalin/lumbridge/lumbridge.drops.toml b/data/area/misthalin/lumbridge/lumbridge.drops.toml
index 91e3ef39c1..bb5a25335f 100644
--- a/data/area/misthalin/lumbridge/lumbridge.drops.toml
+++ b/data/area/misthalin/lumbridge/lumbridge.drops.toml
@@ -3,4 +3,31 @@ roll = 128
drops = [
{ id = "coins", amount = 9, chance = 123 },
{ id = "potato_seed", chance = 5 },
+]
+
+[lumbridge_guard_drop_table]
+type = "all"
+drops = [
+ { table = "bones" },
+ { table = "lumbridge_guard_secondary" },
+ { table = "easy_clue_scroll", roll = 128 },
+]
+
+[lumbridge_guard_secondary]
+roll = 128
+drops = [
+ { id = "cooked_meat", min = 1, max = 2, chance = 10 },
+ { id = "cooked_chicken", min = 1, max = 2, chance = 10 },
+ { id = "meat_pie", min = 1, max = 2, chance = 10 },
+ { id = "bronze_arrow", amount = 16, chance = 4 },
+ { id = "iron_sword" },
+ { id = "body_rune", amount = 7, chance = 5 },
+ { id = "air_rune", min = 2, max = 7, chance = 3 },
+ { id = "mind_rune", min = 13, max = 19, chance = 3 },
+ { id = "water_rune", amount = 6, chance = 6 },
+ { id = "earth_rune", amount = 4, chance = 3 },
+ { id = "staff_of_air" },
+ { id = "coins", amount = 50, chance = 10 },
+ { id = "copper_ore", chance = 5 },
+ { id = "tin_ore", chance = 5 },
]
\ No newline at end of file
diff --git a/data/area/misthalin/lumbridge/lumbridge.npc-spawns.toml b/data/area/misthalin/lumbridge/lumbridge.npc-spawns.toml
index d0f84f987a..d55aba620c 100644
--- a/data/area/misthalin/lumbridge/lumbridge.npc-spawns.toml
+++ b/data/area/misthalin/lumbridge/lumbridge.npc-spawns.toml
@@ -1,5 +1,15 @@
spawns = [
-# 12594
+ # 12594
+ { id = "giant_rat", x = 3192, y = 3203 },
+ { id = "giant_rat", x = 3192, y = 3211 },
+ { id = "giant_rat", x = 3197, y = 3202 },
+ { id = "giant_rat", x = 3193, y = 3203 },
+ { id = "giant_rat", x = 3194, y = 3201 },
+ { id = "giant_rat", x = 3194, y = 3211 },
+ { id = "giant_rat", x = 3192, y = 3208 },
+ { id = "giant_rat", x = 3195, y = 3205 },
+ { id = "lumbridge_guardsman_attackable", x = 3187, y = 3245 },
+ { id = "lumbridge_guardsman_attackable2", x = 3192, y = 3207 },
{ id = "butterfly_1", x = 3183, y = 3254 },
{ id = "butterfly_1", x = 3182, y = 3251 },
{ id = "butterfly_1", x = 3178, y = 3228 },
@@ -49,11 +59,11 @@ spawns = [
{ id = "butterfly_3", x = 3233, y= 3216},
{ id = "roddeck", x = 3232, y = 3237, direction = "WEST" },
{ id = "sigmund_lumbridge", x = 3209, y = 3219, level = 1 },
- { id = "guardsman_deshawn", x = 3206, y = 3214, level = 1 },
- { id = "guardsman_dante", x = 3211, y = 3227, level = 1 },
- { id = "guardsman_brawn", x = 3215, y = 3219, level = 1},
- { id = "guardsman_pazel", x = 3213, y = 3210, level = 1 },
- { id = "guardsman_peale", x = 3208, y = 3227},
+ { id = "lumbridge_guardsman_deshawn", x = 3206, y = 3214, level = 1 },
+ { id = "lumbridge_guardsman_dante", x = 3211, y = 3227, level = 1 },
+ { id = "lumbridge_guardsman_brawn", x = 3215, y = 3219, level = 1},
+ { id = "lumbridge_guardsman_pazel", x = 3213, y = 3210, level = 1 },
+ { id = "lumbridge_guardsman_peale", x = 3208, y = 3227},
{ id = "donie", x = 3219, y = 3247 },
{ id = "imp", x = 3223, y = 3223 },
{ id = "lumbridge_sage", x = 3238, y = 3219, direction = "WEST" },
@@ -190,6 +200,7 @@ spawns = [
{ id = "explorer_jack", x = 3204, y = 3241 },
{ id = "doomsayer", x = 3237, y = 3227, direction = "EAST" },
# 12851
+ { id = "lumbridge_guardsman_attackable", x = 3253, y = 3238 },
{ id = "duck_swim", x = 3235, y = 3265 },
{ id = "duck_swim", x = 3233, y = 3267 },
{ id = "duck_swim", x = 3233, y = 3270 },
@@ -244,11 +255,10 @@ spawns = [
{ id = "farmer_lumbridge", x = 3233, y = 3308 },
{ id = "farmer_lumbridge_3", x = 3262, y = 3325 },
{ id = "farmer_lumbridge_2", x = 3250, y = 3310 },
- { id = "master_farmer", x = 3203, y = 3254, members = true },
{ id = "crow_4", x = 3208, y = 3301, members = true },
{ id = "vasquen", x = 3226, y = 3311 },
- { id = "fayeth", x = 3228, y = 3245 },
- { id = "tools_leprechaun", x = 3231, y = 3248 },
+ { id = "fayeth", x = 3193, y = 3233 },
+ { id = "tools_leprechaun", x = 3196, y = 3228 },
{ id = "dwarf_lumbridge", x = 3224, y = 3286, members = true },
{ id = "ham_member_lumbridge", x = 3230, y = 3285, members = true },
{ id = "seth_groats_lumbridge", x = 3223, y = 3293, members = true },
diff --git a/data/area/misthalin/lumbridge/lumbridge.npcs.toml b/data/area/misthalin/lumbridge/lumbridge.npcs.toml
index 47056f2487..3b7a3f1aaf 100644
--- a/data/area/misthalin/lumbridge/lumbridge.npcs.toml
+++ b/data/area/misthalin/lumbridge/lumbridge.npcs.toml
@@ -123,27 +123,27 @@ wander_radius = 4
collision = "indoors"
examine = "Lumbridge Castle's head cook."
-[guardsman_dante]
+[lumbridge_guardsman_dante]
id = 7885
categories = ["human"]
wander_radius = 4
collision = "indoors"
examine = "He tries to keep order around Lumbridge."
-[guardsman_deshawn]
-clone = "guardsman_dante"
+[lumbridge_guardsman_deshawn]
+clone = "lumbridge_guardsman_dante"
id = 7886
-[guardsman_brawn]
-clone = "guardsman_dante"
+[lumbridge_guardsman_brawn]
+clone = "lumbridge_guardsman_dante"
id = 7887
-[guardsman_pazel]
-clone = "guardsman_dante"
+[lumbridge_guardsman_pazel]
+clone = "lumbridge_guardsman_dante"
id = 7889
-[guardsman_peale]
-clone = "guardsman_dante"
+[lumbridge_guardsman_peale]
+clone = "lumbridge_guardsman_dante"
id = 7890
[lumbridge_man]
@@ -310,3 +310,36 @@ examine = "An old adventurer."
[chief_thief_robin_lumbridge_2]
id = 7955
+
+[lumbridge_guardsman_attackable]
+id = 12367
+hitpoints = 100
+attack = 4
+defence = 4
+max_hit_melee = 16
+attack_speed = 5
+combat_anims = "human"
+style = "slash"
+weapon_style = "sword"
+categories = ["human"]
+combat_anims = "human"
+hunt_mode = "aggressive_npcs"
+respawn_delay = 35
+drop_table = "lumbridge_guard"
+examine = " He tries to keep order around Lumbridge by killing goblins."
+
+[lumbridge_guardsman_attackable2]
+id = 12368
+hitpoints = 100
+attack = 4
+defence = 4
+max_hit_melee = 16
+attack_speed = 5
+style = "stab"
+weapon_style = "sword"
+categories = ["human"]
+combat_anims = "human"
+hunt_mode = "aggressive_npcs"
+respawn_delay = 35
+drop_table = "lumbridge_guard"
+examine = "He tries to keep order around Lumbridge by killing rats."
\ No newline at end of file
diff --git a/data/entity/npc/animal/rat/rat.anims.toml b/data/entity/npc/animal/rat/rat.anims.toml
index 81b7e5dd59..3119ce94be 100644
--- a/data/entity/npc/animal/rat/rat.anims.toml
+++ b/data/entity/npc/animal/rat/rat.anims.toml
@@ -6,12 +6,14 @@ id = 2706
[rat_death]
id = 2707
+ticks = 2
[giant_rat_attack]
id = 14859
[giant_rat_death]
id = 14860
+ticks = 4
[giant_rat_defend]
id = 14861
diff --git a/data/quest/mini/sheep_shearer/sheep_shearer.items.toml b/data/quest/mini/sheep_shearer/sheep_shearer.items.toml
index d3be8594b6..54290f9f1f 100644
--- a/data/quest/mini/sheep_shearer/sheep_shearer.items.toml
+++ b/data/quest/mini/sheep_shearer/sheep_shearer.items.toml
@@ -23,6 +23,7 @@ id = 1738
id = 15415
limit = 5000
weight = 0.003
+tradeable = false
destroy = "You'll have to shear some from another black sheep."
spinning = { to = "ball_of_black_wool", level = 1, xp = 2.5 }
examine = "This probably came from a black sheep. You can spin it into a ball of black wool at a spinning wheel."
@@ -31,5 +32,6 @@ examine = "This probably came from a black sheep. You can spin it into a ball of
id = 15416
limit = 5000
weight = 0.012
+tradeable = false
destroy = "You'll have to make one from more black wool."
examine = "Spun from black wool. You can weave it into black cloth at a loom."
diff --git a/game/src/main/kotlin/content/area/misthalin/ClanCupPlaque.kt b/game/src/main/kotlin/content/area/misthalin/ClanCupPlaque.kt
new file mode 100644
index 0000000000..b855b6bd28
--- /dev/null
+++ b/game/src/main/kotlin/content/area/misthalin/ClanCupPlaque.kt
@@ -0,0 +1,36 @@
+package content.area.misthalin
+
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.ui.open
+
+class ClanCupPlaque : Script {
+ init {
+ // https://www.youtube.com/watch?v=K-Ptq7ZxeWI
+ objectOperate("Read", "lumbridge_clan_cup_plaque") {
+ open("clan_cup_interface")
+ interfaces.sendText("clan_cup_interface", "current_winners_button", "Current")
+ interfaces.sendText("clan_cup_interface", "main_text", "Current Winners")
+ interfaces.sendText("clan_cup_interface", "sub_text", "The victorious winners of the 2010 Jagex Clan Cup
Combat - Runescape Dinasty
Skilling - Divination
Combined - BasedIn2Minutes")
+ }
+ interfaceOption("Current-winners", "clan_cup_interface:current_winners_button") {
+ interfaces.sendText("clan_cup_interface", "current_winners", "Current")
+ interfaces.sendText("clan_cup_interface", "main_text", "Current Winners")
+ interfaces.sendText("clan_cup_interface", "sub_text", "The victorious winners of the 2010 Jagex Clan Cup
Combat - Runescape Dinasty
Skilling - Divination
Combined - Basedin2minutes")
+ }
+ interfaceOption("Combat-winners", "clan_cup_interface:combat_winners_button") {
+ interfaces.sendText("clan_cup_interface", "combat_winners", "Combat")
+ interfaces.sendText("clan_cup_interface", "main_text", "Combat winners")
+ interfaces.sendText("clan_cup_interface", "sub_text", "2010 - Runescape Dinasty
2009 - The Titans")
+ }
+ interfaceOption("Skilling-winners", "clan_cup_interface:skilling_winners_button") {
+ interfaces.sendText("clan_cup_interface", "skilling_winners", "Skilling")
+ interfaces.sendText("clan_cup_interface", "main_text", "Skilling Winners")
+ interfaces.sendText("clan_cup_interface", "sub_text", "2010 - Divination
2009 - Divination")
+ }
+ interfaceOption("Combined-winners", "clan_cup_interface:combined_winners_button") {
+ interfaces.sendText("clan_cup_interface", "combined_winners", "Combined")
+ interfaces.sendText("clan_cup_interface", "main_text", "Combined Winners")
+ interfaces.sendText("clan_cup_interface", "sub_text", "2010 - BasedIn2Minutes
2009 - Wicked Fury")
+ }
+ }
+}
diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/CowFieldSignpost.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/CowFieldSignpost.kt
new file mode 100644
index 0000000000..31cab267cc
--- /dev/null
+++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/CowFieldSignpost.kt
@@ -0,0 +1,22 @@
+package content.area.misthalin.lumbridge
+
+import content.entity.player.dialogue.type.statement
+import world.gregs.voidps.engine.Script
+
+class CowFieldSignpost : Script {
+
+ var cowDeathsToday: Int = 0
+
+ init {
+ npcDeath("cow*") {
+ cowDeathsToday += 1
+ }
+ objectOperate("Read", "lumbridge_signpost_cow") {
+ if (cowDeathsToday > 0) {
+ statement("Local cowherders have reported that $cowDeathsToday cows have been slain in this field today by passing adventurers. Farmers throughout the land fear this may be an epidemic.")
+ } else {
+ statement("The Lumbridge cow population has been thriving today, without a single cow death to worry about!")
+ }
+ }
+ }
+}
diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt
new file mode 100644
index 0000000000..41af32e998
--- /dev/null
+++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/PlayerDeathSignpost.kt
@@ -0,0 +1,22 @@
+package content.area.misthalin.lumbridge
+
+import content.entity.player.dialogue.type.statement
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.data.Settings
+
+class PlayerDeathSignpost : Script {
+ var playerDeathsToday: Int = 0
+
+ init {
+ playerDeath {
+ playerDeathsToday += 1
+ }
+ objectOperate("Read", "lumbridge_signpost_death") {
+ if (playerDeathsToday > 0) {
+ statement("So far today, $playerDeathsToday unlucky adventurers have died on ${Settings["server.name"]} and been sent to their respawn location. Be careful out there.")
+ } else {
+ statement("So far today, not a single adventurer on ${Settings["server.name"]} has met their end grisly or otherwise. Either the streets are getting safer or adventurers are getting warier.")
+ }
+ }
+ }
+}
diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/castle/LumbridgeGuardsman.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/castle/LumbridgeGuardsman.kt
index dd236fc5d1..2d68d05084 100644
--- a/game/src/main/kotlin/content/area/misthalin/lumbridge/castle/LumbridgeGuardsman.kt
+++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/castle/LumbridgeGuardsman.kt
@@ -1,16 +1,23 @@
package content.area.misthalin.lumbridge.castle
+import content.entity.combat.inCombat
import content.entity.player.dialogue.Happy
import content.entity.player.dialogue.Neutral
import content.entity.player.dialogue.Quiz
import content.entity.player.dialogue.type.*
import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.message
import world.gregs.voidps.type.random
class LumbridgeGuardsman : Script {
init {
- npcOperate("Talk-to", "guardsman_*") {
+ npcOperate("Talk-to", "lumbridge_guardsman_*") {
+ // From wiki: When you attack them and try to talk to them *Using the Right Click* a message will say "The guard somehow doesn't feel like talking to you."
+ if (inCombat) {
+ message("The guard somehow doesn't feel like talking to you.")
+ return@npcOperate
+ }
when (random.nextInt(0, 5)) {
0 -> player("Howdy.")
1 -> player("Salutations!")
diff --git a/game/src/main/kotlin/content/entity/npc/combat/melee/LumbridgeGuardsmanAttackble.kt b/game/src/main/kotlin/content/entity/npc/combat/melee/LumbridgeGuardsmanAttackble.kt
new file mode 100644
index 0000000000..0b5c37659d
--- /dev/null
+++ b/game/src/main/kotlin/content/entity/npc/combat/melee/LumbridgeGuardsmanAttackble.kt
@@ -0,0 +1,44 @@
+package content.entity.npc.combat.melee
+
+import content.entity.combat.killer
+import world.gregs.voidps.engine.Script
+import world.gregs.voidps.engine.client.instruction.handle.interactNpc
+import world.gregs.voidps.engine.entity.character.npc.NPC
+import world.gregs.voidps.engine.entity.character.player.skill.Skill
+import world.gregs.voidps.engine.queue.strongQueue
+
+class LumbridgeGuardsmanAttackble : Script {
+ init {
+ huntNPC("aggressive_npcs") { target ->
+ if (id == "lumbridge_guardsman_attackable2" && target.id.endsWith("rat")) {
+ interactNpc(target, "Attack")
+ }
+ if (id == "lumbridge_guardsman_attackable" && target.id.contains("goblin")) {
+ interactNpc(target, "Attack")
+ }
+ }
+ npcDeath("goblin*,giant_rat") {
+ val guard = killer as? NPC ?: return@npcDeath
+ when {
+ // Guard attacked rats and giant rats, but only healed when giant rat was killed https://youtu.be/qC_XoAQK6Cs?si=VH9tOmKXu-_Bgjnr&t=137
+ id == ("giant_rat") && guard.id == "lumbridge_guardsman_attackable2" -> {
+ guard.strongQueue("rat_killer") {
+ guard.delay(2)
+ guard.anim("eat_drink")
+ // From wiki: Every time they kill a monster, they eat a piece of food which heals them to full health, and walk over to the square of their death as to collect a drop.
+ guard.levels.set(Skill.Constitution, guard.levels.getMax(Skill.Constitution))
+ guard.walkTo(tile)
+ }
+ }
+ id.contains("goblin") && guard.id == "lumbridge_guardsman_attackable" -> {
+ guard.strongQueue("goblin_killer") {
+ guard.delay(2)
+ guard.anim("eat_drink")
+ guard.levels.set(Skill.Constitution, guard.levels.getMax(Skill.Constitution))
+ guard.walkTo(tile)
+ }
+ }
+ }
+ }
+ }
+}