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",
|
"weight": "100.0",
|
||||||
"id": "532",
|
"id": "532",
|
||||||
"maxAmount": "1"
|
"maxAmount": "1"
|
||||||
},
|
|
||||||
{
|
|
||||||
"minAmount": "3",
|
|
||||||
"weight": "100.0",
|
|
||||||
"id": "526",
|
|
||||||
"maxAmount": "3"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"charm": [],
|
"charm": [],
|
||||||
|
|
|
||||||
|
|
@ -36019,7 +36019,7 @@
|
||||||
"name": "Tortoise",
|
"name": "Tortoise",
|
||||||
"defence_level": "36",
|
"defence_level": "36",
|
||||||
"safespot": null,
|
"safespot": null,
|
||||||
"lifepoints": "51",
|
"lifepoints": "121",
|
||||||
"strength_level": "36",
|
"strength_level": "36",
|
||||||
"id": "3808",
|
"id": "3808",
|
||||||
"range_level": "1",
|
"range_level": "1",
|
||||||
|
|
@ -36093,61 +36093,68 @@
|
||||||
"examine": "A Gnome Arrow-chucker",
|
"examine": "A Gnome Arrow-chucker",
|
||||||
"combat_style": "1",
|
"combat_style": "1",
|
||||||
"melee_animation": "190",
|
"melee_animation": "190",
|
||||||
"range_animation": "0",
|
"range_animation": "190",
|
||||||
"respawn_delay": "60",
|
"respawn_delay": "60",
|
||||||
"defence_animation": "0",
|
"defence_animation": "193",
|
||||||
"weakness": "2",
|
"weakness": "2",
|
||||||
"magic_animation": "0",
|
"magic_animation": "0",
|
||||||
"death_animation": "196",
|
"death_animation": "196",
|
||||||
"name": "Gnome Archer",
|
"name": "Gnome Archer",
|
||||||
"defence_level": "30",
|
"defence_level": "1",
|
||||||
"safespot": null,
|
"safespot": null,
|
||||||
"lifepoints": "42",
|
"lifepoints": "10",
|
||||||
"strength_level": "1",
|
"strength_level": "1",
|
||||||
"id": "3814",
|
"id": "3814",
|
||||||
"aggressive": "true",
|
"aggressive": "true",
|
||||||
"range_level": "30",
|
"range_level": "5",
|
||||||
"attack_level": "1"
|
"projectile": "10",
|
||||||
|
"attack_level": "1",
|
||||||
|
"prj_height": "30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"examine": "Yee haa!",
|
"examine": "Yee haa!",
|
||||||
"melee_animation": "3969",
|
"melee_animation": "3969",
|
||||||
"range_animation": "0",
|
"range_animation": "0",
|
||||||
"respawn_delay": "60",
|
"respawn_delay": "60",
|
||||||
"defence_animation": "0",
|
"defence_animation": "193",
|
||||||
"weakness": "9",
|
"weakness": "9",
|
||||||
"magic_animation": "0",
|
"magic_animation": "0",
|
||||||
"death_animation": "196",
|
"death_animation": "196",
|
||||||
"name": "Gnome Driver",
|
"name": "Gnome Driver",
|
||||||
"defence_level": "33",
|
"defence_level": "1",
|
||||||
"safespot": null,
|
"safespot": null,
|
||||||
"lifepoints": "47",
|
"lifepoints": "10",
|
||||||
"strength_level": "33",
|
"strength_level": "1",
|
||||||
"id": "3815",
|
"id": "3815",
|
||||||
"aggressive": "true",
|
"aggressive": "true",
|
||||||
"range_level": "1",
|
"range_level": "1",
|
||||||
"attack_level": "33"
|
"attack_level": "5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"examine": "A battle mage of the gnomish variety.",
|
"examine": "A battle mage of the gnomish variety.",
|
||||||
"combat_style": "2",
|
"combat_style": "2",
|
||||||
|
"start_gfx": "93",
|
||||||
|
"start_height": "80",
|
||||||
"melee_animation": "3968",
|
"melee_animation": "3968",
|
||||||
"range_animation": "0",
|
"range_animation": "0",
|
||||||
"magic_level": "34",
|
"magic_level": "5",
|
||||||
|
"spell_id": "",
|
||||||
"respawn_delay": "60",
|
"respawn_delay": "60",
|
||||||
"defence_animation": "0",
|
"defence_animation": "193",
|
||||||
"weakness": "5",
|
"weakness": "5",
|
||||||
"magic_animation": "0",
|
"magic_animation": "200",
|
||||||
"death_animation": "196",
|
"death_animation": "196",
|
||||||
"name": "Gnome Mage",
|
"name": "Gnome Mage",
|
||||||
"defence_level": "34",
|
"defence_level": "1",
|
||||||
"safespot": null,
|
"safespot": null,
|
||||||
"lifepoints": "48",
|
"lifepoints": "10",
|
||||||
"strength_level": "1",
|
"strength_level": "1",
|
||||||
"id": "3816",
|
"id": "3816",
|
||||||
"aggressive": "true",
|
"aggressive": "true",
|
||||||
"range_level": "1",
|
"range_level": "1",
|
||||||
"attack_level": "1"
|
"projectile": "94",
|
||||||
|
"attack_level": "1",
|
||||||
|
"prj_height": "30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"examine": "The cruel tortoise trainer. Boo!",
|
"examine": "The cruel tortoise trainer. Boo!",
|
||||||
|
|
@ -36176,7 +36183,7 @@
|
||||||
"name": "Tortoise",
|
"name": "Tortoise",
|
||||||
"defence_level": "36",
|
"defence_level": "36",
|
||||||
"safespot": null,
|
"safespot": null,
|
||||||
"lifepoints": "51",
|
"lifepoints": "101",
|
||||||
"strength_level": "36",
|
"strength_level": "36",
|
||||||
"id": "3819",
|
"id": "3819",
|
||||||
"range_level": "1",
|
"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