| from flask import Flask, render_template_string
|
| import json
|
| import os
|
| import re
|
|
|
| app = Flask(__name__)
|
|
|
|
|
| BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
| def read_json(filename):
|
|
|
| path = os.path.join(BASE_DIR, 'static', 'json', filename)
|
| if os.path.exists(path):
|
| try:
|
| with open(path, 'r', encoding='utf-8') as f:
|
| return json.load(f)
|
| except Exception as e:
|
| print(f"Ошибка чтения JSON {filename}: {e}")
|
| return []
|
| return []
|
|
|
| def read_txt(filename):
|
| path = os.path.join(BASE_DIR, 'static', 'txt', filename)
|
| if os.path.exists(path):
|
| with open(path, 'r', encoding='utf-8') as f:
|
| content = f.read()
|
| return process_custom_tags(content)
|
| return f"Файл {filename} не найден"
|
|
|
| def process_custom_tags(text):
|
|
|
| img_pattern = r'\[IMG:(.*?)\]'
|
| text = re.sub(img_pattern, r'<img src="static/images/\1" class="thumbnail" onclick="window.open(this.src)">', text)
|
|
|
| table_pattern = r'\[TABLE:(.*?):(.*?)\]'
|
| def build_table(match):
|
| filename = match.group(1)
|
| columns = match.group(2).split(',')
|
| data = read_json(filename)
|
| if not data: return ""
|
| html = '<table class="data-table"><tbody>'
|
| for row in data:
|
| html += '<tr>'
|
| for col in columns:
|
| val = row.get(col, '')
|
| if col == 'Icon':
|
|
|
| content = f'<img src="static/images/{val}" class="unit-icon">'
|
| elif col == 'Order':
|
| content = f'<b style="color:#e67e22">{val}</b>'
|
| else: content = val
|
| html += f'<td>{content}</td>'
|
| html += '</tr>'
|
| html += '</tbody></table>'
|
| return html
|
| return re.sub(table_pattern, build_table, text)
|
|
|
| HTML_TEMPLATE = """
|
| <!DOCTYPE html>
|
| <html lang="ru">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>Game Academy</title>
|
| <style>
|
| body { font-family: -apple-system, sans-serif; background: #f0f2f5; padding: 10px; margin: 0; color: #333; }
|
| .container { max-width: 650px; margin: 0 auto; }
|
| h1 { text-align: center; color: #2c3e50; font-size: 1.5em; margin: 20px 0; }
|
| .section-card { background: white; padding: 15px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 12px; }
|
| summary { cursor: pointer; font-weight: bold; color: #1a73e8; font-size: 1.05em; padding: 5px; outline: none; }
|
| details[open] summary { border-bottom: 1px solid #eee; margin-bottom: 12px; padding-bottom: 8px; }
|
| .description { line-height: 1.6; font-size: 0.92em; }
|
| ul { padding-left: 20px; margin: 10px 0; }
|
| li { margin-bottom: 6px; }
|
| .thumbnail { width: 25%; min-width: 120px; cursor: pointer; border: 2px solid #eee; border-radius: 6px; margin: 8px 0; display: block; }
|
| .unit-icon { width: 42px; height: 42px; background: #2c3e50; border-radius: 4px; display: block; margin: 0 auto; }
|
| .data-table { width: 100%; border-collapse: collapse; margin: 12px 0; background: #fff; }
|
| .data-table td { border: 1px solid #eee; padding: 8px; text-align: center; vertical-align: middle; }
|
| .ladder-table { width: 100%; border-collapse: collapse; }
|
| .ladder-table td { padding: 8px; border-bottom: 1px solid #eee; }
|
| </style>
|
| </head>
|
| <body>
|
| <div class="container">
|
| <h1>🛡️ Game Academy</h1>
|
|
|
| <div class="section-card"><details open><summary>1.1 Стандартная лестница</summary>
|
| <div class="description">{{ t1_1 | safe }}
|
| <table class="ladder-table">
|
| {% for row in l_json %}
|
| <tr>
|
| <td style="width: 45px;"><img src="static/images/{{ row.Icon }}" class="unit-icon"></td>
|
| <td style="font-weight: bold; padding-left: 12px;">{{ row.TXT }}</td>
|
| </tr>
|
| {% endfor %}
|
| </table></div>
|
| </details></div>
|
|
|
| <div class="section-card"><details><summary>1.2 Боевые порядки</summary><div class="description">{{ t1_2 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.3 Формации</summary><div class="description">{{ t1_3 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.4 Механика боя</summary><div class="description">{{ t1_4 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.4.1 Сплоченная атака (СА)</summary><div class="description">{{ t1_4_1 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.4.2 Замок на замок</summary><div class="description">{{ t1_4_2 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.4.3 Саура</summary><div class="description">{{ t1_4_3 | safe }}</div></details></div>
|
| <div class="section-card"><details><summary>1.4.4 Арена, тропы</summary><div class="description">{{ t1_4_4 | safe }}</div></details></div>
|
| </div>
|
| </body>
|
| </html>
|
| """
|
|
|
| @app.route('/')
|
| def index():
|
| return render_template_string(HTML_TEMPLATE,
|
| t1_1=read_txt('chapter1_1.txt'), l_json=read_json('ladder.json'),
|
| t1_2=read_txt('chapter1_2.txt'), t1_3=read_txt('chapter1_3.txt'),
|
| t1_4=read_txt('chapter1_4.txt'), t1_4_1=read_txt('chapter1_4_1.txt'),
|
| t1_4_2=read_txt('chapter1_4_2.txt'), t1_4_3=read_txt('chapter1_4_3.txt'),
|
| t1_4_4=read_txt('chapter1_4_4.txt'))
|
|
|
| if __name__ == '__main__':
|
| app.run(host='0.0.0.0', port=7860) |