Atrinik Server 2.5
plugins/plugin_python/atrinik_player.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_player_fields Python player fields
00036  * <h2>Python player fields</h2>
00037  * List of the player fields and their meaning. */
00038 static fields_struct fields[] =
00039 {
00040     {"next", FIELDTYPE_PLAYER, offsetof(player, next), FIELDFLAG_READONLY, 0},
00041     {"prev", FIELDTYPE_PLAYER, offsetof(player, prev), FIELDFLAG_READONLY, 0},
00042 
00043     {"party", FIELDTYPE_PARTY, offsetof(player, party), FIELDFLAG_READONLY, 0},
00044     /* Shall not be modified in any way. Instead, one should use @ref Atrinik_Player_Fix "player.Fix()",
00045      * which will set class_ob to the last @ref CLASS object it finds in
00046      * player's inventory. In some cases, this is done automatically after
00047      * a script has finished executing (say events do this, for example). */
00048     {"class_ob", FIELDTYPE_OBJECT, offsetof(player, class_ob), FIELDFLAG_READONLY, 0},
00049     {"savebed_map", FIELDTYPE_CARY, offsetof(player, savebed_map), 0, sizeof(((player *) NULL)->savebed_map)},
00050     {"bed_x", FIELDTYPE_SINT16, offsetof(player, bed_x), 0, 0},
00051     {"bed_y", FIELDTYPE_SINT16, offsetof(player, bed_y), 0, 0},
00052     {"ob", FIELDTYPE_OBJECT, offsetof(player, ob), FIELDFLAG_READONLY, 0},
00053     {"quest_container", FIELDTYPE_OBJECT, offsetof(player, quest_container), FIELDFLAG_READONLY, 0},
00054     {"dm_stealth", FIELDTYPE_BOOLEAN, offsetof(player, dm_stealth), 0, 0},
00055     {"target_object", FIELDTYPE_OBJECTREF, offsetof(player, target_object), 0, offsetof(player, target_object_count)},
00056     {"no_shout", FIELDTYPE_BOOLEAN, offsetof(player, no_shout), 0, 0},
00057     {"known_spells", FIELDTYPE_LIST, offsetof(player, known_spells), 0, FIELDTYPE_KNOWN_SPELLS},
00058     {"cmd_permissions", FIELDTYPE_LIST, offsetof(player, cmd_permissions), 0, FIELDTYPE_CMD_PERMISSIONS},
00059     {"factions", FIELDTYPE_LIST, offsetof(player, faction_ids), 0, FIELDTYPE_FACTIONS},
00060     {"fame", FIELDTYPE_SINT64, offsetof(player, fame), 0, FIELDTYPE_FACTIONS},
00061 
00062     {"s_ext_title_flag", FIELDTYPE_BOOLEAN, offsetof(player, socket.ext_title_flag), 0, 0},
00063     {"s_host", FIELDTYPE_CSTR, offsetof(player, socket.host), FIELDFLAG_READONLY, 0},
00064     {"s_socket_version", FIELDTYPE_UINT32, offsetof(player, socket.socket_version), FIELDFLAG_READONLY, 0}
00065 };
00066 /* @endcparser */
00067 
00095 static PyObject *Atrinik_Player_GetEquipment(Atrinik_Player *pl, PyObject *args)
00096 {
00097     int slot;
00098 
00099     if (!PyArg_ParseTuple(args, "i", &slot))
00100     {
00101         return NULL;
00102     }
00103 
00104     if (slot < 0 || slot >= PLAYER_EQUIP_MAX)
00105     {
00106         PyErr_SetString(PyExc_ValueError, "Invalid slot number.");
00107         return NULL;
00108     }
00109 
00110     return wrap_object(pl->pl->equipment[slot]);
00111 }
00112 
00121 static PyObject *Atrinik_Player_CanCarry(Atrinik_Player *pl, PyObject *what)
00122 {
00123     uint32 weight;
00124 
00125     if (PyObject_TypeCheck(what, &Atrinik_ObjectType))
00126     {
00127         OBJEXISTCHECK((Atrinik_Object *) what);
00128         weight = WEIGHT_NROF(((Atrinik_Object *) what)->obj, ((Atrinik_Object *) what)->obj->nrof);
00129     }
00130     else if (PyInt_Check(what))
00131     {
00132         weight = PyInt_AsLong(what);
00133     }
00134     else
00135     {
00136         PyErr_SetString(PyExc_ValueError, "Invalid value for 'what' parameter.");
00137         return NULL;
00138     }
00139 
00140     Py_ReturnBoolean(hooks->player_can_carry(pl->pl->ob, weight));
00141 }
00142 
00153 static PyObject *Atrinik_Player_GetSkill(Atrinik_Player *pl, PyObject *args)
00154 {
00155     sint32 type;
00156     uint32 id;
00157 
00158     if (!PyArg_ParseTuple(args, "iI", &type, &id))
00159     {
00160         return NULL;
00161     }
00162 
00163     if (type == SKILL)
00164     {
00165         if (id >= NROFSKILLS)
00166         {
00167             PyErr_SetString(PyExc_ValueError, "player.GetSkill(): 'id' is not valid for TYPE_SKILL.");
00168             return NULL;
00169         }
00170 
00171         return wrap_object(pl->pl->skill_ptr[id]);
00172     }
00173     else if (type == EXPERIENCE)
00174     {
00175         if (id >= MAX_EXP_CAT)
00176         {
00177             PyErr_SetString(PyExc_ValueError, "player.GetSkill(): 'id' is not valid for TYPE_EXPERIENCE.");
00178             return NULL;
00179         }
00180 
00181         return wrap_object(pl->pl->exp_ptr[id]);
00182     }
00183     else
00184     {
00185         PyErr_SetString(PyExc_ValueError, "player.GetSkill(): 'type' is not valid.");
00186         return NULL;
00187     }
00188 }
00189 
00196 static PyObject *Atrinik_Player_AddExp(Atrinik_Player *pl, PyObject *args)
00197 {
00198     uint32 skill;
00199     sint64 exp_gain;
00200     int exact = 0;
00201 
00202     if (!PyArg_ParseTuple(args, "IL|i", &skill, &exp_gain, &exact))
00203     {
00204         return NULL;
00205     }
00206 
00207     hooks->add_exp(pl->pl->ob, exp_gain, skill, exact);
00208 
00209     Py_INCREF(Py_None);
00210     return Py_None;
00211 }
00212 
00219 static PyObject *Atrinik_Player_WriteToSocket(Atrinik_Player *pl, PyObject *args)
00220 {
00221     char command_id;
00222     const char *command_data;
00223     int command_data_len;
00224 
00225     if (!PyArg_ParseTuple(args, "bs#", &command_id, &command_data, &command_data_len))
00226     {
00227         return NULL;
00228     }
00229 
00230     hooks->Write_String_To_Socket(&pl->pl->socket, command_id, command_data, command_data_len);
00231 
00232     Py_INCREF(Py_None);
00233     return Py_None;
00234 }
00235 
00242 static PyObject *Atrinik_Player_BankDeposit(Atrinik_Player *pl, PyObject *args)
00243 {
00244     const char *text;
00245     int ret;
00246     sint64 value;
00247 
00248     if (!PyArg_ParseTuple(args, "s", &text))
00249     {
00250         return NULL;
00251     }
00252 
00253     ret = hooks->bank_deposit(pl->pl->ob, text, &value);
00254 
00255     return Py_BuildValue("(iL)", ret, value);
00256 }
00257 
00264 static PyObject *Atrinik_Player_BankWithdraw(Atrinik_Player *pl, PyObject *args)
00265 {
00266     const char *text;
00267     int ret;
00268     sint64 value;
00269 
00270     if (!PyArg_ParseTuple(args, "s", &text))
00271     {
00272         return NULL;
00273     }
00274 
00275     ret = hooks->bank_withdraw(pl->pl->ob, text, &value);
00276 
00277     return Py_BuildValue("(iL)", ret, value);
00278 }
00279 
00284 static PyObject *Atrinik_Player_BankBalance(Atrinik_Player *pl, PyObject *args)
00285 {
00286     (void) args;
00287 
00288     return Py_BuildValue("L", hooks->bank_get_balance(pl->pl->ob));
00289 }
00290 
00301 static PyObject *Atrinik_Player_SwapApartments(Atrinik_Player *pl, PyObject *args)
00302 {
00303     const char *mapold, *mapnew;
00304     int x, y;
00305 
00306     if (!PyArg_ParseTuple(args, "ssii", &mapold, &mapnew, &x, &y))
00307     {
00308         return NULL;
00309     }
00310 
00311     Py_ReturnBoolean(hooks->swap_apartments(mapold, mapnew, x, y, pl->pl->ob));
00312 }
00313 
00320 static PyObject *Atrinik_Player_ExecuteCommand(Atrinik_Player *pl, PyObject *args)
00321 {
00322     const char *command;
00323     char *cp;
00324     int ret;
00325 
00326     if (!PyArg_ParseTuple(args, "s", &command))
00327     {
00328         return NULL;
00329     }
00330 
00331     if (pl->pl->state != ST_PLAYING)
00332     {
00333         PyErr_SetString(AtrinikError, "ExecuteCommand(): Player is not in a state to execute commands.");
00334         return NULL;
00335     }
00336 
00337     /* Make a copy of the command, since execute_newserver_command
00338      * modifies the string. */
00339     cp = hooks->strdup_local(command);
00340     ret = hooks->execute_newserver_command(pl->pl->ob, cp);
00341     free(cp);
00342 
00343     return Py_BuildValue("i", ret);
00344 }
00345 
00351 static PyObject *Atrinik_Player_DoKnowSpell(Atrinik_Player *pl, PyObject *args)
00352 {
00353     int spell;
00354 
00355     if (!PyArg_ParseTuple(args, "i", &spell))
00356     {
00357         return NULL;
00358     }
00359 
00360     Py_ReturnBoolean(hooks->check_spell_known(pl->pl->ob, spell));
00361 }
00362 
00368 static PyObject *Atrinik_Player_AcquireSpell(Atrinik_Player *pl, PyObject *args)
00369 {
00370     int spell, learn = 1;
00371 
00372     if (!PyArg_ParseTuple(args, "i|i", &spell, &learn))
00373     {
00374         return NULL;
00375     }
00376 
00377     if (learn)
00378     {
00379         hooks->do_learn_spell(pl->pl->ob, spell, 0);
00380     }
00381     else
00382     {
00383         hooks->do_forget_spell(pl->pl->ob, spell);
00384     }
00385 
00386     Py_INCREF(Py_None);
00387     return Py_None;
00388 }
00389 
00395 static PyObject *Atrinik_Player_DoKnowSkill(Atrinik_Player *pl, PyObject *args)
00396 {
00397     int skill;
00398 
00399     if (!PyArg_ParseTuple(args, "i", &skill))
00400     {
00401         return NULL;
00402     }
00403 
00404     Py_ReturnBoolean(pl->pl->skill_ptr[skill]);
00405 }
00406 
00411 static PyObject *Atrinik_Player_AcquireSkill(Atrinik_Player *pl, PyObject *args)
00412 {
00413     int skill;
00414 
00415     if (!PyArg_ParseTuple(args, "i", &skill))
00416     {
00417         return NULL;
00418     }
00419 
00420     hooks->learn_skill(pl->pl->ob, NULL, NULL, skill, 0);
00421 
00422     Py_INCREF(Py_None);
00423     return Py_None;
00424 }
00425 
00430 static PyObject *Atrinik_Player_FindMarkedObject(Atrinik_Player *pl, PyObject *args)
00431 {
00432     (void) args;
00433 
00434     return wrap_object(hooks->find_marked_object(pl->pl->ob));
00435 }
00436 
00446 static PyObject *Atrinik_Player_Sound(Atrinik_Player *pl, PyObject *args, PyObject *keywds)
00447 {
00448     static char *kwlist[] = {"filename", "type", "x", "y", "loop", "volume", NULL};
00449     const char *filename;
00450     int type = CMD_SOUND_EFFECT, x = 0, y = 0, loop = 0, volume = 0;
00451 
00452     if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|iiiii", kwlist, &filename, &type, &x, &y, &loop, &volume))
00453     {
00454         return NULL;
00455     }
00456 
00457     hooks->play_sound_player_only(pl->pl, type, filename, x, y, loop, volume);
00458 
00459     Py_INCREF(Py_None);
00460     return Py_None;
00461 }
00462 
00467 static PyObject *Atrinik_Player_Examine(Atrinik_Player *pl, PyObject *args)
00468 {
00469     Atrinik_Object *obj;
00470 
00471     if (!PyArg_ParseTuple(args, "O!", &Atrinik_ObjectType, &obj))
00472     {
00473         return NULL;
00474     }
00475 
00476     hooks->examine(pl->pl->ob, obj->obj);
00477 
00478     Py_INCREF(Py_None);
00479     return Py_None;
00480 }
00481 
00485 static PyMethodDef methods[] =
00486 {
00487     {"GetEquipment", (PyCFunction) Atrinik_Player_GetEquipment, METH_VARARGS, 0},
00488     {"CanCarry", (PyCFunction) Atrinik_Player_CanCarry, METH_O, 0},
00489     {"GetSkill", (PyCFunction) Atrinik_Player_GetSkill, METH_VARARGS, 0},
00490     {"AddExp", (PyCFunction) Atrinik_Player_AddExp, METH_VARARGS, 0},
00491     {"WriteToSocket", (PyCFunction) Atrinik_Player_WriteToSocket, METH_VARARGS, 0},
00492     {"BankDeposit", (PyCFunction) Atrinik_Player_BankDeposit, METH_VARARGS, 0},
00493     {"BankWithdraw", (PyCFunction) Atrinik_Player_BankWithdraw, METH_VARARGS, 0},
00494     {"BankBalance", (PyCFunction) Atrinik_Player_BankBalance, METH_NOARGS, 0},
00495     {"SwapApartments", (PyCFunction) Atrinik_Player_SwapApartments, METH_VARARGS, 0},
00496     {"ExecuteCommand", (PyCFunction) Atrinik_Player_ExecuteCommand, METH_VARARGS, 0},
00497     {"DoKnowSpell", (PyCFunction) Atrinik_Player_DoKnowSpell, METH_VARARGS, 0},
00498     {"AcquireSpell", (PyCFunction) Atrinik_Player_AcquireSpell, METH_VARARGS, 0},
00499     {"DoKnowSkill", (PyCFunction) Atrinik_Player_DoKnowSkill, METH_VARARGS, 0},
00500     {"AcquireSkill", (PyCFunction) Atrinik_Player_AcquireSkill, METH_VARARGS, 0},
00501     {"FindMarkedObject", (PyCFunction) Atrinik_Player_FindMarkedObject, METH_NOARGS, 0},
00502     {"Sound", (PyCFunction) Atrinik_Player_Sound, METH_VARARGS | METH_KEYWORDS, 0},
00503     {"Examine", (PyCFunction) Atrinik_Player_Examine, METH_VARARGS, 0},
00504     {NULL, NULL, 0, 0}
00505 };
00506 
00512 static PyObject *get_attribute(Atrinik_Player *pl, void *context)
00513 {
00514     return generic_field_getter((fields_struct *) context, pl->pl);
00515 }
00516 
00523 static int set_attribute(Atrinik_Player *pl, PyObject *value, void *context)
00524 {
00525     fields_struct *field = (fields_struct *) context;
00526 
00527     if (generic_field_setter(field, pl->pl, value) == -1)
00528     {
00529         return -1;
00530     }
00531 
00532     if (field->offset == offsetof(player, target_object))
00533     {
00534         hooks->send_target_command(pl->pl);
00535     }
00536 
00537     return 0;
00538 }
00539 
00546 static PyObject *Atrinik_Player_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
00547 {
00548     Atrinik_Player *pl;
00549 
00550     (void) args;
00551     (void) kwds;
00552 
00553     pl = (Atrinik_Player *) type->tp_alloc(type, 0);
00554 
00555     if (pl)
00556     {
00557         pl->pl = NULL;
00558     }
00559 
00560     return (PyObject *) pl;
00561 }
00562 
00566 static void Atrinik_Player_dealloc(Atrinik_Player *pl)
00567 {
00568     pl->pl = NULL;
00569 #ifndef IS_PY_LEGACY
00570     Py_TYPE(pl)->tp_free((PyObject *) pl);
00571 #else
00572     pl->ob_type->tp_free((PyObject *) pl);
00573 #endif
00574 }
00575 
00580 static PyObject *Atrinik_Player_str(Atrinik_Player *pl)
00581 {
00582     return Py_BuildValue("s", pl->pl->ob->name);
00583 }
00584 
00585 static int Atrinik_Player_InternalCompare(Atrinik_Player *left, Atrinik_Player *right)
00586 {
00587     return (left->pl < right->pl ? -1 : (left->pl == right->pl ? 0 : 1));
00588 }
00589 
00590 static PyObject *Atrinik_Player_RichCompare(Atrinik_Player *left, Atrinik_Player *right, int op)
00591 {
00592     if (!left || !right || !PyObject_TypeCheck((PyObject *) left, &Atrinik_PlayerType) || !PyObject_TypeCheck((PyObject *) right, &Atrinik_PlayerType))
00593     {
00594         Py_INCREF(Py_NotImplemented);
00595         return Py_NotImplemented;
00596     }
00597 
00598     return generic_rich_compare(op, Atrinik_Player_InternalCompare(left, right));
00599 }
00600 
00603 static PyGetSetDef getseters[NUM_FIELDS + 1];
00604 
00606 PyTypeObject Atrinik_PlayerType =
00607 {
00608 #ifdef IS_PY3K
00609     PyVarObject_HEAD_INIT(NULL, 0)
00610 #else
00611     PyObject_HEAD_INIT(NULL)
00612     0,
00613 #endif
00614     "Atrinik.Player",
00615     sizeof(Atrinik_Player),
00616     0,
00617     (destructor) Atrinik_Player_dealloc,
00618     NULL, NULL, NULL,
00619 #ifdef IS_PY3K
00620     NULL,
00621 #else
00622     (cmpfunc) Atrinik_Player_InternalCompare,
00623 #endif
00624     0, 0, 0, 0, 0, 0,
00625     (reprfunc) Atrinik_Player_str,
00626     0, 0, 0,
00627     Py_TPFLAGS_DEFAULT,
00628     "Atrinik players",
00629     NULL, NULL,
00630     (richcmpfunc) Atrinik_Player_RichCompare,
00631     0, 0, 0,
00632     methods,
00633     0,
00634     getseters,
00635     0, 0, 0, 0, 0, 0, 0,
00636     Atrinik_Player_new,
00637     0, 0, 0, 0, 0, 0, 0, 0
00638 #ifndef IS_PY_LEGACY
00639     , 0
00640 #endif
00641 };
00642 
00647 int Atrinik_Player_init(PyObject *module)
00648 {
00649     size_t i;
00650 
00651     /* Field getters */
00652     for (i = 0; i < NUM_FIELDS; i++)
00653     {
00654         PyGetSetDef *def = &getseters[i];
00655 
00656         def->name = fields[i].name;
00657         def->get = (getter) get_attribute;
00658         def->set = (setter) set_attribute;
00659         def->doc = NULL;
00660         def->closure = (void *) &fields[i];
00661     }
00662 
00663     getseters[i].name = NULL;
00664 
00665     Atrinik_PlayerType.tp_new = PyType_GenericNew;
00666 
00667     if (PyType_Ready(&Atrinik_PlayerType) < 0)
00668     {
00669         return 0;
00670     }
00671 
00672     Py_INCREF(&Atrinik_PlayerType);
00673     PyModule_AddObject(module, "Player", (PyObject *) &Atrinik_PlayerType);
00674 
00675     return 1;
00676 }
00677 
00682 PyObject *wrap_player(player *pl)
00683 {
00684     Atrinik_Player *wrapper;
00685 
00686     /* Return None if no player was to be wrapped. */
00687     if (!pl)
00688     {
00689         Py_INCREF(Py_None);
00690         return Py_None;
00691     }
00692 
00693     wrapper = PyObject_NEW(Atrinik_Player, &Atrinik_PlayerType);
00694 
00695     if (wrapper)
00696     {
00697         wrapper->pl = pl;
00698     }
00699 
00700     return (PyObject *) wrapper;
00701 }