Compare commits

...

13 Commits

@ -71,6 +71,7 @@
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<EnableASAN>true</EnableASAN>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

@ -6,7 +6,7 @@
#define SERVER_PORT 2551
// must be unsigned integer
#define GIT_RELEASE_TAG 24
#define GIT_RELEASE_TAG 25
#ifdef DEBUG
@ -14,16 +14,17 @@
#define ASSERT_DO_POPUP_AND_CRASH
// #define SERVER_ADDRESS "207.246.80.160"
// #define PROFILING
#define PROFILING
// #define DEBUG_RENDERING
// #define DEBUG_WORLD
#define DEBUG_WORLD
#define UNLOCK_ALL
#define TIME_BETWEEN_WORLD_SAVE 1000000.0f
#define INFINITE_RESOURCES
// #define TIME_BETWEEN_WORLD_SAVE 1.0f
// #define INFINITE_RESOURCES
#define DEBUG_TOOLS
#define CHIPMUNK_INTEGRITY_CHECK
// #define FAT_THRUSTERS
// #define NO_GRAVITY
#define NO_GRAVITY
// #define NO_SUNS
#else

Binary file not shown.

@ -57,12 +57,6 @@ fromUTF8(
#endif // win32
#endif // ASSERT_DO_POPUP_AND_CRASH
enum
{
PLAYERS = 1 << 0,
BOXES = 1 << 1,
};
FILE *log_file = NULL;
void quit_with_popup(const char *message_utf8, const char *title_utf8)
@ -294,9 +288,9 @@ typedef struct QueryResult
cpVect pointA;
cpVect pointB;
} QueryResult;
static THREADLOCAL char query_result_data[128 * sizeof(QueryResult)] = {0};
static THREADLOCAL char query_result_data[QUEUE_SIZE_FOR_ELEMENTS(sizeof(QueryResult), 128)] = {0};
// the data starts off NULL, on the first call sets it to result data
static THREADLOCAL Queue query_result = {.data_length = 128 * sizeof(QueryResult), .element_size = sizeof(cpShape *)};
static THREADLOCAL Queue query_result = {0};
static void shape_query_callback(cpShape *shape, cpContactPointSet *points, void *data)
{
@ -315,7 +309,7 @@ static void shape_query_callback(cpShape *shape, cpContactPointSet *points, void
// shapes are pushed to query result
static void shape_query(cpSpace *space, cpShape *shape)
{
query_result.data = query_result_data;
queue_init(&query_result, sizeof(QueryResult), query_result_data, ARRLEN(query_result_data));
queue_clear(&query_result);
cpSpaceShapeQuery(space, shape, shape_query_callback, NULL);
}
@ -352,7 +346,7 @@ LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher)
{
cpShape *cur_shape = res->shape;
Entity *other = cp_shape_entity(cur_shape);
flight_assert(other->is_box || other->is_player || other->is_missile);
flight_assert(other->is_box || other->is_player || other->is_missile || other->is_orb);
cpVect to = cpvsub(entity_pos(other), entity_pos(launcher));
bool should_attack = true;
@ -360,6 +354,8 @@ LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher)
should_attack = false;
if (other->owning_squad == launcher->owning_squad)
should_attack = false;
if (other->is_orb)
should_attack = true;
if (should_attack && cpvlength(to) < nearest_dist)
{
@ -532,14 +528,26 @@ void entity_set_rotation(Entity *e, double rot)
void entity_set_pos(Entity *e, cpVect pos)
{
flight_assert(e->is_grid);
flight_assert(e->body != NULL);
cpBodySetPosition(e->body, (pos));
}
// IMPORTANT: all shapes must exist in one of these categories, as by default chipmunk assigns an object
// to be in every category. Which is bad because that doesn't make sense for entities
enum
{
DEFAULT = 1 << 0,
BOXES = 1 << 1,
};
static const cpShapeFilter FILTER_ONLY_BOXES = {CP_NO_GROUP, BOXES, BOXES};
static const cpShapeFilter FILTER_BOXES = {CP_NO_GROUP, DEFAULT | BOXES, CP_ALL_CATEGORIES};
static const cpShapeFilter FILTER_DEFAULT = {CP_NO_GROUP, DEFAULT, CP_ALL_CATEGORIES};
#define PLAYER_FILTER FILTER_DEFAULT
// size is (1/2 the width, 1/2 the height)
void create_rectangle_shape(GameState *gs, Entity *e, Entity *parent, cpVect pos, cpVect size, double mass)
{
// @Robust remove this garbage
if (e->shape != NULL)
{
cpSpaceRemoveShape(gs->space, e->shape);
@ -561,9 +569,18 @@ void create_rectangle_shape(GameState *gs, Entity *e, Entity *parent, cpVect pos
cpShapeSetUserData(e->shape, (void *)e);
cpShapeSetMass(e->shape, mass);
cpSpaceAddShape(gs->space, e->shape);
cpShapeSetFilter(e->shape, FILTER_DEFAULT);
}
void create_circle_shape(GameState *gs, Entity *e, double radius)
{
e->shape = cpCircleShapeNew(e->body, ORB_RADIUS, cpv(0, 0));
e->shape_parent_entity = get_id(gs, e);
e->shape_radius = radius;
cpShapeSetMass(e->shape, ORB_MASS);
cpShapeSetUserData(e->shape, (void *)e);
cpSpaceAddShape(gs->space, e->shape);
cpShapeSetFilter(e->shape, FILTER_DEFAULT);
}
#define PLAYER_SHAPE_FILTER cpShapeFilterNew(CP_NO_GROUP, PLAYERS, CP_ALL_CATEGORIES)
void create_player(Player *player)
{
@ -583,6 +600,14 @@ void create_player(Player *player)
#endif
}
void create_orb(GameState *gs, Entity *e)
{
create_body(gs, e);
create_circle_shape(gs, e, ORB_RADIUS);
e->is_circle_shape = true;
e->is_orb = true;
}
void create_missile(GameState *gs, Entity *e)
{
create_body(gs, e);
@ -593,10 +618,11 @@ void create_missile(GameState *gs, Entity *e)
void create_player_entity(GameState *gs, Entity *e)
{
e->is_player = true;
e->always_visible = true;
e->no_save_to_disk = true;
create_body(gs, e);
create_rectangle_shape(gs, e, e, (cpVect){0}, cpvmult(PLAYER_SIZE, 0.5), PLAYER_MASS);
cpShapeSetFilter(e->shape, PLAYER_SHAPE_FILTER);
cpShapeSetFilter(e->shape, PLAYER_FILTER);
}
void box_add_to_boxes(GameState *gs, Entity *grid, Entity *box_to_add)
@ -622,7 +648,7 @@ void box_create(GameState *gs, Entity *new_box, Entity *grid, cpVect pos)
create_rectangle_shape(gs, new_box, grid, pos, (cpVect){halfbox, halfbox}, 1.0);
cpShapeSetFilter(new_box->shape, cpShapeFilterNew(CP_NO_GROUP, BOXES, CP_ALL_CATEGORIES));
cpShapeSetFilter(new_box->shape, FILTER_BOXES);
box_add_to_boxes(gs, grid, new_box);
}
@ -969,9 +995,11 @@ Entity *box_grid(Entity *box)
return (Entity *)cpBodyGetUserData(cpShapeGetBody(box->shape));
}
// in local space
cpVect entity_shape_pos(Entity *box)
cpVect entity_shape_pos(Entity *e)
{
return (cpShapeGetCenterOfGravity(box->shape));
flight_assert(e != NULL);
flight_assert(e->shape != NULL);
return (cpShapeGetCenterOfGravity(e->shape));
}
double entity_shape_mass(Entity *box)
{
@ -1204,6 +1232,7 @@ SerMaybeFailure ser_fV2(SerState *ser, cpVect *var)
SerMaybeFailure ser_f(SerState *ser, double *d)
{
float f;
if (ser->serializing)
f = (float)*d;
@ -1211,6 +1240,15 @@ SerMaybeFailure ser_f(SerState *ser, double *d)
SER_ASSERT(!isnan(f));
*d = f;
return ser_ok;
// if you're ever sketched out by floating point precision you can use this to test...
/* double f;
if (ser->serializing)
f = (double)*d;
SER_VAR(&f);
SER_ASSERT(!isnan(f));
*d = f;
return ser_ok;*/
}
SerMaybeFailure ser_bodydata(SerState *ser, struct BodyData *data)
@ -1305,10 +1343,18 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
bool has_shape = ser->serializing && e->shape != NULL;
SER_VAR(&has_shape);
if (has_shape)
{
SER_MAYBE_RETURN(ser_fV2(ser, &e->shape_size));
SER_VAR(&e->is_circle_shape);
if (e->is_circle_shape)
{
SER_MAYBE_RETURN(ser_f(ser, &e->shape_radius));
}
else
{
SER_MAYBE_RETURN(ser_fV2(ser, &e->shape_size));
}
SER_MAYBE_RETURN(ser_entityid(ser, &e->shape_parent_entity));
Entity *parent = get_entity(gs, e->shape_parent_entity);
SER_ASSERT(parent != NULL);
@ -1334,7 +1380,14 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_VAR(&filter.mask);
if (!ser->serializing)
{
create_rectangle_shape(gs, e, parent, shape_pos, e->shape_size, shape_mass);
if (e->is_circle_shape)
{
create_circle_shape(gs, e, e->shape_radius);
}
else
{
create_rectangle_shape(gs, e, parent, shape_pos, e->shape_size, shape_mass);
}
cpShapeSetFilter(e->shape, filter);
}
}
@ -1382,12 +1435,17 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_MAYBE_RETURN(ser_entityid(ser, &e->boxes));
}
SER_VAR(&e->is_missile)
SER_VAR(&e->is_missile);
if (e->is_missile)
{
SER_MAYBE_RETURN(ser_f(ser, &e->time_burned_for));
}
SER_VAR(&e->is_orb);
if (e->is_orb)
{
}
SER_VAR(&e->is_box);
if (e->is_box)
{
@ -1409,6 +1467,9 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
SER_MAYBE_RETURN(ser_entityid(ser, &e->player_who_is_inside_of_me));
break;
case BoxThruster:
SER_MAYBE_RETURN(ser_f(ser, &e->thrust));
SER_MAYBE_RETURN(ser_f(ser, &e->wanted_thrust));
break;
case BoxGyroscope:
SER_MAYBE_RETURN(ser_f(ser, &e->thrust));
SER_MAYBE_RETURN(ser_f(ser, &e->wanted_thrust));
@ -1906,7 +1967,6 @@ static bool scanner_filter(Entity *e)
static void do_explosion(GameState *gs, Entity *explosion, double dt)
{
double cur_explosion_damage = dt * EXPLOSION_DAMAGE_PER_SEC;
cpVect explosion_origin = explosion->explosion_pos;
double explosion_push_strength = explosion->explosion_push_strength;
@ -2021,9 +2081,14 @@ bool batteries_have_capacity_for(GameState *gs, Entity *grid, double *energy_lef
return false;
}
// returns any energy unable to burn
// returns any effectiveness
double batteries_use_energy(GameState *gs, Entity *grid, double *energy_left_over, double energy_to_use)
{
if (energy_to_use == 0.0)
{
return 1.0;
}
double energy_wanting_to_use = energy_to_use;
if (*energy_left_over > 0.0)
{
double energy_to_use_from_leftover = fmin(*energy_left_over, energy_to_use);
@ -2039,10 +2104,13 @@ double batteries_use_energy(GameState *gs, Entity *grid, double *energy_left_ove
battery->energy_used += energy_to_burn_from_this_battery;
energy_to_use -= energy_to_burn_from_this_battery;
if (energy_to_use <= 0.0)
return 0.0;
return 1.0;
}
}
return energy_to_use;
double to_return = 1.0 - (energy_to_use / energy_wanting_to_use);
flight_assert(to_return >= 0.0);
flight_assert(to_return <= 1.0);
return to_return;
}
double sun_dist_no_gravity(Entity *sun)
@ -2233,6 +2301,17 @@ void create_initial_world(GameState *gs)
}
#ifndef DEBUG_WORLD
Log("Creating release world\n");
#define ORB_AT(pos) \
{ \
Entity *orb = new_entity(gs); \
create_orb(gs, orb); \
entity_set_pos(orb, pos); \
}
for (int x = -10; x > -1000; x -= 20)
{
ORB_AT(cpv(x, 0.0));
}
create_bomb_station(gs, (cpVect){800.0, 800.0}, BoxExplosive);
// create_hard_shell_station(gs, (cpVect){800.0, 400.0}, BoxGyroscope);
create_bomb_station(gs, (cpVect){800.0, -800.0}, BoxCloaking);
@ -2240,6 +2319,7 @@ void create_initial_world(GameState *gs)
create_hard_shell_station(gs, (cpVect){-7000.0, 200.0}, BoxMerge);
#else
Log("Creating debug world\n");
// pos, mass, radius
create_bomb_station(gs, (cpVect){-5.0, 0.0}, BoxExplosive);
create_bomb_station(gs, (cpVect){0.0, 5.0}, BoxGyroscope);
@ -2302,6 +2382,14 @@ void body_integrity_check(cpBody *body, void *data)
flight_assert(cp_body_entity(body)->exists);
flight_assert(cp_body_entity(body)->body == body);
}
void player_get_in_seat(GameState *gs, Player *player, Entity *seat)
{
Entity *p = get_entity(gs, player->entity);
p->currently_inside_of_box = get_id(gs, seat);
seat->player_who_is_inside_of_me = get_id(gs, p);
if (seat->box_type == BoxMedbay)
player->last_used_medbay = p->currently_inside_of_box;
}
void process(struct GameState *gs, double dt)
{
@ -2393,8 +2481,15 @@ void process(struct GameState *gs, double dt)
entity_ensure_in_orbit(gs, p);
if (medbay != NULL)
{
exit_seat(gs, medbay, p);
p->damage = 0.95;
if (get_entity(gs, medbay->player_who_is_inside_of_me) == NULL)
{
player_get_in_seat(gs, player, medbay);
}
else
{
exit_seat(gs, medbay, p);
}
}
}
flight_assert(p->is_player);
@ -2427,7 +2522,7 @@ void process(struct GameState *gs, double dt)
if (seat_maybe_in == NULL) // not in any seat
{
cpPointQueryInfo query_info = {0};
cpShape *result = cpSpacePointQueryNearest(gs->space, (world_hand_pos), 0.1, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &query_info);
cpShape *result = cpSpacePointQueryNearest(gs->space, (world_hand_pos), 0.1, FILTER_ONLY_BOXES, &query_info);
if (result != NULL)
{
Entity *potential_seat = cp_shape_entity(result);
@ -2453,10 +2548,7 @@ void process(struct GameState *gs, double dt)
// don't let players get inside of cockpits that somebody else is already inside of
if (get_entity(gs, potential_seat->player_who_is_inside_of_me) == NULL)
{
p->currently_inside_of_box = get_id(gs, potential_seat);
potential_seat->player_who_is_inside_of_me = get_id(gs, p);
if (potential_seat->box_type == BoxMedbay)
player->last_used_medbay = p->currently_inside_of_box;
player_get_in_seat(gs, player, potential_seat);
}
}
}
@ -2506,7 +2598,7 @@ void process(struct GameState *gs, double dt)
if (seat_inside_of == NULL)
{
cpShapeSetFilter(p->shape, PLAYER_SHAPE_FILTER);
cpShapeSetFilter(p->shape, PLAYER_FILTER);
cpBodyApplyForceAtWorldPoint(p->body, (cpvmult(movement_this_tick, PLAYER_JETPACK_FORCE)), cpBodyGetPosition(p->body));
cpBodySetTorque(p->body, rotation_this_tick * PLAYER_JETPACK_TORQUE);
p->damage += cpvlength(movement_this_tick) * dt * PLAYER_JETPACK_SPICE_PER_SECOND;
@ -2560,7 +2652,7 @@ void process(struct GameState *gs, double dt)
cpVect world_build = world_hand_pos;
Entity *target_grid = grid_to_build_on(gs, world_hand_pos);
cpShape *maybe_box_to_destroy = cpSpacePointQueryNearest(gs->space, (world_build), 0.01, cpShapeFilterNew(CP_NO_GROUP, CP_ALL_CATEGORIES, BOXES), &info);
cpShape *maybe_box_to_destroy = cpSpacePointQueryNearest(gs->space, (world_build), 0.01, cpShapeFilterNew(CP_NO_GROUP, BOXES, BOXES), &info);
if (maybe_box_to_destroy != NULL)
{
Entity *cur_box = cp_shape_entity(maybe_box_to_destroy);
@ -2650,6 +2742,7 @@ void process(struct GameState *gs, double dt)
else
{
entity_destroy(gs, e);
continue;
}
continue;
}
@ -2666,7 +2759,7 @@ void process(struct GameState *gs, double dt)
if (!e->is_grid) // grids aren't damaged (this edge case sucks!)
{
PROFILE_SCOPE("Grid processing")
// PROFILE_SCOPE("Grid processing")
{
sqdist = cpvlengthsq(cpvsub((entity_pos(e)), (entity_pos(i.sun))));
if (sqdist < (i.sun->sun_radius * i.sun->sun_radius))
@ -2700,10 +2793,37 @@ void process(struct GameState *gs, double dt)
if (e->explosion_progress >= EXPLOSION_TIME)
{
entity_destroy(gs, e);
continue;
}
}
}
if (e->is_orb)
{
PROFILE_SCOPE("Orb")
{
circle_query(gs->space, entity_pos(e), ORB_HEAT_MAX_DETECTION_DIST);
cpVect final_force = cpv(0, 0);
QUEUE_ITER(&query_result, QueryResult, res)
{
Entity *potential_aggravation = cp_shape_entity(res->shape);
if (potential_aggravation->is_box && potential_aggravation->box_type == BoxThruster && fabs(potential_aggravation->thrust) > 0.1)
{
final_force = cpvadd(final_force, cpvmult(cpvsub(entity_pos(potential_aggravation), entity_pos(e)), ORB_HEAT_FORCE_MULTIPLIER));
}
}
if (cpvlength(final_force) > ORB_MAX_FORCE)
{
final_force = cpvmult(cpvnormalize(final_force), ORB_MAX_FORCE);
}
// add drag
final_force = cpvadd(final_force, cpvmult(entity_vel(gs, e), -1.0 * lerp(ORB_DRAG_CONSTANT, ORB_FROZEN_DRAG_CONSTANT, e->damage)));
cpBodyApplyForceAtWorldPoint(e->body, final_force, entity_pos(e));
e->damage -= dt * ORB_HEAL_RATE;
e->damage = clamp01(e->damage);
}
}
if (e->is_missile)
{
PROFILE_SCOPE("Missile")
@ -2718,10 +2838,11 @@ void process(struct GameState *gs, double dt)
Entity *explosion = new_entity(gs);
explosion->is_explosion = true;
explosion->explosion_pos = entity_pos(e);
explosion->explosion_vel = (cpBodyGetVelocity(e->body));
explosion->explosion_vel = cpBodyGetVelocity(e->body);
explosion->explosion_push_strength = MISSILE_EXPLOSION_PUSH;
explosion->explosion_radius = MISSILE_EXPLOSION_RADIUS;
entity_destroy(gs, e);
continue;
}
}
}
@ -2832,9 +2953,11 @@ void process(struct GameState *gs, double dt)
double new_sun = clamp01(fabs(cpvdot(box_facing_vector(cur_box), cpvnormalize(cpvsub(entity_pos(i.sun), entity_pos(cur_box))))));
// less sun the farther away you are!
new_sun *= lerp(1.0, 0.0, clamp01(cpvlength(cpvsub(entity_pos(cur_box), entity_pos(i.sun))) / sun_dist_no_gravity(i.sun)));
new_sun *= lerp(1.0, 0.0, clamp01(cpvdist(entity_pos(cur_box), entity_pos(i.sun)) / sun_dist_no_gravity(i.sun)));
cur_box->sun_amount += new_sun;
}
cur_box->sun_amount = clamp01(cur_box->sun_amount);
energy_to_add += cur_box->sun_amount * SOLAR_ENERGY_PER_SECOND * dt;
}
}
@ -2853,7 +2976,7 @@ void process(struct GameState *gs, double dt)
flight_assert(energy_to_add >= 0.0);
}
// any energy_to_add existing now can also be used to power thrusters/medbay
// any energy_to_add existing now can also be used to power thrusters/medbay. Kind of like a temporary separate battery
double non_battery_energy_left_over = energy_to_add;
// use the energy, stored in the batteries, in various boxes
@ -2861,25 +2984,17 @@ void process(struct GameState *gs, double dt)
{
if (cur_box->box_type == BoxThruster)
{
double energy_to_consume = cur_box->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt;
if (cur_box->wanted_thrust == 0.0)
{
cur_box->thrust = 0.0;
}
if (energy_to_consume > 0.0)
{
cur_box->thrust = 0.0;
double energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, energy_to_consume);
cur_box->thrust = (1.0 - energy_unconsumed / energy_to_consume) * cur_box->wanted_thrust;
if (cur_box->thrust >= 0.0)
cpBodyApplyForceAtWorldPoint(grid->body, (thruster_force(cur_box)), (entity_pos(cur_box)));
}
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, cur_box->wanted_thrust * THRUSTER_ENERGY_USED_PER_SECOND * dt);
cur_box->thrust = cur_box->energy_effectiveness * cur_box->wanted_thrust;
if (cur_box->thrust >= 0.0)
cpBodyApplyForceAtWorldPoint(grid->body, (thruster_force(cur_box)), (entity_pos(cur_box)));
}
if (cur_box->box_type == BoxGyroscope)
{
cur_box->gyrospin_velocity = lerp(cur_box->gyrospin_velocity, cur_box->thrust * 20.0, dt * 5.0);
cur_box->gyrospin_angle += cur_box->gyrospin_velocity * dt;
// wrap to keep the number small
if (cur_box->gyrospin_angle > 2.0 * PI)
{
cur_box->gyrospin_angle -= 2.0 * PI;
@ -2888,6 +3003,7 @@ void process(struct GameState *gs, double dt)
{
cur_box->gyrospin_angle += 2.0 * PI;
}
if (cur_box->wanted_thrust == 0.0)
{
cur_box->thrust = 0.0;
@ -2895,51 +3011,38 @@ void process(struct GameState *gs, double dt)
double thrust_to_want = cur_box->wanted_thrust;
if (cur_box->wanted_thrust == 0.0)
thrust_to_want = clamp(-cpBodyGetAngularVelocity(grid->body) * GYROSCOPE_PROPORTIONAL_INERTIAL_RESPONSE, -1.0, 1.0);
double energy_to_consume = fabs(thrust_to_want * GYROSCOPE_ENERGY_USED_PER_SECOND * dt);
if (energy_to_consume > 0.0)
{
cur_box->thrust = 0.0;
double energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, energy_to_consume);
cur_box->thrust = (1.0 - energy_unconsumed / energy_to_consume) * thrust_to_want;
if (fabs(cur_box->thrust) >= 0.0)
cpBodySetTorque(grid->body, cpBodyGetTorque(grid->body) + cur_box->thrust * GYROSCOPE_TORQUE);
}
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, fabs(thrust_to_want * GYROSCOPE_ENERGY_USED_PER_SECOND * dt));
cur_box->thrust = cur_box->energy_effectiveness * thrust_to_want;
if (fabs(cur_box->thrust) >= 0.0)
cpBodySetTorque(grid->body, cpBodyGetTorque(grid->body) + cur_box->thrust * GYROSCOPE_TORQUE);
}
if (cur_box->box_type == BoxMedbay)
{
Entity *potential_meatbag_to_heal = get_entity(gs, cur_box->player_who_is_inside_of_me);
if (potential_meatbag_to_heal != NULL)
{
double wanted_energy_use = fmin(potential_meatbag_to_heal->damage, PLAYER_ENERGY_RECHARGE_PER_SECOND * dt);
if (wanted_energy_use > 0.0)
{
double energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, wanted_energy_use);
potential_meatbag_to_heal->damage -= (1.0 - energy_unconsumed / wanted_energy_use) * wanted_energy_use;
}
double wanted_energy_to_heal = fmin(potential_meatbag_to_heal->damage, PLAYER_ENERGY_RECHARGE_PER_SECOND * dt);
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, wanted_energy_to_heal);
potential_meatbag_to_heal->damage -= wanted_energy_to_heal * cur_box->energy_effectiveness;
}
}
if (cur_box->box_type == BoxCloaking)
{
double energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, CLOAKING_ENERGY_USE * dt);
if (energy_unconsumed >= CLOAKING_ENERGY_USE * dt)
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, CLOAKING_ENERGY_USE * dt);
cur_box->cloaking_power = lerp(cur_box->cloaking_power, cur_box->energy_effectiveness, dt * 3.0);
if (cur_box->energy_effectiveness >= 1.0)
{
cur_box->cloaking_power = lerp(cur_box->cloaking_power, 0.0, dt * 3.0);
}
else
{
cur_box->cloaking_power = lerp(cur_box->cloaking_power, 1.0, dt * 3.0);
rect_query(gs->space, (BoxCentered){
.pos = entity_pos(cur_box),
.rotation = entity_rotation(cur_box),
// subtract a little from the panel size so that boxes just at the boundary of the panel
// aren't (sometimes cloaked)/(sometimes not) from floating point imprecision
.size = cpv(CLOAKING_PANEL_SIZE - 0.03, CLOAKING_PANEL_SIZE - 0.03),
.size = cpv(CLOAKING_PANEL_SIZE / 2.0 - 0.03, CLOAKING_PANEL_SIZE / 2.0 - 0.03),
});
QUEUE_ITER(&query_result, QueryResult, res)
{
cpShape *shape = res->shape;
Entity *from_cloaking_box = cur_box;
GameState *gs = entitys_gamestate(from_cloaking_box);
Entity *to_cloak = cp_shape_entity(shape);
to_cloak->time_was_last_cloaked = elapsed_time(gs);
@ -2954,8 +3057,9 @@ void process(struct GameState *gs, double dt)
if (cur_box->missile_construction_charge < 1.0)
{
double want_use_energy = dt * MISSILE_CHARGE_RATE;
double energy_charged = want_use_energy - batteries_use_energy(gs, grid, &non_battery_energy_left_over, want_use_energy);
cur_box->missile_construction_charge += energy_charged;
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, want_use_energy);
cur_box->missile_construction_charge += cur_box->energy_effectiveness * want_use_energy;
}
if (target.target_found && cur_box->missile_construction_charge >= 1.0)
@ -2971,11 +3075,12 @@ void process(struct GameState *gs, double dt)
}
if (cur_box->box_type == BoxScanner)
{
// set the nearest platonic solid! only on server as only the server sees everything
cur_box->energy_effectiveness = batteries_use_energy(gs, grid, &non_battery_energy_left_over, SCANNER_ENERGY_USE * dt);
// only the server knows all the positions of all the solids
if (gs->server_side_computing)
{
double energy_unconsumed = batteries_use_energy(gs, grid, &non_battery_energy_left_over, SCANNER_ENERGY_USE * dt);
if (energy_unconsumed >= SCANNER_ENERGY_USE * dt)
if (cur_box->energy_effectiveness < 1.0)
{
cur_box->platonic_detection_strength = 0.0;
cur_box->platonic_nearest_direction = (cpVect){0};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 B

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 618 B

243
main.c

@ -43,7 +43,6 @@ static sg_pipeline goodpixel_pipeline;
static struct GameState gs = {0};
static int my_player_index = -1;
static bool right_mouse_down = false;
#define MAX_KEYDOWN SAPP_KEYCODE_MENU
static bool keydown[MAX_KEYDOWN] = {0};
static bool piloting_rotation_capable_ship = false;
@ -77,6 +76,7 @@ typedef struct MousePressed
} MousePressed;
static MousePressed mousepressed[MAX_MOUSEBUTTON] = {0};
static EntityID maybe_inviting_this_player = {0};
static EntityID hovering_this_player = {0};
bool confirm_invite_this_player = false;
bool accept_invite = false;
bool reject_invite = false;
@ -124,6 +124,9 @@ static sg_image image_rightclick;
static sg_image image_rothelp;
static sg_image image_zoomeasyhelp;
static sg_image image_gyrospin;
static sg_image image_noenergy;
static sg_image image_orb;
static sg_image image_orb_frozen;
static enum BoxType toolbar[TOOLBAR_SLOTS] = {
BoxHullpiece,
@ -651,6 +654,9 @@ static void init(void)
image_rothelp = load_image("loaded/rothelp.png");
image_gyrospin = load_image("loaded/gyroscope_spinner.png");
image_zoomeasyhelp = load_image("loaded/zoomeasyhelp.png");
image_noenergy = load_image("loaded/no_energy.png");
image_orb = load_image("loaded/orb.png");
image_orb_frozen = load_image("loaded/orb_frozen.png");
}
// socket initialization
@ -701,7 +707,7 @@ static void init(void)
}
}
#define transform_scope DeferLoop(sgp_push_transform(), sgp_pop_transform())
#define transform_scope() DeferLoop(sgp_push_transform(), sgp_pop_transform())
static void set_pipeline_and_pull_color(sg_pipeline pip)
{
@ -820,7 +826,7 @@ static void ui(bool draw, double dt, double width, double height)
sgp_set_image(0, image_rothelp);
cpVect draw_at = cpv(width / 2.0, height * 0.25);
transform_scope
transform_scope()
{
scale_at(1.0, -1.0, draw_at.x, draw_at.y);
pipeline_scope(goodpixel_pipeline)
@ -835,7 +841,7 @@ static void ui(bool draw, double dt, double width, double height)
set_color_values(1.0, 1.0, 1.0, alpha);
sgp_set_image(0, image_zoomeasyhelp);
cpVect draw_at = cpv(width * 0.1, height * 0.5);
transform_scope
transform_scope()
{
scale_at(1.0, -1.0, draw_at.x, draw_at.y);
pipeline_scope(goodpixel_pipeline)
@ -933,7 +939,7 @@ static void ui(bool draw, double dt, double width, double height)
{
sgp_set_image(0, image_mystery);
}
transform_scope
transform_scope()
{
scale_at(1.0, -1.0, item_x + item_width / 2.0, item_y + item_height / 2.0);
pipeline_scope(goodpixel_pipeline)
@ -990,7 +996,7 @@ static void ui(bool draw, double dt, double width, double height)
if (invited)
draw_as_squad = myentity()->squad_invited_to;
transform_scope
transform_scope()
{
pipeline_scope(hueshift_pipeline)
{
@ -1005,7 +1011,7 @@ static void ui(bool draw, double dt, double width, double height)
}
// yes
transform_scope
transform_scope()
{
set_color_values(1.0, 1.0, 1.0, 1.0);
scale_at(1.0, -1.0, yes_x, buttons_y);
@ -1018,7 +1024,7 @@ static void ui(bool draw, double dt, double width, double height)
}
// no
transform_scope
transform_scope()
{
set_color_values(1.0, 1.0, 1.0, 1.0);
scale_at(1.0, -1.0, no_x, buttons_y);
@ -1032,8 +1038,31 @@ static void ui(bool draw, double dt, double width, double height)
}
}
// draw maybe inviting
// draw maybe inviting and helper text
{
Entity *maybe_hovering = get_entity(&gs, hovering_this_player);
if (draw && maybe_hovering != NULL && myplayer() != NULL && myplayer()->squad != SquadNone && myplayer()->squad != maybe_hovering->owning_squad)
{
cpVect pos = world_to_screen(width, height, entity_pos(maybe_hovering));
set_color(WHITE);
draw_circle(pos, 28.0 + sin(exec_time * 5.0) * 6.5);
sgp_set_image(0, image_rightclick);
cpVect draw_at = cpvadd(pos, cpv(0, 50.0));
transform_scope()
{
pipeline_scope(goodpixel_pipeline)
{
scale_at(1.0, -1.0, draw_at.x, draw_at.y);
draw_texture_centered(draw_at, 100.0 + sin(exec_time * 5.0) * 10.0);
}
}
sgp_reset_image(0);
}
Entity *inviting = get_entity(&gs, maybe_inviting_this_player);
if (inviting != NULL && myplayer() != NULL)
{
@ -1041,7 +1070,7 @@ static void ui(bool draw, double dt, double width, double height)
width, height,
cpvadd(entity_pos(inviting),
(cpVect){.y = player_scaling * PLAYER_SIZE.y / 2.0}));
cpVect pos = cpvadd(top_of_head, (cpVect){.y = -30.0});
cpVect pos = cpvadd(top_of_head, (cpVect){.y = -30.0}); // -y is up because in screen space here
cpVect to_mouse = cpvsub(mouse_pos,
world_to_screen(width, height, entity_pos(inviting)));
bool selecting_to_invite =
@ -1053,7 +1082,7 @@ static void ui(bool draw, double dt, double width, double height)
confirm_invite_this_player = true;
}
if (draw)
transform_scope
transform_scope()
{
const double size = 64.0;
@ -1155,8 +1184,7 @@ static void ui(bool draw, double dt, double width, double height)
}
double size = 128.0;
bool hovering =
cpvdist(mouse_pos, flag_pos[i]) < size * 0.25 && this_squad_available;
bool hovering = box_has_point((BoxCentered){.pos = flag_pos[i], .rotation = flag_rot[i], .size = cpv(size * 0.5, size)}, mouse_pos) && this_squad_available;
if (!choosing_flags && hovering && build_pressed)
{
@ -1177,7 +1205,7 @@ static void ui(bool draw, double dt, double width, double height)
if (draw)
{
transform_scope
transform_scope()
{
if (this_squad_available)
{
@ -1210,25 +1238,6 @@ static void ui(bool draw, double dt, double width, double height)
}
#undef FLAG_ITER
// draw spice bar
if (draw)
{
static double damage = 0.5;
if (myentity() != NULL)
{
damage = myentity()->damage;
}
set_color_values(0.5, 0.5, 0.5, cur_opacity);
double margin = width * 0.2;
double bar_width = width - margin * 2.0;
double y = height - 150.0;
draw_filled_rect(margin, y, bar_width, 30.0);
set_color_values(1.0, 1.0, 1.0, cur_opacity);
draw_filled_rect(margin, y, bar_width * (1.0 - damage), 30.0);
}
// draw muted
static double toggle_mute_opacity = 0.2;
const double size = 150.0;
@ -1253,7 +1262,7 @@ static void ui(bool draw, double dt, double width, double height)
sgp_set_image(0, image_mic_muted);
else
sgp_set_image(0, image_mic_unmuted);
transform_scope
transform_scope()
{
scale_at(1.0, -1.0, button.x + button.width / 2.0,
button.y + button.height / 2.0);
@ -1263,18 +1272,19 @@ static void ui(bool draw, double dt, double width, double height)
}
// draw item toolbar
double itembar_width = 0.0;
{
double itemframe_width =
(float)sg_query_image_info(image_itemframe).width * 2.0;
double itemframe_height =
(float)sg_query_image_info(image_itemframe).height * 2.0;
double total_width = itemframe_width * (float)TOOLBAR_SLOTS;
itembar_width = itemframe_width * (float)TOOLBAR_SLOTS;
double item_width = itemframe_width * 0.75;
double item_height = itemframe_height * 0.75;
double item_offset_x = (itemframe_width - item_width) / 2.0;
double item_offset_y = (itemframe_height - item_height) / 2.0;
double x = width / 2.0 - total_width / 2.0;
double x = width / 2.0 - itembar_width / 2.0;
double y = height - itemframe_height * 1.5;
for (int i = 0; i < TOOLBAR_SLOTS; i++)
{
@ -1332,7 +1342,7 @@ static void ui(bool draw, double dt, double width, double height)
pipeline_scope(goodpixel_pipeline)
draw_textured_rect(x, y, itemframe_width, itemframe_height);
sgp_reset_image(0);
transform_scope
transform_scope()
{
double item_x = x + item_offset_x;
double item_y = y + item_offset_y;
@ -1368,6 +1378,24 @@ static void ui(bool draw, double dt, double width, double height)
x += itemframe_width;
}
}
// draw spice bar
if (draw)
{
static double damage = 0.5;
if (myentity() != NULL)
{
damage = myentity()->damage;
}
set_color_values(0.5, 0.5, 0.5, cur_opacity);
double bar_width = itembar_width * 1.1;
double margin = (width - bar_width) / 2.0;
double y = height - 150.0;
draw_filled_rect(margin, y, bar_width, 30.0);
set_color_values(1.0, 1.0, 1.0, cur_opacity);
draw_filled_rect(margin, y, bar_width * (1.0 - damage), 30.0);
}
if (draw)
sgp_pop_transform();
@ -1581,7 +1609,7 @@ static void frame(void)
int ticks_should_repredict = (int)predicted_to_tick - (int)server_current_tick;
int healthy_num_ticks_ahead = (int)ceil((((double)peer->roundTripTime) / 1000.0) / TIMESTEP) + 6;
int healthy_num_ticks_ahead = (int)ceil((((double)peer->roundTripTime + (double)peer->roundTripTimeVariance * CAUTIOUS_MULTIPLIER) / 1000.0) / TIMESTEP) + 6;
int ticks_to_repredict = ticks_should_repredict;
@ -1645,7 +1673,9 @@ static void frame(void)
PROFILE_SCOPE("gameplay and prediction")
{
// interpolate zoom
zoom = lerp(zoom, zoom_target, dt * 12.0);
zoom = lerp(zoom, zoom_target, dt * 16.0);
// zoom = lerp(log(zoom), log(zoom_target), dt * 20.0);
// zoom = exp(zoom);
// calculate build preview stuff
cpVect local_hand_pos = {0};
@ -1679,15 +1709,18 @@ static void frame(void)
// process player interaction (squad invites)
if (interact_pressed && myplayer() != NULL && myplayer()->squad != SquadNone)
ENTITIES_ITER(cur)
{
if (get_entity(&gs, hovering_this_player) != NULL)
{
if (cur != myentity() && cur->is_player &&
has_point(centered_at(entity_pos(cur), cpvmult(PLAYER_SIZE, player_scaling)), world_mouse_pos))
{
maybe_inviting_this_player = get_id(&gs, cur);
interact_pressed = false;
}
maybe_inviting_this_player = hovering_this_player;
interact_pressed = false;
}
}
ENTITIES_ITER(cur)
{
{
}
}
// Create and send input packet, and predict a frame of gamestate
static InputFrame cur_input_frame = {
@ -1882,14 +1915,14 @@ static void frame(void)
// WORLD SPACE
// world space coordinates are +Y up, -Y down. Like normal cartesian coords
transform_scope
transform_scope()
{
translate(width / 2, height / 2);
scale_at(zoom, -zoom, 0.0, 0.0);
// parllax layers, just the zooming, but not 100% of the camera panning
#if 1 // space background
transform_scope
transform_scope()
{
cpVect scaled_camera_pos = cpvmult(
camera_pos, 0.0005); // this is how strong/weak the parallax is
@ -1906,7 +1939,7 @@ static void frame(void)
// draw_textured_rect(0, 0, stars_width, stars_height);
sgp_reset_image(0);
}
transform_scope
transform_scope()
{
cpVect scaled_camera_pos = cpvmult(
camera_pos, 0.005); // this is how strong/weak the parallax is
@ -1926,14 +1959,14 @@ static void frame(void)
#endif
#if 1 // parallaxed dots
transform_scope
transform_scope()
{
cpVect scaled_camera_pos = cpvmult(camera_pos, 0.25);
translate(-scaled_camera_pos.x, -scaled_camera_pos.y);
set_color(WHITE);
draw_dots(scaled_camera_pos, 3.0);
}
transform_scope
transform_scope()
{
cpVect scaled_camera_pos = cpvmult(camera_pos, 0.5);
translate(-scaled_camera_pos.x, -scaled_camera_pos.y);
@ -1975,7 +2008,7 @@ static void frame(void)
if (currently_building() != BoxInvalid && can_build(currently_building()))
{
transform_scope
transform_scope()
{
rotate_at(build_preview.grid_rotation +
rotangle(cur_editing_rotation),
@ -1984,22 +2017,24 @@ static void frame(void)
set_color_values(0.5, 0.5, 0.5, (sin((float)exec_time * 9.0) + 1.0) / 3.0 + 0.2);
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(global_hand_pos, BOX_SIZE);
// drawbox(hand_pos, build_preview.grid_rotation, 0.0,
// cur_editing_boxtype, cur_editing_rotation);
sgp_reset_image(0);
}
}
double player_scaling_target = zoom < 6.5 ? PLAYER_BIG_SCALING/zoom : 1.0;
double player_scaling_target = zoom < 6.5 ? PLAYER_BIG_SCALING / zoom : 1.0;
if (zoom > 50.0)
player_scaling = player_scaling_target; // For press tab zoom shortcut. Bad hack to make zooming in not jarring with the bigger player. Comment this out and press tab to see!
player_scaling = lerp(player_scaling, player_scaling_target, dt * 15.0);
hovering_this_player = (EntityID){0};
// draw all types of entities
ENTITIES_ITER(e)
{
// draw grid
if (e->is_grid)
{
Entity *g = e;
// draw boxes
BOXES_ITER(&gs, b, g)
{
set_color_values(1.0, 1.0, 1.0, 1.0);
@ -2012,14 +2047,14 @@ static void frame(void)
Collerp(from, to, b->energy_used / BATTERY_CAPACITY);
set_color_values(result.r, result.g, result.b, cur_alpha);
}
transform_scope
transform_scope()
{
rotate_at(entity_rotation(g) + rotangle(b->compass_rotation),
entity_pos(b).x, entity_pos(b).y);
if (b->box_type == BoxThruster)
{
transform_scope
transform_scope()
{
set_color_values(1.0, 1.0, 1.0, 1.0);
sgp_set_image(0, image_thrusterburn);
@ -2051,11 +2086,6 @@ static void frame(void)
draw_texture_centered(entity_pos(b), BOX_SIZE);
sgp_reset_image(0);
set_color_values(1.0, 1.0, 1.0, 1.0 - b->sun_amount);
/* Color to_set = colhexcode(0xeb9834);
to_set.a = b->sun_amount * 0.5;
set_color(to_set);
draw_color_rect_centered(entity_pos(b), BOX_SIZE);
*/
}
if (box_interactible(b->box_type))
@ -2067,11 +2097,9 @@ static void frame(void)
},
world_mouse_pos))
{
// set_color_values(1.0, 1.0, 1.0, 0.2);
// draw_color_rect_centered(entity_pos(b), BOX_SIZE);
set_color(WHITE);
draw_circle(entity_pos(b), BOX_SIZE / 1.75 + sin(exec_time * 5.0) * BOX_SIZE * 0.1);
transform_scope
transform_scope()
{
pipeline_scope(goodpixel_pipeline)
{
@ -2115,7 +2143,7 @@ static void frame(void)
if (b->box_type == BoxScanner)
{
sgp_set_image(0, image_scanner_head);
transform_scope
transform_scope()
{
pipeline_scope(goodpixel_pipeline)
{
@ -2131,7 +2159,7 @@ static void frame(void)
{
sgp_set_image(0, image_gyrospin);
transform_scope
transform_scope()
{
pipeline_scope(goodpixel_pipeline)
{
@ -2164,6 +2192,27 @@ static void frame(void)
}
// outside of the transform scope
// if not enough energy for box to be used
bool uses_energy = false;
uses_energy |= b->box_type == BoxThruster;
uses_energy |= b->box_type == BoxGyroscope;
uses_energy |= b->box_type == BoxMedbay && get_entity(&gs, b->player_who_is_inside_of_me) != NULL;
uses_energy |= b->box_type == BoxCloaking;
uses_energy |= b->box_type == BoxMissileLauncher;
uses_energy |= b->box_type == BoxScanner;
if (uses_energy)
{
set_color_values(1.0, 1.0, 1.0, 1.0 - b->energy_effectiveness);
sgp_set_image(0, image_noenergy);
pipeline_scope(goodpixel_pipeline)
{
draw_texture_centered(cpvadd(entity_pos(b), cpv(0, -BOX_SIZE / 2.0)), 0.2);
}
sgp_reset_image(0);
}
if (b->box_type == BoxScanner)
{
if (b->platonic_detection_strength > 0.0)
@ -2181,7 +2230,7 @@ static void frame(void)
draw_circle(entity_pos(b), MISSILE_RANGE);
// draw the charging missile
transform_scope
transform_scope()
{
rotate_at(missile_launcher_target(&gs, b).facing_angle, entity_pos(b).x, entity_pos(b).y);
sgp_set_image(0, image_missile);
@ -2197,13 +2246,31 @@ static void frame(void)
}
}
// draw orb
if (e->is_orb)
{
set_color(WHITE);
double effective_radius = ORB_RADIUS * 2.2;
set_color_values(1.0, 1.0, 1.0, 1.0 - e->damage);
sgp_set_image(0, image_orb);
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(entity_pos(e), effective_radius);
sgp_reset_image(0);
set_color_values(1.0, 1.0, 1.0, e->damage);
sgp_set_image(0, image_orb_frozen);
pipeline_scope(goodpixel_pipeline)
draw_texture_centered(entity_pos(e), effective_radius);
sgp_reset_image(0);
}
// draw missile
if (e->is_missile)
{
transform_scope
transform_scope()
{
rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
set_color_values(1.0, 1.0, 1.0, 1.0);
set_color(WHITE);
if (is_burning(e))
{
@ -2225,24 +2292,28 @@ static void frame(void)
}
// draw player
if (e->is_player &&
get_entity(&gs, e->currently_inside_of_box) == NULL)
if (e->is_player)
{
transform_scope
{
rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
set_color_values(1.0, 1.0, 1.0, 1.0);
pipeline_scope(hueshift_pipeline)
if (e != myentity() && has_point(centered_at(entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling)), world_mouse_pos))
hovering_this_player = get_id(&gs, e);
if (get_entity(&gs, e->currently_inside_of_box) == NULL)
transform_scope()
{
setup_hueshift(e->owning_squad);
sgp_set_image(0, image_player);
draw_texture_rectangle_centered(
entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling));
sgp_reset_image(0);
rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y);
set_color_values(1.0, 1.0, 1.0, 1.0);
pipeline_scope(hueshift_pipeline)
{
setup_hueshift(e->owning_squad);
sgp_set_image(0, image_player);
draw_texture_rectangle_centered(
entity_pos(e), cpvmult(PLAYER_SIZE, player_scaling));
sgp_reset_image(0);
}
}
}
}
// draw explosion
if (e->is_explosion)
{
sgp_set_image(0, image_explosion);
@ -2260,14 +2331,14 @@ static void frame(void)
// the SUN
SUNS_ITER(&gs)
{
transform_scope
transform_scope()
{
translate(entity_pos(i.sun).x, entity_pos(i.sun).y);
set_color(WHITE);
sgp_set_image(0, image_sun);
draw_texture_centered((cpVect){0}, i.sun->sun_radius * 8.0);
sgp_reset_image(0);
#ifdef DEBUG_RENDERING
// so you can make sure the sprite is the right size to accurately show when it will burn you
set_color(RED);

@ -17,7 +17,7 @@ typedef struct QueueElementHeader
typedef struct Queue
{
char *data;
size_t data_length; // must be a multiple of sizeof(QueueElementHeader) + element_size
size_t max_elements;
size_t element_size;
QueueElementHeader *next;
} Queue;
@ -25,7 +25,10 @@ typedef struct Queue
#define QUEUE_SIZE_FOR_ELEMENTS(element_size, max_elements) ((sizeof(QueueElementHeader) + element_size) * max_elements)
// oldest to newest
#define QUEUE_ITER(q_ptr, type, cur) for (QueueElementHeader *cur_header = (q_ptr)->next; cur_header != NULL; cur_header = cur_header->next) for(type *cur = (type*)cur_header->data; cur != NULL; cur = NULL)
#define QUEUE_ITER(q_ptr, type, cur) \
for (QueueElementHeader *cur_header = (q_ptr)->next; cur_header != NULL; cur_header = cur_header->next) \
for (type *cur = (type *)cur_header->data; cur != NULL; cur = NULL)
size_t queue_data_length(Queue *q);
void queue_init(Queue *q, size_t element_size, char *data, size_t data_length);
void queue_clear(Queue *q);
void *queue_push_element(Queue *q);
@ -34,26 +37,30 @@ void *queue_pop_element(Queue *q);
void *queue_most_recent_element(Queue *q);
#ifdef QUEUE_IMPL
size_t queue_data_length(Queue *q)
{
return QUEUE_SIZE_FOR_ELEMENTS(q->element_size, q->max_elements);
}
void queue_init(Queue *q, size_t element_size, char *data, size_t data_length)
{
QUEUE_ASSERT(data_length % (sizeof(QueueElementHeader) + element_size) == 0);
q->data = data;
q->data_length = data_length;
q->element_size = element_size;
QUEUE_ASSERT(data_length % (sizeof(QueueElementHeader) + element_size) == 0);
q->max_elements = data_length / (sizeof(QueueElementHeader) + element_size);
}
void queue_clear(Queue *q)
{
QUEUE_ASSERT(q->data != NULL);
for (size_t i = 0; i < q->data_length; i++)
for (size_t i = 0; i < queue_data_length(q); i++)
{
q->data[i] = 0;
}
q->next = NULL;
}
#define QUEUE_ELEM_ITER(cur) for (QueueElementHeader *cur = (QueueElementHeader *)q->data; (char *)cur < q->data + q->data_length; cur = (QueueElementHeader*)((char*)cur + (sizeof(QueueElementHeader) + q->element_size)))
#define QUEUE_ELEM_ITER(cur) for (QueueElementHeader *cur = (QueueElementHeader *)q->data; (char *)cur < q->data + queue_data_length(q); cur = (QueueElementHeader *)((char *)cur + (sizeof(QueueElementHeader) + q->element_size)))
// you push an element, get the return value, cast it to your type, and fill it with data. It's that easy!
// if it's null the queue is out of space

@ -33,6 +33,16 @@
#define PLAYER_JETPACK_ROTATION_ENERGY_PER_SECOND 0.2f
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.08f
#define PLAYER_BIG_SCALING 300.0
#define ORB_MASS 4.0
#define ORB_RADIUS 1.0
#define ORB_HEAT_FORCE_MULTIPLIER 5.0
#define ORB_DRAG_CONSTANT 1.0
#define ORB_FROZEN_DRAG_CONSTANT 10.0
#define ORB_HEAT_MAX_DETECTION_DIST 100.0
#define ORB_HEAL_RATE 0.2
#define ORB_MAX_FORCE 200.0
#define SCANNER_ENERGY_USE 0.05f
#define MAX_HAND_REACH 1.0f
#define SCANNER_SCAN_RATE 0.5f
@ -62,6 +72,7 @@
#define EXPLOSION_DAMAGE_PER_SEC 10.0f
#define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes
#define GOLD_UNLOCK_RADIUS 1.0f
#ifndef TIME_BETWEEN_WORLD_SAVE
#define TIME_BETWEEN_WORLD_SAVE 30.0f
#endif
@ -83,6 +94,7 @@
#define VOIP_DISTANCE_WHEN_CANT_HEAR (VISION_RADIUS * 0.8f)
// multiplayer
#define CAUTIOUS_MULTIPLIER 0.8 // how overboard to go with the time ahead predicting, makes it less likely that inputs are lost
#define TICKS_BEHIND_DO_SNAP 6 // when this many ticks behind, instead of dilating time SNAP to the healthy ticks ahead
#define MAX_MS_SPENT_REPREDICTING 30.0f
#define TIME_BETWEEN_SEND_GAMESTATE (1.0f / 20.0f)
@ -114,12 +126,7 @@
#define flight_assert(condition) __flight_assert(condition, __FILE__, __LINE__, #condition)
// including headers from headers bad
#ifndef CHIPMUNK_H
typedef void cpSpace;
typedef void cpBody;
typedef void cpShape;
#endif
#include <chipmunk.h> // unfortunate but needs cpSpace, cpBody, cpShape etc
#include "queue.h"
#include <stdbool.h>
@ -246,7 +253,7 @@ typedef struct Entity
bool no_save_to_disk; // stuff generated later on, like player's bodies or space stations that respawn.
double damage; // used by box and player
double damage; // used by box, player, and orb
cpBody *body; // used by grid, player, and box
cpShape *shape; // must be a box so shape_size can be set appropriately, and serialized
@ -258,8 +265,10 @@ typedef struct Entity
// for serializing the shape
// @Robust remove shape_parent_entity from this struct, use the shape's body to figure out
// what the shape's parent entity is
bool is_circle_shape;
EntityID shape_parent_entity; // can't be zero if shape is nonzero
cpVect shape_size;
double shape_radius; // only when circle shape
cpVect shape_size; // only when rect shape
// player
bool is_player;
@ -287,6 +296,9 @@ typedef struct Entity
bool is_missile;
double time_burned_for; // until MISSILE_BURN_TIME. Before MISSILE_ARM_TIME cannot explode
// orb
bool is_orb;
// grids
bool is_grid;
double total_energy_capacity;
@ -302,6 +314,11 @@ typedef struct Entity
enum CompassRotation compass_rotation;
bool indestructible;
// used by multiple boxes that use power to see if it should show low power warning.
// not serialized, populated during client side prediction. For the medbay
// is only valid if the medbay has somebody inside of it
double energy_effectiveness; // from 0 to 1, how effectively the box is operating given available power
// merger
bool wants_disconnect; // don't serialized, termporary value not used across frames
@ -311,6 +328,7 @@ typedef struct Entity
// used by medbay and cockpit
EntityID player_who_is_inside_of_me;
// only serialized when box_type is thruster or gyroscope, used for both. Thrust
// can mean rotation thrust!
double wanted_thrust; // the thrust command applied to the thruster

Loading…
Cancel
Save