McRogueFace/src/PyBehavior.cpp
John McCardle 2f1e472245 Phase 2: Entity data model extensions for behavior system
- Add Behavior enum (IDLE..FLEE, 11 values) and Trigger enum (DONE,
  BLOCKED, TARGET) as runtime IntEnum classes (closes #297, closes #298)
- Add entity label system: labels property (frozenset), add_label(),
  remove_label(), has_label(), constructor kwarg (closes #296)
- Add cell_pos integer logical position decoupled from float draw_pos;
  grid_pos now aliases cell_pos; SpatialHash::updateCell() for cell-based
  bucket management; FOV/visibility uses cell_position (closes #295)
- Add step callback and default_behavior properties to Entity for
  grid.step() turn management (closes #299)
- Update updateVisibility, visible_entities, ColorLayer::updatePerspective
  to use cell_position instead of float position

BREAKING: grid_pos no longer derives from float x/y position. Use
cell_pos/grid_pos for logical position, draw_pos for render position.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 22:05:06 -04:00

116 lines
3.5 KiB
C++

#include "PyBehavior.h"
#include <sstream>
// Static storage for cached enum class reference
PyObject* PyBehavior::behavior_enum_class = nullptr;
struct BehaviorEntry {
const char* name;
int value;
const char* description;
};
static const BehaviorEntry behavior_table[] = {
{"IDLE", 0, "No action each turn"},
{"CUSTOM", 1, "Calls step callback only, no built-in movement"},
{"NOISE4", 2, "Random movement in 4 cardinal directions"},
{"NOISE8", 3, "Random movement in 8 directions (incl. diagonals)"},
{"PATH", 4, "Follow a precomputed path to completion"},
{"WAYPOINT", 5, "Path through a sequence of waypoints in order"},
{"PATROL", 6, "Patrol waypoints back and forth (reversing at ends)"},
{"LOOP", 7, "Loop through waypoints cyclically"},
{"SLEEP", 8, "Wait for N turns, then trigger DONE"},
{"SEEK", 9, "Move toward target using Dijkstra map"},
{"FLEE", 10, "Move away from target using Dijkstra map"},
};
static const int NUM_BEHAVIOR_ENTRIES = sizeof(behavior_table) / sizeof(behavior_table[0]);
PyObject* PyBehavior::create_enum_class(PyObject* module) {
std::ostringstream code;
code << "from enum import IntEnum\n\n";
code << "class Behavior(IntEnum):\n";
code << " \"\"\"Enum representing entity behavior types for grid.step() turn management.\n";
code << " \n";
code << " Values:\n";
for (int i = 0; i < NUM_BEHAVIOR_ENTRIES; i++) {
code << " " << behavior_table[i].name << ": " << behavior_table[i].description << "\n";
}
code << " \"\"\"\n";
for (int i = 0; i < NUM_BEHAVIOR_ENTRIES; i++) {
code << " " << behavior_table[i].name << " = " << behavior_table[i].value << "\n";
}
code << R"(
def _Behavior_eq(self, other):
if isinstance(other, str):
if self.name == other:
return True
return False
return int.__eq__(int(self), other)
Behavior.__eq__ = _Behavior_eq
def _Behavior_ne(self, other):
result = type(self).__eq__(self, other)
if result is NotImplemented:
return result
return not result
Behavior.__ne__ = _Behavior_ne
Behavior.__hash__ = lambda self: hash(int(self))
Behavior.__repr__ = lambda self: f"{type(self).__name__}.{self.name}"
Behavior.__str__ = lambda self: self.name
)";
std::string code_str = code.str();
PyObject* globals = PyDict_New();
if (!globals) return NULL;
PyObject* builtins = PyEval_GetBuiltins();
PyDict_SetItemString(globals, "__builtins__", builtins);
PyObject* locals = PyDict_New();
if (!locals) {
Py_DECREF(globals);
return NULL;
}
PyObject* result = PyRun_String(code_str.c_str(), Py_file_input, globals, locals);
if (!result) {
Py_DECREF(globals);
Py_DECREF(locals);
return NULL;
}
Py_DECREF(result);
PyObject* behavior_class = PyDict_GetItemString(locals, "Behavior");
if (!behavior_class) {
PyErr_SetString(PyExc_RuntimeError, "Failed to create Behavior enum class");
Py_DECREF(globals);
Py_DECREF(locals);
return NULL;
}
Py_INCREF(behavior_class);
behavior_enum_class = behavior_class;
Py_INCREF(behavior_enum_class);
if (PyModule_AddObject(module, "Behavior", behavior_class) < 0) {
Py_DECREF(behavior_class);
Py_DECREF(globals);
Py_DECREF(locals);
behavior_enum_class = nullptr;
return NULL;
}
Py_DECREF(globals);
Py_DECREF(locals);
return behavior_class;
}