|
| 1 | +from PIL import Image, ImageDraw, ImageFont |
| 2 | +import os |
| 3 | +import EU4Lib |
| 4 | +srcFile = open(input(".EU4 file name: ").strip('"\n'), "r") |
| 5 | +imgPolitical = Image.open(input(".png political mapmode name: ").strip('"\n')) |
| 6 | +imgPlayers = Image.open(input(".png players mapmode name: ").strip('"\n')) |
| 7 | + |
| 8 | +imgFinal = Image.open("src//finalTemplate.png") |
| 9 | +mapFinal = imgPolitical.copy() |
| 10 | + |
| 11 | +#Clears terminal |
| 12 | +def clear(): |
| 13 | + #print(chr(27) + "[2J") |
| 14 | + os.system('cls' if os.name == 'nt' else 'clear') |
| 15 | + |
| 16 | +class Nation: |
| 17 | + def __init__(self, player): |
| 18 | + self.player = player |
| 19 | + self.tag = None |
| 20 | + self.development = None |
| 21 | + self.prestige = None |
| 22 | + self.stability = None |
| 23 | + #self.manpower = None |
| 24 | + #self.maxManpower = None |
| 25 | + self.army = 0.0 |
| 26 | + self.navy = 0 |
| 27 | + self.debt = 0 |
| 28 | + self.treasury = 0.0 |
| 29 | + self.totalIncome = 0.0 |
| 30 | + self.scorePlace = None |
| 31 | + |
| 32 | +#Start Data Selection |
| 33 | +countries = [] |
| 34 | +playertags = [] |
| 35 | +dlc = [] |
| 36 | +GP = [] |
| 37 | +lines = srcFile.readlines() |
| 38 | +brackets = [] |
| 39 | +print("Reading save file...") |
| 40 | +for line in lines: |
| 41 | + if "{" in line: |
| 42 | + if line.strip("\n ").endswith("}"): |
| 43 | + continue |
| 44 | + else: |
| 45 | + brackets.append(line.rstrip("\n ")) |
| 46 | + #print("{") |
| 47 | + elif "}" in line: |
| 48 | + brackets.pop() |
| 49 | + #print("}") |
| 50 | + #Get rid of long, useless sections |
| 51 | + #Possible to use sections: |
| 52 | + #Provinces |
| 53 | + elif len(brackets) < 0 and ("trade={" == brackets[1] or "provinces={" == brackets[0] or "rebel_faction={" == brackets[0] or (len(brackets) < 1 and "\tledger_data={" == brackets[1]) or "_area={" in brackets[0] or "change_price={" == brackets[0]): |
| 54 | + continue |
| 55 | + #Ignoring all but player / GP |
| 56 | + #Possible to use: HRE / pope / crusade / china |
| 57 | + |
| 58 | + #elif len(brackets) < 0 and ("countries={" == brackets[0] and not (line.strip("\t={\n") in GP or line.strip("\t={\n") in playertags)): |
| 59 | + #print("HI" + line.strip("\t={\n")) |
| 60 | + #continue |
| 61 | + else: |
| 62 | + #This is where we do stuff |
| 63 | + #Get current gamedate |
| 64 | + if line.startswith("date=") and brackets == []: |
| 65 | + date = line.strip('date=\n') |
| 66 | + #Get save DLC (not sure if we use this...) |
| 67 | + elif brackets == ["dlc_enabled={"]: |
| 68 | + dlc.append(line.strip('\t"\n')) |
| 69 | + #Check if game is mp |
| 70 | + elif "multi_player=" in line and brackets == []: |
| 71 | + if "yes" in line: |
| 72 | + mp = True |
| 73 | + else: |
| 74 | + mp = False |
| 75 | + #Get player names and country tags |
| 76 | + elif brackets == ["players_countries={"]: |
| 77 | + #In the file, the format is like this: |
| 78 | + #players_countries={ |
| 79 | + # "playername" |
| 80 | + # "SWE" |
| 81 | + # |
| 82 | + #Where " " is a tab |
| 83 | + #This v adds a new country object and player name if there is none open. |
| 84 | + #print("Found a line in players_countries") |
| 85 | + if len(countries) == 0 or countries[len(countries)-1].tag is not None: |
| 86 | + print("Adding: ", line.strip('\t"\n')) |
| 87 | + countries.append(Nation(line.strip('\t"\n'))) |
| 88 | + #Add country code to most recent country (which, because of ^ cannot have a tag) |
| 89 | + else: |
| 90 | + for x in countries: |
| 91 | + if x.tag == line.strip('\t"\n'): #Players are added later to the list as they join, so we remove all previous players |
| 92 | + countries.remove(x) |
| 93 | + countries[len(countries)-1].tag = line.strip('\t"\n') |
| 94 | + playertags.append(line.strip('\t"\n')) |
| 95 | + print("Country: ", line.strip('\t"\n')) |
| 96 | + #Get current age |
| 97 | + elif "current_age=" in line and brackets == []: |
| 98 | + age = line[12:].strip('"\n') |
| 99 | + print("\nAge: " + age) |
| 100 | + #Get top 8 |
| 101 | + elif "country=" in line and brackets == ["great_powers={", "\toriginal={"]: |
| 102 | + if len(GP) < 8: #Make sure to not include leaving GPs |
| 103 | + GP.append(line.strip('\tcountry="\n')) |
| 104 | + print("Found GP: " + line.strip('\tcountry="\n')) |
| 105 | + #Get HRE emperor tag |
| 106 | + elif "\temperor=" in line and brackets == ["empire={"]: |
| 107 | + HRETag = line.strip('\temperor="\n') |
| 108 | + print("Found HRE Emperor: " + HRETag) |
| 109 | + #Get Celestial emperor tag |
| 110 | + elif "emperor=" in line and brackets == ["celestial_empire={"]: |
| 111 | + chinaTag = line.strip('\temperor="\n') |
| 112 | + print("Found Celestial Empire: " + chinaTag) |
| 113 | + #Get target of crusade ('---' if none) |
| 114 | + elif "crusade_target=" in line and brackets == ["religion_instance_data={", "\tcatholic={", "\t\tpapacy={"]: |
| 115 | + crusade = line.strip('\tcrusade_target="\n') |
| 116 | + print("Found crusade target: " + crusade) |
| 117 | + #Get papal controller |
| 118 | + elif "previous_controller=" in line and brackets == ["religion_instance_data={", "\tcatholic={", "\t\tpapacy={"]: |
| 119 | + continue |
| 120 | + #Country-specific for players |
| 121 | + #TODO: Not sure if this need optimization |
| 122 | + elif len(brackets) > 1 and brackets[0] == "countries={" and brackets[1].strip("\t={\n") in playertags: |
| 123 | + for x in countries: |
| 124 | + if x.tag in brackets[1]: |
| 125 | + #Here we have all the stats for country x on the players list |
| 126 | + if len(brackets) == 2: |
| 127 | + if "raw_development=" in line: |
| 128 | + x.development = round(float(line.strip("\traw_devlopmnt=\n"))) |
| 129 | + elif "score_place=" in line: |
| 130 | + x.scorePlace = round(float(line.strip("\tscore_place=\n"))) |
| 131 | + elif "prestige=" in line: |
| 132 | + x.prestige = round(float(line.strip("\tprestige=\n"))) |
| 133 | + elif "stability=" in line: |
| 134 | + x.stability = round(float(line.strip("\tstability=\n"))) |
| 135 | + elif "treasury=" in line: |
| 136 | + x.treasury = round(float(line.strip("\ttreasury=\n"))) |
| 137 | + elif "estimated_monthly_income=" in line: |
| 138 | + x.totalIncome = round(float(line.strip("\testimated_monthly_income=\n"))) |
| 139 | + #elif "\tmanpower=" in line: |
| 140 | + #x.manpower = round(float(line.strip("\tmanpower=\n"))) |
| 141 | + #elif "max_manpower=" in line: |
| 142 | + #x.maxManpower = round(float(line.strip("\tmax_manpower=\n"))) |
| 143 | + else: continue |
| 144 | + elif len(brackets) == 3: |
| 145 | + #Get size of each loan |
| 146 | + if brackets[2] == "\t\tloan={" and "amount=" in line: |
| 147 | + x.debt += round(float(line.strip("\tamount=\n"))) |
| 148 | + elif len(brackets) == 4: |
| 149 | + #Add 1 to army size for each regiment |
| 150 | + if brackets[2] == "\t\tarmy={" and "regiment={" in brackets[3] and "morale=" in line: |
| 151 | + x.army = x.army + 1000 |
| 152 | + #Subtract damage done to units from army size |
| 153 | + #This needs to be separate from ^ because for full regiments there is no "strength=" tag |
| 154 | + elif brackets[2] == "\t\tarmy={" and "regiment={" in brackets[3] and "strength=" in line: |
| 155 | + try: |
| 156 | + x.army = round(x.army - 1000 + 1000*float(line.strip("\tstrength=\n"))) |
| 157 | + except ValueError: |
| 158 | + continue |
| 159 | + elif brackets[2] == "\t\tnavy={" and brackets[3] == "\t\t\tship={" and "\thome=" in line: |
| 160 | + x.navy += 1 |
| 161 | + |
| 162 | +for x in countries: #Remove dead countries |
| 163 | + if x.development is None or x.development == None or x.development == 0: |
| 164 | + countries.remove(x) |
| 165 | +#Sort Data: |
| 166 | +countries.sort(key=lambda x: x.development, reverse=True) |
| 167 | + |
| 168 | + |
| 169 | +print("Finished extracting save data.") |
| 170 | +print("\nPlayer nations: "+ str(len(countries))) |
| 171 | +print(mp) |
| 172 | +print(date) |
| 173 | +print(dlc) |
| 174 | +for x in countries: |
| 175 | + print("\n"+x.tag+ ": "+ x.player) |
| 176 | + print("Army:", x.army) |
| 177 | + print("Navy:", x.navy) |
| 178 | + print("Dev:", x.development) |
| 179 | + print("Stab:", x.stability) |
| 180 | + print("Treasury:", x.treasury) |
| 181 | + print("Debt:", x.debt) |
| 182 | + #print("Manpower: "+ x.manpower) |
| 183 | + #print("Max Manpower: "+ x.maxManpower) |
| 184 | + print("Prestige:", x.prestige) |
| 185 | + |
| 186 | +#End Data Selection |
| 187 | +print("") |
| 188 | +#Start Map Creation |
| 189 | +#print("Showing political mapmode...") |
| 190 | +#totalPixels = mapFinal.size[0] * mapFinal.size[1] |
| 191 | +#mapFinal.show() |
| 192 | +print("Preparing map editing...") |
| 193 | +mapDraw = ImageDraw.Draw(mapFinal) |
| 194 | +print("Drawing player country borders...") |
| 195 | +for x in range(mapFinal.size[0]): |
| 196 | + for y in range(mapFinal.size[1]): |
| 197 | + #Get color for each pixel |
| 198 | + #In EU4 player mapmode screenshots, |
| 199 | + #Water: (68, 107, 163) |
| 200 | + #AI: (127, 127, 127) |
| 201 | + #Wasteland: (94, 94, 94) |
| 202 | + color = imgPlayers.getpixel((x, y)) |
| 203 | + if color == (68, 107, 163) or color == (127, 127, 127) or color == (94, 94, 94): |
| 204 | + #print(round(100*x*y/totalPixels, 2), "% Done (", x, ", ", y, ")") |
| 205 | + continue |
| 206 | + else: |
| 207 | + #All pixels on the edge should be water and wasteland so not get past ^ if, although custom games may break this by not being real pixels |
| 208 | + #TODO: Make no borders for wasteland |
| 209 | + if color != imgPlayers.getpixel((x - 1, y - 1)) or color != imgPlayers.getpixel((x - 1, y)) or color != imgPlayers.getpixel((x - 1, y + 1)) or color != imgPlayers.getpixel((x, y - 1)) or color != imgPlayers.getpixel((x, y + 1)) or color != imgPlayers.getpixel((x + 1, y - 1)) or color != imgPlayers.getpixel((x + 1, y)) or color != imgPlayers.getpixel((x + 1, y + 1)): |
| 210 | + #Black for player borders |
| 211 | + mapDraw.point((x, y), (255-color[0], 255-color[1], 255-color[2])) |
| 212 | + #print(round(100*x*y/totalPixels, 2), "% Done (", x, ", ", y, ")") |
| 213 | +print("Map editing done.") |
| 214 | +#End Map Creation |
| 215 | +#mapFinal.show() |
| 216 | +#print("Saving map in 'map.png'...") |
| 217 | +#mapFinal.save("map.png", "PNG") |
| 218 | + |
| 219 | +#Start Final Img Creation |
| 220 | +print("Copying map into final image...") |
| 221 | +imgFinal.paste(mapFinal, (0, imgFinal.size[1]-mapFinal.size[1])) #Copy map into bottom of final image |
| 222 | +print("Preparing final image editing...") |
| 223 | +#The top has 5632x1119 |
| 224 | +font = ImageFont.load_default() |
| 225 | +fontbig = ImageFont.load_default() |
| 226 | +try: |
| 227 | + font = ImageFont.truetype("FONT.TTF", 100) |
| 228 | + fontbig = ImageFont.truetype("FONT.TTF", 180) |
| 229 | + print("Found font. Size:", font.getsize) |
| 230 | +except(FileNotFoundError, IOError): |
| 231 | + try: |
| 232 | + font = ImageFont.truetype("GARA.TTF", 100) |
| 233 | + fontbig = ImageFont.truetype("GARA.TTF",180) |
| 234 | + print("Found EU4 font.") |
| 235 | + except(FileNotFoundError, IOError): |
| 236 | + font = ImageFont.load_default() |
| 237 | + fontbig = ImageFont.load_default() |
| 238 | + print("Could not find font. Using default. Size:", font.getsize) |
| 239 | +#Players section from (20,30) to (4710, 1100) half way is x=2345 |
| 240 | +#So start with yborder = 38, yheight = 128 for each player row. x just make it half or maybe thirds depending on how it goes |
| 241 | +imgDraw = ImageDraw.Draw(imgFinal) |
| 242 | +for nat in countries: |
| 243 | + natnum = countries.index(nat) |
| 244 | + x = 38 + 2000*int(natnum/8) |
| 245 | + y = 38 + 128*(natnum%8) |
| 246 | + if (natnum < 16): |
| 247 | + print(nat.tag + " adding") |
| 248 | + #x: Country |
| 249 | + imgFinal.paste(EU4Lib.flag(nat.tag), (x, y)) |
| 250 | + #x+128: Player |
| 251 | + imgDraw.text((x+128, y), nat.player, (255, 255, 255), font) |
| 252 | + #x+760: Army |
| 253 | + imgFinal.paste(Image.open("src//army.png"), (x+760, y)) |
| 254 | + armydisplay = str(round(nat.army/1000, 1)) |
| 255 | + if armydisplay.endswith(".0") or ("." in armydisplay and len(armydisplay) > 4): |
| 256 | + armydisplay = armydisplay.partition(".")[0] |
| 257 | + armydisplay = armydisplay + "k" |
| 258 | + imgDraw.text((x+760+128, y), armydisplay, (255, 255, 255), font) |
| 259 | + #x+1100: Navy |
| 260 | + imgFinal.paste(Image.open("src//navy.png"), (x+1100, y)) |
| 261 | + imgDraw.text((x+1100+128, y), str(nat.navy), (255, 255, 255), font) |
| 262 | + #x+1440: Development |
| 263 | + imgFinal.paste(Image.open("src//development.png"), (x+1440, y)) |
| 264 | + imgDraw.text((x+1440+128, y), str(nat.development), (255, 255, 255), font) |
| 265 | + #x+1780: |
| 266 | + else: |
| 267 | + print(nat.tag + " does not fit!") |
| 268 | + |
| 269 | + |
| 270 | +#Date |
| 271 | +imgDraw.text((4800,85), date, (255, 255, 255), fontbig) |
| 272 | + |
| 273 | + |
| 274 | +print("Final image editing done.") |
| 275 | +imgFinal.show() |
| 276 | +print("Saving final image...") |
| 277 | +imgFinal.save("final.png", "PNG") |
| 278 | +#End Final Img Creation |
| 279 | + |
| 280 | +end = input("Done!") #Press enter to end |
0 commit comments