Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a24de03e6 | ||
|
|
e4025c2aec | ||
|
|
43cf086ea4 | ||
|
|
abd720dd6e |
@@ -41,6 +41,15 @@ radar moved out of the middle of the road (`environ.ini`). Each display's
|
|||||||
button bank now reaches under its glass, so the picture itself is the press
|
button bank now reaches under its glass, so the picture itself is the press
|
||||||
target rather than a sliver at the edge.
|
target rather than a sliver at the edge.
|
||||||
|
|
||||||
|
[v4.12.4](https://gitea.mysticmachines.com/VWE/RP412/releases/tag/v4.12.4)
|
||||||
|
is about the lobby. Both rooms now show the host's mission setup — the track,
|
||||||
|
then time of day, weather and game length — since everyone flies the owner's
|
||||||
|
picks and it was the one thing in the room nobody could see for themselves.
|
||||||
|
The football team sheet names each player's VTV beside their team colours and
|
||||||
|
position. And a lobby holds **eight** players rather than four, a full grid as
|
||||||
|
the pod hall ran it, with the room sizing its roster to whatever space the
|
||||||
|
window gives it.
|
||||||
|
|
||||||
## Playing
|
## Playing
|
||||||
|
|
||||||
Grab the release zip (or run `pack-dist.ps1` on a build). Single player:
|
Grab the release zip (or run `pack-dist.ps1` on a build). Single player:
|
||||||
|
|||||||
+1
-1
@@ -177,7 +177,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_STREAM << "Red Planet 4.12.3" << std::endl << std::flush;
|
DEBUG_STREAM << "Red Planet 4.12.4" << std::endl << std::flush;
|
||||||
DEBUG_STREAM << "L4CONTROLS=" << getenv("L4CONTROLS") << std::endl << std::flush;
|
DEBUG_STREAM << "L4CONTROLS=" << getenv("L4CONTROLS") << std::endl << std::flush;
|
||||||
|
|
||||||
#ifdef RP412_STEAM
|
#ifdef RP412_STEAM
|
||||||
|
|||||||
@@ -1380,6 +1380,122 @@ void
|
|||||||
strcpy(badge, kBadges[b].key);
|
strcpy(badge, kBadges[b].key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//########################################################################
|
||||||
|
// The host's mission setup, as display names.
|
||||||
|
//
|
||||||
|
// The lobby shows everyone what they are about to fly into, and only the
|
||||||
|
// owner knows it - so the owner publishes these into lobby data and the
|
||||||
|
// members read them back. Names rather than catalog keys: the map lists
|
||||||
|
// differ between race and football, so resolving here means the room
|
||||||
|
// never has to know which scenario's table a key came from.
|
||||||
|
//########################################################################
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedMapName()
|
||||||
|
{
|
||||||
|
if (!gHavePersist)
|
||||||
|
{
|
||||||
|
return kMaps[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
const CatalogEntry *maps = ActiveMaps(gPersistSelection, &count);
|
||||||
|
int index = gPersistSelection[GroupMap];
|
||||||
|
if (index < 0 || index >= count)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return maps[index].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedTimeName()
|
||||||
|
{
|
||||||
|
int index = gHavePersist ? gPersistSelection[GroupTime] : 0;
|
||||||
|
if (index < 0 || index >= FE_COUNT(kTimes))
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return kTimes[index].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedWeatherName()
|
||||||
|
{
|
||||||
|
int index = gHavePersist ? gPersistSelection[GroupWeather] : 0;
|
||||||
|
if (index < 0 || index >= FE_COUNT(kWeather))
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return kWeather[index].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedLengthName()
|
||||||
|
{
|
||||||
|
// the menu defaults this one to 5:00, not to entry zero
|
||||||
|
int index = gHavePersist ? gPersistSelection[GroupLength] : 2;
|
||||||
|
if (index < 0 || index >= FE_COUNT(kLengths))
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return kLengths[index].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
//########################################################################
|
||||||
|
// Catalog key -> display name. Member data travels as keys, so the room
|
||||||
|
// needs these to show "Battle Barge" where the wire says "bttlbrg".
|
||||||
|
//########################################################################
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const char *
|
||||||
|
NameForKey(const CatalogEntry *table, int count, const char *key)
|
||||||
|
{
|
||||||
|
if (key == NULL || key[0] == '\0')
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if (strcmp(table[i].key, key) == 0)
|
||||||
|
{
|
||||||
|
return table[i].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key; // unknown to this build: show it raw rather than blank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_VehicleNameForKey(const char *key)
|
||||||
|
{
|
||||||
|
return NameForKey(kVehicles, FE_COUNT(kVehicles), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_PositionNameForKey(const char *key)
|
||||||
|
{
|
||||||
|
return NameForKey(kPositions, FE_COUNT(kPositions), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_TeamNameForKey(const char *key)
|
||||||
|
{
|
||||||
|
if (key == NULL || key[0] == '\0')
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < FE_COUNT(kTeams); ++i)
|
||||||
|
{
|
||||||
|
if (strcmp(kTeams[i].key, key) == 0)
|
||||||
|
{
|
||||||
|
return kTeams[i].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
//########################################################################
|
//########################################################################
|
||||||
// Football team / position picks (the lobby publishes and cycles these)
|
// Football team / position picks (the lobby publishes and cycles these)
|
||||||
//########################################################################
|
//########################################################################
|
||||||
|
|||||||
@@ -50,6 +50,33 @@ int
|
|||||||
void
|
void
|
||||||
RPL4FrontEnd_GetLoadout(char *vehicle, char *color, char *badge);
|
RPL4FrontEnd_GetLoadout(char *vehicle, char *color, char *badge);
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// This player's mission setup as display names. Only the host's
|
||||||
|
// picks decide the mission, so the lobby owner publishes these for
|
||||||
|
// everyone else to read - names rather than keys, because the map
|
||||||
|
// catalog differs between race and football and resolving it here
|
||||||
|
// saves the room from knowing which table a key belongs to.
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedMapName();
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedTimeName();
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedWeatherName();
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_SelectedLengthName();
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Catalog key -> display name, for the keys that travel as lobby
|
||||||
|
// member data. Unknown keys come back unchanged rather than blank.
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_VehicleNameForKey(const char *key);
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_PositionNameForKey(const char *key);
|
||||||
|
const char *
|
||||||
|
RPL4FrontEnd_TeamNameForKey(const char *key);
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Football team / position: the catalogs, and this player's picks.
|
// Football team / position: the catalogs, and this player's picks.
|
||||||
// The lobby publishes the picks as member data and lets members
|
// The lobby publishes the picks as member data and lets members
|
||||||
|
|||||||
+196
-19
@@ -36,7 +36,10 @@ void RPL4Lobby_PullRaceResults() { }
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
enum { kMaxLobbyMembers = 4 };
|
// A full grid is eight pods, as the pod hall ran it. The egg builder
|
||||||
|
// carries the owner plus maxExtraPilots (8), so eight members fit
|
||||||
|
// with room to spare.
|
||||||
|
enum { kMaxLobbyMembers = 8 };
|
||||||
const char kLobbyTagKey[] = "rp412";
|
const char kLobbyTagKey[] = "rp412";
|
||||||
const char kGoKey[] = "go";
|
const char kGoKey[] = "go";
|
||||||
|
|
||||||
@@ -47,6 +50,12 @@ namespace
|
|||||||
const char kResultsKey[] = "res";
|
const char kResultsKey[] = "res";
|
||||||
const char kScenarioKey[] = "sc";
|
const char kScenarioKey[] = "sc";
|
||||||
|
|
||||||
|
// the owner's mission setup, shown to everyone in the room
|
||||||
|
const char kMapKey[] = "mp";
|
||||||
|
const char kTimeKey[] = "td";
|
||||||
|
const char kWeatherKey[] = "wx";
|
||||||
|
const char kLengthKey[] = "ln";
|
||||||
|
|
||||||
// async call-result plumbing
|
// async call-result plumbing
|
||||||
Logical gCallDone = False;
|
Logical gCallDone = False;
|
||||||
Logical gCallOK = False;
|
Logical gCallOK = False;
|
||||||
@@ -154,12 +163,24 @@ namespace
|
|||||||
SteamMatchmaking()->SetLobbyMemberData(gLobby, "ps",
|
SteamMatchmaking()->SetLobbyMemberData(gLobby, "ps",
|
||||||
RPL4FrontEnd_PositionKey(RPL4FrontEnd_GetPositionIndex()));
|
RPL4FrontEnd_PositionKey(RPL4FrontEnd_GetPositionIndex()));
|
||||||
|
|
||||||
// the owner also publishes the scenario, so members know
|
//---------------------------------------------------------------
|
||||||
// whether their team/position pick matters
|
// Only the owner's menu decides the mission, so the owner also
|
||||||
|
// publishes what it picked: the scenario (members need it to know
|
||||||
|
// whether their team/position pick matters) and the setup
|
||||||
|
// everyone is about to fly into.
|
||||||
|
//---------------------------------------------------------------
|
||||||
if (IsOwner())
|
if (IsOwner())
|
||||||
{
|
{
|
||||||
SteamMatchmaking()->SetLobbyData(gLobby, kScenarioKey,
|
SteamMatchmaking()->SetLobbyData(gLobby, kScenarioKey,
|
||||||
RPL4FrontEnd_IsFootballSelected() ? "football" : "race");
|
RPL4FrontEnd_IsFootballSelected() ? "football" : "race");
|
||||||
|
SteamMatchmaking()->SetLobbyData(gLobby, kMapKey,
|
||||||
|
RPL4FrontEnd_SelectedMapName());
|
||||||
|
SteamMatchmaking()->SetLobbyData(gLobby, kTimeKey,
|
||||||
|
RPL4FrontEnd_SelectedTimeName());
|
||||||
|
SteamMatchmaking()->SetLobbyData(gLobby, kWeatherKey,
|
||||||
|
RPL4FrontEnd_SelectedWeatherName());
|
||||||
|
SteamMatchmaking()->SetLobbyData(gLobby, kLengthKey,
|
||||||
|
RPL4FrontEnd_SelectedLengthName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +194,21 @@ namespace
|
|||||||
return (scenario != NULL && strcmp(scenario, "football") == 0);
|
return (scenario != NULL && strcmp(scenario, "football") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
// Lobby data read back as a string, empty when the owner has not
|
||||||
|
// published it yet (a member can be in the room a beat before the
|
||||||
|
// owner's first publish lands).
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
const char *LobbyText(const char *key)
|
||||||
|
{
|
||||||
|
if (!gInLobby)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const char *text = SteamMatchmaking()->GetLobbyData(gLobby, key);
|
||||||
|
return (text != NULL) ? text : "";
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Launch: roster into lobby data, peers into the transport, and
|
// Launch: roster into lobby data, peers into the transport, and
|
||||||
// the hosted-race path primed on the owner
|
// the hosted-race path primed on the owner
|
||||||
@@ -346,11 +382,143 @@ namespace
|
|||||||
-1, &title, DT_CENTER | DT_TOP | DT_SINGLELINE);
|
-1, &title, DT_CENTER | DT_TOP | DT_SINGLELINE);
|
||||||
|
|
||||||
SelectObject(mem, room->textFont);
|
SelectObject(mem, room->textFont);
|
||||||
int row_h = client.bottom / 16;
|
// wider than the old middle-half: the right column now carries
|
||||||
int top = client.bottom / 4;
|
// full catalog names, and in football three of them
|
||||||
int left = client.right / 4;
|
int left = client.right / 6;
|
||||||
int right = 3 * client.right / 4;
|
int right = client.right - client.right / 6;
|
||||||
char text[128];
|
char text[128];
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// The buttons are laid out upward from the bottom edge while the
|
||||||
|
// roster runs down from the top, so everything between the title
|
||||||
|
// and the topmost button has to share one band.
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
int ceiling = client.bottom;
|
||||||
|
if (FootballLobby() && room->teamRect.top > 0)
|
||||||
|
{
|
||||||
|
ceiling = room->teamRect.top;
|
||||||
|
}
|
||||||
|
else if (IsOwner() && room->launchRect.top > 0)
|
||||||
|
{
|
||||||
|
ceiling = room->launchRect.top;
|
||||||
|
}
|
||||||
|
else if (room->leaveRect.top > 0)
|
||||||
|
{
|
||||||
|
ceiling = room->leaveRect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// Size the rows to that band rather than to a fixed fraction of
|
||||||
|
// the window: a full lobby is eight players, and eight rows at a
|
||||||
|
// sixteenth each would run off the bottom of every window we
|
||||||
|
// support. Counted in half rows, the band holds
|
||||||
|
//
|
||||||
|
// setup lines + gap + kMaxLobbyMembers rows + gap + hint
|
||||||
|
//
|
||||||
|
// so the row height falls out of the space actually available.
|
||||||
|
// It never grows past the old sixteenth (a two-player lobby
|
||||||
|
// should not have circus-sized rows) and never shrinks below the
|
||||||
|
// font, which is what would actually break - overlapping text.
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// tmHeight already includes the font's own leading, so a row that
|
||||||
|
// tall cannot clip or collide - asking for more than that just
|
||||||
|
// costs setup lines the room would rather keep.
|
||||||
|
TEXTMETRICA metrics;
|
||||||
|
GetTextMetricsA(mem, &metrics);
|
||||||
|
int min_row = metrics.tmHeight + 2;
|
||||||
|
int max_row = client.bottom / 16;
|
||||||
|
|
||||||
|
int setup_top = title.bottom + client.bottom / 64;
|
||||||
|
int band = ceiling - setup_top;
|
||||||
|
|
||||||
|
int setup_lines = 2;
|
||||||
|
int row_h = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
// halves: setup, half gap, roster, half gap, hint
|
||||||
|
int halves = 2 * setup_lines + 1 + 2 * kMaxLobbyMembers + 1 + 2;
|
||||||
|
row_h = (2 * band) / halves;
|
||||||
|
if (row_h >= min_row || setup_lines == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--setup_lines; // buy room back from the setup lines
|
||||||
|
}
|
||||||
|
if (row_h > max_row) row_h = max_row;
|
||||||
|
if (row_h < 12) row_h = 12;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// On a window too short to give eight rows the room the standard
|
||||||
|
// font wants, draw this block smaller rather than letting the
|
||||||
|
// rows overlap each other or run under the buttons. Nobody plays
|
||||||
|
// at that size, but a squeezed roster still has to be readable.
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
HFONT squeezed = NULL;
|
||||||
|
HFONT previous = NULL;
|
||||||
|
if (row_h < min_row)
|
||||||
|
{
|
||||||
|
squeezed = CreateFontA(-(row_h * 2 / 3), 0, 0, 0, FW_NORMAL,
|
||||||
|
FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS,
|
||||||
|
CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
|
||||||
|
FIXED_PITCH | FF_MODERN, "Consolas");
|
||||||
|
if (squeezed != NULL)
|
||||||
|
{
|
||||||
|
previous = (HFONT) SelectObject(mem, squeezed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int top = setup_top;
|
||||||
|
|
||||||
|
const char *map_name = LobbyText(kMapKey);
|
||||||
|
if (map_name[0] != '\0' && setup_lines > 0)
|
||||||
|
{
|
||||||
|
RECT setup;
|
||||||
|
setup.left = client.right / 12;
|
||||||
|
setup.right = client.right - client.right / 12;
|
||||||
|
setup.top = setup_top;
|
||||||
|
setup.bottom = setup.top + row_h;
|
||||||
|
|
||||||
|
SetTextColor(mem, kGreenBright);
|
||||||
|
DrawTextA(mem, map_name, -1, &setup,
|
||||||
|
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
// Conditions on the line below. Game length is the host's
|
||||||
|
// pick too, and the one people ask about first.
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
const char *time_name = LobbyText(kTimeKey);
|
||||||
|
const char *weather_name = LobbyText(kWeatherKey);
|
||||||
|
const char *length_name = LobbyText(kLengthKey);
|
||||||
|
text[0] = '\0';
|
||||||
|
if (time_name[0] != '\0')
|
||||||
|
{
|
||||||
|
strcat(text, time_name);
|
||||||
|
}
|
||||||
|
if (weather_name[0] != '\0')
|
||||||
|
{
|
||||||
|
if (text[0] != '\0') strcat(text, " - ");
|
||||||
|
strcat(text, weather_name);
|
||||||
|
}
|
||||||
|
if (length_name[0] != '\0')
|
||||||
|
{
|
||||||
|
if (text[0] != '\0') strcat(text, " - ");
|
||||||
|
strcat(text, length_name);
|
||||||
|
}
|
||||||
|
if (text[0] != '\0' && setup_lines > 1)
|
||||||
|
{
|
||||||
|
setup.top = setup.bottom;
|
||||||
|
setup.bottom = setup.top + row_h;
|
||||||
|
SetTextColor(mem, kGreenDim);
|
||||||
|
DrawTextA(mem, text, -1, &setup,
|
||||||
|
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setup.bottom + row_h / 2 > top)
|
||||||
|
{
|
||||||
|
top = setup.bottom + row_h / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < room->memberCount; ++i)
|
for (int i = 0; i < room->memberCount; ++i)
|
||||||
{
|
{
|
||||||
MemberInfo *member = &room->members[i];
|
MemberInfo *member = &room->members[i];
|
||||||
@@ -367,27 +535,29 @@ namespace
|
|||||||
is_owner_row ? " [HOST]" : "");
|
is_owner_row ? " [HOST]" : "");
|
||||||
DrawTextA(mem, text, -1, &row, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
DrawTextA(mem, text, -1, &row, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------
|
||||||
|
// The right column is what this player brings. Football is a
|
||||||
|
// team sheet - team colors and position - but the VTV still
|
||||||
|
// decides how they play it, so name it there too. Keys
|
||||||
|
// travel on the wire; the catalogs turn them back into
|
||||||
|
// names, so nobody reads "bttlbrg".
|
||||||
|
//-----------------------------------------------------------
|
||||||
if (FootballLobby())
|
if (FootballLobby())
|
||||||
{
|
{
|
||||||
// football: the roster is a team sheet
|
|
||||||
if (member->team[0] != '\0')
|
if (member->team[0] != '\0')
|
||||||
{
|
{
|
||||||
const char *position_name = "?";
|
sprintf(text, "%s - %s - %s",
|
||||||
for (int i = 0; i < RPL4FrontEnd_PositionCount(); ++i)
|
RPL4FrontEnd_VehicleNameForKey(member->vehicle),
|
||||||
{
|
RPL4FrontEnd_TeamNameForKey(member->team),
|
||||||
if (strcmp(member->position, RPL4FrontEnd_PositionKey(i)) == 0)
|
RPL4FrontEnd_PositionNameForKey(member->position));
|
||||||
{
|
|
||||||
position_name = RPL4FrontEnd_PositionName(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sprintf(text, "%s - %s", member->team, position_name);
|
|
||||||
DrawTextA(mem, text, -1, &row, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
DrawTextA(mem, text, -1, &row, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (member->vehicle[0] != '\0')
|
else if (member->vehicle[0] != '\0')
|
||||||
{
|
{
|
||||||
sprintf(text, "%s / %s", member->vehicle, member->color);
|
sprintf(text, "%s - %s",
|
||||||
|
RPL4FrontEnd_VehicleNameForKey(member->vehicle),
|
||||||
|
member->color);
|
||||||
DrawTextA(mem, text, -1, &row, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
DrawTextA(mem, text, -1, &row, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -403,6 +573,13 @@ namespace
|
|||||||
: "Waiting for the host to launch...",
|
: "Waiting for the host to launch...",
|
||||||
-1, &hint, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
-1, &hint, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||||
|
|
||||||
|
// back to the room font for the buttons, which have their own room
|
||||||
|
if (squeezed != NULL)
|
||||||
|
{
|
||||||
|
SelectObject(mem, previous);
|
||||||
|
DeleteObject(squeezed);
|
||||||
|
}
|
||||||
|
|
||||||
HBRUSH frame = CreateSolidBrush(kGreenBright);
|
HBRUSH frame = CreateSolidBrush(kGreenBright);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+29
-4
@@ -76,6 +76,28 @@ foreach ($pair in @(
|
|||||||
}
|
}
|
||||||
Set-Content -Path "$dist\CONTROLS.txt" -Encoding ascii -Value $controls
|
Set-Content -Path "$dist\CONTROLS.txt" -Encoding ascii -Value $controls
|
||||||
|
|
||||||
|
# The same map as a page, for anyone who would rather look at the
|
||||||
|
# diagrams than read them. Its source is the published artifact, which is
|
||||||
|
# a fragment - the publisher supplies the document shell - so wrap it to
|
||||||
|
# stand alone: without a doctype the browser drops into quirks mode, and
|
||||||
|
# without a charset the typography arrives as mojibake. Written without a
|
||||||
|
# BOM so the charset declaration is the only thing speaking.
|
||||||
|
$controlsPage = Get-Content (Join-Path $root 'docs\rp412-controls.html') -Raw -Encoding UTF8
|
||||||
|
$page = @"
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
$controlsPage
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"@
|
||||||
|
[System.IO.File]::WriteAllText(
|
||||||
|
"$dist\CONTROLS.html", $page, (New-Object System.Text.UTF8Encoding $false))
|
||||||
|
|
||||||
# --- OpenAL runtime --------------------------------------------------------
|
# --- OpenAL runtime --------------------------------------------------------
|
||||||
# The exe links OpenAL32.dll (32-bit). Prefer shipping the already-installed
|
# The exe links OpenAL32.dll (32-bit). Prefer shipping the already-installed
|
||||||
# runtime beside the exe; oalinst.exe covers machines where that misses.
|
# runtime beside the exe; oalinst.exe covers machines where that misses.
|
||||||
@@ -305,7 +327,7 @@ start rpl4opt.exe -fit
|
|||||||
"@
|
"@
|
||||||
|
|
||||||
Set-Content -Path "$dist\README.txt" -Encoding ascii -Value @"
|
Set-Content -Path "$dist\README.txt" -Encoding ascii -Value @"
|
||||||
Red Planet 4.12.3
|
Red Planet 4.12.4
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Run start-fullscreen.bat for borderless over the whole monitor, or
|
Run start-fullscreen.bat for borderless over the whole monitor, or
|
||||||
@@ -352,8 +374,11 @@ pod's real button banks: click them with the mouse, and they light up
|
|||||||
as the game commands their lamps.
|
as the game commands their lamps.
|
||||||
environ.ini is self-documenting: every option ships in the file with
|
environ.ini is self-documenting: every option ships in the file with
|
||||||
a comment (Steam networking, keyboard lighting, stick inversion, LAN
|
a comment (Steam networking, keyboard lighting, stick inversion, LAN
|
||||||
hosting, developer keys, and more). CONTROLS.txt has the full controls
|
hosting, developer keys, display scaling and radar placement, and more).
|
||||||
map with pad and keyboard diagrams.
|
|
||||||
|
CONTROLS.html is the full controls map - open it in a browser for the
|
||||||
|
pad, keyboard and pod-panel diagrams. CONTROLS.txt is the same thing as
|
||||||
|
plain text.
|
||||||
|
|
||||||
Known prototype notes: pods race untextured (the player1-8 skins come
|
Known prototype notes: pods race untextured (the player1-8 skins come
|
||||||
from the presets system, not shipped data), and text drawn on the plasma
|
from the presets system, not shipped data), and text drawn on the plasma
|
||||||
@@ -367,7 +392,7 @@ $size = (Get-ChildItem $dist -Recurse | Measure-Object Length -Sum).Sum
|
|||||||
Write-Host ("dist ready: {0:N1} MB" -f ($size / 1MB))
|
Write-Host ("dist ready: {0:N1} MB" -f ($size / 1MB))
|
||||||
|
|
||||||
if ($Zip) {
|
if ($Zip) {
|
||||||
$zipPath = Join-Path $root 'RedPlanet-4.12.3.zip'
|
$zipPath = Join-Path $root 'RedPlanet-4.12.4.zip'
|
||||||
Write-Host "zipping to $zipPath..."
|
Write-Host "zipping to $zipPath..."
|
||||||
|
|
||||||
# Everything lives under a single RP412\ folder inside the zip, so
|
# Everything lives under a single RP412\ folder inside the zip, so
|
||||||
|
|||||||
Reference in New Issue
Block a user