Atrinik Server  4.0
atrinik_object.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
33 #include <plugin_python.h>
34 #include <toolkit/bresenham.h>
35 #include <artifact.h>
36 #include <toolkit/packet.h>
37 #include <faction.h>
38 #include <player.h>
39 #include <object.h>
40 
44 static fields_struct fields[] = {
45  {"below", FIELDTYPE_OBJECT, offsetof(object, below), FIELDFLAG_READONLY, 0,
46  "Object stacked below this one.; Atrinik.Object.Object or None "
47  "(readonly)"},
48  {"above", FIELDTYPE_OBJECT, offsetof(object, above), FIELDFLAG_READONLY, 0,
49  "Object stacked above this one.; Atrinik.Object.Object or None "
50  "(readonly)"},
51  {"inv", FIELDTYPE_OBJECT_ITERATOR, offsetof(object, inv),
53  "First object in the inventory.; Atrinik.Object.ObjectIterator "
54  "(readonly)"},
55  {"env", FIELDTYPE_OBJECT, offsetof(object, env), FIELDFLAG_READONLY, 0,
56  "Inventory the object is in.; Atrinik.Object.Object or None "
57  "(readonly)"},
58  {"head", FIELDTYPE_OBJECT, offsetof(object, head), FIELDFLAG_READONLY, 0,
59  "Head part of a linked object.; Atrinik.Object.Object or None "
60  "(readonly)"},
61  {"more", FIELDTYPE_OBJECT, offsetof(object, more), FIELDFLAG_READONLY, 0,
62  "Next linked object.; Atrinik.Object.Object or None (readonly)"},
63  {"map", FIELDTYPE_MAP, offsetof(object, map), FIELDFLAG_READONLY, 0,
64  "Map the object is on.; Atrinik.Map.Map or None (readonly)"},
65  {"name", FIELDTYPE_SHSTR, offsetof(object, name), FIELDFLAG_PLAYER_READONLY,
66  0, "Name of the object.; str (player readonly)"},
67  {"custom_name", FIELDTYPE_SHSTR, offsetof(object, custom_name), 0, 0,
68  "Custom name given to the object.; str or None"},
69  {"glow", FIELDTYPE_SHSTR, offsetof(object, glow), 0, 0,
70  "Glow color, eg, 'ff0000'; str or None"},
71  {"title", FIELDTYPE_SHSTR, offsetof(object, title), 0, 0,
72  "Title of the object.; str or None"},
73  {"race", FIELDTYPE_SHSTR, offsetof(object, race), 0, 0,
74  "Race associated with the object.; str or None"},
75  {"slaying", FIELDTYPE_SHSTR, offsetof(object, slaying), 0, 0,
76  "The slaying field. Used for different purposes, depending on the "
77  "object's type.; str or None"},
78  {"msg", FIELDTYPE_SHSTR, offsetof(object, msg), 0, 0,
79  "The object's story.; str or None"},
80  {"artifact", FIELDTYPE_SHSTR, offsetof(object, artifact), 0, 0,
81  "Artifact name.; str or None"},
82  {"weight", FIELDTYPE_UINT32, offsetof(object, weight), 0, 0,
83  "Weight of the object in grams.; int"},
84  {"count", FIELDTYPE_UINT32, offsetof(object, count), FIELDFLAG_READONLY, 0,
85  "Object's unique identifier.; int (readonly)"},
86 
87  {"weight_limit", FIELDTYPE_UINT32, offsetof(object, weight_limit), 0, 0,
88  "Maximum weight the object's inventory can hold, in grams.; int"},
89  {"carrying", FIELDTYPE_UINT32, offsetof(object, carrying), 0, 0,
90  "Weight the object is currently carrying in its inventory, in "
91  "grams.; int"},
92  {"path_attuned", FIELDTYPE_UINT32, offsetof(object, path_attuned), 0, 0,
93  "Spell paths the object is attuned to.; int"},
94  {"path_repelled", FIELDTYPE_UINT32, offsetof(object, path_repelled), 0, 0,
95  "Spell paths the object is repelled from.; int"},
96  {"path_denied", FIELDTYPE_UINT32, offsetof(object, path_denied), 0, 0,
97  "Spell paths the object is denied access to.; int"},
98  {"value", FIELDTYPE_INT64, offsetof(object, value), 0, 0,
99  "Value of the object.; int"},
100  {"nrof", FIELDTYPE_UINT32, offsetof(object, nrof), 0, 0,
101  "Amount of objects.; int"},
102 
103  {"enemy", FIELDTYPE_OBJECTREF, offsetof(object, enemy),
104  FIELDFLAG_PLAYER_READONLY, offsetof(object, enemy_count),
105  "Enemy of the object.; Atrinik.Object.Object or None (player "
106  "readonly)"},
107  {"attacked_by", FIELDTYPE_OBJECTREF, offsetof(object, attacked_by),
108  FIELDFLAG_READONLY, offsetof(object, attacked_by_count),
109  "Who is attacking the object.; Atrinik.Object.Object or None "
110  "(readonly)"},
111  {"owner", FIELDTYPE_OBJECTREF, offsetof(object, owner), FIELDFLAG_READONLY,
112  offsetof(object, ownercount), "Owner of the object.; "
113  "Atrinik.Object.Object or None (readonly)"},
114 
115  {"x", FIELDTYPE_INT16, offsetof(object, x), FIELDFLAG_READONLY, 0,
116  "If the object is on a :attr:`~Atrinik.Object.Object.map`, X "
117  "coordinate on the map the object is on.; int (readonly)"},
118  {"y", FIELDTYPE_INT16, offsetof(object, y), FIELDFLAG_READONLY, 0,
119  "If the object is on a :attr:`~Atrinik.Object.Object.map`, Y "
120  "coordinate on the map the object is on.; int (readonly)"},
121  {"attacked_by_distance", FIELDTYPE_INT16,
122  offsetof(object, attacked_by_distance), 0, 0,
123  "Distance the object was attacked from.; int"},
124  {"last_damage", FIELDTYPE_UINT16, offsetof(object, last_damage), 0, 0,
125  "Last damage done to the object.; int"},
126  {"terrain_type", FIELDTYPE_UINT16, offsetof(object, terrain_type), 0, 0,
127  "Terrain type of the object.; int"},
128  {"terrain_flag", FIELDTYPE_UINT16, offsetof(object, terrain_flag), 0, 0,
129  "Terrains that this object allows its wearer to walk on.; int"},
130  {"material", FIELDTYPE_UINT16, offsetof(object, material), 0, 0,
131  "What materials this object consists of.; int"},
132  {"material_real", FIELDTYPE_INT16, offsetof(object, material_real), 0, 0,
133  "Holds the real material value like what kind of steel.; int"},
134 
135  {"last_heal", FIELDTYPE_INT16, offsetof(object, last_heal), 0, 0,
136  "Last heal value.; int"},
137  {"last_sp", FIELDTYPE_INT16, offsetof(object, last_sp), 0, 0,
138  "Last mana value.; int"},
139  {"last_grace", FIELDTYPE_INT16, offsetof(object, last_grace), 0, 0,
140  "Last grace value.; int"},
141  {"last_eat", FIELDTYPE_INT16, offsetof(object, last_eat), 0, 0,
142  "Last eat value.; int"},
143 
144  {"magic", FIELDTYPE_INT8, offsetof(object, magic), 0, 0,
145  "Magical bonus of this object.; int"},
146  {"state", FIELDTYPE_UINT8, offsetof(object, state), 0, 0,
147  "Object's animation state.; int"},
148  {"level", FIELDTYPE_INT8, offsetof(object, level),
149  FIELDFLAG_PLAYER_READONLY, 0, "Level of the object.; int (player "
150  "readonly)"},
151  {"direction", FIELDTYPE_INT8, offsetof(object, direction), 0, 0,
152  "Direction the object is facing.; int"},
153  {"quick_pos", FIELDTYPE_UINT8, offsetof(object, quick_pos), 0, 0,
154  "For head object, number of tail parts, for tail part, the tail's "
155  "offset.; int"},
156  {"quickslot", FIELDTYPE_UINT8, offsetof(object, quickslot),
157  FIELDFLAG_READONLY, 0, "Quickslot ID of the object.; "
158  "int (readonly)"},
159 
160  {"type", FIELDTYPE_UINT8, offsetof(object, type), 0, 0,
161  "Type of the object.; int"},
162  {"sub_type", FIELDTYPE_UINT8, offsetof(object, sub_type), 0, 0,
163  "Object's sub-type.; int"},
164  {"item_quality", FIELDTYPE_UINT8, offsetof(object, item_quality), 0, 0,
165  "Object's maximum quality.; int"},
166  {"item_condition", FIELDTYPE_UINT8, offsetof(object, item_condition), 0, 0,
167  "Current object condition.; int"},
168  {"item_race", FIELDTYPE_UINT8, offsetof(object, item_race), 0, 0,
169  "Item race, eg, orcish, dwarvish, etc.; int"},
170  {"item_level", FIELDTYPE_UINT8, offsetof(object, item_level), 0, 0,
171  "Level required to use the item.; int"},
172  {"item_skill", FIELDTYPE_UINT8, offsetof(object, item_skill), 0, 0,
173  "ID of the skill required to use the item.; int"},
174  {"glow_radius", FIELDTYPE_INT8, offsetof(object, glow_radius), 0, 0,
175  "How much light the object emits.; int"},
176  {"move_status", FIELDTYPE_INT8, offsetof(object, move_status), 0, 0,
177  "Stage in move move.; int"},
178  {"move_type", FIELDTYPE_UINT8, offsetof(object, move_type), 0, 0,
179  "What kind of movement the object performs.; int"},
180 
181  {"anim_speed", FIELDTYPE_UINT8, offsetof(object, anim_speed), 0, 0,
182  "Object's animation speed.; int"},
183  {"behavior", FIELDTYPE_UINT8, offsetof(object, behavior), 0, 0,
184  "Monster/NPC behavior flags.; int"},
185  {"run_away", FIELDTYPE_UINT8, offsetof(object, run_away), 0, 0,
186  "Monster runs away if its HP goes below this percentage.; int"},
187 
188  {"layer", FIELDTYPE_UINT8, offsetof(object, layer), 0, 0,
189  "Layer the object is on.; int"},
190  {"sub_layer", FIELDTYPE_UINT8, offsetof(object, sub_layer), 0, 0,
191  "Sub-layer the object is on.; int"},
192  {"speed", FIELDTYPE_DOUBLE, offsetof(object, speed),
193  FIELDFLAG_PLAYER_READONLY, 0, "Speed of the object.; "
194  "float (player readonly)"},
195  {"speed_left", FIELDTYPE_DOUBLE, offsetof(object, speed_left), 0, 0,
196  "How much speed is left to spend this round.; float"},
197  {"weapon_speed", FIELDTYPE_DOUBLE, offsetof(object, weapon_speed), 0, 0,
198  "Weapon speed.; float"},
199  {"weapon_speed_left", FIELDTYPE_DOUBLE, offsetof(object, weapon_speed_left),
200  0, 0, "Weapon speed left this round.; float"},
201  {"exp", FIELDTYPE_INT64, offsetof(object, stats.exp), 0, 0,
202  "Experience of the object.; int"},
203  {"block", FIELDTYPE_UINT8, offsetof(object, block),
205  "Block attribute of the object.; int (player readonly)"},
206  {"absorb", FIELDTYPE_UINT8, offsetof(object, absorb),
208  "Absorb attribute of the object.; int (player readonly)"},
209 
210  {"hp", FIELDTYPE_INT32, offsetof(object, stats.hp), 0, 0,
211  "Object's current HP.; int"},
212  {"maxhp", FIELDTYPE_INT32, offsetof(object, stats.maxhp),
213  FIELDFLAG_PLAYER_READONLY, 0, "Maximum HP of the object.; "
214  "int (player readonly)"},
215  {"sp", FIELDTYPE_INT16, offsetof(object, stats.sp), 0, 0,
216  "Object's current mana points.; int"},
217  {"maxsp", FIELDTYPE_INT16, offsetof(object, stats.maxsp),
218  FIELDFLAG_PLAYER_READONLY, 0, "Maximum mana points of the object.; "
219  "int (player readonly)"},
220 
221  {"food", FIELDTYPE_INT16, offsetof(object, stats.food), 0, 0,
222  "How much food the object gives when eaten.; int"},
223  {"dam", FIELDTYPE_INT16, offsetof(object, stats.dam),
224  FIELDFLAG_PLAYER_READONLY, 0, "Damage of the object.; "
225  "int (player readonly)"},
226  {"wc", FIELDTYPE_INT16, offsetof(object, stats.wc),
227  FIELDFLAG_PLAYER_READONLY, 0, "WC attribute of the object.; "
228  "int (player readonly)"},
229  {"ac", FIELDTYPE_INT16, offsetof(object, stats.ac),
230  FIELDFLAG_PLAYER_READONLY, 0, "AC attribute of the object.; "
231  "int (player readonly)"},
232  {"wc_range", FIELDTYPE_UINT8, offsetof(object, stats.wc_range), 0, 0,
233  "WC range attribute.; int"},
234 
235  {"Str", FIELDTYPE_INT8, offsetof(object, stats.Str), FIELDFLAG_PLAYER_FIX,
236  0, "Strength of the object (or how much it gives when equipped).; "
237  "int"},
238  {"Dex", FIELDTYPE_INT8, offsetof(object, stats.Dex), FIELDFLAG_PLAYER_FIX,
239  0, "Dexterity of the object (or how much it gives when equipped).; "
240  "int"},
241  {"Con", FIELDTYPE_INT8, offsetof(object, stats.Con), FIELDFLAG_PLAYER_FIX,
242  0, "Constitution of the object (or how much it gives when "
243  "equipped).; int"},
244  {"Int", FIELDTYPE_INT8, offsetof(object, stats.Int), FIELDFLAG_PLAYER_FIX,
245  0, "Intelligence of the object (or how much it gives when "
246  "equipped).; int"},
247  {"Pow", FIELDTYPE_INT8, offsetof(object, stats.Pow), FIELDFLAG_PLAYER_FIX,
248  0, "Power of the object (or how much it gives when equipped).; "
249  "int"},
250 
251  {"arch", FIELDTYPE_ARCH, offsetof(object, arch), FIELDFLAG_READONLY, 0,
252  "Archetype of the object.; Atrinik.Archetype.Archetype"},
253  {"z", FIELDTYPE_INT16, offsetof(object, z), 0, 0,
254  "Z-position on the map (in pixels) for this object.; int"},
255  {"zoom_x", FIELDTYPE_INT16, offsetof(object, zoom_x), 0, 0,
256  "How much to zoom the object horizontally.; int"},
257  {"zoom_y", FIELDTYPE_INT16, offsetof(object, zoom_y), 0, 0,
258  "How much to zoom the object vertically.; int"},
259  {"rotate", FIELDTYPE_INT16, offsetof(object, rotate), 0, 0,
260  "Object's rotation value in degrees.; int"},
261  {"align", FIELDTYPE_INT16, offsetof(object, align), 0, 0,
262  "X align of the object on the actual rendered map, in "
263  "pixels.; int"},
264  {"alpha", FIELDTYPE_UINT8, offsetof(object, alpha), 0, 0,
265  "Alpha value of the object.; int"},
266  {"glow_speed", FIELDTYPE_UINT8, offsetof(object, glow_speed), 0, 0,
267  "Glowing animation speed.; int"},
268  {"face", FIELDTYPE_FACE, offsetof(object, face), 0, 0,
269  "The object's face in a tuple containing the face name as a "
270  "string, and the face ID as integer.\nThere are a few different "
271  "ways to set object's face. You can use the face name (obj.face = "
272  "'eyes.101'), the ID (obj.face = 1000), or the tuple returned by a "
273  "previous call to obj.face.; str or int or tuple"},
274  {"animation", FIELDTYPE_ANIMATION, offsetof(object, animation_id), 0, 0,
275  "Returns the object's animation in a tuple containing the "
276  "animation name as string, and the animation ID as integer.\nThere "
277  "are a few different ways to set object's animation. You can use "
278  "the animation name (obj.animation = 'raas'), the ID"
279  "(obj.animation = 100), or the tuple returned by a previous call "
280  "to obj.animation.; str or int or tuple"},
281  {"inv_animation", FIELDTYPE_ANIMATION, offsetof(object, inv_animation_id),
282  0, 0, "Returns the object's inventory animation in a tuple "
283  "containing the animation name as string, and the animation ID as"
284  "integer.\nThere are a few different ways to set object's inventory"
285  " animation. You can use the animation name (obj.inv_animation ="
286  "'raas'), the ID (obj.inv_animation = 100), or the tuple returned"
287  "by a previous call to obj.inv_animation.; str or int or tuple"},
288  {"other_arch", FIELDTYPE_ARCH, offsetof(object, other_arch), 0, 0,
289  "Archetype used for various things, depending on the object's "
290  "type.; Atrinik.Archetype.Archetype or None"},
291  {"connected", FIELDTYPE_CONNECTION, 0, 0, 0,
292  "Connection ID. Used to connect together buttons with gates, for "
293  "example.; int"},
294  {"randomitems", FIELDTYPE_TREASURELIST, offsetof(object, randomitems), 0,
295  0, "Treasure list the object generates.; str or None"},
296 };
297 
299 static char *doc_object_flag_names[NUM_FLAGS + 1] = {
300  "The object is asleep.",
301  "The object is confused.",
302  NULL,
303  "The object is scared.",
304  "The object is blind.",
305  "The object is invisible.",
306  "The object is ethereal.",
307  "The object is good aligned.",
308  "The object cannot be picked up.",
309  "The object generates an event when it's walked upon.",
310  "The object blocks passage.",
311  "The object is animated.",
312  "The object slows movement when moving through it.",
313  "The object is flying.",
314  "The object is a monster.",
315  "The object is friendly.",
316  NULL,
317  "The object has been applied before.",
318  "Automatically does something when loaded onto a map, such as shop floors "
319  " that generate random treasure.",
320  NULL,
321  "The object is neutrally aligned.",
322  "The object can see invisible objects.",
323  "The object can be pushed.",
324  "When the object is triggered, its connection state is immediately reset.",
325  "The object can turn.",
326  "The object generates an event when something walks off of it.",
327  "The object generates an event when it's flown upon.",
328  "The object generates an event when something flies off of it.",
329  "The object disappears when its :attr:`Atrinik.Object.Object.food` "
330  "attribute reaches zero.",
331  "The object is identified.",
332  "The object reflects off of surfaces.",
333  "The object is changing.",
334  "The object can split into parts.",
335  "The object hits back immediately upon being hit.",
336  "The object disappears when it's dropped.",
337  "The object blocks line of sight.",
338  "The object is undead.",
339  "The object can be stacked.",
340  "The object is not aggressive.",
341  "The object reflects missile projectiles.",
342  "The object reflects spell projectiles.",
343  "The object blocks magic use.",
344  "Used to disable object updates.",
345  "The object is evil aligned.",
346  NULL,
347  "The object runs away when its HP gets low enough.",
348  "The object allows passage for objects with "
349  ":attr:`~Atrinik.Object.Object.f_can_pass_thru` set, even if it "
350  "otherwise normally blocks passage.",
351  "The object can pass through blocked objects with "
352  ":attr:`~Atrinik.Object.Object.f_pass_thru` set.",
353  "Outdoor tile.",
354  "The object is unique.",
355  "The object cannot be dropped.",
356  "The object cannot be damaged.",
357  "The object can cast spells.",
358  NULL,
359  "The object requires two hands to wield.",
360  "The object can use bows.",
361  "The object can use armour.",
362  "The object can use weapons.",
363  "The object's connection is not activated when 'pushed'.",
364  "The object's connection is not activated when 'released'.",
365  "The object has a readied bow.",
366  "The object has (and/or gives) x-ray vision.",
367  NULL,
368  "The object is a floor.",
369  "The object saves a player's life once, then destructs itself.",
370  "The object is magical.",
371  NULL,
372  "The object will not move.",
373  "The object will move randomly.",
374  "The object will evaporate if it has no enemy.",
375  NULL,
376  "The object is in stealth and can pass more quietly past monsters, with "
377  "smaller chance of being spotted.",
378  NULL, NULL,
379  "The object is cursed.",
380  "The object is damned (*very* cursed).",
381  "The object can be built upon.",
382  "The object disallows PvP.",
383  NULL, NULL,
384  "The object can be thrown.",
385  NULL, NULL,
386  "The object is a male.",
387  "The object is a female.",
388  "The object is currently applied.",
389  "The object is locked and cannot be dropped.",
390  NULL, NULL, NULL,
391  "The object has a weapon ready.",
392  "The object won't give experience for using skills with it.",
393  NULL,
394  "The object can see even in darkness.",
395  "The object is a cauldron.",
396  "The object is a powder.",
397  NULL,
398  "The object hits once, then evaporates.",
399  "Always draw the object twice.",
400  "The object is in a rage and will attack friends as well.",
401  "The object will never attack.",
402  "The object cannot be killed, and enemies will not consider it for "
403  "attacking.",
404  "The object is a quest item.",
405  "The object is trapped.",
406  NULL, NULL, NULL, NULL, NULL, NULL,
407  "The object is a system object.",
408  "The object always teleports items exactly on the coordinates it leads to.",
409  "The object hasn't been paid for yet.",
410  "The object cannot be seen, ever.",
411  "The object makes its wearer invisible.",
412  "The object makes its wearer ethereal.",
413  "The object is a player.",
414  "The object is named.",
415  NULL,
416  "The object will not be teleported by teleporters.",
417  "The object will drop its corpse when killed.",
418  "The object will *always* drop its corpse when killed.",
419  "Only players can enter a tile that has an object with this flag set.",
420  NULL,
421  "The object is a one-drop item.",
422  "The object is permanently cursed.",
423  "The object is permanently damned.",
424  "The object is a closed door.",
425  "The object is a spell.",
426  "The object is a missile.",
427  "The object is shown based on its direction and the player's position. ",
428  "The object does even more damage to the race specified in the "
429  ":attr:`~Atrinik.Object.Object.slaying` attribute.",
430  NULL,
431  "The object was moved. Used internally.",
432  "The object won't be saved.",
433  NULL,
434 };
435 
437 static const char doc_Atrinik_Object_ActivateRune[] =
438 ".. method:: ActivateRune(who).\n\n"
439 "Activate a rune.\n\n"
440 ":param who: Who should be affected by the effects of the rune.\n"
441 ":type who: :class:`Atrinik.Object.Object`\n"
442 ":raises TypeError: If self is not of type :attr:`Atrinik.Type.RUNE`";
443 
449  PyObject *args)
450 {
451  Atrinik_Object *who;
452 
453  if (!PyArg_ParseTuple(args, "O!", &Atrinik_ObjectType, &who)) {
454  return NULL;
455  }
456 
457  OBJEXISTCHECK(self);
458  OBJEXISTCHECK(who);
459 
460  if (self->obj->type != RUNE) {
461  PyErr_SetString(PyExc_TypeError, "self is not a rune.");
462  return NULL;
463  }
464 
465  hooks->rune_spring(self->obj, who->obj);
466 
467  Py_INCREF(Py_None);
468  return Py_None;
469 }
470 
472 static const char doc_Atrinik_Object_TeleportTo[] =
473 ".. method:: TeleportTo(path, x=0, y=0).\n\n"
474 "Teleports the object to the specified coordinates on a map.\n\n"
475 ":param path: The map path.\n"
476 ":type path: str\n"
477 ":param x: X coordinate on the map.\n"
478 ":type x: int\n"
479 ":param y: Y coordinate on the map.\n"
480 ":type y: int";
481 
486 static PyObject *Atrinik_Object_TeleportTo(Atrinik_Object *self, PyObject *args,
487  PyObject *keywds)
488 {
489  static char *kwlist[] = {"path", "x", "y", NULL};
490  const char *path;
491  int x, y;
492  mapstruct *m;
493 
494  x = y = 0;
495 
496  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|ii", kwlist, &path, &x,
497  &y)) {
498  return NULL;
499  }
500 
501  OBJEXISTCHECK(self);
502  m = hooks->ready_map_name(path, NULL, 0);
503 
504  if (!m) {
505  PyErr_Format(AtrinikError, "Could not load map %s.", path);
506  return NULL;
507  }
508 
509  hooks->object_enter_map(self->obj, NULL, m, x, y, 1);
510 
511  Py_INCREF(Py_None);
512  return Py_None;
513 }
514 
516 static const char doc_Atrinik_Object_InsertInto[] =
517 ".. method:: InsertInto(where).\n\n"
518 "Inserts the object into some other object.\n\n"
519 ":param where: Where to insert the object.\n"
520 ":type where: :class:`Atrinik.Object.Object`\n"
521 ":returns: The inserted object, which may be different from the original (due"
522 "to merging, for example). None is returned on failure.\n"
523 ":rtype: class:`Atrinik.Object.Object` or None";
524 
529 static PyObject *Atrinik_Object_InsertInto(Atrinik_Object *self, PyObject *args)
530 {
531  Atrinik_Object *where;
532 
533  if (!PyArg_ParseTuple(args, "O!", &Atrinik_ObjectType, &where)) {
534  return NULL;
535  }
536 
537  OBJEXISTCHECK(self);
538  OBJEXISTCHECK(where);
539 
540  if (!QUERY_FLAG(self->obj, FLAG_REMOVED)) {
541  hooks->object_remove(self->obj, 0);
542  }
543 
544  object *ret = hooks->object_insert_into(self->obj, where->obj, 0);
545  if (ret == NULL) {
546  Py_INCREF(Py_None);
547  return Py_None;
548  }
549 
550  return wrap_object(ret);
551 }
552 
554 static const char doc_Atrinik_Object_Apply[] =
555 ".. method:: Apply(what, flags=Atrinik.APPLY_NORMAL).\n\n"
556 "Makes the object apply the specified object.\n\n"
557 ":param what: What object to apply.\n"
558 ":type what: :class:`Atrinik.Object.Object`\n"
559 ":param flags: Reasonable combination of :attr:`~Atrinik.APPLY_NORMAL`, "
560 ":attr:`~Atrinik.APPLY_ALWAYS`, :attr:`~Atrinik.APPLY_ALWAYS_UNAPPLY`, "
561 ":attr:`~Atrinik.APPLY_NO_MERGE`, :attr:`~Atrinik.APPLY_IGNORE_CURSE`, "
562 ":attr:`~Atrinik.APPLY_NO_EVENT`.\n"
563 ":type flags: int\n"
564 ":returns: One of OBJECT_METHOD_xxx, eg, :attr:`~Atrinik.OBJECT_METHOD_OK`.\n"
565 ":rtype: int";
566 
571 static PyObject *Atrinik_Object_Apply(Atrinik_Object *self, PyObject *args)
572 {
573  Atrinik_Object *what;
574  int flags = 0;
575 
576  if (!PyArg_ParseTuple(args, "O!|i", &Atrinik_ObjectType, &what, &flags)) {
577  return NULL;
578  }
579 
580  OBJEXISTCHECK(self);
581  OBJEXISTCHECK(what);
582 
583  return Py_BuildValue("i", hooks->manual_apply(self->obj, what->obj, flags));
584 }
585 
587 static const char doc_Atrinik_Object_Take[] =
588 ".. method:: Take(what).\n\n"
589 "Forces the object to pick up the specified object.\n\n"
590 ":param what: What object to pick up. Can be a string instead, in which case "
591 "it's equivalent of the /take command.\n"
592 ":type what: :class:`Atrinik.Object.Object` or str";
593 
598 static PyObject *Atrinik_Object_Take(Atrinik_Object *self, PyObject *what)
599 {
600  OBJEXISTCHECK(self);
601 
602  if (PyObject_TypeCheck(what, &Atrinik_ObjectType)) {
603  OBJEXISTCHECK((Atrinik_Object *) what);
604  hooks->pick_up(self->obj, ((Atrinik_Object *) what)->obj, 0);
605  } else if (PyString_Check(what)) {
606  hooks->command_take(self->obj, "take", PyString_AsString(what));
607  } else {
608  PyErr_SetString(PyExc_TypeError,
609  "Argument 'what' must be either Atrinik object or string.");
610  return NULL;
611  }
612 
613  Py_INCREF(Py_None);
614  return Py_None;
615 }
616 
618 static const char doc_Atrinik_Object_Drop[] =
619 ".. method:: Drop(what).\n\n"
620 "Forces the object to drop the specified object.\n\n"
621 ":param what: What object to drop. Can be a string instead, in which case "
622 "it's equivalent of the /drop command.\n"
623 ":type what: :class:`Atrinik.Object.Object` or str";
624 
629 static PyObject *Atrinik_Object_Drop(Atrinik_Object *self, PyObject *what)
630 {
631  OBJEXISTCHECK(self);
632 
633  if (PyObject_TypeCheck(what, &Atrinik_ObjectType)) {
634  OBJEXISTCHECK((Atrinik_Object *) what);
635  hooks->drop(self->obj, ((Atrinik_Object *) what)->obj, 0);
636  } else if (PyString_Check(what)) {
637  hooks->command_drop(self->obj, "drop", PyString_AsString(what));
638  } else {
639  PyErr_SetString(PyExc_TypeError,
640  "Argument 'what' must be either Atrinik object or string.");
641  return NULL;
642  }
643 
644  Py_INCREF(Py_None);
645  return Py_None;
646 }
647 
649 static const char doc_Atrinik_Object_Say[] =
650 ".. method:: Say(message).\n\n"
651 "Makes the object object say a message to everybody in range.\n\n"
652 ":param message: The message to say.\n"
653 ":type message: str";
654 
659 static PyObject *Atrinik_Object_Say(Atrinik_Object *self, PyObject *args)
660 {
661  const char *message;
662  char buf[HUGE_BUF];
663 
664  if (!PyArg_ParseTuple(args, "s", &message)) {
665  return NULL;
666  }
667 
668  OBJEXISTCHECK(self);
669 
670  char *name = hooks->stringbuffer_finish(hooks->object_get_name(self->obj,
671  NULL, NULL));
672  snprintf(VS(buf), "%s says: %s", name, message);
673  efree(name);
674  hooks->draw_info_map(CHAT_TYPE_GAME, NULL, COLOR_NAVY, self->obj->map,
675  self->obj->x, self->obj->y, MAP_INFO_NORMAL, NULL, NULL, buf);
676 
677  Py_INCREF(Py_None);
678  return Py_None;
679 }
680 
682 static const char doc_Atrinik_Object_GetGender[] =
683 ".. method:: GetGender().\n\n"
684 "Acquire object's gender.\n\n"
685 ":returns: One of the gender constants defined in :mod:`Atrinik.Gender`.\n"
686 ":rtype: int";
687 
693 {
694  OBJEXISTCHECK(self);
695 
696  return Py_BuildValue("i", hooks->object_get_gender(self->obj));
697 }
698 
700 static const char doc_Atrinik_Object_SetGender[] =
701 ".. method:: SetGender(gender).\n\n"
702 "Set object's gender.\n\n"
703 ":param gender: The gender to set. One of the gender constants defined in "
704 ":mod:`Atrinik.Gender`.\n"
705 ":type gender: int";
706 
711 static PyObject *Atrinik_Object_SetGender(Atrinik_Object *self, PyObject *args)
712 {
713  int gender;
714 
715  if (!PyArg_ParseTuple(args, "i", &gender)) {
716  return NULL;
717  }
718 
719  OBJEXISTCHECK(self);
720 
721  /* Set object to neuter */
722  CLEAR_FLAG(self->obj, FLAG_IS_MALE);
723  CLEAR_FLAG(self->obj, FLAG_IS_FEMALE);
724 
725  if (gender == GENDER_MALE || gender == GENDER_HERMAPHRODITE) {
726  SET_FLAG(self->obj, FLAG_IS_MALE);
727  }
728 
729  if (gender == GENDER_FEMALE || gender == GENDER_HERMAPHRODITE) {
730  SET_FLAG(self->obj, FLAG_IS_FEMALE);
731  }
732 
733  /* Update the player's client if object was a player. */
734  if (self->obj->type == PLAYER) {
735  CONTR(self->obj)->cs->ext_title_flag = 1;
736  }
737 
738  Py_INCREF(Py_None);
739  return Py_None;
740 }
741 
743 static const char doc_Atrinik_Object_Update[] =
744 ".. method:: Update().\n\n"
745 "Recalculate player's or monster's stats depending on equipment, forces, "
746 "skills, etc.";
747 
752 static PyObject *Atrinik_Object_Update(Atrinik_Object *self)
753 {
754  OBJEXISTCHECK(self);
755 
756  hooks->living_update(self->obj);
757 
758  Py_INCREF(Py_None);
759  return Py_None;
760 }
761 
763 static const char doc_Atrinik_Object_Hit[] =
764 ".. method:: Hit(target, damage).\n\n"
765 "Makes the object hit the target object for the specified amount of damage.\n\n"
766 ":param target: The target object to hit.\n"
767 ":type target: :class:`Atrinik.Object.Object`\n"
768 ":param damage: How much damage to deal. If -1, the target object will be "
769 "killed, otherwise the actual damage done is calculated depending on the "
770 "object's attack types, the target's protections, etc.\n"
771 ":type damage: int\n"
772 ":raises ValueError: If the target is not on a map or is not alive.";
773 
778 static PyObject *Atrinik_Object_Hit(Atrinik_Object *self, PyObject *args)
779 {
780  Atrinik_Object *target;
781  int damage;
782 
783  if (!PyArg_ParseTuple(args, "O!i", &Atrinik_ObjectType, &target, &damage)) {
784  return NULL;
785  }
786 
787  OBJEXISTCHECK(self);
788  OBJEXISTCHECK(target);
789 
790  /* Cannot kill objects that are not alive or on map. */
791  if (!target->obj->map || !IS_LIVE(target->obj)) {
792  PyErr_SetString(PyExc_ValueError, "Invalid object to hit/kill.");
793  return NULL;
794  }
795 
796  /* Kill the target. */
797  if (damage == -1) {
798  hooks->attack_kill(target->obj, self->obj);
799  } else {
800  /* Do damage. */
801  hooks->attack_hit(target->obj, self->obj, damage);
802  }
803 
804  Py_INCREF(Py_None);
805  return Py_None;
806 }
807 
809 static const char doc_Atrinik_Object_Cast[] =
810 ".. method:: Cast(spell, target=None, mode=-1, direction=0, option=None).\n\n"
811 "Cast the specified spell.\n\n"
812 ":param spell: ID of the spell to cast.\n"
813 ":type spell: int\n"
814 ":param target: Target object for spells that require a valid target.\n"
815 ":type target: :class:`Atrinik.Object.Object` or None\n"
816 ":param mode: One of the CAST_xxx constants defined in :mod:`Atrinik`, eg, "
817 ":attr:`~Atrinik.CAST_NORMAL`. If -1, will try to figure out the appropriate "
818 "mode automatically.\n"
819 ":type mode: int\n"
820 ":param direction: The direction to cast the spell in.\n"
821 ":type direction: int\n"
822 ":param option: Additional string option, required by some spells (create food "
823 "for example).\n"
824 ":type option: str or None";
825 
830 static PyObject *Atrinik_Object_Cast(Atrinik_Object *self, PyObject *args,
831  PyObject *keywds)
832 {
833  static char *kwlist[] = {"spell", "target", "mode", "direction", "option",
834  NULL};
835  Atrinik_Object *target = NULL;
836  int spell, direction = 0, mode = -1;
837  const char *option = NULL;
838 
839  if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|O!iis", kwlist, &spell,
840  &Atrinik_ObjectType, &target, &mode, &direction, &option)) {
841  return NULL;
842  }
843 
844  OBJEXISTCHECK(self);
845 
846  if (target != NULL) {
847  OBJEXISTCHECK(target);
848  }
849 
850  /* Figure out the mode automatically. */
851  if (mode == -1) {
852  if (self->obj->type != PLAYER) {
853  mode = CAST_NPC;
854  } else {
855  mode = CAST_NORMAL;
856  }
857  } else if (mode == CAST_NORMAL && target != NULL && target != self &&
858  self->obj->type != PLAYER) {
859  /* Ensure the mode is valid. */
860  mode = CAST_NPC;
861  }
862 
863  hooks->cast_spell(target != NULL ? target->obj : self->obj, self->obj,
864  direction, spell, 1, mode, option);
865 
866  Py_INCREF(Py_None);
867  return Py_None;
868 }
869 
871 static const char doc_Atrinik_Object_CreateForce[] =
872 ".. method:: CreateForce(name, seconds=0.0, expiration=0).\n\n"
873 "Create a force object in object's inventory.\n\n"
874 ":param name: ID of the force object.\n"
875 ":type name: str\n"
876 ":param seconds: If non-zero, the force will be removed after the specified "
877 "amount of seconds have passed.\n"
878 ":type seconds: float\n"
879 ":param expiration: If non-zero, the force will be removed after *expiration* / "
880 "0.02 ticks.\n"
881 ":type time: int\n"
882 ":returns: The created force object.\n"
883 ":rtype: :class:`Atrinik.Object.Object`";
884 
890  PyObject *args, PyObject *keywds)
891 {
892  static char *kwlist[] = {"name", "seconds", "expiration", NULL};
893  const char *name;
894  double seconds = 0.0;
895  int expiration = 0;
896 
897  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|di", kwlist, &name,
898  &seconds, &expiration)) {
899  return NULL;
900  }
901 
902  OBJEXISTCHECK(self);
903 
904  object *force = hooks->arch_get("force");
905 
906  if (expiration > 0) {
907  SET_FLAG(force, FLAG_IS_USED_UP);
908  force->stats.food = expiration;
909  force->speed = 0.02;
910  force->speed_left = -1.0;
911  } else if (seconds > 0.0) {
912  SET_FLAG(force, FLAG_IS_USED_UP);
913 
914  if (seconds > 10.0) {
915  double integral, fractional = modf(seconds / 10.0, &integral);
916  force->speed = 1.0 / (10.0 + fractional / integral * 10.0) /
917  MAX_TICKS;
918  force->stats.food = integral;
919  } else {
920  force->speed = 1.0 / seconds / MAX_TICKS;
921  }
922 
923  force->speed_left = -1.0;
924  } else {
925  force->speed = 0.0;
926  }
927 
928  hooks->object_update_speed(force);
929  FREE_AND_COPY_HASH(force->name, name);
930 
931  return wrap_object(hooks->object_insert_into(force, self->obj, 0));
932 }
933 
935 static const char doc_Atrinik_Object_CreateObject[] =
936 ".. method:: CreateObject(archname, nrof=1, value=-1, identified=True).\n\n"
937 "Creates a new object from archname and inserts it into the object.\n\n"
938 ":param archname: Name of the arch to create.\n"
939 ":type archname: str\n"
940 ":param nrof: Number of objects to create.\n"
941 ":type nrof: int\n"
942 ":param value: If not -1, will be used as value for the new object.\n"
943 ":type value: int\n"
944 ":param identified: If False, the object will not be identified.\n"
945 ":type identified: bool\n"
946 ":returns: The created (and inserted) object, None on failure.\n"
947 ":rtype: :class:`Atrinik.Object.Object` or None\n"
948 ":raises Atrinik.AtrinikError: If archname references an invalid "
949 "archetype.";
950 
956  PyObject *args, PyObject *keywds)
957 {
958  static char *kwlist[] = {"archname", "nrof", "value", "identified", NULL};
959  const char *archname;
960  uint32_t nrof = 1;
961  int64_t value = -1;
962  int identified = 1;
963  archetype_t *at;
964  object *tmp;
965 
966  if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|ILi", kwlist, &archname,
967  &nrof, &value, &identified)) {
968  return NULL;
969  }
970 
971  OBJEXISTCHECK(self);
972 
973  at = hooks->arch_find(archname);
974 
975  if (at == NULL) {
976  PyErr_Format(AtrinikError, "The archetype '%s' doesn't exist.",
977  archname);
978  return NULL;
979  }
980 
981  tmp = hooks->arch_to_object(at);
982 
983  if (value != -1) {
984  tmp->value = value;
985  }
986 
987  if (nrof > 1) {
988  tmp->nrof = nrof;
989  }
990 
991  if (identified) {
993  }
994 
995  tmp = hooks->object_insert_into(tmp, self->obj, 0);
996 
997  return wrap_object(tmp);
998 }
999 
1006 static object *object_find_object(object *tmp, int mode, shstr *archname,
1007  shstr *name, shstr *title, int type, PyObject *list, bool unpaid)
1008 {
1009  for ( ; tmp != NULL; tmp = tmp->below) {
1010  if ((archname == NULL || tmp->arch->name == archname) &&
1011  (name == NULL || tmp->name == name) &&
1012  (title == NULL || tmp->title == title) &&
1013  (type == -1 || tmp->type == type) &&
1014  (!unpaid || QUERY_FLAG(tmp, FLAG_UNPAID))) {
1015  if (list != NULL) {
1016  PyList_Append(list, wrap_object(tmp));
1017  } else {
1018  return tmp;
1019  }
1020  }
1021 
1022  if (tmp->inv != NULL && (mode == INVENTORY_ALL ||
1023  (mode == INVENTORY_CONTAINERS && tmp->type == CONTAINER))) {
1024  object *tmp2 = object_find_object(tmp->inv, mode, archname, name,
1025  title, type, list, unpaid);
1026  if (tmp2 != NULL) {
1027  return tmp2;
1028  }
1029  }
1030  }
1031 
1032  return NULL;
1033 }
1044  PyObject *args, PyObject *keywds, bool multiple)
1045 {
1046  static char *kwlist[] = {
1047  "mode", "archname", "name", "title", "type", "unpaid", NULL
1048  };
1049  uint8_t mode = INVENTORY_ONLY;
1050  int type = -1, unpaid = 0;
1051  const char *archname = NULL, *name = NULL, *title = NULL;
1052 
1053  if (!PyArg_ParseTupleAndKeywords(args, keywds, "|bzzzii", kwlist, &mode,
1054  &archname, &name, &title, &type, &unpaid)) {
1055  return NULL;
1056  }
1057 
1058  OBJEXISTCHECK(self);
1059 
1060  if (archname == NULL && name == NULL && title == NULL && type == -1 &&
1061  !unpaid) {
1062  PyErr_SetString(PyExc_ValueError, "No conditions to search for given.");
1063  return NULL;
1064  }
1065 
1066  PyObject *list = multiple ? PyList_New(0) : NULL;
1067 
1068  /* Try to find the strings we got from Python in the shared strings
1069  * library. If they are not found, it is impossible that the inventory
1070  * lookups succeed. */
1071 
1072  shstr *archname_sh = NULL;
1073  if (archname != NULL) {
1074  archname_sh = hooks->find_string(archname);
1075  if (archname_sh == NULL) {
1076  goto done;
1077  }
1078  }
1079 
1080  shstr *name_sh = NULL;
1081  if (name != NULL) {
1082  name_sh = hooks->find_string(name);
1083  if (name_sh == NULL) {
1084  goto done;
1085  }
1086  }
1087 
1088  shstr *title_sh = NULL;
1089  if (title != NULL) {
1090  title_sh = hooks->find_string(title);
1091  if (title_sh == NULL) {
1092  goto done;
1093  }
1094  }
1095 
1096  object *match = object_find_object(self->obj->inv, mode, archname_sh,
1097  name_sh, title_sh, type, list, unpaid);
1098  if (match != NULL) {
1099  return wrap_object(match);
1100  }
1101 
1102 done:
1103  if (multiple) {
1104  return list;
1105  }
1106 
1107  Py_INCREF(Py_None);
1108  return Py_None;
1109 }
1110 
1112 static const char doc_Atrinik_Object_FindObject[] =
1113 ".. method:: FindObject(mode=Atrinik.INVENTORY_ONLY, archname=None, name=None, "
1114 "title=None, type=-1, unpaid=False).\n\n"
1115 "Looks for a certain object in object's inventory.\n\n"
1116 ":param mode: How to search the inventory. One of the INVENTORY_xxx constants "
1117 "defined in the :mod:`Atrinik` module, eg, :attr:`~Atrinik.INVENTORY_ALL`.\n"
1118 ":type mode: int\n"
1119 ":param archname: Arch name of the object to search for. If None, can be any.\n"
1120 ":type archname: str or None\n"
1121 ":param name: Name of the object. If None, can be any.\n"
1122 ":type name: str or None\n"
1123 ":param title: Title of the object. If None, can be any.\n"
1124 ":type title: str or None\n"
1125 ":param type: Type of the object. If -1, can be any.\n"
1126 ":type type: int\n"
1127 ":param unpaid: Only match unpaid objects.\n"
1128 ":type unpaid: bool\n"
1129 ":returns: The object we wanted if found, None otherwise\n"
1130 ":rtype: :class:`Atrinik.Object.Object` or None\n"
1131 ":raises ValueError: If there were no conditions to search for.";
1132 
1137 static PyObject *Atrinik_Object_FindObject(Atrinik_Object *self, PyObject *args,
1138  PyObject *keywds)
1139 {
1140  return common_Atrinik_Object_FindObject(self, args, keywds, false);
1141 }
1142 
1144 static const char doc_Atrinik_Object_FindObjects[] =
1145 ".. method:: FindObjects(mode=Atrinik.INVENTORY_ONLY, archname=None, "
1146 "name=None, title=None, type=-1, unpaid=False).\n\n"
1147 "Looks for certain objects in object's inventory.\n\n"
1148 ":param mode: How to search the inventory. One of the INVENTORY_xxx constants "
1149 "defined in the :mod:`Atrinik` module, eg, :attr:`~Atrinik.INVENTORY_ALL`.\n"
1150 ":type mode: int\n"
1151 ":param archname: Arch name of the object to search for. If None, can be any.\n"
1152 ":type archname: str or None\n"
1153 ":param name: Name of the object. If None, can be any.\n"
1154 ":type name: str or None\n"
1155 ":param title: Title of the object. If None, can be any.\n"
1156 ":type title: str or None\n"
1157 ":param type: Type of the object. If -1, can be any.\n"
1158 ":type type: int\n"
1159 ":param unpaid: Only match unpaid objects.\n"
1160 ":type unpaid: bool\n"
1161 ":returns: List of all matching objects.\n"
1162 ":rtype: list of :class:`Atrinik.Object.Object`\n"
1163 ":raises ValueError: If there were no conditions to search for.";
1164 
1170  PyObject *args, PyObject *keywds)
1171 {
1172  return common_Atrinik_Object_FindObject(self, args, keywds, true);
1173 }
1174 
1176 static const char doc_Atrinik_Object_Remove[] =
1177 ".. method:: Remove().\n\n"
1178 "Takes the object out of whatever map or inventory it is in. The object can "
1179 "then be inserted or teleported somewhere else.\n\n"
1180 "Be careful when removing one of the objects involved in the event activation "
1181 "(such as the activator/event/etc). It is recommended you use :meth:"
1182 "`Atrinik.SetReturnValue(1)` or similar before the script exits if doing "
1183 "so.\n\n"
1184 ":raises Atrinik.AtrinikError: If the object is already removed.";
1185 
1190 static PyObject *Atrinik_Object_Remove(Atrinik_Object *self)
1191 {
1192  OBJEXISTCHECK(self);
1193 
1194  if (QUERY_FLAG(self->obj, FLAG_REMOVED)) {
1195  RAISE("Object has been removed already.");
1196  }
1197 
1198  hooks->object_remove(self->obj, 0);
1199 
1200  Py_INCREF(Py_None);
1201  return Py_None;
1202 }
1203 
1205 static const char doc_Atrinik_Object_Destroy[] =
1206 ".. method:: Destroy().\n\n"
1207 "Frees all data associated with the object.";
1208 
1214 {
1215  OBJEXISTCHECK(self);
1216 
1217  if (!QUERY_FLAG(self->obj, FLAG_REMOVED)) {
1218  hooks->object_remove(self->obj, 0);
1219  }
1220 
1221  hooks->object_destroy(self->obj);
1222 
1223  Py_INCREF(Py_None);
1224  return Py_None;
1225 }
1226 
1228 static const char doc_Atrinik_Object_SetPosition[] =
1229 ".. method:: SetPosition(x, y).\n\n"
1230 "Sets new position coordinates for the object.\n\nCannot be used to move "
1231 "objects out of containers, use :meth:`~Atrinik.Drop` or :meth:"
1232 "`~Atrinik.TeleportTo` for that.\n\n"
1233 ":param x: New X position on the same map.\n"
1234 ":type x: int\n"
1235 ":param y: New Y position on the same map.\n"
1236 ":type y: int";
1237 
1243  PyObject *args)
1244 {
1245  int x, y;
1246 
1247  if (!PyArg_ParseTuple(args, "ii", &x, &y)) {
1248  return NULL;
1249  }
1250 
1251  OBJEXISTCHECK(self);
1252 
1253  hooks->transfer_ob(self->obj, x, y, 0, NULL, NULL);
1254 
1255  Py_INCREF(Py_None);
1256  return Py_None;
1257 }
1258 
1260 static const char doc_Atrinik_Object_CastIdentify[] =
1261 ".. method:: CastIdentify(target, mode, marked=None).\n\n"
1262 "Cast identify on target.\n\n"
1263 ":param target: The target object.\n"
1264 ":type target: :class:`Atrinik.Object.Object`\n"
1265 ":param mode: One of IDENTIFY_xxx, eg, :attr:`~Atrinik.IDENTIFY_NORMAL`.\n"
1266 ":type mode: int\n"
1267 ":param marked: Marked item.\n"
1268 ":type marked: :class:`Atrinik.Object.Object` or None\n"
1269 ":raises Atrinik.AtrinikError: If *mode* is :attr:`~Atrinik.IDENTIFY_MARKED` "
1270 "but *marked* is None.";
1271 
1277  PyObject *args)
1278 {
1279  Atrinik_Object *target;
1280  PyObject *marked = NULL;
1281  object *ob = NULL;
1282  int mode;
1283 
1284  if (!PyArg_ParseTuple(args, "O!i|O", &Atrinik_ObjectType, &target, &mode,
1285  &marked)) {
1286  return NULL;
1287  }
1288 
1289  OBJEXISTCHECK(self);
1290  OBJEXISTCHECK(target);
1291 
1292  if (marked != NULL && marked != Py_None) {
1293  if (!PyObject_TypeCheck(marked, &Atrinik_ObjectType)) {
1294  PyErr_SetString(PyExc_TypeError, "Must be Atrinik.Object");
1295  return NULL;
1296  }
1297 
1298  OBJEXISTCHECK((Atrinik_Object *) marked);
1299  ob = ((Atrinik_Object *) marked)->obj;
1300  } else if (mode == IDENTIFY_MARKED) {
1301  PyErr_SetString(AtrinikError, "'marked' object must be set for "
1302  "IDENTIFY_MARKED mode");
1303  return NULL;
1304  }
1305 
1306  hooks->cast_identify(target->obj, self->obj->level, ob, mode);
1307  hooks->play_sound_map(self->obj->map, CMD_SOUND_EFFECT,
1308  hooks->spells[SP_IDENTIFY].sound, self->obj->x, self->obj->y, 0, 0);
1309 
1310  Py_INCREF(Py_None);
1311  return Py_None;
1312 }
1313 
1315 static const char doc_Atrinik_Object_Save[] =
1316 ".. method:: Save().\n\n"
1317 "Dump an object, as if it was being saved to map or player file. Useful for "
1318 "saving the object somewhere for loading later with :meth:"
1319 "`~Atrinik.Object.Object.Save`.\n\n"
1320 ":returns: Saved object.\n"
1321 ":rtype: str";
1322 
1327 static PyObject *Atrinik_Object_Save(Atrinik_Object *self)
1328 {
1329  OBJEXISTCHECK(self);
1330 
1331  StringBuffer *sb = hooks->stringbuffer_new();
1332  hooks->object_dump_rec(self->obj, sb);
1333  char *result = hooks->stringbuffer_finish(sb);
1334  PyObject *ret = Py_BuildValue("s", result);
1335  efree(result);
1336 
1337  return ret;
1338 }
1339 
1341 static const char doc_Atrinik_Object_GetCost[] =
1342 ".. method:: GetCost(flag=Atrinik.COST_TRUE).\n\n"
1343 "Get cost of an object in integer value.\n\n"
1344 ":param flag: One of the COST_xxx constants, eg, :attr:`~Atrinik.COST_BUY`.\n"
1345 ":type flag: int\n"
1346 ":returns: The cost of the item.\n"
1347 ":rtype: int";
1348 
1353 static PyObject *Atrinik_Object_GetCost(Atrinik_Object *self, PyObject *args)
1354 {
1355  int flag = COST_TRUE;
1356 
1357  if (!PyArg_ParseTuple(args, "|i", &flag)) {
1358  return NULL;
1359  }
1360 
1361  OBJEXISTCHECK(self);
1362 
1363  return Py_BuildValue("L", hooks->shop_get_cost(self->obj, flag));
1364 }
1365 
1367 static const char doc_Atrinik_Object_GetMoney[] =
1368 ".. method:: GetMoney().\n\n"
1369 "Get all the money the object is carrying as integer.\n\n"
1370 "Can only be used on player or container objects.\n\n"
1371 ":returns: The amount of money the object is carrying.\n"
1372 ":rtype: int";
1373 
1379 {
1380  OBJEXISTCHECK(self);
1381 
1382  return Py_BuildValue("L", hooks->shop_get_money(self->obj));
1383 }
1384 
1386 static const char doc_Atrinik_Object_PayAmount[] =
1387 ".. method:: PayAmount(value).\n\n"
1388 "Makes the object pay a specified amount of money.\n\n"
1389 ":param value: The amount of money to pay.\n"
1390 ":type value: int"
1391 ":returns: Whether the value was paid successfully (had enough money).\n"
1392 ":rtype: bool";
1393 
1398 static PyObject *Atrinik_Object_PayAmount(Atrinik_Object *self, PyObject *args)
1399 {
1400  int64_t value;
1401 
1402  if (!PyArg_ParseTuple(args, "L", &value)) {
1403  return NULL;
1404  }
1405 
1406  OBJEXISTCHECK(self);
1407 
1408  return Py_BuildBoolean(hooks->shop_pay(self->obj, value));
1409 }
1410 
1412 static const char doc_Atrinik_Object_Clone[] =
1413 ".. method:: Clone(inventory=True).\n\n"
1414 "Clone an object.\nGenerally, you should do something with the clone.\n\n"
1415 ":meth:`~Atrinik.Object.Object.TeleportTo` or "
1416 ":meth:`~Atrinik.Object.Object.InsertInto` are useful methods for that.\n\n"
1417 ":param inventory: Whether to clone the inventory of the object.\n"
1418 ":type inventory: bool\n"
1419 ":returns: Cloned object.\n"
1420 ":rtype: :class:`Atrinik.Object.Object`";
1421 
1426 static PyObject *Atrinik_Object_Clone(Atrinik_Object *self, PyObject *args)
1427 {
1428  int inventory = 1;
1429 
1430  if (!PyArg_ParseTuple(args, "|i", &inventory)) {
1431  return NULL;
1432  }
1433 
1434  OBJEXISTCHECK(self);
1435 
1436  object *clone;
1437  if (inventory) {
1438  clone = hooks->object_clone(self->obj);
1439  } else {
1440  clone = hooks->object_get();
1441  hooks->object_copy(clone, self->obj, false);
1442  }
1443 
1444  if (clone->type == PLAYER || QUERY_FLAG(clone, FLAG_IS_PLAYER)) {
1445  clone->type = MONSTER;
1446  CLEAR_FLAG(clone, FLAG_IS_PLAYER);
1447  }
1448 
1449  return wrap_object(clone);
1450 }
1451 
1453 static const char doc_Atrinik_Object_ReadKey[] =
1454 ".. method:: ReadKey(key).\n\n"
1455 "Get key value of an object.\n\n"
1456 ":param key: Key to look for.\n"
1457 ":type key: str\n"
1458 ":returns: Value for the key if found, None otherwise.\n"
1459 ":rtype: str or None";
1460 
1465 static PyObject *Atrinik_Object_ReadKey(Atrinik_Object *self, PyObject *args)
1466 {
1467  const char *key;
1468 
1469  if (!PyArg_ParseTuple(args, "s", &key)) {
1470  return NULL;
1471  }
1472 
1473  OBJEXISTCHECK(self);
1474 
1475  return Py_BuildValue("s", hooks->object_get_value(self->obj, key));
1476 }
1477 
1479 static const char doc_Atrinik_Object_WriteKey[] =
1480 ".. method:: WriteKey(key, value=None, add_key=True).\n\n"
1481 "Set the key value of an object.\n\n"
1482 ":param key: Key to set.\n"
1483 ":type key: str\n"
1484 ":param value: Value to set for the key. If None, will clear the key's value "
1485 "if the key is found.\n"
1486 ":type value: str or None\n"
1487 ":param add_key: Whether to add the key if it's not found in the object.\n"
1488 ":type add_key: bool\n"
1489 ":returns: Whether the operation was successful.\n"
1490 ":rtype: bool";
1491 
1496 static PyObject *Atrinik_Object_WriteKey(Atrinik_Object *self, PyObject *args)
1497 {
1498  const char *key, *value = NULL;
1499  int add_key = 1;
1500 
1501  if (!PyArg_ParseTuple(args, "s|si", &key, &value, &add_key)) {
1502  return NULL;
1503  }
1504 
1505  OBJEXISTCHECK(self);
1506 
1507  return Py_BuildBoolean(hooks->object_set_value(self->obj, key, value,
1508  add_key));
1509 }
1510 
1512 static const char doc_Atrinik_Object_GetName[] =
1513 ".. method:: GetName(caller=None).\n\n"
1514 "Acquire verbose textual representation of the object's name.\n\n"
1515 ":param caller: Who wants to see the name.\n"
1516 ":type caller: :class:`Atrinik.Object.Object` or None\n"
1517 ":returns: Full name of the object, including material, name, title, etc.\n"
1518 ":rtype: str";
1519 
1524 static PyObject *Atrinik_Object_GetName(Atrinik_Object *self, PyObject *args)
1525 {
1526  Atrinik_Object *ob = NULL;
1527 
1528  if (!PyArg_ParseTuple(args, "|O!", &Atrinik_ObjectType, &ob)) {
1529  return NULL;
1530  }
1531 
1532  OBJEXISTCHECK(self);
1533 
1534  if (ob != NULL) {
1535  OBJEXISTCHECK(ob);
1536  }
1537 
1538  char *name = hooks->stringbuffer_finish(hooks->object_get_short_name(
1539  self->obj, ob != NULL ? ob->obj : NULL, NULL));
1540  PyObject *ret = Py_BuildValue("s", name);
1541  efree(name);
1542  return ret;
1543 }
1544 
1546 static const char doc_Atrinik_Object_Controller[] =
1547 ".. method:: Controller().\n\n"
1548 "Get object's controller (the player).\n\n"
1549 ":returns: The controller if there is one, None otherwise.\n"
1550 ":rtype: :class:`Atrinik.Player.Player` or None\n"
1551 ":raises Atrinik.AtrinikError: If the object is not a player.";
1552 
1558 {
1559  OBJEXISTCHECK(self);
1560 
1561  if (self->obj->type != PLAYER) {
1562  RAISE("Can only be used on players.");
1563  }
1564 
1565  return wrap_player(CONTR(self->obj));
1566 }
1567 
1569 static const char doc_Atrinik_Object_Protection[] =
1570 ".. method:: Protection(protection).\n\n"
1571 "Get object's protection value for the given protection ID.\n\n"
1572 ":param protection: One of the ATNR_xxx constants, eg, "
1573 ":attr:`~Atrinik.ATNR_SLASH`.\n"
1574 ":type protection: int\n"
1575 ":returns: The protection value.\n"
1576 ":rtype: int\n"
1577 ":raises IndexError: If the protection ID is invalid.";
1578 
1583 static PyObject *Atrinik_Object_Protection(Atrinik_Object *self, PyObject *args)
1584 {
1585  int protection;
1586 
1587  if (!PyArg_ParseTuple(args, "i", &protection)) {
1588  return NULL;
1589  }
1590 
1591  OBJEXISTCHECK(self);
1592 
1593  if (protection < 0 || protection >= NROFATTACKS) {
1594  PyErr_SetString(PyExc_IndexError, "Protection ID is invalid.");
1595  return NULL;
1596  }
1597 
1598  return Py_BuildValue("b", self->obj->protection[protection]);
1599 }
1600 
1603 ".. method:: SetProtection(protection, value).\n\n"
1604 "Set object's protection value for the given protection ID.\n\n"
1605 ":param protection: One of the ATNR_xxx constants, eg, "
1606 ":attr:`~Atrinik.ATNR_SLASH`.\n"
1607 ":type protection: int\n"
1608 ":param value: The value to set.\n"
1609 ":type value: int\n"
1610 ":raises IndexError: If the protection ID is invalid.\n"
1611 ":raises OverflowError: If the value to set is not in valid range.";
1612 
1618  PyObject *args)
1619 {
1620  int protection, value;
1621 
1622  if (!PyArg_ParseTuple(args, "ii", &protection, &value)) {
1623  return NULL;
1624  }
1625 
1626  PY_CHECK_INT(value, INT8_MIN, INT8_MAX);
1627  OBJEXISTCHECK(self);
1628 
1629  if (protection < 0 || protection >= NROFATTACKS) {
1630  PyErr_SetString(PyExc_IndexError, "Protection ID is invalid.");
1631  return NULL;
1632  }
1633 
1634  self->obj->protection[protection] = (int8_t) value;
1635 
1636  Py_INCREF(Py_None);
1637  return Py_None;
1638 }
1639 
1641 static const char doc_Atrinik_Object_Attack[] =
1642 ".. method:: Attack(attack).\n\n"
1643 "Get object's attack value for the given attack ID.\n\n"
1644 ":param attack: One of the ATNR_xxx constants, eg, "
1645 ":attr:`~Atrinik.ATNR_SLASH`.\n"
1646 ":type attack: int\n"
1647 ":returns: The attack value.\n"
1648 ":rtype: int\n"
1649 ":raises IndexError: If the attack ID is invalid.";
1650 
1655 static PyObject *Atrinik_Object_Attack(Atrinik_Object *self, PyObject *args)
1656 {
1657  int attack;
1658 
1659  if (!PyArg_ParseTuple(args, "i", &attack)) {
1660  return NULL;
1661  }
1662 
1663  OBJEXISTCHECK(self);
1664 
1665  if (attack < 0 || attack >= NROFATTACKS) {
1666  PyErr_SetString(PyExc_IndexError, "Attack ID is invalid.");
1667  return NULL;
1668  }
1669 
1670  return Py_BuildValue("B", self->obj->attack[attack]);
1671 }
1672 
1674 static const char doc_Atrinik_Object_SetAttack[] =
1675 ".. method:: SetAttack(attack, value).\n\n"
1676 "Set object's attack value for the given attack ID.\n\n"
1677 ":param attack: One of the ATNR_xxx constants, eg, "
1678 ":attr:`~Atrinik.ATNR_SLASH`.\n"
1679 ":type attack: int\n"
1680 ":param value: The value to set.\n"
1681 ":type value: int\n"
1682 ":raises IndexError: If the attack ID is invalid.\n"
1683 ":raises OverflowError: If the value to set is not in valid range.";
1684 
1689 static PyObject *Atrinik_Object_SetAttack(Atrinik_Object *self, PyObject *args)
1690 {
1691  int attack, value;
1692 
1693  if (!PyArg_ParseTuple(args, "ii", &attack, &value)) {
1694  return NULL;
1695  }
1696 
1697  PY_CHECK_INT(value, 0, UINT8_MAX);
1698  OBJEXISTCHECK(self);
1699 
1700  if (attack < 0 || attack >= NROFATTACKS) {
1701  PyErr_SetString(PyExc_IndexError, "Attack ID is invalid.");
1702  return NULL;
1703  }
1704 
1705  self->obj->attack[attack] = (uint8_t) value;
1706 
1707  Py_INCREF(Py_None);
1708  return Py_None;
1709 }
1710 
1712 static const char doc_Atrinik_Object_Decrease[] =
1713 ".. method:: Decrease(num=1).\n\n"
1714 "Decreases an object, removing it if there's nothing left to decrease.\n\n"
1715 ":param num: How much to decrease the object by.\n"
1716 ":type num: int\n"
1717 ":returns: The object if something is left, None otherwise.\n"
1718 ":rtype: :class:`Atrinik.Object.Object` or None";
1719 
1724 static PyObject *Atrinik_Object_Decrease(Atrinik_Object *self, PyObject *args)
1725 {
1726  uint32_t num = 1;
1727 
1728  if (!PyArg_ParseTuple(args, "|I", &num)) {
1729  return NULL;
1730  }
1731 
1732  OBJEXISTCHECK(self);
1733 
1734  return wrap_object(hooks->object_decrease(self->obj, num));
1735 }
1736 
1739 ".. method:: SquaresAround(range, type=Atrinik.AROUND_ALL, beyond=False, "
1740 "callable=None).\n\n"
1741 "Looks around the specified object and returns a list of tuples containing the "
1742 "squares around it in a specified range. The tuples have a format of "
1743 "**(map, x, y)**.\n\n"
1744 "Example that ignores walls and floors with grass::\n\n"
1745 " from Atrinik import *\n"
1746 " activator = WhoIsActivator()\n"
1747 " def cmp_squares(m, x, y, obj):\n"
1748 " try:\n"
1749 " return m.GetLayer(x, y, LAYER_FLOOR)[0].name == \"grass\"\n"
1750 " # Exception was raised; ignore it, as it probably\n"
1751 " # means there is no floor.\n"
1752 " except:\n"
1753 " return False\n\n"
1754 " for (m, x, y) in activator.SquaresAround(1, type=AROUND_WALL,\n"
1755 " callable=cmp_squares):\n"
1756 " for ob in m.GetLayer(x, y, LAYER_FLOOR):\n"
1757 " print(ob)\n"
1758 "\n\n"
1759 ":param range: Range around which to look at the squares. Must be higher "
1760 "than 0.\n"
1761 ":type range: int\n"
1762 ":param type: One of or a combination of the AROUND_xxx constants, eg, "
1763 ":attr:`~Atrinik.AROUND_WALL`.\n"
1764 ":type type: int\n"
1765 ":param beyond: If True and one of checks from *type* parameter matches, all "
1766 "squares beyond the one being checked will be ignored as well (think line of "
1767 "sight).\n"
1768 ":type beyond: bool\n"
1769 ":param callable: Defines function to call for comparisons. The function "
1770 "should have parameters in the order of **map, x, y, obj** where map is the "
1771 "map, x/y are the coordinates and obj is the object that :meth:"
1772 "`~Atrinik.Object.Object.SquaresAround()` was called for. The function should "
1773 "return True if the square should be considered ignored, False otherwise. "
1774 "*type* being :attr:`~Atrinik.AROUND_ALL` takes no effect if this is set, but "
1775 "it can be combined with the other AROUND_xxx constants.\n"
1776 ":type callable: collections.Callable or None\n"
1777 ":returns: A list containing tuples of the squares.\n"
1778 ":rtype: list of tuple";
1779 
1785  PyObject *args, PyObject *keywds)
1786 {
1787  uint8_t range, type = AROUND_ALL;
1788  static char *kwlist[] = {"range", "type", "beyond", "callable", NULL};
1789  int beyond = 0;
1790  PyObject *callable = NULL;
1791 
1792  if (!PyArg_ParseTupleAndKeywords(args, keywds, "b|biO", kwlist, &range,
1793  &type, &beyond, &callable)) {
1794  return NULL;
1795  }
1796 
1797  OBJEXISTCHECK(self);
1798 
1799  if (range == 0) {
1800  PyErr_SetString(PyExc_ValueError, "'range' must be higher than 0.");
1801  return NULL;
1802  }
1803 
1804  if (callable != NULL && !PyCallable_Check(callable)) {
1805  PyErr_SetString(PyExc_TypeError,
1806  "Argument 'callable' must be callable.");
1807  return NULL;
1808  }
1809 
1810 #define SQUARES_AROUND_SKIP(m, x, y) \
1811  ((type & AROUND_BLOCKSVIEW && GET_MAP_FLAGS(m, x, y) & P_BLOCKSVIEW) || \
1812  (type & AROUND_PLAYER_ONLY && GET_MAP_FLAGS(m, x, y) & P_PLAYER_ONLY) || \
1813  (type & AROUND_WALL && hooks->wall(m, x, y)) || (callable != NULL && \
1814  python_call_int(callable, Py_BuildValue("(OiiO)", wrap_map(m), x, y, \
1815  wrap_object(self->obj)))))
1816 
1817  PyObject *list = PyList_New(0);
1818 
1819  /* Go through the squares in the specified range. */
1820  for (int i = -range; i <= range; i++) {
1821  for (int j = -range; j <= range; j++) {
1822  int xt = self->obj->x + i;
1823  int yt = self->obj->y + j;
1824 
1825  /* Skip ourselves. */
1826  if (xt == self->obj->x && yt == self->obj->y) {
1827  continue;
1828  }
1829 
1830  mapstruct *m = hooks->get_map_from_coord(self->obj->map, &xt, &yt);
1831  if (m == NULL) {
1832  continue;
1833  }
1834 
1835  if (type == AROUND_ALL && callable == NULL) {
1836  /* We want all squares. */
1837  SQUARES_AROUND_ADD(m, xt, yt);
1838  } else if (beyond) {
1839  /* Only those that are not blocked by view, or beyond a wall,
1840  * etc, so use the Bresenham algorithm. */
1841 
1842  mapstruct *m2 = self->obj->map;
1843  int xt2 = self->obj->x;
1844  int yt2 = self->obj->y;
1845 
1846  rv_vector rv;
1847  if (!hooks->get_rangevector_from_mapcoords(m2, xt2, yt2, m, xt,
1848  yt, &rv, RV_NO_DISTANCE)) {
1849  continue;
1850  }
1851 
1852  int fraction, dx2, dy2, stepx, stepy;
1853  BRESENHAM_INIT(rv.distance_x, rv.distance_y, fraction, stepx,
1854  stepy, dx2, dy2);
1855 
1856  for ( ; ; ) {
1857  BRESENHAM_STEP(xt2, yt2, fraction, stepx, stepy, dx2, dy2);
1858  m2 = hooks->get_map_from_coord(m2, &xt2, &yt2);
1859 
1860  if (m2 == NULL || SQUARES_AROUND_SKIP(m2, xt2, yt2)) {
1861  break;
1862  }
1863 
1864  if (m2 == m && xt2 == xt && yt2 == yt) {
1865  SQUARES_AROUND_ADD(m, xt, yt);
1866  break;
1867  }
1868  }
1869  } else {
1870  /* We only want to ignore squares that either block view, or
1871  * have a wall, etc, but not any squares behind them. */
1872  if (SQUARES_AROUND_SKIP(m, xt, yt)) {
1873  continue;
1874  }
1875 
1876  SQUARES_AROUND_ADD(m, xt, yt);
1877  }
1878  }
1879  }
1880 
1881 #undef SQUARES_AROUND_SKIP
1882 
1883  return list;
1884 }
1885 
1888 ".. method:: GetRangeVector(to, flags=0).\n\n"
1889 "Get the distance and direction from one object to another.\n\n"
1890 ":param to: Object to which the distance is calculated.\n"
1891 ":type to: :class:`Atrinik.Object.Object`\n"
1892 ":param flags: One or a combination of RV_xxx, eg, :attr:"
1893 "`~Atrinik.RV_MANHATTAN_DISTANCE`\n"
1894 ":type flags: int\n"
1895 ":returns: None if the distance couldn't be calculated, otherwise a tuple "
1896 "containing:\n\n"
1897 " * Direction *object* should head to reach *to*, eg, \n"
1898 " :attr:`~Atrinik.NORTH`\n"
1899 " * Distance between *object* and *to*.\n"
1900 " * X distance.\n"
1901 " * Y distance.\n"
1902 " * Part of the *object* that is closest.\n"
1903 ":rtype: tuple or None";
1904 
1910  PyObject *args)
1911 {
1912  Atrinik_Object *to;
1913  int flags = 0;
1914 
1915  if (!PyArg_ParseTuple(args, "O!|i", &Atrinik_ObjectType, &to, &flags)) {
1916  return NULL;
1917  }
1918 
1919  OBJEXISTCHECK(self);
1920  OBJEXISTCHECK(to);
1921 
1922  rv_vector rv;
1923  if (!hooks->get_rangevector(self->obj, to->obj, &rv, flags)) {
1924  Py_INCREF(Py_None);
1925  return Py_None;
1926  }
1927 
1928  PyObject *tuple = PyTuple_New(5);
1929  PyTuple_SET_ITEM(tuple, 0, Py_BuildValue("i", rv.direction));
1930  PyTuple_SET_ITEM(tuple, 1, Py_BuildValue("i", rv.distance));
1931  PyTuple_SET_ITEM(tuple, 2, Py_BuildValue("i", rv.distance_x));
1932  PyTuple_SET_ITEM(tuple, 3, Py_BuildValue("i", rv.distance_y));
1933  PyTuple_SET_ITEM(tuple, 4, wrap_object(rv.part));
1934 
1935  return tuple;
1936 }
1937 
1940 ".. method:: CreateTreasure(treasure=None, level=0, flags=0, "
1941 "a_chance=Atrinik.TREASURE_ARTIFACT_CHANCE).\n\n"
1942 "Create treasure inside (or below, if :attr:`~Atrinik.GT_ENVIRONMENT` flag was "
1943 "set) the object.\n\n"
1944 ":param treasure: Treasure list name to generate. If None, will try to "
1945 "generate treasure based on the object's randomitems.\n"
1946 ":type treasure: str or None\n"
1947 ":param level: Level of the generated items. If 0, will try to guess the level "
1948 "to use based on the object's level or the difficulty value of the map the "
1949 "object is on. If neither is applicable, will use :attr:`~Atrinik.MAXLEVEL`.\n"
1950 ":type level: int\n"
1951 ":param flags: One or a combination of GT_xxx, eg, :attr:"
1952 "`~Atrinik.GT_ENVIRONMENT`\n"
1953 ":type flags: int\n"
1954 ":param a_chance: Chance for the treasure to become artifact, if possible. A "
1955 "value of 0 will disable any chance for artifacts.\n"
1956 ":type a_chance: int\n"
1957 ":raises ValueError: If treasure is not valid.";
1958 
1964  PyObject *args, PyObject *keywds)
1965 {
1966  static char *kwlist[] = {"treasure", "level", "flags", "a_chance", NULL};
1967  const char *treasure_name = NULL;
1968  int level = 0, flags = 0, a_chance = TREASURE_ARTIFACT_CHANCE;
1969  treasure_list_t *t;
1970 
1971  if (!PyArg_ParseTupleAndKeywords(args, keywds, "|ziii", kwlist,
1972  &treasure_name, &level, &flags, &a_chance)) {
1973  return NULL;
1974  }
1975 
1976  OBJEXISTCHECK(self);
1977 
1978  /* Figure out the treasure list. */
1979  if (treasure_name != NULL) {
1980  t = hooks->treasure_list_find(treasure_name);
1981  } else {
1982  t = self->obj->randomitems;
1983  }
1984 
1985  /* Invalid treasure list. */
1986  if (t == NULL) {
1987  if (treasure_name) {
1988  PyErr_Format(PyExc_ValueError, "'%s' is not a valid treasure list.",
1989  treasure_name);
1990  } else {
1991  PyErr_SetString(PyExc_ValueError, "Object has no treasure list.");
1992  }
1993 
1994  return NULL;
1995  }
1996 
1997  /* Figure out the level if none was given. */
1998  if (level == 0) {
1999  /* Try the object's level first. */
2000  if (self->obj->level != 0) {
2001  level = self->obj->level;
2002  } else if (self->obj->map != NULL) {
2003  /* Otherwise the map's difficulty. */
2004  level = self->obj->map->difficulty;
2005  } else {
2006  /* Default to MAXLEVEL. */
2007  level = MAXLEVEL;
2008  }
2009  }
2010 
2011  /* Create the treasure. */
2012  hooks->treasure_generate(t, self->obj, level, flags);
2013 
2014  Py_INCREF(Py_None);
2015  return Py_None;
2016 }
2017 
2019 static const char doc_Atrinik_Object_Move[] =
2020 ".. method:: Move(direction).\n\n"
2021 "Move the object in the specified direction. The object must have the correct "
2022 "(combination of) :attr:`~Atrinik.Object.Object.terrain_flag` set in order to "
2023 "able to move onto the new square.\n\n"
2024 ":param direction: Direction to move into, eg, :attr:`~Atrinik.EAST`.\n"
2025 ":type direction: int\n"
2026 ":returns: 0 if the object is not able to move to the desired space, -1 if the "
2027 "object was not able to move there yet but some sort of action was performed "
2028 "that might allow us to move there (door opening for example), direction "
2029 "number that the object ended up moving in otherwise.\n"
2030 ":rtype: int";
2031 
2036 static PyObject *Atrinik_Object_Move(Atrinik_Object *self, PyObject *args)
2037 {
2038  int direction;
2039 
2040  if (!PyArg_ParseTuple(args, "i", &direction)) {
2041  return NULL;
2042  }
2043 
2044  OBJEXISTCHECK(self);
2045 
2046  if (self->obj->map == NULL) {
2047  PyErr_SetString(AtrinikError, "Object not on map.");
2048  return NULL;
2049  }
2050 
2051  return Py_BuildValue("i", hooks->move_ob(self->obj, direction, self->obj));
2052 }
2053 
2056 ".. method:: ConnectionTrigger(push=True, button=False).\n\n"
2057 "Triggers the object's connection, if any.\n\n"
2058 ":param push: If true, send a 'push' signal; 'release' signal otherwise.\n"
2059 ":type push: bool\n"
2060 ":param button: If true, handle the connection like a button.\n"
2061 ":type button: bool";
2062 
2068  PyObject *args, PyObject *keywds)
2069 {
2070  static char *kwlist[] = {"push", "button", NULL};
2071  int push = 1, button = 0;
2072 
2073  if (!PyArg_ParseTupleAndKeywords(args, keywds, "|ii", kwlist, &push,
2074  &button)) {
2075  return NULL;
2076  }
2077 
2078  OBJEXISTCHECK(self);
2079 
2080  if (button) {
2081  hooks->connection_trigger_button(self->obj, push);
2082  } else {
2083  hooks->connection_trigger(self->obj, push);
2084  }
2085 
2086  Py_INCREF(Py_None);
2087  return Py_None;
2088 }
2089 
2091 static const char doc_Atrinik_Object_Artificate[] =
2092 ".. method:: Artificate(name).\n\n"
2093 "Copies artifact abilities to the specified object.\n\n"
2094 ":param name: Name of the artifact to copy abilities from.\n"
2095 ":type name: str\n"
2096 ":raises Atrinik.AtrinikError: If the object already has artifact "
2097 "abilities.\n"
2098 ":raises Atrinik.AtrinikError: If the object's type doesn't match "
2099 "any artifact list.\n"
2100 ":raises Atrinik.AtrinikError: If the artifact name is invalid.";
2101 
2106 static PyObject *Atrinik_Object_Artificate(Atrinik_Object *self, PyObject *args)
2107 {
2108  const char *name = NULL;
2109 
2110  if (!PyArg_ParseTuple(args, "s", &name)) {
2111  return NULL;
2112  }
2113 
2114  OBJEXISTCHECK(self);
2115 
2116  if (self->obj->artifact) {
2117  PyErr_SetString(AtrinikError, "Object already has artifact abilities.");
2118  return NULL;
2119  }
2120 
2121  artifact_list_t *artlist = hooks->artifact_list_find(
2122  self->obj->arch->clone.type);
2123  if (artlist == NULL) {
2124  PyErr_SetString(AtrinikError,
2125  "No artifact list matching the object's type.");
2126  return NULL;
2127  }
2128 
2129  for (artifact_t *art = artlist->items; art != NULL; art = art->next) {
2130  if (strcmp(art->def_at->name, name) == 0) {
2131  hooks->artifact_change_object(art, self->obj);
2132  Py_INCREF(Py_None);
2133  return Py_None;
2134  }
2135  }
2136 
2137  PyErr_SetString(AtrinikError, "Invalid artifact name.");
2138  return NULL;
2139 }
2140 
2142 static const char doc_Atrinik_Object_Load[] =
2143 ".. method:: Load(lines).\n\n"
2144 "Load archetype-like attribute/value pairs into the object. For example::\n\n"
2145 " '''\n"
2146 " attack_protect 20\n"
2147 " dam 10\n"
2148 " '''"
2149 "\n\n"
2150 ":param lines: Lines to load into the object.\n"
2151 ":type lines: str";
2152 
2157 static PyObject *Atrinik_Object_Load(Atrinik_Object *self, PyObject *args)
2158 {
2159  const char *lines;
2160 
2161  if (!PyArg_ParseTuple(args, "s", &lines)) {
2162  return NULL;
2163  }
2164 
2165  hooks->set_variable(self->obj, lines);
2166 
2167  Py_INCREF(Py_None);
2168  return Py_None;
2169 }
2170 
2172 static const char doc_Atrinik_Object_GetPacket[] =
2173 ".. method:: GetPacket(pl, flags=0).\n\n"
2174 "Constructs packet data about the object, suitable for "
2175 ":meth:`Atrinik.Player.Player.SendPacket`.\n\n"
2176 ":param pl: Player that will receive the item data.\n"
2177 ":type pl: :class:`Atrinik.Player.Player`\n"
2178 ":param flags: A combination of UPD_xxx flags, eg, :attr:`Atrinik.UPD_FACE`.\n"
2179 ":type flags: int\n"
2180 ":returns: A tuple containing the format specifier and the actual data list.\n"
2181 ":rtype: tuple";
2182 
2187 static PyObject *Atrinik_Object_GetPacket(Atrinik_Object *self, PyObject *args)
2188 {
2189  Atrinik_Player *pl;
2190  uint16_t flags = 0;
2191 
2192  if (!PyArg_ParseTuple(args, "O!|H", &Atrinik_PlayerType, &pl, &flags)) {
2193  return NULL;
2194  }
2195 
2196  OBJEXISTCHECK(self);
2197 
2198  packet_struct *packet = hooks->packet_new(0, 128, 128);
2199  hooks->add_object_to_packet(packet, self->obj, pl->pl->ob,
2200  CMD_APPLY_ACTION_NORMAL, flags, 0);
2201  PyObject *data = PyBytes_FromStringAndSize((const char *) packet->data,
2202  packet->len);
2203  hooks->packet_free(packet);
2204 
2205  PyObject *fmt = Py_BuildValue("s", "Hx");
2206  PyObject *list = PyList_New(2);
2207  PyList_SetItem(list, 0, Py_BuildValue("H", flags));
2208  PyList_SetItem(list, 1, data);
2209 
2210  PyObject *tuple = PyTuple_New(2);
2211  PyTuple_SET_ITEM(tuple, 0, fmt);
2212  PyTuple_SET_ITEM(tuple, 1, list);
2213 
2214  return tuple;
2215 }
2216 
2219 ".. method:: FactionIsFriend(faction).\n\n"
2220 "Checks whether the object is a friend of the specified faction.\n\n"
2221 ":param faction: Name of the faction to check.\n"
2222 ":type faction: str\n"
2223 ":returns: Whether the object is a friend of the faction.\n"
2224 ":rtype: bool\n"
2225 ":raises Atrinik.AtrinikError: If the specified faction doesn't exist.";
2226 
2232  PyObject *args)
2233 {
2234  const char *name;
2235 
2236  if (!PyArg_ParseTuple(args, "s", &name)) {
2237  return NULL;
2238  }
2239 
2240  OBJEXISTCHECK(self);
2241 
2242  shstr *sh_name = hooks->find_string(name);
2243  if (sh_name == NULL) {
2244  PyErr_Format(AtrinikError, "No such faction: %s", name);
2245  return NULL;
2246  }
2247 
2248  faction_t faction = hooks->faction_find(sh_name);
2249  if (faction == NULL) {
2250  PyErr_Format(AtrinikError, "No such faction: %s", name);
2251  return NULL;
2252  }
2253 
2254  return Py_BuildBoolean(hooks->faction_is_friend(faction, self->obj));
2255 }
2256 
2258 static PyMethodDef methods[] = {
2259  {"ActivateRune", (PyCFunction) Atrinik_Object_ActivateRune, METH_VARARGS,
2261  {"TeleportTo", (PyCFunction) Atrinik_Object_TeleportTo,
2262  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_TeleportTo},
2263  {"InsertInto", (PyCFunction) Atrinik_Object_InsertInto, METH_VARARGS,
2265  {"Apply", (PyCFunction) Atrinik_Object_Apply, METH_VARARGS,
2267  {"Take", (PyCFunction) Atrinik_Object_Take, METH_O,
2269  {"Drop", (PyCFunction) Atrinik_Object_Drop, METH_O,
2271  {"Say", (PyCFunction) Atrinik_Object_Say, METH_VARARGS,
2273  {"GetGender", (PyCFunction) Atrinik_Object_GetGender, METH_NOARGS,
2275  {"SetGender", (PyCFunction) Atrinik_Object_SetGender, METH_VARARGS,
2277  {"Update", (PyCFunction) Atrinik_Object_Update, METH_NOARGS,
2279  {"Hit", (PyCFunction) Atrinik_Object_Hit, METH_VARARGS,
2281  {"Cast", (PyCFunction) Atrinik_Object_Cast, METH_VARARGS | METH_KEYWORDS,
2283  {"CreateForce", (PyCFunction) Atrinik_Object_CreateForce,
2284  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_CreateForce},
2285  {"CreateObject", (PyCFunction) Atrinik_Object_CreateObject,
2286  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_CreateObject},
2287  {"FindObject", (PyCFunction) Atrinik_Object_FindObject,
2288  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_FindObject},
2289  {"FindObjects", (PyCFunction) Atrinik_Object_FindObjects,
2290  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_FindObjects},
2291  {"Remove", (PyCFunction) Atrinik_Object_Remove, METH_NOARGS,
2292  doc_Atrinik_Object_Remove},
2293  {"Destroy", (PyCFunction) Atrinik_Object_Destroy, METH_NOARGS,
2294  doc_Atrinik_Object_Destroy},
2295  {"SetPosition", (PyCFunction) Atrinik_Object_SetPosition, METH_VARARGS,
2296  doc_Atrinik_Object_SetPosition},
2297  {"CastIdentify", (PyCFunction) Atrinik_Object_CastIdentify, METH_VARARGS,
2298  doc_Atrinik_Object_CastIdentify},
2299  {"Save", (PyCFunction) Atrinik_Object_Save, METH_NOARGS,
2300  doc_Atrinik_Object_Save},
2301  {"GetCost", (PyCFunction) Atrinik_Object_GetCost, METH_VARARGS,
2302  doc_Atrinik_Object_GetCost},
2303  {"GetMoney", (PyCFunction) Atrinik_Object_GetMoney, METH_NOARGS,
2304  doc_Atrinik_Object_GetMoney},
2305  {"PayAmount", (PyCFunction) Atrinik_Object_PayAmount, METH_VARARGS,
2306  doc_Atrinik_Object_PayAmount},
2307  {"Clone", (PyCFunction) Atrinik_Object_Clone, METH_VARARGS,
2308  doc_Atrinik_Object_Clone},
2309  {"ReadKey", (PyCFunction) Atrinik_Object_ReadKey, METH_VARARGS,
2310  doc_Atrinik_Object_ReadKey},
2311  {"WriteKey", (PyCFunction) Atrinik_Object_WriteKey, METH_VARARGS,
2312  doc_Atrinik_Object_WriteKey},
2313  {"GetName", (PyCFunction) Atrinik_Object_GetName, METH_VARARGS,
2314  doc_Atrinik_Object_GetName},
2315  {"Controller", (PyCFunction) Atrinik_Object_Controller, METH_NOARGS,
2316  doc_Atrinik_Object_Controller},
2317  {"Protection", (PyCFunction) Atrinik_Object_Protection, METH_VARARGS,
2318  doc_Atrinik_Object_Protection},
2319  {"SetProtection", (PyCFunction) Atrinik_Object_SetProtection, METH_VARARGS,
2320  doc_Atrinik_Object_SetProtection},
2321  {"Attack", (PyCFunction) Atrinik_Object_Attack, METH_VARARGS,
2322  doc_Atrinik_Object_Attack},
2323  {"SetAttack", (PyCFunction) Atrinik_Object_SetAttack, METH_VARARGS,
2324  doc_Atrinik_Object_SetAttack},
2325  {"Decrease", (PyCFunction) Atrinik_Object_Decrease, METH_VARARGS,
2326  doc_Atrinik_Object_Decrease},
2327  {"SquaresAround", (PyCFunction) Atrinik_Object_SquaresAround,
2328  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_SquaresAround},
2329  {"GetRangeVector", (PyCFunction) Atrinik_Object_GetRangeVector,
2330  METH_VARARGS, doc_Atrinik_Object_GetRangeVector},
2331  {"CreateTreasure", (PyCFunction) Atrinik_Object_CreateTreasure,
2332  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_CreateTreasure},
2333  {"Move", (PyCFunction) Atrinik_Object_Move, METH_VARARGS,
2334  doc_Atrinik_Object_Move},
2335  {"ConnectionTrigger", (PyCFunction) Atrinik_Object_ConnectionTrigger,
2336  METH_VARARGS | METH_KEYWORDS, doc_Atrinik_Object_ConnectionTrigger},
2337  {"Artificate", (PyCFunction) Atrinik_Object_Artificate, METH_VARARGS,
2338  doc_Atrinik_Object_Artificate},
2339  {"Load", (PyCFunction) Atrinik_Object_Load, METH_VARARGS,
2340  doc_Atrinik_Object_Load},
2341  {"GetPacket", (PyCFunction) Atrinik_Object_GetPacket, METH_VARARGS,
2342  doc_Atrinik_Object_GetPacket},
2343  {"FactionIsFriend", (PyCFunction) Atrinik_Object_FactionIsFriend,
2344  METH_VARARGS, doc_Atrinik_Object_FactionIsFriend},
2345  {NULL, NULL, 0, 0}
2346 };
2347 
2357 static PyObject *Object_GetAttribute(Atrinik_Object *obj, void *context)
2358 {
2359  OBJEXISTCHECK(obj);
2360  fields_struct *field = context;
2361 
2362  if (field->offset == offsetof(object, head)) {
2363  return wrap_object(HEAD(obj->obj));
2364  }
2365 
2366  return generic_field_getter(field, obj->obj);
2367 }
2368 
2380 static int Object_SetAttribute(Atrinik_Object *obj, PyObject *value,
2381  void *context)
2382 {
2383  fields_struct *field = context;
2384  int ret;
2385 
2386  OBJEXISTCHECK_INT(obj);
2387 
2388  if ((field->flags & FIELDFLAG_PLAYER_READONLY) &&
2389  obj->obj->type == PLAYER) {
2390  INTRAISE("Trying to modify a field that is read-only for player "
2391  "objects.");
2392  }
2393 
2394  if (field->offset == offsetof(object, type) &&
2395  obj->obj->custom_attrset != NULL) {
2396  INTRAISE("Cannot modify type of object that has custom_attrset.");
2397  }
2398 
2399  if (obj->obj->map != NULL && (field->offset == offsetof(object, layer) ||
2400  field->offset == offsetof(object, sub_layer))) {
2401  hooks->object_remove(obj->obj, 0);
2402  }
2403 
2404  ret = generic_field_setter(field, obj->obj, value);
2405 
2406  if (field->offset == offsetof(object, layer) ||
2407  field->offset == offsetof(object, sub_layer)) {
2408  obj->obj->layer = MIN(NUM_LAYERS, obj->obj->layer);
2409  obj->obj->sub_layer = MIN(NUM_SUB_LAYERS - 1, obj->obj->sub_layer);
2410 
2411  if (obj->obj->map != NULL) {
2412  hooks->object_insert_map(obj->obj, obj->obj->map, NULL, 0);
2413  }
2414  }
2415 
2416  if (ret == -1) {
2417  return -1;
2418  }
2419 
2420  if (field->offset == offsetof(object, type) && obj->obj->type == PLAYER) {
2421  obj->obj->type = MONSTER;
2422  }
2423 
2424  hooks->esrv_send_item(obj->obj);
2425 
2426  /* Special handling for some player stuff. */
2427  if (obj->obj->type == PLAYER) {
2428  if (field->flags & FIELDFLAG_PLAYER_FIX) {
2429  hooks->living_update(obj->obj);
2430  }
2431  }
2432 
2433  /* Update object's speed. */
2434  if (field->offset == offsetof(object, speed)) {
2435  hooks->object_update_speed(obj->obj);
2436  } else if (field->offset == offsetof(object, type)) {
2437  /* Handle object's type changing. */
2438 
2439  /* Changing to a spawn point monster requires special handling:
2440  * as the object was most likely created and put on active list,
2441  * we must remove it from the active list, as spawn point monsters
2442  * are not allowed to be on the list. */
2443  if (obj->obj->type == SPAWN_POINT_MOB) {
2444  float old_speed;
2445 
2446  /* Store original speed, as in order to actually remove the object
2447  * from the active list, we need to set its speed to 0 and make it
2448  * a non-SPAWN_POINT_MOB type. */
2449  old_speed = obj->obj->speed;
2450  obj->obj->speed = 0.0f;
2451  obj->obj->type = MONSTER;
2452  /* Remove it from the active list. */
2453  hooks->object_update_speed(obj->obj);
2454 
2455  /* Restore original speed and type info. */
2456  obj->obj->speed = old_speed;
2457  obj->obj->type = SPAWN_POINT_MOB;
2458  }
2459  } else if (field->offset == offsetof(object, direction)) {
2460  /* Direction. */
2461 
2462  /* If the object is animated and turnable, update its face. */
2463  if (obj->obj->animation_id && QUERY_FLAG(obj->obj, FLAG_IS_TURNABLE)) {
2464  SET_ANIMATION(obj->obj, (NUM_ANIMATIONS(obj->obj) /
2465  NUM_FACINGS(obj->obj)) * obj->obj->direction +
2466  obj->obj->state);
2467  }
2468  } else if (field->offset == offsetof(object, enemy)) {
2469  if (QUERY_FLAG(obj->obj, FLAG_MONSTER)) {
2470  hooks->monster_enemy_signal(obj->obj, obj->obj->enemy);
2471  }
2472  }
2473 
2474  return 0;
2475 }
2476 
2487 static PyObject *Object_GetFlag(Atrinik_Object *obj, void *context)
2488 {
2489  size_t flagno = (size_t) context;
2490 
2491  /* Should not happen. */
2492  if (flagno >= NUM_FLAGS) {
2493  PyErr_SetString(PyExc_OverflowError, "Invalid flag ID.");
2494  return NULL;
2495  }
2496 
2497  OBJEXISTCHECK(obj);
2498 
2499  return Py_BuildBoolean(QUERY_FLAG(obj->obj, flagno));
2500 }
2501 
2513 static int Object_SetFlag(Atrinik_Object *obj, PyObject *val, void *context)
2514 {
2515  size_t flagno = (size_t) context;
2516 
2517  /* Should not happen. */
2518  if (flagno >= NUM_FLAGS) {
2519  PyErr_SetString(PyExc_OverflowError, "Invalid flag ID.");
2520  return -1;
2521  }
2522 
2523  OBJEXISTCHECK_INT(obj);
2524 
2525  if (val == Py_True) {
2526  SET_FLAG(obj->obj, flagno);
2527  } else if (val == Py_False) {
2528  CLEAR_FLAG(obj->obj, flagno);
2529  } else {
2530  PyErr_SetString(PyExc_TypeError, "Flag value must be either True or "
2531  "False.");
2532  return -1;
2533  }
2534 
2535  hooks->esrv_send_item(obj->obj);
2536  return 0;
2537 }
2538 
2550 static PyObject *Atrinik_Object_new(PyTypeObject *type, PyObject *args,
2551  PyObject *kwds)
2552 {
2553  Atrinik_Object *self = (Atrinik_Object *) type->tp_alloc(type, 0);
2554 
2555  if (self) {
2556  self->obj = NULL;
2557  self->count = 0;
2558  }
2559 
2560  return (PyObject *) self;
2561 }
2562 
2568 static void Atrinik_Object_dealloc(PyObject *self)
2569 {
2570  ((Atrinik_Object *) self)->obj = NULL;
2571  ((Atrinik_Object *) self)->count = 0;
2572 #ifndef IS_PY_LEGACY
2573  Py_TYPE(self)->tp_free(self);
2574 #else
2575  self->ob_type->tp_free(self);
2576 #endif
2577 }
2578 
2586 static PyObject *Atrinik_Object_str(Atrinik_Object *self)
2587 {
2588  OBJEXISTCHECK(self);
2589  return PyString_FromFormat("[%s \"%s\"]", STRING_OBJ_ARCH_NAME(self->obj),
2590  STRING_OBJ_NAME(self->obj));
2591 }
2592 
2593 static int Atrinik_Object_InternalCompare(Atrinik_Object *left,
2594  Atrinik_Object *right)
2595 {
2596  OBJEXISTCHECK_INT(left);
2597  OBJEXISTCHECK_INT(right);
2598  return (left->obj < right->obj ? -1 : (left->obj == right->obj ? 0 : 1));
2599 }
2600 
2601 static PyObject *Atrinik_Object_RichCompare(Atrinik_Object *left,
2602  Atrinik_Object *right, int op)
2603 {
2604  if (left == NULL || right == NULL ||
2605  !PyObject_TypeCheck((PyObject *) left, &Atrinik_ObjectType) ||
2606  !PyObject_TypeCheck((PyObject *) right, &Atrinik_ObjectType)) {
2607  Py_INCREF(Py_NotImplemented);
2608  return Py_NotImplemented;
2609  }
2610 
2611  int result = Atrinik_Object_InternalCompare(left, right);
2612 
2613  /* Handle removed objects. */
2614  if (result == -1 && PyErr_Occurred()) {
2615  return NULL;
2616  }
2617 
2618  return generic_rich_compare(op, result);
2619 }
2620 
2627 {
2628  if (obj == NULL || obj->obj == NULL || obj->obj->count != obj->count ||
2629  OBJECT_FREE(obj->obj)) {
2630  return 0;
2631  }
2632 
2633  return 1;
2634 }
2635 
2637 static PyGetSetDef getseters[NUM_FIELDS + NUM_FLAGS + 1];
2638 
2642 static PyNumberMethods AtrinikObjectNumber = {
2643  NULL,
2644  NULL,
2645  NULL,
2646 #ifndef IS_PY3K
2647  NULL, /* nb_divide */
2648 #endif
2649  NULL,
2650  NULL,
2651  NULL,
2652  NULL,
2653  NULL,
2654  NULL,
2655  (inquiry) atrinik_object_bool,
2656  NULL,
2657  NULL,
2658  NULL,
2659  NULL,
2660  NULL,
2661 #ifndef IS_PY3K
2662  NULL, /* nb_coerce */
2663 #endif
2664  NULL,
2665  NULL,
2666  NULL,
2667 #ifndef IS_PY3K
2668  NULL, /* nb_oct */
2669  NULL, /* nb_hex */
2670 #endif
2671  NULL,
2672  NULL,
2673  NULL,
2674 #ifndef IS_PY3K
2675  NULL, /* nb_inplace_divide */
2676 #endif
2677  NULL,
2678  NULL,
2679  NULL,
2680  NULL,
2681  NULL,
2682  NULL,
2683  NULL,
2684  NULL,
2685  NULL,
2686  NULL,
2687  NULL,
2688  NULL,
2689  NULL
2690 };
2691 
2695 PyTypeObject Atrinik_ObjectType = {
2696 #ifdef IS_PY3K
2697  PyVarObject_HEAD_INIT(NULL, 0)
2698 #else
2699  PyObject_HEAD_INIT(NULL)
2700  0,
2701 #endif
2702  "Atrinik.Object",
2703  sizeof(Atrinik_Object),
2704  0,
2705  (destructor) Atrinik_Object_dealloc,
2706  NULL, NULL, NULL,
2707 #ifdef IS_PY3K
2708  NULL,
2709 #else
2710  (cmpfunc) Atrinik_Object_InternalCompare,
2711 #endif
2712  NULL,
2713  &AtrinikObjectNumber,
2714  0, 0, 0, 0,
2715  (reprfunc) Atrinik_Object_str,
2716  0, 0, 0,
2717  Py_TPFLAGS_DEFAULT,
2718  "Atrinik objects",
2719  NULL, NULL,
2720  (richcmpfunc) Atrinik_Object_RichCompare,
2721  0,
2722  NULL,
2723  NULL,
2724  methods,
2725  0,
2726  getseters,
2727  0, 0, 0, 0, 0, 0, 0,
2729  0, 0, 0, 0, 0, 0, 0, 0
2730 #ifndef IS_PY_LEGACY
2731  , 0
2732 #endif
2733 #ifdef Py_TPFLAGS_HAVE_FINALIZE
2734  , NULL
2735 #endif
2736 };
2737 
2743 static void Atrinik_ObjectIterator_dealloc(PyObject *self)
2744 {
2745  Atrinik_ObjectIterator *iterator = (Atrinik_ObjectIterator *) self;
2746  iterator->obj = NULL;
2747  iterator->count = 0;
2748  iterator->iter_type = 0;
2749  Py_TYPE(self)->tp_free(self);
2750 }
2751 
2760 {
2761  return PyString_FromFormat("[%s \"%s\", type %d]",
2762  self->obj != NULL ? self->obj->name : ">NULL<",
2763  self->obj != NULL ? self->obj->arch->name : ">NULL<",
2764  self->iter_type);
2765 }
2766 
2774 static PyObject *Atrinik_ObjectIterator_iter(PyObject *self)
2775 {
2776  Py_INCREF(self);
2777  return self;
2778 }
2779 
2788 {
2789  return self->obj != NULL;
2790 }
2791 
2800 {
2801  if (self->iterated) {
2802  PyErr_SetString(AtrinikError, "Cannot get length of iterator that has "
2803  "been iterated");
2804  return -1;
2805  }
2806 
2807  Py_ssize_t num = 0;
2809  num++;
2811 
2812  return num;
2813 }
2814 
2825  Py_ssize_t idx)
2826 {
2827  if (self->iterated) {
2828  PyErr_SetString(AtrinikError, "Cannot access items of iterator that "
2829  "has been iterated");
2830  return NULL;
2831  }
2832 
2833  Py_ssize_t i = 0;
2835  if (i++ == idx) {
2836  return wrap_object(tmp);
2837  }
2839 
2840  PyErr_SetString(PyExc_IndexError, "index is out of range");
2841  return NULL;
2842 }
2843 
2854  PyObject *what)
2855 {
2856  if (!PyObject_TypeCheck(what, &Atrinik_ObjectType)) {
2857  PyErr_SetString(PyExc_TypeError, "invalid object type");
2858  return -1;
2859  }
2860 
2861  Atrinik_Object *obj = (Atrinik_Object *) what;
2862  OBJEXISTCHECK_INT(obj);
2863 
2864  if (self->iterated) {
2865  PyErr_SetString(AtrinikError, "Cannot access items of iterator that "
2866  "has been iterated");
2867  return -1;
2868  }
2869 
2871  if (tmp == obj->obj && tmp->count == obj->obj->count) {
2872  return 1;
2873  }
2875 
2876  return 0;
2877 }
2878 
2886 static PyObject *Atrinik_ObjectIterator_iternext(PyObject *self)
2887 {
2888  Atrinik_ObjectIterator *iterator = (Atrinik_ObjectIterator *) self;
2889 
2890  /* Do we need to stop iterating? */
2891  if (iterator->iter_type == OBJ_ITER_TYPE_NONE) {
2892  PyErr_SetNone(PyExc_StopIteration);
2893  return NULL;
2894  }
2895 
2896  if (!OBJECT_VALID(iterator->obj, iterator->count)) {
2897  RAISE("Object disappeared during iteration.")
2898  }
2899 
2900  object *tmp = iterator->obj;
2901 
2902  /* Check which way we're iterating. */
2903  if (iterator->iter_type == OBJ_ITER_TYPE_BELOW) {
2904  iterator->obj = tmp->below;
2905  } else if (iterator->iter_type == OBJ_ITER_TYPE_ABOVE) {
2906  iterator->obj = tmp->above;
2907  } else if (iterator->iter_type == OBJ_ITER_TYPE_ONE) {
2908  iterator->obj = NULL;
2909  }
2910 
2911  iterator->count = iterator->obj != NULL ? iterator->obj->count : 0;
2912 
2913  /* Nothing left, so mark iter_type to show that. */
2914  if (iterator->obj == NULL) {
2915  iterator->iter_type = OBJ_ITER_TYPE_NONE;
2916  }
2917 
2918  iterator->iterated = true;
2919 
2920  return wrap_object(tmp);
2921 }
2922 
2926 static PyNumberMethods Atrinik_ObjectIteratorNumber = {
2927  NULL,
2928  NULL,
2929  NULL,
2930 #ifndef IS_PY3K
2931  NULL, /* nb_divide */
2932 #endif
2933  NULL,
2934  NULL,
2935  NULL,
2936  NULL,
2937  NULL,
2938  NULL,
2939  (inquiry) Atrinik_ObjectIterator_bool,
2940  NULL,
2941  NULL,
2942  NULL,
2943  NULL,
2944  NULL,
2945 #ifndef IS_PY3K
2946  NULL, /* nb_coerce */
2947 #endif
2948  NULL,
2949  NULL,
2950  NULL,
2951 #ifndef IS_PY3K
2952  NULL, /* nb_oct */
2953  NULL, /* nb_hex */
2954 #endif
2955  NULL,
2956  NULL,
2957  NULL,
2958 #ifndef IS_PY3K
2959  NULL, /* nb_inplace_divide */
2960 #endif
2961  NULL,
2962  NULL,
2963  NULL,
2964  NULL,
2965  NULL,
2966  NULL,
2967  NULL,
2968  NULL,
2969  NULL,
2970  NULL,
2971  NULL,
2972  NULL,
2973  NULL
2974 };
2975 
2979 static PySequenceMethods Atrinik_ObjectIteratorSequence = {
2980  (lenfunc) Atrinik_ObjectIterator_len,
2981  NULL,
2982  NULL,
2983  (ssizeargfunc) Atrinik_ObjectIterator_getitem,
2984  NULL,
2985  NULL,
2986  NULL,
2987  (objobjproc) Atrinik_ObjectIterator_contains,
2988  NULL,
2989  NULL
2990 };
2991 
2996  PyVarObject_HEAD_INIT(NULL, 0)
2997  "Atrinik.ObjectIterator",
2998  sizeof(Atrinik_ObjectIterator),
2999  0,
3000  (destructor) Atrinik_ObjectIterator_dealloc,
3001  NULL, NULL, NULL,
3002  NULL,
3003  NULL,
3006  0, 0, 0,
3007  (reprfunc) Atrinik_ObjectIterator_str,
3008  0, 0, 0,
3009  Py_TPFLAGS_DEFAULT,
3010  "Used for iterating object inventories.",
3011  NULL, NULL,
3012  NULL,
3013  0,
3014  (getiterfunc) Atrinik_ObjectIterator_iter,
3015  (iternextfunc) Atrinik_ObjectIterator_iternext,
3016  NULL,
3017  0,
3018  NULL,
3019  0, 0, 0, 0, 0, 0, 0,
3020  PyType_GenericNew,
3021  0, 0, 0, 0, 0, 0, 0, 0, 0
3022 #ifdef Py_TPFLAGS_HAVE_FINALIZE
3023  , NULL
3024 #endif
3025 };
3026 
3034 int Atrinik_Object_init(PyObject *module)
3035 {
3036  size_t i;
3037 
3038  /* Field getseters */
3039  for (i = 0; i < NUM_FIELDS; i++) {
3040  PyGetSetDef *def = &getseters[i];
3041 
3042  def->name = fields[i].name;
3043  def->get = (getter) Object_GetAttribute;
3044  def->set = (setter) Object_SetAttribute;
3045  def->doc = fields[i].doc;
3046  def->closure = &fields[i];
3047  }
3048 
3049  /* Flag getseters */
3050  for (size_t flagno = 0; flagno < NUM_FLAGS; flagno++) {
3051  if (hooks->object_flag_names[flagno] == NULL) {
3052  continue;
3053  }
3054  PyGetSetDef *def = &getseters[i++];
3055 
3056  char buf[MAX_BUF];
3057  snprintf(VS(buf), "f_%s", hooks->object_flag_names[flagno]);
3058  def->name = strdup(buf);
3059 
3060  def->get = (getter) Object_GetFlag;
3061  def->set = (setter) Object_SetFlag;
3062  def->doc = doc_object_flag_names[flagno];
3063  def->closure = (void *) flagno;
3064  }
3065 
3066  getseters[i].name = NULL;
3067 
3068  Atrinik_ObjectType.tp_new = PyType_GenericNew;
3069 
3070  if (PyType_Ready(&Atrinik_ObjectType) < 0) {
3071  return 0;
3072  }
3073 
3074  Py_INCREF(&Atrinik_ObjectType);
3075  PyModule_AddObject(module, "Object", (PyObject *) &Atrinik_ObjectType);
3076 
3077  if (PyType_Ready(&Atrinik_ObjectIteratorType) < 0) {
3078  return 0;
3079  }
3080 
3081  Py_INCREF(&Atrinik_ObjectIteratorType);
3082  PyModule_AddObject(module, "ObjectIterator",
3083  (PyObject *) &Atrinik_ObjectIteratorType);
3084 
3085  return 1;
3086 }
3087 
3095 PyObject *wrap_object(object *what)
3096 {
3097  /* Return None if no object was to be wrapped. */
3098  if (what == NULL || OBJECT_FREE(what)) {
3099  Py_INCREF(Py_None);
3100  return Py_None;
3101  }
3102 
3103  Atrinik_Object *wrapper = PyObject_NEW(Atrinik_Object, &Atrinik_ObjectType);
3104  if (wrapper != NULL) {
3105  wrapper->obj = what;
3106  wrapper->count = wrapper->obj->count;
3107  }
3108 
3109  return (PyObject *) wrapper;
3110 }
3111 
3119 PyObject *wrap_object_iterator(object *what)
3120 {
3121  Atrinik_ObjectIterator *iterator = PyObject_NEW(Atrinik_ObjectIterator,
3122  &Atrinik_ObjectIteratorType);
3123  if (iterator == NULL) {
3124  return NULL;
3125  }
3126 
3127  if (what != NULL) {
3128  iterator->obj = what;
3129  iterator->count = what->count;
3130  iterator->iter_type = OBJ_ITER_TYPE_ONE;
3131 
3132  /* Select which iteration type we're doing. It's possible that
3133  * an object has both below and above set (it's not the first and
3134  * not the last object), in which case we will prefer below. */
3135  if (what->below != NULL) {
3136  iterator->iter_type = OBJ_ITER_TYPE_BELOW;
3137  } else if (what->above != NULL) {
3138  iterator->iter_type = OBJ_ITER_TYPE_ABOVE;
3139  }
3140  } else {
3141  iterator->obj = NULL;
3142  iterator->count = 0;
3143  iterator->iter_type = OBJ_ITER_TYPE_NONE;
3144  }
3145 
3146  iterator->iterated = 0;
3147 
3148  return (PyObject *) iterator;
3149 }
PyTypeObject Atrinik_PlayerType
static const char doc_Atrinik_Object_Clone[]
#define OBJ_ITER_TYPE_ONE
#define GENDER_MALE
Definition: object.h:633
static PyObject * Atrinik_Object_CastIdentify(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_GetCost(Atrinik_Object *self, PyObject *args)
PyTypeObject Atrinik_ObjectType
Definition: object.h:94
#define FOR_ATRINIK_ITERATOR_BEGIN()
static const char doc_Atrinik_Object_GetPacket[]
#define FREE_AND_COPY_HASH(_sv_, _nv_)
Definition: global.h:100
#define MONSTER
Definition: define.h:353
static const char doc_Atrinik_Object_GetMoney[]
static const char doc_Atrinik_Object_SetGender[]
long seconds(void)
Definition: time.c:338
struct artifact * items
Artifacts in this artifact list.
Definition: artifact.h:84
int direction
Definition: map.h:787
static PyObject * Atrinik_Object_SetAttack(Atrinik_Object *self, PyObject *args)
#define STRING_OBJ_NAME(__ob__)
Definition: global.h:264
static const char doc_Atrinik_Object_Say[]
struct Atrinik_Object Atrinik_Object
static PyObject * Atrinik_Object_Move(Atrinik_Object *self, PyObject *args)
static PyNumberMethods Atrinik_ObjectIteratorNumber
struct plugin_hooklist * hooks
Definition: plugin_arena.c:160
static PyObject * Object_GetAttribute(Atrinik_Object *obj, void *context)
unsigned int distance
Definition: map.h:775
#define RV_NO_DISTANCE
Definition: map.h:813
static const char doc_Atrinik_Object_Hit[]
#define AROUND_ALL
#define NUM_FACINGS(ob)
Definition: global.h:300
void * custom_attrset
Definition: object.h:160
static int Object_SetAttribute(Atrinik_Object *obj, PyObject *value, void *context)
static PyObject * Atrinik_Object_Hit(Atrinik_Object *self, PyObject *args)
static const char doc_Atrinik_Object_SquaresAround[]
static const char *const stats[]
Definition: stats.c:38
uint8_t type
One of operation types.
Definition: sound_ambient.c:45
static PyObject * Atrinik_ObjectIterator_iter(PyObject *self)
static const char doc_Atrinik_Object_SetAttack[]
PyObject * wrap_object_iterator(object *what)
double speed_left
Definition: object.h:472
static PyObject * Atrinik_Object_Take(Atrinik_Object *self, PyObject *what)
static Py_ssize_t Atrinik_ObjectIterator_len(Atrinik_ObjectIterator *self)
#define Py_BuildBoolean(val)
uint32_t flags
static PyObject * Atrinik_Object_Attack(Atrinik_Object *self, PyObject *args)
#define SET_ANIMATION(ob, newanim)
Definition: global.h:282
static const char doc_Atrinik_Object_GetName[]
static const char doc_Atrinik_Object_GetGender[]
int distance_x
Definition: map.h:778
#define INVENTORY_CONTAINERS
static PyObject * Atrinik_Object_ConnectionTrigger(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static PyObject * Atrinik_Object_WriteKey(Atrinik_Object *self, PyObject *args)
object * ob
Definition: player.h:185
static PyObject * Atrinik_Object_PayAmount(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_GetMoney(Atrinik_Object *self)
static const char doc_Atrinik_Object_CreateTreasure[]
#define RAISE(msg)
static PyObject * Atrinik_Object_GetPacket(Atrinik_Object *self, PyObject *args)
int generic_field_setter(fields_struct *field, void *ptr, PyObject *value)
uint8_t layer
Definition: object.h:405
struct obj * above
Definition: object.h:120
static const char doc_Atrinik_Object_Drop[]
PyTypeObject Atrinik_ObjectIteratorType
PyObject * generic_rich_compare(int op, int result)
#define IS_LIVE(op)
Definition: define.h:841
#define PLAYER
Definition: define.h:122
static int Object_SetFlag(Atrinik_Object *obj, PyObject *val, void *context)
static char * doc_object_flag_names[NUM_FLAGS+1]
static PyObject * Atrinik_Object_Load(Atrinik_Object *self, PyObject *args)
#define OBJECT_FREE(_ob_)
Definition: object.h:554
#define FLAG_IS_TURNABLE
Definition: define.h:960
static const char doc_Atrinik_Object_CreateObject[]
PyObject * wrap_object(object *what)
static PyObject * Atrinik_Object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static const char doc_Atrinik_Object_GetRangeVector[]
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
static const char doc_Atrinik_Object_ReadKey[]
struct archetype * arch
Definition: object.h:225
struct obj * enemy
Definition: object.h:196
static PyObject * Atrinik_Object_GetRangeVector(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_Say(Atrinik_Object *self, PyObject *args)
PyObject_HEAD object * obj
#define IS_PY_LEGACY
Definition: plugin_python.h:55
int distance_y
Definition: map.h:781
const char * title
Definition: object.h:171
static const char doc_Atrinik_Object_Destroy[]
static PyObject * Atrinik_Object_Remove(Atrinik_Object *self)
static PyObject * Atrinik_Object_FindObjects(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static const char doc_Atrinik_Object_ConnectionTrigger[]
static PyObject * Atrinik_Object_SquaresAround(Atrinik_Object *self, PyObject *args, PyObject *keywds)
#define COST_TRUE
Definition: define.h:1374
static const char doc_Atrinik_Object_Controller[]
uint8_t sub_layer
Definition: object.h:408
#define GENDER_HERMAPHRODITE
Definition: object.h:637
PyObject * AtrinikError
Definition: plugin_python.c:55
int8_t direction
Definition: object.h:350
Definition: arch.h:40
static PyObject * Atrinik_Object_SetProtection(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_str(Atrinik_Object *self)
#define MAP_INFO_NORMAL
Definition: global.h:83
static void Atrinik_ObjectIterator_dealloc(PyObject *self)
#define CAST_NPC
Definition: spells.h:288
#define IDENTIFY_MARKED
Definition: define.h:62
uint16_t animation_id
Definition: object.h:322
static int atrinik_object_bool(Atrinik_Object *obj)
struct mapdef * map
Definition: object.h:139
static const char doc_Atrinik_Object_FindObject[]
static const char doc_Atrinik_Object_Cast[]
struct Atrinik_ObjectIterator Atrinik_ObjectIterator
#define FLAG_UNPAID
Definition: define.h:1251
#define FIELDFLAG_READONLY
static PyObject * Atrinik_Object_Destroy(Atrinik_Object *self)
#define NUM_FIELDS
static PyObject * Atrinik_Object_SetPosition(Atrinik_Object *self, PyObject *args)
#define INVENTORY_ALL
#define FLAG_IS_MALE
Definition: define.h:1174
#define OBJ_ITER_TYPE_BELOW
static const char doc_Atrinik_Object_Decrease[]
#define OBJ_ITER_TYPE_ABOVE
union @21 data
Data about the rule.
static const char doc_Atrinik_Object_SetPosition[]
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
uint32_t weight_limit[MAX_STAT+1]
Definition: living.c:129
static const char doc_Atrinik_Object_Update[]
uint8_t state
Definition: object.h:344
struct obj * below
Definition: object.h:114
#define CONTAINER
Definition: define.h:493
static PyObject * Atrinik_Object_CreateTreasure(Atrinik_Object *self, PyObject *args, PyObject *keywds)
#define FIELDFLAG_PLAYER_FIX
static const char doc_Atrinik_Object_Remove[]
uint32_t nrof
Definition: object.h:264
static const char doc_Atrinik_Object_Move[]
static PyObject * Atrinik_Object_Save(Atrinik_Object *self)
static PyObject * Atrinik_Object_CreateObject(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static PyGetSetDef getseters[NUM_FIELDS+NUM_FLAGS+1]
static const char doc_Atrinik_Object_PayAmount[]
#define FLAG_IS_USED_UP
Definition: define.h:976
#define SPAWN_POINT_MOB
Definition: define.h:365
static const char doc_Atrinik_Object_Artificate[]
static int Atrinik_ObjectIterator_bool(Atrinik_ObjectIterator *self)
#define NUM_LAYERS
Definition: map.h:67
uint64_t num
Number of successful updates.
Definition: metaserver.c:43
static const char doc_Atrinik_Object_TeleportTo[]
double speed
Definition: object.h:469
#define HEAD(op)
Definition: object.h:657
static const char doc_Atrinik_Object_GetCost[]
#define INVENTORY_ONLY
static PyObject * Object_GetFlag(Atrinik_Object *obj, void *context)
#define TREASURE_ARTIFACT_CHANCE
Artifact chance value is not set.
Definition: global.h:173
static PyObject * Atrinik_Object_Artificate(Atrinik_Object *self, PyObject *args)
#define OBJ_ITER_TYPE_NONE
#define NUM_SUB_LAYERS
Definition: map.h:71
#define FLAG_REMOVED
Definition: define.h:930
PyObject_HEAD player * pl
#define MAXLEVEL
Definition: global.h:221
#define GENDER_FEMALE
Definition: object.h:635
#define STRING_OBJ_ARCH_NAME(__ob__)
Definition: global.h:268
PyObject * generic_field_getter(fields_struct *field, void *ptr)
static PyObject * Atrinik_Object_FactionIsFriend(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_CreateForce(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static const char doc_Atrinik_Object_Attack[]
static const char doc_Atrinik_Object_Take[]
static const char doc_Atrinik_Object_Protection[]
static PyObject * Atrinik_Object_Controller(Atrinik_Object *self)
static PyObject * common_Atrinik_Object_FindObject(Atrinik_Object *self, PyObject *args, PyObject *keywds, bool multiple)
#define NUM_ANIMATIONS(ob)
Definition: global.h:298
static PyObject * Atrinik_Object_Drop(Atrinik_Object *self, PyObject *what)
static PyObject * Atrinik_Object_ActivateRune(Atrinik_Object *self, PyObject *args)
static const char doc_Atrinik_Object_WriteKey[]
tag_t count
Definition: object.h:142
living stats
Definition: object.h:481
static PyObject * Atrinik_ObjectIterator_getitem(Atrinik_ObjectIterator *self, Py_ssize_t idx)
static PyObject * Atrinik_Object_FindObject(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static PyObject * Atrinik_ObjectIterator_iternext(PyObject *self)
uint8_t type
Definition: object.h:360
#define CLEAR_FLAG(xyz, p)
Definition: define.h:751
static PySequenceMethods Atrinik_ObjectIteratorSequence
static PyObject * Atrinik_Object_GetGender(Atrinik_Object *self)
shstr * name
More definite name, like "kobold".
Definition: arch.h:46
object * part
Definition: map.h:790
static const char doc_Atrinik_Object_FindObjects[]
static fields_struct fields[]
#define FLAG_IS_PLAYER
Definition: define.h:1267
static PyObject * Atrinik_Object_Cast(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static void Atrinik_Object_dealloc(PyObject *self)
#define RUNE
Definition: define.h:522
static const char doc_Atrinik_Object_Apply[]
int Atrinik_Object_init(PyObject *module)
#define FLAG_MONSTER
Definition: define.h:922
static const char doc_Atrinik_Object_FactionIsFriend[]
#define OBJECT_VALID(_ob_, _count_)
Definition: object.h:548
PyObject * wrap_player(player *pl)
static PyObject * Atrinik_Object_Decrease(Atrinik_Object *self, PyObject *args)
#define INTRAISE(msg)
static int Atrinik_ObjectIterator_contains(Atrinik_ObjectIterator *self, PyObject *what)
struct obj * inv
Definition: object.h:123
static const char doc_Atrinik_Object_ActivateRune[]
static PyObject * Atrinik_Object_TeleportTo(Atrinik_Object *self, PyObject *args, PyObject *keywds)
static PyObject * Atrinik_Object_InsertInto(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_ObjectIterator_str(Atrinik_ObjectIterator *self)
static PyNumberMethods AtrinikObjectNumber
static PyObject * Atrinik_Object_Clone(Atrinik_Object *self, PyObject *args)
static const char doc_Atrinik_Object_CreateForce[]
static const char doc_Atrinik_Object_Save[]
static PyObject * Atrinik_Object_Update(Atrinik_Object *self)
static PyObject * Atrinik_Object_Apply(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_SetGender(Atrinik_Object *self, PyObject *args)
#define NUM_FLAGS
Definition: define.h:1347
#define FLAG_IS_FEMALE
Definition: define.h:1178
static const char doc_Atrinik_Object_SetProtection[]
Definition: map.h:536
#define FOR_ATRINIK_ITERATOR_END()
static PyObject * Atrinik_Object_ReadKey(Atrinik_Object *self, PyObject *args)
static PyObject * Atrinik_Object_Protection(Atrinik_Object *self, PyObject *args)
#define CAST_NORMAL
Definition: spells.h:275
static PyObject * Atrinik_Object_GetName(Atrinik_Object *self, PyObject *args)
int8_t level
Definition: object.h:347
static const char doc_Atrinik_Object_InsertInto[]
static const char doc_Atrinik_Object_CastIdentify[]
int64_t value
Definition: object.h:240
#define FIELDFLAG_PLAYER_READONLY
PyObject_HEAD object * obj
static PyMethodDef methods[]
#define SQUARES_AROUND_ADD(_m, _x, _y)
struct artifact * next
Next artifact in the list.
Definition: artifact.h:40
#define FLAG_IDENTIFIED
Definition: define.h:980
int16_t food
Definition: living.h:84
static const char doc_Atrinik_Object_Load[]