867 lines
35 KiB
C++
867 lines
35 KiB
C++
#include "weapon/Weapon.h"
|
|
|
|
#include "common/Value.h"
|
|
#include "weapon/WeaponAttributes.h"
|
|
#include "weapon/WeaponDefinitionRuntime.h"
|
|
#include "weapon/WeaponSystem.h"
|
|
|
|
#include <squirrel.h>
|
|
#include <simplesquirrel/simplesquirrel.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <memory>
|
|
|
|
namespace eve::weapon {
|
|
|
|
Module_IMPL(Weapon, new Weapon());
|
|
|
|
namespace {
|
|
|
|
template <typename T>
|
|
T* resolve(const ecs::EntityHandle& h) {
|
|
return static_cast<T*>(ecs::try_get(h));
|
|
}
|
|
|
|
template <typename T>
|
|
void destroyHandles(std::vector<ecs::EntityHandle>& hs) {
|
|
for (auto& h : hs) {
|
|
if (ecs::Entity* e = ecs::try_get(h)) ecs::DestroyEntity(e);
|
|
}
|
|
hs.clear();
|
|
}
|
|
|
|
template <typename T>
|
|
void registerCppEntityClassForScript(const ssq::Class& cls) {
|
|
eve::registerCppEntityView(cls, [](ssq::Array& out) {
|
|
HSQUIRRELVM vm = out.getHandle();
|
|
sq_pushobject(vm, out.getRaw());
|
|
ecs::Table* table = ecs::current();
|
|
if (table == nullptr) {
|
|
sq_pop(vm, 1);
|
|
return;
|
|
}
|
|
ecs::IComponentManager& cm = table->getOrCreateManager<T>();
|
|
auto* reg = cm.getOrCreateRegistryComponentBuffer<T>();
|
|
std::vector<ecs::IComponentBuffer*> stack;
|
|
stack.push_back(reg);
|
|
while (!stack.empty()) {
|
|
ecs::IComponentBuffer* buf = stack.back();
|
|
stack.pop_back();
|
|
auto* r = dynamic_cast<ecs::IRegistryComponentBuffer*>(buf);
|
|
if (r != nullptr) {
|
|
for (uint32_t i = 0; i < r->entity_count(); ++i) {
|
|
ecs::Entity* ent = r->entity_at(i);
|
|
if (ent != nullptr && ecs::is_entity_visible(ent)) {
|
|
ssq::detail::pushByPtr<T>(vm, static_cast<T*>(ent));
|
|
sq_arrayappend(vm, -2);
|
|
}
|
|
}
|
|
}
|
|
if (buf->children != nullptr) stack.push_back(buf->children);
|
|
if (buf->next != nullptr) stack.push_back(buf->next);
|
|
}
|
|
sq_pop(vm, 1);
|
|
});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const char* fireModeName(FireMode mode) {
|
|
switch (mode) {
|
|
case FireMode::Single: return "single";
|
|
case FireMode::Burst: return "burst";
|
|
case FireMode::Auto: return "auto";
|
|
}
|
|
return "single";
|
|
}
|
|
|
|
FireMode fireModeFromName(const std::string& name) {
|
|
if (name == "burst") return FireMode::Burst;
|
|
if (name == "auto") return FireMode::Auto;
|
|
return FireMode::Single;
|
|
}
|
|
|
|
const char* weaponKindName(WeaponKind kind) {
|
|
switch (kind) {
|
|
case WeaponKind::Melee: return "melee";
|
|
case WeaponKind::Ranged: return "ranged";
|
|
case WeaponKind::Magic: return "magic";
|
|
case WeaponKind::Missile: return "missile";
|
|
case WeaponKind::Custom: return "custom";
|
|
}
|
|
return "ranged";
|
|
}
|
|
|
|
WeaponKind weaponKindFromName(const std::string& name) {
|
|
if (name == "melee") return WeaponKind::Melee;
|
|
if (name == "magic") return WeaponKind::Magic;
|
|
if (name == "missile") return WeaponKind::Missile;
|
|
if (name == "custom") return WeaponKind::Custom;
|
|
return WeaponKind::Ranged;
|
|
}
|
|
|
|
const char* attackStageName(AttackStage stage) {
|
|
switch (stage) {
|
|
case AttackStage::Idle: return "idle";
|
|
case AttackStage::Windup: return "windup";
|
|
case AttackStage::Active: return "active";
|
|
case AttackStage::Recover: return "recover";
|
|
}
|
|
return "idle";
|
|
}
|
|
|
|
const char* weaponEventTypeName(WeaponEventType type) {
|
|
switch (type) {
|
|
case WeaponEventType::Fire: return "fire";
|
|
case WeaponEventType::ReloadStart: return "reload_start";
|
|
case WeaponEventType::ReloadEnd: return "reload_end";
|
|
case WeaponEventType::Empty: return "empty";
|
|
case WeaponEventType::WindupStart: return "windup_start";
|
|
case WeaponEventType::AttackEnd: return "attack_end";
|
|
case WeaponEventType::AimIn: return "aim_in";
|
|
case WeaponEventType::AimOut: return "aim_out";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
WeaponEntity* WeaponEntity::createWeapon() {
|
|
WeaponEntity* w = WeaponEntity::create();
|
|
w->identity();
|
|
w->definition();
|
|
w->definitionBinding();
|
|
w->state();
|
|
w->aim();
|
|
return w;
|
|
}
|
|
|
|
WeaponMountEntity* WeaponMountEntity::createMount() {
|
|
WeaponMountEntity* m = WeaponMountEntity::create();
|
|
m->identity();
|
|
m->limits();
|
|
m->state();
|
|
return m;
|
|
}
|
|
|
|
WeaponRigEntity* WeaponRigEntity::createRig() {
|
|
WeaponRigEntity* r = WeaponRigEntity::create();
|
|
r->identity();
|
|
r->held();
|
|
r->state();
|
|
return r;
|
|
}
|
|
|
|
AmmoPoolEntity* AmmoPoolEntity::createPool() {
|
|
AmmoPoolEntity* p = AmmoPoolEntity::create();
|
|
p->identity();
|
|
p->state();
|
|
return p;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 模块生命周期
|
|
// ---------------------------------------------------------------------------
|
|
|
|
Weapon::Weapon() {
|
|
WeaponSystem::setEventSink([this](const WeaponEvent& e) { events_.push_back(e); });
|
|
}
|
|
|
|
Weapon::~Weapon() {
|
|
destroyHandles<WeaponEntity>(weapons_);
|
|
destroyHandles<WeaponMountEntity>(mounts_);
|
|
destroyHandles<WeaponRigEntity>(rigs_);
|
|
destroyHandles<AmmoPoolEntity>(pools_);
|
|
WeaponSystem::setEventSink(nullptr);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 武器模板
|
|
// ---------------------------------------------------------------------------
|
|
|
|
int Weapon::registerWeaponsFromJson(const std::string& json) {
|
|
auto canonical = eve::Value::fromJson(json);
|
|
if (!canonical) {
|
|
canonical.ignore("weapon definition input could not be parsed as canonical Value");
|
|
return 0;
|
|
}
|
|
|
|
int n = 0;
|
|
const auto publish = [this, &n](const eve::Value& value) {
|
|
const auto* object = value.getIf<eve::Value::Object>();
|
|
if (object == nullptr) return;
|
|
const auto id = object->find("id");
|
|
const auto* name = id == object->end() ? nullptr : id->second.getIf<std::string>();
|
|
if (name == nullptr || name->empty()) return;
|
|
|
|
auto encoded = value.toJson();
|
|
if (!encoded) {
|
|
encoded.ignore("weapon definition value could not be serialized canonically");
|
|
return;
|
|
}
|
|
eve::definitions::Definition source;
|
|
source.type = "weapon";
|
|
source.id = *name;
|
|
source.version = eve::SchemaVersion(1);
|
|
source.json = encoded.value();
|
|
auto typed = parseWeaponDefinition(source);
|
|
if (!typed) {
|
|
typed.ignore("weapon definition failed typed projection validation");
|
|
return;
|
|
}
|
|
|
|
auto current = definitionRegistry_.resolve("weapon", *name);
|
|
const bool exists = current.ok();
|
|
if (!exists && current.status().code() != eve::StatusCode::NotFound) return;
|
|
auto stored = exists ? definitionRegistry_.replace("weapon", *name, 1, source.json)
|
|
: definitionRegistry_.insert("weapon", *name, 1, source.json);
|
|
if (stored) {
|
|
++n;
|
|
} else {
|
|
stored.ignore("weapon definition registry rejected canonical registration");
|
|
}
|
|
};
|
|
const eve::Value& root = canonical.value();
|
|
if (const auto* array = root.getIf<eve::Value::Array>()) {
|
|
for (const auto& value : *array) publish(value);
|
|
} else {
|
|
publish(root);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
void Weapon::clearWeaponDefinitions() {
|
|
while (definitionRegistry_.countType("weapon") > 0) {
|
|
const auto* definition = definitionRegistry_.atType("weapon", 0);
|
|
if (definition == nullptr) break;
|
|
auto result = definitionRegistry_.remove("weapon", definition->id);
|
|
if (!result) result.ignore("weapon definition registry removal failed");
|
|
}
|
|
}
|
|
|
|
int Weapon::getWeaponDefinitionCount() { return definitionRegistry_.countType("weapon"); }
|
|
|
|
bool Weapon::hasWeaponDefinition(const std::string& id) { return definitionRegistry_.resolve("weapon", id).ok(); }
|
|
|
|
std::string Weapon::getWeaponDefinitionLogic(const std::string& id) {
|
|
auto definition = findDef(id);
|
|
if (!definition) {
|
|
definition.ignore("legacy scalar query returns empty for an unknown weapon definition");
|
|
return {};
|
|
}
|
|
return definition.value().logic;
|
|
}
|
|
|
|
float Weapon::getWeaponDefinitionDamage(const std::string& id) {
|
|
auto definition = findDef(id);
|
|
if (!definition) {
|
|
definition.ignore("legacy scalar query returns zero for an unknown weapon definition");
|
|
return 0.f;
|
|
}
|
|
return definition.value().damage;
|
|
}
|
|
|
|
float Weapon::getWeaponDefinitionRange(const std::string& id) {
|
|
auto definition = findDef(id);
|
|
if (!definition) {
|
|
definition.ignore("legacy scalar query returns zero for an unknown weapon definition");
|
|
return 0.f;
|
|
}
|
|
return definition.value().range;
|
|
}
|
|
|
|
void Weapon::registerLogic(IWeaponLogic* logic) { WeaponSystem::registerLogic(logic); }
|
|
|
|
int Weapon::getLogicCount() { return WeaponSystem::logicCount(); }
|
|
|
|
eve::Result<WeaponDefinition> Weapon::findDef(const std::string& id) const {
|
|
auto resolved = definitionRegistry_.resolve("weapon", id);
|
|
if (!resolved) return eve::Result<WeaponDefinition>::failure(resolved.status());
|
|
return parseWeaponDefinition(resolved.value().get());
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 工厂
|
|
// ---------------------------------------------------------------------------
|
|
|
|
WeaponEntity* Weapon::newWeapon(const std::string& defId) {
|
|
auto definition = findDef(defId);
|
|
if (!definition) {
|
|
definition.ignore("legacy nullable factory returns null for an unknown weapon definition");
|
|
return nullptr;
|
|
}
|
|
WeaponDefinition d = std::move(definition).takeValue();
|
|
|
|
WeaponEntity* w = WeaponEntity::createWeapon();
|
|
w->identity()->id = defId + "#" + std::to_string(nextInstance_++);
|
|
w->identity()->defId = defId;
|
|
w->definition()->owned = std::make_shared<const WeaponDefinition>(std::move(d));
|
|
w->definition()->def = w->definition()->owned.get();
|
|
w->state()->stages = &w->definition()->def->stages;
|
|
const WeaponDefinition& storedDefinition = *w->definition()->def;
|
|
w->state()->selector = storedDefinition.fireMode;
|
|
w->state()->currentSpread = storedDefinition.spreadMin;
|
|
|
|
// 运行时资源:热武器 ammo 映射到弹匣/备用;其余形态从模板 resource 拷贝。
|
|
Resource& r = w->state()->resource;
|
|
r.kind = storedDefinition.resource.kind;
|
|
r.max = storedDefinition.resource.max;
|
|
r.regen = storedDefinition.resource.regen;
|
|
r.cost = storedDefinition.resource.cost;
|
|
r.infinite = storedDefinition.resource.infinite;
|
|
if (storedDefinition.kind == WeaponKind::Ranged) {
|
|
r.kind = ResourceKind::Ammo;
|
|
r.max = static_cast<float>(storedDefinition.magSize);
|
|
r.value = static_cast<float>(storedDefinition.magSize);
|
|
r.cost = 1.f;
|
|
r.reserve = storedDefinition.reserveSize < 0 ? 0 : storedDefinition.reserveSize;
|
|
r.infinite = storedDefinition.reserveSize < 0;
|
|
} else {
|
|
r.value = r.infinite ? 0.f : r.max;
|
|
}
|
|
|
|
weapons_.push_back(ecs::handle_of(w));
|
|
return w;
|
|
}
|
|
|
|
WeaponMountEntity* Weapon::newMount(const std::string& id, const std::string& type) {
|
|
WeaponMountEntity* m = WeaponMountEntity::createMount();
|
|
m->identity()->id = id.empty() ? ("mount#" + std::to_string(nextInstance_++)) : id;
|
|
m->identity()->type = type;
|
|
mounts_.push_back(ecs::handle_of(m));
|
|
return m;
|
|
}
|
|
|
|
WeaponRigEntity* Weapon::newRig(const std::string& id, const std::string& wield) {
|
|
WeaponRigEntity* r = WeaponRigEntity::createRig();
|
|
r->identity()->id = id.empty() ? ("rig#" + std::to_string(nextInstance_++)) : id;
|
|
r->identity()->wield = wield;
|
|
rigs_.push_back(ecs::handle_of(r));
|
|
return r;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 手持位操作
|
|
// ---------------------------------------------------------------------------
|
|
|
|
bool Weapon::rigAttachWeapon(WeaponRigEntity* rig, WeaponEntity* w) {
|
|
if (rig == nullptr || w == nullptr) return false;
|
|
rig->state()->weapon = w;
|
|
return true;
|
|
}
|
|
|
|
WeaponEntity* Weapon::rigGetWeapon(WeaponRigEntity* rig) { return rig == nullptr ? nullptr : rig->state()->weapon; }
|
|
|
|
void Weapon::rigSetPose(WeaponRigEntity* rig, float px, float py, float pz, float rx, float ry, float rz) {
|
|
if (rig == nullptr) return;
|
|
rig->held()->posX = px;
|
|
rig->held()->posY = py;
|
|
rig->held()->posZ = pz;
|
|
rig->held()->rotX = rx;
|
|
rig->held()->rotY = ry;
|
|
rig->held()->rotZ = rz;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 共享弹药池
|
|
// ---------------------------------------------------------------------------
|
|
|
|
AmmoPoolEntity* Weapon::newAmmoPool(const std::string& id, const std::string& ammoType, int max) {
|
|
AmmoPoolEntity* p = AmmoPoolEntity::createPool();
|
|
p->identity()->id = id.empty() ? ("ammo#" + std::to_string(nextInstance_++)) : id;
|
|
p->identity()->ammoType = ammoType;
|
|
p->state()->max = max;
|
|
pools_.push_back(ecs::handle_of(p));
|
|
return p;
|
|
}
|
|
|
|
void Weapon::ammoPoolAdd(AmmoPoolEntity* pool, int n) {
|
|
if (pool == nullptr || n <= 0) return;
|
|
if (pool->state()->max < 0) {
|
|
pool->state()->count += n;
|
|
} else {
|
|
pool->state()->count = std::min(pool->state()->max, pool->state()->count + n);
|
|
}
|
|
}
|
|
|
|
int Weapon::ammoPoolGetCount(AmmoPoolEntity* pool) { return pool == nullptr ? 0 : pool->state()->count; }
|
|
|
|
bool Weapon::bindAmmoPool(WeaponEntity* w, AmmoPoolEntity* pool) {
|
|
if (w == nullptr || pool == nullptr) return false;
|
|
w->state()->ammoPool = pool;
|
|
return true;
|
|
}
|
|
|
|
void Weapon::unbindAmmoPool(WeaponEntity* w) {
|
|
if (w != nullptr) w->state()->ammoPool = nullptr;
|
|
}
|
|
|
|
AmmoPoolEntity* Weapon::getAmmoPool(WeaponEntity* w) { return w == nullptr ? nullptr : w->state()->ammoPool; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 挂点操作
|
|
// ---------------------------------------------------------------------------
|
|
|
|
bool Weapon::mountAttachWeapon(WeaponMountEntity* m, WeaponEntity* w) {
|
|
if (m == nullptr || w == nullptr || m->state()->destroyed) return false;
|
|
m->state()->weapon = w;
|
|
return true;
|
|
}
|
|
|
|
WeaponEntity* Weapon::mountGetWeapon(WeaponMountEntity* m) { return m == nullptr ? nullptr : m->state()->weapon; }
|
|
|
|
void Weapon::mountSetLimits(WeaponMountEntity* m, float yawMin, float yawMax, float pitchMin, float pitchMax,
|
|
float rotSpeed, float firingArc) {
|
|
if (m == nullptr) return;
|
|
m->limits()->yawMin = yawMin;
|
|
m->limits()->yawMax = yawMax;
|
|
m->limits()->pitchMin = pitchMin;
|
|
m->limits()->pitchMax = pitchMax;
|
|
m->limits()->rotSpeed = rotSpeed;
|
|
m->limits()->firingArc = firingArc;
|
|
}
|
|
|
|
void Weapon::mountAimAt(WeaponMountEntity* m, float yaw, float pitch) {
|
|
if (m == nullptr || m->state()->destroyed) return;
|
|
const float cyaw = std::clamp(yaw, m->limits()->yawMin, m->limits()->yawMax);
|
|
const float cpitch = std::clamp(pitch, m->limits()->pitchMin, m->limits()->pitchMax);
|
|
|
|
if (WeaponEntity* w = m->state()->weapon) {
|
|
w->aim()->desiredYaw = cyaw;
|
|
w->aim()->desiredPitch = cpitch;
|
|
w->aim()->turnSpeed = m->limits()->rotSpeed;
|
|
} else {
|
|
m->state()->yaw = cyaw;
|
|
m->state()->pitch = cpitch;
|
|
}
|
|
}
|
|
|
|
void Weapon::mountDestroy(WeaponMountEntity* m) {
|
|
if (m == nullptr) return;
|
|
m->state()->destroyed = true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 武器操作
|
|
// ---------------------------------------------------------------------------
|
|
|
|
bool Weapon::fire(WeaponEntity* w, const FireRequest& req) { return w != nullptr && WeaponSystem::tryFire(*w, req); }
|
|
|
|
bool Weapon::fireAt(WeaponEntity* w, float x, float y, float z, int shooterId) {
|
|
if (w == nullptr) return false;
|
|
AttackRequest req;
|
|
req.targetX = x;
|
|
req.targetY = y;
|
|
req.targetZ = z;
|
|
req.hasTarget = true;
|
|
req.shooterId = shooterId;
|
|
return WeaponSystem::tryFire(*w, req);
|
|
}
|
|
|
|
bool Weapon::attack(WeaponEntity* w, float yaw, float pitch, int shooterId) {
|
|
if (w == nullptr) return false;
|
|
AttackRequest req;
|
|
req.yaw = yaw;
|
|
req.pitch = pitch;
|
|
req.shooterId = shooterId;
|
|
if (const WeaponDefinition* d = w->definition()->def) {
|
|
req.arcAngle = d->arc;
|
|
req.aoeRadius = d->projectile.aoe;
|
|
}
|
|
return WeaponSystem::tryFire(*w, req);
|
|
}
|
|
|
|
bool Weapon::canFire(WeaponEntity* w) { return w != nullptr && WeaponSystem::canFire(*w); }
|
|
|
|
void Weapon::startReload(WeaponEntity* w) {
|
|
if (w != nullptr) WeaponSystem::startReload(*w);
|
|
}
|
|
|
|
void Weapon::cancelReload(WeaponEntity* w) {
|
|
if (w != nullptr) WeaponSystem::cancelReload(*w);
|
|
}
|
|
|
|
void Weapon::setAim(WeaponEntity* w, float yaw, float pitch) {
|
|
if (w == nullptr) return;
|
|
w->aim()->desiredYaw = yaw;
|
|
w->aim()->desiredPitch = pitch;
|
|
}
|
|
|
|
bool Weapon::setFireMode(WeaponEntity* w, const std::string& mode) {
|
|
if (w == nullptr) return false;
|
|
const WeaponDefinition* d = w->definition()->def;
|
|
if (d == nullptr) return false;
|
|
const FireMode m = fireModeFromName(mode);
|
|
// 无可选模式 = 固定 fireMode,不允许切换。
|
|
if (d->selectableModes.empty()) return m == d->fireMode;
|
|
for (const FireMode sm : d->selectableModes)
|
|
if (sm == m) {
|
|
w->state()->selector = m;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string Weapon::getFireMode(WeaponEntity* w) {
|
|
return w != nullptr ? fireModeName(w->state()->selector) : std::string{};
|
|
}
|
|
|
|
int Weapon::getSelectableModeCount(WeaponEntity* w) {
|
|
const WeaponDefinition* d = w != nullptr ? w->definition()->def : nullptr;
|
|
return d != nullptr ? static_cast<int>(d->selectableModes.size()) : 0;
|
|
}
|
|
|
|
std::string Weapon::getSelectableMode(WeaponEntity* w, int index) {
|
|
const WeaponDefinition* d = w != nullptr ? w->definition()->def : nullptr;
|
|
if (d == nullptr || index < 0 || index >= static_cast<int>(d->selectableModes.size())) return std::string{};
|
|
return fireModeName(d->selectableModes[static_cast<size_t>(index)]);
|
|
}
|
|
|
|
bool Weapon::setAiming(WeaponEntity* w, bool aiming) {
|
|
if (w == nullptr || w->state()->aiming == aiming) return false;
|
|
w->state()->aiming = aiming;
|
|
WeaponEvent e;
|
|
e.type = aiming ? WeaponEventType::AimIn : WeaponEventType::AimOut;
|
|
e.weaponId = w->identity()->id;
|
|
if (const WeaponDefinition* d = w->definition()->def) e.defId = d->id;
|
|
WeaponSystem::emitEvent(e);
|
|
return true;
|
|
}
|
|
|
|
bool Weapon::isAiming(WeaponEntity* w) { return w != nullptr && w->state()->aiming; }
|
|
|
|
float Weapon::getZoomFov(WeaponEntity* w) {
|
|
const WeaponDefinition* d = w != nullptr ? w->definition()->def : nullptr;
|
|
return d != nullptr ? d->zoomFov : 0.f;
|
|
}
|
|
|
|
float Weapon::getSpread(WeaponEntity* w) { return w != nullptr ? w->state()->currentSpread : 0.f; }
|
|
|
|
float Weapon::getRecoilPitch(WeaponEntity* w) { return w != nullptr ? w->state()->recoilPitch : 0.f; }
|
|
|
|
float Weapon::getRecoilYaw(WeaponEntity* w) { return w != nullptr ? w->state()->recoilYaw : 0.f; }
|
|
|
|
void Weapon::update(float dt) {
|
|
for (const ecs::EntityHandle& h : weapons_) {
|
|
if (WeaponEntity* w = resolve<WeaponEntity>(h)) WeaponSystem::update(*w, dt);
|
|
}
|
|
for (const ecs::EntityHandle& h : mounts_) {
|
|
WeaponMountEntity* m = resolve<WeaponMountEntity>(h);
|
|
if (m == nullptr || m->state()->destroyed) continue;
|
|
if (WeaponEntity* w = m->state()->weapon) {
|
|
m->state()->yaw = w->aim()->yaw;
|
|
m->state()->pitch = w->aim()->pitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 事件队列
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void Weapon::clearEvents() { events_.clear(); }
|
|
|
|
int Weapon::getEventCount() const { return static_cast<int>(events_.size()); }
|
|
|
|
std::string Weapon::getEventType(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? weaponEventTypeName(events_[index].type) : std::string{};
|
|
}
|
|
|
|
std::string Weapon::getEventWeaponId(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].weaponId : std::string{};
|
|
}
|
|
|
|
std::string Weapon::getEventDefId(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].defId : std::string{};
|
|
}
|
|
|
|
std::string Weapon::getEventMountId(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].mountId : std::string{};
|
|
}
|
|
|
|
int Weapon::getEventAmmoLeft(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].ammoLeft : 0;
|
|
}
|
|
|
|
float Weapon::getEventArc(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].arc : 0.f;
|
|
}
|
|
|
|
float Weapon::getEventAoe(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].aoe : 0.f;
|
|
}
|
|
|
|
float Weapon::getEventSpread(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].spread : 0.f;
|
|
}
|
|
|
|
int Weapon::getEventPellets(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].pellets : 0;
|
|
}
|
|
|
|
float Weapon::getEventRecoilPitch(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].recoilPitch : 0.f;
|
|
}
|
|
|
|
float Weapon::getEventRecoilYaw(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].recoilYaw : 0.f;
|
|
}
|
|
|
|
std::string Weapon::getEventDamageType(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].damageType : std::string{};
|
|
}
|
|
|
|
std::string Weapon::getEventElement(int index) const {
|
|
return (index >= 0 && index < getEventCount()) ? events_[index].element : std::string{};
|
|
}
|
|
|
|
std::string Weapon::getStage(WeaponEntity* w) const {
|
|
return w != nullptr ? attackStageName(w->state()->stage) : std::string{};
|
|
}
|
|
|
|
float Weapon::getResourceValue(WeaponEntity* w) const { return w != nullptr ? w->state()->resource.value : 0.f; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 脚本绑定
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void Weapon::expose(ssq::Table& table) {
|
|
auto cls = table.addClass(name, Weapon::create, false);
|
|
expose(cls);
|
|
|
|
auto wCls = table.addClass<WeaponEntity>(
|
|
"WeaponEntity", std::function<WeaponEntity*()>([]() -> WeaponEntity* { return nullptr; }), false);
|
|
registerCppEntityClassForScript<WeaponEntity>(wCls);
|
|
wCls.addFunc("getId", [](WeaponEntity* w) -> std::string { return w ? w->identity()->id : std::string{}; });
|
|
wCls.addFunc("getDefId", [](WeaponEntity* w) -> std::string { return w ? w->identity()->defId : std::string{}; });
|
|
wCls.addFunc("getOwnerId", [](WeaponEntity* w) -> int { return w ? w->identity()->ownerId : 0; });
|
|
wCls.addFunc("setOwnerId", [](WeaponEntity* w, int v) {
|
|
if (w) w->identity()->ownerId = v;
|
|
});
|
|
wCls.addFunc("getMagAmmo", [](WeaponEntity* w) -> int {
|
|
return w ? static_cast<int>(w->state()->resource.value) : 0;
|
|
});
|
|
wCls.addFunc("getReserveAmmo", [](WeaponEntity* w) -> int { return w ? w->state()->resource.reserve : 0; });
|
|
wCls.addFunc("getResourceValue", [](WeaponEntity* w) -> float { return w ? w->state()->resource.value : 0.f; });
|
|
wCls.addFunc("getSpread", [](WeaponEntity* w) -> float { return w ? w->state()->currentSpread : 0.f; });
|
|
wCls.addFunc("getRecoilPitch", [](WeaponEntity* w) -> float { return w ? w->state()->recoilPitch : 0.f; });
|
|
wCls.addFunc("getRecoilYaw", [](WeaponEntity* w) -> float { return w ? w->state()->recoilYaw : 0.f; });
|
|
wCls.addFunc("getFireMode", [](WeaponEntity* w) -> std::string {
|
|
return w ? fireModeName(w->state()->selector) : std::string{};
|
|
});
|
|
wCls.addFunc("setFireMode", [](WeaponEntity* w, const std::string& mode) -> bool {
|
|
if (w == nullptr) return false;
|
|
const WeaponDefinition* d = w->definition()->def;
|
|
if (d == nullptr) return false;
|
|
const FireMode m = fireModeFromName(mode);
|
|
if (d->selectableModes.empty()) return m == d->fireMode;
|
|
for (const FireMode sm : d->selectableModes)
|
|
if (sm == m) {
|
|
w->state()->selector = m;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
wCls.addFunc("isAiming", [](WeaponEntity* w) -> bool { return w != nullptr && w->state()->aiming; });
|
|
wCls.addFunc("setAiming", [](WeaponEntity* w, bool aiming) -> bool {
|
|
if (w == nullptr || w->state()->aiming == aiming) return false;
|
|
w->state()->aiming = aiming;
|
|
WeaponEvent e;
|
|
e.type = aiming ? WeaponEventType::AimIn : WeaponEventType::AimOut;
|
|
e.weaponId = w->identity()->id;
|
|
if (const WeaponDefinition* d = w->definition()->def) e.defId = d->id;
|
|
WeaponSystem::emitEvent(e);
|
|
return true;
|
|
});
|
|
wCls.addFunc("getZoomFov", [](WeaponEntity* w) -> float {
|
|
const WeaponDefinition* d = w != nullptr ? w->definition()->def : nullptr;
|
|
return d != nullptr ? d->zoomFov : 0.f;
|
|
});
|
|
wCls.addFunc("getAmmoPool",
|
|
[](WeaponEntity* w) -> AmmoPoolEntity* { return w != nullptr ? w->state()->ammoPool : nullptr; });
|
|
wCls.addFunc("getStage", [](WeaponEntity* w) -> std::string {
|
|
return w ? attackStageName(w->state()->stage) : std::string{};
|
|
});
|
|
wCls.addFunc("isReloading", [](WeaponEntity* w) -> bool { return w != nullptr && w->state()->resource.reloading; });
|
|
wCls.addFunc("getCooldown", [](WeaponEntity* w) -> float { return w ? w->state()->cooldown : 0.f; });
|
|
wCls.addFunc("getYaw", [](WeaponEntity* w) -> float { return w ? w->aim()->yaw : 0.f; });
|
|
wCls.addFunc("getPitch", [](WeaponEntity* w) -> float { return w ? w->aim()->pitch : 0.f; });
|
|
wCls.addFunc("canFire", [](WeaponEntity* w) -> bool { return w != nullptr && WeaponSystem::canFire(*w); });
|
|
wCls.addFunc("fireAt", [](WeaponEntity* w, float x, float y, float z, int shooterId) -> bool {
|
|
if (w == nullptr) return false;
|
|
AttackRequest req;
|
|
req.targetX = x;
|
|
req.targetY = y;
|
|
req.targetZ = z;
|
|
req.hasTarget = true;
|
|
req.shooterId = shooterId;
|
|
return WeaponSystem::tryFire(*w, req);
|
|
});
|
|
wCls.addFunc("attack", [](WeaponEntity* w, float yaw, float pitch, int shooterId) -> bool {
|
|
if (w == nullptr) return false;
|
|
AttackRequest req;
|
|
req.yaw = yaw;
|
|
req.pitch = pitch;
|
|
req.shooterId = shooterId;
|
|
if (const WeaponDefinition* d = w->definition()->def) {
|
|
req.arcAngle = d->arc;
|
|
req.aoeRadius = d->projectile.aoe;
|
|
}
|
|
return WeaponSystem::tryFire(*w, req);
|
|
});
|
|
wCls.addFunc("startReload", [](WeaponEntity* w) {
|
|
if (w) WeaponSystem::startReload(*w);
|
|
});
|
|
wCls.addFunc("cancelReload", [](WeaponEntity* w) {
|
|
if (w) WeaponSystem::cancelReload(*w);
|
|
});
|
|
wCls.addFunc("setAim", [](WeaponEntity* w, float yaw, float pitch) {
|
|
if (w) {
|
|
w->aim()->desiredYaw = yaw;
|
|
w->aim()->desiredPitch = pitch;
|
|
}
|
|
});
|
|
|
|
auto mCls = table.addClass<WeaponMountEntity>(
|
|
"WeaponMountEntity", std::function<WeaponMountEntity*()>([]() -> WeaponMountEntity* { return nullptr; }),
|
|
false);
|
|
registerCppEntityClassForScript<WeaponMountEntity>(mCls);
|
|
mCls.addFunc("getId", [](WeaponMountEntity* m) -> std::string { return m ? m->identity()->id : std::string{}; });
|
|
mCls.addFunc("getType",
|
|
[](WeaponMountEntity* m) -> std::string { return m ? m->identity()->type : std::string{}; });
|
|
mCls.addFunc("getNodePath",
|
|
[](WeaponMountEntity* m) -> std::string { return m ? m->identity()->nodePath : std::string{}; });
|
|
mCls.addFunc("setNodePath", [](WeaponMountEntity* m, const std::string& v) {
|
|
if (m) m->identity()->nodePath = v;
|
|
});
|
|
mCls.addFunc("getWeapon", [](WeaponMountEntity* m) -> WeaponEntity* { return m ? m->state()->weapon : nullptr; });
|
|
mCls.addFunc("getYaw", [](WeaponMountEntity* m) -> float { return m ? m->state()->yaw : 0.f; });
|
|
mCls.addFunc("getPitch", [](WeaponMountEntity* m) -> float { return m ? m->state()->pitch : 0.f; });
|
|
mCls.addFunc("isDestroyed", [](WeaponMountEntity* m) -> bool { return m != nullptr && m->state()->destroyed; });
|
|
mCls.addFunc("destroy", [](WeaponMountEntity* m) {
|
|
if (m) m->state()->destroyed = true;
|
|
});
|
|
mCls.addFunc("aimAt", [](WeaponMountEntity* m, float yaw, float pitch) {
|
|
if (m == nullptr || m->state()->destroyed) return;
|
|
const float cyaw = std::clamp(yaw, m->limits()->yawMin, m->limits()->yawMax);
|
|
const float cpitch = std::clamp(pitch, m->limits()->pitchMin, m->limits()->pitchMax);
|
|
if (WeaponEntity* w = m->state()->weapon) {
|
|
w->aim()->desiredYaw = cyaw;
|
|
w->aim()->desiredPitch = cpitch;
|
|
w->aim()->turnSpeed = m->limits()->rotSpeed;
|
|
} else {
|
|
m->state()->yaw = cyaw;
|
|
m->state()->pitch = cpitch;
|
|
}
|
|
});
|
|
|
|
auto rCls = table.addClass<WeaponRigEntity>(
|
|
"WeaponRigEntity", std::function<WeaponRigEntity*()>([]() -> WeaponRigEntity* { return nullptr; }), false);
|
|
registerCppEntityClassForScript<WeaponRigEntity>(rCls);
|
|
rCls.addFunc("getId", [](WeaponRigEntity* r) -> std::string { return r ? r->identity()->id : std::string{}; });
|
|
rCls.addFunc("getWield",
|
|
[](WeaponRigEntity* r) -> std::string { return r ? r->identity()->wield : std::string{}; });
|
|
rCls.addFunc("getNodePath",
|
|
[](WeaponRigEntity* r) -> std::string { return r ? r->identity()->nodePath : std::string{}; });
|
|
rCls.addFunc("setNodePath", [](WeaponRigEntity* r, const std::string& v) {
|
|
if (r) r->identity()->nodePath = v;
|
|
});
|
|
rCls.addFunc("getWeapon", [](WeaponRigEntity* r) -> WeaponEntity* { return r ? r->state()->weapon : nullptr; });
|
|
rCls.addFunc("attachWeapon", [](WeaponRigEntity* r, WeaponEntity* w) -> bool {
|
|
if (r == nullptr || w == nullptr) return false;
|
|
r->state()->weapon = w;
|
|
return true;
|
|
});
|
|
rCls.addFunc("setPose", [](WeaponRigEntity* r, float px, float py, float pz, float rx, float ry, float rz) {
|
|
if (r == nullptr) return;
|
|
r->held()->posX = px;
|
|
r->held()->posY = py;
|
|
r->held()->posZ = pz;
|
|
r->held()->rotX = rx;
|
|
r->held()->rotY = ry;
|
|
r->held()->rotZ = rz;
|
|
});
|
|
|
|
auto pCls = table.addClass<AmmoPoolEntity>(
|
|
"AmmoPoolEntity", std::function<AmmoPoolEntity*()>([]() -> AmmoPoolEntity* { return nullptr; }), false);
|
|
registerCppEntityClassForScript<AmmoPoolEntity>(pCls);
|
|
pCls.addFunc("getId", [](AmmoPoolEntity* p) -> std::string { return p ? p->identity()->id : std::string{}; });
|
|
pCls.addFunc("getAmmoType",
|
|
[](AmmoPoolEntity* p) -> std::string { return p ? p->identity()->ammoType : std::string{}; });
|
|
pCls.addFunc("getCount", [](AmmoPoolEntity* p) -> int { return p ? p->state()->count : 0; });
|
|
pCls.addFunc("addAmmo", [](AmmoPoolEntity* p, int n) {
|
|
if (p == nullptr || n <= 0) return;
|
|
if (p->state()->max < 0) {
|
|
p->state()->count += n;
|
|
} else {
|
|
p->state()->count = std::min(p->state()->max, p->state()->count + n);
|
|
}
|
|
});
|
|
pCls.addFunc("setCount", [](AmmoPoolEntity* p, int n) {
|
|
if (p != nullptr) p->state()->count = n;
|
|
});
|
|
}
|
|
|
|
void Weapon::expose(ssq::Class& cls) {
|
|
cls.addFunc("registerWeaponsFromJson", &Weapon::registerWeaponsFromJson);
|
|
cls.addFunc("clearWeaponDefinitions", &Weapon::clearWeaponDefinitions);
|
|
cls.addFunc("getWeaponDefinitionCount", &Weapon::getWeaponDefinitionCount);
|
|
cls.addFunc("hasWeaponDefinition", &Weapon::hasWeaponDefinition);
|
|
cls.addFunc("getWeaponDefinitionLogic", &Weapon::getWeaponDefinitionLogic);
|
|
cls.addFunc("getWeaponDefinitionDamage", &Weapon::getWeaponDefinitionDamage);
|
|
cls.addFunc("getWeaponDefinitionRange", &Weapon::getWeaponDefinitionRange);
|
|
cls.addFunc("getLogicCount", [](Weapon*) -> int { return WeaponSystem::logicCount(); });
|
|
cls.addFunc("newWeapon", &Weapon::newWeapon);
|
|
cls.addFunc("newMount", &Weapon::newMount);
|
|
cls.addFunc("newRig", &Weapon::newRig);
|
|
cls.addFunc("rigAttachWeapon", &Weapon::rigAttachWeapon);
|
|
cls.addFunc("rigGetWeapon", &Weapon::rigGetWeapon);
|
|
cls.addFunc("rigSetPose", &Weapon::rigSetPose);
|
|
cls.addFunc("newAmmoPool", &Weapon::newAmmoPool);
|
|
cls.addFunc("ammoPoolAdd", &Weapon::ammoPoolAdd);
|
|
cls.addFunc("ammoPoolGetCount", &Weapon::ammoPoolGetCount);
|
|
cls.addFunc("bindAmmoPool", &Weapon::bindAmmoPool);
|
|
cls.addFunc("unbindAmmoPool", &Weapon::unbindAmmoPool);
|
|
cls.addFunc("getAmmoPool", &Weapon::getAmmoPool);
|
|
cls.addFunc("setFireMode", &Weapon::setFireMode);
|
|
cls.addFunc("getFireMode", &Weapon::getFireMode);
|
|
cls.addFunc("getSelectableModeCount", &Weapon::getSelectableModeCount);
|
|
cls.addFunc("getSelectableMode", &Weapon::getSelectableMode);
|
|
cls.addFunc("setAiming", &Weapon::setAiming);
|
|
cls.addFunc("isAiming", &Weapon::isAiming);
|
|
cls.addFunc("getZoomFov", &Weapon::getZoomFov);
|
|
cls.addFunc("getSpread", &Weapon::getSpread);
|
|
cls.addFunc("getRecoilPitch", &Weapon::getRecoilPitch);
|
|
cls.addFunc("getRecoilYaw", &Weapon::getRecoilYaw);
|
|
cls.addFunc("mountAttachWeapon", &Weapon::mountAttachWeapon);
|
|
cls.addFunc("mountGetWeapon", &Weapon::mountGetWeapon);
|
|
cls.addFunc("mountSetLimits", &Weapon::mountSetLimits);
|
|
cls.addFunc("mountAimAt", &Weapon::mountAimAt);
|
|
cls.addFunc("mountDestroy", &Weapon::mountDestroy);
|
|
cls.addFunc("fireAt", &Weapon::fireAt);
|
|
cls.addFunc("attack", &Weapon::attack);
|
|
cls.addFunc("canFire", &Weapon::canFire);
|
|
cls.addFunc("startReload", &Weapon::startReload);
|
|
cls.addFunc("cancelReload", &Weapon::cancelReload);
|
|
cls.addFunc("setAim", &Weapon::setAim);
|
|
cls.addFunc("update", &Weapon::update);
|
|
cls.addFunc("clearEvents", &Weapon::clearEvents);
|
|
cls.addFunc("getEventCount", &Weapon::getEventCount);
|
|
cls.addFunc("getEventType", &Weapon::getEventType);
|
|
cls.addFunc("getEventWeaponId", &Weapon::getEventWeaponId);
|
|
cls.addFunc("getEventDefId", &Weapon::getEventDefId);
|
|
cls.addFunc("getEventMountId", &Weapon::getEventMountId);
|
|
cls.addFunc("getEventAmmoLeft", &Weapon::getEventAmmoLeft);
|
|
cls.addFunc("getEventArc", &Weapon::getEventArc);
|
|
cls.addFunc("getEventAoe", &Weapon::getEventAoe);
|
|
cls.addFunc("getEventSpread", &Weapon::getEventSpread);
|
|
cls.addFunc("getEventPellets", &Weapon::getEventPellets);
|
|
cls.addFunc("getEventRecoilPitch", &Weapon::getEventRecoilPitch);
|
|
cls.addFunc("getEventRecoilYaw", &Weapon::getEventRecoilYaw);
|
|
cls.addFunc("getEventDamageType", &Weapon::getEventDamageType);
|
|
cls.addFunc("getEventElement", &Weapon::getEventElement);
|
|
cls.addFunc("getStage", &Weapon::getStage);
|
|
cls.addFunc("getResourceValue", &Weapon::getResourceValue);
|
|
}
|
|
|
|
} // namespace eve::weapon
|