# Common Herb Combat System
#
# A complete combat tick in HERB. This is the first real port from
# the JavaScript implementation.
#
# Combat flow per tick:
# 1. Determine who can fight (same location, both alive)
# 2. Calculate damage (attacker's attack - defender's defense)
# 3. Apply damage (reduce HP)
# 4. Check for death (HP <= 0)
# 5. Handle loot (dead monsters drop gold)
#
# Key question: Does this feel cleaner than JavaScript, or awkward?

# === INITIAL STATE ===

# Hero setup
FACT hero is_a player
FACT hero hp 100
FACT hero max_hp 100
FACT hero attack 25
FACT hero defense 10
FACT hero location forest
FACT hero gold 50

# Equipment (bonuses computed via aggregation)
FACT hero equipped iron_sword
FACT hero equipped leather_armor
FACT iron_sword attack_bonus 10
FACT leather_armor defense_bonus 5

# Monster setup
FACT goblin is_a monster
FACT goblin hp 30
FACT goblin attack 12
FACT goblin defense 3
FACT goblin location forest
FACT goblin loot_gold 15

FACT wolf is_a monster
FACT wolf hp 20
FACT wolf attack 8
FACT wolf defense 2
FACT wolf location forest
FACT wolf loot_gold 8

# A dead monster (should be excluded from combat)
FACT skeleton is_a monster
FACT skeleton hp 0
FACT skeleton is_dead true
FACT skeleton location forest
FACT skeleton loot_gold 25

# === EQUIPMENT BONUS AGGREGATION ===

# Phase 1: Extract individual bonuses from equipped items
RULE equipment_attack_bonus
  WHEN ?p equipped ?item
  AND ?item attack_bonus ?bonus
  THEN ?p has_attack_bonus ?bonus

RULE equipment_defense_bonus
  WHEN ?p equipped ?item
  AND ?item defense_bonus ?bonus
  THEN ?p has_defense_bonus ?bonus

# Phase 2: Sum up all bonuses (aggregate)
RULE total_attack_bonus
  WHEN ?p is_a player
  THEN ?p total_attack_bonus (sum ?b WHERE ?p has_attack_bonus ?b)

RULE total_defense_bonus
  WHEN ?p is_a player
  THEN ?p total_defense_bonus (sum ?b WHERE ?p has_defense_bonus ?b)

# Compute effective stats (this is where we might want functional relations)
# For now, we just derive them - they don't replace base attack/defense
RULE effective_attack
  WHEN ?p is_a player
  AND ?p attack ?base
  AND ?p total_attack_bonus ?bonus
  THEN ?p effective_attack (+ ?base ?bonus)

RULE effective_defense
  WHEN ?p is_a player
  AND ?p defense ?base
  AND ?p total_defense_bonus ?bonus
  THEN ?p effective_defense (+ ?base ?bonus)

# Monsters use raw stats
RULE monster_effective_attack
  WHEN ?m is_a monster
  AND ?m attack ?a
  THEN ?m effective_attack ?a

RULE monster_effective_defense
  WHEN ?m is_a monster
  AND ?m defense ?d
  THEN ?m effective_defense ?d

# === COMBAT TARGETING ===

# Who can attack whom?
# Players can target monsters, monsters can target players
# Must be in same location and BOTH must be alive

# First, mark who is alive (hp > 0)
RULE mark_alive
  WHEN ?e hp ?hp
  IF (> ?hp 0)
  THEN ?e is_alive true

# NOTE: We can't use (not-exists ?e is_dead true) in a base rule
# because negation is Phase 2. This is a potential issue.
# For now, we explicitly track is_alive.

# Combat targeting - player vs monster
RULE player_can_target_monster
  WHEN ?p is_a player
  AND ?m is_a monster
  AND ?p location ?loc
  AND ?m location ?loc
  AND ?p is_alive true
  AND ?m is_alive true
  IF (not= ?p ?m)
  THEN ?p can_target ?m

# Combat targeting - monster vs player
RULE monster_can_target_player
  WHEN ?m is_a monster
  AND ?p is_a player
  AND ?m location ?loc
  AND ?p location ?loc
  AND ?m is_alive true
  AND ?p is_alive true
  THEN ?m can_target ?p

# === DAMAGE CALCULATION ===

# When a player attacks a monster they can target
# Damage = attacker's effective_attack - defender's effective_defense (min 1)
#
# TODO: This doesn't actually trigger attacks - it just shows who CAN fight.
# In the real game, attacks are triggered by player action or monster AI.
# For this demo, let's say "if you can target, you attack"

RULE player_attacks
  WHEN ?p is_a player
  AND ?p can_target ?m
  AND ?p effective_attack ?atk
  AND ?m effective_defense ?def
  THEN ?m pending_damage (max 1 (- ?atk ?def))

RULE monster_attacks
  WHEN ?m is_a monster
  AND ?m can_target ?p
  AND ?m effective_attack ?atk
  AND ?p effective_defense ?def
  THEN ?p pending_damage (max 1 (- ?atk ?def))

# === DAMAGE APPLICATION ===
# This is where explicit RETRACT gets verbose.
# Every HP update needs to retract the old value.

RULE apply_damage
  WHEN ?e hp ?old_hp
  AND ?e pending_damage ?dmg
  IF (> ?old_hp 0)
  THEN ?e hp (- ?old_hp ?dmg)
  RETRACT ?e hp ?old_hp
  RETRACT ?e pending_damage ?dmg

# === DEATH CHECK ===

RULE check_death
  WHEN ?e hp ?hp
  IF (<= ?hp 0)
  THEN ?e is_dead true
  RETRACT ?e is_alive true

# === LOOT DROP ===
# When a monster dies, player in same location gets the gold
# This uses negation to check "was not already dead"

# Actually, we need to track "just died this tick" vs "was already dead"
# This is where temporal facts help - we can check the tick the death happened

# For now, simple version: if monster is dead and has loot, player gets it
# (In practice this would fire every tick - need to consume the loot)

RULE loot_drop
  WHEN ?m is_a monster
  AND ?m is_dead true
  AND ?m loot_gold ?gold
  AND ?p is_a player
  AND ?p location ?loc
  AND ?m location ?loc
  AND ?p gold ?current
  THEN ?p gold (+ ?current ?gold)
  RETRACT ?p gold ?current
  RETRACT ?m loot_gold ?gold
