Sorry, was too early for that patch.
[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 #include "log.hpp"
25
26 static const float WALKSPEED = 100;
27 static const float JUMP_ON_SPEED_Y = -400;
28 static const float JUMP_OFF_SPEED_Y = -500;
29 static const std::string LAND_ON_TOTEM_SOUND = "sounds/totem.ogg";
30
31 Totem::Totem(const lisp::Lisp& reader)
32         : BadGuy(reader, "images/creatures/totem/totem.sprite")
33 {
34   carrying = 0;
35   carried_by = 0;
36   sound_manager->preload( LAND_ON_TOTEM_SOUND );
37 }
38
39 Totem::Totem(const Totem& other)
40         : BadGuy(other), carrying(other.carrying), carried_by(other.carried_by)
41 {
42   sound_manager->preload( LAND_ON_TOTEM_SOUND );
43 }
44
45 Totem::~Totem()
46 {
47   if (carrying) carrying->jump_off();
48   if (carried_by) jump_off();
49 }
50
51 bool
52 Totem::updatePointers(const GameObject* from_object, GameObject* to_object)
53 {
54   if (from_object == carrying) {
55     carrying = dynamic_cast<Totem*>(to_object);
56     return true;
57   }
58   if (from_object == carried_by) {
59     carried_by = dynamic_cast<Totem*>(to_object);
60     return true;
61   }
62   return false;
63 }
64
65 void
66 Totem::write(lisp::Writer& writer)
67 {
68   writer.start_list("totem");
69
70   writer.write_float("x", start_position.x);
71   writer.write_float("y", start_position.y);
72
73   writer.end_list("totem");
74 }
75
76 void
77 Totem::activate()
78 {
79   if (!carried_by) {
80     physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
81     sprite->set_action(dir == LEFT ? "walking-left" : "walking-right");
82     return;
83   } else {
84     synchronize_with(carried_by);
85     sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
86     return;
87   }
88 }
89
90 void
91 Totem::active_update(float elapsed_time)
92 {
93   BadGuy::active_update(elapsed_time);
94
95   if (!carried_by) {
96     if (on_ground() && might_fall())
97     {
98       dir = (dir == LEFT ? RIGHT : LEFT);
99       activate();
100     }
101
102     Sector* s = Sector::current();
103     if (s) {
104       // jump a bit if we find a suitable totem
105       for (std::vector<MovingObject*>::iterator i = s->moving_objects.begin(); i != s->moving_objects.end(); i++) {
106         Totem* t = dynamic_cast<Totem*>(*i);
107         if (!t) continue;
108
109         // skip if we are not approaching each other
110         if (!((this->dir == LEFT) && (t->dir == RIGHT))) continue;
111
112         Vector p1 = this->get_pos();
113         Vector p2 = t->get_pos();
114
115         // skip if not on same height
116         float dy = (p1.y - p2.y);
117         if (fabsf(dy - 0) > 2) continue;
118
119         // skip if too far away
120         float dx = (p1.x - p2.x);
121         if (fabsf(dx - 128) > 2) continue;
122
123         physic.set_velocity_y(JUMP_ON_SPEED_Y);
124         p1.y -= 1;
125         this->set_pos(p1);
126         break;
127       }
128     }
129   }
130
131   if (carried_by) {
132     this->synchronize_with(carried_by);
133   }
134
135   if (carrying) {
136     carrying->synchronize_with(this);
137   }
138
139 }
140
141 bool
142 Totem::collision_squished(GameObject& object)
143 {
144   if (carrying) carrying->jump_off();
145   if (carried_by) {
146     Player* player = dynamic_cast<Player*>(&object);
147     if (player) player->bounce(*this);
148     jump_off();
149   }
150
151   sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
152   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
153
154   kill_squished(object);
155   return true;
156 }
157
158 void
159 Totem::collision_solid(const CollisionHit& hit)
160 {
161   update_on_ground_flag(hit);
162
163   // if we are being carried around, pass event to bottom of stack and ignore it
164   if (carried_by) {
165     carried_by->collision_solid(hit);
166     return;
167   }
168
169   // If we hit something from above or below: stop moving in this direction
170   if (hit.top || hit.bottom) {
171     physic.set_velocity_y(0);
172   }
173
174   // If we are hit from the direction we are facing: turn around
175   if (hit.left && (dir == LEFT)) {
176     dir = RIGHT;
177     activate();
178   }
179   if (hit.right && (dir == RIGHT)) {
180     dir = LEFT;
181     activate();
182   }
183 }
184
185 HitResponse
186 Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
187 {
188   // if we are being carried around, pass event to bottom of stack and ignore it
189   if (carried_by) {
190     carried_by->collision_badguy(badguy, hit);
191     return CONTINUE;
192   }
193
194   // if we hit a Totem that is not from our stack: have our base jump on its top
195   Totem* totem = dynamic_cast<Totem*>(&badguy);
196   if (totem) {
197     Totem* thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by;
198     Totem* srcBase = totem; while (srcBase->carried_by)  srcBase=srcBase->carried_by;
199     Totem* thisTop = this;  while (thisTop->carrying)    thisTop=thisTop->carrying;
200     if (srcBase != thisBase) {
201       srcBase->jump_on(thisTop);
202     }
203   }
204
205   // If we are hit from the direction we are facing: turn around
206   if(hit.left && (dir == LEFT)) {
207     dir = RIGHT;
208     activate();
209   }
210   if(hit.right && (dir == RIGHT)) {
211     dir = LEFT;
212     activate();
213   }
214
215   return CONTINUE;
216 }
217
218 void
219 Totem::kill_fall()
220 {
221   if (carrying) carrying->jump_off();
222   if (carried_by) jump_off();
223
224   BadGuy::kill_fall();
225 }
226
227 void
228 Totem::jump_on(Totem* target)
229 {
230   if (target->carrying) {
231     log_warning << "target is already carrying someone" << std::endl;
232     return;
233   }
234
235   target->carrying = this;
236
237   this->carried_by = target;
238   this->activate();
239   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
240
241   sound_manager->play( LAND_ON_TOTEM_SOUND , get_pos());
242
243
244   this->synchronize_with(target);
245 }
246
247 void
248 Totem::jump_off() {
249   if (!carried_by) {
250     log_warning << "not carried by anyone" << std::endl;
251     return;
252   }
253
254   carried_by->carrying = 0;
255
256   this->carried_by = 0;
257
258   this->activate();
259   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
260
261
262   physic.set_velocity_y(JUMP_OFF_SPEED_Y);
263 }
264
265 void
266 Totem::synchronize_with(Totem* base)
267 {
268
269   if (dir != base->dir) {
270     dir = base->dir;
271     sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
272   }
273
274   Vector pos = base->get_pos();
275   pos.y -= sprite->get_current_hitbox_height();
276   set_pos(pos);
277
278   physic.set_velocity_x(base->physic.get_velocity_x());
279   physic.set_velocity_y(base->physic.get_velocity_y());
280 }
281
282
283 IMPLEMENT_FACTORY(Totem, "totem")