| 1526 | } |
| 1527 | |
| 1528 | void WorldClient::handleDamageNotifications() { |
| 1529 | if (!inWorld()) |
| 1530 | return; |
| 1531 | |
| 1532 | auto renderParticle = [&](Vec2F position, float amount, String const& damageNumberParticleKind) { |
| 1533 | int displayValue = (int)ceil(amount - 0.1f); |
| 1534 | if (displayValue <= 0) |
| 1535 | return; |
| 1536 | Particle particle = Root::singleton().particleDatabase()->particle(damageNumberParticleKind); |
| 1537 | particle.position += position; |
| 1538 | particle.string = particle.string.replace("$dmg$", toString(displayValue)); |
| 1539 | m_particles->add(particle); |
| 1540 | }; |
| 1541 | |
| 1542 | eraseWhere(m_damageNumbers, [&](std::pair<DamageNumberKey, DamageNumber> const& entry) -> bool { |
| 1543 | if (Time::monotonicTime() - entry.second.timestamp > m_damageNotificationBatchDuration) { |
| 1544 | renderParticle(entry.second.position, entry.second.amount, entry.first.damageNumberParticleKind); |
| 1545 | return true; |
| 1546 | } |
| 1547 | return false; |
| 1548 | }); |
| 1549 | |
| 1550 | for (auto const& damageNotification : m_damageManager->pullPendingNotifications()) { |
| 1551 | auto damageDatabase = Root::singleton().damageDatabase(); |
| 1552 | DamageKind const& damageKind = damageDatabase->damageKind(damageNotification.damageSourceKind); |
| 1553 | ElementalType const& elementalType = damageDatabase->elementalType(damageKind.elementalType); |
| 1554 | |
| 1555 | auto damageNumberParticleKind = elementalType.damageNumberParticles.get(damageNotification.hitType); |
| 1556 | auto damageNumberKey = DamageNumberKey{ damageNumberParticleKind, damageNotification.sourceEntityId, damageNotification.targetEntityId}; |
| 1557 | |
| 1558 | |
| 1559 | DamageNumber number; |
| 1560 | if (m_damageNumbers.contains(damageNumberKey)) { |
| 1561 | number = m_damageNumbers.take(damageNumberKey); |
| 1562 | |
| 1563 | if (damageNotification.hitType == HitType::Kill) |
| 1564 | renderParticle(damageNotification.position, |
| 1565 | damageNotification.damageDealt + number.amount, |
| 1566 | damageNumberKey.damageNumberParticleKind); |
| 1567 | } else { |
| 1568 | if (damageNotification.hitType == HitType::Kill) |
| 1569 | renderParticle(damageNotification.position, damageNotification.damageDealt, damageNumberParticleKind); |
| 1570 | number.amount = 0; |
| 1571 | number.timestamp = Time::monotonicTime(); |
| 1572 | } |
| 1573 | |
| 1574 | if (damageNotification.hitType != HitType::Kill) { |
| 1575 | number.position = damageNotification.position; |
| 1576 | number.amount += damageNotification.damageDealt; |
| 1577 | m_damageNumbers[damageNumberKey] = number; |
| 1578 | } |
| 1579 | |
| 1580 | String material = damageNotification.targetMaterialKind; |
| 1581 | if (!material.empty() && damageKind.effects.contains(material)) { |
| 1582 | // default to normal hit |
| 1583 | HitType effectHitType = damageKind.effects.get(material).contains(damageNotification.hitType) ? damageNotification.hitType : HitType::Hit; |
| 1584 | m_samples.appendAll(soundsFromDefinition(damageKind.effects.get(material).get(effectHitType).sounds, damageNotification.position)); |
| 1585 |
nothing calls this directly
no test coverage detected