New Badguy "Totem" - a variable-height stack of wooden faces
[supertux.git] / src / badguy / totem.cpp
1 //  $Id: totem.cpp 3096 2006-03-17 12:03:02Z sommer $
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 "msg.hpp"
25
26 static const float WALKSPEED = 100;
27 static const float WALKSPEED_SMALL = 120;
28 static const float INVINCIBLE_TIME = 1;
29
30 static const float JUMP_OFF_SPEED_Y = 500;
31
32 Totem::Totem(const lisp::Lisp& reader)
33 {
34   stay_on_platform = false;
35   carrying = 0;
36   carried_by = 0;
37   bbox.set_size(48, 49);
38
39   reader.get("x", start_position.x);
40   reader.get("y", start_position.y);
41   reader.get("stay-on-platform", stay_on_platform);
42   sprite = sprite_manager->create("images/creatures/totem/totem.sprite");
43 }
44
45 Totem::~Totem() 
46 {
47   if (carrying) carrying->jump_off();
48   if (carried_by) jump_off();
49 }
50
51 void
52 Totem::write(lisp::Writer& writer)
53 {
54   writer.start_list("totem");
55
56   writer.write_float("x", start_position.x);
57   writer.write_float("y", start_position.y);
58   writer.write_bool("stay-on-platform", stay_on_platform);
59
60   writer.end_list("totem");
61 }
62
63 void
64 Totem::activate()
65 {
66   if (!carried_by) {
67     physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED);
68     sprite->set_action(dir == LEFT ? "walking-left" : "walking-right");
69     return;
70   } else {
71     synchronize_with(carried_by);
72     sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
73     return;
74   }
75 }
76
77 void
78 Totem::active_update(float elapsed_time)
79 {
80   BadGuy::active_update(elapsed_time);
81
82   if (!carried_by) {
83     if (stay_on_platform && may_fall_off_platform())
84     {
85       dir = (dir == LEFT ? RIGHT : LEFT);
86       activate();
87     }
88   }
89
90   if (carried_by) {
91     this->synchronize_with(carried_by);
92   }
93
94   if (carrying) {
95     carrying->synchronize_with(this);
96   }
97
98 }
99
100 bool
101 Totem::collision_squished(Player& player)
102 {
103   if (carrying) carrying->jump_off();
104   if (carried_by) {
105     player.bounce(*this);
106     jump_off();
107   }
108
109   sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
110   this->bbox.set_size(48, 45);
111   kill_squished(player);
112   return true;
113 }
114
115 HitResponse
116 Totem::collision_solid(GameObject& object, const CollisionHit& hit)
117 {
118   // if we are being carried around, pass event to bottom of stack and ignore it
119   if (carried_by) {
120     carried_by->collision_solid(object, hit);
121     return CONTINUE;
122   }
123
124   // If we hit something from above or below: stop moving in this direction 
125   if (hit.normal.y != 0) {
126     physic.set_velocity_y(0);
127   }
128
129   // If we are hit from the direction we are facing: turn around
130   if ((hit.normal.x > .8) && (dir == LEFT)) {
131     dir = RIGHT;
132     activate();
133   }
134   if ((hit.normal.x < -.8) && (dir == RIGHT)) {
135     dir = LEFT;
136     activate();
137   }
138
139   return CONTINUE;
140 }
141
142 HitResponse
143 Totem::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
144 {
145   // if we are being carried around, pass event to bottom of stack and ignore it
146   if (carried_by) {
147     carried_by->collision_badguy(badguy, hit);
148     return CONTINUE;
149   }
150  
151   // if we hit a Totem that is not from our stack: have our base jump on its top
152   Totem* totem = dynamic_cast<Totem*>(&badguy);
153   if (totem) {
154     Totem* thisBase = this; while (thisBase->carried_by) thisBase=thisBase->carried_by;
155     Totem* srcBase = totem; while (srcBase->carried_by)  srcBase=srcBase->carried_by;
156     Totem* thisTop = this;  while (thisTop->carrying)    thisTop=thisTop->carrying;
157     if (srcBase != thisBase) {
158       srcBase->jump_on(thisTop);
159     }
160   }
161
162   // If we are hit from the direction we are facing: turn around
163   if ((hit.normal.x > .8) && (dir == LEFT)) {
164     dir = RIGHT;
165     activate();
166   }
167   if ((hit.normal.x < -.8) && (dir == RIGHT)) {
168     dir = LEFT;
169     activate();
170   }
171
172   return CONTINUE;
173 }
174
175 void
176 Totem::kill_fall()
177 {
178   if (carrying) carrying->jump_off();
179   if (carried_by) jump_off();
180
181   BadGuy::kill_fall();
182 }
183
184 void 
185 Totem::jump_on(Totem* target)
186 {
187   if (target->carrying) {
188     msg_warning("target is already carrying someone");
189     return;
190   }
191   
192   target->carrying = this;
193
194   this->carried_by = target;
195   this->bbox.set_size(48, 45);
196   this->activate();
197   
198   this->synchronize_with(target);
199 }
200
201 void
202 Totem::jump_off() {
203   if (!carried_by) {
204     msg_warning("not carried by anyone");
205     return;
206   }
207
208   carried_by->carrying = 0;
209
210   this->carried_by = 0;
211   this->bbox.set_size(48, 49);
212
213   this->activate();
214
215   physic.set_velocity_y(JUMP_OFF_SPEED_Y);
216 }
217
218 void 
219 Totem::synchronize_with(Totem* base)
220 {
221
222   if (dir != base->dir) {
223     dir = base->dir;
224     sprite->set_action(dir == LEFT ? "stacked-left" : "stacked-right");
225   }
226   
227   Vector pos = base->get_pos();
228   pos.y -= 45;
229   set_pos(pos);
230
231   physic.set_velocity_x(base->physic.get_velocity_x());
232   physic.set_velocity_y(base->physic.get_velocity_y());
233 }
234
235
236 IMPLEMENT_FACTORY(Totem, "totem")
237