Add unlockable explosive block

main
Cameron Murphy Reikes 2 years ago
parent a92656699d
commit 238c81ecde

@ -471,11 +471,18 @@ static cpBool on_damage(cpArbiter* arb, cpSpace* space, cpDataPointer userData)
entity_b = cp_shape_entity(b);
float damage = V2length(cp_to_v2(cpArbiterTotalImpulse(arb))) * COLLISION_DAMAGE_SCALING;
if (entity_a->is_box && entity_a->box_type == BoxExplosive)
entity_a->damage += 2.0f*EXPLOSION_DAMAGE_THRESHOLD;
if (entity_b->is_box && entity_b->box_type == BoxExplosive)
entity_b->damage += 2.0f*EXPLOSION_DAMAGE_THRESHOLD;
if (damage > 0.05f)
{
// Log("Collision with damage %f\n", damage);
entity_a->damage += damage;
entity_b->damage += damage;
}
// b must be the key passed into the post step removed, the key is cast into its shape
@ -515,12 +522,6 @@ V2 grid_com(Entity* grid)
return cp_to_v2(cpBodyLocalToWorld(grid->body, cpBodyGetCenterOfGravity(grid->body)));
}
V2 entity_pos(Entity* e)
{
assert(!e->is_box);
// @Robust merge entity_pos with box_pos
return cp_to_v2(cpBodyGetPosition(e->body));
}
V2 grid_vel(Entity* grid)
{
return cp_to_v2(cpBodyGetVelocity(grid->body));
@ -568,15 +569,20 @@ float entity_shape_mass(Entity* box)
assert(box->shape != NULL);
return (float)cpShapeGetMass(box->shape);
}
V2 box_pos(Entity* box)
{
assert(box->is_box);
return V2add(entity_pos(box_grid(box)), V2rotate(entity_shape_pos(box), entity_rotation(box_grid(box))));
}
float box_rotation(Entity* box)
{
return (float)cpBodyGetAngle(cpShapeGetBody(box->shape));
}
V2 entity_pos(Entity* e)
{
if (e->is_box) {
return V2add(entity_pos(box_grid(e)), V2rotate(entity_shape_pos(e), entity_rotation(box_grid(e))));
}
else {
assert(e->body != NULL);
return cp_to_v2(cpBodyGetPosition(e->body));
}
}
struct BodyData
{
@ -711,6 +717,7 @@ void ser_player(SerState* ser, Player* p)
SER_VAR(&p->connected);
if (p->connected)
{
SER_VAR(&p->unlocked_bombs);
ser_entityid(ser, &p->entity);
ser_inputframe(ser, &p->input);
}
@ -779,6 +786,14 @@ void ser_entity(SerState* ser, GameState* gs, Entity* e)
SER_VAR(&e->goldness);
}
SER_VAR(&e->is_explosion);
if (e->is_explosion)
{
ser_V2(ser, &e->explosion_pos);
ser_V2(ser, &e->explosion_vel);
SER_VAR(&e->explosion_progresss);
}
SER_VAR(&e->is_grid);
if (e->is_grid)
{
@ -955,6 +970,34 @@ Entity* closest_to_point_in_radius(GameState* gs, V2 point, float radius)
return NULL;
}
static float cur_explosion_damage = 0.0f;
static V2 explosion_origin = { 0 };
static void explosion_callback_func(cpShape* shape, cpContactPointSet* points, void* data)
{
GameState* gs = (GameState*)data;
cp_shape_entity(shape)->damage += cur_explosion_damage;
Entity* parent = get_entity(gs, cp_shape_entity(shape)->shape_parent_entity);
V2 from_pos = entity_pos(cp_shape_entity(shape));
V2 impulse = V2scale(V2normalize(V2sub(from_pos, explosion_origin)), EXPLOSION_PUSH_STRENGTH);
assert(parent->body != NULL);
cpBodyApplyImpulseAtWorldPoint(parent->body, v2_to_cp(impulse), v2_to_cp(from_pos));
}
static void do_explosion(GameState* gs, Entity* explosion, float dt)
{
cur_explosion_damage = dt * EXPLOSION_DAMAGE_PER_SEC;
explosion_origin = explosion->explosion_pos;
cpBody* tmpbody = cpBodyNew(0.0f, 0.0f);
cpShape* circle = cpCircleShapeNew(tmpbody, EXPLOSION_RADIUS, v2_to_cp(explosion_origin));
cpSpaceShapeQuery(gs->space, circle, explosion_callback_func, (void*)gs);
cpShapeFree(circle);
cpBodyFree(tmpbody);
}
V2 box_facing_vector(Entity* box)
{
assert(box->is_box);
@ -1029,7 +1072,7 @@ void process(GameState* gs, float dt)
assert(p->is_player);
#ifdef INFINITE_RESOURCES
p->spice_taken_away = 0.0f;
p->damage = 0.0f;
#endif
// update gold win condition
if (V2length(V2sub(cp_to_v2(cpBodyGetPosition(p->body)), gs->goldpos)) < GOLD_COLLECT_RADIUS)
@ -1066,7 +1109,7 @@ void process(GameState* gs, float dt)
}
else
{
V2 pilot_seat_exit_spot = V2add(box_pos(the_seat), V2scale(box_facing_vector(the_seat), BOX_SIZE));
V2 pilot_seat_exit_spot = V2add(entity_pos(the_seat), V2scale(box_facing_vector(the_seat), BOX_SIZE));
cpBodySetPosition(p->body, v2_to_cp(pilot_seat_exit_spot));
cpBodySetVelocity(p->body, v2_to_cp(player_vel(gs, p)));
the_seat->player_who_is_inside_of_me = (EntityID){ 0 };
@ -1095,10 +1138,10 @@ void process(GameState* gs, float dt)
{
assert(seat_inside_of->is_box);
cpShapeSetFilter(p->shape, CP_SHAPE_FILTER_NONE); // no collisions while in a seat
cpBodySetPosition(p->body, v2_to_cp(box_pos(seat_inside_of)));
cpBodySetPosition(p->body, v2_to_cp(entity_pos(seat_inside_of)));
// set thruster thrust from movement
if(seat_inside_of->box_type == BoxCockpit) {
if (seat_inside_of->box_type == BoxCockpit) {
Entity* g = get_entity(gs, seat_inside_of->shape_parent_entity);
V2 target_direction = { 0 };
@ -1133,7 +1176,7 @@ void process(GameState* gs, float dt)
{
Entity* cur_box = cp_shape_entity(nearest);
Entity* cur_grid = cp_body_entity(cpShapeGetBody(nearest));
p->damage -= DAMAGE_TO_PLAYER_PER_BLOCK*((BATTERY_CAPACITY - cur_box->energy_used)/BATTERY_CAPACITY);
p->damage -= DAMAGE_TO_PLAYER_PER_BLOCK * ((BATTERY_CAPACITY - cur_box->energy_used) / BATTERY_CAPACITY);
grid_remove_box(gs, cur_grid, cur_box);
}
else if (target_grid == NULL)
@ -1179,7 +1222,7 @@ void process(GameState* gs, float dt)
{
cpVect p = cpvsub(cpBodyGetPosition(e->body), v2_to_cp(SUN_POS));
cpFloat sqdist = cpvlengthsq(p);
if(sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN*INSTANT_DEATH_DISTANCE_FROM_SUN))
if (sqdist > (INSTANT_DEATH_DISTANCE_FROM_SUN * INSTANT_DEATH_DISTANCE_FROM_SUN))
{
entity_destroy(gs, e);
continue;
@ -1194,8 +1237,27 @@ void process(GameState* gs, float dt)
cpBodyUpdateVelocity(e->body, g, 1.0f, dt);
}
if (e->is_explosion)
{
e->explosion_progresss += dt;
e->explosion_pos = V2add(e->explosion_pos, V2scale(e->explosion_vel, dt));
do_explosion(gs, e, dt);
if (e->explosion_progresss >= EXPLOSION_TIME)
{
entity_destroy(gs, e);
}
}
if (e->is_box)
{
if (e->box_type == BoxExplosive && e->damage >= EXPLOSION_DAMAGE_THRESHOLD)
{
Entity* explosion = new_entity(gs);
explosion->is_explosion = true;
explosion->explosion_pos = entity_pos(e);
explosion->explosion_vel = grid_vel(box_grid(e));
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
}
if (e->damage >= 1.0f)
{
grid_remove_box(gs, get_entity(gs, e->shape_parent_entity), e);
@ -1208,7 +1270,7 @@ void process(GameState* gs, float dt)
BOXES_ITER(gs, cur, e)
{
if (cur->box_type == BoxSolarPanel) {
cur->sun_amount = clamp01(V2dot(box_facing_vector(cur), V2normalize(V2sub(SUN_POS, box_pos(cur)))));
cur->sun_amount = clamp01(V2dot(box_facing_vector(cur), V2normalize(V2sub(SUN_POS, entity_pos(cur)))));
energy_to_add += cur->sun_amount * SOLAR_ENERGY_PER_SECOND * dt;
}
}
@ -1237,7 +1299,7 @@ void process(GameState* gs, float dt)
if (possibly_use_energy(gs, e, energy_to_consume))
{
cur->thrust = cur->wanted_thrust;
cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(box_pos(cur)));
cpBodyApplyForceAtWorldPoint(e->body, v2_to_cp(thruster_force(cur)), v2_to_cp(entity_pos(cur)));
}
}
if (cur->box_type == BoxMedbay)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

@ -54,6 +54,8 @@ static sg_image image_stars;
static sg_image image_stars2;
static sg_image image_sun;
static sg_image image_medbay_used;
static sg_image image_mystery;
static sg_image image_explosion;
static int cur_editing_boxtype = -1;
static int cur_editing_rotation = 0;
@ -61,6 +63,7 @@ static struct BoxInfo {
enum BoxType type;
const char* image_path;
sg_image image;
bool needs_tobe_unlocked;
} boxes[] = {
// if added to here will show up in toolbar, is placeable
{
@ -75,10 +78,6 @@ static struct BoxInfo {
.type = BoxBattery,
.image_path = "loaded/battery.png",
},
{
.type = BoxSolarPanel,
.image_path = "loaded/solarpanel.png",
},
{
.type = BoxCockpit,
.image_path = "loaded/cockpit.png",
@ -87,6 +86,15 @@ static struct BoxInfo {
.type = BoxMedbay,
.image_path = "loaded/medbay.png",
},
{
.type = BoxSolarPanel,
.image_path = "loaded/solarpanel.png",
},
{
.type = BoxExplosive,
.image_path = "loaded/explosive.png",
.needs_tobe_unlocked = true,
},
};
const int boxes_len = sizeof(boxes) / sizeof(*boxes);
@ -171,6 +179,8 @@ init(void)
image_stars2 = load_image("loaded/stars2.png");
image_sun = load_image("loaded/sun.png");
image_medbay_used = load_image("loaded/medbay_used.png");
image_mystery = load_image("loaded/mystery.png");
image_explosion = load_image("loaded/explosion.png");
}
// socket initialization
@ -273,14 +283,30 @@ myentity()
return to_return;
}
bool can_build(int i)
{
bool allow_building = true;
if (boxinfo((enum BoxType)i).needs_tobe_unlocked)
{
allow_building = gs.players[myplayer].unlocked_bombs;
}
return allow_building;
}
void attempt_to_build(int i)
{
if (can_build(i))
cur_editing_boxtype = i;
}
static void
ui(bool draw, float dt, float width, float height)
{
static float cur_opacity = 1.0f;
cur_opacity = lerp(cur_opacity, myentity() != NULL ? 1.0f : 0.0f, dt * 5.0f);
if (cur_opacity <= 0.01f) {
return;
}
cur_opacity = lerp(cur_opacity, myentity() != NULL ? 1.0f : 0.0f, dt * 5.0f);
if (cur_opacity <= 0.01f) {
return;
}
if (draw)
sgp_push_transform();
@ -326,8 +352,8 @@ ui(bool draw, float dt, float width, float height)
mouse_pos)
&& mouse_pressed) {
// "handle" mouse pressed
attempt_to_build(i);
mouse_pressed = false;
cur_editing_boxtype = i;
}
if (draw) {
@ -339,7 +365,14 @@ ui(bool draw, float dt, float width, float height)
sgp_set_image(0, image_itemframe);
}
sgp_draw_textured_rect(x, y, itemframe_width, itemframe_height);
sgp_set_image(0, boxinfo((enum BoxType)i).image);
struct BoxInfo info = boxinfo((enum BoxType)i);
if (can_build(i))
{
sgp_set_image(0, info.image);
}
else {
sgp_set_image(0, image_mystery);
}
transform_scope
{
float item_x = x + item_offset_x;
@ -370,8 +403,8 @@ static void draw_dots(V2 camera_pos, float gap)
if (fabsf(star.y - camera_pos.y) > VISION_RADIUS)
continue;
star.x += hash11(star.x * 100.0f + star.y * 67.0f)*gap;
star.y += hash11(star.y * 93.0f + star.x * 53.0f)*gap;
star.x += hash11(star.x * 100.0f + star.y * 67.0f) * gap;
star.y += hash11(star.y * 93.0f + star.x * 53.0f) * gap;
sgp_draw_point(star.x, star.y);
}
}
@ -706,8 +739,8 @@ frame(void)
{
if (b->type == BoxThruster)
{
dbg_rect(box_pos(b));
dbg_line(box_pos(b), V2add(box_pos(b), V2scale(thruster_force(b), -1.0f)));
dbg_rect(entity_pos(b));
dbg_line(entity_pos(b), V2add(entity_pos(b), V2scale(thruster_force(b), -1.0f)));
}
}
#endif
@ -715,14 +748,14 @@ frame(void)
float cur_alpha = sgp_get_color().a;
Color from = WHITE;
Color to = colhex(255, 0, 0);
Color result = Collerp(from, to, b->energy_used/BATTERY_CAPACITY);
Color result = Collerp(from, to, b->energy_used / BATTERY_CAPACITY);
sgp_set_color(result.r, result.g, result.b, cur_alpha);
}
transform_scope
{
sgp_rotate_at(entity_rotation(g) + rotangle(b->compass_rotation),
box_pos(b).x,
box_pos(b).y);
entity_pos(b).x,
entity_pos(b).y);
if (b->box_type == BoxThruster) {
transform_scope
@ -735,8 +768,8 @@ frame(void)
// float scaling = 1.1;
// sgp_translate(-(scaling*BOX_SIZE - BOX_SIZE), 0.0);
// sgp_scale(scaling, 1.0);
sgp_scale_at(scaling, 1.0f, box_pos(b).x, box_pos(b).y);
draw_texture_centered(box_pos(b), BOX_SIZE);
sgp_scale_at(scaling, 1.0f, entity_pos(b).x, entity_pos(b).y);
draw_texture_centered(entity_pos(b), BOX_SIZE);
sgp_reset_image(0);
}
}
@ -752,19 +785,19 @@ frame(void)
img = image_medbay_used;
}
sgp_set_image(0, img);
draw_texture_centered(box_pos(b), BOX_SIZE);
draw_texture_centered(entity_pos(b), BOX_SIZE);
sgp_reset_image(0);
if (b->box_type == BoxSolarPanel)
{
Color to_set = colhexcode(0xeb9834);
to_set.a = b->sun_amount*0.5f;
to_set.a = b->sun_amount * 0.5f;
set_color(to_set);
draw_color_rect_centered(box_pos(b), BOX_SIZE);
draw_color_rect_centered(entity_pos(b), BOX_SIZE);
}
sgp_set_color(0.5f, 0.1f, 0.1f, b->damage);
draw_color_rect_centered(box_pos(b), BOX_SIZE);
draw_color_rect_centered(entity_pos(b), BOX_SIZE);
}
}
@ -784,7 +817,15 @@ frame(void)
sgp_reset_image(0);
}
}
if (e->is_explosion)
{
sgp_set_image(0, image_explosion);
sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f - (e->explosion_progresss / EXPLOSION_TIME));
draw_texture_centered(e->explosion_pos, EXPLOSION_RADIUS*2.0f);
sgp_reset_image(0);
}
}
// gold target
set_color(GOLD);
sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f);
@ -807,7 +848,7 @@ frame(void)
dbg_drawall();
} // world space transform end
}
}
// UI drawn in screen space
ui(true, dt, width, height);
@ -843,7 +884,7 @@ void event(const sapp_event* e)
int key_num = e->key_code - SAPP_KEYCODE_0;
int target_box = key_num - 1;
if (target_box < BoxLast) {
cur_editing_boxtype = target_box;
attempt_to_build(target_box);
}
if (!mouse_frozen) {

@ -21,6 +21,11 @@ void server(void* data)
initialize(&gs, entity_data, entities_size);
Log("Allocated %zu bytes for entities\n", entities_size);
// unlock the explosive
if (true)
{
}
// one box policy
if (false)
{
@ -122,6 +127,9 @@ void server(void* data)
event.peer->data = (void*)player_slot;
gs.players[player_slot] = (struct Player){ 0 };
gs.players[player_slot].connected = true;
#ifdef UNLOCK_ALL
gs.players[player_slot].unlocked_bombs = true;
#endif
}
break;

@ -6,13 +6,12 @@
#define PLAYER_SIZE ((V2){.x = BOX_SIZE, .y = BOX_SIZE})
#define PLAYER_MASS 0.5f
#define PLAYER_JETPACK_FORCE 2.0f
//#define PLAYER_JETPACK_SPICE_PER_SECOND 0.3f
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.0f
#define PLAYER_JETPACK_SPICE_PER_SECOND 0.3f
#define MAX_HAND_REACH 1.0f
#define GOLD_COLLECT_RADIUS 0.3f
#define BUILD_BOX_SNAP_DIST_TO_SHIP 0.2f
#define BOX_MASS 1.0f
#define COLLISION_DAMAGE_SCALING 0.1f
#define COLLISION_DAMAGE_SCALING 0.15f
#define THRUSTER_FORCE 4.0f
#define THRUSTER_ENERGY_USED_PER_SECOND 0.05f
#define VISION_RADIUS 16.0f
@ -25,6 +24,11 @@
#define DAMAGE_TO_PLAYER_PER_BLOCK 0.1f
#define BATTERY_CAPACITY DAMAGE_TO_PLAYER_PER_BLOCK
#define PLAYER_ENERGY_RECHARGE_PER_SECOND 0.1f
#define EXPLOSION_TIME 0.5f
#define EXPLOSION_PUSH_STRENGTH 5.0f
#define EXPLOSION_DAMAGE_PER_SEC 10.0f
#define EXPLOSION_RADIUS 1.0f
#define EXPLOSION_DAMAGE_THRESHOLD 0.2f // how much damage until it explodes
#define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on
#define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
@ -86,6 +90,7 @@ enum BoxType
BoxCockpit,
BoxMedbay,
BoxSolarPanel,
BoxExplosive,
BoxLast,
};
@ -150,6 +155,12 @@ typedef struct Entity
EntityID currently_inside_of_box;
float goldness; // how much the player is a winner
// explosion
bool is_explosion;
V2 explosion_pos;
V2 explosion_vel;
float explosion_progresss; // in seconds
// grids
bool is_grid;
float total_energy_capacity;
@ -171,6 +182,7 @@ typedef struct Entity
typedef struct Player
{
bool connected;
bool unlocked_bombs;
EntityID entity;
InputFrame input;
} Player;
@ -269,7 +281,6 @@ V2 grid_world_to_local(Entity* grid, V2 world);
V2 grid_snapped_box_pos(Entity* grid, V2 world); // returns the snapped pos in world coords
float entity_angular_velocity(Entity* grid);
V2 entity_shape_pos(Entity* box);
V2 box_pos(Entity* box); // returns in world coords
float box_rotation(Entity* box);
// thruster

Loading…
Cancel
Save