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