Advertisement
Guest User

XLSToTads

a guest
Jul 13th, 2018
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 18.13 KB | None | 0 0
  1. #!/usr/bin/python
  2. # -*- coding: cp1251 -*-
  3. """Обрабатывает Эксель-файлы для игры Комбикорм-2 в результате получаются t-файлы уровней.
  4.  
  5. """
  6.  
  7. from openpyxl import Workbook
  8. import openpyxl
  9. import sys
  10. import time
  11. import datetime
  12. import argparse
  13.  
  14. __author__ = "Anton Lastochkin"
  15. __copyright__ = "Copyright 2017"
  16. __credits__ = ["Anton Lastochkin", "Alik Ganzimuradov"]
  17. __license__ = "GPL"
  18. __version__ = "1.4"
  19.  
  20. #Парсинг аргументов командной строки
  21. #args.log=True - печать логов
  22. print('XLSToTads version ' + __version__)
  23. parser = argparse.ArgumentParser(description='Transform XLSX to Tads game files.')
  24. parser.add_argument("--log", help="Need full parsing log.",action='store_const', const=True)
  25. args = parser.parse_args()
  26. #print args.log
  27. #raw_input('Press Enter to continue...')
  28. #exit()
  29.  
  30. #карта переводов для инфо, классов и параметров
  31. #название -> desc
  32. #прилагательные -> adjective
  33. #описание ->ldesc
  34.  
  35. #константы блоков
  36. BLOCK_WAIT = "WAIT"
  37. BLOCK_INFO = ("инфо").decode('cp1251')
  38. BLOCK_MAP = ("карта").decode('cp1251')
  39. BLOCK_OBJ = ("объекты").decode('cp1251')
  40. DIALOGUE_EVENT = ("диалоги").decode('cp1251')
  41.  
  42. DIALOGUE_WAIT_HEADER = "wait header"
  43. DIALOGUE_READ_CONTENT = "read content"
  44.  
  45. BLOCK_END = ("конец").decode('cp1251')
  46.  
  47. ALPHA_FOR_MAP = (" АБВГДЕЖЗИКЛМНОПРСТУФХЦЧ").decode('cp1251')
  48. #   для карты
  49. GROUND_TYPE_ROCK = ('непроход').decode('cp1251')
  50. GROUND_TYPE_WALL = ('стена').decode('cp1251')
  51. GROUND_TYPE_WATER = ('вода').decode('cp1251')
  52. CELL_TYPE_FINAL = ('финал').decode('cp1251')
  53. START_TYPE_ZOND = ('зонд').decode('cp1251')
  54. START_TYPE_KIT = ('кит').decode('cp1251')
  55. START_TYPE_VINT = ('винт').decode('cp1251')
  56. START_TYPE_SONYA = ('соня').decode('cp1251')
  57. #   для инфо
  58. KEY_INFO_CAPTION = ('название').decode('cp1251')
  59. KEY_INFO_BREEF = ('брифинг').decode('cp1251')
  60. KEY_INFO_FINAL = ('финал').decode('cp1251')
  61. KEY_INFO_NEXT = ('следующ').decode('cp1251')
  62. KEY_INFO_CODE = ('код').decode('cp1251')
  63.  
  64.  
  65. INIT_BLOCK = BLOCK_MAP
  66.  
  67. #текущее состояние обработчика блоков
  68. state = INIT_BLOCK
  69. levelId = 1
  70.  
  71. #текст ячейки в нужной кодировке
  72. def get_cell_text(cell):
  73.     try:
  74.         t = float(cell.value)
  75.         t = str(t)
  76.     except:
  77.         t = cell.value
  78.     return t
  79.  
  80. #получить число из ячейки
  81. def get_cell_int(cell):
  82.     return int(float(get_cell_text(cell)))
  83.  
  84. #вывод текста на консоль в нужной кодировке
  85. def print_to_console(text,isImportant):
  86.     if isImportant == True or args.log == True:
  87.         print(text.encode('cp866','replace'))
  88.     return
  89.  
  90. #перевод координаты вида А1,Б1 в цифровой эквивалент
  91. def coord_to_pos(coord):
  92.     t_col = coord[1:]
  93.     letter = coord[0]
  94.     n_row = ALPHA_FOR_MAP.find(letter)
  95.     return str(n_row) + t_col
  96.  
  97. #Подготовка описаний
  98. def prepareDesc(dsc):
  99.     #жкранируем кавычки
  100.     return dsc.replace('"','\\"')
  101.  
  102. #печать комнаты в Tads-файл уровня
  103. def print_room(coord,lit_name,lit_desc,cell_type,g_type,startZond,startKit,startVint,startSonya):
  104.     with open('level' + str(levelId) + '.t', 'a') as f:
  105.         f.write('room' + coord_to_pos(coord) + 'Level' + str(levelId) + ' :' + cell_type + '\n')
  106.         f.write('    levelId=' + str(levelId) + '\n')
  107.         f.write('    coord=' + "'" + coord.encode('cp1251') + "'\n")
  108.         f.write('    groundType=' + g_type + '\n')
  109.         f.write('    lit_name=' + "'" + lit_name.encode('cp1251') + "'\n")
  110.         f.write('    lit_desc=' + "'" + prepareDesc(lit_desc).encode('cp1251') + "'\n")
  111.         if startZond == True: f.write('    startZond = true\n')
  112.         if startKit == True: f.write('    startKit = true\n')
  113.         if startVint == True: f.write('    startVint = true\n')
  114.         if startSonya == True: f.write('    startSonya = true\n')
  115.         f.write(";\n\n")
  116.     return
  117.  
  118. def print_info(caption,brief,final_link,next_lev,code):
  119.     with open('level' + str(levelId) + '.t', 'a') as f:
  120.         f.write('InfoLevel' + str(levelId) + ' : LevelInfo\n')
  121.         f.write('    levelId=' + str(levelId) + '\n')
  122.         f.write('    caption=' + "'" + prepareDesc(caption).encode('cp1251') + "'" + '\n')
  123.         f.write('    task=' + "'" + prepareDesc(brief).encode('cp1251') + "'" + '\n')
  124.         f.write('    finalTextFun=' + final_link + '\n')
  125.         f.write('    nextLevelId=' + next_lev + '\n')
  126.         f.write('    code=' + "'" + code.encode('cp1251') + "'" + '\n')
  127.         f.write(";\n\n")
  128.     return
  129.  
  130. def print_object_header(pos,obj_class,obj_name,obj_desc,gender,coord):
  131.     with open('level' + str(levelId) + '.t', 'a') as f:
  132.         f.write(obj_class + str(pos) + 'Level' + str(levelId) + ' : ' + obj_class + ' \n')
  133.         f.write('    levelId=' + str(levelId) + '\n')
  134.         f.write('    coord=' + "'" + coord.encode('cp1251') + "'\n")
  135.         list_desc = obj_name.split(' ')
  136.         for i in range(len(list_desc)):
  137.             if gender[0] == 'M': list_desc[i] += ('/1м').decode('cp1251')
  138.             elif gender[0] == 'F': list_desc[i] += ('/1ж').decode('cp1251')
  139.             else: list_desc[i] += ('/1').decode('cp1251')
  140.             if len(gender) == 2 and gender[1] == 'O': list_desc[i] += ('о').decode('cp1251')
  141.             if i != len(list_desc) - 1: list_desc[i] += ('п').decode('cp1251')
  142.         gendered_desc = ' '.join(list_desc)
  143.         f.write('    desc=' + "'" + gendered_desc.encode('cp1251') + "'\n")
  144.         f.write('    ldesc=' + '"' + prepareDesc(obj_desc).encode('cp1251') + '"\n')
  145.         if (gender[0] == 'M'): f.write('    isHim=true\n')
  146.         elif (gender[0] == 'F'): f.write('    isHer=true\n')
  147.     return
  148.  
  149. def print_object_footer():
  150.     with open('level' + str(levelId) + '.t', 'a') as f:
  151.         f.write(";\n\n")
  152.     return
  153.  
  154. def print_object_path(coord):
  155.     with open('level' + str(levelId) + '.t', 'a') as f:
  156.         coord_list = coord.split(',')
  157.         new_list = []
  158.         for coord in coord_list:
  159.            new_list.append("'" + coord.upper().replace(' ','').encode('cp1251') + "'")
  160.        
  161.         final_path = ' '.join(new_list)
  162.         f.write('    path=[' + final_path + ']\n')
  163.     return
  164. #Гранат Патронов Капканов Аптечек Капсул превращения
  165. def print_ammo_num(grenades,bullets,kap,apt,trans):
  166.     with open('level' + str(levelId) + '.t', 'a') as f:
  167.         f.write('    grenades=' + str(grenades) + '\n')
  168.         f.write('    bullets=' + str(bullets) + '\n')
  169.         f.write('    capcans=' + str(kap) + '\n')
  170.         f.write('    aids=' + str(apt) + '\n')
  171.         f.write('    capsules=' + str(trans) + '\n')
  172.     return
  173.  
  174. def print_transport_hit(isKill):
  175.     if isKill is True:
  176.         with open('level' + str(levelId) + '.t', 'a') as f:
  177.             f.write('    canKill = true\n')
  178.     return
  179.  
  180. def print_dialogue(dia_caption,dia_link,dia_text):
  181.     with open('level' + str(levelId) + '.t', 'a') as f:
  182.         f.write('\n//' + dia_caption.encode('cp1251') + '\n')
  183.         f.write(dia_link + ': function' + '\n')
  184.         f.write('{' + '\n')
  185.         list_desc = prepareDesc(dia_text).split('\n')
  186.         for i in range(len(list_desc)):
  187.             f.write('"' + list_desc[i].encode('cp1251') + '\\n";\n')
  188.         f.write('}' + '\n')
  189.     return
  190. ############################################
  191. #Обработчики
  192. #карты пар
  193. info_pair_map = {}
  194.  
  195. def process_info_block(row):
  196.     global info_pair_map
  197.     if len(row) >= 2:
  198.         info_key = get_cell_text(row[0]).lower() #ключи всегда сравниваются без регистра
  199.         info_val = get_cell_text(row[1])
  200.         info_pair_map[info_key] = info_val
  201.     return
  202.  
  203. #раскладываем пары по полкам и проверям на наличие всего необходимого
  204. def finalize_info():
  205.     caption = 'undefined'
  206.     brief = 'undefined'
  207.     final_link = 'nil'
  208.     next_lev = '0'
  209.     code = ''
  210.     for key in info_pair_map:
  211.         if KEY_INFO_CAPTION in key: caption = info_pair_map[key]
  212.         elif KEY_INFO_BREEF in key: brief = info_pair_map[key]
  213.         elif KEY_INFO_FINAL in key: final_link = info_pair_map[key]
  214.         elif KEY_INFO_NEXT in key: next_lev = info_pair_map[key]
  215.         elif KEY_INFO_CODE in key: code = info_pair_map[key]
  216.  
  217.     next_lev = next_lev.replace('.0','')
  218.     print_info(caption,brief,final_link,next_lev,code)
  219.     return
  220.  
  221. #обработка карты
  222. game_map = {}
  223.  
  224. def process_map_block(row):
  225.     global game_map
  226.     global map_row
  227.     global ALPHA_FOR_MAP
  228.  
  229.     if len(row) > 0:
  230.         map_col = 0
  231.         for cell in row:
  232.             if map_col > 0 and map_row > 0 and get_cell_text(cell) is not None:
  233.                 ind = ALPHA_FOR_MAP[map_col] + str(get_cell_int(row[0])) #формируем индекс для ячейки
  234.                 print_to_console(ind,False)
  235.                 print_to_console(get_cell_text(cell),False)
  236.                 game_map[ind] = get_cell_text(cell)
  237.             map_col += 1
  238.        
  239.     return
  240.  
  241. #распечатываем карту
  242. def finalize_map():
  243.     global game_map
  244.     room_total = 0
  245.     #обрабатываем все ячейки
  246.     for cell in game_map:
  247.         if game_map[cell] is not None:
  248.             str_list = game_map[cell].split('\n')
  249.             #print_to_console(game_map[cell],False)
  250.             if len(str_list) >= 2:
  251.                 #отделяем заголовок от лит.  описания
  252.                 header = str_list[0]
  253.                 del str_list[0]
  254.                 lit_desc = ' '.join(str_list)
  255.                 lit_desc = lit_desc.replace('\n','')
  256.                 #разделяем лит.  название локации и параметры карты
  257.                 header_param = header.split('.')
  258.                 lit_header = header_param[0]
  259.                 del header_param[0]
  260.                 header_param = '.'.join(header_param)
  261.                 header_param = header_param.lower()
  262.                 #определяем тип земли
  263.                 g_type = 'GROUND_TYPE_NORMAL'
  264.                 if GROUND_TYPE_ROCK in header_param: g_type = 'GROUND_TYPE_ROCK'
  265.                 elif GROUND_TYPE_WATER in header_param: g_type = 'GROUND_TYPE_WATER'
  266.                 elif GROUND_TYPE_WALL in header_param: g_type = 'GROUND_TYPE_WALL'
  267.                 #определяем тип ячейки
  268.                 cell_type = 'Cell'
  269.                 if CELL_TYPE_FINAL in header_param: cell_type = 'FinalCell'
  270.                 #определяем старт персонажей
  271.                 startZond = False
  272.                 startKit = False
  273.                 startVint = False
  274.                 startSonya = False
  275.                 if START_TYPE_ZOND in header_param: startZond = True
  276.                 if START_TYPE_KIT in header_param: startKit = True
  277.                 if START_TYPE_VINT in header_param: startVint = True
  278.                 if START_TYPE_SONYA in header_param: startSonya = True
  279.                 #печать комнаты
  280.                 print_room(cell,lit_header,lit_desc,cell_type,g_type,startZond,startKit,startVint,startSonya)
  281.                 room_total += 1
  282.             else:
  283.                 print('Error parse header')
  284.                 print_to_console(game_map[cell],True)
  285.     print('Created map, rooms: ' + str(room_total))
  286.     return
  287.  
  288.  
  289. #обработка объектов
  290. def get_tads_class(str):
  291.    CLASS_MAP = {'бык'.decode('cp1251'):'BykMonster',
  292.                 'бочка'.decode('cp1251'):'GasTank',
  293.                 'коробка'.decode('cp1251'):'AmmoBox',
  294.                 'машина'.decode('cp1251'):'Transport',
  295.                 'дом'.decode('cp1251'):'House'}
  296.    if str in CLASS_MAP: return CLASS_MAP[str]
  297.    return None
  298.  
  299. def get_tads_gender(str):
  300.    CLASS_MAP = {'муж.'.decode('cp1251'):'M',
  301.                 'жен.'.decode('cp1251'):'F',
  302.                 'ср.'.decode('cp1251'):'C' ,
  303.                 'муж.од.'.decode('cp1251'):'MO',
  304.                 'жен.од.'.decode('cp1251'):'FO',
  305.                 'ср.од.'.decode('cp1251'):'CO' }
  306.    return CLASS_MAP[str]
  307.  
  308. globalObjPos = 0
  309. isUserHeaderRow = True
  310.  
  311. def process_objects_block(obj_pos,row):
  312.     global isUserHeaderRow
  313.  
  314.     #пропускаем первую строку с описанием колонок
  315.     if isUserHeaderRow == True:
  316.         return
  317.  
  318.     #получаем инфу
  319.     #Класс Имя Род Описание Локация Маршрут (для быков) Гранат Патронов
  320.     #Капканов Аптечек Капсул превращения
  321.     obj_class = get_cell_text(row[0]).lower().replace(' ','')
  322.     curr_class = get_tads_class(obj_class)
  323.     if (curr_class == None): return
  324.     obj_name = get_cell_text(row[1])
  325.     obj_gender = get_cell_text(row[2]).lower().replace(' ','')
  326.     curr_gender = get_tads_gender(obj_gender)
  327.     obj_ldesc = get_cell_text(row[3])
  328.     obj_loc = get_cell_text(row[4])
  329.  
  330.     print('proc obj class: ' + curr_class)
  331.  
  332.     #печатаем базу
  333.     print_object_header(obj_pos,curr_class,obj_name,obj_ldesc,curr_gender,obj_loc)
  334.  
  335.     #печатаем уникальные данные для классов
  336.     if curr_class == 'BykMonster':
  337.         print_to_console(get_cell_text(row[5]),False)
  338.         if get_cell_text(row[5]) is not None: print_object_path(get_cell_text(row[5]))
  339.     elif curr_class == 'AmmoBox':
  340.         grenades = 0
  341.         bullets = 0
  342.         kap = 0
  343.         apt = 0
  344.         trans = 0
  345.         if get_cell_text(row[6]) is not None: grenades = get_cell_int(row[6])
  346.         if get_cell_text(row[7]) is not None: bullets = get_cell_int(row[7])
  347.         if get_cell_text(row[8]) is not None: kap = get_cell_int(row[8])
  348.         if get_cell_text(row[9]) is not None: apt = get_cell_int(row[9])
  349.         if get_cell_text(row[10]) is not None: trans = get_cell_int(row[10])
  350.         print_ammo_num(grenades,bullets,kap,apt,trans)
  351.     elif curr_class == 'Transport':
  352.         isKillTransport = False
  353.         if get_cell_text(row[11]) is not None and get_cell_int(row[11]) > 0: isKillTransport = True
  354.         print_transport_hit(isKillTransport)
  355.  
  356.     #печатаем окончание класса
  357.     print_object_footer()
  358.  
  359.     return
  360.  
  361.    
  362.    
  363. def process_dialogue_block(row):
  364.     global isUserHeaderRow
  365.  
  366.     print('proc dia...')
  367.     #пропускаем первую строку с описанием колонок
  368.     if isUserHeaderRow == True:
  369.         return
  370.  
  371.     #получаем инфу
  372.     #Название Ссылка Текст
  373.     dia_caption = get_cell_text(row[0])
  374.     dia_link = get_cell_text(row[1]).lower().replace(' ','')
  375.     dia_text = get_cell_text(row[2])
  376.  
  377.     #печатаем диалог
  378.     print_dialogue(dia_caption,dia_link,dia_text)
  379.  
  380.     return
  381.  
  382. ############################################
  383. #загружаем книгу
  384. wb = openpyxl.load_workbook('kombi2map.xlsx')
  385.  
  386. #распечатываем все листки
  387. print('Total sheets:')
  388. print(wb.get_sheet_names())
  389. ts = time.time()
  390. time_st = datetime.datetime.fromtimestamp(ts).strftime('%d.%m.%Y %H:%M:%S')
  391.  
  392. #для каждого листка
  393. for ws in wb.worksheets:
  394.     state = INIT_BLOCK
  395.     #код уровня
  396.     levelId = get_cell_int(ws['A1'])
  397.     print('Process level ' + str(levelId) + '...')
  398.     #очищаем карту
  399.     open('level' + str(levelId) + '.t', 'w')
  400.     with open('level' + str(levelId) + '.t', 'a') as f:
  401.         f.write('//GENERATED: ' + time_st + '\n\n')
  402.     game_map = {}
  403.     map_row = 0
  404.  
  405.     for row in ws.iter_rows():
  406.         if len(row) > 0:
  407.             try:
  408.                 block_cell = row[0]
  409.                 block_text = get_cell_text(block_cell).lower()
  410.                 #проверяем состояние, в котором идёт обработка
  411.                 if state == BLOCK_WAIT:
  412.                     if BLOCK_INFO in block_text:
  413.                         print('Start info')
  414.                         state = BLOCK_INFO
  415.                     elif BLOCK_MAP in block_text:
  416.                         print('Start map')
  417.                         state = BLOCK_MAP
  418.                     elif BLOCK_OBJ in block_text:
  419.                         print('Start obj')
  420.                         isUserHeaderRow = True
  421.                         globalObjPos = 1
  422.                         state = BLOCK_OBJ
  423.                     elif DIALOGUE_EVENT in block_text:
  424.                         print('Start dialogue')
  425.                         isUserHeaderRow = True
  426.                         state = DIALOGUE_EVENT
  427.                 elif state == BLOCK_INFO:
  428.                     if BLOCK_END in block_text:
  429.                         finalize_info()
  430.                         print('End info')
  431.                         state = BLOCK_WAIT
  432.                     else:
  433.                         process_info_block(row)
  434.                 elif state == BLOCK_MAP:
  435.                     if BLOCK_END in block_text:
  436.                         finalize_map()
  437.                         state = BLOCK_WAIT
  438.                     else:
  439.                         process_map_block(row)
  440.                         map_row+=1
  441.                 elif state == BLOCK_OBJ:
  442.                     if BLOCK_END in block_text:
  443.                         print('End obj')
  444.                         state = BLOCK_WAIT
  445.                     else:
  446.                         process_objects_block(globalObjPos,row)
  447.                         isUserHeaderRow = False
  448.                         globalObjPos+=1
  449.                 elif state == DIALOGUE_EVENT:
  450.                     if BLOCK_END in block_text:
  451.                         print('End dialogue')
  452.                         state = BLOCK_WAIT
  453.                     else:
  454.                         process_dialogue_block(row)
  455.                         isUserHeaderRow = False
  456.  
  457.  
  458.             except AttributeError:
  459.                 continue  
  460.     #break #DEBUG
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement