|
6 | 6 | use bevy::prelude::*; |
7 | 7 | use serde::{Deserialize, Serialize}; |
8 | 8 |
|
| 9 | +use crate::game::constants::camera::{MAX_SMOOTHING, MIN_SMOOTHING}; |
| 10 | + |
9 | 11 | /// Marker component for the player entity |
10 | 12 | #[derive(Component, Debug, Default)] |
11 | 13 | pub struct Player; |
@@ -130,6 +132,125 @@ impl Default for JumpConfig { |
130 | 132 | } |
131 | 133 | } |
132 | 134 |
|
| 135 | +/// Marker component for obstacle entities |
| 136 | +#[derive(Component, Debug, Default)] |
| 137 | +pub struct Obstacle; |
| 138 | + |
| 139 | +/// Auto-movement component for entities that move automatically |
| 140 | +#[derive(Component, Debug, Clone)] |
| 141 | +pub struct AutoMove { |
| 142 | + /// Direction of movement (normalized) |
| 143 | + pub direction: Vec2, |
| 144 | + /// Movement speed |
| 145 | + pub speed: f32, |
| 146 | +} |
| 147 | + |
| 148 | +impl Default for AutoMove { |
| 149 | + fn default() -> Self { |
| 150 | + Self { |
| 151 | + direction: Vec2::new(-1.0, 0.0), // Move left by default |
| 152 | + speed: 150.0, |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +impl AutoMove { |
| 158 | + pub fn new(direction: Vec2, speed: f32) -> Self { |
| 159 | + Self { |
| 160 | + direction: direction.normalize_or_zero(), |
| 161 | + speed, |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + /// Creates a left-moving auto-move component |
| 166 | + pub fn left(speed: f32) -> Self { |
| 167 | + Self::new(Vec2::new(-1.0, 0.0), speed) |
| 168 | + } |
| 169 | + |
| 170 | + /// Creates a right-moving auto-move component |
| 171 | + pub fn right(speed: f32) -> Self { |
| 172 | + Self::new(Vec2::new(1.0, 0.0), speed) |
| 173 | + } |
| 174 | +} |
| 175 | + |
| 176 | +/// Marker component for the main camera that follows the player |
| 177 | +#[derive(Component, Debug, Default)] |
| 178 | +pub struct MainCamera; |
| 179 | + |
| 180 | +/// Camera follow configuration component |
| 181 | +#[derive(Component, Debug, Clone)] |
| 182 | +pub struct CameraFollow { |
| 183 | + /// Target entity to follow (usually the player) |
| 184 | + pub target: Option<Entity>, |
| 185 | + /// Offset from the target position |
| 186 | + pub offset: Vec3, |
| 187 | + /// Smoothing factor (0.0 = instant, 1.0 = very smooth/slow) |
| 188 | + pub smoothing: f32, |
| 189 | +} |
| 190 | + |
| 191 | +impl Default for CameraFollow { |
| 192 | + fn default() -> Self { |
| 193 | + Self { |
| 194 | + target: None, |
| 195 | + offset: Vec3::ZERO, |
| 196 | + smoothing: 0.1, |
| 197 | + } |
| 198 | + } |
| 199 | +} |
| 200 | + |
| 201 | +impl CameraFollow { |
| 202 | + pub fn new(target: Entity) -> Self { |
| 203 | + Self { |
| 204 | + target: Some(target), |
| 205 | + ..Default::default() |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + pub fn with_offset(mut self, offset: Vec3) -> Self { |
| 210 | + self.offset = offset; |
| 211 | + self |
| 212 | + } |
| 213 | + |
| 214 | + pub fn with_smoothing(mut self, smoothing: f32) -> Self { |
| 215 | + self.smoothing = smoothing.clamp(MIN_SMOOTHING, MAX_SMOOTHING); |
| 216 | + self |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +/// Marker component for UI root node |
| 221 | +#[derive(Component, Debug, Default)] |
| 222 | +pub struct GameUI; |
| 223 | + |
| 224 | +/// Marker component for score display UI |
| 225 | +#[derive(Component, Debug, Default)] |
| 226 | +pub struct ScoreDisplay; |
| 227 | + |
| 228 | +/// Marker component for health bar UI |
| 229 | +#[derive(Component, Debug, Default)] |
| 230 | +pub struct HealthBar; |
| 231 | + |
| 232 | +/// Marker component for health bar fill (the colored part) |
| 233 | +#[derive(Component, Debug, Default)] |
| 234 | +pub struct HealthBarFill; |
| 235 | + |
| 236 | +/// Damage value component for obstacles |
| 237 | +#[derive(Component, Debug, Clone)] |
| 238 | +pub struct DamageOnContact { |
| 239 | + pub damage: f32, |
| 240 | +} |
| 241 | + |
| 242 | +impl Default for DamageOnContact { |
| 243 | + fn default() -> Self { |
| 244 | + Self { damage: 10.0 } |
| 245 | + } |
| 246 | +} |
| 247 | + |
| 248 | +impl DamageOnContact { |
| 249 | + pub fn new(damage: f32) -> Self { |
| 250 | + Self { damage } |
| 251 | + } |
| 252 | +} |
| 253 | + |
133 | 254 | #[cfg(test)] |
134 | 255 | mod tests { |
135 | 256 | use super::*; |
@@ -211,4 +332,52 @@ mod tests { |
211 | 332 | assert_eq!(config.jump_velocity, 450.0); |
212 | 333 | assert_eq!(config.jump_cut_multiplier, 0.5); |
213 | 334 | } |
| 335 | + |
| 336 | + #[test] |
| 337 | + fn test_auto_move_default() { |
| 338 | + let auto_move = AutoMove::default(); |
| 339 | + assert_eq!(auto_move.direction, Vec2::new(-1.0, 0.0)); |
| 340 | + assert_eq!(auto_move.speed, 150.0); |
| 341 | + } |
| 342 | + |
| 343 | + #[test] |
| 344 | + fn test_auto_move_new() { |
| 345 | + let auto_move = AutoMove::new(Vec2::new(2.0, 0.0), 200.0); |
| 346 | + assert!((auto_move.direction.x - 1.0).abs() < f32::EPSILON); |
| 347 | + assert_eq!(auto_move.speed, 200.0); |
| 348 | + } |
| 349 | + |
| 350 | + #[test] |
| 351 | + fn test_auto_move_left() { |
| 352 | + let auto_move = AutoMove::left(100.0); |
| 353 | + assert_eq!(auto_move.direction, Vec2::new(-1.0, 0.0)); |
| 354 | + assert_eq!(auto_move.speed, 100.0); |
| 355 | + } |
| 356 | + |
| 357 | + #[test] |
| 358 | + fn test_auto_move_right() { |
| 359 | + let auto_move = AutoMove::right(100.0); |
| 360 | + assert_eq!(auto_move.direction, Vec2::new(1.0, 0.0)); |
| 361 | + assert_eq!(auto_move.speed, 100.0); |
| 362 | + } |
| 363 | + |
| 364 | + #[test] |
| 365 | + fn test_camera_follow_default() { |
| 366 | + let follow = CameraFollow::default(); |
| 367 | + assert!(follow.target.is_none()); |
| 368 | + assert_eq!(follow.offset, Vec3::ZERO); |
| 369 | + assert!((follow.smoothing - 0.1).abs() < f32::EPSILON); |
| 370 | + } |
| 371 | + |
| 372 | + #[test] |
| 373 | + fn test_damage_on_contact_default() { |
| 374 | + let damage = DamageOnContact::default(); |
| 375 | + assert_eq!(damage.damage, 10.0); |
| 376 | + } |
| 377 | + |
| 378 | + #[test] |
| 379 | + fn test_damage_on_contact_new() { |
| 380 | + let damage = DamageOnContact::new(25.0); |
| 381 | + assert_eq!(damage.damage, 25.0); |
| 382 | + } |
214 | 383 | } |
0 commit comments