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