/* Copyright (C) 2005 by Jorrit Tyberghein This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "cssysdef.h" #include "wheeledtest.h" #include "csutil/sysfunc.h" #include "csgeom/plane3.h" #include "iutil/vfs.h" #include "iutil/object.h" #include "iutil/cmdline.h" #include "csutil/cscolor.h" #include "cstool/csview.h" #include "cstool/initapp.h" #include "csutil/event.h" #include "iutil/eventq.h" #include "iutil/event.h" #include "iutil/objreg.h" #include "iutil/csinput.h" #include "iutil/virtclk.h" #include "iutil/plugin.h" #include "iengine/sector.h" #include "iengine/engine.h" #include "iengine/camera.h" #include "iengine/campos.h" #include "iengine/collectn.h" #include "iengine/light.h" #include "iengine/texture.h" #include "iengine/mesh.h" #include "iengine/movable.h" #include "iengine/material.h" #include "iengine/region.h" #include "imesh/thing.h" #include "imesh/sprite3d.h" #include "imesh/object.h" #include "ivideo/graph3d.h" #include "ivideo/graph2d.h" #include "ivideo/txtmgr.h" #include "ivideo/texture.h" #include "ivideo/material.h" #include "ivideo/fontserv.h" #include "igraphic/imageio.h" #include "imap/loader.h" #include "ivaria/reporter.h" #include "ivaria/stdrep.h" #include "ivaria/collider.h" #include "csutil/cmdhelp.h" #include "csutil/debug.h" #include "csutil/csshlib.h" #include "celtool/initapp.h" #include "celtool/persisthelper.h" #include "physicallayer/pl.h" #include "physicallayer/propfact.h" #include "physicallayer/propclas.h" #include "physicallayer/entity.h" #include "physicallayer/persist.h" #include "behaviourlayer/bl.h" #include "propclass/test.h" #include "propclass/mesh.h" #include "propclass/meshsel.h" #include "propclass/inv.h" #include "propclass/chars.h" #include "propclass/move.h" #include "propclass/tooltip.h" #include "propclass/defcam.h" #include "propclass/gravity.h" #include "propclass/timer.h" #include "propclass/region.h" #include "propclass/input.h" #include "propclass/navgraph.h" #include "propclass/linmove.h" #include "propclass/actormove.h" #include "propclass/quest.h" #include "propclass/trigger.h" #include "propclass/zone.h" #include "propclass/mechsys.h" #include "propclass/wheeled.h" #define PATHFIND_VERBOSE 0 //----------------------------------------------------------------------------- WheeledTest::WheeledTest () { SetApplicationName ("wheeledtest"); } WheeledTest::~WheeledTest () { } void WheeledTest::OnExit () { if (pl) pl->CleanCache (); csDebuggingGraph::Dump (0); } void WheeledTest::ProcessFrame () { // We let the entity system do this so there is nothing here. } void WheeledTest::FinishFrame () { g3d->FinishDraw (); g3d->Print (0); } bool WheeledTest::OnKeyboard (iEvent &ev) { // We got a keyboard event. if (csKeyEventHelper::GetEventType (&ev) == csKeyEventTypeDown) { // The user pressed a key (as opposed to releasing it). utf32_char code = csKeyEventHelper::GetCookedCode (&ev); if (code == CSKEY_ESC) { // The user pressed escape. For now we will simply exit the // application. The proper way to quit a Crystal Space application // is by broadcasting a csevQuit event. That will cause the // main runloop to stop. To do that we get the event queue from // the object registry and then post the event. //First, give the vehicle a chance to remove itself properly. csRef vehicle=pl->FindEntity("vehicle"); pl->RemoveEntity(vehicle); csRef q = CS_QUERY_REGISTRY (object_reg, iEventQueue); q->GetEventOutlet ()->Broadcast (csevQuit (object_reg)); } else if (code == 's') { csRef p = CS_QUERY_REGISTRY (object_reg, iCelPersistence); celStandardLocalEntitySet set (pl); size_t i; for (i = 0 ; i < pl->GetEntityCount () ; i++) set.AddEntity (pl->GetEntityByIndex (i)); if (!p->Save (&set, "/this/savefile")) { printf ("Error!\n"); fflush (stdout); } else { printf ("Saved to /this/savefile!\n"); fflush (stdout); game = 0; pl->RemoveEntities (); } } else if (code == 'l') { csRef p = CS_QUERY_REGISTRY (object_reg, iCelPersistence); celStandardLocalEntitySet set (pl); if (!p->Load (&set, "/this/savefile")) { printf ("Error!\n"); fflush (stdout); } else { game = pl->FindEntity ("map"); } } } return false; } csPtr WheeledTest::CreateVehicle (const char* name, const char* sectorname, const csVector3& pos) { // The vehicle csRef entity_cam = pl->CreateEntity (name, bltest, "wheeled", "pccommandinput", "pcmesh", "pcdefaultcamera", "pcmechobject", "pcwheeled", // "pcmeshdeform", CEL_PROPCLASS_END); if (!entity_cam) return 0; csRef pcinp = CEL_QUERY_PROPCLASS_ENT (entity_cam, iPcCommandInput); pcinp->Bind ("up", "accelerate"); pcinp->Bind ("down", "reverse"); pcinp->Bind ("left", "steerleft"); pcinp->Bind ("right", "steerright"); pcinp->Bind ("space", "handbrake"); pcinp->Bind ("x", "center"); pcinp->Bind ("pgup", "lookup"); pcinp->Bind ("pgdn", "lookdown"); csRef pcmesh = CEL_QUERY_PROPCLASS_ENT (entity_cam, iPcMesh); pcmesh->SetMesh ("celCarBody","/cel/data/celcarbody"); iSector* sector = engine->FindSector (sectorname); pcmesh->MoveMesh (sector, pos); csRef pccamera = CEL_QUERY_PROPCLASS_ENT ( entity_cam, iPcDefaultCamera); pccamera->SetMode (iPcDefaultCamera::firstperson); pccamera->SetSpringParameters (10.0f, 0.1f, 0.01f); pccamera->SetMode (iPcDefaultCamera::thirdperson); pccamera->SetSpringParameters (3.5f, 0.25f, 0.01f); pccamera->SetMode (iPcDefaultCamera::m64_thirdperson); pccamera->SetSpringParameters (3.5f, 0.25f, 0.01f); pccamera->SetMinMaxCameraDistance (2.0f, 6.0f); pccamera->SetTurnSpeed (1.0f); pccamera->SetMode (iPcDefaultCamera::lara_thirdperson); pccamera->SetSpringParameters (3.5f, 0.25f, 0.01f); pccamera->SetMinMaxCameraDistance (4.0f, 12.0f); pccamera->SetTurnSpeed (1.0f); pccamera->SetSwingCoef (0.1f); pccamera->SetMode (iPcDefaultCamera::freelook); pccamera->SetSpringParameters (3.5f, 0.25f, 0.01f); pccamera->SetMinMaxCameraDistance (2.0f, 16.0f); pccamera->SetFirstPersonOffset (csVector3 (0, 1.0f, 0)); pccamera->SetThirdPersonOffset (csVector3 (0, 1.0f, 3.0f)); pccamera->SetModeName ("thirdperson"); csRef pcmech=CEL_QUERY_PROPCLASS_ENT(entity_cam, iPcMechanicsObject); //The mass of the vehicle pcmech->SetMass(1000.0); pcmech->SetDensity(1.0); pcmech->SetFriction(0.4f); pcmech->AttachColliderBoundingBox(); csRef pcwheeled=CEL_QUERY_PROPCLASS_ENT(entity_cam,iPcWheeled); pcwheeled->SetWheelMesh("celCarWheel","/cel/data/celcarwheel"); //Activate this, and the vehicle will steer like a tank. Ownage! pcwheeled->SetTankMode(true); /*This part demos how to use presets to modify the steer and drive settings of groups of wheels. It isn't neccessary, as the vehicle defaults to front-wheel steer 4-wheel-drive, but tweaking these provides an easy way to modify car handling. The first setting is steering sensitivity, second setting is drive power.*/ pcwheeled->SetFrontWheelPreset(1.0f,0.8f, 0.000125, 0.125); /*By letting the rear wheels steer a small amount, and giving them most power, the vehicle's handling becomes much more twitchy. */ pcwheeled->SetRearWheelPreset(0.2f,1.0f, 0.000125, 0.125); //Making the outer wheels steer a wider arc also improves traction while turning. pcwheeled->SetOuterWheelSteerPreset(0.7f); pcwheeled->SetBrakeForce(1000); pcwheeled->SetSteerAmount(0.7f); pcwheeled->AddWheelAuto(csVector3(-0.5,0,-0.7f)); pcwheeled->AddWheelAuto(csVector3(0.5,0,-0.7f)); pcwheeled->AddWheelAuto(csVector3(-0.5,0,-0.25f)); pcwheeled->AddWheelAuto(csVector3(0.5,0,-0.25f)); pcwheeled->AddWheelAuto(csVector3(-0.5,0,0.25f)); pcwheeled->AddWheelAuto(csVector3(0.5,0,0.25f)); pcwheeled->AddWheelAuto(csVector3(-0.5,0,0.7f)); pcwheeled->AddWheelAuto(csVector3(0.5,0,0.7f)); pcwheeled->SetGearSettings(1,10,3000); pcwheeled->SetGearSettings(2,20,2000); pcwheeled->SetGearSettings(3,50,700); pcwheeled->SetGearSettings(4,75,450); pcwheeled->SetGearSettings(5,100,200); // csRef pcmeshdeform // = CEL_QUERY_PROPCLASS_ENT(entity_cam,iPcMeshDeform); return csPtr (entity_cam); } bool WheeledTest::CreateMap () { csRef entity_map; csRef entity_dummy; //=============================== // Create the map entity. //=============================== entity_map = pl->CreateEntity ("ent_level", 0, 0, "pczonemanager", "pcinventory", "pcmechsys", CEL_PROPCLASS_END); //=============================== // Engine init. //=============================== engine->Prepare (); csRef cmdline = CS_QUERY_REGISTRY (object_reg, iCommandLineParser); csString path, file; path = cmdline->GetName (0); if (!path.IsEmpty ()) { file = cmdline->GetName (1); if (file.IsEmpty ()) file = "level.xml"; } else { path = "/cellib/track4"; file = "level.xml"; } csRef vfs = CS_QUERY_REGISTRY (object_reg, iVFS); csStringArray paths; paths.Push ("/cellib/lev/"); if (!vfs->ChDirAuto (path, &paths, 0, file)) return ReportError ("Bad file path '%s' at '%s'!", file.GetData (), path.GetData ()); csRef pczonemgr = CEL_QUERY_PROPCLASS_ENT (entity_map, iPcZoneManager); //I need to set loadall in order to load the map NOW, so the wheels know what sector to go to. pczonemgr->SetLoadingMode(CEL_ZONE_LOADALL); if (!pczonemgr->Load (0, file)) return ReportError ("Error loading level '%s' at '%s'!", file.GetData (), path.GetData ()); scfString regionname, startname; pczonemgr->GetLastStartLocation (®ionname, &startname); entity_dummy = CreateVehicle ("vehicle", "track4", csVector3 (0,2.0f,1.0f)); if (!entity_dummy) return false; csRef pccamera = CEL_QUERY_PROPCLASS_ENT (entity_dummy, iPcCamera); if (!pccamera) return false; pccamera->SetZoneManager (pczonemgr, true, regionname, startname); if (pczonemgr->PointMesh ("dyn", regionname, startname) != CEL_ZONEERROR_OK) return ReportError ("Error finding start position!"); csRef pcinv_map = CEL_QUERY_PROPCLASS_ENT (entity_map, iPcInventory); if (!pcinv_map->AddEntity (entity_dummy)) return false; csRef pcmechsys = CEL_QUERY_PROPCLASS_ENT (entity_map, iPcMechanicsSystem); pcmechsys->EnableQuickStep (); pcmechsys->SetStepTime (0.01f); /*csRef pcmesh = CEL_QUERY_PROPCLASS_ENT (entity_map, iPcMesh); pcmesh->SetMesh(engine->FindMeshObject("Terrain")); csRef pcmechobj = CEL_QUERY_PROPCLASS_ENT (entity_map, iPcMechanicsObject); //This is sometimes neccessary to stop the map from shifting lol pcmechobj->SetMass(999999999999999999.0f); pcmechobj->SetDensity(99999999999999999999.0f); pcmechobj->SetFriction(0.6f); pcmechobj->AttachColliderMesh (); pcmechobj->MakeStatic (true);*/ game = entity_map; return true; } bool WheeledTest::OnInitialize (int argc, char* argv[]) { csDebuggingGraph::SetupGraph (object_reg); if (!celInitializer::RequestPlugins (object_reg, CS_REQUEST_VFS, CS_REQUEST_OPENGL3D, CS_REQUEST_ENGINE, CS_REQUEST_FONTSERVER, CS_REQUEST_IMAGELOADER, CS_REQUEST_LEVELLOADER, CS_REQUEST_REPORTER, CS_REQUEST_REPORTERLISTENER, CS_REQUEST_PLUGIN ("cel.physicallayer", iCelPlLayer), CS_REQUEST_PLUGIN ("cel.behaviourlayer.test:iCelBlLayer.Test", iCelBlLayer), CS_REQUEST_PLUGIN ("cel.persistence.xml", iCelPersistence), CS_REQUEST_PLUGIN ("crystalspace.collisiondetection.opcode", iCollideSystem), CS_REQUEST_END)) { return ReportError ("Can't initialize plugins!"); } csBaseEventHandler::Initialize (object_reg); // Now we need to setup an event handler for our application. // Crystal Space is fully event-driven. Everything (except for this // initialization) happens in an event. if (!RegisterQueue (object_reg, csevAllEvents (object_reg))) return ReportError ("Can't setup event handler!"); return true; } bool WheeledTest::Application () { // Open the main system. This will open all the previously loaded plug-ins. // i.e. all windows will be opened. if (!OpenApplication (object_reg)) return ReportError ("Error opening system!"); // The virtual clock. vc = CS_QUERY_REGISTRY (object_reg, iVirtualClock); if (!vc) return ReportError ("Can't find the virtual clock!"); //vc->SetClockSpeed(0.3f); // Find the pointer to engine plugin engine = CS_QUERY_REGISTRY (object_reg, iEngine); if (!engine) return ReportError ("No iEngine plugin!"); loader = CS_QUERY_REGISTRY (object_reg, iLoader); if (!loader) return ReportError ("No iLoader plugin!"); g3d = CS_QUERY_REGISTRY (object_reg, iGraphics3D); if (!g3d) return ReportError ("No iGraphics3D plugin!"); kbd = CS_QUERY_REGISTRY (object_reg, iKeyboardDriver); if (!kbd) return ReportError ("No iKeyboardDriver plugin!"); pl = CS_QUERY_REGISTRY (object_reg, iCelPlLayer); if (!pl) return ReportError ("CEL physical layer missing!"); bltest = CS_QUERY_REGISTRY_TAG_INTERFACE (object_reg, "iCelBlLayer.Test", iCelBlLayer); if (!bltest) return ReportError ("CEL test behaviour layer missing!"); pl->RegisterBehaviourLayer (bltest); csRef vfs = CS_QUERY_REGISTRY (object_reg, iVFS); #if defined(VFS_PKGDATADIR) && defined(VFS_TOPSRCDIR) vfs->Mount ("cel", VFS_PKGDATADIR"$/, "VFS_TOPSRCDIR"$/"); #else // VFS_PKGDATADIR vfs->Mount ("cel", "$.$/"); #endif // VFS_PKGDATADIR // XXX: This should be in a config file... if (!pl->LoadPropertyClassFactory ("cel.pcfactory.test")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.linmove")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.actormove")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.solid")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.colldet")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.region")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.zonemanager")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.defaultcamera")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.tooltip")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.timer")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.inventory")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.characteristics")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.mesh")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.light")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.portal")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.meshselect")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.pccommandinput")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.quest")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.properties")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.trigger")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.billboard")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.graph")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.link")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.node")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.navgraphrules")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.navgraphrulesfps")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.mechsys")) return false; if (!pl->LoadPropertyClassFactory ("cel.pcfactory.mechobject")) return false; if(!pl->LoadPropertyClassFactory("cel.pcfactory.wheeled")) return false; // if(!pl->LoadPropertyClassFactory("cel.pcfactory.meshdeform")) // return false; if (!CreateMap ()) return false; // This calls the default runloop. This will basically just keep // broadcasting process events to keep the game going. Run (); return true; }