1 /*
2 	This file is part of cave9.
3 
4 	cave9 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 	cave9 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 cave9.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
22 #include <float.h>
23 #include <assert.h>
24 #include <SDL_opengl.h>
25 #include <GL/gl.h>
26 #include "vec.h"
27 #include "game.h"
28 #include "util.h"
29 #include "detrand.h"
30 #include "time.h"
31 
32 const char* data_paths[] =
33 {
34 	DATADIR,
35 	NULL
36 };
37 
cave_len(Cave * cave)38 float cave_len (Cave* cave)
39 {
40 	int head = cave->i;
41 	int tail = (head - 1 + SEGMENT_COUNT) % SEGMENT_COUNT;
42 	return cave->segs[tail][0][2] - cave->segs[head][0][2];
43 }
44 
generate_stalactites(Cave * cave,float mult_y,float cos_a)45 static inline float generate_stalactites (Cave* cave, float mult_y, float cos_a)
46 {
47 	static const float change_prob = 0.001;
48 	static const float some_prob = 0.01;
49 	static const float many_prob = 0.05;
50 	float prob = 0;
51 	float change = DRAND;
52 
53 	switch (cave->stalactites_status)
54 	{
55 	case STALACT_NONE:
56 		if (change < change_prob)
57 			cave->stalactites_status = STALACT_SOME;
58 		return mult_y;
59 	case STALACT_SOME:
60 		prob = some_prob;
61 		if (change < change_prob)
62 			cave->stalactites_status = STALACT_NONE;
63 		else if (change > (1 - change_prob))
64 			cave->stalactites_status = STALACT_MANY;
65 		break;
66 	case STALACT_MANY:
67 		prob = many_prob;
68 		if (change < change_prob)
69 			cave->stalactites_status = STALACT_SOME;
70 		break;
71 	default:
72 		//WTF??
73 		assert ("stalactites_status not possible" == 0);
74 	}
75 
76 	return DRAND < prob ? 1 : mult_y;
77 }
78 
cave_gen(Cave * cave,Digger * digger)79 void cave_gen (Cave* cave, Digger* digger)
80 {
81 	Ship *ship = SHIP(digger);
82 	// check if the ship advanced to the next segment
83 	int i = (cave->i - 1 + SEGMENT_COUNT) % SEGMENT_COUNT;
84 	if (ship->pos[2] > ship->start+1  &&  ship->pos[2]-SEGMENT_LEN < cave->segs[i][0][2])
85 		return;
86 
87 	// invalidate GL list for this segment
88 	cave->dirty[cave->i] = true;
89 
90 	const float B = 2/(WALL_MULT_MAX - WALL_MULT_MIN);
91 	const float A = B*WALL_MULT_MAX - 1;
92 
93 	// generate new segment
94 	for (i = 0; i < SECTOR_COUNT; ++i) {
95 		float a = M_PI_2+(i-1)*M_PI*2/SECTOR_COUNT;
96 		float r = ship->radius;
97 
98 		float cos_a = cos(a);
99 		float sin_a = sin(a);
100 
101 		float mult_x = (cos_a > 0)? digger->y_top_radius : digger->y_bottom_radius;
102 		float mult_y = (sin_a > 0)? digger->x_left_radius: digger->x_right_radius;
103 
104 		// clamp the multipliers to [mult_min .. mult_max]
105 		mult_x = (A + sin(mult_x))/B;
106 		mult_y = (A + sin(mult_y))/B;
107 
108 		if (cave->stalactites_status != STALACT_DISABLED)
109 			mult_y = generate_stalactites(cave, mult_y, cos_a);
110 
111 		SET(cave->segs[cave->i][i],
112 			ship->pos[0] + (r * mult_x * cos_a) + 2 * DRAND,
113 			ship->pos[1] + (r * mult_y * sin_a) + 2 * DRAND,
114 			ship->pos[2]
115 		);
116 	}
117 	COPY (cave->centers[cave->i], ship->pos);
118 
119 	// increment segment circular pointer
120 	cave->i = (cave->i + 1) % SEGMENT_COUNT;
121 
122 	// place monolith on rooms
123 	if (ship->pos[2] > cave->monolith_pos[2]+ROOM_SPACING  &&  ship->pos[2] > ROOM_START)
124 	{
125 		cave->monolith_pos[0] = ship->pos[0];
126 		cave->monolith_pos[1] = ship->pos[1];
127 		cave->monolith_pos[2] = ROOM_LEN/2 + ROOM_SPACING*(int)(ship->pos[2]/ROOM_SPACING);
128 		cave->monolith_yaw = atan2 (ship->vel[0], ship->vel[2]);
129 	}
130 }
131 
cave_init(Cave * cave,Digger * digger,Args * args)132 static void cave_init (Cave* cave, Digger* digger, Args* args)
133 {
134 	memset (cave, 0, sizeof(Cave));
135 
136 	int game_mode = TWO_BUTTONS;
137 	if (args != NULL) {
138 		game_mode = args->game_mode;
139 		cave->stalactites_status = args->stalactites?STALACT_NONE:STALACT_DISABLED;
140 	}
141 
142 	Ship *ship = SHIP(digger);
143 	cave->i = 0;
144 	do {
145 		digger_control(digger, game_mode);
146 		ship_move(ship, 1./50);
147 		cave_gen(cave, digger);
148 	}
149 	while(cave->i != 0);
150 }
151 
ship_init(Ship * ship,float radius)152 static void ship_init (Ship* ship, float radius)
153 {
154 	SET (ship->pos, 0,0,ship->start);
155 	SET (ship->vel, 0,0,VELOCITY);
156 	SET (ship->lookAt, 0,0,VELOCITY);
157 	ship->roll = 0;
158 	ship->radius = radius;
159 	ship->dist = FLT_MAX;
160 	SET (ship->repulsion,0,1,0);
161 	ship->lefton = ship->righton = false;
162 }
163 
digger_init(Digger * digger,float radius)164 static void digger_init(Digger *digger, float radius)
165 {
166 	ship_init(SHIP(digger), radius);
167 
168 	digger->x_left_radius = 0.0;
169 	digger->x_right_radius = 0.0;
170 	digger->y_top_radius = 0.0;
171 	digger->y_bottom_radius = 0.0;
172 }
173 
fast_forward(Game * game)174 void fast_forward (Game* game)
175 {
176 	while ((game->digger.ship.pos[2] - cave_len(&game->cave)) / (game->mode==ONE_BUTTON?2:1) < game->start)
177 	{
178 		digger_control (&game->digger, game->mode);
179 		cave_gen (&game->cave, &game->digger);
180 		ship_move (SHIP(&game->digger), 0.05);
181 	}
182 	// put the player in the middle of the cave
183 	COPY(game->player.pos, game->cave.centers[(game->cave.i + 1) % SEGMENT_COUNT]);
184 
185 	game->player.vel[0] += (game->cave.centers[(game->cave.i + 2) % SEGMENT_COUNT][0] -
186 	                        game->cave.centers[(game->cave.i + 1) % SEGMENT_COUNT][0]) * 20;
187 
188 	game->player.vel[1] += (game->cave.centers[(game->cave.i + 2) % SEGMENT_COUNT][1] -
189 	                        game->cave.centers[(game->cave.i + 1) % SEGMENT_COUNT][1]) * 20;
190 
191 	// the y-component of the vector should be biased towards upwards movement,
192 	// as reverting downwards-movement is *very* hard.
193 	if (game->player.vel[1] < 0)
194 		game->player.vel[1] *= 0.1;
195 
196 	COPY(game->player.lookAt, game->player.vel);
197 }
198 
game_init(Game * game,Args * args)199 void game_init (Game* game, Args* args)
200 {
201 	assert(args != NULL);
202 
203 	game->mode = args->game_mode;
204 	game->monoliths = args->monoliths;
205 	game->caveseed = args->caveseed;
206 	if (game->caveseed != 0)
207 	{
208 		game->start = args->start;
209 		detsrand(game->caveseed);
210 	} else {
211 		game->player.start = game->digger.ship.start = (float)args->start;
212 		game->start = 0;
213 		detsrand(time(NULL));
214 	}
215 
216 	ship_init (&game->player, SHIP_RADIUS);
217 	digger_init (&game->digger, MAX_CAVE_RADIUS);
218 	cave_init (&game->cave, &game->digger, args);
219 	if (game->start)
220 		fast_forward(game);
221 
222 	score_init (&game->score, args, game->caveseed, game->monoliths * 2/* + game->stalactites*/); // XXX uncomment this, once stalactites are implemented
223 }
224 
ship_move(Ship * ship,float dt)225 void ship_move (Ship* ship, float dt)
226 {
227 	float acc = THRUST*dt;
228 	int roll = 0;
229 
230 	if(ship->lefton) {
231 		Vec3 leftup = { -acc, acc/2, 0 };
232 		ADD(ship->vel, leftup);
233 		roll--;
234 	}
235 
236 	if(ship->righton) {
237 		Vec3 rightup = { +acc, acc/2, 0 };
238 		ADD(ship->vel, rightup);
239 		roll++;
240 	}
241 
242 	ship->vel[1] -= GRAVITY*dt;
243 
244 	ADDSCALE(ship->pos, ship->vel, dt);
245 
246 	// smooth lookAt
247 	Vec3 d;
248 	SUB2 (d, ship->vel, ship->lookAt);
249 	ADDSCALE (ship->lookAt, d, .2);
250 
251 	// smooth roll
252 	ship->roll += (roll?.015:.06)*(roll - ship->roll);
253 }
254 
digger_control(Digger * digger,int game_mode)255 void digger_control (Digger* digger, int game_mode)
256 {
257 	Ship *ship = SHIP(digger);
258 
259 	float twist_factor = 500;
260 	float noise = .1;
261 	float twist = 1 - 1/(1 + ship->pos[2]/twist_factor);
262 	float max_vel[3] = {
263 		MAX_VEL_X * twist,
264 		MAX_VEL_Y * twist,
265 		MAX_VEL_Z
266 	};
267 
268 	if(
269 			DRAND < twist*noise ||
270 			ship->vel[1] >  max_vel[1] ||
271 			ship->vel[1] < -max_vel[1] ||
272 			ship->vel[0] >  max_vel[0] ||
273 			ship->vel[0] < -max_vel[0]
274 		)
275 	{
276 		if(DRAND>twist/2)
277 			ship->lefton = DRAND<twist*noise ? DRAND_BIG % 2 :
278 				ship->vel[1] < 0 || ship->vel[0] > +max_vel[0];
279 
280 		if(DRAND>twist/2)
281 			ship->righton = DRAND<twist*noise ? DRAND_BIG % 2 :
282 				ship->vel[1] < 0 || ship->vel[0] < -max_vel[0];
283 
284 		if (game_mode == ONE_BUTTON)
285 			ship->lefton = ship->righton = (ship->lefton | ship->righton);
286 	}
287 
288 	float scale = 1-MIN(1,log(1+ship->pos[2])/log(1+MIN_CAVE_RADIUS_DEPTH));
289 	ship->radius = MIN_CAVE_RADIUS+(MAX_CAVE_RADIUS-MIN_CAVE_RADIUS)*scale;
290 
291 	// rooms
292 	if (ship->pos[2] >= ROOM_START) {
293 		int r = ((int)ship->pos[2])%ROOM_SPACING;
294 		if (r < ROOM_LEN)
295 			ship->radius *= 1+(ROOM_MUL-1)*(cos(M_PI*(r-ROOM_LEN/2)/ROOM_LEN));
296 	}
297 
298 	// start falling
299 	if (ship->pos[2] - ship->start  <  .33*SEGMENT_COUNT*SEGMENT_LEN)
300 		ship->lefton = ship->righton = false;
301 
302 	digger->x_right_radius += DRAND - 0.5;
303 	digger->y_top_radius += DRAND - 0.5;
304 	digger->x_left_radius += DRAND - 0.5;
305 	digger->y_bottom_radius += DRAND - 0.5;
306 }
307 
autopilot(Game * game,float dt)308 void autopilot (Game* game, float dt)
309 {
310 	Vec3 d;
311 	SUB2 (d, game->player.pos, game->cave.centers[(game->cave.i+2)%SEGMENT_COUNT]);
312 
313 	bool* R = &game->player.righton;
314 	bool* L = &game->player.lefton;
315 
316 	if (fabsf(d[1]) > fabsf(d[0])) {
317 		*R = *L = (d[1] < 0)  &&  (game->player.vel[1] < VELOCITY/8);
318 	}
319 	else {
320 		*R = d[0] < 0  &&  (game->player.vel[0] < VELOCITY/8);
321 		*L = !*R;
322 	}
323 }
324 
X(Cave * cave,int i,float xn,float yn,int k0,int k1)325 static float X (Cave* cave, int i, float xn, float yn, int k0, int k1)
326 {// used by collision()
327 
328 	float x1 = cave->segs[i][k0][0];
329 	float y1 = cave->segs[i][k0][1];
330 	float x2 = cave->segs[i][k1][0];
331 	float y2 = cave->segs[i][k1][1];
332 	float t = (yn - y2)/(y1 - y2);
333 	if( t < 0 || t > 1 )
334 		return 0;
335 	float x = (x1 - xn)*t + (x2 - xn)*(1 - t);
336 	return x;
337 }
338 
collision(Cave * cave,Ship * ship)339 float collision (Cave* cave, Ship* ship)
340 {
341 	float min = FLT_MAX;
342 	Vec3 center = {0,0,0};
343 
344 	// This method counts the number of intersections of a semi-line
345 	// starting from the point being checked against the poligon,
346 	// that in this case is the segment of the cave.
347 	// If the number of intersections is odd, the point is inside.
348 
349 	// In fact we'll check four points around the center of the ship
350 	// (to simulate a diamond-shaped bounding box).
351 
352 	// The return value is the distance from the ship to the cave.
353 
354 	int intersection_count[4];
355 	memset(intersection_count, 0, sizeof(intersection_count));
356 
357 
358 	for(int n = 0, i = (cave->i + SEGMENT_COUNT) % SEGMENT_COUNT; // -1
359 			n < 3; ++n, i = (i+1) % SEGMENT_COUNT ) // -1,0,1
360 	{
361 
362 		for(int j = 0; j < SECTOR_COUNT; ++j ) {
363 
364 			// find center
365 			ADD(center,cave->segs[i][j]);
366 
367 			// find minimum distance
368 			// FIXME needs to be a line to point distance
369 			Vec3 dist;
370 			SUB2(dist, ship->pos, cave->segs[i][j]);
371 			float len = LEN(dist);
372 			if(len < min)
373 				min = len;
374 		}
375 	}
376 
377 	// find if it's inside
378 	// TODO take in account the radius toward the previous and next segment,
379 	//      like in the distance calculation before, and merge both loops
380 	int i = cave->i;
381 	{
382 		for(int j = 0; j < SECTOR_COUNT; ++j ) {
383 			int j0 = (j+0)%SECTOR_COUNT;
384 			int j1 = (j+1)%SECTOR_COUNT;
385 
386 			// optimize
387 			if(cave->segs[i][j0][0] < ship->pos[0]-ship->radius &&
388 					cave->segs[i][j1][0] < ship->pos[0]-ship->radius)
389 				continue;
390 			if(cave->segs[i][j0][1] > ship->pos[1]+ship->radius &&
391 					cave->segs[i][j1][1] > ship->pos[1]+ship->radius)
392 				continue;
393 			if(cave->segs[i][j0][1] < ship->pos[1]-ship->radius &&
394 					cave->segs[i][j1][1] < ship->pos[1]-ship->radius)
395 				continue;
396 
397 			if(X(cave, i, ship->pos[0] - ship->radius, ship->pos[1], j0, j1) > 0)
398 				++intersection_count[0];
399 
400 			if(X(cave, i, ship->pos[0], ship->pos[1] + ship->radius, j0, j1) > 0)
401 				++intersection_count[1];
402 
403 			if(X(cave, i, ship->pos[0] + ship->radius, ship->pos[1], j0, j1) > 0)
404 				++intersection_count[2];
405 
406 			if(X(cave, i, ship->pos[0], ship->pos[1] - ship->radius, j0, j1) > 0)
407 				++intersection_count[3];
408 		}
409 	}
410 
411 	SCALE(center,SECTOR_COUNT);
412 	SUB2(ship->repulsion, center, ship->pos);
413 	ship->repulsion[2] = 0;
414 	NORM(ship->repulsion);
415 
416 	for(int i = 0; i < 4; ++i) {
417 		if(intersection_count[i] % 2 == 0) {
418 			return ship->dist = 0;  // hit
419 		}
420 	}
421 
422 	ship->dist = min - 2*ship->radius;
423 	return min;  // miss
424 }
425 
game_nocheat(Game * game)426 bool game_nocheat (Game *game)
427 {
428 	return (game->player.start == 0 && game->start == 0);
429 }
430 
game_score(Game * game)431 int game_score (Game *game)
432 {
433 	return game->player.pos[2] / (game->mode==ONE_BUTTON?2:1);
434 }
435 
game_score_update(Game * game)436 void game_score_update (Game *game)
437 {
438 	score_update (&game->score, game_score(game), game_nocheat(game));
439 }
440 
ship_hit(Ship * ship)441 float ship_hit (Ship *ship)
442 {
443 	return 1-CLAMP(ship->dist / (2*SHIP_RADIUS),0,1);
444 }
445 
446 float MAX_VEL[3] = { MAX_VEL_X, MAX_VEL_Y, MAX_VEL_Z };
ship_speed(Ship * ship)447 float ship_speed (Ship *ship)
448 {
449 	return MIN(1,
450 			log(1+MAX(0,LEN(ship->vel)-MAX_VEL_Z)) /
451 			log(1+MAX(0,LEN(MAX_VEL  )-MAX_VEL_Z)));
452 }
453 
454 // vim600:fdm=syntax:fdn=1:
455