Add missile block

main
Cameron Murphy Reikes 2 years ago
parent 657c791aad
commit b45974af1d

Binary file not shown.

@ -40,6 +40,12 @@ static cpVect v2_to_cp(V2 v)
return cpv(v.x, v.y);
}
bool is_burning(Entity *missile)
{
assert(missile->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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@ -111,6 +111,8 @@ static sg_image image_solarpanel_charging;
static sg_image image_scanner_head;
static sg_image image_itemswitch;
static sg_image image_cloaking_panel;
static sg_image image_missile;
static sg_image image_missile_burning;
static enum BoxType toolbar[TOOLBAR_SLOTS] = {
BoxHullpiece,
@ -191,12 +193,15 @@ static struct BoxInfo
.type = BoxCloaking,
.image_path = "loaded/cloaking_device.png",
},
{
.type = BoxMissileLauncher,
.image_path = "loaded/missile_launcher.png",
},
};
#define ENTITIES_ITER(cur) \
for (Entity *cur = gs.entities; cur < gs.entities + gs.cur_next_entity; \
cur++) \
if (cur->exists)
#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));

@ -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

@ -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);

Loading…
Cancel
Save