Phase 1 - FOV Enum System: - Create PyFOV.h/cpp with mcrfpy.FOV IntEnum (BASIC, DIAMOND, SHADOW, etc.) - Add mcrfpy.default_fov module property initialized to FOV.BASIC - Add grid.fov and grid.fov_radius properties for per-grid defaults - Remove deprecated module-level FOV_* constants (breaking change) Phase 2 - Layer Operations: - Implement ColorLayer.fill_rect(pos, size, color) for rectangle fills - Implement TileLayer.fill_rect(pos, size, index) for tile rectangle fills - Implement ColorLayer.draw_fov(source, radius, fov, visible, discovered, unknown) to paint FOV-based visibility on color layers using parent grid's TCOD map The FOV enum uses Python's IntEnum for type safety while maintaining backward compatibility with integer values. Tests updated to use new API. Addresses #114 (FOV enum), #113 (layer operations) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
0545dd4861
commit
018e73590f
11 changed files with 1061 additions and 407 deletions
148
src/PyFOV.cpp
Normal file
148
src/PyFOV.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#include "PyFOV.h"
|
||||
#include "McRFPy_API.h"
|
||||
|
||||
// Static storage for cached enum class reference
|
||||
PyObject* PyFOV::fov_enum_class = nullptr;
|
||||
|
||||
PyObject* PyFOV::create_enum_class(PyObject* module) {
|
||||
// Import IntEnum from enum module
|
||||
PyObject* enum_module = PyImport_ImportModule("enum");
|
||||
if (!enum_module) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject* int_enum = PyObject_GetAttrString(enum_module, "IntEnum");
|
||||
Py_DECREF(enum_module);
|
||||
if (!int_enum) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create dict of enum members
|
||||
PyObject* members = PyDict_New();
|
||||
if (!members) {
|
||||
Py_DECREF(int_enum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add all FOV algorithm members
|
||||
struct {
|
||||
const char* name;
|
||||
int value;
|
||||
} fov_members[] = {
|
||||
{"BASIC", FOV_BASIC},
|
||||
{"DIAMOND", FOV_DIAMOND},
|
||||
{"SHADOW", FOV_SHADOW},
|
||||
{"PERMISSIVE_0", FOV_PERMISSIVE_0},
|
||||
{"PERMISSIVE_1", FOV_PERMISSIVE_1},
|
||||
{"PERMISSIVE_2", FOV_PERMISSIVE_2},
|
||||
{"PERMISSIVE_3", FOV_PERMISSIVE_3},
|
||||
{"PERMISSIVE_4", FOV_PERMISSIVE_4},
|
||||
{"PERMISSIVE_5", FOV_PERMISSIVE_5},
|
||||
{"PERMISSIVE_6", FOV_PERMISSIVE_6},
|
||||
{"PERMISSIVE_7", FOV_PERMISSIVE_7},
|
||||
{"PERMISSIVE_8", FOV_PERMISSIVE_8},
|
||||
{"RESTRICTIVE", FOV_RESTRICTIVE},
|
||||
{"SYMMETRIC_SHADOWCAST", FOV_SYMMETRIC_SHADOWCAST},
|
||||
};
|
||||
|
||||
for (const auto& m : fov_members) {
|
||||
PyObject* value = PyLong_FromLong(m.value);
|
||||
if (!value) {
|
||||
Py_DECREF(members);
|
||||
Py_DECREF(int_enum);
|
||||
return NULL;
|
||||
}
|
||||
if (PyDict_SetItemString(members, m.name, value) < 0) {
|
||||
Py_DECREF(value);
|
||||
Py_DECREF(members);
|
||||
Py_DECREF(int_enum);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(value);
|
||||
}
|
||||
|
||||
// Call IntEnum("FOV", members) to create the enum class
|
||||
PyObject* name = PyUnicode_FromString("FOV");
|
||||
if (!name) {
|
||||
Py_DECREF(members);
|
||||
Py_DECREF(int_enum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// IntEnum(name, members) using functional API
|
||||
PyObject* args = PyTuple_Pack(2, name, members);
|
||||
Py_DECREF(name);
|
||||
Py_DECREF(members);
|
||||
if (!args) {
|
||||
Py_DECREF(int_enum);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject* fov_class = PyObject_Call(int_enum, args, NULL);
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(int_enum);
|
||||
|
||||
if (!fov_class) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Cache the reference for fast type checking
|
||||
fov_enum_class = fov_class;
|
||||
Py_INCREF(fov_enum_class);
|
||||
|
||||
// Add to module
|
||||
if (PyModule_AddObject(module, "FOV", fov_class) < 0) {
|
||||
Py_DECREF(fov_class);
|
||||
fov_enum_class = nullptr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fov_class;
|
||||
}
|
||||
|
||||
int PyFOV::from_arg(PyObject* arg, TCOD_fov_algorithm_t* out_algo, bool* was_none) {
|
||||
if (was_none) *was_none = false;
|
||||
|
||||
// Accept None -> caller should use default
|
||||
if (arg == Py_None) {
|
||||
if (was_none) *was_none = true;
|
||||
*out_algo = FOV_BASIC;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Accept FOV enum member (check if it's an instance of our enum)
|
||||
if (fov_enum_class && PyObject_IsInstance(arg, fov_enum_class)) {
|
||||
// IntEnum members have a 'value' attribute
|
||||
PyObject* value = PyObject_GetAttrString(arg, "value");
|
||||
if (!value) {
|
||||
return 0;
|
||||
}
|
||||
long val = PyLong_AsLong(value);
|
||||
Py_DECREF(value);
|
||||
if (val == -1 && PyErr_Occurred()) {
|
||||
return 0;
|
||||
}
|
||||
*out_algo = (TCOD_fov_algorithm_t)val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Accept int (for backwards compatibility)
|
||||
if (PyLong_Check(arg)) {
|
||||
long val = PyLong_AsLong(arg);
|
||||
if (val == -1 && PyErr_Occurred()) {
|
||||
return 0;
|
||||
}
|
||||
if (val < 0 || val >= NB_FOV_ALGORITHMS) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Invalid FOV algorithm value: %ld. Must be 0-%d or use mcrfpy.FOV enum.",
|
||||
val, NB_FOV_ALGORITHMS - 1);
|
||||
return 0;
|
||||
}
|
||||
*out_algo = (TCOD_fov_algorithm_t)val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"FOV algorithm must be mcrfpy.FOV enum member, int, or None");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue