"""The Case of Bad Bureaucracy: adapta o projeto para a web e tira a marca reservada. O README do repositorio reserva o nome do projeto, os icones e logos (incluindo a arte do titulo) e o nome e os logos do estudio (pasta assets/branding). Este script, rodado pelo build-case-of-bad-bureaucracy.sh: - troca a arte do titulo (main_menu_title.png / main_menu_subtitle.png) por texto na fonte Aika Bold (OFL 1.1, ja no projeto): "The Case of" / "Bad Bureaucracy". Nos tres lugares que usam a arte (menu, creditos finais e a cena de trailer, que e apagada por nao ser usada pelo jogo); - tira o logo do estudio das cenas (as TextureRect ficam vazias) e o botao de apoio do menu, que abria a pagina do estudio com OS.shell_open; - Integrations.open_store() e a abertura da pasta de traducoes deixam de chamar OS.shell_open; - steamworks.gd (precisa da extensao GodotSteam, ausente) vira um stub vazio; - update_resolution() nao faz nada na web (set_size() redimensionava o canvas para 1920x1080); - o nome do projeto vira "The Case of Bad Bureaucracy". Uso: python3 -I adapt.py """ import os import re import sys project = sys.argv[1] def path(rel): return os.path.join(project, rel) def read(rel): return open(path(rel), encoding="utf-8").read() def write(rel, s): open(path(rel), "w", encoding="utf-8").write(s) def sub_once(s, old, new): assert s.count(old) == 1, (old, s.count(old)) return s.replace(old, new) # --- GDScript --------------------------------------------------------------------------------------------------- s = read("integrations/integrations.gd") a = s.index("func open_store()") b = s.index("#endregion", a) s = s[:a] + "func open_store() -> void:\n\t# Sem loja nesta versao (era OS.shell_open para a pagina do jogo).\n\tpass\n" + s[b:] write("integrations/integrations.gd", s) write( "integrations/steam/steamworks.gd", "extends SteamworksBase\n# Stub: a versao web nao tem Steam (o original usa a extensao GodotSteam).\n", ) s = read("source/scripts/ui/main_menu_buttons.gd") s = sub_once( s, 'func _on_support_us_button_pressed() -> void:\n\tOS.shell_open("https://wanderingdoorgames.obviousgain.ca/supportUs/")\n\n\n', "", ) write("source/scripts/ui/main_menu_buttons.gd", s) s = read("source/scripts/ui/settings_menu.gd") s = sub_once(s, "\tOS.shell_open(absolute_path)\n", "\t# Sem gerenciador de arquivos no navegador (era OS.shell_open(absolute_path)).\n\tpass\n") write("source/scripts/ui/settings_menu.gd", s) # No navegador, set_size() da janela redimensiona o canvas para a resolucao salva (1920x1080) e a pagina corta o jogo. s = read("source/scripts/global_settings.gd") s = sub_once( s, "func update_resolution(value: Vector2i) -> void:\n", "func update_resolution(value: Vector2i) -> void:\n\tif OS.has_feature(\"web\"):\n\t\treturn # o canvas acompanha a janela do navegador\n", ) write("source/scripts/global_settings.gd", s) # --- projeto ---------------------------------------------------------------------------------------------------- s = read("project.godot") s = sub_once(s, 'config/name="Aster Pebble and the Case of Bad Bureaucracy"', 'config/name="The Case of Bad Bureaucracy"') write("project.godot", s) # --- cenas ------------------------------------------------------------------------------------------------------ AIKA = '[ext_resource type="FontFile" path="res://assets/third_party/fonts/Aika-Bold.otf" id="aika_bold_ofl"]\n' TITLE_PNG = "assets/branding/main_menu_title.png" SUB_PNG = "assets/branding/main_menu_subtitle.png" def blocks(s): """Parte o .tscn em blocos que comecam em '[' no inicio da linha.""" parts = re.split(r"(?m)^(?=\[)", s) return parts def ext_ids(s, needle): return re.findall(r'(?m)^\[ext_resource [^\n]*' + re.escape(needle) + r'[^\n]* id="([^"]+)"\]', s) def label_props(kind): if kind == "title": return ( 'text = "THE CASE OF"\n' 'horizontal_alignment = 1\n' 'vertical_alignment = 0\n' 'theme_override_colors/font_color = Color(0.93, 0.36, 0.6, 1)\n' 'theme_override_colors/font_outline_color = Color(0.25, 0.05, 0.15, 1)\n' 'theme_override_constants/outline_size = 16\n' 'theme_override_fonts/font = ExtResource("aika_bold_ofl")\n' 'theme_override_font_sizes/font_size = 150\n' ) return ( 'text = "BAD BUREAUCRACY"\n' 'horizontal_alignment = 1\n' 'vertical_alignment = 1\n' 'theme_override_colors/font_color = Color(0.36, 0.25, 0.1, 1)\n' 'theme_override_fonts/font = ExtResource("aika_bold_ofl")\n' 'theme_override_font_sizes/font_size = 90\n' 'theme_override_styles/normal = SubResource("StyleBoxFlat_signboard")\n' ) SIGN_STYLE = ( '[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_signboard"]\n' "bg_color = Color(0.98, 0.84, 0.5, 1)\n" "border_width_left = 8\nborder_width_top = 8\nborder_width_right = 8\nborder_width_bottom = 8\n" "border_color = Color(0.6, 0.4, 0.18, 1)\n" "corner_detail = 1\n\n" ) def convert(rel): s = read(rel) logos = ext_ids(s, "wandering_door_games_logo") titles = ext_ids(s, TITLE_PNG) subs = ext_ids(s, SUB_PNG) if not (logos or titles or subs): return False out = [] used_sign = False for blk in blocks(s): if blk.startswith("[ext_resource") and any( ('id="%s"' % i) in blk for i in logos + titles + subs ): continue # logo do estudio: a TextureRect/Button fica sem imagem for i in logos: blk = re.sub(r'(?m)^(texture|icon) = ExtResource\("%s"\)\n' % re.escape(i), "", blk) for i, kind in [(t, "title") for t in titles] + [(t, "sub") for t in subs]: tag = 'texture = ExtResource("%s")\n' % i if blk.startswith("[node ") and tag in blk: assert 'type="TextureRect"' in blk.split("\n", 1)[0] head, body = blk.split("\n", 1) head = head.replace('type="TextureRect"', 'type="Label"') body = re.sub(r"(?m)^(expand_mode|stretch_mode) = \d+\n", "", body) body = body.replace(tag, label_props(kind)) if kind == "sub": used_sign = True blk = head + "\n" + body out.append(blk) s = "".join(out) for i in logos + titles + subs: assert 'ExtResource("%s")' % i not in s, (rel, i) # fonte e estilo entram depois dos ext_resource existentes first_sub = re.search(r"(?m)^\[sub_resource", s) last_ext = list(re.finditer(r"(?m)^\[ext_resource[^\n]*\n", s))[-1] s = s[: last_ext.end()] + AIKA + s[last_ext.end():] if used_sign: m = re.search(r"(?m)^\[sub_resource", s) if m: s = s[: m.start()] + SIGN_STYLE + s[m.start():] else: m = re.search(r"(?m)^\[node ", s) s = s[: m.start()] + SIGN_STYLE + s[m.start():] write(rel, s) return True changed = [] for dp, dn, fn in os.walk(path("source")): for f in sorted(fn): if f.endswith(".tscn"): rel = os.path.relpath(os.path.join(dp, f), project) if convert(rel): changed.append(rel) print("convertidas:", changed) # --- botao de apoio do menu --------------------------------------------------------------------------------------- rel = "source/scenes/states/main_menu.tscn" s = read(rel) kept = [] for blk in blocks(s): head = blk.split("\n", 1)[0] if head.startswith("[node ") and ('name="SupportUsButton"' in head or "/SupportUsButton" in head): continue if head.startswith("[connection ") and "SupportUsButton" in head: continue kept.append(blk) s = "".join(kept) s = re.sub(r'(?m)^focus_neighbor_\w+ = NodePath\("[^"]*SupportUsButton"\)\n', "", s) assert "SupportUsButton" not in s write(rel, s) # --- cena de trailer (nao usada pelo jogo) ------------------------------------------------------------------------- for ext in ("tscn", "tscn.uid"): p = path("source/scenes/utility/logo_at_village_entrance." + ext) if os.path.exists(p): os.remove(p) # --- texto da demonstracao (so aparece no build "demo") -------------------------------------------------------------- rel = "source/scenes/states/demo_outro.tscn" s = read(rel) s = sub_once(s, "Aster Pebble and the\nCase of Bad Bureaucracy!", "The Case of\nBad Bureaucracy!") write(rel, s)