Пример #1
0
void CSovereign::InitRelationships (void)

//	InitRelationships
//
//	Initialize relationships from XML element

	{
	int i;

	DeleteRelationships();

	if (m_pInitialRelationships)
		{
		for (i = 0; i < m_pInitialRelationships->GetContentElementCount(); i++)
			{
			CXMLElement *pRelDesc = m_pInitialRelationships->GetContentElement(i);
			CSovereign *pTarget = g_pUniverse->FindSovereign(pRelDesc->GetAttributeInteger(SOVEREIGN_ATTRIB));
			if (pTarget)
				{
				CString sDisposition = pRelDesc->GetAttribute(DISPOSITION_ATTRIB);
				if (strEquals(sDisposition, DISP_FRIEND))
					SetDispositionTowards(pTarget, dispFriend);
				else if (strEquals(sDisposition, DISP_NEUTRAL))
					SetDispositionTowards(pTarget, dispNeutral);
				else if (strEquals(sDisposition, DISP_ENEMY))
					SetDispositionTowards(pTarget, dispEnemy);
				}
			}
		}
	}
Пример #2
0
ALERROR CEffectVariantCreator::OnEffectCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID)

//	OnEffectCreateFromXML
//
//	Creates from XML

	{
	ALERROR error;
	int i;

	//	Allocate the creator array

	int iCount = pDesc->GetContentElementCount();
	if (iCount == 0)
		{
		Ctx.sError = CONSTLIT("<Variants> effect must have at least one sub-element.");
		return ERR_FAIL;
		}

	m_Effects.InsertEmpty(iCount);

	for (i = 0; i < iCount; i++)
		{
		CString sSubUNID = strPatternSubst(CONSTLIT("%s/%d"), sUNID, i);

		CXMLElement *pCreatorDesc = pDesc->GetContentElement(i);
		if (error = CEffectCreator::CreateFromXML(Ctx, pCreatorDesc, sSubUNID, &m_Effects[i].pEffect))
			return error;

		m_Effects[i].iMaxValue = pCreatorDesc->GetAttributeInteger(MAX_VALUE_ATTRIB);
		}

	return NOERROR;
	}
Пример #3
0
ALERROR CGroupOfGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	//	Load content elements

	m_Table.InsertEmpty(pDesc->GetContentElementCount());
	for (i = 0; i < m_Table.GetCount(); i++)
		{
		CXMLElement *pEntry = pDesc->GetContentElement(i);
			
		m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB);
		if (m_Table[i].iChance == 0)
			m_Table[i].iChance = 100;

		CString sCount = pEntry->GetAttribute(COUNT_ATTRIB);
		if (sCount.IsBlank())
			m_Table[i].Count = DiceRange(0, 0, 1);
		else
			m_Table[i].Count.LoadFromXML(sCount);

		if (error = IItemGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pItem))
			return error;
		}

	//	See if we force an average value

	CString sAttrib;
	if (pDesc->FindAttribute(LEVEL_VALUE_ATTRIB, &sAttrib))
		{
		TArray<int> Values;
		ParseIntegerList(sAttrib, 0, &Values);

		m_AverageValue.InsertEmpty(MAX_ITEM_LEVEL + 1);
		m_AverageValue[0] = 0;
		for (i = 0; i < Values.GetCount(); i++)
			m_AverageValue[i + 1] = Values[i];

		for (i = Values.GetCount() + 1; i <= MAX_ITEM_LEVEL; i++)
			m_AverageValue[i] = 0;
		}
	else if (pDesc->FindAttribute(VALUE_ATTRIB, &sAttrib))
		{
		int iValue = strToInt(sAttrib, 0);

		m_AverageValue.InsertEmpty(MAX_ITEM_LEVEL + 1);
		m_AverageValue[0] = 0;
		for (i = 1; i <= MAX_ITEM_LEVEL; i++)
			m_AverageValue[i] = iValue;
		}

	return NOERROR;
	}
Пример #4
0
bool AddTrait (CNPWorld *pWorld, 
			   CXMLElement *pTraitTable, 
			   CXMLElement *pTrait, 
			   CSymbolTable &Symbols)
	{
	int i;

	//	First check to see if the world has any of the traits
	//	listed in the "unless" sections

	for (i = 0; i < pTrait->GetContentElementCount(); i++)
		{
		CXMLElement *pUnless = pTrait->GetContentElement(i);
		if (strCompare(pUnless->GetTag(), CONSTLIT("Unless")) != 0)
			continue;

		//	If the world has the prohibited trait then we cannot
		//	add the desired trait.

		if (pWorld->HasTrait(GetSymbolicAttribute(Symbols, pUnless, CONSTLIT("Trait"))))
			return false;
		}

	//	Add the trait

	pWorld->SetTrait(GetSymbolicAttribute(Symbols, pTrait, CONSTLIT("Trait")));

	//	Add any traits that are implied by this trait

	for (i = 0; i < pTrait->GetContentElementCount(); i++)
		{
		CXMLElement *pImply = pTrait->GetContentElement(i);
		if (strCompare(pImply->GetTag(), CONSTLIT("Imply")) != 0)
			continue;

		//	Random chance of actually having this trait

		if (mathRandom(1, 100) > pImply->GetAttributeInteger(CONSTLIT("Prob")))
			continue;

		//	If we already have this trait the don't bother

		if (pWorld->HasTrait(GetSymbolicAttribute(Symbols, pImply, CONSTLIT("Trait"))))
			continue;

		//	Look for the implied trait in the table

		CXMLElement *pNewTrait = FindTraitInTable(pTraitTable, pImply->GetAttribute(CONSTLIT("Trait")));

		//	Add it. Note that we don't care if we cannot

		if (pNewTrait)
			AddTrait(pWorld, pTraitTable, pNewTrait, Symbols);
		}

	return true;
	}
Пример #5
0
ALERROR CShapeEffectCreator::OnEffectCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID)

//	OnEffectCreateFromXML
//
//	Load from XML

	{
	int i;
	CString sAttrib;

	m_iWidth = pDesc->GetAttributeInteger(SCALE_WIDTH_ATTRIB);
	m_iLength = pDesc->GetAttributeInteger(SCALE_LENGTH_ATTRIB);
	m_bDirectional = pDesc->GetAttributeBool(DIRECTIONAL_ATTRIB);
	m_iWidthInc = pDesc->GetAttributeInteger(SCALE_WIDTH_INC_ATTRIB);
	m_iLengthInc = pDesc->GetAttributeInteger(SCALE_LENGTH_INC_ATTRIB);
	m_wColor = ::LoadRGBColor(pDesc->GetAttribute(COLOR_ATTRIB));

	if (pDesc->FindAttribute(OPACITY_ATTRIB, &sAttrib))
		m_byOpacity = strToInt(sAttrib, 255);
	else
		m_byOpacity = 255;

	//	Initialize the points structure

	m_iPointCount = pDesc->GetContentElementCount();
	if (m_iPointCount > 0)
		{
		m_Points = new SPoint [m_iPointCount];
		m_TransBuffer = new SPoint [m_iPointCount];

		for (i = 0; i < m_iPointCount; i++)
			{
			CXMLElement *pPointDesc = pDesc->GetContentElement(i);
			if (!strEquals(pPointDesc->GetTag(), POINT_TAG))
				{
				Ctx.sError = CONSTLIT("<Point> element expected");
				return ERR_FAIL;
				}

			m_Points[i].x = pPointDesc->GetAttributeInteger(X_ATTRIB);
			m_Points[i].y = pPointDesc->GetAttributeInteger(Y_ATTRIB);
			}

		//	Convex polygons (which remain convex no matter the rotation) can be
		//	computed faster with an optimized algorithm, so we keep track here.

		m_bConvexPolygon = IsConvexPolygon(m_iPointCount, m_Points);
		}
	else
		{
		m_Points = NULL;
		m_TransBuffer = NULL;
		m_bConvexPolygon = true;
		}

	return NOERROR;
	}
Пример #6
0
ALERROR CSovereign::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	OnCreateFromXML
//
//	Create from XML

	{
	int i;

	//	Initialize

	m_sName = pDesc->GetAttribute(CONSTLIT(g_NameAttrib));

	//	Alignment

	CString sAlignment = pDesc->GetAttribute(ALIGNMENT_ATTRIB);
	if (strEquals(sAlignment, CONSTRUCTIVE_CHAOS_ALIGN))
		m_iAlignment = alignConstructiveChaos;
	else if (strEquals(sAlignment, CONSTRUCTIVE_ORDER_ALIGN))
		m_iAlignment = alignConstructiveOrder;
	else if (strEquals(sAlignment, NEUTRAL_ALIGN))
		m_iAlignment = alignNeutral;
	else if (strEquals(sAlignment, DESTRUCTIVE_ORDER_ALIGN))
		m_iAlignment = alignDestructiveOrder;
	else if (strEquals(sAlignment, DESTRUCTIVE_CHAOS_ALIGN))
		m_iAlignment = alignDestructiveChaos;
	else
		return ERR_FAIL;

	//	Load language

	CXMLElement *pLanguage = pDesc->GetContentElementByTag(LANGUAGE_TAG);
	if (pLanguage)
		{
		int iCount = pLanguage->GetContentElementCount();
		for (i = 0; i < iCount; i++)
			{
			CXMLElement *pItem = pLanguage->GetContentElement(i);
			CString *pText = new CString(pItem->GetAttribute(TEXT_ATTRIB));
			DWORD dwID = pItem->GetAttributeInteger(ID_ATTRIB);
			if (dwID != 0)
				m_Language.AddEntry(dwID, pText);
			}
		}

	//	Load relationships

	m_pInitialRelationships = pDesc->GetContentElementByTag(RELATIONSHIPS_TAG);
	if (m_pInitialRelationships)
		m_pInitialRelationships = m_pInitialRelationships->OrphanCopy();

	//	Done

	return NOERROR;
	}
Пример #7
0
void CDockingPorts::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement)

//	InitPortsFromXML
//
//	InitPortsFromXML 

	{
	//	See if we've got a special element with docking port geometry

	CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG);
	if (pDockingPorts)
		{
		m_iPortCount = pDockingPorts->GetContentElementCount();
		if (m_iPortCount > 0)
			{
			m_pPort = new DockingPort[m_iPortCount];

			for (int i = 0; i < m_iPortCount; i++)
				{
				CXMLElement *pPort = pDockingPorts->GetContentElement(i);
				CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel),
						(pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel));

				m_pPort[i].iStatus = psEmpty;
				m_pPort[i].pObj = NULL;
				m_pPort[i].vPos = vDockPos;
				m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360;
				}
			}
		}

	//	Otherwise, initialize ports based on a count

	else
		InitPorts(pOwner,
				pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB),
				64 * g_KlicksPerPixel);
	}
Пример #8
0
ALERROR CUniverse::InitSounds (SDesignLoadCtx &Ctx, CXMLElement *pSounds, CResourceDb &Resources)

//	InitSounds
//
//	Loads sound resources

	{
	ALERROR error;

	//	Nothing to do if we don't want sound resources

	if (m_pSoundMgr == NULL || Ctx.bNoResources)
		return NOERROR;

	//	Figure out if we've got a special folder for the resources

	CString sRoot = pSounds->GetAttribute(FOLDER_ATTRIB);

	//	Loop over all sound resources

	for (int i = 0; i < pSounds->GetContentElementCount(); i++)
		{
		CXMLElement *pItem = pSounds->GetContentElement(i);
		DWORD dwUNID = (DWORD)pItem->GetAttributeInteger(UNID_ATTRIB);
		CString sFilename = pItem->GetAttribute(FILENAME_ATTRIB);
		int iChannel;

		//	Load the resource
		if (error = Resources.LoadSound(*m_pSoundMgr, sRoot, sFilename, &iChannel))
			{
			Ctx.sError = strPatternSubst(CONSTLIT("Unable to load sound file: %s"), sFilename);
			return error;
			}

		//	Add to our map

		if (error = m_Sounds.AddEntry((int)dwUNID, (CObject *)iChannel))
			{
			Ctx.sError = strPatternSubst(CONSTLIT("Unable to add sound file: %x"), dwUNID);
			return error;
			}
		}

	return NOERROR;
	}
Пример #9
0
ALERROR CTableOfGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	m_iTotalChance = 0;
	int iCount = pDesc->GetContentElementCount();
	if (iCount > 0)
		{
		m_Table.InsertEmpty(iCount);

		//	Pre-initialize to NULL in case we exit with an error

		for (i = 0; i < iCount; i++)
			m_Table[i].pItem = NULL;

		//	Load

		for (i = 0; i < iCount; i++)
			{
			CXMLElement *pEntry = pDesc->GetContentElement(i);
			
			m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB);
			m_iTotalChance += m_Table[i].iChance;

			CString sCount = pEntry->GetAttribute(COUNT_ATTRIB);
			if (sCount.IsBlank())
				m_Table[i].Count = DiceRange(0, 0, 1);
			else
				m_Table[i].Count.LoadFromXML(sCount);

			if (error = IItemGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pItem))
				return error;
			}
		}

	return NOERROR;
	}
Пример #10
0
ALERROR CGroupOfGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	m_iCount = pDesc->GetContentElementCount();
	if (m_iCount > 0)
		{
		m_Table = new SEntry [m_iCount];
		utlMemSet(m_Table, sizeof(SEntry) * m_iCount, 0);

		for (i = 0; i < m_iCount; i++)
			{
			CXMLElement *pEntry = pDesc->GetContentElement(i);
			
			m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB);
			if (m_Table[i].iChance == 0)
				m_Table[i].iChance = 100;

			CString sCount = pEntry->GetAttribute(COUNT_ATTRIB);
			if (sCount.IsBlank())
				m_Table[i].Count = DiceRange(0, 0, 1);
			else
				m_Table[i].Count.LoadFromXML(sCount);

			if (error = IItemGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pItem))
				return error;
			}
		}
	else
		m_Table = NULL;

	return NOERROR;
	}
Пример #11
0
ALERROR CTableOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB));
	if (m_Count.IsEmpty())
		m_Count.SetConstant(1);

	m_iTotalChance = 0;
	int iCount = pDesc->GetContentElementCount();
	if (iCount > 0)
		{
		m_Table.InsertEmpty(iCount);
		for (i = 0; i < iCount; i++)
			{
			CXMLElement *pEntry = pDesc->GetContentElement(i);
			
			m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB);
			m_iTotalChance += m_Table[i].iChance;

			if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pDevice))
				{
				m_Table[i].pDevice = NULL;
				return error;
				}
			}
		}

	return NOERROR;
	}
Пример #12
0
ALERROR CAdventureDesc::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	OnCreateFromXML
//
//	Load from XML

{
    ALERROR error;
    int i;

    //	If we are part of the default resource, then get the adventure UNID

    if (Ctx.pExtension == NULL)
    {
        m_dwExtensionUNID = pDesc->GetAttributeInteger(ADVENTURE_UNID_ATTRIB);
        m_fInDefaultResource = true;
    }

    //	Otherwise, we remember the extension that we were loaded from

    else
    {
        m_dwExtensionUNID = Ctx.pExtension->GetUNID();
        m_fInDefaultResource = false;
    }

    //	Load the name, etc

    m_sName = pDesc->GetAttribute(NAME_ATTRIB);
    if (error = ::LoadUNID(Ctx, pDesc->GetAttribute(BACKGROUND_ID_ATTRIB), &m_dwBackgroundUNID))
        return error;

    //	Starting ship criteria

    CString sCriteria;
    if (!pDesc->FindAttribute(STARTING_SHIP_CRITERIA_ATTRIB, &sCriteria))
        sCriteria = CONSTLIT("*");

    if (error = CDesignTypeCriteria::ParseCriteria(sCriteria, &m_StartingShips))
        return ComposeLoadError(Ctx, ERR_STARTING_SHIP_CRITERIA);

    m_fIncludeOldShipClasses = pDesc->GetAttributeBool(INCLUDE_10_STARTING_CLASSES_ATTRIB);

    //	Starting position

    m_sStartingNodeID = pDesc->GetAttribute(STARTING_SYSTEM_ATTRIB);
    m_sStartingPos = pDesc->GetAttribute(STARTING_POS_ATTRIB);

    //	Welcome message

    if (!pDesc->FindAttribute(WELCOME_MESSAGE_ATTRIB, &m_sWelcomeMessage))
        m_sWelcomeMessage = CONSTLIT("Welcome to Transcendence!");

    //	Init some flags

    m_fIsCurrentAdventure = false;

    //	If we don't have a name, then get it from the extension

    if (m_sName.IsBlank())
    {
        if (Ctx.pExtension)
            m_sName = Ctx.pExtension->GetName();
    }

    //	Otherwise, if the extension doesn't have a name, then we can set it

    else if (Ctx.pExtension && strFind(Ctx.pExtension->GetName(), CONSTLIT("Extension")) == 0)
        Ctx.pExtension->SetName(m_sName);

    //	Initialize armor and shield damage adjustment tables

    InitDefaultDamageAdj();
    for (i = 1; i <= MAX_ITEM_LEVEL; i++)
    {
        m_ArmorDamageAdj[i - 1] = g_ArmorDamageAdj[i - 1];
        m_ShieldDamageAdj[i - 1] = g_ShieldDamageAdj[i - 1];
    }

    //	Load constants

    CXMLElement *pConstants = pDesc->GetContentElementByTag(CONSTANTS_TAG);
    if (pConstants)
    {
        for (i = 0; i < pConstants->GetContentElementCount(); i++)
        {
            CXMLElement *pItem = pConstants->GetContentElement(i);

            if (strEquals(pItem->GetTag(), ARMOR_DAMAGE_ADJ_TAG))
            {
                int iLevel = pItem->GetAttributeInteger(LEVEL_ATTRIB);
                if (iLevel < 1 || iLevel > MAX_ITEM_LEVEL)
                {
                    Ctx.sError = strPatternSubst(CONSTLIT("Invalid level: %d."), iLevel);
                    return ERR_FAIL;
                }

                if (error = m_ArmorDamageAdj[iLevel - 1].InitFromXML(Ctx, pItem, true))
                    return error;
            }
            else if (strEquals(pItem->GetTag(), SHIELD_DAMAGE_ADJ_TAG))
            {
                int iLevel = pItem->GetAttributeInteger(LEVEL_ATTRIB);
                if (iLevel < 1 || iLevel > MAX_ITEM_LEVEL)
                {
                    Ctx.sError = strPatternSubst(CONSTLIT("Invalid level: %d."), iLevel);
                    return ERR_FAIL;
                }

                if (error = m_ShieldDamageAdj[iLevel - 1].InitFromXML(Ctx, pItem, true))
                    return error;
            }
            else
            {
                Ctx.sError = strPatternSubst(CONSTLIT("Invalid constant definition element: %s."), pItem->GetTag());
                return ERR_FAIL;
            }
        }
    }

    return NOERROR;
}
Пример #13
0
ALERROR CNPUniverse::LoadClassesFromXML (CXMLElement *pTemplate, CSymbolTable &Symbols, CString *retsError)

//	LoadClassesFromXML
//
//	Loads the unit classes, etc.

	{
	ALERROR error;
	CXMLElement *pSection;
	CXMLElement *pTable;
	int i;

	//	Load universal constants section

	if ((pSection = FindElement(pTemplate, CONSTLIT("DefineUniversalConstants"), retsError)) == NULL)
		return ERR_FAIL;

	//	Load tech table

	if ((pTable = FindElement(pSection, CONSTLIT("TechTable"), retsError)) == NULL)
		return ERR_FAIL;

	if (pTable->GetContentElementCount() != techCount - 1)
		{
		*retsError = CONSTLIT("<TechTable> does not have the right number of entries.");
		return ERR_FAIL;
		}

	for (i = 0; i < pTable->GetContentElementCount(); i++)
		{
		CXMLElement *pEntry = pTable->GetContentElement(i);
		m_TechTable[i + 1].sName = pEntry->GetAttribute(CONSTLIT("Name"));
		m_TechTable[i + 1].iProductionFactor = pEntry->GetAttributeInteger(CONSTLIT("Prod"));
		m_TechTable[i + 1].iStdLivingFactor = pEntry->GetAttributeInteger(CONSTLIT("StdLiving"));
		m_TechTable[i + 1].iPollutionFactor = pEntry->GetAttributeInteger(CONSTLIT("Pollution"));
		}

	//	Load unit classes

	if ((pSection = FindElement(pTemplate, CONSTLIT("DefineUnitClasses"), retsError)) == NULL)
		return ERR_FAIL;

	for (i = 0; i < pSection->GetContentElementCount(); i++)
		{
		CNPUnitClass *pClass = new CNPUnitClass;
		if (pClass == NULL)
			{
			*retsError = CONSTLIT("Out of memory");
			return ERR_FAIL;
			}

		if (error = pClass->LoadFromXML(pSection->GetContentElement(i), Symbols))
			{
			*retsError = CONSTLIT("Unable to load unit class");
			return ERR_FAIL;
			}

		m_UnitClasses.SetEntry(pClass->GetUNID(), pClass);
		}

	//	Get the default sovereign

	if ((pSection = FindElement(pTemplate, CONSTLIT("DefineSovereigns"), retsError)) == NULL)
		return ERR_FAIL;

	if ((m_pPlayerTemplate = FindElement(pSection, CONSTLIT("DefineDefaultPlayer"), retsError)) == NULL)
		return ERR_FAIL;

	m_pPlayerTemplate = m_pPlayerTemplate->OrphanCopy();

	return NOERROR;
	}
Пример #14
0
ALERROR CNPUniverse::InitFromXML (CXMLElement *pTemplate, CSymbolTable &Symbols, CString *retsError)

//	InitFromXML
//
//	Initializes from an XML template

	{
	ALERROR error;
	int iNullPoints;
	CreateCtx Ctx;

	//	Initialize context

	Ctx.pTemplate = pTemplate;
	Ctx.pSymbols = &Symbols;

	//	Get some elements

	CXMLElement *pOptions = pTemplate->GetContentElementByTag(CONSTLIT("DefineOptions"));

	//	Initialize

	iNullPoints = pOptions->GetAttributeInteger(CONSTLIT("NullPointCount"));
	m_iMaxPlayers = pOptions->GetAttributeInteger(CONSTLIT("MaxPlayers"));
	m_iBaseAnno = pOptions->GetAttributeInteger(CONSTLIT("BaseAnno"));
	m_iTurn = 0;

	//	Create the Overlord sovereign

	CNPSovereign *pOverlord;
	if (error = CreateSovereign(LITERAL("The Universe"), NULL, NULL, &pOverlord))
		{
		*retsError = LITERAL("Unable to create Overlord sovereign");
		return error;
		}

	m_dwOverlord = pOverlord->GetUNID();

	//	Create the default, independent sovereign

	CNPSovereign *pIndep;
	if (error = CreateSovereign(LITERAL("Independent"), NULL, NULL, &pIndep))
		{
		*retsError = LITERAL("Unable to create independent sovereign");
		return error;
		}

	m_dwIndependent = pIndep->GetUNID();

	//	Create the null point network.

	if (error = CreateNullPointNetwork(iNullPoints))
		{
		*retsError = LITERAL("Unable to create null point network");
		return error;
		}

	//	Create worlds

	if (error = CreateRandomWorlds(Ctx))
		{
		*retsError = LITERAL("Unable to create worlds");
		return error;
		}

	//	Next turn

	m_dwNextTurn = ::GetTickCount() + (DWORD)(SecondsPerOro * 1000);

	return NOERROR;
	}
Пример #15
0
ALERROR CNPUniverse::CreateSovereign (CString sName, CNPWorld *pCapital, CXMLElement *pSovTemplate, CNPSovereign **retpSovereign)

//	CreateSovereign
//
//	Creates a new sovereign

	{
	ALERROR error;
	CNPSovereign *pSovereign;
	int i;

	if (error = CNPSovereign::Create(m_Sovereigns.RegisterEntry(), sName, &pSovereign))
		return error;

	m_Sovereigns.SetEntry(pSovereign->GetUNID(), pSovereign);

	//	Obviously we know about ourselves

	pSovereign->SetKnowledge(pSovereign->GetUNID());

	//	Set a capital

	if (pCapital)
		{
		pSovereign->SetCapital(pCapital->GetUNID());

		pCapital->RemoveTrait(traitReservedCapital);
		pCapital->SetTrait(traitCapital);
		pCapital->SetSovereign(pSovereign);

		//	Scan the capital

		CNPNullPoint *pNP = GetNullPoint(pCapital->GetLocation());
		ScanNullPoint(pSovereign, pNP, 1);

		//	Scan the area around the capital

		for (i = 0; i < pNP->GetLinkCount(); i++)
			{
			CNPNullPoint *pDest = pNP->GetLinkDest(i);
			ScanNullPoint(pSovereign, pDest, 1);
			}

		//	Create ships

		if (pSovTemplate)
			{
			CNPFleet *pFleet = NULL;
			for (i = 0; i < pSovTemplate->GetContentElementCount(); i++)
				{
				CXMLElement *pShip = pSovTemplate->GetContentElement(i);
				if (strCompare(pShip->GetTag(), CONSTLIT("Unit")) == 0)
					{
					//	Create the fleet, if needed

					if (pFleet == NULL)
						if (error = CreateFleet(pNP, pSovereign, &pFleet))
							return error;

					//	Add the ship to the fleet

					CNPUnit Unit(pShip->GetAttributeInteger(CONSTLIT("Class")), 0, 0);
					pFleet->GetAssetList().AddUnit(Unit);
					}
				}
			}
		}

	//	Done

	if (retpSovereign)
		*retpSovereign = pSovereign;

	return NOERROR;
	}
Пример #16
0
ALERROR CNPUniverse::CreateRandomWorld (CreateCtx &Ctx, CNPNullPoint *pNP, CNPSovereign *pSovereign)

//	CreateRandomWorld
//
//	Creates a random world at the given null point

	{
	ALERROR error;
	CNPWorld *pWorld;
	int i, iRoll;

	//	Create the world

	if (error = CNPWorld::Create(m_Worlds.RegisterEntry(),
			pNP->GetUNID(),
			pSovereign,
			&pWorld))
		{
		Ctx.sError = LITERAL("Out of memory");
		return error;
		}

	m_Worlds.SetEntry(pWorld->GetUNID(), pWorld);

	//	Add to the null point

	if (error = pNP->AddWorld(pWorld))
		{
		delete pWorld;
		Ctx.sError = LITERAL("Out of memory");
		return error;
		}

	//	Add some traits to the world

	CXMLElement *pTraitTable = Ctx.pTemplate->GetContentElementByTag(CONSTLIT("DefineWorldTraitTable"));

	if (mathRandom(1, 100) <= 20)
		AddRandomTrait(Ctx, pWorld, ttMajorDisadvantage, 1, pTraitTable);

	if (mathRandom(1, 100) <= 35)
		AddRandomTrait(Ctx, pWorld, ttMinorDisadvantage, 1, pTraitTable);

	if (mathRandom(1, 100) <= 20)
		AddRandomTrait(Ctx, pWorld, ttMinorDisadvantage, 1, pTraitTable);


	if (mathRandom(1, 100) <= 20)
		AddRandomTrait(Ctx, pWorld, ttMajorAdvantage, 1, pTraitTable);

	if (mathRandom(1, 100) <= 35)
		AddRandomTrait(Ctx, pWorld, ttMinorAdvantage, 1, pTraitTable);

	if (mathRandom(1, 100) <= 20)
		AddRandomTrait(Ctx, pWorld, ttMinorAdvantage, 1, pTraitTable);

	//	Pick a tech level for the world

	CXMLElement *pTechTable = Ctx.pTemplate->GetContentElementByTag(CONSTLIT("DefineWorldTechLevelTable"));
	iRoll = mathRandom(1, 100);
	NPTechLevels iMinTech = pWorld->GetMinTech();
	for (i = 0; i < pTechTable->GetContentElementCount(); i++)
		{
		CXMLElement *pEntry = pTechTable->GetContentElement(i);
		int iProb = pEntry->GetAttributeInteger(CONSTLIT("Prob"));
		if (iRoll <= iProb)
			{
			NPTechLevels iTechLevel = (NPTechLevels)GetSymbolicAttribute(*Ctx.pSymbols, pEntry, CONSTLIT("Tech"));

			//	Make sure this tech level is appropriate for the world

			while (iTechLevel < iMinTech)
				iTechLevel = (NPTechLevels)(iTechLevel + 1);

			pWorld->SetTechLevel(iTechLevel);
			break;
			}

		iRoll -= iProb;
		}

	//	Set the initial population

	int iPopulation = 0;
	switch (pWorld->GetTechLevel())
		{
		case techAgricultural:
			iPopulation = mathRandom(250, 750);
			break;

		case techSteam:
		case techCyber:
			{
			for (i = 0; i < 12; i++)
				iPopulation += mathRandom(0, 500);
			break;
			}

		case techBio:
			{
			for (i = 0; i < 10; i++)
				iPopulation += mathRandom(0, 1000);
			break;
			}

		case techFusion:
		case techAI:
			{
			for (i = 0; i < 8; i++)
				iPopulation += mathRandom(0, 1600);
			break;
			}

		default:
			{
			for (i = 0; i < 10; i++)
				iPopulation += mathRandom(0, 2000);
			break;
			}
		}

	pWorld->SetPopulation((1000 * iPopulation) + mathRandom(-500, 500));

	//	Set the efficiency

	pWorld->SetEfficiency(25 + mathRandom(0, 25) 
			+ mathRandom(0, 25) 
			+ mathRandom(0, 25));

	//	Figure out if this world is a suitable candidate for a capital

	if (IsWorldSuitableAsCapital(this, pWorld))
		pWorld->SetTrait(traitReservedCapital);

	return NOERROR;
	}
Пример #17
0
ALERROR CPlayerSettings::InitFromXML (SDesignLoadCtx &Ctx, CShipClass *pClass, CXMLElement *pDesc)

//	InitFromXML
//
//	Initialize from an XML element

	{
	ALERROR error;
	int i;

	m_sDesc = pDesc->GetAttribute(DESC_ATTRIB);
	if (error = LoadUNID(Ctx, pDesc->GetAttribute(LARGE_IMAGE_ATTRIB), &m_dwLargeImage))
		return error;

	m_fDebug = pDesc->GetAttributeBool(DEBUG_ONLY_ATTRIB);
	CString sInitialClass = pDesc->GetAttribute(INITIAL_CLASS_ATTRIB);
	if (strEquals(sInitialClass, CONSTLIT("always")))
		{
		m_fInitialClass = true;
		m_fIncludeInAllAdventures = true;
		}
	else
		{
		m_fInitialClass = CXMLElement::IsBoolTrueValue(sInitialClass);
		m_fIncludeInAllAdventures = false;
		}

	m_fHasArmorDesc = false;
	m_fHasReactorDesc = false;
	m_fHasShieldDesc = false;

	//	Some ship capabilities

	bool bValue;
	if (pDesc->FindAttributeBool(AUTOPILOT_ATTRIB, &bValue))
		m_fAutopilot = bValue;
	else
		m_fAutopilot = true;

	//	Load some miscellaneous data

	CString sAttrib;
	if (!pDesc->FindAttribute(STARTING_CREDITS_ATTRIB, &sAttrib))
		sAttrib = CONSTLIT("5d20+200");

	if (error = m_StartingCredits.InitFromXML(Ctx, sAttrib))
		return error;

	m_sStartNode = pDesc->GetAttribute(STARTING_SYSTEM_ATTRIB);
	m_sStartPos = pDesc->GetAttribute(STARTING_POS_ATTRIB);
	if (m_sStartPos.IsBlank())
		m_sStartPos = CONSTLIT("Start");

	//	Load the ship screen

	CString sShipScreenUNID = pDesc->GetAttribute(SHIP_SCREEN_ATTRIB);
	if (sShipScreenUNID.IsBlank())
		sShipScreenUNID = strFromInt(DEFAULT_SHIP_SCREEN_UNID, false);
	m_pShipScreen.LoadUNID(Ctx, sShipScreenUNID);

	//	Load the armor display data

	CXMLElement *pArmorDisplay = pDesc->GetContentElementByTag(ARMOR_DISPLAY_TAG);
	if (pArmorDisplay && pArmorDisplay->GetContentElementCount() > 0)
		{
		//	Loop over all sub elements

		for (i = 0; i < pArmorDisplay->GetContentElementCount(); i++)
			{
			CXMLElement *pSub = pArmorDisplay->GetContentElement(i);

			if (strEquals(pSub->GetTag(), ARMOR_SECTION_TAG))
				{
				SArmorSegmentImageDesc &ArmorDesc = *m_ArmorDesc.Segments.Insert();

				if (error = ArmorDesc.Image.InitFromXML(Ctx, pSub))
					return ComposeLoadError(Ctx, ERR_ARMOR_DISPLAY_NEEDED);

				ArmorDesc.sName = pSub->GetAttribute(NAME_ATTRIB);
				ArmorDesc.xDest = pSub->GetAttributeInteger(DEST_X_ATTRIB);
				ArmorDesc.yDest = pSub->GetAttributeInteger(DEST_Y_ATTRIB);
				ArmorDesc.xHP = pSub->GetAttributeInteger(HP_X_ATTRIB);
				ArmorDesc.yHP = pSub->GetAttributeInteger(HP_Y_ATTRIB);
				ArmorDesc.yName = pSub->GetAttributeInteger(NAME_Y_ATTRIB);
				ArmorDesc.cxNameBreak = pSub->GetAttributeInteger(NAME_BREAK_WIDTH);
				ArmorDesc.xNameDestOffset = pSub->GetAttributeInteger(NAME_DEST_X_ATTRIB);
				ArmorDesc.yNameDestOffset = pSub->GetAttributeInteger(NAME_DEST_Y_ATTRIB);
				}
			else if (strEquals(pSub->GetTag(), SHIP_IMAGE_TAG))
				{
				if (error = m_ArmorDesc.ShipImage.InitFromXML(Ctx, pSub))
					return ComposeLoadError(Ctx, ERR_SHIP_IMAGE_NEEDED);
				}
			else
				return ComposeLoadError(Ctx, strPatternSubst(CONSTLIT("Unknown ArmorDisplay element: "), pSub->GetTag()));
			}

		m_fHasArmorDesc = true;
		}
	else
		m_fHasArmorDesc = false;

	//	Load shield display data

	CXMLElement *pShieldDisplay = pDesc->GetContentElementByTag(SHIELD_DISPLAY_TAG);
	if (pShieldDisplay)
		{
		//	Load the new shield effect

		if (error = m_ShieldDesc.pShieldEffect.LoadEffect(Ctx,
				strPatternSubst(CONSTLIT("%d:p:s"), pClass->GetUNID()),
				pShieldDisplay->GetContentElementByTag(SHIELD_EFFECT_TAG),
				pShieldDisplay->GetAttribute(SHIELD_EFFECT_ATTRIB)))
			return error;

		//	If we don't have the new effect, load the backwards compatibility
		//	image.

		if (m_ShieldDesc.pShieldEffect.IsEmpty())
			{
			if (error = m_ShieldDesc.Image.InitFromXML(Ctx, 
					pShieldDisplay->GetContentElementByTag(IMAGE_TAG)))
				return ComposeLoadError(Ctx, ERR_SHIELD_DISPLAY_NEEDED);
			}

		m_fHasShieldDesc = true;
		}

	//	If we have a shield effect then we must have an armor image

	if (!m_ShieldDesc.pShieldEffect.IsEmpty() && m_ArmorDesc.ShipImage.GetBitmapUNID() == 0)
		return ComposeLoadError(Ctx, ERR_MUST_HAVE_SHIP_IMAGE);

	//	Load reactor display data

	CXMLElement *pReactorDisplay = pDesc->GetContentElementByTag(REACTOR_DISPLAY_TAG);
	if (pReactorDisplay)
		{
		if (error = m_ReactorDesc.ReactorImage.InitFromXML(Ctx,
				pReactorDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		CXMLElement *pImage = pReactorDisplay->GetContentElementByTag(POWER_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.PowerLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xPowerLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yPowerLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xFuelLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yFuelLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LOW_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLowLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(REACTOR_TEXT_TAG),
				&m_ReactorDesc.rcReactorText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(POWER_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcPowerLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcFuelLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_fHasReactorDesc = true;
		}

	//	Done

	return NOERROR;
	}
Пример #18
0
ALERROR CNPUniverse::AddRandomTrait (CreateCtx &Ctx,
									 CNPWorld *pWorld, 
									 TraitType iSection, 
									 int iCount, 
									 CXMLElement *pTraitTable)

//	AddRandomTrait
//
//	Adds traits from the given table

	{
	int iSectionStart;

	//	Find the beginning of the section

	iSectionStart = 0;
	while (iSectionStart < pTraitTable->GetContentElementCount()
			&& pTraitTable->GetContentElement(iSectionStart)->GetAttributeInteger(CONSTLIT("Table")) != iSection)
		iSectionStart++;

	//	If not found, we fail

	if (iSectionStart == pTraitTable->GetContentElementCount())
		{
		Ctx.sError = LITERAL("Unable to find appropriate trait table section");
		return ERR_FAIL;
		}

	//	Keep looping for each trait to add

	for (int i = 0; i < iCount; i++)
		{
		//	Keep trying until we successfully add some trait or
		//	until we get sick of trying.

		for (int j = 0; j < 10; j++)
			{
			int iRoll = mathRandom(1, 100);
			int iEntry = iSectionStart;
			CXMLElement *pEntry;

			while (iEntry < pTraitTable->GetContentElementCount()
					&& (pEntry = pTraitTable->GetContentElement(iEntry))
					&& (pEntry->GetAttributeInteger(CONSTLIT("Table")) == iSection)
					&& (iRoll = iRoll - pEntry->GetAttributeInteger(CONSTLIT("Prob"))) > 0)
				iEntry++;

			//	If not found, we fail

			if (iRoll > 0)
				{
				Ctx.sError = LITERAL("Trait table probabilities do not add up to 100%");
				return ERR_FAIL;
				}

			//	Add the trait. If we add it successfully then we stop
			//	the inner loop.

			if (AddTrait(pWorld, pTraitTable, pEntry, *Ctx.pSymbols))
				break;
			}
		}

	return NOERROR;
	}
Пример #19
0
void InitStationTypeImage (SEntryDesc &Entry, CStationType *pStationType)
	{
	struct SSatImageDesc
		{
		const CObjectImageArray *pImage;
		CCompositeImageSelector Selector;
		int xOffset;
		int yOffset;
		};

	int i;

	SSelectorInitCtx InitCtx;
	pStationType->SetImageSelector(InitCtx, &Entry.Selector);
	const CObjectImageArray *pMainImage = &pStationType->GetImage(Entry.Selector, CCompositeImageModifiers());

	//	If we have no satellites, then we can just return the single station 
	//	image.

	CXMLElement *pSatellites = pStationType->GetSatellitesDesc();
	if (pSatellites == NULL)
		{
		Entry.pImage = pMainImage;
		return;
		}

	//	Figure out the extents of the image

	RECT rcMainImage = pMainImage->GetImageRect();
	RECT rcBounds;
	rcBounds.left = -(RectWidth(rcMainImage) / 2);
	rcBounds.top = -(RectHeight(rcMainImage) / 2);
	rcBounds.right = rcBounds.left + RectWidth(rcMainImage);
	rcBounds.bottom = rcBounds.top + RectHeight(rcMainImage);

	//	Loop over all satellites and get metrics

	TArray<SSatImageDesc> SatImages;
	for (i = 0; i < pSatellites->GetContentElementCount(); i++)
		{
		CXMLElement *pSatDesc = pSatellites->GetContentElement(i);
		if (!pSatDesc->FindAttribute(SEGMENT_ATTRIB)
				|| !strEquals(STATION_TAG, pSatDesc->GetTag()))
			continue;

		//	Get the type of the satellite

		CStationType *pSatType = g_pUniverse->FindStationType(pSatDesc->GetAttributeInteger(TYPE_ATTRIB));
		if (pSatType == NULL)
			continue;

		//	Prepare the image for the satellite

		SSatImageDesc *pSatImage = SatImages.Insert();
		pSatType->SetImageSelector(InitCtx, &pSatImage->Selector);

		//	If we have an image variant, then set it

		int iVariant;
		if (pSatDesc->FindAttributeInteger(IMAGE_VARIANT_ATTRIB, &iVariant))
			{
			IImageEntry *pRoot = pSatType->GetImage().GetRoot();
			DWORD dwID = (pRoot ? pRoot->GetID() : DEFAULT_SELECTOR_ID);

			pSatImage->Selector.DeleteAll();
			pSatImage->Selector.AddVariant(dwID, iVariant);
			}

		pSatImage->pImage = &pSatType->GetImage(pSatImage->Selector, CCompositeImageModifiers());

		//	Now get the offset

		pSatImage->xOffset = pSatDesc->GetAttributeInteger(X_OFFSET_ATTRIB);
		pSatImage->yOffset = pSatDesc->GetAttributeInteger(Y_OFFSET_ATTRIB);

		//	Compute the satellite rect

		RECT rcSatImage = pSatImage->pImage->GetImageRect();
		RECT rcSatBounds;
		rcSatBounds.left = pSatImage->xOffset - (RectWidth(rcSatImage) / 2);
		rcSatBounds.top = -pSatImage->yOffset - (RectHeight(rcSatImage) / 2);
		rcSatBounds.right = rcSatBounds.left + RectWidth(rcSatImage);
		rcSatBounds.bottom = rcSatBounds.top + RectHeight(rcSatImage);

		//	Increase the size of the bounds

		rcBounds.left = Min(rcBounds.left, rcSatBounds.left);
		rcBounds.right = Max(rcBounds.right, rcSatBounds.right);
		rcBounds.top = Min(rcBounds.top, rcSatBounds.top);
		rcBounds.bottom = Max(rcBounds.bottom, rcSatBounds.bottom);
		}

	//	If no segments, then we just return the basic image

	if (SatImages.GetCount() == 0)
		{
		Entry.pImage = pMainImage;
		return;
		}

	//	Create an image that will hold the composite

	CG32bitImage *pCompositeImage = new CG32bitImage;
	pCompositeImage->Create(RectWidth(rcBounds), RectHeight(rcBounds), CG32bitImage::alpha8, CG32bitPixel::Null());
	int xCenter = -rcBounds.left;
	int yCenter = -rcBounds.top;

	//	Paint the main image

	pMainImage->PaintImage(*pCompositeImage, xCenter, yCenter, 0, Entry.iRotation, true);

	//	Paint all the satellites

	for (i = 0; i < SatImages.GetCount(); i++)
		SatImages[i].pImage->PaintImage(*pCompositeImage, xCenter + SatImages[i].xOffset, yCenter - SatImages[i].yOffset, 0, 0, true);

	//	Now create the proper image array

	RECT rcResult;
	rcResult.left = 0;
	rcResult.top = 0;
	rcResult.right = RectWidth(rcBounds);
	rcResult.bottom = RectHeight(rcBounds);

    int xOffset = (RectWidth(rcBounds) / 2) - xCenter;
    int yOffset = (RectHeight(rcBounds) / 2) - yCenter;

	Entry.pCompositeImageArray = new CObjectImageArray;
	Entry.pCompositeImageArray->Init(pCompositeImage, rcResult, 0, 0, true, xOffset, yOffset);

	//	Done

	Entry.pImage = Entry.pCompositeImageArray;
	}
Пример #20
0
void CDockingPorts::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement)

//	InitPortsFromXML
//
//	InitPortsFromXML 

	{
	int i;

	//	See if we've got a special element with docking port geometry

	CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG);
	if (pDockingPorts)
		{
		//	Initialize max dist
		//	NOTE: pOwner can be NULL because sometimes we init ports from a ship class 
		//	(without an object).

		int iDefaultDist = Max(DEFAULT_DOCK_DISTANCE_LS, (pOwner ? 8 + (int)((pOwner->GetBoundsRadius() / LIGHT_SECOND) + 0.5) : 0));
		m_iMaxDist = pDockingPorts->GetAttributeIntegerBounded(MAX_DIST_ATTRIB, 1, -1, iDefaultDist);


		//	If we have sub-elements then these are port definitions.

		m_iPortCount = pDockingPorts->GetContentElementCount();
		if (m_iPortCount > 0)
			{
			m_pPort = new SDockingPort[m_iPortCount];

			for (i = 0; i < m_iPortCount; i++)
				{
				CXMLElement *pPort = pDockingPorts->GetContentElement(i);
				CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel),
						(pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel));

				m_pPort[i].vPos = vDockPos;

				if (pPort->FindAttributeInteger(ROTATION_ATTRIB, &m_pPort[i].iRotation))
					m_pPort[i].iRotation = (m_pPort[i].iRotation % 360);
				else
					m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360;

				if (pPort->GetAttributeBool(BRING_TO_FRONT_ATTRIB))
					m_pPort[i].iLayer = plBringToFront;
				else if (pPort->GetAttributeBool(SEND_TO_BACK_ATTRIB))
					m_pPort[i].iLayer = plSendToBack;
				}
			}

		//	Otherwise, we expect a port count and radius

		else if ((m_iPortCount = pDockingPorts->GetAttributeIntegerBounded(PORT_COUNT_ATTRIB, 0, -1, 0)) > 0)
			{
			int iRadius = pDockingPorts->GetAttributeIntegerBounded(PORT_RADIUS_ATTRIB, 0, -1, DEFAULT_PORT_POS_RADIUS);
			int iAngle = 360 / m_iPortCount;

			//	We need the image scale to adjust coordinates

			int iScale = (pOwner ? pOwner->GetImage().GetImageViewportSize() : 512);

			//	Initialize ports

			m_pPort = new SDockingPort[m_iPortCount];
			for (i = 0; i < m_iPortCount; i++)
				{
				C3DConversion::CalcCoord(iScale, i * iAngle, iRadius, 0, &m_pPort[i].vPos);
				m_pPort[i].iRotation = ((i * iAngle) + 180) % 360;
				}
			}

		//	Otherwise, no ports

		else
			{
			m_iPortCount = 0;
			m_pPort = NULL;
			}
		}

	//	Otherwise, initialize ports based on a count

	else
		InitPorts(pOwner,
				pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB),
				64 * g_KlicksPerPixel);
	}
Пример #21
0
void CDockingPorts::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement)

//	InitPortsFromXML
//
//	InitPortsFromXML 

	{
	int i;

	//	See if we've got a special element with docking port geometry

	CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG);
	if (pDockingPorts)
		{
		//	Initialize max dist

		m_iMaxDist = pDockingPorts->GetAttributeIntegerBounded(MAX_DIST_ATTRIB, 1, -1, DEFAULT_DOCK_DISTANCE_LS);

		//	If we have sub-elements then these are port definitions.

		m_iPortCount = pDockingPorts->GetContentElementCount();
		if (m_iPortCount > 0)
			{
			m_pPort = new DockingPort[m_iPortCount];

			for (i = 0; i < m_iPortCount; i++)
				{
				CXMLElement *pPort = pDockingPorts->GetContentElement(i);
				CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel),
						(pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel));

				m_pPort[i].iStatus = psEmpty;
				m_pPort[i].pObj = NULL;
				m_pPort[i].vPos = vDockPos;

				if (pPort->FindAttributeInteger(ROTATION_ATTRIB, &m_pPort[i].iRotation))
					m_pPort[i].iRotation = (m_pPort[i].iRotation % 360);
				else
					m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360;
				}
			}

		//	Otherwise, we expect a port count and radius

		else if ((m_iPortCount = pDockingPorts->GetAttributeIntegerBounded(PORT_COUNT_ATTRIB, 0, -1, 0)) > 0)
			{
			m_pPort = new DockingPort[m_iPortCount];
			
			int iRadius = pDockingPorts->GetAttributeIntegerBounded(PORT_RADIUS_ATTRIB, 0, -1, DEFAULT_PORT_POS_RADIUS);
			Metric rRadius = g_KlicksPerPixel * iRadius;

			int iAngle = 360 / m_iPortCount;
			for (i = 0; i < m_iPortCount; i++)
				{
				m_pPort[i].iStatus = psEmpty;
				m_pPort[i].pObj = NULL;
				m_pPort[i].vPos = PolarToVector(i * iAngle, rRadius);
				m_pPort[i].iRotation = ((i * iAngle) + 180) % 360;
				}
			}

		//	Otherwise, no ports

		else
			{
			m_iPortCount = 0;
			m_pPort = NULL;
			}
		}

	//	Otherwise, initialize ports based on a count

	else
		InitPorts(pOwner,
				pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB),
				64 * g_KlicksPerPixel);
	}
Пример #22
0
ALERROR CPlayerSettings::InitFromXML (SDesignLoadCtx &Ctx, CShipClass *pClass, CXMLElement *pDesc)

//	InitFromXML
//
//	Initialize from an XML element

	{
	ALERROR error;
	int i;

	m_sDesc = pDesc->GetAttribute(DESC_ATTRIB);
	m_dwLargeImage = LoadUNID(Ctx, pDesc->GetAttribute(LARGE_IMAGE_ATTRIB));
	m_fDebug = pDesc->GetAttributeBool(DEBUG_ONLY_ATTRIB);
	m_fInitialClass = pDesc->GetAttributeBool(INITIAL_CLASS_ATTRIB);

	m_fHasArmorDesc = false;
	m_fHasReactorDesc = false;
	m_fHasShieldDesc = false;

	//	Some ship capabilities

	bool bValue;
	if (pDesc->FindAttributeBool(AUTOPILOT_ATTRIB, &bValue))
		m_fAutopilot = bValue;
	else
		m_fAutopilot = true;

	//	Load some miscellaneous data

	CString sAttrib;
	if (!pDesc->FindAttribute(STARTING_CREDITS_ATTRIB, &sAttrib))
		sAttrib = CONSTLIT("5d20+200");

	if (error = m_StartingCredits.InitFromXML(Ctx, sAttrib))
		return error;

	m_sStartNode = pDesc->GetAttribute(STARTING_SYSTEM_ATTRIB);
	m_sStartPos = pDesc->GetAttribute(STARTING_POS_ATTRIB);
	if (m_sStartPos.IsBlank())
		m_sStartPos = CONSTLIT("Start");

	//	Load the ship screen

	CString sShipScreenUNID = pDesc->GetAttribute(SHIP_SCREEN_ATTRIB);
	if (sShipScreenUNID.IsBlank())
		sShipScreenUNID = strFromInt(DEFAULT_SHIP_SCREEN_UNID, FALSE);
	m_pShipScreen.LoadUNID(Ctx, sShipScreenUNID);

	//	Load the armor display data

	CXMLElement *pArmorDisplay = pDesc->GetContentElementByTag(ARMOR_DISPLAY_TAG);
	if (pArmorDisplay && pArmorDisplay->GetContentElementCount() > 0)
		{
		m_iArmorDescCount = pArmorDisplay->GetContentElementCount();
		m_pArmorDesc = new SArmorImageDesc [m_iArmorDescCount];
		for (i = 0; i < m_iArmorDescCount; i++)
			{
			SArmorImageDesc &ArmorDesc = m_pArmorDesc[i];
			CXMLElement *pSegment = pArmorDisplay->GetContentElement(i);

			if (error = ArmorDesc.Image.InitFromXML(Ctx, pSegment))
				return ComposeLoadError(Ctx, ERR_ARMOR_DISPLAY_NEEDED);

			ArmorDesc.sName = pSegment->GetAttribute(NAME_ATTRIB);
			ArmorDesc.xDest = pSegment->GetAttributeInteger(DEST_X_ATTRIB);
			ArmorDesc.yDest = pSegment->GetAttributeInteger(DEST_Y_ATTRIB);
			ArmorDesc.xHP = pSegment->GetAttributeInteger(HP_X_ATTRIB);
			ArmorDesc.yHP = pSegment->GetAttributeInteger(HP_Y_ATTRIB);
			ArmorDesc.yName = pSegment->GetAttributeInteger(NAME_Y_ATTRIB);
			ArmorDesc.cxNameBreak = pSegment->GetAttributeInteger(NAME_BREAK_WIDTH);
			ArmorDesc.xNameDestOffset = pSegment->GetAttributeInteger(NAME_DEST_X_ATTRIB);
			ArmorDesc.yNameDestOffset = pSegment->GetAttributeInteger(NAME_DEST_Y_ATTRIB);
			}

		m_fHasArmorDesc = true;
		}
	else
		{
		m_fHasArmorDesc = false;
		m_iArmorDescCount = 0;
		m_pArmorDesc = NULL;
		}

	//	Load shield display data

	CXMLElement *pShieldDisplay = pDesc->GetContentElementByTag(SHIELD_DISPLAY_TAG);
	if (pShieldDisplay)
		{
		if (error = m_ShieldDesc.Image.InitFromXML(Ctx, 
				pShieldDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_SHIELD_DISPLAY_NEEDED);

		m_fHasShieldDesc = true;
		}

	//	Load reactor display data

	CXMLElement *pReactorDisplay = pDesc->GetContentElementByTag(REACTOR_DISPLAY_TAG);
	if (pReactorDisplay)
		{
		if (error = m_ReactorDesc.ReactorImage.InitFromXML(Ctx,
				pReactorDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		CXMLElement *pImage = pReactorDisplay->GetContentElementByTag(POWER_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.PowerLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xPowerLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yPowerLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xFuelLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yFuelLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LOW_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLowLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(REACTOR_TEXT_TAG),
				&m_ReactorDesc.rcReactorText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(POWER_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcPowerLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcFuelLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_fHasReactorDesc = true;
		}

	//	Done

	return NOERROR;
	}
Пример #23
0
ALERROR CTopologyNode::InitFromSystemXML (CXMLElement *pSystem, CString *retsError)

//	InitFromSystemXML
//
//	Initializes the system information based on an XML element.
//	NOTE: We assume the universe is fully bound at this point.

	{
	ALERROR error;
	CString sSystemUNID = pSystem->GetAttribute(UNID_ATTRIB);
	DWORD dwUNID = strToInt(sSystemUNID, 0, NULL);

	//	If the system node contains a table of different system types, then
	//	remember the root node because some of the system information (such as the
	//	name) may be there.

	CXMLElement *pSystemParent = NULL;

	//	If there is no UNID attribute then it means that the system
	//	is randomly determined based on a table

	if (dwUNID == 0 && pSystem->GetContentElementCount() == 1)
		{
		CXMLElement *pTableElement = pSystem->GetContentElement(0);
		if (pTableElement == NULL)
			{
			ASSERT(false);
			return ERR_FAIL;
			}

		CRandomEntryResults System;
		if (error = CRandomEntryGenerator::Generate(pTableElement, System))
			{
			*retsError = strPatternSubst(CONSTLIT("Topology %s: Unable to generate random system UNID"), m_sID);
			return ERR_FAIL;
			}

		if (System.GetCount() != 1)
			{
			*retsError = strPatternSubst(CONSTLIT("Topology %s: Table generated no systems"), m_sID);
			return ERR_FAIL;
			}

		pSystemParent = pSystem;
		pSystem = System.GetResult(0);
		dwUNID = pSystem->GetAttributeInteger(UNID_ATTRIB);
		}

	//	Set the system UNID

	if (dwUNID != 0)
		m_SystemUNID = dwUNID;

	//	Get the system type

	CSystemType *pSystemType = g_pUniverse->FindSystemType(m_SystemUNID);

	//	Set the name of the system

	CString sName;
	if (!pSystem->FindAttribute(NAME_ATTRIB, &sName))
		if (pSystemParent)
			sName = pSystemParent->GetAttribute(NAME_ATTRIB);

	if (!sName.IsBlank())
		SetName(sName);

	//	Set the level

	int iLevel = 0;
	if (!pSystem->FindAttributeInteger(LEVEL_ATTRIB, &iLevel))
		if (pSystemParent)
			iLevel = pSystemParent->GetAttributeInteger(LEVEL_ATTRIB);

	if (iLevel > 0)
		SetLevel(iLevel);

	if (GetLevel() == 0)
		SetLevel(1);

	//	Add variants for the system

	CString sVariant;
	if (pSystem->FindAttribute(VARIANT_ATTRIB, &sVariant))
		AddVariantLabel(sVariant);

	if (pSystemParent && pSystemParent->FindAttribute(VARIANT_ATTRIB, &sVariant))
		AddVariantLabel(sVariant);

	//	Add attributes for the node/system

	CString sAttribs;
	if (pSystem->FindAttribute(ATTRIBUTES_ATTRIB, &sAttribs))
		AddAttributes(sAttribs);

	if (pSystemParent && pSystemParent->FindAttribute(ATTRIBUTES_ATTRIB, &sAttribs))
		AddAttributes(sAttribs);

	if (pSystemType && !pSystemType->GetAttributes().IsBlank())
		AddAttributes(pSystemType->GetAttributes());

	return NOERROR;
	}
Пример #24
0
ALERROR CWeaponFireDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID, bool bDamageOnly)

//	InitFromXML
//
//	Loads shot data from an element

	{
	ALERROR error;
	int i;

	m_pExtension = Ctx.pExtension;
	m_fVariableInitialSpeed = false;
	m_bFragment = false;

	//	Load basic attributes

	m_sUNID = sUNID;
	m_Lifetime.LoadFromXML(pDesc->GetAttribute(LIFETIME_ATTRIB));
	int iMaxLifetime = m_Lifetime.GetMaxValue();
	m_bCanDamageSource = pDesc->GetAttributeBool(CAN_HIT_SOURCE_ATTRIB);
	m_bAutoTarget = pDesc->GetAttributeBool(AUTO_TARGET_ATTRIB);
	m_bNoFriendlyFire = pDesc->GetAttributeBool(NO_FRIENDLY_FIRE_ATTRIB);
	m_InitialDelay.LoadFromXML(pDesc->GetAttribute(INITIAL_DELAY_ATTRIB));

	//	Load missile speed

	CString sData;
	if (pDesc->FindAttribute(MISSILE_SPEED_ATTRIB, &sData))
		{
		if (error = m_MissileSpeed.LoadFromXML(sData))
			{
			Ctx.sError = CONSTLIT("Invalid missile speed attribute");
			return ERR_FAIL;
			}

		m_fVariableInitialSpeed = !m_MissileSpeed.IsConstant();
		m_rMissileSpeed = (double)m_MissileSpeed.GetAveValue() * LIGHT_SPEED / 100;
		}
	else
		{
		m_fVariableInitialSpeed = false;
		m_rMissileSpeed = LIGHT_SPEED;
		}

	//	Load the effect to use

	if (error = m_pEffect.LoadEffect(Ctx, 
			strPatternSubst("%s:e", sUNID),
			pDesc->GetContentElementByTag(EFFECT_TAG),
			pDesc->GetAttribute(EFFECT_ATTRIB)))
		return error;

	//	Load stealth

	m_iStealth = pDesc->GetAttributeInteger(STEALTH_ATTRIB);
	if (m_iStealth == 0)
		m_iStealth = CSpaceObject::stealthNormal;

	//	Other properties

	m_iSplashChance = pDesc->GetAttributeIntegerBounded(PARTICLE_SPLASH_CHANCE_ATTRIB, 0, 100, 0);
	m_iMissChance = pDesc->GetAttributeIntegerBounded(PARTICLE_MISS_CHANCE_ATTRIB, 0, 100, 0);

	//	Load specific properties

	CString sValue = pDesc->GetAttribute(FIRE_TYPE_ATTRIB);
	if (strEquals(sValue, FIRE_TYPE_MISSILE) || strEquals(sValue, FIRE_TYPE_BEAM))
		{
		m_iFireType = (strEquals(sValue, FIRE_TYPE_BEAM) ? ftBeam : ftMissile);

		//	For backwards compatibility, if we don't have an effect, assume
		//	a beam effect.

		if (m_iFireType == ftBeam && m_pEffect == NULL)
			{
			if (error = m_pEffect.CreateBeamEffect(Ctx, pDesc, strPatternSubst("%s:e", sUNID)))
				return error;

			//	Backwards compatibility in case a CBeam object is loaded from
			//	an old save file.

			m_iBeamType = beamLaser;
			m_wPrimaryColor = CG16bitImage::RGBValue(0xf1, 0x5f, 0x2a);
			m_wSecondaryColor = CG16bitImage::RGBValue(0xff, 0x00, 0x00);
			m_iIntensity = 1;
			}

		//	Load the image for the missile

		CXMLElement *pImage = pDesc->GetContentElementByTag(IMAGE_TAG);
		if (pImage)
			if (error = m_Image.InitFromXML(Ctx, pImage))
				return error;

		m_bDirectional = pDesc->GetAttributeBool(DIRECTIONAL_ATTRIB);
		if (m_bDirectional && m_pEffect)
			m_pEffect->SetVariants(g_RotationRange);

		m_iAccelerationFactor = pDesc->GetAttributeInteger(ACCELERATION_FACTOR_ATTRIB);
		int iMaxSpeed = pDesc->GetAttributeInteger(MAX_MISSILE_SPEED_ATTRIB);
		if (iMaxSpeed == 0)
			m_rMaxMissileSpeed = m_rMissileSpeed;
		else
			m_rMaxMissileSpeed = (Metric)iMaxSpeed * LIGHT_SPEED / 100.0;

		//	Hit points and interaction

		m_iHitPoints = pDesc->GetAttributeInteger(HIT_POINTS_ATTRIB);
		CString sInteraction;
		if (pDesc->FindAttribute(INTERACTION_ATTRIB, &sInteraction))
			m_iInteraction = strToInt(sInteraction, 100);
		else
			m_iInteraction = (m_iFireType == ftBeam ? 0 : 100);

		//	Load exhaust data

		CXMLElement *pExhaust = pDesc->GetContentElementByTag(MISSILE_EXHAUST_TAG);
		if (pExhaust)
			{
			m_iExhaustRate = pExhaust->GetAttributeInteger(EXHAUST_RATE_ATTRIB);
			m_iExhaustLifetime = pExhaust->GetAttributeInteger(EXHAUST_LIFETIME_ATTRIB);
			m_rExhaustDrag = pExhaust->GetAttributeInteger(EXHAUST_DRAG_ATTRIB) / 100.0;

			CXMLElement *pImage = pExhaust->GetContentElementByTag(IMAGE_TAG);
			if (error = m_ExhaustImage.InitFromXML(Ctx, pImage))
				return error;
			}
		else
			{
			m_iExhaustRate = 0;
			m_iExhaustLifetime = 0;
			m_rExhaustDrag = 0.0;
			}
		}
	else if (strEquals(sValue, FIRE_TYPE_AREA))
		{
		m_iFireType = ftArea;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		//	Load expansion speed

		if (pDesc->FindAttribute(EXPANSION_SPEED_ATTRIB, &sData))
			{
			if (error = m_ExpansionSpeed.LoadFromXML(sData))
				{
				Ctx.sError = CONSTLIT("Invalid expansionSpeed attribute");
				return ERR_FAIL;
				}
			}
		else
			m_ExpansionSpeed.SetConstant(20);

		//	Area damage density

		if (pDesc->FindAttribute(AREA_DAMAGE_DENSITY_ATTRIB, &sData))
			{
			if (error = m_AreaDamageDensity.LoadFromXML(sData))
				{
				Ctx.sError = CONSTLIT("Invalid areaDamageDensity attribute");
				return ERR_FAIL;
				}
			}
		else
			m_AreaDamageDensity.SetConstant(32);

		//	Must have effect

		if (m_pEffect == NULL)
			{
			Ctx.sError = CONSTLIT("Must have <Effect> for area damage.");
			return ERR_FAIL;
			}
		}
	else if (strEquals(sValue, FIRE_TYPE_PARTICLES))
		{
		m_iFireType = ftParticles;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		if (error = m_ParticleCount.LoadFromXML(pDesc->GetAttribute(PARTICLE_COUNT_ATTRIB)))
			{
			Ctx.sError = CONSTLIT("Invalid particle count.");
			return error;
			}

		if (error = m_ParticleEmitTime.LoadFromXML(pDesc->GetAttribute(PARTICLE_EMIT_TIME_ATTRIB)))
			{
			Ctx.sError = CONSTLIT("Invalid particle emit time.");
			return error;
			}

		m_iParticleSpread = pDesc->GetAttributeInteger(PARTICLE_SPREAD_ANGLE_ATTRIB);
		m_iParticleSpreadWidth = pDesc->GetAttributeInteger(PARTICLE_SPREAD_WIDTH_ATTRIB);
		}
	else if (strEquals(sValue, FIRE_TYPE_RADIUS))
		{
		m_iFireType = ftRadius;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		m_rMinRadius = LIGHT_SECOND * (Metric)pDesc->GetAttributeInteger(MIN_RADIUS_ATTRIB);
		m_rMaxRadius = LIGHT_SECOND * (Metric)pDesc->GetAttributeInteger(MAX_RADIUS_ATTRIB);

		//	For radius, lifetime attribute is not required. We always set the lifetime
		//	to the effect lifetime.

		if (m_pEffect && iMaxLifetime == 0)
			{
			int iEffectLifetime = m_pEffect->GetLifetime();

			//	If the effect lifetime is infinite then change it
			//	to something more finite (this is technically an error condition)

			if (iEffectLifetime == -1)
				iEffectLifetime = 666;

			m_Lifetime.SetConstant(iEffectLifetime);
			iMaxLifetime = iEffectLifetime;
			}
		}
	else if (!bDamageOnly)
		{
		Ctx.sError = CONSTLIT("Invalid weapon fire type");
		return ERR_FAIL;
		}

	//	The effect should have the same lifetime as the shot
	//	Note: For radius damage it is the other way around (we set iMaxLifetime based on
	//	the effect--see above)

	if (m_pEffect)
		m_pEffect->SetLifetime(iMaxLifetime);

	//	We initialize this with the UNID, and later resolve the reference
	//	during OnDesignLoadComplete

	m_pAmmoType.LoadUNID(Ctx, pDesc->GetAttribute(AMMO_ID_ATTRIB));

	//	Maneuverability

	m_iManeuverability = pDesc->GetAttributeInteger(MANEUVERABILITY_ATTRIB);
	m_iManeuverRate = pDesc->GetAttributeIntegerBounded(MANEUVER_RATE_ATTRIB, 1, 180, -1);
	if (m_iManeuverRate == -1 && m_iManeuverability > 0)
		m_iManeuverRate = g_RotationAngle;
	else if (m_iManeuverability == 0 && m_iManeuverRate > 0)
		m_iManeuverability = 1;

	//	Load continuous and passthrough

	m_iContinuous = pDesc->GetAttributeInteger(BEAM_CONTINUOUS_ATTRIB);
	if (pDesc->FindAttributeInteger(PASSTHROUGH_ATTRIB, &m_iPassthrough))
		{
		//	In previous versions passthrough was a boolean value, so for backwards
		//	compatibility we treat 0 as 50%.
		//
		//	Note: We don't do this for ftArea because we need a way to specify
		//	passthrough=0 (since ftArea defaults to non-zero passthrough). Also,
		//	ftArea has no backwards compatibility issues (passthrough is only
		//	supported for 1.1 and above).

		if (m_iPassthrough == 0 && m_iFireType != ftArea)
			m_iPassthrough = 50;
		}
	else
		{
		//	If this is an area weapon, we set passthrough to a default value
		//	(for backwards compatibility)

		if (m_iFireType == ftArea)
			m_iPassthrough = 80;
		else
			m_iPassthrough = 0;
		}

	//	Load damage

	if (error = m_Damage.LoadFromXML(Ctx, pDesc->GetAttribute(DAMAGE_ATTRIB)))
		{
		Ctx.sError = strPatternSubst(CONSTLIT("Invalid damage specification: %s"), pDesc->GetAttribute(DAMAGE_ATTRIB));
		return error;
		}

	//	Fragments

	m_pFirstFragment = NULL;
	SFragmentDesc *pLastFragment = NULL;
	int iFragCount = 0;
	for (i = 0; i < pDesc->GetContentElementCount(); i++)
		{
		CXMLElement *pFragDesc = pDesc->GetContentElement(i);
		if (!strEquals(FRAGMENT_TAG, pFragDesc->GetTag()))
			continue;

		//	Create a new fragmentation descriptor

		SFragmentDesc *pNewDesc = new SFragmentDesc;
		pNewDesc->pNext = NULL;
		if (pLastFragment)
			pLastFragment->pNext = pNewDesc;
		else
			m_pFirstFragment = pNewDesc;

		pLastFragment = pNewDesc;

		//	Load fragment data

		pNewDesc->pDesc = new CWeaponFireDesc;
		CString sFragUNID = strPatternSubst("%s/f%d", sUNID, iFragCount++);
		if (error = pNewDesc->pDesc->InitFromXML(Ctx, pFragDesc, sFragUNID))
			return error;

		pNewDesc->pDesc->m_bFragment = true;

		//	Set the fragment count

		CString sCount = pFragDesc->GetAttribute(COUNT_ATTRIB);
		if (sCount.IsBlank())
			sCount = pDesc->GetAttribute(FRAGMENT_COUNT_ATTRIB);
		pNewDesc->Count.LoadFromXML(sCount);

		//	Set MIRV flag

		pNewDesc->bMIRV = (pFragDesc->GetAttributeBool(MULTI_TARGET_ATTRIB) 
				|| pDesc->GetAttributeBool(FRAGMENT_TARGET_ATTRIB));
		}

	//	If we have fragments, then set proximity appropriately
	//	NOTE: We set the fail safe value even if we don't set the proximity
	//	blast because we might set m_bProximityBlast later if there
	//	is an OnFragment event.

	m_bProximityBlast = (iFragCount != 0);
	m_iProximityFailsafe = pDesc->GetAttributeInteger(FAILSAFE_ATTRIB);

	//	Compute max effective range

	if (m_iFireType == ftArea)
		m_rMaxEffectiveRange = (m_ExpansionSpeed.GetAveValue() * LIGHT_SECOND / 100.0) * Ticks2Seconds(iMaxLifetime) * 0.75;
	else
		{
		Metric rEffectiveLifetime;
		if (m_iManeuverability > 0)
			rEffectiveLifetime = Ticks2Seconds(iMaxLifetime) * 0.75;
		else
			rEffectiveLifetime = Min(Ticks2Seconds(iMaxLifetime), 100.0);

		Metric rSpeed = (m_rMissileSpeed + m_rMaxMissileSpeed) / 2;
		m_rMaxEffectiveRange = rSpeed * rEffectiveLifetime;

		//	If we have fragments, add to the effective range

		if (m_pFirstFragment)
			m_rMaxEffectiveRange += m_pFirstFragment->pDesc->m_rMaxEffectiveRange;
		}

	//	Effects

	if (error = m_pHitEffect.LoadEffect(Ctx,
			strPatternSubst("%s:h", sUNID),
			pDesc->GetContentElementByTag(HIT_EFFECT_TAG),
			pDesc->GetAttribute(HIT_EFFECT_ATTRIB)))
		return error;

	if (error = m_pFireEffect.LoadEffect(Ctx,
			strPatternSubst("%s:f", sUNID),
			pDesc->GetContentElementByTag(FIRE_EFFECT_TAG),
			pDesc->GetAttribute(FIRE_EFFECT_ATTRIB)))
		return error;

	//	Vapor trail

	if (!pDesc->FindAttributeInteger(VAPOR_TRAIL_WIDTH_ATTRIB, &m_iVaporTrailWidth))
		m_iVaporTrailWidth = 100 * pDesc->GetAttributeInteger(VAPOR_TRAIL_ATTRIB);

	if (m_iVaporTrailWidth)
		{
		m_wVaporTrailColor = LoadRGBColor(pDesc->GetAttribute(VAPOR_TRAIL_COLOR_ATTRIB));
		m_iVaporTrailLength = pDesc->GetAttributeInteger(VAPOR_TRAIL_LENGTH_ATTRIB);
		if (m_iVaporTrailLength <= 0)
			m_iVaporTrailLength = 64;

		if (!pDesc->FindAttributeInteger(VAPOR_TRAIL_WIDTH_INC_ATTRIB, &m_iVaporTrailWidthInc))
			m_iVaporTrailWidthInc = 25;
		}
	else
		m_iVaporTrailLength = 0;

	//	Sound

	DWORD dwSoundID = LoadUNID(Ctx, pDesc->GetAttribute(SOUND_ATTRIB));
	if (dwSoundID)
		m_iFireSound = g_pUniverse->FindSound(dwSoundID);
	else
		m_iFireSound = -1;

	//	Events

	CXMLElement *pEventsDesc = pDesc->GetContentElementByTag(EVENTS_TAG);
	if (pEventsDesc)
		{
		if (error = m_Events.InitFromXML(Ctx, pEventsDesc))
			return error;
		}

	//	Check to see if this element has an enhanced sub-element. If so, then we
	//	recurse.

	CXMLElement *pEnhanced = pDesc->GetContentElementByTag(ENHANCED_TAG);
	if (pEnhanced)
		{
		m_pEnhanced = new CWeaponFireDesc(*this);
		if (error = m_pEnhanced->OverrideDesc(Ctx, pEnhanced))
			return error;
		}
	else
		m_pEnhanced = NULL;

	return NOERROR;
	}