Skip to content
This repository was archived by the owner on Oct 14, 2024. It is now read-only.

Commit e261dd9

Browse files
committed
Pregenerate province positions and tag capitals
While tag capital locations were already pregenerated, instead we switch this system to generate 1) the position of each province by id, and 2) the id of the initial capital province or each nation by tag in respective yaml files. This removes the need to include an entire save file, significantly reducing slug size, as well as reducing lookup time in some cases.
1 parent 79622dc commit e261dd9

19 files changed

+37677
-5125344
lines changed

EU4Bot.py

+5-39
Original file line numberDiff line numberDiff line change
@@ -1337,58 +1337,24 @@ async def process(self, message: discord.Message):
13371337
elif text.upper() == "END" and await checkResAdmin(message.guild, message.author): # END
13381338
await message.delete()
13391339
finalReserves: List[EU4Reserve.reservePick] = []
1340-
# This stores the capitals of all possible tags, so that their factions can be determined.
1341-
tagCapitals: Dict[str, int] = {}
1342-
# Add all possibly reserved nations to the tagCapitals dictionary with a capital of -1
13431340
reserves = self.reserve.getPlayers()
1344-
for res in reserves:
1345-
for tag in res.picks:
1346-
if tagCapitals.get(tag.upper()) is None:
1347-
tagCapitals[tag.upper()] = -1
1348-
# Get the actual capitals and add to tagCapitals.
1349-
srcFile = open("resources/save_1444.eu4", "r", encoding="cp1252")
1350-
brackets: List[str] = []
1351-
linenum = 0
1352-
for line in srcFile:
1353-
linenum += 1
1354-
if "{" in line:
1355-
if line.count("{") == line.count("}"):
1356-
continue
1357-
elif line.count("}") == 0 and line.count("{") == 1:
1358-
brackets.append(line.rstrip("\n "))
1359-
elif line.count("}") == 0 and line.count("{") > 1:
1360-
for x in range(line.count("{")):
1361-
# TODO: fix this so it has more
1362-
brackets.append("{")
1363-
else:
1364-
print(f"Unexpected brackets at line {linenum}: {line}")
1365-
elif "}" in line:
1366-
try:
1367-
brackets.pop()
1368-
except IndexError: # This shouldn't happen.
1369-
print(
1370-
f"No brackets to delete at line {linenum}: {line}")
1371-
elif len(brackets) > 1 and brackets[0] == "countries={":
1372-
for x in tagCapitals:
1373-
if x in brackets[1]:
1374-
# Here we have all the stats for country x on the players list
1375-
if len(brackets) == 2 and "capital=" in line and not "original_capital=" in line and not "fixed_capital=" in line:
1376-
tagCapitals[x] = int(line.strip("\tcapitl=\n"))
1341+
13771342
# Draft Executive Reserves
13781343
for res in reserves:
13791344
if res.priority:
13801345
finalReserves.append(EU4Reserve.reservePick(
13811346
res.userID, res.picks[0].upper()))
1382-
self.getFaction(
1383-
tagCapitals[res.picks[0].upper()]).taken += 1
1347+
self.getFaction(EU4Lib.tagCapital(
1348+
res.picks[0].upper())).taken += 1
13841349
reserves.remove(res)
13851350
# Shuffle
13861351
shuffle(reserves)
13871352
# Draft Reserves
13881353
for res in reserves:
13891354
finaltag = None
13901355
for tag in res.picks:
1391-
resFaction = self.getFaction(tagCapitals[tag.upper()])
1356+
resFaction = self.getFaction(
1357+
EU4Lib.tagCapital(tag.upper()))
13921358
# if faction is full, skip to next one
13931359
if resFaction is None or resFaction.taken >= resFaction.maxPlayers:
13941360
continue

EU4Lib.py

+30-143
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# This will need to be updated. Currently for 1.30.2
2-
from typing import Dict, List, Optional, Tuple, Union
1+
from typing import Dict, Optional, Tuple, Union
2+
import yaml
33

44
from PIL import Image, ImageDraw
5+
from os import listdir, path
56

67

78
def country(text: str, mod: str = "vanilla") -> Optional[str]:
@@ -72,69 +73,40 @@ def tagToName(tag: str, mod: str = "vanilla") -> Optional[str]:
7273
return None
7374

7475

75-
def province(id: Union[str, int], mod: str = "vanilla") -> Optional[Tuple[float, float]]:
76+
# LOAD PROVINCE LOCATION DATA
77+
# Even if not __main__
78+
province_locations: Dict[str, Dict[int, Tuple[float, float]]] = {}
79+
for p in listdir("resources"):
80+
filepath = path.join("resources", p, "positions.yml")
81+
if path.isfile(filepath):
82+
province_locations_file = open(filepath, "r")
83+
province_locations[p] = yaml.load(
84+
province_locations_file, yaml.CLoader)
85+
province_locations_file.close()
86+
87+
88+
def province(id: Union[str, int], mod: str = "vanilla") -> Tuple[float, float]:
7689
"""
7790
Gets the location of a province on a screenshot map.
7891
7992
Returns a tuple of floats, (x, y).
8093
"""
81-
# Read file
82-
srcFile = open(f"resources/{mod}/positions.txt", "r", encoding="cp1252")
83-
"""
84-
Format of the file:
85-
1={
86-
position={
87-
3085.000 1723.000 3086.000 1730.000 3084.500 1729.000 3095.000 1724.500 3082.000 1730.000 3080.000 1736.000 0.000 0.000
88-
}
89-
rotation={
90-
0.000 0.000 0.087 -0.698 0.000 0.000 0.000
91-
}
92-
height={
93-
0.000 0.000 1.000 0.000 0.000 0.000 0.000
94-
}
95-
}
96-
97-
So we want the 3085.000 1723.000 from the 1={ because the first two are the location of the city in the province
98-
"""
99-
beyond = 0
100-
for line in srcFile:
101-
if beyond == 2: # Two after the province, this is the line with the position.
102-
vals = line.split()
103-
# Need to subtract the y value because the position starts from the bottom rather than the top like images
104-
return (float(vals[0]), 2048-float(vals[1]))
105-
elif beyond == 1: # One after the province, wait one more line for the position
106-
beyond = 2
107-
elif line.strip() == str(id)+"={":
108-
# So we have the province... Wait two lines for the position
109-
beyond = 1
110-
return None
94+
return province_locations[mod][int(id)]
11195

11296

113-
def provinces(ids: List[Union[str, int]], mod: str = "vanilla") -> Dict[int, Tuple[float, float]]:
114-
"""
115-
Gets the location of multiple provinces on a screenshot map.
116-
Similar to province() except that it will only go through the file once to find all provinces.
117-
"""
118-
out: Dict[int, Optional[Tuple[float, float]]] = {}
119-
ids: List[int] = [int(x) for x in ids]
120-
srcFile = open(f"resources/{mod}/positions.txt", "r", encoding="cp1252")
121-
currentID: int = None
122-
beyond = 0
123-
for line in srcFile:
124-
if beyond == 2: # Two after the province, this is the line with the position.
125-
vals = line.split()
126-
# Need to subtract the y value because the position starts from the bottom rather than the top like images
127-
out[currentID] = (float(vals[0]), 2048-float(vals[1]))
128-
currentID = None
129-
beyond = 0
130-
elif beyond == 1: # One after the province, wait one more line for the position
131-
beyond = 2
132-
elif line.strip("\t ={\n").isdigit(): # Province top-level bracket
133-
id = int(line[:line.index("={")])
134-
if id in ids:
135-
currentID = id
136-
beyond = 1
137-
return out
97+
# LOAD INITIAL TAG CAPITAL DATA
98+
# Even if not __main__
99+
tag_capitals: Dict[str, Dict[str, int]] = {}
100+
for p in listdir("resources"):
101+
filepath = path.join("resources", p, "tagCapitals.yml")
102+
if path.isfile(filepath):
103+
tag_capitals_file = open(filepath, "r")
104+
tag_capitals[p] = yaml.load(tag_capitals_file, yaml.CLoader)
105+
tag_capitals_file.close()
106+
107+
108+
def tagCapital(tag: str, mod: str = "vanilla") -> int:
109+
return tag_capitals[mod][tag]
138110

139111

140112
def flag(tag: str, mod: str = "vanilla") -> Image.Image:
@@ -328,88 +300,3 @@ def colonialFlag(overlordTag: str, colReg: str, mod: str = "vanilla") -> Image.I
328300
flagDraw = ImageDraw.Draw(flagimg)
329301
flagDraw.rectangle([64, 0, 127, 127], color)
330302
return flagimg
331-
332-
333-
class dataReq:
334-
DATATYPE_PROVINCEDAT = 0
335-
REQUEST_PROVINCE_NAME = 0
336-
REQUEST_PROVINCE_TRADE = 1
337-
REQUEST_PROVINCE_CULTURE_ORIGINAL = 2
338-
REQUEST_PROVINCE_RELIGION_ORIGINAL = 3
339-
340-
def __init__(self, datatype: int, key: str, request: int):
341-
self.datatype = datatype
342-
self.key = key
343-
self.request = request
344-
self.response = None
345-
346-
def respond(self, r):
347-
if self.datatype == self.DATATYPE_PROVINCEDAT:
348-
if self.request == self.REQUEST_PROVINCE_NAME:
349-
if isinstance(r, str):
350-
self.response = r
351-
else:
352-
raise ValueError(
353-
f"PROVINCE NAME request for {self.key} was the wrong type.")
354-
elif self.request == self.REQUEST_PROVINCE_TRADE:
355-
if isinstance(r, str):
356-
self.response = r
357-
else:
358-
raise ValueError(
359-
f"PROVINCE TRADE request for {self.key} was the wrong type.")
360-
elif self.request == self.REQUEST_PROVINCE_CULTURE_ORIGINAL:
361-
if isinstance(r, str):
362-
self.response = r
363-
else:
364-
raise ValueError(
365-
f"PROVINCE CULTURE ORIGINAL request for {self.key} was the wrong type.")
366-
elif self.request == self.REQUEST_PROVINCE_RELIGION_ORIGINAL:
367-
if isinstance(r, str):
368-
self.response = r
369-
else:
370-
raise ValueError(
371-
f"PROVINCE RELIGION ORIGINAL request for {self.key} was the wrong type.")
372-
# More things
373-
# More datatypes
374-
375-
376-
def provinceData(*requests: dataReq, mod: str = "vanilla") -> List[dataReq]:
377-
data = requests
378-
srcFile = open(f"resources/{mod}/save_1444.eu4", encoding="cp1252")
379-
brackets: List[str] = []
380-
381-
# Reading save file...
382-
linenum = 0
383-
for line in srcFile:
384-
linenum += 1
385-
if "{" in line:
386-
if line.count("{") == line.count("}"):
387-
continue
388-
elif line.count("}") == 0 and line.count("{") == 1:
389-
brackets.append(line.rstrip("\n "))
390-
elif line.count("}") == 0 and line.count("{") > 1:
391-
# TODO: fix this so it has more stuff
392-
brackets.append("{" * line.count("{"))
393-
else:
394-
pass
395-
elif "}" in line:
396-
try:
397-
brackets.pop()
398-
except IndexError:
399-
pass
400-
elif len(brackets) == 2 and "provinces={" == brackets[0]:
401-
for request in data:
402-
if request.response is None and request.datatype == dataReq.DATATYPE_PROVINCEDAT and ("-" + str(request.key) + "={") == brackets[1]:
403-
if request.request == dataReq.REQUEST_PROVINCE_NAME and line.startswith("\t\tname="):
404-
request.respond(line.split("\"", 2)[1].strip("\n\t "))
405-
elif request.request == dataReq.REQUEST_PROVINCE_TRADE and line.startswith("\t\ttrade="):
406-
request.respond(line.split("\"", 2)[1].strip("\n\t "))
407-
elif request.request == dataReq.REQUEST_PROVINCE_CULTURE_ORIGINAL and line.startswith("\t\toriginal_culture="):
408-
request.respond(line.split("=", 1)[1].strip("\n\t "))
409-
elif request.request == dataReq.REQUEST_PROVINCE_RELIGION_ORIGINAL and line.startswith("\t\toriginal_religion="):
410-
request.respond(line.split("=", 1)[1].strip("\n\t "))
411-
# elif len(brackets) < 0 and ("trade={" == brackets[1] or "rebel_faction={" == brackets[0] or (len(brackets) < 1 and "\tledger_data={" == brackets[1]) or "_area={" in brackets[0] or "change_price={" == brackets[0]):
412-
# continue
413-
else:
414-
pass
415-
return data

EU4Reserve.py

+5-13
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from PIL import Image
1010
from pymongo.collection import Collection
1111
from pymongo.database import Database
12+
import EU4Lib
1213

1314
dotenv.load_dotenv()
1415
client = pymongo.MongoClient(
@@ -404,20 +405,11 @@ def createMap(reserve: Reserve) -> Image.Image:
404405
countries: List[reservePick] = reserve.getPlayers()
405406
capitalLocs: Dict[str, Tuple[float, float]] = {}
406407

407-
srcFile = open("resources/tagCapitals.txt", "r", encoding="cp1252")
408+
for country in countries:
409+
capitalLocs[country.tag] = EU4Lib.province(
410+
EU4Lib.tagCapital(country.tag))
408411

409-
for line in srcFile:
410-
for natnum in range(len(countries)):
411-
if line.startswith(countries[natnum].tag):
412-
capitalLocs[countries[natnum].tag] = (
413-
float(line[4:line.index(",", 4)]), float(line[line.index(",", 4) + 1:]))
414-
countries.pop(natnum)
415-
break
416-
if len(countries) == 0:
417-
break
418-
srcFile.close()
419-
420-
mapFinal: Image.Image = Image.open("resources/map_1444.png")
412+
mapFinal: Image.Image = Image.open("resources/vanilla/map_1444.png")
421413
imgX: Image.Image = Image.open("resources/xIcon.png")
422414
for x in capitalLocs:
423415
mapFinal.paste(

EU4cpplib.cpp

-40
Original file line numberDiff line numberDiff line change
@@ -522,45 +522,6 @@ py::bytes pyDrawMap(const std::map<std::string, std::tuple<uint8_t, uint8_t, uin
522522
return drawMap(tagColors, provinceOwners, mod);
523523
}
524524

525-
std::pair<float, float> provinceLocation(size_t id, const std::string &mod) {
526-
std::string idstr = std::to_string(id);
527-
std::ifstream file;
528-
file.open(std::string("resources/") + mod + "/positions.txt");
529-
530-
std::string line;
531-
while (std::getline(file, line)) {
532-
// While items such as "11={" will catch "1={", that is fine because the smaller number will come first, meaning the larger number should not be reached.
533-
if (line.find(idstr + "={") != std::string::npos) {
534-
535-
std::getline(file, line); // position={
536-
std::getline(file, line); // Line with position data. We want the first two.
537-
538-
std::pair<float, float> out;
539-
bool second = false;
540-
size_t start = std::string::npos;
541-
542-
// Go through each character to get the first two numbers
543-
for (size_t i = 0; i < line.size(); ++i) {
544-
if (start == std::string::npos && !std::isspace(line[i])) {
545-
// The first character of the number
546-
start = i;
547-
} else if (start != std::string::npos && std::isspace(line[i])) {
548-
// The first whitespace after the number
549-
if (second) {
550-
out.second = std::stof(line.substr(start, i - start));
551-
return out;
552-
} else {
553-
out.first = std::stof(line.substr(start, i - start));
554-
start = std::string::npos;
555-
second = true;
556-
}
557-
}
558-
}
559-
}
560-
}
561-
return std::pair<float, float>(-1.0f, -1.0f);
562-
}
563-
564525
PYBIND11_MODULE(EU4cpplib, m) {
565526
m.doc() = "Libraries for eu4img written in C++ to improve speed and resource-consumption.";
566527

@@ -581,7 +542,6 @@ PYBIND11_MODULE(EU4cpplib, m) {
581542
.def("isEU4Date", &EU4Date::isEU4Date)
582543
.def_static("stringValid", &EU4Date::stringValid, py::arg("text"));
583544

584-
m.def("provinceLocation", &provinceLocation, py::arg("id"), py::arg("mod"));
585545
m.def("drawBorders", &drawBorders, py::arg("playerColors"), py::arg("pixels"), py::arg("width"), py::arg("height"));
586546
m.def("loadProvinceMap", &pyProvMap, py::arg("mod"));
587547
m.def("loadMapDef", &loadMapDef, py::arg("mod"));

EU4cpplib.pyi

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class EU4Date:
1616
def stringValid(text: str) -> bool: ...
1717

1818

19-
def provinceLocation(id: int, mod: str) -> Tuple[float, float]: ...
2019
def drawBorders(playerColors: Dict, pixels: bytes,
2120
width: int, height: int) -> Dict: ...
2221

requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Pillow>=8.3.2
44
pymongo[srv]>=3.11.3
55
python-dateutil>=2.8.1
66
python-dotenv>=0.17.0
7+
pyyaml>=6.0
78
requests>=2.25.1
89
cppimport>=21.3.7
910
pybind11>=2.6.2

0 commit comments

Comments
 (0)