1use std::{cell::RefCell, rc::Rc};
2
3use crate::{
6 objects::{
7 balloon::Balloon, bigchest::BigChest, chest::Chest, fakewall::FakeWall,
8 fallfloor::FallFloor, flag::Flag, flyfruit::FlyFruit, fruit::Fruit, key::Key,
9 lifeup::LifeUp, message::Message, orb::Orb, platform::Platform, player::Player,
10 playerspawn::PlayerSpawn, roomtitle::RoomTitle, smoke::Smoke, spring::Spring,
11 },
12 utils::*,
13 Celeste,
14};
15
16use serde::{Deserialize, Serialize};
17
18#[derive(Clone, PartialEq, Serialize, Deserialize)]
19pub struct Vector {
20 pub x: f32,
21 pub y: f32,
22}
23#[derive(Serialize, Deserialize)]
24
25pub struct Rectangle {
26 pub x: f32,
27 pub y: f32,
28 pub w: f32,
29 pub h: f32,
30}
31#[derive(Clone, Serialize, Deserialize)]
32pub struct FlipState {
33 pub x: bool,
34 pub y: bool,
35}
36#[derive(Serialize, Deserialize)]
37
38pub struct Object {
39 pub pos: Vector,
40 pub spd: Vector,
41 pub rem: Vector,
42 pub spr: u8,
43 pub hitbox: Rectangle,
44 pub flip: FlipState,
45
46 pub collidable: bool,
47 pub solids: bool,
48
49 pub obj_type: ObjectType,
50 #[serde(skip, default)]
51 pub draw: ObjFunc,
52 #[serde(skip)]
53 pub update: ObjFunc,
54
55 #[serde(skip)]
56 pub name: &'static str,
57}
58pub struct ObjFunc(pub fn(&mut Object, &mut Celeste));
59impl Default for ObjFunc {
60 fn default() -> Self {
61 ObjFunc(noop)
62 }
63}
64pub fn noop(_: &mut Object, _: &mut Celeste) {}
65impl Object {
66 pub fn draw(&mut self, celeste: &mut Celeste) {
67 (self.draw).0(self, celeste);
68 }
69 pub fn update(&mut self, celeste: &mut Celeste) {
70 (self.update).0(self, celeste);
71 }
72 pub fn left(&self) -> f32 {
73 self.pos.x + self.hitbox.x
74 }
75 pub fn right(&self) -> f32 {
76 self.left() + self.hitbox.w - 1f32
77 }
78 pub fn top(&self) -> f32 {
79 self.pos.y + self.hitbox.y
80 }
81 pub fn bottom(&self) -> f32 {
82 self.top() + self.hitbox.h - 1f32
83 }
84
85 pub fn init_smoke(&self, celeste: &mut Celeste, x: f32, y: f32) {
86 let smoke = Smoke::init(celeste, self.pos.x + x, self.pos.y + y);
87 celeste.objects.push(Rc::new(RefCell::new(smoke)));
88 }
89
90 pub fn draw_sprite(&self, celeste: &mut Celeste) {
91 celeste.mem.spr(
92 self.spr,
93 self.pos.x as i32,
94 self.pos.y as i32,
95 Some(self.flip.clone()),
96 )
97 }
98
99 pub fn do_move(&mut self, celeste: &mut Celeste, ox: f32, oy: f32, start: f32) {
100 self.rem.x += ox;
101 let amt = (self.rem.x + 0.5).floor();
102 self.rem.x -= amt;
103 if self.solids {
104 let step = sign(amt);
105 let mut i = start;
106 loop {
107 if !self.is_solid(step, 0f32, celeste) {
108 self.pos.x += step;
109 } else {
110 self.spd.x = 0f32;
111 self.rem.x = 0f32;
112 break;
113 }
114 if i >= amt.abs() {
115 break;
116 }
117 i += 1f32;
118 }
119 } else {
120 self.pos.x += amt;
121 }
122
123 self.rem.y += oy;
124 let amt = (self.rem.y + 0.5).floor();
125 self.rem.y -= amt;
126 if self.solids {
127 let step = sign(amt);
128 let mut i = 0f32; loop {
130 if !self.is_solid(0f32, step, celeste) {
131 self.pos.y += step;
132 } else {
133 self.spd.y = 0f32;
134 self.rem.y = 0f32;
135 break;
136 }
137 if i >= amt.abs() {
138 break;
139 }
140 i += 1f32;
141 }
142 } else {
143 self.pos.y += amt;
144 }
145 }
146 pub fn check(
147 &mut self,
148 celeste: &mut Celeste,
149 name: &'static str,
150 x: f32,
151 y: f32,
152 ) -> Option<usize> {
153 let obj = self;
154 for i in 0..celeste.objects.len() {
155 match celeste.objects[i].try_borrow() {
156 Ok(other) => {
157 if other.name == name && other.collidable {
158 if other.right() >= obj.left() + x
159 && other.bottom() >= obj.top() + y
160 && other.left() <= obj.right() + x
161 && other.top() <= obj.bottom() + y
162 {
163 return Some(i);
164 }
165 }
166 }
167 Err(_) => (), };
169 }
170 None
171 }
172 pub fn is_ice(&self, x: f32, y: f32, celeste: &mut Celeste) -> bool {
173 self.is_flag(x, y, 4, celeste)
174 }
175 pub fn is_solid(&mut self, x: f32, y: f32, celeste: &mut Celeste) -> bool {
176 return (y > 0f32
177 && self.check(celeste, "Platform", x, 0f32).is_none()
178 && self.check(celeste, "Platform", x, y).is_some())
179 || self.is_flag(x, y, 1, celeste)
180 || self.check(celeste, "FallFloor", x, y).is_some()
181 || self.check(celeste, "FakeWall", x, y).is_some();
182 }
183 pub fn is_flag(&self, x: f32, y: f32, flag: u8, celeste: &mut Celeste) -> bool {
184 for i in max(0f32, (self.left() + x) / 8f32) as i32
185 ..(min(15f32, (self.right() + x) / 8f32)) as i32 + 1
186 {
187 for j in max(0f32, (self.top() + y) / 8f32) as i32
188 ..min(15f32, (self.bottom() + y) / 8f32) as i32 + 1
189 {
190 let fg = celeste.mem.fget_all(celeste.mem.mget(
191 (celeste.room.x as u8 * 16) + i as u8,
192 (celeste.room.y as u8 * 16) + j as u8,
193 ));
194 if (flag & fg) == flag {
195 return true;
196 }
197 }
198 }
199 false
200 }
201
202 pub fn init_fruit(&mut self, celeste: &mut Celeste, ox: f32, oy: f32) {
204 let fruit = Fruit::init(celeste, self.pos.x + ox, self.pos.y + oy);
206 celeste.objects.push(Rc::new(RefCell::new(fruit)));
207 self.destroy_self(celeste);
208 }
209
210 pub fn destroy_self(&mut self, celeste: &mut Celeste) {
212 celeste.objects.retain(|o| o.try_borrow().is_ok());
213 }
215
216 pub fn destroy_other(&mut self, celeste: &mut Celeste) {
217 celeste.objects.retain(|objref| match objref.try_borrow() {
218 Ok(obj) => obj.name == self.name && obj.pos == self.pos,
219 Err(_) => true,
220 });
221 }
222}
223
224#[derive(Serialize, Deserialize)]
225pub enum ObjectType {
226 Player(Rc<RefCell<Player>>),
227 PlayerSpawn(Rc<RefCell<PlayerSpawn>>),
228 Balloon(Rc<RefCell<Balloon>>),
229 Spring(Rc<RefCell<Spring>>),
230 FallFloor(Rc<RefCell<FallFloor>>),
231 Platform(Rc<RefCell<Platform>>),
232 Smoke(Rc<RefCell<Smoke>>),
233 BigChest(Rc<RefCell<BigChest>>),
234 Flag(Rc<RefCell<Flag>>),
235 Fruit(Rc<RefCell<Fruit>>),
236 FlyFruit(Rc<RefCell<FlyFruit>>),
237 LifeUp(Rc<RefCell<LifeUp>>),
238 FakeWall(Rc<RefCell<FakeWall>>),
239 Key(Rc<RefCell<Key>>),
240 Chest(Rc<RefCell<Chest>>),
241 Message(Rc<RefCell<Message>>),
242 RoomTitle(Rc<RefCell<RoomTitle>>),
243 Orb(Rc<RefCell<Orb>>),
244}