TinTin++ Configs for DiscworldMUD
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
13 KiB

  1. #!/usr/bin/env python
  2. import os
  3. import re
  4. import sys
  5. import math
  6. import json
  7. class MapDoorText:
  8. number_map = {
  9. "a ": 1,
  10. "an ": 1,
  11. "the ": 1,
  12. "one ": 1,
  13. "two ": 2,
  14. "three ": 3,
  15. "four ": 4,
  16. "five ": 5,
  17. "six ": 6,
  18. "seven ": 7,
  19. "eight ": 8,
  20. "nine ": 9,
  21. "ten ": 10,
  22. "eleven ": 11,
  23. "twelve ": 12,
  24. "thirteen ": 13,
  25. "fourteen ": 14,
  26. "fifteen ": 15,
  27. "sixteen ": 16,
  28. "seventeen ": 17,
  29. "eighteen ": 18,
  30. "nineteen ": 19,
  31. "twenty ": 20,
  32. "twenty-one ": 21,
  33. "twenty-two ": 22,
  34. "twenty-three ": 23,
  35. "twenty-four ": 24,
  36. "twenty-five ": 25,
  37. "twenty-six ": 26,
  38. "twenty-seven ": 27,
  39. "twenty-eight ": 28,
  40. "twenty-nine ": 29,
  41. "thirty ": 30,
  42. }
  43. direction_map = {
  44. "north": "n",
  45. "northeast": "ne",
  46. "east": "e",
  47. "southeast": "se",
  48. "south": "s",
  49. "southwest": "sw",
  50. "west": "w",
  51. "northwest": "nw",
  52. "n": "n",
  53. "ne": "ne",
  54. "e": "e",
  55. "se": "se",
  56. "s": "s",
  57. "sw": "sw",
  58. "w": "w",
  59. "nw": "nw",
  60. }
  61. colour_map = {
  62. # http://terminal-color-builder.mudasobwa.ru/
  63. "orange": "\033[01;38;05;214m",
  64. "reset": "\033[00;39;49m"
  65. }
  66. def __init__(self):
  67. self.return_value = []
  68. self.custom_matches = []
  69. # Load the JSON configuration file
  70. with open('mdtconfig.json', 'r') as config_file:
  71. config = json.load(config_file)
  72. # Strip comments from custom matches
  73. for match in config['custom_matches']:
  74. if len(match) > 1:
  75. self.custom_matches.append(match)
  76. self.default_npc_value = config['default_npc_value']
  77. self.bonus_player_value = config['bonus_player_value']
  78. self.minimum_room_value = config['minimum_room_value']
  79. self.show_hidden_room_count = config['show_hidden_room_count']
  80. @staticmethod
  81. def explode(div, mdt):
  82. if div == '':
  83. return False
  84. pos, fragments = 0, []
  85. for m in re.finditer(div, mdt):
  86. fragments.append(mdt[pos:m.start()])
  87. pos = m.end()
  88. fragments.append(mdt[pos:])
  89. return fragments
  90. def parse_mdt(self, mdt_line):
  91. # Make lower case, do initial replacements
  92. mdt_line = mdt_line.lower()
  93. mdt_line = mdt_line.replace(" are ", " is ")
  94. mdt_line = mdt_line.replace(" black and white ", " black white ")
  95. mdt_line = mdt_line.replace(" brown and white ", " brown white ")
  96. mdt_line = mdt_line.replace("the limit of your vision is ", "the limit of your vision:")
  97. mdt_line = mdt_line.replace(" and ", ", ")
  98. mdt_line = mdt_line.replace(" is ", ", ")
  99. mdt_table = self.explode(', ', mdt_line)
  100. data = {
  101. 'last_direction': '',
  102. 'last_enemy_line': '',
  103. 'last_count': 0,
  104. 'last_was_dir': 0,
  105. 'enemies_by_square': [],
  106. 'ignoring_exits': False,
  107. 'entity_table': [],
  108. 'room_id': 1,
  109. 'room_value': 0,
  110. 'longest_direction': 0,
  111. 'nothing': True,
  112. 'next_color': ''
  113. }
  114. exit_strings = ['doors ', 'a door ', 'exits ', 'an exit ', 'a hard to see through exit ']
  115. for entry in mdt_table:
  116. if entry != "" and ' of a ' not in entry:
  117. if entry.startswith(tuple(exit_strings)):
  118. # print('Special exit, ignore this line? next line is processed...')
  119. data['ignoring_exits'] = True
  120. elif entry.startswith('the limit of your vision:'):
  121. if data['last_count'] > 0:
  122. this_square = [data['last_count'], data['last_direction'], data['room_id'], int(math.floor(data['room_value']))]
  123. data['enemies_by_square'].append(this_square)
  124. data['nothing'] = False
  125. data['next_color'] = ''
  126. data['room_id'] = data['room_id'] + 1
  127. data['room_value'] = 0
  128. data['last_direction'] = ''
  129. data['last_enemy_line'] = ''
  130. data['last_count'] = 0
  131. data['last_was_dir'] = 0
  132. else:
  133. # find the quantity first
  134. quantity = 1
  135. for nm_key in self.number_map:
  136. if entry.startswith(nm_key):
  137. quantity = self.number_map[nm_key]
  138. entry = entry[len(nm_key):]
  139. break
  140. is_direction = 0
  141. this_direction = ''
  142. if entry.startswith("northeast"):
  143. is_direction = 1
  144. this_direction = "northeast"
  145. elif entry.startswith("northwest"):
  146. is_direction = 1
  147. this_direction = "northwest"
  148. elif entry.startswith("southeast"):
  149. is_direction = 1
  150. this_direction = "southeast"
  151. elif entry.startswith("southwest"):
  152. is_direction = 1
  153. this_direction = "southwest"
  154. elif entry.startswith("north"):
  155. is_direction = 1
  156. this_direction = "north"
  157. elif entry.startswith("east"):
  158. is_direction = 1
  159. this_direction = "east"
  160. elif entry.startswith("south"):
  161. is_direction = 1
  162. this_direction = "south"
  163. elif entry.startswith("west"):
  164. is_direction = 1
  165. this_direction = "west"
  166. if is_direction == 1:
  167. if not data['ignoring_exits']:
  168. # print('[handling direction, not exits]')
  169. data['last_was_dir'] = 1
  170. if data['last_direction'] != '':
  171. data['last_direction'] = '{}, '.format(data['last_direction'])
  172. data['last_direction'] = '{}{} {}'.format(
  173. data['last_direction'], quantity, self.direction_map[this_direction]
  174. )
  175. else:
  176. # print('[ignoring exits direction line]')
  177. pass
  178. else:
  179. data['ignoring_exits'] = False
  180. if data['last_was_dir'] == 1:
  181. # reset count
  182. if data['last_count'] > 0:
  183. this_square = [data['last_count'], data['last_direction'], data['room_id'], int(math.floor(data['room_value']))]
  184. data['enemies_by_square'].append(this_square)
  185. data['nothing'] = False
  186. data['next_color'] = ''
  187. data['room_id'] = data['room_id'] + 1
  188. data['room_value'] = 0
  189. data['last_direction'] = ''
  190. data['last_enemy_line'] = ''
  191. data['last_count'] = 0
  192. data['last_was_dir'] = 0
  193. data['next_color'] = ''
  194. add_player_value = False
  195. # Special GMCP MDT colour codes
  196. if entry[0:6] == 'u001b[':
  197. # u001b[38;5;37mRuhsbaaru001b[39;49mu001b[0m
  198. here = entry.index('m')
  199. data['next_color'] = entry[7:here]
  200. # entry = entry[here + 1:-20]
  201. # entry = entry.replace('u001b', '')
  202. entry = entry.replace('u001b', '\033')
  203. # Might be a second colour code for PK
  204. if entry[0:6] == 'u001b[':
  205. here = entry.index('m')
  206. data['next_color'] = entry[7:here]
  207. entry = entry[here + 1:-20]
  208. add_player_value = True
  209. this_value = self.default_npc_value
  210. for custom_match in self.custom_matches:
  211. if custom_match[3]:
  212. # This is a regex match
  213. rexp = re.compile(custom_match[0])
  214. if rexp.match(entry):
  215. if custom_match[1] and custom_match[1] in self.colour_map:
  216. entry = '{}{}{}'.format(
  217. self.colour_map[custom_match[1]],
  218. entry,
  219. self.colour_map['reset']
  220. )
  221. this_value = custom_match[2]
  222. else:
  223. # This is a regular string match
  224. if custom_match[0] in entry:
  225. if custom_match[1] and custom_match[1] in self.colour_map:
  226. entry = '{}{}{}'.format(
  227. self.colour_map[custom_match[1]],
  228. entry,
  229. self.colour_map['reset']
  230. )
  231. this_value = custom_match[2]
  232. if add_player_value == True:
  233. this_value = this_value + self.bonus_player_value
  234. data['room_value'] = data['room_value'] + (this_value * quantity)
  235. if quantity > 1:
  236. entry = '{} {}'.format(quantity, entry)
  237. data['entity_table'].append([data['room_id'], entry, data['next_color']])
  238. data['last_count'] = data['last_count'] + quantity
  239. if data['last_enemy_line'] != '':
  240. data['last_enemy_line'] = '{}, '.format(data['last_enemy_line'])
  241. data['last_enemy_line'] = '{}{}'.format(data['last_enemy_line'], entry)
  242. if data['nothing']:
  243. self.return_value.append('Nothing seen, try elsewhere!')
  244. else:
  245. done_here = False
  246. rooms_ignored = 0
  247. data['enemies_by_square'].sort(key=lambda square: square[3])
  248. # Grab shortest
  249. for square in data['enemies_by_square']:
  250. # Only show if this room meets the minimum value
  251. if square[3] >= self.minimum_room_value and len(square[1]) > data['longest_direction']:
  252. data['longest_direction'] = len(square[1])
  253. for square in data['enemies_by_square']:
  254. # Only show if this room meets the minimum value
  255. if square[3] >= self.minimum_room_value:
  256. done_here = False
  257. fstring = '{{:<{}}} [{{}}] '.format(data['longest_direction'])
  258. output = fstring.format(square[1], square[3])
  259. for entity in data['entity_table']:
  260. if entity[0] == square[2]:
  261. if done_here:
  262. output = '{}, '.format(output)
  263. output = '{}{}'.format(output, entity[1])
  264. done_here = True
  265. if square[0] < 2:
  266. output = '{} [{} thing]'.format(output, square[0])
  267. else:
  268. output = '{} [{} things]'.format(output, square[0])
  269. self.return_value.append(output)
  270. else:
  271. rooms_ignored = rooms_ignored + 1
  272. square = None
  273. for entity in data['entity_table']:
  274. entity = None
  275. if rooms_ignored > 0 and self.show_hidden_room_count:
  276. output = '({} rooms below your value limit of {})'.format(rooms_ignored, self.minimum_room_value)
  277. self.return_value.append(output)
  278. if __name__ == '__main__':
  279. if len(sys.argv) < 2:
  280. print('[error] No input provided.')
  281. sys.exit()
  282. argument, mdt_line = sys.argv.pop(1), None
  283. # Is this a file passed to us?
  284. if os.path.exists(argument):
  285. with open(argument, 'r') as f:
  286. mdt_line = f.readline()
  287. else:
  288. mdt_line = argument
  289. mdt = MapDoorText()
  290. mdt.parse_mdt(mdt_line)
  291. for line in mdt.return_value:
  292. print(line)