载入中...
搜索中...
未找到
Vehicle.cpp
浏览该文件的文档.
1#include "vehicle/Vehicle.h"
2
3#include "common/Capability.h"
4#include "common/Value.h"
9#include "weapon/Weapon.h"
10
11#include <squirrel.h>
12#include <simplesquirrel/simplesquirrel.hpp>
13
14#include <algorithm>
15#include <cmath>
16
17namespace eve::vehicle {
18
20
21namespace {
22
23constexpr float kPi = 3.14159265358979323846f;
24
25template <typename T>
26T* resolve(const ecs::EntityHandle& h) {
27 return static_cast<T*>(ecs::try_get(h));
28}
29
30void destroyHandles(std::vector<ecs::EntityHandle>& hs) {
31 for (auto& h : hs) {
32 if (ecs::Entity* e = ecs::try_get(h)) ecs::DestroyEntity(e);
33 }
34 hs.clear();
35}
36
37template <typename T>
38void registerCppEntityClassForScript(const ssq::Class& cls) {
39 eve::registerCppEntityView(cls, [](ssq::Array& out) {
40 HSQUIRRELVM vm = out.getHandle();
41 sq_pushobject(vm, out.getRaw());
42 ecs::Table* table = ecs::current();
43 if (table == nullptr) {
44 sq_pop(vm, 1);
45 return;
46 }
47 ecs::IComponentManager& cm = table->getOrCreateManager<T>();
48 auto* reg = cm.getOrCreateRegistryComponentBuffer<T>();
49 std::vector<ecs::IComponentBuffer*> stack;
50 stack.push_back(reg);
51 while (!stack.empty()) {
52 ecs::IComponentBuffer* buf = stack.back();
53 stack.pop_back();
54 auto* r = dynamic_cast<ecs::IRegistryComponentBuffer*>(buf);
55 if (r != nullptr) {
56 for (uint32_t i = 0; i < r->entity_count(); ++i) {
57 ecs::Entity* ent = r->entity_at(i);
58 if (ent != nullptr && ecs::is_entity_visible(ent)) {
59 ssq::detail::pushByPtr<T>(vm, static_cast<T*>(ent));
60 sq_arrayappend(vm, -2);
61 }
62 }
63 }
64 if (buf->children != nullptr) stack.push_back(buf->children);
65 if (buf->next != nullptr) stack.push_back(buf->next);
66 }
67 sq_pop(vm, 1);
68 });
69}
70
71float normalizeHeading(float deg) {
72 deg = std::fmod(deg, 360.f);
73 if (deg < 0.f) deg += 360.f;
74 return deg;
75}
76
77} // namespace
78
80 switch (type) {
81 case VehicleOrderType::Move: return "move";
82 case VehicleOrderType::AttackMove: return "attack_move";
83 case VehicleOrderType::Attack: return "attack";
84 case VehicleOrderType::Stop: return "stop";
85 case VehicleOrderType::Hold: return "hold";
86 }
87 return "none";
88}
89
91 switch (type) {
92 case VehicleEventType::OrderCompleted: return "order_completed";
93 case VehicleEventType::Damaged: return "damaged";
94 case VehicleEventType::Destroyed: return "destroyed";
95 }
96 return "unknown";
97}
98
100 VehicleEntity* v = VehicleEntity::create();
101 v->identity();
102 v->definition();
103 v->input();
104 v->motion();
105 v->health();
106 v->orders();
107 v->orders()->adapter->syncCompatibility(*v->orders());
108 v->mounts();
109 v->physicsBody();
110 v->suspension();
111 v->seats();
112 v->stateFlags();
113 return v;
114}
115
116// ---------------------------------------------------------------------------
117// 模块生命周期
118// ---------------------------------------------------------------------------
119
121 VehicleSystem::setEventSink([this](const VehicleEvent& e) { events_.push_back(e); });
122}
123
125 destroyHandles(vehicles_);
127}
128
129// ---------------------------------------------------------------------------
130// 载具模板
131// ---------------------------------------------------------------------------
132
133int Vehicle::registerVehiclesFromJson(const std::string& json) {
134 auto canonical = eve::Value::fromJson(json);
135 if (!canonical) {
136 canonical.ignore("vehicle definition input could not be parsed as canonical Value");
137 return 0;
138 }
139
140 int n = 0;
141 const auto publish = [this, &n](const eve::Value& value) {
142 const auto* object = value.getIf<eve::Value::Object>();
143 if (object == nullptr) return;
144 const auto id = object->find("id");
145 const auto* name = id == object->end() ? nullptr : id->second.getIf<std::string>();
146 if (name == nullptr || name->empty()) return;
147
148 auto encoded = value.toJson();
149 if (!encoded) {
150 encoded.ignore("vehicle definition value could not be serialized canonically");
151 return;
152 }
154 source.type = "vehicle";
155 source.id = *name;
156 source.version = eve::SchemaVersion(1);
157 source.json = encoded.value();
158 auto typed = parseVehicleDefinition(source);
159 if (!typed) {
160 typed.ignore("vehicle definition failed typed projection validation");
161 return;
162 }
163
164 auto current = definitionRegistry_.resolve("vehicle", *name);
165 const bool exists = current.ok();
166 if (!exists && current.status().code() != eve::StatusCode::NotFound) return;
167 auto stored = exists ? definitionRegistry_.replace("vehicle", *name, 1, source.json)
168 : definitionRegistry_.insert("vehicle", *name, 1, source.json);
169 if (stored) {
170 ++n;
171 } else {
172 stored.ignore("vehicle definition registry rejected canonical registration");
173 }
174 };
175 const eve::Value& owningRoot = canonical.value();
176 if (const auto* array = owningRoot.getIf<eve::Value::Array>()) {
177 for (const auto& value : *array) publish(value);
178 } else {
179 publish(owningRoot);
180 }
181 return n;
182}
183
185 while (definitionRegistry_.countType("vehicle") > 0) {
186 const auto* definition = definitionRegistry_.atType("vehicle", 0);
187 if (definition == nullptr) break;
188 auto result = definitionRegistry_.remove("vehicle", definition->id);
189 if (!result) result.ignore("vehicle definition registry removal failed");
190 }
191}
192
193int Vehicle::getVehicleDefinitionCount() { return definitionRegistry_.countType("vehicle"); }
194
195bool Vehicle::hasVehicleDefinition(const std::string& id) {
196 auto result = definitionRegistry_.resolve("vehicle", id);
197 return result.ok();
198}
199
200std::string Vehicle::getVehicleDefinitionMobility(const std::string& id) {
201 auto definition = findDef(id);
202 if (!definition) {
203 definition.ignore("legacy scalar query returns an empty projection for an unknown definition");
204 return {};
205 }
206 return definition.value().mobility;
207}
208
209float Vehicle::getVehicleDefinitionMaxHealth(const std::string& id) {
210 auto definition = findDef(id);
211 if (!definition) {
212 definition.ignore("legacy scalar query returns zero for an unknown definition");
213 return 0.f;
214 }
215 return definition.value().maxHealth;
216}
217
219
221
222eve::Result<VehicleDefinition> Vehicle::findDef(const std::string& id) const {
223 auto resolved = definitionRegistry_.resolve("vehicle", id);
224 if (!resolved) return eve::Result<VehicleDefinition>::failure(resolved.status());
225 return parseVehicleDefinition(resolved.value().get());
226}
227
228// ---------------------------------------------------------------------------
229// 工厂
230// ---------------------------------------------------------------------------
231
232VehicleEntity* Vehicle::newVehicle(const std::string& defId, float x, float y, float heading,
233 const std::string& faction) {
234 auto definition = findDef(defId);
235 if (!definition) {
236 definition.ignore("legacy nullable factory returns null for an unknown definition");
237 return nullptr;
238 }
239 VehicleDefinition def = std::move(definition).takeValue();
240
242 v->identity()->id = defId + "#" + std::to_string(nextInstance_++);
243 v->identity()->defId = defId;
244 v->identity()->faction = faction;
245 v->definition()->owned = std::make_shared<const VehicleDefinition>(def);
246 v->definition()->def = v->definition()->owned.get();
247 v->motion()->x = x;
248 v->motion()->y = y;
249 v->motion()->heading = normalizeHeading(heading);
250 v->health()->maxHp = def.maxHealth;
251 v->health()->hp = def.maxHealth;
252
253 if (!def.mounts.empty()) {
254 if (eve::weapon::Weapon* wmod = requireModInst(eve::weapon, Weapon)) {
255 for (const MountDef& md : def.mounts) {
256 eve::weapon::WeaponMountEntity* m = wmod->newMount(md.name, md.type);
257 if (m == nullptr) continue;
258 m->identity()->nodePath = md.name;
259 wmod->mountSetLimits(m, md.yawMin, md.yawMax, md.pitchMin, md.pitchMax, md.rotSpeed, md.firingArc);
260 if (!md.weapon.empty()) {
261 if (eve::weapon::WeaponEntity* w = wmod->newWeapon(md.weapon)) {
262 wmod->mountAttachWeapon(m, w);
263 }
264 }
266 slot.mount = m;
267 slot.aimMode = md.aimMode;
268 v->mounts()->list.push_back(slot);
269 }
270 }
271 }
272
273 for (const SeatDef& sd : def.seats) {
275 slot.name = sd.name;
276 slot.driver = sd.driver;
277 slot.cameraMode = sd.cameraMode;
278 slot.mountIndex = sd.mountIndex;
279 v->seats()->list.push_back(slot);
280 }
281
282 vehicles_.push_back(ecs::handle_of(v));
283 return v;
284}
285
286// ---------------------------------------------------------------------------
287// RTS 命令
288// ---------------------------------------------------------------------------
289
290void Vehicle::moveTo(VehicleEntity* v, float x, float y) {
291 if (v == nullptr) return;
292 VehicleOrder o;
294 o.x = x;
295 o.y = y;
297 if (!queued) queued.ignore("Vehicle script facade cannot propagate order Result");
298}
299
300void Vehicle::attackMove(VehicleEntity* v, float x, float y) {
301 if (v == nullptr) return;
302 VehicleOrder o;
304 o.x = x;
305 o.y = y;
307 if (!queued) queued.ignore("Vehicle script facade cannot propagate order Result");
308}
309
310void Vehicle::attack(VehicleEntity* v, float x, float y, int targetId) {
311 if (v == nullptr) return;
312 VehicleOrder o;
314 o.x = x;
315 o.y = y;
316 o.targetId = targetId;
318 if (!queued) queued.ignore("Vehicle script facade cannot propagate order Result");
319}
320
322 if (v == nullptr) return;
323 VehicleOrder o;
326 if (!queued) queued.ignore("Vehicle script facade cannot propagate order Result");
327}
328
330 if (v == nullptr) return;
331 VehicleOrder o;
334 if (!queued) queued.ignore("Vehicle script facade cannot propagate order Result");
335}
336
340
342 return v == nullptr || v->orders()->adapter == nullptr ? 0 : v->orders()->adapter->activeOrQueuedCount();
343}
344
346 if (v == nullptr) return "none";
348 return o == nullptr ? "none" : vehicleOrderTypeName(o->type);
349}
350
351// ---------------------------------------------------------------------------
352// 状态
353// ---------------------------------------------------------------------------
354
355float Vehicle::getX(VehicleEntity* v) { return v == nullptr ? 0.f : v->motion()->x; }
356float Vehicle::getY(VehicleEntity* v) { return v == nullptr ? 0.f : v->motion()->y; }
357float Vehicle::getHeading(VehicleEntity* v) { return v == nullptr ? 0.f : v->motion()->heading; }
358float Vehicle::getSpeed(VehicleEntity* v) { return v == nullptr ? 0.f : v->motion()->speed; }
359
360void Vehicle::setPosition(VehicleEntity* v, float x, float y) {
361 if (v == nullptr) return;
362 v->motion()->x = x;
363 v->motion()->y = y;
364}
365
367 if (v != nullptr) v->motion()->heading = normalizeHeading(deg);
368}
369
370bool Vehicle::isArrived(VehicleEntity* v) { return v != nullptr && v->motion()->arrived; }
371
372float Vehicle::getHealth(VehicleEntity* v) { return v == nullptr ? 0.f : v->health()->hp; }
374 if (v != nullptr) v->health()->hp = std::max(0.f, hp);
375}
376float Vehicle::getMaxHealth(VehicleEntity* v) { return v == nullptr ? 0.f : v->health()->maxHp; }
377
378std::string Vehicle::getFaction(VehicleEntity* v) { return v == nullptr ? std::string{} : v->identity()->faction; }
379
380void Vehicle::setFaction(VehicleEntity* v, const std::string& faction) {
381 if (v != nullptr) v->identity()->faction = faction;
382}
383
384// ---------------------------------------------------------------------------
385// 伤害管线
386// ---------------------------------------------------------------------------
387
388void Vehicle::applyDamage(VehicleEntity* v, float amount, const std::string& zone, int sourceId) {
389 if (v == nullptr || amount <= 0.f) return;
390 if (v->stateFlags()->destroyed) return;
391
392 // 修饰器(多重监听,游戏侧叠加规则,如科技减伤/暴击)
393 eve::cap::forEach<IVehicleDamageModifier>(
394 [&](IVehicleDamageModifier* m) { amount = m->modifyDamage(*v, amount, zone, sourceId); });
395 if (amount <= 0.f) return;
396
397 // 装甲区倍率
398 float mult = 1.f;
399 if (!zone.empty()) {
400 for (const ArmorZone& z : v->definition()->def->armorZones) {
401 if (z.name == zone) {
402 mult = z.mult;
403 break;
404 }
405 }
406 }
407
408 const float hp = v->health()->hp - amount * mult;
409 v->health()->hp = std::max(0.f, hp);
410
411 VehicleEvent e;
412 e.vehicleId = v->identity()->id;
413 e.defId = v->identity()->defId;
414 e.x = v->motion()->x;
415 e.y = v->motion()->y;
416 if (hp <= 0.f) {
417 v->stateFlags()->destroyed = true;
419 } else {
421 }
422 events_.push_back(e);
423}
424
425float Vehicle::getArmorZoneMult(VehicleEntity* v, const std::string& zone) {
426 if (v == nullptr || v->definition()->def == nullptr || zone.empty()) return 1.f;
427 for (const ArmorZone& z : v->definition()->def->armorZones) {
428 if (z.name == zone) return z.mult;
429 }
430 return 1.f;
431}
432
433bool Vehicle::isDestroyed(VehicleEntity* v) { return v != nullptr && v->stateFlags()->destroyed; }
434
435// ---------------------------------------------------------------------------
436// 座位(FPS 面)
437// ---------------------------------------------------------------------------
438
439int Vehicle::getSeatCount(VehicleEntity* v) { return v == nullptr ? 0 : static_cast<int>(v->seats()->list.size()); }
440
441std::string Vehicle::getSeatName(VehicleEntity* v, int seatIndex) {
442 if (v == nullptr || seatIndex < 0 || static_cast<size_t>(seatIndex) >= v->seats()->list.size()) {
443 return std::string{};
444 }
445 return v->seats()->list[static_cast<size_t>(seatIndex)].name;
446}
447
448std::string Vehicle::getSeatCameraMode(VehicleEntity* v, int seatIndex) {
449 if (v == nullptr || seatIndex < 0 || static_cast<size_t>(seatIndex) >= v->seats()->list.size()) {
450 return std::string{};
451 }
452 return v->seats()->list[static_cast<size_t>(seatIndex)].cameraMode;
453}
454
456 if (v == nullptr || seatIndex < 0 || static_cast<size_t>(seatIndex) >= v->seats()->list.size()) {
457 return false;
458 }
459 return v->seats()->list[static_cast<size_t>(seatIndex)].occupied;
460}
461
463 if (v == nullptr || seatIndex < 0 || static_cast<size_t>(seatIndex) >= v->seats()->list.size()) {
464 return 0;
465 }
466 return v->seats()->list[static_cast<size_t>(seatIndex)].occupant;
467}
468
470 if (v == nullptr || seatIndex < 0 || static_cast<size_t>(seatIndex) >= v->seats()->list.size()) {
471 return nullptr;
472 }
473 const VehicleEntity::SeatSlot& s = v->seats()->list[static_cast<size_t>(seatIndex)];
474 if (s.mountIndex < 0 || static_cast<size_t>(s.mountIndex) >= v->mounts()->list.size()) {
475 return nullptr;
476 }
477 return v->mounts()->list[static_cast<size_t>(s.mountIndex)].mount;
478}
479
480bool Vehicle::enterSeat(VehicleEntity* v, int seatIndex, int playerId) {
481 return v != nullptr && VehicleSystem::enterSeat(*v, seatIndex, playerId);
482}
483
484bool Vehicle::exitSeat(VehicleEntity* v, int seatIndex) {
485 return v != nullptr && VehicleSystem::exitSeat(*v, seatIndex);
486}
487
489 if (v == nullptr) return -1;
490 const int idx = VehicleSystem::findSeatByPlayer(*v, playerId);
491 if (idx >= 0) VehicleSystem::exitSeat(*v, idx);
492 return idx;
493}
494
495void Vehicle::setPlayerControls(int playerId, float throttle, float steer, float brake, bool fire, float aimYaw,
496 float aimPitch) {
498 c.throttle = throttle;
499 c.steer = steer;
500 c.brake = brake;
501 c.fire = fire;
502 c.aimYaw = aimYaw;
503 c.aimPitch = aimPitch;
505}
506
507// ---------------------------------------------------------------------------
508// 驾驶输入与物理绑定
509// ---------------------------------------------------------------------------
510
511void Vehicle::setInput(VehicleEntity* v, float throttle, float steer, float brake, bool handbrake) {
512 if (v == nullptr) return;
513 auto in = v->input();
514 in->throttle = throttle;
515 in->steer = steer;
516 in->brake = brake;
517 in->handbrake = handbrake;
518}
519
523
527
531
535
537 return VehiclePhysics::isAttached(v) ? v->physicsBody()->space : std::string{};
538}
539
541
542// ---------------------------------------------------------------------------
543// 挂点
544// ---------------------------------------------------------------------------
545
546int Vehicle::getMountCount(VehicleEntity* v) { return v == nullptr ? 0 : static_cast<int>(v->mounts()->list.size()); }
547
549 if (v == nullptr || index < 0 || static_cast<size_t>(index) >= v->mounts()->list.size()) {
550 return nullptr;
551 }
552 return v->mounts()->list[index].mount;
553}
554
555// ---------------------------------------------------------------------------
556// 帧推进与自动瞄准
557// ---------------------------------------------------------------------------
558
559void Vehicle::update(float dt) {
560 for (const ecs::EntityHandle& h : vehicles_) {
561 if (VehicleEntity* v = resolve<VehicleEntity>(h)) {
562 updateSeats(*v); // 座位输入写入(无命令时生效)
563 VehicleSystem::update(*v, dt); // 命令优先,然后移动
564 autoAim(*v); // RTS 攻击命令自动瞄准
565 }
566 }
567}
568
569void Vehicle::updateSeats(VehicleEntity& v) {
570 auto seats = v.seats();
571 if (seats->list.empty()) return;
572
573 eve::weapon::Weapon* wmod = eve::ModuleManager::getInstance<eve::weapon::Weapon>("Weapon");
574 bool driverApplied = false;
575
576 for (const VehicleEntity::SeatSlot& s : seats->list) {
577 if (!s.occupied) continue;
579 if (driver == nullptr) continue;
580
581 VehicleInput in;
582 if (!driver->sample(v, s.occupant, in)) continue;
583
584 // 驾驶员座位:写移动输入(只取第一个有效驾驶员)
585 if (!driverApplied && s.name == "driver") {
586 auto vi = v.input();
587 vi->throttle = in.throttle;
588 vi->steer = in.steer;
589 vi->brake = in.brake;
590 vi->handbrake = in.handbrake;
591 vi->fire = in.fire;
592 vi->aimYaw = in.aimYaw;
593 vi->aimPitch = in.aimPitch;
594 driverApplied = true;
595 }
596
597 // 武器座位:瞄准 + 开火
598 if (s.mountIndex >= 0 && static_cast<size_t>(s.mountIndex) < v.mounts()->list.size() && wmod != nullptr) {
599 eve::weapon::WeaponMountEntity* m = v.mounts()->list[s.mountIndex].mount;
600 if (m == nullptr || m->state()->destroyed) continue;
601 wmod->mountAimAt(m, in.aimYaw, in.aimPitch);
602 if (in.fire) {
605 req.yaw = in.aimYaw;
606 req.pitch = in.aimPitch;
607 req.shooterId = s.occupant;
608 wmod->fire(w, req);
609 }
610 }
611 }
612 }
613}
614
615void Vehicle::autoAim(VehicleEntity& v) {
616 const VehicleOrder* cur = VehicleSystem::currentOrder(v);
617 if (cur == nullptr || (cur->type != VehicleOrderType::Attack && cur->type != VehicleOrderType::AttackMove)) {
618 return;
619 }
620 if (v.mounts()->list.empty()) return;
621
622 eve::weapon::Weapon* wmod = eve::ModuleManager::getInstance<eve::weapon::Weapon>("Weapon");
623 if (wmod == nullptr) return;
624
625 auto mo = v.motion();
626 const float worldYaw = std::atan2(cur->y - mo->y, cur->x - mo->x) * 180.f / kPi;
627 float localYaw = worldYaw - mo->heading;
628 localYaw = std::fmod(localYaw + 180.f, 360.f);
629 if (localYaw < 0.f) localYaw += 360.f;
630 localYaw -= 180.f;
631
632 const float dist = std::sqrt((cur->x - mo->x) * (cur->x - mo->x) +
633 (cur->y - mo->y) * (cur->y - mo->y));
634 for (const VehicleEntity::MountSlot& slot : v.mounts()->list) {
635 if (slot.mount == nullptr || slot.mount->state()->destroyed) continue;
636 if (slot.aimMode != "auto") continue;
637 wmod->mountAimAt(slot.mount, localYaw, 0.f);
638 // 攻击命令:炮塔在射程内自动开火;attackMove 只负责边走边瞄准。
639 if (cur->type != VehicleOrderType::Attack) continue;
640 eve::weapon::WeaponEntity* w = wmod->mountGetWeapon(slot.mount);
641 if (w == nullptr || !wmod->canFire(w)) continue;
642 const std::string& defId = w->identity()->defId;
643 const float range = wmod->getWeaponDefinitionRange(defId);
644 if (range > 0.f && dist > range) continue;
645 wmod->fireAt(w, cur->x, cur->y, 0.f, cur->targetId);
646 }
647}
648
649// ---------------------------------------------------------------------------
650// 事件队列
651// ---------------------------------------------------------------------------
652
653void Vehicle::clearEvents() { events_.clear(); }
654
655int Vehicle::getEventCount() const { return static_cast<int>(events_.size()); }
656
657std::string Vehicle::getEventType(int index) const {
658 return (index >= 0 && index < getEventCount()) ? vehicleEventTypeName(events_[index].type) : std::string{};
659}
660
661std::string Vehicle::getEventVehicleId(int index) const {
662 return (index >= 0 && index < getEventCount()) ? events_[index].vehicleId : std::string{};
663}
664
665std::string Vehicle::getEventDefId(int index) const {
666 return (index >= 0 && index < getEventCount()) ? events_[index].defId : std::string{};
667}
668
669std::string Vehicle::getEventOrderType(int index) const {
670 return (index >= 0 && index < getEventCount()) ? events_[index].orderType : std::string{};
671}
672
673float Vehicle::getEventX(int index) const { return (index >= 0 && index < getEventCount()) ? events_[index].x : 0.f; }
674
675float Vehicle::getEventY(int index) const { return (index >= 0 && index < getEventCount()) ? events_[index].y : 0.f; }
676
677// ---------------------------------------------------------------------------
678// 脚本绑定
679// ---------------------------------------------------------------------------
680
681void Vehicle::expose(ssq::Table& table) {
682 auto cls = table.addClass(name, Vehicle::create, false);
683 expose(cls);
684
685 auto vCls = table.addClass<VehicleEntity>(
686 "VehicleEntity", std::function<VehicleEntity*()>([]() -> VehicleEntity* { return nullptr; }), false);
687 registerCppEntityClassForScript<VehicleEntity>(vCls);
688 vCls.addFunc("getId", [](VehicleEntity* v) -> std::string { return v ? v->identity()->id : std::string{}; });
689 vCls.addFunc("getDefId", [](VehicleEntity* v) -> std::string { return v ? v->identity()->defId : std::string{}; });
690 vCls.addFunc("getFaction",
691 [](VehicleEntity* v) -> std::string { return v ? v->identity()->faction : std::string{}; });
692 vCls.addFunc("setFaction", [](VehicleEntity* v, const std::string& f) {
693 if (v) v->identity()->faction = f;
694 });
695 vCls.addFunc("getX", [](VehicleEntity* v) -> float { return v ? v->motion()->x : 0.f; });
696 vCls.addFunc("getY", [](VehicleEntity* v) -> float { return v ? v->motion()->y : 0.f; });
697 vCls.addFunc("getHeading", [](VehicleEntity* v) -> float { return v ? v->motion()->heading : 0.f; });
698 vCls.addFunc("getSpeed", [](VehicleEntity* v) -> float { return v ? v->motion()->speed : 0.f; });
699 vCls.addFunc("getHealth", [](VehicleEntity* v) -> float { return v ? v->health()->hp : 0.f; });
700 vCls.addFunc("getMaxHealth", [](VehicleEntity* v) -> float { return v ? v->health()->maxHp : 0.f; });
701 vCls.addFunc("isDestroyed", [](VehicleEntity* v) -> bool { return v != nullptr && v->stateFlags()->destroyed; });
702 vCls.addFunc("isArrived", [](VehicleEntity* v) -> bool { return v != nullptr && v->motion()->arrived; });
703 vCls.addFunc("getCurrentOrderType", [](VehicleEntity* v) -> std::string {
704 if (v == nullptr) return "none";
705 const VehicleOrder* o = VehicleSystem::currentOrder(*v);
706 return o == nullptr ? "none" : vehicleOrderTypeName(o->type);
707 });
708 vCls.addFunc("getMountCount",
709 [](VehicleEntity* v) -> int { return v == nullptr ? 0 : static_cast<int>(v->mounts()->list.size()); });
710 vCls.addFunc("getMount", [](VehicleEntity* v, int index) -> eve::weapon::WeaponMountEntity* {
711 if (v == nullptr || index < 0 || static_cast<size_t>(index) >= v->mounts()->list.size()) {
712 return nullptr;
713 }
714 return v->mounts()->list[index].mount;
715 });
716}
717
718void Vehicle::expose(ssq::Class& cls) {
719 cls.addFunc("registerVehiclesFromJson", &Vehicle::registerVehiclesFromJson);
720 cls.addFunc("clearVehicleDefinitions", &Vehicle::clearVehicleDefinitions);
721 cls.addFunc("getVehicleDefinitionCount", &Vehicle::getVehicleDefinitionCount);
722 cls.addFunc("hasVehicleDefinition", &Vehicle::hasVehicleDefinition);
723 cls.addFunc("getVehicleDefinitionMobility", &Vehicle::getVehicleDefinitionMobility);
724 cls.addFunc("getVehicleDefinitionMaxHealth", &Vehicle::getVehicleDefinitionMaxHealth);
725 cls.addFunc("getMobilityCount", [](Vehicle*) -> int { return VehicleSystem::mobilityCount(); });
726 cls.addFunc("newVehicle", &Vehicle::newVehicle);
727 cls.addFunc("moveTo", &Vehicle::moveTo);
728 cls.addFunc("attackMove", &Vehicle::attackMove);
729 cls.addFunc("attack", &Vehicle::attack);
730 cls.addFunc("stop", &Vehicle::stop);
731 cls.addFunc("hold", &Vehicle::hold);
732 cls.addFunc("clearOrders", &Vehicle::clearOrders);
733 cls.addFunc("orderCount", &Vehicle::orderCount);
734 cls.addFunc("getCurrentOrderType", &Vehicle::getCurrentOrderType);
735 cls.addFunc("setInput", &Vehicle::setInput);
736 cls.addFunc("getX", &Vehicle::getX);
737 cls.addFunc("getY", &Vehicle::getY);
738 cls.addFunc("getHeading", &Vehicle::getHeading);
739 cls.addFunc("getSpeed", &Vehicle::getSpeed);
740 cls.addFunc("setPosition", &Vehicle::setPosition);
741 cls.addFunc("setHeading", &Vehicle::setHeading);
742 cls.addFunc("isArrived", &Vehicle::isArrived);
743 cls.addFunc("getHealth", &Vehicle::getHealth);
744 cls.addFunc("setHealth", &Vehicle::setHealth);
745 cls.addFunc("getMaxHealth", &Vehicle::getMaxHealth);
746 cls.addFunc("getFaction", &Vehicle::getFaction);
747 cls.addFunc("setFaction", &Vehicle::setFaction);
748 cls.addFunc("applyDamage", &Vehicle::applyDamage);
749 cls.addFunc("getArmorZoneMult", &Vehicle::getArmorZoneMult);
750 cls.addFunc("isDestroyed", &Vehicle::isDestroyed);
751 cls.addFunc("getSeatCount", &Vehicle::getSeatCount);
752 cls.addFunc("getSeatName", &Vehicle::getSeatName);
753 cls.addFunc("getSeatCameraMode", &Vehicle::getSeatCameraMode);
754 cls.addFunc("isSeatOccupied", &Vehicle::isSeatOccupied);
755 cls.addFunc("getSeatOccupant", &Vehicle::getSeatOccupant);
756 cls.addFunc("getSeatMount", &Vehicle::getSeatMount);
757 cls.addFunc("enterSeat", &Vehicle::enterSeat);
758 cls.addFunc("exitSeat", &Vehicle::exitSeat);
759 cls.addFunc("exitSeatByPlayer", &Vehicle::exitSeatByPlayer);
760 cls.addFunc("setPlayerControls", &Vehicle::setPlayerControls);
761 cls.addFunc("detachPhysics", &Vehicle::detachPhysics);
762 cls.addFunc("hasPhysics", &Vehicle::hasPhysics);
763 cls.addFunc("getPhysicsSpace", &Vehicle::getPhysicsSpace);
764 cls.addFunc("getHeight", &Vehicle::getHeight);
765 cls.addFunc("attachPhysics2D", &Vehicle::attachPhysics2D);
766 cls.addFunc("attachPhysics3D", &Vehicle::attachPhysics3D);
767 cls.addFunc("getMountCount", &Vehicle::getMountCount);
768 cls.addFunc("getMount", &Vehicle::getMount);
769 cls.addFunc("update", &Vehicle::update);
770 cls.addFunc("clearEvents", &Vehicle::clearEvents);
771 cls.addFunc("getEventCount", &Vehicle::getEventCount);
772 cls.addFunc("getEventType", &Vehicle::getEventType);
773 cls.addFunc("getEventVehicleId", &Vehicle::getEventVehicleId);
774 cls.addFunc("getEventDefId", &Vehicle::getEventDefId);
775 cls.addFunc("getEventOrderType", &Vehicle::getEventOrderType);
776 cls.addFunc("getEventX", &Vehicle::getEventX);
777 cls.addFunc("getEventY", &Vehicle::getEventY);
778}
779
780} // namespace eve::vehicle
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
struct SQVM * HSQUIRRELVM
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::uint64_t sourceId
std::vector< std::uint8_t > stored
Definition Evpack.cpp:171
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::int32_t c
int h
Range range
bool exists
std::string name
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
#define requireModInst(N, T)
Definition Module.h:39
size_t queued
Definition OnnxGpgpu.cpp:60
int idx
float f
World3D * world
std::uint32_t targetId
double current
std::string string
bool occupied
V3 heading
Definition TreeMesh.cpp:292
uint32_t index
const UnitySourceAsset & source
Owning, renderer-independent dynamic values.
Strongly typed Vehicle definition/entity runtime adapter.
float m[16]
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
eve::ResultRef< const Definition > resolve(const std::string &type, const std::string &id) const
Resolves a definition through the common registry API.
eve::Result< DefinitionHandle > insert(const std::string &type, const std::string &id, int version, const std::string &json)
Inserts a definition using the common generation-qualified registry.
eve::Result< DefinitionHandle > replace(const std::string &type, const std::string &id, int version, const std::string &json)
Replaces a live definition and invalidates its previous handle.
eve::Result< DefinitionHandle > remove(const std::string &type, const std::string &id)
Removes a definition and returns the retained tombstone handle.
int countType(const std::string &type) const
Returns the number of definitions registered for a type.
const Definition * atType(const std::string &type, int index) const
Returns a definition by id order within one type, or nullptr.
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
Box2D world wrapper (2D physics) with pixel-space coordinates. Handles stepping, gravity,...
Definition World.h:41
伤害修饰器(游戏侧注册,多重监听,按优先级调用)。
驾驶者:把某乘客的控制转成载具输入。
virtual bool sample(VehicleEntity &v, int occupantId, VehicleInput &out)=0
采样 occupantId 乘客的控制;返回是否有效(有效则填充 out)。
移动模型:读输入、更新运动状态。
载具实体:数据全部在组件里,行为在 VehicleSystem。
static VehicleEntity * createVehicle()
创建并触摸全部组件。
Definition Vehicle.cpp:99
static VehiclePhysicsStatus detach(VehicleEntity *v)
Destroy attached bodies if any.
static VehiclePhysicsStatus attach2D(VehicleEntity *v, eve::physics::World *world)
Attach a 2D dynamic body when physics is in the build.
static bool isAttached(VehicleEntity *v)
Whether the entity has a live, resolvable physics body.
static VehiclePhysicsStatus attach3D(VehicleEntity *v, eve::physics::World3D *world, float heightY)
Attach a 3D dynamic body when physics is in the build.
static float height(VehicleEntity *v)
3D body height, or 0 when no 3D body is attached.
static void update(VehicleEntity &v, float dt)
每帧推进:命令 → 输入 → 移动模型。自动瞄准由 Vehicle 模块完成。
static eve::Result< void > pushOrder(VehicleEntity &v, const VehicleOrder &order)
追加一条命令;Move/AttackMove 会清空旧命令(RTS 习惯)。
static void setPlayerControls(int playerId, const PlayerControl &control)
写入某玩家的控制状态(游戏每帧填充)。
static IVehicleDriver * findDriver(const std::string &name)
按名字取驾驶者;未注册返回 nullptr。
static const VehicleOrder * currentOrder(VehicleEntity &v)
当前命令;无命令返回 nullptr。
static bool exitSeat(VehicleEntity &v, int seatIndex)
Exit seat.
static void setEventSink(VehicleEventSink sink)
注册事件汇(替换旧的;传 nullptr 清空)。
static void registerMobility(IVehicleMobility *mobility)
注册移动模型实现;同名替换。
static int findSeatByPlayer(VehicleEntity &v, int playerId)
Finds seat by player.
static void clearOrders(VehicleEntity &v)
清空命令队列。
static bool enterSeat(VehicleEntity &v, int seatIndex, int playerId)
座位:进入 / 离开 / 按玩家找座位。
static int mobilityCount()
已注册移动模型数量。
载具模块(eve.Vehicle)。
Definition Vehicle.h:33
bool enterSeat(VehicleEntity *v, int seatIndex, int playerId)
Enter seat.
Definition Vehicle.cpp:480
int getSeatOccupant(VehicleEntity *v, int seatIndex)
Returns the seat occupant.
Definition Vehicle.cpp:462
eve::weapon::WeaponMountEntity * getMount(VehicleEntity *v, int index)
Returns the mount.
Definition Vehicle.cpp:548
int getVehicleDefinitionCount()
已注册载具模板数量。
Definition Vehicle.cpp:193
bool attachPhysics2D(VehicleEntity *v, eve::physics::World *world)
将载具绑定到 physics world 拥有的 2D 刚体。
Definition Vehicle.cpp:520
void applyDamage(VehicleEntity *v, float amount, const std::string &zone="", int sourceId=0)
伤害管线:修饰器 → 装甲区倍率 → 扣血 → 事件。
Definition Vehicle.cpp:388
float getMaxHealth(VehicleEntity *v)
Returns the max health.
Definition Vehicle.cpp:376
float getArmorZoneMult(VehicleEntity *v, const std::string &zone)
装甲区倍率(未命中任何区返回 1)。
Definition Vehicle.cpp:425
VehicleEntity * newVehicle(const std::string &defId, float x, float y, float heading=0.f, const std::string &faction="")
工厂:按模板创建载具并挂载武器挂点(ECS 表持有,脚本持有句柄)。
Definition Vehicle.cpp:232
void attack(VehicleEntity *v, float x, float y, int targetId=0)
Attack.
Definition Vehicle.cpp:310
bool hasVehicleDefinition(const std::string &id)
载具模板查询。
Definition Vehicle.cpp:195
~Vehicle() override
Vehicle.
Definition Vehicle.cpp:124
void setPlayerControls(int playerId, float throttle, float steer, float brake, bool fire, float aimYaw, float aimPitch)
写入玩家控制(归一化;角度为度)。
Definition Vehicle.cpp:495
float getHealth(VehicleEntity *v)
Returns the health.
Definition Vehicle.cpp:372
void setHealth(VehicleEntity *v, float hp)
Sets the health.
Definition Vehicle.cpp:373
void moveTo(VehicleEntity *v, float x, float y)
RTS 命令。
Definition Vehicle.cpp:290
void setHeading(VehicleEntity *v, float deg)
Sets the heading.
Definition Vehicle.cpp:366
bool hasPhysics(VehicleEntity *v)
查询当前绑定是否仍能解析为存活刚体。
Definition Vehicle.cpp:532
std::string getSeatCameraMode(VehicleEntity *v, int seatIndex)
Returns the seat camera mode.
Definition Vehicle.cpp:448
void attackMove(VehicleEntity *v, float x, float y)
Attack move.
Definition Vehicle.cpp:300
std::string getEventType(int index) const
Returns the event type.
Definition Vehicle.cpp:657
std::string getVehicleDefinitionMobility(const std::string &id)
Returns the vehicle definition mobility.
Definition Vehicle.cpp:200
float getHeight(VehicleEntity *v)
3D 车身高度(无 3D 物理时返回 0)。
Definition Vehicle.cpp:540
int getEventCount() const
Returns the event count.
Definition Vehicle.cpp:655
int getSeatCount(VehicleEntity *v)
座位(FPS 面)。
Definition Vehicle.cpp:439
int getMountCount(VehicleEntity *v)
挂点查询(返回 weapon 模块的挂点实体)。
Definition Vehicle.cpp:546
void update(float dt)
每帧推进全部载具(命令 → 移动 → RTS 自动瞄准)。
Definition Vehicle.cpp:559
void clearEvents()
事件队列(order_completed 等,上一次 update 产生)。
Definition Vehicle.cpp:653
float getY(VehicleEntity *v)
Returns the y.
Definition Vehicle.cpp:356
float getVehicleDefinitionMaxHealth(const std::string &id)
Returns the vehicle definition max health.
Definition Vehicle.cpp:209
void clearVehicleDefinitions()
清空全部载具模板。
Definition Vehicle.cpp:184
std::string getFaction(VehicleEntity *v)
Returns the faction.
Definition Vehicle.cpp:378
float getHeading(VehicleEntity *v)
Returns the heading.
Definition Vehicle.cpp:357
float getEventY(int index) const
Returns the event y.
Definition Vehicle.cpp:675
std::string getSeatName(VehicleEntity *v, int seatIndex)
Returns the seat name.
Definition Vehicle.cpp:441
std::string getPhysicsSpace(VehicleEntity *v)
返回存活绑定的 "2d"/"3d";缺失或 stale 时返回空字符串。
Definition Vehicle.cpp:536
std::string getEventDefId(int index) const
Returns the event def id.
Definition Vehicle.cpp:665
static void registerMobility(IVehicleMobility *mobility)
注册移动模型(C++ 插件/游戏侧扩展点;同名替换)。
Definition Vehicle.cpp:218
eve::weapon::WeaponMountEntity * getSeatMount(VehicleEntity *v, int seatIndex)
座位绑定的武器挂点(mountIndex 无效返回 nullptr)。
Definition Vehicle.cpp:469
static int getMobilityCount()
已注册移动模型数量(内置 kinematic 起步)。
Definition Vehicle.cpp:220
float getSpeed(VehicleEntity *v)
Returns the speed.
Definition Vehicle.cpp:358
bool isDestroyed(VehicleEntity *v)
是否已摧毁。
Definition Vehicle.cpp:433
bool attachPhysics3D(VehicleEntity *v, eve::physics::World3D *world, float heightY=1.f)
将载具绑定到 physics world 拥有的 3D 刚体。
Definition Vehicle.cpp:524
int exitSeatByPlayer(VehicleEntity *v, int playerId)
Exit seat by player.
Definition Vehicle.cpp:488
float getEventX(int index) const
Returns the event x.
Definition Vehicle.cpp:673
std::string getEventOrderType(int index) const
Returns the event order type.
Definition Vehicle.cpp:669
bool isArrived(VehicleEntity *v)
True when arrived.
Definition Vehicle.cpp:370
void setFaction(VehicleEntity *v, const std::string &faction)
Sets the faction.
Definition Vehicle.cpp:380
std::string getCurrentOrderType(VehicleEntity *v)
当前命令类型名("move" / "attack_move" / "attack" / "stop" / "hold" / "none")。
Definition Vehicle.cpp:345
float getX(VehicleEntity *v)
状态查询 / 设置。
Definition Vehicle.cpp:355
int orderCount(VehicleEntity *v)
Order count.
Definition Vehicle.cpp:341
void clearOrders(VehicleEntity *v)
Clears orders.
Definition Vehicle.cpp:337
void hold(VehicleEntity *v)
Hold.
Definition Vehicle.cpp:329
std::string getEventVehicleId(int index) const
Returns the event vehicle id.
Definition Vehicle.cpp:661
void stop(VehicleEntity *v)
Stops .
Definition Vehicle.cpp:321
int registerVehiclesFromJson(const std::string &json)
从 JSON 注册载具模板;返回成功注册数量。
Definition Vehicle.cpp:133
void setInput(VehicleEntity *v, float throttle, float steer, float brake=0.f, bool handbrake=false)
直接驾驶输入(FPS / 脚本驱动;RTS 命令也会写 input)。
Definition Vehicle.cpp:511
bool exitSeat(VehicleEntity *v, int seatIndex)
Exit seat.
Definition Vehicle.cpp:484
bool detachPhysics(VehicleEntity *v)
解除绑定,并在 world 仍存活时销毁由载具创建的刚体。
Definition Vehicle.cpp:528
bool isSeatOccupied(VehicleEntity *v, int seatIndex)
True when seat occupied.
Definition Vehicle.cpp:455
void setPosition(VehicleEntity *v, float x, float y)
Sets the position.
Definition Vehicle.cpp:360
武器实体:数据全部在组件里,行为在 WeaponSystem。
武器挂点实体:可持有武器并限制旋转(炮塔 / 机枪座 / 固定挂架)。
武器模块(eve.Weapon)。
Definition Weapon.h:24
bool fireAt(WeaponEntity *w, float x, float y, float z, int shooterId=0)
向目标点开火(自动生成 FireRequest)。
Definition Weapon.cpp:450
WeaponEntity * mountGetWeapon(WeaponMountEntity *m)
Returns the weapon mounted on a mount, or null when empty.
Definition Weapon.cpp:411
float getWeaponDefinitionRange(const std::string &id)
Returns the weapon definition range.
Definition Weapon.cpp:264
bool fire(WeaponEntity *w, const FireRequest &req)
武器操作(脚本便捷入口,转发 WeaponSystem)。
Definition Weapon.cpp:448
bool canFire(WeaponEntity *w)
Can fire.
Definition Weapon.cpp:474
void mountAimAt(WeaponMountEntity *m, float yaw, float pitch)
按限位夹取目标角并写入挂载武器的瞄准目标(角度,度)。
Definition Weapon.cpp:424
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
WidgetDesc list(std::string listId, const std::vector< std::string > &items, const std::function< WidgetDesc(const std::string &, int)> &itemFn)
Expand a string list into a Group of item widgets. itemFn(label, index) builds each row; keys default...
Definition Widget.cpp:715
驾驶者接口与玩家控制状态。
const char * vehicleOrderTypeName(VehicleOrderType type)
命令类型的字符串名("move" | "attack_move" | ...)。
Definition Vehicle.cpp:79
eve::Result< VehicleDefinition > parseVehicleDefinition(const eve::definitions::Definition &source)
Parse one canonical registry definition into a short-lived typed projection.
VehicleEventType
载具事件类型。
const char * vehicleEventTypeName(VehicleEventType type)
事件类型的字符串名。
Definition Vehicle.cpp:90
@ Applied
Physics handled the request.
VehicleOrderType
RTS 命令类型。
载具模块入口(eve.Vehicle):定义注册 / 实体工厂 / RTS 命令 / 帧调度。 设计文档:docs/dev/通用载具系统设计.md
detail::StrongUint64< detail::SchemaVersionTag > SchemaVersion
Persistent data-format version; not a runtime replacement generation.
void registerCppEntityView(const ssq::Class &cls, CppEntityViewFn fn)
Registers cpp entity view.
Definition ECS.cpp:861
One immutable view of a registered versioned JSON definition.
Definition Definitions.h:31
装甲区(Phase 4 伤害管线使用;Phase 2 仅作为定义数据保留)。
武器挂点声明(载具模板的一部分)。
一名玩家的原始控制(归一化,游戏侧填充)。角度为度。
座位声明(载具模板的一部分,FPS 面)。
载具模板(registerVehiclesFromJson 注册,进程级注册表)。
std::vector< MountDef > mounts
std::vector< SeatDef > seats
一个挂点槽位:挂点实体 + 瞄准模式。
eve::weapon::WeaponMountEntity * mount
座位槽位(运行时,与模板 seats 一一对应)。
载具事件(帧缓存,脚本轮询)。
每帧驾驶输入(由 PlayerDriver / OrderSystem / 脚本写入)。
一条 Vehicle 领域命令(生命周期由 Vehicle order adapter 执行)。
一次攻击请求(C++ 系统 / 脚本发起)。角度均为度。 旧名 FireRequest 保留为别名,向后兼容。
int shooterId
游戏侧实体/玩家 id。
float yaw
发射方向(度)。