Crossfire Server, Trunk
check_1727944.cpp
Go to the documentation of this file.
1 /*
2  * CrossFire, A Multiplayer game for X-windows
3  *
4  * Copyright (C) 2007 Crossfire Development Team
5  * Copyright (C) 1992 Frank Tore Johansen
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  * The authors can be reached via e-mail at crossfire-devel@real-time.com
22  */
23 
34 #include <stdlib.h>
35 #include <check.h>
36 #include <global.h>
37 #include <sproto.h>
38 #include <toolkit_common.h>
39 
40 static void setup(void) {
41  /* put any initialisation steps here, they will be run before each testcase */
42 }
43 
44 static void teardown(void) {
45  /* put any cleanup steps here, they will be run after each testcase */
46 }
47 
48 #if 0
49 static mapstruct *get_random_map(mapstruct *map) {
50  object *exit_ob;
51  mapstruct *random;
52  RMParms rp;
53  char newmap_name[HUGE_BUF], *cp;
54  static int reference_number = 0;
55  int x, y;
56 
57  exit_ob = NULL;
58  for (x = 0; x < MAP_WIDTH(map) && exit_ob == NULL; x++) {
59  for (y = 0; y < MAP_HEIGHT(map) && exit_ob == NULL; y++) {
60  for (exit_ob = GET_MAP_OB(map, x, y); exit_ob != NULL; exit_ob = exit_ob->above)
61  if (exit_ob->type == EXIT && exit_ob->msg != NULL)
62  break;
63  }
64  }
65 
66  if (!exit_ob)
67  /* this means we reached the end of the random part. */
68  return NULL;
69 
70  /* copied from server/server.c:enter_random_map(). */
71  memset(&rp, 0, sizeof(RMParms));
72  rp.Xsize = -1;
73  rp.Ysize = -1;
74  rp.region = get_region_by_map(exit_ob->map);
75  if (exit_ob->msg)
76  set_random_map_variable(&rp, exit_ob->msg);
77  rp.origin_x = exit_ob->x;
78  rp.origin_y = exit_ob->y;
79  strcpy(rp.origin_map, map->path);
80 
81  /* If we have a final_map, use it as a base name to give some clue
82  * as where the player is. Otherwise, use the origin map.
83  * Take the last component (after the last slash) to give
84  * shorter names without bogus slashes.
85  */
86  if (rp.final_map[0]) {
87  cp = strrchr(rp.final_map, '/');
88  if (!cp)
89  cp = rp.final_map;
90  } else {
91  char buf[HUGE_BUF];
92 
93  cp = strrchr(rp.origin_map, '/');
94  if (!cp)
95  cp = rp.origin_map;
96  /* Need to strip of any trailing digits, if it has them */
97  snprintf(buf, sizeof(buf), "%s", cp);
98  while (isdigit(buf[strlen(buf)-1]))
99  buf[strlen(buf)-1] = 0;
100  cp = buf;
101  }
102  snprintf(newmap_name, sizeof(newmap_name), "/random/%s%04d", cp+1, reference_number++);
103  /* now to generate the actual map. */
104  return generate_random_map(newmap_name, &rp, NULL);
105 }
106 
107 static void do_run(void) {
108  mapstruct *worldmap;
109  mapstruct *random;
110  mapstruct *old;
111  int iteration, x, y, map;
112  object *check;
113  char path[150];
114 
115  for (map = 1; map <= 3; map++) {
116  snprintf(path, sizeof(path), "/whalingoutpost/underwaterdungeon/level%d", map);
117  worldmap = ready_map_name(path, 0);
118  fail_unless(worldmap != NULL, "Can't load %s", path);
119 
120  random = worldmap;
121  old = NULL;
122  iteration = 0;
123  while (random != NULL) {
124  random = get_random_map(random);
125  if (!random)
126  break;
127  if (old)
128  delete_map(old);
129  old = random;
130  iteration++;
131  for (x = 0; x < MAP_WIDTH(random); x++) {
132  for (y = 0; y < MAP_HEIGHT(random); y++) {
133  for (check = GET_MAP_OB(random, x, y); check; check = check->above) {
134  if (check->type == ROD && check->title && strcmp(check->title, "of Plenty") == 0)
135  fail_unless(check->inv != NULL, "Horn has empty inventory!");
136  }
137  }
138  }
139  }
140  fail_unless(iteration != 0, "did %d iterations", iteration);
141  if (old)
142  delete_map(old);
143  }
144 }
145 #endif
146 
147 #if 0
148 static void do_run(void) {
149  mapstruct *map, *overlay;
150  int x, y, found = 0, test = 0;
151  object *check;
152 
153  overlay = ready_map_name("../../rsc/bug_1727944_unique", MAP_PLAYER_UNIQUE);
154  fail_unless(overlay != NULL, "Couldn't load unique map ../../rsc/bug_1727944_unique");
155 
156  while (found == 0 && test < 10) {
157  map = ready_map_name("../../rsc/bug_1727944", MAP_PLAYER_UNIQUE);
158  fail_unless(map != NULL, "couldn't load map ../../rsc/bug_1727944");
159 
160  for (x = 0; x < MAP_WIDTH(map); x++) {
161  for (y = 0; y < MAP_HEIGHT(map); y++) {
162  for (check = GET_MAP_OB(map, x, y); check; check = check->above) {
163  if (check->type == ROD) {
164  fail_unless(check->inv != NULL, "Horn has empty inventory!");
165  fail_unless(check->inv->below == NULL, "Horn has 2 items in inventory!");
166  if (check->title && strcmp(check->title, "of Plenty") == 0) {
168  object_insert_in_map_at(check, overlay, NULL, 0, 2, 3);
169  found++;
170  break;
171  }
172  }
173  }
174  }
175  }
176  delete_map(map);
177  test++;
178  }
179  save_map(overlay, SAVE_MODE_OVERLAY);
180  delete_map(overlay);
181 }
182 #endif
183 
184 static void local_check_loaded_object(object *op) {
185  int ip;
186 
187  /* We do some specialized handling to handle legacy cases of name_pl.
188  * If the object doesn't have a name_pl, we just use the object name -
189  * this isn't perfect (things won't be properly pluralized), but works to
190  * that degree (5 heart is still quite understandable). But the case we
191  * also have to catch is if this object is not using the normal name for
192  * the object. In that case, we also want to use the loaded name.
193  * Otherwise, what happens is that the the plural name will lose
194  * information (appear as just 'hearts' and not 'goblins heart')
195  */
196  if (op->arch && op->name != op->arch->clone.name && op->name_pl == op->arch->clone.name_pl) {
197  if (op->name_pl)
198  free_string(op->name_pl);
199  op->name_pl = NULL;
200  }
201  if (!op->name_pl && op->name)
202  op->name_pl = add_string(op->name);
203 
204  /* objects now have a materialname. try to patch it in */
205  if (!(IS_WEAPON(op) && op->level > 0)) {
207  }
208  /* only do these when program is first run - a bit
209  * excessive to do this at every run - most of this is
210  * really just to catch any errors - program will still run, but
211  * not in the ideal fashion.
212  */
213  if ((op->type == WEAPON || op->type == BOW)) {
214  if (!op->skill) {
215  LOG(llevError, "Weapon %s lacks a skill.\n", op->name);
216  } else if ((!strcmp(op->skill, "one handed weapons") && op->body_info[1] != -1)
217  || (!strcmp(op->skill, "two handed weapons") && op->body_info[1] != -2)) {
218  LOG(llevError, "weapon %s arm usage does not match skill: %d, %s\n",
219  op->name, op->body_info[1], op->skill);
220  }
221  }
222 
223  /* We changed last_heal to gen_sp_armour, which is what it
224  * really does for many objects. Need to catch any in maps
225  * that may have an old value.
226  */
227  if ((op->type == WEAPON)
228  || (op->type == ARMOUR)
229  || (op->type == HELMET)
230  || (op->type == SHIELD)
231  || (op->type == RING)
232  || (op->type == BOOTS)
233  || (op->type == GLOVES)
234  || (op->type == AMULET)
235  || (op->type == GIRDLE)
236  || (op->type == BRACERS)
237  || (op->type == CLOAK)) {
238  if (op->last_heal) {
239  LOG(llevDebug, "Object %s still has last_heal set, not gen_sp_armour\n", op->name ? op->name : "NULL");
240  op->gen_sp_armour = op->last_heal;
241  op->last_heal = 0;
242  }
243  ip = calc_item_power(op);
244  /* Legacy objects from before item power was in the game */
245  if (!op->item_power && ip) {
246  if (ip > 3) {
247  LOG(llevDebug, "Object %s had no item power, using %d\n", op->name ? op->name : "NULL", ip);
248  }
249  op->item_power = ip;
250  }
251  /* Check for possibly bogus values. Has to meet both these criteria -
252  * something that has item_power 1 is probably just fine if our calculated
253  * value is 1 or 2 - these values are small enough that hard to be precise.
254  * similarly, it item_power is 0, the first check will always pass,
255  * but not the second one.
256  */
257  if (ip > 2*op->item_power && ip > (op->item_power+3)) {
258  LOG(llevDebug, "Object %s seems to have too low item power? %d > %d\n", op->name ? op->name : "NULL", ip, op->item_power);
259  }
260  }
261  /* Old spellcasting object - need to load in the appropiate object */
262  if ((op->type == ROD || op->type == WAND || op->type == SCROLL || op->type == FIREWALL || /* POTIONS and ALTARS don't always cast spells, but if they do, update them */ ((op->type == POTION || op->type == ALTAR) && op->stats.sp))
263  && !op->inv) {
264  object *tmp;
265 
266  /* Fireall is bizarre in that spell type was stored in dam. Rest are 'normal'
267  * in that spell was stored in sp.
268  */
269  tmp = create_archetype(spell_mapping[op->type == FIREWALL ? op->stats.dam : op->stats.sp]);
271  op->randomitems = NULL; /* So another spell isn't created for this object */
272  }
273 
274  /* spellbooks & runes use slaying. But not to arch name, but to spell name */
275  if ((op->type == SPELLBOOK || op->type == RUNE) && op->slaying && !op->inv) {
276  object *tmp;
277 
280  op->randomitems = NULL; /* So another spell isn't created for this object */
281  /* without this, value is all screwed up */
282  op->value = op->arch->clone.value*op->inv->value;
283  }
284 
285  if (QUERY_FLAG(op, FLAG_MONSTER)) {
286  if (op->stats.hp > op->stats.maxhp)
287  LOG(llevDebug, "Monster %s has hp set higher than maxhp (%d>%d)\n", op->name, op->stats.hp, op->stats.maxhp);
288  }
290  || op->type == CREATOR
291  || op->type == CONVERTER) {
292  /* Object will duplicate it's content as part of the
293  * generation process. To do this, we must flag inventory
294  * so it remains unevaluated concerning the randomitems and
295  * the living (a demonlord shouldn't cast from inside generator!)
296  */
298  }
299 
300  /* Here we'll handle custom monsters. In order to handle them correctly, especially in the fix_object
301  * method, we'll create a new temporary archetype containing defined values.
302  * Of course this doesn't apply when loading archetypes or artifacts.
303  */
304  if (QUERY_FLAG(op, FLAG_MONSTER) && op->arch && !object_can_merge(op, &op->arch->clone)) {
306 
307  temp->reference_count++;
308  temp->name = add_string(op->arch->name);
309  temp->tail_x = op->arch->tail_x;
310  temp->tail_y = op->arch->tail_y;
311  object_copy(op, &temp->clone);
312  temp->clone.inv = NULL;
313  temp->clone.env = NULL;
314  temp->clone.x = 0;
315  temp->clone.y = 0;
316  temp->clone.map = NULL;
317  if (FABS(temp->clone.speed) > MIN_ACTIVE_SPEED) {
318  /* Clone has a speed, so need to clear that because it isn't on a map.
319  * But we need to keep the value, because otherwise the customized object
320  * will have no speed (fix_player() will use the 0 value). So set it
321  * to zero, call object_update_speed() to remove it from active list, then
322  * set its speed back to the original.
323  */
324  temp->clone.speed = 0;
325  object_update_speed(&temp->clone);
326  temp->clone.speed = op->speed;
327  }
328 
329  temp->more = op->arch->more;
330  op->arch = temp;
331  /* LOG(llevDebug, "created temporary archetype for %s at %d,%d\n", op->name, op->x, op->y); */
332  }
333 }
334 
335 START_TEST(test_randommaps) {
336 #if 0
337  int test;
338  mapstruct *overlay;
339  object *check;
340 
341  for (test = 0; test < 50; test++)
342  do_run();
343 
344  for (test = 0; test < 50; test++) {
345  overlay = ready_map_name("../../rsc/bug_1727944_unique", MAP_PLAYER_UNIQUE);
346  fail_unless(overlay != NULL, "Couldn't load unique map ../../rsc/bug_1727944_unique");
347  fail_unless(GET_MAP_OB(overlay, 2, 3) != NULL, "No item on spot 2,3?");
348 
349  for (check = GET_MAP_OB(overlay, 2, 3)->above; check != NULL; check = check->above) {
350  fail_unless(check->type == ROD, "Found a non horn?");
351  fail_unless(check->inv != NULL, "Horn without a spell!");
352  fail_unless(check->inv->below == NULL, "Horn with 2 items in inventory.");
353  }
354  save_map(overlay, SAVE_MODE_OVERLAY);
355  delete_map(overlay);
356  }
357 #endif
358 
359 #if 0
360  int test;
361  archetype *horn = find_archetype("horn");
362  fail_unless(horn != NULL, "couldn't find archetype horn.");
363  archetype *horn2 = find_archetype("horn2");
364  fail_unless(horn2 != NULL, "couldn't find archetype horn2.");
365 
366  for (test = 0; test < 100000; test++) {
367  object *check = arch_to_object(RANDOM()%2 ? horn : horn2);
368 
370  fail_unless(check->inv != NULL, "horn without inventory!");
371  }
372 #endif
373 
374  int test, level, found = 0;
375  object *the_chest, *check;
376  mapstruct *map;
377  treasurelist *tlist = find_treasurelist("uncommon_items");
378  fail_unless(tlist != NULL, "couldn't find treasure list uncommon_items");
379 
380  for (test = 0; test < 10; test++) {
381  for (level = 1; level < 120; level++) {
382  map = get_empty_map(1, 1);
383  fail_unless(map != NULL, "failed to get empty map");
384  map->difficulty = level;
385 
386  the_chest = create_archetype("chest"); /* was "chest_2" */
387  fail_unless(the_chest != NULL, "failed to get chest");
388  the_chest->randomitems = tlist;
389  the_chest->stats.hp = RANDOM()%100;
390  object_insert_in_map_at(the_chest, map, NULL, 0, 0, 0);
392  the_chest = GET_MAP_OB(map, 0, 0);
393  fail_unless(the_chest != NULL, "failed to recover chest?");
394  for (check = the_chest->inv; check; check = check->below) {
395  if (check->type != ROD)
396  continue;
398  fail_unless(check->inv != NULL, "horn without inventory");
399  fail_unless(check->inv->below == NULL, "horn with 2 items");
400  fail_unless(check->randomitems == NULL, "horn with randomitems set");
401  found++;
402  }
403  delete_map(map);
404  }
405  }
406  fail_unless(found > 100, "didn't find 100 horn but %d??", found);
407 
408 }
409 END_TEST
410 
411 static Suite *bug_suite(void) {
412  Suite *s = suite_create("bug");
413  TCase *tc_core = tcase_create("Core");
414 
415  /*setup and teardown will be called before each test in testcase 'tc_core' */
416  tcase_add_checked_fixture(tc_core, setup, teardown);
417 
418  suite_add_tcase(s, tc_core);
419  tcase_add_test(tc_core, test_randommaps);
420  tcase_set_timeout(tc_core, 0);
421 
422  return s;
423 }
424 
425 int main(void) {
426  int nf;
427  Suite *s = bug_suite();
428  SRunner *sr = srunner_create(s);
429 
430  srunner_set_fork_status(sr, CK_NOFORK);
431  cctk_setdatadir(SOURCE_ROOT "lib");
432  init(0, NULL);
433 
434  srunner_set_xml(sr, LOGDIR "/bugs/bugtrack/1727944.xml");
435  srunner_set_log(sr, LOGDIR "/bugs/bugtrack/1727944.out");
436  srunner_run_all(sr, CK_ENV); /*verbosity from env variable*/
437  nf = srunner_ntests_failed(sr);
438  srunner_free(sr);
439  return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
440 }
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
global.h
BOW
@ BOW
Definition: object.h:121
BRACERS
@ BRACERS
Definition: object.h:220
llevError
@ llevError
Definition: logger.h:11
object_set_flag_inv
void object_set_flag_inv(object *op, int flag)
Definition: object.cpp:3232
RMParms::origin_map
char origin_map[RM_SIZE]
Definition: random_map.h:55
FABS
#define FABS(x)
Definition: define.h:22
WAND
@ WAND
Definition: object.h:223
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
get_empty_map
mapstruct * get_empty_map(int sizex, int sizey)
Definition: map.cpp:869
START_TEST
START_TEST(test_randommaps)
Definition: check_1727944.cpp:335
FLAG_GENERATOR
#define FLAG_GENERATOR
Definition: define.h:248
GLOVES
@ GLOVES
Definition: object.h:216
object::inv
object * inv
Definition: object.h:296
GIRDLE
@ GIRDLE
Definition: object.h:226
diamondslots.x
x
Definition: diamondslots.py:15
ready_map_name
mapstruct * ready_map_name(const char *name, int flags)
Definition: map.cpp:1793
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
get_archetype_struct
archetype * get_archetype_struct(void)
Definition: arch.cpp:195
FLAG_CONTENT_ON_GEN
#define FLAG_CONTENT_ON_GEN
Definition: define.h:365
cctk_setdatadir
void cctk_setdatadir(const char *datadir)
Definition: toolkit_common.cpp:69
RMParms::Ysize
int Ysize
Definition: random_map.h:75
ARMOUR
@ ARMOUR
Definition: object.h:123
WEAPON
@ WEAPON
Definition: object.h:122
set_random_map_variable
int set_random_map_variable(RMParms *rp, const char *buf)
Definition: reader.c:2579
AMULET
@ AMULET
Definition: object.h:142
RUNE
@ RUNE
Definition: object.h:243
set_materialname
void set_materialname(object *op)
Definition: utils.cpp:297
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
MAP_PLAYER_UNIQUE
#define MAP_PLAYER_UNIQUE
Definition: map.h:97
find_treasurelist
treasurelist * find_treasurelist(const char *name)
Definition: assets.cpp:249
RMParms::origin_y
int origin_y
Definition: random_map.h:88
Ice.tmp
int tmp
Definition: Ice.py:207
CREATOR
@ CREATOR
Definition: object.h:145
generate_random_map
mapstruct * generate_random_map(const char *OutFileName, RMParms *RP, char **use_layout, sstring reset_group)
Definition: random_map.cpp:75
buf
StringBuffer * buf
Definition: readable.cpp:1551
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2851
RMParms
Definition: random_map.h:14
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
SAVE_MODE_OVERLAY
#define SAVE_MODE_OVERLAY
Definition: map.h:123
FLAG_IS_A_TEMPLATE
#define FLAG_IS_A_TEMPLATE
Definition: define.h:366
RMParms::origin_x
int origin_x
Definition: random_map.h:89
CLOAK
@ CLOAK
Definition: object.h:207
apply_auto_fix
void apply_auto_fix(mapstruct *m)
Definition: main.cpp:258
toolkit_common.h
HELMET
@ HELMET
Definition: object.h:139
disinfect.map
map
Definition: disinfect.py:4
POTION
@ POTION
Definition: object.h:114
treasurelist
Definition: treasure.h:85
RMParms::final_map
char final_map[RM_SIZE]
Definition: random_map.h:57
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
ROD
@ ROD
Definition: object.h:112
bug_suite
static END_TEST Suite * bug_suite(void)
Definition: check_1727944.cpp:411
python_init.path
path
Definition: python_init.py:8
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1349
CONVERTER
@ CONVERTER
Definition: object.h:219
archetype
Definition: object.h:472
sproto.h
delete_map
void delete_map(mapstruct *m)
Definition: map.cpp:1730
RING
@ RING
Definition: object.h:188
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2098
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
MAP_WIDTH
#define MAP_WIDTH(m)
Definition: map.h:78
log_login.ip
ip
Definition: log_login.py:6
local_check_loaded_object
static void local_check_loaded_object(object *op)
Definition: check_1727944.cpp:184
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
IS_WEAPON
#define IS_WEAPON(op)
Definition: define.h:163
main
int main(void)
Definition: check_1727944.cpp:425
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
RANDOM
#define RANDOM()
Definition: define.h:644
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
EXIT
@ EXIT
Definition: object.h:184
bigchest.check
check
Definition: bigchest.py:10
create_archetype_by_object_name
object * create_archetype_by_object_name(const char *name)
Definition: arch.cpp:116
FIREWALL
@ FIREWALL
Definition: object.h:171
mapstruct
Definition: map.h:316
give.op
op
Definition: give.py:33
setup
static void setup(void)
Definition: check_1727944.cpp:40
find_archetype
archetype * find_archetype(const char *name)
Definition: assets.cpp:266
spell_mapping
const char *const spell_mapping[SPELL_MAPPINGS]
Definition: object.cpp:74
init
void init(int argc, char **argv)
Definition: init.cpp:1079
diamondslots.y
y
Definition: diamondslots.py:16
MAP_HEIGHT
#define MAP_HEIGHT(m)
Definition: map.h:80
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
level
int level
Definition: readable.cpp:1549
MIN_ACTIVE_SPEED
#define MIN_ACTIVE_SPEED
Definition: define.h:639
get_region_by_map
region * get_region_by_map(mapstruct *m)
Definition: region.cpp:72
object::randomitems
struct treasurelist * randomitems
Definition: object.h:393
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
ALTAR
@ ALTAR
Definition: object.h:125
save_map
int save_map(mapstruct *m, int flag)
Definition: map.cpp:1428
SCROLL
@ SCROLL
Definition: object.h:224
object::stats
living stats
Definition: object.h:376
calc_item_power
int calc_item_power(const object *op)
Definition: item.cpp:239
BOOTS
@ BOOTS
Definition: object.h:215
guildbuy.temp
def temp
Definition: guildbuy.py:26
SHIELD
@ SHIELD
Definition: object.h:138
teardown
static void teardown(void)
Definition: check_1727944.cpp:44
generate_artifact
void generate_artifact(object *op, int difficulty)
Definition: artifact.cpp:177
RMParms::region
struct region * region
Definition: random_map.h:96
SPELLBOOK
@ SPELLBOOK
Definition: object.h:206
RMParms::Xsize
int Xsize
Definition: random_map.h:74
living::hp
int16_t hp
Definition: living.h:40
object_can_merge
int object_can_merge(object *ob1, object *ob2)
Definition: object.cpp:433
llevDebug
@ llevDebug
Definition: logger.h:13
level
Definition: level.py:1