1
1
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1908 lines
54 KiB

#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;
}