Summary
Teach bots to adapt their playstyle based on equipped keystones, starting with Zealot Martyrdom as proof-of-concept. A Martyrdom Zealot should stay at low health (where it gets up to +50% melee damage, +50% attack speed, -37.5% toughness DR) and refuse healing that would remove those stacks.
Requires Tertium 5/6 — vanilla bots have talents = {} and never have keystones.
Background: How Martyrdom Works
- Passive keystone — always active, no activation needed
- Grants stacking bonuses per missing health segment (max 5 stacks):
- Base: +10% melee damage per stack (max +50%)
- Optional modifier: +10% melee attack speed per stack (max +50%)
- Optional modifier: -7.5% toughness damage taken per stack (max -37.5%)
- Optional modifier: +0.1 ability CDR per stack per second (max +0.5/s)
- Internal buff:
zealot_martyrdom_base
- Talent ID:
zealot_martyrdom
Detection API
-- Option A: Check talent table (requires Tertium 5/6 to populate talents)
local talent_ext = ScriptUnit.has_extension(unit, "talent_system")
local talents = talent_ext and talent_ext:talents() or {}
local has_martyrdom = talents["zealot_martyrdom"] ~= nil
-- Option B: Check active buff stacks (works if buff is running)
local buff_ext = ScriptUnit.has_extension(unit, "buff_system")
local has_martyrdom = buff_ext and buff_ext:current_stacks("zealot_martyrdom_base") > 0
-- Stack count (for threshold scaling)
local health_ext = ScriptUnit.extension(unit, "health_system")
local max_wounds = health_ext:max_wounds()
local damage_taken = health_ext:damage_taken()
local permanent_damage = health_ext:permanent_damage_taken()
local max_health = health_ext:max_health()
local current_wounds = Health.calculate_num_segments(
math.max(damage_taken, permanent_damage), max_health, max_wounds
)
local martyrdom_stacks = math.min(max_wounds - current_wounds, 5)
API notes:
zealot_martyrdom_grants_ally_power_bonus is a modifier talent special rule, NOT the base keystone — don't use it for base detection
buff_extension:has_buff_name() does not exist — use current_stacks() instead
Health.damage_taken_percent() does not exist — use 1 - Health.current_health_percent(unit)
Gracefully degrades: vanilla bots and non-Zealots get false, no error.
Implementation Plan
1. Talent detection in build_context()
local talent_ext = ScriptUnit.has_extension(unit, "talent_system")
local talents = talent_ext and talent_ext:talents() or {}
ctx.has_martyrdom = talents["zealot_martyrdom"] ~= nil
2. Suppress healing
Bot healing uses three independent subsystems. Martyrdom bots should opt out of the first two:
| System |
What it does |
Where tracked |
Hook target |
Martyrdom behavior |
| Health stations |
Fixed level objects (medicae stations) |
_update_health_stations() in bot_behavior_extension.lua sets needs_health = true |
Hook _update_health_stations |
Skip needs_health = true — Martyrdom bot never queues, freeing charges for others |
| Deployed med-crates |
Medical crates placed on ground by players |
BotGroup._update_pickups_and_deployables_near_player() sets pickup_component.health_deployable |
Hook the bot_group function or clear health_deployable after assignment |
Skip — don't interact with deployed med-crates |
| Pocketable pickups |
Med-kits / wound cures carried in inventory |
BotGroup._update_mule_pickups() sets pickup_component.mule_pickup |
Hook _update_mule_pickups or filter by slot |
Allow wound cures (slot_pocketable_small / syringe_corruption_pocketable), block med-kits (slot_pocketable / medical_crate_pocketable) |
Wound cure exception: Wound cures heal corruption (heal_types.blessing_syringe), not green health. Martyrdom wants low green health but NOT high corruption — corruption reduces max health segments, which reduces available Martyrdom stacks. Wound cures should remain allowed.
Distinguishing med-kits from wound cures: Different pickup templates and inventory slots:
- Med-kit:
medical_crate_pocketable, slot slot_pocketable
- Wound cure:
syringe_corruption_pocketable, slot slot_pocketable_small
3. Adjust heuristic thresholds
Current Zealot heuristics treat low health as emergency → defensive ability use. Martyrdom inverts this:
| Heuristic |
Current behavior |
Martyrdom adjustment |
zealot_dash |
Activates at toughness < 30% (defensive) |
Keep — toughness recovery is still valuable at low health |
zealot_invisibility |
Emergency at health < 25% |
Raise threshold or disable health-based panic — low health is intended |
zealot_relic |
Team toughness support |
No change — team utility unrelated to Martyrdom |
The key change: don't treat low health as a crisis. A Martyrdom Zealot at 20% health with 5 stacks is at peak performance, not in danger.
4. Follow-up: cross-bot Martyrdom awareness
Current heuristics don't inspect ally health — ogryn_taunt keys off target_ally_needs_aid (a perception/disable signal, not "ally is low HP"). So there's no immediate risk of other bots panic-rescuing a low-health Martyrdom Zealot.
If future heuristics add ally-health checks (e.g. #10 rescue dash), they should exclude Martyrdom Zealots from the "allies in trouble" calculation. This is a follow-up concern, not part of the initial implementation.
5. Optional: prefer melee
All Martyrdom bonuses are melee-only. Could bias weapon selection toward melee when stacks are high, but this is a stretch goal — the existing BT melee/ranged switching works acceptably.
Future: Other Keystones
This establishes a talent-aware framework that can extend to:
| Keystone |
Class |
Behavioral change |
Detection |
| Martyrdom |
Zealot |
Don't heal, fight at low HP |
talents["zealot_martyrdom"] or current_stacks("zealot_martyrdom_base") > 0 |
| Alt Peril Explosion |
Psyker |
Higher peril tolerance (no knockdown) |
has_special_rule("psyker_no_knock_down_overload") |
| Discharge Debuff |
Psyker |
Gate Shout behind safety window (+10% dmg taken for 8s) |
has_special_rule("psyker_discharge_damage_debuff") |
| Chemical Dependency |
Broker |
Spam abilities to maintain stacking buff |
has_special_rule("broker_keystone_chemical_dependency_sub_1_crit_chance") |
| Adrenaline Junkie |
Broker |
Melee-weighted, chain abilities during Frenzy |
has_special_rule("broker_keystone_adrenaline_junkie_extra_killing_blow_stacks") |
| Carapace Armor |
Ogryn |
Stack management via Charge/Taunt timing |
has_special_rule("ogryn_carapace_armor_explosion_on_zero_stacks") |
Start with Martyrdom as the simplest (binary "don't heal" rule), then extend the framework.
Scope & Constraints
- Tertium-only: Vanilla bots have
talents = {}. Graceful degradation — feature simply doesn't activate.
- No new modules needed: Talent check in
build_context(), healing hooks in BetterBots.lua, heuristic adjustments in heuristics.lua.
- No mod settings needed initially: Auto-detect from talent. Could add a manual override toggle later ("Force Martyrdom playstyle" for vanilla bot users).
- Testing: Unit tests can mock
talent_extension in build_context(). In-game validation requires Tertium 5/6 + Zealot with Martyrdom equipped.
Acceptance Criteria
Summary
Teach bots to adapt their playstyle based on equipped keystones, starting with Zealot Martyrdom as proof-of-concept. A Martyrdom Zealot should stay at low health (where it gets up to +50% melee damage, +50% attack speed, -37.5% toughness DR) and refuse healing that would remove those stacks.
Requires Tertium 5/6 — vanilla bots have
talents = {}and never have keystones.Background: How Martyrdom Works
zealot_martyrdom_basezealot_martyrdomDetection API
API notes:
zealot_martyrdom_grants_ally_power_bonusis a modifier talent special rule, NOT the base keystone — don't use it for base detectionbuff_extension:has_buff_name()does not exist — usecurrent_stacks()insteadHealth.damage_taken_percent()does not exist — use1 - Health.current_health_percent(unit)Gracefully degrades: vanilla bots and non-Zealots get
false, no error.Implementation Plan
1. Talent detection in
build_context()2. Suppress healing
Bot healing uses three independent subsystems. Martyrdom bots should opt out of the first two:
_update_health_stations()inbot_behavior_extension.luasetsneeds_health = true_update_health_stationsneeds_health = true— Martyrdom bot never queues, freeing charges for othersBotGroup._update_pickups_and_deployables_near_player()setspickup_component.health_deployablehealth_deployableafter assignmentBotGroup._update_mule_pickups()setspickup_component.mule_pickup_update_mule_pickupsor filter by slotslot_pocketable_small/syringe_corruption_pocketable), block med-kits (slot_pocketable/medical_crate_pocketable)Wound cure exception: Wound cures heal corruption (
heal_types.blessing_syringe), not green health. Martyrdom wants low green health but NOT high corruption — corruption reduces max health segments, which reduces available Martyrdom stacks. Wound cures should remain allowed.Distinguishing med-kits from wound cures: Different pickup templates and inventory slots:
medical_crate_pocketable, slotslot_pocketablesyringe_corruption_pocketable, slotslot_pocketable_small3. Adjust heuristic thresholds
Current Zealot heuristics treat low health as emergency → defensive ability use. Martyrdom inverts this:
zealot_dashtoughness < 30%(defensive)zealot_invisibilityhealth < 25%zealot_relicThe key change: don't treat low health as a crisis. A Martyrdom Zealot at 20% health with 5 stacks is at peak performance, not in danger.
4. Follow-up: cross-bot Martyrdom awareness
Current heuristics don't inspect ally health —
ogryn_tauntkeys offtarget_ally_needs_aid(a perception/disable signal, not "ally is low HP"). So there's no immediate risk of other bots panic-rescuing a low-health Martyrdom Zealot.If future heuristics add ally-health checks (e.g. #10 rescue dash), they should exclude Martyrdom Zealots from the "allies in trouble" calculation. This is a follow-up concern, not part of the initial implementation.
5. Optional: prefer melee
All Martyrdom bonuses are melee-only. Could bias weapon selection toward melee when stacks are high, but this is a stretch goal — the existing BT melee/ranged switching works acceptably.
Future: Other Keystones
This establishes a talent-aware framework that can extend to:
talents["zealot_martyrdom"]orcurrent_stacks("zealot_martyrdom_base") > 0has_special_rule("psyker_no_knock_down_overload")has_special_rule("psyker_discharge_damage_debuff")has_special_rule("broker_keystone_chemical_dependency_sub_1_crit_chance")has_special_rule("broker_keystone_adrenaline_junkie_extra_killing_blow_stacks")has_special_rule("ogryn_carapace_armor_explosion_on_zero_stacks")Start with Martyrdom as the simplest (binary "don't heal" rule), then extend the framework.
Scope & Constraints
talents = {}. Graceful degradation — feature simply doesn't activate.build_context(), healing hooks inBetterBots.lua, heuristic adjustments inheuristics.lua.talent_extensioninbuild_context(). In-game validation requires Tertium 5/6 + Zealot with Martyrdom equipped.Acceptance Criteria
zealot_invisibilitydoes not panic-activate at low health when Martyrdom is activebuild_context()includeshas_martyrdomfield