From 3cf492b0b458239b6e8a857eb5c686a0e635c9f9 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Wed, 27 Sep 2023 20:06:50 -0700 Subject: [PATCH] Level editor assets and camera, room toggling --- assets.mdesk | 4 ++ assets/Move.png | 3 + assets/PlayerSpawn.png | 3 + assets/Retry.png | 3 + assets/RightArrow.png | 3 + main.c | 160 ++++++++++++++++++++++++++++++++++------- makeprompt.h | 10 +++ 7 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 assets/Move.png create mode 100644 assets/PlayerSpawn.png create mode 100644 assets/Retry.png create mode 100644 assets/RightArrow.png diff --git a/assets.mdesk b/assets.mdesk index 50c2f2b..4fc20d2 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -114,6 +114,10 @@ { filepath: "hovering_circle.png", } +@image right_arrow: +{ + filepath: "RightArrow.png", +} @image white_square: { filepath: "white square.png", diff --git a/assets/Move.png b/assets/Move.png new file mode 100644 index 0000000..2737a18 --- /dev/null +++ b/assets/Move.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58a67d4dfd7d22d59f0f33c249dd275c4fe3660866345f7012f9eb33a3ef483d +size 2825 diff --git a/assets/PlayerSpawn.png b/assets/PlayerSpawn.png new file mode 100644 index 0000000..0f661f1 --- /dev/null +++ b/assets/PlayerSpawn.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a66b5c17c1d23cd6d9ead831157daed74e14785922f46d5fca3454ad8e80fd9 +size 2780 diff --git a/assets/Retry.png b/assets/Retry.png new file mode 100644 index 0000000..b1e2eb0 --- /dev/null +++ b/assets/Retry.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e29996325bc990f4da84cf6d7386bbe541a9b5c03121f369f3a080563a82d86 +size 3261 diff --git a/assets/RightArrow.png b/assets/RightArrow.png new file mode 100644 index 0000000..1f501e2 --- /dev/null +++ b/assets/RightArrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:143f490892d15f4d7ded687293e8cd783b211c4e1d1077080388818ba04bdb9f +size 2144 diff --git a/main.c b/main.c index 14d2bfe..0f21333 100644 --- a/main.c +++ b/main.c @@ -1330,6 +1330,7 @@ typedef struct CollisionCylinder typedef struct Room { struct Room *next; + struct Room *prev; bool camera_offset_is_overridden; Vec3 camera_offset; @@ -1342,23 +1343,11 @@ typedef struct Room typedef struct { Mesh *mesh_list; - Room *room_list; + Room *room_list_first; + Room *room_list_last; } ThreeDeeLevel; -Room *get_cur_room(GameState *gs, ThreeDeeLevel *level) -{ - Room *in_room = 0; - for(Room *cur = level->room_list; cur; cur = cur->next) - { - if(S8Match(cur->name, gs->current_room_name, 0)) - { - in_room = cur; - break; - } - } - assert(in_room); - return in_room; -} + void ser_BlenderTransform(SerState *ser, BlenderTransform *t) { @@ -1490,7 +1479,7 @@ ThreeDeeLevel load_level(Arena *arena, String8 binary_file) assert(num_placed == 0); // not thinking about how to go from name to entity kind right now, but in the future this will be for like machines or interactible things like the fishing rod } - StackPush(out.room_list, new_room); + DblPushBack(out.room_list_first, out.room_list_last, new_room); } assert(!ser.cur_error.failed); @@ -1503,6 +1492,7 @@ ThreeDeeLevel load_level(Arena *arena, String8 binary_file) #include "threedee.glsl.h" AABB level_aabb = { .upper_left = { 0.0f, 0.0f }, .lower_right = { TILE_SIZE * LEVEL_TILES, -(TILE_SIZE * LEVEL_TILES) } }; +ThreeDeeLevel level_threedee = {0}; GameState gs = { 0 }; bool flycam = false; Vec3 flycam_pos = {0}; @@ -1512,6 +1502,53 @@ float flycam_speed = 1.0f; Mat4 view = {0}; Mat4 projection = {0}; +Room mystery_room = { + .name = S8LitC("???"), +}; +Room *room_by_name(ThreeDeeLevel *level, String8 name) { + Room *ret = &mystery_room; + for(Room *cur = level->room_list_first; cur; cur = cur->next) + { + if(S8Match(cur->name, name, 0)) + { + ret = cur; + break; + } + } + 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); + } else { + in_room = room_by_name(level, gs->current_room_name); + } + assert(in_room); + return in_room; +} +int num_rooms(ThreeDeeLevel *level) { + int ret = 0; + for(Room *cur = level->room_list_first; cur; cur = cur->next) { + ret++; + } + return ret; +} + + Vec4 IsPoint(Vec3 point) { return V4(point.x, point.y, point.z, 1.0f); @@ -2161,7 +2198,6 @@ 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) -ThreeDeeLevel level_threedee = {0}; void transition_to_room(GameState *gs, ThreeDeeLevel *level, String8 new_room_name) { @@ -2184,7 +2220,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve rnd_gamerand_seed(&gs->random, RANDOM_SEED); // make entities for all rooms - for(Room *cur_room = level->room_list; cur_room; cur_room = cur_room->next) + for(Room *cur_room = level->room_list_first; cur_room; cur_room = cur_room->next) { for (PlacedEntity *cur = cur_room->placed_entity_list; cur; cur = cur->next) { @@ -2199,7 +2235,11 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve gs->world_entity = new_entity(gs); gs->world_entity->is_world = true; - transition_to_room(gs, &level_threedee, level->room_list->name); +#ifdef DEVTOOLS + gs->edit.enabled = true; +#endif + + transition_to_room(gs, &level_threedee, level->room_list_first->name); } @@ -3926,12 +3966,18 @@ int rendering_compare(const void *a, const void *b) return (int)((a_draw->sorting_key - b_draw->sorting_key)); } -void swap(Vec2 *p1, Vec2 *p2) +void swapVec2(Vec2 *p1, Vec2 *p2) { Vec2 tmp = *p1; *p1 = *p2; *p2 = tmp; } +void swapfloat(float *a, float *b) +{ + float tmp = *a; + *a = *b; + *b = tmp; +} Vec2 tile_id_to_coord(sg_image tileset_image, Vec2 tile_size, uint16_t tile_id) { @@ -4839,6 +4885,10 @@ typedef struct bool force_down; Layer layer; LoadedFont *font; + sg_image *icon; + bool icon_flipped; + bool nobg; + float icon_padding; // dist between icon png's top and bottom edges and the button's top and bottom edges } ImbuttonArgs; bool imbutton_key(ImbuttonArgs args) @@ -4876,7 +4926,27 @@ bool imbutton_key(ImbuttonArgs args) if (aabb_is_valid(args.button_aabb)) { - draw_quad((DrawParams) { quad_aabb(args.button_aabb), IMG(image_white_square), blendalpha(WHITE, button_alpha), .layer = layer, }); + if(!args.nobg) + draw_quad((DrawParams) { quad_aabb(args.button_aabb), IMG(image_white_square), blendalpha(WHITE, button_alpha), .layer = layer, }); + + if(args.icon) { + Vec2 button_size = aabb_size(args.button_aabb); + float icon_vertical_size = button_size.y - 2.0f*args.icon_padding; + Vec2 icon_size = V2(icon_vertical_size, icon_vertical_size); + Vec2 center = aabb_center(args.button_aabb); + AABB icon_aabb = aabb_centered(center, icon_size); + /* + if(args.icon_flipped) { + swapVec2(&quad.ul, &quad.ur); + swapVec2(&quad.ll, &quad.lr); + } + */ + AABB region = full_region(*args.icon); + if(args.icon_flipped) { + swapVec2(®ion.upper_left, ®ion.lower_right); + } + draw_quad((DrawParams) { quad_aabb(icon_aabb), *args.icon, region, blendalpha(WHITE, button_alpha), .layer = layer, }); + } // don't use draw centered text here because it looks funny for some reason... I think it's because the vertical line advance of the font, used in draw_centered_text, is the wrong thing for a button like this TextParams t = (TextParams) { false, args.text, aabb_center(args.button_aabb), BLACK, args.text_scale, .clip_to = args.button_aabb, .do_clipping = true, .layer = layer, .use_font = font }; @@ -5406,6 +5476,12 @@ Vec3 point_on_plane_from_camera_point(Mat4 view, Vec2 screenspace_camera_point) return marker; } +int mod(int a, int b) +{ + int r = a % b; + return r < 0 ? r + b : r; +} + void frame(void) { static float speed_factor = 1.0f; @@ -5735,6 +5811,40 @@ void frame(void) } // @Place(UI rendering that happens before gameplay processing so can consume events before the gameplay needs them) + PROFILE_SCOPE("Editor UI Rendering") + { + if(keypressed[SAPP_KEYCODE_TAB]) { + gs.edit.enabled = !gs.edit.enabled; + } + if(gs.edit.enabled) { + draw_text((TextParams){false, S8Lit("Editing"), V2(0,0), WHITE, .scale = 1.0f, .use_font = &font_for_text_input}); + + LoadedFont room_name_font = font_for_text_input; + float max_room_name_width = 0.0f; + float max_height = 0.0f; + for(Room *cur = level_threedee.room_list_first; cur; cur = cur->next) { + Vec2 bounds = aabb_size(draw_text((TextParams){true, cur->name, .use_font = &room_name_font, .scale = 1.0f})); + max_room_name_width = max(max_room_name_width, bounds.x); + max_height = max(max_height, bounds.y); + } + float padding = 10.0; + float whole_height = max_height + padding; + 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); + 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)); + } + } PROFILE_SCOPE("Entity UI Rendering") { ENTITIES_ITER(gs.entities) @@ -6871,9 +6981,11 @@ void frame(void) float new_vertices[ FLOATS_PER_VERTEX*4 ] = { 0 }; Vec2 region_size = SubV2(d.image_region.lower_right, d.image_region.upper_left); - assert(region_size.X > 0.0); - assert(region_size.Y > 0.0); - //Vec2 lower_left = AddV2(d.image_region.upper_left, V2(0, region_size.y)); + + // the region size can be negative if the image is desired to be flipped + // assert(region_size.X > 0.0); + // assert(region_size.Y > 0.0); + Vec2 tex_coords[4] = { // upper left vertex, upper right vertex, lower right vertex, lower left vertex diff --git a/makeprompt.h b/makeprompt.h index c6ff8c1..bc51e96 100644 --- a/makeprompt.h +++ b/makeprompt.h @@ -285,11 +285,18 @@ typedef struct Npc { TextChunk prompt; } Npc; +typedef struct EditorState { + bool enabled; + int room_index; +} EditorState; + typedef struct GameState { Arena *arena; // all allocations done with the lifecycle of a gamestate (loading/reloading entire levels essentially) must be allocated on this arena. uint64_t tick; bool won; + EditorState edit; + bool finished_reading_dying_dialog; double time; // in seconds, fraction of length of day @@ -334,6 +341,9 @@ Npc *npc_data(GameState *gs, NpcKind kind) { return 0; } +// to fix initializer is not constant +#define S8LitC(s) {(u8 *)(s), sizeof(s)-1} + String8 npc_to_human_readable(GameState *gs, Entity *me, NpcKind kind) { if(me->npc_kind == kind)