8#include <Tempest/Painter>
47 case zenkit::MaterialGroup::UNDEFINED:
48 case zenkit::MaterialGroup::NONE:
50 case zenkit::MaterialGroup::METAL:
52 case zenkit::MaterialGroup::STONE:
54 case zenkit::MaterialGroup::WOOD:
56 case zenkit::MaterialGroup::EARTH:
58 case zenkit::MaterialGroup::WATER:
60 case zenkit::MaterialGroup::SNOW:
67 :wname(std::move(file)), game(game), wsound(game,*this), wobj(*this) {
70 if(entry ==
nullptr) {
71 Tempest::Log::e(
"unable to open Zen-file: \"",wname,
"\"");
76 auto buf = entry->open_read();
78 world.load(buf.get(),
version().game == 1 ? zenkit::GameVersion::GOTHIC_1
79 : zenkit::GameVersion::GOTHIC_2);
82 auto& worldMesh = world.world_mesh;
84 auto wdynamicFut = std::async(std::launch::async, [&]() {
86 return std::unique_ptr<DynamicWorld>(
new DynamicWorld(*
this,worldMesh));
88 auto wviewFut = std::async(std::launch::async, [&]() {
91 return std::unique_ptr<WorldView>(
new WorldView(*
this,vmesh));
97 bsp.nodes = std::move(world.world_bsp_tree.nodes);
98 bsp.sectors = std::move(world.world_bsp_tree.sectors);
99 bsp.leaf_node_indices = std::move(world.world_bsp_tree.leaf_node_indices);
100 bsp.sectorsData.resize(bsp.sectors.size());
101 world.world_bsp_tree = zenkit::BspTree();
105 wview = wviewFut.get();
108 wdynamic = wdynamicFut.get();
112 wmatrix.reset(
new WayMatrix(*
this, *world.way_net));
113 for(
auto& vob:world.world_vobs)
116 wmatrix->buildIndex();
120 Tempest::Log::e(
"unable to load landscape mesh");
132 std::string_view waypoint = wmatrix->startPoint().name;
134 npcPlayer = wobj.
insertPlayer(std::move(npc), waypoint);
149 if(!npcPlayer->
isDead()) {
172 fin.
setEntry(
"worlds/",wname,
"/world");
177 for(
size_t i=0;i<sz;++i) {
180 if(
auto p = portalAt(tag))
190 fout.
setEntry(
"worlds/",wname,
"/world");
192 fout.
write(uint32_t(bsp.sectorsData.size()));
193 for(
size_t i=0;i<bsp.sectorsData.size();++i) {
194 fout.
write(bsp.sectors[i].name,bsp.sectorsData[i].guild);
201 return wobj.
npcId(ptr);
206 return &wobj.
npc(
id);
220 return &wobj.
mobsi(
id);
225 return wobj.
itmId(ptr);
230 return &wobj.
itm(
id);
240 while(vfx!=
nullptr) {
241 globFx->stopEffect(*vfx);
242 vfx = vfx->emFXCreate;
248 return globFx->startEffect(what,len,argv,argc);
256 return view()->
addView(visual,headTex,teetTex,bodyColor);
260 return view()->
addView(itm.visual,itm.material,0,itm.material);
300 return wobj.
takeNpc(npcPlayer);
312 if(bsp.nodes.empty())
315 const auto* node=&bsp.nodes[0];
318 const auto v = node->plane;
319 float sgn = v.x*p.x + v.y*p.y + v.z*p.z - v.w;
320 uint32_t next = (sgn>0) ? uint32_t(node->front_index) : uint32_t(node->back_index);
321 if(next>=bsp.nodes.size())
324 node = &bsp.nodes[next];
327 if(node->bbox.min.x <= p.x && p.x <node->bbox.max.x &&
328 node->bbox.min.y <= p.y && p.y <node->bbox.max.y &&
329 node->bbox.min.z <= p.z && p.z <node->bbox.max.z) {
337 const std::string* ret=
nullptr;
339 auto id = &node-bsp.nodes.data();(void)
id;
342 for(
auto r:i.node_indices)
344 size_t idx = bsp.leaf_node_indices[r];
345 if(idx>=bsp.nodes.size())
347 if(&bsp.nodes[idx]==&node) {
357 static std::string empty;
361World::BspSector* World::portalAt(std::string_view tag) {
365 for(
size_t i=0;i<bsp.sectors.size();++i)
366 if(bsp.sectors[i].name==tag)
367 return &bsp.sectorsData[i];
372 globFx->scaleTime(dt);
376 static bool doTicks=
true;
393 auto day = now.
day();
422 auto& policy = searchPolicy(pl,collAlgo,collType,opt);
424 WorldObjects::SearchOpt optNpc {policy.npc_range1, policy.npc_range2, policy.npc_azi, collAlgo, collType, opt};
428 auto n = policy.npc_prio <0 ? nullptr : wobj.
findNpcNear (pl,def.
npc, optNpc);
429 auto it = policy.item_prio<0 ? nullptr : wobj.
findItem (pl,def.
item, optItm);
437 if(policy.npc_prio>=policy.item_prio &&
438 policy.npc_prio>=policy.mob_prio) {
441 if(policy.item_prio>=policy.mob_prio && it)
446 if(policy.mob_prio>=policy.item_prio &&
447 policy.mob_prio>=policy.npc_prio) {
449 return Focus(*inter);
450 if(policy.npc_prio>=policy.item_prio && n)
455 if(policy.item_prio>=policy.mob_prio &&
456 policy.item_prio>=policy.npc_prio) {
459 if(policy.npc_prio>=policy.mob_prio && n)
468 if(npcPlayer==
nullptr || npcPlayer->
interactive()!=
nullptr)
477 auto& policy = searchPolicy(*npcPlayer,collAlgo,collType,opt);
511 if(e.
target==
"EVT_LEFT_ROOM_01_TRAP_MOVER_FOR_DMG_MASTER" ||
512 e.
target==
"EVT_LEFT_UP_01_TOGGLE_TRIGGER_01" ||
513 e.
target==
"EVT_RIGHT_ROOM_01_SPAWN_ROT_02_SOUND" ||
516 Tempest::Log::d(
"unable to process trigger: \"",e.
target,
"\"");
567 wmatrix->marchPoints(p);
594 return wobj.
addNpc(
id,at);
598 return wobj.
addNpc(npcInstance,at);
602 return wobj.
addNpc(npcInstance,at);
610 return wobj.
addItem(itemInstance,at);
618 return wobj.
addItem(itemInstance, pos);
622 return wobj.
addItemDyn(itemInstance, pos, owner);
638 Tempest::Vec3 dir = {1.f,0.f,0.f};
643 if(target!=
nullptr) {
663 Tempest::Vec3 dir = {1.f,0.f,0.f};
666 if(target!=
nullptr) {
669 float lxz = std::sqrt(dir.x*dir.x+0*0+dir.z*dir.z);
680 float lxz = std::sqrt(dir.x*dir.x+0*0+dir.z*dir.z);
725 Tempest::Matrix4x4 pos;
727 pos.translate((p0+p1)*0.5f);
729 const char* armor =
"FL";
737 if(srcArrow!=
nullptr && !srcArrow->
isSpell()) {
739 return addHitEffect(
materialTag(m),armor,
"IAM",pos);
742 if(
auto w = src.
inventory().activeWeapon()) {
744 return addHitEffect(
materialTag(m),armor,
"IAM",pos);
748 return addHitEffect(
"JA",armor,
"MAM",pos);
else
749 return addHitEffect(
"FI",armor,
"MAM",pos);
763Sound World::addHitEffect(std::string_view src, std::string_view dst, std::string_view scheme,
const Tempest::Matrix4x4& pos) {
770 string_frm buf(
"CPFX_",scheme,
'_',src,
'_',dst);
796 return wview->isInPfxRange(p);
799void World::addDlgSound(std::string_view s,
const Tempest::Vec3& pos,
float range, uint64_t& timeLen) {
800 auto sfx = wsound.
addDlgSound(s,pos,range,timeLen);
813 wmatrix->addStartPoint(pos,dir,
name);
817 wmatrix->addFreePoint(pos,dir,
name);
821 if(vob.type==zenkit::VirtualObjectType::zCVobSound || vob.type==zenkit::VirtualObjectType::zCVobSoundDaytime) {
822 wsound.
addSound(
reinterpret_cast<const zenkit::VSound&
>(vob));
824 else if(vob.type==zenkit::VirtualObjectType::oCZoneMusic) {
825 wsound.
addZone(
reinterpret_cast<const zenkit::VZoneMusic&
>(vob));
827 else if(vob.type==zenkit::VirtualObjectType::oCZoneMusicDefault) {
828 wsound.
setDefaultZone(
reinterpret_cast<const zenkit::VZoneMusicDefault&
>(vob));
852 if(
auto weapon = pl.
inventory().activeWeapon()) {
853 int32_t
id = weapon->spellId();
856 collType =
TargetType(spl.target_collect_type);
872 return wmatrix->findPoint(
name,inexact);
876 return wmatrix->findWayPoint(
name);
880 return wmatrix->findWayPoint(pos,[](
const WayPoint&){
return true; });
884 return wmatrix->findWayPoint(pos,f);
888 return wmatrix->findWayPoint(pos,[
name](
const WayPoint& wp) ->
bool {
899 if(p->isFreePoint() && p->checkName(
name)) {
906 return wmatrix->findFreePoint(pos,
name,[&npc](
const WayPoint& wp) ->
bool {
916 return wmatrix->findFreePoint(pos,
name,[](
const WayPoint& wp) ->
bool {
927 if(cur!=
nullptr && !cur->checkName(
name)) {
930 auto filter = [&](
const WayPoint& wp) {
931 if(wp.isLocked() || &wp==cur)
933 if(!npc.
canRayHitPoint(Tempest::Vec3(wp.pos.x,wp.pos.y+10,wp.pos.z),
true))
937 auto wp = wmatrix->findFreePoint(pos,
name, filter);
946 if(nearest==
nullptr || nearest->isFreePoint()) {
949 auto filter = [&](
const WayPoint& wp) {
950 if(!npc.
canRayHitPoint(Tempest::Vec3(wp.pos.x,wp.pos.y+10,wp.pos.z),
true))
952 return nearest != ℘
958 return wmatrix->findNextPoint(pos.
position());
962 return wmatrix->startPoint();
966 return wmatrix->deadPoint();
995 return wmatrix->wayTo(&begin,1,npcPos,end);
997 auto near = wmatrix->findWayPoint(npcPos, [&npc](
const WayPoint &wp) {
1008 std::vector<const WayPoint*> wpoint;
1009 wpoint.push_back(near);
1010 for(
auto& i:near->connections()) {
1011 auto p = i.point->position();
1013 wpoint.push_back(i.point);
1016 return wmatrix->wayTo(wpoint.data(),wpoint.size(),npcPos,end);
1028 auto room = std::string(r);
1030 i =
char(std::toupper(i));
1032 if(
auto rx=portalAt(room)){
1037 Tempest::Log::d(
"room not found: ",room);
1041 std::string_view tg =
roomAt(pos);
1042 if(
auto room=portalAt(tg)) {
1050 size_t b = portalName.find(
':');
1051 if(b==std::string::npos)
1055 size_t e=portalName.find(
'_');
1056 if(e==std::string::npos || b>e)
1059 auto name = portalName.substr(b,e-b);
1061 for(
size_t i=0;i<bsp.sectors.size();++i) {
1062 auto& s = bsp.sectors[i].name;
1064 return bsp.sectorsData[i].guild;
ItemMaterial itemMaterial() const
void setTarget(const Npc *n)
bool isPlayerMovable() const
static constexpr float bulletSpeed
static constexpr float gravity
static constexpr float spellSpeed
Interactive * interactive
size_t findSymbolIndex(std::string_view s)
const zenkit::IFocus & focusRange() const
const zenkit::IFocus & focusMage() const
const zenkit::IFocus & focusMelee() const
const zenkit::ISpell & spellDesc(int32_t splId)
const VisualFx * spellVfx(int32_t splId)
void setInstanceNPC(std::string_view name, Npc &npc)
const zenkit::IFocus & focusNorm() const
GameScript * script() const
void changeWorld(std::string_view world, std::string_view wayPoint)
uint64_t tickCount() const
auto version() const -> const VersionInfo &
AiOuputPipe * openDlgOuput(Npc &player, Npc &npc)
static bool isClose(const Npc &npc, const Npc &p, float dist)
auto mapWeaponBone() const -> Tempest::Vec3
auto mapBone(std::string_view bone) const -> Tempest::Vec3
auto weaponState() const -> WeaponState
auto currentWayPoint() const -> const WayPoint *
float rotationRad() const
void setPerceptionDisable(PercType t)
void clearState(bool noFinalize)
void setProcessPolicy(NpcProcessPolicy t)
auto inventory() const -> const Inventory &
auto interactive() const -> Interactive *
auto centerPosition() const -> Tempest::Vec3
auto position() const -> Tempest::Vec3
bool canRayHitPoint(const Tempest::Vec3 pos, bool freeLos=true, float extRange=0.f) const
static const zenkit::Vfs & vdfsIndex()
void setContext(World *ctx)
void write(const Arg &... a)
bool setEntry(const Args &... args)
std::string emTrjTargetNode
std::string emTrjOriginNode
Tempest::Vec3 position() const
void add(const WayPoint &p)
Tempest::Vec3 position() const
bool checkName(std::string_view name, bool inexact=true) const
static void setThreadName(const char *threadName)
Item * validateItem(Item *def)
void addRoot(const std::shared_ptr< zenkit::VirtualObject > &vob, bool startup)
auto takeItem(Item &it) -> std::unique_ptr< Item >
void addInteractive(Interactive *obj)
void load(Serialize &fout)
void detectNpc(const float x, const float y, const float z, const float r, const std::function< void(Npc &)> &f)
CsCamera * currentCs() const
Interactive & mobsi(size_t i)
void tick(uint64_t dt, uint64_t dtPlayer)
Item * findItem(const Npc &pl, Item *def, const SearchOpt &opt)
void enableDefTrigger(AbstractTrigger &trigger)
void enableTicks(AbstractTrigger &t)
uint32_t itmId(const void *ptr) const
void addTrigger(AbstractTrigger *trigger)
void updateAnimation(uint64_t dt)
void setMobRoutine(gtime time, std::string_view scheme, int32_t state)
void detectItem(const float x, const float y, const float z, const float r, const std::function< void(Item &)> &f)
void runEffect(Effect &&e)
Npc * findNpcNear(const Npc &pl, Npc *def, const SearchOpt &opt)
void stopEffect(const VisualFx &vfx)
void invalidateVobIndex()
bool isTargeted(Npc &npc)
Interactive * findInteractive(const Npc &pl, Interactive *def, const SearchOpt &opt)
uint32_t npcId(const Npc *ptr) const
Bullet & shootBullet(const Item &itmId, const Tempest::Vec3 &pos, const Tempest::Vec3 &dir, float tgRange, float speed)
bool triggerOnStart(bool firstTime)
Interactive * validateInteractive(Interactive *def)
void marchInteractives(DbgPainter &p) const
uint32_t mobsiId(const void *ptr) const
Item * addItemDyn(size_t itemInstance, const Tempest::Matrix4x4 &pos, size_t owner)
void save(Serialize &fout)
void enableCollizionZone(CollisionZone &z)
const Npc & npc(size_t i) const
Npc * insertPlayer(std::unique_ptr< Npc > &&npc, std::string_view at)
Npc * validateNpc(Npc *def)
Npc * addNpc(size_t itemInstance, std::string_view at)
void setCurrentCs(CsCamera *cs)
void sendPassivePerc(Npc &self, Npc &other, Npc *victim, Item *itm, int32_t perc)
void removeItem(Item &it)
size_t hasItems(std::string_view tag, size_t itemCls)
Npc * findNpcByInstance(size_t instance, size_t n=0)
void sendImmediatePerc(Npc &self, Npc &other, Npc &victim, Item *itm, int32_t perc)
auto takeNpc(const Npc *npc) -> std::unique_ptr< Npc >
bool testFocusNpc(const Npc &pl, Npc *def, const SearchOpt &opt)
bool execTriggerEvent(const TriggerEvent &e)
void disableCollizionZone(CollisionZone &z)
size_t mobsiCount() const
void detectNpcNear(const std::function< void(Npc &)> &f)
Item * findItemByInstance(size_t instance, size_t n=0)
void triggerEvent(const TriggerEvent &e)
Interactive * availableMob(const Npc &pl, std::string_view name)
void disableTicks(AbstractTrigger &t)
Item * addItem(size_t itemInstance, std::string_view at)
void marchCsCameras(DbgPainter &p) const
Sound addDlgSound(std::string_view s, const Tempest::Vec3 &pos, float range, uint64_t &timeLen)
void setDefaultZone(const zenkit::VZoneMusic &vob)
bool isInListenerRange(const Tempest::Vec3 &pos, float sndRgn) const
bool execTriggerEvent(const TriggerEvent &e)
static const float talkRange
void addSound(const zenkit::VSound &vob)
void addZone(const zenkit::VZoneMusic &vob)
void aiOutput(const Tempest::Vec3 &pos, std::string_view outputname)
MeshObjects::Mesh addStaticView(const ProtoMesh *visual, bool staticDraw=false)
MeshObjects::Mesh addDecalView(const zenkit::VisualDecal &vob)
MeshObjects::Mesh addView(std::string_view visual, int32_t headTex, int32_t teethTex, int32_t bodyColor)
MeshObjects::Mesh addAtachView(const ProtoMesh::Attach &visual, const int32_t version)
LightGroup::Light addLight(const zenkit::VLight &vob)
auto version() const -> const VersionInfo &
void sendPassivePerc(Npc &self, Npc &other, int32_t perc)
int32_t guildOfRoom(const Tempest::Vec3 &pos)
CsCamera * currentCs() const
void save(Serialize &fout)
void triggerOnStart(bool firstTime)
void addDlgSound(std::string_view s, const Tempest::Vec3 &pos, float range, uint64_t &timeLen)
std::vector< zenkit::BspSector > sectors
void runEffect(Effect &&e)
void marchCsCameras(DbgPainter &p) const
size_t hasItems(std::string_view tag, size_t itemCls)
void triggerChangeWorld(std::string_view world, std::string_view wayPoint)
void disableCollizionZone(CollisionZone &z)
void scaleTime(uint64_t &dt)
LightGroup::Light addLight(const zenkit::VLight &vob)
void triggerEvent(const TriggerEvent &e)
AiOuputPipe * openDlgOuput(Npc &player, Npc &npc)
const WayPoint * findNextWayPoint(const Npc &npc) const
Interactive * findInteractive(const Npc &pl)
bool isTargeted(Npc &npc)
void load(Serialize &fin)
MeshObjects::Mesh addAtachView(const ProtoMesh::Attach &visual, const int32_t version)
std::string_view roomAt(const Tempest::Vec3 &arr)
Interactive * availableMob(const Npc &pl, std::string_view name)
Focus validateFocus(const Focus &def)
void enableCollizionZone(CollisionZone &z)
auto takeHero() -> std::unique_ptr< Npc >
WayPath wayTo(const Npc &pos, const WayPoint &end) const
uint64_t tickCount() const
bool isCutsceneLock() const
Npc * addNpc(std::string_view name, std::string_view at)
const WayPoint * findNextFreePoint(const Npc &pos, std::string_view name) const
void sendImmediatePerc(Npc &self, Npc &other, Npc &victim, int32_t perc)
void enableTicks(AbstractTrigger &t)
Sound addLandHitEffect(ItemMaterial src, zenkit::MaterialGroup reciver, const Tempest::Matrix4x4 &pos)
const WayPoint * findPoint(std::string_view name, bool inexact=true) const
uint32_t itmId(const void *ptr) const
void detectNpcNear(std::function< void(Npc &)> f)
bool testFocusNpc(Npc *def)
void invalidateVobIndex()
Item * itmById(uint32_t id)
void insertPlayer(std::unique_ptr< Npc > &&npc, std::string_view waypoint)
Sound addWeaponBlkEffect(ItemMaterial src, ItemMaterial reciver, const Tempest::Matrix4x4 &pos)
uint32_t npcId(const Npc *ptr) const
uint32_t mobsiId(const Interactive *ptr) const
void disableTicks(AbstractTrigger &t)
Npc * findNpcByInstance(size_t instance, size_t n=0)
bool isInSfxRange(const Tempest::Vec3 &pos) const
std::vector< std::uint64_t > leaf_node_indices
void updateAnimation(uint64_t dt)
Item * addItemDyn(size_t itemInstance, const Tempest::Matrix4x4 &pos, size_t owner)
void addTrigger(AbstractTrigger *trigger)
void removeItem(Item &it)
void createPlayer(std::string_view cls)
const WayPoint & deadPoint() const
void detectItem(const Tempest::Vec3 &p, const float r, const std::function< void(Item &)> &f)
Npc * npcById(uint32_t id)
std::string_view name() const
void marchPoints(DbgPainter &p) const
Item * addItem(size_t itemInstance, std::string_view at)
auto takeItem(Item &it) -> std::unique_ptr< Item >
void addSound(const zenkit::VirtualObject &vob)
GlobalFx addGlobalEffect(std::string_view what, uint64_t len, const std::string *argv, size_t argc)
void addFreePoint(const Tempest::Vec3 &pos, const Tempest::Vec3 &dir, std::string_view name)
bool isInPfxRange(const Tempest::Vec3 &pos) const
void setCurrentCs(CsCamera *cs)
void detectNpc(const Tempest::Vec3 &p, const float r, const std::function< void(Npc &)> &f)
Interactive * mobsiById(uint32_t id)
void stopEffect(const VisualFx &vfx)
MeshObjects::Mesh addView(std::string_view visual) const
void enableDefTrigger(AbstractTrigger &t)
uint32_t npcCount() const
const WayPoint * findFreePoint(const Npc &pos, std::string_view name) const
const WayPoint & startPoint() const
void setMobRoutine(gtime time, std::string_view scheme, int32_t state)
const WayPoint * findWayPoint(std::string_view name) const
void addStartPoint(const Tempest::Vec3 &pos, const Tempest::Vec3 &dir, std::string_view name)
void aiOutputSound(Npc &player, std::string_view msg)
void setDayTime(int32_t h, int32_t min)
MeshObjects::Mesh addStaticView(const ProtoMesh *visual, bool staticDraw)
Sound addWeaponHitEffect(Npc &src, const Bullet *srcArrow, Npc &reciver)
const WayPoint * findNextPoint(const WayPoint &pos) const
Item * findItemByInstance(size_t instance, size_t n=0)
void assignRoomToGuild(std::string_view room, int32_t guildId)
GameScript & script() const
void marchInteractives(DbgPainter &p) const
MeshObjects::Mesh addDecalView(const zenkit::VisualDecal &decal)
void addInteractive(Interactive *inter)
Bullet & shootSpell(const Item &itm, const Npc &npc, const Npc *target)
Focus findFocus(const Npc &pl, const Focus &def)
void execTriggerEvent(const TriggerEvent &e)
Bullet & shootBullet(const Item &itmId, const Npc &npc, const Npc *target, const Interactive *inter)
static CommandLine * instance
const char * materialTag(ItemMaterial src)