mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 14:10:45 +00:00
sam protects againt mirv warhead (#376)
Fixes #483 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors  ## Please put your Discord username so you can be contacted if a bug or regression is found: respectful pinguin
This commit is contained in:
@@ -52,6 +52,7 @@ export interface NukeMagnitude {
|
||||
|
||||
export interface Config {
|
||||
samHittingChance(): number;
|
||||
samWarheadHittingChance(): number;
|
||||
spawnImmunityDuration(): Tick;
|
||||
serverConfig(): ServerConfig;
|
||||
gameConfig(): GameConfig;
|
||||
|
||||
@@ -136,6 +136,10 @@ export class DefaultConfig implements Config {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
samWarheadHittingChance(): number {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
traitorDefenseDebuff(): number {
|
||||
return 0.8;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,14 @@ export class DevServerConfig extends DefaultServerConfig {
|
||||
return Math.random() < 0.5 ? 2 : 3;
|
||||
}
|
||||
|
||||
samWarheadHittingChance(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
samHittingChance(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
discordRedirectURI(): string {
|
||||
return "http://localhost:3000/auth/callback";
|
||||
}
|
||||
|
||||
@@ -170,7 +170,6 @@ export class MirvExecution implements Execution {
|
||||
if (!this.mg.isValidCoord(x, y)) {
|
||||
continue;
|
||||
}
|
||||
console.log(`got coord ${x}, ${y}`);
|
||||
const tile = this.mg.ref(x, y);
|
||||
if (!this.mg.isLand(tile)) {
|
||||
continue;
|
||||
|
||||
@@ -19,8 +19,13 @@ export class SAMLauncherExecution implements Execution {
|
||||
private active: boolean = true;
|
||||
|
||||
private target: Unit = null;
|
||||
private warheadTargets: Unit[] = [];
|
||||
|
||||
private searchRangeRadius = 75;
|
||||
// As MIRV go very fast we have to detect them very early but we only
|
||||
// shoot the one targeting very close (MIRVWarheadProtectionRadius)
|
||||
private MIRVWarheadSearchRadius = 400;
|
||||
private MIRVWarheadProtectionRadius = 50;
|
||||
|
||||
private pseudoRandom: PseudoRandom;
|
||||
|
||||
@@ -39,6 +44,52 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.player = mg.player(this.ownerId);
|
||||
}
|
||||
|
||||
private getSingleTarget(): Unit | null {
|
||||
const nukes = this.mg
|
||||
.nearbyUnits(this.sam.tile(), this.searchRangeRadius, [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
])
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
);
|
||||
|
||||
return (
|
||||
nukes.sort((a, b) => {
|
||||
const { unit: unitA, distSquared: distA } = a;
|
||||
const { unit: unitB, distSquared: distB } = b;
|
||||
|
||||
// Prioritize Hydrogen Bombs
|
||||
if (
|
||||
unitA.type() === UnitType.HydrogenBomb &&
|
||||
unitB.type() !== UnitType.HydrogenBomb
|
||||
)
|
||||
return -1;
|
||||
if (
|
||||
unitA.type() !== UnitType.HydrogenBomb &&
|
||||
unitB.type() === UnitType.HydrogenBomb
|
||||
)
|
||||
return 1;
|
||||
|
||||
// If both are the same type, sort by distance (lower `distSquared` means closer)
|
||||
return distA - distB;
|
||||
})[0]?.unit ?? null
|
||||
);
|
||||
}
|
||||
|
||||
private isHit(type: UnitType, random: number): boolean {
|
||||
if (type == UnitType.AtomBomb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == UnitType.MIRVWarhead) {
|
||||
return random < this.mg.config().samWarheadHittingChance();
|
||||
}
|
||||
|
||||
return random < this.mg.config().samHittingChance();
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.sam == null) {
|
||||
const spawnTile = this.player.canBuild(UnitType.SAMLauncher, this.tile);
|
||||
@@ -64,36 +115,26 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.pseudoRandom = new PseudoRandom(this.sam.id());
|
||||
}
|
||||
|
||||
const nukes = this.mg
|
||||
.nearbyUnits(this.sam.tile(), this.searchRangeRadius, [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
])
|
||||
this.warheadTargets = this.mg
|
||||
.nearbyUnits(
|
||||
this.sam.tile(),
|
||||
this.MIRVWarheadSearchRadius,
|
||||
UnitType.MIRVWarhead,
|
||||
)
|
||||
.map(({ unit }) => unit)
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
(unit) =>
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
)
|
||||
.filter(
|
||||
(unit) =>
|
||||
this.mg.manhattanDist(unit.detonationDst(), this.sam.tile()) <
|
||||
this.MIRVWarheadProtectionRadius,
|
||||
);
|
||||
|
||||
this.target =
|
||||
nukes.sort((a, b) => {
|
||||
const { unit: unitA, distSquared: distA } = a;
|
||||
const { unit: unitB, distSquared: distB } = b;
|
||||
|
||||
// Prioritize Hydrogen Bombs
|
||||
if (
|
||||
unitA.type() === UnitType.HydrogenBomb &&
|
||||
unitB.type() !== UnitType.HydrogenBomb
|
||||
)
|
||||
return -1;
|
||||
if (
|
||||
unitA.type() !== UnitType.HydrogenBomb &&
|
||||
unitB.type() === UnitType.HydrogenBomb
|
||||
)
|
||||
return 1;
|
||||
|
||||
// If both are the same type, sort by distance (lower `distSquared` means closer)
|
||||
return distA - distB;
|
||||
})[0]?.unit ?? null;
|
||||
if (this.warheadTargets.length == 0) {
|
||||
this.target = this.getSingleTarget();
|
||||
}
|
||||
|
||||
if (
|
||||
this.sam.isCooldown() &&
|
||||
@@ -102,29 +143,46 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.sam.setCooldown(false);
|
||||
}
|
||||
|
||||
if (this.target && !this.sam.isCooldown() && !this.target.targetedBySAM()) {
|
||||
const isSingleTarget = this.target && !this.target.targetedBySAM();
|
||||
if (
|
||||
(isSingleTarget || this.warheadTargets.length > 0) &&
|
||||
!this.sam.isCooldown()
|
||||
) {
|
||||
this.sam.setCooldown(true);
|
||||
const type =
|
||||
this.warheadTargets.length > 0
|
||||
? UnitType.MIRVWarhead
|
||||
: this.target.type();
|
||||
const random = this.pseudoRandom.next();
|
||||
let hit = true;
|
||||
if (this.target.type() != UnitType.AtomBomb) {
|
||||
hit = random < this.mg.config().samHittingChance();
|
||||
}
|
||||
const hit = this.isHit(type, random);
|
||||
if (!hit) {
|
||||
this.mg.displayMessage(
|
||||
`Missile failed to intercept ${this.target.type()}`,
|
||||
`Missile failed to intercept ${type}`,
|
||||
MessageType.ERROR,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
} else {
|
||||
this.target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
this.target,
|
||||
),
|
||||
);
|
||||
if (this.warheadTargets.length > 0) {
|
||||
// Message
|
||||
this.mg.displayMessage(
|
||||
`${this.warheadTargets.length} MIRV warheads intercepted`,
|
||||
MessageType.SUCCESS,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
// Delete warheads
|
||||
this.warheadTargets.forEach((u) => u.delete());
|
||||
} else {
|
||||
this.target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
this.target,
|
||||
),
|
||||
);
|
||||
this.warheadTargets = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ export class UnitImpl implements Unit {
|
||||
this._active = false;
|
||||
this.mg.addUpdate(this.toUpdate());
|
||||
this.mg.removeUnit(this);
|
||||
if (displayMessage) {
|
||||
if (displayMessage && this.type() != UnitType.MIRVWarhead) {
|
||||
this.mg.displayMessage(
|
||||
`Your ${this.type()} was destroyed`,
|
||||
MessageType.ERROR,
|
||||
|
||||
Reference in New Issue
Block a user