- Added a new script command to display text files
[supertux.git] / src / badguy / yeti.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.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 #include <config.h>
21
22 #include <float.h>
23 #include <sstream>
24 #include "yeti.h"
25 #include "object/camera.h"
26 #include "yeti_stalactite.h"
27 #include "bouncing_snowball.h"
28 #include "game_session.h"
29 #include "scripting/script_interpreter.h"
30
31 static const float JUMP_VEL1 = 250;
32 static const float JUMP_VEL2 = 700;
33 static const float RUN_SPEED = 350;
34 static const float JUMP_TIME = 1.6;
35 static const float ANGRY_JUMP_WAIT = .5;
36 /// the time we are safe when tux just hit us
37 static const float SAFE_TIME = .5;
38 static const int INITIAL_HITPOINTS = 3;
39
40 Yeti::Yeti(const lisp::Lisp& reader)
41 {
42   reader.get("x", start_position.x);
43   reader.get("y", start_position.y);
44   bbox.set_size(80, 120);
45   sprite = sprite_manager->create("yeti");
46   sprite->set_action("right");
47   state = INIT;
48   side = LEFT;
49   sound_manager->preload_sound("yeti_gna");
50   sound_manager->preload_sound("yeti_roar");
51   hit_points = INITIAL_HITPOINTS;
52   reader.get("dead-script", dead_script);
53 }
54
55 Yeti::~Yeti()
56 {
57 }
58
59 void
60 Yeti::draw(DrawingContext& context)
61 {
62   // we blink when we are safe
63   if(safe_timer.started() && size_t(global_time*40)%2)
64     return;
65
66   BadGuy::draw(context);
67 }
68
69 void
70 Yeti::active_update(float elapsed_time)
71 {
72   switch(state) {
73     case INIT:
74       break;
75     case GO_RIGHT:
76       physic.set_velocity_x(RUN_SPEED);
77       if(timer.check())
78         physic.set_velocity_y(JUMP_VEL2);
79       break;
80     case GO_LEFT:
81       physic.set_velocity_x(-RUN_SPEED);
82       if(timer.check())
83         physic.set_velocity_y(JUMP_VEL2);
84       break;
85     case ANGRY_JUMPING:
86       if(timer.check()) {
87         // jump
88         sound_manager->play_sound("yeti_gna");
89         physic.set_velocity_y(JUMP_VEL1);
90       }
91       break;
92     default:
93       break;
94   }
95
96   movement = physic.get_movement(elapsed_time);
97 }
98
99 void
100 Yeti::go_right()
101 {
102   // jump and move right
103   physic.set_velocity_y(JUMP_VEL1);
104   physic.set_velocity_x(RUN_SPEED);
105   state = GO_RIGHT;
106   timer.start(JUMP_TIME);
107 }
108
109 void
110 Yeti::go_left()
111 {
112   physic.set_velocity_y(JUMP_VEL1);
113   physic.set_velocity_x(-RUN_SPEED);
114   state = GO_LEFT;
115   timer.start(JUMP_TIME);
116 }
117
118 void
119 Yeti::angry_jumping()
120 {
121   jumpcount = 0;
122   timer.start(ANGRY_JUMP_WAIT);
123   state = ANGRY_JUMPING;
124   physic.set_velocity_x(0);
125 }
126
127 void
128 Yeti::summon_snowball()
129 {
130   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(side == LEFT ? 64 : -64), get_pos().y, (side == LEFT ? RIGHT : LEFT)));
131 }
132
133 bool
134 Yeti::collision_squished(Player& player)
135 {
136   if(safe_timer.started())
137     return true;
138
139   player.bounce(*this);
140   sound_manager->play_sound("yeti_roar");
141   hit_points--;
142   if(hit_points <= 0) {
143     sprite->set_action("dead");
144     kill_squished(player);
145
146     // start script
147     if(dead_script != "") {
148       try {
149         ScriptInterpreter* interpreter 
150           = new ScriptInterpreter(GameSession::current()->get_working_directory());
151         interpreter->register_sector(Sector::current());
152         std::istringstream in(dead_script);
153         interpreter->load_script(in, "Yeti - dead-script");
154         interpreter->start_script();
155         Sector::current()->add_object(interpreter);
156       } catch(std::exception& e) {
157         std::cerr << "Couldn't execute yeti dead script: " << e.what() << "\n";
158       }
159     }
160   } else {
161     safe_timer.start(SAFE_TIME);
162   }
163   
164   return true;
165 }
166
167 void
168 Yeti::kill_fall()
169 {
170   // shooting bullets or being invincible won't work :)
171 }
172
173 void
174 Yeti::write(lisp::Writer& )
175 {
176 }
177
178 void
179 Yeti::drop_stalactite()
180 {
181   YetiStalactite* nearest = 0;
182   float dist = FLT_MAX;
183
184   Sector* sector = Sector::current();
185   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
186       i != sector->gameobjects.end(); ++i) {
187     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
188     if(stalactite && stalactite->is_hanging()) {
189       float sdist 
190         = fabsf(stalactite->get_pos().x - sector->player->get_pos().x);
191       if(sdist < dist) {
192         nearest = stalactite;
193         dist = sdist;
194       }
195     }
196   }
197
198   if(nearest)
199     nearest->start_shaking();
200 }
201
202 HitResponse
203 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
204 {
205   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
206     physic.set_velocity_y(0);
207     if(state == INIT) {
208       go_right();
209     } else if(state == GO_LEFT && !timer.started()) {
210       side = LEFT;
211       summon_snowball();
212       sprite->set_action("right");
213       angry_jumping();
214     } else if(state == GO_RIGHT && !timer.started()) {
215       side = RIGHT;
216       summon_snowball();
217       sprite->set_action("left");
218       angry_jumping();
219     } else if(state == ANGRY_JUMPING) {
220       if(!timer.started()) {
221         // we just landed
222         jumpcount++;
223         // make a stalactite falling down and shake camera a bit
224         Sector::current()->camera->shake(.1, 0, 10);
225         drop_stalactite();
226         
227         // go to other side after 3 jumps
228         if(jumpcount == 3) {
229           if(side == LEFT)
230             go_right();
231           else
232             go_left();
233         } else {
234           // jump again
235           timer.start(ANGRY_JUMP_WAIT);
236         }
237       }
238     }
239   }
240   
241   return CONTINUE;
242 }
243
244 IMPLEMENT_FACTORY(Yeti, "yeti")