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