diff --git a/Flight.vcxproj b/Flight.vcxproj
index 26ad283..3b33e41 100644
--- a/Flight.vcxproj
+++ b/Flight.vcxproj
@@ -177,12 +177,13 @@
NDEBUG;RELEASE;_CONSOLE;%(PreprocessorDefinitions)
true
$(ProjectDir)\thirdparty\enet\include;$(ProjectDir)\thirdparty\Chipmunk2D\include\chipmunk;$(ProjectDir)\thirdparty;$(ProjectDir)\thirdparty\Chipmunk2D\include;$(ProjectDir)\thirdparty\minilzo;$(ProjectDir)\thirdparty\opus\include
+ false
Console
true
true
- true
+ DebugFull
$(CoreLibraryDependencies);%(AdditionalDependencies);Ws2_32.lib;winmm.lib;$(ProjectDir)\thirdparty\opus\win32\VS2015\x64\Release\opus.lib
diff --git a/Flight.vcxproj.user b/Flight.vcxproj.user
index a8c5a48..840ad31 100644
--- a/Flight.vcxproj.user
+++ b/Flight.vcxproj.user
@@ -4,7 +4,7 @@
false
- --host
+ host=true
WindowsLocalDebugger
diff --git a/buildsettings.h b/buildsettings.h
index 875321d..455f21e 100644
--- a/buildsettings.h
+++ b/buildsettings.h
@@ -26,7 +26,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 2ea4401..41aeac4 100644
Binary files a/flight.rdbg and b/flight.rdbg differ
diff --git a/gamestate.c b/gamestate.c
index 88a73d5..e5fb6d3 100644
--- a/gamestate.c
+++ b/gamestate.c
@@ -407,6 +407,13 @@ LauncherTarget missile_launcher_target(GameState *gs, Entity *launcher)
return (LauncherTarget){.target_found = target_found, .facing_angle = to_face};
}
+void destroy_constraints(cpBody *body, cpConstraint *constraint, void *data)
+{
+ ((Entity*)cpConstraintGetUserData(constraint))->landed_constraint = NULL;
+ cpSpaceRemoveConstraint(cpBodyGetSpace(body), constraint);
+ cpConstraintFree(constraint);
+}
+
void destroy_child_shape(cpBody *body, cpShape *shape, void *data)
{
GameState *gs = (GameState *)data;
@@ -447,14 +454,22 @@ void entity_memory_free(GameState *gs, Entity *e)
cpShapeFree(e->shape);
e->shape = NULL;
}
+ if (e->landed_constraint != NULL)
+ {
+ cpSpaceRemoveConstraint(gs->space, e->landed_constraint);
+ cpConstraintFree(e->landed_constraint);
+ e->landed_constraint = NULL;
+ }
if (e->body != NULL)
{
+ // need to do this here because body which constraint is attached to can be destroyed
+ // NOT TRUE: can't do this here because the handle to the constraint cannot be set to NULL. Constraints are freed by the entities that own them
+ cpBodyEachConstraint(e->body, destroy_constraints, NULL);
cpBodyEachShape(e->body, destroy_child_shape, (void *)gs);
cpSpaceRemoveBody(gs->space, e->body);
cpBodyFree(e->body);
e->body = NULL;
}
-
Entity *front_of_free_list = get_entity(gs, gs->free_list);
if (front_of_free_list != NULL)
flight_assert(!front_of_free_list->exists);
@@ -518,6 +533,12 @@ void create_body(GameState *gs, Entity *e)
cpBodySetUserData(e->body, (void *)e);
}
+// must always call this after creating a constraint
+void on_create_constraint(Entity *e, cpConstraint* c)
+{
+ cpConstraintSetUserData(c, (cpDataPointer)e);
+}
+
cpVect player_vel(GameState *gs, Entity *player)
{
flight_assert(player->is_player);
@@ -627,8 +648,6 @@ void create_player(Player *player)
#endif
}
-
-
void create_orb(GameState *gs, Entity *e)
{
create_body(gs, e);
@@ -1606,6 +1625,41 @@ SerMaybeFailure ser_entity(SerState *ser, GameState *gs, Entity *e)
case BoxMissileLauncher:
SER_MAYBE_RETURN(ser_f(ser, &e->missile_construction_charge));
break;
+ case BoxLandingGear:
+ {
+ bool is_null = e->landed_constraint == NULL;
+ SER_VAR(&is_null);
+ if (!is_null)
+ {
+ EntityID from = {0};
+ EntityID to = {0};
+ cpVect pin = {0};
+ if (ser->serializing)
+ {
+ from = get_id(gs, cp_body_entity(cpConstraintGetBodyA(e->landed_constraint)));
+ to = get_id(gs, cp_body_entity(cpConstraintGetBodyB(e->landed_constraint)));
+ pin = cpPivotJointGetAnchorA(e->landed_constraint);
+ }
+ SER_MAYBE_RETURN(ser_entityid(ser, &from));
+ SER_MAYBE_RETURN(ser_entityid(ser, &to));
+ SER_MAYBE_RETURN(ser_V2(ser, &pin));
+ if (!ser->serializing)
+ {
+ Entity *from_entity = get_entity(gs, from);
+ Entity *to_entity = get_entity(gs, to);
+ if (from_entity == NULL || to_entity == NULL)
+ {
+ }
+ else
+ {
+ e->landed_constraint = cpPivotJointNew(from_entity->body, to_entity->body, pin);
+ cpSpaceAddConstraint(gs->space, e->landed_constraint);
+ on_create_constraint(e, e->landed_constraint);
+ }
+ }
+ }
+ break;
+ }
default:
break;
}
@@ -2434,7 +2488,7 @@ void create_initial_world(GameState *gs)
create_hard_shell_station(gs, (cpVect){-5.0, 5.0}, BoxCloaking);
#endif
-#if 1 // scanner box
+#if 0 // scanner box
bool indestructible = false;
enum CompassRotation rot = Right;
{
@@ -2458,6 +2512,29 @@ void create_initial_world(GameState *gs)
#endif
+#if 1 // landing gear box
+ bool indestructible = false;
+ cpVect from = cpv(4.0 * BOX_SIZE, 0.0);
+ enum CompassRotation rot = Right;
+ {
+ Entity *grid = new_entity(gs);
+ grid_create(gs, grid);
+ entity_set_pos(grid, from);
+ BOX_AT_TYPE(grid, ((cpVect){0.0, 0.0}), BoxLandingGear);
+ // BOX_AT(grid, ((cpVect){0.0, -BOX_SIZE}));
+ // BOX_AT_TYPE(grid, ((cpVect){BOX_SIZE, 0.0}), BoxMerge);
+ entity_ensure_in_orbit(gs, grid);
+ cpBodySetVelocity(grid->body, cpv(0.1, 0.0));
+ }
+ {
+ Entity *grid = new_entity(gs);
+ grid_create(gs, grid);
+ entity_set_pos(grid, cpvadd(from, cpv(2.0 * BOX_SIZE, 0.0)));
+ BOX_AT_TYPE(grid, ((cpVect){0.0, 0.0}), BoxHullpiece);
+ entity_ensure_in_orbit(gs, grid);
+ }
+#endif
+
#if 0 // merge box
bool indestructible = false;
double theta = deg2rad(65.0);
@@ -3415,6 +3492,27 @@ void process(struct GameState *gs, double dt)
cur_box->scanner_head_rotate += cur_box->scanner_head_rotate_speed * dt;
cur_box->scanner_head_rotate = fmod(cur_box->scanner_head_rotate, 2.0 * PI);
}
+ if (cur_box->box_type == BoxLandingGear)
+ {
+ if (cur_box->landed_constraint == NULL)
+ {
+ cpVect along = box_facing_vector(cur_box);
+ cpVect from = cpvadd(entity_pos(cur_box), cpvmult(along, BOX_SIZE / 2.0 + 0.03));
+ cpVect to = cpvadd(from, cpvmult(along, LANDING_GEAR_MAX_DIST));
+
+ cpSegmentQueryInfo query_result = {0};
+ cpShape *found = cpSpaceSegmentQueryFirst(gs->space, from, to, 0.0, FILTER_DEFAULT, &query_result);
+ if (found != NULL && cpShapeGetBody(found) != box_grid(cur_box)->body)
+ {
+ cpVect anchor = cpvadd(entity_pos(cur_box), cpvmult(along, BOX_SIZE / 2.0));
+ cpBody *a = box_grid(cur_box)->body;
+ cpBody *b = cpShapeGetBody(found);
+ cur_box->landed_constraint = cpPivotJointNew(a, b, anchor);
+ cpSpaceAddConstraint(gs->space, cur_box->landed_constraint);
+ on_create_constraint(cur_box, cur_box->landed_constraint);
+ }
+ }
+ }
}
}
}
diff --git a/loaded/landing_gear.png b/loaded/landing_gear.png
new file mode 100644
index 0000000..8a6e462
Binary files /dev/null and b/loaded/landing_gear.png differ
diff --git a/main.c b/main.c
index 6d6e269..eb77b5c 100644
--- a/main.c
+++ b/main.c
@@ -139,6 +139,7 @@ static sg_image image_noenergy;
static sg_image image_orb;
static sg_image image_orb_frozen;
static sg_image image_radardot;
+static sg_image image_landing_gear;
static sg_image image_pip;
static sg_image fire_rendertarget;
@@ -232,6 +233,10 @@ static struct BoxInfo
.type = BoxMerge,
.image_path = "loaded/merge.png",
},
+ {
+ .type = BoxLandingGear,
+ .image_path = "loaded/landing_gear.png",
+ },
};
// suppress compiler warning about ^^ above used in floating point context
#define ARRLENF(arr) ((float)sizeof(arr) / sizeof(*arr))
@@ -852,6 +857,7 @@ static void init(void)
image_orb = load_image("loaded/orb.png");
image_orb_frozen = load_image("loaded/orb_frozen.png");
image_radardot = load_image("loaded/radardot.png");
+ image_landing_gear = load_image("loaded/landing_gear.png");
image_pip = load_image("loaded/pip.png");
}
diff --git a/types.h b/types.h
index aabc873..3594cc6 100644
--- a/types.h
+++ b/types.h
@@ -58,6 +58,8 @@
#define THRUSTER_ENERGY_USED_PER_SECOND 0.005
#define THRUSTER_DAMAGE_PER_SEC 2.0
+#define LANDING_GEAR_MAX_DIST (BOX_SIZE * 0.25)
+
#define GYROSCOPE_ENERGY_USED_PER_SECOND 0.005f
#define GYROSCOPE_TORQUE 1.5f
#define GYROSCOPE_PROPORTIONAL_INERTIAL_RESPONSE 0.7 // between 0-1. How strongly responds to rotation, to stop the rotation
@@ -189,6 +191,7 @@ enum BoxType
BoxCloaking,
BoxMissileLauncher,
BoxMerge,
+ BoxLandingGear,
BoxLast,
};
@@ -374,6 +377,9 @@ typedef struct Entity
double scanner_head_rotate_speed; // not serialized, cosmetic
double scanner_head_rotate;
+ // landing gear only
+ cpConstraint *landed_constraint; // when null is landed
+
PlatonicDetection detected_platonics[SCANNER_MAX_PLATONICS]; // intensity of 0.0 means undetected
struct ScannerPoint