int CShieldClass::GetDamageEffectiveness (CSpaceObject *pAttacker, CInstalledDevice *pWeapon) // GetDamageEffectiveness // // Returns the effectiveness of the given weapon against this shield. // // < 0 The weapon is ineffective against us. // 0-99 The weapon is less effective than average. // 100 The weapon has average effectiveness // > 100 The weapon is more effective than average. { const DamageDesc *pDamage = pWeapon->GetDamageDesc(CItemCtx(pAttacker, pWeapon)); if (pDamage == NULL) return 100; int iBonus = m_DamageAdj.GetHPBonus(pDamage->GetDamageType()); if (iBonus <= -100) return -1; // Compute score based on bonus int iScore; if (iBonus <= 0) iScore = 100 - iBonus; else iScore = 100 - Min(100, (iBonus / 2)); // See if the weapon does extra damage to shields if (pDamage->GetShieldDamageLevel()) { int iAdj = 100 * Max(100, 300 + (50 * (pDamage->GetShieldDamageLevel() - GetLevel()))) / 100; iScore += (iAdj / 2); } // Done return iScore; }
int CShieldClass::CalcBalance (void) // CalcBalance // // Computes the relative balance of this shield relative to it level { int i; int iBalance = 0; SStdStats *pStd = GetStdStats(GetLevel()); if (pStd == NULL) return 0; // Calc HPs. +1 for each 1% above standard HP // If hp is below the standard, then invert the ratio // (i.e., 50% below is 1/0.5 or half strength, which // counts as -100). int iMaxHP; CalcMinMaxHP(CItemCtx(), m_iMaxCharges, 0, 0, NULL, &iMaxHP); int iDiff = (iMaxHP - pStd->iHP); if (iDiff > 0) iBalance += iDiff * 100 / pStd->iHP; else if (iMaxHP > 0) iBalance -= (pStd->iHP * 100 / iMaxHP) - 100; else iBalance -= 200; // Compute the regen HP for each 180 ticks // +1 for each 1% above standard rate int iRegen = (int)m_Regen.GetHPPer180(); iDiff = (iRegen - pStd->iRegen); if (iDiff > 0) iBalance += iDiff * 100 / pStd->iHP; else if (iRegen > 0) iBalance -= (pStd->iRegen * 100 / iRegen) - 100; else iBalance -= 200; // Account for damage adjustments int iBalanceAdj = 0; for (i = 0; i < damageCount; i++) { int iStdAdj; int iDamageAdj; m_DamageAdj.GetAdjAndDefault((DamageTypes)i, &iDamageAdj, &iStdAdj); if (iStdAdj != iDamageAdj) { if (iDamageAdj > 0) { int iBonus = (int)((100.0 * (iStdAdj - iDamageAdj) / iDamageAdj) + 0.5); if (iBonus > 0) iBalanceAdj += iBonus / 4; else iBalanceAdj -= ((int)((100.0 * iDamageAdj / iStdAdj) + 0.5) - 100) / 4; } else if (iStdAdj > 0) { iBalanceAdj += iStdAdj; } } } iBalance += Max(Min(iBalanceAdj, 100), -100); // Done return iBalance; }
bool CShieldClass::AbsorbDamage (CInstalledDevice *pDevice, CSpaceObject *pShip, SDamageCtx &Ctx) // AbsorbDamage // // Absorbs damage. // NOTE: We always set Ctx.iAbsorb properly, regardless of the return value. { DEBUG_TRY // If we're depleted then we cannot absorb anything Ctx.iHPLeft = GetHPLeft(pDevice, pShip); if (Ctx.iHPLeft == 0) { Ctx.iAbsorb = 0; return false; } // Calculate how much we will absorb int iAbsorbAdj = (Ctx.Damage.GetDamageType() == damageGeneric ? 100 : m_iAbsorbAdj[Ctx.Damage.GetDamageType()]); Ctx.iAbsorb = (Ctx.iDamage * iAbsorbAdj) / 100; if (pDevice->GetMods().IsNotEmpty()) Ctx.iAbsorb = Ctx.iAbsorb * pDevice->GetMods().GetAbsorbAdj(Ctx.Damage) / 100; // Compute how much damage we take (based on the type of damage) int iAdj = GetDamageAdj(pDevice->GetMods(), Ctx.Damage); Ctx.iShieldDamage = (Ctx.iAbsorb * iAdj) / 100; // If shield generator is damaged then sometimes we take extra damage if (pDevice->IsDamaged() || pDevice->IsDisrupted()) { int iRoll = mathRandom(1, 100); if (iRoll <= 10) Ctx.iAbsorb = 75 * Ctx.iAbsorb / 100; else if (iRoll <= 25) Ctx.iShieldDamage *= 2; } // If the amount of shield damage is greater than HP left, then scale // the amount of HP that we aborb if (Ctx.iShieldDamage > Ctx.iHPLeft) { // ASSERT: We know that iShieldDamage is > 0 because iHPLeft is > 0. Ctx.iAbsorb = Ctx.iHPLeft * Ctx.iAbsorb / Ctx.iShieldDamage; Ctx.iShieldDamage = Ctx.iHPLeft; } // See if we're reflective int iReflectChance; if (!pDevice->GetMods().IsReflective(Ctx.Damage, &iReflectChance)) iReflectChance = 0; if (m_Reflective.InSet(Ctx.Damage.GetDamageType())) iReflectChance = Max(iReflectChance, MAX_REFLECTION_CHANCE); if (iReflectChance && Ctx.pCause && Ctx.Damage.GetShieldDamageLevel() == 0) { // Compute the chance that we will reflect (based on the strength of // our shields) int iMaxHP = GetMaxHP(pDevice, pShip); int iEfficiency = (iMaxHP == 0 ? 100 : 50 + (Ctx.iHPLeft * 50 / iMaxHP)); int iChance = iEfficiency * iReflectChance / 100; // See if we reflect if (Ctx.bReflect = (mathRandom(1, 100) <= iChance)) { Ctx.iOriginalAbsorb = Ctx.iAbsorb; Ctx.iOriginalShieldDamage = Ctx.iShieldDamage; Ctx.iAbsorb = Ctx.iDamage; Ctx.iShieldDamage = 0; } } else Ctx.bReflect = false; // Give custom damage a chance to react. These events can modify the // following variables: // // Ctx.bReflect // Ctx.iAbsorb // Ctx.iShieldDamage // // OR // // Ctx.iDamage (if we skip further processing) if (Ctx.pDesc->FireOnDamageShields(Ctx, pDevice->GetDeviceSlot())) return (Ctx.iDamage == 0); FireOnShieldDamage(CItemCtx(pShip, pDevice), Ctx); // If we reflect, then create the reflection if (Ctx.bReflect) Ctx.pCause->CreateReflection(Ctx.vHitPos, (Ctx.iDirection + 120 + mathRandom(0, 120)) % 360); // Create shield effect if (Ctx.iAbsorb || Ctx.bReflect) { if (m_pHitEffect) m_pHitEffect->CreateEffect(pShip->GetSystem(), NULL, Ctx.vHitPos, pShip->GetVel(), Ctx.iDirection); } // Shield takes damage if (Ctx.iShieldDamage > 0) { if (Ctx.iShieldDamage >= Ctx.iHPLeft) SetDepleted(pDevice, pShip); else SetHPLeft(pDevice, Ctx.iHPLeft - Ctx.iShieldDamage); pShip->OnComponentChanged(comShields); } // Set the remaining damage Ctx.iDamage -= Ctx.iAbsorb; return (Ctx.iDamage == 0); DEBUG_CATCH }
void GenerateArmorTable (CUniverse &Universe, CXMLElement *pCmdLine) { int i; printf("ARMOR TABLE\n\n"); printf("Level\tArmor\tCost\tWeight\tHP\tLasr\tImpc\tPart\tBlst\tIon\tThrm\tPosi\tPlsm\tAnti\tNano\tGrav\tSing\tDacd\tDstl\tDlgt\tDfir\n"); for (i = 0; i < Universe.GetItemTypeCount(); i++) { CItemType *pItem = Universe.GetItemType(i); CArmorClass *pArmor = pItem->GetArmorClass(); if (pArmor == NULL) continue; CItem Item(pItem, 1); CString sName = pItem->GetName(NULL); int iHP = pArmor->GetMaxHP(CItemCtx(&Item)); printf("%d\t%s\t%d\t%d\t%d\t", pItem->GetLevel(), sName.GetASCIIZPointer(), Item.GetValue(true), Item.GetMassKg(), iHP); // For each damage type, compute the adjusted hit points of the armor int iDamage; for (iDamage = damageLaser; iDamage < damageCount; iDamage++) { printf("%d", pArmor->GetDamageAdj(CItemCtx(Item), (DamageTypes)iDamage)); if (iDamage != damageCount - 1) printf("\t"); } printf("\n"); } printf("ARMOR TABLE DAMAGE ADJUSTMENTS\n\n"); printf("Level\tArmor\tHP\tLasr\tImpc\tPart\tBlst\tIon\tThrm\tPosi\tPlsm\tAnti\tNano\tGrav\tSing\tDacd\tDstl\tDlgt\tDfir\n"); for (i = 0; i < Universe.GetItemTypeCount(); i++) { CItemType *pItem = Universe.GetItemType(i); CArmorClass *pArmor = pItem->GetArmorClass(); if (pArmor == NULL) continue; CString sName = pItem->GetName(NULL); CItem Item(pItem, 1); int iHP = pArmor->GetMaxHP(CItemCtx(&Item)); printf("%d\t%s\t%d\t", pItem->GetLevel(), sName.GetASCIIZPointer(), iHP); // For each damage type, compute the adjusted hit points of the armor int iDamage; for (iDamage = damageLaser; iDamage < damageCount; iDamage++) { int iAdj = pArmor->GetDamageAdj(CItemCtx(Item), (DamageTypes)iDamage); if (iAdj == 0) printf("----"); else printf("%d", iHP * 100 / iAdj); if (iDamage != damageCount - 1) printf("\t"); } printf("\n"); } }
CurrencyValue CSingleItem::GetAverageValue (int iLevel) // GetAverageValue // // Returns the average value (in credits) { return CEconomyType::ExchangeToCredits(m_pItemType->GetCurrencyType(), m_pItemType->GetValue(CItemCtx(), true)); }
CurrencyValue CRandomItems::GetAverageValue (int iLevel) // GetAverageValue // // Returns the average value. { int i; // If this is a dynamic table we need to compute all levels if (m_bDynamicLevelFrequency) { Metric rTotal = 0.0; InitTable(GenerateLevelFrequency(m_sLevelFrequency, iLevel)); m_iDynamicLevel = iLevel; for (i = 0; i < m_iCount; i++) { CItemType *pType = m_Table[i].pType; CurrencyValue ItemValue = CEconomyType::ExchangeToCredits(pType->GetCurrencyType(), pType->GetValue(CItemCtx(), true)); rTotal += (pType->GetNumberAppearing().GetAveValueFloat() * (Metric)ItemValue * (Metric)m_Table[i].iProbability / 1000.0); } return (CurrencyValue)(rTotal + 0.5); } // Otherwise the table is already initialized. else { // Average value is proportional to chances. Metric rTotal = 0.0; for (i = 0; i < m_iCount; i++) { CItemType *pType = m_Table[i].pType; CurrencyValue ItemValue = CEconomyType::ExchangeToCredits(pType->GetCurrencyType(), pType->GetValue(CItemCtx(), true)); rTotal += (pType->GetNumberAppearing().GetAveValueFloat() * (Metric)ItemValue * (Metric)m_Table[i].iProbability / 1000.0); } return (CurrencyValue)(rTotal + 0.5); } }
void CNewGameSession::SetShipClass (CShipClass *pClass, int x, int y, int cxWidth) // SetShipClass // // Sets the ship class { int i; g_pUniverse->SetLogImageLoad(false); const CPlayerSettings *pPlayerSettings = pClass->GetPlayerSettings(); const CVisualPalette &VI = m_HI.GetVisuals(); const CG16bitFont &MediumBoldFont = VI.GetFont(fontMediumBold); const CG16bitFont &SubTitleFont = VI.GetFont(fontSubTitle); // Ship class name SetShipClassName(pClass->GetNounPhrase(nounGeneric), x, y, cxWidth); SetShipClassDesc(pPlayerSettings->GetDesc(), x, y, cxWidth); // Offset int yOffset = SMALL_BUTTON_HEIGHT + SMALL_SPACING_VERT + MediumBoldFont.GetHeight() + 2 * SubTitleFont.GetHeight(); // Ship class image SetShipClassImage(pClass, x, y + yOffset, cxWidth); // Delete previous info DeleteElement(ID_SHIP_CLASS_INFO); // Create a sequencer for all class info components CAniSequencer *pClassInfo; CAniSequencer::Create(CVector(x, y + yOffset + SubTitleFont.GetHeight()), &pClassInfo); pClassInfo->SetID(ID_SHIP_CLASS_INFO); // Generate default devices for the ship class CDeviceDescList Devices; pClass->GenerateDevices(1, Devices); // Generate list of all weapons, sorted by level and name TSortMap<CString, CItem> RightSide; for (i = 0; i < Devices.GetCount(); i++) { const CItem &DevItem = Devices.GetDeviceDesc(i).Item; CDeviceClass *pDevice = Devices.GetDeviceClass(i); if (pDevice->GetCategory() == itemcatWeapon || pDevice->GetCategory() == itemcatLauncher) RightSide.Insert(strPatternSubst(CONSTLIT("%02d_%02d_%s"), 1, DevItem.GetLevel(), DevItem.GetNounPhrase(CItemCtx())), DevItem); } // Add shields TSortMap<CString, CItem> LeftSide; const SDeviceDesc *pShields = Devices.GetDeviceDescByName(devShields); if (pShields) RightSide.Insert(strPatternSubst(CONSTLIT("%02d_%02d_%s"), 2, pShields->Item.GetLevel(), pShields->Item.GetNounPhrase(CItemCtx())), pShields->Item); // Add armor RightSide.Insert(CONSTLIT("03"), CItem(g_pUniverse->GetItemType(0), SPECIAL_ARMOR)); // Add reactor LeftSide.Insert(CONSTLIT("01"), CItem(g_pUniverse->GetItemType(0), SPECIAL_REACTOR)); // Add engines LeftSide.Insert(CONSTLIT("02"), CItem(g_pUniverse->GetItemType(0), SPECIAL_DRIVE)); // Add cargo LeftSide.Insert(CONSTLIT("03"), CItem(g_pUniverse->GetItemType(0), SPECIAL_CARGO)); // Add misc devices for (i = 0; i < Devices.GetCount(); i++) { const CItem &DevItem = Devices.GetDeviceDesc(i).Item; CDeviceClass *pDevice = Devices.GetDeviceClass(i); if (pDevice->GetCategory() == itemcatMiscDevice) LeftSide.Insert(strPatternSubst(CONSTLIT("%02d_%02d_%s"), 4, DevItem.GetLevel(), DevItem.GetNounPhrase(CItemCtx())), DevItem); } // Add device slots LeftSide.Insert(CONSTLIT("05"), CItem(g_pUniverse->GetItemType(0), SPECIAL_DEVICE_SLOTS)); // Set the ship class info. All weapons go to the right of the ship image int xPos = (cxWidth / 2) + (SHIP_IMAGE_RECT_WIDTH / 2); int yPos = 0; int cxInfo = (cxWidth - xPos); for (i = 0; i < RightSide.GetCount(); i++) { int cyInfo; IAnimatron *pInfo; AddClassInfo(pClass, Devices, RightSide[i], xPos, yPos, cxInfo, 0, &cyInfo, &pInfo); pClassInfo->AddTrack(pInfo, 0); yPos += cyInfo + ITEM_INFO_PADDING_VERT; } // Misc devices go on the left xPos = (cxWidth / 2) - (SHIP_IMAGE_RECT_WIDTH / 2); yPos = 0; cxInfo = xPos; for (i = 0; i < LeftSide.GetCount(); i++) { int cyInfo; IAnimatron *pInfo; AddClassInfo(pClass, Devices, LeftSide[i], xPos, yPos, cxInfo, CUIHelper::OPTION_ITEM_RIGHT_ALIGN, &cyInfo, &pInfo); pClassInfo->AddTrack(pInfo, 0); yPos += cyInfo + ITEM_INFO_PADDING_VERT; } m_pRoot->AddLine(pClassInfo); g_pUniverse->SetLogImageLoad(true); }