/** * Combat System Enhancement Migration * Adds comprehensive combat tables and enhancements for production-ready combat system */ exports.up = function (knex) { return knex.schema // Combat types table - defines different combat resolution types .createTable('combat_types', (table) => { table.increments('id').primary(); table.string('name', 100).unique().notNullable(); table.text('description'); table.string('plugin_name', 100); // References plugins table table.jsonb('config'); table.boolean('is_active').defaultTo(true); table.index(['is_active']); table.index(['plugin_name']); }) // Main battles table - tracks all combat encounters .createTable('battles', (table) => { table.bigIncrements('id').primary(); table.string('battle_type', 50).notNullable(); // 'fleet_vs_fleet', 'fleet_vs_colony', 'siege' table.string('location', 20).notNullable(); table.integer('combat_type_id').references('combat_types.id'); table.jsonb('participants').notNullable(); // Array of fleet/player IDs table.string('status', 20).notNullable().defaultTo('pending'); // 'pending', 'active', 'completed', 'cancelled' table.jsonb('battle_data'); // Additional battle configuration table.jsonb('result'); // Final battle results table.timestamp('started_at').defaultTo(knex.fn.now()); table.timestamp('completed_at').nullable(); table.timestamp('created_at').defaultTo(knex.fn.now()); table.index(['location']); table.index(['status']); table.index(['completed_at']); table.index(['started_at']); }) // Combat encounters table for detailed battle tracking .createTable('combat_encounters', (table) => { table.bigIncrements('id').primary(); table.integer('battle_id').references('battles.id').onDelete('CASCADE'); table.integer('attacker_fleet_id').references('fleets.id').onDelete('CASCADE').notNullable(); table.integer('defender_fleet_id').references('fleets.id').onDelete('CASCADE'); table.integer('defender_colony_id').references('colonies.id').onDelete('CASCADE'); table.string('encounter_type', 50).notNullable(); // 'fleet_vs_fleet', 'fleet_vs_colony', 'siege' table.string('location', 20).notNullable(); table.jsonb('initial_forces').notNullable(); // Starting forces for both sides table.jsonb('final_forces').notNullable(); // Remaining forces after combat table.jsonb('casualties').notNullable(); // Detailed casualty breakdown table.jsonb('combat_log').notNullable(); // Round-by-round combat log table.decimal('experience_gained', 10, 2).defaultTo(0); table.jsonb('loot_awarded'); // Resources/items awarded to winner table.string('outcome', 20).notNullable(); // 'attacker_victory', 'defender_victory', 'draw' table.integer('duration_seconds').notNullable(); // Combat duration table.timestamp('started_at').notNullable(); table.timestamp('completed_at').notNullable(); table.timestamp('created_at').defaultTo(knex.fn.now()); table.index(['battle_id']); table.index(['attacker_fleet_id']); table.index(['defender_fleet_id']); table.index(['defender_colony_id']); table.index(['location']); table.index(['outcome']); table.index(['started_at']); }) // Combat logs for detailed event tracking .createTable('combat_logs', (table) => { table.bigIncrements('id').primary(); table.bigInteger('encounter_id').references('combat_encounters.id').onDelete('CASCADE').notNullable(); table.integer('round_number').notNullable(); table.string('event_type', 50).notNullable(); // 'damage', 'destruction', 'ability_use', 'experience_gain' table.jsonb('event_data').notNullable(); // Detailed event information table.timestamp('timestamp').defaultTo(knex.fn.now()); table.index(['encounter_id', 'round_number']); table.index(['event_type']); table.index(['timestamp']); }) // Combat statistics for analysis and balancing .createTable('combat_statistics', (table) => { table.bigIncrements('id').primary(); table.integer('player_id').references('players.id').onDelete('CASCADE').notNullable(); table.integer('battles_initiated').defaultTo(0); table.integer('battles_won').defaultTo(0); table.integer('battles_lost').defaultTo(0); table.integer('ships_lost').defaultTo(0); table.integer('ships_destroyed').defaultTo(0); table.bigInteger('total_damage_dealt').defaultTo(0); table.bigInteger('total_damage_received').defaultTo(0); table.decimal('total_experience_gained', 15, 2).defaultTo(0); table.jsonb('resources_looted').defaultTo('{}'); table.timestamp('last_battle').nullable(); table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); table.index(['player_id']); table.index(['battles_won']); table.index(['last_battle']); }) // Ship combat experience and veterancy .createTable('ship_combat_experience', (table) => { table.bigIncrements('id').primary(); table.integer('fleet_id').references('fleets.id').onDelete('CASCADE').notNullable(); table.integer('ship_design_id').references('ship_designs.id').onDelete('CASCADE').notNullable(); table.integer('battles_survived').defaultTo(0); table.integer('enemies_destroyed').defaultTo(0); table.bigInteger('damage_dealt').defaultTo(0); table.decimal('experience_points', 15, 2).defaultTo(0); table.integer('veterancy_level').defaultTo(1); table.jsonb('combat_bonuses').defaultTo('{}'); // Experience-based bonuses table.timestamp('last_combat').nullable(); table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); table.unique(['fleet_id', 'ship_design_id']); table.index(['fleet_id']); table.index(['veterancy_level']); table.index(['last_combat']); }) // Combat configurations for different combat types .createTable('combat_configurations', (table) => { table.increments('id').primary(); table.string('config_name', 100).unique().notNullable(); table.string('combat_type', 50).notNullable(); // 'instant', 'turn_based', 'real_time' table.jsonb('config_data').notNullable(); // Combat-specific configuration table.boolean('is_active').defaultTo(true); table.string('description', 500); table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); table.index(['combat_type']); table.index(['is_active']); }) // Combat modifiers for temporary effects .createTable('combat_modifiers', (table) => { table.bigIncrements('id').primary(); table.string('entity_type', 50).notNullable(); // 'fleet', 'colony', 'player' table.integer('entity_id').notNullable(); table.string('modifier_type', 50).notNullable(); // 'attack_bonus', 'defense_bonus', 'speed_bonus' table.decimal('modifier_value', 8, 4).notNullable(); table.string('source', 100).notNullable(); // 'technology', 'event', 'building', 'experience' table.timestamp('start_time').defaultTo(knex.fn.now()); table.timestamp('end_time').nullable(); table.boolean('is_active').defaultTo(true); table.jsonb('metadata'); // Additional modifier information table.index(['entity_type', 'entity_id']); table.index(['modifier_type']); table.index(['is_active']); table.index(['end_time']); }) // Fleet positioning for tactical combat .createTable('fleet_positions', (table) => { table.bigIncrements('id').primary(); table.integer('fleet_id').references('fleets.id').onDelete('CASCADE').notNullable(); table.string('location', 20).notNullable(); table.decimal('position_x', 8, 2).defaultTo(0); table.decimal('position_y', 8, 2).defaultTo(0); table.decimal('position_z', 8, 2).defaultTo(0); table.string('formation', 50).defaultTo('standard'); // 'standard', 'defensive', 'aggressive', 'flanking' table.jsonb('tactical_settings').defaultTo('{}'); // Formation-specific settings table.timestamp('last_updated').defaultTo(knex.fn.now()); table.unique(['fleet_id']); table.index(['location']); table.index(['formation']); }) // Combat queue for processing battles .createTable('combat_queue', (table) => { table.bigIncrements('id').primary(); table.bigInteger('battle_id').references('battles.id').onDelete('CASCADE').notNullable(); table.string('queue_status', 20).defaultTo('pending'); // 'pending', 'processing', 'completed', 'failed' table.integer('priority').defaultTo(100); table.timestamp('scheduled_at').defaultTo(knex.fn.now()); table.timestamp('started_processing').nullable(); table.timestamp('completed_at').nullable(); table.integer('retry_count').defaultTo(0); table.text('error_message').nullable(); table.jsonb('processing_metadata'); table.index(['queue_status']); table.index(['priority', 'scheduled_at']); table.index(['battle_id']); }) // Extend battles table with additional fields .alterTable('battles', (table) => { table.integer('combat_configuration_id').references('combat_configurations.id'); table.jsonb('tactical_settings').defaultTo('{}'); table.integer('spectator_count').defaultTo(0); table.jsonb('environmental_effects'); // Weather, nebulae, asteroid fields table.decimal('estimated_duration', 8, 2); // Estimated battle duration in seconds }) // Extend fleets table with combat-specific fields .alterTable('fleets', (table) => { table.decimal('combat_rating', 10, 2).defaultTo(0); // Calculated combat effectiveness table.integer('total_ship_count').defaultTo(0); table.jsonb('fleet_composition').defaultTo('{}'); // Ship type breakdown table.timestamp('last_combat').nullable(); table.integer('combat_victories').defaultTo(0); table.integer('combat_defeats').defaultTo(0); }) // Extend ship_designs table with detailed combat stats .alterTable('ship_designs', (table) => { table.integer('hull_points').defaultTo(100); table.integer('shield_points').defaultTo(0); table.integer('armor_points').defaultTo(0); table.decimal('attack_power', 8, 2).defaultTo(10); table.decimal('attack_speed', 6, 2).defaultTo(1.0); // Attacks per second table.decimal('movement_speed', 6, 2).defaultTo(1.0); table.integer('cargo_capacity').defaultTo(0); table.jsonb('special_abilities').defaultTo('[]'); table.jsonb('damage_resistances').defaultTo('{}'); }) // Colony defense enhancements .alterTable('colonies', (table) => { table.integer('defense_rating').defaultTo(0); table.integer('shield_strength').defaultTo(0); table.boolean('under_siege').defaultTo(false); table.timestamp('last_attacked').nullable(); table.integer('successful_defenses').defaultTo(0); table.integer('times_captured').defaultTo(0); }); }; exports.down = function (knex) { return knex.schema // Remove added columns first .alterTable('colonies', (table) => { table.dropColumn('defense_rating'); table.dropColumn('shield_strength'); table.dropColumn('under_siege'); table.dropColumn('last_attacked'); table.dropColumn('successful_defenses'); table.dropColumn('times_captured'); }) .alterTable('ship_designs', (table) => { table.dropColumn('hull_points'); table.dropColumn('shield_points'); table.dropColumn('armor_points'); table.dropColumn('attack_power'); table.dropColumn('attack_speed'); table.dropColumn('movement_speed'); table.dropColumn('cargo_capacity'); table.dropColumn('special_abilities'); table.dropColumn('damage_resistances'); }) .alterTable('fleets', (table) => { table.dropColumn('combat_rating'); table.dropColumn('total_ship_count'); table.dropColumn('fleet_composition'); table.dropColumn('last_combat'); table.dropColumn('combat_victories'); table.dropColumn('combat_defeats'); }) .alterTable('battles', (table) => { table.dropColumn('combat_configuration_id'); table.dropColumn('tactical_settings'); table.dropColumn('spectator_count'); table.dropColumn('environmental_effects'); table.dropColumn('estimated_duration'); }) // Drop new tables .dropTableIfExists('combat_queue') .dropTableIfExists('fleet_positions') .dropTableIfExists('combat_modifiers') .dropTableIfExists('combat_configurations') .dropTableIfExists('ship_combat_experience') .dropTableIfExists('combat_statistics') .dropTableIfExists('combat_logs') .dropTableIfExists('combat_encounters') .dropTableIfExists('battles') .dropTableIfExists('combat_types'); };