88620f913b5c50b5538edfdaa07b8dcbc2daa0bb
[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
35   reader.get("x", start_position.x);
36   reader.get("y", start_position.y);
37   sprite = sprite_manager->create("images/creatures/totem/totem.sprite");
38   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
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   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
134
135   kill_squished(player);
136   return true;
137 }
138
139 HitResponse
140 Totem::collision_solid(GameObject& object, const CollisionHit& hit)
141 {
142   // if we are being carried around, pass event to bottom of stack and ignore it
143   if (carried_by) {
144     carried_by->collision_solid(object, hit);
145     return CONTINUE;
146   }
147
148   // If we hit something from above or below: stop moving in this direction 
149   if (hit.normal.y != 0) {
150     physic.set_velocity_y(0);
151   }
152
153   // If we are hit from the direction we are facing: turn around
154   if ((hit.normal.x > .8) && (dir == LEFT)) {
155     dir = RIGHT;
156     activate();
157   }
158   if ((hit.normal.x < -.8) && (dir == RIGHT)) {
159     dir = LEFT;
160     activate();
161   }
162
163   return CONTINUE;
164 }
165
166 HitResponse
167 Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
168 {
169   // if we are being carried around, pass event to bottom of stack and ignore it
170   if (carried_by) {
171     carried_by->collision_badguy(badguy, hit);
172     return CONTINUE;
173   }
174  
175   // if we hit a Totem that is not from our stack: have our base jump on its top
176   Totem* totem = dynamic_cast<Totem*>(&badguy);
177   if (totem) {
178     Totem* thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by;
179     Totem* srcBase = totem; while (srcBase->carried_by)  srcBase=srcBase->carried_by;
180     Totem* thisTop = this;  while (thisTop->carrying)    thisTop=thisTop->carrying;
181     if (srcBase != thisBase) {
182       srcBase->jump_on(thisTop);
183     }
184   }
185
186   // If we are hit from the direction we are facing: turn around
187   if ((hit.normal.x > .8) && (dir == LEFT)) {
188     dir = RIGHT;
189     activate();
190   }
191   if ((hit.normal.x < -.8) && (dir == RIGHT)) {
192     dir = LEFT;
193     activate();
194   }
195
196   return CONTINUE;
197 }
198
199 void
200 Totem::kill_fall()
201 {
202   if (carrying) carrying->jump_off();
203   if (carried_by) jump_off();
204
205   BadGuy::kill_fall();
206 }
207
208 void 
209 Totem::jump_on(Totem* target)
210 {
211   if (target->carrying) {
212     log_warning << "target is already carrying someone" << std::endl;
213     return;
214   }
215   
216   target->carrying = this;
217
218   this->carried_by = target;
219   this->activate();
220   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
221   
222   this->synchronize_with(target);
223 }
224
225 void
226 Totem::jump_off() {
227   if (!carried_by) {
228     log_warning << "not carried by anyone" << std::endl;
229     return;
230   }
231
232   carried_by->carrying = 0;
233
234   this->carried_by = 0;
235
236   this->activate();
237   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
238
239
240   physic.set_velocity_y(JUMP_OFF_SPEED_Y);
241 }
242
243 void 
244 Totem::synchronize_with(Totem* base)
245 {
246
247   if (dir != base->dir) {
248     dir = base->dir;
249     sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
250   }
251   
252   Vector pos = base->get_pos();
253   pos.y -= sprite->get_current_hitbox_height();
254   set_pos(pos);
255
256   physic.set_velocity_x(base->physic.get_velocity_x());
257   physic.set_velocity_y(base->physic.get_velocity_y());
258 }
259
260
261 IMPLEMENT_FACTORY(Totem, "totem")
262