Skip to content

Commit

Permalink
Merge pull request #144 from MatthewARoy/LurkerBunkerDefense
Browse files Browse the repository at this point in the history
Lurker bunker defense
  • Loading branch information
bencbartlett authored Jun 14, 2019
2 parents a71b353 + 903ea1d commit 5eca49a
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 9 deletions.
30 changes: 30 additions & 0 deletions src/creepSetups/setups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const Roles = {
melee : 'zergling',
ranged : 'hydralisk',
healer : 'transfuser',
bunkerGuard : 'bunkerGuard',
dismantler: 'lurker',
};

Expand Down Expand Up @@ -297,6 +298,35 @@ export const CombatSetups = {

},

/**
* Pure melee raw power creeps that should never leave the bunker. These are the final guards for a room
*/
bunkerGuard: {

early: new CreepSetup(Roles.bunkerGuard, {
pattern : [ATTACK, MOVE],
sizeLimit: Infinity,
}),

default: new CreepSetup(Roles.bunkerGuard, {
pattern : [ATTACK, ATTACK, MOVE],
sizeLimit: Infinity,
}),

halfMove: new CreepSetup(Roles.bunkerGuard, {
pattern : [ATTACK, ATTACK, ATTACK, ATTACK, MOVE],
sizeLimit: Infinity,
}),

boosted_T3: new CreepSetup(Roles.bunkerGuard, {
// 22 ATTACK, 3 MOVE times 2
pattern : [ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK,
ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, ATTACK, MOVE, MOVE, MOVE],
sizeLimit: Infinity,
}),

},

/**
* Dismantlers (lurkers) are creeps with work parts for dismantle sieges
*/
Expand Down
14 changes: 11 additions & 3 deletions src/directives/defense/invasionDefense.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {ColonyStage} from '../../Colony';
import {CombatIntel} from '../../intel/CombatIntel';
import {BunkerDefenseOverlord} from '../../overlords/defense/bunkerDefense';
import {MeleeDefenseOverlord} from '../../overlords/defense/meleeDefense';
import {RangedDefenseOverlord} from '../../overlords/defense/rangedDefense';
import {profile} from '../../profiler/decorator';

import {ColonyStage} from '../../Colony';
import {Directive} from '../Directive';
import {NotifierPriority} from '../Notifier';

Expand Down Expand Up @@ -36,8 +38,9 @@ export class DirectiveInvasionDefense extends Directive {
if (!this.room) {
return;
}
const expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousHostiles);
const useBoosts = (expectedDamage > ATTACK_POWER * 75)
const expectedDamage = CombatIntel.maxDamageByCreeps(this.room.dangerousPlayerHostiles);
const expectedHealing = CombatIntel.maxHealingByCreeps(this.room.dangerousPlayerHostiles);
const useBoosts = (expectedDamage > ATTACK_POWER * 50) || (expectedHealing > RANGED_ATTACK_POWER * 100)
&& !!this.colony.terminal
&& !!this.colony.evolutionChamber;
const percentWalls = _.filter(this.room.barriers, s => s.structureType == STRUCTURE_WALL).length /
Expand All @@ -50,6 +53,11 @@ export class DirectiveInvasionDefense extends Directive {
} else {
this.overlords.meleeDefense = new MeleeDefenseOverlord(this, useBoosts);
}
// If serious bunker busting attempt, spawn lurkers
// TODO understand dismantlers damage output
if (meleeHostiles.length > 0 && (expectedDamage > ATTACK_POWER * 70)) {
this.overlords.bunkerDefense = new BunkerDefenseOverlord(this, false);
}

}

Expand Down
6 changes: 5 additions & 1 deletion src/movement/Movement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const STATE_CURRENT_Y = 8;
export const MovePriorities = {
[Roles.manager] : 1,
[Roles.queen] : 2,
[Roles.bunkerGuard]: 3,
[Roles.melee] : 3,
[Roles.ranged] : 4,
[Roles.guardMelee]: 5,
Expand Down Expand Up @@ -92,6 +93,7 @@ export interface CombatMoveOptions {
avoidPenalty?: number;
approachBonus?: number;
preferRamparts?: boolean;
requireRamparts?: boolean;
displayCostMatrix?: boolean;
displayAvoid?: boolean;
}
Expand Down Expand Up @@ -998,6 +1000,7 @@ export class Movement {
avoidPenalty : 10,
approachBonus : 5,
preferRamparts: true,
requireRamparts: false,
});

const debug = false;
Expand All @@ -1006,6 +1009,7 @@ export class Movement {
const matrix = Pathing.getDefaultMatrix(creep.room).clone();
Pathing.blockMyCreeps(matrix, creep.room);
Pathing.blockHostileCreeps(matrix, creep.room);
if (options.requireRamparts) { Pathing.blockNonRamparts(matrix, creep.room); }
Movement.combatMoveCallbackModifier(creep.room, matrix, approach, avoid, options);
if (options.displayCostMatrix) {
Visualizer.displayCostMatrix(matrix, roomName);
Expand Down Expand Up @@ -1059,7 +1063,7 @@ export class Movement {
}

// Try to maneuver under ramparts if possible
if (options.preferRamparts && !creep.inRampart && approach.length > 0) {
if ((options.preferRamparts || options.requireRamparts) && !creep.inRampart && approach.length > 0) {
const openRamparts = _.filter(creep.room.walkableRamparts,
rampart => _.any(approach,
g => rampart.pos.inRangeToXY(g.pos.x, g.pos.y, g.range))
Expand Down
14 changes: 14 additions & 0 deletions src/movement/Pathing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,20 @@ export class Pathing {
});
}

/**
* Sets walkable rampart positions to 1, everything else is blocked
*/
static blockNonRamparts(matrix: CostMatrix, room: Room) {
for (let y = 0; y < 50; ++y) {
for (let x = 0; x < 50; ++x) {
matrix.set(x, y, 0xff);
}
}
_.forEach(room.walkableRamparts, rampart => {
matrix.set(rampart.pos.x, rampart.pos.y, 1);
});
}

/**
* Explicitly blocks off walls for a room
*/
Expand Down
4 changes: 2 additions & 2 deletions src/overlords/CombatOverlord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export abstract class CombatOverlord extends Overlord {
spawnGroup: SpawnGroup;
requiredRCL: number; // default required RCL

constructor(directive: Directive, name: string, priority: number, requiredRCL: number) {
constructor(directive: Directive, name: string, priority: number, requiredRCL: number, maxPathDistance?: number) {
super(directive, name, priority);
this.directive = directive;
this.requiredRCL = requiredRCL;
this.spawnGroup = new SpawnGroup(this, {requiredRCL: this.requiredRCL});
this.spawnGroup = new SpawnGroup(this, {requiredRCL: this.requiredRCL, maxPathDistance: maxPathDistance});
}

// Standard sequence of actions for running combat creeps
Expand Down
2 changes: 1 addition & 1 deletion src/overlords/SwarmOverlord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export abstract class SwarmOverlord extends CombatOverlord {
creepQuantities[setup.role] += existingCreepsOfRole.length;
if (!neededQuantities[setup.role]) neededQuantities[setup.role] = 0;
neededQuantities[setup.role] += amount;
// Spawn the neede quantity of creeps
// Spawn the needed quantity of creeps
const spawnQuantity = amount - existingCreepsOfRole.length;
for (let i = 0; i < spawnQuantity; i++) {
this.requestCreep(setup, {priority: priority});
Expand Down
62 changes: 62 additions & 0 deletions src/overlords/defense/bunkerDefense.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {log} from '../../console/log';
import {CombatSetups, Roles} from '../../creepSetups/setups';
import {DirectiveInvasionDefense} from '../../directives/defense/invasionDefense';
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';

/**
* Spawns bunker-only defenders to defend against incoming sieges
*/
@profile
export class BunkerDefenseOverlord extends CombatOverlord {

lurkers: CombatZerg[];
room: Room;

static settings = {
retreatHitsPercent : 0.85,
reengageHitsPercent: 0.95,
};

constructor(directive: DirectiveInvasionDefense, boosted = false, priority = OverlordPriority.defense.meleeDefense) {
// Only spawn inside room
super(directive, 'bunkerDefense', priority, 1, 30);
this.lurkers = this.combatZerg(Roles.bunkerGuard, {
boostWishlist: boosted ? [boostResources.attack[3], boostResources.move[3]]
: undefined
});
}

private handleDefender(lurker: CombatZerg): void {
log.debug(`Running BunkerDefender in room ${this.room.print}`);
if (!lurker.inRampart) {
const nearRampart = _.find(lurker.room.walkableRamparts, rampart => rampart.pos.getRangeTo(lurker) < 5);
if (nearRampart) {
lurker.goTo(nearRampart);
}
}
if (lurker.room.hostiles.length > 0) {
lurker.autoBunkerCombat(lurker.room.name);
} else {
// go out of way in bunker
}
}

init() {
this.reassignIdleCreeps(Roles.bunkerGuard);
if (this.canBoostSetup(CombatSetups.bunkerGuard.boosted_T3)) {
const setup = CombatSetups.bunkerGuard.boosted_T3;
this.wishlist(1, setup);
} else {
const setup = CombatSetups.bunkerGuard.halfMove;
this.wishlist(1, setup);
}
}

run() {
this.autoRun(this.lurkers, lurkers => this.handleDefender(lurkers));
}
}
4 changes: 2 additions & 2 deletions src/overlords/defense/rangedDefense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export class RangedDefenseOverlord extends CombatOverlord {
priority = OverlordPriority.defense.rangedDefense) {
super(directive, 'rangedDefense', priority, 1);
this.hydralisks = this.combatZerg(Roles.ranged, {
boostWishlist: boosted ? [boostResources.ranged_attack[3], boostResources.heal[3], boostResources.move[3]]
: undefined
boostWishlist: boosted ? [boostResources.tough[3], boostResources.ranged_attack[3],
boostResources.heal[3], boostResources.move[3]] : undefined
});
}

Expand Down
26 changes: 26 additions & 0 deletions src/zerg/CombatZerg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {CombatIntel} from '../intel/CombatIntel';
import {Movement, NO_ACTION} from '../movement/Movement';
import {profile} from '../profiler/decorator';
import {insideBunkerBounds} from '../roomPlanner/layouts/bunker';
import {CombatTargeting} from '../targeting/CombatTargeting';
import {GoalFinder} from '../targeting/GoalFinder';
import {randomHex} from '../utilities/utils';
Expand Down Expand Up @@ -271,6 +272,31 @@ export class CombatZerg extends Zerg {

}

autoBunkerCombat(roomName: string, verbose = false) {
if (this.getActiveBodyparts(ATTACK) > 0) {
this.autoMelee(); // Melee should be performed first
}
if (this.getActiveBodyparts(RANGED_ATTACK) > 0) {
this.autoRanged();
}

// Travel to the target room
if (!this.safelyInRoom(roomName)) {
this.debug(`Going to room!`);
return this.goToRoom(roomName, {ensurePath: true});
}

// TODO check if right colony, also yes colony check is in there to stop red squigglies
const siegingCreeps = this.room.hostiles.filter(creep =>
_.any(creep.pos.neighbors, pos => this.colony && insideBunkerBounds(pos, this.colony)));

const target = CombatTargeting.findTarget(this, siegingCreeps);

if (target) {
return Movement.combatMove(this, [{pos: target.pos, range: 1}], [], {preferRamparts: true, requireRamparts: true});
}
}

needsToRecover(recoverThreshold = CombatIntel.minimumDamageTakenMultiplier(this.creep) < 1 ? 0.85 : 0.75,
reengageThreshold = 1.0): boolean {
let recovering: boolean;
Expand Down

0 comments on commit 5eca49a

Please sign in to comment.