bool CArmorClass::IsReflective (CItemCtx &ItemCtx, const DamageDesc &Damage) // IsReflective // // Returns TRUE if the armor reflects this damage { const CItemEnhancement &Mods = ItemCtx.GetMods(); int iReflectChance = 0; // Base armor chance if (m_Reflective.InSet(Damage.GetDamageType())) iReflectChance = MAX_REFLECTION_CHANCE; // Mods int iModReflect; if (Mods.IsNotEmpty() && Mods.IsReflective(Damage, &iModReflect)) iReflectChance = Max(iReflectChance, iModReflect); // Done if (iReflectChance) { CInstalledArmor *pSect = ItemCtx.GetArmor(); int iMaxHP = GetMaxHP(ItemCtx); int iHP = (pSect ? pSect->GetHitPoints() : iMaxHP); // Adjust based on how damaged the armor is iReflectChance = (iMaxHP > 0 ? iHP * iReflectChance / iMaxHP : iReflectChance); return (mathRandom(1, 100) <= iReflectChance); } else return false; }
void CGSelectorArea::PaintInstalledItem (CG32bitImage &Dest, const RECT &rcRect, const SEntry &Entry) // PaintInstalledItem // // Paints the installed item. { const CItem &Item = Entry.pItemCtx->GetItem(); if (Item.GetType() == NULL) return; CSpaceObject *pSource = Entry.pItemCtx->GetSource(); CInstalledArmor *pArmor = Entry.pItemCtx->GetArmor(); CInstalledDevice *pDevice = Entry.pItemCtx->GetDevice(); CDeviceClass *pDeviceClass; // Paint the item icon bool bGrayed = (pDevice && !pDevice->IsEnabled()); int xIcon = rcRect.left + (RectWidth(rcRect) - ITEM_ICON_WIDTH) / 2; int yIcon = rcRect.top + ITEM_ENTRY_PADDING_TOP; DrawItemTypeIcon(Dest, xIcon, yIcon, Item.GetType(), ITEM_ICON_WIDTH, ITEM_ICON_HEIGHT, bGrayed); // Paint the name of the item below. RECT rcText; rcText.left = rcRect.left + ITEM_ENTRY_PADDING_LEFT; rcText.right = rcRect.right - ITEM_ENTRY_PADDING_RIGHT; rcText.top = yIcon + ITEM_ICON_HEIGHT; rcText.bottom = rcRect.bottom; m_VI.GetFont(fontMedium).DrawText(Dest, rcText, m_rgbTextColor, Item.GetNounPhrase(nounShort | nounNoModifiers), 0, CG16bitFont::AlignCenter); // If this is an armor segment, then paint HP, etc. if (pArmor) { int x = rcRect.right - ITEM_ENTRY_PADDING_RIGHT; int y = rcRect.top + ITEM_ENTRY_PADDING_TOP; // HP CString sHP = strFromInt(pArmor->GetHitPoints()); m_VI.GetFont(fontLarge).DrawText(Dest, x, y, m_rgbTextColor, sHP, CG16bitFont::AlignRight); y += m_VI.GetFont(fontLarge).GetHeight(); // Damage int iMaxHP = pArmor->GetMaxHP(pSource); if (iMaxHP != pArmor->GetHitPoints() && iMaxHP > 0) { int iPercent = ((1000 * pArmor->GetHitPoints() / iMaxHP) + 5) / 10; CString sPercent = strPatternSubst(CONSTLIT("%d%%"), iPercent); m_VI.GetFont(fontMedium).DrawText(Dest, x, y, m_VI.GetColor(colorTextDockWarning), sPercent, CG16bitFont::AlignRight); y += m_VI.GetFont(fontMedium).GetHeight(); } // Modifiers if (pArmor->GetMods().IsNotEmpty()) { CString sMods = Item.GetEnhancedDesc(pSource); if (!sMods.IsBlank()) { bool bIsDisadvantage = *sMods.GetASCIIZPointer() == '-'; CG32bitPixel rgbBackColor = (bIsDisadvantage ? m_VI.GetColor(colorAreaDisadvantage) : m_VI.GetColor(colorAreaAdvantage)); CG32bitPixel rgbTextColor = (bIsDisadvantage ? m_VI.GetColor(colorTextDisadvantage) : m_VI.GetColor(colorTextAdvantage)); PaintModifier(Dest, x, y, sMods, rgbTextColor, rgbBackColor, &y); } } } // If this is a device, then paint device-specific stuff else if (pDevice && (pDeviceClass = pDevice->GetClass())) { int x = rcRect.right - ITEM_ENTRY_PADDING_RIGHT; int y = rcRect.top + ITEM_ENTRY_PADDING_TOP; // HP if (pDevice->IsEnabled()) { if (pDevice->GetCategory() == itemcatShields) { int iHP; int iMaxHP; pDevice->GetStatus(pSource, &iHP, &iMaxHP); CString sHP = strFromInt(iHP); m_VI.GetFont(fontLarge).DrawText(Dest, x, y, m_rgbTextColor, sHP, CG16bitFont::AlignRight); y += m_VI.GetFont(fontLarge).GetHeight(); // Shield level if (iMaxHP != iHP && iMaxHP > 0) { int iPercent = ((1000 * iHP / iMaxHP) + 5) / 10; CString sPercent = strPatternSubst(CONSTLIT("%d%%"), iPercent); m_VI.GetFont(fontMedium).DrawText(Dest, x, y, m_VI.GetColor(colorTextShields), sPercent, CG16bitFont::AlignRight); y += m_VI.GetFont(fontMedium).GetHeight(); } } } else PaintModifier(Dest, x, y, CONSTLIT("disabled"), m_VI.GetColor(colorTextNormal), CG32bitPixel::Null(), &y); // External if (pDevice->IsExternal() || pDeviceClass->IsExternal()) PaintModifier(Dest, x, y, CONSTLIT("external"), m_VI.GetColor(colorTextNormal), CG32bitPixel::Null(), &y); // Damaged if (pDevice->IsDamaged()) PaintModifier(Dest, x, y, CONSTLIT("damaged"), m_VI.GetColor(colorTextDisadvantage), m_VI.GetColor(colorAreaDisadvantage), &y); if (pDevice->IsDisrupted()) PaintModifier(Dest, x, y, CONSTLIT("ionized"), m_VI.GetColor(colorTextDisadvantage), m_VI.GetColor(colorAreaDisadvantage), &y); // Modifiers if (pDevice->GetEnhancements() != NULL) { CString sMods = pDevice->GetEnhancedDesc(pSource, &Item); if (!sMods.IsBlank()) { bool bIsDisadvantage = *sMods.GetASCIIZPointer() == '-'; CG32bitPixel rgbBackColor = (bIsDisadvantage ? m_VI.GetColor(colorAreaDisadvantage) : m_VI.GetColor(colorAreaAdvantage)); CG32bitPixel rgbTextColor = (bIsDisadvantage ? m_VI.GetColor(colorTextDisadvantage) : m_VI.GetColor(colorTextAdvantage)); PaintModifier(Dest, x, y, sMods, rgbTextColor, rgbBackColor, &y); } } } }
void CArmorClass::CalcDamageEffects (CItemCtx &ItemCtx, SDamageCtx &Ctx) // CalcDamageEffects // // Initialize the damage effects based on the damage and on this armor type. { CSpaceObject *pSource = ItemCtx.GetSource(); CInstalledArmor *pArmor = ItemCtx.GetArmor(); // Compute all the effects (if we don't have installed armor, then the // caller is responsible for setting this). if (pArmor) Ctx.iHPLeft = pArmor->GetHitPoints(); // Reflect Ctx.bReflect = (IsReflective(ItemCtx, Ctx.Damage) && Ctx.iDamage > 0); // Disintegration int iDisintegration = Ctx.Damage.GetDisintegrationDamage(); Ctx.bDisintegrate = (iDisintegration > 0 && !IsDisintegrationImmune(ItemCtx)); // Shatter int iShatter = Ctx.Damage.GetShatterDamage(); if (iShatter) { // Compute the threshold mass. Below this size, we shatter the object int iMassLimit = 10 * mathPower(5, iShatter); Ctx.bShatter = (pSource && pSource->GetMass() < iMassLimit); } else Ctx.bShatter = false; // Blinding int iBlinding = Ctx.Damage.GetBlindingDamage(); if (iBlinding && !IsBlindingDamageImmune(ItemCtx)) { // The chance of being blinded is dependent // on the rating. int iChance = 4 * iBlinding * iBlinding * GetBlindingDamageAdj() / 100; Ctx.bBlind = (mathRandom(1, 100) <= iChance); Ctx.iBlindTime = Ctx.iDamage * g_TicksPerSecond / 2; } else Ctx.bBlind = false; // EMP int iEMP = Ctx.Damage.GetEMPDamage(); if (iEMP && !IsEMPDamageImmune(ItemCtx)) { // The chance of being paralyzed is dependent // on the EMP rating. int iChance = 4 * iEMP * iEMP * GetEMPDamageAdj() / 100; Ctx.bParalyze = (mathRandom(1, 100) <= iChance); Ctx.iParalyzeTime = Ctx.iDamage * g_TicksPerSecond / 2; } else Ctx.bParalyze = false; // Device disrupt int iDeviceDisrupt = Ctx.Damage.GetDeviceDisruptDamage(); if (iDeviceDisrupt && !IsDeviceDamageImmune(ItemCtx)) { // The chance of damaging a device depends on the rating. int iChance = 4 * iDeviceDisrupt * iDeviceDisrupt * GetDeviceDamageAdj() / 100; Ctx.bDeviceDisrupt = (mathRandom(1, 100) <= iChance); Ctx.iDisruptTime = 2 * Ctx.iDamage * g_TicksPerSecond; } else Ctx.bDeviceDisrupt = false; // Device damage int iDeviceDamage = Ctx.Damage.GetDeviceDamage(); if (iDeviceDamage && !IsDeviceDamageImmune(ItemCtx)) { // The chance of damaging a device depends on the rating. int iChance = 4 * iDeviceDamage * iDeviceDamage * GetDeviceDamageAdj() / 100; Ctx.bDeviceDamage = (mathRandom(1, 100) <= iChance); } else Ctx.bDeviceDamage = false; // Radiation int iRadioactive = Ctx.Damage.GetRadiationDamage(); Ctx.bRadioactive = (iRadioactive > 0 && !IsRadiationImmune(ItemCtx)); // Some effects decrease damage if (iBlinding || iEMP) Ctx.iDamage = 0; else if (iDeviceDamage) Ctx.iDamage = Ctx.iDamage / 2; }
EDamageResults CArmorClass::AbsorbDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // AbsorbDamage // // Handles getting hit by damage. // // Returns damageNoDamage if all the damage was absorbed and no further processing is necessary // Returns damageDestroyed if the source was destroyed // Returns damageArmorHit if source was damage and further processing (destroy check) is needed // // Sets Ctx.iDamage to the amount of hit points left after damage absorption. { CSpaceObject *pSource = ItemCtx.GetSource(); CInstalledArmor *pArmor = ItemCtx.GetArmor(); if (pSource == NULL || pArmor == NULL) return damageNoDamage; // Compute all the effects (this initializes elements in Ctx). CalcDamageEffects(ItemCtx, Ctx); // First give custom weapons a chance bool bCustomDamage = Ctx.pDesc->FireOnDamageArmor(Ctx); if (pSource->IsDestroyed()) return damageDestroyed; // Damage adjustment CalcAdjustedDamage(ItemCtx, Ctx); // If the armor has custom code to deal with damage, handle it here. FireOnArmorDamage(ItemCtx, Ctx); if (pSource->IsDestroyed()) return damageDestroyed; // If this armor section reflects this kind of damage then // send the damage on if (Ctx.bReflect) { if (Ctx.pCause) Ctx.pCause->CreateReflection(Ctx.vHitPos, (Ctx.iDirection + 120 + mathRandom(0, 120)) % 360); return damageNoDamage; } // If this is a disintegration attack, then disintegrate the ship if (Ctx.bDisintegrate) { if (!pSource->OnDestroyCheck(killedByDisintegration, Ctx.Attacker)) return damageNoDamage; pSource->Destroy(killedByDisintegration, Ctx.Attacker); return damageDestroyed; } // If this is a shatter attack, see if the ship is destroyed if (Ctx.bShatter) { if (!pSource->OnDestroyCheck(killedByShatter, Ctx.Attacker)) return damageNoDamage; pSource->Destroy(killedByShatter, Ctx.Attacker); return damageDestroyed; } // If this is a paralysis attack and we've gotten past the shields // then freeze the ship. if (Ctx.bParalyze) pSource->MakeParalyzed(Ctx.iParalyzeTime); // If this is blinding damage then our sensors are disabled if (Ctx.bBlind) pSource->MakeBlind(Ctx.iBlindTime); // If this attack is radioactive, then contaminate the ship if (Ctx.bRadioactive) pSource->OnHitByRadioactiveDamage(Ctx); // If this is device damage, then see if any device is damaged if (Ctx.bDeviceDamage) pSource->OnHitByDeviceDamage(); if (Ctx.bDeviceDisrupt) pSource->OnHitByDeviceDisruptDamage(Ctx.iDisruptTime); // Create a hit effect. (Many weapons show an effect even if no damage was // done.) Ctx.pDesc->CreateHitEffect(pSource->GetSystem(), Ctx); // If no damage has reached us, then we're done if (Ctx.iDamage == 0 && !bCustomDamage) return damageNoDamage; // Give source events a chance to change the damage before we // subtract from armor. if (pSource->HasOnDamageEvent()) { pSource->FireOnDamage(Ctx); if (pSource->IsDestroyed()) return damageDestroyed; } // Take damage if (Ctx.iDamage <= pArmor->GetHitPoints()) { pArmor->IncHitPoints(-Ctx.iDamage); Ctx.iDamage = 0; } else { Ctx.iDamage -= pArmor->GetHitPoints(); pArmor->SetHitPoints(0); } return damageArmorHit; }
void CArmorDisplay::Update (void) // Update // // Updates buffer from data { int i; if (m_pPlayer == NULL) return; CShip *pShip = m_pPlayer->GetShip(); const CPlayerSettings *pSettings = pShip->GetClass()->GetPlayerSettings(); CItemListManipulator ItemList(pShip->GetItemList()); const CG16bitFont &SmallFont = m_pPlayer->GetTrans()->GetFonts().Small; m_Text.DeleteAll(); // If we've changed ships then we need to delete the painters if (m_dwCachedShipID != pShip->GetID()) { if (m_pShieldPainter) { m_pShieldPainter->Delete(); m_pShieldPainter = NULL; } m_dwCachedShipID = pShip->GetID(); } // Erase everything m_Buffer.Fill(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, DEFAULT_TRANSPARENT_COLOR); // Figure out the status of the shields int iHP = 0; int iMaxHP = 10; CInstalledDevice *pShield = pShip->GetNamedDevice(devShields); if (pShield) pShield->GetStatus(pShip, &iHP, &iMaxHP); // Draw the base ship image, if we have it const SArmorImageDesc &ArmorDesc = pSettings->GetArmorDesc(); if (!ArmorDesc.ShipImage.IsEmpty()) { const RECT &rcShip = ArmorDesc.ShipImage.GetImageRect(); m_Buffer.ColorTransBlt(rcShip.left, rcShip.top, RectWidth(rcShip), RectHeight(rcShip), 255, ArmorDesc.ShipImage.GetImage(NULL_STR), DESCRIPTION_WIDTH + ((SHIELD_IMAGE_WIDTH - RectWidth(rcShip)) / 2), (SHIELD_IMAGE_HEIGHT - RectHeight(rcShip)) / 2); } // Draw the old-style shields const SShieldImageDesc &ShieldDesc = pSettings->GetShieldDesc(); if (!ShieldDesc.pShieldEffect) { int iWhole = (iMaxHP > 0 ? (iHP * 100) / iMaxHP : 100); int iIndex = (100 - iWhole) / 20; const RECT &rcShield = ShieldDesc.Image.GetImageRect(); m_Buffer.ColorTransBlt(rcShield.left, rcShield.top + (RectHeight(rcShield) * iIndex), RectWidth(rcShield), RectHeight(rcShield), 255, ShieldDesc.Image.GetImage(NULL_STR), DESCRIPTION_WIDTH + ((SHIELD_IMAGE_WIDTH - RectWidth(rcShield)) / 2), (SHIELD_IMAGE_HEIGHT - RectHeight(rcShield)) / 2); } if (pShield) { m_Buffer.Fill(SHIELD_HP_DISPLAY_X, SHIELD_HP_DISPLAY_Y, SHIELD_HP_DISPLAY_WIDTH, SHIELD_HP_DISPLAY_HEIGHT, SHIELD_HP_DISPLAY_BACK_COLOR); CString sHP = strFromInt(iHP); int cxWidth = m_pFonts->Medium.MeasureText(sHP, NULL); m_pFonts->Medium.DrawText(m_Buffer, SHIELD_HP_DISPLAY_X + (SHIELD_HP_DISPLAY_WIDTH - cxWidth) / 2, SHIELD_HP_DISPLAY_Y - 1, CG16bitImage::LightenPixel(m_pFonts->wAltGreenColor, 60), sHP); DrawBrokenLine(m_Buffer, 0, SHIELD_HP_DISPLAY_Y, SHIELD_HP_DISPLAY_X, SHIELD_HP_DISPLAY_Y, 0, SHIELD_HP_DISPLAY_LINE_COLOR); WORD wColor; if (pShield->IsEnabled() && !pShield->IsDamaged() && !pShield->IsDisrupted()) wColor = m_pFonts->wAltGreenColor; else wColor = DISABLED_TEXT_COLOR; CString sShieldName = pShield->GetClass()->GetName(); int cyHeight; cxWidth = m_pFonts->Medium.MeasureText(sShieldName, &cyHeight); // Add the shield name to list of text to paint STextPaint *pPaint = m_Text.Insert(); pPaint->sText = sShieldName; pPaint->x = 0; pPaint->y = SHIELD_HP_DISPLAY_Y; pPaint->pFont = &m_pFonts->Medium; pPaint->wColor = wColor; // Paint the modifiers if (pShield->GetMods().IsNotEmpty() || pShield->GetBonus() != 0) { pShip->SetCursorAtNamedDevice(ItemList, devShields); CString sMods = pShield->GetEnhancedDesc(pShip, &ItemList.GetItemAtCursor()); if (!sMods.IsBlank()) { bool bDisadvantage = (*(sMods.GetASCIIZPointer()) == '-'); int cx = SmallFont.MeasureText(sMods); m_Buffer.Fill(SHIELD_HP_DISPLAY_X - cx - 8, SHIELD_HP_DISPLAY_Y, cx + 8, SHIELD_HP_DISPLAY_HEIGHT, (bDisadvantage ? ARMOR_DAMAGED_BACK_COLOR : ARMOR_ENHANCE_BACK_COLOR)); SmallFont.DrawText(m_Buffer, SHIELD_HP_DISPLAY_X - cx - 4, SHIELD_HP_DISPLAY_Y + (SHIELD_HP_DISPLAY_HEIGHT - SmallFont.GetHeight()) / 2, (bDisadvantage ? ARMOR_DAMAGED_TEXT_COLOR : ARMOR_ENHANCE_TEXT_COLOR), sMods); } } } // Draw armor int iArmorCount = Min(pShip->GetArmorSectionCount(), pSettings->GetArmorDescCount()); for (i = 0; i < iArmorCount; i++) { const SArmorSegmentImageDesc *pImage = &pSettings->GetArmorDesc(i); CInstalledArmor *pArmor = pShip->GetArmorSection(i); int iMaxHP = pArmor->GetMaxHP(pShip); int iWhole = (iMaxHP == 0 ? 100 : (pArmor->GetHitPoints() * 100) / iMaxHP); int iIndex = (100 - iWhole) / 20; if (iIndex < 5) { const RECT &rcImage = pImage->Image.GetImageRect(); m_Buffer.ColorTransBlt(rcImage.left, rcImage.top + iIndex * RectHeight(rcImage), RectWidth(rcImage), RectHeight(rcImage), 255, pImage->Image.GetImage(NULL_STR), DESCRIPTION_WIDTH + pImage->xDest, pImage->yDest); } } // Draw the new style shields on top if (ShieldDesc.pShieldEffect) { int x = DESCRIPTION_WIDTH + SHIELD_IMAGE_WIDTH / 2; int y = SHIELD_IMAGE_HEIGHT / 2; SViewportPaintCtx Ctx; Ctx.iTick = g_pUniverse->GetTicks(); Ctx.iVariant = (iMaxHP > 0 ? (iHP * 100) / iMaxHP : 0); Ctx.iDestiny = pShip->GetDestiny(); Ctx.iRotation = 90; if (m_pShieldPainter == NULL) m_pShieldPainter = ShieldDesc.pShieldEffect->CreatePainter(); m_pShieldPainter->Paint(m_Buffer, x, y, Ctx); } // Draw armor names for (i = 0; i < iArmorCount; i++) { const SArmorSegmentImageDesc *pImage = &pSettings->GetArmorDesc(i); CInstalledArmor *pArmor = pShip->GetArmorSection(i); // Paint the HPs if (i == m_iSelection) { m_Buffer.Fill(DESCRIPTION_WIDTH + pImage->xHP - 1, pImage->yHP - 1, HP_DISPLAY_WIDTH + 2, HP_DISPLAY_HEIGHT + 2, CG16bitImage::DarkenPixel(m_pFonts->wSelectBackground, 128)); } else { m_Buffer.Fill(DESCRIPTION_WIDTH + pImage->xHP, pImage->yHP, HP_DISPLAY_WIDTH, HP_DISPLAY_HEIGHT, HP_DISPLAY_BACK_COLOR); } CString sHP = strFromInt(pArmor->GetHitPoints()); int cxWidth = m_pFonts->Medium.MeasureText(sHP, NULL); m_pFonts->Medium.DrawText(m_Buffer, DESCRIPTION_WIDTH + pImage->xHP + (HP_DISPLAY_WIDTH - cxWidth) / 2, pImage->yHP - 1, m_pFonts->wTitleColor, sHP); // Paint the armor name line DrawBrokenLine(m_Buffer, 0, pImage->yName + m_pFonts->Medium.GetHeight(), DESCRIPTION_WIDTH + pImage->xHP + pImage->xNameDestOffset, pImage->yHP + pImage->yNameDestOffset, pImage->cxNameBreak, (i == m_iSelection ? CG16bitImage::DarkenPixel(m_pFonts->wSelectBackground, 128) : ARMOR_LINE_COLOR)); // Paint the armor names CString sName = pArmor->GetClass()->GetShortName(); int cy; int cx = m_pFonts->Medium.MeasureText(sName, &cy) + 4; if (i == m_iSelection) { m_Buffer.Fill(0, pImage->yName, cx, cy, CG16bitImage::DarkenPixel(m_pFonts->wSelectBackground, 128)); } STextPaint *pPaint = m_Text.Insert(); pPaint->sText = sName; pPaint->x = 2; pPaint->y = pImage->yName; pPaint->pFont = &m_pFonts->Medium; pPaint->wColor = m_pFonts->wTitleColor; // Paint the modifiers if (pArmor->GetMods().IsNotEmpty()) { pShip->SetCursorAtArmor(ItemList, i); CString sMods = ItemList.GetItemAtCursor().GetEnhancedDesc(pShip); if (!sMods.IsBlank()) { int cx = SmallFont.MeasureText(sMods); m_Buffer.Fill(ARMOR_ENHANCE_X - cx - 4, pImage->yName + m_pFonts->Medium.GetHeight() - HP_DISPLAY_HEIGHT, cx + 8, HP_DISPLAY_HEIGHT, ARMOR_ENHANCE_BACK_COLOR); SmallFont.DrawText(m_Buffer, ARMOR_ENHANCE_X - cx, pImage->yName + 3, ARMOR_ENHANCE_TEXT_COLOR, sMods); } } } }