#include "StarDamage.hpp" #include "StarJsonExtra.hpp" #include "StarDataStreamExtra.hpp" #include "StarRoot.hpp" namespace Star { DamageSource::DamageSource() : damageType(DamageType::NoDamage), damage(0.0), sourceEntityId(NullEntityId), rayCheck(false) {} DamageSource::DamageSource(Json const& config) { if (auto dtype = config.optString("damageType")) damageType = DamageTypeNames.getLeft(*dtype); else damageType = DamageType::Damage; if (config.contains("poly")) damageArea = jsonToPolyF(config.get("poly")); else if (config.contains("line")) damageArea = jsonToLine2F(config.get("line")); else throw StarException("No 'poly' or 'line' key in DamageSource config"); damage = config.getFloat("damage"); trackSourceEntity = config.getBool("trackSourceEntity", true); sourceEntityId = config.getInt("sourceEntityId", NullEntityId); if (auto tconfig = config.opt("team")) { team = EntityDamageTeam(*tconfig); } else { team.type = TeamTypeNames.getLeft(config.getString("teamType", "passive")); team.team = config.getUInt("teamNumber", 0); } damageRepeatGroup = config.optString("damageRepeatGroup"); damageRepeatTimeout = config.optFloat("damageRepeatTimeout"); damageSourceKind = config.getString("damageSourceKind", ""); statusEffects = config.getArray("statusEffects", {}).transformed(jsonToEphemeralStatusEffect); auto knockbackJson = config.get("knockback", Json(0.0f)); if (knockbackJson.isType(Json::Type::Array)) knockback = jsonToVec2F(knockbackJson); else knockback = knockbackJson.toFloat(); rayCheck = config.getBool("rayCheck", false); } DamageSource::DamageSource(DamageType damageType, DamageArea damageArea, float damage, bool trackSourceEntity, EntityId sourceEntityId, EntityDamageTeam team, Maybe damageRepeatGroup, Maybe damageRepeatTimeout, String damageSourceKind, List statusEffects, Knockback knockback, bool rayCheck) : damageType(std::move(damageType)), damageArea(std::move(damageArea)), damage(std::move(damage)), trackSourceEntity(std::move(trackSourceEntity)), sourceEntityId(std::move(sourceEntityId)), team(std::move(team)), damageRepeatGroup(std::move(damageRepeatGroup)), damageRepeatTimeout(std::move(damageRepeatTimeout)), damageSourceKind(std::move(damageSourceKind)), statusEffects(std::move(statusEffects)), knockback(std::move(knockback)), rayCheck(std::move(rayCheck)) {} Json DamageSource::toJson() const { Json damageAreaJson; if (auto p = damageArea.ptr()) damageAreaJson = jsonFromPolyF(*p); else if (auto l = damageArea.ptr()) damageAreaJson = jsonFromLine2F(*l); Json knockbackJson; if (auto p = knockback.ptr()) knockbackJson = *p; else if (auto p = knockback.ptr()) knockbackJson = jsonFromVec2F(*p); return JsonObject{{"damageType", DamageTypeNames.getRight(damageType)}, {"damageArea", damageAreaJson}, {"damage", damage}, {"trackSourceEntity", trackSourceEntity}, {"sourceEntityId", sourceEntityId}, {"team", team.toJson()}, {"damageRepeatGroup", jsonFromMaybe(damageRepeatGroup)}, {"damageRepeatTimeout", jsonFromMaybe(damageRepeatTimeout)}, {"damageSourceKind", damageSourceKind}, {"statusEffects", statusEffects.transformed(jsonFromEphemeralStatusEffect)}, {"knockback", knockbackJson}, {"rayCheck", rayCheck}}; } bool DamageSource::intersectsWithPoly(WorldGeometry const& geometry, PolyF const& targetPoly) const { if (auto poly = damageArea.ptr()) return geometry.polyIntersectsPoly(*poly, targetPoly); else if (auto line = damageArea.ptr()) return geometry.lineIntersectsPoly(*line, targetPoly); else return false; } Vec2F DamageSource::knockbackMomentum(WorldGeometry const& worldGeometry, Vec2F const& targetCenter) const { if (auto v = knockback.ptr()) { return *v; } else if (auto s = knockback.ptr()) { if (*s != 0) { if (auto poly = damageArea.ptr()) return worldGeometry.diff(targetCenter, poly->center()).normalized() * *s; else if (auto line = damageArea.ptr()) return vnorm(line->diff()) * *s; } } return Vec2F(); } bool DamageSource::operator==(DamageSource const& rhs) const { return tie(damageType, damageArea, damage, trackSourceEntity, sourceEntityId, team, damageSourceKind, statusEffects, knockback, rayCheck) == tie(rhs.damageType, rhs.damageArea, rhs.damage, rhs.trackSourceEntity, rhs.sourceEntityId, rhs.team, rhs.damageSourceKind, rhs.statusEffects, rhs.knockback, rhs.rayCheck); } DamageSource& DamageSource::translate(Vec2F const& position) { if (auto poly = damageArea.ptr()) poly->translate(position); else if (auto line = damageArea.ptr()) line->translate(position); return *this; } DataStream& operator<<(DataStream& ds, DamageSource const& damageSource) { ds.write(damageSource.damageType); ds.write(damageSource.damageArea); ds.write(damageSource.damage); ds.write(damageSource.trackSourceEntity); ds.write(damageSource.sourceEntityId); ds.write(damageSource.team); ds.write(damageSource.damageRepeatGroup); ds.write(damageSource.damageRepeatTimeout); ds.write(damageSource.damageSourceKind); ds.write(damageSource.statusEffects); ds.write(damageSource.knockback); ds.write(damageSource.rayCheck); return ds; } DataStream& operator>>(DataStream& ds, DamageSource& damageSource) { ds.read(damageSource.damageType); ds.read(damageSource.damageArea); ds.read(damageSource.damage); ds.read(damageSource.trackSourceEntity); ds.read(damageSource.sourceEntityId); ds.read(damageSource.team); ds.read(damageSource.damageRepeatGroup); ds.read(damageSource.damageRepeatTimeout); ds.read(damageSource.damageSourceKind); ds.read(damageSource.statusEffects); ds.read(damageSource.knockback); ds.read(damageSource.rayCheck); return ds; } DamageRequest::DamageRequest() : hitType(HitType::Hit), damageType(DamageType::Damage), damage(0.0f), sourceEntityId(NullEntityId) {} DamageRequest::DamageRequest(Json const& v) { hitType = HitTypeNames.getLeft(v.getString("hitType", "hit")); damageType = DamageTypeNames.getLeft(v.getString("damageType", "damage")); damage = v.getFloat("damage"); knockbackMomentum = jsonToVec2F(v.get("knockbackMomentum", JsonArray{0, 0})); sourceEntityId = v.getInt("sourceEntityId", NullEntityId); damageSourceKind = v.getString("damageSourceKind", ""); statusEffects = v.getArray("statusEffects", {}).transformed(jsonToEphemeralStatusEffect); } DamageRequest::DamageRequest(HitType hitType, DamageType damageType, float damage, Vec2F const& knockbackMomentum, EntityId sourceEntityId, String const& damageSourceKind, List const& statusEffects) : hitType(hitType), damageType(damageType), damage(damage), knockbackMomentum(knockbackMomentum), sourceEntityId(sourceEntityId), damageSourceKind(damageSourceKind), statusEffects(statusEffects) {} Json DamageRequest::toJson() const { return JsonObject{{"hitType", HitTypeNames.getRight(hitType)}, {"damageType", DamageTypeNames.getRight(damageType)}, {"damage", damage}, {"knockbackMomentum", jsonFromVec2F(knockbackMomentum)}, {"sourceEntityId", sourceEntityId}, {"damageSourceKind", damageSourceKind}, {"statusEffects", statusEffects.transformed(jsonFromEphemeralStatusEffect)}}; } DataStream& operator<<(DataStream& ds, DamageRequest const& damageRequest) { ds << damageRequest.hitType; ds << damageRequest.damageType; ds << damageRequest.damage; ds << damageRequest.knockbackMomentum; ds << damageRequest.sourceEntityId; ds << damageRequest.damageSourceKind; ds << damageRequest.statusEffects; return ds; } DataStream& operator>>(DataStream& ds, DamageRequest& damageRequest) { ds >> damageRequest.hitType; ds >> damageRequest.damageType; ds >> damageRequest.damage; ds >> damageRequest.knockbackMomentum; ds >> damageRequest.sourceEntityId; ds >> damageRequest.damageSourceKind; ds >> damageRequest.statusEffects; return ds; } DamageNotification::DamageNotification() : sourceEntityId(), targetEntityId(), damageDealt(), healthLost() {} DamageNotification::DamageNotification(Json const& v) { sourceEntityId = v.getInt("sourceEntityId"); targetEntityId = v.getInt("targetEntityId"); position = jsonToVec2F(v.get("position")); damageDealt = v.getFloat("damageDealt"); healthLost = v.getFloat("healthLost"); hitType = HitTypeNames.getLeft(v.getString("hitType")); damageSourceKind = v.getString("damageSourceKind"); targetMaterialKind = v.getString("targetMaterialKind"); } DamageNotification::DamageNotification(EntityId sourceEntityId, EntityId targetEntityId, Vec2F position, float damageDealt, float healthLost, HitType hitType, String damageSourceKind, String targetMaterialKind) : sourceEntityId(sourceEntityId), targetEntityId(targetEntityId), position(position), damageDealt(damageDealt), healthLost(healthLost), hitType(hitType), damageSourceKind(std::move(damageSourceKind)), targetMaterialKind(std::move(targetMaterialKind)) {} Json DamageNotification::toJson() const { return JsonObject{{"sourceEntityId", sourceEntityId}, {"targetEntityId", targetEntityId}, {"position", jsonFromVec2F(position)}, {"damageDealt", damageDealt}, {"healthLost", healthLost}, {"hitType", HitTypeNames.getRight(hitType)}, {"damageSourceKind", damageSourceKind}, {"targetMaterialKind", targetMaterialKind}}; } DataStream& operator<<(DataStream& ds, DamageNotification const& damageNotification) { ds.viwrite(damageNotification.sourceEntityId); ds.viwrite(damageNotification.targetEntityId); ds.vfwrite(damageNotification.position[0], 0.01f); ds.vfwrite(damageNotification.position[1], 0.01f); ds.write(damageNotification.damageDealt); ds.write(damageNotification.healthLost); ds.write(damageNotification.hitType); ds.write(damageNotification.damageSourceKind); ds.write(damageNotification.targetMaterialKind); return ds; } DataStream& operator>>(DataStream& ds, DamageNotification& damageNotification) { ds.viread(damageNotification.sourceEntityId); ds.viread(damageNotification.targetEntityId); ds.vfread(damageNotification.position[0], 0.01f); ds.vfread(damageNotification.position[1], 0.01f); ds.read(damageNotification.damageDealt); ds.read(damageNotification.healthLost); ds.read(damageNotification.hitType); ds.read(damageNotification.damageSourceKind); ds.read(damageNotification.targetMaterialKind); return ds; } }