|
|
|
@ -471,11 +471,18 @@ static cpBool on_damage(cpArbiter* arb, cpSpace* space, cpDataPointer userData)
|
|
|
|
|
entity_b = cp_shape_entity(b);
|
|
|
|
|
|
|
|
|
|
float damage = V2length(cp_to_v2(cpArbiterTotalImpulse(arb))) * COLLISION_DAMAGE_SCALING;
|
|
|
|
|
|
|
|
|
|
if (entity_a->is_box && entity_a->box_type == BoxExplosive)
|
|
|
|
|
entity_a->damage += 2.0f * EXPLOSION_DAMAGE_THRESHOLD;
|
|
|
|
|
if (entity_b->is_box && entity_b->box_type == BoxExplosive)
|
|
|
|
|
entity_b->damage += 2.0f * EXPLOSION_DAMAGE_THRESHOLD;
|
|
|
|
|
|
|
|
|
|
if (damage > 0.05f)
|
|
|
|
|
{
|
|
|
|
|
// Log("Collision with damage %f\n", damage);
|
|
|
|
|
entity_a->damage += damage;
|
|
|
|
|
entity_b->damage += damage;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// b must be the key passed into the post step removed, the key is cast into its shape
|
|
|
|
@ -515,12 +522,6 @@ V2 grid_com(Entity* grid)
|
|
|
|
|
return cp_to_v2(cpBodyLocalToWorld(grid->body, cpBodyGetCenterOfGravity(grid->body)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V2 entity_pos(Entity* e)
|
|
|
|
|
{
|
|
|
|
|
assert(!e->is_box);
|
|
|
|
|
// @Robust merge entity_pos with box_pos
|
|
|
|
|
return cp_to_v2(cpBodyGetPosition(e->body));
|
|
|
|
|
}
|
|
|
|
|
V2 grid_vel(Entity* grid)
|
|
|
|
|
{
|
|
|
|
|
return cp_to_v2(cpBodyGetVelocity(grid->body));
|
|
|
|
@ -568,15 +569,20 @@ float entity_shape_mass(Entity* box)
|
|
|
|
|
assert(box->shape != NULL);
|
|
|
|
|
return (float)cpShapeGetMass(box->shape);
|
|
|
|
|
}
|
|
|
|
|
V2 box_pos(Entity* box)
|
|
|
|
|
{
|
|
|
|
|
assert(box->is_box);
|
|
|
|
|
return V2add(entity_pos(box_grid(box)), V2rotate(entity_shape_pos(box), entity_rotation(box_grid(box))));
|
|
|
|
|
}
|
|
|
|
|
float box_rotation(Entity* box)
|
|
|
|
|
{
|
|
|
|
|
return (float)cpBodyGetAngle(cpShapeGetBody(box->shape));
|
|
|
|
|
}
|
|
|
|
|
V2 entity_pos(Entity* e)
|
|
|
|
|
{
|
|
|
|
|
if (e->is_box) {
|
|
|
|
|
return V2add(entity_pos(box_grid(e)), V2rotate(entity_shape_pos(e), entity_rotation(box_grid(e))));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
assert(e->body != NULL);
|
|
|
|
|
return cp_to_v2(cpBodyGetPosition(e->body));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct BodyData
|
|
|
|
|
{
|
|
|
|
@ -711,6 +717,7 @@ void ser_player(SerState* ser, Player* p)
|
|
|
|
|
SER_VAR(&p->connected);
|
|
|
|
|
if (p->connected)
|
|
|
|
|
{
|
|
|
|
|
SER_VAR(&p->unlocked_bombs);
|
|
|
|
|
ser_entityid(ser, &p->entity);
|
|
|
|
|
ser_inputframe(ser, &p->input);
|
|
|
|
|
}
|
|
|
|
@ -779,6 +786,14 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e)
|
|
|
|
|
SER_VAR(&e->goldness);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SER_VAR(&e->is_explosion);
|
|
|
|
|
if (e->is_explosion)
|
|
|
|
|
{
|
|
|
|
|
ser_V2(ser, &e->explosion_pos);
|
|
|
|
|
ser_V2(ser, &e->explosion_vel);
|
|
|
|
|
SER_VAR(&e->explosion_progresss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SER_VAR(&e->is_grid);
|
|
|
|
|
if (e->is_grid)
|
|
|
|
|
{
|
|
|
|
@ -790,9 +805,11 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e)
|
|
|
|
|
if (e->is_box)
|
|
|
|
|
{
|
|
|
|
|
SER_VAR(&e->box_type);
|
|
|
|
|
SER_VAR(&e->is_explosion_unlock);
|
|
|
|
|
ser_entityid(ser, &e->next_box);
|
|
|
|
|
ser_entityid(ser, &e->prev_box);
|
|
|
|
|
SER_VAR(&e->compass_rotation);
|
|
|
|
|
SER_VAR(&e->indestructible);
|
|
|
|
|
SER_VAR(&e->thrust);
|
|
|
|
|
SER_VAR(&e->wanted_thrust);
|
|
|
|
|
SER_VAR(&e->energy_used);
|
|
|
|
@ -872,7 +889,8 @@ void ser_server_to_client(SerState* ser, ServerToClient* s)
|
|
|
|
|
Entity* e = &gs->entities[next_index];
|
|
|
|
|
e->exists = true;
|
|
|
|
|
ser_entity(ser, gs, e);
|
|
|
|
|
gs->cur_next_entity = (unsigned int)max(gs->cur_next_entity, next_index + 1);
|
|
|
|
|
unsigned int possible_next_index = (unsigned int)(next_index + 1);
|
|
|
|
|
gs->cur_next_entity = gs->cur_next_entity < possible_next_index ? possible_next_index : gs->cur_next_entity;
|
|
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < gs->cur_next_entity; i++)
|
|
|
|
|
{
|
|
|
|
@ -955,6 +973,34 @@ Entity* closest_to_point_in_radius(GameState* gs, V2 point, float radius)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float cur_explosion_damage = 0.0f;
|
|
|
|
|
static V2 explosion_origin = { 0 };
|
|
|
|
|
static void explosion_callback_func(cpShape* shape, cpContactPointSet* points, void* data)
|
|
|
|
|
{
|
|
|
|
|
GameState* gs = (GameState*)data;
|
|
|
|
|
cp_shape_entity(shape)->damage += cur_explosion_damage;
|
|
|
|
|
Entity* parent = get_entity(gs, cp_shape_entity(shape)->shape_parent_entity);
|
|
|
|
|
V2 from_pos = entity_pos(cp_shape_entity(shape));
|
|
|
|
|
V2 impulse = V2scale(V2normalize(V2sub(from_pos, explosion_origin)), EXPLOSION_PUSH_STRENGTH);
|
|
|
|
|
assert(parent->body != NULL);
|
|
|
|
|
cpBodyApplyImpulseAtWorldPoint(parent->body, v2_to_cp(impulse), v2_to_cp(from_pos));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_explosion(GameState* gs, Entity* explosion, float dt)
|
|
|
|
|
{
|
|
|
|
|
cur_explosion_damage = dt * EXPLOSION_DAMAGE_PER_SEC;
|
|
|
|
|
explosion_origin = explosion->explosion_pos;
|
|
|
|
|
|
|
|
|
|
cpBody* tmpbody = cpBodyNew(0.0f, 0.0f);
|
|
|
|
|
cpShape* circle = cpCircleShapeNew(tmpbody, EXPLOSION_RADIUS, v2_to_cp(explosion_origin));
|
|
|
|
|
|
|
|
|
|
cpSpaceShapeQuery(gs->space, circle, explosion_callback_func, (void*)gs);
|
|
|
|
|
|
|
|
|
|
cpShapeFree(circle);
|
|
|
|
|
cpBodyFree(tmpbody);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V2 box_facing_vector(Entity* box)
|
|
|
|
|
{
|
|
|
|
|
assert(box->is_box);
|
|
|
|
@ -1002,6 +1048,53 @@ bool possibly_use_energy(GameState* gs, Entity* grid, float wanted_energy)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void entity_ensure_in_orbit(Entity* e)
|
|
|
|
|
{
|
|
|
|
|
cpVect pos = v2_to_cp(V2sub(entity_pos(e), SUN_POS));
|
|
|
|
|
cpFloat r = cpvlength(pos);
|
|
|
|
|
cpFloat v = cpfsqrt(SUN_GRAVITY_STRENGTH / r) / r;
|
|
|
|
|
cpBodySetVelocity(e->body, cpvmult(cpvperp(pos), v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EntityID create_spacestation(GameState* gs)
|
|
|
|
|
{
|
|
|
|
|
#define BOX_AT_TYPE(grid, pos, type) { Entity* box = new_entity(gs); box_create(gs, box, grid, pos); box->box_type = type; box->indestructible = indestructible; }
|
|
|
|
|
#define BOX_AT(grid, pos) BOX_AT_TYPE(grid, pos, BoxHullpiece)
|
|
|
|
|
|
|
|
|
|
bool indestructible = false;
|
|
|
|
|
Entity* grid = new_entity(gs);
|
|
|
|
|
grid_create(gs, grid);
|
|
|
|
|
entity_set_pos(grid, (V2) { -150.0f, 0.0f });
|
|
|
|
|
entity_ensure_in_orbit(grid);
|
|
|
|
|
Entity* explosion_box = new_entity(gs);
|
|
|
|
|
box_create(gs, explosion_box, grid, (V2) { 0 });
|
|
|
|
|
explosion_box->is_explosion_unlock = true;
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE, 0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*2, 0 }), BoxHullpiece);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*3, 0 }), BoxHullpiece);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*4, 0 }), BoxHullpiece);
|
|
|
|
|
|
|
|
|
|
indestructible = true;
|
|
|
|
|
for (float y = -BOX_SIZE * 5.0; y <= BOX_SIZE * 5.0; y += BOX_SIZE)
|
|
|
|
|
{
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { BOX_SIZE*5.0, y }), BoxHullpiece);
|
|
|
|
|
}
|
|
|
|
|
for (float x = -BOX_SIZE * 5.0; x <= BOX_SIZE * 5.0; x += BOX_SIZE)
|
|
|
|
|
{
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { x, BOX_SIZE*5.0 }), BoxHullpiece);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { x, -BOX_SIZE*5.0 }), BoxHullpiece);
|
|
|
|
|
}
|
|
|
|
|
indestructible = false;
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*5.0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*3.0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, BOX_SIZE*1.0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*2.0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*3.0 }), BoxExplosive);
|
|
|
|
|
BOX_AT_TYPE(grid, ((V2) { -BOX_SIZE*6.0, -BOX_SIZE*5.0 }), BoxExplosive);
|
|
|
|
|
|
|
|
|
|
return get_id(gs, grid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void process(GameState* gs, float dt)
|
|
|
|
|
{
|
|
|
|
|
assert(gs->space != NULL);
|
|
|
|
@ -1010,26 +1103,20 @@ void process(GameState* gs, float dt)
|
|
|
|
|
gs->time += dt;
|
|
|
|
|
|
|
|
|
|
// process input
|
|
|
|
|
for (int i = 0; i < MAX_PLAYERS; i++)
|
|
|
|
|
PLAYERS_ITER(gs->players, player)
|
|
|
|
|
{
|
|
|
|
|
struct Player* player = &gs->players[i];
|
|
|
|
|
if (!player->connected)
|
|
|
|
|
continue;
|
|
|
|
|
Entity* p = get_entity(gs, player->entity);
|
|
|
|
|
if (p == NULL)
|
|
|
|
|
{
|
|
|
|
|
p = new_entity(gs);
|
|
|
|
|
create_player(gs, p);
|
|
|
|
|
player->entity = get_id(gs, p);
|
|
|
|
|
cpVect pos = v2_to_cp(V2sub(entity_pos(p), SUN_POS));
|
|
|
|
|
cpFloat r = cpvlength(pos);
|
|
|
|
|
cpFloat v = cpfsqrt(SUN_GRAVITY_STRENGTH / r) / r;
|
|
|
|
|
cpBodySetVelocity(p->body, cpvmult(cpvperp(pos), v));
|
|
|
|
|
entity_ensure_in_orbit(p);
|
|
|
|
|
}
|
|
|
|
|
assert(p->is_player);
|
|
|
|
|
|
|
|
|
|
#ifdef INFINITE_RESOURCES
|
|
|
|
|
p->spice_taken_away = 0.0f;
|
|
|
|
|
p->damage = 0.0f;
|
|
|
|
|
#endif
|
|
|
|
|
// update gold win condition
|
|
|
|
|
if (V2length(V2sub(cp_to_v2(cpBodyGetPosition(p->body)), gs->goldpos)) < GOLD_COLLECT_RADIUS)
|
|
|
|
@ -1066,7 +1153,7 @@ void process(GameState* gs, float dt)
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
V2 pilot_seat_exit_spot = V2add(box_pos(the_seat), V2scale(box_facing_vector(the_seat), BOX_SIZE));
|
|
|
|
|
V2 pilot_seat_exit_spot = V2add(entity_pos(the_seat), V2scale(box_facing_vector(the_seat), BOX_SIZE));
|
|
|
|
|
cpBodySetPosition(p->body, v2_to_cp(pilot_seat_exit_spot));
|
|
|
|
|
cpBodySetVelocity(p->body, v2_to_cp(player_vel(gs, p)));
|
|
|
|
|
the_seat->player_who_is_inside_of_me = (EntityID){ 0 };
|
|
|
|
@ -1095,10 +1182,10 @@ void process(GameState* gs, float dt)
|
|
|
|
|
{
|
|
|
|
|
assert(seat_inside_of->is_box);
|
|
|
|
|
cpShapeSetFilter(p->shape, CP_SHAPE_FILTER_NONE); // no collisions while in a seat
|
|
|
|
|
cpBodySetPosition(p->body, v2_to_cp(box_pos(seat_inside_of)));
|
|
|
|
|
cpBodySetPosition(p->body, v2_to_cp(entity_pos(seat_inside_of)));
|
|
|
|
|
|
|
|
|
|
// set thruster thrust from movement
|
|
|
|
|
if(seat_inside_of->box_type == BoxCockpit) {
|
|
|
|
|
if (seat_inside_of->box_type == BoxCockpit) {
|
|
|
|
|
Entity* g = get_entity(gs, seat_inside_of->shape_parent_entity);
|
|
|
|
|
|
|
|
|
|
V2 target_direction = { 0 };
|
|
|
|
@ -1132,9 +1219,12 @@ void process(GameState* gs, float dt)
|
|
|
|
|
if (nearest != NULL)
|
|
|
|
|
{
|
|
|
|
|
Entity* cur_box = cp_shape_entity(nearest);
|
|
|
|
|
Entity* cur_grid = cp_body_entity(cpShapeGetBody(nearest));
|
|
|
|
|
p->damage -= DAMAGE_TO_PLAYER_PER_BLOCK*((BATTERY_CAPACITY - cur_box->energy_used)/BATTERY_CAPACITY);
|
|
|
|
|
grid_remove_box(gs, cur_grid, cur_box);
|
|
|
|
|
if (!cur_box->indestructible)
|
|
|
|
|
{
|
|
|
|
|
Entity* cur_grid = cp_body_entity(cpShapeGetBody(nearest));
|
|
|
|
|
p->damage -= DAMAGE_TO_PLAYER_PER_BLOCK * ((BATTERY_CAPACITY - cur_box->energy_used) / BATTERY_CAPACITY);
|
|
|
|
|
grid_remove_box(gs, cur_grid, cur_box);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (target_grid == NULL)
|
|
|
|
|
{
|
|
|
|
@ -1169,17 +1259,34 @@ void process(GameState* gs, float dt)
|
|
|
|
|
p->damage = clamp01(p->damage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(get_entity(gs, gs->cur_spacestation) == NULL)
|
|
|
|
|
{
|
|
|
|
|
gs->cur_spacestation = create_spacestation(gs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// process entities
|
|
|
|
|
for (size_t i = 0; i < gs->cur_next_entity; i++) {
|
|
|
|
|
Entity* e = &gs->entities[i];
|
|
|
|
|
if (!e->exists)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (e->is_explosion_unlock)
|
|
|
|
|
{
|
|
|
|
|
PLAYERS_ITER(gs->players, player)
|
|
|
|
|
{
|
|
|
|
|
Entity* player_entity = get_entity(gs, player->entity);
|
|
|
|
|
if (player_entity != NULL && V2length(V2sub(entity_pos(player_entity), entity_pos(e))) < GOLD_UNLOCK_RADIUS)
|
|
|
|
|
{
|
|
|
|
|
player->unlocked_bombs = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e->body != NULL)
|
|
|
|
|
{
|
|
|
|
|
cpVect p = cpvsub(cpBodyGetPosition(e->body), v2_to_cp(SUN_POS));
|
|
|
|
|
cpFloat sqdist = cpvlengthsq(p);
|
|
|
|
|
if(sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN*INSTANT_DEATH_DISTANCE_FROM_SUN))
|
|
|
|
|
if (sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN * INSTANT_DEATH_DISTANCE_FROM_SUN))
|
|
|
|
|
{
|
|
|
|
|
entity_destroy(gs, e);
|
|
|
|
|
continue;
|
|
|
|
@ -1194,8 +1301,27 @@ void process(GameState* gs, float dt)
|
|
|
|
|
cpBodyUpdateVelocity(e->body, g, 1.0f, dt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e->is_explosion)
|
|
|
|
|
{
|
|
|
|
|
e->explosion_progresss += dt;
|
|
|
|
|
e->explosion_pos = V2add(e->explosion_pos, V2scale(e->explosion_vel, dt));
|
|
|
|
|
do_explosion(gs, e, dt);
|
|
|
|
|
if (e->explosion_progresss >= EXPLOSION_TIME)
|
|
|
|
|
{
|
|
|
|
|
entity_destroy(gs, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e->is_box)
|
|
|
|
|
{
|
|
|
|
|
if (e->box_type == BoxExplosive && e->damage >= EXPLOSION_DAMAGE_THRESHOLD)
|
|
|
|
|
{
|
|
|
|
|
Entity* explosion = new_entity(gs);
|
|
|
|
|
explosion->is_explosion = true;
|
|
|
|
|
explosion->explosion_pos = entity_pos(e);
|
|
|
|
|
explosion->explosion_vel = grid_vel(box_grid(e));
|
|
|
|
|
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
|
|
|
|
|
}
|
|
|
|
|
if (e->damage >= 1.0f)
|
|
|
|
|
{
|
|
|
|
|
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
|
|
|
|
@ -1208,7 +1334,7 @@ void process(GameState* gs, float dt)
|
|
|
|
|
BOXES_ITER(gs, cur, e)
|
|
|
|
|
{
|
|
|
|
|
if (cur->box_type == BoxSolarPanel) {
|
|
|
|
|
cur->sun_amount = clamp01(V2dot(box_facing_vector(cur), V2normalize(V2sub(SUN_POS, box_pos(cur)))));
|
|
|
|
|
cur->sun_amount = clamp01(V2dot(box_facing_vector(cur), V2normalize(V2sub(SUN_POS, entity_pos(cur)))));
|
|
|
|
|
energy_to_add += cur->sun_amount * SOLAR_ENERGY_PER_SECOND * dt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1237,7 +1363,7 @@ void process(GameState* gs, float dt)
|
|
|
|
|
if (possibly_use_energy(gs, e, energy_to_consume))
|
|
|
|
|
{
|
|
|
|
|
cur->thrust = cur->wanted_thrust;
|
|
|
|
|
cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(box_pos(cur)));
|
|
|
|
|
cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(entity_pos(cur)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (cur->box_type == BoxMedbay)
|
|
|
|
@ -1245,7 +1371,7 @@ void process(GameState* gs, float dt)
|
|
|
|
|
Entity* potential_meatbag_to_heal = get_entity(gs, cur->player_who_is_inside_of_me);
|
|
|
|
|
if (potential_meatbag_to_heal != NULL)
|
|
|
|
|
{
|
|
|
|
|
float energy_to_recharge = min(potential_meatbag_to_heal->damage, PLAYER_ENERGY_RECHARGE_PER_SECOND * dt);
|
|
|
|
|
float energy_to_recharge = fminf(potential_meatbag_to_heal->damage, PLAYER_ENERGY_RECHARGE_PER_SECOND * dt);
|
|
|
|
|
if (possibly_use_energy(gs, e, energy_to_recharge))
|
|
|
|
|
{
|
|
|
|
|
potential_meatbag_to_heal->damage -= energy_to_recharge;
|
|
|
|
|