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
![Capture d'écran 2025-03-30
174759](https://github.com/user-attachments/assets/8245723d-68de-4b96-ab19-5d85be18e4d9)


## Please put your Discord username so you can be contacted if a bug or
regression is found:
respectful pinguin
This commit is contained in:
Ilan Schemoul
2025-04-18 04:02:50 +02:00
committed by GitHub
parent 9aa933b676
commit 8925f48ba7
6 changed files with 113 additions and 43 deletions
+1
View File
@@ -52,6 +52,7 @@ export interface NukeMagnitude {
export interface Config {
samHittingChance(): number;
samWarheadHittingChance(): number;
spawnImmunityDuration(): Tick;
serverConfig(): ServerConfig;
gameConfig(): GameConfig;
+4
View File
@@ -136,6 +136,10 @@ export class DefaultConfig implements Config {
return 0.8;
}
samWarheadHittingChance(): number {
return 0.5;
}
traitorDefenseDebuff(): number {
return 0.8;
}
+8
View File
@@ -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";
}
-1
View File
@@ -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;
+99 -41
View File
@@ -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 = [];
}
}
}
}
+1 -1
View File
@@ -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,