#pragma once // Deterministic safari zone layout. The server and every client call // Zone::generate(id, seed) and get the exact same terrain, props, water, // tall grass and landmarks, so only the zone id + seed travel over the wire. // Everything that matters for gameplay (collision, heights, water, tall // grass, NPC and spawn points) lives here; purely visual decoration (grass // tufts, flowers, the tall-grass sprites themselves) is added by the client. #include "Rng.h" #include #include #include #include #include namespace bs { // Terrain materials (client maps them to texture layers). enum ZoneMat : uint8_t { MatGrass = 0, MatMeadow = 1, MatPath = 2, MatSand = 3, MatStone = 4, MatRidge = 5, MatPlaza = 6 }; // One placed 3D model. `radius` > 0 makes it a round obstacle. struct ZoneProp { std::string model; // model name in scenery.glb / safari.glb glm::vec3 pos{0.0f}; float yaw = 0.0f; glm::vec3 scale{1.0f}; float tint = 1.0f; float radius = 0.0f; }; // Thin straight obstacle (fences, tent walls) as a capsule from a to b. struct ZoneWall { glm::vec2 a, b; float halfWidth = 0.15f; }; // Walkable wooden deck over water. struct ZoneDock { glm::vec2 min, max; float deckY = 0.05f; }; struct ZonePatch { glm::vec2 c; float r; }; class Zone { public: static constexpr float kWaterLevel = -0.2f; static constexpr float kLevelHeight = 0.5f; // Known ids: "meadow" (the grassy safari zone). Returns false for unknown ids. bool generate(const std::string& id, uint32_t seed); const std::string& id() const { return id_; } uint32_t seed() const { return seed_; } int width() const { return w_; } int depth() const { return d_; } bool inBounds(int x, int z) const { return x >= 0 && z >= 0 && x < w_ && z < d_; } int level(int x, int z) const; uint8_t material(int x, int z) const; bool tallGrassTile(int x, int z) const; // Same rule as hd2d::Terrain::heightAt (flat tile tops). float terrainHeight(float x, float z) const; float groundHeight(glm::vec2 p) const; // dock deck or terrain bool onDock(glm::vec2 p) const; bool isWater(glm::vec2 p) const; // open water (not under a dock) bool isDeepWater(glm::vec2 p) const; // basin, at least 1 level below the shelf bool inTallGrass(glm::vec2 p) const; bool walkable(glm::vec2 from, glm::vec2 to, float radius = 0.28f) const; // Where a line cast from `from` along `dir` lands: first open water point // 1.6 .. 4.5 units ahead. False when there is no water in range. bool fishingTarget(glm::vec2 from, glm::vec2 dir, glm::vec2& bobber) const; bool randomTallGrassNear(Rng& rng, glm::vec2 c, float rmin, float rmax, glm::vec2& out) const; bool randomWater(Rng& rng, glm::vec2& out, bool deep = true) const; bool randomWaterNear(Rng& rng, glm::vec2 c, float rmax, glm::vec2& out) const; // Open grass / meadow for wandering creatures (away from the entrance plaza). bool randomWanderSpot(Rng& rng, glm::vec2& out) const; // True when a creature may stand / walk here (land creatures stay out of water). bool creatureWalkable(glm::vec2 p, bool water) const; std::vector props; std::vector walls; std::vector docks; std::vector tallGrassPatches; glm::vec2 spawn{0.0f}; // where new players appear float spawnYaw = 0.0f; glm::vec2 henry{0.0f}; // Professor Henry's post float henryYaw = 0.0f; glm::vec2 plaza{0.0f}; // entrance plaza center (no wild spawns nearby) glm::vec2 waterMin{0.0f}, waterMax{0.0f}; // bounds of all water tiles (+ margin) private: void buildMeadow(uint32_t seed); void setTile(int x, int z, int level, uint8_t mat); void paintPath(const std::vector& pts, float halfWidth, bool clearGrass = true); bool blocked(glm::vec2 p, float r) const; // overlaps a prop or wall bool clearForProp(glm::vec2 p, float r) const; void addProp(const std::string& model, glm::vec2 p, float yaw, float scale, float radius, float tint = 1.0f, float yOffset = 0.0f); void addFence(glm::vec2 a, glm::vec2 b); std::string id_; uint32_t seed_ = 0; int w_ = 0, d_ = 0; std::vector levels_; std::vector mats_; std::vector grass_; }; // 0 = north (-z), pi/2 = east (+x). inline float yawFromDir(glm::vec2 d) { return std::atan2(d.x, -d.y); } inline glm::vec2 dirFromYaw(float yaw) { return {std::sin(yaw), -std::cos(yaw)}; } } // namespace bs