|
|
|
#include "game.h"
|
|
|
|
#include "draw.h"
|
|
|
|
#include "audio.h"
|
|
|
|
#include "types.h"
|
|
|
|
#include <SDL_mixer.h>
|
|
|
|
#include <SDL_scancode.h>
|
|
|
|
#include <SDL_timer.h>
|
|
|
|
#include <SDL_ttf.h>
|
|
|
|
#include <SDL_mutex.h>
|
|
|
|
|
|
|
|
#define FLOOR_THICKNESS 200
|
|
|
|
#define MAX_ROPE_GRAB_LEN 80000
|
|
|
|
#define MIN_PHYSICS_STEP 6.0
|
|
|
|
|
|
|
|
#define TIMESTEP_LENGTH 3.0
|
|
|
|
|
|
|
|
#define BREAKPOINT *(int *)0 = 1;
|
|
|
|
|
|
|
|
GlobWorld world;
|
|
|
|
|
|
|
|
player_st *glob_player;
|
|
|
|
SDL_Renderer *debug_ren;
|
|
|
|
|
|
|
|
|
|
|
|
bool in_game;
|
|
|
|
bool game_paused = false;
|
|
|
|
extern bool mute;
|
|
|
|
int level;
|
|
|
|
int quality = 100;
|
|
|
|
long level_time;
|
|
|
|
|
|
|
|
int get_floor_ceiling();
|
|
|
|
|
|
|
|
#ifdef SCORE_SYSTEM
|
|
|
|
struct sg_times_list save_times_lst;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* object that draw.c watches */
|
|
|
|
struct draw_watcher draw_watch = {};
|
|
|
|
/* object that handles ui interactions in game.c and which draw watches */
|
|
|
|
struct textbox_info textboxes[1];
|
|
|
|
struct button mute_button;
|
|
|
|
|
|
|
|
|
|
|
|
struct ui_state gameui = {};
|
|
|
|
|
|
|
|
struct timespec last_tick;
|
|
|
|
double time_remaining = 0;
|
|
|
|
struct timespec get_now();
|
|
|
|
|
|
|
|
/* array of all the things in the world and their kinds */
|
|
|
|
//world_thing *world;
|
|
|
|
|
|
|
|
void startgame(SDL_Renderer * ren) ;
|
|
|
|
|
|
|
|
void process_keydown(SDL_Keysym key);
|
|
|
|
void process_keyup(SDL_Keysym key);
|
|
|
|
|
|
|
|
void add_to_world(world_thing thing);
|
|
|
|
void set_motor_timeout(Body *thing, int motorID, uint32_t timeout);
|
|
|
|
void set_motor_status(Body *thing, int motorID, bool run);
|
|
|
|
void stop_pull_rope(void);
|
|
|
|
|
|
|
|
player_st player;
|
|
|
|
|
|
|
|
// local
|
|
|
|
void set_motor_newtons(Body *thing, int motorID, double x, double y);
|
|
|
|
void set_motor_max_velocity(Body *thing, int motorID, double max);
|
|
|
|
void get_new_physics(Body **phys);
|
|
|
|
void ratcheted_winch_motor_update(Motor* motor);
|
|
|
|
void winch_motor_update (struct motorstruct *motor);
|
|
|
|
void load_level();
|
|
|
|
|
|
|
|
void reset_textbox(struct textbox_info *tb) {
|
|
|
|
tb->text_input_bufpos = 0;
|
|
|
|
memset(tb->text_input, 0, sizeof(tb->text_input));
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_to_textbox(struct textbox_info *tb, char c) {
|
|
|
|
if (tb->text_input_bufpos >= sizeof(tb->text_input)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tb->text_input[tb->text_input_bufpos++] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void delete_from_textbox(struct textbox_info *tb) {
|
|
|
|
if (tb->text_input_bufpos > 0)
|
|
|
|
tb->text_input[--tb->text_input_bufpos] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// move the collision poly to the position of the player
|
|
|
|
void default_update_collision_poly(Body *body) {
|
|
|
|
for (int i=0; i < body->collision_poly_size; i++) {
|
|
|
|
|
|
|
|
double x = body->collision_shape[i].x + body->position.x;
|
|
|
|
double y = body->collision_shape[i].y + body->position.y;
|
|
|
|
|
|
|
|
body->collision_poly[i].x = x;
|
|
|
|
body->collision_poly[i].y = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void new_level_tb_close_callback(struct textbox_info*textbox, void*callback) {
|
|
|
|
for (int i = 0; i < textbox->text_input_bufpos; i++) {
|
|
|
|
if (textbox->text_input[i] < '0' || textbox->text_input[i] > '9')
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lid = atoi(textbox->text_input);
|
|
|
|
|
|
|
|
gameui.goto_new_level = lid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void level_timer_update(bool reset) {
|
|
|
|
static int start_point = 0;
|
|
|
|
long now = SDL_GetTicks();
|
|
|
|
if (reset) {
|
|
|
|
level_time = 0;
|
|
|
|
start_point = now;
|
|
|
|
} else {
|
|
|
|
level_time = now - start_point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int long_comparator(const void *a, const void *b, void *non) {
|
|
|
|
long A = *(long *)a;
|
|
|
|
long B = *(long *)b;
|
|
|
|
|
|
|
|
return A - B;
|
|
|
|
}
|
|
|
|
|
|
|
|
int win_long_comparator(void *context_unused, const void *a, const void *b) {
|
|
|
|
long A = *(long *)a;
|
|
|
|
long B = *(long *)b;
|
|
|
|
|
|
|
|
return A - B;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SCORE_SYSTEM
|
|
|
|
void sort_times(void) {
|
|
|
|
#ifdef __linux__
|
|
|
|
qsort_r(save_times_lst.times, save_times_lst.size, sizeof(long), long_comparator, NULL);
|
|
|
|
#elif WIN32
|
|
|
|
qsort_s(save_times_lst.times, save_times_lst.size, sizeof(long), win_long_comparator, NULL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
long get_best_time(void) {
|
|
|
|
save_times_lst.sort();
|
|
|
|
if (save_times_lst.size > 0)
|
|
|
|
return save_times_lst.times[0];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int load_score_times(char *filename) {
|
|
|
|
|
|
|
|
char *fn;
|
|
|
|
char lvl_str[250];
|
|
|
|
snprintf(lvl_str, 250, "%d", level);
|
|
|
|
int fnlen = strlen(filename) + strlen(lvl_str) + 3;
|
|
|
|
fn = malloc(fnlen);
|
|
|
|
snprintf(fn, fnlen, "%s-%s", filename, lvl_str);
|
|
|
|
|
|
|
|
//printf("%d, lvl_str %s fn %s\n", level, lvl_str, fn);
|
|
|
|
|
|
|
|
FILE *f = fopen(fn, "r");
|
|
|
|
// unload existing score times
|
|
|
|
if (save_times_lst.times) {
|
|
|
|
write_times();
|
|
|
|
free(save_times_lst.times);
|
|
|
|
free(save_times_lst.filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f) {
|
|
|
|
// get file size
|
|
|
|
fseek(f, 0L, SEEK_END);
|
|
|
|
long sz = ftell(f);
|
|
|
|
fseek(f, 0L, SEEK_SET);
|
|
|
|
char *text = calloc((sz + 1), (sizeof(char)));
|
|
|
|
|
|
|
|
// read file
|
|
|
|
fread(text,sizeof(char),sz, f);
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
int count = 1;
|
|
|
|
for (int i = 0; i < strlen(text); i++) {
|
|
|
|
if (text[i] == '\n') {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
long *times = malloc(count * sizeof(long));
|
|
|
|
char *saveptr;
|
|
|
|
|
|
|
|
// extract times
|
|
|
|
char * token = strtok_r(text, "\n", &saveptr);
|
|
|
|
int i = 0;
|
|
|
|
while (token != NULL) {
|
|
|
|
if (strlen(token) > 2) {
|
|
|
|
times[i] = atol(token);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
token = strtok_r(NULL, "\n", &saveptr);
|
|
|
|
}
|
|
|
|
/* here somweher? */
|
|
|
|
|
|
|
|
struct sg_times_list stl = {.size=i, .capacity = count, .times=times, .sort=sort_times};
|
|
|
|
save_times_lst = stl;
|
|
|
|
sort_times();
|
|
|
|
free(text);
|
|
|
|
} else {
|
|
|
|
perror("loading times");
|
|
|
|
save_times_lst = (struct sg_times_list){.size=0, .capacity = 5, .times=calloc(5, sizeof(long)), .sort=sort_times};
|
|
|
|
}
|
|
|
|
|
|
|
|
save_times_lst.filename = fn;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int add_time(long time) {
|
|
|
|
if (save_times_lst.capacity == save_times_lst.size + 5) {
|
|
|
|
long newcap = save_times_lst.capacity * 2;
|
|
|
|
save_times_lst.times = realloc(save_times_lst.times, newcap);
|
|
|
|
save_times_lst.capacity = newcap;
|
|
|
|
}
|
|
|
|
save_times_lst.times[save_times_lst.size] = time;
|
|
|
|
save_times_lst.size++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int write_times(void) {
|
|
|
|
FILE *f = fopen(save_times_lst.filename, "w");
|
|
|
|
if (!f) {
|
|
|
|
perror("Saving game");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < save_times_lst.size; i++) {
|
|
|
|
fprintf(f, "%ld\n", save_times_lst.times[i]);
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // SCORE_SYSTEM
|
|
|
|
|
|
|
|
|
|
|
|
// collision poly size must be 2x shape_size + 1
|
|
|
|
void cast_update_collision_poly(Body *body) {
|
|
|
|
for (int i=0; i < body->collision_shape_size; i++) {
|
|
|
|
double x = body->collision_shape[i].x + body->position.x;
|
|
|
|
double y = body->collision_shape[i].y + body->position.y;
|
|
|
|
|
|
|
|
body->collision_poly[i].x = x;
|
|
|
|
body->collision_poly[i].y = y;
|
|
|
|
}
|
|
|
|
for (int i=body->collision_shape_size - 1; i >= 0; i--) {
|
|
|
|
double x = body->collision_shape[i].x + body->next_position.x;
|
|
|
|
double y = body->collision_shape[i].y + body->next_position.y;
|
|
|
|
int k = body->collision_shape_size + i;
|
|
|
|
|
|
|
|
body->collision_poly[k].x = x;
|
|
|
|
body->collision_poly[k].y = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*int i=body->collision_shape_size - 1;*/
|
|
|
|
/*double x = body->collision_shape[i].x + body->next_position.x;*/
|
|
|
|
/*double y = body->collision_shape[i].y + body->next_position.y;*/
|
|
|
|
|
|
|
|
/*body->collision_poly[body->collision_shape_size - 1 + i].x = x; */
|
|
|
|
/*body->collision_poly[body->collision_shape_size - 1 + i].y = y; */
|
|
|
|
|
|
|
|
|
|
|
|
/*body->collision_poly[(body->collision_poly_size - 1) * 2 + 1] = body->collision_poly[(body->collision_poly_size - 1) * 2 + 1];*/
|
|
|
|
/*body->collision_poly[(body->collision_poly_size - 1) * 2 + 2] = body->collision_poly[(body->collision_poly_size - 1)];*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void default_motor_curve(Motor *motor) {
|
|
|
|
// constant
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
world_thing world_getter(int i) {
|
|
|
|
world_thing *item = arlst_get(&world.items, i);
|
|
|
|
return *item;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FloorPoly* generate_floor_simple(int num_polys, bool extend_down, int st_height) {
|
|
|
|
FloorPoly *floor = calloc(num_polys, sizeof(FloorPoly));
|
|
|
|
Vect last, next;
|
|
|
|
last.x = 10;
|
|
|
|
last.y = st_height;
|
|
|
|
|
|
|
|
int n = 1;
|
|
|
|
for (int i = 0; i < num_polys; i++) {
|
|
|
|
double run = (get_rand(&n) % 900);
|
|
|
|
double rise = (get_rand(&n) % 100) - (get_rand(&n) % 100);
|
|
|
|
next.x = last.x + run;
|
|
|
|
next.y = last.y + rise;
|
|
|
|
|
|
|
|
FloorPoly poly;
|
|
|
|
poly.left = last;
|
|
|
|
poly.right = next;
|
|
|
|
get_new_physics(&poly.physics);
|
|
|
|
|
|
|
|
poly.physics->position = last;
|
|
|
|
|
|
|
|
int offset = extend_down ? FLOOR_THICKNESS : -FLOOR_THICKNESS;
|
|
|
|
|
|
|
|
poly.physics->collision_poly_size = 4;
|
|
|
|
poly.physics->collision_poly = calloc(4, sizeof(Vect));
|
|
|
|
poly.physics->collision_shape = calloc(4, sizeof(Vect));
|
|
|
|
|
|
|
|
poly.physics->collision_shape[0].x = 0;
|
|
|
|
poly.physics->collision_shape[0].y = 0;
|
|
|
|
|
|
|
|
poly.physics->collision_shape[1].x = run;
|
|
|
|
poly.physics->collision_shape[1].y = rise;
|
|
|
|
|
|
|
|
poly.physics->collision_shape[2].x = run;
|
|
|
|
poly.physics->collision_shape[2].y = rise+offset;
|
|
|
|
|
|
|
|
poly.physics->collision_shape[3].x = 0;
|
|
|
|
poly.physics->collision_shape[3].y = rise+offset;
|
|
|
|
|
|
|
|
default_update_collision_poly(poly.physics);
|
|
|
|
|
|
|
|
last = next;
|
|
|
|
floor[i] = poly;
|
|
|
|
}
|
|
|
|
|
|
|
|
return floor;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @param uninitialised Body pointer
|
|
|
|
// @result: malloc and configure a physics thing pointer
|
|
|
|
void get_new_physics(Body **phys) {
|
|
|
|
static int uid = 0;
|
|
|
|
|
|
|
|
/* physics */
|
|
|
|
Body * physics = malloc(sizeof(Body));
|
|
|
|
memset(physics, 0, sizeof(Body));
|
|
|
|
|
|
|
|
// give it the next uid
|
|
|
|
physics->uid = uid++;
|
|
|
|
|
|
|
|
physics->dynamics = false;
|
|
|
|
physics->glob_gravity = false;
|
|
|
|
physics->glob_friction = 0.0000;
|
|
|
|
|
|
|
|
physics->obj_mass = 100;
|
|
|
|
//kgs
|
|
|
|
|
|
|
|
physics->motors = malloc(sizeof(Motor) * 100);
|
|
|
|
memset(physics->motors, 0, sizeof(Motor) * 100);
|
|
|
|
physics->max_motors = 100;
|
|
|
|
physics->num_motors = 0;
|
|
|
|
|
|
|
|
// gravity
|
|
|
|
add_motor(physics, 0.0, 0.0);
|
|
|
|
// friction
|
|
|
|
add_motor(physics, 0.0, 0.0);
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &physics->last_advance_time);
|
|
|
|
|
|
|
|
physics->updateCollisionPoly = default_update_collision_poly;
|
|
|
|
*phys = physics;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*void updatePlayerCollision(Body *physics) {*/
|
|
|
|
/*physics->collision_poly[0].x = physics->x_pos;*/
|
|
|
|
/*physics->collision_poly[0].y = physics->y_pos;*/
|
|
|
|
/*}*/
|
|
|
|
|
|
|
|
|
|
|
|
int string_update_fixed(String *string) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int string_set_end(String *string, Vect *setting) {
|
|
|
|
string->end_point = *setting;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
String *get_fixed_strings(int number) {
|
|
|
|
String *strings = calloc(number, sizeof(String));
|
|
|
|
for (int i = 0; i < number; i++) {
|
|
|
|
|
|
|
|
strings[i].attached = false;
|
|
|
|
|
|
|
|
// takes self (String) and does nothing.
|
|
|
|
strings[i].update_end_point = string_update_fixed;
|
|
|
|
|
|
|
|
// takes self and vect to set string end point
|
|
|
|
strings[i].set_end_point = string_set_end;
|
|
|
|
}
|
|
|
|
return strings;
|
|
|
|
}
|
|
|
|
|
|
|
|
player_st get_player(int x, int y) {
|
|
|
|
/* creates player at given postion and zeroes physics */
|
|
|
|
|
|
|
|
// player
|
|
|
|
player_st player;
|
|
|
|
memset(&player, 0, sizeof(player));
|
|
|
|
player.max_walking_speed = 100;
|
|
|
|
|
|
|
|
// physics settings
|
|
|
|
get_new_physics(&player.physics);
|
|
|
|
player.physics->dynamics = true;
|
|
|
|
player.physics->lock = SDL_CreateMutex();
|
|
|
|
|
|
|
|
player.physics->position.x = x;
|
|
|
|
player.physics->position.y = y;
|
|
|
|
player.physics->next_position = player.physics->position;
|
|
|
|
player.physics->obj_elasticity = 0.5;
|
|
|
|
player.physics->obj_friction = 0.2;
|
|
|
|
|
|
|
|
// friction (not in use)
|
|
|
|
player.physics->glob_friction = 40; // drag coef * area
|
|
|
|
|
|
|
|
player.physics->collision_poly_size = 4 + 4;
|
|
|
|
player.physics->collision_poly = calloc(player.physics->collision_poly_size, sizeof(Vect));
|
|
|
|
|
|
|
|
player.physics->collision_shape_size = 4;
|
|
|
|
player.physics->collision_shape = calloc(4, sizeof(Vect));
|
|
|
|
|
|
|
|
player.physics->updateCollisionPoly = cast_update_collision_poly;
|
|
|
|
|
|
|
|
// swing rope
|
|
|
|
player.physics->num_strings = 1;
|
|
|
|
player.physics->max_strings = 1;
|
|
|
|
player.physics->strings = get_fixed_strings(1);
|
|
|
|
player.physics->strings->max_length = 210;
|
|
|
|
|
|
|
|
Vect *rect = player.physics->collision_shape;
|
|
|
|
rect[0].x = -10; rect[0].y = -10;
|
|
|
|
rect[1].x = 10; rect[1].y = -10;
|
|
|
|
rect[2].x = 10; rect[2].y = 10;
|
|
|
|
rect[3].x = -10; rect[3].y = 10;
|
|
|
|
|
|
|
|
// gravity
|
|
|
|
set_motor_newtons(player.physics, M_GRAVITY, 0.0,
|
|
|
|
player.physics->obj_mass * 5);
|
|
|
|
set_motor_status(player.physics, M_GRAVITY, true);
|
|
|
|
set_motor_timeout(player.physics, M_GRAVITY, 99999999);
|
|
|
|
|
|
|
|
// walking motor
|
|
|
|
add_motor(player.physics, 0.0, player.physics->obj_mass * 9.81);
|
|
|
|
set_motor_max_velocity(player.physics, M_PLAYER_WALK, 5); // has to be > grav
|
|
|
|
set_motor_max_velocity(player.physics, M_GRAVITY, 5);
|
|
|
|
|
|
|
|
// winch motor for string
|
|
|
|
add_motor(player.physics, 0.0, 0);
|
|
|
|
player.physics->motors[M_WINCH].update_motor = ratcheted_winch_motor_update;
|
|
|
|
|
|
|
|
return (player);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool point_point_colcheck(Vect one, Vect two) {
|
|
|
|
if (one.x == two.x && one.y == two.y) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
Vect one;
|
|
|
|
Vect two;
|
|
|
|
} Collision;
|
|
|
|
|
|
|
|
bool point_line_colcheck(Vect line[2], Vect point) {
|
|
|
|
// point is outside the rectangle made by the line
|
|
|
|
if ((point.x > line[0].x && point.x > line[1].x)
|
|
|
|
|| (point.x < line[0].x && point.x < line[0].x)
|
|
|
|
|| (point.y > line[0].y && point.y > line[0].y)
|
|
|
|
|| (point.y < line[0].y && point.y < line[0].y)
|
|
|
|
){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
double m = (double)(line[1].y - line[0].y) / (double)(line[1].x - line[0].x);
|
|
|
|
double c = (double)(line[0].y - (m * line[0].x));
|
|
|
|
|
|
|
|
double y = point.x * m + c;
|
|
|
|
|
|
|
|
// point is in the line +- 1
|
|
|
|
if ((int)y == point.y || ((int)y < point.y && (int)y + 1 > point.y)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
double *project_col_poly(Body *shape, Vect V) {
|
|
|
|
double *proj = calloc(shape->collision_poly_size, sizeof(double));
|
|
|
|
for (int i = 0; i < shape->collision_poly_size; i++) {
|
|
|
|
Vect point;
|
|
|
|
point.x = shape->collision_poly[i].x;
|
|
|
|
point.y = shape->collision_poly[i].y;
|
|
|
|
//double mag = vect_mag(point);
|
|
|
|
// printf("point: %f %f mag: %f\n", point.x, point.y, vect_mag(point));
|
|
|
|
// printf("Vectp: %f %f mag: %f\n", V.x, V.y, vect_mag(V));
|
|
|
|
proj[i] = vect_scalar_projection(point, V);
|
|
|
|
}
|
|
|
|
double min, max;
|
|
|
|
max = -99999999;
|
|
|
|
min = 99999999;
|
|
|
|
for (int i = 0; i < shape->collision_poly_size; i++) {
|
|
|
|
if (proj[i] > max) {
|
|
|
|
max = proj[i];
|
|
|
|
}
|
|
|
|
if (proj[i] < min) {
|
|
|
|
min = proj[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
double *res = calloc(2, sizeof(double));
|
|
|
|
res[0] = min;
|
|
|
|
res[1] = max;
|
|
|
|
|
|
|
|
free(proj);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vect get_normal(Vect start, Vect end) {
|
|
|
|
Vect norm;
|
|
|
|
double x = (end.x - start.x);
|
|
|
|
double y = (end.y - start.y);
|
|
|
|
norm.x = -y;
|
|
|
|
norm.y = x;
|
|
|
|
double len = vect_mag(norm);
|
|
|
|
norm.x /= len;
|
|
|
|
norm.y /= len;
|
|
|
|
|
|
|
|
return norm;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reference:
|
|
|
|
// http://www.dyn4j.org/2010/01/sat/#sat-inter
|
|
|
|
//
|
|
|
|
// TODO: Maybe avoid calculating the centre of the boxes; have body->position
|
|
|
|
// be the centre and translate the collision box to be around that. It does
|
|
|
|
// make placing a little more complex but no bother.
|
|
|
|
bool sat_collision_check(Body *one, Body *two, Vect *translation) {
|
|
|
|
int num_axes = one->collision_poly_size + two->collision_poly_size;
|
|
|
|
Vect *axes = calloc(num_axes, sizeof(Vect));
|
|
|
|
|
|
|
|
Vect end;
|
|
|
|
Vect start = one->collision_poly[0];
|
|
|
|
Vect one_position;
|
|
|
|
Vect two_position;
|
|
|
|
|
|
|
|
two_position = two->collision_poly[0];
|
|
|
|
one_position = one->collision_poly[0];
|
|
|
|
|
|
|
|
for (int i = 1; i < one->collision_poly_size; i++) {
|
|
|
|
one_position = vect_add(one_position, one->collision_poly[i]);
|
|
|
|
end = one->collision_poly[i];
|
|
|
|
axes[i-1] = get_normal(start, end);
|
|
|
|
start = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = one->collision_poly[0];
|
|
|
|
axes[one->collision_poly_size - 1] = get_normal(start, end);
|
|
|
|
|
|
|
|
start = two->collision_poly[0];
|
|
|
|
for (int i = 1; i < two->collision_poly_size; i++) {
|
|
|
|
two_position = vect_add(two_position, two->collision_poly[i]);
|
|
|
|
end = two->collision_poly[i];
|
|
|
|
axes[i - 1 + one->collision_poly_size] = get_normal(start, end);
|
|
|
|
start = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = two->collision_poly[0];
|
|
|
|
axes[two->collision_poly_size + one->collision_poly_size - 1] = get_normal(start, end);
|
|
|
|
|
|
|
|
double *proj_one, *proj_two;
|
|
|
|
proj_one = proj_two = 0;
|
|
|
|
|
|
|
|
Vect min_axis;
|
|
|
|
double min_overlap = 99999999;
|
|
|
|
for (int i = 0; i < num_axes; i++) {
|
|
|
|
// project
|
|
|
|
if (proj_one) {
|
|
|
|
free(proj_one);
|
|
|
|
}
|
|
|
|
if (proj_two) {
|
|
|
|
free(proj_two);
|
|
|
|
}
|
|
|
|
proj_one = project_col_poly(one, axes[i]);
|
|
|
|
proj_two = project_col_poly(two, axes[i]);
|
|
|
|
|
|
|
|
if ((proj_one[0] >= proj_two[1])
|
|
|
|
|| (proj_two[0] >= proj_one[1])) {
|
|
|
|
free(axes);
|
|
|
|
free(proj_one);
|
|
|
|
free(proj_two);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
double overlap;
|
|
|
|
double left = proj_one[1] < proj_two[1] ? proj_one[1] : proj_two[1];
|
|
|
|
double right = proj_one[0] > proj_two[0] ? proj_one[0] : proj_two[0];
|
|
|
|
overlap = left - right;
|
|
|
|
|
|
|
|
// one of the shapes is contained
|
|
|
|
if ((overlap > (proj_one[1] - proj_one[0])
|
|
|
|
|| (overlap > (proj_two[1] - proj_two[0])))) {
|
|
|
|
|
|
|
|
double min = proj_one[0] - proj_two[0];
|
|
|
|
double max = proj_one[1] - proj_two[1];
|
|
|
|
|
|
|
|
min = min < 0 ? -min : min;
|
|
|
|
max = max < 0 ? -max : max;
|
|
|
|
|
|
|
|
overlap += min < max ? min : max;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (overlap < min_overlap && overlap > 0) {
|
|
|
|
min_overlap = overlap;
|
|
|
|
min_axis = axes[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// flip the MTV if it is pointing INTO the object
|
|
|
|
Vect trans;
|
|
|
|
trans.x = min_overlap * min_axis.x;
|
|
|
|
trans.y = min_overlap * min_axis.y;
|
|
|
|
|
|
|
|
// https://gamedev.stackexchange.com/questions/27596/implementing-separating-axis-theorem-sat-and-minimum-translation-vector-mtv/27629#27629
|
|
|
|
Vect direction;
|
|
|
|
one_position.x /= one->collision_poly_size;
|
|
|
|
one_position.y /= one->collision_poly_size;
|
|
|
|
two_position.x /= two->collision_poly_size;
|
|
|
|
two_position.y /= two->collision_poly_size;
|
|
|
|
|
|
|
|
/*printf("ONE POS: %f %f", one_position.x, one_position.y);*/
|
|
|
|
/*printf("TWO POS: %f %f", two_position.x, one_position.y);*/
|
|
|
|
|
|
|
|
direction.x = one_position.x - two_position.x;
|
|
|
|
direction.y = one_position.y - two_position.y;
|
|
|
|
|
|
|
|
double dot = vect_scalar_projection(trans, direction);
|
|
|
|
|
|
|
|
/*printf("DIRECTION: %f %f\n\n", direction.x, direction.y);*/
|
|
|
|
/*printf("DOT: %f\n\n", dot);*/
|
|
|
|
|
|
|
|
if (dot > 0) {
|
|
|
|
trans = vect_scalar(trans, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// return the MTV
|
|
|
|
*translation = trans;
|
|
|
|
|
|
|
|
|
|
|
|
free(axes);
|
|
|
|
free(proj_one);
|
|
|
|
free(proj_two);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool check_collision(Body *one, Body *two, Vect *translation) {
|
|
|
|
int onesize = one->collision_poly_size;
|
|
|
|
int twosize = two->collision_poly_size;
|
|
|
|
|
|
|
|
// point-point
|
|
|
|
if (one->collision_poly_size == 1 && two->collision_poly_size == 1) {
|
|
|
|
if (point_point_colcheck(one->collision_poly[0], two->collision_poly[0])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// point-line
|
|
|
|
if ((onesize == 1 || twosize == 1) && (onesize == 2 || twosize == 2)) {
|
|
|
|
Vect line[2];
|
|
|
|
Vect point;
|
|
|
|
if (onesize > twosize) {
|
|
|
|
line[0] = one->collision_poly[0];
|
|
|
|
line[1] = one->collision_poly[1];
|
|
|
|
point = two->collision_poly[0];
|
|
|
|
} else {
|
|
|
|
line[0] = two->collision_poly[0];
|
|
|
|
line[1] = two->collision_poly[1];
|
|
|
|
point = one->collision_poly[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return point_line_colcheck(line, point);
|
|
|
|
}
|
|
|
|
|
|
|
|
// line-line
|
|
|
|
if ((onesize == 2 && twosize == 2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// point-poly
|
|
|
|
if ((onesize == 1 || twosize == 1) && (onesize > 2 || twosize > 2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// line-poly
|
|
|
|
if ((onesize == 2 || twosize == 2) && (onesize > 2 || twosize > 2)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// poly-poly
|
|
|
|
if ((onesize > 2 && twosize > 2)) {
|
|
|
|
return sat_collision_check(one, two, translation);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroy_physics_body(Body *b) {
|
|
|
|
// collisions
|
|
|
|
Vect *collision_poly;
|
|
|
|
Vect *collision_shape;
|
|
|
|
int collision_poly_size;
|
|
|
|
int collision_shape_size;
|
|
|
|
void (*updateCollisionPoly)(struct BodyStruct *);
|
|
|
|
// applying forces
|
|
|
|
int num_strings;
|
|
|
|
int max_strings;
|
|
|
|
String *strings;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroy_physics_collection(struct physics_collection *s) {
|
|
|
|
for (int i = 0; i < s->numItems; i++) {
|
|
|
|
if (s->items[i]) {
|
|
|
|
//free(s->items[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(s->items);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void next_level(int lvl) {
|
|
|
|
level = lvl;
|
|
|
|
load_level();
|
|
|
|
}
|
|
|
|
|
|
|
|
void load_level() {
|
|
|
|
SDL_LockMutex(player.physics->lock);
|
|
|
|
Vect v;
|
|
|
|
v.x = 0;
|
|
|
|
v.y = 0;
|
|
|
|
player.physics->vel = v;
|
|
|
|
player.physics->acc = v;
|
|
|
|
stop_pull_rope();
|
|
|
|
player.physics->strings[0].attached = false;
|
|
|
|
|
|
|
|
int d = -1;
|
|
|
|
for (int i = 0; i < world.items.size; i++) {
|
|
|
|
if (world.get(i).kind == ROOM_W) {
|
|
|
|
d = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d != -1) {
|
|
|
|
world_thing *w = arlst_del(&world.items, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
destroy_physics_collection(&world.uniques_index[ROOM_W]->room->ceil);
|
|
|
|
destroy_physics_collection(&world.uniques_index[ROOM_W]->room->floor);
|
|
|
|
//destroy_environment(&world.uniques_index[ROOM_W]->room->env);
|
|
|
|
|
|
|
|
get_floor_ceiling();
|
|
|
|
#ifdef SCORE_SYSTEM
|
|
|
|
draw_watch.best_time = get_best_time();
|
|
|
|
load_score_times("saves/times");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
v = world.uniques_index[ROOM_W]->room->ceil.items[2]->collision_poly[0];
|
|
|
|
v.y -= 15;
|
|
|
|
|
|
|
|
player.physics->position = v;
|
|
|
|
player.physics->next_position = v;
|
|
|
|
|
|
|
|
quality = 100;
|
|
|
|
level_timer_update(true);
|
|
|
|
draw_watch.finish_level = false;
|
|
|
|
|
|
|
|
viewport_pos.x = player.physics->position.x - width / 2;
|
|
|
|
viewport_pos.y = player.physics->position.y - height / 2;
|
|
|
|
|
|
|
|
SDL_UnlockMutex(player.physics->lock);
|
|
|
|
|
|
|
|
Mix_HaltChannel(-1);
|
|
|
|
|
|
|
|
// reset physics
|
|
|
|
|
|
|
|
struct timespec now = get_now();
|
|
|
|
double time_delta = now.tv_nsec - last_tick.tv_nsec;
|
|
|
|
if (now.tv_nsec < last_tick.tv_nsec) {
|
|
|
|
time_delta = now.tv_nsec - (last_tick.tv_nsec - 1000000000);
|
|
|
|
}
|
|
|
|
last_tick = now;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_floor_ceiling() {
|
|
|
|
struct environment e = get_scene_at(viewport_pos, level);
|
|
|
|
struct physics_collection floor;
|
|
|
|
struct physics_collection ceil;
|
|
|
|
|
|
|
|
floor.items = calloc(e.floor.size - 1, sizeof(Body*));
|
|
|
|
ceil.items = calloc(e.ceil.size - 1, sizeof(Body*));
|
|
|
|
|
|
|
|
floor.numItems = e.floor.size - 1;
|
|
|
|
ceil.numItems = e.ceil.size - 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < e.floor.size-1; i++) {
|
|
|
|
get_new_physics(&floor.items[i]);
|
|
|
|
get_new_physics(&ceil.items[i]);
|
|
|
|
Body *fseg = floor.items[i];
|
|
|
|
Body *cseg = ceil.items[i];
|
|
|
|
|
|
|
|
fseg->position = *(Vect *)arlst_get(&e.floor, i);
|
|
|
|
fseg->collision_poly_size = 4;
|
|
|
|
fseg->collision_poly = calloc(4, sizeof(Vect));
|
|
|
|
fseg->collision_shape = calloc(4, sizeof(Vect));
|
|
|
|
|
|
|
|
cseg->position = *(Vect *)arlst_get(&e.ceil, i);
|
|
|
|
cseg->collision_poly_size = 4;
|
|
|
|
cseg->collision_poly = calloc(4, sizeof(Vect));
|
|
|
|
cseg->collision_shape = calloc(4, sizeof(Vect));
|
|
|
|
|
|
|
|
Vect fthis = fseg->position;
|
|
|
|
Vect cthis = cseg->position;
|
|
|
|
|
|
|
|
Vect fnext = *(Vect *)arlst_get(&e.floor, i + 1);
|
|
|
|
Vect cnext= *(Vect *)arlst_get(&e.ceil, i + 1);
|
|
|
|
|
|
|
|
double frise = fnext.y - fthis.y;
|
|
|
|
double frun = fnext.x - fthis.x;
|
|
|
|
|
|
|
|
double crise = cnext.y - cthis.y;
|
|
|
|
double crun = cnext.x - cthis.x;
|
|
|
|
|
|
|
|
double fdepth = -(fabs(frise) * 2 + 100);
|
|
|
|
double cdepth = fabs(crise) * 2 + 100;
|
|
|
|
|
|
|
|
fseg->collision_shape[0].x = 0;
|
|
|
|
fseg->collision_shape[0].y = 0;
|
|
|
|
|
|
|
|
fseg->collision_shape[1].x = frun;
|
|
|
|
fseg->collision_shape[1].y = frise;
|
|
|
|
|
|
|
|
fseg->collision_shape[2].x = frun;
|
|
|
|
fseg->collision_shape[2].y = frise + fdepth;
|
|
|
|
|
|
|
|
fseg->collision_shape[3].x = 0;
|
|
|
|
fseg->collision_shape[3].y = frise + fdepth;
|
|
|
|
|
|
|
|
cseg->collision_shape[0].x = 0;
|
|
|
|
cseg->collision_shape[0].y = 0;
|
|
|
|
|
|
|
|
cseg->collision_shape[1].x = crun;
|
|
|
|
cseg->collision_shape[1].y = crise;
|
|
|
|
|
|
|
|
cseg->collision_shape[2].x = crun;
|
|
|
|
cseg->collision_shape[2].y = crise + cdepth;
|
|
|
|
|
|
|
|
cseg->collision_shape[3].x = 0;
|
|
|
|
cseg->collision_shape[3].y = crise + cdepth;
|
|
|
|
|
|
|
|
default_update_collision_poly(cseg);
|
|
|
|
default_update_collision_poly(fseg);
|
|
|
|
}
|
|
|
|
|
|
|
|
world_thing room_world;
|
|
|
|
room_world.kind = ROOM_W;
|
|
|
|
|
|
|
|
struct room* room = malloc(sizeof(struct room));
|
|
|
|
room->ceil = ceil;
|
|
|
|
room->floor = floor;
|
|
|
|
room_world.room = room;
|
|
|
|
|
|
|
|
add_to_world(room_world);
|
|
|
|
world.uniques_index[ROOM_W]->room->env = e;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Wall *get_long_wall(int numNodes, int *nodes) {
|
|
|
|
Wall wall;
|
|
|
|
memset(&wall, 0, sizeof(wall));
|
|
|
|
|
|
|
|
wall.numNodes = numNodes;
|
|
|
|
wall.nodes = calloc(numNodes, sizeof(SDL_Point));
|
|
|
|
get_new_physics(&wall.physics);
|
|
|
|
|
|
|
|
wall.physics->collision_poly = calloc(numNodes, sizeof(Vect));
|
|
|
|
wall.physics->collision_shape = calloc(numNodes, sizeof(Vect));
|
|
|
|
wall.physics->collision_poly_size = numNodes;
|
|
|
|
|
|
|
|
// collisions
|
|
|
|
//SDL_Point *collision_poly;
|
|
|
|
for (int i = 0; i < numNodes; i++) {
|
|
|
|
wall.nodes[i].x = nodes[2*i];
|
|
|
|
wall.nodes[i].y = nodes[2*i+1];
|
|
|
|
|
|
|
|
wall.physics->collision_shape[i].x = nodes[2*i];
|
|
|
|
wall.physics->collision_shape[i].y = nodes[2*i+1];
|
|
|
|
}
|
|
|
|
|
|
|
|
Wall *wallwall = malloc(sizeof(wall));
|
|
|
|
*wallwall = wall;
|
|
|
|
return wallwall;
|
|
|
|
}
|
|
|
|
|
|
|
|
void accel_thing(Body * thing, double x, double y) {
|
|
|
|
/* takes acceleration in m/s2 and converts to m/ms adding
|
|
|
|
* it to physics_thing
|
|
|
|
*/
|
|
|
|
|
|
|
|
// convert to m / millisecond
|
|
|
|
double x_adj = x / 1000.0;
|
|
|
|
double y_adj = y / 1000.0;
|
|
|
|
|
|
|
|
thing->acc.y += y_adj;
|
|
|
|
thing->acc.x += x_adj;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_motor_max_velocity(Body *thing, int motorID, double max) {
|
|
|
|
thing->motors[motorID].max_velocity = max;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_motor_timeout(Body *thing, int motorID, uint32_t timeout) {
|
|
|
|
// this don't work yo
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &thing->motors[motorID].timeout);
|
|
|
|
thing->motors[motorID].timeout.tv_nsec += 1000000 * timeout;
|
|
|
|
thing->motors[motorID].timeout.tv_sec
|
|
|
|
= thing->motors[motorID].timeout.tv_nsec * 0.000000001;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_motor_status(Body *thing, int motorID, bool run) {
|
|
|
|
if (motorID == M_GRAVITY && run == false) {
|
|
|
|
*(int *)0 = 1;
|
|
|
|
}
|
|
|
|
thing->motors[motorID].stop = !run;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_motor_newtons(Body *thing, int motorID, double x, double y) {
|
|
|
|
thing->motors[motorID].x =x;
|
|
|
|
thing->motors[motorID].y =y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_motor_newtons(Body *thing, int motorID, double x, double y) {
|
|
|
|
thing->motors[motorID].x +=x;
|
|
|
|
thing->motors[motorID].y +=y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// @param thing: the body to apply the motor to
|
|
|
|
// @param x, y: The initial motor force vector.
|
|
|
|
void add_motor(Body *thing, double x, double y) {
|
|
|
|
Motor motor;
|
|
|
|
memset(&motor, 0, sizeof(Motor));
|
|
|
|
motor.x = x;
|
|
|
|
motor.y = y;
|
|
|
|
motor.stop = true;
|
|
|
|
|
|
|
|
motor.timeout.tv_sec = 0;
|
|
|
|
motor.timeout.tv_nsec = 0;
|
|
|
|
|
|
|
|
motor.max_velocity = 999899;
|
|
|
|
|
|
|
|
motor.update_motor = default_motor_curve;
|
|
|
|
|
|
|
|
motor.end_object = thing;
|
|
|
|
|
|
|
|
if (thing->num_motors == thing->max_motors) {
|
|
|
|
thing->motors = realloc(thing->motors, sizeof(Motor) * (thing->max_motors *=2));
|
|
|
|
}
|
|
|
|
|
|
|
|
thing->motors[thing->num_motors] = motor;
|
|
|
|
thing->num_motors += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ratcheted_winch_motor_update(Motor* motor) {
|
|
|
|
|
|
|
|
Body *body = motor->end_object;
|
|
|
|
|
|
|
|
if (body->strings[0].max_length < 0.1
|
|
|
|
|| !body->strings[0].attached || motor->stop) {
|
|
|
|
motor->stop = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vect st;
|
|
|
|
st.x = body->position.x - body->strings[0].end_point.x;
|
|
|
|
st.y = body->position.y - body->strings[0].end_point.y;
|
|
|
|
|
|
|
|
if (body->strings[0].max_length > vect_mag(st))
|
|
|
|
body->strings[0].max_length = vect_mag(st);
|
|
|
|
winch_motor_update(motor);
|
|
|
|
|
|
|
|
play_game_sound(game_sounds.rope_pull, 300, BC_ROPE_PULL);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void winch_motor_update (Motor* motor) {
|
|
|
|
Vect v;
|
|
|
|
v.x = motor->x;
|
|
|
|
v.y = motor->y;
|
|
|
|
double mod = vect_mag(v);
|
|
|
|
|
|
|
|
Body *body = motor->end_object;
|
|
|
|
|
|
|
|
if (body->strings[0].max_length < 0.1
|
|
|
|
|| !body->strings[0].attached) {
|
|
|
|
motor->stop = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the motor direction to the string direction
|
|
|
|
Vect end_point = body->strings[0].end_point;
|
|
|
|
Vect start_point = body->position;
|
|
|
|
Vect dir = vect_add(end_point, vect_scalar(start_point, -1));
|
|
|
|
|
|
|
|
double arg = vect_arg(dir);
|
|
|
|
Vect force = vect_modarg(mod, arg);
|
|
|
|
|
|
|
|
motor->x = force.x;
|
|
|
|
motor->y = force.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void pull_rope(double newtons) {
|
|
|
|
if (!player.physics->strings[0].attached) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!player.physics->motors[M_WINCH].stop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set force
|
|
|
|
set_motor_newtons(player.physics, M_WINCH, 0, newtons);
|
|
|
|
set_motor_status(player.physics, M_WINCH, true);
|
|
|
|
// point it in the right direction
|
|
|
|
winch_motor_update(player.physics->motors + M_WINCH);
|
|
|
|
player.physics->motors[M_WINCH].stop = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop_pull_rope(void) {
|
|
|
|
Mix_HaltChannel(BC_ROPE_PULL);
|
|
|
|
player.physics->motors[M_WINCH].stop = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void add_rope(int mouse_x, int mouse_y) {
|
|
|
|
if (player.physics->strings[0].attached) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (game_paused || !in_game) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vect mouse;
|
|
|
|
mouse.x = mouse_x;
|
|
|
|
mouse.y = mouse_y;
|
|
|
|
mouse = vect_add(mouse, viewport_pos);
|
|
|
|
|
|
|
|
Vect start = player.physics->position;
|
|
|
|
|
|
|
|
Vect end;
|
|
|
|
end.x = 0;
|
|
|
|
end.y = 0;
|
|
|
|
|
|
|
|
struct room *room = world.uniques_index[ROOM_W]->room;
|
|
|
|
struct physics_collection floor = room->floor;
|
|
|
|
struct physics_collection ceil = room->ceil;
|
|
|
|
|
|
|
|
Vect ray_e = player.physics->position;
|
|
|
|
Vect ray_s = mouse;
|
|
|
|
|
|
|
|
double x1 = ray_s.x;
|
|
|
|
double y1 = ray_s.y;
|
|
|
|
|
|
|
|
double x2 = ray_e.x;
|
|
|
|
double y2 = ray_e.y;
|
|
|
|
|
|
|
|
x1 += (x1 - x2) * 100;
|
|
|
|
y1 += (y1 - y2) * 100;
|
|
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
struct physics_collection s;
|
|
|
|
bool found = false;
|
|
|
|
double len = MAX_ROPE_GRAB_LEN;
|
|
|
|
double t, u;
|
|
|
|
for (int j = 0; j < (floor.numItems + ceil.numItems - 2); j++) {
|
|
|
|
if (j >= floor.numItems - 1) {
|
|
|
|
s = ceil;
|
|
|
|
i = j - floor.numItems + 1;
|
|
|
|
} else {
|
|
|
|
s = floor;
|
|
|
|
i = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vect wall_s = s.items[i]->position;
|
|
|
|
Vect wall_e = s.items[i+1]->position;
|
|
|
|
|
|
|
|
Vect intersect;
|
|
|
|
|
|
|
|
double x3 = wall_s.x;
|
|
|
|
double y3 = wall_s.y;
|
|
|
|
|
|
|
|
double x4 = wall_e.x;
|
|
|
|
double y4 = wall_e.y;
|
|
|
|
|
|
|
|
t = ((x1 - x3)*(y3 - y4) - (y1 - y3)*(x3-x4))
|
|
|
|
/ ((x1-x2)*(y3-y4) - (y1 - y2)*(x3-x4));
|
|
|
|
|
|
|
|
u = ((x1 - x2)*(y1 - y3) - (y1 - y2)*(x1 - x3))
|
|
|
|
/ ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4));
|
|
|
|
|
|
|
|
if (t < 0 || u > 0 || t > 1 || u < -1) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
intersect.x = ((x1*y2 - y1*x2)*(x3 - x4) -(x1 - x2)*(x3*y4 - y3*x4))
|
|
|
|
/ ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4));
|
|
|
|
|
|
|
|
intersect.y = ((x1*y2 - y1*x2)*(y3 - y4) - (y1 - y2) * (x3*y4 - y3 * x4))
|
|
|
|
/ ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4));
|
|
|
|
|
|
|
|
Vect rope = vect_add(intersect, vect_scalar(start, -1));
|
|
|
|
double mag = vect_mag(rope);
|
|
|
|
if (mag < len) {
|
|
|
|
len = mag;
|
|
|
|
found = true;
|
|
|
|
end = intersect;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
|
|
|
SDL_SetRenderDrawColor(debug_ren, 200, 255, 30, 255);
|
|
|
|
SDL_RenderDrawLine(debug_ren, start.x - viewport_pos.x,start.y-viewport_pos.y,
|
|
|
|
end.x - viewport_pos.x, end.y -viewport_pos.y);
|
|
|
|
|
|
|
|
Vect rop = vect_add(end, vect_scalar(start, -1));
|
|
|
|
|
|
|
|
player.physics->strings[0].max_length = vect_mag(rop);
|
|
|
|
|
|
|
|
player.physics->strings[0].set_end_point(player.physics->strings, &end);
|
|
|
|
player.physics->strings[0].attached = true;
|
|
|
|
play_game_sound(game_sounds.rope_attach, 100, BC_ROPE_ATTACH);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void delete_rope(void) {
|
|
|
|
player.physics->strings[0].attached = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// basic collision handler for testing
|
|
|
|
//
|
|
|
|
int process_collisions(Body *thing, Vect *trans) {
|
|
|
|
Vect translation;
|
|
|
|
translation.x = translation.y = 0;
|
|
|
|
Vect temptrans;
|
|
|
|
temptrans.x = temptrans.y = 0;
|
|
|
|
int num_cols = 0;
|
|
|
|
int num = 0;
|
|
|
|
|
|
|
|
thing->was_colliding += thing->colliding > 0 ? 1 : -1;
|
|
|
|
if (thing->was_colliding < -1000) {
|
|
|
|
thing->was_colliding = -1000;
|
|
|
|
}
|
|
|
|
if (thing->was_colliding > 1000) {
|
|
|
|
thing->was_colliding = 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int k = 0; k < world.items.size; k++) {
|
|
|
|
if (world.get(k).kind == STATIC_WALL_W
|
|
|
|
&& world.get(k).wall->physics->uid != thing->uid) {
|
|
|
|
if ((world.get(k).wall->physics->position.x - viewport_pos.x) > (width * 2)
|
|
|
|
|| world.get(k).wall->physics->position.x - viewport_pos.x < 0) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
if (check_collision(world.get(k).wall->physics, thing, &temptrans)) {
|
|
|
|
num++;
|
|
|
|
thing->colliding = true;
|
|
|
|
translation = vect_add(translation, temptrans);
|
|
|
|
}
|
|
|
|
} else if (world.get(k).kind == FLOOR || world.get(k).kind == CEILING) {
|
|
|
|
for (int i = 0; i < world.get(k).floor->numPolys; i++) {
|
|
|
|
if ((world.get(k).floor->polys[i].physics->position.x - viewport_pos.x) > (2 *width)
|
|
|
|
|| (world.get(k).floor->polys[i].physics->position.x - viewport_pos.x) < -width) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
if (check_collision(world.get(k).floor->polys[i].physics, thing, &temptrans)) {
|
|
|
|
num_cols++;
|
|
|
|
thing->colliding = true;
|
|
|
|
translation = vect_add(translation, temptrans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (world.get(k).kind == ROOM_W) {
|
|
|
|
for (int i = 0; i < world.get(k).room->floor.numItems; i++) {
|
|
|
|
Body *s = world.get(k).room->floor.items[i];
|
|
|
|
if (s->position.x + E_ROOM_RES < viewport_pos.x) {
|
|
|
|
continue;
|
|
|
|
} else if (s->position.x > viewport_pos.x + width) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_collision(s, thing, &temptrans)) {
|
|
|
|
num_cols++;
|
|
|
|
thing->colliding = true;
|
|
|
|
translation = vect_add(translation, temptrans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < world.get(k).room->ceil.numItems; i++) {
|
|
|
|
Body *s = world.get(k).room->ceil.items[i];
|
|
|
|
if (s->position.x + E_ROOM_RES < viewport_pos.x) {
|
|
|
|
continue;
|
|
|
|
} else if (s->position.x > viewport_pos.x + width) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
num++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_collision(s, thing, &temptrans)) {
|
|
|
|
num_cols++;
|
|
|
|
thing->colliding = true;
|
|
|
|
translation = vect_add(translation, temptrans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!num_cols) {
|
|
|
|
thing->colliding = false;
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
*trans = translation;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec get_now() {
|
|
|
|
struct timespec now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
if (now.tv_nsec >= 1000000000) {
|
|
|
|
now.tv_nsec -= 1000000000;
|
|
|
|
}
|
|
|
|
return now;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool get_motor_active(Body *thing, int i) {
|
|
|
|
if (thing->motors[i].stop) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec now = get_now();
|
|
|
|
if (thing->motors[i].timeout.tv_nsec
|
|
|
|
|| thing->motors[i].timeout.tv_sec) {
|
|
|
|
// 0 is a sentinel for infinity
|
|
|
|
if (thing->motors[i].timeout.tv_sec > now.tv_sec
|
|
|
|
&& thing->motors[i].timeout.tv_nsec > now.tv_nsec) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Basic physics works by adding up the acceleratino caused by all the forces on
|
|
|
|
* the object, converting it to velocity, then doing collision detection and
|
|
|
|
* applying various reactive forces (friction etc) and finally adjusting the
|
|
|
|
* object's position.
|
|
|
|
*/
|
|
|
|
void advance_thing(Body * thing) {
|
|
|
|
// TODO: fix ordering of collision detection + physics sim so that collisions
|
|
|
|
// are less bad.
|
|
|
|
|
|
|
|
thing->acc.x = 0;
|
|
|
|
thing->acc.y = 0;
|
|
|
|
|
|
|
|
if (!thing->dynamics) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
double time_delta = TIMESTEP_LENGTH;
|
|
|
|
|
|
|
|
Vect translation;
|
|
|
|
Vect friction;
|
|
|
|
translation.x = translation.y = 0;
|
|
|
|
int numcols = 0;
|
|
|
|
|
|
|
|
// collisions
|
|
|
|
if ((numcols = process_collisions(thing, &translation))) {
|
|
|
|
/*double check = vect_scalar_projection(translation, thing->vel);*/
|
|
|
|
/*if (check >= 0) {*/
|
|
|
|
/*translation.x *= -1;*/
|
|
|
|
/*translation.y *= -1;*/
|
|
|
|
/*}*/
|
|
|
|
|
|
|
|
// correct position using translation vector.
|
|
|
|
thing->next_position.x += translation.x;
|
|
|
|
thing->next_position.y += translation.y;
|
|
|
|
|
|
|
|
// make unit vector
|
|
|
|
double mag = vect_mag(translation);
|
|
|
|
translation.x = translation.x / mag;
|
|
|
|
translation.y = translation.y / mag;
|
|
|
|
|
|
|
|
if (mag > 0.02) {
|
|
|
|
Mix_Volume(BC_COLLISION, 1.2 * mag * MIX_MAX_VOLUME);
|
|
|
|
play_game_sound(game_sounds.collision, 100, BC_COLLISION);
|
|
|
|
Mix_FadeOutChannel(BC_COLLISION, 100* 0.8);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get velocity in direction of collision
|
|
|
|
mag = vect_scalar_projection(thing->vel, translation);
|
|
|
|
|
|
|
|
Vect revert_vel;
|
|
|
|
|
|
|
|
revert_vel.x = cos(vect_dir(translation)) * mag;
|
|
|
|
revert_vel.y = sin(vect_dir(translation)) * mag;
|
|
|
|
|
|
|
|
thing->vel.x -= revert_vel.x;
|
|
|
|
thing->vel.y -= revert_vel.y;
|
|
|
|
|
|
|
|
// add elasticity
|
|
|
|
thing->vel.x -= revert_vel.x * sqrt(thing->obj_elasticity);
|
|
|
|
thing->vel.y -= revert_vel.y * sqrt(thing->obj_elasticity);
|
|
|
|
|
|
|
|
// add friction
|
|
|
|
|
|
|
|
if (vect_mag(translation) < 0.1) {
|
|
|
|
Vect friction = vect_rotate(revert_vel, M_PI / 2);
|
|
|
|
double dir = vect_scalar_projection(thing->vel, friction);
|
|
|
|
if (dir > 0) {
|
|
|
|
friction = vect_rotate(friction, M_PI);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(thing->vel.x) <= fabs(friction.x)) {
|
|
|
|
thing->vel.x = 0;
|
|
|
|
} else {
|
|
|
|
thing->vel.x += thing->obj_friction * friction.x;
|
|
|
|
}
|
|
|
|
if (fabs(thing->vel.y) <= fabs(friction.y)) {
|
|
|
|
thing->vel.y = 0;
|
|
|
|
} else {
|
|
|
|
thing->vel.y += thing->obj_friction * friction.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*double norm = 9.81 * thing->obj_mass;*/
|
|
|
|
/*Vect x;*/
|
|
|
|
/*x.y = 0; x.x = 1;*/
|
|
|
|
/*if (vect_scalar_projection(thing->vel, translation) > 0) {*/
|
|
|
|
/*friction.x = translation.y;*/
|
|
|
|
/*friction.y = -translation.x;*/
|
|
|
|
/*} else {*/
|
|
|
|
/*friction.x = -translation.y;*/
|
|
|
|
/*friction.y = translation.x;*/
|
|
|
|
/*}*/
|
|
|
|
|
|
|
|
// force
|
|
|
|
/*friction.x = friction.x * norm * fric_const;*/
|
|
|
|
/*friction.y = friction.y * norm * fric_const;*/
|
|
|
|
// printf("friction accel: %e %e\n", friction.x, friction.y);
|
|
|
|
// printf("velocity: %e %e\n", thing->vel.x, thing->vel.y);
|
|
|
|
|
|
|
|
/*set_motor_newtons(thing, M_FRICTION, friction.x, friction.y);*/
|
|
|
|
/*set_motor_max_velocity(thing, M_FRICTION, vect_mag(project_vect(thing->vel, friction)));*/
|
|
|
|
|
|
|
|
// restitution force
|
|
|
|
/*rest.x = 0;*/
|
|
|
|
/*rest.y = 0;*/
|
|
|
|
/*double impulse = thing->obj_mass * vect_mag(thing->vel) * thing->obj_elasticity;*/
|
|
|
|
/*rest.x = cos(vect_dir(translation)) * impulse;*/
|
|
|
|
/*rest.y = sin(vect_dir(translation)) * impulse;*/
|
|
|
|
/*accel_thing(thing, rest.x, rest.y);*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// pendulums
|
|
|
|
for (int i = 0; i < thing->num_strings; i++) {
|
|
|
|
if (!thing->strings[i].attached) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
double st_len = vect_distance(thing->next_position, thing->strings[i].end_point);
|
|
|
|
double max_len = thing->strings[i].max_length;
|
|
|
|
if (st_len > max_len) {
|
|
|
|
Vect string_end = thing->strings[i].end_point;
|
|
|
|
Vect thing_position = thing->next_position;
|
|
|
|
|
|
|
|
Vect string;
|
|
|
|
string.x = string_end.x - thing_position.x;
|
|
|
|
string.y = string_end.y - thing_position.y;
|
|
|
|
double mag = vect_mag(string);
|
|
|
|
string.x /= mag;
|
|
|
|
string.y /= mag;
|
|
|
|
double angle = atan2(string.y, string.x);
|
|
|
|
double disp = mag - max_len;
|
|
|
|
|
|
|
|
thing->next_position.x += cos(angle) * disp;
|
|
|
|
thing->next_position.y += sin(angle) * disp;
|
|
|
|
|
|
|
|
// set velocity to 0 in direction of string
|
|
|
|
double corr_mag = vect_scalar_projection(thing->vel, string);
|
|
|
|
|
|
|
|
thing->vel.x -= corr_mag * cos(angle);
|
|
|
|
thing->vel.y -= corr_mag * sin(angle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// motors
|
|
|
|
for (int i = 0; i < thing->num_motors; i++) {
|
|
|
|
|
|
|
|
if (!get_motor_active(thing, i)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// dynamic motor curve
|
|
|
|
if (thing->motors[i].update_motor) {
|
|
|
|
thing->motors[i].update_motor(thing->motors + i);
|
|
|
|
}
|
|
|
|
Vect F;
|
|
|
|
Vect V;
|
|
|
|
F.x = thing->motors[i].x;
|
|
|
|
F.y = thing->motors[i].y;
|
|
|
|
V.x = thing->vel.x;
|
|
|
|
V.y = thing->vel.y;
|
|
|
|
|
|
|
|
Vect vel_in_dir = project_vect(V, F);
|
|
|
|
double dirF = atan2(F.y, F.x);
|
|
|
|
double dirV = atan2(V.y, V.x);
|
|
|
|
|
|
|
|
double diff = dirV > dirF ? dirV - dirF: dirF - dirV;
|
|
|
|
|
|
|
|
if (thing->motors[i].max_velocity > vect_mag(vel_in_dir)
|
|
|
|
|| diff >= M_PI) {
|
|
|
|
double acc_x = thing->motors[i].x / thing->obj_mass;
|
|
|
|
double acc_y = thing->motors[i].y / thing->obj_mass;
|
|
|
|
accel_thing(thing, acc_x, acc_y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// accelerate based on accel
|
|
|
|
thing->vel.x += thing->acc.x * (double)time_delta;
|
|
|
|
thing->vel.y += thing->acc.y * (double)time_delta;
|
|
|
|
|
|
|
|
double velocity = sqrt((double)(thing->vel.x * thing->vel.x + thing->vel.y * thing->vel.y));
|
|
|
|
|
|
|
|
// simple air drag
|
|
|
|
|
|
|
|
/*if (velocity > 0.000000000000000) {*/
|
|
|
|
/*double dir = atan2((double)thing->y_vel, (double)thing->x_vel) + M_PI;*/
|
|
|
|
|
|
|
|
/*double absolute_force = 5 * thing->obj_mass * (velocity / time_delta); // 2 * 0.1231 * 5;*/
|
|
|
|
/*printf("dir %e %e\n\n", dir, dir - M_PI);*/
|
|
|
|
/*printf("force %e\n\n", absolute_force);*/
|
|
|
|
|
|
|
|
/*double f_x = (cos(dir)) * absolute_force;*/
|
|
|
|
/*double f_y = (sin(dir)) * absolute_force;*/
|
|
|
|
|
|
|
|
/*accel_thing(thing, (float)f_x, (float)f_y);*/
|
|
|
|
|
|
|
|
/*}*/
|
|
|
|
|
|
|
|
if (fabsl((*thing).vel.x) < 0.001) {
|
|
|
|
(*thing).vel.x = 0;
|
|
|
|
}
|
|
|
|
if (fabsl((*thing).vel.y) < 0.001) {
|
|
|
|
(*thing).vel.y = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thing->lock) {
|
|
|
|
SDL_LockMutex(thing->lock);
|
|
|
|
}
|
|
|
|
thing->position = thing->next_position;
|
|
|
|
|
|
|
|
double oldx = thing->position.x;
|
|
|
|
double oldy = thing->position.y;
|
|
|
|
thing->next_position.x += (thing->vel.x * 1/2 * (double)time_delta);
|
|
|
|
thing->next_position.y += (thing->vel.y * 1/2 * (double)time_delta);
|
|
|
|
|
|
|
|
|
|
|
|
if (!thing->collision_poly[0].y) {
|
|
|
|
thing->updateCollisionPoly(thing);
|
|
|
|
}
|
|
|
|
|
|
|
|
thing->updateCollisionPoly(thing);
|
|
|
|
if (thing->lock) {
|
|
|
|
SDL_UnlockMutex(thing->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*void destroy_projectile(Projectile** proj) {*/
|
|
|
|
/*for (int i = 0; i < things_in_world; i ++) {*/
|
|
|
|
/*if (world[i].projectile == *proj) {*/
|
|
|
|
|
|
|
|
/*}*/
|
|
|
|
/*}*/
|
|
|
|
/*}*/
|
|
|
|
|
|
|
|
void *default_projectile_step(struct Projectile *self) {
|
|
|
|
advance_thing(self->physics);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *default_projectile_on_collision(struct Projectile *self) {
|
|
|
|
self->physics->dynamics = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_projectile(Projectile** proj) {
|
|
|
|
*proj = calloc(1, sizeof(Projectile));
|
|
|
|
get_new_physics(&(*proj)->physics);
|
|
|
|
(*proj)->physics->dynamics = true;
|
|
|
|
(*proj)->on_step = default_projectile_step;
|
|
|
|
(*proj)->on_collision = default_projectile_on_collision;
|
|
|
|
}
|
|
|
|
|
|
|
|
void advance_things(void) {
|
|
|
|
int numcols;
|
|
|
|
Vect translation;
|
|
|
|
|
|
|
|
/* update the timer */
|
|
|
|
struct timespec now = get_now();
|
|
|
|
double time_delta = now.tv_nsec - last_tick.tv_nsec;
|
|
|
|
|
|
|
|
if (now.tv_nsec < last_tick.tv_nsec) {
|
|
|
|
time_delta = now.tv_nsec - (last_tick.tv_nsec - 1000000000);
|
|
|
|
}
|
|
|
|
time_delta *= 0.000001; // convert to ms from ns
|
|
|
|
time_remaining += time_delta;
|
|
|
|
last_tick = now;
|
|
|
|
|
|
|
|
while (time_remaining > TIMESTEP_LENGTH) {
|
|
|
|
time_remaining -= TIMESTEP_LENGTH;
|
|
|
|
for (int i = 0; i < world.items.size; i++) {
|
|
|
|
switch (world.get(i).kind) {
|
|
|
|
case PLAYER_W :
|
|
|
|
advance_thing((world.get(i).player->physics));
|
|
|
|
continue;
|
|
|
|
case STATIC_WALL_W:
|
|
|
|
advance_thing(world.get(i).wall->physics);
|
|
|
|
continue;
|
|
|
|
case FLOOR:
|
|
|
|
continue;
|
|
|
|
/*for (int k = 0; k < world.get(i).floor->numPolys; k++) {*/
|
|
|
|
/*advance_thing(world.get(i).floor->polys[k].physics);*/
|
|
|
|
/*}*/
|
|
|
|
/*break;*/
|
|
|
|
case CEILING:
|
|
|
|
continue;
|
|
|
|
/*for (int k = 0; k < world.get(i).floor->numPolys; k++) {*/
|
|
|
|
/*advance_thing(world.get(i).floor->polys[k].physics);*/
|
|
|
|
/*}*/
|
|
|
|
/*break;*/
|
|
|
|
case PROJECTILE:
|
|
|
|
if ((numcols = process_collisions(world.get(i).projectile->physics,
|
|
|
|
&translation))) {
|
|
|
|
world.get(i).projectile->on_collision(world.get(i).projectile);
|
|
|
|
}
|
|
|
|
world.get(i).projectile->on_step(world.get(i).projectile);
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool unique_world_kind(enum world_thing_kind k) {
|
|
|
|
switch (k) {
|
|
|
|
case PLAYER_W:
|
|
|
|
case FLOOR:
|
|
|
|
case CEILING:
|
|
|
|
case ROOM_W:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// grow array of world things if needed
|
|
|
|
void add_to_world(world_thing thing) {
|
|
|
|
thing.nid = world.items.size;
|
|
|
|
world_thing *t = calloc(1, sizeof(world_thing));
|
|
|
|
memcpy(t, &thing, sizeof(world_thing));
|
|
|
|
|
|
|
|
arlst_add(&world.items, t);
|
|
|
|
|
|
|
|
if (unique_world_kind(thing.kind)) {
|
|
|
|
void *ref = arlst_get(&world.items, world.items.size - 1);
|
|
|
|
world.uniques_index[thing.kind] = ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send a projectile from body in direction dir (in degrees) with the force of
|
|
|
|
* sten newtons */
|
|
|
|
void fire_projectile(Body *from, int stren, int dir) {
|
|
|
|
Projectile *proj;
|
|
|
|
get_projectile(&proj);
|
|
|
|
|
|
|
|
double radians = (double)dir * (M_PI / 180);
|
|
|
|
set_motor_newtons(proj->physics, 0, stren * cos(radians),
|
|
|
|
stren * sin(radians));
|
|
|
|
set_motor_timeout(proj->physics, 0, 2);
|
|
|
|
|
|
|
|
world_thing proj_world;
|
|
|
|
proj_world.projectile = proj;
|
|
|
|
proj_world.kind = PROJECTILE;
|
|
|
|
add_to_world(proj_world);
|
|
|
|
}
|
|
|
|
|
|
|
|
void get_room(void) {
|
|
|
|
int floorsize = 100;
|
|
|
|
FloorPoly *polys = generate_floor_simple(floorsize, true, 1300);
|
|
|
|
Floor *floor = calloc(1, sizeof(Floor));
|
|
|
|
floor->polys = polys;
|
|
|
|
floor->numPolys = floorsize;
|
|
|
|
|
|
|
|
FloorPoly *ceil = generate_floor_simple(floorsize, false, 1000);
|
|
|
|
|
|
|
|
// printf("floor: %f %f\n", polys[2].left.x, polys[2].left.y);
|
|
|
|
// printf("ceil: %f %f\n", ceil[2].left.x, ceil[2].left.y);
|
|
|
|
|
|
|
|
Floor* ceiling = calloc(1, sizeof(Floor));
|
|
|
|
ceiling->polys = ceil;
|
|
|
|
ceiling->numPolys = floorsize;
|
|
|
|
|
|
|
|
world_thing ceilingthing;
|
|
|
|
ceilingthing.kind = CEILING;
|
|
|
|
ceilingthing.floor = ceiling;
|
|
|
|
add_to_world(ceilingthing);
|
|
|
|
|
|
|
|
world_thing floorthing;
|
|
|
|
floorthing.kind = FLOOR;
|
|
|
|
floorthing.floor = floor;
|
|
|
|
add_to_world(floorthing);
|
|
|
|
}
|
|
|
|
|
|
|
|
GlobWorld create_world() {
|
|
|
|
GlobWorld c;
|
|
|
|
c.items = new_arlst(100);
|
|
|
|
c.uniques_index = calloc(10, sizeof(world_thing*));
|
|
|
|
c.get = world_getter; // gross
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void startgame(SDL_Renderer * ren) {
|
|
|
|
get_input_map();
|
|
|
|
|
|
|
|
textboxes[TB_LEVEL_CHOOSER].id = TB_LEVEL_CHOOSER;
|
|
|
|
textboxes[TB_LEVEL_CHOOSER].close_callback = new_level_tb_close_callback;
|
|
|
|
|
|
|
|
debug_ren = ren;
|
|
|
|
|
|
|
|
level = rand();
|
|
|
|
|
|
|
|
#ifdef SCORE_SYSTEM
|
|
|
|
save_times_lst = (struct sg_times_list){};
|
|
|
|
load_score_times("saves/times");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
world = create_world();
|
|
|
|
|
|
|
|
SDL_GetRendererOutputSize(ren, &width, &height);
|
|
|
|
|
|
|
|
// player = get_player(200,1300);
|
|
|
|
|
|
|
|
get_floor_ceiling();
|
|
|
|
Vect stpos = *world.uniques_index[ROOM_W]->room->ceil.items[2]->collision_poly;
|
|
|
|
player = get_player(stpos.x, stpos.y - 15);
|
|
|
|
world_thing player_world;
|
|
|
|
|
|
|
|
player_world.kind = PLAYER_W;
|
|
|
|
player_world.player = malloc(sizeof(player));
|
|
|
|
*player_world.player = player;
|
|
|
|
glob_player = player_world.player;
|
|
|
|
|
|
|
|
add_to_world(player_world);
|
|
|
|
|
|
|
|
viewport_pos.x = 700;
|
|
|
|
viewport_pos.y = 0;
|
|
|
|
|
|
|
|
level_timer_update(true);
|
|
|
|
game_paused = 0;
|
|
|
|
viewport_pos.x = player.physics->position.x - width / 2;
|
|
|
|
viewport_pos.y = player.physics->position.y - height / 2;
|
|
|
|
|
|
|
|
|
|
|
|
mute_button.x = 90.0/100 * width;
|
|
|
|
mute_button.y = 85.0/100 * height;
|
|
|
|
mute_button.w = 90;
|
|
|
|
mute_button.h = 90;
|
|
|
|
mute_button.state = mute;
|
|
|
|
mute_button.held = false;
|
|
|
|
|
|
|
|
//get_room();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool aabb_check(float x, float y, float rx, float ry, float w, float h) {
|
|
|
|
return x > rx && x < rx + w && y > ry && y < ry + h;
|
|
|
|
}
|
|
|
|
|
|
|
|
double get_abs(double x,double y) {
|
|
|
|
return (sqrt(x*x + y*y));
|
|
|
|
}
|
|
|
|
|
|
|
|
void walk_player(int x, int y) {
|
|
|
|
|
|
|
|
set_motor_status(player.physics, M_PLAYER_WALK, true);
|
|
|
|
set_motor_newtons(player.physics, M_PLAYER_WALK, 0, 0);
|
|
|
|
// turned off special case for going down for TESTing
|
|
|
|
if (false && y == -1) {
|
|
|
|
add_motor_newtons(glob_player->physics, M_PLAYER_WALK, 0, 100 * 10 * y);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
add_motor_newtons(glob_player->physics, M_PLAYER_WALK, 100 *5* x , 100 * 5 * y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_input_event(SDL_Event event) {
|
|
|
|
SDL_Scancode sc = event.key.keysym.scancode;
|
|
|
|
static bool mouse_down = false;
|
|
|
|
switch (event.type) {
|
|
|
|
case SDL_KEYDOWN:
|
|
|
|
|
|
|
|
if (gameui.currently_bound_textbox) {
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE
|
|
|
|
|| event.key.keysym.scancode == SDL_SCANCODE_DELETE) {
|
|
|
|
delete_from_textbox(gameui.currently_bound_textbox);
|
|
|
|
} else if (event.key.keysym.scancode == SDL_SCANCODE_RETURN
|
|
|
|
|| event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
|
|
|
|
if (gameui.currently_bound_textbox->close_callback) {
|
|
|
|
gameui.currently_bound_textbox->close_callback(
|
|
|
|
gameui.currently_bound_textbox, NULL);
|
|
|
|
}
|
|
|
|
gameui.currently_bound_textbox = NULL;
|
|
|
|
}
|
|
|
|
else if (event.key.keysym.sym >= 32
|
|
|
|
&& event.key.keysym.sym <= 126) {
|
|
|
|
write_to_textbox(gameui.currently_bound_textbox,
|
|
|
|
(char)event.key.keysym.sym);
|
|
|
|
}
|
|
|
|
// dont do anything else when the textbox is bound;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUGMODE
|
|
|
|
if (sc == input_map.player_up) {
|
|
|
|
walk_player(0, -1);
|
|
|
|
} if ( sc == input_map.player_left) {
|
|
|
|
walk_player(-1, 0);
|
|
|
|
} if (sc == input_map.player_down) {
|
|
|
|
walk_player(0, 1);
|
|
|
|
} if (sc == input_map.player_right) {
|
|
|
|
walk_player(1, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (sc == input_map.player_pull_rope) {
|
|
|
|
pull_rope(900);
|
|
|
|
} if (sc == input_map.mute) {
|
|
|
|
mute = !mute;
|
|
|
|
mute_button.state = mute;
|
|
|
|
} if (sc == input_map.pause) {
|
|
|
|
if (in_game)
|
|
|
|
game_paused = !game_paused;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc == input_map.goto_level) {
|
|
|
|
gameui.currently_bound_textbox = textboxes + TB_LEVEL_CHOOSER;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
|
|
if (event.key.keysym.scancode == input_map.player_rope) {
|
|
|
|
delete_rope();
|
|
|
|
}
|
|
|
|
#ifdef DEBUGMODE
|
|
|
|
if (sc == input_map.player_up || sc == input_map.player_down
|
|
|
|
|| sc == input_map.player_left
|
|
|
|
|| sc == input_map.player_right) {
|
|
|
|
set_motor_newtons(player.physics, M_PLAYER_WALK, 0, 0);
|
|
|
|
set_motor_status(player.physics, M_PLAYER_WALK, false);
|
|
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_F10) {
|
|
|
|
next_level();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (sc == input_map.player_pull_rope) {
|
|
|
|
stop_pull_rope();
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
|
|
|
|
if (aabb_check(event.button.x, event.button.y,
|
|
|
|
mute_button.x, mute_button.y, mute_button.w,
|
|
|
|
mute_button.h)) {
|
|
|
|
mute_button.held = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_rope(event.button.x, event.button.y);
|
|
|
|
if (event.button.button == input_map.mouse_attach_rope)
|
|
|
|
mouse_down = true;
|
|
|
|
if (event.button.button == input_map.mouse_attach_rope_pull)
|
|
|
|
pull_rope(900);
|
|
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
|
|
|
|
mute_button.held = false;
|
|
|
|
if (aabb_check(event.button.x, event.button.y,
|
|
|
|
mute_button.x, mute_button.y, mute_button.w,
|
|
|
|
mute_button.y)) {
|
|
|
|
mute = !mute;
|
|
|
|
mute_button.state = mute;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (event.button.button == input_map.mouse_attach_rope) {
|
|
|
|
mouse_down = false;
|
|
|
|
delete_rope();
|
|
|
|
}
|
|
|
|
if (event.button.button == input_map.mouse_attach_rope_pull) {
|
|
|
|
stop_pull_rope();
|
|
|
|
if (!mouse_down) {
|
|
|
|
delete_rope();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* temporary storage variable *n should be initialised to 1 */
|
|
|
|
int get_rand(int *n) {
|
|
|
|
const unsigned c = 11;
|
|
|
|
const unsigned m = (1 << 31) - 1;
|
|
|
|
const unsigned initial_n = 1;
|
|
|
|
const unsigned a = 48271;
|
|
|
|
|
|
|
|
*n = (a * *n + c) % m;
|
|
|
|
return *n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int step(void) {
|
|
|
|
static int first_run = 0;
|
|
|
|
if (!first_run) {
|
|
|
|
start_audio();
|
|
|
|
// play_game_sound(game_sounds.background, -1, BC_MUSIC);
|
|
|
|
first_run = 1;
|
|
|
|
game_paused = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gameui.goto_new_level) {
|
|
|
|
int nevl = gameui.goto_new_level;
|
|
|
|
gameui.goto_new_level = 0;
|
|
|
|
return nevl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (player.physics->position.x > world.uniques_index[ROOM_W]->room
|
|
|
|
->floor.items[world.uniques_index[ROOM_W]->room->floor.numItems - 1]->position.x) {
|
|
|
|
draw_watch.finish_level = true;
|
|
|
|
return level + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!game_paused) {
|
|
|
|
if (player.physics->position.x > world.uniques_index[ROOM_W]->room
|
|
|
|
->floor.items[3]->position.x) {
|
|
|
|
level_timer_update(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_game) {
|
|
|
|
advance_things();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
last_tick = get_now();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|