Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions runtime/hle/warp.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ static volatile int g_pending;
static volatile uint64_t g_request;

static uint64_t g_req; // our level-jump request (HLE heap)
static int g_place_queued, g_place_armed; // (warp_request_place, below)
static volatile int g_give_pending;
// A warp waits for the one before it (issue #41: a jump handed to the game while its last one was still under way
// crashed in its request processor, null+0x3f8): every level load registers a new request object (g_loads); after a
Expand Down Expand Up @@ -80,6 +81,7 @@ void warp_request(const char *level, const char *locator) {
strlcpy(g_level, level, sizeof g_level);
strlcpy(g_locator, locator, sizeof g_locator);
g_pending = 1;
g_place_queued = g_place_armed = 0; // (warp_request_place sets it again after this)
pthread_mutex_unlock(&g_lock);
LOG("[warp] queued %s / %s\n", level, locator);
}
Expand Down Expand Up @@ -111,6 +113,45 @@ bool warp_blocked(const char *locator) {
}
int warp_busy(void) { return g_pending; } // a queued warp not applied yet (shader tour)

// A warp to a room with no arrival point of its own (the Warp tab's dungeon rooms): the jump goes to the nearest
// arrival point of the level, then, once the level has loaded and settled, Link is moved onto a floor tile just inside
// the room (the PlayerCollision position doorjump.c moves him by). The game sees him in that room and scrolls there,
// as after a door jump.
enum { PLACE_DELAY = 40, PLACE_TIMEOUT = 1500 };
static int player_component_missing(void);
static unsigned g_place_loads;
static uint64_t g_place_since;
static float g_place_pos[3];
static char g_place_level[64];
void warp_request_place(const char *level, const char *locator, float x, float y, float z) {
warp_request(level, locator);
pthread_mutex_lock(&g_lock);
g_place_queued = 1, g_place_armed = 0;
g_place_pos[0] = x, g_place_pos[1] = y, g_place_pos[2] = z;
strlcpy(g_place_level, level, sizeof g_place_level);
pthread_mutex_unlock(&g_lock);
LOG("[warp] then Link -> (%.2f, %.2f, %.2f)\n", x, y, z);
}
static void place_tick(uint64_t now) {
if (!g_place_armed) return;
if (now - g_place_since > PLACE_TIMEOUT) { g_place_armed = 0; LOG("[warp] room placement dropped (timeout)\n"); return; }
if (g_loads == g_place_loads || now - g_load_frame < PLACE_DELAY || g_change || player_component_missing()) return;
uint64_t ps = LD64(GA(PLAYER_STATUS_PTR));
if (!ps || strncmp((const char *)(uintptr_t)ps, g_place_level, sizeof g_place_level)) return;
extern void doorjump_player_rescan(void);
extern uint64_t doorjump_player(uint64_t frame);
static unsigned rescanned = ~0u;
if (rescanned != g_loads) rescanned = g_loads, doorjump_player_rescan(); // (a warp within the same level)
uint64_t pc = doorjump_player(now);
if (!pc) return;
float *pos = (float *)(uintptr_t)(pc + 0x80), *pos2 = (float *)(uintptr_t)(pc + 0xb0);
pos[0] = pos2[0] = g_place_pos[0];
pos[1] = pos2[1] = g_place_pos[1];
pos[2] = pos2[2] = g_place_pos[2];
g_place_armed = 0;
LOG("[warp] Link placed at (%.2f, %.2f, %.2f) in %s\n", g_place_pos[0], g_place_pos[1], g_place_pos[2], g_place_level);
}

// LARECOMP_WARP_AT=<frame>:<level>:<locator>[,<frame>:<level>:<locator>...] (test hook)
// LARECOMP_WARP_ON_LOAD=<level>:<locator>: warp once, ~1 s after a save is loaded (hand-off to the user)
static void warp_on_load(uint64_t frame) {
Expand Down Expand Up @@ -264,6 +305,7 @@ static int player_component_missing(void) {
void warp_game_tick(Cpu *c) {
uint64_t now = g_frame_now;
watch_level_change(now);
place_tick(now);
if (!g_pending) return;
if (g_warp_waiting && g_loads == g_loads_at_warp && now - g_warp_frame < WARP_TIMEOUT)
return; // (the last warp's level isn't loaded yet: this one stays queued)
Expand Down Expand Up @@ -295,6 +337,7 @@ void warp_game_tick(Cpu *c) {
pthread_mutex_unlock(&g_lock);
LOG("[warp] jumping to %s / %s\n", (char *)(uintptr_t)(req + 0xa8), (char *)(uintptr_t)(req + 0xf0));
g_warp_waiting = 1, g_loads_at_warp = g_loads, g_warp_frame = now;
if (g_place_queued) g_place_queued = 0, g_place_armed = 1, g_place_loads = g_loads, g_place_since = now;
uint64_t saved[31], sp = c->sp, pc = c->pc;
memcpy(saved, c->x, sizeof saved);
c->x[0] = req;
Expand Down
26 changes: 26 additions & 0 deletions runtime/ui/warp_maps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// The Warp tab's maps (runtime/ui/warp_maps.mm), drawn with ImGui draw lists from our own simple shapes: the island
// as a 16 x 16 grid of squares coloured by region with small terrain marks and an icon per place, and a dungeon as its
// real floor of rooms (warp_rooms.h) with a closer sketch of one room.
#pragma once
#include "imgui.h"
#include "warp_rooms.h"

enum WarpIcon { WI_DOOR, WI_CAVE, WI_DUNGEON, WI_STAIRS, WI_LADDER, WI_DROP, WI_WARP, WI_PHONE, WI_FAIRY, WI_NOTE, WI_PIN,
WI_ROOM, WI_ENTRANCE, WI_BOAT, WI_N };
void warp_icon(ImDrawList *dl, int icon, ImVec2 c, float size, ImU32 col);

// One square of the island: its region (an index into regions[]) and the icons of the places on it
struct WarpIslandSquare { int region = -1; int icons[4]; int n = 0; };
struct WarpIsland {
WarpIslandSquare sq[16][16];
const char *regions[32];
int nregions = 0;
};
// a: top-left, b: bottom-right; cur: the square to frame (row, col; -1: none); region: the region to light up (-1)
void warp_draw_island(ImDrawList *dl, ImVec2 a, ImVec2 b, const WarpIsland &is, int cur_r, int cur_c, int region);

// The floor: every top-down room; sel: the room to frame (-1: none), dots: arrival points to mark ({room, x, z})
struct WarpDot { int room; float x, z; bool hot; };
void warp_draw_floor(ImDrawList *dl, ImVec2 a, ImVec2 b, const WarpLevel &L, int sel, const WarpDot *dots, int ndots);
// One room (all the cells of its unit) closer, with its arrival points
void warp_draw_room(ImDrawList *dl, ImVec2 a, ImVec2 b, const WarpLevel &L, int room, const WarpDot *dots, int ndots);
Loading
Loading