Merge branch 'feat/enemy-star': afegir tipus STAR i 3 nous shapes
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
name: star
|
||||
ai_type: star # Validat contra el directori; mapeja a EnemyType::STAR.
|
||||
|
||||
shape:
|
||||
path: star_5.shp
|
||||
scale: 0.7 # Lleugerament més petit que els altres enemics per diferenciar visualment.
|
||||
collision_factor: 1.0
|
||||
|
||||
physics:
|
||||
mass: 5.0
|
||||
speed: 35.0 # Mateixos paràmetres que pentagon (esquivador lent).
|
||||
rotation_delta_min: 0.75
|
||||
rotation_delta_max: 3.75
|
||||
restitution: 1.0
|
||||
linear_damping: 0.0
|
||||
angular_damping: 0.0
|
||||
|
||||
behavior:
|
||||
# Hereta el comportament de Pentagon (zigzag esquivador).
|
||||
angle_change_max: 1.0
|
||||
zigzag_prob_per_second: 0.8
|
||||
|
||||
animation:
|
||||
pulse:
|
||||
trigger_prob_per_second: 0.01
|
||||
duration_min: 1.0
|
||||
duration_max: 3.0
|
||||
amplitude_min: 0.08
|
||||
amplitude_max: 0.20
|
||||
frequency_min: 1.5
|
||||
frequency_max: 3.0
|
||||
rotation_accel:
|
||||
trigger_prob_per_second: 0.02
|
||||
duration_min: 3.0
|
||||
duration_max: 8.0
|
||||
multiplier_min: 0.3
|
||||
multiplier_max: 4.0
|
||||
|
||||
wounded:
|
||||
duration: 1.0
|
||||
blink_hz: 10.0
|
||||
|
||||
spawn:
|
||||
invulnerability_duration: 3.0
|
||||
invulnerability_brightness_start: 0.3
|
||||
invulnerability_brightness_end: 0.7
|
||||
invulnerability_scale_start: 0.0
|
||||
invulnerability_scale_end: 1.0
|
||||
safety_distance: 36.0
|
||||
|
||||
colors:
|
||||
normal: [255, 255, 0] # Groc estrella
|
||||
wounded: [255, 220, 60]
|
||||
|
||||
score: 100
|
||||
@@ -0,0 +1,17 @@
|
||||
# bullet_double.shp - Bala anular (dos cercles concèntrics)
|
||||
# © 2026 JailDesigner
|
||||
#
|
||||
# Dos octàgons concèntrics al centre (0,0):
|
||||
# - Exterior: radi 4 (lleugerament més gran que la bala estàndard, radi 3)
|
||||
# - Interior: radi 2 (lleugerament més petit que la bala estàndard)
|
||||
# Aspecte d'anell / aura de plasma. Bounding radius natiu = 4.
|
||||
|
||||
name: bullet_double
|
||||
scale: 1.0
|
||||
center: 0, 0
|
||||
|
||||
# Cercle exterior (octàgon, radi 4)
|
||||
polyline: 0,-4 2.83,-2.83 4,0 2.83,2.83 0,4 -2.83,2.83 -4,0 -2.83,-2.83 0,-4
|
||||
|
||||
# Cercle interior (octàgon, radi 2)
|
||||
polyline: 0,-2 1.41,-1.41 2,0 1.41,1.41 0,2 -1.41,1.41 -2,0 -1.41,-1.41 0,-2
|
||||
@@ -0,0 +1,28 @@
|
||||
# bullet_long.shp - Bala allargada (dos octàgons tangents + tapes superior i inferior)
|
||||
# © 2026 JailDesigner
|
||||
#
|
||||
# Dos cercles (octàgons radi 3) tangents externament al punt (0,0), units
|
||||
# per una línia horitzontal superior i una d'inferior. La silueta resultant
|
||||
# és una càpsula amb la separació visible dels dos cercles al centre.
|
||||
#
|
||||
# Geometria:
|
||||
# Centre octàgon esquerre: (-3, 0)
|
||||
# Centre octàgon dret: ( 3, 0)
|
||||
# Punt de tangència: ( 0, 0)
|
||||
# Bounding radius natiu ≈ 6 (extrem horitzontal a x=±6).
|
||||
|
||||
name: bullet_long
|
||||
scale: 1.0
|
||||
center: 0, 0
|
||||
|
||||
# Octàgon esquerre (centre x=-3, radi 3)
|
||||
polyline: -3,-3 -0.88,-2.12 0,0 -0.88,2.12 -3,3 -5.12,2.12 -6,0 -5.12,-2.12 -3,-3
|
||||
|
||||
# Octàgon dret (centre x=3, radi 3)
|
||||
polyline: 3,-3 5.12,-2.12 6,0 5.12,2.12 3,3 0.88,2.12 0,0 0.88,-2.12 3,-3
|
||||
|
||||
# Tapa superior: uneix el cim de l'octàgon esquerre amb el del dret
|
||||
polyline: -3,-3 3,-3
|
||||
|
||||
# Tapa inferior: uneix la base de l'octàgon esquerre amb la del dret
|
||||
polyline: -3,3 3,3
|
||||
@@ -0,0 +1,15 @@
|
||||
# star_5.shp - ORNI enemic (estrella de 5 puntes, només perímetre)
|
||||
# © 2026 JailDesigner
|
||||
#
|
||||
# Pentagrama clàssic: 5 vèrtexs exteriors (radi 20) alternant amb 5 vèrtexs
|
||||
# interiors (radi 7.64 = 20/φ² ≈ proporció àuria) per donar puntes esveltes.
|
||||
# Vèrtex apuntant amunt (igual que enemy_pentagon).
|
||||
#
|
||||
# Sense línies interiors: una única polyline que recorre el perímetre.
|
||||
# Bounding radius natiu ≈ 20 (alineat amb pentagon/square/pinwheel).
|
||||
|
||||
name: star_5
|
||||
scale: 1.0
|
||||
center: 0, 0
|
||||
|
||||
polyline: 0,-20 4.49,-6.18 19.02,-6.18 7.27,2.36 11.76,16.18 0,7.64 -11.76,16.18 -7.27,2.36 -19.02,-6.18 -4.49,-6.18 0,-20
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
description: "Progressive difficulty curve from novice to expert"
|
||||
|
||||
stages:
|
||||
# STAGE 1: Tutorial - Mix de tots els tipus, velocitat lenta
|
||||
# STAGE 1: Tutorial - Mix de tots 4 tipus al 25% per mostrar-los junts
|
||||
- stage_id: 1
|
||||
total_enemies: 50
|
||||
spawn_config:
|
||||
@@ -15,9 +15,10 @@ stages:
|
||||
initial_delay: 0.3
|
||||
spawn_interval: 0.4
|
||||
enemy_distribution:
|
||||
pentagon: 34
|
||||
cuadrado: 33
|
||||
molinillo: 33
|
||||
pentagon: 25
|
||||
cuadrado: 25
|
||||
molinillo: 25
|
||||
star: 25
|
||||
difficulty_multipliers:
|
||||
speed_multiplier: 0.7
|
||||
rotation_multiplier: 0.8
|
||||
|
||||
@@ -170,6 +170,9 @@ void Enemy::update(float delta_time) {
|
||||
if (!isWounded()) {
|
||||
switch (type_) {
|
||||
case EnemyType::PENTAGON:
|
||||
case EnemyType::STAR:
|
||||
// STAR reusa el zigzag esquivador de Pentagon. Si en el futur
|
||||
// vol comportament propi, separa-li el cas.
|
||||
behaviorPentagon(delta_time);
|
||||
break;
|
||||
case EnemyType::SQUARE:
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
enum class EnemyType : uint8_t {
|
||||
PENTAGON = 0, // Pentágono esquivador (zigzag)
|
||||
SQUARE = 1, // Square perseguidor (tracks ship)
|
||||
PINWHEEL = 2 // Molinillo agresivo (rápido, girando)
|
||||
PINWHEEL = 2, // Molinillo agresivo (rápido, girando)
|
||||
STAR = 3 // Estrella de 5 puntes (clone visual de Pentagon, comportament zigzag)
|
||||
};
|
||||
|
||||
// Forward declaration — EnemyConfig viu a enemy_config.hpp i s'inclou només a enemy.cpp.
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace {
|
||||
if (s == "pentagon") { return EnemyType::PENTAGON; }
|
||||
if (s == "square") { return EnemyType::SQUARE; }
|
||||
if (s == "pinwheel") { return EnemyType::PINWHEEL; }
|
||||
if (s == "star") { return EnemyType::STAR; }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
EnemyConfig EnemyRegistry::pentagon_config;
|
||||
EnemyConfig EnemyRegistry::square_config;
|
||||
EnemyConfig EnemyRegistry::pinwheel_config;
|
||||
EnemyConfig EnemyRegistry::star_config;
|
||||
bool EnemyRegistry::loaded = false;
|
||||
|
||||
namespace {
|
||||
@@ -36,10 +37,11 @@ namespace {
|
||||
auto EnemyRegistry::loadAll() -> bool {
|
||||
const bool OK = loadOne("pentagon", EnemyType::PENTAGON, pentagon_config) &&
|
||||
loadOne("square", EnemyType::SQUARE, square_config) &&
|
||||
loadOne("pinwheel", EnemyType::PINWHEEL, pinwheel_config);
|
||||
loadOne("pinwheel", EnemyType::PINWHEEL, pinwheel_config) &&
|
||||
loadOne("star", EnemyType::STAR, star_config);
|
||||
loaded = OK;
|
||||
if (OK) {
|
||||
std::cout << "[EnemyRegistry] 3 configuracions d'enemic carregades.\n";
|
||||
std::cout << "[EnemyRegistry] 4 configuracions d'enemic carregades.\n";
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@@ -56,6 +58,8 @@ auto EnemyRegistry::get(EnemyType type) -> const EnemyConfig& {
|
||||
return square_config;
|
||||
case EnemyType::PINWHEEL:
|
||||
return pinwheel_config;
|
||||
case EnemyType::STAR:
|
||||
return star_config;
|
||||
}
|
||||
std::cerr << "[EnemyRegistry] FATAL: tipus desconegut\n";
|
||||
std::exit(EXIT_FAILURE);
|
||||
|
||||
@@ -26,5 +26,6 @@ class EnemyRegistry {
|
||||
static EnemyConfig pentagon_config;
|
||||
static EnemyConfig square_config;
|
||||
static EnemyConfig pinwheel_config;
|
||||
static EnemyConfig star_config;
|
||||
static bool loaded;
|
||||
};
|
||||
|
||||
@@ -136,8 +136,11 @@ namespace StageSystem {
|
||||
if (rand_val < config_->distribucio.pentagon + config_->distribucio.cuadrado) {
|
||||
return EnemyType::SQUARE;
|
||||
}
|
||||
if (rand_val < config_->distribucio.pentagon + config_->distribucio.cuadrado + config_->distribucio.molinillo) {
|
||||
return EnemyType::PINWHEEL;
|
||||
}
|
||||
return EnemyType::STAR;
|
||||
}
|
||||
|
||||
void SpawnController::spawnEnemy(Enemy& enemy, EnemyType type, const Vec2* ship_pos) {
|
||||
// Initialize enemy (with safe spawn if ship_pos provided)
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace StageSystem {
|
||||
uint8_t pentagon; // 0-100
|
||||
uint8_t cuadrado; // 0-100
|
||||
uint8_t molinillo; // 0-100
|
||||
uint8_t star{0}; // 0-100 (opcional al YAML; default 0 per compat amb stages antics)
|
||||
// Suma ha de ser 100, validat en StageLoader
|
||||
};
|
||||
|
||||
@@ -59,7 +60,7 @@ namespace StageSystem {
|
||||
// el tipo; basta con confirmar que no es 0 (sentinela "sin asignar").
|
||||
return stage_id >= 1 &&
|
||||
total_enemies > 0 && total_enemies <= 200 &&
|
||||
distribucio.pentagon + distribucio.cuadrado + distribucio.molinillo == 100;
|
||||
distribucio.pentagon + distribucio.cuadrado + distribucio.molinillo + distribucio.star == 100;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
namespace StageSystem {
|
||||
|
||||
auto StageLoader::load(const std::string& path) -> std::unique_ptr<StageSystemConfig> {
|
||||
auto StageLoader::load(const std::string& path) -> std::unique_ptr<StageSystemConfig> {
|
||||
try {
|
||||
// Normalize path: "data/stages/stages.yaml" → "stages/stages.yaml"
|
||||
std::string normalized = path;
|
||||
@@ -83,9 +83,9 @@ auto StageLoader::load(const std::string& path) -> std::unique_ptr<StageSystemCo
|
||||
std::cerr << "[StageLoader] Excepció: " << e.what() << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseMetadata(const fkyaml::node& yaml, MetadataStages& meta) -> bool {
|
||||
auto StageLoader::parseMetadata(const fkyaml::node& yaml, MetadataStages& meta) -> bool {
|
||||
try {
|
||||
if (!yaml.contains("version") || !yaml.contains("total_stages")) {
|
||||
std::cerr << "[StageLoader] Error: metadata incompleta" << '\n';
|
||||
@@ -103,9 +103,9 @@ auto StageLoader::parseMetadata(const fkyaml::node& yaml, MetadataStages& meta)
|
||||
std::cerr << "[StageLoader] Error parsing metadata: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseStage(const fkyaml::node& yaml, StageConfig& stage) -> bool {
|
||||
auto StageLoader::parseStage(const fkyaml::node& yaml, StageConfig& stage) -> bool {
|
||||
try {
|
||||
if (!yaml.contains("stage_id") || !yaml.contains("total_enemies") ||
|
||||
!yaml.contains("spawn_config") || !yaml.contains("enemy_distribution") ||
|
||||
@@ -138,9 +138,9 @@ auto StageLoader::parseStage(const fkyaml::node& yaml, StageConfig& stage) -> bo
|
||||
std::cerr << "[StageLoader] Error parsing stage: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseSpawnConfig(const fkyaml::node& yaml, ConfigSpawn& config) -> bool {
|
||||
auto StageLoader::parseSpawnConfig(const fkyaml::node& yaml, ConfigSpawn& config) -> bool {
|
||||
try {
|
||||
if (!yaml.contains("mode") || !yaml.contains("initial_delay") ||
|
||||
!yaml.contains("spawn_interval")) {
|
||||
@@ -158,9 +158,9 @@ auto StageLoader::parseSpawnConfig(const fkyaml::node& yaml, ConfigSpawn& config
|
||||
std::cerr << "[StageLoader] Error parsing spawn_config: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseDistribution(const fkyaml::node& yaml, DistribucioEnemics& dist) -> bool {
|
||||
auto StageLoader::parseDistribution(const fkyaml::node& yaml, DistribucioEnemics& dist) -> bool {
|
||||
try {
|
||||
if (!yaml.contains("pentagon") || !yaml.contains("cuadrado") ||
|
||||
!yaml.contains("molinillo")) {
|
||||
@@ -171,9 +171,11 @@ auto StageLoader::parseDistribution(const fkyaml::node& yaml, DistribucioEnemics
|
||||
dist.pentagon = yaml["pentagon"].get_value<uint8_t>();
|
||||
dist.cuadrado = yaml["cuadrado"].get_value<uint8_t>();
|
||||
dist.molinillo = yaml["molinillo"].get_value<uint8_t>();
|
||||
// 'star' és opcional per compatibilitat amb stages antics (default 0).
|
||||
dist.star = yaml.contains("star") ? yaml["star"].get_value<uint8_t>() : 0;
|
||||
|
||||
// Validar que suma 100
|
||||
int sum = dist.pentagon + dist.cuadrado + dist.molinillo;
|
||||
int sum = dist.pentagon + dist.cuadrado + dist.molinillo + dist.star;
|
||||
if (sum != 100) {
|
||||
std::cerr << "[StageLoader] Error: distribució no suma 100 (suma=" << sum << ")" << '\n';
|
||||
return false;
|
||||
@@ -184,9 +186,9 @@ auto StageLoader::parseDistribution(const fkyaml::node& yaml, DistribucioEnemics
|
||||
std::cerr << "[StageLoader] Error parsing distribution: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseMultipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult) -> bool {
|
||||
auto StageLoader::parseMultipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult) -> bool {
|
||||
try {
|
||||
if (!yaml.contains("speed_multiplier") || !yaml.contains("rotation_multiplier") ||
|
||||
!yaml.contains("tracking_strength")) {
|
||||
@@ -214,9 +216,9 @@ auto StageLoader::parseMultipliers(const fkyaml::node& yaml, MultiplicadorsDific
|
||||
std::cerr << "[StageLoader] Error parsing multipliers: " << e.what() << '\n';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::parseSpawnMode(const std::string& mode_str) -> ModeSpawn {
|
||||
auto StageLoader::parseSpawnMode(const std::string& mode_str) -> ModeSpawn {
|
||||
if (mode_str == "progressive") {
|
||||
return ModeSpawn::PROGRESSIVE;
|
||||
}
|
||||
@@ -229,9 +231,9 @@ auto StageLoader::parseSpawnMode(const std::string& mode_str) -> ModeSpawn {
|
||||
std::cerr << "[StageLoader] Warning: mode de spawn desconegut '" << mode_str
|
||||
<< "', usant PROGRESSIVE" << '\n';
|
||||
return ModeSpawn::PROGRESSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
auto StageLoader::validateConfig(const StageSystemConfig& config) -> bool {
|
||||
auto StageLoader::validateConfig(const StageSystemConfig& config) -> bool {
|
||||
if (config.stages.empty()) {
|
||||
std::cerr << "[StageLoader] Error: sin stage carregat" << '\n';
|
||||
return false;
|
||||
@@ -254,6 +256,6 @@ auto StageLoader::validateConfig(const StageSystemConfig& config) -> bool {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace StageSystem
|
||||
|
||||
Reference in New Issue
Block a user