1 from server.config.world_data import world_db
2 from server.config.io import io_db
3 from server.io import log, strong_write
4 from server.utils import integer_test, id_setter
5 from server.world import build_fov_map, update_map_memory, set_world_inactive,\
9 def command_plugin(str_plugin):
10 """Run code in plugins/[str_plugin]."""
12 if (str_plugin.replace("_", "").isalnum()
13 and os.access("plugins/" + str_plugin, os.F_OK)):
14 exec(open("plugins/" + str_plugin).read())
16 print("Bad plugin name:", str_plugin)
20 """Send PONG line to server output file."""
21 strong_write(io_db["file_out"], "PONG\n")
25 """Abort server process."""
26 from server.io import save_world, atomic_write
27 from server.utils import opts
28 if None == opts.replay:
29 if world_db["WORLD_ACTIVE"]:
31 atomic_write(io_db["path_record"], io_db["record_chunk"],
33 raise SystemExit("received QUIT command")
36 def command_thingshere(str_y, str_x):
37 """Write to out file list of Things known to player at coordinate y, x."""
38 if world_db["WORLD_ACTIVE"]:
39 y = integer_test(str_y, 0, 255)
40 x = integer_test(str_x, 0, 255)
41 length = world_db["MAP_LENGTH"]
42 if None != y and None != x and y < length and x < length:
43 pos = (y * world_db["MAP_LENGTH"]) + x
44 strong_write(io_db["file_out"], "THINGS_HERE START\n")
45 if "v" == chr(world_db["Things"][0]["fovmap"][pos]):
46 for id in [id for tid in sorted(list(world_db["ThingTypes"]))
47 for id in world_db["Things"]
48 if not world_db["Things"][id]["carried"]
49 if world_db["Things"][id]["T_TYPE"] == tid
50 if y == world_db["Things"][id]["T_POSY"]
51 if x == world_db["Things"][id]["T_POSX"]]:
52 type = world_db["Things"][id]["T_TYPE"]
53 name = world_db["ThingTypes"][type]["TT_NAME"]
54 strong_write(io_db["file_out"], name + "\n")
56 for mt in [mt for tid in sorted(list(world_db["ThingTypes"]))
57 for mt in world_db["Things"][0]["T_MEMTHING"]
58 if mt[0] == tid if y == mt[1] if x == mt[2]]:
59 name = world_db["ThingTypes"][mt[0]]["TT_NAME"]
60 strong_write(io_db["file_out"], name + "\n")
61 strong_write(io_db["file_out"], "THINGS_HERE END\n")
63 print("Ignoring: Invalid map coordinates.")
65 print("Ignoring: Command only works on existing worlds.")
68 def command_seedrandomness(seed_string):
69 """Set rand seed to int(seed_string)."""
70 from server.utils import rand
71 val = integer_test(seed_string, 0, 4294967295)
76 def command_makeworld(seed_string):
77 """Call make_world()."""
78 val = integer_test(seed_string, 0, 4294967295)
80 from server.world import make_world
84 def command_maplength(maplength_string):
85 """Redefine map length. Invalidate map, therefore lose all things on it."""
86 val = integer_test(maplength_string, 1, 256)
88 from server.utils import libpr
89 world_db["MAP_LENGTH"] = val
90 world_db["MAP"] = False
92 world_db["Things"] = {}
93 libpr.set_maplength(val)
96 def command_worldactive(worldactive_string):
97 """Toggle world_db["WORLD_ACTIVE"] if possible.
99 An active world can always be set inactive. An inactive world can only be
100 set active with a "wait" ThingAction, and a player Thing (of ID 0), and a
101 map. On activation, rebuild all Things' FOVs, and the player's map memory.
103 val = integer_test(worldactive_string, 0, 1)
105 if 0 != world_db["WORLD_ACTIVE"]:
109 print("World already active.")
110 elif 0 == world_db["WORLD_ACTIVE"]:
112 for ThingAction in world_db["ThingActions"]:
113 if "wait" == world_db["ThingActions"][ThingAction]["TA_NAME"]:
116 player_exists = False
117 for Thing in world_db["Things"]:
121 if wait_exists and player_exists and world_db["MAP"]:
122 for id in world_db["Things"]:
123 if world_db["Things"][id]["T_LIFEPOINTS"]:
124 build_fov_map(world_db["Things"][id])
126 update_map_memory(world_db["Things"][id], False)
127 if not world_db["Things"][0]["T_LIFEPOINTS"]:
128 empty_fovmap = bytearray(b" " * world_db["MAP_LENGTH"] ** 2)
129 world_db["Things"][0]["fovmap"] = empty_fovmap
130 world_db["WORLD_ACTIVE"] = 1
132 print("Ignoring: Not all conditions for world activation met.")
135 def command_tid(id_string):
136 """Set ID of Thing to manipulate. ID unused? Create new one.
138 Default new Thing's type to the first available ThingType, others: zero.
140 id = id_setter(id_string, "Things", command_tid)
142 if world_db["ThingTypes"] == {}:
143 print("Ignoring: No ThingType to settle new Thing in.")
145 type = list(world_db["ThingTypes"].keys())[0]
146 from server.world import new_Thing
147 world_db["Things"][id] = new_Thing(type)
150 def command_ttid(id_string):
151 """Set ID of ThingType to manipulate. ID unused? Create new one.
153 Default new ThingType's TT_SYMBOL to "?", TT_CORPSE_ID to self, TT_TOOL to
156 id = id_setter(id_string, "ThingTypes", command_ttid)
158 world_db["ThingTypes"][id] = {
163 "TT_START_NUMBER": 0,
170 def command_taid(id_string):
171 """Set ID of ThingAction to manipulate. ID unused? Create new one.
173 Default new ThingAction's TA_EFFORT to 1, its TA_NAME to "wait".
175 id = id_setter(id_string, "ThingActions", command_taid, True)
177 world_db["ThingActions"][id] = {
183 def test_for_id_maker(object, category):
184 """Return decorator testing for object having "id" attribute."""
187 if hasattr(object, "id"):
190 print("Ignoring: No " + category +
191 " defined to manipulate yet.")
196 test_Thing_id = test_for_id_maker(command_tid, "Thing")
197 test_ThingType_id = test_for_id_maker(command_ttid, "ThingType")
198 test_ThingAction_id = test_for_id_maker(command_taid, "ThingAction")
202 def command_tcommand(str_int):
203 """Set T_COMMAND of selected Thing."""
204 val = integer_test(str_int, 0)
206 if 0 == val or val in world_db["ThingActions"]:
207 world_db["Things"][command_tid.id]["T_COMMAND"] = val
209 print("Ignoring: ThingAction ID belongs to no known ThingAction.")
213 def command_ttype(str_int):
214 """Set T_TYPE of selected Thing."""
215 val = integer_test(str_int, 0)
217 if val in world_db["ThingTypes"]:
218 world_db["Things"][command_tid.id]["T_TYPE"] = val
220 print("Ignoring: ThingType ID belongs to no known ThingType.")
224 def command_tcarries(str_int):
225 """Append int(str_int) to T_CARRIES of selected Thing.
227 The ID int(str_int) must not be of the selected Thing, and must belong to a
228 Thing with unset "carried" flag. Its "carried" flag will be set on owning.
230 val = integer_test(str_int, 0)
232 if val == command_tid.id:
233 print("Ignoring: Thing cannot carry itself.")
234 elif val in world_db["Things"] \
235 and not world_db["Things"][val]["carried"]:
236 world_db["Things"][command_tid.id]["T_CARRIES"].append(val)
237 world_db["Things"][val]["carried"] = True
239 print("Ignoring: Thing not available for carrying.")
240 # Note that the whole carrying structure is different from the C version:
241 # Carried-ness is marked by a "carried" flag, not by Things containing
246 def command_tmemthing(str_t, str_y, str_x):
247 """Add (int(str_t), int(str_y), int(str_x)) to selected Thing's T_MEMTHING.
249 The type must fit to an existing ThingType, and the position into the map.
251 type = integer_test(str_t, 0)
252 posy = integer_test(str_y, 0, 255)
253 posx = integer_test(str_x, 0, 255)
254 if None != type and None != posy and None != posx:
255 if type not in world_db["ThingTypes"] \
256 or posy >= world_db["MAP_LENGTH"] or posx >= world_db["MAP_LENGTH"]:
257 print("Ignoring: Illegal value for thing type or position.")
259 memthing = (type, posy, posx)
260 world_db["Things"][command_tid.id]["T_MEMTHING"].append(memthing)
264 def command_ttname(name):
265 """Set TT_NAME of selected ThingType."""
266 world_db["ThingTypes"][command_ttid.id]["TT_NAME"] = name
270 def command_tttool(name):
271 """Set TT_TOOL of selected ThingType."""
272 world_db["ThingTypes"][command_ttid.id]["TT_TOOL"] = name
276 def command_ttsymbol(char):
277 """Set TT_SYMBOL of selected ThingType. """
279 world_db["ThingTypes"][command_ttid.id]["TT_SYMBOL"] = char
281 print("Ignoring: Argument must be single character.")
285 def command_ttcorpseid(str_int):
286 """Set TT_CORPSE_ID of selected ThingType."""
287 val = integer_test(str_int, 0)
289 if val in world_db["ThingTypes"]:
290 world_db["ThingTypes"][command_ttid.id]["TT_CORPSE_ID"] = val
292 print("Ignoring: Corpse ID belongs to no known ThignType.")
296 def command_taname(name):
297 """Set TA_NAME of selected ThingAction.
299 The name must match a valid thing action function. If after the name
300 setting no ThingAction with name "wait" remains, call set_world_inactive().
302 if name == "wait" or name == "move" or name == "use" or name == "drop" \
303 or name == "pick_up":
304 world_db["ThingActions"][command_taid.id]["TA_NAME"] = name
305 if 1 == world_db["WORLD_ACTIVE"]:
307 for id in world_db["ThingActions"]:
308 if "wait" == world_db["ThingActions"][id]["TA_NAME"]:
314 print("Ignoring: Invalid action name.")
315 # In contrast to the original,naming won't map a function to a ThingAction.
318 def setter(category, key, min, max=None):
319 """Build setter for world_db([category + "s"][id])[key] to >=min/<=max."""
322 val = integer_test(val_string, min, max)
326 if category == "Thing":
327 id_store = command_tid
328 decorator = test_Thing_id
329 elif category == "ThingType":
330 id_store = command_ttid
331 decorator = test_ThingType_id
332 elif category == "ThingAction":
333 id_store = command_taid
334 decorator = test_ThingAction_id
338 val = integer_test(val_string, min, max)
340 world_db[category + "s"][id_store.id][key] = val
344 def setter_map(maptype):
345 """Set (world or Thing's) map of maptype's int(str_int)-th line to mapline.
347 If no map of maptype exists yet, initialize it with ' ' bytes first.
350 def valid_map_line(str_int, mapline):
351 val = integer_test(str_int, 0, 255)
353 if val >= world_db["MAP_LENGTH"]:
354 print("Illegal value for map line number.")
355 elif len(mapline) != world_db["MAP_LENGTH"]:
356 print("Map line length is unequal map width.")
361 def nonThingMap_helper(str_int, mapline):
362 val = valid_map_line(str_int, mapline)
364 length = world_db["MAP_LENGTH"]
365 if not world_db["MAP"]:
366 map = bytearray(b' ' * (length ** 2))
368 map = world_db["MAP"]
369 map[val * length:(val * length) + length] = mapline.encode()
370 if not world_db["MAP"]:
371 world_db["MAP"] = map
374 def ThingMap_helper(str_int, mapline):
375 val = valid_map_line(str_int, mapline)
377 length = world_db["MAP_LENGTH"]
378 if not world_db["Things"][command_tid.id][maptype]:
379 map = bytearray(b' ' * (length ** 2))
381 map = world_db["Things"][command_tid.id][maptype]
382 map[val * length:(val * length) + length] = mapline.encode()
383 if not world_db["Things"][command_tid.id][maptype]:
384 world_db["Things"][command_tid.id][maptype] = map
386 return nonThingMap_helper if maptype == "MAP" else ThingMap_helper
390 def setter_tpos(axis):
391 """Generate setter for T_POSX or T_POSY of selected Thing.
393 If world is active, rebuilds animate things' fovmap, player's memory map.
397 val = integer_test(str_int, 0, 255)
399 if val < world_db["MAP_LENGTH"]:
400 world_db["Things"][command_tid.id]["T_POS" + axis] = val
401 if world_db["WORLD_ACTIVE"] \
402 and world_db["Things"][command_tid.id]["T_LIFEPOINTS"]:
403 build_fov_map(world_db["Things"][command_tid.id])
404 if 0 == command_tid.id:
405 update_map_memory(world_db["Things"][command_tid.id])
407 print("Ignoring: Position is outside of map.")
411 def set_command(action):
412 """Set player's T_COMMAND, then call turn_over()."""
413 id = [x for x in world_db["ThingActions"]
414 if world_db["ThingActions"][x]["TA_NAME"] == action][0]
415 world_db["Things"][0]["T_COMMAND"] = id
420 """Try "wait" as player's T_COMMAND."""
425 """Try "pick_up" as player's T_COMMAND"."""
426 t = world_db["Things"][0]
427 ids = [id for id in world_db["Things"] if id
428 if not world_db["Things"][id]["carried"]
429 if world_db["Things"][id]["T_POSY"] == t["T_POSY"]
430 if world_db["Things"][id]["T_POSX"] == t["T_POSX"]]
432 log("NOTHING to pick up.")
434 set_command("pick_up")
437 def play_drop(str_arg):
438 """Try "drop" as player's T_COMMAND, int(str_arg) as T_ARGUMENT / slot."""
439 t = world_db["Things"][0]
440 if 0 == len(t["T_CARRIES"]):
441 log("You have NOTHING to drop in your inventory.")
443 val = integer_test(str_arg, 0, 255)
444 if None != val and val < len(t["T_CARRIES"]):
445 world_db["Things"][0]["T_ARGUMENT"] = val
448 print("Illegal inventory index.")
451 def play_use(str_arg):
452 """Try "use" as player's T_COMMAND, int(str_arg) as T_ARGUMENT / slot."""
453 t = world_db["Things"][0]
454 if 0 == len(t["T_CARRIES"]):
455 log("You have NOTHING to use in your inventory.")
457 val = integer_test(str_arg, 0, 255)
458 if None != val and val < len(t["T_CARRIES"]):
459 id = t["T_CARRIES"][val]
460 type = world_db["Things"][id]["T_TYPE"]
461 if not world_db["ThingTypes"][type]["TT_TOOL"] == "food":
462 log("You CAN'T consume this thing.")
464 world_db["Things"][0]["T_ARGUMENT"] = val
467 print("Illegal inventory index.")
470 def play_move(str_arg):
471 """Try "move" as player's T_COMMAND, str_arg as T_ARGUMENT / direction."""
472 from server.config.world_data import directions_db
473 t = world_db["Things"][0]
474 if not str_arg in directions_db:
475 print("Illegal move direction string.")
477 dir = ord(directions_db[str_arg])
478 from server.utils import mv_yx_in_dir_legal
479 move_result = mv_yx_in_dir_legal(chr(dir), t["T_POSY"], t["T_POSX"])
480 if 1 == move_result[0]:
481 pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
482 if ord(".") == world_db["MAP"][pos]:
483 world_db["Things"][0]["T_ARGUMENT"] = dir
486 log("You CAN'T move there.")
490 """Call ai() on player Thing, then turn_over()."""
491 from server.ai import ai
492 ai(world_db["Things"][0])