diff --git a/src/base/base.cgh b/src/base/base.cgh index 1ca2c53a..a76a6c2a 100644 --- a/src/base/base.cgh +++ b/src/base/base.cgh @@ -33,6 +33,10 @@ #error Missing compile time definition for 'IsHotSwappingEnabled' #endif +#ifndef DefaultAppName + #error Default application name not defined +#endif + //////////////////////////////////////////////////////////// //~ Machine context @@ -784,6 +788,7 @@ u64 MixU64s(u64 seed_a, u64 seed_b) #if IsLanguageC StringList GetRawCommandline(void); + String GetAppDirectory(void); void Echo(String msg); b32 Panic(String msg); Callstack CaptureCallstack(u64 skip_frames); diff --git a/src/base/base_crum.c b/src/base/base_crum.c index f4095457..9cec3800 100644 --- a/src/base/base_crum.c +++ b/src/base/base_crum.c @@ -177,24 +177,31 @@ Vec4 CR_Vec4FromString(String str) b32 ok = 1; - if (StringBeginsWith(str, Lit("("))) - { - str.text += 1; - str.len -= 1; - } - if (StringEndsWith(str, Lit(")"))) - { - str.len -= 1; - } + // if (StringBeginsWith(str, Lit("("))) + // { + // str.text += 1; + // str.len -= 1; + // } + // if (StringEndsWith(str, Lit(")"))) + // { + // str.len -= 1; + // } u64 pos = 0; u64 parts_found = 0; String part = Zi; part.text = str.text; + if (StringBeginsWith(str, Lit("("))) + { + part.text += 1; + pos += 1; + } + while (pos < str.len && parts_found < 4) { u8 c = str.text[pos]; - if (c == ',' || c == ')' || pos + 1 >= str.len) + u8 next_c = 0; + if (c == ',' || c == ')') { part.len = (str.text + pos) - part.text; f64 part_float = CR_FloatFromString(part); @@ -347,16 +354,7 @@ CR_Item *CR_ItemFromString(Arena *arena, String str) if (text_end != -1) { - if (!cur_item) - { - cur_item = PushStruct(arena, CR_Item); - cur_item->parent = top_item->item; - DllQueuePush(cur_item->parent->first, cur_item->parent->last, cur_item); - cur_item->parent->count += 1; - } - is_escaped = 0; - text_is_string = 0; String src_text = Zi; if (text_end > text_start) @@ -365,8 +363,12 @@ CR_Item *CR_ItemFromString(Arena *arena, String str) src_text.text = str.text + text_start; } - if (is_name) + if (!cur_item) { + cur_item = PushStruct(arena, CR_Item); + cur_item->parent = top_item->item; + DllQueuePush(cur_item->parent->first, cur_item->parent->last, cur_item); + cur_item->parent->count += 1; } // FIXME: Arrays always have an item even if empty @@ -406,9 +408,8 @@ CR_Item *CR_ItemFromString(Arena *arena, String str) } } - - mode = Mode_None; + text_is_string = 0; } pos += 1; diff --git a/src/base/base_string.c b/src/base/base_string.c index f457f8f6..cb0eb222 100644 --- a/src/base/base_string.c +++ b/src/base/base_string.c @@ -482,6 +482,21 @@ String StringFromArray(Arena *arena, StringArray a) return result; } +String PathFromString(Arena *arena, String str, u8 path_delimiter) +{ + String result = Zi; + result = PushString(arena, str); + for (u64 char_idx = 0; char_idx < result.len; ++char_idx) + { + u8 c = result.text[char_idx]; + if ((c == '\\' || c == '/') && c != path_delimiter) + { + result.text[char_idx] = path_delimiter; + } + } + return result; +} + //////////////////////////////////////////////////////////// //~ String list helpers diff --git a/src/base/base_string.h b/src/base/base_string.h index db90874c..7ffc08e2 100644 --- a/src/base/base_string.h +++ b/src/base/base_string.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// //~ Formatting types -#define DefaultFmtPrecision 3 +#define DefaultFmtPrecision 6 #define Base16Chars ("0123456789abcdef") Enum(FmtArgKind) @@ -100,6 +100,7 @@ b32 StringContains(String str, String substring); b32 StringBeginsWith(String str, String substring); b32 StringEndsWith(String str, String substring); String StringFromArray(Arena *arena, StringArray a); +String PathFromString(Arena *arena, String str, u8 path_delimiter); //////////////////////////////////////////////////////////// //~ Trimming helpers diff --git a/src/base/base_win32/base_win32.c b/src/base/base_win32/base_win32.c index 4c98354d..e0f195a8 100644 --- a/src/base/base_win32/base_win32.c +++ b/src/base/base_win32/base_win32.c @@ -189,6 +189,11 @@ void SleepSeconds(f64 seconds) } } +String GetAppDirectory(void) +{ + return W32.appdir_path; +} + //////////////////////////////////////////////////////////// //~ @hookimpl Swap @@ -206,7 +211,7 @@ String SwappedStateFromName(Arena *arena, String name) { TempArena scratch = BeginScratch(arena); String result = Zi; - String path = StringF(scratch.arena, "ppswap/%F.swp", FmtString(name)); + String path = StringF(scratch.arena, "%F/swap/%F.swp", FmtString(GetAppDirectory()), FmtString(name)); wchar_t *path_wstr = WstrFromString(scratch.arena, path); HANDLE handle = CreateFileW(path_wstr, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (handle != INVALID_HANDLE_VALUE) @@ -234,11 +239,11 @@ String SwappedStateFromName(Arena *arena, String name) void WriteSwappedState(String name, String data) { TempArena scratch = BeginScratchNoConflict(); - // TODO: Use directory non-relative to executable - CreateDirectoryW(L"ppswap", 0); - String result = Zi; - String path = StringF(scratch.arena, "ppswap/%F.swp", FmtString(name)); + String dir_path = PathFromString(scratch.arena, StringF(scratch.arena, "%F/swap", FmtString(GetAppDirectory())), '\\'); + String path = PathFromString(scratch.arena, StringF(scratch.arena, "%F/%F.swp", FmtString(dir_path), FmtString(name)), '\\'); + wchar_t *dir_path_wstr = WstrFromString(scratch.arena, dir_path); wchar_t *path_wstr = WstrFromString(scratch.arena, path); + SHCreateDirectoryExW(0, dir_path_wstr, 0); HANDLE handle = CreateFileW(path_wstr, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (handle != INVALID_HANDLE_VALUE) { @@ -277,29 +282,25 @@ void ExitNow(i32 code) //////////////////////////////////////////////////////////// //~ Log -void W32_BootstrapLogs(String logfile_path) +void W32_BootstrapLogs(void) { + Arena *perm = PermArena(); W32.logs_arena = AcquireArena(Gibi(64)); W32.log_msgs_arena = AcquireArena(Gibi(64)); W32.readable_log_events = ArenaNext(W32.logs_arena, LogEvent); - if (logfile_path.len > 0) - { - TempArena scratch = BeginScratchNoConflict(); - { - wchar_t *path_wstr = WstrFromString(scratch.arena, logfile_path); - W32.logfile = CreateFileW( - path_wstr, - FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - 0 - ); - } - EndScratch(scratch); - } + String logfile_path = StringF(perm, "%F/log.log", FmtString(GetAppDirectory())); + wchar_t *path_wstr = WstrFromString(perm, logfile_path); + W32.logfile = CreateFileW( + path_wstr, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0 + ); Atomic32Set(&W32.logs_initialized, 1); + LogInfoF("Log file path: %F", FmtString(logfile_path)); } void W32_Log(i32 level, String msg) @@ -443,8 +444,8 @@ i32 W32_Main(void) W32_InitCurrentThread(Lit("Main")); // Get raw args from command line + Arena *perm = PermArena(); { - Arena *perm = PermArena(); StringList args_list = Zi; { LPCWSTR cmdline_wstr = GetCommandLineW(); @@ -469,10 +470,78 @@ i32 W32_Main(void) // Bootstrap command line BootstrapCmdline(); + // Init app directory + String appdir_error = Zi; + { + String appdir_path = Zi; + CommandlineArg appdir_arg = CommandlineArgFromName(Lit("appdir")); + CommandlineArg appname_arg = CommandlineArgFromName(Lit("appname")); + if (appdir_arg.exists && appdir_arg.value.len > 0) + { + appdir_path = appdir_arg.value; + } + else + { + String appname = Lit(Stringize(DefaultAppName)); + if (appname_arg.exists && appname_arg.value.len > 0) + { + appname = appname_arg.value; + } + + wchar_t *path_wstr = 0; + HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, 0, &path_wstr); + if (!SUCCEEDED(hr)) + { + Panic(Lit("Failed to locate AppData directory")); + } + appdir_path = StringFromWstrNoLimit(perm, path_wstr); + CoTaskMemFree(path_wstr); + appdir_path = PathFromString(perm, StringF(perm, "%F\\Cabin\\%F\\", FmtString(appdir_path), FmtString(appname)), '/'); + } + // Create app dir + { + String path = PathFromString(perm, appdir_path, '\\'); + wchar_t *path_wstr = WstrFromString(perm, appdir_path); + i32 err_code = SHCreateDirectoryExW(0, path_wstr, 0); + String err = StringF(perm, "Error code %F", FmtSint(err_code)); + switch (err_code) + { + default: break; + case ERROR_BAD_PATHNAME: + { + err = Lit("Bad path name"); + } break; + case ERROR_FILENAME_EXCED_RANGE: + { + err = Lit("Path name too long"); + } break; + case ERROR_CANCELLED: + { + err = Lit("User canceled the operation"); + } break; + } + if (err_code != ERROR_SUCCESS && err_code != ERROR_ALREADY_EXISTS && err_code != ERROR_FILE_EXISTS) + { + appdir_error = StringF( + perm, + "Failed to initialize app directory at \"%F\": %F", + FmtString(path), + FmtString(err) + ); + wchar_t *msg_wstr = WstrFromString(perm, appdir_error); + MessageBoxExW(0, msg_wstr, L"Warning", MB_ICONWARNING | MB_SETFOREGROUND | MB_TOPMOST, 0); + } + } + W32.appdir_path = appdir_path; + } + // Bootstrap log system - // FIXME: Remove hardcoded log path - W32_BootstrapLogs(Lit("log.log")); + W32_BootstrapLogs(); LogInfoF("Main thread ID: %F", FmtUint(ThreadId())); + if (appdir_error.len > 0) + { + LogError(appdir_error); + } // Bootstrap resource system { diff --git a/src/base/base_win32/base_win32.h b/src/base/base_win32/base_win32.h index f996b8fc..50ca3162 100644 --- a/src/base/base_win32/base_win32.h +++ b/src/base/base_win32/base_win32.h @@ -68,6 +68,7 @@ Struct(W32_Ctx) i64 ns_per_qpc; StringList raw_command_line; + String appdir_path; //- Application control flow @@ -107,7 +108,7 @@ BOOL W32_FindEmbeddedRcData(HMODULE module, LPCWSTR type, LPWSTR wstr_entry_name //////////////////////////////////////////////////////////// //~ Log -void W32_BootstrapLogs(String logfile_path); +void W32_BootstrapLogs(); void W32_Log(i32 level, String msg); //////////////////////////////////////////////////////////// diff --git a/src/meta/meta.c b/src/meta/meta.c index 9a98cbfa..7e7897b1 100644 --- a/src/meta/meta.c +++ b/src/meta/meta.c @@ -349,6 +349,7 @@ void BuildEntryPoint(WaveLaneCtx *lane) PushStringToList(perm, &cp.defs, Lit("-DIsUnoptimized=1")); PushStringToList(perm, &cp.defs, Lit("-DIsTestingEnabled=0")); PushStringToList(perm, &cp.defs, Lit("-DIsHotSwappingEnabled=1")); + PushStringToList(perm, &cp.defs, StringF(perm, "-DDefaultAppName=%F", FmtString(cmdline.leaf_layer_name))); } //- Msvc @@ -1015,7 +1016,7 @@ void BuildEntryPoint(WaveLaneCtx *lane) if (lane->idx == 0) { - EchoLine(StringF(perm, "Runtime: %Fs", FmtFloat(SecondsFromNs(TimeNs())))); + EchoLine(StringF(perm, "Runtime: %Fs", FmtFloat(SecondsFromNs(TimeNs()), .p = 3))); ExitNow(GetBuildStatus()); } } diff --git a/src/meta/meta.h b/src/meta/meta.h index c7600e0f..c3b37c53 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -39,6 +39,10 @@ #define IsHotSwappingEnabled 0 #endif +#ifndef DefaultAppName + #define DefaultAppName meta +#endif + //////////////////////////////////////////////////////////// //~ Includes diff --git a/src/platform/platform.h b/src/platform/platform.h index 2177ba27..101780ff 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -77,10 +77,8 @@ void PLT_Bootstrap(void); // NOTE: File paths use forward slash '/' as delimiter //- File system helpers -String PLT_GetWritePath(Arena *arena); b32 PLT_IsFile(String path); b32 PLT_IsDir(String path); -void PLT_MkDir(String path); //- File creation PLT_File PLT_OpenFileRead(String path); diff --git a/src/platform/platform_win32/platform_win32.c b/src/platform/platform_win32/platform_win32.c index c3bfcca0..9c51ee00 100644 --- a/src/platform/platform_win32/platform_win32.c +++ b/src/platform/platform_win32/platform_win32.c @@ -212,25 +212,6 @@ void PLT_W32_SyncTimerForever(WaveLaneCtx *lane) //////////////////////////////////////////////////////////// //~ @hookimpl File system -String PLT_GetWritePath(Arena *arena) -{ - u16 *p = 0; - // TODO: cache this? - HRESULT result = SHGetKnownFolderPath( - &FOLDERID_LocalAppData, - 0, - 0, - &p - ); - String path = Zi; - if (result == S_OK) - { - path = PLT_W32_StringFromWin32Path(arena, p); - } - CoTaskMemFree(p); - return path; -} - b32 PLT_IsFile(String path) { TempArena scratch = BeginScratchNoConflict(); @@ -249,47 +230,16 @@ b32 PLT_IsDir(String path) return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY); } -void PLT_MkDir(String path) +b32 PLT_MkDir(String path) { TempArena scratch = BeginScratchNoConflict(); - wchar_t *path_wstr = WstrFromString(scratch.arena, path); - int err_code = SHCreateDirectory(0, path_wstr); - String err = Zi; - switch (err_code) + i32 err = 0; { - default: break; - - case ERROR_BAD_PATHNAME: - { - err = Lit("Bad path name"); - } break; - - case ERROR_FILENAME_EXCED_RANGE: - { - err = Lit("Path name too long"); - } break; - - case ERROR_FILE_EXISTS: - { - err = Lit("A file already exists at this location"); - } break; - - case ERROR_CANCELLED: - { - err = Lit("User canceled the operation"); - } break; - } - if (err.len > 0) - { - String msg = StringF( - scratch.arena, - "Failed to create directory \"%F\": %F", - FmtString(path), - FmtString(err) - ); - Panic(msg); + wchar_t *path_wstr = WstrFromString(scratch.arena, path); + err = SHCreateDirectory(0, path_wstr); } EndScratch(scratch); + return err == ERROR_SUCCESS || err == ERROR_ALREADY_EXISTS || err == ERROR_FILE_EXISTS; } PLT_File PLT_OpenFileRead(String path) diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index 44d3bd38..bd20e1f9 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -2,7 +2,6 @@ S_Ctx S = Zi; ThreadLocal S_ThreadLocalCtx S_tl = Zi; Readonly S_Ent S_NilEnt = { - .last_xf = CompXformIdentity, .xf = CompXformIdentity, .look = { 0, -1 }, }; @@ -220,6 +219,30 @@ Rng2 S_BoundingBoxFromShape(S_Shape shape) return result; } +S_Shape S_LocalShapeFromEnt(S_Ent *ent) +{ + S_Shape result = Zi; + + // TODO: This is a temporary hack. Use prefab-lookup table. + if (ent->is_player) + { + result = S_ShapeFromDesc( + .mass = 10, + .count = 1, + .radius = 0.3, + ); + } + + return result; +} + +S_Shape S_WorldShapeFromEnt(S_Ent *ent) +{ + S_Shape local = S_LocalShapeFromEnt(ent); + S_Shape world = S_MulXformShape(ent->xf, local); + return world; +} + //////////////////////////////////////////////////////////// //~ Collision @@ -1254,7 +1277,7 @@ void S_TickForever(WaveLaneCtx *lane) String packed = Zi; if (swapin) { - packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp")); + packed = SwappedStateFromName(frame_arena, Lit("pp_sim")); } S_UnpackedWorld unpacked = S_UnpackWorld(frame_arena, packed); @@ -1263,6 +1286,10 @@ void S_TickForever(WaveLaneCtx *lane) world->seed = unpacked.seed; world->tick = unpacked.tick; world->time_ns = unpacked.time_ns; + if (world->seed == 0) + { + TrueRand(StringFromStruct(&world->seed)); + } world->ent_bins_count = Kibi(16); world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count); @@ -1279,7 +1306,7 @@ void S_TickForever(WaveLaneCtx *lane) if (swapout) { String packed = S_PackWorld(frame_arena, world); - WriteSwappedState(Lit("pp_sim.swp"), packed); + WriteSwappedState(Lit("pp_sim"), packed); } } @@ -1309,9 +1336,30 @@ void S_TickForever(WaveLaneCtx *lane) ////////////////////////////// //- Update double-buffered entity data - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) + // { + // ent->last_xf = ent->xf; + // } + + ////////////////////////////// + //- Process save commands + + // FIXME: Only accept save command from local user + + b32 should_save = 0; + for (S_CmdNode *cmd_node = input->first_cmd_node; cmd_node; cmd_node = cmd_node->next) { - ent->last_xf = ent->xf; + S_Cmd *cmd = &cmd_node->cmd; + if (cmd->kind == S_CmdKind_Save) + { + should_save = 1; + break; + } + } + if (should_save) + { + String path = Lit("HAH"); + LogInfoF("Saving world to %F", FmtString(path)); } ////////////////////////////// @@ -1410,7 +1458,8 @@ void S_TickForever(WaveLaneCtx *lane) { desired_xf = XformWithWorldRotation(xf, AngleFromVec2(ent->look)); } - desired_xf.og = AddVec2(xf.og, MulVec2(ent->move, ent->move_speed)); + f32 move_speed = TweakFloat("Player move speed", 6.5, 0, 20); + desired_xf.og = AddVec2(xf.og, MulVec2(ent->move, move_speed * sim_dt)); Vec2 pos_diff = SubVec2(desired_xf.og, xf.og); f32 angle_diff = UnwindAngleF32(RotationFromXform(desired_xf) - RotationFromXform(xf)); @@ -1436,56 +1485,46 @@ void S_TickForever(WaveLaneCtx *lane) - // for (i32 tile_y = bb_tiles.p0.y; tile_y < bb_tiles.p1.y; ++tile_y) + + // for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) // { - // for (i32 tile_x = bb_tiles.p0.x; tile_x < bb_tiles.p1.x; ++tile_x) + // if (ent->is_player) // { - // Vec2I32 tile_pos = VEC2I32(tile_x, tile_y); - // S_TileKind tile = S_TileFromPos(tile_pos); + // Xform last_xf = ent->last_xf; + // S_Shape last_world_shape = S_MulXformShape(last_xf, ent->local_shape); + + // Xform xf = ent->xf; + // S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); + + // Rng2 bb0 = S_BoundingBoxFromShape(last_world_shape); + // Rng2 bb1 = S_BoundingBoxFromShape(world_shape); + + // if (constraints_count < max_constraints) + // { + // S_Constraint *constraint = &constraints[constraints_count]; + + // // TODO: Real constraint data + + // constraint->ent0 = ent->key; + // constraint->shape0 = world_shape; + + // Rng2 test_rect = Zi; + // test_rect.p0 = VEC2(-1, -1); + // test_rect.p1 = VEC2(1, 1); + // constraint->shape1 = S_ShapeFromDesc( + // .radius = 0.5, + // .count = 4, + // .points[0] = VEC2(test_rect.p0.x, test_rect.p0.y), + // .points[1] = VEC2(test_rect.p1.x, test_rect.p0.y), + // .points[2] = VEC2(test_rect.p1.x, test_rect.p1.y), + // .points[3] = VEC2(test_rect.p0.x, test_rect.p1.y), + // ); + + // constraints_count += 1; + // } // } // } - - - for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) - { - if (ent->is_player) - { - Xform last_xf = ent->last_xf; - S_Shape last_world_shape = S_MulXformShape(last_xf, ent->local_shape); - - Xform xf = ent->xf; - S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); - - Rng2 bb0 = S_BoundingBoxFromShape(last_world_shape); - Rng2 bb1 = S_BoundingBoxFromShape(world_shape); - - if (constraints_count < max_constraints) - { - S_Constraint *constraint = &constraints[constraints_count]; - - // TODO: Real constraint data - - constraint->ent0 = ent->key; - constraint->shape0 = world_shape; - - Rng2 test_rect = Zi; - test_rect.p0 = VEC2(-1, -1); - test_rect.p1 = VEC2(1, 1); - constraint->shape1 = S_ShapeFromDesc( - .radius = 0.5, - .count = 4, - .points[0] = VEC2(test_rect.p0.x, test_rect.p0.y), - .points[1] = VEC2(test_rect.p1.x, test_rect.p0.y), - .points[2] = VEC2(test_rect.p1.x, test_rect.p1.y), - .points[3] = VEC2(test_rect.p0.x, test_rect.p1.y), - ); - - constraints_count += 1; - } - } - } - ////////////////////////////// //- Solve constraints @@ -1643,8 +1682,7 @@ void S_TickForever(WaveLaneCtx *lane) i64 tick_bullets_count = bullets_per_fire; if (tick_bullets_count > 0) { - Xform firer_xf = firer->xf; - S_Shape firer_world_shape = S_MulXformShape(firer_xf, firer->local_shape); + S_Shape firer_world_shape = S_WorldShapeFromEnt(firer); Vec2 pos = S_EdgePointFromShape(firer_world_shape, firer->look); @@ -1706,8 +1744,7 @@ void S_TickForever(WaveLaneCtx *lane) { if (victim->is_player && !S_MatchKey(victim->key, bullet->bullet_firer)) { - Xform victim_xf = victim->xf; - S_Shape victim_world_shape = S_MulXformShape(victim_xf, victim->local_shape); + S_Shape victim_world_shape = S_WorldShapeFromEnt(victim); S_RaycastResult entrance_raycast = S_RaycastShape(victim_world_shape, ray_start, ray_dir); Vec2 entrance = entrance_raycast.p; @@ -1885,8 +1922,7 @@ void S_TickForever(WaveLaneCtx *lane) { for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) { - Xform xf = ent->xf; - S_Shape world_shape = S_MulXformShape(xf, ent->local_shape); + S_Shape world_shape = S_WorldShapeFromEnt(ent); // Draw aabb { diff --git a/src/pp/pp_sim/pp_sim_core.h b/src/pp/pp_sim/pp_sim_core.h index dc731b1d..ac1ff941 100644 --- a/src/pp/pp_sim/pp_sim_core.h +++ b/src/pp/pp_sim/pp_sim_core.h @@ -62,12 +62,8 @@ Struct(S_Ent) b32 is_player; f32 health; - Xform last_xf; Xform xf; - S_Shape local_shape; - - f32 move_speed; Vec2 move; Vec2 look; f32 fire_held; @@ -245,6 +241,7 @@ Struct(S_SnapshotNode) Enum(S_CmdKind) { S_CmdKind_Nop, + S_CmdKind_Save, S_CmdKind_Delta, S_CmdKind_Control, }; @@ -394,6 +391,9 @@ S_Shape S_ShapeFromDescEx(S_ShapeDesc desc); S_Shape S_MulXformShape(Xform xf, S_Shape shape); Rng2 S_BoundingBoxFromShape(S_Shape shape); +S_Shape S_LocalShapeFromEnt(S_Ent *ent); +S_Shape S_WorldShapeFromEnt(S_Ent *ent); + //////////////////////////////////////////////////////////// //~ Collision diff --git a/src/pp/pp_sim/pp_sim_shared.cgh b/src/pp/pp_sim/pp_sim_shared.cgh index 9e1bb6f6..d9c24054 100644 --- a/src/pp/pp_sim/pp_sim_shared.cgh +++ b/src/pp/pp_sim/pp_sim_shared.cgh @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// //~ Tile types -#define S_WorldPitch 80.0 +#define S_WorldPitch 60.0 #define S_TilesPitch (S_WorldPitch * 2) #define S_TilesCount (S_TilesPitch * S_TilesPitch) diff --git a/src/pp/pp_sim/pp_sim_transcode.c b/src/pp/pp_sim/pp_sim_transcode.c index 33ef2729..9740576a 100644 --- a/src/pp/pp_sim/pp_sim_transcode.c +++ b/src/pp/pp_sim/pp_sim_transcode.c @@ -15,6 +15,11 @@ String S_PackWorld(Arena *arena, S_World *src_world) result.len += StringF(arena, "version: %F\n", FmtUint(S_Tv_Latest)).len; + result.len += StringF(arena, "\n").len; + result.len += StringF(arena, "seed: 0x%F\n", FmtHex(src_world->seed)).len; + result.len += StringF(arena, "tick: %F\n", FmtSint(src_world->tick)).len; + result.len += StringF(arena, "time: %F\n", FmtSint(src_world->time_ns)).len; + // Pack entities // FIXME: Precision result.len += PushString(arena, Lit("\nentities:\n")).len; @@ -24,14 +29,23 @@ String S_PackWorld(Arena *arena, S_World *src_world) result.len += StringF(arena, " 0x%F:\n", FmtHex(ent->key.v)).len; result.len += PushString(arena, Lit(" {\n")).len; { + result.len += StringF(arena, " props: \n").len; + result.len += StringF(arena, " {\n").len; + { + if (ent->is_player) + { + result.len += PushString(arena, Lit(" player\n")).len; + } + if (ent->is_bullet) + { + result.len += PushString(arena, Lit(" bullet\n")).len; + } + } + result.len += StringF(arena, " }\n").len; result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.og)).len; result.len += StringF(arena, " rot: \"%F\"\n", FmtFloat2(RightFromXform(ent->xf))).len; result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len; result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->look)).len; - if (ent->is_player) - { - result.len += PushString(arena, Lit(" player: true\n")).len; - } } result.len += PushString(arena, Lit(" }\n")).len; } @@ -69,7 +83,7 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) CR_Item *root = CR_ItemFromString(scratch.arena, packed); - // Get version + // Unpack version S_Tv version = 0; for (CR_Item *root_item = root->first; root_item; root_item = root_item->next) { @@ -82,47 +96,67 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) for (CR_Item *top_item = root->first; top_item; top_item = top_item->next) { - // Parse entities + // Unpack metadata + if (MatchString(top_item->name, Lit("seed"))) + { + result.seed = CR_IntFromString(top_item->value); + } + if (MatchString(top_item->name, Lit("tick"))) + { + result.tick = CR_IntFromString(top_item->value); + } + if (MatchString(top_item->name, Lit("time"))) + { + result.time_ns = CR_IntFromString(top_item->value); + } + + // Unpack entities if (MatchString(top_item->name, Lit("entities"))) { for (CR_Item *ent_item = top_item->first; ent_item; ent_item = ent_item->next) { S_Ent *ent = S_PushTempEnt(arena, &result.ents); ent->key = (S_Key) { .v = CR_IntFromString(ent_item->name) }; - for (CR_Item *prop = ent_item->first; prop; prop = prop->next) + for (CR_Item *attr = ent_item->first; attr; attr = attr->next) { - if (MatchString(prop->name, Lit("pos"))) + if (MatchString(attr->name, Lit("props"))) { - Vec2 pos = CR_Vec2FromString(prop->value); + for (CR_Item *prop = attr->first; prop; prop = prop->next) + { + if (MatchString(prop->value, Lit("player"))) + { + ent->is_player = 1; + ent->has_weapon = 1; + } + if (MatchString(prop->value, Lit("bullet"))) + { + ent->is_bullet = 1; + } + } + } + if (MatchString(attr->name, Lit("pos"))) + { + Vec2 pos = CR_Vec2FromString(attr->value); ent->xf.og = pos; } - if (MatchString(prop->name, Lit("rot"))) + if (MatchString(attr->name, Lit("rot"))) { - Vec2 rot = CR_Vec2FromString(prop->value); + Vec2 rot = CR_Vec2FromString(attr->value); ent->xf = XformWithWorldRotation(ent->xf, AngleFromVec2(rot)); } - if (MatchString(prop->name, Lit("exists"))) + if (MatchString(attr->name, Lit("exists"))) { - ent->exists = CR_FloatFromString(prop->value); + ent->exists = CR_FloatFromString(attr->value); } - if (MatchString(prop->name, Lit("health"))) + if (MatchString(attr->name, Lit("health"))) { - ent->health = CR_FloatFromString(prop->value); + ent->health = CR_FloatFromString(attr->value); } - if (MatchString(prop->name, Lit("look"))) + if (MatchString(attr->name, Lit("look"))) { - Vec2 look = CR_Vec2FromString(prop->value); + Vec2 look = CR_Vec2FromString(attr->value); ent->look = look; } - if (MatchString(prop->name, Lit("player")) && CR_BoolFromString(prop->value)) - { - ent->is_player = 1; - ent->has_weapon = 1; - } - // if (MatchString(prop->name, Lit("bullet")) && CR_BoolFromString(prop->value)) - // { - // ent->is_bullet = 1; - // } } } } @@ -148,160 +182,3 @@ S_UnpackedWorld S_UnpackWorld(Arena *arena, String packed) EndScratch(scratch); return result; }; - - - - -// //////////////////////////////////////////////////////////// -// //~ Transcode - -// S_TranscodeResult S_TranscodeWorld(Arena *arena, S_World *src_world, String src_packed, b32 pack) -// { -// S_TranscodeResult result = Zi; -// result.ok = 1; -// result.version = S_Tv_Latest; - -// ////////////////////////////// -// //- Init bitbuff - -// u32 level_magic = 0xa2bf209c; - -// BB_Buff bb = Zi; -// BB_Writer bw = Zi; -// BB_Reader br = Zi; -// if (pack) -// { -// bb = BB_AcquireDynamicBuff(Gibi(4)); -// bw = BB_WriterFromBuff(&bb); -// BB_WriteUBits(&bw, level_magic, 32); -// BB_WriteUBits(&bw, result.version, 32); -// } -// else -// { -// bb = BB_BuffFromString(src_packed); -// br = BB_ReaderFromBuff(&bb); -// result.ok = BB_ReadUBits(&br, 32) == level_magic; -// result.version = (S_Tv)BB_ReadUBits(&br, 32); -// result.unpacked = PushStruct(arena, S_World); -// } - -// ////////////////////////////// -// //- Transcode world metadata - -// if (pack) -// { -// BB_WriteUBits(&bw, src_world->seed, 64); -// BB_WriteIBits(&bw, src_world->tick, 64); -// BB_WriteIBits(&bw, src_world->time_ns, 64); -// } -// else -// { -// result.unpacked->seed = BB_ReadUBits(&br, 64); -// result.unpacked->tick = BB_ReadIBits(&br, 64); -// result.unpacked->time_ns = BB_ReadIBits(&br, 64); -// } - -// ////////////////////////////// -// //- Transcode tiles - -// // TODO: Compress tile data - -// if (pack) -// { -// String tiles_str = Zi; -// tiles_str.len = S_TilesCount; -// tiles_str.text = src_world->tiles; -// BB_WriteUBits(&bw, tiles_str.len, 64); -// BB_WriteBytes(&bw, tiles_str); -// } -// else -// { -// u64 raw_tiles_count = BB_ReadUBits(&br, 64); -// u8 *raw_tiles = BB_ReadBytesRaw(&br, raw_tiles_count); -// if (raw_tiles && raw_tiles_count == S_TilesCount) -// { -// result.unpacked->tiles = PushStructsNoZero(arena, u8, raw_tiles_count); -// CopyBytes(result.unpacked->tiles, raw_tiles, raw_tiles_count); -// } -// else -// { -// result.unpacked->tiles = PushStructs(arena, u8, S_TilesCount); -// result.ok = 0; -// } -// } - -// ////////////////////////////// -// //- Transcode entities - -// // TODO: Compress entity data - -// if (result.ok) -// { -// if (pack) -// { -// u32 ent_size = sizeof(S_Ent); -// u32 ent_align = alignof(S_Ent); -// i64 ents_count = src_world->ents_count; -// BB_WriteUBits(&bw, ent_size, 32); -// BB_WriteUBits(&bw, ent_align, 32); -// BB_WriteUBits(&bw, ents_count, 64); -// for (S_Ent *ent = S_FirstEnt(src_world); ent->valid; ent = S_NextEnt(ent)) -// { -// BB_WriteBytes(&bw, StringFromStruct(ent)); -// } -// } -// else -// { -// i64 ent_size = BB_ReadUBits(&br, 32); -// i64 ent_align = BB_ReadUBits(&br, 32); -// i64 ents_count = BB_ReadUBits(&br, 64); -// if (ent_size == sizeof(S_Ent) && ent_align == alignof(S_Ent)) -// { -// for (i64 i = 0; i < ents_count; ++i) -// { -// S_Ent *raw_ent = (S_Ent *)BB_ReadBytesRaw(&br, sizeof(S_Ent)); -// if (raw_ent) -// { -// S_Ent *ent = PushStructNoZero(arena, S_Ent); -// { -// CopyBytes(ent, raw_ent, ent_size); -// ent->valid = 1; -// } -// DllQueuePush(result.unpacked->first_ent, result.unpacked->last_ent, ent); -// result.unpacked->ents_count += 1; -// } -// else -// { -// result.ok = 0; -// break; -// } -// } -// } -// else -// { -// result.ok = 0; -// } -// } -// } - -// ////////////////////////////// -// //- Finalize - -// if (result.ok) -// { -// if (pack) -// { -// result.packed = BB_GetWritten(arena, &bw); -// BB_ReleaseDynamicBuff(&bb); -// } -// else -// { -// if (!result.unpacked->seed) -// { -// TrueRand(StringFromStruct(&result.unpacked->seed)); -// } -// } -// } - -// return result; -// } diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 7ef09c34..69e0af6d 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -274,7 +274,7 @@ V_WidgetTheme V_GetWidgetTheme(void) theme.icon_font = UI_BuiltinIconFont(); // theme.font_size = 14; - theme.font_size = TweakFloat("Font size", 14, 6, 50, .precision = 0); + theme.font_size = TweakFloat("Font size", 14, 8, 24, .precision = 0); theme.h1 = 2.00; theme.h2 = 1.50; theme.h3 = 1.25; @@ -558,7 +558,7 @@ void V_TickForever(WaveLaneCtx *lane) } else { - String swap_encoded = SwappedStateFromName(frame->arena, Lit("pp_vis.swp")); + String swap_encoded = SwappedStateFromName(frame->arena, Lit("pp_vis")); bb = BB_BuffFromString(swap_encoded); br = BB_ReaderFromBuff(&bb); } @@ -586,7 +586,7 @@ void V_TickForever(WaveLaneCtx *lane) // Write swapout if (swapout) { - WriteSwappedState(Lit("pp_vis.swp"), STRING(BB_GetNumBytesWritten(&bw), BB_GetWrittenRaw(&bw))); + WriteSwappedState(Lit("pp_vis"), STRING(BB_GetNumBytesWritten(&bw), BB_GetWrittenRaw(&bw))); } } } @@ -861,7 +861,7 @@ void V_TickForever(WaveLaneCtx *lane) look_ratio.y = 0.25; look_ratio.x = look_ratio.y / (16.0 / 9.0); S_Ent *player = S_EntFromKey(world, V.player_key); - target_camera_pos = MulXformV2(player->xf, player->local_shape.centroid); + target_camera_pos = S_WorldShapeFromEnt(player).centroid; target_camera_pos = AddVec2(target_camera_pos, MulVec2Vec2(player->look, look_ratio)); target_camera_zoom = 1; } @@ -1010,7 +1010,7 @@ void V_TickForever(WaveLaneCtx *lane) S_Shape cursor_shape = S_ShapeFromDesc(.count = 1, .points = { frame->world_cursor }); for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent)) { - S_Shape ent_shape = S_MulXformShape(ent->xf, ent->local_shape); + S_Shape ent_shape = S_WorldShapeFromEnt(ent); b32 is_hovered = S_CollisionResultFromShapes(ent_shape, cursor_shape).collision_points_count > 0; if (is_hovered) { @@ -2379,9 +2379,8 @@ void V_TickForever(WaveLaneCtx *lane) UI_Push(BackgroundColor, color); UI_Push(Width, UI_GROW(1, 0)); UI_Push(Height, UI_FNT(1.5, 1)); - UI_Push(BorderColor, Rgb(0.25, 0.25, 0.25)); UI_Push(Rounding, UI_RPIX(0)); - UI_Push(BorderSize, 1); + UI_Push(BorderSize, 0); UI_Push(ChildAlignment, UI_Region_Left); UI_PushCP(UI_BuildRow()); { @@ -2517,13 +2516,7 @@ void V_TickForever(WaveLaneCtx *lane) *ent = S_NilEnt; ent->key = V.player_key; ent->xf = XformFromPos(player_pos); - ent->move_speed = 0.075; ent->is_player = 1; - ent->local_shape = S_ShapeFromDesc( - .mass = 10, - .count = 1, - .radius = 0.3, - ); ent->has_weapon = 1; ent->exists = 1; } @@ -2537,13 +2530,7 @@ void V_TickForever(WaveLaneCtx *lane) *ent = S_NilEnt; ent->key = S_RandKey(); ent->xf = XformFromPos(frame->world_cursor); - ent->move_speed = 0.075; ent->is_player = 1; - ent->local_shape = S_ShapeFromDesc( - .mass = 10, - .count = 1, - .radius = 0.3, - ); ent->has_weapon = 1; ent->exists = 1; } break; @@ -2560,6 +2547,14 @@ void V_TickForever(WaveLaneCtx *lane) } } break; + case V_CmdKind_save_level: + { + if (frame->is_editing) + { + S_Cmd *cmd = V_PushSimCmd(S_CmdKind_Save); + } + } break; + case V_CmdKind_clear_particles: { should_clear_particles = 1; @@ -2582,7 +2577,7 @@ void V_TickForever(WaveLaneCtx *lane) f32 fire_presses = fire_held && !last_frame->held_buttons[Button_M1]; Vec2 look = Zi; { - Vec2 center = MulXformV2(player->xf, player->local_shape.centroid); + Vec2 center = S_WorldShapeFromEnt(player).centroid; look = SubVec2(frame->world_cursor, center); } if (frame->is_editing) diff --git a/src/pp/pp_vis/pp_vis_core.h b/src/pp/pp_vis/pp_vis_core.h index e0098011..d29889df 100644 --- a/src/pp/pp_vis/pp_vis_core.h +++ b/src/pp/pp_vis/pp_vis_core.h @@ -8,6 +8,7 @@ X(zoom_in, Zoom In, V_CmdDescFlag_HideFromPalette, V_HOTKEY( Button_MWheelUp ), ) \ X(zoom_out, Zoom Out, V_CmdDescFlag_HideFromPalette, V_HOTKEY( Button_MWheelDown ), ) \ X(toggle_editor, Toggle Editor, V_CmdDescFlag_None, V_HOTKEY( Button_F1 ), ) \ + X(save_level, Save level, V_CmdDescFlag_None, V_HOTKEY( Button_S, .ctrl = 1 ), ) \ X(toggle_ui_debug, Toggle UI Debug, V_CmdDescFlag_None, V_HOTKEY( Button_F5 ), ) \ X(toggle_console, Toggle Developer Console, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent ), ) \ X(toggle_fullscreen, Toggle Fullscreen Mode, V_CmdDescFlag_None, V_HOTKEY( Button_Enter, .alt = 1 ) ) \ @@ -15,7 +16,7 @@ X(spawn, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \ X(spawn_dummy, Spawn Dummy, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \ X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \ - X(reset_world, Reset world, V_CmdDescFlag_None, V_HOTKEY( Button_R ), ) \ + X(reset_world, Reset world, V_CmdDescFlag_None, V_HOTKEY( Button_R, .ctrl = 1, .alt = 1 ), ) \ X(clear_particles, Clear particles, V_CmdDescFlag_None, V_HOTKEY( Button_C ), ) \ /* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */