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