Skip to content

Commit 6fe7bbf

Browse files
committed
1.4.0: Initial Sundarpatan support, new logo, migrate to FreeSimpleGUI
1 parent aae8996 commit 6fe7bbf

22 files changed

Lines changed: 1614 additions & 607 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ apc.egg-info
55
mods
66
backups
77
venv
8+
.python-version
89
*/__pycache__
910
.DS_Store
1011
build

README.md

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# animal-population-changer
22

3-
A tool that allows anyone to change the animal population on all the reserves in theHunter: Call of the Wild (COTW).
3+
A tool to modify attributes of animals across all reserves in theHunter: Call of the Wild (COTW).
44

5-
![GUI](https://user-images.githubusercontent.com/2107385/248629158-47bec7aa-a978-4cc7-8fd0-dfeaea8e6777.gif)
5+
Release builds are available here on GitHub and on NexusMods: https://www.nexusmods.com/thehuntercallofthewild/mods/440
6+
7+
![Screenshot](screenshot.png)
68

79
This tool can make all species a diamond, the appropriate species a Great one, or have a rare fur.
810

@@ -13,30 +15,32 @@ The following mods are possible with this tool:
1315
1. Make a female animal a male.
1416
1. Make a male a female.
1517

16-
The modded population files can be found in a `mods` folder in the same directory you are running the tool.
17-
18-
To download the latest releases of this tool, go to [NexusMods](https://www.nexusmods.com/thehuntercallofthewild/mods/225) where you can also post bugs and have a conversation with the COTW modding community.
19-
20-
Testing with game version: 2649775
18+
The modded population files can be found in a `mods` folder in the same directory you are running the tool.
2119

2220
## Limitations:
21+
2322
* This tool was tested on Windows 11 with the game installed via Steam. It is smart enough to also look where Epic Games saves its files too. If your game files are saved somewhere else besides where Steam or Epic saves them, use the `Configure Game Path` button.
2423
* The species that use the newer TruRACS trophy system may not become a diamond. This is an area where I am still doing research to figure out how exactly to manipulate.
2524
* If you use the executables (EXE) files, your system may complain there is a virus. This is not true, but the `pyinstaller` package that builds the executable is often used by hackers, and so it is being flagged. To avoid this, simply install the tool from the `wheel` file or build it from source.
26-
25+
2726
## How To Build
2827

2928
> Note: This code was built and tested with Python 3.10.10.
3029
31-
To install dependencies:
30+
Setup virtual environment:
31+
```sh
32+
python -m venv venv
33+
venv\Scripts\activate
34+
```
35+
36+
Install dependencies:
3237
```sh
3338
pip install -r requirements.txt
34-
python -m PySimpleGUI.PySimpleGUI upgrade
3539
```
3640

3741
You can run the packages directly by using:
3842
```sh
39-
pyton -m apcgui
43+
python -m apcgui
4044
```
4145

4246
You can install a developer version by using:
@@ -48,15 +52,15 @@ If you want to build from a wheel:
4852
```sh
4953
pip install -U build
5054
python -m build
51-
pip install dist/apc-0.1.0-py3-none-any.whl
55+
pip install dist/apc-1.4.0-py3-none-any.whl
5256
```
5357

5458
If you want to build directly from GitHub:
5559
```sh
56-
pip install -e git+https://github.com/rollerb/apc.git#egg=apc
60+
pip install -e git+https://github.com/RyMaxim/apc.git#egg=apc
5761
```
5862

59-
If you want to build an executable (i.e., from Windows):
63+
If you want to build an executable (for Windows):
6064
```sh
6165
pip install -U pyinstaller
6266
pyinstaller --noconsole --add-data "apc/config;config" --add-data "apc/locale;locale" --add-data "apcgui/locale;locale" apcgui.py

apc/config.py

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ def get_languages() -> list:
2121
use_languages = env_language.split(':')
2222
else:
2323
use_languages = [default_locale]
24-
25-
use_languages = list(filter(lambda x: x in SUPPORTED_LANGUAGES, use_languages))
24+
25+
use_languages = list(filter(lambda x: x in SUPPORTED_LANGUAGES, use_languages))
2626
if len(use_languages) == 0:
2727
use_languages = ["en_US"]
28-
28+
2929
return (default_locale, use_languages)
3030

3131
LOCALE_PATH = Path(getattr(sys, '_MEIPASS', Path(__file__).resolve().parent)) / "locale/"
@@ -317,6 +317,10 @@ def _find_saves_path() -> str:
317317
# TODO: diamonds that can be both genders need different weight / score values
318318
# TODO: kangaroos with multiple white furs
319319
# TODO: crocodiles with multiple spots
320+
# TODO: wild hogs with multiple light brown variants
321+
# TODO: bengal tigers with multiple pseudo leucistic and pseudo melanistic variants
322+
# TODO: tahr great one
323+
# TODO: missing great one furs
320324

321325
class Reserve(str, Enum):
322326
hirsch = "hirsch"
@@ -332,6 +336,8 @@ class Reserve(str, Enum):
332336
mississippi = "mississippi"
333337
revontuli = "revontuli"
334338
newengland = "newengland"
339+
emerald = "emerald"
340+
sundarpatan = "sundarpatan"
335341

336342
class Strategy(str, Enum):
337343
go_all = "go-all"
@@ -352,6 +358,7 @@ class GreatOnes(str, Enum):
352358
whitetail_deer = "whitetail_deer"
353359
red_deer = "red_deer"
354360
fallow_deer = "fallow_deer"
361+
tahr = "tahr"
355362

356363
class Levels(int, Enum):
357364
TRIVIAL = 1
@@ -364,7 +371,7 @@ class Levels(int, Enum):
364371
MYTHICAL = 8
365372
LEGENDARY = 9
366373
GREAT_ONE = 10
367-
374+
368375
def get_level_name(level: Levels):
369376
if level == Levels.TRIVIAL:
370377
return translate("Trivial")
@@ -388,11 +395,36 @@ def get_level_name(level: Levels):
388395
return translate("Great One")
389396
return None
390397

398+
def get_difficulty(difficulty: str):
399+
match difficulty:
400+
case "Trivial":
401+
return 1
402+
case "Minor":
403+
return 2
404+
case "Very Easy":
405+
return 3
406+
case "Easy":
407+
return 4
408+
case "Medium":
409+
return 5
410+
case "Hard":
411+
return 6
412+
case "Very Hard":
413+
return 7
414+
case "Mythical":
415+
return 8
416+
case "Legendary":
417+
return 9
418+
case "Great One":
419+
return 10
420+
case _:
421+
return None
422+
391423
def format_key(key: str) -> str:
392424
key = [s.capitalize() for s in re.split("_|-", key)]
393425
return " ".join(key)
394426

395-
def load_config(config_path: Path) -> int:
427+
def load_config(config_path: Path) -> int:
396428
config_path.read_text()
397429

398430
def get_save_path() -> Path:
@@ -450,7 +482,7 @@ def get_reserve_species_name(species_key: str, reserve_key: str) -> str:
450482
renames = get_reserve_species_renames(reserve_key)
451483
species_key = renames[species_key] if species_key in renames else species_key
452484
return get_species_name(species_key)
453-
485+
454486
def get_reserve_name(key: str) -> str:
455487
return translate(RESERVE_NAMES[key]["reserve_name"])
456488

@@ -459,7 +491,7 @@ def reserve_keys() -> list:
459491

460492
def reserves(include_keys = False) -> list:
461493
keys = list(dict.keys(RESERVES))
462-
return [f"{get_reserve_name(r)}{' (' + r + ')' if include_keys else ''}" for r in keys]
494+
return [f"{get_reserve_name(r)}{' (' + r + ')' if include_keys else ''}" for r in keys]
463495

464496
def get_reserve(reserve_key: str) -> dict:
465497
return RESERVES[reserve_key]
@@ -472,7 +504,7 @@ def get_species(species_key: str) -> dict:
472504

473505
def get_diamond_gender(species_key: str) -> str:
474506
species_config = get_species(species_key)["diamonds"]
475-
return species_config["gender"] if "gender" in species_config else "male"
507+
return species_config["gender"] if "gender" in species_config else "male"
476508

477509
def _get_fur(furs: dict, seed: int) -> str:
478510
try:
@@ -512,14 +544,14 @@ def valid_fur_species(species_key: str) -> bool:
512544
def get_population_file_name(reserve: str):
513545
index = RESERVES[reserve]["index"]
514546
return f"animal_population_{index}"
515-
547+
516548
def get_population_reserve_key(filename: str):
517549
for _reserve, details in RESERVES.items():
518550
reserve_filename = f"animal_population_{details['index']}"
519551
if reserve_filename == filename:
520552
return _reserve
521553
return None
522-
554+
523555
def get_population_name(filename: str):
524556
for _reserve, details in RESERVES.items():
525557
reserve_filename = f"animal_population_{details['index']}"

0 commit comments

Comments
 (0)