data/claims/tww.yamlThe Legend of Zelda: The Wind Waker (the zeldaret decompilation, zeldaret/tww)
In the decompilation's C++ for the boat (d_a_ship, which doesn't yet match the game's bytes as a whole; the USA GZLE01 build), procSteerMove, while the boat isn't airborne (daSFLG_FLY_e off), takes the gap between the wind vector's yaw (cM_atan2s of its x and z) and the boat's facing shape_angle.y, as a 16-bit angle from 0 to 0x8000, and turns it into a wind factor: 1 − gap/0x10000 below 0x4000 (90°), 0.75 − (gap − 0x4000) × 5.493164e-05 up to 0x6000 (135°), then 0.3 − (gap − 0x6000) × 0.00032967035, floored at 0. So the factor is 1 with the wind along the boat's facing, 0.875 at 45°, 0.75 at 90°, 0.3 at 135°, and 0 from 140° to 180° (computed). The target speed is the wind power divided by 0.5, capped at 1, times the factor times 55. The branches then run in this order, one per update. First, on the body animation's frame 7, if speedF is below twice the target (at most 80) it is raised to that and mTargetSpeed is set to the target (10000 if the boat is being moved by force), and nothing else changes speedF that update. Otherwise, while mTargetSpeed is below speedF, firstDecrementShipSpeed lowers speedF toward mTargetSpeed (a tenth of the gap, at most 5, at least 1) and sets mTargetSpeed to 10000 once the gap is under 3; this comes before the sail check, so it applies with the sail flag off too. Otherwise, with the sail-on flag off or a target of 0, decrementShipSpeed eases speedF toward 10 × the stick's value × the cosine of its angle (a twentieth of the gap, at most 0.1, at least 0.015). Otherwise decrementShipSpeed eases it down to a lower target, and cLib_addCalc up to a higher one (a tenth of the gap, at most 0.5, at least 0.1). tww/sail-speed
About: The Legend of Zelda: The Wind Waker Sailing
Value
{
"factor": {
"0": 1,
"45": 0.875,
"90": 0.75,
"135": 0.3,
"140": 0,
"180": 0
},
"topSpeed": 55,
"fullPowerAt": 0.5,
"sailOffSpeed": 10,
"kickCap": 80
}src/d/actor/d_a_ship.cpp#L1594-L1637if (!checkStateFlg(daSFLG_FLY_e)) {
cXyz* windVec = dKyw_get_wind_vec();
f32 targetSpeed = *dKyw_get_wind_power() / 0.5f;
int angleDiff = abs((s16)(cM_atan2s(windVec->x, windVec->z) - shape_angle.y));
…
if (angleDiff < 0x4000) {
windFactor = 1.0f - angleDiff * (1.0f / 0x10000);
} else if (angleDiff <= 0x6000) {
windFactor = 0.75f - (angleDiff - 0x4000) * 5.493164e-05f;
} else {
windFactor = 0.3f - (angleDiff - 0x6000) * 0.00032967035f;
if (windFactor < 0.0f) {
…tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/actor/d_a_ship.cpp#L1090-L1098void daShip_c::decrementShipSpeed(f32 i_targetSpeed) {
cLib_addCalc(&speedF, i_targetSpeed, 0.05f, 0.1f, 0.015f);
}
…
void daShip_c::firstDecrementShipSpeed(f32 i_targetSpeed) {
if (cLib_addCalc(&speedF, i_targetSpeed, 0.1f, 5.0f, 1.0f) < 3.0f) {
mTargetSpeed = 10000.0f;tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/SSystem/SComponent/c_lib.cpp#L22-L32f32 cLib_addCalc(f32* pValue, f32 target, f32 scale, f32 maxStep, f32 minStep) {
if (*pValue != target) {
f32 step = scale * (target - *pValue);
if (step >= minStep || step <= -minStep) {
if (step > maxStep) {
step = maxStep;
}
if (step < -maxStep) {
step = -maxStep;
}
*pValue += step;Checked by claude, 2026-10-22 — read procSteerMove, the two decrement helpers, and cLib_addCalc at the pinned commit; worked the factor at 45, 90, 135 degrees and its zero (0x6000 + 910 units) by handThe factor's three pieces meet: 0.75 at 0x4000 and 0.3 at 0x6000 (0.45 / 0x2000 = 5.4931640625e-05), and 0.3 / 0.00032967035 = 910 units past 0x6000, about 140.0 degrees. Below the minimum step, cLib_addCalc adds the minimum and stops at the target (c_lib.cpp lines 33-48). Which way the wind vector points (toward where the wind blows, or from it) is the atlas's reading from the factor: the boat is fastest when the vector lies along its facing. What animation plays at the frame-7 check, and the units of speedF, are unread; the d_a_ship object doesn't match the original as a whole, so a per-function match is unconfirmed.
Cited by: Sailing, The Legend of Zelda: The Wind Waker
In the USA build, dKyw_wind_set, when no actor has set a wind vector override (mpWindVecOverride), takes the wind's strength from the room's file list (dStage_FileList_dt_GlobalWindLevel): level 0 gives 0.3, level 1 gives 0.6, level 2 gives 0.9, any other level 0. A custom wind power above 0 replaces it, an event wind setting of 2 makes it 0, and it is capped at 1. While mInitAnimTimer is nonzero the wind vector and power jump to the new values; otherwise each call moves each vector component a tenth of the way (at most 2, at least 0.001) and the power a tenth of the way (at most 1, at least 0.005). Since the boat, in the decompilers' not-yet-matching C++ for d_a_ship (procSteerMove), divides the power by 0.5 and caps it at 1, a level-0 room tops out at 0.6 of the boat's 55, 33 units an update, and levels 1 and 2 at the full 55 (computed, once the power has settled). tww/wind-strength
About: The Legend of Zelda: The Wind Waker Wind
Value
{
"levels": {
"0": 0.3,
"1": 0.6,
"2": 0.9
},
"other": 0,
"cap": 1,
"boatTop": {
"0": 33,
"1": 55,
"2": 55
}
}src/d/d_kankyo_wether.cpp#L990-L1040if (g_env_light.mWind.mpWindVecOverride != NULL) {
wind_vec = *g_env_light.mWind.mpWindVecOverride;
strength = g_env_light.mWind.mWindStrengthOverride;
…
if (fili_p != NULL) {
wind_level = dStage_FileList_dt_GlobalWindLevel(fili_p);
}
switch (wind_level) {
case 0:
strength = 0.3f;
break;
…tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo_wether.cpp#L1062-L1084if (g_env_light.mWind.mCustomWindPower > 0.0f) {
strength = g_env_light.mWind.mCustomWindPower;
…
if (g_env_light.mWind.mEvtWindSet == 2) {
strength = 0.0f;
}
if (strength > 1.0f) {
strength = 1.0f;
}
if (g_env_light.mInitAnimTimer != 0) {
…tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/actor/d_a_ship.cpp#L1596-L1596f32 targetSpeed = *dKyw_get_wind_power() / 0.5f;Checked by claude, 2026-10-22 — read dKyw_wind_set at the pinned commit; the VERSION_DEMO block at lines 1041-1059 (a fixed wind on the demo's sea_E stage) is outside the USA build and outside the cited rangesEach room's wind level is stage data the repo doesn't hold, so which seas blow at which level is unread. dKyw_gbwind_use_check treats a level of 2 or less as using the global wind.
Cited by: The Legend of Zelda: The Wind Waker, Wind
In the USA build, the wind-direction picker dOperate_wind_c (which the atlas reads as the Wind's Requiem's, from its TAKT_WIND sounds; "takt" is the baton) sets mDeg on opening from the current wind vector's yaw and then, with the stick pushed to at least 0.9, sets mDeg to one of eight compass values, 0 to 315 in 45-degree steps. A confirms it: windSet passes dKyw_tact_wind_set a pitch of 0 and a yaw of 180° − mDeg (0 gives −0x8000, 90 gives 0x4000, 270 gives −0x4000). dKyw_tact_wind_set stores both angles for the environment and in the save (dComIfGs_setWindX, setWindY) and sets the tact-wind flag; dKyw_wind_set then builds the wind vector as (cos pitch × cos yaw, sin pitch, cos pitch × sin yaw). dKyw_wind_init reloads the saved angles unless both are −1. B cancels the picker. tww/wind-requiem
About: The Legend of Zelda: The Wind Waker Wind
Value
{
"directions": 8,
"step": 45,
"yawFromDeg": "180 - mDeg",
"persists": "save"
}src/d/d_operate_wind.cpp#L370-L384if (stick->getValueStick() >= 0.89999998f && fabs(stick_value) - fabs(field_0x8b8) >= 0.0) {
int stick_control = dOw_stickControl(field_0x8d0, stick->getAngleStick());
…
switch (stick_control) {
case 8:
if (mDeg != 315) {
field_0x8ca = 7;
mDeg = 315;
mDoAud_seStart(JA_SE_TAKT_WIND_CURSOR);tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_operate_wind.cpp#L462-L488void dOperate_wind_c::windSet() {
switch (mDeg) {
case 315:
dKyw_tact_wind_set(0, -0x6000);
break;
case 0:
dKyw_tact_wind_set(0, -0x8000);
break;
case 45:
dKyw_tact_wind_set(0, 0x6000);
break;
case 90:
…tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_operate_wind.cpp#L613-L616cXyz* wind_vec = dKyw_get_wind_vec();
s16 temp_r3 = cM_atan2s(wind_vec->x, wind_vec->z) + 0x8000;
mDeg = dOw_angleRegular(((180.0f * temp_r3) / (f32)0x8000) - 90.0f);
arrowRotate(&field_0xec, mDeg);tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_operate_wind.cpp#L661-L666if (CPad_CHECK_TRIG_A(0)) {
windSet();
dComIfGp_setOperateWindChangeOff();
} else if (CPad_CHECK_TRIG_B(0)) {
dComIfGp_setOperateWindCancelOff();
}tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo_wether.cpp#L1328-L1338void dKyw_tact_wind_set(s16 i_windX, s16 i_windY) {
…
env_light.mWind.mTactWindAngleX = i_windX;
env_light.mWind.mTactWindAngleY = i_windY;
dComIfGs_setWindX(i_windX);
dComIfGs_setWindY(i_windY);
…
env_light.mWind.mTactWindAngleFlags = 1;tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo_wether.cpp#L1010-L1020} else if (g_env_light.mWind.mTactWindAngleFlags != 0) {
tact_wind_x = g_env_light.mWind.mTactWindAngleX;
tact_wind_y = g_env_light.mWind.mTactWindAngleY;
…
wind_vec.x = cM_scos(tact_wind_x) * cM_scos(tact_wind_y);
wind_vec.y = cM_ssin(tact_wind_x);
wind_vec.z = cM_scos(tact_wind_x) * cM_ssin(tact_wind_y);tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo_wether.cpp#L967-L974if (dComIfGs_getWindX() == -1 && dComIfGs_getWindY() == -1) {
g_env_light.mWind.mTactWindAngleFlags = 0;
…
g_env_light.mWind.mTactWindAngleFlags = 1;
g_env_light.mWind.mTactWindAngleX = dComIfGs_getWindX();
g_env_light.mWind.mTactWindAngleY = dComIfGs_getWindY();Checked by claude, 2026-10-22 — read arrowDirection, windSet, the picker's setup, and _move in d_operate_wind.cpp, and dKyw_tact_wind_set, dKyw_wind_set, and dKyw_wind_init at the pinned commit; the yaws match 180 - mDeg in 0x10000-per-turn units for all eight casesAn event wind (mEvtWindSet nonzero and not 0xFF) takes precedence over the tact wind in dKyw_wind_set (line 1007). Which stick direction gives which mDeg depends on dOw_stickControl, not cited. dKyw_get_windsdir (lines 1360-1391) sorts the current wind vector into eight 0x2000-wide sectors. How the baton song opens the picker is in the player code, not fetched.
Cited by: The Legend of Zelda: The Wind Waker, Wind
In the decompilation's C++ for the boat (d_a_ship, not yet matching as a whole; the USA build), procSteerMove moves the unnamed angle m0366, which the atlas reads as the tiller, toward mStickMVal × 0x2000 × −sin(mStickMAng) with cLib_addCalcAngleS(…, 4, l_HIO.tiller_speed, 0x100), tiller_speed being 700, and, unless the boat is airborne, passes it to setMoveAngle. Unless the boat is being moved by force (checkForceMove), setMoveAngle sets a rate of 4 − |speedF| / 55 × 3, held between 0.1 and 3.6, blends it toward 3.6 by the unnamed counter m0388 over 15 (3.6 once m0388 reaches 15), and turns the facing by −rate × (m0366 >> 6). With m0388 at 0 and a tiller of 0x2000, that is about 461 units (2.5°) an update at rest and 128 units (0.7°) at speed 55 (computed): the boat turns slower the faster it sails. tww/turn-rate
About: The Legend of Zelda: The Wind Waker Sailing
Value
{
"rateAtRest": 3.6,
"rateAt55": 1,
"rateMin": 0.1,
"maxTiller": 8192,
"tillerSpeed": 700,
"turnAtRest": 461,
"turnAt55": 128
}src/d/actor/d_a_ship.cpp#L1586-L1589cLib_addCalcAngleS(&m0366, mStickMVal * 0x2000 * -cM_ssin(mStickMAng), 4, l_HIO.tiller_speed, 0x100);
if (!checkStateFlg(daSFLG_FLY_e)) {
setMoveAngle(m0366);
}tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/actor/d_a_ship.cpp#L71-L71const s16 daShip_HIO_c0::tiller_speed = 700;tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/actor/d_a_ship.cpp#L1113-L1130void daShip_c::setMoveAngle(s16 moveAngle) {
if (!checkForceMove()) {
s16 initialAngle = shape_angle.y;
f32 turnRate = 4.0f - std::abs(speedF) / 55.0f * 3.0f;
if (turnRate > 3.6f) {
turnRate = 3.6f;
}
else if (turnRate < 0.1f) {
turnRate = 0.1f;
}
if (m0388 >= 0xF) {
turnRate = 3.6f;
…Checked by claude, 2026-10-22 — read procSteerMove's first lines, the tiller_speed constant, and setMoveAngle at the pinned commit; 0x2000 >> 6 = 128, times 3.6 and 1.0The range of mStickMVal (taken here as 0 to 1, so the tiller target is at most 0x2000) and the semantics of cLib_addCalcAngleS's arguments are unread, as is what m0388 counts.
Cited by: Sailing, The Legend of Zelda: The Wind Waker
In the decompilation's C++ for the boat (d_a_ship, not yet matching as a whole; the USA build), procSteerMove allows a hop when no event runs, Link isn't barred from control (checkNoControll), the body animation is FN_MAST_ON2 at frame 7 or later, the boat isn't airborne, its forward speed mFwdVel is over 16.5, it isn't being moved by force, and the sailing target speed is at least 11. Then the R trigger's lock (mDoCPd_R_LOCK_TRIGGER) sets the airborne and jump flags and an upward speed equal to the target speed, held between 15 and 40, and sets mJumpRate to (speed.y − 11) × 0.014545455. tww/hop
About: The Legend of Zelda: The Wind Waker Sailing
Value
{
"minForward": 16.5,
"minTarget": 11,
"upMin": 15,
"upMax": 40
}src/d/actor/d_a_ship.cpp#L1638-L1658if (!dComIfGp_event_runCheck() &&
!daPy_getPlayerLinkActorClass()->checkNoControll() &&
m0392 == dRes_INDEX_SHIP_BCK_FN_MAST_ON2_e &&
mpBodyAnm->getFrame() >= 7.0f &&
!checkStateFlg(daSFLG_FLY_e) &&
mFwdVel > 16.5f) {
if (!checkForceMove() && targetSpeed >= 11.0f) {
onStateFlg(daSFLG_JUMP_OK_e);
if (mDoCPd_R_LOCK_TRIGGER(0)) {
onStateFlg((daSHIP_SFLG)(daSFLG_FLY_e | daSFLG_JUMP_e));
speed.y = targetSpeed;
if (speed.y < 15.0f) {
…Checked by claude, 2026-10-22 — read the end of procSteerMove at the pinned commitThe hop sits inside the not-airborne branch whose target speed sail-speed describes, so a hop needs enough wind at a narrow enough angle. FN_MAST_ON2 reads as the mast-raised animation by its name; what mJumpRate drives is unread.
Cited by: The Legend of Zelda: The Wind Waker
In the USA build, the time of day is a float from 0 to 360, 15 to an hour (dKy_getdaytime_hour divides by 15); dKy_daynight_check calls 6:00 to 17:59 day. The environment's setup sets the advance mTimeAdv to 0.02, which dKy_custom_timeset can change. Each setDaytime call adds it only when no event-night stop is set, the save's get-item flag (2, 0) is set, no event runs, and (USA and PAL only) Link isn't on stage "sea" room 14 with stage layer 2 or 3. In rooms that pass time (getTimePass) the clock wraps from 360 to 0 and the day of the week goes up by one; in other rooms it advances only by day before 165 (11:00) and by night from 270 (18:00) to 345 (23:00). At 0.02 a day is 18,000 setDaytime calls (computed). tww/clock
About: The Legend of Zelda: The Wind Waker Time of day
Value
{
"unitsPerDay": 360,
"unitsPerHour": 15,
"perUpdate": 0.02,
"updatesPerDay": 18000,
"dayFrom": "6:00",
"dayUntil": "18:00"
}src/d/d_kankyo.cpp#L476-L476g_env_light.mTimeAdv = 0.02f;tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo.cpp#L502-L538void dScnKy_env_light_c::setDaytime() {
…
#if VERSION > VERSION_JPN
if (strcmp(dComIfGp_getStartStageName(), "sea") == 0 && dComIfGp_roomControl_getStayNo() == 14)
{
if (dComIfGp_getStartStageLayer() == 2 || dComIfGp_getStartStageLayer() == 3) {
var_r30 = true;
…
if (!dKy_checkEventNightStop() && dComIfGs_isGetItem(2, 0) && dComIfGp_event_runCheck() == FALSE
#if VERSION > VERSION_DEMO
&& !var_r30
#endif
…tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo.cpp#L614-L631int dKy_getdaytime_hour() {
return dComIfGs_getTime() / 15.0f;
…
int dKy_daynight_check() {
s32 hour = dKy_getdaytime_hour();
if (hour >= 6 && hour < 18) {
return dKy_TIME_DAY_e;tww: a decompilation of The Legend of Zelda: The Wind Waker (zeldaret/tww) · src/d/d_kankyo.cpp#L3361-L3362void dKy_custom_timeset(f32 i_speed) {
g_env_light.mTimeAdv = i_speed;Checked by claude, 2026-10-22 — read setDaytime, its init, the hour and day/night helpers, and dKy_custom_timeset at the pinned commit; include/global.h puts VERSION_USA above VERSION_JPN, so both version blocks compile for GZLE01How often setDaytime runs, and so a day's real length, is unread; at one call a frame and 30 frames a second, 18,000 calls would be 10 minutes, but neither rate is in the cited code. The stage's TimeH can set the hour on load (lines 472-475). What get-item flag (2, 0) stands for is unread.
Cited by: The Legend of Zelda: The Wind Waker, Time of day