Merge branch 'feat/demo-no-friendly-fire': el pilot IA de la demo no dispara si té el company en la línia de tir

This commit is contained in:
2026-05-30 10:15:50 +02:00
3 changed files with 40 additions and 3 deletions
+7
View File
@@ -408,8 +408,15 @@ auto GameScene::stepDemo(float delta_time, bool input_blocked) -> bool {
demo_ctrls_[i] = {}; // nau inactiva/morta: sense control
continue;
}
// Company per a evitar foc amic en demo de 2 naus: només es passa si l'altre
// jugador està actiu al match (un slot no usat tindria isActive()==true).
const uint8_t OTHER = 1U - i;
const bool OTHER_ACTIVE = (OTHER == 0) ? match_config_.player1_active
: match_config_.player2_active;
const Ship* teammate = OTHER_ACTIVE ? &ships_[OTHER] : nullptr;
demo_ctrls_[i] = demo_pilots_[i].compute(
ships_[i],
teammate,
enemies_,
bullets_,
Defaults::Zones::PLAYAREA,
+32 -3
View File
@@ -36,6 +36,10 @@ namespace Systems::Demo {
constexpr float DODGE_SCAN_RADIUS = 190.0F; // px: distancia a la que reacciona
constexpr float DODGE_HEADING_MIN = 0.25F; // dot mínimo: la bala viene hacia la nave
// Foc amic: retindre el tret si el company està en la línia de tir.
constexpr float FRIENDLY_BLOCK_RANGE = 1200.0F; // px endavant que es vigilen
constexpr float FRIENDLY_BLOCK_MARGIN = 22.0F; // px: marge sobre el radi del company
// [-1, 1] aleatorio (estética: jitter de apuntado; no afecta a la simulación).
auto randSigned() -> float {
return (static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX) * 2.0F) - 1.0F;
@@ -136,9 +140,26 @@ namespace Systems::Demo {
}
}
// ¿El company està en la línia de tir? (davant del morro, dins de l'abast i
// a prou poca distància perpendicular de la trajectòria recta de la bala).
// La bala ix en la direcció del morro: (cos, sin)(angle - PI/2).
auto teammateInLineOfFire(const Ship& ship, const Ship& mate) -> bool {
const float NOSE = ship.getAngle() - (PI / 2.0F);
const Vec2 FORWARD{.x = std::cos(NOSE), .y = std::sin(NOSE)};
const Vec2 TO_MATE = mate.getCenter() - ship.getCenter();
const float ALONG = TO_MATE.dot(FORWARD); // distància al llarg del tret
if (ALONG <= 0.0F || ALONG > FRIENDLY_BLOCK_RANGE) {
return false; // darrere de la nau o massa lluny
}
const float PERP2 = TO_MATE.lengthSquared() - (ALONG * ALONG);
const float CLEAR = mate.getCollisionRadius() + FRIENDLY_BLOCK_MARGIN;
return PERP2 < CLEAR * CLEAR;
}
} // namespace
auto DemoPilot::compute(const Ship& ship,
const Ship* teammate,
const std::array<Enemy, Constants::MAX_ORNIS>& enemies,
const std::array<Bullet, static_cast<std::size_t>(Defaults::Entities::MAX_BULLETS_TOTAL)>& bullets,
const SDL_FRect& play_area,
@@ -218,10 +239,18 @@ namespace Systems::Demo {
std::fabs(ERROR) < FIRE_TOLERANCE) {
ctrl.thrust = true;
}
// Disparar cuando está bien encarada y el cooldown lo permite.
// Disparar cuando está bien encarada y el cooldown lo permite, però NO
// si el company està en la línia de tir (i seria víctima vàlida de foc
// amic): es retén el tret sense gastar cooldown, així dispara tan prompte
// com l'altra nau isca de la línia.
if (std::fabs(ERROR) < FIRE_TOLERANCE && fire_cooldown_ <= 0.0F) {
ctrl.shoot = true;
fire_cooldown_ = FIRE_COOLDOWN;
const bool FRIENDLY_BLOCK = (teammate != nullptr) &&
teammate->isActive() && !teammate->isInvulnerable() &&
teammateInLineOfFire(ship, *teammate);
if (!FRIENDLY_BLOCK) {
ctrl.shoot = true;
fire_cooldown_ = FIRE_COOLDOWN;
}
}
}
+1
View File
@@ -57,6 +57,7 @@ namespace Systems::Demo {
class DemoPilot {
public:
[[nodiscard]] auto compute(const Ship& ship,
const Ship* teammate, // altra nau (nullptr si no n'hi ha): evita foc amic
const std::array<Enemy, Constants::MAX_ORNIS>& enemies,
const std::array<Bullet, static_cast<std::size_t>(Defaults::Entities::MAX_BULLETS_TOTAL)>& bullets,
const SDL_FRect& play_area,