2023-05-02 09:42:43 +03:00
|
|
|
//
|
2023-05-24 23:41:23 +03:00
|
|
|
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
|
|
|
|
|
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
|
2023-05-02 09:42:43 +03:00
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include <yapb.h>
|
|
|
|
|
|
|
|
|
|
void GraphVistable::rebuild () {
|
|
|
|
|
if (!m_rebuild) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_length = graph.length ();
|
|
|
|
|
|
2023-05-07 21:28:24 +03:00
|
|
|
// stop generation if graph has changed, or erased
|
|
|
|
|
if (!m_length || graph.hasChanged ()) {
|
|
|
|
|
m_rebuild = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-05-02 09:42:43 +03:00
|
|
|
TraceResult tr {};
|
|
|
|
|
uint8_t res, shift;
|
|
|
|
|
|
|
|
|
|
if (!graph.exists (m_sliceIndex)) {
|
|
|
|
|
m_sliceIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!graph.exists (m_curIndex)) {
|
|
|
|
|
m_curIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
const auto &vis = graph[m_curIndex];
|
|
|
|
|
|
|
|
|
|
auto sourceCrouch = vis.origin;
|
|
|
|
|
auto sourceStand = vis.origin;
|
|
|
|
|
|
|
|
|
|
if (vis.flags & NodeFlag::Crouch) {
|
|
|
|
|
sourceCrouch.z += 12.0f;
|
|
|
|
|
sourceStand.z += 18.0f + 28.0f;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sourceCrouch.z += -18.0f + 12.0f;
|
|
|
|
|
sourceStand.z += 28.0f;
|
|
|
|
|
}
|
|
|
|
|
auto end = m_sliceIndex + rg.get (250, 400);
|
|
|
|
|
|
|
|
|
|
if (end > m_length) {
|
|
|
|
|
end = m_length;
|
|
|
|
|
}
|
|
|
|
|
uint16_t standCount = 0, crouchCount = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = m_sliceIndex; i < end; ++i) {
|
|
|
|
|
const auto &path = graph[i];
|
|
|
|
|
|
|
|
|
|
// first check ducked visibility
|
|
|
|
|
Vector dest = path.origin;
|
|
|
|
|
|
|
|
|
|
game.testLine (sourceCrouch, dest, TraceIgnore::Monsters, nullptr, &tr);
|
|
|
|
|
|
|
|
|
|
// check if line of sight to object is not blocked (i.e. visible)
|
|
|
|
|
if (!cr::fequal (tr.flFraction, 1.0f)) {
|
|
|
|
|
res = VisIndex::Stand;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
res = VisIndex::None;
|
|
|
|
|
}
|
|
|
|
|
res <<= 1;
|
|
|
|
|
|
|
|
|
|
game.testLine (sourceStand, dest, TraceIgnore::Monsters, nullptr, &tr);
|
|
|
|
|
|
|
|
|
|
// check if line of sight to object is not blocked (i.e. visible)
|
|
|
|
|
if (!cr::fequal (tr.flFraction, 1.0f)) {
|
|
|
|
|
res |= VisIndex::Stand;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res != VisIndex::None) {
|
|
|
|
|
dest = path.origin;
|
|
|
|
|
|
|
|
|
|
// first check ducked visibility
|
|
|
|
|
if (path.flags & NodeFlag::Crouch) {
|
|
|
|
|
dest.z += 18.0f + 28.0f;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dest.z += 28.0f;
|
|
|
|
|
}
|
|
|
|
|
game.testLine (sourceCrouch, dest, TraceIgnore::Monsters, nullptr, &tr);
|
|
|
|
|
|
|
|
|
|
// check if line of sight to object is not blocked (i.e. visible)
|
|
|
|
|
if (!cr::fequal (tr.flFraction, 1.0f)) {
|
2023-05-08 23:54:38 +03:00
|
|
|
res |= VisIndex::Crouch;
|
2023-05-02 09:42:43 +03:00
|
|
|
}
|
|
|
|
|
else {
|
2023-05-08 23:54:38 +03:00
|
|
|
res &= VisIndex::Stand;
|
2023-05-02 09:42:43 +03:00
|
|
|
}
|
|
|
|
|
game.testLine (sourceStand, dest, TraceIgnore::Monsters, nullptr, &tr);
|
|
|
|
|
|
|
|
|
|
// check if line of sight to object is not blocked (i.e. visible)
|
|
|
|
|
if (!cr::fequal (tr.flFraction, 1.0f)) {
|
|
|
|
|
res |= VisIndex::Stand;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-05-08 23:54:38 +03:00
|
|
|
res &= VisIndex::Crouch;
|
2023-05-02 09:42:43 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
shift = static_cast <uint8_t> ((path.number % 4) << 1);
|
|
|
|
|
|
|
|
|
|
m_vistable[vis.number * m_length + path.number] &= ~static_cast <uint8_t> (VisIndex::Any << shift);
|
|
|
|
|
m_vistable[vis.number * m_length + path.number] |= res << shift;
|
|
|
|
|
|
|
|
|
|
if (!(res & VisIndex::Crouch)) {
|
|
|
|
|
++crouchCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(res & VisIndex::Stand)) {
|
|
|
|
|
++standCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
graph[vis.number].vis.crouch = crouchCount;
|
|
|
|
|
graph[vis.number].vis.stand = standCount;
|
|
|
|
|
|
|
|
|
|
if (end == m_length) {
|
|
|
|
|
m_sliceIndex = 0;
|
|
|
|
|
m_curIndex++;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
m_sliceIndex += rg.get (250, 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// notify host about rebuilding
|
|
|
|
|
if (m_notifyMsgTimestamp > 0.0f && m_notifyMsgTimestamp < game.time () && end == m_length) {
|
|
|
|
|
game.print ("Rebuilding vistable... %d%% done.", m_curIndex * 100 / m_length);
|
|
|
|
|
m_notifyMsgTimestamp = game.time () + 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_curIndex == m_length && end == m_length) {
|
|
|
|
|
m_rebuild = false;
|
|
|
|
|
m_notifyMsgTimestamp = 0.0f;
|
|
|
|
|
|
|
|
|
|
save ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GraphVistable::startRebuild () {
|
|
|
|
|
m_rebuild = true;
|
|
|
|
|
m_notifyMsgTimestamp = game.time ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GraphVistable::visible (int srcIndex, int destIndex, VisIndex vis) {
|
|
|
|
|
if (!graph.exists (srcIndex) || !graph.exists (destIndex)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return !(((m_vistable[srcIndex * m_length + destIndex] >> ((destIndex % 4) << 1)) & vis) == vis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GraphVistable::load () {
|
|
|
|
|
m_rebuild = true;
|
|
|
|
|
m_length = graph.length ();
|
|
|
|
|
|
|
|
|
|
m_sliceIndex = 0;
|
|
|
|
|
m_curIndex = 0;
|
|
|
|
|
m_notifyMsgTimestamp = 0.0f;
|
|
|
|
|
|
|
|
|
|
if (!m_length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-06-24 02:36:51 +03:00
|
|
|
const bool dataLoaded = bstor.load <VisStorage> (m_vistable);
|
2023-05-02 09:42:43 +03:00
|
|
|
|
|
|
|
|
// if loaded, do not recalculate visibility
|
|
|
|
|
if (dataLoaded) {
|
|
|
|
|
m_rebuild = false;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
m_vistable.resize (cr::sqrf (m_length));
|
|
|
|
|
m_notifyMsgTimestamp = game.time ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GraphVistable::save () {
|
|
|
|
|
if (!graph.length () || graph.hasChanged () || m_rebuild) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bstor.save <VisStorage> (m_vistable);
|
|
|
|
|
}
|