diff --git a/buildsettings.h b/buildsettings.h index 9274935..419f35c 100644 --- a/buildsettings.h +++ b/buildsettings.h @@ -23,7 +23,7 @@ #define DEBUG_TOOLS #define CHIPMUNK_INTEGRITY_CHECK // #define FAT_THRUSTERS -#define NO_GRAVITY +// #define NO_GRAVITY // #define NO_SUNS #else diff --git a/flight.rdbg b/flight.rdbg index 4f73ccc..bb1d2c5 100644 Binary files a/flight.rdbg and b/flight.rdbg differ diff --git a/gamestate.c b/gamestate.c index c482a4f..00d2a8b 100644 --- a/gamestate.c +++ b/gamestate.c @@ -352,7 +352,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 +360,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 +534,18 @@ 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)); } +static const cpShapeFilter BOXES_FILTER = {CP_NO_GROUP, BOXES, CP_ALL_CATEGORIES}; +static const cpShapeFilter NOT_BOXES_FILTER = {CP_NO_GROUP, CP_ALL_CATEGORIES & (~BOXES), CP_ALL_CATEGORIES}; +#define PLAYER_SHAPE_FILTER cpShapeFilterNew(CP_NO_GROUP, PLAYERS, CP_ALL_CATEGORIES) + // 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 +567,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, NOT_BOXES_FILTER); +} +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, NOT_BOXES_FILTER); } - -#define PLAYER_SHAPE_FILTER cpShapeFilterNew(CP_NO_GROUP, PLAYERS, CP_ALL_CATEGORIES) void create_player(Player *player) { @@ -583,6 +598,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); @@ -623,7 +646,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, BOXES_FILTER); box_add_to_boxes(gs, grid, new_box); } @@ -970,9 +993,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) { @@ -1316,10 +1341,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); @@ -1345,8 +1378,15 @@ 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); - cpShapeSetFilter(e->shape, filter); + 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); + } } } @@ -1393,12 +1433,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) { @@ -1920,7 +1965,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; @@ -2255,6 +2299,16 @@ 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); \ + } + ORB_AT(cpv(-5.0, 0.0)); + ORB_AT(cpv(-50.0, 100.0)); + ORB_AT(cpv(-50.0, -100.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); @@ -2464,7 +2518,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, BOXES_FILTER, &query_info); if (result != NULL) { Entity *potential_seat = cp_shape_entity(result); @@ -2594,7 +2648,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); @@ -2684,6 +2738,7 @@ void process(struct GameState *gs, double dt) else { entity_destroy(gs, e); + continue; } continue; } @@ -2734,7 +2789,34 @@ 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); } } @@ -2752,10 +2834,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; } } } diff --git a/loaded/flag_available.png b/loaded/flag_available.png index 62d70ff..f193c39 100644 Binary files a/loaded/flag_available.png and b/loaded/flag_available.png differ diff --git a/loaded/flag_ripped.png b/loaded/flag_ripped.png index 1b8a1e3..80ac3f4 100644 Binary files a/loaded/flag_ripped.png and b/loaded/flag_ripped.png differ diff --git a/loaded/hullpiece.png b/loaded/hullpiece.png index 1f3bead..3b0ede3 100644 Binary files a/loaded/hullpiece.png and b/loaded/hullpiece.png differ diff --git a/loaded/orb.png b/loaded/orb.png new file mode 100644 index 0000000..867391a Binary files /dev/null and b/loaded/orb.png differ diff --git a/loaded/orb_frozen.png b/loaded/orb_frozen.png new file mode 100644 index 0000000..eef98ff Binary files /dev/null and b/loaded/orb_frozen.png differ diff --git a/loaded/player.png b/loaded/player.png index f437715..1549365 100644 Binary files a/loaded/player.png and b/loaded/player.png differ diff --git a/loaded/rothelp.png b/loaded/rothelp.png index f0d1658..40c1b8a 100644 Binary files a/loaded/rothelp.png and b/loaded/rothelp.png differ diff --git a/loaded/scanner_base.png b/loaded/scanner_base.png index d21bf42..973066c 100644 Binary files a/loaded/scanner_base.png and b/loaded/scanner_base.png differ diff --git a/loaded/scanner_head.png b/loaded/scanner_head.png index daa50e2..c14f9fd 100644 Binary files a/loaded/scanner_head.png and b/loaded/scanner_head.png differ diff --git a/loaded/zoomeasyhelp.png b/loaded/zoomeasyhelp.png index 5d9e738..eef8a3b 100644 Binary files a/loaded/zoomeasyhelp.png and b/loaded/zoomeasyhelp.png differ diff --git a/main.c b/main.c index a690033..32dc6ce 100644 --- a/main.c +++ b/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; @@ -126,6 +125,8 @@ 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, @@ -654,6 +655,8 @@ static void init(void) 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 @@ -1236,25 +1239,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; @@ -1289,18 +1273,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++) { @@ -1394,6 +1379,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(); @@ -1671,7 +1674,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}; @@ -2250,13 +2255,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 { 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)) { diff --git a/types.h b/types.h index 03c0fd3..831668b 100644 --- a/types.h +++ b/types.h @@ -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 80.0 +#define ORB_HEAL_RATE 0.2 +#define ORB_MAX_FORCE 160.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 @@ -115,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 // unfortunate but needs cpSpace, cpBody, cpShape etc #include "queue.h" #include @@ -247,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 @@ -259,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; @@ -288,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;