#include "rpl4.h" #pragma hdrstop #include "rpl4app.h" #include "rpl4vid.h" #include "rpl4arnd.h" #include "rpl4grnd.h" #include "rpl4mssn.h" #include "..\munga_l4\l4gauge.h" #include "..\rp\rpreg.h" #include "..\munga_l4\l4ctrl.h" #include "..\munga_l4\l4mppr.h" #include "..\munga\appmgr.h" #include "rpl4mode.h" #include "..\rp\vtv.h" #include "rpl4mppr.h" #include "..\rp\rpplayer.h" #include "..\munga_l4\l4video.h" #include "..\munga\dropzone.h" #include "..\munga\director.h" #include "..\munga\nttmgr.h" #include "..\munga_l4\l4vb16.h" // //############################################################################# // RPL4Application //############################################################################# // const Receiver::HandlerEntry RPL4Application::MessageHandlerEntries[]= { MESSAGE_ENTRY(RPL4Application, StopMission), MESSAGE_ENTRY(RPL4Application, WinnersCircle) }; Receiver::MessageHandlerSet& RPL4Application::GetMessageHandlers() { static Receiver::MessageHandlerSet messageHandlers( ELEMENTS(RPL4Application::MessageHandlerEntries), RPL4Application::MessageHandlerEntries, L4Application::GetMessageHandlers()); return messageHandlers; } Derivation* RPL4Application::GetClassDerivations() { static Derivation classDerivations( L4Application::GetClassDerivations(), "RPL4Application"); return &classDerivations; } RPL4Application::SharedData RPL4Application::DefaultData( RPL4Application::GetClassDerivations(), RPL4Application::GetMessageHandlers() ); // // How long to sit on black between the race fading out and the podium // fading in. The race fade is FADE_OUT_TIME (half a second), so this is // that plus a beat, to land on black rather than on the tail of it. // const Scalar winnersCircleFadeOutTime = 0.7f; // and how quickly the stand comes up out of the black afterwards const Scalar winnersCircleFadeInTime = 0.45f; RPL4Application::RPL4Application( HINSTANCE hInstance, HWND hWnd, ResourceFile *resource_file, ClassID class_ID, SharedData &shared_data ): L4Application(hInstance, hWnd, resource_file, RPL4, class_ID, shared_data) { Check_Fpu(); } // //############################################################################# // ~RPL4Application //############################################################################# // RPL4Application::~RPL4Application() { Check_Fpu(); } // //############################################################################# // ShowWinnersCircle //############################################################################# // // The pod hall's award platform. Every map carries a "wcircle" stand at // (1200,0,0) with eight ranked dropzones named win1..win8 on it - rank 1 at // the front on the low tier, ranks 4-8 across the back on the high one. All // this shipped; only the mission-review build ever drove it. // // Stand the finishers on their spots in finishing order, freeze them, and // look back at the stand from in front of it. // void RPL4Application::ShowWinnersCircle() { Check(this); // RP412PODIUM=0 skips the whole thing const char *podium_mode = getenv("RP412PODIUM"); if (podium_mode != NULL && atoi(podium_mode) == 0) { DEBUG_STREAM << "WinnersCircle: disabled\n" << std::flush; return; } EntityManager *entity_manager = GetEntityManager(); if (entity_manager == NULL) { return; } EntityGroup *dropzones = entity_manager->FindGroup("DropZones"); if (dropzones == NULL) { DEBUG_STREAM << "WinnersCircle: no DropZones group\n" << std::flush; return; } // //--------------------------------------------------------------------- // Walk the finishing order. Player::CalcRanking has been ranking every // scoring player by score each frame, in football as much as in a race // (CalcFootballRanking exists but is never called), so rank order is // meaningful in both. //--------------------------------------------------------------------- // char winners_spot[] = "win?"; char *place = winners_spot + 3; int placed = 0; Point3D standFront(0.0f, 0.0f, 0.0f); Point3D standCentre(0.0f, 0.0f, 0.0f); Player *p; for (int rank = 0; (p = CameraDirector::FindPlayerByRank(rank)) != NULL; ++rank) { if (rank > 7) { break; // only eight spots exist on the stand } *place = (char) ('1' + rank); ChainIteratorOf iterator(dropzones->groupMembers); DropZone *dropzone; while ((dropzone = (DropZone*) iterator.ReadAndNext()) != NULL) { if (!strcmp(dropzone->GetDropZoneName(), winners_spot)) { break; } } if (dropzone == NULL) { continue; } Entity *vehicle = p->GetPlayerVehicle(); if (vehicle == NULL || vehicle->GetClassID() != VTVClassID) { continue; } VTV *vtv = (VTV*) vehicle; vtv->Reset(dropzone->localOrigin, VTV::MissionReviewReset); vtv->SetPerformance(&VTV::DoNothing); vtv->FlushEvents(); Point3D spot = dropzone->localOrigin.linearPosition; if (placed == 0) { // remember where the front of the stand is - the shot is framed // off it rather than off hardcoded map coordinates standFront = spot; } standCentre += spot; DEBUG_STREAM << "WinnersCircle: " << winners_spot << " at " << spot.x << "," << spot.y << "," << spot.z << "\n" << std::flush; ++placed; } if (placed == 0) { DEBUG_STREAM << "WinnersCircle: nobody to place\n" << std::flush; return; } // //--------------------------------------------------------------------- // Switch the world back on. // // Dying collapses the view and sets the application "dead" until the // pilot reincarnates - while that flag is up the renderer skips every // static object, which is the whole map. A pilot killed near the buzzer // is still waiting for a respawn that will never come, so the flag is // still up and the podium would play out against a black screen. // // The mission-review build never hit this: it watches from a CameraShip, // which cannot die, so nothing ever set the flag there. // // Standing the finishers up IS the resurrection, so clear it. //--------------------------------------------------------------------- // SetIsDead(false); DPLRenderer *dpl_renderer = GetVideoRenderer(); if (dpl_renderer == NULL) { return; } // //--------------------------------------------------------------------- // Your own vehicle is built as a cockpit with no hull, so from any // camera outside it there is nothing where you should be - on the stand // you would be the one empty spot. Turn it inside out before the shot. //--------------------------------------------------------------------- // dpl_renderer->ShowViewpointFromOutside(); // //--------------------------------------------------------------------- // The name plates are drawn per rank slot, so they have to be re-sorted // now that the finishing order is final - otherwise the signs read in // whatever order the players were created. //--------------------------------------------------------------------- // dpl_renderer->SortAndReloadNameBitmaps(); // //--------------------------------------------------------------------- // Widen to 45 degrees and pull back in front of the stand so the whole // line-up frames up. The stand runs from z~3 (rank 1, low) to z~39 // (ranks 4-8, high) and x~1180..1219; at 45 degrees that needs roughly // 30 units of standoff. Eye is above the top tier looking slightly // down at the middle of the group. //--------------------------------------------------------------------- // standCentre.x /= (Scalar) placed; standCentre.y /= (Scalar) placed; standCentre.z /= (Scalar) placed; // // Stand off along the line from the middle of the group out through the // front spot, so the shot faces the stand however the map has it turned, // and lift the eye above the top tier to look down on the line-up. // Vector3D facing; facing.x = standFront.x - standCentre.x; facing.y = 0.0f; facing.z = standFront.z - standCentre.z; Scalar reach = (Scalar) sqrt(facing.x * facing.x + facing.z * facing.z); if (reach < 0.01f) { facing.x = 0.0f; facing.z = -1.0f; reach = 1.0f; } // // Framing is tunable while the shot is being dialled in: // RP412PODIUMSTANDOFF distance out in front of the stand // RP412PODIUMHEIGHT eye height above the group // RP412PODIUMAIM height of the aim point above the group // // // Framed off the stand itself: down low and tilted up across the tiers, // which is how you photograph a podium. It is a balance in both // directions - drop the camera further or tilt harder and the sky takes // the top half while the winner's spot slides off the bottom; tilt down // instead and it becomes a floor plan. Pillarboxing to 4:3 is what lets // it sit this close without the platform trailing off at the sides. // Scalar standoff = 36.0f; Scalar height = 12.0f; Scalar aim_lift = 2.0f; const char *tune = getenv("RP412PODIUMSTANDOFF"); if (tune != NULL && atof(tune) != 0.0) standoff = (Scalar) atof(tune); tune = getenv("RP412PODIUMHEIGHT"); if (tune != NULL && atof(tune) != 0.0) height = (Scalar) atof(tune); tune = getenv("RP412PODIUMAIM"); if (tune != NULL) aim_lift = (Scalar) atof(tune); Point3D eye; eye.x = standCentre.x + (facing.x / reach) * standoff; eye.y = standCentre.y + height; eye.z = standCentre.z + (facing.z / reach) * standoff; standCentre.y += aim_lift; // // RP412PODIUMCAM=0 leaves the view in the cockpit, which is also the // way to tell a bad camera from a scene that is not drawing at all. // // // Clear the cockpit glass away. The MFDs and the radar sit over the // viewscreen like the pod's bezels and have nothing to say once the // race is over; the podium gets the whole canvas. // SVGA16 *cockpit = SVGA16::GetCockpit(); if (cockpit != NULL) { cockpit->HideSecondaryDisplays(); } // // Fog first: it pulls the clip plane in to 1100, and SetViewAngle is what // rebuilds the projection that reads it. // dpl_renderer->SetFogStyle(DPLRenderer::winnersCircleFogStyle); const char *camera_mode = getenv("RP412PODIUMCAM"); if (camera_mode == NULL || atoi(camera_mode) != 0) { dpl_renderer->SetViewAngle(Degree(45.0f)); dpl_renderer->SetPresentationCamera(eye, standCentre); } else { // still rebuild the projection so the new clip plane takes effect dpl_renderer->SetViewAngle(Degree(40.0f)); } if (camera_mode != NULL && atoi(camera_mode) == 0) { DEBUG_STREAM << "WinnersCircle: presentation camera disabled\n" << std::flush; } // // Everything is in place behind the black - bring it up. // RP412PODIUMFADEIN sets the ramp in seconds. // Scalar fade_in = winnersCircleFadeInTime; const char *fade_tune = getenv("RP412PODIUMFADEIN"); if (fade_tune != NULL && atof(fade_tune) > 0.0) { fade_in = (Scalar) atof(fade_tune); } dpl_renderer->StartPresentationFadeIn(fade_in); DEBUG_STREAM << "WinnersCircle: " << placed << " placed; centre " << standCentre.x << "," << standCentre.y << "," << standCentre.z << " eye " << eye.x << "," << eye.y << "," << eye.z << "\n" << std::flush; } // //############################################################################# // StopMissionMessageHandler //############################################################################# // // The mission is over. Show the podium, then let the base handler run - it // puts the player into MissionEndingState, whose fade timer is what actually // keeps the sim and the renderer alive until teardown. // void RPL4Application::StopMissionMessageHandler(StopMissionMessage *message) { Check(this); // // StopMission arrives twice: once from the console at the buzzer, and // again from the player when the ending fade runs out - that second one // is what actually retires the application. Only the first is the end of // the race. // if (GetApplicationState() != Application::EndingMission) { // //------------------------------------------------------------- // Don't cut straight to the podium. The race gets its own // fade-to-black first - that fade is already running by the time // this returns - and the Winners Circle comes up out of the // black afterwards. Standing everyone up now would just fade out // the podium instead of the race. // // FADE_OUT_TIME is half a second; a beat more than that lands on // black rather than on the tail of the fade. //------------------------------------------------------------- // Receiver::Message podium_message( WinnersCircleMessageID, sizeof(Receiver::Message)); Time event_time; event_time = Now(); event_time += winnersCircleFadeOutTime; Post(LowEventPriority, this, &podium_message, event_time); DEBUG_STREAM << "WinnersCircle: race over, fading out\n" << std::flush; } L4Application::StopMissionMessageHandler(message); Check_Fpu(); } // //############################################################################# // WinnersCircleMessageHandler //############################################################################# // // The race has faded to black. Set the stand up behind the black and fade // back in to it. // void RPL4Application::WinnersCircleMessageHandler(Receiver::Message *) { Check(this); DEBUG_STREAM << "WinnersCircle: standing the finishers up\n" << std::flush; ShowWinnersCircle(); Check_Fpu(); } // //############################################################################# // MakeRegistry //############################################################################# // Registry* RPL4Application::MakeRegistry() { Check_Fpu(); return new RPRegistry(GetResourceFile()); } // //############################################################################# // MakeVideoRenderer //############################################################################# // VideoRenderer* RPL4Application::MakeVideoRenderer() { if (divisionParameters) { return new RPL4VideoRenderer( ghWnd, L4Application::mScreenWidth, L4Application::mScreenHeight, L4Application::mFullscreen, VisualInterestType, DefaultInterestDepth ); } return NULL; } // //############################################################################# // MakeAudioRenderer //############################################################################# // AudioRenderer* RPL4Application::MakeAudioRenderer() { Check_Fpu(); // char *blaster1, *blaster2; // blaster1 = getenv(FRONT_CARD_ENV_VAR); // blaster2 = getenv(REAR_CARD_ENV_VAR); //if (blaster1 != NULL && blaster2 != NULL) //{ return new RPL4AudioRenderer( 1000.0f, GetMissionReviewMode() ); //} // return NULL; } //############################################################################# // MakeModeManager //############################################################################# ModeManager* RPL4Application::MakeModeManager() { RPL4ModeManager *manager = new RPL4ModeManager(RPL4ModeManager::ModeInitial); Check(manager); return manager; } // //############################################################################# // MakeGaugeRenderer //############################################################################# // GaugeRenderer* RPL4Application::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index) { char *mode_string = getenv("L4GAUGE"); if (mode_string != NULL && (secondaryIndex != NULL || aux1Index != NULL || aux2Index != NULL)) { return new RPL4GaugeRenderer(!L4Application::mFullscreen, secondaryIndex, aux1Index, aux2Index); } return NULL; } // //############################################################################# // MakeMission //############################################################################# // Mission* RPL4Application::MakeMission( NotationFile *notation_file, ResourceFile *resources ) { Check(this); Check(notation_file); Check(resources); Check_Fpu(); return new RPL4Mission(notation_file, resources); } // //############################################################################# // MakeViewpointEntity //############################################################################# // Entity* RPL4Application::MakeViewpointEntity(Entity::MakeMessage *message) { Check(this); // Entity // *viewing_entity = L4Application::MakeViewpointEntity(message); // // if(viewing_entity) // { // Check_Fpu(); // return viewing_entity; // } LBE4ControlsManager* controls = (LBE4ControlsManager *)GetControlsManager(); Check(controls); Entity *viewing_entity = NULL; char *primary_mapping_name = NULL; switch(message->classToCreate) { case CameraShipClassID: //======================================================================= // Create Camera ship //======================================================================= { CameraShip *viewing_camera = CameraShip::Make((CameraShip::MakeMessage*)message); Check(viewing_camera); viewing_entity = viewing_camera; //-------------------------------------------------- // Create controls mapping object //-------------------------------------------------- CameraControlsMapper::SubsystemResource control_subsystem_resource; Str_Copy( control_subsystem_resource.subsystemName, "ControlsMapper", sizeof(control_subsystem_resource.subsystemName) ); control_subsystem_resource.classID = TrivialSubsystemClassID; control_subsystem_resource.subsystemModelSize = sizeof(control_subsystem_resource); CameraControlsMapper *camera_mapper; switch (controls->primaryControlType) { case LBE4ControlsManager::PrimaryThrustMaster: Tell("RPL4Application, CameraShip using ThrustMaster\n"); camera_mapper = new CameraThrustmasterMapper( viewing_camera, CameraShip::ControlsMapperSubsystem, &control_subsystem_resource ); primary_mapping_name = "Thrustmaster"; break; case LBE4ControlsManager::PrimaryRIO: Tell("RPL4Application, CameraShip using RIO\n"); camera_mapper = new CameraRIOMapper( viewing_camera, CameraShip::ControlsMapperSubsystem, &control_subsystem_resource ); primary_mapping_name = "L4"; break; default: Tell("RPL4Application, CameraShip using default mapper\n"); camera_mapper = new CameraL4Mapper( viewing_camera, CameraShip::ControlsMapperSubsystem, &control_subsystem_resource ); // primary mapping name is NULL break; } //------------------------------------------- // Set mapping subsystem for this entity //------------------------------------------- Register_Object(camera_mapper); viewing_camera->SetMappingSubsystem(camera_mapper); } break; case VTVClassID: //======================================================================= // Create VTV //======================================================================= { VTV *viewing_VTV = VTV::Make((VTV::MakeMessage*)message); Check(viewing_VTV); viewing_entity = viewing_VTV; //-------------------------------------------------- // Set control deadbanding //-------------------------------------------------- controls->SetJoystickDeadBand(.1); controls->SetThrottleDeadBand(.1); controls->SetPedalsDeadBand(.1); //-------------------------------------------------- // Create controls mapping object //-------------------------------------------------- VTVControlsMapper::SubsystemResource control_subsystem_resource; Str_Copy( control_subsystem_resource.subsystemName, "ControlsMapper", sizeof(control_subsystem_resource.subsystemName) ); control_subsystem_resource.classID = TrivialSubsystemClassID; control_subsystem_resource.subsystemModelSize = sizeof(control_subsystem_resource); control_subsystem_resource.segmentIndex = -1; control_subsystem_resource.subsystemFlags = 0; VTVControlsMapper *vtv_mapper; switch (controls->primaryControlType) { case LBE4ControlsManager::PrimaryThrustMaster: Tell("RPL4Application, VTV using ThrustMaster\n"); vtv_mapper = new VTVThrustmasterMapper( viewing_VTV, VTV::ControlsMapperSubsystem, &control_subsystem_resource ); primary_mapping_name = "Thrustmaster"; break; case LBE4ControlsManager::PrimaryRIO: Tell("RPL4Application, VTV using RIO\n"); vtv_mapper = new VTVRIOMapper( viewing_VTV, VTV::ControlsMapperSubsystem, &control_subsystem_resource ); primary_mapping_name = "L4"; break; default: DEBUG_STREAM << "*****VTV has no controls mapping!*****" << std::flush; Fail("RPL4Application::MakeViewpointEntity"); vtv_mapper = NULL; // primary mapping name is NULL break; } //------------------------------------------- // Set mapping subsystem for this entity //------------------------------------------- Register_Object(vtv_mapper); viewing_VTV->SetMappingSubsystem(vtv_mapper); //------------------------------------------- // Enable collision detection for this entity //------------------------------------------- viewing_VTV->StartCollisionAssistant(); } break; } Check(viewing_entity); //------------------------------------------------------------------------ // Load up the control mapping stream for the entity //------------------------------------------------------------------------ if (primary_mapping_name != NULL) { ResourceDescription *resource_description = application->GetResourceFile()->SearchList( viewing_entity->GetResourceID(), ResourceDescription::ControlMappingsListResourceType ); if (resource_description != NULL) { Check(resource_description); Check(GetResourceFile()); resource_description = GetResourceFile()->SearchList( resource_description->resourceID, primary_mapping_name ); if (resource_description != NULL) { Check(resource_description); resource_description->Lock(); controls->CreateStreamedMappings( viewing_entity, (int*)resource_description->resourceAddress ); resource_description->Unlock(); } } } //------------------------------------------------------------------------ // Create gauges for this entity //------------------------------------------------------------------------ RPL4GaugeRenderer *gauge_renderer = (RPL4GaugeRenderer *) GetGaugeRenderer(); if (gauge_renderer != NULL) { Check(gauge_renderer); gauge_renderer->ConfigureForModel("Init", viewing_entity); } Check_Fpu(); return viewing_entity; }