1use crate::{
5 actor::{
6 ActorMessageQueue, ActorType, ActorsList, SingleAnimationType,
7 },
8 game::GameCommands,
9 hero::Hero,
10 level::tiles::LevelTiles,
11 rendering::Renderer,
12 HorizontalDirection, Result, Sizes, LEVELWINDOW_WIDTH, OBJECT_SHOT,
13};
14use sdl2::rect::Rect;
15
16pub type ShotList = Vec<Shot>;
17
18#[derive(Debug)]
19pub struct Shot {
20 position: Rect,
21 pub is_alive: bool,
22 pub direction: HorizontalDirection,
23 counter: usize,
24 countdown: usize,
25}
26
27impl Shot {
28 pub fn new(
29 sizes: &dyn Sizes,
30 x: i32,
31 y: i32,
32 direction: HorizontalDirection,
33 ) -> Self {
34 let w = sizes.width() / 4;
35 let h = sizes.height() / 4 * 3;
36 Shot {
37 position: Rect::new(
38 x + sizes.width() as i32 - w as i32 / 2
39 + direction.as_factor_i32() * w as i32,
40 y + sizes.height() as i32 - h as i32,
41 w,
42 h,
43 ),
44 is_alive: true,
45 direction,
46 counter: 0,
47 countdown: 2,
48 }
49 }
50
51 pub fn act(
53 &mut self,
54 sizes: &dyn Sizes,
55 hero: &mut Hero,
56 actors: &mut ActorsList,
57 tiles: &mut LevelTiles,
58 game_commands: &mut dyn GameCommands,
59 actor_message_queue: &mut ActorMessageQueue,
60 ) -> bool {
61 self.counter += 1;
62 self.counter %= 4;
63
64 if self.countdown == 1 {
65 self.is_alive = false;
66 self.countdown -= 1;
67 }
68
69 let x_start = hero.position.geometry.x()
70 - sizes.width() as i32 * LEVELWINDOW_WIDTH as i32 / 2;
71 let x_end = hero.position.geometry.x()
72 + hero.position.geometry.w as i32
73 + sizes.width() as i32 * LEVELWINDOW_WIDTH as i32 / 2;
74
75 if self.countdown >= 2 {
76 let distance =
77 sizes.half_width() as i32 * self.direction.as_factor_i32();
78
79 self.push(
83 sizes,
84 hero,
85 actors,
86 tiles,
87 distance,
88 game_commands,
89 actor_message_queue,
90 );
91 self.push(
92 sizes,
93 hero,
94 actors,
95 tiles,
96 distance,
97 game_commands,
98 actor_message_queue,
99 );
100
101 let x = self.position.x;
102
103 if x < x_start || x > x_end {
104 self.countdown = 1;
105 }
106 }
107 self.is_alive
108 }
109
110 pub fn render(
111 &self,
112 renderer: &mut dyn Renderer,
113 sizes: &dyn Sizes,
114 draw_collision_bounds: bool,
115 ) -> Result<()> {
116 if self.is_alive {
117 let mut destrect = self.position;
118 destrect.set_x(
119 destrect.x() + destrect.width() as i32 / 2
120 - sizes.half_width() as i32,
121 );
122 destrect.set_width(sizes.width());
123
124 renderer.place_tile(
125 OBJECT_SHOT + self.counter,
126 destrect.top_left(),
127 )?;
128 if draw_collision_bounds {
129 let color = crate::collision_bounds_color();
130 renderer.draw_rect(self.position, color)?;
131 }
132 }
133 Ok(())
134 }
135
136 #[allow(clippy::too_many_arguments)]
137 pub fn push(
138 &mut self,
139 sizes: &dyn Sizes,
140 hero: &mut Hero,
141 actors: &mut ActorsList,
142 tiles: &mut LevelTiles,
143 offset: i32,
144 game_commands: &mut dyn GameCommands,
145 actor_message_queue: &mut ActorMessageQueue,
146 ) {
147 if self.countdown >= 2 {
148 self.position.x += offset;
149 if self.countdown == 2 {
150 if actors.process_shot(
151 self.position,
152 sizes,
153 tiles,
154 game_commands,
155 hero,
156 actor_message_queue,
157 ) {
158 self.countdown = 1;
159 }
160 } else {
161 self.countdown -= 1;
162 }
163 }
164 if self.countdown >= 2 && tiles.collides(sizes, self.position) {
165 self.countdown = 1;
166 game_commands.add_actor(
167 ActorType::SingleAnimation(SingleAnimationType::Explosion),
168 self.position.top_left().offset(
169 self.position.width() as i32 / 2
170 - sizes.half_width() as i32,
171 0,
172 ),
173 );
174 }
175 }
176
177 pub fn set_is_alive(&mut self, is_alive: bool) {
178 self.is_alive = is_alive;
179 }
180}