[Et tu] Implement Start{X|Y}Pos (#585)

* [Et tu] Implement `Start{X|Y}Pos`, `ViewX|Y`
This commit is contained in:
Mike Klaas
2026-07-26 14:07:55 -07:00
committed by GitHub
parent 8e60a2fba2
commit 1db054e47a
6 changed files with 126 additions and 4 deletions
+5
View File
@@ -42,6 +42,11 @@ The following settings were moved into [`<DAT>/config/game.cfg`](files/ce.dat/co
| ddraw.ini section | ddraw.ini key | game.cfg section | game.cfg key |
| --- | --- | --- | --- |
| `Misc` | `StartingMap` | `start` | `map` |
| `Misc` | `StartXPos` | `start` | `worldmap_x` |
| `Misc` | `StartYPos` | `start` | `worldmap_y` |
| `Misc` | `ViewXPos` | `start` | `worldmap_view_x` |
| `Misc` | `ViewYPos` | `start` | `worldmap_view_y` |
| `Misc` | `StartGDialogFix` | `dialog` | `start_gdialog_fix` |
## Opcodes / Metarules
+6
View File
@@ -11,6 +11,12 @@ month=6
day=24
; To start a new game somewhere other than artemple.map, set this to the map file name.
map=
; Starting worldmap marker position. -1 uses the starting map's world area.
worldmap_x=-1
worldmap_y=-1
; Starting worldmap viewport position. -1 uses the default upper-left view.
worldmap_view_x=-1
worldmap_view_y=-1
; Starting (tribal) and default (jumpsuit) player models. Can also be changed in-game via script.
model_male=hmwarr
model_male_default=hmjmps
+4
View File
@@ -175,6 +175,10 @@ namespace {
constexpr SfallMigrationEntry kSfallMigrationEntries[] = {
// [start]
{ kSfallMisc, "StartingMap", CONTENT_CONFIG_START_SECTION, "map", "" },
{ kSfallMisc, "StartXPos", CONTENT_CONFIG_START_SECTION, "worldmap_x", "-1" },
{ kSfallMisc, "StartYPos", CONTENT_CONFIG_START_SECTION, "worldmap_y", "-1" },
{ kSfallMisc, "ViewXPos", CONTENT_CONFIG_START_SECTION, "worldmap_view_x", "-1" },
{ kSfallMisc, "ViewYPos", CONTENT_CONFIG_START_SECTION, "worldmap_view_y", "-1" },
{ kSfallMisc, "MaleStartModel", CONTENT_CONFIG_START_SECTION, "model_male", "hmwarr" },
{ kSfallMisc, "MaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_male_default", "hmjmps" },
{ kSfallMisc, "FemaleStartModel", CONTENT_CONFIG_START_SECTION, "model_female", "hfprim" },
+13
View File
@@ -331,6 +331,19 @@ static int _main_load_new(char* mapFileName)
gameMouseSetCursor(MOUSE_CURSOR_NONE);
mouseShowCursor();
mapLoadByName(mapFileName);
// SFALL: Fix the starting position of the player's marker on the world map
// when starting a new game with a custom starting map.
int areaIdx;
if (wmMatchAreaContainingMapIdx(gMapHeader.index, &areaIdx) == 0) {
if (wmStartWorldPosIsConfigured()) {
wmSetPartyCurArea(areaIdx);
wmClearPartyWalking();
} else {
wmTeleportToArea(areaIdx);
}
}
wmMapMusicStart();
paletteFadeTo(gPaletteWhite);
windowDestroy(win);
+95 -4
View File
@@ -480,6 +480,10 @@ static std::vector<std::pair<int, std::string>> wmTerrainNameOverrides;
static void wmSetFlags(int* flagsPtr, int flag, int value);
static int wmGenDataInit();
static int wmGenDataReset();
static void wmGenDataSetStartWorldPos();
static bool wmGetStartWorldMapConfigValue(const char* key, int* valuePtr);
static void wmGenDataClampWorldPosToBounds();
static void wmSetStartWorldView();
static int wmWorldMapSaveTempData();
static int wmWorldMapLoadTempData();
static int wmConfigInit();
@@ -979,8 +983,11 @@ int wmWorldMap_init()
return -1;
}
wmGenDataClampWorldPosToBounds();
wmGenData.viewportMaxX = WM_TILE_WIDTH * wmNumHorizontalTiles - WM_VIEW_WIDTH;
wmGenData.viewportMaxY = WM_TILE_HEIGHT * (wmMaxTileNum / wmNumHorizontalTiles) - WM_VIEW_HEIGHT;
wmSetStartWorldView();
circleBlendTable = _getColorBlendTable(COLOR_GREEN);
wmMarkSubTileRadiusVisited(wmGenData.worldPosX, wmGenData.worldPosY);
@@ -1009,8 +1016,7 @@ static int wmGenDataInit()
{
gDidMeetFrankHorrigan = false;
wmGenData.currentAreaId = -1;
wmGenData.worldPosX = 173;
wmGenData.worldPosY = 122;
wmGenDataSetStartWorldPos();
wmGenData.currentSubtile = nullptr;
wmGenData.dword_672E18 = 0;
wmGenData.isWalking = false;
@@ -1076,8 +1082,8 @@ static int wmGenDataReset()
wmGenData.encounterIconIsVisible = false;
mousePressed = false;
wmGenData.currentAreaId = -1;
wmGenData.worldPosX = 173;
wmGenData.worldPosY = 122;
wmGenDataSetStartWorldPos();
wmGenDataClampWorldPosToBounds();
wmGenData.walkDestinationX = -1;
wmGenData.walkDestinationY = -1;
wmGenData.encounterMapId = -1;
@@ -1113,6 +1119,70 @@ static int wmGenDataReset()
return 0;
}
static void wmGenDataSetStartWorldPos()
{
wmGenData.worldPosX = 173;
wmGenData.worldPosY = 122;
int value;
if (wmGetStartWorldMapConfigValue("worldmap_x", &value)) {
wmGenData.worldPosX = value;
}
if (wmGetStartWorldMapConfigValue("worldmap_y", &value)) {
wmGenData.worldPosY = value;
}
}
static bool wmGetStartWorldMapConfigValue(const char* key, int* valuePtr)
{
assert(key != nullptr);
assert(valuePtr != nullptr);
int value;
if (!configGetInt(&gContentConfig, CONTENT_CONFIG_START_SECTION, key, &value)) {
return false;
}
if (value == -1) {
return false;
}
*valuePtr = std::max(value, 0);
return true;
}
static void wmGenDataClampWorldPosToBounds()
{
if (wmNumHorizontalTiles <= 0 || wmMaxTileNum <= 0) {
return;
}
int worldMaxX = WM_TILE_WIDTH * wmNumHorizontalTiles;
int worldMaxY = WM_TILE_HEIGHT * (wmMaxTileNum / wmNumHorizontalTiles);
if (worldMaxX <= 0 || worldMaxY <= 0) {
return;
}
wmGenData.worldPosX = std::clamp(wmGenData.worldPosX, 0, worldMaxX - 1);
wmGenData.worldPosY = std::clamp(wmGenData.worldPosY, 0, worldMaxY - 1);
}
static void wmSetStartWorldView()
{
wmWorldOffsetX = 0;
wmWorldOffsetY = 0;
int value;
if (wmGetStartWorldMapConfigValue("worldmap_view_x", &value)) {
wmWorldOffsetX = std::clamp(value, 0, std::max(wmGenData.viewportMaxX, 0));
}
if (wmGetStartWorldMapConfigValue("worldmap_view_y", &value)) {
wmWorldOffsetY = std::clamp(value, 0, std::max(wmGenData.viewportMaxY, 0));
}
}
// 0x4BCE00 wmWorldMap_exit
void wmWorldMap_exit()
{
@@ -1177,6 +1247,7 @@ int wmWorldMap_reset()
gGameTimeIncRemainder = 0.0;
wmWorldMapLoadTempData();
wmSetStartWorldView();
wmMarkAllSubTiles(0);
return wmGenDataReset();
@@ -6170,6 +6241,14 @@ int wmGetPartyCurArea(int* areaIdxPtr)
return -1;
}
bool wmStartWorldPosIsConfigured()
{
int x;
int y;
return wmGetStartWorldMapConfigValue("worldmap_x", &x)
|| wmGetStartWorldMapConfigValue("worldmap_y", &y);
}
// 0x4C47D8 wmMarkAllSubTiles
static void wmMarkAllSubTiles(int state)
{
@@ -7087,6 +7166,18 @@ void wmSetPartyWorldPos(int x, int y)
wmGenData.worldPosY = y;
}
void wmSetPartyCurArea(int areaIdx)
{
wmGenData.currentAreaId = cityIsValid(areaIdx) ? areaIdx : -1;
}
void wmClearPartyWalking()
{
wmGenData.walkDestinationX = 0;
wmGenData.walkDestinationY = 0;
wmGenData.isWalking = false;
}
void wmCarSetCurrentArea(int area)
{
wmGenData.currentCarAreaId = area;
+3
View File
@@ -285,6 +285,9 @@ int wmMatchAreaContainingMapIdx(int mapIdx, int* areaIdxPtr);
int wmTeleportToArea(int areaIdx);
// CE
bool wmStartWorldPosIsConfigured();
void wmSetPartyCurArea(int areaIdx);
void wmClearPartyWalking();
void wmSetPartyWorldPos(int x, int y);
void wmCarSetCurrentArea(int area);
void wmForceEncounter(int map, unsigned int flags);