RogueLife/src/comp/mechanical.c

129 lines
3.8 KiB
C

#include "comp/entity.h"
#include "comp/mechanical.h"
#include "comp/physical.h"
#include "comp/fighter.h"
#include <gint/defs/util.h>
// Movement functions
void mechanical_move(entity_t *e, vec2 direction, fixed_t dt, map_t const *map)
{
physical_t *p = getcomp(e, physical);
mechanical_t *m = getcomp(e, mechanical);
fighter_t *f = getcomp(e, fighter);
mechanical_limits_t const *limits = m->limits;
direction = fnormalize(direction);
bool stunned = f && f->stun_delay > 0;
/* Determine facing */
int facing = -1;
bool horz = abs(direction.x) >= abs(direction.y);
if(direction.x == 0 && direction.y == 0) facing = -1;
else if( horz && direction.x >= 0) facing = RIGHT;
else if( horz && direction.x <= 0) facing = LEFT;
else if(!horz && direction.y <= 0) facing = UP;
else if(!horz && direction.y >= 0) facing = DOWN;
/* Targeted speed, with two components: dash motion and walk motion */
vec2 target = { 0, 0 };
if(m->dash > 0) {
/* The dash speed is set to one direction and cannot changed */
vec2 dir = fdir(m->dash_facing);
target.x += fmul(limits->dash_speed, dir.x);
target.y += fmul(limits->dash_speed, dir.y);
}
if(facing >= 0 && !stunned) {
/* Walking speed can be directed anywhere, anytime */
target.x += fmul(limits->max_speed, direction.x);
target.y += fmul(limits->max_speed, direction.y);
}
/* Friction from environment */
fixed_t friction_x = fmul(fmul(-target.x, limits->friction), dt);
fixed_t friction_y = fmul(fmul(-target.y, limits->friction), dt);
/* Get there exponentially fast */
m->vx = target.x + friction_x + m->vdx;
m->vy = target.y + friction_y + m->vdy;
/* Round very small speeds (smaller than 1/256 tiles/s) to 0 */
if(m->vx >= -256 && m->vx <= 255) m->vx = 0;
if(m->vy >= -256 && m->vy <= 255) m->vy = 0;
/* Decrement time left on the dash */
m->dash = max(m->dash - dt, fix(0.0));
if(facing >= 0)
p->facing = facing;
fixed_t new_x = p->x + fmul(m->vx, dt);
fixed_t new_y = p->y + fmul(m->vy, dt);
rect new_hitbox = rect_translate(p->hitbox, (vec2){ new_x, new_y });
// TODO ECS: New collision/ejection system based on teleports
if(!map_collides(map, new_hitbox)) {
if(f && f->current_attack && f->attack_follows_movement) {
physical_t *attack = getcomp(f->current_attack, physical);
attack->x += (new_x - p->x);
attack->y += (new_y - p->y);
}
p->x = new_x;
p->y = new_y;
}
/* TODO: Without acceleration, the movement model is broken */
m->vdx = fmul(m->vdx, fix(0.8));
m->vdy = fmul(m->vdy, fix(0.8));
}
void mechanical_move4(entity_t *e, int direction, fixed_t dt, map_t const *map)
{
return mechanical_move(e, fdir(direction), dt, map);
}
void mechanical_dash(entity_t *e, int direction)
{
mechanical_t *m = getcomp(e, mechanical);
if(m->dash != 0)
return;
m->dash = m->limits->dash_duration;
m->dash_facing = direction;
}
// Information functions
bool mechanical_moving(entity_t const *e)
{
mechanical_t *m = getcomp(e, mechanical);
return (m->vx != 0) || (m->vy != 0);
}
bool mechanical_dashing(entity_t const *e)
{
physical_t *p = getcomp(e, physical);
mechanical_t *m = getcomp(e, mechanical);
mechanical_limits_t const *limits = m->limits;
/* True only if the direction of the dash movement is preserved */
if(p->facing != m->dash_facing)
return false;
/* True during initial propulsion */
if(m->dash > 0)
return true;
/* Also true as long as 1.5x over-speed is maintained */
fixed_t cur_v2 = fmul(m->vx, m->vx) + fmul(m->vy, m->vy);
fixed_t max_v2 = fmul(limits->max_speed, limits->max_speed);
if(2 * cur_v2 > 3 * max_v2)
return true;
return false;
}