1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-03 09:09:47 +02:00

Implement Hierarchy of game ZoneData

This allows querying of the most specific ZoneData for a given point
This commit is contained in:
Daniel Evans 2017-01-17 23:57:20 +00:00
parent f12186dede
commit bb6698e373
11 changed files with 190 additions and 56 deletions

View File

@ -1,15 +1,16 @@
#pragma once
#ifndef _ZONEDATA_HPP_
#define _ZONEDATA_HPP_
#ifndef RWENGINE_DATA_ZONEDATA_HPP
#define RWENGINE_DATA_ZONEDATA_HPP
#include <algorithm>
#include <glm/glm.hpp>
#include <memory>
#include <string>
#include <vector>
#define ZONE_GANG_COUNT 13
/**
* \class Zone
* A Zone entry
* Zone Data loaded from IPL/zon files
*/
struct ZoneData {
/**
@ -37,30 +38,87 @@ struct ZoneData {
/**
* Text of the zone?
*/
std::string Text;
std::string text = {};
/**
* Gang spawn density for daytime (8:00-19:00)
*/
unsigned int gangDensityDay[ZONE_GANG_COUNT];
unsigned int gangDensityDay[ZONE_GANG_COUNT] = {};
/**
* Gang spawn density for nighttime (19:00-8:00)
*/
unsigned int gangDensityNight[ZONE_GANG_COUNT];
unsigned int gangDensityNight[ZONE_GANG_COUNT] = {};
/**
* Gang car spawn density for daytime (8:00-19:00)
*/
unsigned int gangCarDensityDay[ZONE_GANG_COUNT];
unsigned int gangCarDensityDay[ZONE_GANG_COUNT] = {};
/**
* Gang car spawn density for nighttime (19:00-8:00)
*/
unsigned int gangCarDensityNight[ZONE_GANG_COUNT];
unsigned int gangCarDensityNight[ZONE_GANG_COUNT] = {};
unsigned int pedGroupDay;
unsigned int pedGroupNight;
unsigned int pedGroupDay = 0;
unsigned int pedGroupNight = 0;
/**
* Pointer to parent zone in zone array
*/
ZoneData* parent_ = nullptr;
/**
* Totally contained zones
*/
std::vector<ZoneData*> children_ = {};
static bool isZoneContained(const ZoneData& inner, const ZoneData& outer) {
return glm::all(glm::greaterThanEqual(inner.min, outer.min)) &&
glm::all(glm::lessThanEqual(inner.max, outer.max));
}
bool containsPoint(const glm::vec3& point) const {
return glm::all(glm::greaterThanEqual(point, min)) &&
glm::all(glm::lessThanEqual(point, max));
}
ZoneData* findLeafAtPoint(const glm::vec3& point) {
for (ZoneData* child : children_) {
auto descendent = child->findLeafAtPoint(point);
if (descendent) {
return descendent;
}
}
return containsPoint(point) ? this : nullptr;
}
bool insertZone(ZoneData& inner) {
if (!isZoneContained(inner, *this)) {
return false;
}
for (ZoneData* child : children_) {
if (child->insertZone(inner)) {
return true;
}
}
// inner is a child of outer
// Move any zones that are really within inner to inner
auto it = std::stable_partition(
children_.begin(), children_.end(),
[&](ZoneData* a) { return !inner.insertZone(*a); });
children_.erase(it, children_.end());
children_.push_back(&inner);
inner.parent_ = this;
return true;
}
};
#endif
using ZoneDataList = std::vector<ZoneData>;
#endif

View File

@ -56,6 +56,10 @@ void GameData::load() {
loadIFP("ped.ifp");
// Clear existing zones
gamezones = ZoneDataList{
{"CITYZON", 0, {-4000.f, -4000.f, -500.f}, {4000.f, 4000.f, 500.f}, 0}};
loadLevelFile("data/default.dat");
loadLevelFile("data/gta3.dat");
}
@ -164,20 +168,24 @@ void GameData::loadIPL(const std::string& path) {
bool GameData::loadZone(const std::string& path) {
LoaderIPL ipll;
if (ipll.load(path)) {
if (ipll.zones.size() > 0) {
for (auto& z : ipll.zones) {
zones.insert({z.name, z});
}
logger->info("Data", "Loaded " + std::to_string(ipll.zones.size()) +
" zones from " + path);
return true;
}
} else {
// Load the zones
if (!ipll.load(path)) {
logger->error("Data", "Failed to load zones from " + path);
return false;
}
return false;
gamezones.insert(gamezones.end(), ipll.zones.begin(), ipll.zones.end());
// Build zone hierarchy
for (ZoneData& zone : gamezones) {
zone.children_.clear();
if (&zone == &gamezones.front()) {
continue;
}
gamezones[0].insertZone(zone);
}
return true;
}
enum ColSection {

View File

@ -43,6 +43,7 @@ private:
Logger* logger;
LoaderDFF dffLoader;
public:
/**
* ctor
@ -193,10 +194,22 @@ public:
*/
std::map<std::string, LoaderIMG> archives;
/**
* Map Zones
*/
std::map<std::string, ZoneData> zones;
ZoneDataList gamezones;
ZoneDataList mapzones;
ZoneData* findZone(const std::string& name) {
auto it =
std::find_if(gamezones.begin(), gamezones.end(),
[&](const ZoneData& a) { return a.name == name; });
return it != gamezones.end() ? &(*it) : nullptr;
}
ZoneData* findZoneAt(const glm::vec3& pos) {
RW_CHECK(!gamezones.empty(), "No game zones loaded");
ZoneData* zone = gamezones[0].findLeafAtPoint(pos);
return zone;
}
std::unordered_map<ModelID, std::unique_ptr<BaseModelInfo>> modelinfo;

View File

@ -124,4 +124,4 @@ bool LoaderIPL::load(const std::string& filename) {
}
return true;
}
}

View File

@ -20,7 +20,7 @@ public:
std::vector<std::shared_ptr<InstanceData>> m_instances;
/// List of Zones
std::vector<ZoneData> zones;
ZoneDataList zones;
};
#endif // LoaderIPL_h__

View File

@ -108,11 +108,11 @@ inline bool objectInRadiusNear(const ScriptArguments& args, GameObject* object,
template <class Tobj>
inline bool objectInZone(const ScriptArguments& args, Tobj object,
const ScriptString zone) {
auto zfind = args.getWorld()->data->zones.find(zone);
if (zfind != args.getWorld()->data->zones.end()) {
auto& min = zfind->second.min;
auto& max = zfind->second.max;
const ScriptString name) {
auto zone = args.getWorld()->data->findZone(name);
if (zone) {
auto& min = zone->min;
auto& max = zone->max;
return objectInBounds(object, min, max);
}
return false;

View File

@ -3694,12 +3694,11 @@ void opcode_0152(const ScriptArguments& args, const ScriptString arg1, const Scr
RW_UNUSED(arg15);
RW_UNUSED(arg16);
RW_UNUSED(arg17);
auto& zones = args.getWorld()->data->zones;
auto it = zones.find(arg1);
if (it != zones.end()) {
auto density = (it->second.gangCarDensityNight);
if (arg1) {
density = it->second.gangCarDensityDay;
auto zone = args.getWorld()->data->findZone(arg1);
if (zone) {
auto density = (zone->gangCarDensityNight);
if (arg2) {
density = zone->gangCarDensityDay;
}
auto count = args.getParameters().size();
for (auto g = 0u; g < count - 2; ++g) {
@ -3824,12 +3823,11 @@ void opcode_015c(const ScriptArguments& args, const ScriptString areaName, const
RW_UNUSED(arg9);
RW_UNUSED(arg10);
RW_UNUSED(arg11);
auto& zones = args.getWorld()->data->zones;
auto it = zones.find(areaName);
if (it != zones.end()) {
auto density = (it->second.gangCarDensityNight);
auto zone = args.getWorld()->data->findZone(areaName);
if (zone) {
auto density = (zone->gangCarDensityNight);
if (arg2) {
density = it->second.gangCarDensityDay;
density = zone->gangCarDensityDay;
}
auto count = args.getParameters().size();
for (auto g = 0u; g < count - 2; ++g) {
@ -8101,13 +8099,12 @@ void opcode_02dd(const ScriptArguments& args, const ScriptString areaName, Scrip
RW_UNIMPLEMENTED_OPCODE(0x02dd);
RW_UNUSED(areaName);
RW_UNUSED(character);
const auto& zones = args.getWorld()->data->zones;
std::string zname(args[0].string);
// Only try to find a character if this is a known zone
auto zfind = zones.find(zname);
if(zfind != zones.end()) {
auto zone = args.getWorld()->data->findZone(areaName);
if(zone) {
// Create a list of candidate characters by iterating and checking if the char is in this zone
std::vector<std::pair<GameObjectID, GameObject*>> candidates;
@ -8122,8 +8119,8 @@ void opcode_02dd(const ScriptArguments& args, const ScriptString areaName, Scrip
// Check if character is in this zone
auto cp = character->getPosition();
auto& min = zfind->second.min;
auto& max = zfind->second.max;
auto& min = zone->min;
auto& max = zone->max;
if (cp.x > min.x && cp.y > min.y && cp.z > min.z &&
cp.x < max.x && cp.y < max.y && cp.z < max.z) {
candidates.push_back(p);
@ -9246,17 +9243,17 @@ void opcode_0324(const ScriptArguments& args, const ScriptString arg1, const Scr
RW_UNUSED(arg1);
RW_UNUSED(arg2);
RW_UNUSED(arg3);
auto it = args.getWorld()->data->zones.find(args[0].string);
if( it != args.getWorld()->data->zones.end() )
auto zone = args.getWorld()->data->findZone(arg1);
if (zone)
{
auto day = args[1].integer == 1;
if( day )
{
it->second.pedGroupDay = args[2].integer;
zone->pedGroupDay = args[2].integer;
}
else
{
it->second.pedGroupNight = args[2].integer;
zone->pedGroupNight = args[2].integer;
}
}
}

View File

@ -232,7 +232,9 @@ void DebugState::tick(float dt) {
void DebugState::draw(GameRenderer* r) {
// Draw useful information like camera position.
std::stringstream ss;
ss << "Camera Position: " << glm::to_string(_debugCam.position);
ss << "Camera Position: " << glm::to_string(_debugCam.position) << "\n";
auto zone = getWorld()->data->findZoneAt(_debugCam.position);
ss << (zone ? zone->name : "No Zone") << "\n";
TextRenderer::TextInfo ti;
ti.text = GameStringUtil::fromString(ss.str());

View File

@ -28,6 +28,7 @@ set(TEST_SOURCES
"test_Input.cpp"
"test_lifetime.cpp"
"test_loaderdff.cpp"
"test_LoaderIPL.cpp"
"test_Logger.cpp"
"test_menu.cpp"
"test_object.cpp"
@ -45,6 +46,7 @@ set(TEST_SOURCES
"test_VisualFX.cpp"
"test_weapon.cpp"
"test_world.cpp"
"test_ZoneData.cpp"
# Hack in rwgame sources until there's a per-target test suite
"${CMAKE_SOURCE_DIR}/rwgame/GameConfig.cpp"

21
tests/test_LoaderIPL.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <boost/test/unit_test.hpp>
#include <loaders/LoaderIPL.hpp>
#include "test_globals.hpp"
BOOST_AUTO_TEST_SUITE(LoaderIPLTests)
#if RW_TEST_WITH_DATA
BOOST_AUTO_TEST_CASE(test_load_zones) {
LoaderIPL loader;
const auto& gdpath = Global::get().getGamePath();
BOOST_REQUIRE(loader.load(gdpath + "/data/gta3.zon"));
BOOST_REQUIRE(loader.zones.size() > 2);
auto& zone1 = loader.zones[0];
BOOST_CHECK_EQUAL(zone1.name, "ROADBR1");
}
#endif
BOOST_AUTO_TEST_SUITE_END()

33
tests/test_ZoneData.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <boost/test/unit_test.hpp>
#include <data/ZoneData.hpp>
#include "test_globals.hpp"
BOOST_AUTO_TEST_SUITE(ZoneDataTests)
BOOST_AUTO_TEST_CASE(test_contains_point) {
ZoneData zone;
zone.min = glm::vec3(10.f, 10.f, -5.f);
zone.max = glm::vec3(30.f, 40.f, 5.f);
BOOST_CHECK(zone.containsPoint({15.f, 15.f, 0.f}));
BOOST_CHECK(zone.containsPoint({10.f, 10.f, 0.f}));
BOOST_CHECK(zone.containsPoint({30.f, 35.f, 0.f}));
BOOST_CHECK(!zone.containsPoint({35.f, 30.f, 0.f}));
BOOST_CHECK(!zone.containsPoint({0.f, 0.f, 0.f}));
BOOST_CHECK(!zone.containsPoint({-15.f, -15.f, 0.f}));
}
BOOST_AUTO_TEST_CASE(test_hierarchy) {
ZoneData zone;
zone.min = glm::vec3(-10.f,-10.f,-5.f);
zone.max = glm::vec3( 10.f, 10.f, 5.f);
ZoneData leaf;
leaf.min = glm::vec3(0.f, 0.f,-5.f);
leaf.max = glm::vec3(10.f,10.f, 5.f);
BOOST_CHECK(zone.insertZone(leaf));
BOOST_CHECK_EQUAL(zone.findLeafAtPoint({-5.f, 0.f, 0.f}), &zone);
BOOST_CHECK_EQUAL(zone.findLeafAtPoint({ 5.f, 5.f, 0.f}), &leaf);
}
BOOST_AUTO_TEST_SUITE_END()