diff --git a/src/creepSetups/setups.ts b/src/creepSetups/setups.ts index 1c57dcbee..98638a9c4 100644 --- a/src/creepSetups/setups.ts +++ b/src/creepSetups/setups.ts @@ -229,6 +229,11 @@ export const CombatSetups = { sizeLimit: Infinity, }), + distraction: new CreepSetup(Roles.ranged, { + pattern : [MOVE, MOVE, MOVE, RANGED_ATTACK, MOVE], + sizeLimit: 1, + }), + default: new CreepSetup(Roles.ranged, { pattern : [RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, MOVE, MOVE, MOVE, MOVE, HEAL], sizeLimit: Infinity, diff --git a/src/directives/defense/invasionDefense.ts b/src/directives/defense/invasionDefense.ts index 1d6cab663..d7346d649 100644 --- a/src/directives/defense/invasionDefense.ts +++ b/src/directives/defense/invasionDefense.ts @@ -1,5 +1,6 @@ import {ColonyStage} from '../../Colony'; import {CombatIntel} from '../../intel/CombatIntel'; +import {DistractionOverlord} from '../../overlords/defense/distraction'; import {MeleeDefenseOverlord} from '../../overlords/defense/meleeDefense'; import {RangedDefenseOverlord} from '../../overlords/defense/rangedDefense'; import {profile} from '../../profiler/decorator'; @@ -51,6 +52,11 @@ export class DirectiveInvasionDefense extends Directive { this.overlords.meleeDefense = new MeleeDefenseOverlord(this, useBoosts); } + // Secondary defense + if (this.colony.level > 4 && meleeHostiles.length > 1 && expectedDamage > (ATTACK_POWER * 20)) { + this.overlords.distraction = new DistractionOverlord(this); + } + } init(): void { diff --git a/src/overlords/defense/distraction.ts b/src/overlords/defense/distraction.ts new file mode 100644 index 000000000..8bd10abf6 --- /dev/null +++ b/src/overlords/defense/distraction.ts @@ -0,0 +1,61 @@ +import {CreepSetup} from '../../creepSetups/CreepSetup'; +import {CombatSetups, Roles} from '../../creepSetups/setups'; +import {DirectiveInvasionDefense} from '../../directives/defense/invasionDefense'; +import {CombatIntel} from '../../intel/CombatIntel'; +import {OverlordPriority} from '../../priorities/priorities_overlords'; +import {profile} from '../../profiler/decorator'; +import {boostResources} from '../../resources/map_resources'; +import {CombatZerg} from '../../zerg/CombatZerg'; +import {CombatOverlord} from '../CombatOverlord'; + +/** + * 5 Move 1 RA creep that avoids all enemies and distracts attackers. + * Just for fun + * TODO: Make them prefer swamps when at max hp + */ +@profile +export class DistractionOverlord extends CombatOverlord { + + distractions: CombatZerg[]; + room: Room; + + static settings = { + retreatHitsPercent : 0.85, + reengageHitsPercent: 0.95, + }; + + constructor(directive: DirectiveInvasionDefense, + boosted = false, + priority = OverlordPriority.defense.rangedDefense) { + super(directive, 'distraction', priority, 1); + this.distractions = this.combatZerg(Roles.ranged); + } + + private handleDistraction(distraction: CombatZerg): void { + if (this.room.hostiles.length > 0) { + distraction.autoCombat(this.room.name, false, 5, {preferRamparts: false}); + DistractionOverlord.taunt(distraction, this.room.hostiles[0].owner.username); + const nearbyHostiles = this.room.hostiles.filter(hostile => hostile.pos.getRangeTo(distraction) <= 6); + if (nearbyHostiles.length > 0) { + distraction.kite(nearbyHostiles); + } + } + } + + static taunt(distraction: CombatZerg, name?: string) { + const taunts: string[] = ['Heylisten!', 'Pssssst', 'So close', '🎣', 'Try harder', 'Get good;)', 'Base ⬆️', '🔜', + '⚠️Swamp⚠️', 'Follow me!', 'Catch Me!', `Hi ${name || ''}`, '🍑🍑🍑', '🏎️ VROOM']; + distraction.sayRandom(taunts, true); + } + + init() { + this.reassignIdleCreeps(Roles.ranged); + const setup = CombatSetups.hydralisks.distraction; + this.wishlist(1, setup); + } + + run() { + console.log(`Distraction overlord running in ${this.room.print} with ${this.distractions}!`); + this.autoRun(this.distractions, distraction => this.handleDistraction(distraction)); + } +} diff --git a/src/zerg/CombatZerg.ts b/src/zerg/CombatZerg.ts index 567219dfc..12067059d 100644 --- a/src/zerg/CombatZerg.ts +++ b/src/zerg/CombatZerg.ts @@ -1,5 +1,5 @@ import {CombatIntel} from '../intel/CombatIntel'; -import {Movement, NO_ACTION} from '../movement/Movement'; +import {CombatMoveOptions, Movement, MoveOptions, NO_ACTION} from '../movement/Movement'; import {profile} from '../profiler/decorator'; import {CombatTargeting} from '../targeting/CombatTargeting'; import {GoalFinder} from '../targeting/GoalFinder'; @@ -227,7 +227,7 @@ export class CombatZerg extends Zerg { /** * Navigate to a room, then engage hostile creeps there, perform medic actions, etc. */ - autoCombat(roomName: string, verbose = false) { + autoCombat(roomName: string, verbose = false, preferredRange?: number, options?: CombatMoveOptions) { // Do standard melee, ranged, and heal actions if (this.getActiveBodyparts(ATTACK) > 0) { @@ -255,7 +255,7 @@ export class CombatZerg extends Zerg { // Fight within the room const target = CombatTargeting.findTarget(this); const preferRanged = this.getActiveBodyparts(RANGED_ATTACK) > this.getActiveBodyparts(ATTACK); - const targetRange = preferRanged ? 3 : 1; + const targetRange = preferredRange || preferRanged ? 3 : 1; this.debug(`${target}, ${targetRange}`); if (target) { const avoid = []; @@ -263,10 +263,10 @@ export class CombatZerg extends Zerg { if (preferRanged) { const meleeHostiles = _.filter(this.room.hostiles, h => CombatIntel.getAttackDamage(h) > 0); for (const hostile of meleeHostiles) { - avoid.push({pos: hostile.pos, range: 2}); + avoid.push({pos: hostile.pos, range: targetRange - 1}); } } - return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], []); + return Movement.combatMove(this, [{pos: target.pos, range: targetRange}], avoid, options); } }