417 lines
13 KiB
C
417 lines
13 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_image.h>
|
|
|
|
#include "structure.h"
|
|
#include "base.h"
|
|
#include "cars.h"
|
|
#include "music.h"
|
|
#include "collisions.h"
|
|
|
|
// use after updating the position
|
|
void updateChunk(level* lvl, int nPl) {
|
|
car* c = players[nPl].c;
|
|
if(c->pos.fx < 0.0) {
|
|
c->pos.fx += 1.0*ROOM_SIZE;
|
|
if(c->chx > 0){c->chx -= 1;}
|
|
}
|
|
if(c->pos.fx >= 1.0*ROOM_SIZE) {
|
|
c->pos.fx -= 1.0*ROOM_SIZE;
|
|
if(c->chx < lvl->cols){c->chx += 1;}
|
|
}
|
|
if(c->pos.fy < 0.0) {
|
|
c->pos.fy += 1.0*ROOM_SIZE;
|
|
if(c->chy > 0){c->chy -= 1;}
|
|
}
|
|
if(c->pos.fy >= 1.0*ROOM_SIZE) {
|
|
c->pos.fy -= 1.0*ROOM_SIZE;
|
|
if(c->chy < lvl->lines){c->chy += 1;}
|
|
}
|
|
}
|
|
|
|
ptf get_absolute_coords(int nPl) {
|
|
return (ptf){
|
|
.fx = players[nPl].c->pos.fx + ROOM_SIZE*players[nPl].c->chx,
|
|
.fy = players[nPl].c->pos.fy + ROOM_SIZE*players[nPl].c->chy
|
|
};
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------- //
|
|
// make the player move DT (does not take friction into consideration)
|
|
|
|
// start
|
|
void move_on_START(int nPl) {
|
|
car* c = players[nPl].c;
|
|
c->pos.fx += c->vel.fx*DT;
|
|
c->pos.fy += c->vel.fy*DT;
|
|
if(
|
|
(START_DIR != 3 && c->pos.fx-PLAYER_R <= 0.0 && c->vel.fx < 0.0) ||
|
|
(START_DIR == 3 && c->pos.fx-PLAYER_R <= 0.0 && c->vel.fx < 0.0 && (c->pos.fy-PLAYER_R <= ROOM_SIZE*DIST_EDGE || c->pos.fy+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE)))
|
|
) { // left wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(START_DIR != 1 && c->pos.fx+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fx > 0.0) ||
|
|
(START_DIR == 1 && c->pos.fx+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fx > 0.0 && (c->pos.fy-PLAYER_R <= ROOM_SIZE*DIST_EDGE || c->pos.fy+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE)))
|
|
) { // right wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(START_DIR != 0 && c->pos.fy-PLAYER_R <= 0.0 && c->vel.fy < 0.0) ||
|
|
(START_DIR == 0 && c->pos.fy-PLAYER_R <= 0.0 && c->vel.fy < 0.0 && (c->pos.fx-PLAYER_R <= ROOM_SIZE*DIST_EDGE || c->pos.fx+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE)))
|
|
) { // top wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(START_DIR != 2 && c->pos.fy+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fy > 0.0) ||
|
|
(START_DIR == 2 && c->pos.fy+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fy > 0.0 && (c->pos.fx-PLAYER_R <= ROOM_SIZE*DIST_EDGE || c->pos.fx+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE)))
|
|
) { // bottom wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
}
|
|
|
|
// finish
|
|
void move_on_FINISH(int nPl) {
|
|
car* c = players[nPl].c;
|
|
c->pos.fx += c->vel.fx*DT;
|
|
c->pos.fy += c->vel.fy*DT;
|
|
if(
|
|
(c->pos.fx-PLAYER_R <= 0.0 && c->vel.fx < 0.0)
|
|
) { // left wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(c->pos.fx+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fx > 0.0)
|
|
) { // right wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(c->pos.fy-PLAYER_R <= 0.0 && c->vel.fy < 0.0)
|
|
) { // top wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
if(
|
|
(c->pos.fy+PLAYER_R >= 1.0*ROOM_SIZE && c->vel.fy > 0.0)
|
|
) { // bottom wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
}
|
|
|
|
void move_on_STR_V(int nPl) {
|
|
car* c = players[nPl].c;
|
|
c->pos.fx += c->vel.fx*DT;
|
|
c->pos.fy += c->vel.fy*DT;
|
|
if(c->pos.fx-PLAYER_R <= ROOM_SIZE*DIST_EDGE && c->vel.fx < 0.0) { // left wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
if(c->pos.fx+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE) && c->vel.fx > 0.0) { // right wall
|
|
c->vel.fx *= -1.0;
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->vel.fx *= RESTITUTION_WALL;
|
|
}
|
|
}
|
|
|
|
void move_on_STR_H(int nPl) {
|
|
car* c = players[nPl].c;
|
|
c->pos.fx += c->vel.fx*DT;
|
|
c->pos.fy += c->vel.fy*DT;
|
|
if(c->pos.fy-PLAYER_R <= ROOM_SIZE*DIST_EDGE && c->vel.fy < 0.0) { // top wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
if(c->pos.fy+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE) && c->vel.fy > 0.0) { // bottom wall
|
|
c->vel.fy *= -1.0;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
c->vel.fy *= RESTITUTION_WALL;
|
|
}
|
|
}
|
|
|
|
void move_on_TURN(int nPl, int cenX, int cenY) {
|
|
car* c = players[nPl].c;
|
|
ptf prev = (ptf){.fx = c->pos.fx, .fy = c->pos.fy};
|
|
c->pos.fx += c->vel.fx*DT;
|
|
c->pos.fy += c->vel.fy*DT;
|
|
ptf next = c->pos;
|
|
|
|
double prevDist = distance(prev, (ptf){.fx = 1.0*cenX, .fy = 1.0*cenY});
|
|
double nextDist = distance(next, (ptf){.fx = 1.0*cenX, .fy = 1.0*cenY});
|
|
|
|
// inner circle
|
|
if(prevDist-PLAYER_R > ROOM_SIZE*DIST_EDGE && nextDist-PLAYER_R <= ROOM_SIZE*DIST_EDGE) {
|
|
ptf to_in = normalize((ptf){
|
|
.fx = cenX-prev.fx,
|
|
.fy = cenY-prev.fy
|
|
});
|
|
double inwards = dot(to_in, c->vel);
|
|
c->vel = add(c->vel, (ptf){.fx = -2.0*to_in.fx*inwards, .fy = -2.0*to_in.fy*inwards});
|
|
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
}
|
|
|
|
// in case someone clips (inner)
|
|
if(nextDist-prevDist < 0.0 && prevDist-PLAYER_R < ROOM_SIZE*DIST_EDGE) {
|
|
ptf to_in = normalize((ptf){
|
|
.fx = cenX-prev.fx,
|
|
.fy = cenY-prev.fy
|
|
});
|
|
double inwards = dot(to_in, c->vel);
|
|
c->vel = add(c->vel, (ptf){.fx = -2.0*to_in.fx*inwards, .fy = -2.0*to_in.fy*inwards});
|
|
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
}
|
|
|
|
// outer circle
|
|
if(prevDist+PLAYER_R < ROOM_SIZE*(1.0-DIST_EDGE) && nextDist+PLAYER_R >= ROOM_SIZE*(1.0-DIST_EDGE)) {
|
|
ptf to_out = normalize((ptf){
|
|
.fx = prev.fx-cenX,
|
|
.fy = prev.fy-cenY
|
|
});
|
|
double outwards = dot(to_out, c->vel);
|
|
c->vel = add(c->vel, (ptf){.fx = -2.0*to_out.fx*outwards, .fy = -2.0*to_out.fy*outwards});
|
|
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
}
|
|
|
|
// in case someone clips (outer)
|
|
if(nextDist-prevDist > 0.0 && prevDist-PLAYER_R > ROOM_SIZE*(1.0-DIST_EDGE)) {
|
|
ptf to_in = normalize((ptf){
|
|
.fx = cenX-prev.fx,
|
|
.fy = cenY-prev.fy
|
|
});
|
|
double inwards = dot(to_in, c->vel);
|
|
c->vel = add(c->vel, (ptf){.fx = -2.0*to_in.fx*inwards, .fy = -2.0*to_in.fy*inwards});
|
|
|
|
c->pos.fx += 2.0*c->vel.fx*DT;
|
|
c->pos.fy += 2.0*c->vel.fy*DT;
|
|
}
|
|
}
|
|
|
|
void bumpOtherCars(int nPl) {
|
|
for(int p = 0; p < nPlayers; p++) {
|
|
if(p != nPl && 0==winners[p]) {
|
|
double sep = distance(
|
|
get_absolute_coords(p),
|
|
get_absolute_coords(nPl)
|
|
);
|
|
if(sep <= 2*PLAYER_R) {
|
|
// p_total is multiplied by RESTITUTION_PLAYER
|
|
ptf director = normalize((ptf){
|
|
.fx = players[p].c->pos.fx - players[nPl].c->pos.fx,
|
|
.fy = players[p].c->pos.fy - players[nPl].c->pos.fy
|
|
});
|
|
double speed_P = norm(players[p].c->vel);
|
|
double speed_N = norm(players[nPl].c->vel);
|
|
|
|
players[p].c->vel.fx = director.fx*RESTITUTION_PLAYER*(speed_N);
|
|
players[p].c->vel.fy = director.fy*RESTITUTION_PLAYER*(speed_N);
|
|
|
|
players[nPl].c->vel.fx = -director.fx*RESTITUTION_PLAYER*(speed_P);
|
|
players[nPl].c->vel.fy = -director.fy*RESTITUTION_PLAYER*(speed_P);
|
|
|
|
// snap players away from each other (so they dont overlap)
|
|
while(distance(get_absolute_coords(p),get_absolute_coords(nPl)) <= 2*PLAYER_R) {
|
|
players[p].c->pos.fx += DT*players[p].c->vel.fx;
|
|
players[p].c->pos.fy += DT*players[p].c->vel.fy;
|
|
|
|
players[nPl].c->pos.fx += DT*players[nPl].c->vel.fx;
|
|
players[nPl].c->pos.fy += DT*players[nPl].c->vel.fy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void apply_friction(int nPl) {
|
|
players[nPl].c->vel.fx *= (1.0 - FRICTION*DT);
|
|
players[nPl].c->vel.fy *= (1.0 - FRICTION*DT);
|
|
}
|
|
|
|
double addVX = 0.0;
|
|
double addVY = 0.0;
|
|
void addEnviroSpeed(int nPl) {
|
|
car* c = players[nPl].c;
|
|
|
|
// Wind
|
|
if(WIND_X*WIND_X + WIND_Y*WIND_Y >= 0.001) {
|
|
c->vel.fx += WIND_X*DT;
|
|
c->vel.fx += WIND_Y*DT;
|
|
}
|
|
|
|
// B
|
|
if(B > 0.001) {
|
|
addVX = c->vel.fy*B;
|
|
addVY = -c->vel.fx*B;
|
|
|
|
c->vel.fx += addVX*DT;
|
|
c->vel.fy += addVY*DT;
|
|
}
|
|
}
|
|
|
|
void removeEnviroSpeed(int nPl) {
|
|
// no
|
|
}
|
|
|
|
// -- //
|
|
|
|
// return true iif the car has moved
|
|
bool updateCar(level* lvl, int nPl) {
|
|
if(norm(players[nPl].c->vel) >= DV) {
|
|
switch (lvl->map[players[nPl].c->chy][players[nPl].c->chx]){
|
|
case NONE:
|
|
fprintf(stderr, "[player %d] Is in an empty chunk (at %d %d)\n", nPl, players[nPl].c->chx, players[nPl].c->chy);
|
|
return false;
|
|
|
|
case START:
|
|
addEnviroSpeed(nPl);
|
|
move_on_START(nPl);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case END:
|
|
addEnviroSpeed(nPl);
|
|
move_on_FINISH(nPl);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case STR_V:
|
|
addEnviroSpeed(nPl);
|
|
move_on_STR_V(nPl);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case STR_H:
|
|
addEnviroSpeed(nPl);
|
|
move_on_STR_H(nPl);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case TURN_NE:
|
|
addEnviroSpeed(nPl);
|
|
move_on_TURN(nPl, ROOM_SIZE, 0);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case TURN_NW:
|
|
addEnviroSpeed(nPl);
|
|
move_on_TURN(nPl, 0, 0);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case TURN_SE:
|
|
addEnviroSpeed(nPl);
|
|
move_on_TURN(nPl, ROOM_SIZE, ROOM_SIZE);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
case TURN_SW:
|
|
addEnviroSpeed(nPl);
|
|
move_on_TURN(nPl, 0, ROOM_SIZE);
|
|
removeEnviroSpeed(nPl);
|
|
|
|
bumpOtherCars(nPl);
|
|
apply_friction(nPl);
|
|
updateChunk(lvl, nPl);
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void hasWon(int nPl, int* ranks, int* incr) {
|
|
if(0==winners[nPl]) {
|
|
car* c = players[nPl].c;
|
|
winners[nPl] = ((c->chx == FINISH_CHY && c->chy == FINISH_CHX)?(1):(0));
|
|
if(winners[nPl]==1 && ranks[nPl] == nPlayers) {
|
|
ranks[nPl] = nextRank;
|
|
*incr += 1;
|
|
play_sound("sound/audio/kahoot_10s.wav");
|
|
}
|
|
if(winners[nPl]==1 && remainingTurns == -1) {
|
|
remainingTurns = 4;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void updateWins(int* ranks) {
|
|
int incr = 0;
|
|
for(int p = 0; p < nPlayers; p++) {
|
|
hasWon(p, ranks, &incr);
|
|
}
|
|
nextRank += incr;
|
|
}
|
|
|
|
// return true if at least one car moved
|
|
bool updateCars(level* lvl) {
|
|
bool res = false;
|
|
for(int p = 0; p < nPlayers; p++) {
|
|
if(0==winners[p]) {
|
|
bool rr = updateCar(lvl, p);
|
|
res = res || rr;
|
|
}
|
|
}
|
|
return res;
|
|
} |