# combat.ludic — the co-op turn-based battle state machine. # The battle is a state machine over the `turn_phase` variable. `machine` # dispatches on its value and `become` transitions to a named state — no more # `if ph == N` ladders. Knight turn (0,1), Mage turn (2,3), Enemy turn (4). # Menu selections, named instead of matched on bare integers (menu_cursor holds one). enum KnightAct { Attack, Guard, Item, Flee } enum MageAct { Attack, Heal, Guard } handler Battle phase Update { if game_mode == 1 { let k = Input.key() for (e) in query [Stats, {Enemy}] { machine turn_phase { state KnightMenu { for (st, pt) in query [Stats, Party] { if pt.slot == 0 { if st.hp <= 0 { become MageMenu menu_cursor = 0 } else { if k == 'w' { menu_cursor = max(0, menu_cursor - 1) } if k == 's' { menu_cursor = min(3, menu_cursor + 1) } if is_confirm(k) { match menu_cursor { KnightAct.Attack => { let d = max(1, st.atk - e.def + Random.range(0, 4)) e.hp -= d player_hit = d } KnightAct.Guard => { st.guard = 1; player_hit = 0 } KnightAct.Item => { if potions > 0 { potions -= 1 st.hp = min(st.maxhp, st.hp + 24) player_hit = 24 } else { player_hit = 0 } } KnightAct.Flee => { if Random.chance(50) { e.hp = 0; game_mode = 0 } else { player_hit = 0 } } } become KnightResolve menu_cursor = 0 } } } } } state KnightResolve { if k != 0 { if e.hp <= 0 { grant_xp(e.xp) gold += e.xp if enemy_type == 2 { boss_down = 1; game_mode = 3; ack = 0 } else { game_mode = 0 } become KnightMenu } else { become MageMenu menu_cursor = 0 } } } state MageMenu { for (st, pt) in query [Stats, Party] { if pt.slot == 1 { if st.hp <= 0 { become EnemyTurn } else { if k == 'i' { menu_cursor = max(0, menu_cursor - 1) } if k == 'k' { menu_cursor = min(2, menu_cursor + 1) } if k == 'j' { match menu_cursor { MageAct.Attack => { let d = max(1, st.atk + 5 - e.def + Random.range(0, 5)) e.hp -= d player_hit = d } MageAct.Heal => { if st.mp >= 4 { st.mp -= 4 for (t2, p2) in query [Stats, Party] { if p2.slot == 0 { t2.hp = min(t2.maxhp, t2.hp + 16) } } player_hit = 16 } else { player_hit = 0 } } MageAct.Guard => { st.guard = 1; player_hit = 0 } } become MageResolve menu_cursor = 0 } } } } } state MageResolve { if k != 0 { if e.hp <= 0 { grant_xp(e.xp) gold += e.xp if enemy_type == 2 { boss_down = 1; game_mode = 3; ack = 0 } else { game_mode = 0 } become KnightMenu } else { become EnemyTurn } } } state EnemyTurn { if k != 0 { var done = 0 for (t3) in query [Stats, {Party}] { if done == 0 { if t3.hp > 0 { var ed = max(1, e.atk - t3.def + Random.range(0, 4)) if t3.guard == 1 { ed /= 2; t3.guard = 0 } t3.hp -= ed enemy_hit = ed done = 1 } } } var alive = 0 for (t4) in query [Stats, {Party}] { if t4.hp > 0 { alive += 1 } } if alive == 0 { game_mode = 2; ack = 0 } else { become KnightMenu menu_cursor = 0 } } } } } } } handler CleanBattle phase LateUpdate { if game_mode != 1 { for (e) in query [Stats, {Enemy}] { despawn self() } } } handler Meta phase Update { let k = Input.key() if game_mode == 2 { if k == 0 { ack = 1 } if k != 0 { if ack == 1 { for (h) in query [Stats, {Party}] { h.hp = h.maxhp } game_mode = 0 } } } if game_mode == 3 { if k == 0 { ack = 1 } if k != 0 { if ack == 1 { game_mode = 0 } } } }