Refer to rooms by unique id, stored in blend. More assets

main
Cameron Murphy Reikes 7 months ago
parent c61d6a0a96
commit 00186f233f

@ -14,6 +14,10 @@ D = bpy.data
ROOMS_EXPORT_NAME = "rooms"
EXPORT_DIRECTORY = "../assets/exported_3d"
# the roomid is a unique reference to a room the gameplay code does. It's saved in the blend file and incremented as its used.
if not "next_roomid" in C.scene:
C.scene["next_roomid"] = 0
if os.path.exists(bpy.path.abspath(f"//{EXPORT_DIRECTORY}")):
shutil.rmtree(bpy.path.abspath(f"//{EXPORT_DIRECTORY}"))
os.makedirs(bpy.path.abspath(f"//{EXPORT_DIRECTORY}"))
@ -345,6 +349,11 @@ def export_meshes_and_levels():
for room_collection in rooms.children:
write_string(f, room_collection.name) # the name of the room is the name of the room collection
if not "roomid" in room_collection:
room_collection["roomid"] = C.scene["next_roomid"]
C.scene["next_roomid"] += 1
write_u64(f, room_collection["roomid"])
# placed meshes (exported mesh name (which is the object's name), location, rotation, scale)
placed_meshes = []
if True:

BIN
art/SpawnRing.png (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

@ -1,3 +1,11 @@
@image edit_brain:
{
filepath: "EditBrain.png",
}
@image place:
{
filepath: "Place.png",
}
@image shifted_farmer:
{
filepath: "shifted_farmer.png",

BIN
assets/EditBrain.png (Stored with Git LFS)

Binary file not shown.

BIN
assets/Place.png (Stored with Git LFS)

Binary file not shown.

BIN
assets/PlayerSpawn.png (Stored with Git LFS)

Binary file not shown.

122
main.c

@ -1370,6 +1370,7 @@ typedef struct Room
bool camera_offset_is_overridden;
Vec3 camera_offset;
String8 name;
u64 roomid;
PlacedMesh *placed_mesh_list;
CollisionCylinder *collision_list;
PlacedEntity *placed_entity_list;
@ -1431,6 +1432,7 @@ ThreeDeeLevel load_level(Arena *arena, String8 binary_file)
{
Room *new_room = PushArray(arena, Room, 1);
ser_String8(&ser, &new_room->name, arena);
ser_u64(&ser, &new_room->roomid);
ser_bool(&ser, &new_room->camera_offset_is_overridden);
if(new_room->camera_offset_is_overridden)
@ -1540,11 +1542,11 @@ Mat4 projection = {0};
Room mystery_room = {
.name = S8LitC("???"),
};
Room *room_by_name(ThreeDeeLevel *level, String8 name) {
Room *get_room(ThreeDeeLevel *level, u64 roomid) {
Room *ret = &mystery_room;
for(Room *cur = level->room_list_first; cur; cur = cur->next)
{
if(S8Match(cur->name, name, 0))
if(cur->roomid == roomid)
{
ret = cur;
break;
@ -1552,25 +1554,13 @@ Room *room_by_name(ThreeDeeLevel *level, String8 name) {
}
return ret;
}
Room *room_by_index(ThreeDeeLevel *level, int index) {
Room *ret = &mystery_room;
int i = 0;
for(Room *cur = level->room_list_first; cur; cur = cur->next) {
if(i == index) {
ret = cur;
break;
}
i += 1;
}
return ret;
}
Room *get_cur_room(GameState *gs, ThreeDeeLevel *level)
{
Room *in_room = 0;
if(gs->edit.enabled) {
in_room = room_by_index(level, gs->edit.room_index);
in_room = get_room(level, gs->edit.current_roomid);
} else {
in_room = room_by_name(level, gs->current_room_name);
in_room = get_room(level, gs->current_roomid);
}
assert(in_room);
return in_room;
@ -1583,6 +1573,18 @@ int num_rooms(ThreeDeeLevel *level) {
return ret;
}
Entity *npcs_entity(NpcKind kind) {
Entity *ret = 0;
ENTITIES_ITER(gs.entities)
{
if (it->is_npc && it->npc_kind == kind)
{
assert(!ret); // no duplicate entities for this character. Bad juju when more than one npc
ret = it;
}
}
return ret;
}
Vec4 IsPoint(Vec3 point)
{
@ -1794,7 +1796,7 @@ CanTalkTo get_can_talk_to(Entity *e)
CanTalkTo to_return = {0};
ENTITIES_ITER(gs.entities)
{
if(it != e && (it->is_npc) && S8Match(it->current_room_name, e->current_room_name, 0))
if(it != e && (it->is_npc) && it->current_roomid == e->current_roomid)
{
BUFF_APPEND(&to_return, it);
}
@ -1808,7 +1810,7 @@ Entity *get_targeted(Entity *from, NpcKind targeted)
{
if(it != from && (it->is_npc) && it->npc_kind == targeted)
{
if(S8Match(it->current_room_name, from->current_room_name, 0))
if(it->current_roomid == from->current_roomid)
{
return it;
}
@ -2066,7 +2068,7 @@ bool perform_action(GameState *gs, Entity *from, Action a)
{
if(
!it->is_player
&& S8Match(it->current_room_name, from->current_room_name, 0)
&& it->current_roomid == from->current_roomid
&& it != from
&& it != targeted
) {
@ -2234,13 +2236,12 @@ Vec2 point_plane(Vec3 p)
#define parse_enumstr(arena, enum_str, errors, string_array, enum_kind_name, prefix) parse_enumstr_impl(arena, enum_str, string_array, ARRLEN(string_array), errors, enum_kind_name, prefix)
void transition_to_room(GameState *gs, ThreeDeeLevel *level, String8 new_room_name)
void transition_to_room(GameState *gs, ThreeDeeLevel *level, u64 new_roomid)
{
Log("Transitioning to %.*s...\n", S8VArg(new_room_name));
Room *new_room = get_room(level, new_roomid);
Log("Transitioning to %.*s...\n", S8VArg(new_room->name));
assert(gs);
(void)level;
gs->current_room_name = new_room_name;
gs->current_roomid = new_roomid;
}
@ -2263,7 +2264,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve
Entity *cur_entity = new_entity(gs);
cur_entity->npc_kind = cur->npc_kind;
cur_entity->pos = point_plane(cur->t.offset);
cur_entity->current_room_name = cur_room->name;
cur_entity->current_roomid = cur_room->roomid;
}
}
@ -2274,7 +2275,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve
gs->edit.enabled = true;
#endif
transition_to_room(gs, &level_threedee, level->room_list_first->name);
transition_to_room(gs, &level_threedee, level->room_list_first->roomid);
}
@ -2401,6 +2402,12 @@ void ser_entity(SerState *ser, Entity *e)
ser_EntityRef(ser, &e->talking_to);
}
void ser_Npc(SerState *ser, Npc *npc) {
ser_TextChunk(ser, &npc->name);
ser_int(ser, &npc->kind);
ser_TextChunk(ser, &npc->prompt);
}
void ser_GameState(SerState *ser, GameState *gs)
{
if(ser->serializing) ser->version = VMax - 1;
@ -2412,6 +2419,10 @@ void ser_GameState(SerState *ser, GameState *gs)
ser_uint64_t(ser, &gs->tick);
ser_bool(ser, &gs->won);
ser_double(ser, &gs->time);
SER_BUFF(ser, Npc, &gs->characters);
int num_entities = MAX_ENTITIES;
ser_int(ser, &num_entities);
@ -3167,6 +3178,7 @@ Armature *armatures[] = {
Mesh mesh_simple_worm = {0};
Mesh mesh_shotgun = {0};
Mesh mesh_spawnring = {0};
Mesh mesh_angel_totem = {0};
Mesh mesh_tombstone = {0};
@ -3257,6 +3269,9 @@ void init(void)
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/Shotgun.bin"));
mesh_shotgun = load_mesh(persistent_arena, binary_file, S8Lit("Shotgun.bin"));
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/SpawnRing.bin"));
mesh_spawnring = load_mesh(persistent_arena, binary_file, S8Lit("SpawnRing.bin"));
binary_file = LoadEntireFile(frame_arena, S8Lit("assets/exported_3d/AngelTotem.bin"));
mesh_angel_totem = load_mesh(persistent_arena, binary_file, S8Lit("AngelTotem.bin"));
@ -4546,7 +4561,7 @@ Vec2 move_and_slide(MoveSlideParams p)
{
ENTITIES_ITER(gs.entities)
{
if (it != p.from && !(it->is_npc && it->dead) && !it->is_world && S8Match(it->current_room_name, p.from->current_room_name, 0))
if (it != p.from && !(it->is_npc && it->dead) && !it->is_world && it->current_roomid == p.from->current_roomid)
{
BUFF_APPEND(&to_check, ((CollisionObj){.circle.center = it->pos, .circle.radius = entity_radius(it), it}));
}
@ -5707,7 +5722,7 @@ void frame(void)
ENTITIES_ITER(gs.entities)
{
if(it->is_npc && S8Match(it->current_room_name, gs.current_room_name, 0))
if(it->is_npc && it->current_roomid == get_cur_room(&gs, &level_threedee)->roomid)
{
assert(it->is_npc);
Transform draw_with = entity_transform(it);
@ -5870,28 +5885,30 @@ void frame(void)
Vec2 left_right_buttons_size = V2(whole_height, whole_height);
float whole_width = left_right_buttons_size.x*2.0f + padding*2.0f + max_room_name_width;
Room *cur_room = room_by_index(&level_threedee, gs.edit.room_index);
Room *cur_room = get_cur_room(&gs, &level_threedee);
bool left = imbutton(aabb_at(V2(screen_size().x/2.0f - whole_width/2.0f, screen_size().y - padding/2.0f), left_right_buttons_size), .icon = &image_right_arrow, .icon_padding = whole_height*0.1f, .nobg = true, .icon_flipped = true);
bool right = imbutton(aabb_at(V2(screen_size().x/2.0f + whole_width/2.0f - left_right_buttons_size.x, screen_size().y - padding/2.0f), left_right_buttons_size), .icon = &image_right_arrow, .icon_padding = whole_height*0.1f, .nobg = true);
// bool left = imbutton(aabb_at(mouse_pos, left_right_buttons_size), .icon = &image_left_arrow, .icon_padding = whole_height*0.1f, .nobg = true);
AABB drawn_bounds = draw_centered_text((TextParams){false, cur_room->name, V2(screen_size().x/2.0f, screen_size().y - padding), WHITE, 1.0f, .use_font = &room_name_font});
dbgline(V2(screen_size().x*0.25f, screen_size().y), V2(screen_size().x*0.25f, screen_size().y - whole_height));
dbgrect(drawn_bounds);
if(left) gs.edit.room_index -= 1;
if(right) gs.edit.room_index += 1;
// gs.edit.room_index %= num_rooms(&level_threedee); Why the fuck doesn't this just work
gs.edit.room_index = mod(gs.edit.room_index, num_rooms(&level_threedee));
if(left) cur_room = cur_room->prev ? cur_room->prev : level_threedee.room_list_last;
if(right) cur_room = cur_room->next ? cur_room->next : level_threedee.room_list_first;
gs.edit.current_roomid = cur_room->roomid;
if(mouse_down) {
Vec3 mouse_movement_on_plane = {0};
{
Vec2 to_pos = mouse_pos;
Vec2 from_pos = AddV2(mouse_pos, mouse_movement);
// dbgline(from_pos, to_pos);
Vec3 to_plane = point_on_plane_from_camera_point(view, to_pos);
Vec3 from_plane = point_on_plane_from_camera_point(view, from_pos);
Vec3 movement_on_plane = SubV3(to_plane, from_plane);
mouse_movement_on_plane = SubV3(to_plane, from_plane);
// dbg3dline(from_plane, to_plane);
Vec2 movement = V2(movement_on_plane.x, movement_on_plane.z);
gs.edit.camera_panning_target = AddV2(gs.edit.camera_panning_target, movement);
}
if(mouse_down) {
gs.edit.camera_panning_target = AddV2(gs.edit.camera_panning_target, point_plane(mouse_movement_on_plane));
}
// characters sidebar
@ -5908,12 +5925,14 @@ void frame(void)
draw_centered_text((TextParams){false, S8Lit("Characters"), AddV2(sidebar.upper_left, V2(aabb_size(sidebar).x/2.0f, 30.0f)), WHITE, 1.0f, .use_font = &font_for_text_input});
float plus_size = 50.0f;
if(imbutton(aabb_centered(AddV2(sidebar.lower_right, V2(-aabb_size(sidebar).x/2.0f, -plus_size - screen_margin )),V2(plus_size, plus_size)), 1.0f, S8Lit(""), .icon = &image_add, .nobg = true)) {
BUFF_APPEND(&gs.characters, (Npc){.name = TextChunkLit("<Unnamed>")});
Npc new = (Npc){.name = TextChunkLit("<Unnamed>"), .kind = get_next_kind(&gs)};
BUFF_APPEND(&gs.characters, new);
}
Vec2 cur = sidebar.upper_left; // upper left corner of current
BUFF_ITER_I(Npc, &gs.characters, i) {
draw_quad((DrawParams){quad_at(cur, V2(width, character_panel_height)), IMG(image_white_square), ((Color) { 0.7f, 0.7f, 0.7f, 1.0f }), 1.0f, .layer = LAYER_UI});
Quad panel = quad_at(cur, V2(width, character_panel_height));
draw_quad((DrawParams){panel, IMG(image_white_square), ((Color) { 0.7f, 0.7f, 0.7f, 1.0f }), 1.0f, .layer = LAYER_UI});
String8 name = TextChunkString8(it->name);
bool rename = imbutton_key((ImbuttonArgs){aabb_at(cur, V2(width, 50.0f)), 1.0f, name, .key = tprint("%d %d", __LINE__, i), .dt = unwarped_dt}); // the hack here in the key is off the charts. Holy moly.
get_state(state, TextInputResultKey, "%d %d", __LINE__, i);
@ -5924,8 +5943,29 @@ void frame(void)
if(new.size > 0) {
chunk_from_s8(&it->name, new);
}
Vec2 button_size = V2(75.0f, 75.0f);
bool place = imbutton_key((ImbuttonArgs){aabb_at(AddV2(panel.lr, V2(-button_size.x, button_size.y)), button_size), 1.0f, S8Lit(""), .key = tprint("%d %d", __LINE__, i), .dt = unwarped_dt, .icon = &image_place, .nobg = true});
if(place) {
Entity *existing = npcs_entity(it->kind);
if(!existing) {
existing = new_entity(&gs);
existing->npc_kind = it->kind;
existing->is_npc = true;
}
existing->current_roomid = cur_room->roomid;
gs.edit.placing_npc = it->kind;
}
cur.y -= character_panel_height;
}
Entity *placing = npcs_entity(gs.edit.placing_npc);
if(placing) {
Log("%llu\n", placing->current_roomid);
placing->pos = point_plane(point_on_plane_from_camera_point(view, mouse_pos));
if(pressed.mouse_down) {
gs.edit.placing_npc = NPC_nobody;
}
}
}
gs.edit.camera_panning = LerpV2(gs.edit.camera_panning, unwarped_dt * 19.0f, gs.edit.camera_panning_target);
@ -5935,7 +5975,7 @@ void frame(void)
{
ENTITIES_ITER(gs.entities)
{
if (it->is_npc && !it->is_player && S8Match(it->current_room_name, gs.current_room_name, 0))
if (it->is_npc && !it->is_player && it->current_roomid == get_cur_room(&gs, &level_threedee)->roomid)
{
if(it->undismissed_action)
{
@ -6564,7 +6604,7 @@ void frame(void)
if(it->is_npc)
{
bool doesnt_prompt_on_dirty_perceptions = false
|| !S8Match(it->current_room_name, gs.current_room_name, 0)
|| it->current_roomid != get_cur_room(&gs, &level_threedee)->roomid
;
if (it->perceptions_dirty && doesnt_prompt_on_dirty_perceptions)
{

@ -232,7 +232,7 @@ typedef struct Entity
Vec2 vel; // only used sometimes, like in old man and bullet
float damage; // at 1.0, dead! zero initialized
bool dead;
String8 current_room_name;
u64 current_roomid;
// npcs
bool is_player;
@ -287,9 +287,10 @@ typedef struct Npc {
typedef struct EditorState {
bool enabled;
int room_index;
u64 current_roomid;
Vec2 camera_panning_target;
Vec2 camera_panning;
NpcKind placing_npc;
} EditorState;
typedef struct GameState {
@ -308,7 +309,7 @@ typedef struct GameState {
BUFF(Npc, 10) characters;
// these must point entities in its own array.
String8 current_room_name;
u64 current_roomid;
Entity *player;
Entity *world_entity;
Entity entities[MAX_ENTITIES];
@ -342,6 +343,14 @@ Npc *npc_data(GameState *gs, NpcKind kind) {
assert(false);
return 0;
}
NpcKind get_next_kind(GameState *gs) {
NpcKind max_found = 0;
BUFF_ITER(Npc, &gs->characters) {
assert(it->kind != 0);
if(it->kind > max_found) max_found = it->kind;
}
return max_found + 1;
}
// to fix initializer is not constant
#define S8LitC(s) {(u8 *)(s), sizeof(s)-1}

Loading…
Cancel
Save