Skip to content

Commit 506dd9d

Browse files
authored
Merge pull request #18 from cpp-gamedev/issue-15
Issue 15
2 parents 5121759 + 65e238b commit 506dd9d

File tree

13 files changed

+311
-54
lines changed

13 files changed

+311
-54
lines changed

.clang-format

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ Standard: c++17
99
TabWidth: 4
1010
IndentWidth: 4
1111
UseTab: Always
12+
AllowAllArgumentsOnNextLine: false
1213
AllowShortFunctionsOnASingleLine: false
14+
SortIncludes: true
1315
IncludeCategories:
1416
# Headers in <> without extension.
1517
- Regex: '<([A-Za-z0-9\/-_])+>'

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,8 @@ FodyWeavers.xsd
367367
venv/
368368

369369
# project-specific files and directories
370-
assets/
370+
assets/textures/*.txt
371+
assets/data/*.json
371372
manifest.json
372373

373374
# VSCode, clangd etc

README.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,34 @@
2323

2424
## Build & Debug
2525

26+
Initialize and update all submodules after you have cloned this repository:
27+
28+
```bash
29+
git submodule update --init --recursive
30+
```
31+
32+
For python specifically, you may need to install `python3-venv` to create virtual
33+
environments on Linux.
34+
2635
### Generating new Pokemon
2736

37+
---
38+
39+
*Note: You can also use the `./easy_install.sh` script to skip this section.*
40+
41+
---
42+
2843
If this is your first time using a python script, use
2944

3045
```bash
3146
python -m venv venv/
32-
source venv/Scripts/activate
47+
source venv/bin/activate
3348
python -m pip install --upgrade pip
3449
pip install -r requirements.txt --only-binary all
3550
```
3651

37-
to install the dependencies in a virtual environment. Note that this script
52+
to install the dependencies in a virtual environment. On Windows, you need to use
53+
`.\venv\Scripts\Activate.ps1` to activate a virtual environment. Note that this script
3854
assumes that it is being run from the project's root directory. You need to create
3955
at least two new pkmn file sets before you can start playing this game:
4056

@@ -60,4 +76,7 @@ to start debugging this project (`F5`).
6076

6177
### On Visual Studio Code (Linux)
6278

63-
TODO
79+
Useful links for first timers:
80+
81+
- <https://cpp-gamedev.netlify.app/series/101/1_getting_started/>
82+
- <https://cpp-gamedev.netlify.app/series/101/2_development_environment/>

assets/splashscreen.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
,'\
2+
_.----. ____ ,' _\ ___ ___ ____
3+
_,-' `. | | /`. \,-' | \ / | | \ |`.
4+
\ __ \ '-. | / `. ___ | \/ | '-. \ | |
5+
\. \ \ | __ | |/ ,','_ `. | | __ | \| |
6+
\ \/ /,' _`.| ,' / / / / | ,' _`.| | |
7+
\ ,-'/ / \ ,' | \/ / ,`.| / / \ | |
8+
\ \ | \_/ | `-. \ `' /| | || \_/ | |\ |
9+
\ \ \ / `-.`.___,-' | |\ /| \ / | | |
10+
\ \ `.__,'| |`-._ `| |__| \/ | `.__,'| | | |
11+
\_.-' |__| `-._ | '-.| '-.| | |
12+
`' '-._|

easy_install.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
3+
echo "+++ PKMN Asset Builder +++"
4+
5+
echo -e "\nAccording to the National Pok\u00e9mon Index, the first 151 entries served in Generation I."
6+
echo -e "You may use any of these numbers to create new asset files to play this game.\n"
7+
8+
echo -n "Pokemon ID #1: "
9+
read id1
10+
11+
echo -n "Pokemon ID #2: "
12+
read id2
13+
14+
if [[ ! -d "venv/" ]]; then
15+
echo "Creating a new virtual environment . . ."
16+
python3 -m venv venv/
17+
source venv/bin/activate
18+
echo "Installing dependencies . . ."
19+
python3 -m pip install --upgrade pip
20+
python3 -m pip install -r requirements.txt --only-binary all
21+
else
22+
source venv/bin/activate
23+
fi
24+
25+
python3 gen_data.py --verbose make --id $id1 $id2
26+
python3 gen_data.py manifest
27+
28+
echo "Updating submodules . . ."
29+
git submodule update --init --recursive
30+
31+
echo "Done!"

gen_data.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from random import randint, random
2828
from typing import List
2929
from urllib.parse import urljoin
30+
from itertools import chain
3031

3132
import colorama
3233
import requests
@@ -35,14 +36,23 @@
3536
from rich.console import Console
3637
from rich.progress import track
3738

39+
#region global variables
40+
3841
__version__ = "0.0.1"
39-
assets = Path('assets/')
40-
assets.mkdir(parents=True, exist_ok=True)
41-
ASSETS = assets
42+
43+
def setup() -> List[Path]:
44+
assets = [Path('assets/'), Path('assets/textures'), Path('assets/data')]
45+
for dir in assets:
46+
dir.mkdir(parents=True, exist_ok=True)
47+
return assets
48+
49+
ASSETS, TEXTURES, DATA = setup()
4250
BASE_API = "https://pokeapi.co/api/v2/pokemon/"
4351
SPRITE_API = "https://pokeres.bastionbot.org/images/pokemon/"
4452
CONSOLE = Console()
4553

54+
#endregion
55+
4656
#region image processing
4757

4858
CHARS = [' ', '.', 'o', 'v', '@', '#', 'W']
@@ -119,13 +129,15 @@ def req_pkmn_data(id_: int, verbose: bool) -> None:
119129
}
120130
result['moves'] = moves
121131

122-
with open(ASSETS.joinpath(f"{result['id']}.json"), mode='w', encoding='utf-8') as file_handler:
132+
data = DATA.joinpath(f"{result['id']}.json")
133+
134+
with open(data, mode='w', encoding='utf-8') as file_handler:
123135
json.dump(result, file_handler)
124136

125137
if verbose:
126138
pprint(result)
127139

128-
print(f"{Fore.YELLOW}Done! A new JSON file was created in {str(ASSETS)!r}.{Style.RESET_ALL}")
140+
print(f"{Fore.YELLOW}Created {str(data)!r}{Style.RESET_ALL}")
129141

130142
def gen_sprite(id_: int, mirror: bool, verbose: bool) -> None:
131143
colorama.init(autoreset=False)
@@ -139,22 +151,25 @@ def gen_sprite(id_: int, mirror: bool, verbose: bool) -> None:
139151
file_handler.write(chunk)
140152

141153
ascii_art = img2ascii(Image.open(image_path), width=20, mirror_image=mirror)
142-
with open(ASSETS.joinpath(f"{id_}.txt"), mode='w', encoding='utf-8') as file_handler:
154+
155+
sprite = TEXTURES.joinpath(f"{id_}.txt")
156+
157+
with open(sprite, mode='w', encoding='utf-8') as file_handler:
143158
file_handler.writelines(ascii_art)
144159

145160
image_path.unlink(missing_ok=True)
146161

147162
if verbose:
148163
print(f"\n{''.join(ascii_art)}")
149164

150-
print(f"{Fore.YELLOW}Done! A new ASCII image was created in {str(ASSETS)!r}.{Style.RESET_ALL}")
165+
print(f"{Fore.YELLOW}Created {str(sprite)!r}{Style.RESET_ALL}")
151166

152167
def check_manifest(verbose: bool) -> None:
153168
extensions = ['.txt', '.json']
154169

155170
files = list(filter(
156171
lambda file: file.suffix in extensions and file.stem.isnumeric(),
157-
[file for file in ASSETS.glob(r'**/*')]
172+
[file for file in chain(DATA.glob(r'**/*'), TEXTURES.glob(r'**/*'))]
158173
))
159174

160175
ids = list(map(lambda file: int(file.stem), files))
@@ -190,7 +205,7 @@ def main():
190205
if args.command == 'make':
191206
for id_ in args.id:
192207
req_pkmn_data(id_, verbose=False)
193-
gen_sprite(id_, args.mirror, args.verbose)
208+
gen_sprite(id_, args.mirror, verbose=False)
194209
elif args.command == 'manifest':
195210
check_manifest(args.verbose)
196211
else:

src/anim.cpp

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
#include <algorithm>
22
#include <filesystem>
33
#include <iostream>
4+
#include <numeric>
45
#include <string>
56
#include <vector>
7+
8+
#include <str_format/str_format.hpp>
9+
610
#include "models.hpp"
711
#include "utils.hpp"
8-
#include <str_format/str_format.hpp>
912

10-
std::vector<std::string> gen_healthbar(Pokemon& pkmn)
13+
namespace anim
14+
{
15+
std::vector<std::string> gen_healthbar(const models::Pokemon& pkmn)
1116
{
1217
/*
1318
* bulbasaur :L30 // label
@@ -24,7 +29,7 @@ std::vector<std::string> gen_healthbar(Pokemon& pkmn)
2429

2530
std::string stars = std::string(hp_scaled, '*');
2631
stars = stars.append(std::string(10 - hp_scaled, ' '));
27-
Color star_color = (hp_scaled >= 5) ? Color::GREEN : (3 < hp_scaled && hp_scaled < 5) ? Color::YELLOW : Color::RED;
32+
utils::Color star_color = (hp_scaled >= 5) ? utils::Color::GREEN : (3 < hp_scaled && hp_scaled < 5) ? utils::Color::YELLOW : utils::Color::RED;
2833
std::string progressbar = kt::format_str("HP [{}]", style(stars, star_color));
2934
progressbar = std::string(max_width - 15, ' ').append(progressbar);
3035

@@ -34,7 +39,49 @@ std::vector<std::string> gen_healthbar(Pokemon& pkmn)
3439
return {label, progressbar, hitpoints};
3540
}
3641

37-
void print_frame(Pokemon& pkmn1, Pokemon& pkmn2)
42+
void print_splash_screen(const std::filesystem::path& assets_dir)
43+
{
44+
auto logo = utils::read_file(assets_dir / "splashscreen.txt");
45+
std::cout << utils::style(std::accumulate(logo.begin(), logo.end(), std::string("")), utils::Color::YELLOW) << '\n';
46+
47+
std::cout << '\n' << std::setfill(' ') << std::setw(19);
48+
49+
for (const char& c : "copyright (c) 2021 cpp-gamedev")
50+
{
51+
std::cout << c;
52+
utils::sleep(100);
53+
}
54+
}
55+
56+
std::vector<models::Pokemon> load_main_menu(utils::Manifest manifest)
57+
{
58+
// 1. set difficulty
59+
int selection{};
60+
utils::print_enum_table({"easy", "moderate", "hard"}, "difficulty");
61+
selection = utils::get_user_input<int>(">>> ");
62+
auto difficulty = (selection == 1) ? models::Difficulty::EASY : (selection == 2) ? models::Difficulty::MODERATE : models::Difficulty::HARD;
63+
64+
// 2. instantiate all available pokemons
65+
std::vector<models::Pokemon> pkmns{};
66+
pkmns.reserve(manifest.duplicates.size());
67+
std::for_each(manifest.duplicates.begin(), manifest.duplicates.end(),
68+
[&manifest, &pkmns, &difficulty](int id) { pkmns.push_back(models::Pokemon(id, manifest.asset_dir, difficulty)); });
69+
70+
// 3. select pokemon
71+
std::vector<std::string> names{};
72+
names.reserve(pkmns.size());
73+
std::for_each(pkmns.begin(), pkmns.end(), [&names](models::Pokemon& pkmn) { names.push_back(pkmn.name); });
74+
utils::print_enum_table(names, "pokemons");
75+
selection = utils::get_user_input<int>(">>> ");
76+
models::Pokemon player = pkmns[selection - 1];
77+
78+
// 4. remove selection from pkmns, so that player won't fight against his doppelganger
79+
pkmns.erase(std::remove_if(pkmns.begin(), pkmns.end(), [player](models::Pokemon pkmn) { return player.id == pkmn.id; }), pkmns.end());
80+
81+
return {player, pkmns.size() > 1 ? utils::random_choice(pkmns) : pkmns[0]};
82+
}
83+
84+
void print_frame(const models::Pokemon& pkmn1, const models::Pokemon& pkmn2)
3885
{
3986
std::string healthbars{};
4087
std::string sprites{};
@@ -63,3 +110,4 @@ void print_frame(Pokemon& pkmn1, Pokemon& pkmn2)
63110

64111
return;
65112
}
113+
} // namespace anim

src/anim.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
#include<string>
44
#include<vector>
5+
56
#include "models.hpp"
67

7-
std::vector<std::string> gen_healthbar(Pokemon& pkmn);
8+
namespace anim
9+
{
10+
std::vector<std::string> gen_healthbar(const models::Pokemon& pkmn);
11+
12+
void print_splash_screen(const std::filesystem::path& assets_dir);
13+
14+
std::vector<models::Pokemon> load_main_menu(utils::Manifest manifest);
815

9-
void print_frame(Pokemon& pkmn1, Pokemon& pkmn2);
16+
void print_frame(const models::Pokemon& pkmn1, const models::Pokemon& pkmn2);
17+
} // namespace anim

src/main.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,42 @@
1+
#include <algorithm>
2+
#include <filesystem>
13
#include <iostream>
4+
#include <numeric>
25
#include <string>
6+
7+
#include "anim.hpp"
38
#include "models.hpp"
49
#include "utils.hpp"
10+
11+
using namespace anim;
12+
using namespace models;
13+
using namespace utils;
14+
515
int main()
616
{
7-
/* auto name = get_user_input<std::string>("What's your name? ");
8-
std::cout << "Hello, " << name << '!' << '\n';
9-
std::cin.get(); */
17+
const auto assets_dir = find_upwards("assets");
18+
Manifest manifest = check_manifest(assets_dir.parent_path() / "manifest.json");
19+
20+
if (std::filesystem::exists(assets_dir) && manifest.game_ready)
21+
{
22+
print_splash_screen(assets_dir);
23+
clear_screen();
24+
25+
auto pkmns = load_main_menu(manifest);
26+
sleep(1000);
27+
clear_screen();
28+
29+
std::cout << "TODO: init game loop" << '\n';
30+
sleep(1000);
31+
clear_screen();
32+
33+
print_frame(pkmns[0], pkmns[1]);
1034

11-
return 0;
35+
return EXIT_SUCCESS;
36+
}
37+
else
38+
{
39+
std::cerr << "Error: The assets directory is in an invalid state. Consult the README for further instructions." << '\n';
40+
return EXIT_FAILURE;
41+
}
1242
}

0 commit comments

Comments
 (0)