cyphesis/rulesets/Py_Object.cpp
Al Riddoch d3f2f1a351 2005-12-30 Al Riddoch <alriddoch@zepler.org>
* 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.
2005-12-30 18:02:40 +00:00

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();
}