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; }
void GenerateWordList (const CString &sDataFile, CXMLElement *pCmdLine) // GenerateWordList // // Generate a list of unique words used in the game { ALERROR error; int i; CString sError; // Open the XML file CResourceDb Resources(sDataFile); if (error = Resources.Open()) { printf("Unable to initialize data file.\n"); return; } CXMLElement *pGameFile; if (error = Resources.LoadGameFile(&pGameFile, NULL, &sError)) { printf("%s\n", sError.GetASCIIZPointer()); return; } // Create the context CSymbolTable WordList(FALSE, TRUE); TraverseCtx Ctx; Ctx.pWordList = &WordList; // Recursive descent ParseWordList(Ctx, pGameFile); // Parse all modules too CXMLElement *pModules = pGameFile->GetContentElementByTag(MODULES_TAG); if (pModules) { for (i = 0; i < pModules->GetContentElementCount(); i++) { CXMLElement *pModule = pModules->GetContentElement(i); CXMLElement *pModuleXML; if (error = Resources.LoadModule(NULL_STR, pModule->GetAttribute(FILENAME_ATTRIB), &pModuleXML, &sError)) { printf("%s\n", sError.GetASCIIZPointer()); return; } ParseWordList(Ctx, pModuleXML); } } // Print out the word list for (i = 0; i < WordList.GetCount(); i++) printf("%s\n", WordList.GetKey(i).GetASCIIZPointer()); }
bool IDockScreenDisplay::GetDisplayOptions (SInitCtx &Ctx, SDisplayOptions *retOptions, CString *retsError) // GetDisplayOptions // // Initializes the display options structure, which is used by list and // selector displays. { DEBUG_TRY // Initialize background image options CString sBackgroundID; if (Ctx.pDesc->FindAttribute(BACKGROUND_ID_ATTRIB, &sBackgroundID)) { // If the attribute exists, but is empty (or equals "none") then // we don't have a background if (sBackgroundID.IsBlank() || strEquals(sBackgroundID, CONSTLIT("none"))) retOptions->BackgroundDesc.iType = backgroundNone; else if (strEquals(sBackgroundID, TYPE_HERO)) { retOptions->BackgroundDesc.iType = backgroundObjHeroImage; retOptions->BackgroundDesc.pObj = Ctx.pLocation; } // If the ID is "object" then we should ask the object else if (strEquals(sBackgroundID, TYPE_OBJECT)) { retOptions->BackgroundDesc.pObj = Ctx.pLocation; if (Ctx.pLocation->IsPlayer()) retOptions->BackgroundDesc.iType = backgroundObjSchematicImage; else retOptions->BackgroundDesc.iType = backgroundObjHeroImage; } else if (strEquals(sBackgroundID, TYPE_SCHEMATIC)) { retOptions->BackgroundDesc.iType = backgroundObjSchematicImage; retOptions->BackgroundDesc.pObj = Ctx.pLocation; } // If the ID is "player" then we should ask the player ship else if (strEquals(sBackgroundID, DATA_FROM_PLAYER)) { CSpaceObject *pPlayer = g_pUniverse->GetPlayerShip(); if (pPlayer) { retOptions->BackgroundDesc.iType = backgroundObjSchematicImage; retOptions->BackgroundDesc.pObj = pPlayer; } } // Otherwise, we expect an integer else { retOptions->BackgroundDesc.iType = backgroundImage; retOptions->BackgroundDesc.dwImageID = strToInt(sBackgroundID, 0); } } // Initialize control rect. If we have a background, then initialize to // backwards compatible position. Otherwise, we take up the full range. if (retOptions->BackgroundDesc.iType != backgroundDefault) { retOptions->rcControl.left = 4; retOptions->rcControl.top = 12; retOptions->rcControl.right = 548; retOptions->rcControl.bottom = 396; } else { retOptions->rcControl.left = 0; retOptions->rcControl.top = 23; retOptions->rcControl.right = 600; retOptions->rcControl.bottom = 502; } // Get the type if (Ctx.pDisplayDesc && Ctx.pDisplayDesc->FindAttribute(TYPE_ATTRIB, &retOptions->sType)) NULL; else retOptions->sType = Ctx.pDesc->GetAttribute(TYPE_ATTRIB); // There are a couple of different ways to get options (for backwards // compatibility). CXMLElement *pOptions; if ((pOptions = Ctx.pDesc->GetContentElementByTag(LIST_OPTIONS_TAG)) == NULL && (pOptions = Ctx.pDesc->GetContentElementByTag(LIST_TAG)) == NULL && (pOptions = Ctx.pDisplayDesc) == NULL) return true; retOptions->pOptions = pOptions; // Read from the element retOptions->sDataFrom = pOptions->GetAttribute(DATA_FROM_ATTRIB); if (!pOptions->FindAttribute(CRITERIA_ATTRIB, &retOptions->sItemCriteria)) retOptions->sItemCriteria = pOptions->GetAttribute(LIST_ATTRIB); retOptions->sInitialItemCode = pOptions->GetAttribute(INITIAL_ITEM_ATTRIB); retOptions->sRowHeightCode = pOptions->GetAttribute(ROW_HEIGHT_ATTRIB); // Init code CXMLElement *pInitCode = pOptions->GetContentElementByTag(ON_DISPLAY_INIT_TAG); if (pInitCode == NULL) pInitCode = pOptions; retOptions->sCode = pInitCode->GetContentText(0); // List options retOptions->bNoArmorSpeedDisplay = pOptions->GetAttributeBool(NO_ARMOR_SPEED_DISPLAY_ATTRIB); retOptions->bActualItems = pOptions->GetAttributeBool(DISPLAY_ACTUAL_ATTRIB); retOptions->cxIcon = pOptions->GetAttributeIntegerBounded(ICON_WIDTH_ATTRIB, 0, -1, ICON_WIDTH); retOptions->cyIcon = pOptions->GetAttributeIntegerBounded(ICON_HEIGHT_ATTRIB, 0, -1, ICON_HEIGHT); retOptions->rIconScale = pOptions->GetAttributeDoubleBounded(ICON_SCALE_ATTRIB, 0.0, -1.0, 1.0); // Selector options retOptions->bNoEmptySlots = pOptions->GetAttributeBool(NO_EMPTY_SLOTS_ATTRIB); retOptions->sSlotNameCode = pOptions->GetAttribute(SLOT_NAME_ATTRIB); // See if we have control position if (pOptions->FindAttributeInteger(POS_X_ATTRIB, (int *)&retOptions->rcControl.left)) { retOptions->rcControl.top = pOptions->GetAttributeIntegerBounded(POS_Y_ATTRIB, 0, -1); retOptions->rcControl.right = retOptions->rcControl.left + pOptions->GetAttributeIntegerBounded(WIDTH_ATTRIB, 0, -1); retOptions->rcControl.bottom = retOptions->rcControl.top + pOptions->GetAttributeIntegerBounded(HEIGHT_ATTRIB, 0, -1); } return true; DEBUG_CATCH }
void GenerateEffectImage (CUniverse &Universe, CXMLElement *pCmdLine) { CString sError; int i, j; // Input file CString sInput = pCmdLine->GetAttribute(CONSTLIT("input")); if (sInput.IsBlank()) { printf("Input file required.\n"); return; } // Output file (optional) CString sFilespec = pCmdLine->GetAttribute(CONSTLIT("output")); if (!sFilespec.IsBlank()) sFilespec = pathAddExtensionIfNecessary(sFilespec, CONSTLIT(".bmp")); // Load a resource file so that we can create a design load context CResourceDb Resources(CONSTLIT("Transcendence")); if (Resources.Open(DFOPEN_FLAG_READ_ONLY, &sError) != NOERROR) { printf("%s\n", (LPSTR)sError); return; } CExternalEntityTable *pEntities; if (Resources.LoadEntities(&sError, &pEntities) != NOERROR) { printf("%s\n", sError.GetASCIIZPointer()); return; } // Generate a list of effect render structures TArray<SEffectRender> Effects; // Load the input file CFileReadBlock InputFile(sInput); if (InputFile.Open() != NOERROR) { printf("Unable to open input file: %s\n", sInput.GetASCIIZPointer()); return; } // Parse the file CXMLElement *pRenderFile; if (CXMLElement::ParseXML(&InputFile, pEntities, &pRenderFile, &sError) != NOERROR) { printf("%s\n", sError.GetASCIIZPointer()); return; } // Keep track of the max cell size and frame count int cxCellWidth = 0; int cyCellHeight = 0; int iCellsPerEffect = 1; // Generate structures SDesignLoadCtx LoadCtx; LoadCtx.sResDb = Resources.GetFilespec(); LoadCtx.pResDb = &Resources; LoadCtx.bNoVersionCheck = true; for (i = 0; i < pRenderFile->GetContentElementCount(); i++) { CXMLElement *pRender = pRenderFile->GetContentElement(i); CXMLElement *pEffectDesc = pRender->GetContentElementByTag(CONSTLIT("Effect")); if (pEffectDesc == NULL) { printf("<Effect> tag required.\n"); return; } SEffectRender *pEffect = Effects.Insert(); // Parse the effect if (pEffect->pEffectCreator.LoadEffect(LoadCtx, CONSTLIT("none"), pEffectDesc, NULL_STR) != NOERROR) { printf("%s\n", LoadCtx.sError.GetASCIIZPointer()); return; } // Bind if (pEffect->pEffectCreator.Bind(LoadCtx) != NOERROR) { printf("%s\n", LoadCtx.sError.GetASCIIZPointer()); return; } // Create a painter pEffect->pPainter = pEffect->pEffectCreator->CreatePainter(); if (pEffect->pPainter == NULL) { printf("Unable to create painter.\n"); return; } // Render specs int cyHeight = pRender->GetAttributeIntegerBounded(CONSTLIT("height"), 0, -1, 128); int cxWidth = pRender->GetAttributeIntegerBounded(CONSTLIT("width"), 0, -1, 128); cxCellWidth = Max(cxCellWidth, cxWidth); cyCellHeight = Max(cyCellHeight, cyHeight); // Figure out how many animation cells pEffect->iLifetime = Max(1, pEffect->pEffectCreator->GetLifetime()); iCellsPerEffect = Max(iCellsPerEffect, pEffect->iLifetime); } // Create the resulting image CImageGrid Output; Output.Create(iCellsPerEffect * Effects.GetCount(), cxCellWidth, cyCellHeight); // Paint for (i = 0; i < Effects.GetCount(); i++) { SEffectRender *pEffect = &Effects[i]; SViewportPaintCtx Ctx; for (j = 0; j < pEffect->iLifetime; j++) { int x, y; Output.GetCellCenter(i * iCellsPerEffect + j, &x, &y); // Create a context Ctx.iTick = j; // Paint the effect pEffect->pPainter->Paint(Output.GetImage(), x, y, Ctx); // Update pEffect->pPainter->OnUpdate(); } } // Output OutputImage(Output.GetImage(), sFilespec); }
ALERROR CEffectCreator::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // OnCreateFromXML // // Load from XML. This is only called if we go through the EffectType path // (as opposed to plain Effect types). { ALERROR error; // Basic info m_sUNID = strFromInt(GetUNID(), false); if (error = InitBasicsFromXML(Ctx, pDesc)) return error; // Allow our subclass to initialize based on the effect // (We know we have one because we couldn't have gotten this far // without one. See CreateTypeFromXML.) CXMLElement *pEffect = pDesc->GetContentElementByTag(EFFECT_TAG); ASSERT(pEffect); // Continue if (pEffect->GetContentElementCount() == 1) { CXMLElement *pEffectDesc = pEffect->GetContentElement(0); // Load events for this effect, in case they're here. CXMLElement *pEventsDesc = pEffectDesc->GetContentElementByTag(EVENTS_TAG); if (pEventsDesc) { if (error = m_Events.InitFromXML(Ctx, pEventsDesc)) return error; } // Load the single effect error = OnEffectCreateFromXML(Ctx, pEffectDesc, m_sUNID); } else error = OnEffectCreateFromXML(Ctx, pEffect, m_sUNID); if (error) return error; // Load damage descriptors CXMLElement *pDamageDesc = pDesc->GetContentElementByTag(DAMAGE_TAG); if (pDamageDesc) { m_pDamage = new CWeaponFireDesc; CString sUNID = strPatternSubst(CONSTLIT("%d/d"), GetUNID()); if (error = m_pDamage->InitFromXML(Ctx, pDamageDesc, sUNID, true)) return error; } return NOERROR; }
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; }
ALERROR CConquerNodesProc::OnInitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID) // OnInitFromXML // // Initialize from XML element { ALERROR error; int i; // Initialize criteria CTopologyNode::ParseCriteria(NULL, &m_Criteria); // Loop over all elements for (i = 0; i < pDesc->GetContentElementCount(); i++) { CXMLElement *pItem = pDesc->GetContentElement(i); // If we have a criteria, parse it and remember it // (Note: If multiple criteria are found, we take the latest one). if (strEquals(pItem->GetTag(), CRITERIA_TAG)) { // Parse the filter if (error = CTopologyNode::ParseCriteria(pItem, &m_Criteria, &Ctx.sError)) return error; } // Otherwise, treat it as a conqueror definition and insert it in the list else { CString sNewUNID = strPatternSubst(CONSTLIT("%s/%d"), sUNID, m_Conquerors.GetCount()); SConqueror *pConqueror = m_Conquerors.Insert(); // Get some properties pConqueror->iSeedChance = pItem->GetAttributeIntegerBounded(SEED_CHANCE_ATTRIB, 1, 100, 1); pConqueror->iMaxSeeds = pItem->GetAttributeIntegerBounded(MAX_SEEDS_ATTRIB, 1, -1, 1); pConqueror->iExpandChance = pItem->GetAttributeIntegerBounded(EXPAND_CHANCE_ATTRIB, 1, 100, 1); pConqueror->iMaxNodes = pItem->GetAttributeIntegerBounded(MAX_NODES_ATTRIB, 1, -1, 1); // Get the seed and expand probabilities if (error = LoadNodeWeightTable(Ctx, pItem->GetContentElementByTag(SEED_CHANCE_TAG), &pConqueror->Seed)) return error; if (error = LoadNodeWeightTable(Ctx, pItem->GetContentElementByTag(EXPAND_CHANCE_TAG), &pConqueror->Expand)) return error; // Get the processor itself CXMLElement *pProcXML = pItem->GetContentElementByTag(PROCESSOR_TAG); if (pProcXML) { if (error = ITopologyProcessor::CreateFromXMLAsGroup(Ctx, pProcXML, sNewUNID, &pConqueror->pProc)) return error; } else { Ctx.sError = CONSTLIT("<Processor> element not found in <ConquerNodes>"); return ERR_FAIL; } } } return NOERROR; }
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; }
ALERROR WriteModule (const CString &sFilename, const CString &sFolder, CExternalEntityTable *pEntityTable, CSymbolTable &Resources, CDataFile &Out, int *retiModuleEntry) { ALERROR error; int i; // Parse the file CXMLElement *pModule; CExternalEntityTable EntityTable; if (pEntityTable) { CFileReadBlock DataFile(sFilename); CString sError; printf("Parsing %s...", sFilename.GetASCIIZPointer()); if (error = CXMLElement::ParseXML(&DataFile, pEntityTable, &pModule, &sError)) { printf("\n%s\n", sError.GetASCIIZPointer()); return error; } printf("done.\n"); } else { CFileReadBlock DataFile(sFilename); CString sError; printf("Parsing %s...", sFilename.GetASCIIZPointer()); if (error = CXMLElement::ParseXML(&DataFile, &pModule, &sError, &EntityTable)) { printf("\n%s\n", sError.GetASCIIZPointer()); return error; } pEntityTable = &EntityTable; printf("done.\n"); } // Write the module itself int iEntry; if (error = WriteGameFile(sFilename, Out, &iEntry)) return error; // If the caller doesn't want the module entry, then it means that this is // a module (instead of the main file). If so, add it to the resources table if (retiModuleEntry == NULL) Resources.AddEntry(sFilename, (CObject *)iEntry); // Store all the image resources if (error = WriteModuleImages(pModule, sFolder, Resources, Out)) return error; // Store all the sound resources if (error = WriteModuleSounds(pModule, sFolder, Resources, Out)) return error; // Store all modules CXMLElement *pModules = pModule->GetContentElementByTag(TAG_MODULES); if (pModules) { for (i = 0; i < pModules->GetContentElementCount(); i++) { CXMLElement *pItem = pModules->GetContentElement(i); CString sFilename = pItem->GetAttribute(ATTRIB_FILENAME); if (error = WriteModule(sFilename, sFolder, pEntityTable, Resources, Out, NULL)) continue; } } // Done if (retiModuleEntry) *retiModuleEntry = iEntry; return NOERROR; }