rustic_mountain_core/
structures.rs

1use std::{cell::RefCell, rc::Rc};
2
3// #[macro_use]
4
5use 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; //start
129            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(_) => (), //println!("couldn't borrow {} from {}", &name, &obj.name),
168            };
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    /// and then they turned themself into a strawberry. funniest shit i've ever seen
203    pub fn init_fruit(&mut self, celeste: &mut Celeste, ox: f32, oy: f32) {
204        // sfx shit
205        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    /// WARNING: Only use this function if it is being called by the object itself in an update loop
211    pub fn destroy_self(&mut self, celeste: &mut Celeste) {
212        celeste.objects.retain(|o| o.try_borrow().is_ok());
213        // this particular bit of jank will delete any object that is currently being used in memory
214    }
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}