ALERROR CDesignCollection::AddDynamicType (SExtensionDesc *pExtension, DWORD dwUNID, const CString &sSource, bool bNewGame, CString *retsError) // AddDynamicType // // Adds a dynamic type at runtime { ALERROR error; // If we're pass game-create, the UNID must not already exist if (!bNewGame && FindEntry(dwUNID)) { if (retsError) *retsError = strPatternSubst(CONSTLIT("Type already exists: %x"), dwUNID); return ERR_FAIL; } // Add it to the dynamics table CDesignType *pType; if (error = m_DynamicTypes.DefineType(pExtension, dwUNID, sSource, &pType, retsError)) return error; // Make sure that the type can be created at this point. // For example, we can't create SystemMap types after the game has started. switch (pType->GetType()) { case designAdventureDesc: case designGlobals: case designImage: case designSound: case designEconomyType: case designTemplateType: { m_DynamicTypes.Delete(dwUNID); if (retsError) *retsError = CONSTLIT("Dynamic design type not supported."); return ERR_FAIL; } case designSystemType: case designSystemTable: case designSystemMap: { if (!bNewGame) { m_DynamicTypes.Delete(dwUNID); if (retsError) *retsError = CONSTLIT("Dynamic design type not supported after new game created."); return ERR_FAIL; } } } // Since we've already bound, we need to simulate that here (although we // [obviously] don't allow existing types to bind to dynamic types). // // We start by adding the type to the AllTypes list m_AllTypes.AddOrReplaceEntry(pType); // If this is new game time, then it means that we are inside of BindDesign. In // that case, we don't do anything more (since BindDesign will take care of // it). if (!bNewGame) { // Next we add it to the specific type tables m_ByType[pType->GetType()].AddEntry(pType); // Bind SDesignLoadCtx Ctx; Ctx.pExtension = pExtension; Ctx.bNewGame = bNewGame; if (error = pType->PrepareBindDesign(Ctx)) { m_AllTypes.Delete(dwUNID); m_ByType[pType->GetType()].Delete(dwUNID); m_DynamicTypes.Delete(dwUNID); if (retsError) *retsError = Ctx.sError; return error; } if (error = pType->BindDesign(Ctx)) { m_AllTypes.Delete(dwUNID); m_ByType[pType->GetType()].Delete(dwUNID); m_DynamicTypes.Delete(dwUNID); if (retsError) *retsError = Ctx.sError; return error; } // Cache some global events CacheGlobalEvents(pType); // Done binding if (error = pType->FinishBindDesign(Ctx)) { m_AllTypes.Delete(dwUNID); m_ByType[pType->GetType()].Delete(dwUNID); m_DynamicTypes.Delete(dwUNID); if (retsError) *retsError = Ctx.sError; return error; } } return NOERROR; }
ALERROR CDesignCollection::BindDesign (SDesignLoadCtx &Ctx) // BindDesign // // Bind the design collection so that design types point the appropriate // pointers by UNID { ALERROR error; int i, j; // Unbind everything for (i = 0; i < m_AllTypes.GetCount(); i++) m_AllTypes.GetEntry(i)->UnbindDesign(); m_AllTypes.DeleteAll(); // Reset the bind tables for (i = 0; i < designCount; i++) m_ByType[i].DeleteAll(); // We start with all the base types for (i = 0; i < m_Base.GetCount(); i++) m_AllTypes.AddEntry(m_Base.GetEntry(i)); // Start with base topology m_pTopology = &m_BaseTopology; m_pAdventureExtension = NULL; // Now add all enabled extensions for (i = 0; i < GetExtensionCount(); i++) { SExtensionDesc *pExtension = GetExtension(i); if (pExtension->bEnabled) { // Add design elements in extension for (j = 0; j < pExtension->Table.GetCount(); j++) { CDesignType *pEntry = pExtension->Table.GetEntry(j); m_AllTypes.AddOrReplaceEntry(pEntry); } // Handle adventure extensions if (pExtension->iType == extAdventure) { // Keep track of extension m_pAdventureExtension = pExtension; // Add topology m_pTopology = &pExtension->Topology; } } else { if (pExtension->iType == extAdventure) { DWORD dwCoverImage = 0; // Adventure desc elements are added even if not enabled for (j = 0; j < pExtension->Table.GetCount(); j++) { CDesignType *pEntry = pExtension->Table.GetEntry(j); if (pEntry->GetType() == designAdventureDesc) { m_AllTypes.AddOrReplaceEntry(pEntry); // Get the cover image used by the adventure, because // we need to load that too. CAdventureDesc *pDesc = CAdventureDesc::AsType(pEntry); dwCoverImage = pDesc->GetBackgroundUNID(); } } // Make sure we load the cover image if (dwCoverImage) { for (j = 0; j < pExtension->Table.GetCount(); j++) { CDesignType *pEntry = pExtension->Table.GetEntry(j); if (pEntry->GetUNID() == dwCoverImage) m_AllTypes.AddOrReplaceEntry(pEntry); } } } } } // If this is a new game, then create all the Template types if (Ctx.bNewGame) { m_DynamicUNIDs.DeleteAll(); m_DynamicTypes.DeleteAll(); if (error = FireOnGlobalTypesInit(Ctx)) return error; if (error = CreateTemplateTypes(Ctx)) return error; } // Add all the dynamic types. These came either from the saved game file or // from the Template types above. for (i = 0; i < m_DynamicTypes.GetCount(); i++) m_AllTypes.AddOrReplaceEntry(m_DynamicTypes.GetType(i)); // Initialize the byType lists for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); m_ByType[pEntry->GetType()].AddEntry(pEntry); } // Set our adventure desc as current; since adventure descs are always // loaded this is the only thing that we can use to tell if we should // call global events. // // This must happen after Unbind (because that clears it) and before // PrepareBindDesign. // // NOTE: m_pAdventureDesc can be NULL (e.g., in the intro screen). if (m_pAdventureDesc) m_pAdventureDesc->SetCurrentAdventure(); // Cache a map between currency name and economy type // We need to do this before Bind because some types will lookup // a currency name during Bind. m_EconomyIndex.DeleteAll(); for (i = 0; i < GetCount(designEconomyType); i++) { CEconomyType *pEcon = CEconomyType::AsType(GetEntry(designEconomyType, i)); const CString &sName = pEcon->GetSID(); bool bUnique; CEconomyType **ppDest = m_EconomyIndex.SetAt(sName, &bUnique); if (!bUnique) return pEcon->ComposeLoadError(Ctx, CONSTLIT("Currency ID must be unique")); *ppDest = pEcon; } // Prepare to bind. This is used by design elements // that need two passes to bind. for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->PrepareBindDesign(Ctx)) return error; } // Now call Bind on all active design entries for (i = 0; i < evtCount; i++) m_EventsCache[i]->DeleteAll(); for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->BindDesign(Ctx)) return error; // Cache some global events. We keep track of the global events for // all types so that we can access them faster. CacheGlobalEvents(pEntry); } // Finish binding. This pass is used by design elements // that need to do stuff after all designs are bound. for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->FinishBindDesign(Ctx)) return error; } return NOERROR; }
ALERROR CDesignCollection::BindDesign (const TArray<CExtension *> &BindOrder, bool bNewGame, bool bNoResources, CString *retsError) // BindDesign // // Binds the design collection to the set of design types in the given list of // extensions. { DEBUG_TRY ALERROR error; int i; // Unbind everything DEBUG_TRY CShipClass::UnbindGlobal(); for (i = 0; i < m_AllTypes.GetCount(); i++) m_AllTypes.GetEntry(i)->UnbindDesign(); m_AllTypes.DeleteAll(); DEBUG_CATCH_MSG("Crash unbinding types."); // Reset the bind tables for (i = 0; i < designCount; i++) m_ByType[i].DeleteAll(); m_CreatedTypes.DeleteAll(true); m_OverrideTypes.DeleteAll(); // Reset m_pTopology = NULL; m_pAdventureExtension = NULL; // Create a design load context SDesignLoadCtx Ctx; Ctx.bBindAsNewGame = bNewGame; Ctx.bNoResources = bNoResources; // Loop over the bind list in order and add appropriate types to m_AllTypes // (The order guarantees that the proper types override) for (i = 0; i < BindOrder.GetCount(); i++) { CExtension *pExtension = BindOrder[i]; try { const CDesignTable &Types = pExtension->GetDesignTypes(); #ifdef DEBUG_BIND ::OutputDebugString(strPatternSubst(CONSTLIT("EXTENSION %s\n"), pExtension->GetName())); for (int j = 0; j < Types.GetCount(); j++) { ::OutputDebugString(strPatternSubst(CONSTLIT("%08x: %s\n"), Types.GetEntry(j)->GetUNID(), Types.GetEntry(j)->GetTypeName())); } #endif // Run globals for the extension if (error = pExtension->ExecuteGlobals(Ctx)) { *retsError = Ctx.sError; return error; } // Add the types m_AllTypes.Merge(Types, &m_OverrideTypes); // If this is the adventure, then remember it if (pExtension->GetType() == extAdventure) { m_pAdventureExtension = pExtension; m_pAdventureDesc = pExtension->GetAdventureDesc(); } // If this is an adventure or the base extension then take the // topology. if (pExtension->GetType() == extAdventure || pExtension->GetType() == extBase) m_pTopology = &pExtension->GetTopology(); } catch (...) { ::kernelDebugLogMessage("Crash processing extension:"); CExtension::DebugDump(pExtension, true); throw; } } // If this is a new game, then create all the Template types if (bNewGame) { DEBUG_TRY m_DynamicUNIDs.DeleteAll(); m_DynamicTypes.DeleteAll(); if (error = FireOnGlobalTypesInit(Ctx)) { *retsError = Ctx.sError; return error; } if (error = CreateTemplateTypes(Ctx)) { *retsError = Ctx.sError; return error; } DEBUG_CATCH_MSG("Crash defining dynamic types."); } // Add all the dynamic types. These came either from the saved game file or // from the Template types above. m_AllTypes.Merge(m_DynamicTypes, &m_OverrideTypes); // Now resolve all overrides and inheritance if (error = ResolveOverrides(Ctx)) { *retsError = Ctx.sError; return error; } // Initialize the byType lists DEBUG_TRY for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); m_ByType[pEntry->GetType()].AddEntry(pEntry); } DEBUG_CATCH_MSG("Crash initializing byType lists."); // Set our adventure desc as current; since adventure descs are always // loaded this is the only thing that we can use to tell if we should // call global events. // // This must happen after Unbind (because that clears it) and before // PrepareBindDesign. // // NOTE: m_pAdventureDesc can be NULL (e.g., in the intro screen). DEBUG_TRY if (m_pAdventureDesc) m_pAdventureDesc->SetCurrentAdventure(); DEBUG_CATCH_MSG("Crash setting current adventure."); // Cache a map between currency name and economy type // We need to do this before Bind because some types will lookup // a currency name during Bind. DEBUG_TRY m_EconomyIndex.DeleteAll(); for (i = 0; i < GetCount(designEconomyType); i++) { CEconomyType *pEcon = CEconomyType::AsType(GetEntry(designEconomyType, i)); const CString &sName = pEcon->GetSID(); bool bUnique; CEconomyType **ppDest = m_EconomyIndex.SetAt(sName, &bUnique); if (!bUnique) { pEcon->ComposeLoadError(Ctx, CONSTLIT("Currency ID must be unique")); *retsError = Ctx.sError; return ERR_FAIL; } *ppDest = pEcon; } DEBUG_CATCH_MSG("Crash initializing economies."); // Prepare to bind. This is used by design elements that need two passes // to bind. We also use it to set up the inheritence hierarchy, which means // that we rely on the map from UNID to valid design type (m_AllTypes) m_DisplayAttribs.DeleteAll(); DEBUG_TRY for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->PrepareBindDesign(Ctx)) { *retsError = Ctx.sError; return error; } // We take this opportunity to build a list of display attributes // defined by each type. const CDisplayAttributeDefinitions &Attribs = pEntry->GetDisplayAttributes(); if (!Attribs.IsEmpty()) m_DisplayAttribs.Append(Attribs); } DEBUG_CATCH_MSG("Crash in PrepareBind."); // Now call Bind on all active design entries for (i = 0; i < evtCount; i++) m_EventsCache[i]->DeleteAll(); DEBUG_TRY for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->BindDesign(Ctx)) { *retsError = Ctx.sError; return error; } // Cache some global events. We keep track of the global events for // all types so that we can access them faster. CacheGlobalEvents(pEntry); } DEBUG_CATCH_MSG("Crash in BindDesign."); // Finish binding. This pass is used by design elements // that need to do stuff after all designs are bound. DEBUG_TRY for (i = 0; i < m_AllTypes.GetCount(); i++) { CDesignType *pEntry = m_AllTypes.GetEntry(i); if (error = pEntry->FinishBindDesign(Ctx)) { *retsError = Ctx.sError; return error; } } DEBUG_CATCH_MSG("Crash in FinishBind."); // Remember what we bound m_BoundExtensions = BindOrder; return NOERROR; DEBUG_CATCH }