From b45974af1d5c33d245a7508b4fbff970e3eb5a9a Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Tue, 22 Nov 2022 00:00:11 -0800 Subject: [PATCH] Add missile block --- flight.rdbg | Bin 1435 -> 1435 bytes gamestate.c | 178 +++++++++++++++++++++++++++++++++++- loaded/missile.png | Bin 0 -> 4973 bytes loaded/missile_burning.png | Bin 0 -> 660 bytes loaded/missile_launcher.png | Bin 0 -> 5969 bytes main.c | 53 ++++++++++- tooling.ahk | 4 +- types.h | 30 +++++- 8 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 loaded/missile.png create mode 100644 loaded/missile_burning.png create mode 100644 loaded/missile_launcher.png diff --git a/flight.rdbg b/flight.rdbg index 5da8056435e54c1b62702879699eae61de5d7a7f..f805329acbb0de2660c566c281716bef2333b948 100644 GIT binary patch delta 106 zcmbQuJ)3(%A*0LW^^A_}PWdU7ddW5u=NnAE#>CI4HF3V`lnQmHGmwp+{Da0z2u4W%-IqP3iL9}C(bu#0n#9alNh-t&a;_VsJmI0Igbed DF{~nJ delta 140 zcmbQuJ)3(%;Us2VMvKXe%xV)KaB&vrXXod{r>9j+p2%d(Xbu(Th6)$xWx!NU_GRLp z{DD!2(H*L0as;Cwmt#>is_missile); + return missile->time_burned_for < MISSILE_BURN_TIME; +} + bool was_entity_deleted(GameState *gs, EntityID id) { if (id.generation == 0) @@ -87,11 +93,11 @@ bool is_cloaked(GameState *gs, Entity *e, Entity *this_players_perspective) bool cloaked = cloaking_active(gs, e); if (e->is_player) { - return cloaked && e->presenting_squad != this_players_perspective->presenting_squad; + return cloaked && e->owning_squad != this_players_perspective->owning_squad; } else { - return cloaked && this_players_perspective->presenting_squad != e->last_cloaked_by_squad; + return cloaked && this_players_perspective->owning_squad != e->last_cloaked_by_squad; } } @@ -197,6 +203,75 @@ void box_remove_from_boxes(GameState *gs, Entity *box) box->prev_box = (EntityID){0}; } +V2 player_vel(GameState *gs, Entity *e); +V2 entity_vel(GameState *gs, Entity *e) +{ + assert(e->is_box || e->is_player || e->is_grid || e->is_explosion); + if (e->is_box) + return box_vel(e); + if (e->is_player) + return player_vel(gs, e); + if (e->is_grid) + return grid_vel(e); + if (e->is_explosion) + return e->explosion_vel; + assert(false); + return (V2){0}; +} + +static THREADLOCAL float to_face = 0.0f; +static THREADLOCAL float nearest_dist = INFINITY; +static THREADLOCAL bool target_found = false; +static void on_missile_shape(cpShape *shape, cpContactPointSet *points, void *data) +{ + Entity *launcher = (Entity *)data; + Entity *other = cp_shape_entity(shape); + GameState *gs = entitys_gamestate(launcher); + assert(other->is_box || other->is_player || other->is_missile); + + V2 to = V2sub(entity_pos(other), entity_pos(launcher)); + bool should_attack = true; + if (other->is_box && box_grid(other) == box_grid(launcher)) + should_attack = false; + if (other->owning_squad == launcher->owning_squad) + should_attack = false; + + if (should_attack && V2length(to) < nearest_dist) + { + target_found = true; + nearest_dist = V2length(to); + + // lookahead by their velocity + V2 rel_velocity = V2sub(entity_vel(gs, other), entity_vel(gs, launcher)); + float dist = V2dist(entity_pos(other), entity_pos(launcher)); + + float time_of_travel = sqrtf( (2.0f * dist) / (MISSILE_BURN_FORCE/MISSILE_MASS) ); + + V2 other_future_pos = V2add(entity_pos(other), V2scale(rel_velocity, time_of_travel)); + + V2 adjusted_to = V2sub(other_future_pos, entity_pos(launcher)); + + to_face = V2angle(adjusted_to); + } +} + +LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher) +{ + to_face = 0.0f; + cpBody *tmp = cpBodyNew(0.0f, 0.0f); + cpBodySetPosition(tmp, v2_to_cp(entity_pos(launcher))); + cpShape *circle = cpCircleShapeNew(tmp, MISSILE_RANGE, cpv(0, 0)); + + nearest_dist = INFINITY; + to_face = 0.0f; + target_found = false; + cpSpaceShapeQuery(gs->space, circle, on_missile_shape, (void *)launcher); + + cpBodyFree(tmp); + cpShapeFree(circle); + return (LauncherTarget){.target_found = target_found, .facing_angle = to_face}; +} + void on_entity_child_shape(cpBody *body, cpShape *shape, void *data); // gs is for iterating over all child shapes and destroying those, too @@ -366,6 +441,13 @@ void create_player(Player *player) #endif } +void create_missile(GameState *gs, Entity *e) +{ + create_body(gs, e); + create_rectangle_shape(gs, e, e, (V2){0}, V2scale(MISSILE_COLLIDER_SIZE, 0.5f), PLAYER_MASS); + e->is_missile = true; +} + void create_player_entity(GameState *gs, Entity *e) { e->is_player = true; @@ -566,6 +648,34 @@ static void on_damage(cpArbiter *arb, cpSpace *space, cpDataPointer userData) entity_a = cp_shape_entity(a); entity_b = cp_shape_entity(b); + Entity *potential_missiles[] = {entity_a, entity_b}; + for (Entity **missile_ptr = potential_missiles; missile_ptr - potential_missiles < ARRLEN(potential_missiles); missile_ptr++) + { + Entity *missile = entity_a; + cpVect (*getPointFunc)(const cpArbiter *arb, int i) = NULL; + if (missile == entity_a) + getPointFunc = cpArbiterGetPointA; + if (missile == entity_b) + getPointFunc = cpArbiterGetPointB; + + if (missile->is_missile) + { + int count = cpArbiterGetCount(arb); + for (int i = 0; i < count; i++) + { + cpVect collision_point = getPointFunc(arb, i); + V2 local_collision_point = cp_to_v2(cpBodyWorldToLocal(missile->body, collision_point)); + if (local_collision_point.x > MISSILE_COLLIDER_SIZE.x * 0.2f) + { + missile->damage += MISSILE_DAMAGE_THRESHOLD * 2.0f; + } + } + } + } + + // if(entity_a->is_missile) {getPointFunc = cpArbiterGetPointA; + // if(entity_b->is_missile) getPointFunc = cpArbiterGetPointB; + float damage = V2length(cp_to_v2(cpArbiterTotalImpulse(arb))) * COLLISION_DAMAGE_SCALING; if (entity_a->is_box && entity_a->box_type == BoxExplosive) @@ -847,6 +957,7 @@ enum GameVersion { VInitial, VMoreBoxes, + VMissileMerge, VMax, // this minus one will be the version used }; @@ -989,13 +1100,19 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) if (ser->version >= VMoreBoxes && !ser->save_or_load_from_disk) SER_VAR(&e->time_was_last_cloaked); + if (ser->version >= VMissileMerge) + SER_VAR(&e->owning_squad); + SER_VAR(&e->is_player); if (e->is_player) { SER_ASSERT(e->no_save_to_disk); SER_MAYBE_RETURN(ser_entityid(ser, &e->currently_inside_of_box)); - SER_VAR(&e->presenting_squad); + if (ser->version < VMissileMerge) + { + SER_VAR_NAME(&e->owning_squad, "&e->presenting_squad"); + } SER_VAR(&e->squad_invited_to); SER_VAR(&e->goldness); } @@ -1015,6 +1132,15 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) SER_MAYBE_RETURN(ser_entityid(ser, &e->boxes)); } + if (ser->version >= VMissileMerge) + { + SER_VAR(&e->is_missile) + if (e->is_missile) + { + SER_VAR(&e->time_burned_for); + } + } + SER_VAR(&e->is_box); if (e->is_box) { @@ -1058,6 +1184,9 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e) case BoxCloaking: SER_VAR(&e->cloaking_power); break; + case BoxMissileLauncher: + SER_VAR(&e->missile_construction_charge); + break; default: break; } @@ -1799,7 +1928,7 @@ void process(GameState *gs, float dt) } } assert(p->is_player); - p->presenting_squad = player->squad; + p->owning_squad = player->squad; if (p->squad_invited_to != SquadNone) { @@ -2077,6 +2206,24 @@ void process(GameState *gs, float dt) } } + if (e->is_missile) + { + if (is_burning(e)) + { + e->time_burned_for += dt; + cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(V2rotate((V2){.x = MISSILE_BURN_FORCE, .y = 0.0f}, entity_rotation(e))), v2_to_cp(entity_pos(e))); + } + if (e->damage >= MISSILE_DAMAGE_THRESHOLD) + { + + Entity *explosion = new_entity(gs); + explosion->is_explosion = true; + explosion->explosion_pos = entity_pos(e); + explosion->explosion_vel = cp_to_v2(cpBodyGetVelocity(e->body)); + entity_destroy(gs, e); + } + } + if (e->is_box) { if (e->is_platonic) @@ -2194,6 +2341,29 @@ void process(GameState *gs, float dt) cpBodyFree(tmp); } } + if (cur_box->box_type == BoxMissileLauncher) + { + LauncherTarget target = missile_launcher_target(gs, cur_box); + + if (cur_box->missile_construction_charge < 1.0f) + { + float want_use_energy = dt * MISSILE_CHARGE_RATE; + float 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; + } + + if (target.target_found && cur_box->missile_construction_charge >= 1.0f) + { + cur_box->missile_construction_charge = 0.0f; + Entity *new_missile = new_entity(gs); + create_missile(gs, new_missile); + new_missile->owning_squad = cur_box->owning_squad; // missiles have teams and attack eachother! + float missile_spawn_dist = sqrtf((BOX_SIZE / 2.0f) * (BOX_SIZE / 2.0f) * 2.0f) + MISSILE_COLLIDER_SIZE.x / 2.0f + 0.1f; + cpBodySetPosition(new_missile->body, v2_to_cp(V2add(entity_pos(cur_box), V2rotate((V2){.x = missile_spawn_dist, 0.0f}, target.facing_angle)))); + cpBodySetAngle(new_missile->body, target.facing_angle); + cpBodySetVelocity(new_missile->body, v2_to_cp(box_vel(cur_box))); + } + } if (cur_box->box_type == BoxScanner) { // set the nearest platonic solid! only on server as only the server sees everything diff --git a/loaded/missile.png b/loaded/missile.png new file mode 100644 index 0000000000000000000000000000000000000000..d3c7ede137b652916eb7369e56bd14f0b5c6565a GIT binary patch literal 4973 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 Bgx>%F literal 0 HcmV?d00001 diff --git a/loaded/missile_burning.png b/loaded/missile_burning.png new file mode 100644 index 0000000000000000000000000000000000000000..d3fe210442f543bc54ea3f1803f24c6360f2cf35 GIT binary patch literal 660 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 -#define ARRLEN(arr) (sizeof(arr) / sizeof(*arr)) // suppress compiler warning about ^^ above used in floating point context #define ARRLENF(arr) ((float)sizeof(arr) / sizeof(*arr)) static struct SquadMeta @@ -534,6 +539,8 @@ static void init(void) image_scanner_head = load_image("loaded/scanner_head.png"); image_itemswitch = load_image("loaded/itemswitch.png"); image_cloaking_panel = load_image("loaded/cloaking_panel.png"); + image_missile_burning = load_image("loaded/missile_burning.png"); + image_missile = load_image("loaded/missile.png"); } // socket initialization @@ -1793,7 +1800,7 @@ static void frame(void) // all of these box types show team colors so are drawn with the hue shifting shader // used with the player - if (b->box_type == BoxCloaking) + if (b->box_type == BoxCloaking || b->box_type == BoxMissileLauncher) { pipeline_scope(hueshift_pipeline) { @@ -1808,6 +1815,23 @@ static void frame(void) } sgp_reset_image(0); + if (b->box_type == BoxMissileLauncher) + { + set_color(RED); + draw_circle(entity_pos(b), MISSILE_RANGE); + + // draw the charging missile + transform_scope + { + sgp_rotate_at(missile_launcher_target(&gs, b).facing_angle, entity_pos(b).x, entity_pos(b).y); + sgp_set_color(1.0f, 1.0f, 1.0f, b->missile_construction_charge); + sgp_set_image(0, image_missile); + pipeline_scope(goodpixel_pipeline) + draw_texture_centered(entity_pos(b), BOX_SIZE); + sgp_reset_image(0); + } + } + if (b->box_type == BoxScanner) { sgp_set_image(0, image_scanner_head); @@ -1823,6 +1847,7 @@ static void frame(void) set_color(WHITE); } + // scanner range, visualizes what scanner can scan if (b->box_type == BoxScanner) { set_color(BLUE); @@ -1857,6 +1882,28 @@ static void frame(void) } } + // draw missile + if (e->is_missile) + { + transform_scope + { + sgp_rotate_at(entity_rotation(e), entity_pos(e).x, entity_pos(e).y); + sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); + + if (is_burning(e)) + { + sgp_set_image(0, image_missile_burning); + } + else + { + sgp_set_image(0, image_missile); + } + + pipeline_scope(goodpixel_pipeline) + draw_texture_rectangle_centered(entity_pos(e), MISSILE_SPRITE_SIZE); + } + } + // draw player if (e->is_player && get_entity(&gs, e->currently_inside_of_box) == NULL) @@ -1868,7 +1915,7 @@ static void frame(void) pipeline_scope(hueshift_pipeline) { - setup_hueshift(e->presenting_squad); + setup_hueshift(e->owning_squad); sgp_set_image(0, image_player); draw_texture_rectangle_centered( entity_pos(e), V2scale(PLAYER_SIZE, player_scaling)); diff --git a/tooling.ahk b/tooling.ahk index f5d8c5a..77cf1f3 100644 --- a/tooling.ahk +++ b/tooling.ahk @@ -14,7 +14,7 @@ WinActivate, flightbuild If WinActive("flightbuild") { Send, {Enter} - Send, remedybg continue-execution && sleep 0.1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging {Enter} + Send, remedybg continue-execution && timeout 1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging {Enter} } return @@ -25,6 +25,6 @@ WinActivate, flightbuild If WinActive("flightbuild") { Send, {Enter} - Send, remedybg continue-execution && sleep 0.1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging && sleep 0.2 && x64\Debug\Flight.exe {Enter} + Send, remedybg continue-execution && timeout 1 && remedybg.exe stop-debugging && msbuild && remedybg.exe start-debugging && sleep 0.2 && x64\Debug\Flight.exe {Enter} } return \ No newline at end of file diff --git a/types.h b/types.h index 67f2473..c3391f9 100644 --- a/types.h +++ b/types.h @@ -10,6 +10,16 @@ #define PLAYER_MASS 0.5f #define PLAYER_JETPACK_FORCE 1.5f #define PLAYER_JETPACK_TORQUE 0.05f +#define MISSILE_RANGE 4.0f +#define MISSILE_BURN_TIME 1.5f +#define MISSILE_BURN_FORCE 2.0f +#define MISSILE_MASS 1.0f +// how many missiles grown per second +#define MISSILE_DAMAGE_THRESHOLD 0.2f +#define MISSILE_CHARGE_RATE 0.5f +// centered on the sprite +#define MISSILE_SPRITE_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE}) +#define MISSILE_COLLIDER_SIZE ((V2){.x = BOX_SIZE*0.5f, .y = BOX_SIZE*0.5f}) // #define PLAYER_JETPACK_FORCE 20.0f // distance at which things become geostationary and no more solar power! #define PLAYER_JETPACK_ROTATION_ENERGY_PER_SECOND 0.2f @@ -80,6 +90,8 @@ #define THREADLOCAL __thread #endif +#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) + // must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1" #include "ipsettings.h" // don't leak IP! @@ -154,6 +166,7 @@ enum BoxType BoxScanner, BoxGyroscope, BoxCloaking, + BoxMissileLauncher, BoxLast, }; @@ -236,7 +249,7 @@ typedef struct Entity // player bool is_player; - enum Squad presenting_squad; // also controls what the player can see, because of cloaking! + enum Squad owning_squad; // also controls what the player can see, because of cloaking! EntityID currently_inside_of_box; enum Squad squad_invited_to; // if squad none, then no squad invite float goldness; // how much the player is a winner @@ -247,6 +260,10 @@ typedef struct Entity V2 explosion_vel; float explosion_progresss; // in seconds + // missile + bool is_missile; + float time_burned_for; // until MISSILE_BURN_TIME + // grids bool is_grid; float total_energy_capacity; @@ -254,7 +271,6 @@ typedef struct Entity // boxes bool is_box; - enum Squad owning_squad; // which squad owns this box enum BoxType box_type; bool is_platonic; // can't be destroyed, unaffected by physical forces bool always_visible; // always serialized to the player. @Robust check if not used @@ -263,6 +279,9 @@ typedef struct Entity enum CompassRotation compass_rotation; bool indestructible; + // missile launcher + float missile_construction_charge; + // used by medbay and cockpit EntityID player_who_is_inside_of_me; @@ -401,6 +420,7 @@ bool client_to_server_deserialize(GameState *gs, struct ClientToServer *msg, uns bool client_to_server_serialize(GameState *gs, struct ClientToServer *msg, unsigned char*bytes, size_t *out_len, size_t max_len); // entities +bool is_burning(Entity *missile); Entity *get_entity(struct GameState *gs, EntityID id); Entity *new_entity(struct GameState *gs); EntityID get_id(struct GameState *gs, Entity *e); @@ -412,6 +432,12 @@ void entity_ensure_in_orbit(Entity *e); void entity_destroy(GameState *gs, Entity *e); #define BOX_CHAIN_ITER(gs, cur, starting_box) for (Entity *cur = get_entity(gs, starting_box); cur != NULL; cur = get_entity(gs, cur->next_box)) #define BOXES_ITER(gs, cur, grid_entity_ptr) BOX_CHAIN_ITER(gs, cur, (grid_entity_ptr)->boxes) +typedef struct LauncherTarget +{ + bool target_found; + float facing_angle; // in global coords +} LauncherTarget; +LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher); // grid void grid_create(struct GameState *gs, Entity *e);