use super::patterns::BulletBehavior;
use bevy::prelude::*;
use souprune_api::{DanmakuInstance, PropC};
use std::collections::HashMap;
use std::ffi::CString;
#[derive(Component)]
pub struct Bullet;
#[derive(Component, Debug)]
pub struct BulletContainer {
pub center: Vec2,
}
#[derive(Component)]
pub struct BulletLifetime {
pub timer: Timer,
}
impl BulletLifetime {
pub fn new(seconds: f32) -> Self {
Self {
timer: Timer::from_seconds(seconds, TimerMode::Once),
}
}
}
#[derive(Component)]
pub struct BulletDamage(pub f32);
impl Default for BulletDamage {
fn default() -> Self {
Self(1.0)
}
}
#[derive(Component, Debug, Clone, Default)]
pub struct BulletHitBehavior {
pub despawn_on_hit: bool,
pub damage_on_player_moving: bool,
pub damage_on_player_stationary: bool,
pub invincibility_duration: f32,
}
impl BulletHitBehavior {
pub fn default_despawn() -> Self {
Self {
despawn_on_hit: true,
damage_on_player_moving: false,
damage_on_player_stationary: false,
invincibility_duration: 0.0,
}
}
pub fn persistent() -> Self {
Self {
despawn_on_hit: false,
damage_on_player_moving: false,
damage_on_player_stationary: false,
invincibility_duration: 0.5, }
}
pub fn damage_when_moving() -> Self {
Self {
despawn_on_hit: true,
damage_on_player_moving: true,
damage_on_player_stationary: false,
invincibility_duration: 0.0,
}
}
pub fn damage_when_stationary() -> Self {
Self {
despawn_on_hit: true,
damage_on_player_moving: false,
damage_on_player_stationary: true,
invincibility_duration: 0.0,
}
}
}
#[derive(Component, Debug, Clone, Default)]
pub struct BulletLastHitTime(pub f32);
#[derive(Component, Debug, Clone, Copy)]
pub struct BulletBaseScale(pub f32);
impl Default for BulletBaseScale {
fn default() -> Self {
Self(1.0)
}
}
#[derive(Component)]
pub struct DespawnBullet;
#[derive(Component)]
pub struct BulletMotionState {
pub elapsed: f32,
pub spawn_center: Vec2,
pub initial_offset: Vec2,
pub initial_angle: f32,
pub initial_radius: f32,
pub velocity_dir: Vec2,
}
impl BulletMotionState {
pub fn new(spawn_center: Vec2) -> Self {
Self {
elapsed: 0.0,
spawn_center,
initial_offset: Vec2::ZERO,
initial_angle: 0.0,
initial_radius: 0.0,
velocity_dir: Vec2::NEG_Y,
}
}
pub fn with_offset(mut self, offset: Vec2) -> Self {
self.initial_offset = offset;
self
}
pub fn with_angle(mut self, angle: f32) -> Self {
self.initial_angle = angle;
self.velocity_dir = Vec2::new(angle.cos(), angle.sin());
self
}
pub fn with_radius(mut self, radius: f32) -> Self {
self.initial_radius = radius;
self
}
}
#[derive(Component, Clone, Default)]
pub struct BehaviorStack {
pub behaviors: Vec<BulletBehavior>,
pub cached_params: Vec<f32>,
}
impl BehaviorStack {
pub fn new(behaviors: Vec<BulletBehavior>) -> Self {
Self {
behaviors,
cached_params: Vec::new(),
}
}
pub fn with_cached_params(mut self, params: Vec<f32>) -> Self {
self.cached_params = params;
self
}
}
#[derive(Component, Default)]
pub struct TweenState {
pub timers: Vec<f32>,
}
#[derive(Component)]
pub struct PerformancePlayer {
pub elapsed: f32,
pub next_event_index: usize,
pub finished: bool,
pub spawn_center: Vec2,
pub container_entity: Option<Entity>,
}
impl PerformancePlayer {
pub fn new(spawn_center: Vec2) -> Self {
Self {
elapsed: 0.0,
next_event_index: 0,
finished: false,
spawn_center,
container_entity: None,
}
}
}
#[derive(Component)]
pub struct PerformanceHandle(pub Handle<super::patterns::DanmakuPerformance>);
#[derive(Component)]
pub struct PerformancePlayerMarker;
#[derive(Component)]
pub struct ActiveDanmaku {
instance: DanmakuInstance,
initialized: bool,
pub props: HashMap<String, f32>,
pub params: Vec<f32>,
cached_props: Vec<PropC>,
#[allow(dead_code)]
cached_names: Vec<CString>,
}
unsafe impl Send for ActiveDanmaku {}
unsafe impl Sync for ActiveDanmaku {}
impl ActiveDanmaku {
pub fn new(instance: DanmakuInstance, props: HashMap<String, f32>, params: Vec<f32>) -> Self {
let mut cached_names = Vec::with_capacity(props.len());
let mut cached_props = Vec::with_capacity(props.len());
for (name, value) in &props {
let c_name = CString::new(name.as_str()).unwrap_or_default();
cached_props.push(PropC {
name: c_name.as_ptr() as *const u8,
name_len: name.len(),
value: *value,
});
cached_names.push(c_name);
}
Self {
instance,
initialized: false,
props,
params,
cached_props,
cached_names,
}
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn call_on_enter(&mut self, ctx: &souprune_api::BulletContextC) {
if !self.initialized {
if let Some(on_enter) = self.instance.vtable.on_enter {
on_enter(self.instance.instance, ctx);
}
self.initialized = true;
}
}
pub fn ffi_props(&self) -> (*const PropC, usize) {
(self.cached_props.as_ptr(), self.cached_props.len())
}
pub fn call_on_update(
&mut self,
ctx: &souprune_api::BulletContextC,
) -> souprune_api::BulletOutputC {
if let Some(on_update) = self.instance.vtable.on_update {
on_update(self.instance.instance, ctx)
} else {
souprune_api::BulletOutputC::default()
}
}
}
impl Drop for ActiveDanmaku {
fn drop(&mut self) {
if self.initialized
&& let Some(on_exit) = self.instance.vtable.on_exit
{
on_exit(self.instance.instance);
}
if let Some(destroy) = self.instance.vtable.destroy {
destroy(self.instance.instance);
}
}
}