home · contact · privacy
898f1025a3cd3065e6bbc9fbf06a1e2adcdbb831
[plomrogue] / plugins / server / TheCrawlingEater.py
1 # This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
2 # or any later version. For details on its copyright, license, and warranties,
3 # see the file NOTICE in the root directory of the PlomRogue source package.
4
5
6 from server.config.world_data import world_db
7
8
9 def command_help(str_int):
10     val = integer_test(str_int, 0, 4)
11     if None != val:
12         log(str_int)
13
14
15 def command_ai():
16     if world_db["WORLD_ACTIVE"]:
17         world_db["ai"](world_db["Things"][0])
18         world_db["turn_over"]()
19
20
21 def play_drink():
22     if not (action_exists("drink") and world_db["WORLD_ACTIVE"]
23             and world_db["Things"][0]["T_LIFEPOINTS"] > 0):
24         return
25     pos = world_db["Things"][0]["pos"]
26     if not (chr(world_db["MAP"][pos]) == "0"
27             and world_db["wetmap"][pos] > ord("0")):
28         log("NOTHING to drink here.")
29         return
30     elif world_db["Things"][0]["T_KIDNEY"] >= 32:
31         log("You're too FULL to drink more.")
32         return
33     world_db["set_command"]("drink")
34
35
36 def actor_drink(t):
37     pos = t["pos"]
38     if chr(world_db["MAP"][pos]) == "0" and \
39                 world_db["wetmap"][pos] > ord("0") and t["T_KIDNEY"] < 32:
40         if world_db["Things"][0] == t:
41             log("You DRINK.")
42         t["T_KIDNEY"] += 1
43         world_db["wetmap"][pos] -= 1
44         if world_db["wetmap"][pos] == ord("0"):
45             world_db["MAP"][pos] = ord("0")
46     elif t == world_db["Things"][0]:
47         log("YOU FAIL TO DRINK " + str(world_db["MAP"][pos] - ord("0")))
48
49
50 def play_pee():
51     if not (action_exists("pee") and world_db["WORLD_ACTIVE"]
52             and world_db["Things"][0]["T_LIFEPOINTS"] > 0):
53         return
54     if world_db["Things"][0]["T_BLADDER"] < 1:
55         log("Nothing to drop from empty bladder.")
56         return
57     world_db["set_command"]("pee")
58
59
60 def actor_pee(t):
61     if t["T_BLADDER"] < 1:
62         return
63     if t == world_db["Things"][0]:
64         log("You LOSE fluid.")
65     if not world_db["test_air"](t):
66         return
67     t["T_BLADDER"] -= 1
68     world_db["wetmap"][t["pos"]] += 1
69
70
71 def play_drop():
72     if not (action_exists("drop") and world_db["WORLD_ACTIVE"]
73             and world_db["Things"][0]["T_LIFEPOINTS"] > 0):
74         return
75     if world_db["Things"][0]["T_BOWEL"] < 1:
76         log("Nothing to drop from empty bowel.")
77         return
78     world_db["set_command"]("drop")
79
80
81 def actor_drop(t):
82     if t["T_BOWEL"] < 1:
83         return
84     if t == world_db["Things"][0]:
85         log("You DROP waste.")
86     if not world_db["test_air"](t):
87         return
88     world_db["MAP"][t["pos"]] += 1
89     t["T_BOWEL"] -= 1
90
91
92 def play_move(str_arg):
93     if not (action_exists("move") and world_db["WORLD_ACTIVE"]
94             and world_db["Things"][0]["T_LIFEPOINTS"] > 0):
95         return
96     from server.config.world_data import directions_db, symbols_passable
97     t = world_db["Things"][0]
98     if not str_arg in directions_db:
99         print("Illegal move direction string.")
100         return
101     d = ord(directions_db[str_arg])
102     from server.utils import mv_yx_in_dir_legal
103     move_result = mv_yx_in_dir_legal(chr(d), t["T_POSY"], t["T_POSX"])
104     if 1 == move_result[0]:
105         pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
106         hitted = [tid for tid in world_db["Things"]
107                   if world_db["Things"][tid]["T_POSY"] == move_result[1]
108                   if world_db["Things"][tid]["T_POSX"] == move_result[2]]
109         if len(hitted) > 0:
110             if t["T_STOMACH"] >= 32 and t["T_KIDNEY"] >= 32:
111                 if t == world_db["Things"][0]:
112                     log("You're too FULL to suck from another creature.")
113                 return
114             world_db["Things"][0]["T_ARGUMENT"] = d
115             world_db["set_command"]("eat")
116             return
117         if chr(world_db["MAP"][pos]) in "34":
118             if t["T_STOMACH"] >= 32:
119                 if t == world_db["Things"][0]:
120                     log("You're too FULL to eat.")
121                 return
122             world_db["Things"][0]["T_ARGUMENT"] = d
123             world_db["set_command"]("eat")
124             return
125         if chr(world_db["MAP"][pos]) in symbols_passable:
126             world_db["Things"][0]["T_ARGUMENT"] = d
127             world_db["set_command"]("move")
128             return
129     log("You CAN'T eat your way through there.")
130
131
132 def actor_eat(t):
133     from server.utils import mv_yx_in_dir_legal, rand
134     from server.config.world_data import symbols_passable
135     passable = False
136     move_result = mv_yx_in_dir_legal(chr(t["T_ARGUMENT"]),
137                                      t["T_POSY"], t["T_POSX"])
138     if 1 == move_result[0]:
139         pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
140         hitted = [tid for tid in world_db["Things"]
141                   if world_db["Things"][tid]["T_POSY"] == move_result[1]
142                   if world_db["Things"][tid]["T_POSX"] == move_result[2]]
143         if len(hitted):
144             hit_id = hitted[0]
145             hitted_tid = world_db["Things"][hit_id]["T_TYPE"]
146             if t == world_db["Things"][0]:
147                 hitted_name = world_db["ThingTypes"][hitted_tid]["TT_NAME"]
148                 log("You SUCK from " + hitted_name + ".")
149             elif 0 == hit_id:
150                 hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
151                 log(hitter_name +" SUCKS from you.")
152             hitted = world_db["Things"][hit_id]
153             if t["T_STOMACH"] < 32:
154                 t["T_STOMACH"] = t["T_STOMACH"] + 1
155                 hitted["T_STOMACH"] -= 1
156             if t["T_KIDNEY"] < 32:
157                 t["T_KIDNEY"] = t["T_KIDNEY"] + 1
158                 hitted["T_KIDNEY"] -= 1
159             return
160         passable = chr(world_db["MAP"][pos]) in symbols_passable
161     if passable and t == world_db["Things"][0]:
162         log("You try to EAT, but fail.")
163     else:
164         height = world_db["MAP"][pos] - ord("0")
165         if t["T_STOMACH"] >= 32 or height == 5:
166             return
167         t["T_STOMACH"] += 1
168         if t == world_db["Things"][0]:
169             log("You EAT.")
170         eaten = (height == 3 and 0 == int(rand.next() % 2)) or \
171                 (height == 4 and 0 == int(rand.next() % 5))
172         if eaten:
173             world_db["MAP"][pos] = ord("0")
174             if t["T_STOMACH"] > 32:
175                 t["T_STOMACH"] = 32
176
177
178 def actor_move(t):
179     from server.build_fov_map import build_fov_map
180     from server.utils import mv_yx_in_dir_legal, rand
181     from server.config.world_data import symbols_passable
182     passable = False
183     move_result = mv_yx_in_dir_legal(chr(t["T_ARGUMENT"]),
184                                      t["T_POSY"], t["T_POSX"])
185     if 1 == move_result[0]:
186         pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
187         hitted = [tid for tid in world_db["Things"]
188                   if world_db["Things"][tid]["T_POSY"] == move_result[1]
189                   if world_db["Things"][tid]["T_POSX"] == move_result[2]]
190         if len(hitted):
191             hit_id = hitted[0]
192             hitted_tid = world_db["Things"][hit_id]["T_TYPE"]
193             if t == world_db["Things"][0]:
194                 hitted_name = world_db["ThingTypes"][hitted_tid]["TT_NAME"]
195                 log("You BUMP into " + hitted_name + ".")
196             elif 0 == hit_id:
197                 hitter_name = world_db["ThingTypes"][t["T_TYPE"]]["TT_NAME"]
198                 log(hitter_name +" BUMPS into you.")
199             return
200         passable = chr(world_db["MAP"][pos]) in symbols_passable
201     if passable:
202         t["T_POSY"] = move_result[1]
203         t["T_POSX"] = move_result[2]
204         t["pos"] = move_result[1] * world_db["MAP_LENGTH"] + move_result[2]
205         world_db["soundmap"][t["pos"]] = ord("9")
206     elif t == world_db["Things"][0]:
207         log("You try to MOVE there, but fail.")
208
209
210 def test_hole(t):
211     if world_db["MAP"][t["pos"]] == ord("-"):
212         world_db["die"](t, "You FALL in a hole, and die.")
213         return False
214     return True
215 world_db["test_hole"] = test_hole
216
217
218 def test_air(t):
219     if world_db["terrain_fullness"](t["pos"]) > 5:
220         world_db["die"](t, "You SUFFOCATE")
221         return False
222     return True
223 world_db["test_air"] = test_air
224
225
226 def die(t, message):
227     t["T_LIFEPOINTS"] = 0
228     if t == world_db["Things"][0]:
229         t["fovmap"] = bytearray(b' ' * (world_db["MAP_LENGTH"] ** 2))
230         t["T_MEMMAP"][t["pos"]] = ord("@")
231         log(message)
232     else:
233         world_db["MAP"][t["pos"]] = ord("5")
234         world_db["HUMILITY"] = t["T_KIDNEY"] + t["T_BLADDER"] + \
235             (world_db["wetmap"][t["pos"]] - ord("0"))
236         world_db["wetmap"][t["pos"]] = 0
237         tid = next(tid for tid in world_db["Things"]
238                    if world_db["Things"][tid] == t)
239         del world_db["Things"][tid]
240 world_db["die"] = die
241
242
243 def make_map():
244     from server.make_map import new_pos, is_neighbor
245     from server.utils import rand
246     world_db["MAP"] = bytearray(b'5' * (world_db["MAP_LENGTH"] ** 2))
247     length = world_db["MAP_LENGTH"]
248     add_half_width = (not (length % 2)) * int(length / 2)
249     world_db["MAP"][int((length ** 2) / 2) + add_half_width] = ord("4")
250     while (1):
251         y, x, pos = new_pos()
252         if "5" == chr(world_db["MAP"][pos]) and is_neighbor((y, x), "4"):
253             if y == 0 or y == (length - 1) or x == 0 or x == (length - 1):
254                 break
255             world_db["MAP"][pos] = ord("4")
256     n_ground = int((length ** 2) / 16)
257     i_ground = 0
258     while (i_ground <= n_ground):
259         single_allowed = rand.next() % 32
260         y, x, pos = new_pos()
261         if "4" == chr(world_db["MAP"][pos]) \
262                 and ((not single_allowed) or is_neighbor((y, x), "0")):
263             world_db["MAP"][pos] = ord("0")
264             i_ground += 1
265     n_water = int((length ** 2) / 32)
266     i_water = 0
267     while (i_water <= n_water):
268         y, x, pos = new_pos()
269         if ord("0") == world_db["MAP"][pos] and \
270                 ord("0") == world_db["wetmap"][pos]:
271             world_db["wetmap"][pos] = ord("3")
272             i_water += 1
273
274
275 def calc_effort(ta, t):
276     from server.utils import mv_yx_in_dir_legal
277     if ta["TA_NAME"] == "move":
278         move_result = mv_yx_in_dir_legal(chr(t["T_ARGUMENT"]),
279                                          t["T_POSY"], t["T_POSX"])
280         if 1 == move_result[0]:
281             pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
282             narrowness = world_db["MAP"][pos] - ord("0")
283             return 2 ** narrowness
284     return 1
285 world_db["calc_effort"] = calc_effort
286
287
288 def turn_over():
289     from server.ai import ai
290     from server.config.actions import action_db
291     from server.update_map_memory import update_map_memory
292     from server.io import try_worldstate_update
293     from server.config.io import io_db
294     from server.utils import rand
295     from server.build_fov_map import build_fov_map
296     while world_db["Things"][0]["T_LIFEPOINTS"]:
297         for tid in [tid for tid in world_db["Things"]]:
298             if not tid in world_db["Things"]:
299                 continue
300             t = world_db["Things"][tid]
301             if t["T_LIFEPOINTS"]:
302                 if not (world_db["test_air"](t) and world_db["test_hole"](t)):
303                     continue
304                 if not t["T_COMMAND"]:
305                     update_map_memory(t)
306                     build_fov_map(t)
307                     if 0 == tid:
308                         return
309                     world_db["ai"](t)
310                 if t["T_LIFEPOINTS"]:
311                     t["T_PROGRESS"] += 1
312                     taid = [a for a in world_db["ThingActions"]
313                               if a == t["T_COMMAND"]][0]
314                     ThingAction = world_db["ThingActions"][taid]
315                     effort = world_db["calc_effort"](ThingAction, t)
316                     if t["T_PROGRESS"] >= effort:
317                         action = action_db["actor_" + ThingAction["TA_NAME"]]
318                         action(t)
319                         t["T_COMMAND"] = 0
320                         t["T_PROGRESS"] = 0
321                     if t["T_BOWEL"] > 16:
322                         if 0 == (rand.next() % (33 - t["T_BOWEL"])):
323                             action_db["actor_drop"](t)
324                     if t["T_BLADDER"] > 16:
325                         if 0 == (rand.next() % (33 - t["T_BLADDER"])):
326                             action_db["actor_pee"](t)
327                     if 0 == world_db["TURN"] % 5:
328                         t["T_STOMACH"] -= 1
329                         t["T_BOWEL"] += 1
330                         t["T_KIDNEY"] -= 1
331                         t["T_BLADDER"] += 1
332                     if t["T_STOMACH"] <= 0:
333                         world_db["die"](t, "You DIE of hunger.")
334                     elif t["T_KIDNEY"] <= 0:
335                         world_db["die"](t, "You DIE of dehydration.")
336         for pos in range(world_db["MAP_LENGTH"] ** 2):
337             wetness = world_db["wetmap"][pos] - ord("0")
338             height = world_db["MAP"][pos] - ord("0")
339             if height == 0 and wetness > 0 \
340                     and 0 == rand.next() % ((2 ** 13) / (2 ** wetness)):
341                 world_db["MAP"][pos] = ord("-")
342             if ((wetness > 0 and height != 0) or wetness > 1) \
343                 and 0 == rand.next() % 5:
344                 world_db["wetmap"][pos] -= 1
345                 world_db["HUMIDITY"] += 1
346         if world_db["HUMIDITY"] > 0:
347             if world_db["HUMIDITY"] > 2 and 0 == rand.next() % 2:
348                 world_db["NEW_SPAWN"] += 1
349                 world_db["HUMIDITY"] -= 1
350             if world_db["NEW_SPAWN"] >= 16:
351                 world_db["NEW_SPAWN"] -= 16
352                 from server.new_thing import new_Thing
353                 while 1:
354                     y = rand.next() % world_db["MAP_LENGTH"]
355                     x = rand.next() % world_db["MAP_LENGTH"]
356                     if chr(world_db["MAP"][y * world_db["MAP_LENGTH"] + x]) !=\
357                         "5":
358                         from server.utils import id_setter
359                         tid = id_setter(-1, "Things")
360                         world_db["Things"][tid] = new_Thing(
361                             world_db["PLAYER_TYPE"], (y, x))
362                         pos = y * world_db["MAP_LENGTH"] + x
363                         break
364             positions_to_wet = []
365             for pos in range(world_db["MAP_LENGTH"] ** 2):
366                 if world_db["MAP"][pos] == ord("0") \
367                         and world_db["wetmap"][pos] < ord("5"):
368                     positions_to_wet += [pos]
369             while world_db["HUMIDITY"] > 0 and len(positions_to_wet) > 0:
370                 select = rand.next() % len(positions_to_wet)
371                 pos = positions_to_wet[select]
372                 world_db["wetmap"][pos] += 1
373                 positions_to_wet.remove(pos)
374                 world_db["HUMIDITY"] -= 1
375         for pos in range(world_db["MAP_LENGTH"] ** 2):
376             if world_db["soundmap"][pos] > ord("0"):
377                 world_db["soundmap"][pos] -= 1
378         world_db["TURN"] += 1
379         io_db["worldstate_updateable"] = True
380         try_worldstate_update()
381 world_db["turn_over"] = turn_over
382
383
384 def command_ai():
385     """Call ai() on player Thing, then turn_over()."""
386     if not (world_db["WORLD_ACTIVE"]
387             and world_db["Things"][0]["T_LIFEPOINTS"] > 0):
388         return
389     from server.ai import ai
390     ai(world_db["Things"][0])
391     world_db["turn_over"]()
392
393
394 def set_command(action):
395     """Set player's T_COMMAND, then call turn_over()."""
396     tid = [x for x in world_db["ThingActions"]
397            if world_db["ThingActions"][x]["TA_NAME"] == action][0]
398     world_db["Things"][0]["T_COMMAND"] = tid
399     world_db["turn_over"]()
400 world_db["set_command"] = set_command
401
402
403 def play_wait():
404     """Try "wait" as player's T_COMMAND."""
405     if world_db["WORLD_ACTIVE"]:
406         world_db["set_command"]("wait")
407
408
409 def save_maps():
410     length = world_db["MAP_LENGTH"]
411     string = ""
412     for i in range(length):
413         line = world_db["wetmap"][i * length:(i * length) + length].decode()
414         string = string + "WETMAP" + " "  + str(i) + " " + line + "\n"
415     for i in range(length):
416         line = world_db["soundmap"][i * length:(i * length) + length].decode()
417         string = string + "SOUNDMAP" + " "  + str(i) + " " + line + "\n"
418     return string
419
420
421 def soundmapset(str_int, mapline):
422     def valid_map_line(str_int, mapline):
423         from server.utils import integer_test
424         val = integer_test(str_int, 0, 255)
425         if None != val:
426             if val >= world_db["MAP_LENGTH"]:
427                 print("Illegal value for map line number.")
428             elif len(mapline) != world_db["MAP_LENGTH"]:
429                 print("Map line length is unequal map width.")
430             else:
431                 return val
432         return None
433     val = valid_map_line(str_int, mapline)
434     if None != val:
435         length = world_db["MAP_LENGTH"]
436         if not world_db["soundmap"]:
437             m = bytearray(b' ' * (length ** 2))
438         else:
439             m = world_db["soundmap"]
440         m[val * length:(val * length) + length] = mapline.encode()
441         if not world_db["soundmap"]:
442             world_db["soundmap"] = m
443
444
445 def wetmapset(str_int, mapline):
446     def valid_map_line(str_int, mapline):
447         from server.utils import integer_test
448         val = integer_test(str_int, 0, 255)
449         if None != val:
450             if val >= world_db["MAP_LENGTH"]:
451                 print("Illegal value for map line number.")
452             elif len(mapline) != world_db["MAP_LENGTH"]:
453                 print("Map line length is unequal map width.")
454             else:
455                 return val
456         return None
457     val = valid_map_line(str_int, mapline)
458     if None != val:
459         length = world_db["MAP_LENGTH"]
460         if not world_db["wetmap"]:
461             m = bytearray(b' ' * (length ** 2))
462         else:
463             m = world_db["wetmap"]
464         m[val * length:(val * length) + length] = mapline.encode()
465         if not world_db["wetmap"]:
466             world_db["wetmap"] = m
467
468
469 def write_soundmap():
470     from server.worldstate_write_helpers import write_map
471     length = world_db["MAP_LENGTH"]
472     return write_map(world_db["soundmap"], world_db["MAP_LENGTH"])
473
474
475 def write_wetmap():
476     from server.worldstate_write_helpers import write_map
477     length = world_db["MAP_LENGTH"]
478     visible_wetmap = bytearray(b' ' * (length ** 2))
479     for i in range(length ** 2):
480         if world_db["Things"][0]["fovmap"][i] == ord('v'):
481             visible_wetmap[i] = world_db["wetmap"][i]
482     return write_map(visible_wetmap, world_db["MAP_LENGTH"])
483
484
485 def get_dir_to_target(t, target):
486
487     from server.utils import rand, libpr, c_pointer_to_bytearray
488     from server.config.world_data import symbols_passable
489
490     def get_map_score(pos):
491         result = libpr.get_map_score(pos)
492         if result < 0:
493             raise RuntimeError("No score map allocated for get_map_score().")
494         return result
495
496     def zero_score_map_where_char_on_memdepthmap(c):
497         map = c_pointer_to_bytearray(t["T_MEMDEPTHMAP"])
498         if libpr.zero_score_map_where_char_on_memdepthmap(c, map):
499             raise RuntimeError("No score map allocated for "
500                                "zero_score_map_where_char_on_memdepthmap().")
501
502     def set_map_score(pos, score):
503         test = libpr.set_map_score(pos, score)
504         if test:
505             raise RuntimeError("No score map allocated for set_map_score().")
506
507     def set_movement_cost_map():
508         copy_memmap = t["T_MEMMAP"][:]
509         copy_memmap.replace(b' ', b'4')
510         memmap = c_pointer_to_bytearray(copy_memmap)
511         if libpr.TCE_set_movement_cost_map(memmap):
512             raise RuntimeError("No movement cost map allocated for "
513                                "set_movement_cost_map().")
514
515     def animates_in_fov(maplength):
516         return [Thing for Thing in world_db["Things"].values()
517                 if Thing["T_LIFEPOINTS"] and 118 == t["fovmap"][Thing["pos"]]
518                 and (not Thing == t)]
519
520     def seeing_thing():
521         def exists(gen):
522             try:
523                 next(gen)
524             except StopIteration:
525                 return False
526             return True
527         mapsize = world_db["MAP_LENGTH"] ** 2
528         if target == "food" and t["T_MEMMAP"]:
529             return exists(pos for pos in range(mapsize)
530                            if ord("2") < t["T_MEMMAP"][pos] < ord("5"))
531         elif target == "fluid_certain" and t["fovmap"]:
532             return exists(pos for pos in range(mapsize)
533                            if t["fovmap"] == ord("v")
534                            if world_db["MAP"][pos] == ord("0")
535                            if world_db["wetmap"][pos] > ord("0"))
536         elif target == "fluid_potential" and t["T_MEMMAP"] and t["fovmap"]:
537             return exists(pos for pos in range(mapsize)
538                            if t["T_MEMMAP"][pos] == ord("0")
539                            if t["fovmap"] != ord("v"))
540         elif target == "space_big" and t["T_MEMMAP"] and t["fovmap"]:
541             return exists(pos for pos in range(mapsize)
542                           if ord("0") <= t["T_MEMMAP"][pos] <= ord("2")
543                           if (t["fovmap"] != ord("v")
544                               or world_db["terrain_fullness"](pos) < 5))
545         elif target in {"hunt", "flee"} and t["fovmap"]:
546             return exists(Thing for
547                           Thing in animates_in_fov(world_db["MAP_LENGTH"])) \
548                 or exists(pos for pos in range(mapsize)
549                           if world_db["soundmap"][pos] > ord("0")
550                           if t["fovmap"][pos] != ord("v"))
551         return False
552
553     def init_score_map():
554         mapsize = world_db["MAP_LENGTH"] ** 2
555         test = libpr.TCE_init_score_map()
556         [set_map_score(pos, 65535) for pos in range(mapsize)
557          if chr(t["T_MEMMAP"][pos]) in "5-"]
558         set_movement_cost_map()
559         if test:
560             raise RuntimeError("Malloc error in init_score_map().")
561         if target == "food" and t["T_MEMMAP"]:
562             [set_map_score(pos, 0) for pos in range(mapsize)
563              if ord("2") < t["T_MEMMAP"][pos] < ord("5")]
564         elif target == "fluid_certain" and t["fovmap"]:
565             [set_map_score(pos, 0) for pos in range(mapsize)
566              if t["fovmap"] == ord("v")
567              if world_db["MAP"][pos] == ord("0")
568              if world_db["wetmap"][pos] > ord("0")]
569         elif target == "fluid_potential" and t["T_MEMMAP"] and t["fovmap"]:
570             [set_map_score(pos, 0) for pos in range(mapsize)
571              if t["T_MEMMAP"][pos] == ord("0")
572              if t["fovmap"] != ord("v")]
573         elif target == "space" and t["T_MEMMAP"] and t["fovmap"]:
574             [set_map_score(pos, 0) for pos in range(mapsize)
575              if ord("0") <= t["T_MEMMAP"][pos] <= ord("2")
576              if (t["fovmap"] != ord("v")
577                  or world_db["terrain_fullness"](pos) < 5)]
578         elif target == "search":
579             zero_score_map_where_char_on_memdepthmap(mem_depth_c[0])
580         elif target in {"hunt", "flee"}:
581             [set_map_score(Thing["pos"], 0) for
582              Thing in animates_in_fov(world_db["MAP_LENGTH"])]
583             [set_map_score(pos, 0) for pos in range(mapsize)
584              if world_db["soundmap"][pos] > ord("0")
585              if t["fovmap"][pos] != ord("v")]
586
587     def rand_target_dir(neighbors, cmp, dirs):
588         candidates = []
589         n_candidates = 0
590         for i in range(len(dirs)):
591             if cmp == neighbors[i]:
592                 candidates.append(dirs[i])
593                 n_candidates += 1
594         return candidates[rand.next() % n_candidates] if n_candidates else 0
595
596     def get_neighbor_scores(dirs, eye_pos):
597         scores = []
598         if libpr.ready_neighbor_scores(eye_pos):
599             raise RuntimeError("No score map allocated for " +
600                                "ready_neighbor_scores.()")
601         for i in range(len(dirs)):
602             scores.append(libpr.get_neighbor_score(i))
603         return scores
604
605     def get_dir_from_neighbors():
606         import math
607         dir_to_target = False
608         dirs = "edcxsw"
609         eye_pos = t["pos"]
610         neighbors = get_neighbor_scores(dirs, eye_pos)
611         minmax_start = 0 if "flee" == target else 65535 - 1
612         minmax_neighbor = minmax_start
613         for i in range(len(dirs)):
614             if ("flee" == target and get_map_score(t["pos"]) < neighbors[i] and
615                 minmax_neighbor < neighbors[i] and 65535 != neighbors[i]) \
616                or ("flee" != target and minmax_neighbor > neighbors[i]):
617                 minmax_neighbor = neighbors[i]
618         if minmax_neighbor != minmax_start:
619             dir_to_target = rand_target_dir(neighbors, minmax_neighbor, dirs)
620         if "flee" == target:
621             distance = get_map_score(t["pos"])
622             fear_distance = 5
623             attack_distance = 1
624             if not dir_to_target:
625                 if attack_distance >= distance:
626                     dir_to_target = rand_target_dir(neighbors,
627                                                     distance - 1, dirs)
628             elif dir_to_target and fear_distance < distance:
629                 dir_to_target = 0
630         return dir_to_target, minmax_neighbor
631
632     dir_to_target = False
633     mem_depth_c = b' '
634     run_i = 9 + 1 if "search" == target else 1
635     minmax_neighbor = 0
636     while run_i and not dir_to_target and \
637             ("search" == target or seeing_thing()):
638         run_i -= 1
639         init_score_map()
640         mem_depth_c = b'9' if b' ' == mem_depth_c \
641             else bytes([mem_depth_c[0] - 1])
642         if libpr.TCE_dijkstra_map_with_movement_cost():
643             raise RuntimeError("No score map allocated for dijkstra_map().")
644         dir_to_target, minmax_neighbor = get_dir_from_neighbors()
645         libpr.free_score_map()
646         if dir_to_target and str == type(dir_to_target):
647             action = "move"
648             from server.utils import mv_yx_in_dir_legal
649             move_result = mv_yx_in_dir_legal(dir_to_target, t["T_POSY"],
650                                                             t["T_POSX"])
651             if 1 != move_result[0]:
652                 return False, 0
653             pos = (move_result[1] * world_db["MAP_LENGTH"]) + move_result[2]
654             hitted = [tid for tid in world_db["Things"]
655                       if world_db["Things"][tid]["pos"] == pos]
656             if world_db["MAP"][pos] > ord("2") or len(hitted) > 0:
657                 action = "eat"
658             t["T_COMMAND"] = [taid for taid in world_db["ThingActions"]
659                               if world_db["ThingActions"][taid]["TA_NAME"]
660                               == action][0]
661             t["T_ARGUMENT"] = ord(dir_to_target)
662     return dir_to_target, minmax_neighbor
663 world_db["get_dir_to_target"] = get_dir_to_target
664
665
666 def terrain_fullness(pos):
667     return (world_db["MAP"][pos] - ord("0")) + \
668         (world_db["wetmap"][pos] - ord("0"))
669 world_db["terrain_fullness"] = terrain_fullness
670
671
672 def ai(t):
673
674     if t["T_LIFEPOINTS"] == 0:
675         return
676
677     def standing_on_fluid(t):
678         if world_db["MAP"][t["pos"]] == ord("0") and \
679             world_db["wetmap"][t["pos"]] > ord("0"):
680                 return True
681         else:
682             return False
683
684     def thing_action_id(name):
685         return [taid for taid in world_db["ThingActions"]
686                 if world_db["ThingActions"][taid]
687                 ["TA_NAME"] == name][0]
688
689     t["T_COMMAND"] = thing_action_id("wait")
690     needs = {
691         "flee": 24,
692         "safe_pee": (world_db["terrain_fullness"](t["pos"]) * t["T_BLADDER"]) / 4,
693         "safe_drop": (world_db["terrain_fullness"](t["pos"]) * t["T_BOWEL"]) / 4,
694         "food": 33 - t["T_STOMACH"],
695         "fluid_certain": 33 - t["T_KIDNEY"],
696         "fluid_potential": 32 - t["T_KIDNEY"],
697         "search": 1,
698     }
699     from operator import itemgetter
700     needs = sorted(needs.items(), key=itemgetter(1,0))
701     needs.reverse()
702     for need in needs:
703         if need[1] > 0:
704             if need[0] in {"fluid_certain", "fluid_potential"}:
705                 if standing_on_fluid(t):
706                     t["T_COMMAND"] = thing_action_id("drink")
707                     return
708                 elif t["T_BLADDER"] > 0 and \
709                          world_db["MAP"][t["pos"]] == ord("0"):
710                     t["T_COMMAND"] = thing_action_id("pee")
711                     return
712             elif need[0] in {"safe_pee", "safe_drop"}:
713                 action_name = need[0][len("safe_"):]
714                 if world_db["terrain_fullness"](t["pos"]) <= 3:
715                     t["T_COMMAND"] = thing_action_id(action_name)
716                     return
717                 test = world_db["get_dir_to_target"](t, "space")
718                 if test[0]:
719                     if test[1] < 5:
720                         return
721                     elif world["terrain_fullness"](t["pos"]) < 5:
722                         t["T_COMMAND"] = thing_action_id(action_name)
723                     return
724                 if t["T_STOMACH"] < 32 and \
725                         world_db["get_dir_to_target"](t, "food")[0]:
726                     return
727                 continue
728             if need[0] in {"fluid_certain", "fluid_potential", "food"}:
729                 if world_db["get_dir_to_target"](t, need[0])[0]:
730                     return
731                 elif world_db["get_dir_to_target"](t, "hunt")[0]:
732                     return
733                 elif need[0] != "food" and t["T_STOMACH"] < 32 and \
734                         world_db["get_dir_to_target"](t, "food")[0]:
735                     return
736             elif world_db["get_dir_to_target"](t, need[0])[0]:
737                 return
738 world_db["ai"] = ai
739
740
741 from server.config.io import io_db
742 io_db["worldstate_write_order"] += [["T_STOMACH", "player_int"]]
743 io_db["worldstate_write_order"] += [["T_KIDNEY", "player_int"]]
744 io_db["worldstate_write_order"] += [["T_BOWEL", "player_int"]]
745 io_db["worldstate_write_order"] += [["T_BLADDER", "player_int"]]
746 io_db["worldstate_write_order"] += [[write_wetmap, "func"]]
747 io_db["worldstate_write_order"] += [[write_soundmap, "func"]]
748 import server.config.world_data
749 server.config.world_data.symbols_hide = "345"
750 server.config.world_data.symbols_passable = "012-"
751 server.config.world_data.thing_defaults["T_STOMACH"] = 16
752 server.config.world_data.thing_defaults["T_BOWEL"] = 0
753 server.config.world_data.thing_defaults["T_KIDNEY"] = 16
754 server.config.world_data.thing_defaults["T_BLADDER"] = 0
755 world_db["soundmap"] = bytearray(b"0" * world_db["MAP_LENGTH"] ** 2)
756 world_db["wetmap"] = bytearray(b"0" * world_db["MAP_LENGTH"] ** 2)
757 if not "NEW_SPAWN" in world_db:
758     world_db["NEW_SPAWN"] = 0
759 if not "HUMIDITY" in world_db:
760     world_db["HUMIDITY"] = 0
761 io_db["hook_save"] = save_maps
762 import server.config.make_world_helpers
763 server.config.make_world_helpers.make_map = make_map
764 from server.config.commands import commands_db
765 commands_db["THINGS_HERE"] = (2, True, lambda x, y: None)
766 commands_db["HELP"] = (1, False, command_help)
767 commands_db["ai"] = (0, False, command_ai)
768 commands_db["move"] = (1, False, play_move)
769 commands_db["eat"] = (1, False, play_move)
770 commands_db["wait"] = (0, False, play_wait)
771 commands_db["drop"] = (0, False, play_drop)
772 commands_db["drink"] = (0, False, play_drink)
773 commands_db["pee"] = (0, False, play_pee)
774 commands_db["use"] = (1, False, lambda x: None)
775 commands_db["pickup"] = (0, False, lambda: None)
776 commands_db["NEW_SPAWN"] = (1, False, setter(None, "NEW_SPAWN", 0, 255))
777 commands_db["HUMIDITY"] = (1, False, setter(None, "HUMIDITY", 0, 65535))
778 commands_db["T_STOMACH"] = (1, False, setter("Thing", "T_STOMACH", 0, 255))
779 commands_db["T_KIDNEY"] = (1, False, setter("Thing", "T_KIDNEY", 0, 255))
780 commands_db["T_BOWEL"] = (1, False, setter("Thing", "T_BOWEL", 0, 255))
781 commands_db["T_BLADDER"] = (1, False, setter("Thing", "T_BLADDER", 0, 255))
782 commands_db["WETMAP"] = (2, False, wetmapset)
783 commands_db["SOUNDMAP"] = (2, False, soundmapset)
784 from server.actions import actor_wait
785 import server.config.actions
786 server.config.actions.action_db = {
787     "actor_wait": actor_wait,
788     "actor_move": actor_move,
789     "actor_drop": actor_drop,
790     "actor_drink": actor_drink,
791     "actor_pee": actor_pee,
792     "actor_eat": actor_eat,
793 }
794
795 strong_write(io_db["file_out"], "PLUGIN TheCrawlingEater\n")