1 | // SuperTux - "Totem" Badguy |
2 | // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "badguy/totem.hpp" |
18 | |
19 | #include <math.h> |
20 | |
21 | #include "audio/sound_manager.hpp" |
22 | #include "object/player.hpp" |
23 | #include "sprite/sprite.hpp" |
24 | #include "supertux/sector.hpp" |
25 | |
26 | static const float JUMP_ON_SPEED_Y = -400; |
27 | static const float JUMP_OFF_SPEED_Y = -500; |
28 | static const std::string LAND_ON_TOTEM_SOUND = "sounds/totem.ogg" ; |
29 | |
30 | Totem::Totem(const ReaderMapping& reader) : |
31 | BadGuy(reader, "images/creatures/totem/totem.sprite" ), |
32 | carrying(nullptr), |
33 | carried_by(nullptr) |
34 | { |
35 | SoundManager::current()->preload( LAND_ON_TOTEM_SOUND ); |
36 | } |
37 | |
38 | Totem::~Totem() |
39 | { |
40 | if (carrying) carrying->jump_off(); |
41 | if (carried_by) jump_off(); |
42 | } |
43 | |
44 | bool |
45 | Totem::updatePointers(const GameObject* from_object, GameObject* to_object) |
46 | { |
47 | if (from_object == carrying) { |
48 | carrying = dynamic_cast<Totem*>(to_object); |
49 | return true; |
50 | } |
51 | if (from_object == carried_by) { |
52 | carried_by = dynamic_cast<Totem*>(to_object); |
53 | return true; |
54 | } |
55 | return false; |
56 | } |
57 | |
58 | void |
59 | Totem::initialize() |
60 | { |
61 | if (!carried_by) { |
62 | static const float WALKSPEED = 100; |
63 | m_physic.set_velocity_x(m_dir == Direction::LEFT ? -WALKSPEED : WALKSPEED); |
64 | m_sprite->set_action(m_dir == Direction::LEFT ? "walking-left" : "walking-right" ); |
65 | return; |
66 | } else { |
67 | synchronize_with(carried_by); |
68 | m_sprite->set_action(m_dir == Direction::LEFT ? "stacked-left" : "stacked-right" ); |
69 | return; |
70 | } |
71 | } |
72 | |
73 | void |
74 | Totem::active_update(float dt_sec) |
75 | { |
76 | BadGuy::active_update(dt_sec); |
77 | |
78 | if (!carried_by) { |
79 | if (on_ground() && might_fall()) |
80 | { |
81 | m_dir = (m_dir == Direction::LEFT ? Direction::RIGHT : Direction::LEFT); |
82 | initialize(); |
83 | } |
84 | |
85 | // jump a bit if we find a suitable totem |
86 | for (auto& obj : Sector::get().get_objects_by_type<MovingObject>()) { |
87 | auto t = dynamic_cast<Totem*>(&obj); |
88 | if (!t) continue; |
89 | |
90 | // skip if we are not approaching each other |
91 | if (!((m_dir == Direction::LEFT) && (t->m_dir == Direction::RIGHT))) continue; |
92 | |
93 | Vector p1 = m_col.m_bbox.p1(); |
94 | Vector p2 = t->get_pos(); |
95 | |
96 | // skip if not on same height |
97 | float dy = (p1.y - p2.y); |
98 | if (fabsf(dy - 0) > 2) continue; |
99 | |
100 | // skip if too far away |
101 | float dx = (p1.x - p2.x); |
102 | if (fabsf(dx - 128) > 2) continue; |
103 | |
104 | m_physic.set_velocity_y(JUMP_ON_SPEED_Y); |
105 | p1.y -= 1; |
106 | set_pos(p1); |
107 | break; |
108 | } |
109 | } |
110 | |
111 | if (carried_by) { |
112 | synchronize_with(carried_by); |
113 | } |
114 | |
115 | if (carrying) { |
116 | carrying->synchronize_with(this); |
117 | } |
118 | |
119 | } |
120 | |
121 | bool |
122 | Totem::collision_squished(GameObject& object) |
123 | { |
124 | /// Tux shouldn't be able to bisect totem stack by sacrificing his powerup. |
125 | /// --Hume2 |
126 | if (carrying) { |
127 | return false; |
128 | } |
129 | |
130 | if (carried_by) { |
131 | auto player = dynamic_cast<Player*>(&object); |
132 | if (player) player->bounce(*this); |
133 | jump_off(); |
134 | } |
135 | |
136 | m_sprite->set_action(m_dir == Direction::LEFT ? "squished-left" : "squished-right" ); |
137 | m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height()); |
138 | |
139 | kill_squished(object); |
140 | return true; |
141 | } |
142 | |
143 | void |
144 | Totem::collision_solid(const CollisionHit& hit) |
145 | { |
146 | update_on_ground_flag(hit); |
147 | |
148 | // if we are being carried around, pass event to bottom of stack and ignore it |
149 | if (carried_by) { |
150 | carried_by->collision_solid(hit); |
151 | return; |
152 | } |
153 | |
154 | // If we hit something from above or below: stop moving in this direction |
155 | if (hit.top || hit.bottom) { |
156 | m_physic.set_velocity_y(0); |
157 | } |
158 | |
159 | // If we are hit from the direction we are facing: turn around |
160 | if (hit.left && (m_dir == Direction::LEFT)) { |
161 | m_dir = Direction::RIGHT; |
162 | initialize(); |
163 | } |
164 | if (hit.right && (m_dir == Direction::RIGHT)) { |
165 | m_dir = Direction::LEFT; |
166 | initialize(); |
167 | } |
168 | } |
169 | |
170 | HitResponse |
171 | Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit) |
172 | { |
173 | // if we are being carried around, pass event to bottom of stack and ignore it |
174 | if (carried_by) { |
175 | carried_by->collision_badguy(badguy, hit); |
176 | return CONTINUE; |
177 | } |
178 | |
179 | // if we hit a Totem that is not from our stack: have our base jump on its top |
180 | auto totem = dynamic_cast<Totem*>(&badguy); |
181 | if (totem) { |
182 | auto thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by; |
183 | auto srcBase = totem; while (srcBase->carried_by) srcBase=srcBase->carried_by; |
184 | auto thisTop = this; while (thisTop->carrying) thisTop=thisTop->carrying; |
185 | if (srcBase != thisBase) { |
186 | srcBase->jump_on(thisTop); |
187 | } |
188 | } |
189 | |
190 | // If we are hit from the direction we are facing: turn around |
191 | if (hit.left && (m_dir == Direction::LEFT)) { |
192 | m_dir = Direction::RIGHT; |
193 | initialize(); |
194 | } |
195 | if (hit.right && (m_dir == Direction::RIGHT)) { |
196 | m_dir = Direction::LEFT; |
197 | initialize(); |
198 | } |
199 | |
200 | return CONTINUE; |
201 | } |
202 | |
203 | void |
204 | Totem::kill_fall() |
205 | { |
206 | if (carrying) carrying->jump_off(); |
207 | if (carried_by) jump_off(); |
208 | |
209 | BadGuy::kill_fall(); |
210 | } |
211 | |
212 | void |
213 | Totem::jump_on(Totem* target) |
214 | { |
215 | if (target->carrying) { |
216 | log_warning << "target is already carrying someone" << std::endl; |
217 | return; |
218 | } |
219 | |
220 | target->carrying = this; |
221 | |
222 | carried_by = target; |
223 | initialize(); |
224 | m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height()); |
225 | |
226 | SoundManager::current()->play( LAND_ON_TOTEM_SOUND , get_pos()); |
227 | |
228 | synchronize_with(target); |
229 | } |
230 | |
231 | void |
232 | Totem::jump_off() { |
233 | if (!carried_by) { |
234 | log_warning << "not carried by anyone" << std::endl; |
235 | return; |
236 | } |
237 | |
238 | carried_by->carrying = nullptr; |
239 | |
240 | carried_by = nullptr; |
241 | |
242 | initialize(); |
243 | m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height()); |
244 | |
245 | m_physic.set_velocity_y(JUMP_OFF_SPEED_Y); |
246 | } |
247 | |
248 | void |
249 | Totem::synchronize_with(Totem* base) |
250 | { |
251 | |
252 | if (m_dir != base->m_dir) { |
253 | m_dir = base->m_dir; |
254 | m_sprite->set_action(m_dir == Direction::LEFT ? "stacked-left" : "stacked-right" ); |
255 | } |
256 | |
257 | Vector pos = base->get_pos(); |
258 | pos.y -= m_sprite->get_current_hitbox_height(); |
259 | set_pos(pos); |
260 | |
261 | m_physic.set_velocity_x(base->m_physic.get_velocity_x()); |
262 | m_physic.set_velocity_y(base->m_physic.get_velocity_y()); |
263 | } |
264 | |
265 | /* EOF */ |
266 | |