예제 #1
1
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;
	}
예제 #2
0
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;
	}
예제 #3
0
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
	}