1 /*************************************************************************
2
3 "I Have No Tomatoes"
4 Copyright (c) 2004, Mika Halttunen
5
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute
12 it freely, subject to the following restrictions:
13
14 1. The origin of this software must not be misrepresented; you must
15 not claim that you wrote the original software. If you use this
16 software in a product, an acknowledgment in the product documentation
17 would be appreciated but is not required.
18
19 2. Altered source versions must be plainly marked as such, and must
20 not be misrepresented as being the original software.
21
22 3. This notice may not be removed or altered from any source
23 distribution.
24
25
26 Mika Halttunen <lsoft@mbnet.fi>
27
28 *************************************************************************/
29
30 #include <stdio.h>
31 #include "SDL.h"
32 #include "SDL_opengl.h"
33 #include "texture.h"
34 #include "init.h"
35 #include "player.h"
36 #include "mymath.h"
37 #include "enemy.h"
38 #include "tilemap.h"
39 #include "particle.h"
40 #include "helpers.h"
41 #include "soundmusic.h"
42 #include "levels.h"
43
44
45 // The helpers
46 WISP wisp;
47 POTATOMAN potatoman;
48
49
50 //////////////////////////////////////////////////
51 // WISP STUFF
52 //////////////////////////////////////////////////
53
54
55 // The height where the wisp flies
56 #define WISP_HEIGHT 9.0f
57
58
59 // Wisp flying speed
60 #define WISP_SPEED 0.01f;
61
62
63 // Wisp texture
64 GLuint wisp_tex;
65
66
67 // Wisp time
68 const int wisp_counter = 1800;
69
70
71
72 // Create a wisp
create_wisp()73 void create_wisp() {
74 wisp.clear();
75 wisp.pos.x = RANDF(0,MAP_W+1);
76 wisp.pos.y = 15.0f;
77 wisp.pos.z = RANDF(0,MAP_H+1);
78 wisp.owner = 1; // The owner is unused since the bonuses are common
79 wisp.alive = true;
80 }
81
82
83 // Load the wisps
load_wisps()84 void load_wisps() {
85 wisp_tex = load_jpg("wisp.jpg", false, false, true);
86 }
87
88
89 // Move the wisps
move_wisps()90 void move_wisps() {
91 wisp.move();
92 }
93
94
95 // Draw the wisps
draw_wisps()96 void draw_wisps() {
97 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
98 glDepthMask(GL_FALSE);
99
100 wisp.draw();
101
102 glDepthMask(GL_TRUE);
103 }
104
105
106
107 // Move the wisp
move()108 void WISP::move() {
109 if(!alive)
110 return;
111
112 // Don't move if the level is finished
113 if(level_pause)
114 return;
115
116 // Advance the time
117 counter++;
118 if(counter > wisp_counter) {
119 alive = false;
120 // Create a particle explosion
121 for(int f=0; f<RAND(10,20); f++) {
122 VECT ppos(pos.x, pos.y, pos.z);
123 VECT pdir(RANDF(-0.1f,0.1f),RANDF(-0.1f,0.1f),RANDF(-0.1f,0.1f));
124 float c1[4] = { 0.1f, 1, 0.1f, 1 };
125 float c2[4] = { 0.1f, 1, 0.1f, 0 };
126 add_particle(ppos, pdir, RAND(30,60), RANDF(0.1f,0.2f), 0, c1, c2, part_glow);
127 }
128
129 play_sound(SND_WISP, false);
130 }
131
132 // Animate the glow
133 glow_anim = add_angle(glow_anim, 6.0f);
134
135 // Create a particle trail
136 VECT ppos(pos.x, pos.y, pos.z);
137 VECT pdir = 0.0f;
138 float c1[4] = { 0.1f, 1, 0.1f, 1 };
139 float c2[4] = { 0.1f, 1, 0.1f, 0 };
140 float size = RANDF(0.05f, 0.15f);
141 add_particle(ppos, pdir, RAND(10,30), size, size, c1, c2, part_glow);
142
143
144 // If there are no targets, fly to the center of the screen
145 if(!bonus) {
146 // Target vector
147 if(bonuslist.size() == 0) {
148 target.x = MAP_W * 0.5f;
149 target.y = WISP_HEIGHT;
150 target.z = MAP_H * 0.5f;
151 }
152 else {
153 // Choose a target
154 bonus = &(*(bonuslist.begin()));
155 target.x = bonus->x + 0.5f;
156 target.z = bonus->y + 0.5f;
157 target.y = 0.5f;
158 }
159 }
160
161 // Head towards the target
162 VECT tdir = target - pos;
163 if(!is_zero_vector(tdir))
164 normalize(tdir);
165 float dx = pos.x - target.x;
166 float dy = pos.y - target.y;
167 float dz = pos.z - target.z;
168 float dist = dx*dx + dy*dy + dz*dz;
169 if(dist > 27 || !bonus) {
170 dir += tdir * WISP_SPEED;
171 }
172 else {
173 dir += tdir * 0.04f;
174 }
175
176 // Add some randomness
177 if(RAND(0,100) > 95) {
178 dir += VECT(RANDF(-0.01f,0.01f),RANDF(-0.01f,0.01f),RANDF(-0.01f,0.01f));
179 }
180
181 // Restrict the speed
182 if(vector_length(dir) > 0.2f)
183 set_vector_length(dir, 0.2f);
184
185 // Move
186 pos += dir;
187
188 // If we're below the ground, fix that
189 if(pos.y < 0.6f) {
190 pos.y = 0.6f;
191 dir.y *= -1.5f;
192 }
193
194 // Check the distance to the target
195 if(bonus) {
196 if(dist <= 1) {
197 // Player gets the bonus
198 // The bonuses really are common, so we just let p1 get it here.
199 p1.pick_bonus(bonus);
200 }
201 }
202 }
203
204
205 // Draw the wisp
draw()206 void WISP::draw() {
207 if(!alive)
208 return;
209
210 // Translate to the position
211 glPushMatrix();
212 glTranslatef(pos.x, pos.y, pos.z);
213
214
215 // Negate the camera rotation
216 glMultMatrixf(cam_neg_matrix);
217 // glRotatef(45.0f, 0,1,0);
218 // glRotatef(-30.0f, 1,0,0);
219
220
221 // Draw the sprite
222 glColor3f(1,1,1);
223 BIND_TEXTURE(wisp_tex);
224 float size = 0.2f;
225 glBegin(GL_TRIANGLE_STRIP);
226 glTexCoord2f(1,1); glVertex3f( size, size, size);
227 glTexCoord2f(0,1); glVertex3f(-size, size, size);
228 glTexCoord2f(1,0); glVertex3f( size, -size, -size);
229 glTexCoord2f(0,0); glVertex3f(-size, -size, -size);
230 glEnd();
231
232
233 // Draw the glow
234 BIND_TEXTURE(part_glow);
235 glColor4f(0.1f,1,0.1f, 0.5f);
236 size = 0.75f + (SIN(glow_anim) * 0.2f);
237 glBegin(GL_TRIANGLE_STRIP);
238 glTexCoord2f(1,1); glVertex3f( size, size, size);
239 glTexCoord2f(0,1); glVertex3f(-size, size, size);
240 glTexCoord2f(1,0); glVertex3f( size, -size, -size);
241 glTexCoord2f(0,0); glVertex3f(-size, -size, -size);
242 glEnd();
243
244 glPopMatrix();
245 }
246
247
248 // Clear the wisp
clear()249 void WISP::clear() {
250 pos = dir = target = 0.0f;
251 bonus = NULL;
252 owner = 0;
253 alive = false;
254 glow_anim = RANDF(0,359);
255 counter = 0;
256 }
257
258
259 //////////////////////////////////////////////////
260 // POTATO MAN STUFF
261 //////////////////////////////////////////////////
262
263 // How far up does the potato man raise?
264 #define POTATO_HEIGHT 15.0f
265
266
267 // Potato man texture
268 GLuint potatoman_tex;
269 static int anim_frames[4] = { 0, 1, 0, 2 };
270
271 // Shadow texture from player.cpp
272 extern GLuint sprite_shadow;
273
274 // Function get_dir() from enemy.cpp
275 int get_dir(int dx, int dy);
276
277 // Potato man time
278 const int potato_counter = 900;
279
280
281 // Create a potato man
create_potatoman()282 void create_potatoman() {
283 potatoman.clear();
284 int px = RAND(0, MAP_W-1);
285 int py = RAND(0, MAP_H-1);
286 while(map_solid(px, py)) {
287 px = RAND(0, MAP_W-1);
288 py = RAND(0, MAP_H-1);
289 }
290 potatoman.x = px;
291 potatoman.y = py;
292 potatoman.tx = potatoman.x;
293 potatoman.ty = potatoman.y;
294 potatoman.alive = true;
295 }
296
297
298 // Load the potato man
load_potatoman()299 void load_potatoman() {
300 potatoman_tex = load_png("potatoman.png", true, false, false);
301 }
302
303
304 // Draw the potato men
draw_potatomen()305 void draw_potatomen() {
306 potatoman.draw();
307 }
308
309
310 // Move the potato men
move_potatomen()311 void move_potatomen() {
312 potatoman.move();
313 }
314
315
316 // Does the potato man collide with the enemy?
collide_with(ENEMY * e)317 bool POTATOMAN::collide_with(ENEMY *e) {
318 if(!alive)
319 return false;
320
321 if(raise_pos != 0.0f)
322 return false;
323
324 float dx = get_real_x() - e->get_real_x();
325 float dy = get_real_y() - e->get_real_y();
326 if(dx*dx + dy*dy <= 0.9f)
327 return true;
328
329 return false;
330 }
331
332
333 // Look for enemies and chase them
look_enemy()334 void POTATOMAN::look_enemy() {
335 if(enemylist.size() == 0 || chase)
336 return;
337
338 // Movement deltas for each direction
339 const int dx[4] = { 0, 1, 0, -1 };
340 const int dy[4] = { -1, 0, 1, 0 };
341
342 // Sweep in a straigth line and check for the player presence
343 for(int pos=1; pos < MAP_W; pos++) {
344 int xx, yy;
345 xx = x + dx[dir] * pos;
346 yy = y + dy[dir] * pos;
347
348 // Check for solid tile
349 if(map_solid(xx, yy))
350 break;
351
352 // Check for the enemies
353 list<ENEMY>::iterator i;
354 for(i = enemylist.begin(); i != enemylist.end(); ++i) {
355 if((int)(*i).get_real_x() == xx && (int)(*i).get_real_y() == yy && !(*i).dying) {
356 // Begin the chase
357 chase = true;
358 speed += 0.03f;
359 break;
360 }
361
362 if(chase)
363 break;
364 }
365 }
366 }
367
368
369 // Kick the enemy
kick(ENEMY * e)370 void POTATOMAN::kick(ENEMY *e) {
371 e->kicked = true;
372 e->chase = 0;
373 e->speed = 0.2f;
374
375 // Compute the right direction
376 int pdir = dir + 2;
377 if(pdir > DIR_W)
378 pdir -= 4;
379
380 if(e->dir == pdir || e->dir == dir)
381 e->dir = dir;
382 else {
383 e->dir += 2;
384 if(e->dir > DIR_W)
385 e->dir -= 4;
386 }
387
388 e->nextdir = e->dir;
389 e->offset = 0.0f;
390 e->tx = e->x; e->ty = e->y;
391 play_sound(SND_KICK, false);
392 }
393
394
395 // Move the potato man
move()396 void POTATOMAN::move() {
397 if(!alive)
398 return;
399
400 // Don't move if the level is finished
401 if(level_pause)
402 return;
403
404 // Create particles
405 if(raise_pos != 0.0f) {
406 VECT pos(get_real_x(), 0.25f, get_real_y());
407 pos.y += POTATO_HEIGHT * raise_pos;
408
409 VECT dir;
410 for(int f=0; f<5; f++) {
411 VECT ppos = pos + VECT(RANDF(-0.5f,0.5f),RANDF(-0.5f,0.5f),RANDF(-0.5f,0.5f));
412 dir.x = dir.y = dir.z = 0.0f;
413 float c1[4] = { 0.3f, 1, 0.3f, 1 };
414 float c2[4] = { 1, 1, 0.3f, 0.1f };
415
416 add_particle(ppos, dir, RAND(10,30), 0.3f, 0.1f, c1, c2, part_star);
417 }
418 }
419
420
421 // Go up or down
422 if(raise_dir == DIR_DOWN) {
423 raise_pos -= 0.015f;
424 if(raise_pos <= 0.0f) {
425 raise_pos = 0.0f;
426 raise_dir = -1;
427 }
428
429 return;
430 }
431 else if(raise_dir == DIR_UP) {
432 raise_pos += 0.015f;
433 if(raise_pos >= 1.0f) {
434 raise_pos = 1.0f;
435 alive = false;
436 }
437
438 return;
439 }
440
441
442 // Advance the time
443 counter++;
444 if(counter > potato_counter) {
445 // Go up
446 raise_dir = DIR_UP;
447 raise_pos = 0.0f;
448 anim = 0.0f;
449
450 // Play the sound
451 play_sound(SND_POTATOMAN2, false);
452 return;
453 }
454
455
456 // Advance the animation
457 if(!chase)
458 anim += 0.20f;
459 else
460 anim += 0.30f;
461 if((int)anim > 3)
462 anim = 0.0f;
463
464
465 // Choose a random destination
466 if(path_pos == -1) {
467 // Choose a valid target
468 int dx = RAND(0, MAP_W-1);
469 int dy = RAND(0, MAP_H-1);
470 while(map_solid(dx, dy) || dx == x || dy == y) {
471 dx = RAND(0, MAP_W-1);
472 dy = RAND(0, MAP_H-1);
473 }
474
475 // Calculate the path
476 if(pf.find_path(x, y, dx, dy) == PATH_FAILED) {
477 // Well, tough luck. We'll just wait and try again later.
478 return;
479 }
480
481 // Now we've got a nice path for us!
482 path_pos = 0;
483 offset = 0.0f;
484 tx = pf.path[0].x;
485 ty = pf.path[0].y;
486 dir = get_dir(tx - x, ty - y);
487 look_enemy();
488 }
489
490 // Move one step
491 if(tx == x && ty == y && path_pos > -1) {
492 offset = 0.0f;
493
494 // Follow the path if we're not chasing
495 if(chase == false) {
496 path_pos++;
497 tx = pf.path[path_pos].x;
498 ty = pf.path[path_pos].y;
499 dir = get_dir(tx - x, ty - y);
500 look_enemy();
501 }
502 else {
503 // We are chasing. Don't stop until there's a wall.
504 switch(dir) {
505 default:
506 case DIR_N: tx = x; ty = y - 1; break;
507 case DIR_E: tx = x + 1; ty = y; break;
508 case DIR_S: tx = x; ty = y + 1; break;
509 case DIR_W: tx = x - 1; ty = y; break;
510 }
511
512 // Check if the target is passable?
513 if(map_solid(tx, ty)) {
514 // Stop and choose a new path
515 tx = x;
516 ty = y;
517
518 path_pos = -1;
519 chase = false;
520 speed = 0.1f;
521 }
522 }
523
524 }
525
526 // Move towards the target tile
527 if(offset < 1.0f && (tx != x || ty != y) && path_pos > -1) {
528 offset += speed;
529
530 // If we've reached the target tile, move again
531 if(offset >= 1.0f) {
532 x = tx;
533 y = ty;
534 offset = 0.0f;
535
536 // If this is the final destination, stay put and choose a new path
537 // on the next cycle
538 if(x == pf.dx && y == pf.dy && !chase)
539 path_pos = -1;
540 }
541 }
542
543 }
544
545
546 // Draw the potato man
draw()547 void POTATOMAN::draw() {
548 if(!alive)
549 return;
550
551 // Sprite size
552 const float size = 0.85f;
553
554 // Calculate the offset
555 float offx = 0, offz = 0;
556 switch(dir) {
557 default:
558 case DIR_N: offz = -offset; break;
559 case DIR_E: offx = offset; break;
560 case DIR_S: offz = offset; break;
561 case DIR_W: offx = -offset; break;
562 }
563
564 // Translate to the position
565 glPushMatrix();
566 glTranslatef(x + offx + 0.5f, size - 0.20f, y + offz + 0.5f);
567
568 // Draw the shadow
569 glDepthMask(GL_FALSE);
570 glColor3f(1,1,1);
571 BIND_TEXTURE(sprite_shadow);
572 float sh = -(size - 0.21f);
573 glBegin(GL_TRIANGLE_STRIP);
574 glTexCoord2f(1,1); glVertex3f( 0.6f, sh, -0.6f);
575 glTexCoord2f(0,1); glVertex3f(-0.6f, sh, -0.6f);
576 glTexCoord2f(1,0); glVertex3f( 0.6f, sh, 0.6f);
577 glTexCoord2f(0,0); glVertex3f(-0.6f, sh, 0.6f);
578 glEnd();
579 glDepthMask(GL_TRUE);
580
581 // Translate up
582 glTranslatef(0, POTATO_HEIGHT * raise_pos, 0);
583
584
585 // Negate the camera rotation
586 glMultMatrixf(cam_neg_matrix);
587 // glRotatef(45.0f, 0,1,0);
588 // glRotatef(-30.0f, 1,0,0);
589
590 // Draw the sprite
591
592 // Compute the texture coords according the animation frame and direction
593 BIND_TEXTURE(potatoman_tex);
594 int f = anim_frames[(int)anim];
595 float textx = 0.25f * f;
596 float texty = 0.25f * (3-dir);
597
598 glBegin(GL_TRIANGLE_STRIP);
599 glTexCoord2f(textx + 0.25f, texty + 0.25f); glVertex3f( size, size, size);
600 glTexCoord2f(textx, texty + 0.25f); glVertex3f(-size, size, size);
601 glTexCoord2f(textx + 0.25f, texty); glVertex3f( size, -size, -size);
602 glTexCoord2f(textx, texty); glVertex3f(-size, -size, -size);
603 glEnd();
604
605 glPopMatrix();
606 }
607
608
609 // Get current x with offset
get_real_x()610 float POTATOMAN::get_real_x() {
611 // Calculate the offset
612 float offx = 0;
613 if(dir == DIR_E)
614 offx = offset;
615 else if(dir == DIR_W)
616 offx = -offset;
617
618 return (float)x + offx + 0.5f;
619 }
620
621
622 // Get current y with offset
get_real_y()623 float POTATOMAN::get_real_y() {
624 // Calculate the offset
625 float offy = 0;
626 if(dir == DIR_N)
627 offy = -offset;
628 else if(dir == DIR_S)
629 offy = offset;
630
631 return (float)y + offy + 0.5f;
632 }
633
634
635 // Clear the potato man
clear()636 void POTATOMAN::clear() {
637 x = y = tx = ty = 0;
638 offset = 0.0f;
639 speed = 0.1f;
640 dir = RAND(DIR_N, DIR_W);
641 nextdir = dir;
642 anim = 0.0f;
643 alive = false;
644 path_pos = -1;
645 chase = false;
646 counter = 0;
647 raise_dir = DIR_DOWN;
648 raise_pos = 1.0f;
649 }
650
651