diff --git a/RP_L4/RPL4FE.cpp b/RP_L4/RPL4FE.cpp index 34c31b1..9cb3407 100644 --- a/RP_L4/RPL4FE.cpp +++ b/RP_L4/RPL4FE.cpp @@ -166,6 +166,19 @@ namespace { "clear", "Clear" }, { "fog", "Light Fog" }, { "soup", "Heavy Fog" }, }; + // + // What the host does with the race it is setting up. A Live Cam host + // still owns the lobby, picks the track and the conditions, and + // marshals every pod - it just watches instead of racing, which is how + // the arcade ran its camera cabinet. Only offered on a lobby race: + // a camera needs pods to point at, so see HostIsCamera below. + // + const CatalogEntry kRoles[] = + { + { "racer", "Racer" }, { "camera", "Live Cam" }, + }; + enum { kRoleRacer = 0, kRoleCamera = 1 }; + struct LengthEntry { int seconds; // 0 = endless (length line omitted) @@ -199,6 +212,7 @@ namespace GroupTime, GroupWeather, GroupLength, + GroupRole, // racer or Live Cam (lobby races only) GroupScenario, GroupTeam, // football only GroupPosition, // football only @@ -214,6 +228,19 @@ namespace return selection[GroupScenario] == 1; } + // + // Did the host ask to watch rather than race? The selection alone is + // not enough: the option persists in pilot.cfg, so somebody who set + // Live Cam for a lobby race and later launched a single-player one + // would otherwise get a camera with nothing to point at - which is + // the one configuration known to hang the map load. The egg builder + // therefore also requires other pods to be present; see BuildEggText. + // + Logical WantsCamera(const int *selection) + { + return RPL4Lobby_Configured() && selection[GroupRole] == kRoleCamera; + } + const CatalogEntry *ActiveMaps(const int *selection, int *count) { if (IsFootball(selection)) @@ -300,6 +327,7 @@ namespace { GroupBadge, "badge" }, { GroupTeam, "team" }, { GroupPosition, "position" }, { GroupTime, "time" }, { GroupWeather, "weather" }, { GroupLength, "length" }, + { GroupRole, "role" }, }; // @@ -327,6 +355,7 @@ namespace case GroupTime: return FE_COUNT(kTimes); case GroupWeather: return FE_COUNT(kWeather); case GroupLength: return FE_COUNT(kLengths); + case GroupRole: return FE_COUNT(kRoles); } return 0; } @@ -635,6 +664,17 @@ namespace groups[n++] = GroupTime; groups[n++] = GroupWeather; groups[n++] = GroupLength; + // + // Heads the loadout column: what you ARE reads before what you + // are driving, and on Live Cam the lists under it stop applying. + // Only when Steam is configured - the role is meaningless without + // a lobby, and a camera with nothing to watch is worse than + // meaningless (see HostIsCamera). + // + if (RPL4Lobby_Configured()) + { + groups[n++] = GroupRole; + } groups[n++] = GroupVehicle; if (IsFootball(selection)) { @@ -936,6 +976,7 @@ namespace case GroupTime: return "TIME OF DAY"; case GroupWeather: return "WEATHER"; case GroupLength: return "GAME LENGTH"; + case GroupRole: return "YOUR ROLE"; } return ""; } @@ -961,6 +1002,7 @@ namespace case GroupTime: return kTimes[index].name; case GroupWeather: return kWeather[index].name; case GroupLength: return kLengths[index].name; + case GroupRole: return kRoles[index].name; case GroupLaunch: return "L A U N C H G A M E"; case GroupSteamHost: return "HOST STEAM GAME"; case GroupSteamJoin: return "JOIN STEAM GAME"; @@ -1557,18 +1599,43 @@ namespace egg += line; } + // + // The host's Live Cam pick, resolved here because this is where + // the other pods are finally known. A camera needs something to + // watch: with no extras this is a single-player race, the pick is + // stale (pilot.cfg remembers it), and honouring it would hand the + // map load a camera host with no peers - the case that hangs. So + // it is quietly ignored and the host races, which is what someone + // pressing LAUNCH on a solo game meant anyway. + // + // hostType 1 is CameraShipHostType and vehicle 'camera' makes the + // mission's game model 'camera'; together they are what turn a + // station into a camera director (see RP/RPREG.cpp). Everything + // else on the line stays - dropzone and colour cost nothing and + // the egg readers still expect them. + // + Logical owner_is_camera = WantsCamera(fe->selection) && extra_count > 0; + if (WantsCamera(fe->selection) && extra_count == 0) + { + DEBUG_STREAM << "FE: Live Cam ignored - no other pods in this race\n" + << std::flush; + } + for (int p = 0; p < pilot_count; ++p) { + Logical camera_entry = (p == 0 && owner_is_camera); + sprintf(line, "[%s]\n", pilots[p].address); egg += line; - egg += "hostType=0\n"; + egg += camera_entry ? "hostType=1\n" : "hostType=0\n"; egg += "dropzone=one\n"; sprintf(line, "name=%s\n", pilots[p].name); egg += line; sprintf(line, "bitmapindex=%d\n", p + 1); egg += line; egg += "loadzones=1\n"; - sprintf(line, "vehicle=%s\n", pilots[p].vehicle); + sprintf(line, "vehicle=%s\n", + camera_entry ? "camera" : pilots[p].vehicle); egg += line; sprintf(line, "color=%s\n", pilots[p].color); egg += line;