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