Atrinik Server 2.5
plugins/plugin_python/atrinik_map.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <plugin_python.h>
00031 
00034 /* @cparser
00035  * @page plugin_python_map_fields Python map fields
00036  * <h2>Python map fields</h2>
00037  * List of the map fields and their meaning. */
00038 static fields_struct fields[] =
00039 {
00040     {"next", FIELDTYPE_MAP, offsetof(mapstruct, next), FIELDFLAG_READONLY, 0},
00041     {"previous", FIELDTYPE_MAP, offsetof(mapstruct, previous), FIELDFLAG_READONLY, 0},
00042 
00043     {"name", FIELDTYPE_CSTR, offsetof(mapstruct, name), 0, 0},
00044     {"msg", FIELDTYPE_CSTR, offsetof(mapstruct, msg), 0, 0},
00045     {"reset_timeout", FIELDTYPE_UINT32, offsetof(mapstruct, reset_timeout), 0, 0},
00046     {"timeout", FIELDTYPE_SINT32, offsetof(mapstruct, timeout), 0, 0},
00047     {"difficulty", FIELDTYPE_UINT16, offsetof(mapstruct, difficulty), 0, 0},
00048     {"height", FIELDTYPE_UINT16, offsetof(mapstruct, height), FIELDFLAG_READONLY, 0},
00049     {"width", FIELDTYPE_UINT16, offsetof(mapstruct, width), FIELDFLAG_READONLY, 0},
00050     {"darkness", FIELDTYPE_UINT8, offsetof(mapstruct, darkness), 0, 0},
00051     {"path", FIELDTYPE_SHSTR, offsetof(mapstruct, path), FIELDFLAG_READONLY, 0},
00052     {"enter_x", FIELDTYPE_UINT8, offsetof(mapstruct, enter_x), 0, 0},
00053     {"enter_y", FIELDTYPE_UINT8, offsetof(mapstruct, enter_y), 0, 0},
00054     {"region", FIELDTYPE_REGION, offsetof(mapstruct, region), FIELDFLAG_READONLY, 0},
00055     {"bg_music", FIELDTYPE_CSTR, offsetof(mapstruct, bg_music), 0, 0},
00056     {"weather", FIELDTYPE_CSTR, offsetof(mapstruct, weather), 0, 0}
00057 };
00058 /* @endcparser */
00059 
00064 /* @cparser MAP_FLAG_(.*)
00065  * @page plugin_python_map_flags Python map flags
00066  * <h2>Python map flags</h2>
00067  * List of the map flags and their meaning. */
00068 static char *mapflag_names[] =
00069 {
00070     "f_outdoor", "f_unique", "f_fixed_rtime", "f_nomagic",
00071     "f_nopriest", "f_noharm", "f_nosummon", "f_fixed_login",
00072     "f_player_no_save", "f_unused2", "f_unused3", "f_pvp",
00073     "f_no_save"
00074 };
00075 /* @endcparser */
00076 
00078 #define NUM_MAPFLAGS (sizeof(mapflag_names) / sizeof(mapflag_names[0]))
00079 
00091 static PyObject *Atrinik_Map_GetFirstObject(Atrinik_Map *map, PyObject *args)
00092 {
00093     int x, y;
00094     object *val = NULL;
00095     mapstruct *m = map->map;
00096 
00097     if (!PyArg_ParseTuple(args, "ii", &x, &y))
00098     {
00099         return NULL;
00100     }
00101 
00102     if ((m = hooks->get_map_from_coord(m, &x, &y)))
00103     {
00104         /* Since map objects are loaded in reverse mode, the last one in
00105          * in the list is actually the first. */
00106         val = GET_MAP_OB_LAST(m, x, y);
00107     }
00108 
00109     return wrap_object(val);
00110 }
00111 
00118 static PyObject *Atrinik_Map_GetLastObject(Atrinik_Map *map, PyObject *args)
00119 {
00120     int x, y;
00121     object *val = NULL;
00122     mapstruct *m = map->map;
00123 
00124     if (!PyArg_ParseTuple(args, "ii", &x, &y))
00125     {
00126         return NULL;
00127     }
00128 
00129     if ((m = hooks->get_map_from_coord(m, &x, &y)))
00130     {
00131         /* Since map objects are loaded in reverse mode, the first one in
00132          * in the list is actually the last. */
00133         val = GET_MAP_OB(m, x, y);
00134     }
00135 
00136     return wrap_object(val);
00137 }
00138 
00157 static PyObject *Atrinik_Map_GetLayer(Atrinik_Map *map, PyObject *args)
00158 {
00159     int x, y;
00160     uint8 layer;
00161     mapstruct *m;
00162     PyObject *list;
00163     object *tmp;
00164 
00165     if (!PyArg_ParseTuple(args, "iiB", &x, &y, &layer))
00166     {
00167         return NULL;
00168     }
00169 
00170     /* Validate the layer ID. */
00171     if (layer > NUM_LAYERS)
00172     {
00173         PyErr_SetString(PyExc_ValueError, "map.GetLayer(): Invalid layer ID.");
00174         return NULL;
00175     }
00176 
00177     if (!(m = hooks->get_map_from_coord(map->map, &x, &y)))
00178     {
00179         RAISE("map.GetLayer(): Unable to get map using get_map_from_coord().");
00180     }
00181 
00182     list = PyList_New(0);
00183 
00184     /* Handle layer 0 specially: it's always at the start. */
00185     if (layer == 0)
00186     {
00187         for (tmp = GET_MAP_OB(m, x, y); tmp && tmp->layer == layer; tmp = tmp->above)
00188         {
00189             PyList_Append(list, wrap_object(tmp));
00190         }
00191     }
00192     else
00193     {
00194         for (tmp = GET_MAP_OB_LAYER(m, x, y, layer - 1); tmp && tmp->layer == layer; tmp = tmp->above)
00195         {
00196             PyList_Append(list, wrap_object(tmp));
00197         }
00198     }
00199 
00200     return list;
00201 }
00202 
00210 static PyObject *Atrinik_Map_GetMapFromCoord(Atrinik_Map *map, PyObject *args)
00211 {
00212     int x, y;
00213     mapstruct *m;
00214     PyObject *tuple;
00215 
00216     if (!PyArg_ParseTuple(args, "ii", &x, &y))
00217     {
00218         return NULL;
00219     }
00220 
00221     m = hooks->get_map_from_coord(map->map, &x, &y);
00222     tuple = PyTuple_New(3);
00223     PyTuple_SET_ITEM(tuple, 0, wrap_map(m));
00224     PyTuple_SET_ITEM(tuple, 1, Py_BuildValue("i", x));
00225     PyTuple_SET_ITEM(tuple, 2, Py_BuildValue("i", y));
00226 
00227     return tuple;
00228 }
00229 
00239 static PyObject *Atrinik_Map_PlaySound(Atrinik_Map *map, PyObject *args, PyObject *keywds)
00240 {
00241     static char *kwlist[] = {"filename", "x", "y", "type", "loop", "volume", NULL};
00242     const char *filename;
00243     int x, y, type = CMD_SOUND_EFFECT, loop = 0, volume = 0;
00244 
00245     if (!PyArg_ParseTupleAndKeywords(args, keywds, "sii|iii", kwlist, &filename, &x, &y, &type, &loop, &volume))
00246     {
00247         return NULL;
00248     }
00249 
00250     hooks->play_sound_map(map->map, type, filename, x, y, loop, volume);
00251 
00252     Py_INCREF(Py_None);
00253     return Py_None;
00254 }
00255 
00267 static PyObject *Atrinik_Map_Message(Atrinik_Map *map, PyObject *args, PyObject *keywds)
00268 {
00269     static char *kwlist[] = {"x", "y", "distance", "message", "color", "flags", NULL};
00270     int flags = 0, x, y, d;
00271     const char *message, *color = COLOR_BLUE;
00272 
00273     if (!PyArg_ParseTupleAndKeywords(args, keywds, "iiis|si", kwlist, &x, &y, &d, &message, &color, &flags))
00274     {
00275         return NULL;
00276     }
00277 
00278     hooks->new_info_map(flags, color, map->map, x, y, d, message);
00279 
00280     Py_INCREF(Py_None);
00281     return Py_None;
00282 }
00283 
00292 static PyObject *Atrinik_Map_CreateObject(Atrinik_Map *map, PyObject *args)
00293 {
00294     const char *archname;
00295     int x, y;
00296     archetype *arch;
00297     object *newobj;
00298 
00299     if (!PyArg_ParseTuple(args, "sii", &archname, &x, &y))
00300     {
00301         return NULL;
00302     }
00303 
00304     if (!(arch = hooks->find_archetype(archname)) || !(newobj = hooks->arch_to_object(arch)))
00305     {
00306         RAISE("map.CreateObject(): Invalid archetype.");
00307         return NULL;
00308     }
00309 
00310     newobj->x = x;
00311     newobj->y = y;
00312     newobj = hooks->insert_ob_in_map(newobj, map->map, NULL, 0);
00313 
00314     return wrap_object(newobj);
00315 }
00316 
00321 static PyObject *Atrinik_Map_CountPlayers(Atrinik_Map *map, PyObject *args)
00322 {
00323     (void) args;
00324 
00325     return Py_BuildValue("i", hooks->players_on_map(map->map));
00326 }
00327 
00332 static PyObject *Atrinik_Map_GetPlayers(Atrinik_Map *map, PyObject *args)
00333 {
00334     PyObject *list = PyList_New(0);
00335     object *tmp;
00336 
00337     (void) args;
00338 
00339     for (tmp = map->map->player_first; tmp; tmp = CONTR(tmp)->map_above)
00340     {
00341         PyList_Append(list, wrap_object(tmp));
00342     }
00343 
00344     return list;
00345 }
00346 
00353 static PyObject *Atrinik_Map_Insert(Atrinik_Map *map, PyObject *args)
00354 {
00355     Atrinik_Object *obj;
00356     sint16 x, y;
00357 
00358     if (!PyArg_ParseTuple(args, "O!hh", &Atrinik_ObjectType, &obj, &x, &y))
00359     {
00360         return NULL;
00361     }
00362 
00363     OBJEXISTCHECK(obj);
00364 
00365     if (!QUERY_FLAG(obj->obj, FLAG_REMOVED))
00366     {
00367         hooks->object_remove_esrv_update(obj->obj);
00368     }
00369 
00370     obj->obj->x = x;
00371     obj->obj->y = y;
00372     hooks->insert_ob_in_map(obj->obj, map->map, NULL, 0);
00373 
00374     Py_INCREF(Py_None);
00375     return Py_None;
00376 }
00377 
00384 static PyObject *Atrinik_Map_Wall(Atrinik_Map *map, PyObject *args)
00385 {
00386     sint16 x, y;
00387 
00388     if (!PyArg_ParseTuple(args, "hh", &x, &y))
00389     {
00390         return NULL;
00391     }
00392 
00393     return Py_BuildValue("i", hooks->wall(map->map, x, y));
00394 }
00395 
00410 static PyObject *Atrinik_Map_Blocked(Atrinik_Map *map, PyObject *args)
00411 {
00412     Atrinik_Object *ob;
00413     int x, y, terrain;
00414     mapstruct *m;
00415 
00416     if (!PyArg_ParseTuple(args, "O!iii", &Atrinik_ObjectType, &ob, &x, &y, &terrain))
00417     {
00418         return NULL;
00419     }
00420 
00421     OBJEXISTCHECK(ob);
00422 
00423     if (!(m = hooks->get_map_from_coord(map->map, &x, &y)))
00424     {
00425         RAISE("Unable to get map using get_map_from_coord().");
00426     }
00427 
00428     return Py_BuildValue("i", hooks->blocked(ob->obj, m, x, y, terrain));
00429 }
00430 
00445 static PyObject *Atrinik_Map_FreeSpot(Atrinik_Map *map, PyObject *args)
00446 {
00447     Atrinik_Object *ob;
00448     int x, y, start, stop;
00449     mapstruct *m;
00450 
00451     if (!PyArg_ParseTuple(args, "O!iiii", &Atrinik_ObjectType, &ob, &x, &y, &start, &stop))
00452     {
00453         return NULL;
00454     }
00455 
00456     OBJEXISTCHECK(ob);
00457 
00458     if (start < 0 || stop < 0)
00459     {
00460         PyErr_SetString(PyExc_ValueError, "map.FreeSpot(): 'start' and 'stop' cannot be negative.");
00461         return NULL;
00462     }
00463 
00464     if (stop > SIZEOFFREE)
00465     {
00466         PyErr_SetString(PyExc_ValueError, "map.FreeSpot(): 'stop' cannot be higher than SIZEOFFREE.");
00467         return NULL;
00468     }
00469 
00470     if (!(m = hooks->get_map_from_coord(map->map, &x, &y)))
00471     {
00472         return Py_BuildValue("i", -1);
00473     }
00474 
00475     return Py_BuildValue("i", hooks->find_free_spot(ob->obj->arch, ob->obj, m, x, y, start, stop + 1));
00476 }
00477 
00481 static PyMethodDef MapMethods[] =
00482 {
00483     {"GetFirstObject", (PyCFunction) Atrinik_Map_GetFirstObject, METH_VARARGS, 0},
00484     {"GetLastObject", (PyCFunction) Atrinik_Map_GetLastObject, METH_VARARGS, 0},
00485     {"GetLayer", (PyCFunction) Atrinik_Map_GetLayer, METH_VARARGS, 0},
00486     {"GetMapFromCoord", (PyCFunction) Atrinik_Map_GetMapFromCoord, METH_VARARGS, 0},
00487     {"PlaySound", (PyCFunction) Atrinik_Map_PlaySound, METH_VARARGS | METH_KEYWORDS, 0},
00488     {"Message", (PyCFunction) Atrinik_Map_Message, METH_VARARGS | METH_KEYWORDS, 0},
00489     {"CreateObject", (PyCFunction) Atrinik_Map_CreateObject, METH_VARARGS, 0},
00490     {"CountPlayers", (PyCFunction) Atrinik_Map_CountPlayers, METH_NOARGS, 0},
00491     {"GetPlayers", (PyCFunction) Atrinik_Map_GetPlayers, METH_NOARGS, 0},
00492     {"Insert", (PyCFunction) Atrinik_Map_Insert, METH_VARARGS, 0},
00493     {"Wall", (PyCFunction) Atrinik_Map_Wall, METH_VARARGS, 0},
00494     {"Blocked", (PyCFunction) Atrinik_Map_Blocked, METH_VARARGS, 0},
00495     {"FreeSpot", (PyCFunction) Atrinik_Map_FreeSpot, METH_VARARGS, 0},
00496     {NULL, NULL, 0, 0}
00497 };
00498 
00504 static PyObject *get_attribute(Atrinik_Map *map, void *context)
00505 {
00506     return generic_field_getter((fields_struct *) context, map->map);
00507 }
00508 
00515 static int set_attribute(Atrinik_Map *map, PyObject *value, void *context)
00516 {
00517     if (generic_field_setter((fields_struct *) context, map->map, value) == -1)
00518     {
00519         return -1;
00520     }
00521 
00522     if (((fields_struct *) context)->offset == offsetof(mapstruct, darkness))
00523     {
00524         hooks->set_map_darkness(map->map, map->map->darkness);
00525     }
00526 
00527     return 0;
00528 }
00529 
00537 static PyObject *Map_GetFlag(Atrinik_Map *map, void *context)
00538 {
00539     size_t flagno = (size_t) context;
00540 
00541     /* Should not happen. */
00542     if (flagno >= NUM_MAPFLAGS)
00543     {
00544         PyErr_SetString(PyExc_OverflowError, "Invalid flag ID.");
00545         return NULL;
00546     }
00547 
00548     Py_ReturnBoolean(map->map->map_flags & (1 << flagno));
00549 }
00550 
00557 static int Map_SetFlag(Atrinik_Map *map, PyObject *val, void *context)
00558 {
00559     size_t flagno = (size_t) context;
00560 
00561     /* Should not happen. */
00562     if (flagno >= NUM_MAPFLAGS)
00563     {
00564         PyErr_SetString(PyExc_OverflowError, "Invalid flag ID.");
00565         return -1;
00566     }
00567 
00568     if (val == Py_True)
00569     {
00570         map->map->map_flags |= (1U << flagno);
00571     }
00572     else if (val == Py_False)
00573     {
00574         map->map->map_flags &= ~(1U << flagno);
00575     }
00576     else
00577     {
00578         PyErr_SetString(PyExc_TypeError, "Flag value must be either True or False.");
00579         return -1;
00580     }
00581 
00582     return 0;
00583 }
00584 
00591 static PyObject *Atrinik_Map_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
00592 {
00593     Atrinik_Map *self;
00594 
00595     (void) args;
00596     (void) kwds;
00597 
00598     self = (Atrinik_Map *) type->tp_alloc(type, 0);
00599 
00600     if (self)
00601     {
00602         self->map = NULL;
00603     }
00604 
00605     return (PyObject *) self;
00606 }
00607 
00611 static void Atrinik_Map_dealloc(Atrinik_Map *self)
00612 {
00613     self->map = NULL;
00614 #ifndef IS_PY_LEGACY
00615     Py_TYPE(self)->tp_free((PyObject *) self);
00616 #else
00617     self->ob_type->tp_free((PyObject *) self);
00618 #endif
00619 }
00620 
00625 static PyObject *Atrinik_Map_str(Atrinik_Map *self)
00626 {
00627     char buf[HUGE_BUF];
00628 
00629     snprintf(buf, sizeof(buf), "[%s \"%s\"]", self->map->path, self->map->name);
00630     return Py_BuildValue("s", buf);
00631 }
00632 
00633 static int Atrinik_Map_InternalCompare(Atrinik_Map *left, Atrinik_Map *right)
00634 {
00635     return left->map < right->map ? -1 : (left->map == right->map ? 0 : 1);
00636 }
00637 
00638 static PyObject *Atrinik_Map_RichCompare(Atrinik_Map *left, Atrinik_Map *right, int op)
00639 {
00640     if (!left || !right || !PyObject_TypeCheck((PyObject *) left, &Atrinik_MapType) || !PyObject_TypeCheck((PyObject *) right, &Atrinik_MapType))
00641     {
00642         Py_INCREF(Py_NotImplemented);
00643         return Py_NotImplemented;
00644     }
00645 
00646     return generic_rich_compare(op, Atrinik_Map_InternalCompare(left, right));
00647 }
00648 
00650 static PyGetSetDef getseters[NUM_FIELDS + NUM_MAPFLAGS + 1];
00651 
00653 PyTypeObject Atrinik_MapType =
00654 {
00655 #ifdef IS_PY3K
00656     PyVarObject_HEAD_INIT(NULL, 0)
00657 #else
00658     PyObject_HEAD_INIT(NULL)
00659     0,
00660 #endif
00661     "Atrinik.Map",
00662     sizeof(Atrinik_Map),
00663     0,
00664     (destructor) Atrinik_Map_dealloc,
00665     NULL, NULL, NULL,
00666 #ifdef IS_PY3K
00667     NULL,
00668 #else
00669     (cmpfunc) Atrinik_Map_InternalCompare,
00670 #endif
00671     0, 0, 0, 0, 0, 0,
00672     (reprfunc) Atrinik_Map_str,
00673     0, 0, 0,
00674     Py_TPFLAGS_DEFAULT,
00675     "Atrinik maps",
00676     NULL, NULL,
00677     (richcmpfunc) Atrinik_Map_RichCompare,
00678     0, 0, 0,
00679     MapMethods,
00680     0,
00681     getseters,
00682     0, 0, 0, 0, 0, 0, 0,
00683     Atrinik_Map_new,
00684     0, 0, 0, 0, 0, 0, 0, 0
00685 #ifndef IS_PY_LEGACY
00686     , 0
00687 #endif
00688 };
00689 
00694 int Atrinik_Map_init(PyObject *module)
00695 {
00696     size_t i, flagno;
00697 
00698     /* Field getters */
00699     for (i = 0; i < NUM_FIELDS; i++)
00700     {
00701         PyGetSetDef *def = &getseters[i];
00702 
00703         def->name = fields[i].name;
00704         def->get = (getter) get_attribute;
00705         def->set = (setter) set_attribute;
00706         def->doc = NULL;
00707         def->closure = (void *) &fields[i];
00708     }
00709 
00710     /* Flag getters */
00711     for (flagno = 0; flagno < NUM_MAPFLAGS; flagno++)
00712     {
00713         PyGetSetDef *def = &getseters[i++];
00714 
00715         def->name = mapflag_names[flagno];
00716         def->get = (getter) Map_GetFlag;
00717         def->set = (setter) Map_SetFlag;
00718         def->doc = NULL;
00719         def->closure = (void *) flagno;
00720     }
00721 
00722     getseters[i].name = NULL;
00723 
00724     Atrinik_MapType.tp_new = PyType_GenericNew;
00725 
00726     if (PyType_Ready(&Atrinik_MapType) < 0)
00727     {
00728         return 0;
00729     }
00730 
00731     Py_INCREF(&Atrinik_MapType);
00732     PyModule_AddObject(module, "Map", (PyObject *) &Atrinik_MapType);
00733 
00734     return 1;
00735 }
00736 
00741 PyObject *wrap_map(mapstruct *what)
00742 {
00743     Atrinik_Map *wrapper;
00744 
00745     /* Return None if no map was to be wrapped. */
00746     if (!what)
00747     {
00748         Py_INCREF(Py_None);
00749         return Py_None;
00750     }
00751 
00752     wrapper = PyObject_NEW(Atrinik_Map, &Atrinik_MapType);
00753 
00754     if (wrapper)
00755     {
00756         wrapper->map = what;
00757     }
00758 
00759     return (PyObject *) wrapper;
00760 }