BosonX/src/game.cpp

231 lines
6.0 KiB
C++

#include "game.h"
#include "level.h"
#include "frame.h"
//===== Player implementation =======//
num player::world_angle() const
{
num arc = space_platform_arc();
num angle = this->platform * arc;
if(this->airborne()) {
/* Time spent rotating; caps at the full rotation time */
num t = num_clamp(this->jump_t, 0, TIME_ROTATION);
angle += this->jump_dir * arc * t / TIME_ROTATION;
}
return angle;
}
vec2 player::world_angle_vector() const
{
num angle = this->world_angle();
return vec2(num_cos(angle), num_sin(angle));
}
vec3 player::pos() const
{
/* Local position without accounting for current platform's angle */
vec3 pos(0, -space_cylinder_quasiradius() + this->height, this->z);
return vec_rotate_around_z(pos, this->world_angle_vector());
}
vec3 player::forward() const
{
return vec3(0, 0, 1);
}
vec3 player::backward() const
{
return -this->forward();
}
vec3 player::up() const
{
return vec_rotate_around_z(vec3(0, 1, 0), this->world_angle_vector());
}
vec3 player::down() const
{
return -this->up();
}
void player::get_anim(struct anim **anim, int *frame)
{
extern struct anim anim_erik_running;
extern struct anim anim_erik_jumping;
num vanim = num(1) + this->energy_percent / 200;
if(this->stance == Running) {
this->frame = this->frame + vanim;
if(this->frame >= anim_erik_running.frame_count)
this->frame = 0;
*anim = &anim_erik_running;
*frame = (int)this->frame;
}
else {
this->frame = this->frame + vanim;
if(this->frame >= anim_erik_jumping.frame_count)
this->frame = 12;
*anim = &anim_erik_jumping;
*frame = (int)this->frame;
}
}
//======= Functions on the game world space =======//
static vec2 _platform_rotations[PLATFORM_COUNT];
static num _cylinder_quasiradius;
num space_cylinder_quasiradius(void)
{
return _cylinder_quasiradius;
}
/* We can't use a constructor function because g++ already generates a call to
the default constructor, which would run after us and override the data. */
void space_init(void)
{
num arc = space_platform_arc();
for(int i = 0; i < PLATFORM_COUNT; i++) {
num angle = arc * i - (arc / 2);
_platform_rotations[i] = vec2(num_cos(angle), num_sin(angle));
}
_cylinder_quasiradius = LEVEL_RADIUS * num_cos(arc / 2);
}
struct prect space_platform_position(struct platform const *p)
{
struct prect r;
vec3 radius(0, -space_cylinder_quasiradius() + p->height, p->z);
int angle_l = p->face;
int angle_r = (p->face + 1) % PLATFORM_COUNT;
r.nl = r.fl = vec_rotate_around_z(radius, _platform_rotations[angle_l]);
r.nr = r.fr = vec_rotate_around_z(radius, _platform_rotations[angle_r]);
r.fl.z += p->length;
r.fr.z += p->length;
return r;
}
struct frect space_block_position(struct platform const *p)
{
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
vec3 r2(0, -space_cylinder_quasiradius() + p->height, p->z);
int angle_l = p->face;
int angle_r = (p->face + 1) % PLATFORM_COUNT;
struct frect f;
f.tr = vec_rotate_around_z(r1, _platform_rotations[angle_r]);
f.tl = vec_rotate_around_z(r1, _platform_rotations[angle_l]);
f.br = vec_rotate_around_z(r2, _platform_rotations[angle_r]);
f.bl = vec_rotate_around_z(r2, _platform_rotations[angle_l]);
return f;
}
struct prect space_block_top_position(struct platform const *p)
{
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
int angle_l = p->face;
int angle_r = (p->face + 1) % PLATFORM_COUNT;
struct prect t;
t.nl = t.fl = vec_rotate_around_z(r1, _platform_rotations[angle_l]);
t.nr = t.fr = vec_rotate_around_z(r1, _platform_rotations[angle_r]);
t.fl.z += p->length;
t.fr.z += p->length;
return t;
}
struct srect space_wall_position(struct platform const *p)
{
vec3 r1(0, -2 * PLATFORM_HEIGHT_STEP, p->z);
vec3 r2(0, -space_cylinder_quasiradius() + p->height, p->z);
struct srect s;
s.nt = s.ft = vec_rotate_around_z(r1, _platform_rotations[p->face]);
s.nb = s.fb = vec_rotate_around_z(r2, _platform_rotations[p->face]);
s.ft.z += p->length;
s.fb.z += p->length;
return s;
}
//======= Game implementation =======//
void game_advance(struct game *game)
{
struct level *level = &game->level;
auto it = level->platform_buffer.begin();
while(it != level->platform_buffer.end()) {
/* If a platform is dead, remove it. We include the camera's distance
in the calculation because we only remove platforms that have been
passed *and* are now invisible. */
if(game->player.z - RENDER_CAMERA_BACK_DISTANCE > it->z + it->length)
it = level->platform_buffer.erase(it);
else
++it;
}
}
static bool standable(struct platform const &p)
{
return p.type != PLATFORM_SEPARATING_WALL
&& p.type != PLATFORM_BLOCK
&& p.type != PLATFORM_BLOCK_TOP;
}
static bool collidable(struct platform const &p)
{
return p.type == PLATFORM_BLOCK_TOP;
}
num game_height_at(struct game *game, num z, int face)
{
num max_h = -KILL_PLANE_RADIUS;
for(auto const &p: game->level.platform_buffer) {
if(standable(p) && p.face == face && z >= p.z && z <= p.z + p.length)
max_h = (max_h >= p.height) ? max_h : p.height;
}
return max_h;
}
struct platform *game_platform_under_player(struct game *game,
struct player const *player)
{
if(player->stance != player::Running)
return NULL;
for(auto &p: game->level.platform_buffer) {
if(standable(p) && player->platform == p.face
&& player->z >= p.z && player->z <= p.z + p.length) {
return &p;
}
}
return NULL;
}
struct platform *game_block_hitting_player(struct game *game,
struct player const *player)
{
for(auto &p: game->level.platform_buffer) {
if(collidable(p) && player->platform == p.face
&& player->z >= p.z && player->z <= p.z + p.length) {
return &p;
}
}
return NULL;
}