diff --git a/data/definitions/animations.yml b/data/definitions/animations.yml index c543a42fe..d02b79ff7 100644 --- a/data/definitions/animations.yml +++ b/data/definitions/animations.yml @@ -1690,4 +1690,7 @@ abyssal_guardian_attack: 2186 abyssal_guardian_death: 2189 abyssal_walker_hit: 2192 abyssal_walker_attack: 2193 -abyssal_walker_death: 2194 \ No newline at end of file +abyssal_walker_death: 2194 +gecko_hit: 7208 +gecko_attack: 7207 +gecko_death: 7205 \ No newline at end of file diff --git a/data/definitions/hunt-modes.yml b/data/definitions/hunt-modes.yml index 6f44eba51..0b91e3b79 100644 --- a/data/definitions/hunt-modes.yml +++ b/data/definitions/hunt-modes.yml @@ -6,6 +6,9 @@ aggressive: check_not_combat: true check_not_combat_self: true check_not_busy: false +aggressive_npcs: + <<: *aggressive + type: npc not_busy: <<: *aggressive check_not_busy: true diff --git a/data/definitions/interfaces.yml b/data/definitions/interfaces.yml index 4c75a5aed..e5a05c325 100644 --- a/data/definitions/interfaces.yml +++ b/data/definitions/interfaces.yml @@ -1804,6 +1804,39 @@ report_abuse_select: 594 godwars_killcount: 598 warning_wilderness2: 600 unknown_side1: 602 +ourania_bank_charge: + id: 619 + type: main_screen + components: + air_rune: 28 + mind_rune: 29 + water_rune: 30 + earth_rune: 31 + fire_rune: 32 + body_rune: 33 + cosmic_rune: 34 + chaos_rune: 35 + astral_rune: 36 + law_rune: 37 + death_rune: 38 + blood_rune: 39 + nature_rune: 40 + soul_rune: 41 + text: 44 + mind_rune_hide: 45 + air_rune_hide: 46 + water_rune_hide: 47 + earth_rune_hide: 48 + fire_rune_hide: 49 + body_rune_hide: 50 + cosmic_rune_hide: 51 + chaos_rune_hide: 52 + astral_rune_hide: 53 + law_rune_hide: 54 + death_rune_hide: 55 + blood_rune_hide: 56 + nature_rune_hide: 57 + soul_rune_hide: 58 shop: id: 620 type: main_screen diff --git a/data/definitions/item-on-item.yml b/data/definitions/item-on-item.yml index 6f8ca1872..073ec55e2 100644 --- a/data/definitions/item-on-item.yml +++ b/data/definitions/item-on-item.yml @@ -6285,9 +6285,9 @@ sapphire_bullseye_lantern_make: ticks: 1 remove: - item: bullseye_lantern_frame - - item: sapphire_lens + - item: sapphire add: - - item: sapphire_lantern + - item: sapphire_lantern_empty message: "You add the lens to the bullseye lantern frame." default_bullseye_lantern_oiled_make: skill: crafting diff --git a/data/definitions/items.yml b/data/definitions/items.yml index 5b2547b1d..f886594a1 100644 --- a/data/definitions/items.yml +++ b/data/definitions/items.yml @@ -2774,6 +2774,7 @@ fire_rune: air: [ smoke, 8.5 ] water: [ steam, 9.3 ] earth: [ lava, 10.0 ] + ourania_chance: 25.0 aka: [ fires, fire_runes ] examine: "One of the four basic elemental runes." kept: "Wilderness" @@ -2788,6 +2789,7 @@ water_rune: air: [ mist, 8.0 ] earth: [ mud, 9.5 ] fire: [ steam, 10.0 ] + ourania_chance: 25.0 aka: [ waters, water_runes ] examine: "One of the four basic elemental runes." kept: "Wilderness" @@ -2802,6 +2804,7 @@ air_rune: water: [ mist, 8.5 ] earth: [ dust, 9.0 ] fire: [ smoke, 9.5 ] + ourania_chance: 25.0 aka: [ airs, air_runes ] examine: "One of the four basic elemental runes." kept: "Wilderness" @@ -2816,6 +2819,7 @@ earth_rune: air: [ dust, 8.3 ] water: [ mud, 9.3 ] fire: [ lava, 10.5 ] + ourania_chance: 25.0 aka: [ earths, earth_runes ] examine: "One of the four basic elemental runes." kept: "Wilderness" @@ -2826,6 +2830,7 @@ mind_rune: runecrafting: xp: 5.5 levels: [ 2, 14, 28, 42, 56, 84, 98 ] + ourania_chance: 25.0 aka: [ minds, mind_runes ] examine: "Used for basic level missile spells." kept: "Wilderness" @@ -2836,6 +2841,7 @@ body_rune: runecrafting: xp: 7.5 levels: [ 20, 46, 92 ] + ourania_chance: 25.0 aka: [ bodies, bodys, body_runes ] examine: "Used for Curse spells" kept: "Wilderness" @@ -2847,6 +2853,7 @@ death_rune: xp: 10.0 levels: [ 65 ] pure: true + ourania_chance: 17.5 aka: [ deaths, death_runes ] examine: "Used for medium level missile spells." nature_rune: @@ -2857,6 +2864,7 @@ nature_rune: xp: 9.0 levels: [ 44, 91 ] pure: true + ourania_chance: 22.5 aka: [ nat, nats, natures, nature_runes ] examine: "Used for alchemy spells." chaos_rune: @@ -2867,6 +2875,7 @@ chaos_rune: xp: 8.5 levels: [ 35, 74 ] pure: true + ourania_chance: 25.0 aka: [ chaoss, chaoses, chaos_runes ] examine: "Used for low level missile spells." law_rune: @@ -2877,6 +2886,7 @@ law_rune: xp: 9.5 levels: [ 54 ] pure: true + ourania_chance: 20.0 aka: [ laws, law_runes ] examine: "Used for teleport spells." cosmic_rune: @@ -2887,6 +2897,7 @@ cosmic_rune: xp: 8.0 levels: [ 27, 59 ] pure: true + ourania_chance: 25.0 aka: [ cosmics, cosmic_runes ] examine: "Used for enchant spells." blood_rune: @@ -2897,6 +2908,7 @@ blood_rune: xp: 10.5 levels: [ 77 ] pure: true + ourania_chance: 15.0 aka: [ bloods, blood_runes ] examine: "Used for high level missile spells." soul_rune: @@ -2907,6 +2919,7 @@ soul_rune: xp: 29.7 levels: [ 90 ] pure: true + ourania_chance: 10.0 aka: [ souls, soul_runes ] examine: "Used for high level curse spells." unpowered_orb: @@ -24148,6 +24161,7 @@ medium_pouch: tradeable: false charges: 45 degrade: medium_pouch_damaged + degrade_message: "Your pouch has decayed through use." weight: 1.0 examine: "A medium-sized pouch used for storing essence." medium_pouch_damaged: @@ -24155,6 +24169,7 @@ medium_pouch_damaged: tradeable: false charges: 20 degrade: destroy + degrade_message: "Your pouch has decayed through use." weight: 1.0 examine: "A damaged medium-sized pouch used for storing essence." large_pouch: @@ -24162,6 +24177,7 @@ large_pouch: tradeable: false charges: 29 degrade: large_pouch_damaged + degrade_message: "Your pouch has decayed through use." weight: 1.0 examine: "A large pouch used for storing essence." large_pouch_damaged: @@ -24169,6 +24185,7 @@ large_pouch_damaged: tradeable: false charges: 20 degrade: destroy + degrade_message: "Your pouch has decayed through use." weight: 1.0 examine: "A large damaged pouch used for storing essence." giant_pouch: diff --git a/data/definitions/npcs.yml b/data/definitions/npcs.yml index d2f932947..c9d93ebcb 100644 --- a/data/definitions/npcs.yml +++ b/data/definitions/npcs.yml @@ -1918,4 +1918,135 @@ wizard_cromperty: brimstail: id: 171 old_model: true - examine: "Small but wise, he is." \ No newline at end of file + examine: "Small but wise, he is." +mage_of_zamorak_ourania: + id: 6370 + wander_radius: 4 + examine: "A representative of the Z.M.I." +eniola: + id: 6362 + examine: "A roving banker." +zamorak_warrior_kite: + &zamorak_warrior + id: 6363 + hitpoints: 450 + att: 85 + str: 70 + def: 90 + style: slash + max_hit_melee: 80 + wander_radius: 5 + attack_radius: 11 + respawn_delay: 50 + hunt_mode: aggressive + weapon_style: sword + drop_table: zamorak_warrior + race: human + examine: "A warrior of Zamorak." +zamorak_warrior_square: + <<: *zamorak_warrior + id: 6364 + style: crush +zamorak_mage_fancy: &zamorak_mage + id: 6367 + hitpoints: 500 + att: 75 + str: 80 + def: 80 + mage: 85 + spell: fire_bolt + max_hit_magic: 90 + respawn_delay: 50 + hunt_mode: aggressive + attack_range: 3 + attack_radius: 10 + wander_radius: 5 + race: human + drop_table: zamorak_mage + examine: "A mage of Zamorak." +zamorak_mage: + <<: *zamorak_mage + id: 6368 +zamorak_ranger: &zamorak_ranger + id: 6365 + hitpoints: 500 + att: 75 + str: 80 + def: 80 + range: 80 + max_hit_range: 90 + respawn_delay: 50 + hunt_mode: aggressive + attack_range: 5 + attack_radius: 10 + wander_radius: 5 + race: human + style: accurate + weapon_style: bow + ammo: mithril_arrow + drop_table: zamorak_ranger + height: 40 + examine: "A ranger of Zamorak." +zamorak_ranger_coat: + <<: *zamorak_ranger + id: 6366 +cave_lizard: + id: 6369 + hitpoints: 200 + att: 38 + str: 36 + def: 35 + race: gecko + style: crush + height: 15 + hunt_range: 2 + attack_range: 1 + attack_radius: 11 + wander_radius: 5 + max_hit_melee: 50 + hunt_mode: aggressive + drop_table: empty + respawn_delay: 50 + examine: "A cave dweller." +zamorak_crafter_start: + &zamorak_crafter + id: 6371 + solid: false + hitpoints: 250 + att: 15 + str: 15 + def: 12 + mage: 20 + race: human + style: crush + height: 40 + hunt_range: 2 + attack_range: 1 + attack_radius: 11 + wander_radius: 5 + max_hit_melee: 20 + drop_table: zamorak_crafter + respawn_delay: 50 + examine: "A Z.M.I. runecrafter." +zamorak_crafter_end: + <<: *zamorak_crafter + id: 6372 +monk_of_zamorak_ourania: + id: 189 + hitpoints: 100 + att: 8 + str: 8 + def: 12 + mage: 25 + race: human + style: crush + height: 40 + hunt_range: 2 + attack_range: 1 + attack_radius: 11 + wander_radius: 5 + max_hit_melee: 50 + hunt_mode: aggressive + drop_table: monk_of_zamorak + respawn_delay: 50 + examine: "An evil human cleric." \ No newline at end of file diff --git a/data/definitions/objects.yml b/data/definitions/objects.yml index 42f491dea..6c12488b0 100644 --- a/data/definitions/objects.yml +++ b/data/definitions/objects.yml @@ -11820,4 +11820,19 @@ brimstails_cave_exit_right: examine: "The way out!" rune_essence_exit_portal: id: 2492 - examine: "A portal from this mystical place." \ No newline at end of file + examine: "A portal from this mystical place." +ourania_cave_ladder_down: + id: 26849 + examine: "Into the darkness..." +ourania_cave_ladder_up: + id: 26850 + examine: "Into the light!" +ourania_crack_enter: + id: 26844 + examine: "I might be able to squeeze through." +ourania_crack_exit: + id: 26845 + examine: "I might be able to squeeze through." +ourania_altar: + id: 26847 + examine: "An altar upon which to craft runes." diff --git a/data/definitions/patrols.yml b/data/definitions/patrols.yml new file mode 100644 index 000000000..845f16569 --- /dev/null +++ b/data/definitions/patrols.yml @@ -0,0 +1,25 @@ +zamorak_crafter_to_altar: + points: + - { x: 3270, y: 4841 } + - { x: 3266, y: 4837 } + - { x: 3266, y: 4835 } + - { x: 3269, y: 4832 } + - { x: 3269, y: 4819 } + - { x: 3278, y: 4810 } + - { x: 3286, y: 4810 } + - { x: 3288, y: 4813 } + - { x: 3301, y: 4813 } + - { x: 3303, y: 4811 } + - { x: 3314, y: 4811, delay: 10 } +zamorak_crafter_to_bank: + points: + - { x: 3303, y: 4811 } + - { x: 3301, y: 4813 } + - { x: 3288, y: 4813 } + - { x: 3286, y: 4810 } + - { x: 3278, y: 4810 } + - { x: 3269, y: 4819 } + - { x: 3269, y: 4832 } + - { x: 3266, y: 4835 } + - { x: 3266, y: 4837 } + - { x: 3270, y: 4856, delay: 10 } \ No newline at end of file diff --git a/data/map/teleports.yml b/data/map/teleports.yml index 2400ef176..3cc0d9b50 100644 --- a/data/map/teleports.yml +++ b/data/map/teleports.yml @@ -5247,4 +5247,22 @@ tile: x: 3283 y: 2794 - to: *guardian_mummy_room \ No newline at end of file + to: *guardian_mummy_room +######## 9778 ######## +- id: ourania_cave_ladder_down + option: Climb + tile: + x: 2452 + y: 3231 + to: + x: 3271 + y: 4861 +######## 13131 ######## +- id: ourania_cave_ladder_up + option: Climb + tile: + x: 3271 + y: 4862 + to: + x: 2452 + y: 3232 \ No newline at end of file diff --git a/data/spawns/drops.yml b/data/spawns/drops.yml index 6cc982a9e..a297a7764 100644 --- a/data/spawns/drops.yml +++ b/data/spawns/drops.yml @@ -1,3 +1,9 @@ +empty_drop_table: + type: all + drops: + - type: all + drops: + - id: bones allotment_seed_drop_table: roll: 128 drops: @@ -1234,4 +1240,459 @@ abyssal_leech_drop_table: - roll: 256 drops: - id: clue_scroll_medium - lacks: clue_scroll_medium* \ No newline at end of file + lacks: clue_scroll_medium* +ourania_rune_table_level_0: + roll: 10000 + drops: + - id: mind_rune + chance: 5000 + - id: water_rune + chance: 2500 + - id: earth_rune + chance: 1200 + - id: fire_rune + chance: 600 + - id: body_rune + chance: 300 + - id: cosmic_rune + chance: 150 + - id: chaos_rune + chance: 85 + - id: astral_rune + chance: 60 + - id: nature_rune + chance: 45 + - id: law_rune + chance: 30 + - id: death_rune + chance: 15 + - id: blood_rune + chance: 8 + - id: soul_rune + chance: 5 +ourania_rune_table_level_1: + roll: 10000 + drops: + - id: mind_rune + chance: 1500 + - id: water_rune + chance: 1800 + - id: earth_rune + chance: 2100 + - id: fire_rune + chance: 2400 + - id: body_rune + chance: 1200 + - id: cosmic_rune + chance: 600 + - id: chaos_rune + chance: 175 + - id: astral_rune + chance: 80 + - id: nature_rune + chance: 60 + - id: law_rune + chance: 40 + - id: death_rune + chance: 24 + - id: blood_rune + chance: 12 + - id: soul_rune + chance: 6 +ourania_rune_table_level_2: + roll: 10000 + drops: + - id: mind_rune + chance: 1200 + - id: water_rune + chance: 1300 + - id: earth_rune + chance: 1350 + - id: fire_rune + chance: 1400 + - id: body_rune + chance: 1500 + - id: cosmic_rune + chance: 1600 + - id: chaos_rune + chance: 800 + - id: astral_rune + chance: 420 + - id: nature_rune + chance: 210 + - id: law_rune + chance: 110 + - id: death_rune + chance: 55 + - id: blood_rune + chance: 32 + - id: soul_rune + chance: 15 +ourania_rune_table_level_3: + roll: 1000 + drops: + - id: mind_rune + chance: 70 + - id: water_rune + chance: 80 + - id: earth_rune + chance: 90 + - id: fire_rune + chance: 110 + - id: body_rune + chance: 120 + - id: cosmic_rune + chance: 130 + - id: chaos_rune + chance: 200 + - id: astral_rune + chance: 100 + - id: nature_rune + chance: 50 + - id: law_rune + chance: 25 + - id: death_rune + chance: 13 + - id: blood_rune + chance: 6 + - id: soul_rune + chance: 4 +ourania_rune_table_level_4: + roll: 1000 + drops: + - id: mind_rune + chance: 60 + - id: water_rune + chance: 65 + - id: earth_rune + chance: 70 + - id: fire_rune + chance: 75 + - id: body_rune + chance: 80 + - id: cosmic_rune + chance: 100 + - id: chaos_rune + chance: 150 + - id: astral_rune + chance: 200 + - id: nature_rune + chance: 100 + - id: law_rune + chance: 50 + - id: death_rune + chance: 26 + - id: blood_rune + chance: 12 + - id: soul_rune + chance: 8 +ourania_rune_table_level_5: + roll: 1000 + drops: + - id: mind_rune + chance: 50 + - id: water_rune + chance: 55 + - id: earth_rune + chance: 60 + - id: fire_rune + chance: 65 + - id: body_rune + chance: 70 + - id: cosmic_rune + chance: 75 + - id: chaos_rune + chance: 100 + - id: astral_rune + chance: 110 + - id: nature_rune + chance: 150 + - id: law_rune + chance: 135 + - id: death_rune + chance: 70 + - id: blood_rune + chance: 35 + - id: soul_rune + chance: 17 +ourania_rune_table_level_6: + roll: 200 + drops: + - id: mind_rune + chance: 9 + - id: water_rune + chance: 10 + - id: earth_rune + chance: 11 + - id: fire_rune + chance: 12 + - id: body_rune + chance: 14 + - id: cosmic_rune + chance: 15 + - id: chaos_rune + chance: 19 + - id: astral_rune + chance: 21 + - id: nature_rune + chance: 28 + - id: law_rune + chance: 31 + - id: death_rune + chance: 16 + - id: blood_rune + chance: 8 + - id: soul_rune + chance: 4 +ourania_rune_table_level_7: + roll: 100 + drops: + - id: mind_rune + chance: 3 + - id: water_rune + chance: 3 + - id: earth_rune + chance: 3 + - id: fire_rune + chance: 4 + - id: body_rune + chance: 4 + - id: cosmic_rune + chance: 5 + - id: chaos_rune + chance: 7 + - id: astral_rune + chance: 9 + - id: nature_rune + chance: 12 + - id: law_rune + chance: 15 + - id: death_rune + chance: 18 + - id: blood_rune + chance: 10 + - id: soul_rune + chance: 5 +ourania_rune_table_level_8: + roll: 200 + drops: + - id: mind_rune + chance: 4 + - id: water_rune + chance: 4 + - id: earth_rune + chance: 6 + - id: fire_rune + chance: 8 + - id: body_rune + chance: 10 + - id: cosmic_rune + chance: 12 + - id: chaos_rune + chance: 14 + - id: astral_rune + chance: 16 + - id: nature_rune + chance: 21 + - id: law_rune + chance: 27 + - id: death_rune + chance: 29 + - id: blood_rune + chance: 29 + - id: soul_rune + chance: 12 +ourania_rune_table_level_9: + roll: 200 + drops: + - id: mind_rune + chance: 2 + - id: water_rune + chance: 2 + - id: earth_rune + chance: 4 + - id: fire_rune + chance: 6 + - id: body_rune + chance: 8 + - id: cosmic_rune + chance: 10 + - id: chaos_rune + chance: 12 + - id: astral_rune + chance: 14 + - id: nature_rune + chance: 20 + - id: law_rune + chance: 27 + - id: death_rune + chance: 29 + - id: blood_rune + chance: 33 + - id: soul_rune + chance: 20 +ourania_rune_table_level_10: + roll: 200 + drops: + - id: mind_rune + chance: 2 + - id: water_rune + chance: 2 + - id: earth_rune + chance: 4 + - id: fire_rune + chance: 6 + - id: body_rune + chance: 6 + - id: cosmic_rune + chance: 8 + - id: chaos_rune + chance: 10 + - id: astral_rune + chance: 12 + - id: nature_rune + chance: 19 + - id: law_rune + chance: 27 + - id: death_rune + chance: 29 + - id: blood_rune + chance: 31 + - id: soul_rune + chance: 26 +zamorak_warrior_drop_table: + type: all + drops: + - type: all + drops: + - id: bones + - roll: 50 + drops: + - id: rune_dagger + chance: 2 + - id: adamant_longsword + - id: steel_scimitar + chance: 2 + - id: steel_dagger + chance: 2 + - id: rune_scimitar + - roll: 50 + drops: + - id: coins + chance: 40 + amount: 100 + - id: lobster + chance: 2 + amount: 3 +zamorak_mage_drop_table: + type: all + drops: + - type: all + drops: + - id: bones + - roll: 50 + drops: + - id: fire_rune + chance: 2 + amount: 7 + - id: water_rune + chance: 2 + amount: 6 + - id: air_rune + chance: 2 + amount: 5 + - id: nature_rune + chance: 2 + amount: 2 + - id: law_rune + amount: 2 + - id: blood_rune + - roll: 50 + drops: + - id: coins + chance: 40 + amount: 100 +zamorak_ranger_drop_table: + type: all + drops: + - type: all + drops: + - id: bones + - roll: 50 + drops: + - id: willow_shortbow + chance: 2 + - id: willow_longbow + - id: steel_arrow + chance: 2 + amount: 10 + - id: mithril_arrow + chance: 2 + amount: 10 + - id: mithril_arrow + amount: 20 + - id: bow_string_noted + chance: 2 + amount: 10 + - roll: 50 + drops: + - id: coins + chance: 40 + amount: 100 +zamorak_crafter_drop_table: + type: all + drops: + - type: all + drops: + - id: bones + - roll: 70 + drops: + - id: air_rune + chance: 10 + amount: 5 + - id: body_rune + chance: 5 + amount: 3 + - id: blood_rune + chance: 2 + amount: 4 + - id: chaos_rune + chance: 5 + amount: 4 + - id: death_rune + chance: 2 + amount: 3 + - id: earth_rune + chance: 5 + amount: 6 + - id: water_rune + chance: 10 + amount: 5 + - id: soul_rune + chance: 2 + amount: 3 + - id: fire_rune + chance: 10 + amount: 2 + - id: law_rune + amount: 3 + - id: nature_rune + chance: 2 + amount: 2 + - id: pure_essence + chance: 5 + amount: 3 + - id: pure_essence + amount: 10 + - id: coins + chance: 10 + amount: 50 +monk_of_zamorak_drop_table: + type: all + drops: + - drops: + - id: bones + - roll: 20 + drops: + - id: zamorak_monk_bottom + - id: zamorak_monk_top \ No newline at end of file diff --git a/data/spawns/npc-spawns.yml b/data/spawns/npc-spawns.yml index dab4df533..7f72939af 100644 --- a/data/spawns/npc-spawns.yml +++ b/data/spawns/npc-spawns.yml @@ -447,6 +447,17 @@ - { id: giant_spider, x: 2488, y: 2888, members: true } - { id: giant_spider, x: 2494, y: 2888, members: true } - { id: giant_spider, x: 2490, y: 2883, members: true } +# 9778 +- { id: mage_of_zamorak_ourania, x: 2455, y: 3233, members: true } +- { id: monk_of_zamorak_ourania, x: 2454, y: 3247, members: true } +- { id: monk_of_zamorak_ourania, x: 2455, y: 3240, members: true } +- { id: monk_of_zamorak_ourania, x: 2465, y: 3248, members: true } +- { id: monk_of_zamorak_ourania, x: 2468, y: 3243, members: true } +- { id: monk_of_zamorak_ourania, x: 2470, y: 3243, members: true } +- { id: monk_of_zamorak_ourania, x: 2473, y: 3241, members: true } +- { id: monk_of_zamorak_ourania, x: 2474, y: 3248, members: true } +- { id: monk_of_zamorak_ourania, x: 2477, y: 3238, members: true } +- { id: monk_of_zamorak_ourania, x: 2477, y: 3244, members: true } # 9779 - { id: rat, x: 2468, y: 3326, members: true } - { id: rat, x: 2473, y: 3326, members: true } @@ -2114,6 +2125,28 @@ - { id: musician_lumber_yard, x: 3291, y: 3455, direction: WEST } # 13110 - { id: man_3, x: 3283, y: 3501 } +# 13131 +- { id: eniola, x: 3269, y: 4857, direction: SOUTH, members: true } +- { id: zamorak_warrior_square, x: 3271, y: 4815, members: true } +- { id: zamorak_warrior_kite, x: 3271, y: 4838, members: true } +- { id: zamorak_warrior_kite, x: 3284, y: 4806, members: true } +- { id: zamorak_mage, x: 3268, y: 4810, members: true } +- { id: zamorak_mage_fancy, x: 3273, y: 4809, members: true } +- { id: zamorak_mage_fancy, x: 3291, y: 4813, members: true } +- { id: zamorak_ranger, x: 3267, y: 4828, members: true } +- { id: zamorak_ranger_coat, x: 3287, y: 4806, members: true } +- { id: zamorak_ranger, x: 3301, y: 4808, members: true } +- { id: cave_lizard, x: 3266, y: 4806, members: true } +- { id: cave_lizard, x: 3266, y: 4836, members: true } +- { id: cave_lizard, x: 3269, y: 4806, members: true } +- { id: cave_lizard, x: 3270, y: 4804, members: true } +- { id: cave_lizard, x: 3271, y: 4804, members: true } +- { id: cave_lizard, x: 3284, y: 4803, members: true } +- { id: cave_lizard, x: 3288, y: 4802, members: true } +- { id: cave_lizard, x: 3289, y: 4805, members: true } +- { id: cave_lizard, x: 3301, y: 4811, members: true } +- { id: zamorak_crafter_start, x: 3270, y: 4856, members: true } +- { id: zamorak_crafter_end, x: 3314, y: 4811, members: true } # 13210 - { id: rat, x: 3269, y: 9869 } - { id: rat, x: 3274, y: 9873 } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt index 6bfc3213d..0da765858 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/EngineModules.kt @@ -20,7 +20,10 @@ import world.gregs.voidps.engine.entity.item.drop.DropTables import world.gregs.voidps.engine.entity.item.floor.FloorItemTracking import world.gregs.voidps.engine.entity.item.floor.FloorItems import world.gregs.voidps.engine.entity.obj.GameObjects -import world.gregs.voidps.engine.map.collision.* +import world.gregs.voidps.engine.map.collision.CollisionStrategyProvider +import world.gregs.voidps.engine.map.collision.Collisions +import world.gregs.voidps.engine.map.collision.GameObjectCollisionAdd +import world.gregs.voidps.engine.map.collision.GameObjectCollisionRemove import world.gregs.voidps.engine.map.zone.DynamicZones import world.gregs.voidps.network.client.ConnectionQueue import world.gregs.voidps.type.Tile @@ -30,7 +33,7 @@ import java.io.File val engineModule = module { // Entities - single { NPCs(get(), get(), get()) } + single { NPCs(get(), get(), get(), get()) } single { Players() } single { GameObjects(get(), get(), get(), get(), getProperty("loadUnusedObjects") == "true").apply { get().register(this) } } single { FloorItems(get(), get()).apply { get().register(this) } } @@ -93,6 +96,7 @@ val engineModule = module { single(createdAtStart = true) { VariableDefinitions().load() } single(createdAtStart = true) { JingleDefinitions().load() } single(createdAtStart = true) { SpellDefinitions().load() } + single(createdAtStart = true) { PatrolDefinitions().load() } single(createdAtStart = true) { PrayerDefinitions().load() } single(createdAtStart = true) { GearDefinitions().load() } single(createdAtStart = true) { ItemOnItemDefinitions().load() } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/HuntModeDefinition.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/HuntModeDefinition.kt index 41df5af6e..e3f72efbe 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/HuntModeDefinition.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/HuntModeDefinition.kt @@ -10,13 +10,13 @@ import world.gregs.voidps.engine.entity.obj.ObjectLayer * @param checkNotCombat checks if target is already in combat * @param checkNotCombatSelf checks if npc is in combat * @param checkNotBusy checks target doesn't have menu open - * @param checkAfk check if target is afk after 10 minutes (dark beasts don't check) - * @param checkTolerance check if target has tolerance by being in the same area for a while (custom) + * @param checkAfk check if target is "afk" aka has tolerance by staying in the same area for 10 minutes (dark beasts don't check) * @param findKeepHunting unknown * @param pauseIfNobodyNear stop finding new target when no players are around * @param rate ticks between checking for new targets. Non-player targets have min 3 ticks. * @param id the id for object or floor item target * @param layer the [ObjectLayer] for object targets + * @param maxMultiAttackers maximum number of attackers the target can have (custom) */ data class HuntModeDefinition( val type: String, @@ -25,13 +25,13 @@ data class HuntModeDefinition( val checkNotCombat: Boolean = true, val checkNotCombatSelf: Boolean = true, val checkNotBusy: Boolean = true, - val checkAfk: Boolean = true, - val checkTolerance: Boolean = true, + val checkAfk: Boolean = type == "player", val findKeepHunting: Boolean = false, val pauseIfNobodyNear: Boolean = true, val rate: Int = if (type == "player") 1 else 3, val id: String? = null, - val layer: Int = -1 + val layer: Int = -1, + val maxMultiAttackers: Int = 2, ) { var filter: ((Entity) -> Boolean)? = null @@ -65,6 +65,7 @@ data class HuntModeDefinition( pauseIfNobodyNear = map["pause_if_nobody_near"] as? Boolean ?: EMPTY.pauseIfNobodyNear, id = map["id"] as? String ?: EMPTY.id, rate = map["rate"] as? Int ?: EMPTY.rate, + maxMultiAttackers = map["max_multi_attackers"] as? Int ?: EMPTY.maxMultiAttackers, ) } } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/PatrolDefinition.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/PatrolDefinition.kt new file mode 100644 index 000000000..b2d299592 --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/config/PatrolDefinition.kt @@ -0,0 +1,22 @@ +package world.gregs.voidps.engine.data.config + +import world.gregs.voidps.cache.definition.Extra +import world.gregs.voidps.type.Tile + +data class PatrolDefinition( + override var stringId: String = "", + val waypoints: List> = emptyList(), + override var extras: Map? = null +) : Extra { + companion object { + operator fun invoke(key: String, map: Map): PatrolDefinition { + val extras = map.toMutableMap() + val points = extras.remove("points") as? List> ?: emptyList() + val waypoints = points.map { + val delay = it["delay"] as? Int ?: 0 + Tile(it["x"] as Int, it["y"] as Int, it["level"] as? Int ?: 0) to delay + } + return PatrolDefinition(key, waypoints, extras) + } + } +} \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemOnItemDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemOnItemDefinitions.kt index 992930f58..730187103 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemOnItemDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemOnItemDefinitions.kt @@ -34,7 +34,7 @@ class ItemOnItemDefinitions { super.add(list, if (value is Map<*, *>) { val id = value["item"] as String if (itemDefinitions != null && !itemDefinitions.contains(id)) { - logger.warn { "Invalid item id: $id" } + logger.warn { "Invalid item-on-item id: $id" } } Item(id, value["amount"] as? Int ?: 1) } else { diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/PatrolDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/PatrolDefinitions.kt new file mode 100644 index 000000000..f758f817f --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/PatrolDefinitions.kt @@ -0,0 +1,45 @@ +package world.gregs.voidps.engine.data.definition + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import world.gregs.voidps.engine.data.config.PatrolDefinition +import world.gregs.voidps.engine.get +import world.gregs.voidps.engine.getProperty +import world.gregs.voidps.engine.timedLoad +import world.gregs.yaml.Yaml +import world.gregs.yaml.read.YamlReaderConfiguration + +class PatrolDefinitions { + + private lateinit var definitions: Map + + fun get(key: String) = definitions[key] ?: PatrolDefinition() + + @Suppress("UNCHECKED_CAST") + fun load(yaml: Yaml = get(), path: String = getProperty("patrolDefinitionsPath")): PatrolDefinitions { + timedLoad("patrol definition") { + val definitions = Object2ObjectOpenHashMap() + val config = object : YamlReaderConfiguration() { + override fun set(map: MutableMap, key: String, value: Any, indent: Int, parentMap: String?) { + if (key == "<<") { + map.putAll(value as Map) + return + } + if (indent == 0) { + definitions[key] = if (value is Map<*, *>) { + PatrolDefinition(key, value as MutableMap) + } else { + PatrolDefinition(stringId = key) + } + } else { + super.set(map, key, value, indent, parentMap) + } + } + } + yaml.load(path, config) + this.definitions = definitions + this.definitions.size + } + return this + } + +} \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Rune.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Rune.kt index 5922d6a7b..93d30b881 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Rune.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Rune.kt @@ -7,12 +7,14 @@ import world.gregs.voidps.engine.entity.character.player.skill.Skill * @param xp experience from successfully crafting a rune * @param pure whether pure essence is required * @param levels level required for each amount of runes created + * @param doubleChance of getting double at ourania altar with medium ardougne diary */ data class Rune( val xp: Double = 0.0, val pure: Boolean = false, val levels: IntArray = intArrayOf(), - val combinations: Map> = emptyMap() + val combinations: Map> = emptyMap(), + val doubleChance: Double = 0.0 ) { fun multiplier(player: Player): Int { var multiplier = 1 @@ -51,6 +53,7 @@ data class Rune( pure = map["pure"] as? Boolean ?: EMPTY.pure, levels = (map["levels"] as? List)?.toIntArray() ?: EMPTY.levels, combinations = (map["combinations"] as? Map>) ?: EMPTY.combinations, + doubleChance = (map["ourania_chance"] as? Double) ?: EMPTY.doubleChance, ) val EMPTY = Rune() diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Face.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Face.kt index 058b19300..de17da06e 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Face.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Face.kt @@ -30,8 +30,8 @@ class Face( } } - override fun stop() { - super.stop() + override fun stop(replacement: Mode) { + super.stop(replacement) character.clearWatch() } } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Follow.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Follow.kt index 3eacbc998..108a90baa 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Follow.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Follow.kt @@ -65,7 +65,9 @@ class Follow( override fun onCompletion() { } - override fun stop() { - character.clearWatch() + override fun stop(replacement: Mode) { + if (replacement !is Follow) { + character.clearWatch() + } } } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Mode.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Mode.kt index 1d870815f..2dabdde0e 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Mode.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Mode.kt @@ -8,5 +8,5 @@ import world.gregs.voidps.engine.entity.character.Character interface Mode { fun start() {} fun tick() - fun stop() {} + fun stop(replacement: Mode) {} } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Patrol.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Patrol.kt index 1c78bb5bb..edb7ad4c9 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Patrol.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Patrol.kt @@ -51,8 +51,11 @@ class Patrol( super.tick() } - override fun stop() { - super.stop() + override fun onCompletion() { + } + + override fun stop(replacement: Mode) { + super.stop(replacement) character.clear("patrol_delay") character.clear("patrol_index") } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Rest.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Rest.kt index e6bf6dc5e..2a784b929 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Rest.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/Rest.kt @@ -25,7 +25,7 @@ class Rest( override fun tick() { } - override fun stop() { + override fun stop(replacement: Mode) { val type = player["movement_temp", "walk"] player["movement"] = type player.start("movement_delay", if (type == "walk") 2 else 1) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt index 5f51302d1..96c5e5c2c 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/combat/CombatMovement.kt @@ -5,6 +5,7 @@ import world.gregs.voidps.engine.client.variable.hasClock import world.gregs.voidps.engine.client.variable.stop import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.EmptyMode +import world.gregs.voidps.engine.entity.character.mode.Mode import world.gregs.voidps.engine.entity.character.mode.Retreat import world.gregs.voidps.engine.entity.character.mode.interact.Interact import world.gregs.voidps.engine.entity.character.mode.move.Movement @@ -108,7 +109,9 @@ class CombatMovement( override fun onCompletion() { } - override fun stop() { - character.emit(CombatStop(target)) + override fun stop(replacement: Mode) { + if (replacement !is CombatMovement || replacement.target != target) { + character.emit(CombatStop(target)) + } } } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaEntered.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaEntered.kt index e02f4fb2a..b31bd909f 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaEntered.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaEntered.kt @@ -2,6 +2,7 @@ package world.gregs.voidps.engine.entity.character.mode.move import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.CharacterContext +import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.event.EventDispatcher import world.gregs.voidps.engine.event.Events @@ -35,7 +36,15 @@ fun enterArea(area: String = "*", tag: String = "*", handler: suspend AreaEntere } fun npcEnterArea(npc: String = "*", area: String = "*", tag: String = "*", handler: suspend AreaEntered.() -> Unit) { - Events.handle("npc_enter", area, npc, tag, "*") { + Events.handle("npc_enter", area, npc, tag, "*") { handler.invoke(this) } +} + +fun characterEnterArea(area: String = "*", tag: String = "*", handler: suspend AreaEntered.() -> Unit) { + val block: suspend AreaEntered.(EventDispatcher) -> Unit = { + handler.invoke(this) + } + Events.handle("player_enter", area, "player", tag, "*", handler = block) + Events.handle("npc_enter", area, "*", tag, "*", handler = block) } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaExited.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaExited.kt index af407cfc0..dfbe960ef 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaExited.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/AreaExited.kt @@ -2,6 +2,7 @@ package world.gregs.voidps.engine.entity.character.mode.move import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.CharacterContext +import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.event.EventDispatcher import world.gregs.voidps.engine.event.Events @@ -35,7 +36,15 @@ fun exitArea(area: String = "*", tag: String = "*", handler: suspend AreaExited. } fun npcExitArea(npc: String = "*", area: String = "*", tag: String = "*", handler: suspend AreaExited.() -> Unit) { - Events.handle("npc_exit", area, npc, tag, "*") { + Events.handle("npc_exit", area, npc, tag, "*") { handler.invoke(this) } +} + +fun characterExitArea(area: String = "*", tag: String = "*", handler: suspend AreaExited.() -> Unit) { + val block: suspend AreaExited.(EventDispatcher) -> Unit = { + handler.invoke(this) + } + Events.handle("player_exit", area, "player", tag, "*", handler = block) + Events.handle("npc_exit", area, "*", tag, "*", handler = block) } \ No newline at end of file diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/PathFinder.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/PathFinder.kt index 94e402e32..6346b79ab 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/PathFinder.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/PathFinder.kt @@ -25,7 +25,7 @@ fun PathFinder.findPath(character: Character, strategy: TargetStrategy, shape: I ) fun StepValidator.canTravel(character: Character, x: Int, y: Int): Boolean { - val flag = if (character is NPC) CollisionFlag.BLOCK_PLAYERS or CollisionFlag.BLOCK_NPCS else 0 + val flag = if (character is NPC && character.def["solid", true]) CollisionFlag.BLOCK_PLAYERS or CollisionFlag.BLOCK_NPCS else 0 return canTravel( level = character.tile.level, x = character.tile.x, diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt index 8d41e9176..5f2f02dbc 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPC.kt @@ -27,7 +27,7 @@ data class NPC( override var mode: Mode = EmptyMode set(value) { - field.stop() + field.stop(value) field = value value.start() } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPCs.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPCs.kt index 35468eed5..c431b6ea1 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPCs.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/NPCs.kt @@ -1,6 +1,7 @@ package world.gregs.voidps.engine.entity.character.npc import com.github.michaelbull.logging.InlineLogger +import world.gregs.voidps.engine.data.definition.AreaDefinitions import world.gregs.voidps.engine.data.definition.NPCDefinitions import world.gregs.voidps.engine.entity.Despawn import world.gregs.voidps.engine.entity.MAX_NPCS @@ -9,6 +10,7 @@ import world.gregs.voidps.engine.entity.character.CharacterList import world.gregs.voidps.engine.entity.character.CharacterMap import world.gregs.voidps.engine.entity.character.face import world.gregs.voidps.engine.entity.character.mode.Wander +import world.gregs.voidps.engine.entity.character.mode.move.AreaEntered import world.gregs.voidps.engine.entity.character.player.skill.Skill import world.gregs.voidps.engine.getProperty import world.gregs.voidps.engine.map.collision.CollisionStrategyProvider @@ -21,7 +23,8 @@ import world.gregs.voidps.type.Zone data class NPCs( private val definitions: NPCDefinitions, private val collisions: Collisions, - private val collision: CollisionStrategyProvider + private val collision: CollisionStrategyProvider, + private val areaDefinitions: AreaDefinitions ) : CharacterList() { override val indexArray: Array = arrayOfNulls(MAX_NPCS) private val logger = InlineLogger() @@ -86,6 +89,11 @@ data class NPCs( npc["respawn_direction"] = direction } npc.emit(Spawn) + for (def in areaDefinitions.get(npc.tile.zone)) { + if (npc.tile in def.area) { + npc.emit(AreaEntered(npc, def.name, def.tags, def.area)) + } + } return npc } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/hunt/Hunting.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/hunt/Hunting.kt index 152c0f36c..514293fcb 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/hunt/Hunting.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/npc/hunt/Hunting.kt @@ -23,8 +23,10 @@ import world.gregs.voidps.engine.entity.obj.GameObjects import world.gregs.voidps.type.Direction import world.gregs.voidps.type.Tile import world.gregs.voidps.type.Zone +import world.gregs.voidps.type.random import java.util.* import kotlin.math.ceil +import kotlin.random.Random /** * Picks new target for an [NPC] based on it's [HuntModeDefinition] @@ -40,12 +42,13 @@ class Hunting( private val objects: GameObjects, private val floorItems: FloorItems, private val huntModes: HuntModeDefinitions, - private val lineValidator: LineValidator + private val lineValidator: LineValidator, + private val seed: Random = random ) : Runnable { override fun run() { for (npc in npcs) { - val mode: String = npc.def.getOrNull("hunt_mode") ?: continue + val mode: String = npc["hunt_mode"] ?: npc.def.getOrNull("hunt_mode") ?: continue if (npc.hasClock("delay") || npc.dec("hunt_count_down") >= 0) { continue } @@ -55,22 +58,22 @@ class Hunting( when (definition.type) { "player" -> { val targets = getCharacters(npc, players, range, definition) - val target = targets.randomOrNull() ?: continue + val target = targets.randomOrNull(seed) ?: continue npc.emit(HuntPlayer(mode, target)) } "npc" -> { val targets = getCharacters(npc, npcs, range, definition) - val target = targets.randomOrNull() ?: continue + val target = targets.randomOrNull(seed) ?: continue npc.emit(HuntNPC(mode, target)) } "object" -> { val targets = getObjects(npc, definition) - val target = targets.randomOrNull() ?: continue + val target = targets.randomOrNull(seed) ?: continue npc.emit(HuntObject(mode, target)) } "floor_item" -> { val targets = getItems(npc, range, definition) - val target = targets.randomOrNull() ?: continue + val target = targets.randomOrNull(seed) ?: continue npc.emit(HuntFloorItem(mode, target)) } } @@ -184,34 +187,34 @@ class Hunting( } /** - * Checks if [character] meets all the [definition] requirements + * Checks if [target] meets all the [definition] requirements */ private fun canHunt( npc: NPC, - character: Character, + target: Character, definition: HuntModeDefinition, range: Int ): Boolean { // Npc checks from south-west tile - if (character.tile.distanceTo(npc.tile) > range) { + if (target.tile.distanceTo(npc.tile) > range) { return false } - if (!canSee(npc, character.tile, character.size, character.size, definition)) { + if (!canSee(npc, target.tile, target.size, target.size, definition)) { return false } - if (definition.checkNotTooStrong && targetTooStrong(npc, character)) { + if (definition.checkNotTooStrong && targetTooStrong(npc, target)) { return false } - if (definition.checkNotCombat && character.hasClock("in_combat")) { + if (definition.checkNotCombat && target.hasClock("in_combat") && !target.contains("in_multi_combat")) { return false } if (definition.checkNotCombatSelf && npc.hasClock("in_combat")) { return false } - if (definition.checkTolerance && !character.hasClock("tolerance")) { + if (definition.checkAfk && !target.hasClock("tolerance")) { return false } - if (definition.checkNotBusy && (character.hasClock("delay") || character.hasMenuOpen())) { + if (definition.checkNotBusy && (target.hasClock("delay") || target.hasMenuOpen())) { return false } return true diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Player.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Player.kt index 82a815c5c..37b7f6fce 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Player.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/Player.kt @@ -48,7 +48,7 @@ class Player( override var mode: Mode = EmptyMode set(value) { - field.stop() + field.stop(value) field = value value.start() } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/skill/level/Level.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/skill/level/Level.kt index 59955fb3e..cd2a60d4d 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/skill/level/Level.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/player/skill/level/Level.kt @@ -37,7 +37,6 @@ object Level { return interpolate(level.coerceIn(MIN_LEVEL, MAX_LEVEL), chances.first, chances.last, MIN_LEVEL, MAX_LEVEL) } - fun experience(skill: Skill, level: Int) = experience(if (skill == Skill.Constitution) level / 10 else level) fun experience(level: Int): Double = (1 until level) diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/CharacterUpdateTaskTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/CharacterUpdateTaskTest.kt index 8b286715f..1f093d9d3 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/CharacterUpdateTaskTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/CharacterUpdateTaskTest.kt @@ -11,6 +11,7 @@ import world.gregs.voidps.engine.client.update.batch.ZoneBatchUpdates import world.gregs.voidps.engine.client.update.iterator.SequentialIterator import world.gregs.voidps.engine.client.update.npc.NPCUpdateTask import world.gregs.voidps.engine.client.update.player.PlayerUpdateTask +import world.gregs.voidps.engine.data.definition.AreaDefinitions import world.gregs.voidps.engine.entity.character.npc.NPCs import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.Players @@ -26,7 +27,7 @@ internal class CharacterUpdateTaskTest : KoinMock() { private lateinit var batches: ZoneBatchUpdates override val modules = listOf( module { - single { NPCs(get(), get(), get()) } + single { NPCs(get(), get(), get(), AreaDefinitions()) } single { Players() } } ) diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/npc/NPCUpdateTaskTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/npc/NPCUpdateTaskTest.kt index d46d03bdc..2f97425ea 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/npc/NPCUpdateTaskTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/client/update/npc/NPCUpdateTaskTest.kt @@ -13,6 +13,7 @@ import world.gregs.voidps.buffer.write.BufferWriter import world.gregs.voidps.buffer.write.Writer import world.gregs.voidps.cache.definition.data.NPCDefinition import world.gregs.voidps.engine.client.update.view.Viewport +import world.gregs.voidps.engine.data.definition.AreaDefinitions import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.npc.NPCs import world.gregs.voidps.engine.entity.character.player.Player @@ -32,7 +33,7 @@ internal class NPCUpdateTaskTest : KoinMock() { private lateinit var viewport: Viewport override val modules = listOf( module { - single { NPCs(get(), get(), get()) } + single { NPCs(get(), get(), get(), AreaDefinitions()) } } ) private lateinit var encoder: VisualEncoder diff --git a/game/src/main/kotlin/world/gregs/voidps/world/command/admin/AdminCommands.kts b/game/src/main/kotlin/world/gregs/voidps/world/command/admin/AdminCommands.kts index 26b2d05c7..7bd235dde 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/command/admin/AdminCommands.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/command/admin/AdminCommands.kts @@ -366,6 +366,7 @@ adminCommand("reload") { "music", "music effects", "jingles" -> get().load() "interfaces" -> get().load() "spells" -> get().load() + "patrols", "paths" -> get().load() "prayers" -> get().load() "drops" -> get().load() "cs2", "cs2s", "client scripts" -> get().load() diff --git a/game/src/main/kotlin/world/gregs/voidps/world/command/debug/DebugCommands.kts b/game/src/main/kotlin/world/gregs/voidps/world/command/debug/DebugCommands.kts index efc35f776..a6d82c7d8 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/command/debug/DebugCommands.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/command/debug/DebugCommands.kts @@ -11,6 +11,9 @@ import world.gregs.voidps.engine.client.ui.event.adminCommand import world.gregs.voidps.engine.client.ui.event.modCommand import world.gregs.voidps.engine.client.ui.open import world.gregs.voidps.engine.client.variable.PlayerVariables +import world.gregs.voidps.engine.data.definition.PatrolDefinitions +import world.gregs.voidps.engine.entity.character.mode.Patrol +import world.gregs.voidps.engine.entity.character.move.tele import world.gregs.voidps.engine.entity.character.npc.NPCs import world.gregs.voidps.engine.entity.character.player.chat.ChatType import world.gregs.voidps.engine.entity.character.player.rights @@ -41,6 +44,12 @@ modCommand("test") { println(player.rights) } +modCommand("patrol") { + val patrol = get().get(content) + player.tele(patrol.waypoints.first().first) + player.mode = Patrol(player, patrol.waypoints) +} + modCommand("reset_cam") { player.client?.clearCamera() } diff --git a/game/src/main/kotlin/world/gregs/voidps/world/community/clan/LootShare.kts b/game/src/main/kotlin/world/gregs/voidps/world/community/clan/LootShare.kts index 60e4d0bf8..17e4dbd50 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/community/clan/LootShare.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/community/clan/LootShare.kts @@ -7,11 +7,16 @@ import world.gregs.voidps.engine.entity.character.player.chat.ChatType import world.gregs.voidps.engine.entity.character.player.chat.clan.Clan import world.gregs.voidps.engine.entity.character.player.chat.clan.ClanRank import world.gregs.voidps.engine.entity.character.player.chat.clan.clanChatLeave +import world.gregs.voidps.engine.entity.playerSpawn import world.gregs.voidps.engine.timer.timerStart import world.gregs.voidps.engine.timer.timerTick import world.gregs.voidps.engine.timer.toTicks import java.util.concurrent.TimeUnit +playerSpawn { player -> + player.sendVariable("loot_share") +} + interfaceOption(component = "loot_share", id = "clan_chat") { val clan = player.clan ?: return@interfaceOption if (clan.lootRank == ClanRank.None) { diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Combat.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Combat.kts index 4861619fc..33592a464 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Combat.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Combat.kts @@ -99,6 +99,7 @@ characterCombatStop { character -> } else { character.clearWatch() } + character.target?.attackers?.remove(character) character.target = null } @@ -130,6 +131,12 @@ fun retaliate(character: Character, source: Character) { if (character.dead || character.levels.get(Skill.Constitution) <= 0 || !retaliates(character)) { return } + if (character is Player && character.mode != EmptyMode) { + return + } + if (character is NPC && character.mode is CombatMovement && character.hasClock("in_combat")) { + return + } character.mode = CombatMovement(character, source) character.target = source val delay = character.attackSpeed / 2 diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Target.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Target.kt index 1bb786a1e..ade9dcc08 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Target.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Target.kt @@ -49,7 +49,7 @@ object Target { } } } - if (target.inSingleCombat && target.inCombat && !target.attackers.contains(source)) { + if (target.inSingleCombat && target.inCombat && !target.attackers.contains(source) && source.target != target) { if (target is NPC) { (source as? Player)?.message("Someone else is fighting that.") } else { @@ -57,7 +57,7 @@ object Target { } return false } - if (source.inSingleCombat && source.inCombat && !source.attackers.contains(target)) { + if (source.inSingleCombat && source.inCombat && !source.attackers.contains(target) && source.target != target) { (source as? Player)?.message("You are already in combat.") return false } diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Weapon.kt b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Weapon.kt index bbd9e561d..d4f961c9f 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Weapon.kt +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/combat/Weapon.kt @@ -3,6 +3,7 @@ package world.gregs.voidps.world.interact.entity.combat import world.gregs.voidps.engine.client.ui.chat.toInt import world.gregs.voidps.engine.client.variable.hasClock import world.gregs.voidps.engine.client.variable.start +import world.gregs.voidps.engine.data.definition.WeaponStyleDefinitions import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.player.Player @@ -10,6 +11,7 @@ import world.gregs.voidps.engine.entity.character.player.equip.equipped import world.gregs.voidps.engine.entity.character.player.skill.Skill import world.gregs.voidps.engine.entity.distanceTo import world.gregs.voidps.engine.entity.item.Item +import world.gregs.voidps.engine.get import world.gregs.voidps.network.login.protocol.visual.update.player.EquipSlot import world.gregs.voidps.type.random import world.gregs.voidps.world.interact.entity.player.combat.magic.spell.spell @@ -83,10 +85,12 @@ object Weapon { if (character.spell.isNotBlank()) { return "magic" } - return when (weapon.def["weapon_style", 0]) { - 13, 16, 17, 18, 19, 24 -> "range" - 20 -> if (character.attackType == "aim_and_fire") "range" else "melee" - 21 -> when (character.attackType) { + val definitions = get() + val style = if (character is NPC) definitions.get(character.def["weapon_style", "unarmed"]) else definitions.get(weapon.def["weapon_style", 0]) + return when (style.stringId) { + "pie", "bow", "crossbow", "thrown", "chinchompa", "sling" -> "range" + "fixed_device" -> if (character.attackType == "aim_and_fire") "range" else "melee" + "salamander" -> when (character.attackType) { "blaze" -> "blaze" "scorch" -> "scorch" else -> "range" diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Aggression.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Aggression.kts index fedc6cf2e..ff0dbb706 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Aggression.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Aggression.kts @@ -1,23 +1,48 @@ package world.gregs.voidps.world.interact.entity.npc.combat +import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.mode.combat.CombatMovement import world.gregs.voidps.engine.entity.character.mode.interact.Interact +import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.npc.NPCOption import world.gregs.voidps.engine.entity.character.npc.hunt.huntNPC import world.gregs.voidps.engine.entity.character.npc.hunt.huntPlayer import world.gregs.voidps.engine.entity.character.player.PlayerOption huntPlayer(mode = "aggressive") { npc -> + if (attacking(npc, target)) { + return@huntPlayer + } npc.mode = Interact(npc, target, PlayerOption(npc, target, "Attack")) } huntPlayer(mode = "cowardly") { npc -> + if (attacking(npc, target)) { + return@huntPlayer + } npc.mode = Interact(npc, target, PlayerOption(npc, target, "Attack")) } huntNPC(mode = "aggressive") { npc -> + if (attacking(npc, target)) { + return@huntNPC + } npc.mode = Interact(npc, target, NPCOption(npc, target, target.def, "Attack")) } huntNPC(mode = "cowardly") { npc -> + if (attacking(npc, target)) { + return@huntNPC + } npc.mode = Interact(npc, target, NPCOption(npc, target, target.def, "Attack")) +} + +fun attacking(npc: NPC, target: Character): Boolean { + val current = npc.mode + if (current is Interact && current.target == target) { + return true + } else if (current is CombatMovement && current.target == target) { + return true + } + return false } \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Attack.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Attack.kts index dd2824f73..3c1b2ea94 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Attack.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/Attack.kts @@ -34,7 +34,8 @@ fun attackAnimation(npc: NPC): String { if (npc.def.contains("weapon_style")) { val id = npc.def["weapon_style", "unarmed"] val styleDefinition = definitions.get(id) - var style = styleDefinition.combatStyles.indexOf(npc.def["style"]) + val styleName: String? = npc.def.getOrNull("style") + var style = styleName?.let { styleDefinition.combatStyles.indexOf(it) } ?: -1 if (style == -1) { style = 0 } diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Archers.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Archers.kts new file mode 100644 index 000000000..ade0105d9 --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Archers.kts @@ -0,0 +1,8 @@ +package world.gregs.voidps.world.interact.entity.npc.combat.type + +import world.gregs.voidps.world.interact.entity.combat.npcCombatPrepare +import world.gregs.voidps.world.interact.entity.player.combat.range.ammo + +npcCombatPrepare { npc -> + npc.ammo = npc.def.getOrNull("ammo") ?: return@npcCombatPrepare +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Wizards.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Wizards.kts new file mode 100644 index 000000000..0433cb809 --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/npc/combat/type/Wizards.kts @@ -0,0 +1,8 @@ +package world.gregs.voidps.world.interact.entity.npc.combat.type + +import world.gregs.voidps.world.interact.entity.combat.npcCombatPrepare +import world.gregs.voidps.world.interact.entity.player.combat.magic.spell.spell + +npcCombatPrepare { npc -> + npc.spell = npc.def.getOrNull("spell") ?: return@npcCombatPrepare +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/obj/Stairs.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/obj/Stairs.kts index aacc8d616..4cba16267 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/obj/Stairs.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/obj/Stairs.kts @@ -28,7 +28,7 @@ teleportTakeOff { if (remaining > 0) { delay = remaining } else if (remaining < 0) { - player.setAnimation(if (option == "Climb-down") "climb_down" else "climb_up") + player.setAnimation(if (option == "Climb-down" || obj.stringId.endsWith("_down")) "climb_down" else "climb_up") player.start("teleport_delay", 2) delay = 2 } diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/range/Ranged.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/range/Ranged.kts index 7bc35847e..f7a243573 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/range/Ranged.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/combat/range/Ranged.kts @@ -3,15 +3,13 @@ package world.gregs.voidps.world.interact.entity.player.combat.range import world.gregs.voidps.engine.data.definition.WeaponAnimationDefinitions import world.gregs.voidps.engine.data.definition.WeaponStyleDefinitions import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.setAnimation import world.gregs.voidps.engine.entity.character.setGraphic import world.gregs.voidps.engine.inject -import world.gregs.voidps.world.interact.entity.combat.attackType -import world.gregs.voidps.world.interact.entity.combat.combatPrepare -import world.gregs.voidps.world.interact.entity.combat.combatSwing +import world.gregs.voidps.world.interact.entity.combat.* import world.gregs.voidps.world.interact.entity.combat.hit.hit -import world.gregs.voidps.world.interact.entity.combat.weapon import world.gregs.voidps.world.interact.entity.player.combat.special.SpecialAttack import world.gregs.voidps.world.interact.entity.player.combat.special.specialAttack import world.gregs.voidps.world.interact.entity.proj.shoot @@ -27,56 +25,59 @@ combatPrepare("range") { player -> } combatSwing(style = "scorch") { player -> - extracted(player, target) + swing(player, target) } -combatSwing(style = "range") { player -> - extracted(player, target) +characterCombatSwing(style = "range") { character -> + swing(character, target) } -fun extracted(player: Player, target: Character) { - var ammo = player.ammo - val required = Ammo.requiredAmount(player.weapon, player.specialAttack) - if (player.specialAttack && SpecialAttack.drain(player)) { - val id: String = player.weapon.def.getOrNull("special") ?: return - player.emit(SpecialAttack(id, target)) - return +fun swing(character: Character, target: Character) { + var ammo = character.ammo + val style = if (character is NPC) weaponStyles.get(character.def["weapon_style", "unarmed"]) else weaponStyles.get(character.weapon.def["weapon_style", 0]) + if (character is Player) { + val required = Ammo.requiredAmount(character.weapon, character.specialAttack) + if (character.specialAttack && SpecialAttack.drain(character)) { + val id: String = character.weapon.def.getOrNull("special") ?: return + character.emit(SpecialAttack(id, target)) + return + } + if (style.stringId != "sling") { + Ammo.remove(character, target, ammo, required) + } } - val style = weaponStyles.get(player.weapon.def["weapon_style", 0]) - if (style.stringId != "sling") { - Ammo.remove(player, target, ammo, required) - } else { - player.setAnimation(ammo) + if (style.stringId == "sling") { + character.setAnimation(ammo) } if (style.stringId == "crossbow") { ammo = if (ammo == "barbed_bolts" || ammo == "bone_bolts" || ammo == "hand_cannon_shot") ammo else "crossbow_bolt" } else if (style.stringId == "bow" && ammo.endsWith("brutal")) { ammo = "brutal_arrow" } - var time = player.shoot(id = ammo, target = target) - val weapon = player.weapon.id + var time = character.shoot(id = ammo, target = target) + val weapon = character.weapon.id when (style.stringId) { "thrown" -> { - val ammoName = player.ammo.removePrefix("corrupt_").removeSuffix("_p++").removeSuffix("_p+").removeSuffix("_p") - player.setGraphic("${ammoName}_throw") + val ammoName = character.ammo.removePrefix("corrupt_").removeSuffix("_p++").removeSuffix("_p+").removeSuffix("_p") + character.setGraphic("${ammoName}_throw") if (weapon.contains("dart")) { - player.playSound("dart_throw") + character.playSound("dart_throw") } else if (weapon.contains("javelin")) { - player.playSound("javelin_throw") + character.playSound("javelin_throw") } else if (weapon.contains("knife")) { - player.playSound("knife_throw") + character.playSound("knife_throw") } else if (weapon.contains("axe")) { - player.playSound("axe_throw") + character.playSound("axe_throw") } else { - player.playSound("thrown") + character.playSound("thrown") } } "bow" -> { - player.setGraphic("${if (ammo.endsWith("brutal")) "brutal" else ammo}_shoot") + character.setGraphic("${if (ammo.endsWith("brutal")) "brutal" else ammo}_shoot") if (weapon.contains("shortbow")) { - player.playSound("shortbow_shoot") + character.playSound("shortbow_shoot") } else { - player.playSound("longbow_shoot") + character.playSound("longbow_shoot") } } "fixed_device" -> { @@ -84,16 +85,16 @@ fun extracted(player: Player, target: Character) { } "salamander" -> { time = 0 - player.setGraphic("salamander_${player.attackType}") + character.setGraphic("salamander_${character.attackType}") } - "crossbow" -> player.playSound("crossbow_shoot") + "crossbow" -> character.playSound("crossbow_shoot") } - val type = player.weapon.def.getOrNull("weapon_type") ?: style.stringId + val type = character.weapon.def.getOrNull("weapon_type") ?: style.stringId val definition = animationDefinitions.get(type) - var animation = definition.attackTypes.getOrDefault(player.attackType, definition.attackTypes["default"]) + var animation = definition.attackTypes.getOrDefault(character.attackType, definition.attackTypes["default"]) if (animation == null) { - animation = "${style.stringId}_${player.attackType}" + animation = "${style.stringId}_${character.attackType}" } - player.setAnimation(animation) - player.hit(target, delay = if (time == -1) 64 else time) + character.setAnimation(animation) + character.hit(target, delay = if (time == -1) 64 else time) } \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/effect/Skull.kts b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/effect/Skull.kts index 8b896098c..a7aedf392 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/effect/Skull.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/interact/entity/player/effect/Skull.kts @@ -1,6 +1,5 @@ package world.gregs.voidps.world.interact.entity.player.effect -import world.gregs.voidps.engine.entity.character.Character import world.gregs.voidps.engine.entity.character.mode.combat.combatStart import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.appearance @@ -9,6 +8,7 @@ import world.gregs.voidps.engine.entity.playerSpawn import world.gregs.voidps.engine.timer.timerStart import world.gregs.voidps.engine.timer.timerStop import world.gregs.voidps.engine.timer.timerTick +import world.gregs.voidps.world.interact.entity.combat.attackers import world.gregs.voidps.world.interact.entity.combat.inWilderness playerSpawn { player -> @@ -18,7 +18,7 @@ playerSpawn { player -> } combatStart { player -> - if (player.inWilderness && target is Player && player.get>("attackers")?.contains(target) != true) { + if (player.inWilderness && target is Player && !player.attackers.contains(target)) { player.skull() } } diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/edgeville/MultiCombat.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/edgeville/MultiCombat.kts index 4d2eb2f48..9783505ca 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/map/edgeville/MultiCombat.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/edgeville/MultiCombat.kts @@ -1,15 +1,15 @@ package world.gregs.voidps.world.map.edgeville import world.gregs.voidps.engine.client.variable.variableSet -import world.gregs.voidps.engine.entity.character.mode.move.enterArea -import world.gregs.voidps.engine.entity.character.mode.move.exitArea +import world.gregs.voidps.engine.entity.character.mode.move.characterEnterArea +import world.gregs.voidps.engine.entity.character.mode.move.characterExitArea -enterArea(tag = "multi_combat") { - player["in_multi_combat"] = true +characterEnterArea(tag = "multi_combat") { + character["in_multi_combat"] = true } -exitArea(tag = "multi_combat") { - player.clear("in_multi_combat") +characterExitArea(tag = "multi_combat") { + character.clear("in_multi_combat") } variableSet("in_multi_combat", to = true) { player -> diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/CaveLizard.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/CaveLizard.kts new file mode 100644 index 000000000..cc9c616ac --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/CaveLizard.kts @@ -0,0 +1,31 @@ +package world.gregs.voidps.world.map.ourania + +import world.gregs.voidps.engine.entity.character.mode.interact.Interact +import world.gregs.voidps.engine.entity.character.npc.NPCOption +import world.gregs.voidps.engine.entity.character.npc.hunt.huntNPC +import world.gregs.voidps.engine.entity.character.npc.hunt.huntPlayer +import world.gregs.voidps.engine.entity.character.player.PlayerOption +import world.gregs.voidps.engine.entity.npcSpawn +import world.gregs.voidps.engine.timer.npcTimerTick +import world.gregs.voidps.engine.timer.timerStart +import world.gregs.voidps.type.random + +npcSpawn("cave_lizard") { npc -> + npc.softTimers.start("aggressive_hunt_mode_switch") +} + +huntNPC("cave_lizard", "zamorak_*", "aggressive_npcs") { npc -> + npc.mode = Interact(npc, target, NPCOption(npc, target, target.def, "Attack")) +} + +huntPlayer("cave_lizard", "aggressive") { npc -> + npc.mode = Interact(npc, target, PlayerOption(npc, target, "Attack")) +} + +timerStart("aggressive_hunt_mode_switch") { + interval = random.nextInt(6, 12) +} + +npcTimerTick("aggressive_hunt_mode_switch") { npc -> + npc["hunt_mode"] = if (random.nextBoolean()) "aggressive" else "aggressive_npcs" +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/Eniola.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/Eniola.kts new file mode 100644 index 000000000..fc26d13bd --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/Eniola.kts @@ -0,0 +1,161 @@ +package world.gregs.voidps.world.map.ourania + +import world.gregs.voidps.engine.client.ui.close +import world.gregs.voidps.engine.client.ui.dialogue.continueDialogue +import world.gregs.voidps.engine.client.ui.event.interfaceOpen +import world.gregs.voidps.engine.client.ui.interfaceOption +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.CharacterContext +import world.gregs.voidps.engine.entity.character.npc.NPCOption +import world.gregs.voidps.engine.entity.character.npc.npcOperate +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.queue.queue +import world.gregs.voidps.engine.suspend.dialogue.StringSuspension +import world.gregs.voidps.engine.suspend.resumeDialogueSuspension +import world.gregs.voidps.world.community.trade.lend.Loan.getSecondsRemaining +import world.gregs.voidps.world.interact.dialogue.* +import world.gregs.voidps.world.interact.dialogue.type.ChoiceBuilder +import world.gregs.voidps.world.interact.dialogue.type.choice +import world.gregs.voidps.world.interact.dialogue.type.npc +import world.gregs.voidps.world.interact.dialogue.type.player + +npcOperate("Talk-to", "eniola") { + npc("Well met, fellow adventurer! How can I help you?") + val loanReturned = getSecondsRemaining(player, "lend_timeout") < 0 + val collection = false + if (loanReturned) { + npc("Before we go any further, I should inform you that an item you lent out has been returned to you.") + } else if (collection) { + npc("Before we go any further, I should inform you that you have items ready for collection from the Grand Exchange.") + } + choice { + option("Who are you?") { + npc("How frightfully rude of me, my dear chap. My name is Eniola and I work for that most excellent enterprise, the Bank of Gielinor.") + choice { + option("If you work for the bank, what are you doing here?") { + npc("My presence here is the start of a new enterprise of travelling banks.") + npc("I, and others like me, will provide you with the convenience of having banking facilities where they will be of optimum use to you.") + player("So...what are you doing here?") + npc("The Z.M.I., that is, the Zamorakian Magical Institute, requested my services upon discovery of this altar. We at the Bank of Gielinor are a neutral party and are willing to offer our services regardless of affiliation.") + npc("So that is why I am here.") + player("Can I access my bank account by speaking to you?") + npc("Of course, dear sir.") + npc("However, I must inform you that because the Z.M.I. are paying for my services, they require anyone not part of the Institute to pay an access fee to open their bank account.") + npc("But, as our goal as travelling bankers is to make our customers' lives more convenient, we have accomodated to your needs. We know you will be busy creating runes and do not wish to carry money with you. The") + npc("charge to open your account is the small amount of twenty of one type of rune. The type of rune is up to you.") + npc("Would you like to pay the price of twenty runes to open your bank account?") + choice { + option("Yes please.") { + player.open("ourania_bank_charge") + } + option("Let me open my account and then I'll give you the runes.") { + player("I don't have the runes on me. Let me open my account and then I'll give them to you.") + npc("It's not that I don't trust you, old chap, but as the old adage goes: 'Payment comes before friends.'") + } + option("No way! I'm not paying to withdraw my own money.") { + player("That's preposterous! I'm not paying runes to withdraw my own stuff.") + npc("I'm sorry to hear that, sir. If you should reconsider, because I believe this service offers excellent value for money, do not hesitate to contact me.") + } + } + } + accessBank() + pinSettings() + collectionBox() + } + } + accessBank() + pinSettings() + collectionBox() + } +} + +npcOperate("Bank", "eniola") { + openBank() +} + +npcOperate("Collect", "eniola") { + openCollection() +} + +fun ChoiceBuilder.accessBank() { + option("I'd like to access my bank account, please.") { + openBank() + } +} + +fun ChoiceBuilder.collectionBox() { + option("I'd like to see my collection box.") { + openCollection() + } +} + +fun ChoiceBuilder.pinSettings() { + option("I'd like to check my PIN settings.") { + } +} + +suspend fun NPCOption.openCollection() { + if (runePayment()) { + player.open("collection_box") + } +} + + +suspend fun NPCOption.openBank() { + if (runePayment()) { + player.open("bank") + } +} + +val runes = listOf("air_rune", + "mind_rune", + "water_rune", + "earth_rune", + "fire_rune", + "body_rune", + "cosmic_rune", + "chaos_rune", + "astral_rune", + "law_rune", + "death_rune", + "blood_rune", + "nature_rune", + "soul_rune" +) + +suspend fun CharacterContext.runePayment(): Boolean { + player.open("ourania_bank_charge") + val rune = StringSuspension() + player.close("ourania_bank_charge") + + if (!player.inventory.remove(rune, 20)) { + npc("I'm afraid you don't have the necessary runes with you at this time, so I can't allow you to access your account. Please bring twenty runes of one type and you can open your account.") + return false + } + return true +} + +interfaceOpen("ourania_bank_charge") { player -> + for (rune in runes) { + player.interfaces.sendVisibility(id, "${rune}_hide", !player.inventory.contains(rune, 20)) + } + player.interfaces.sendText("ourania_bank_charge", "text", "Choose a highlighted rune to make your payment.") +} + +continueDialogue("ourania_bank_charge", "*_rune") { player -> + val suspension = player.dialogueSuspension as? StringSuspension ?: return@continueDialogue + suspension.string = component + player.resumeDialogueSuspension() +} + +interfaceOption("* Runes", "*_rune", "ourania_bank_charge") { + if (player.inventory.remove(id, 20)) { + val id = player["ourania_interface", "bank"] + player.open(id) + } else { + player.queue("not_enough_runes") { + npc("I'm afraid you don't have the necessary runes with you at this time, so I can't allow you to access your account. Please bring twenty runes of one type and you can open your account.") + } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/MageOfZamorakOurania.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/MageOfZamorakOurania.kts new file mode 100644 index 000000000..16fa41a98 --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/MageOfZamorakOurania.kts @@ -0,0 +1,21 @@ +package world.gregs.voidps.world.map.ourania + +import world.gregs.voidps.engine.entity.character.npc.npcOperate +import world.gregs.voidps.world.interact.dialogue.* +import world.gregs.voidps.world.interact.dialogue.type.npc +import world.gregs.voidps.world.interact.dialogue.type.player + +npcOperate("Talk-to", "mage_of_zamorak_ourania") { + player("What's that ladder next to the altar for?") + npc("An...archeological dig. Yeah, a dig.") + npc("Why?") + player("Next to a Chaos Altar? That's a bit odd. Can I go down and have a look?") + npc("Well...I suppose. See, we found this ancient altar, but it was pretty broken so the Z.M.I. sent some of its researchers to try and repair it.") + player("How'd that work out for them?") + npc("Pretty well - only one died. Still, they got it working...sort of. This ancient technology can be tricky.") + player("What do you mean, 'sort of'? Altars either work or don't work.") + npc("This one works, just not as you'd expect. You put pure essence in, but get random runes back. Some of them we don't even know how to craft!") + player("Sounds like a pretty good deal. Can I give it a go?") + npc("I don't think so. The mages don't like outsiders going in; they'll probably attack you. They only patrol the short path, though, so if you used the long path, through a tunnel near the entrance, you'd probably be safe.") + player("Thanks.") +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltar.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltar.kts new file mode 100644 index 000000000..9cbc5eb0a --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltar.kts @@ -0,0 +1,72 @@ +package world.gregs.voidps.world.map.ourania + +import com.github.michaelbull.logging.InlineLogger +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.ui.interact.itemOnObjectOperate +import world.gregs.voidps.engine.client.variable.start +import world.gregs.voidps.engine.data.definition.data.Rune +import world.gregs.voidps.engine.entity.character.player.chat.ChatType +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.exp.exp +import world.gregs.voidps.engine.entity.character.setAnimation +import world.gregs.voidps.engine.entity.character.setGraphic +import world.gregs.voidps.engine.entity.item.drop.DropTables +import world.gregs.voidps.engine.entity.item.drop.ItemDrop +import world.gregs.voidps.engine.entity.obj.objectOperate +import world.gregs.voidps.engine.inject +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItemLimit.removeToLimit +import world.gregs.voidps.type.random +import world.gregs.voidps.world.interact.entity.sound.playSound + +val drops: DropTables by inject() +val logger = InlineLogger() + +objectOperate("Craft-rune", "ourania_altar") { + val level = player.levels.get(Skill.Runecrafting) + val table = drops.get("ourania_rune_table_level_${if (level >= 99) 10 else level / 10}") ?: return@objectOperate + var experience = 0.0 + var usedArdougneCloak = false + player.inventory.transaction { + val essence = removeToLimit("pure_essence", 28) + if (essence == 0) { + error = TransactionError.Deficient() + return@transaction + } + val runes = mutableListOf() + for (i in 0 until essence) { + table.role(list = runes) + } + for (drop in runes) { + val item = drop.toItem() + val rune: Rune = item.def["runecrafting"] + val amount = if (player["ardougne_medium_diary_complete", false] && random.nextDouble(100.0) <= rune.doubleChance) 2 else 1 + usedArdougneCloak = usedArdougneCloak || amount == 2 + add(item.id, amount) + experience += rune.xp * 2.0 + } + } + player.start("movement_delay", 3) + when (player.inventory.transaction.error) { + is TransactionError.Deficient, is TransactionError.Invalid -> { + player.message("You don't have any pure essences to bind.") + } + TransactionError.None -> { + player.exp(Skill.Runecrafting, experience) + player.setAnimation("bind_runes") + player.setGraphic("bind_runes") + player.playSound("bind_runes") + player.message("You bind the temple's power into runes.", ChatType.Filter) + if (usedArdougneCloak) { + player.message("Your Ardougne cloak seems to shimmer with power.", ChatType.Filter) + } + } + else -> logger.warn { "Error binding runes $player ${player.levels.get(Skill.Runecrafting)} $experience" } + } +} + +itemOnObjectOperate("*_talisman", "ourania_altar") { + player.message("Your talisman has no effect on the altar.") +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaCrack.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaCrack.kts new file mode 100644 index 000000000..82bfdbaf4 --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/OuraniaCrack.kts @@ -0,0 +1,22 @@ +package world.gregs.voidps.world.map.ourania + +import world.gregs.voidps.engine.client.ui.open +import world.gregs.voidps.engine.entity.character.move.tele +import world.gregs.voidps.engine.entity.obj.objectOperate +import world.gregs.voidps.engine.suspend.delay + +objectOperate("Squeeze-through", "ourania_crack_enter") { + player.open("fade_out") + delay(3) + player.tele(3312, 4817) + delay(1) + player.open("fade_in") +} + +objectOperate("Squeeze-through", "ourania_crack_exit") { + player.open("fade_out") + delay(3) + player.tele(3308, 4819) + delay(1) + player.open("fade_in") +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/ZamorakCrafter.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/ZamorakCrafter.kts new file mode 100644 index 000000000..03f07ea45 --- /dev/null +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/ourania/ZamorakCrafter.kts @@ -0,0 +1,44 @@ +package world.gregs.voidps.world.map.ourania + +import world.gregs.voidps.engine.data.definition.PatrolDefinitions +import world.gregs.voidps.engine.entity.character.face +import world.gregs.voidps.engine.entity.character.mode.Patrol +import world.gregs.voidps.engine.entity.character.mode.move.npcMove +import world.gregs.voidps.engine.entity.character.setAnimation +import world.gregs.voidps.engine.entity.character.setGraphic +import world.gregs.voidps.engine.entity.npcSpawn +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.inject +import world.gregs.voidps.engine.queue.queue +import world.gregs.voidps.engine.queue.softQueue +import world.gregs.voidps.type.Tile + +val objects: GameObjects by inject() +val patrols: PatrolDefinitions by inject() + +npcSpawn("zamorak_crafter*") { npc -> + val patrol = patrols.get(if (npc.id == "zamorak_crafter_start") "zamorak_crafter_to_altar" else "zamorak_crafter_to_bank") + npc.mode = Patrol(npc, patrol.waypoints) +} + +npcMove("zamorak_crafter*", to = Tile(3314, 4811)) { + val altar = objects[Tile(3315, 4810), "ourania_altar"] + if (altar != null) { + npc.face(altar) + } + npc.queue("bind", 4) { + npc.setAnimation("bind_runes") + npc.setGraphic("bind_runes") + } + npc.softQueue("return_to_bank", 8) { + val patrol = patrols.get("zamorak_crafter_to_bank") + npc.mode = Patrol(npc, patrol.waypoints) + } +} + +npcMove("zamorak_crafter*", to = Tile(3270, 4856)) { + npc.softQueue("return_to_altar", 5) { + val patrol = patrols.get("zamorak_crafter_to_altar") + npc.mode = Patrol(npc, patrol.waypoints) + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/world/gregs/voidps/world/map/varrock/abyss/DarkMage.kts b/game/src/main/kotlin/world/gregs/voidps/world/map/varrock/abyss/DarkMage.kts index 85aba6b53..213d3a3d1 100644 --- a/game/src/main/kotlin/world/gregs/voidps/world/map/varrock/abyss/DarkMage.kts +++ b/game/src/main/kotlin/world/gregs/voidps/world/map/varrock/abyss/DarkMage.kts @@ -2,6 +2,7 @@ package world.gregs.voidps.world.map.varrock.abyss import world.gregs.voidps.engine.entity.character.npc.NPCOption import world.gregs.voidps.engine.entity.character.npc.npcOperate +import world.gregs.voidps.engine.inv.add import world.gregs.voidps.engine.inv.inventory import world.gregs.voidps.engine.inv.transact.operation.ReplaceItem.replace import world.gregs.voidps.world.activity.bank.ownsItem @@ -74,13 +75,17 @@ fun ChoiceBuilder.needHelp() { choice { option("Can I have another Abyssal book?") { if (player.ownsItem("abyssal_book")) { - // TODO + npc("You already have one, don't waste my time.") // TODO proper message } else { if (player.inventory.isFull()) { npc("Don't waste my time if you don't have enough free space to take it.") } else { npc("Here, take it. It is important to pool our research.") - item("abyssal_book", 400, "You have been given a book.") + if (player.inventory.add("abyssal_book")) { + item("abyssal_book", 400, "You have been given a book.") + } else { + item("abyssal_book", 400, "The mage tries to hand you a book, but you don't have enough room to take it.") // TODO proper message + } choice { askForPouch() option("Thanks.") { diff --git a/game/src/main/resources/game.properties b/game/src/main/resources/game.properties index 5ca70b8d0..bcd187394 100644 --- a/game/src/main/resources/game.properties +++ b/game/src/main/resources/game.properties @@ -63,6 +63,7 @@ renderEmoteDefinitionsPath=./data/definitions/render-emotes.yml midiDefinitionsPath=./data/definitions/midis.yml jingleDefinitionsPath=./data/definitions/jingles.yml spellDefinitionsPath=./data/definitions/spells.yml +patrolDefinitionsPath=./data/definitions/patrols.yml prayerDefinitionsPath=./data/definitions/prayers.yml itemOnItemDefinitionsPath=./data/definitions/item-on-item.yml gearDefinitionsPath=./data/definitions/gear-sets.yml diff --git a/game/src/test/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltarTest.kt b/game/src/test/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltarTest.kt new file mode 100644 index 000000000..78da261f2 --- /dev/null +++ b/game/src/test/kotlin/world/gregs/voidps/world/map/ourania/OuraniaAltarTest.kt @@ -0,0 +1,44 @@ +package world.gregs.voidps.world.map.ourania + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Test +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.type.Tile +import world.gregs.voidps.world.script.WorldTest +import world.gregs.voidps.world.script.objectOption + +internal class OuraniaAltarTest : WorldTest() { + + @Test + fun `Craft random runes with pure essence`() { + val player = createPlayer("player", Tile(3315, 4813)) + player.inventory.add("pure_essence", 28) + + val altar = objects[Tile(3315, 4810), "ourania_altar"]!! + player.objectOption(altar, "Craft-rune") + tick(1) + + assertFalse(player.inventory.contains("pure_essence")) + assertEquals(28, player.inventory.count("mind_rune")) + assertEquals(308.0, player.experience.get(Skill.Runecrafting)) + } + + @Test + fun `Craft random runes with ardougne medium diary`() { + val player = createPlayer("player", Tile(3315, 4813)) + player.inventory.add("pure_essence", 28) + player["ardougne_medium_diary_complete"] = true + + val altar = objects[Tile(3315, 4810), "ourania_altar"]!! + player.objectOption(altar, "Craft-rune") + tick(1) + + assertFalse(player.inventory.contains("pure_essence")) + assertEquals(56, player.inventory.count("mind_rune")) + assertEquals(308.0, player.experience.get(Skill.Runecrafting)) + } + +} \ No newline at end of file diff --git a/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt b/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt index c420d2263..0b5c8e8e1 100644 --- a/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt +++ b/game/src/test/kotlin/world/gregs/voidps/world/script/WorldTest.kt @@ -28,6 +28,7 @@ import world.gregs.voidps.engine.data.definition.* import world.gregs.voidps.engine.entity.World import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.engine.entity.character.npc.NPCs +import world.gregs.voidps.engine.entity.character.npc.hunt.Hunting import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.character.player.Players import world.gregs.voidps.engine.entity.item.Item @@ -160,7 +161,11 @@ abstract class WorldTest : KoinTest { single { mapDefinitions } single { collisions } single { objectCollisionAdd } + single { objectCollisionAdd } single { objectCollisionRemove } + single { Hunting(get(), get(), get(), get(), get(), get(), object : FakeRandom() { + override fun nextBits(bitCount: Int) = 0 + }) } }) } loadScripts() diff --git a/game/src/test/resources/test.properties b/game/src/test/resources/test.properties index fcc6996fb..9b8176999 100644 --- a/game/src/test/resources/test.properties +++ b/game/src/test/resources/test.properties @@ -30,6 +30,7 @@ midiDefinitionsPath=../data/definitions/midis.yml jingleDefinitionsPath=../data/definitions/jingles.yml itemOnItemDefinitionsPath=../data/definitions/item-on-item.yml spellDefinitionsPath=../data/definitions/spells.yml +patrolDefinitionsPath=../data/definitions/patrols.yml prayerDefinitionsPath=../data/definitions/prayers.yml gearDefinitionsPath=../data/definitions/gear-sets.yml enumDefinitionsPath=../data/definitions/enums.yml