mirror of
https://github.com/worldforge/cyphesis
synced 2026-08-13 12:26:04 -04:00
* rulesets/Character.cpp, rulesets/StatisticsProperty.h, rulesets/StatisticsProperty.cpp: Add a property handler for statistics. * rulesets/Character.cpp: When setting task, check if an existing task needs to be cleared first. When clearing a task, check whether a task exists or not. * rulesets/Character.cpp: When initiating combat task from task factory, check to see if the task is successfully created before using it. * rulesets/Py_Property.cpp, rulesets/Py_Property.h: New python wrapper class for entity properties. * rulesets/Py_Mind.cpp: Re-work getattr logic to make it clearer. * rulesets/Py_Object.cpp: Return Py_None when an Atlas Element cannot be converted to python. This ensures that this function never returns null. * rulesets/Py_Statistics.cpp: Add temporary handlers for hardcoded statistics for "attack", "defence", and "strength". * rulesets/Py_Task.cpp: Add wrappers for methods Task.irrelevant and Task.obsolete, as these are now required in combat scripts. * rulesets/Py_Task.h: Fix the PyTask_Check macro, which was miscoded. * rulesets/Py_Thing.cpp: Add wrapper for Entity.send_world so that scripts can channel ops through entities. * rulesets/Py_Thing.h, rulesets/Py_Thing.cpp: Add an addition structure for directlty wrapping Character in a type safe way. Add method structure pointer to Entity and Character wrappers so that the same getattr method is used for both. Add new method table for Character including wrappers for set_task and clear_task, allowing task scripts to add themselves and remove themselves from characters. Modify wrapEntity() to ensure that Character entities get the right wrapper. * rulesets/Py_Thing.cpp: Re-work getattr code so that properties and soft attributes are checked individually, rather than in a single call to Entity.get. Use new python property wrappers to wrap properties explicitly if required rather than converting to Atlas then to generic Python data. * rulesets/TerrainProperty.h: Fix incorrect class description in documentation. * rulesets/mason/world/tasks/Combat.py: Complete implementation of simple example combat. * server/TaskFactory.cpp: Remove debug output.
310 lines
9.4 KiB
C++
310 lines
9.4 KiB
C++
// This file may be redistributed and modified only under the terms of
|
|
// the GNU General Public License (See COPYING for details).
|
|
// Copyright (C) 2000 Alistair Riddoch
|
|
|
|
#include "Py_Object.h"
|
|
#include "Py_Operation.h"
|
|
#include "Py_Oplist.h"
|
|
#include "Py_Location.h"
|
|
|
|
#include "modules/Location.h"
|
|
|
|
#include "common/log.h"
|
|
#include "common/compose.hpp"
|
|
#include "common/debug.h"
|
|
|
|
#include <iostream>
|
|
|
|
using Atlas::Message::Element;
|
|
using Atlas::Message::MapType;
|
|
using Atlas::Message::ListType;
|
|
|
|
static const bool debug_flag = false;
|
|
|
|
/*
|
|
* Beginning of Object methods section.
|
|
*/
|
|
|
|
static PyObject* Object_get_name(PyMessageElement * self)
|
|
{
|
|
#ifndef NDEBUG
|
|
if (self->m_obj == NULL) {
|
|
PyErr_SetString(PyExc_AssertionError,"NULL MessageElement in MessageElement.get_name");
|
|
return NULL;
|
|
}
|
|
#endif // NDEBUG
|
|
return PyString_FromString("obj");
|
|
}
|
|
|
|
/*
|
|
* Object methods structure.
|
|
*/
|
|
|
|
static PyMethodDef Object_methods[] = {
|
|
{"get_name", (PyCFunction)Object_get_name, METH_NOARGS},
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
/*
|
|
* Beginning of Object standard methods section.
|
|
*/
|
|
|
|
static void Object_dealloc(PyMessageElement *self)
|
|
{
|
|
if (self->m_obj != NULL) {
|
|
delete self->m_obj;
|
|
}
|
|
PyMem_DEL(self);
|
|
}
|
|
|
|
static PyObject * Object_getattr(PyMessageElement *self, char *name)
|
|
{
|
|
#ifndef NDEBUG
|
|
if (self->m_obj == NULL) {
|
|
PyErr_SetString(PyExc_AssertionError,"NULL MessageElement in MessageElement.getattr");
|
|
return NULL;
|
|
}
|
|
#endif // NDEBUG
|
|
if (self->m_obj->isMap()) {
|
|
const MapType & omap = self->m_obj->asMap();
|
|
MapType::const_iterator I = omap.find(name);
|
|
if (I != omap.end()) {
|
|
return MessageElement_asPyObject(I->second);
|
|
}
|
|
}
|
|
return Py_FindMethod(Object_methods, (PyObject *)self, name);
|
|
}
|
|
|
|
static int Object_setattr( PyMessageElement *self, char *name, PyObject *v)
|
|
{
|
|
#ifndef NDEBUG
|
|
if (self->m_obj == NULL) {
|
|
PyErr_SetString(PyExc_AssertionError,"NULL MessageElement in MessageElement.setattr");
|
|
return -1;
|
|
}
|
|
#endif // NDEBUG
|
|
log(WARNING, String::compose("Setting %1 attribute on an Atlas Message", name).c_str());
|
|
if (self->m_obj->isMap()) {
|
|
MapType & omap = self->m_obj->asMap();
|
|
Element v_obj = PyObject_asMessageElement(v);
|
|
if (v_obj.getType() != Element::TYPE_NONE) {
|
|
omap[name] = v_obj;
|
|
return 0;
|
|
} else {
|
|
PyErr_SetString(PyExc_TypeError, "object cannot be converted to Atlas data in MessageElement.setattr");
|
|
return -1;
|
|
}
|
|
}
|
|
PyErr_SetString(PyExc_TypeError, "Cannot set attribute on non-map in MessageElement.setattr");
|
|
return -1;
|
|
}
|
|
|
|
PyTypeObject PyMessageElement_Type = {
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
0, /*ob_size*/
|
|
"MessageElement", /*tp_name*/
|
|
sizeof(PyMessageElement), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
/* methods */
|
|
(destructor)Object_dealloc, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
(getattrfunc)Object_getattr, /*tp_getattr*/
|
|
(setattrfunc)Object_setattr, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash*/
|
|
};
|
|
|
|
/*
|
|
* Beginning of Object creation functions section.
|
|
*/
|
|
|
|
PyMessageElement * newPyMessageElement()
|
|
{
|
|
PyMessageElement * self;
|
|
self = PyObject_NEW(PyMessageElement, &PyMessageElement_Type);
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
/*
|
|
* Utility functions to munge between Object related types and python types
|
|
*/
|
|
|
|
static PyObject * MapType_asPyObject(const MapType & map)
|
|
{
|
|
PyObject * args_pydict = PyDict_New();
|
|
PyMessageElement * item;
|
|
MapType::const_iterator Iend = map.end();
|
|
for (MapType::const_iterator I = map.begin(); I != Iend; ++I) {
|
|
const std::string & key = I->first;
|
|
item = newPyMessageElement();
|
|
if (item == NULL) {
|
|
PyErr_SetString(PyExc_MemoryError,"error creating map");
|
|
return NULL;
|
|
}
|
|
item->m_obj = new Element(I->second);
|
|
// PyDict_SetItem() does not eat the reference passed to it
|
|
PyDict_SetItemString(args_pydict,(char *)key.c_str(),(PyObject *)item);
|
|
Py_DECREF(item);
|
|
}
|
|
return args_pydict;
|
|
}
|
|
|
|
static PyObject * ListType_asPyObject(const ListType & list)
|
|
{
|
|
PyObject * args_pylist = PyList_New(list.size());
|
|
int j = 0;
|
|
PyMessageElement * item;
|
|
ListType::const_iterator Iend = list.end();
|
|
for (ListType::const_iterator I = list.begin(); I != Iend; ++I, ++j) {
|
|
item = newPyMessageElement();
|
|
if (item == NULL) {
|
|
PyErr_SetString(PyExc_MemoryError,"error creating list");
|
|
return NULL;
|
|
}
|
|
item->m_obj = new Element(*I);
|
|
// PyList_SetItem() eats the reference passed to it
|
|
PyList_SetItem(args_pylist, j, (PyObject *)item);
|
|
}
|
|
return args_pylist;
|
|
}
|
|
|
|
PyObject * MessageElement_asPyObject(const Element & obj)
|
|
{
|
|
PyObject * ret = NULL;
|
|
switch (obj.getType()) {
|
|
case Element::TYPE_INT:
|
|
ret = PyInt_FromLong(obj.Int());
|
|
break;
|
|
case Element::TYPE_FLOAT:
|
|
ret = PyFloat_FromDouble(obj.Float());
|
|
break;
|
|
case Element::TYPE_STRING:
|
|
ret = PyString_FromString(obj.String().c_str());
|
|
break;
|
|
case Element::TYPE_MAP:
|
|
ret = MapType_asPyObject(obj.Map());
|
|
break;
|
|
case Element::TYPE_LIST:
|
|
ret = ListType_asPyObject(obj.List());
|
|
break;
|
|
default:
|
|
Py_INCREF(Py_None);
|
|
ret = Py_None;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Element PyListObject_asElement(PyObject * list)
|
|
{
|
|
ListType argslist;
|
|
PyMessageElement * item;
|
|
for(int i = 0; i < PyList_Size(list); i++) {
|
|
item = (PyMessageElement *)PyList_GetItem(list, i);
|
|
if (PyMessageElement_Check(item)) {
|
|
argslist.push_back(*(item->m_obj));
|
|
} else {
|
|
Element o = PyObject_asMessageElement((PyObject*)item);
|
|
if (o.getType() != Element::TYPE_NONE) {
|
|
argslist.push_back(o);
|
|
} else {
|
|
debug( std::cout << "Python to atlas conversion failed on element " << i << " of list" << std::endl << std::flush; );
|
|
return Element();
|
|
}
|
|
}
|
|
}
|
|
return argslist;
|
|
}
|
|
|
|
Element PyDictObject_asElement(PyObject * dict)
|
|
{
|
|
MapType argsmap;
|
|
PyMessageElement * item;
|
|
PyObject * keys = PyDict_Keys(dict);
|
|
PyObject * vals = PyDict_Values(dict);
|
|
for(int i = 0; i < PyDict_Size(dict); i++) {
|
|
PyObject * key = PyList_GetItem(keys, i);
|
|
item = (PyMessageElement *)PyList_GetItem(vals, i);
|
|
if (PyMessageElement_Check(item)) {
|
|
argsmap[PyString_AsString(key)] = *(item->m_obj);
|
|
} else {
|
|
Element o = PyObject_asMessageElement((PyObject*)item);
|
|
if (o.getType() != Element::TYPE_NONE) {
|
|
argsmap[PyString_AsString(key)] = o;
|
|
} else {
|
|
debug( std::cout << "Python to atlas conversion failed on element " << PyString_AsString(key) << " of map" << std::endl << std::flush; );
|
|
return Element();
|
|
}
|
|
}
|
|
}
|
|
Py_DECREF(keys);
|
|
Py_DECREF(vals);
|
|
return argsmap;
|
|
}
|
|
|
|
Element PyObject_asMessageElement(PyObject * o)
|
|
{
|
|
if (PyInt_Check(o)) {
|
|
return Element((int)PyInt_AsLong(o));
|
|
}
|
|
if (PyFloat_Check(o)) {
|
|
return Element(PyFloat_AsDouble(o));
|
|
}
|
|
if (PyString_Check(o)) {
|
|
return Element(PyString_AsString(o));
|
|
}
|
|
if (PyList_Check(o)) {
|
|
return PyListObject_asElement(o);
|
|
}
|
|
if (PyDict_Check(o)) {
|
|
return PyDictObject_asElement(o);
|
|
}
|
|
if (PyTuple_Check(o)) {
|
|
ListType list;
|
|
int i, size = PyTuple_Size(o);
|
|
for(i = 0; i < size; i++) {
|
|
Element item = PyObject_asMessageElement(PyTuple_GetItem(o, i));
|
|
if (item.getType() != Element::TYPE_NONE) {
|
|
list.push_back(item);
|
|
} else {
|
|
debug( std::cout << "Python to atlas conversion failed on element " << i << " of tuple" << std::endl << std::flush; );
|
|
return Element();
|
|
}
|
|
}
|
|
return Element(list);
|
|
}
|
|
if (PyMessageElement_Check(o)) {
|
|
PyMessageElement * obj = (PyMessageElement *)o;
|
|
return *(obj->m_obj);
|
|
}
|
|
if (PyOperation_Check(o)) {
|
|
PyOperation * op = (PyOperation *)o;
|
|
return op->operation->asMessage();
|
|
}
|
|
if (PyOplist_Check(o)) {
|
|
PyOplist * opl = (PyOplist *)o;
|
|
ListType _list;
|
|
Element msg(_list);
|
|
ListType & entlist = msg.asList();
|
|
const OpVector & ops = *opl->ops;
|
|
OpVector::const_iterator Iend = ops.end();
|
|
for (OpVector::const_iterator I = ops.begin(); I != Iend; ++I) {
|
|
entlist.push_back((*I)->asMessage());
|
|
}
|
|
return msg;
|
|
}
|
|
if (PyLocation_Check(o)) {
|
|
PyLocation * loc = (PyLocation *)o;
|
|
MapType _map;
|
|
loc->location->addToMessage(_map);
|
|
return Element(_map);
|
|
}
|
|
return Element();
|
|
}
|