Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
7eec454
feat(bots): add player-vs-player combat action
HarleyGilpin Apr 16, 2026
14cd8e9
feat(bots): clan wars portal-entry resolver
HarleyGilpin Apr 16, 2026
72a7b6a
feat(bots): add perception layer (BotCombatContext)
HarleyGilpin Apr 16, 2026
50ecf1d
feat(bots): pray action
HarleyGilpin Apr 16, 2026
4d84b79
feat(bots): perception conditions for combat
HarleyGilpin Apr 16, 2026
1f0f121
feat(bots): intermediate tier — prayer + style switching
HarleyGilpin Apr 17, 2026
28ebe69
feat(bots): switch_setup + retreat actions
HarleyGilpin Apr 17, 2026
1d16a59
feat(bots): utility scoring curve library
HarleyGilpin Apr 17, 2026
95c63f3
feat(bots): role detection
HarleyGilpin Apr 17, 2026
dc728e3
feat(bots): advanced tier - target calling via utility scoring
HarleyGilpin Apr 17, 2026
6c670f0
feat(bots): positioning/anti-clump awareness
HarleyGilpin Apr 17, 2026
a975716
feat(clan-wars): bot roster + spawn configuration
HarleyGilpin Apr 17, 2026
ac2ea43
bug: fix pvp bot pathing to cw ffa portals
HarleyGilpin Apr 17, 2026
46529be
fix: add reactive protection prayers to bot context
HarleyGilpin Apr 17, 2026
5a40793
feat: add spec weapon switching to pvp bots
HarleyGilpin Apr 17, 2026
08ebf81
bug: fix stop PvP bots stalling after kills
HarleyGilpin Apr 17, 2026
44068fa
bug: fix ranged pvp bots running out of ammo.
HarleyGilpin Apr 18, 2026
5409e8b
feat: add ancient mage pvp bots to clan wars FFA arena.
HarleyGilpin Apr 19, 2026
8822021
bug: fix timeout for magic based pvp bots due to survivability of kit…
HarleyGilpin Apr 19, 2026
da202fa
bug: fix issue with degraded equipment breaking pvp bots
HarleyGilpin Apr 19, 2026
43cfd24
bug: fix pvp bots from being interrupted by bank chest.
HarleyGilpin Apr 19, 2026
8663b12
bug: fix pvp bots idling due to kiting targets too far away from the …
HarleyGilpin Apr 20, 2026
04b92b2
docs: document pinned activity intent on Bot
HarleyGilpin Apr 23, 2026
5154071
refactor: rename BotRole to BotClanWarRole
HarleyGilpin Apr 23, 2026
ec51a6d
bug: widen PvP produces so activity timeout stays reachable
HarleyGilpin Apr 23, 2026
c6582ea
docs: document PvP restart loop exit conditions
HarleyGilpin Apr 23, 2026
3106625
refactor: move PvP bot spawn data to .tables.toml
HarleyGilpin Apr 23, 2026
389f08a
perf: defer spiral scan in BotCombatContext until first read
HarleyGilpin Apr 23, 2026
569b521
bug: use list<string> not list<skill> for tier skills column
HarleyGilpin Apr 23, 2026
62b9cf3
feat: add BotMetrics + /bot_stress harness for perf baseline
HarleyGilpin Apr 24, 2026
d3af568
feat: add memory benchmark to BotMetrics
HarleyGilpin Apr 24, 2026
7d07669
perf: add non-allocating Players.forEachInRadius
HarleyGilpin Apr 24, 2026
36b3ed5
perf: scan combat context by zone iteration instead of per-tile spiral
HarleyGilpin Apr 24, 2026
2de3f31
perf: add silent flag to Target.attackable, suppress in bot scans
HarleyGilpin Apr 25, 2026
bee8cf0
perf: skip target re-pick while current target valid
HarleyGilpin Apr 25, 2026
5bfc1ac
perf: only scan for loot after a confirmed kill
HarleyGilpin Apr 25, 2026
0950683
feat: cap bot looting at 27 occupied slots
HarleyGilpin Apr 25, 2026
f6a31d5
perf: remove per-tick spiral scan from BotCastSpell.chooseSpell
HarleyGilpin Apr 26, 2026
f21ca5e
feat: set loot_strategy = survival for all PvP bot templates
HarleyGilpin Apr 26, 2026
43e92dd
feat: configurable PvP bot population per FFA arena
HarleyGilpin Apr 26, 2026
d5627ec
feat: add clan wars pvp bots rules to game.properties
HarleyGilpin Apr 26, 2026
da6beab
fix: wire loot_strategy + restore loot_pending and inventory cap
HarleyGilpin Apr 26, 2026
5d17dbb
Disable pvp bot viewports
GregHib Apr 26, 2026
d825125
Fix null npc removal message
GregHib Apr 26, 2026
46d88c3
perf: look up loot at the recorded kill tile, not via spiral
HarleyGilpin Apr 26, 2026
a8abebf
feat: PvP bots drop their kit when killed in dangerous arena
HarleyGilpin Apr 26, 2026
b1b47c5
feat: skip gravestone in Clan Wars FFA dangerous arena
HarleyGilpin Apr 26, 2026
528c7da
feat: apply wilderness PvP rules in Clan Wars FFA dangerous arena
HarleyGilpin Apr 26, 2026
0e86324
feat: dangerous arena drops as if skulled, no overhead skull
HarleyGilpin Apr 26, 2026
25971f2
feat: PvP bots retreat through arena portal when out of food
HarleyGilpin Apr 26, 2026
4b582a3
test: cover BotPvpRetreatNeeded and BotInteractObject if-gate
HarleyGilpin Apr 27, 2026
1281f87
feat: PvP bots retreat via games necklace teleport
HarleyGilpin Apr 27, 2026
4c45971
feat: log when a dangerous-arena PvP bot retreats to clan_wars_teleport
HarleyGilpin Apr 27, 2026
8164080
fix: skip retreat log for death-respawn teleports
HarleyGilpin Apr 27, 2026
d1dd090
bug: fixed pvp bot retreat mechanic causing pvp bots to timeout.
HarleyGilpin Apr 27, 2026
530d2aa
feat: added new pvp bot templates for dangerous portal with less expe…
HarleyGilpin Apr 28, 2026
3942bf5
feat: added potion drinking to pvp bots
HarleyGilpin Apr 28, 2026
fd9d780
feat: add vengeance casting for melee based pvp bots with magic levels.
HarleyGilpin Apr 28, 2026
37efa4e
feat: add loudouts to pvp bots to enable hybrid switching
HarleyGilpin Apr 28, 2026
a150888
improvement: prayer switch command
HarleyGilpin Apr 20, 2026
b0f7d6f
feat: add ancient curse prayers to PVP bots
HarleyGilpin Apr 29, 2026
40395d3
improvement: added fallback for hybriding to melee.
HarleyGilpin Apr 29, 2026
8197526
bug: fixed issue with vengeance cast casting every tick and interrupt…
HarleyGilpin Apr 29, 2026
a3f4be6
improvement: bots only drop items for player killer, drops never beco…
HarleyGilpin Apr 30, 2026
405344c
feat: add potion drinking management for pvp bots
HarleyGilpin Apr 30, 2026
36ce967
feat: scoped combat bots for minigame activities
HarleyGilpin Apr 30, 2026
67eaeeb
perf: look up loot at the recorded kill tile, not via spiral
HarleyGilpin Apr 26, 2026
d5781a6
Strip dormant utility/scorer + dead conditions/actions from PR 967
HarleyGilpin May 1, 2026
e88a08a
improvement: increase default bot spawn frequency to 60 seconds, disa…
HarleyGilpin May 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions data/bot/clan_wars_arenas.tables.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[clan_wars_arenas]
row_id = "string"
spawn_area = "string"
tiers = "list<string>"

[.clan_wars_ffa_safe]
spawn_area = "clan_wars_teleport"
tiers = [
"clan_wars_ffa_safe_zerker",
"clan_wars_ffa_safe_dharoker",
"clan_wars_ffa_safe_ags_main",
"clan_wars_ffa_safe_obby_pure",
"clan_wars_ffa_safe_msb_pure",
"clan_wars_ffa_safe_karils_tank",
"clan_wars_ffa_safe_ancient_tank",
"clan_wars_ffa_safe_ancient_hybrid",
]

[.clan_wars_ffa_dangerous]
spawn_area = "clan_wars_teleport"
tiers = [
"clan_wars_ffa_dangerous_melee",
"clan_wars_ffa_dangerous_ranged",
"clan_wars_ffa_dangerous_magic",
"clan_wars_ffa_dangerous_hybrid",
]
65 changes: 65 additions & 0 deletions data/bot/clan_wars_tiers.tables.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[clan_wars_tiers]
row_id = "string"
combat_style = "string"
skills = "list<string>"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
skills = "list<string>"
skills = "list<skill>"

levels = "list<int>"

[.clan_wars_ffa_safe_zerker]
combat_style = "slash"
skills = ["attack", "strength", "defence", "constitution", "prayer"]
levels = [60, 80, 45, 75, 95]

[.clan_wars_ffa_safe_dharoker]
combat_style = "slash"
skills = ["attack", "strength", "defence", "constitution", "prayer"]
levels = [70, 70, 70, 70, 95]

[.clan_wars_ffa_safe_ags_main]
combat_style = "slash"
skills = ["attack", "strength", "defence", "constitution", "prayer"]
levels = [75, 85, 75, 85, 95]

[.clan_wars_ffa_safe_obby_pure]
combat_style = "crush"
skills = ["attack", "strength", "defence", "constitution"]
levels = [1, 80, 1, 70]

[.clan_wars_ffa_safe_msb_pure]
combat_style = "rapid"
skills = ["attack", "strength", "defence", "constitution", "ranged", "prayer"]
levels = [1, 1, 1, 70, 70, 95]

[.clan_wars_ffa_safe_karils_tank]
combat_style = "rapid"
skills = ["attack", "strength", "defence", "constitution", "ranged", "prayer"]
levels = [1, 1, 70, 75, 75, 95]

[.clan_wars_ffa_safe_ancient_tank]
combat_style = "accurate"
skills = ["magic", "defence", "constitution", "prayer"]
levels = [94, 70, 80, 95]

[.clan_wars_ffa_safe_ancient_hybrid]
combat_style = "accurate"
skills = ["magic", "attack", "strength", "ranged", "defence", "constitution", "prayer"]
levels = [94, 75, 80, 75, 70, 85, 95]

[.clan_wars_ffa_dangerous_melee]
combat_style = "slash"
skills = ["attack", "strength", "defence", "constitution", "prayer", "magic"]
levels = [60, 70, 40, 70, 95, 94]

[.clan_wars_ffa_dangerous_ranged]
combat_style = "rapid"
skills = ["ranged", "defence", "constitution", "prayer"]
levels = [70, 40, 70, 95]

[.clan_wars_ffa_dangerous_magic]
combat_style = "accurate"
skills = ["magic", "defence", "constitution", "prayer"]
levels = [94, 40, 70, 95]

[.clan_wars_ffa_dangerous_hybrid]
combat_style = "slash"
skills = ["attack", "strength", "ranged", "magic", "defence", "constitution", "prayer"]
levels = [75, 80, 75, 94, 60, 80, 95]
729 changes: 729 additions & 0 deletions data/bot/minigame_combat.templates.toml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions data/minigame/clan_wars/clan_wars.areas.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ y = [5512, 5631]
x = [2948, 3071]
y = [5571, 5631]
tags = ["multi_combat"]

[clan_wars_ffa_safe_lobby]
x = [2810, 2820]
y = [5508, 5511]

[clan_wars_ffa_dangerous_lobby]
x = [3002, 3012]
y = [5508, 5511]
87 changes: 87 additions & 0 deletions data/minigame/clan_wars/clan_wars.bots.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
[clan_wars_ffa_safe_zerker]
template = "pvp_zerker"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_dharoker]
template = "pvp_dharoker"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_ags_main]
template = "pvp_ags_main"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_obby_pure]
template = "pvp_obby_pure"
capacity = 3
timeout = 500
fields = { area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_msb_pure]
template = "pvp_msb_pure"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_karils_tank]
template = "pvp_karils_tank"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_ancient_tank]
template = "pvp_ancient_tank"
capacity = 3
timeout = 500
fields = { area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_safe_ancient_hybrid]
template = "pvp_ancient_hybrid"
capacity = 3
timeout = 500
fields = { area = "clan_wars_ffa_safe_arena", safe_area = "clan_wars_ffa_safe_lobby" }

[clan_wars_ffa_dangerous_melee]
template = "pvp_dangerous_melee"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_dangerous_arena", safe_area = "clan_wars_ffa_dangerous_lobby" }
reactive = [
{ interface = { option = "Wear", id = "inventory:inventory:games_necklace_6", if = { pvp_retreat_needed = {} }, success = { any = [{ area = { id = "clan_wars_teleport" } }, { equipment = { amulet = { id = "games_necklace_*" } } }] } } },
{ jewellery_teleport = { item = "games_necklace_6", area = "clan_wars_teleport", if = { pvp_retreat_needed = {} }, success = { area = { id = "clan_wars_teleport" } } } },
]

[clan_wars_ffa_dangerous_ranged]
template = "pvp_dangerous_ranged"
capacity = 3
timeout = 500
fields = { style = "style2", area = "clan_wars_ffa_dangerous_arena", safe_area = "clan_wars_ffa_dangerous_lobby" }
reactive = [
{ interface = { option = "Wear", id = "inventory:inventory:games_necklace_6", if = { pvp_retreat_needed = {} }, success = { any = [{ area = { id = "clan_wars_teleport" } }, { equipment = { amulet = { id = "games_necklace_*" } } }] } } },
{ jewellery_teleport = { item = "games_necklace_6", area = "clan_wars_teleport", if = { pvp_retreat_needed = {} }, success = { area = { id = "clan_wars_teleport" } } } },
]

[clan_wars_ffa_dangerous_magic]
template = "pvp_dangerous_magic"
capacity = 3
timeout = 500
fields = { area = "clan_wars_ffa_dangerous_arena", safe_area = "clan_wars_ffa_dangerous_lobby" }
reactive = [
{ interface = { option = "Wear", id = "inventory:inventory:games_necklace_6", if = { pvp_retreat_needed = {} }, success = { any = [{ area = { id = "clan_wars_teleport" } }, { equipment = { amulet = { id = "games_necklace_*" } } }] } } },
{ jewellery_teleport = { item = "games_necklace_6", area = "clan_wars_teleport", if = { pvp_retreat_needed = {} }, success = { area = { id = "clan_wars_teleport" } } } },
]

[clan_wars_ffa_dangerous_hybrid]
template = "pvp_dangerous_hybrid"
capacity = 3
timeout = 500
fields = { area = "clan_wars_ffa_dangerous_arena", safe_area = "clan_wars_ffa_dangerous_lobby" }
reactive = [
{ interface = { option = "Wear", id = "inventory:inventory:games_necklace_6", if = { pvp_retreat_needed = {} }, success = { any = [{ area = { id = "clan_wars_teleport" } }, { equipment = { amulet = { id = "games_necklace_*" } } }] } } },
{ jewellery_teleport = { item = "games_necklace_6", area = "clan_wars_teleport", if = { pvp_retreat_needed = {} }, success = { area = { id = "clan_wars_teleport" } } } },
]
68 changes: 68 additions & 0 deletions data/minigame/clan_wars/clan_wars.setups.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[teleport_clan_wars_games_necklace]
weight = 100
requires = [
{ equipment = { amulet = { id = "games_necklace_8" } } },
]
actions = [
{ interface = { option = "Clan Wars", id = "worn_equipment:amulet_slot:games_necklace_8" } },
{ wait = { ticks = 5 } },
]
produces = [
{ area = "clan_wars_teleport" },
]

[enter_clan_wars_ffa_safe]
weight = -10
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think the weight needs to be negative, 0 would be fine?

requires = [
{ combat_level = { min = 30 } },
{ area = { id = "clan_wars_teleport" } },
]
actions = [
{ object = { option = "Enter", id = "clan_wars_portal_ffa_safe", radius = 15, success = { any = [{ interface_open = { id = "warning_clan_wars_ffa_safe" } }, { area = { id = "clan_wars_ffa_safe_lobby" } }, { area = { id = "clan_wars_ffa_safe_arena" } }] } } },
{ interface = { option = "Go in", id = "warning_clan_wars_ffa_safe:yes", if = { interface_open = { id = "warning_clan_wars_ffa_safe" } }, success = { any = [{ area = { id = "clan_wars_ffa_safe_lobby" } }, { area = { id = "clan_wars_ffa_safe_arena" } }] } } },
{ tile = { x = 2815, y = 5515, radius = 2 } },
]
produces = [
{ area = "clan_wars_ffa_safe_arena" },
]

[enter_clan_wars_ffa_safe_from_lobby]
weight = -10
requires = [
{ combat_level = { min = 30 } },
{ area = { id = "clan_wars_ffa_safe_lobby" } },
]
actions = [
{ tile = { x = 2815, y = 5515, radius = 2 } },
]
produces = [
{ area = "clan_wars_ffa_safe_arena" },
]

[enter_clan_wars_ffa_dangerous]
weight = -10
requires = [
{ combat_level = { min = 30 } },
{ area = { id = "clan_wars_teleport" } },
]
actions = [
{ object = { option = "Enter", id = "clan_wars_portal_ffa_dangerous", radius = 15, success = { any = [{ interface_open = { id = "warning_clan_wars_ffa_safe" } }, { area = { id = "clan_wars_ffa_dangerous_lobby" } }, { area = { id = "clan_wars_ffa_dangerous_arena" } }] } } },
{ interface = { option = "Go in", id = "warning_clan_wars_ffa_safe:yes", if = { interface_open = { id = "warning_clan_wars_ffa_safe" } }, success = { any = [{ area = { id = "clan_wars_ffa_dangerous_lobby" } }, { area = { id = "clan_wars_ffa_dangerous_arena" } }] } } },
{ tile = { x = 3007, y = 5515, radius = 2 } },
]
produces = [
{ area = "clan_wars_ffa_dangerous_arena" },
]

[enter_clan_wars_ffa_dangerous_from_lobby]
weight = -10
requires = [
{ combat_level = { min = 30 } },
{ area = { id = "clan_wars_ffa_dangerous_lobby" } },
]
actions = [
{ tile = { x = 3007, y = 5515, radius = 2 } },
]
produces = [
{ area = "clan_wars_ffa_dangerous_arena" },
]
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ data class PrayerDefinition(
val drains: Map<String, Int> = emptyMap(),
val bonuses: Map<String, Int> = emptyMap(),
val members: Boolean = false,
val isCurse: Boolean = false,
override var stringId: String = "",
override var params: Map<Int, Any>? = null,
) : Parameterized {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ class PrayerDefinitions {
else -> throw IllegalArgumentException("Unexpected key: '$key' ${exception()}")
}
val id = stringId.substring(0, stringId.lastIndexOf('_'))
val definition = PrayerDefinition(index, level, drain, groups, drains, bonuses, members, id)
val isCurse = stringId.endsWith("_curse")
val definition = PrayerDefinition(index, level, drain, groups, drains, bonuses, members, isCurse, id)
definitions[id] = definition
if (stringId.endsWith("_curse")) {
if (isCurse) {
curses[index] = definition
} else {
prayers[index] = definition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class CharacterIndexMap(size: Int) {
/**
* Table mapping tiles to sets
*/
private val table = Int2ObjectOpenHashMap<MutableSet<Int>>(size)
@PublishedApi
internal val table = Int2ObjectOpenHashMap<MutableSet<Int>>(size)

/**
* Which tile set the index is currently in
Expand Down Expand Up @@ -51,8 +52,11 @@ class CharacterIndexMap(size: Int) {
current.fill(INVALID)
}

fun onEach(id: Int, action: (Int) -> Unit) {
table.get(id)?.onEach(action)
inline fun onEach(id: Int, action: (Int) -> Unit) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again with the inline and public, keep these things private

val set = table.get(id) ?: return
for (index in set) {
action(index)
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class CombatMovement(
character.mode = EmptyMode
return
}
if (target["dead", false]) {
character.mode = EmptyMode
return
}
if (character is NPC) {
val spawn: Tile = character["spawn_tile"] ?: return
val definition = get<CombatDefinitions>().get(character.transformDef["combat_def", character.id])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ object NPCs : Runnable,
}

fun remove(npc: NPC?): Boolean {
if (npc == null || npc.index == -1) {
if (npc == null) {
return false
}
if (npc.index == -1) {
logger.warn { "Unable to remove npc $npc." }
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import world.gregs.voidps.type.Zone

object Players : Iterable<Player>, CharacterSearch<Player> {
private val players = mutableListOf<Player>()
private val indexArray: Array<Player?> = arrayOfNulls(MAX_PLAYERS)
@PublishedApi internal val indexArray: Array<Player?> = arrayOfNulls(MAX_PLAYERS)
private var indexer = 1
private val map = CharacterIndexMap(MAX_PLAYERS)
@PublishedApi internal val map = CharacterIndexMap(MAX_PLAYERS)
val size: Int
get() = players.size

Expand Down Expand Up @@ -71,6 +71,34 @@ object Players : Iterable<Player>, CharacterSearch<Player> {
return list
}

/**
* Non-allocating chebyshev-radius scan centred on [center]. Iterates only the zones whose
* bounds overlap `[center.x ± radius]` × `[center.y ± radius]` on [center]'s level, then
* filters each zone's players by exact tile bounds.
*
* Dramatically cheaper than calling [at] per-tile in a spiral: one zone lookup per overlapping
* zone (≤ 25 for radius 15) versus 961 tile lookups each scanning the full zone.
*/
inline fun forEachInRadius(center: Tile, radius: Int, action: (Player) -> Unit) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be inline? and indexArray/map can stay private

val minZx = (center.x - radius) shr 3
val maxZx = (center.x + radius) shr 3
val minZy = (center.y - radius) shr 3
val maxZy = (center.y + radius) shr 3
val level = center.level
for (zx in minZx..maxZx) {
for (zy in minZy..maxZy) {
map.onEach(Zone.id(zx, zy, level)) { index ->
val player = indexArray[index] ?: return@onEach
val dx = player.tile.x - center.x
if (dx < -radius || dx > radius) return@onEach
val dy = player.tile.y - center.y
if (dy < -radius || dy > radius) return@onEach
action(player)
}
}
}
Comment on lines +83 to +99
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Double check this behaviour)

Suggested change
val minZx = (center.x - radius) shr 3
val maxZx = (center.x + radius) shr 3
val minZy = (center.y - radius) shr 3
val maxZy = (center.y + radius) shr 3
val level = center.level
for (zx in minZx..maxZx) {
for (zy in minZy..maxZy) {
map.onEach(Zone.id(zx, zy, level)) { index ->
val player = indexArray[index] ?: return@onEach
val dx = player.tile.x - center.x
if (dx < -radius || dx > radius) return@onEach
val dy = player.tile.y - center.y
if (dy < -radius || dy > radius) return@onEach
action(player)
}
}
}
val level = center.level
val area = center.toCuboid(radius)
for (zone in center.toCuboid(radius).toZones(level)) {
map.onEach(zone) { index ->
val player = indexArray[index] ?: return@onEach
if (player.tile !in area) {
return@onEach
}
action(player)
}
}

}

fun clear() {
for (player in this) {
Despawn.player(player)
Expand Down
9 changes: 9 additions & 0 deletions game/src/main/kotlin/content/area/wilderness/Wilderness.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package content.area.wilderness

import world.gregs.voidps.engine.data.definition.Areas
import world.gregs.voidps.engine.entity.character.Character
import world.gregs.voidps.engine.entity.character.player.Player
import world.gregs.voidps.engine.entity.character.player.combatLevel
Expand Down Expand Up @@ -35,6 +36,14 @@ val Character.inPvp: Boolean
val Character.inWilderness: Boolean
get() = get("in_wilderness", false)

/**
* True when wilderness-style PvP consequences apply: combat-start skull and full item drops on
* death. Wilderness itself, plus the Clan Wars FFA dangerous arena. Distinct from [inPvp], which
* only gates "can you attack here" and is also true in safer arenas (FFA safe, etc.).
*/
val Character.inFullPvp: Boolean
get() = inWilderness || tile in Areas["clan_wars_ffa_dangerous_arena"]

val Character.inMultiCombat: Boolean
get() = contains("in_multi_combat")

Expand Down
Loading