| 183 | } |
| 184 | |
| 185 | List<DamageSource> Projectile::damageSources() const { |
| 186 | if (m_onlyHitTerrain) |
| 187 | return {}; |
| 188 | |
| 189 | float time_per_frame = m_animationCycle / m_config->frameNumber; |
| 190 | if ((m_config->intangibleWindup && m_animationTimer < time_per_frame * m_config->windupFrames) |
| 191 | || (m_config->intangibleWinddown && m_timeToLive < time_per_frame * m_config->winddownFrames)) |
| 192 | return {}; |
| 193 | |
| 194 | EntityDamageTeam sourceTeam = getTeam(); |
| 195 | |
| 196 | auto statusEffects = m_config->statusEffects; |
| 197 | statusEffects.appendAll(m_parameters.getArray("statusEffects", {}).transformed(jsonToEphemeralStatusEffect)); |
| 198 | |
| 199 | float knockbackMagnitude = m_parameters.getFloat("knockback", m_config->knockback); |
| 200 | |
| 201 | DamageSource::Knockback knockback; |
| 202 | if (m_parameters.getBool("knockbackDirectional", m_config->knockbackDirectional)) |
| 203 | knockback = Vec2F::withAngle(m_movementController->rotation()) * knockbackMagnitude; |
| 204 | else |
| 205 | knockback = knockbackMagnitude; |
| 206 | |
| 207 | List<DamageSource> res; |
| 208 | auto addDamageSource = [&](DamageSource::DamageArea damageArea) { |
| 209 | res.append(DamageSource(m_damageType, damageArea, m_power * m_powerMultiplier, true, m_sourceEntity, sourceTeam, |
| 210 | m_damageRepeatGroup, m_damageRepeatTimeout, m_damageKind, statusEffects, knockback, m_rayCheckToSource)); |
| 211 | }; |
| 212 | |
| 213 | Vec2F positionDelta = world()->geometry().diff(m_travelLine.min(), m_travelLine.max()); |
| 214 | static float const MinimumDamageLineDelta = 0.1f; |
| 215 | bool useDamageLine = positionDelta.magnitudeSquared() >= square(MinimumDamageLineDelta); |
| 216 | if (useDamageLine) |
| 217 | addDamageSource(Line2F(positionDelta, Vec2F())); |
| 218 | |
| 219 | if (!m_config->damagePoly.isNull()) { |
| 220 | PolyF damagePoly = m_config->damagePoly; |
| 221 | if (m_config->flippable) { |
| 222 | auto angleSide = getAngleSide(m_movementController->rotation(), true); |
| 223 | if (angleSide.second == Direction::Left) |
| 224 | damagePoly.flipHorizontal(0); |
| 225 | damagePoly.rotate(angleSide.first); |
| 226 | } else { |
| 227 | damagePoly.rotate(m_movementController->rotation()); |
| 228 | } |
| 229 | addDamageSource(damagePoly); |
| 230 | } else if (!useDamageLine) { |
| 231 | addDamageSource(PolyF(RectF::withCenter(Vec2F(), Vec2F::filled(MinimumDamageLineDelta)))); |
| 232 | } |
| 233 | |
| 234 | return res; |
| 235 | } |
| 236 | |
| 237 | void Projectile::hitOther(EntityId entity, DamageRequest const&) { |
| 238 | if (!m_parameters.getBool("piercing", m_config->piercing)) { |
nothing calls this directly
no test coverage detected