312 lines
11 KiB
C++
312 lines
11 KiB
C++
|
#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<String> damageRepeatGroup,
|
||
|
Maybe<float> damageRepeatTimeout,
|
||
|
String damageSourceKind,
|
||
|
List<EphemeralStatusEffect> statusEffects,
|
||
|
Knockback knockback,
|
||
|
bool rayCheck)
|
||
|
: damageType(move(damageType)),
|
||
|
damageArea(move(damageArea)),
|
||
|
damage(move(damage)),
|
||
|
trackSourceEntity(move(trackSourceEntity)),
|
||
|
sourceEntityId(move(sourceEntityId)),
|
||
|
team(move(team)),
|
||
|
damageRepeatGroup(move(damageRepeatGroup)),
|
||
|
damageRepeatTimeout(move(damageRepeatTimeout)),
|
||
|
damageSourceKind(move(damageSourceKind)),
|
||
|
statusEffects(move(statusEffects)),
|
||
|
knockback(move(knockback)),
|
||
|
rayCheck(move(rayCheck)) {}
|
||
|
|
||
|
Json DamageSource::toJson() const {
|
||
|
Json damageAreaJson;
|
||
|
if (auto p = damageArea.ptr<PolyF>())
|
||
|
damageAreaJson = jsonFromPolyF(*p);
|
||
|
else if (auto l = damageArea.ptr<Line2F>())
|
||
|
damageAreaJson = jsonFromLine2F(*l);
|
||
|
|
||
|
Json knockbackJson;
|
||
|
if (auto p = knockback.ptr<float>())
|
||
|
knockbackJson = *p;
|
||
|
else if (auto p = knockback.ptr<Vec2F>())
|
||
|
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<PolyF>())
|
||
|
return geometry.polyIntersectsPoly(*poly, targetPoly);
|
||
|
else if (auto line = damageArea.ptr<Line2F>())
|
||
|
return geometry.lineIntersectsPoly(*line, targetPoly);
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Vec2F DamageSource::knockbackMomentum(WorldGeometry const& worldGeometry, Vec2F const& targetCenter) const {
|
||
|
if (auto v = knockback.ptr<Vec2F>()) {
|
||
|
return *v;
|
||
|
} else if (auto s = knockback.ptr<float>()) {
|
||
|
if (*s != 0) {
|
||
|
if (auto poly = damageArea.ptr<PolyF>())
|
||
|
return worldGeometry.diff(targetCenter, poly->center()).normalized() * *s;
|
||
|
else if (auto line = damageArea.ptr<Line2F>())
|
||
|
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<PolyF>())
|
||
|
poly->translate(position);
|
||
|
else if (auto line = damageArea.ptr<Line2F>())
|
||
|
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<EphemeralStatusEffect> 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(move(damageSourceKind)),
|
||
|
targetMaterialKind(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;
|
||
|
}
|
||
|
|
||
|
}
|