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