mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-09 16:45:44 -07:00
Tortoise no longer drop 3 regular-sized bones on death
Tortoises have corrected HP (101 for level 79, 121 for level 92) Added stats and animations for Gnome Driver, Gnome Mage, and Gnome Archer Gnome-Mounted Tortoise now attacks with its mounted gnomes, and the gnomes spawn on the ground upon tortoise death
This commit is contained in:
parent
07e48d91a8
commit
12f217f8f3
3 changed files with 310 additions and 25 deletions
|
|
@ -36458,12 +36458,6 @@
|
|||
"weight": "100.0",
|
||||
"id": "532",
|
||||
"maxAmount": "1"
|
||||
},
|
||||
{
|
||||
"minAmount": "3",
|
||||
"weight": "100.0",
|
||||
"id": "526",
|
||||
"maxAmount": "3"
|
||||
}
|
||||
],
|
||||
"charm": [],
|
||||
|
|
|
|||
|
|
@ -36019,7 +36019,7 @@
|
|||
"name": "Tortoise",
|
||||
"defence_level": "36",
|
||||
"safespot": null,
|
||||
"lifepoints": "51",
|
||||
"lifepoints": "121",
|
||||
"strength_level": "36",
|
||||
"id": "3808",
|
||||
"range_level": "1",
|
||||
|
|
@ -36093,61 +36093,68 @@
|
|||
"examine": "A Gnome Arrow-chucker",
|
||||
"combat_style": "1",
|
||||
"melee_animation": "190",
|
||||
"range_animation": "0",
|
||||
"range_animation": "190",
|
||||
"respawn_delay": "60",
|
||||
"defence_animation": "0",
|
||||
"defence_animation": "193",
|
||||
"weakness": "2",
|
||||
"magic_animation": "0",
|
||||
"death_animation": "196",
|
||||
"name": "Gnome Archer",
|
||||
"defence_level": "30",
|
||||
"defence_level": "1",
|
||||
"safespot": null,
|
||||
"lifepoints": "42",
|
||||
"lifepoints": "10",
|
||||
"strength_level": "1",
|
||||
"id": "3814",
|
||||
"aggressive": "true",
|
||||
"range_level": "30",
|
||||
"attack_level": "1"
|
||||
"range_level": "5",
|
||||
"projectile": "10",
|
||||
"attack_level": "1",
|
||||
"prj_height": "30"
|
||||
},
|
||||
{
|
||||
"examine": "Yee haa!",
|
||||
"melee_animation": "3969",
|
||||
"range_animation": "0",
|
||||
"respawn_delay": "60",
|
||||
"defence_animation": "0",
|
||||
"defence_animation": "193",
|
||||
"weakness": "9",
|
||||
"magic_animation": "0",
|
||||
"death_animation": "196",
|
||||
"name": "Gnome Driver",
|
||||
"defence_level": "33",
|
||||
"defence_level": "1",
|
||||
"safespot": null,
|
||||
"lifepoints": "47",
|
||||
"strength_level": "33",
|
||||
"lifepoints": "10",
|
||||
"strength_level": "1",
|
||||
"id": "3815",
|
||||
"aggressive": "true",
|
||||
"range_level": "1",
|
||||
"attack_level": "33"
|
||||
"attack_level": "5"
|
||||
},
|
||||
{
|
||||
"examine": "A battle mage of the gnomish variety.",
|
||||
"combat_style": "2",
|
||||
"start_gfx": "93",
|
||||
"start_height": "80",
|
||||
"melee_animation": "3968",
|
||||
"range_animation": "0",
|
||||
"magic_level": "34",
|
||||
"magic_level": "5",
|
||||
"spell_id": "",
|
||||
"respawn_delay": "60",
|
||||
"defence_animation": "0",
|
||||
"defence_animation": "193",
|
||||
"weakness": "5",
|
||||
"magic_animation": "0",
|
||||
"magic_animation": "200",
|
||||
"death_animation": "196",
|
||||
"name": "Gnome Mage",
|
||||
"defence_level": "34",
|
||||
"defence_level": "1",
|
||||
"safespot": null,
|
||||
"lifepoints": "48",
|
||||
"lifepoints": "10",
|
||||
"strength_level": "1",
|
||||
"id": "3816",
|
||||
"aggressive": "true",
|
||||
"range_level": "1",
|
||||
"attack_level": "1"
|
||||
"projectile": "94",
|
||||
"attack_level": "1",
|
||||
"prj_height": "30"
|
||||
},
|
||||
{
|
||||
"examine": "The cruel tortoise trainer. Boo!",
|
||||
|
|
@ -36176,7 +36183,7 @@
|
|||
"name": "Tortoise",
|
||||
"defence_level": "36",
|
||||
"safespot": null,
|
||||
"lifepoints": "51",
|
||||
"lifepoints": "101",
|
||||
"strength_level": "36",
|
||||
"id": "3819",
|
||||
"range_level": "1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,284 @@
|
|||
package content.region.kandarin.handlers
|
||||
|
||||
import core.api.*
|
||||
import core.game.node.entity.Entity
|
||||
import core.game.node.entity.combat.CombatStyle
|
||||
import core.game.node.entity.combat.CombatSwingHandler
|
||||
import core.game.node.entity.combat.MultiSwingHandler
|
||||
import core.game.node.entity.combat.equipment.SwitchAttack
|
||||
import core.game.node.entity.impl.Animator.Priority
|
||||
import core.game.node.entity.impl.Projectile
|
||||
import core.game.node.entity.npc.AbstractNPC
|
||||
import core.game.node.entity.npc.NPC
|
||||
import core.game.node.entity.npc.NPCBehavior
|
||||
import core.game.node.entity.npc.agg.AggressiveBehavior
|
||||
import core.game.node.entity.npc.agg.AggressiveHandler
|
||||
import core.game.node.entity.player.Player
|
||||
import core.game.world.GameWorld
|
||||
import core.game.world.map.Direction
|
||||
import core.game.world.map.Location
|
||||
import core.game.world.update.flag.context.Animation
|
||||
import core.plugin.Initializable
|
||||
import org.rs09.consts.NPCs
|
||||
|
||||
/*
|
||||
* Behavior is based on the following sources:
|
||||
* https://www.youtube.com/watch?v=u2A-_ihV_2w (november 2008)
|
||||
* https://www.youtube.com/watch?v=O0EIZu7-iys (august 2009)
|
||||
* https://runescape.wiki/w/Tortoise?oldid=815524 https://web.archive.org/web/20090308094532/http://runescape.wikia.com/wiki/Tortoise
|
||||
*
|
||||
* todo fix att, str, def? I'm not sure what is the typical way to calculate these when they are unknown.
|
||||
* level 79: HP 101, max hit 12, "low attack but good defence"
|
||||
* level 92: HP 121, max hit 12, "low attack but good defence"
|
||||
*
|
||||
* https://runescape.wiki/w/Gnome_Archer?oldid=949978 https://web.archive.org/web/20090929124104/http://runescape.wikia.com/wiki/Gnome_archer
|
||||
* https://runescape.wiki/w/Gnome_Driver?oldid=1235409 https://web.archive.org/web/20090929124109/http://runescape.wikia.com:80/wiki/Gnome_driver
|
||||
* https://runescape.wiki/w/Gnome_Mage?oldid=1425756 https://web.archive.org/web/20090928131013/http://runescape.wikia.com:80/wiki/Gnome_mage
|
||||
*/
|
||||
|
||||
@Initializable
|
||||
class GnomeTortoiseNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) {
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return GnomeTortoiseNPC(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.TORTOISE_3808)
|
||||
}
|
||||
|
||||
fun spawnGnomes(location: Location, direction: Direction) {
|
||||
|
||||
//todo: sometimes the child spawns or direction is wrong on death. does a move trigger right before it dies?
|
||||
|
||||
var archerLoc = location
|
||||
var driverLoc = location
|
||||
var mageLoc = location
|
||||
|
||||
// X D X 3x3 turtle, C center
|
||||
// M C A D driver, M mage, A archer
|
||||
// X X X
|
||||
|
||||
// If I was smart I could probably do this with vectors, but I'm dumb so just doing the possibilities by hand.
|
||||
if(direction == Direction.NORTH) {
|
||||
archerLoc = location.transform(1,0,0)
|
||||
driverLoc = location.transform(0,1,0)
|
||||
mageLoc = location.transform(-1,0,0)
|
||||
}else if(direction == Direction.NORTH_EAST) {
|
||||
archerLoc = location.transform(1,-1,0)
|
||||
driverLoc = location.transform(1,1,0)
|
||||
mageLoc = location.transform(-1,1,0)
|
||||
}else if(direction == Direction.EAST) {
|
||||
archerLoc = location.transform(0,-1,0)
|
||||
driverLoc = location.transform(1,0,0)
|
||||
mageLoc = location.transform(0,1,0)
|
||||
}else if(direction == Direction.SOUTH_EAST) {
|
||||
archerLoc = location.transform(-1,-1,0)
|
||||
driverLoc = location.transform(1,-1,0)
|
||||
mageLoc = location.transform(1,1,0)
|
||||
}else if(direction == Direction.SOUTH) {
|
||||
archerLoc = location.transform(-1,0,0)
|
||||
driverLoc = location.transform(0,-1,0)
|
||||
mageLoc = location.transform(1,0,0)
|
||||
}else if(direction == Direction.SOUTH_WEST) {
|
||||
archerLoc = location.transform(-1,1,0)
|
||||
driverLoc = location.transform(-1,-1,0)
|
||||
mageLoc = location.transform(1,-1,0)
|
||||
}else if(direction == Direction.WEST) {
|
||||
archerLoc = location.transform(0,1,0)
|
||||
driverLoc = location.transform(-1,0,0)
|
||||
mageLoc = location.transform(0,-1,0)
|
||||
}else if(direction == Direction.NORTH_WEST) {
|
||||
archerLoc = location.transform(1,1,0)
|
||||
driverLoc = location.transform(-1,1,0)
|
||||
mageLoc = location.transform(-1,-1,0)
|
||||
}
|
||||
|
||||
val npcArcher = GnomeArcherNPC(NPCs.GNOME_ARCHER_3814, archerLoc)
|
||||
npcArcher.sendChat("Argh!")
|
||||
npcArcher.init()
|
||||
|
||||
val npcDriver = GnomeDriverNPC(NPCs.GNOME_DRIVER_3815, driverLoc)
|
||||
npcDriver.sendChat("Nooooo! Dobbie's dead!")
|
||||
npcDriver.init()
|
||||
|
||||
val npcMage = GnomeMageNPC(NPCs.GNOME_MAGE_3816, mageLoc)
|
||||
npcMage.sendChat("Kill the infidel!")
|
||||
npcMage.init()
|
||||
}
|
||||
|
||||
override fun finalizeDeath(killer: Entity?) {
|
||||
val turtleLoc = this.centerLocation
|
||||
val turtleDir = this.direction
|
||||
spawnGnomes(turtleLoc, turtleDir)
|
||||
super.finalizeDeath(killer)
|
||||
// todo remove this debug if not needed. It's just telling me the "direction" the tortoise dies in so I can verify the direction that the child NPCs should spawn.
|
||||
if (killer is Player) {
|
||||
killer.debug(direction.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//handles the attack switching
|
||||
class GnomeTortoiseBehavior : NPCBehavior(NPCs.TORTOISE_3808) {
|
||||
|
||||
private val combatHandler = MultiSwingHandler(
|
||||
true,
|
||||
// per wiki source, melee has a max hit of 12
|
||||
SwitchAttack(
|
||||
CombatStyle.MELEE.swingHandler,
|
||||
Animation(3953, Priority.HIGH)
|
||||
),
|
||||
// todo correct the projectile locations (they should originate from the range or mage gnome, not the center). not sure how to do this.
|
||||
SwitchAttack(
|
||||
CombatStyle.RANGE.swingHandler,
|
||||
Animation(3954, Priority.HIGH),
|
||||
null,
|
||||
null,
|
||||
Projectile.create(
|
||||
null as Entity?,
|
||||
null,
|
||||
10, //bronze arrow
|
||||
35,
|
||||
30,
|
||||
10,
|
||||
50,
|
||||
14,
|
||||
255
|
||||
)
|
||||
),
|
||||
// per wiki source above, spell should be water strike with the sounds of ice barrage
|
||||
SwitchAttack(
|
||||
CombatStyle.MAGIC.swingHandler,
|
||||
Animation(3955, Priority.HIGH),
|
||||
null,
|
||||
null,
|
||||
Projectile.create(
|
||||
null as Entity?,
|
||||
null,
|
||||
94, //water strike
|
||||
35,
|
||||
30,
|
||||
10,
|
||||
50,
|
||||
14,
|
||||
255
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler {
|
||||
return combatHandler
|
||||
}
|
||||
}
|
||||
|
||||
// Handles Gnome Archer behavior
|
||||
class GnomeArcherNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) {
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return GnomeArcherNPC(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.GNOME_ARCHER_3814)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
this.isRespawn = false
|
||||
this.isAggressive = true
|
||||
this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() {
|
||||
override fun ignoreCombatLevelDifference(): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class GnomeArcherBehavior : NPCBehavior(NPCs.GNOME_ARCHER_3814) {
|
||||
override fun onCreation(self: NPC) {
|
||||
// stops the entity from instantly moving.
|
||||
delayEntity(self, 1)
|
||||
setAttribute(self, "despawn-time", GameWorld.ticks + 25)
|
||||
}
|
||||
|
||||
override fun tick(self: NPC): Boolean {
|
||||
if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks))
|
||||
self.clear()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Handles Gnome Mage behavior
|
||||
class GnomeMageNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) {
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return GnomeMageNPC(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.GNOME_MAGE_3816)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
this.isRespawn = false
|
||||
this.isAggressive = true
|
||||
this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() {
|
||||
override fun ignoreCombatLevelDifference(): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class GnomeMageBehavior : NPCBehavior(NPCs.GNOME_MAGE_3816) {
|
||||
override fun onCreation(self: NPC) {
|
||||
// stops the entity from instantly moving.
|
||||
delayEntity(self, 1)
|
||||
setAttribute(self, "despawn-time", GameWorld.ticks + 25)
|
||||
}
|
||||
|
||||
override fun tick(self: NPC): Boolean {
|
||||
if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks))
|
||||
self.clear()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Handles Gnome Driver behavior
|
||||
class GnomeDriverNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) {
|
||||
|
||||
override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC {
|
||||
return GnomeDriverNPC(id, location)
|
||||
}
|
||||
|
||||
override fun getIds(): IntArray {
|
||||
return intArrayOf(NPCs.GNOME_DRIVER_3815)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
this.isRespawn = false
|
||||
this.isAggressive = true
|
||||
this.aggressiveHandler = AggressiveHandler(this, object : AggressiveBehavior() {
|
||||
override fun ignoreCombatLevelDifference(): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class GnomeDriverBehavior : NPCBehavior(NPCs.GNOME_DRIVER_3815) {
|
||||
override fun onCreation(self: NPC) {
|
||||
// stops the entity from instantly moving.
|
||||
delayEntity(self, 1)
|
||||
setAttribute(self, "despawn-time", GameWorld.ticks + 25)
|
||||
}
|
||||
|
||||
override fun tick(self: NPC): Boolean {
|
||||
if (!self.inCombat() && (getAttribute(self, "despawn-time", 0) <= GameWorld.ticks))
|
||||
self.clear()
|
||||
return true
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue