A multithreaded, event-driven combat simulator for Star Wars: The Old Republic (SWTOR). This tool simulates rotation loops, ability usage, passive procs, damage mitigation, and resource pools to analyze character performance through single test runs or parallelized Monte Carlo simulations.
-
1. Prompt for chosen spec to get file locations.
-
Top-Level Handler:
cli.select_loadout_paths() -
Functions Called:
cli.prompt_menu_choice()
-
Top-Level Handler:
-
2. Prompt & Extract Optional Choices
-
Top-Level Handler:
cli.prompt_optional_choices() -
Functions Called:
-
cli.draft_choices()config_load.load_json_file()cli._process_item_additions()
-
-
Top-Level Handler:
-
3. Load Character Stats
-
Top-Level Handler:
config_load.load_character_stats_from_json() -
Functions Called:
config_load.load_json_file()
-
Top-Level Handler:
-
4. Load Rotation Sequence
-
Top-Level Handler:
config_load.load_rotation_from_json() -
Functions Called:
config_load.load_json_file()
-
Top-Level Handler:
-
5. Load Abilities, Procs, and Effects
-
Top-Level Handler:
config_load.build_complete_loadout() -
Functions Called:
-
5.1 - Load Base Files:
-
config_load.load_abilities_from_json()$\rightarrow$ config_load.load_abilities_from_dict() -
config_load.load_procs_from_json()$\rightarrow$ config_load.load_procs_from_dict() -
config_load.load_permanent_effects_from_json()$\rightarrow$ config_load.load_permanent_effects_from_dict()
-
-
5.2 - Merge Choices with Base Loadout:
config_load.load_abilities_from_dict()config_load.load_procs_from_dict()config_load.load_permanent_effects_from_dict()
-
5.1 - Load Base Files:
-
Top-Level Handler:
-
1. Instantiate Core Simulation Entities
- Top-Level Handler:
setup.prepare_simulation() - Actions:
- Deep-copies
abilities_dbto isolate ability state per simulation. - Instantiates
Playerentity with class name and abilities. - Instantiates
Dummytarget entity with specified HP (dummy_hp). - Merges custom stat dictionary into
player.base_stats. - Instantiates the core
Simulationevent engine.
- Deep-copies
- Top-Level Handler:
-
2. Bind Databases and Recalculate Player State
- Top-Level Handler:
setup.prepare_simulation() - Actions:
- Deep-copies and assigns
procs_dbtoplayer.procs. - Deep-copies and assigns
buffs_dbtoplayer.effects. - Calls
player.recalculate_stats()to compute final baseline attributes.
- Deep-copies and assigns
- Top-Level Handler:
-
3. Apply Pre-Simulation Static Modifications
- Top-Level Handler:
setup.prepare_simulation() - Functions Called:
setup.pre_sim_effects()
- Actions:
- Scans player passive effects for cooldown modifications (e.g., CDR) and applies adjustments directly to matching abilities in
ability_db. - Scans for max charge modification effects and increases ability max/current charges.
- Scans player passive effects for cooldown modifications (e.g., CDR) and applies adjustments directly to matching abilities in
- Top-Level Handler:
-
4. Attach Rotation Engine
- Top-Level Handler:
setup.prepare_simulation() - Actions:
- Instantiates
Rotationusingrotation_configsteps and attaches it toplayer.rotation.
- Instantiates
- Top-Level Handler:
-
5. Schedule Initial Simulation Events
- Top-Level Handler:
setup.prepare_simulation() - Functions Called:
setup.schedule_periodic()
- Actions:
- Schedules
PlayerReadyevent att = 0.0. - Finds all
periodictrigger procs and schedules their initialPeriodicProcTickat a randomized offset between0.0andICD. - Schedules initial
ResourceTickevent at a randomized offset between0.0and1.0.
- Schedules
- Top-Level Handler:
-
6. Execute Simulation Run
- Top-Level Handler:
Tester.run_test()orTester.run_monte_carlo() - Functions Called:
sim.run_timed()Tester.execute_single_worker_task()(for parallel Monte Carlo execution)
- Top-Level Handler:
-
1. Initialize Event Engine Loop
-
Main Handler:
Simulation.run_timed() -
Functions Called:
heapq.heappop()Event.resolve()
-
Actions:
- Pops priority events ordered by absolute timestamp and sequence ID from the priority queue.
- Advances
sim.current_timeto match the event's timestamp. - Calls
event.resolve(sim)for execution. - Terminates loop when the queue empties, the configured fight duration is reached, or target HP drops to
$\le 0$ .
-
Main Handler:
-
2. Evaluate Rotation & Decision State
-
Main Handler:
PlayerReady.resolve() -
Functions Called:
-
Rotation.evaluate()-
FixedAbilityStep.evaluate()/PriorityBlockStep.evaluate()/OptionalAbilityStep.evaluate()Ability.cast()
-
-
Simulation.schedule_absolute()/Simulation.schedule_relative()
-
-
Actions:
- Evaluates active step in the rotation sequence against character state, energy, GCD lockouts, and target conditions.
-
If Cast Succeeds: Advances rotation step pointer and schedules the next
PlayerReadyevent atmax(current_time, player.next_gcd). -
If Blocked/Waiting: Re-schedules
PlayerReadyto retry$0.1\text{s}$ later.
-
Main Handler:
-
3. Cast & Ability Execution Processing
-
Main Handler:
Ability.cast() -
Functions Called:
Ability.can_cast()ResourcePool.spend()Ability.apply_cooldown_locks()Ability.evaluate_on_cast_procs()abilities.execute_single_action()
-
Actions:
- Validates ability GCD, resource cost, cooldown, charges, and conditions.
- Interrupts active channel if a new ability is cast during channeling.
- Spends resource cost, updates player's
next_gcd, applies cooldown/charge consumption, and triggerscastprocs. - Loops over defined actions (
damage,dot,channel,buff,debuff, etc.) and executes them.
-
Main Handler:
-
4. Action Handling & Sub-Event Dispatching
-
Main Handler:
abilities.execute_single_action() -
Functions Called:
-
abilities.handle_damage_action()$\rightarrow$ combat_math.accuracy_roll()$\rightarrow$ schedulesDamageHit -
abilities.handle_dot_action()$\rightarrow$ instantiatesActiveDot$\rightarrow$ schedulesDotTick -
abilities.handle_channel_action()$\rightarrow$ instantiatesActiveChannel$\rightarrow$ schedulesChannelTickEvent -
abilities.handle_buff_action()/handle_debuff_action()$\rightarrow$ Entity.apply_effect()$\rightarrow$ schedulesEffectExpire -
abilities.handle_resource_gain_action()$\rightarrow$ schedules or resolvesResourceGainEvent -
abilities.handle_cooldown_modification()$\rightarrow$ modifies active cooldowns -
abilities.handle_restore_charge()$\rightarrow$ Ability.restore_charge()
-
-
4.A. Combat Math & Damage Resolution
-
Main Handler:
DamageHit.resolve() -
Functions Called:
-
combat_math.calculate_hit()-
combat_math.handle_modifiers()$\rightarrow$ handle_caster_buffs(),handle_target_debuffs(),consume_charges() combat_math.calculate_base_damage()combat_math.handle_mitigation()combat_math.calculate_crit()
-
-
ApplyDamageLand.resolve()(scheduled or instant)Metrics.log_damage()
DamageHit.evaluate_on_hit_procs()
-
-
Actions:
- Computes raw base damage, applies buff/debuff buckets, armor mitigation/penetration, and crit rolls.
- Schedules
ApplyDamageLandafterimpact_delay(or resolves immediately). - Deducts target HP and logs hit/damage/crit metrics to
Metricstracker. - Evaluates eligible
hitorcritprocs and executes their associated actions.
-
Main Handler:
-
4.B. Recurring & Ticking Event Execution
-
Main Handlers:
-
DotTick.resolve(): Processes DoT tick actions, reducesticks_remaining, and schedules next tick or deletes DoT on expiration. -
ChannelTickEvent.resolve(): Processes channel tick actions, spends resource tick costs, and schedules next tick or clears channel state and queuesPlayerReadyon completion/clip. -
PeriodicProcTick.resolve(): Runs periodic proc actions and schedules the next interval tick. -
ResourceTick.resolve(): Executes passive energy/heat/force regeneration scaled by Alacrity and schedules the next tick. -
EffectExpire.resolve(): Cleans up expired buff/debuff instances and triggers entity stat recalculation if necessary. -
ChargeRestoreEvent.resolve(): Restores charges to multi-charge abilities and chains timer events.
-
-
Main Handlers:
-
| Module | Purpose / Responsibility |
|---|---|
cli.py |
Interactive terminal UI for selecting paths, skills, tacticals, implants, and relics. |
config_load.py |
JSON parser for character statistics, rotation step configurations, abilities, and effects. |
engine.py |
Core event-driven execution loop (Simulation) using Python's heapq priority queue. |
setup.py |
Environment bootstrapper (prepare_simulation) that sets up player/target entities and initial events. |
tester.py |
Test orchestrator running single iterations or multi-core parallelized Monte Carlo simulations. |
events.py |
Discrete engine event definitions (PlayerReady, DamageHit, DotTick, ResourceTick, etc.). |
abilities.py |
Ability usage rules, cooldown locking, charge management, and action execution dispatching. |
combat_math.py |
Accuracy rolls, modifier bucketing, diminishing returns curves, mitigation, and crits/supercrits. |
entities.py |
Entity, Player, and Dummy state management, effect tracking, and dynamic stat updates. |
effects.py |
Data structures for DoTs (ActiveDot), channels (ActiveChannel), and passive procs (ProcData). |
resources.py |
Class resource pool logic (Force, Energy, Heat, Rage/Focus). |
rotation.py |
Rotation step engine supporting fixed actions, priority blocks, optional steps, and anchors(discern opener). |
metrics.py |
Damage logger tracking total DPS, ability hit breakdowns, and execute phase ( |
requirements.py |
Conditional validator (CONDITION_REGISTRY) evaluating resource thresholds, buffs, and HP percentages. |
The simulator is transitioning from handwritten JSON blueprints to raw extracted game data to achieve 100% data fidelity with live SWTOR servers. With the Extracted Data Parser Engine already completed—resolving internal SWTOR node hashes, 64-bit integer IDs, and effect references into clean blueprint structures (all nida/sinrai) — the next phase focuses on integrating the new file format directly into the engine runtime:
- Data Loader Overhaul (
config_load.py): Rewrite loading routines to parse the new extracted file schemas and databases, mapping nida-resolved node hashes directly to runtime entity models. - Load-Time Key & Tag Sanitization: Shift key normalization (
snake_case) and tag processing (frozenset) into the data loading layer to maximize runtime event queue performance.
- Node-Based Ability Resolution (
abilities.py): RefactorAbility.cast()andexecute_single_action()to execute abilities directly from the extracted node trees (action sequences, child effect links, conditional branches). - Dynamic Condition Matching: Map extracted condition IDs (tho id's might be gone) and target criteria into
requirements.py.
- Native Stack & Charge Consumption: Align buff/debuff stack handling, refresh logic, and stack-draining triggers in
combat_math.pyandentities.pywith extracted node rules. - Proc & Trigger Mechanics: Re-architect proc listeners (
cast,hit,crit,periodic) to subscribe dynamically to extracted effect trigger IDs. - Advanced Resource & Event Features: Finalize passive resource decay timers for builder pools (
Rage/Focus) and properly implement channel clipping and partial channel tick resolution.
readme written with Gemini assistance because author is clueless but can review shit