diff --git a/CHANGELOG.md b/CHANGELOG.md index d5aae3482..c2f7c32f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - fixed `/tp` console command reporting teleport fails as success (#1484, regression from 4.1) - fixed console commands causing improper ring shutdown with selected inventory item (#1460, regression from 3.0) - fixed console input immediately ending demo (#1480, regression from 4.1) +- changed `/tp` console command to look for the closest place to teleport to when targeting items (#1484) - improved level load times - improved logs module names readability - improved crash debug information on Windows diff --git a/src/game/console_cmd.c b/src/game/console_cmd.c index 1db046f4b..8f993cd13 100644 --- a/src/game/console_cmd.c +++ b/src/game/console_cmd.c @@ -121,7 +121,7 @@ static COMMAND_RESULT Console_Cmd_Teleport(const char *const args) z += 0.5f; } - if (Item_Teleport(g_LaraItem, x * WALL_L, y * WALL_L, z * WALL_L)) { + if (Lara_Cheat_Teleport(x * WALL_L, y * WALL_L, z * WALL_L)) { Console_Log(GS(OSD_POS_SET_POS), x, y, z); return CR_SUCCESS; } @@ -153,7 +153,7 @@ static COMMAND_RESULT Console_Cmd_Teleport(const char *const args) int32_t x = x1 + Random_GetControl() * (x2 - x1) / 0x7FFF; int32_t y = y1; int32_t z = z1 + Random_GetControl() * (z2 - z1) / 0x7FFF; - if (Item_Teleport(g_LaraItem, x, y, z)) { + if (Lara_Cheat_Teleport(x, y, z)) { Console_Log(GS(OSD_POS_SET_ROOM), room_num); return CR_SUCCESS; } @@ -206,9 +206,8 @@ static COMMAND_RESULT Console_Cmd_Teleport(const char *const args) } if (best_item != NULL) { - if (Item_Teleport( - g_LaraItem, best_item->pos.x, best_item->pos.y, - best_item->pos.z)) { + if (Lara_Cheat_Teleport( + best_item->pos.x, best_item->pos.y, best_item->pos.z)) { Console_Log(GS(OSD_POS_SET_ITEM), args); } else { Console_Log(GS(OSD_POS_SET_ITEM_FAIL), args); diff --git a/src/game/items.c b/src/game/items.c index 3264e44ef..5430caf10 100644 --- a/src/game/items.c +++ b/src/game/items.c @@ -2,7 +2,6 @@ #include "config.h" #include "game/anim.h" -#include "game/camera.h" #include "game/carrier.h" #include "game/interpolation.h" #include "game/room.h" @@ -248,32 +247,6 @@ void Item_NewRoom(int16_t item_num, int16_t room_num) r->item_number = item_num; } -bool Item_Teleport(ITEM_INFO *item, int32_t x, int32_t y, int32_t z) -{ - int16_t room_num = Room_GetIndexFromPos(x, y, z); - if (room_num == NO_ROOM) { - return false; - } - const SECTOR_INFO *const sector = Room_GetSector(x, y, z, &room_num); - const int16_t height = Room_GetHeight(sector, x, y, z); - if (height != NO_HEIGHT) { - item->pos.x = x; - item->pos.y = y; - item->pos.z = z; - item->floor = height; - if (item->room_number != room_num) { - const int16_t item_num = item - g_Items; - Item_NewRoom(item_num, room_num); - } - - if (item->object_number == O_LARA) { - Camera_ResetPosition(); - } - return true; - } - return false; -} - void Item_UpdateRoom(ITEM_INFO *item, int32_t height) { int32_t x = item->pos.x; diff --git a/src/game/items.h b/src/game/items.h index 65a88435d..c0a9e7ea0 100644 --- a/src/game/items.h +++ b/src/game/items.h @@ -38,7 +38,6 @@ bool Item_MovePosition( int32_t velocity); void Item_ShiftCol(ITEM_INFO *item, COLL_INFO *coll); void Item_Translate(ITEM_INFO *item, int32_t x, int32_t y, int32_t z); -bool Item_Teleport(ITEM_INFO *item, int32_t x, int32_t y, int32_t z); int32_t Item_GetDistance(const ITEM_INFO *item, const XYZ_32 *target); bool Item_TestAnimEqual(ITEM_INFO *item, int16_t anim_index); diff --git a/src/game/lara/lara_cheat.c b/src/game/lara/lara_cheat.c index b4a5a3e5d..2887aaa5f 100644 --- a/src/game/lara/lara_cheat.c +++ b/src/game/lara/lara_cheat.c @@ -1,5 +1,6 @@ #include "game/lara/lara_cheat.h" +#include "game/camera.h" #include "game/carrier.h" #include "game/console.h" #include "game/effects/exploding_death.h" @@ -16,8 +17,10 @@ #include "global/const.h" #include "global/types.h" #include "global/vars.h" +#include "math/math_misc.h" #include +#include #include #include @@ -368,3 +371,80 @@ bool Lara_Cheat_KillEnemy(const int16_t item_num) Carrier_TestItemDrops(item_num); return true; } + +bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z) +{ + int16_t room_num = Room_GetIndexFromPos(x, y, z); + if (room_num == NO_ROOM) { + return false; + } + + const SECTOR_INFO *sector = Room_GetSector(x, y, z, &room_num); + int16_t height = Room_GetHeight(sector, x, y, z); + + if (height == NO_HEIGHT) { + // Sample a sphere of points around target x, y, z + // and teleport to the first available location. + VECTOR *const points = Vector_Create(sizeof(XYZ_32)); + + const int32_t radius = 5; + const int32_t unit = STEP_L; + for (int32_t dx = -radius; dx <= radius; dx++) { + for (int32_t dz = -radius; dz <= radius; dz++) { + if (SQUARE(dx) + SQUARE(dz) > SQUARE(radius)) { + continue; + } + + const XYZ_32 point = { + .x = ROUND_TO_SECTOR(x + dx * unit) + WALL_L / 2, + .y = y, + .z = ROUND_TO_SECTOR(z + dz * unit) + WALL_L / 2, + }; + sector = Room_GetSector(point.x, point.y, point.z, &room_num); + height = Room_GetHeight(sector, point.x, point.y, point.z); + if (height != NO_HEIGHT) { + Vector_Add(points, (void *)&point); + } + } + } + + int32_t best_distance = INT32_MAX; + for (int32_t i = 0; i < points->count; i++) { + const XYZ_32 *const point = (const XYZ_32 *)Vector_Get(points, i); + const int32_t distance = + XYZ_32_GetDistance(point, &g_LaraItem->pos); + if (distance < best_distance) { + best_distance = distance; + x = point->x; + y = point->y; + z = point->z; + } + } + + Vector_Free(points); + if (best_distance == INT32_MAX) { + return false; + } + } + + sector = Room_GetSector(x, y, z, &room_num); + height = Room_GetHeight(sector, x, y, z); + if (height == NO_HEIGHT) { + return false; + } + + g_LaraItem->pos.x = x; + g_LaraItem->pos.y = y; + g_LaraItem->pos.z = z; + g_LaraItem->floor = height; + + if (g_LaraItem->room_number != room_num) { + const int16_t item_num = g_LaraItem - g_Items; + Item_NewRoom(item_num, room_num); + } + + if (g_LaraItem->object_number == O_LARA) { + Camera_ResetPosition(); + } + return true; +} diff --git a/src/game/lara/lara_cheat.h b/src/game/lara/lara_cheat.h index 3b563f9b7..e195ffa28 100644 --- a/src/game/lara/lara_cheat.h +++ b/src/game/lara/lara_cheat.h @@ -12,3 +12,4 @@ bool Lara_Cheat_GiveAllGuns(void); bool Lara_Cheat_GiveAllItems(void); bool Lara_Cheat_OpenNearestDoor(void); bool Lara_Cheat_KillEnemy(int16_t item_num); +bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z); diff --git a/src/math/math_misc.c b/src/math/math_misc.c index 7f699541c..3fe493a0f 100644 --- a/src/math/math_misc.c +++ b/src/math/math_misc.c @@ -49,3 +49,14 @@ int32_t Math_AngleMean(int32_t angle1, int32_t angle2, double ratio) return result; } + +int32_t XYZ_32_GetDistance(const XYZ_32 *const pos1, const XYZ_32 *const pos2) +{ + // clang-format off + return Math_Sqrt( + SQUARE(pos1->x - pos2->x) + + SQUARE(pos1->y - pos2->y) + + SQUARE(pos1->z - pos2->z) + ); + // clang-format on +} diff --git a/src/math/math_misc.h b/src/math/math_misc.h index bec7d6850..bc236430e 100644 --- a/src/math/math_misc.h +++ b/src/math/math_misc.h @@ -1,7 +1,8 @@ #pragma once -#include +#include "global/types.h" void Math_GetVectorAngles(int32_t x, int32_t y, int32_t z, int16_t *dest); int32_t Math_AngleInCone(int32_t angle1, int32_t angle2, int32_t cone); int32_t Math_AngleMean(int32_t angle1, int32_t angle2, double ratio); +int32_t XYZ_32_GetDistance(const XYZ_32 *pos1, const XYZ_32 *pos2);