/* * psworld.cpp - author Matze Braun and * Keith Fulton * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "engine/celbase.h" #include "psbehave/psworld.h" #include "util/psconst.h" #include "util/log.h" #include #define SHARED_REGION_NAME "SharedDataRegion" psWorld::psWorld() { room = NULL; sharedRegion = NULL; } psWorld::~psWorld() { if (sharedRegion != NULL) { engine = CS_QUERY_REGISTRY (object_reg, iEngine); engine->GetRegions()->Remove(sharedRegion); } transarray.Empty(); } bool psWorld::Initialize( iObjectRegistry* objectReg) { object_reg = objectReg; /** * In order to delete regions later without deleting our * friendly players, we must create a region for them to live * in independently from the map regions. Region loading, * unloading and selection logic below always restores the * region back to this, so the GetCurrentRegion() will always * be spriteworld, unless we are actually in the act of loading * a map. * * (Note that SelectRegion here creates the region also.) */ engine = CS_QUERY_REGISTRY (object_reg, iEngine); sharedRegion = engine->CreateRegion(SHARED_REGION_NAME); return true; } void psWorld::MoveToSharedRegion(iRegion * source, iObject * something) { if (source->IsInRegion(something)) { source->Remove(something); sharedRegion->Add(something); } } void psWorld::MoveToSharedRegion(iRegion * source) { engine = CS_QUERY_REGISTRY (object_reg, iEngine); csRef materials = engine->GetMaterialList(); csRef textures = engine->GetTextureList(); csRef factories = engine->GetMeshFactories(); int i; for (i=0; i < materials->GetCount(); i++) MoveToSharedRegion(source, materials->Get(i)->QueryObject()); for (i=0; i < textures->GetCount(); i++) MoveToSharedRegion(source, textures->Get(i)->QueryObject()); for (i=0; i < factories->GetCount(); i++) MoveToSharedRegion(source, factories->Get(i)->QueryObject()); } bool psWorld::CreateMap( const char* name, const char* mapfile, bool load_now, bool loadMeshes) { if ( !NewRegion(mapfile,load_now, loadMeshes) ) return false; return true; } psRegion* psWorld::NewRegion(const char *mapfile,bool load, bool loadMeshes) { for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; if (!strcmp(rgn->GetName(),mapfile)) return rgn; } psRegion *newregion = new psRegion(object_reg, this, mapfile); if (load && !newregion->Load(loadMeshes)) { delete newregion; return NULL; } // This must be rebuilt when the sector list changes BuildWarpCache(); regions.Push(newregion); return newregion; } void psWorld::GetAllRegionNames(csString& str) { str = ""; for (unsigned i=0; i < regions.Length(); i++) { str.Append( regions[i]->GetName() ); str.Append( "|" ); } } void psWorld::FlagAllRegionsAsNotNeeded() { for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; rgn->SetNeededFlag(psWorld::NOT_NEEDED); } } void psWorld::FlagRegionAsNeeded(const char *map) { // if on the list, just retain it with the flag for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; if (rgn->GetName() == map) { rgn->SetNeededFlag(psWorld::NEEDED); return; } } // if not on the list, make a new entry psRegion *rgn = NewRegion(map,psWorld::DEFER_LOAD); rgn->SetNeededFlag(psWorld::NEEDED); } void ConnectPortalToSector(iEngine * engine, const char * portalName, const char * sectorName) { iMeshList * meshes = engine->GetMeshes(); iMeshWrapper * wrap = meshes->FindByName(portalName); if (wrap) { iMeshObject *obj=wrap->GetMeshObject(); if (obj) { csRef portalc = SCF_QUERY_INTERFACE(obj, iPortalContainer); if (portalc==NULL) return; for (int pn=0; pn < portalc->GetPortalCount(); pn++) { iPortal * portal = portalc->GetPortal(pn); iSector * sector = engine->FindSector(sectorName); if (sector) { Error3("sector %s connected to portal %s", sectorName, portalName); portal->SetSector(sector); } } } } } bool psWorld::ExecuteFlaggedRegions(bool transitional, bool one) { bool oneDeleted = false; // Transitional means that we should allow whatever // levels are already loaded to stay loaded, but still // ensure that listed levels are loaded also. if (!transitional) { // Delete and unload regions not flagged to be saved for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; if (!rgn->IsNeeded() ) { if (one && oneDeleted) return false; Debug2(LOG_LOAD,0,"Region %s is being deleted.\n",rgn->GetName() ); regions.DeleteIndex(i); i--; oneDeleted = true; } } } bool oneLoaded = false; // Now load any remaining regions on the list which are not already loaded for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; if (!rgn->IsLoaded() ) { if (one && oneLoaded) return false; if(!rgn->Load()) { Error2("Loading region %s failed!", rgn->GetName()); abort(); } oneLoaded = true; } } return true; } bool psWorld::NeedsLoading(bool transitional) { // Transitional means that we should allow whatever // levels are already loaded to stay loaded, but still // ensure that listed levels are loaded also. if (!transitional) for (unsigned i=0; i < regions.Length(); i++) if (!regions[i]->IsNeeded() ) return true; for (unsigned i=0; i < regions.Length(); i++) if (!regions[i]->IsLoaded() ) return true; return false; } bool psWorld::IsAllLoaded() { for (unsigned i=0; i < regions.Length(); i++) { psRegion *rgn = regions[i]; if (!rgn->IsLoaded() ) { return false; } } return true; } void psWorld::GetNotNeededRegions(csArray & regs) { for (unsigned i=0; i < regions.Length(); i++) if (!regions[i]->IsNeeded() ) regs.Push(regions[i]->GetRegion()); } void psWorld::BuildWarpCache() { int sectorCount = engine->GetSectors()->GetCount(); /// Clear existing entries transarray.Empty(); transarray.SetSize(sectorCount); csReversibleTransform identity; for(int i=0; i >& portals = engine->GetSectors()->Get(i)->GetPortalMeshes(); csSet >::GlobalIterator it = portals.GetIterator (); while (it.HasNext ()) { iMeshWrapper* portal_mesh = it.Next (); iPortalContainer* pc = portal_mesh->GetPortalContainer (); int i; for (i = 0 ; i < pc->GetPortalCount () ; i++) { iPortal* portal = pc->GetPortal (i); if (portal->CompleteSector(0)) { if (engine->GetSectors()->Find(portal->GetSector()) != -1) transarray[i].Set(portal->GetSector(), portal->GetWarp()); } } } } } bool psWorld::WarpSpace(const iSector* from, const iSector* to, csVector3& pos) { if(from == to) return true; // No need to transform, pos ok. int i = engine->GetSectors()->Find((iSector*)from); csReversibleTransform* transform = transarray[i].Get((iSector*)to); if(transform) { pos = *transform * pos; return true; // Position transformed, pos ok. } return false; // Didn't find transformation, pos not ok. } float psWorld::Distance(const csVector3& from_pos, const iSector* from_sector, csVector3 to_pos, const iSector* to_sector) { if (from_sector == to_sector) { return (from_pos - to_pos).Norm(); } else { if (WarpSpace(to_sector, from_sector, to_pos)) { return (from_pos - to_pos).Norm(); } else { return 9999999.99f; // No transformation found, so just set larg distance. } } } //-------------------------------------------------------------------------- psRegion::psRegion(iObjectRegistry *obj_reg, psWorld * world, const char *file) { object_reg = obj_reg; this->world = world; worlddir.Format("/planeshift/world/%s", file); worldfile = "world"; regionname = file; loaded = false; } psRegion::~psRegion() { if (loaded) Unload(); } bool psRegion::Load(bool loadMeshes) { if (loaded) return true; bool using3D; csRef engine = CS_QUERY_REGISTRY (object_reg, iEngine); // Find out if we are ever going to render 3D csRef g3d = CS_QUERY_REGISTRY(object_reg, iGraphics3D); csRef factory = SCF_QUERY_INTERFACE(g3d, iFactory); const char* g3Dname = factory->QueryClassID(); using3D = (strcmp("crystalspace.graphics3d.null", g3Dname)? true: false); csString target; target.Format("%s/world", worlddir.GetData()); csRef vfs = CS_QUERY_REGISTRY( object_reg, iVFS ); csRef xml ( CS_QUERY_REGISTRY (object_reg, iDocumentSystem)); csRef doc = xml->CreateDocument(); csRef buf (vfs->ReadFile (target.GetData())); if (!buf || !buf->GetSize ()) { Error2("Error loading world file. %s\n", target.GetData()); return false; } const char* error = doc->Parse( buf ); if( error ) { Error3("Error %s loading file to be cleaned. %s\n",error, target.GetData()); return false; } csRef worldNode = doc->GetRoot()->GetNode("world"); if(!loadMeshes) { // Clean the world file to remove all textures/meshes/models printf("Cleaning map file.\n"); worldNode = Clean(worldNode); } // Save the old region to restore it if this load doesn't work // Create a new region with the given name, or select it if already there iRegion* cur_region = engine->CreateRegion (regionname); // Clear it out if it already existed cur_region->DeleteAll (); // Now load the map into the selected region csRef loader (CS_QUERY_REGISTRY (object_reg, iLoader)); CS_ASSERT (loader != NULL); csRef VFS (CS_QUERY_REGISTRY (object_reg, iVFS)); CS_ASSERT (VFS != NULL); VFS->ChDir (worlddir); engine->SetCacheManager(NULL); csTicks start = csGetTicks(); Debug2(LOG_LOAD, 0,"Loading map file %s", worlddir.GetData()); if (!loader->LoadMap(worldNode, CS_LOADER_KEEP_WORLD,cur_region, CS_LOADER_ACROSS_REGIONS, true)) //original // if (!loader->LoadMapFile(worldfile, CS_LOADER_KEEP_WORLD,cur_region, CS_LOADER_ACROSS_REGIONS)) // if (!loader->LoadMapFile(worldfile, false,0, false)) { Error3("loader->LoadMapFile(%s,%s) failed.",worlddir.GetData(),worldfile.GetData() ); Error2("Region name was: %s", regionname.GetData() ); return false; } Debug2(LOG_LOAD, 0,"After LoadMapFile, %dms elapsed", csGetTicks()-start); // Successfully loaded. Now get textures ready, etc. and return. if (using3D) { cur_region->Prepare (); Debug2(LOG_LOAD, 0,"After Prepare, %dms elapsed", csGetTicks()-start); } if (using3D) { engine->PrecacheDraw (cur_region); Debug2(LOG_LOAD, 0,"After Precache, %dms elapsed", csGetTicks()-start); } world->MoveToSharedRegion(cur_region); if (loadMeshes) { SetupWorldColliders(engine,cur_region); Debug2(LOG_LOAD, 0,"After SetupWorldColliders, %dms elapsed\n", csGetTicks()-start); } loaded = true; printf("Region %s loaded successfully in %dms\n", (const char *)regionname, csGetTicks()-start); VFS->ChDir("/planeshift"); engine->SetCacheManager(NULL); engine->GetCacheManager(); return true; } csRef psRegion::Clean(csRef world) { csRef xml ( CS_QUERY_REGISTRY (object_reg, iDocumentSystem)); csRef doc = xml->CreateDocument(); csRef node = doc->CreateRoot(); // Copy the world node csRef cleanedWorld = node->CreateNodeBefore(CS_NODE_ELEMENT); cleanedWorld->SetValue("world"); // Copy the sector node csRef sectors = world->GetNodes("sector"); while ( sectors->HasNext() ) { csRef sector = sectors->Next(); csRef cleanedSector = cleanedWorld->CreateNodeBefore(CS_NODE_ELEMENT); cleanedSector->SetValue(sector->GetValue()); // Copy the sector attributes csRef attrs = sector->GetAttributes(); while(attrs->HasNext()) { csRef attr = attrs->Next(); cleanedSector->SetAttribute(attr->GetName(), attr->GetValue()); } // Copy the portals csRef nodes = sector->GetNodes("portal"); while(nodes->HasNext()) { csRef portal = nodes->Next(); csRef cleanedportal = cleanedSector->CreateNodeBefore(CS_NODE_ELEMENT); CloneNode(portal, cleanedportal); } } // Copy the start node csRef startLocations = world->GetNodes("start"); while (startLocations->HasNext()) { csRef start = startLocations->Next(); csRef cleanedStart = cleanedWorld->CreateNodeBefore(CS_NODE_ELEMENT); CloneNode(start, cleanedStart); } return cleanedWorld; } void psRegion::CloneNode (iDocumentNode* from, iDocumentNode* to) { to->SetValue (from->GetValue ()); csRef it = from->GetNodes (); while (it->HasNext ()) { csRef child = it->Next (); csRef child_clone = to->CreateNodeBefore ( child->GetType (), 0); CloneNode (child, child_clone); } csRef atit = from->GetAttributes (); while (atit->HasNext ()) { csRef attr = atit->Next (); to->SetAttribute (attr->GetName (), attr->GetValue ()); } } bool psRegion::Unload() { if (!loaded) return true; loaded = false; // The engine needs to not be in the region when it is unloaded! csRef engine = CS_QUERY_REGISTRY (object_reg, iEngine); iRegion* cur_region = engine->CreateRegion (regionname); // Delete everything in the region cur_region->DeleteAll (); return true; } void psRegion::SetupWorldColliders(iEngine *engine,iRegion* cur_region) { csRef cdsys = CS_QUERY_REGISTRY (object_reg, iCollideSystem); csRef iter = cur_region->QueryObject()->GetIterator(); //return; csRef mesh; iObject *curr; while ( iter->HasNext() ) { curr = iter->Next(); // regions hold many objects, but only meshes are collide-able csRef sp = SCF_QUERY_INTERFACE(curr,iMeshWrapper); if (sp && sp->GetMeshObject() ) { mesh = sp->GetMeshObject()->GetObjectModel()->GetPolygonMeshColldet(); if ( mesh ) { csRef cw = csPtr ( new csColliderWrapper( sp->QueryObject(), cdsys, mesh)); cw->SetName(sp->QueryObject()->GetName()); } csRef thing = SCF_QUERY_INTERFACE(sp->GetMeshObject(),iThingState); if (thing) thing->Prepare (); } } } iRegion * psRegion::GetRegion() { csRef engine = CS_QUERY_REGISTRY (object_reg, iEngine); return engine->GetRegions()->FindByName(regionname); }