315 lines
14 KiB
C++
315 lines
14 KiB
C++
#pragma once
|
|
#include "Common.h"
|
|
#include "Python.h"
|
|
#include "structmember.h"
|
|
#include <SFML/Graphics.hpp>
|
|
#include <libtcod.h>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
// Forward declarations
|
|
class UIGrid;
|
|
class PyTexture;
|
|
class UIEntity;
|
|
|
|
// Include PyTexture.h for PyTextureObject (typedef, not struct)
|
|
#include "PyTexture.h"
|
|
|
|
// Layer type enumeration
|
|
enum class GridLayerType {
|
|
Color,
|
|
Tile
|
|
};
|
|
|
|
// Abstract base class for grid layers
|
|
class GridLayer {
|
|
public:
|
|
// Chunk size for per-chunk dirty tracking (matches GridChunk::CHUNK_SIZE)
|
|
static constexpr int CHUNK_SIZE = 64;
|
|
|
|
GridLayerType type;
|
|
std::string name; // #150 - Layer name for GridPoint property access
|
|
int z_index; // Negative = below entities, >= 0 = above entities
|
|
int grid_x, grid_y; // Dimensions
|
|
UIGrid* parent_grid; // Parent grid reference
|
|
bool visible; // Visibility flag
|
|
|
|
// Chunk dimensions
|
|
int chunks_x, chunks_y;
|
|
|
|
// Per-chunk dirty flags and RenderTextures
|
|
std::vector<bool> chunk_dirty; // One flag per chunk
|
|
std::vector<std::unique_ptr<sf::RenderTexture>> chunk_textures; // One texture per chunk
|
|
std::vector<bool> chunk_texture_initialized; // Track which textures are created
|
|
int cached_cell_width, cached_cell_height; // Cell size used for cached textures
|
|
|
|
GridLayer(GridLayerType type, int z_index, int grid_x, int grid_y, UIGrid* parent);
|
|
virtual ~GridLayer() = default;
|
|
|
|
// Mark entire layer as needing re-render
|
|
void markDirty();
|
|
|
|
// Mark specific cell's chunk as dirty
|
|
void markDirty(int cell_x, int cell_y);
|
|
|
|
// Get chunk index for a cell
|
|
int getChunkIndex(int cell_x, int cell_y) const;
|
|
|
|
// Get chunk coordinates for a cell
|
|
void getChunkCoords(int cell_x, int cell_y, int& chunk_x, int& chunk_y) const;
|
|
|
|
// Get cell bounds for a chunk
|
|
void getChunkBounds(int chunk_x, int chunk_y, int& start_x, int& start_y, int& end_x, int& end_y) const;
|
|
|
|
// Ensure a specific chunk's texture is properly sized
|
|
void ensureChunkTexture(int chunk_idx, int cell_width, int cell_height);
|
|
|
|
// Initialize chunk tracking arrays
|
|
void initChunks();
|
|
|
|
// Render a specific chunk to its cached texture (called when chunk is dirty)
|
|
virtual void renderChunkToTexture(int chunk_x, int chunk_y, int cell_width, int cell_height) = 0;
|
|
|
|
// Render the layer content to the cached texture (legacy - marks all dirty)
|
|
virtual void renderToTexture(int cell_width, int cell_height) = 0;
|
|
|
|
// Render the layer to a RenderTarget with the given transformation parameters
|
|
// Uses cached chunk textures, only re-renders visible dirty chunks
|
|
virtual void render(sf::RenderTarget& target,
|
|
float left_spritepixels, float top_spritepixels,
|
|
int left_edge, int top_edge, int x_limit, int y_limit,
|
|
float zoom, int cell_width, int cell_height) = 0;
|
|
|
|
// Resize the layer (reallocates storage and reinitializes chunks)
|
|
virtual void resize(int new_grid_x, int new_grid_y) = 0;
|
|
};
|
|
|
|
// Color layer - stores RGBA color per cell
|
|
class ColorLayer : public GridLayer {
|
|
public:
|
|
std::vector<sf::Color> colors;
|
|
|
|
// Perspective binding (#113) - binds layer to entity for automatic FOV updates
|
|
std::weak_ptr<UIEntity> perspective_entity;
|
|
sf::Color perspective_visible;
|
|
sf::Color perspective_discovered;
|
|
sf::Color perspective_unknown;
|
|
bool has_perspective;
|
|
|
|
ColorLayer(int z_index, int grid_x, int grid_y, UIGrid* parent);
|
|
|
|
// Access color at position
|
|
sf::Color& at(int x, int y);
|
|
const sf::Color& at(int x, int y) const;
|
|
|
|
// Fill entire layer with a color
|
|
void fill(const sf::Color& color);
|
|
|
|
// Fill a rectangular region with a color (#113)
|
|
void fillRect(int x, int y, int width, int height, const sf::Color& color);
|
|
|
|
// Draw FOV-based visibility (#113)
|
|
// Paints cells based on current FOV state from parent grid
|
|
void drawFOV(int source_x, int source_y, int radius,
|
|
TCOD_fov_algorithm_t algorithm,
|
|
const sf::Color& visible,
|
|
const sf::Color& discovered,
|
|
const sf::Color& unknown);
|
|
|
|
// Perspective binding (#113) - bind layer to entity for automatic updates
|
|
void applyPerspective(std::shared_ptr<UIEntity> entity,
|
|
const sf::Color& visible,
|
|
const sf::Color& discovered,
|
|
const sf::Color& unknown);
|
|
|
|
// Update perspective - redraws based on bound entity's current position
|
|
void updatePerspective();
|
|
|
|
// Clear perspective binding
|
|
void clearPerspective();
|
|
|
|
// Render a specific chunk to its texture (called when chunk is dirty AND visible)
|
|
void renderChunkToTexture(int chunk_x, int chunk_y, int cell_width, int cell_height) override;
|
|
|
|
// #148 - Render all content to cached texture (legacy - calls renderChunkToTexture for all)
|
|
void renderToTexture(int cell_width, int cell_height) override;
|
|
|
|
void render(sf::RenderTarget& target,
|
|
float left_spritepixels, float top_spritepixels,
|
|
int left_edge, int top_edge, int x_limit, int y_limit,
|
|
float zoom, int cell_width, int cell_height) override;
|
|
|
|
void resize(int new_grid_x, int new_grid_y) override;
|
|
};
|
|
|
|
// Tile layer - stores sprite index per cell with texture reference
|
|
class TileLayer : public GridLayer {
|
|
public:
|
|
std::vector<int> tiles; // Sprite indices (-1 = no tile)
|
|
std::shared_ptr<PyTexture> texture;
|
|
|
|
TileLayer(int z_index, int grid_x, int grid_y, UIGrid* parent,
|
|
std::shared_ptr<PyTexture> texture = nullptr);
|
|
|
|
// Access tile index at position
|
|
int& at(int x, int y);
|
|
int at(int x, int y) const;
|
|
|
|
// Fill entire layer with a tile index
|
|
void fill(int tile_index);
|
|
|
|
// Fill a rectangular region with a tile index (#113)
|
|
void fillRect(int x, int y, int width, int height, int tile_index);
|
|
|
|
// Render a specific chunk to its texture (called when chunk is dirty AND visible)
|
|
void renderChunkToTexture(int chunk_x, int chunk_y, int cell_width, int cell_height) override;
|
|
|
|
// #148 - Render all content to cached texture (legacy - calls renderChunkToTexture for all)
|
|
void renderToTexture(int cell_width, int cell_height) override;
|
|
|
|
void render(sf::RenderTarget& target,
|
|
float left_spritepixels, float top_spritepixels,
|
|
int left_edge, int top_edge, int x_limit, int y_limit,
|
|
float zoom, int cell_width, int cell_height) override;
|
|
|
|
void resize(int new_grid_x, int new_grid_y) override;
|
|
};
|
|
|
|
// Python wrapper types
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
std::shared_ptr<GridLayer> data;
|
|
std::shared_ptr<UIGrid> grid; // Parent grid reference
|
|
} PyGridLayerObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
std::shared_ptr<ColorLayer> data;
|
|
std::shared_ptr<UIGrid> grid;
|
|
} PyColorLayerObject;
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
std::shared_ptr<TileLayer> data;
|
|
std::shared_ptr<UIGrid> grid;
|
|
} PyTileLayerObject;
|
|
|
|
// Python API classes
|
|
class PyGridLayerAPI {
|
|
public:
|
|
// ColorLayer methods
|
|
static int ColorLayer_init(PyColorLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* ColorLayer_at(PyColorLayerObject* self, PyObject* args);
|
|
static PyObject* ColorLayer_set(PyColorLayerObject* self, PyObject* args);
|
|
static PyObject* ColorLayer_fill(PyColorLayerObject* self, PyObject* args);
|
|
static PyObject* ColorLayer_fill_rect(PyColorLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* ColorLayer_draw_fov(PyColorLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* ColorLayer_apply_perspective(PyColorLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* ColorLayer_update_perspective(PyColorLayerObject* self, PyObject* args);
|
|
static PyObject* ColorLayer_clear_perspective(PyColorLayerObject* self, PyObject* args);
|
|
static PyObject* ColorLayer_get_z_index(PyColorLayerObject* self, void* closure);
|
|
static int ColorLayer_set_z_index(PyColorLayerObject* self, PyObject* value, void* closure);
|
|
static PyObject* ColorLayer_get_visible(PyColorLayerObject* self, void* closure);
|
|
static int ColorLayer_set_visible(PyColorLayerObject* self, PyObject* value, void* closure);
|
|
static PyObject* ColorLayer_get_grid_size(PyColorLayerObject* self, void* closure);
|
|
static PyObject* ColorLayer_repr(PyColorLayerObject* self);
|
|
|
|
// TileLayer methods
|
|
static int TileLayer_init(PyTileLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* TileLayer_at(PyTileLayerObject* self, PyObject* args);
|
|
static PyObject* TileLayer_set(PyTileLayerObject* self, PyObject* args);
|
|
static PyObject* TileLayer_fill(PyTileLayerObject* self, PyObject* args);
|
|
static PyObject* TileLayer_fill_rect(PyTileLayerObject* self, PyObject* args, PyObject* kwds);
|
|
static PyObject* TileLayer_get_z_index(PyTileLayerObject* self, void* closure);
|
|
static int TileLayer_set_z_index(PyTileLayerObject* self, PyObject* value, void* closure);
|
|
static PyObject* TileLayer_get_visible(PyTileLayerObject* self, void* closure);
|
|
static int TileLayer_set_visible(PyTileLayerObject* self, PyObject* value, void* closure);
|
|
static PyObject* TileLayer_get_texture(PyTileLayerObject* self, void* closure);
|
|
static int TileLayer_set_texture(PyTileLayerObject* self, PyObject* value, void* closure);
|
|
static PyObject* TileLayer_get_grid_size(PyTileLayerObject* self, void* closure);
|
|
static PyObject* TileLayer_repr(PyTileLayerObject* self);
|
|
|
|
// Method and getset arrays
|
|
static PyMethodDef ColorLayer_methods[];
|
|
static PyGetSetDef ColorLayer_getsetters[];
|
|
static PyMethodDef TileLayer_methods[];
|
|
static PyGetSetDef TileLayer_getsetters[];
|
|
};
|
|
|
|
namespace mcrfpydef {
|
|
// ColorLayer type
|
|
static PyTypeObject PyColorLayerType = {
|
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
|
.tp_name = "mcrfpy.ColorLayer",
|
|
.tp_basicsize = sizeof(PyColorLayerObject),
|
|
.tp_itemsize = 0,
|
|
.tp_dealloc = (destructor)[](PyObject* self) {
|
|
PyColorLayerObject* obj = (PyColorLayerObject*)self;
|
|
obj->data.reset();
|
|
obj->grid.reset();
|
|
Py_TYPE(self)->tp_free(self);
|
|
},
|
|
.tp_repr = (reprfunc)PyGridLayerAPI::ColorLayer_repr,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = PyDoc_STR("ColorLayer(z_index=-1, grid_size=None)\n\n"
|
|
"A grid layer that stores RGBA colors per cell.\n\n"
|
|
"Args:\n"
|
|
" z_index (int): Render order. Negative = below entities. Default: -1\n"
|
|
" grid_size (tuple): Dimensions as (width, height). Default: parent grid size\n\n"
|
|
"Attributes:\n"
|
|
" z_index (int): Layer z-order relative to entities\n"
|
|
" visible (bool): Whether layer is rendered\n"
|
|
" grid_size (tuple): Layer dimensions (read-only)\n\n"
|
|
"Methods:\n"
|
|
" at(x, y): Get color at cell position\n"
|
|
" set(x, y, color): Set color at cell position\n"
|
|
" fill(color): Fill entire layer with color"),
|
|
.tp_methods = PyGridLayerAPI::ColorLayer_methods,
|
|
.tp_getset = PyGridLayerAPI::ColorLayer_getsetters,
|
|
.tp_init = (initproc)PyGridLayerAPI::ColorLayer_init,
|
|
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* {
|
|
PyColorLayerObject* self = (PyColorLayerObject*)type->tp_alloc(type, 0);
|
|
return (PyObject*)self;
|
|
}
|
|
};
|
|
|
|
// TileLayer type
|
|
static PyTypeObject PyTileLayerType = {
|
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
|
.tp_name = "mcrfpy.TileLayer",
|
|
.tp_basicsize = sizeof(PyTileLayerObject),
|
|
.tp_itemsize = 0,
|
|
.tp_dealloc = (destructor)[](PyObject* self) {
|
|
PyTileLayerObject* obj = (PyTileLayerObject*)self;
|
|
obj->data.reset();
|
|
obj->grid.reset();
|
|
Py_TYPE(self)->tp_free(self);
|
|
},
|
|
.tp_repr = (reprfunc)PyGridLayerAPI::TileLayer_repr,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = PyDoc_STR("TileLayer(z_index=-1, texture=None, grid_size=None)\n\n"
|
|
"A grid layer that stores sprite indices per cell.\n\n"
|
|
"Args:\n"
|
|
" z_index (int): Render order. Negative = below entities. Default: -1\n"
|
|
" texture (Texture): Sprite atlas for tile rendering. Default: None\n"
|
|
" grid_size (tuple): Dimensions as (width, height). Default: parent grid size\n\n"
|
|
"Attributes:\n"
|
|
" z_index (int): Layer z-order relative to entities\n"
|
|
" visible (bool): Whether layer is rendered\n"
|
|
" texture (Texture): Tile sprite atlas\n"
|
|
" grid_size (tuple): Layer dimensions (read-only)\n\n"
|
|
"Methods:\n"
|
|
" at(x, y): Get tile index at cell position\n"
|
|
" set(x, y, index): Set tile index at cell position\n"
|
|
" fill(index): Fill entire layer with tile index"),
|
|
.tp_methods = PyGridLayerAPI::TileLayer_methods,
|
|
.tp_getset = PyGridLayerAPI::TileLayer_getsetters,
|
|
.tp_init = (initproc)PyGridLayerAPI::TileLayer_init,
|
|
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* {
|
|
PyTileLayerObject* self = (PyTileLayerObject*)type->tp_alloc(type, 0);
|
|
return (PyObject*)self;
|
|
}
|
|
};
|
|
}
|