Trails In Sky Combat Emu - Procedural Anonymous https://bbs.gikopoi.com/atom/thread/1747949301 2025-05-26T20:49:57+00:00 Trails In Sky Combat Emu - Procedural https://bbs.gikopoi.com/post/1747949301/1 2025-05-22T21:28:21+00:00 2025-05-22T21:28:21+00:00 #!/usr/bin/env python3<br>"""<br>Trails in the Sky - Procedural Combat Timeline Engine with Persistent Event Labels<br>"""<br>import os<br>import sys<br>import time<br>import random<br><br># === EVENT TYPES ===<br>TYPE_CHARACTER = 0 # Player character taking action<br>TYPE_ENEMY = 1 # Enemy AI taking action<br>TYPE_SPELL_RESOLVE = 2 # Spell resolves after cast delay<br>TYPE_STATUS_EXPIRE = 4 # Status effect ends<br>TYPE_PLAYER_INPUT = 5 # Wait for player input before proceeding<br><br># === STATUS EFFECTS COLORS ===<br>STATUS_COLORS = {<br> "Casting": "\033[37m", # Light grey / off-white<br> "Poisoned": "\033[95m", # Purple<br> "Burning": "\033[91m", # Red<br> "Slowed": "\033[94m", # Blue<br> "Haste": "\033[92m", # Green<br> "Muted": "\033[93m", # Yellow<br> "Dead": "\033[90m" # Gray<br>}<br>RESET_COLOR = "\033[0m"<br><br># === LABEL POOL (1-9,a-z,A-Z) ===<br>label_pool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"<br>label_counter = 0<br><br>def get_label():<br> """Get next label in the cyclic pool"""<br> global label_counter<br> label = label_pool[label_counter % len(label_pool)]<br> label_counter += 1<br> return label<br><br># === DATA STRUCTURES ===<br>def create_character(name, hp, ep, cp, atk, defense, speed, mov, is_player=False):<br> """Create a new character dictionary with persistent label"""<br> return {<br> 'name': name,<br> 'hp': hp,<br> 'max_hp': hp,<br> 'ep': ep,<br> 'cp': cp,<br> 'atk': atk,<br> 'defense': defense,<br> 'speed': speed,<br> 'mov': mov,<br> 'is_player': is_player,<br> 'alive': True,<br> 'status_effects': [],<br> 'next_action_time': 0,<br> 'orbment': [4, 2, 0, 1, 1, 0, 0], # Fire, Water, Earth, Wind, Time, Space, Mirage<br> 'label': get_label()<br> }<br><br>def create_spell_resolve_event(caster_id, target_id, spell_name, resolve_time):<br> """Create a spell resolve event with persistent label"""<br> return {<br> 'name': f"{spell_name} ({caster_id}→{target_id})",<br> 'next_action_time': resolve_time,<br> 'caster_id': caster_id,<br> 'target_id': target_id,<br> 'spell_name': spell_name,<br> 'type': TYPE_SPELL_RESOLVE,<br> 'label': get_label()<br> }<br><br>def create_status_expire_event(target_id, effect_name, expire_time):<br> """Create a status expiration event with persistent label"""<br> return {<br> 'name': f"{effect_name} expires",<br> 'next_action_time': expire_time,<br> 'target_id': target_id,<br> 'effect_name': effect_name,<br> 'type': TYPE_STATUS_EXPIRE,<br> 'label': get_label()<br> }<br><br># === UTILITY FUNCTIONS ===<br>def get_terminal_size():<br> try:<br> return os.get_terminal_size()<br> except:<br> return type('obj', (object,), {'columns': 80, 'lines': 24})<br><br>def clear_screen():<br> os.system('cls' if os.name == 'nt' else 'clear')<br><br>def format_time(time_val):<br> return f"{time_val:08d}"<br><br>def is_valid_participant(p):<br> """Check if participant should be displayed on timeline"""<br> if isinstance(p, dict):<br> if p.get('type') == TYPE_STATUS_EXPIRE:<br> return p['next_action_time'] > global_time<br> return p.get('alive', True) or p.get('type') in [TYPE_SPELL_RESOLVE]<br> return False<br><br>def get_status_color_for(event):<br> """Determine color based on spell name, status expiration, or character status"""<br> if isinstance(event, dict):<br> # Spell Resolve: check spell name<br> if event.get('type') == TYPE_SPELL_RESOLVE:<br> if event['spell_name'] == "Burn":<br> return STATUS_COLORS["Burning"]<br> elif event['spell_name'] == "Poison":<br> return STATUS_COLORS["Poisoned"]<br> elif event['spell_name'] == "Heal":<br> return STATUS_COLORS["Haste"]<br><br> # Status Expire: use the status name<br> elif event.get('type') == TYPE_STATUS_EXPIRE:<br> effect_name = event.get('effect_name', '')<br> return STATUS_COLORS.get(effect_name, "")<br><br> # Character: check their status effects<br> elif 'status_effects' in event:<br> for se in event['status_effects']:<br> if se['name'] in STATUS_COLORS:<br> return STATUS_COLORS[se['name']]<br> return ""<br><br># === STATUS EFFECTS ===<br>def apply_status_effect(target, effect_name, duration, on_apply=None, on_tick=None, on_expire=None):<br> """Apply a status effect to a target character"""<br> existing = None<br> for se in target['status_effects']:<br> if se['name'] == effect_name:<br> existing = se<br> break<br> if existing:<br> if existing['duration'] > 0:<br> existing['duration'] = max(existing['duration'], duration)<br> return<br> # Add the effect to the target's status list<br> new_effect = {<br> 'name': effect_name,<br> 'duration': duration,<br> 'on_apply': on_apply,<br> 'on_tick': on_tick,<br> 'on_expire': on_expire<br> }<br> target['status_effects'].append(new_effect)<br> # Call apply function if it exists<br> if new_effect['on_apply']:<br> new_effect['on_apply'](target)<br> # Create expiration event<br> if new_effect['duration'] > 0:<br> expire_event = create_status_expire_event(<br> id(target),<br> effect_name,<br> global_time + new_effect['duration']<br> )<br> participants.append(expire_event)<br><br>def tick_status_effects(characters, global_time):<br> """Tick all active status effects"""<br> for char in characters:<br> if not char['alive']:<br> continue<br> for effect in list(char['status_effects']):<br> effect['duration'] -= 1<br> if effect['on_tick']:<br> effect['on_tick'](char)<br> if effect['duration'] <= 0:<br> if effect['on_expire']:<br> effect['on_expire'](char)<br> char['status_effects'].remove(effect)<br><br># === SPELL SYSTEM ===<br>def cast_burn_spell(caster, target, resolve_time):<br> message = f"{caster['name']} casts Burn on {target['name']}!"<br> print(message.rjust(31))<br> damage = int(caster['atk'] * 1.5)<br> target['hp'] = max(0, target['hp'] - damage)<br> if target['hp'] == 0:<br> print(f"{target['name']} collapses!")<br> target['alive'] = False<br><br> # Remove Casting status<br> caster['status_effects'] = [se for se in caster['status_effects'] if se['name'] != 'Casting']<br><br> # Apply burn effect (30 AT duration)<br> apply_status_effect(<br> target,<br> "Burning",<br> 30,<br> on_apply=lambda t: print(f"{t['name']} catches fire!"),<br> on_tick=lambda t: t.update({'hp': max(0, t['hp'] - 1)}),<br> on_expire=lambda t: print(f"{t['name']} is no longer burning.")<br> )<br><br>def cast_poison_spell(caster, target, resolve_time):<br> print(f"{caster['name']} casts Poison on {target['name']}!")<br> damage = int(caster['atk'] * 1.5)<br> target['hp'] = max(0, target['hp'] - damage)<br> if target['hp'] == 0:<br> print(f"{target['name']} collapses!")<br> target['alive'] = False<br><br> # Remove Casting status<br> caster['status_effects'] = [se for se in caster['status_effects'] if se['name'] != 'Casting']<br><br> # Apply poison effect (20 AT duration)<br> apply_status_effect(<br> target,<br> "Poisoned",<br> 20,<br> on_apply=lambda t: print(f"{t['name']} is poisoned!"),<br> on_tick=lambda t: t.update({'hp': max(0, t['hp'] - 1)}),<br> on_expire=lambda t: print(f"{t['name']} is no longer poisoned.")<br> )<br><br>def cast_heal_spell(caster, target, resolve_time):<br> print(f"{caster['name']} casts Heal on {target['name']}!")<br> target['hp'] = min(target['max_hp'], target['hp'] + 50)<br> print(f"{target['name']} healed to {target['hp']}/{target['max_hp']}")<br><br> # Remove Casting status<br> caster['status_effects'] = [se for se in caster['status_effects'] if se['name'] != 'Casting']<br><br># === COMBAT ACTIONS ===<br>def handle_player_turn(actor, enemies, participants, global_time):<br> """Handle a player's turn"""<br> print(f"\n{actor['name']}'s Turn:")<br> print("A: Attack | B: Cast Burn | P: Cast Poison | H: Heal | R: Run")<br> choice = input("Choose action: ").strip().lower()<br><br> targets = [e for e in enemies if e['alive']]<br> if not targets:<br> print("No enemies alive.")<br> return<br><br> if choice == 'a':<br> target = select_target_from_list(targets)<br> if target:<br> print(f"{ Reply to thread https://bbs.gikopoi.com/post/1747949301/2 2025-05-22T21:32:03+00:00 2025-05-22T21:32:03+00:00 Hm can't post screenshot? Well<br><br>'no OOP' version <br><br>maybe easier to understand than OOP version.<br><br> Reply to thread https://bbs.gikopoi.com/post/1747949301/3 2025-05-26T20:49:57+00:00 2025-05-26T20:49:57+00:00 There are Emus in Trails in the Sky? I've seen many bird type monsters but never anything like an emu.