| 277 | } |
| 278 | |
| 279 | List<DamageSource> Monster::damageSources() const { |
| 280 | List<DamageSource> damageSources = m_damageSources.get(); |
| 281 | |
| 282 | float levelPowerMultiplier = Root::singleton().functionDatabase()->function(m_monsterVariant.powerLevelFunction)->evaluate(*m_monsterLevel); |
| 283 | if (m_damageOnTouch && !m_monsterVariant.touchDamageConfig.isNull()) { |
| 284 | DamageSource damageSource(m_monsterVariant.touchDamageConfig); |
| 285 | if (auto damagePoly = damageSource.damageArea.ptr<PolyF>()) |
| 286 | damagePoly->rotate(m_movementController->rotation()); |
| 287 | damageSource.damage *= m_monsterVariant.touchDamageMultiplier * levelPowerMultiplier * m_statusController->stat("powerMultiplier"); |
| 288 | damageSource.sourceEntityId = entityId(); |
| 289 | damageSource.team = getTeam(); |
| 290 | damageSources.append(damageSource); |
| 291 | } |
| 292 | |
| 293 | auto animationDamageParts = m_monsterVariant.animationDamageParts; |
| 294 | for (auto pair : m_monsterVariant.animationDamageParts) { |
| 295 | if (!m_animationDamageParts.get().contains(pair.first)) |
| 296 | continue; |
| 297 | |
| 298 | String anchorPart = pair.second.getString("anchorPart"); |
| 299 | DamageSource ds = DamageSource(pair.second.get("damageSource")); |
| 300 | ds.damage *= levelPowerMultiplier * m_statusController->stat("powerMultiplier"); |
| 301 | ds.damageArea.call([this,&anchorPart](auto& poly) { |
| 302 | poly.transform(m_networkedAnimator.partTransformation(anchorPart)); |
| 303 | if (m_networkedAnimator.flipped()) |
| 304 | poly.flipHorizontal(m_networkedAnimator.flippedRelativeCenterLine()); |
| 305 | }); |
| 306 | if (ds.knockback.is<Vec2F>()) { |
| 307 | Vec2F knockback = ds.knockback.get<Vec2F>(); |
| 308 | knockback = m_networkedAnimator.partTransformation(anchorPart).transformVec2(knockback); |
| 309 | if (m_networkedAnimator.flipped()) |
| 310 | knockback = Vec2F(-knockback[0], knockback[1]); |
| 311 | ds.knockback = knockback; |
| 312 | } |
| 313 | |
| 314 | List<DamageSource> partSources; |
| 315 | if (auto line = ds.damageArea.maybe<Line2F>()) { |
| 316 | if (pair.second.getBool("checkLineCollision", false)) { |
| 317 | Line2F worldLine = line.value().translated(position()); |
| 318 | float length = worldLine.length(); |
| 319 | |
| 320 | auto bounces = pair.second.getInt("bounces", 0); |
| 321 | while (auto collision = world()->lineTileCollisionPoint(worldLine.min(), worldLine.max())) { |
| 322 | worldLine = Line2F(worldLine.min(), collision.value().first); |
| 323 | ds.damageArea = worldLine.translated(-position()); |
| 324 | length = length - worldLine.length(); |
| 325 | |
| 326 | if (--bounces >= 0 && length > 0.0f) { |
| 327 | partSources.append(ds); |
| 328 | ds = DamageSource(ds); |
| 329 | Vec2F dir = worldLine.direction(); |
| 330 | Vec2F normal = Vec2F((*collision).second); |
| 331 | Vec2F reflection = dir - (2 * dir.piecewiseMultiply(normal).sum() * normal); |
| 332 | if (ds.knockback.is<Vec2F>()) |
| 333 | ds.knockback = ds.knockback.get<Vec2F>().rotate(reflection.angleBetween(worldLine.direction())); |
| 334 | |
| 335 | worldLine.min() = (*collision).first; |
| 336 | worldLine.max() = worldLine.min() + (reflection * length); |
nothing calls this directly
no test coverage detected