# frozen_string_literal: true require "json" require_relative "../config/app" Hanami.app.prepare module ClashDeckGenerator2 module Scripts class BuildRolesJson OUTPUT_PATH = File.expand_path("../config/cards/roles.json", __dir__) BASE_ROLES_BY_TYPE = { "spell" => %w[spell], "building" => %w[building defense], "troop" => %w[support] }.freeze ROLE_OVERRIDES = { # cycle "Skeletons" => %w[cycle defense], "Ice Spirit" => %w[cycle support], "Electro Spirit" => %w[cycle support], "Fire Spirit" => %w[cycle support splash], "Heal Spirit" => %w[cycle support], "Goblins" => %w[cycle defense], "Spear Goblins" => %w[cycle support], "Bats" => %w[cycle anti_air], "Wall Breakers" => %w[cycle win_condition], "Bomber" => %w[cycle splash support], # win conditions "Hog Rider" => %w[win_condition], "Royal Giant" => %w[win_condition tank], "Goblin Barrel" => %w[win_condition], "Balloon" => %w[win_condition], "Giant" => %w[win_condition tank], "Golem" => %w[win_condition tank], "Electro Giant" => %w[win_condition tank], "Goblin Drill" => %w[win_condition], "X-Bow" => %w[win_condition building defense], "Mortar" => %w[win_condition building defense], "Ram Rider" => %w[win_condition support], "Graveyard" => %w[win_condition spell], "Royal Hogs" => %w[win_condition], "Battle Ram" => %w[win_condition], "Elixir Golem" => %w[win_condition tank], "Lava Hound" => %w[win_condition tank anti_air], "Goblin Barrel" => %w[win_condition], "Goblin Drill" => %w[win_condition building], "Miner" => %w[win_condition support], "Goblin Giant" => %w[win_condition tank], "Three Musketeers" => %w[win_condition support anti_air], "Skeleton Barrel" => %w[win_condition], "Rocket" => %w[spell finisher], "Fireball" => %w[spell finisher splash], "Poison" => %w[spell finisher splash], "Lightning" => %w[spell finisher], "Void" => %w[spell finisher], # anti air "Archers" => %w[anti_air support cycle], "Musketeer" => %w[anti_air support], "Wizard" => %w[anti_air support splash], "Baby Dragon" => %w[anti_air support splash], "Electro Dragon" => %w[anti_air support splash], "Inferno Dragon" => %w[anti_air defense], "Mega Minion" => %w[anti_air support], "Flying Machine" => %w[anti_air support], "Hunter" => %w[anti_air defense], "Firecracker" => %w[anti_air support splash], "Minions" => %w[anti_air support], "Minion Horde" => %w[anti_air support], "Skeleton Dragons" => %w[anti_air support splash], "Phoenix" => %w[anti_air support], "Executioner" => %w[anti_air support splash], "Tesla" => %w[anti_air building defense], "Inferno Tower" => %w[building defense], "Magic Archer" => %w[support anti_air], "Zappies" => %w[support anti_air defense], "Dart Goblin" => %w[support anti_air], "Electro Wizard" => %w[support anti_air defense], "Mother Witch" => %w[support anti_air], "Little Prince" => %w[support anti_air], # tanks / mini tanks "Knight" => %w[tank defense], "Valkyrie" => %w[tank defense splash], "Ice Golem" => %w[tank cycle defense], "P.E.K.K.A" => %w[tank defense], "Mini P.E.K.K.A" => %w[defense tank], "Mega Knight" => %w[tank defense splash], "Royal Ghost" => %w[tank support splash], "Dark Prince" => %w[tank support splash], "Prince" => %w[tank support], "Skeleton King" => %w[tank support], "Golden Knight" => %w[tank support], "Monk" => %w[tank defense], "Mighty Miner" => %w[defense tank], "Giant Skeleton" => %w[tank splash], "Lumberjack" => %w[support tank], "Bandit" => %w[support], "Battle Healer" => %w[support tank], "Goblin Giantess" => %w[tank support], # buildings / defense "Cannon" => %w[building defense], "Tesla" => %w[building defense anti_air], "Bomb Tower" => %w[building defense splash], "Tombstone" => %w[building defense], "Goblin Cage" => %w[building defense], "Goblin Hut" => %w[building defense], "Barbarian Hut" => %w[building defense], "Furnace" => %w[building defense splash], "Elixir Collector" => %w[building support], "Goblin Drill" => %w[building win_condition], "X-Bow" => %w[building win_condition defense], "Mortar" => %w[building win_condition defense], # splash / control "Bowler" => %w[splash support defense], "Witch" => %w[support splash], "Bomber" => %w[support splash cycle], "Arrows" => %w[spell splash], "Zap" => %w[spell cycle], "Giant Snowball" => %w[spell cycle], "Royal Delivery" => %w[spell defense splash], "Tornado" => %w[spell control defense], "Rage" => %w[spell support], "Freeze" => %w[spell control], "Clone" => %w[spell support], "Mirror" => %w[spell support], "Earthquake" => %w[spell building_hate], "The Log" => %w[spell cycle defense], "Barbarian Barrel" => %w[spell cycle defense], # support / utility "Skeleton Army" => %w[defense], "Guards" => %w[defense], "Royal Recruits" => %w[defense], "Rascals" => %w[defense support anti_air], "Cannon Cart" => %w[support defense], "Sparky" => %w[support defense], "Night Witch" => %w[support], "Princess" => %w[support splash], "Ice Wizard" => %w[support defense splash], "Fisherman" => %w[defense control], "Goblin Demolisher" => %w[support splash], "Suspicious Bush" => %w[support], "Battle Ram" => %w[win_condition], "Royal Hogs" => %w[win_condition], "Royal Recruits" => %w[defense], "Electro Giant" => %w[win_condition tank], "Electro Dragon" => %w[anti_air support splash], "Goblin Machine" => %w[support], "Spirit Empress" => %w[support], "Boss Bandit" => %w[support], "Goblins" => %w[cycle defense] }.freeze def call repo = ClashDeckGenerator2::Repos::CardsRepo.new cards = unwrap(repo.all) cards_by_name = {} cards.each do |card| cards_by_name[card.name] ||= card end result = { "_meta" => { "version" => 1, "description" => "Card roles mapping for deck generation", "generated_from_db" => true, "cards_count" => cards_by_name.size, "available_roles" => available_roles }, "cards" => {} } cards_by_name.keys.sort.each do |card_name| card = cards_by_name[card_name] roles = roles_for(card) result["cards"][card_name] = roles end FileUtils.mkdir_p(File.dirname(OUTPUT_PATH)) File.write(OUTPUT_PATH, JSON.pretty_generate(result)) puts "roles.json generated: #{OUTPUT_PATH}" puts "Unique cards exported: #{cards_by_name.size}" end private def unwrap(result) if result.is_a?(Array) && result.first == :cards result.last else result end end def roles_for(card) roles = [] roles.concat(BASE_ROLES_BY_TYPE.fetch(card.type, [])) base_name = base_card_name(card.name) override_roles = ROLE_OVERRIDES[card.name] || ROLE_OVERRIDES[base_name] || [] roles.concat(override_roles) roles = roles.map(&:to_s).map(&:strip).reject(&:empty?).uniq.sort roles = fallback_roles(card) if roles.empty? roles end def base_card_name(name) name .sub(/ Evolution\z/, "") .sub(/ Hero\z/, "") end def fallback_roles(card) case card.type when "spell" ["spell"] when "building" %w[building defense] when "troop" ["support"] else ["support"] end end def available_roles %w[ anti_air building building_hate control cycle defense finisher spell splash support tank win_condition ] end end end end ClashDeckGenerator2::Scripts::BuildRolesJson.new.call