Skip to content

Commit 074bc67

Browse files
committed
Add some initial lessons
1 parent ebc1d30 commit 074bc67

File tree

9 files changed

+263
-42
lines changed

9 files changed

+263
-42
lines changed

CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ include(FetchContent)
1414
set(FETCHCONTENT_UPDATES_DISCONNECTED TRUE)
1515
FetchContent_Declare(ftxui
1616
GIT_REPOSITORY https://github.com/ArthurSonzogni/ftxui
17-
GIT_TAG v2.0.0
17+
GIT_TAG v3.0.0
1818
)
1919

2020
FetchContent_GetProperties(ftxui)
@@ -145,11 +145,11 @@ if(MSVC)
145145
endif()
146146

147147
# set the startup project for the "play" button in MSVC
148-
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT intro)
148+
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT awesome_game)
149149

150150
# Add other targets that you want installed here, be default we just package the one executable
151151
# we know we want to ship
152-
package_project(TARGETS intro)
152+
package_project(TARGETS awesome_game)
153153

154154
# Experience shows that explicit package naming can help make it easier to sort
155155
# out potential ABI related issues before they start, while helping you

src/CMakeLists.txt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,23 @@ find_package(docopt CONFIG)
44
find_package(lodepng CONFIG)
55

66
# Generic test that uses conan libs
7-
add_executable(intro main.cpp color.hpp size.hpp point.hpp vector2d.hpp bitmap.hpp bitmap.cpp game_components.hpp game.cpp game.hpp)
7+
add_executable(
8+
awesome_game
9+
main.cpp
10+
color.hpp
11+
size.hpp
12+
point.hpp
13+
vector2d.hpp
14+
bitmap.hpp
15+
bitmap.cpp
16+
game_components.hpp
17+
game_hacking_lesson_00.cpp
18+
game_hacking_lesson_00.hpp
19+
game_hacking_lesson_01.cpp
20+
game_hacking_lesson_01.hpp)
21+
822
target_link_libraries(
9-
intro
23+
awesome_game
1024
PRIVATE project_options
1125
project_warnings
1226
docopt::docopt
@@ -15,10 +29,10 @@ target_link_libraries(
1529
lodepng::lodepng)
1630

1731
target_link_system_libraries(
18-
intro
32+
awesome_game
1933
PRIVATE
2034
ftxui::screen
2135
ftxui::dom
2236
ftxui::component)
2337

24-
target_include_directories(intro PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include")
38+
target_include_directories(awesome_game PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include")

src/game.hpp

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/game_components.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct Menu
5757
std::vector<MenuItem> items;
5858
};
5959

60-
using Variable = std::variant<double, std::int64_t, std::string>;
60+
using Variable = std::variant<double, std::int64_t, std::string, bool>;
6161

6262
inline std::string to_string(const Variable &variable)
6363
{

src/game.cpp renamed to src/game_hacking_lesson_00.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#include "game.hpp"
1+
#include "game_hacking_lesson_00.hpp"
22
#include "bitmap.hpp"
33
#include "game_components.hpp"
44

55

6-
namespace lefticus::my_awesome_game {
6+
namespace lefticus::my_awesome_game::game_hacking::lesson_00 {
77
Game_Map make_map()// NOLINT cognitive complexity
88
{
99
Game_Map map{ Size{ 10, 10 } };// NOLINT magic numbers
@@ -80,11 +80,11 @@ Game_Map make_map()// NOLINT cognitive complexity
8080
map.locations.at(Point{ 5, 5 }) = Flashing_Tile;// NOLINT magic numbers
8181

8282
map.locations.at(Point{ 2, 1 }).enter_action = [](Game &game, Point, Direction) {
83-
game.last_message = "Hint: go to location {4,3}";
83+
game.last_message = "Hint: you'll have to edit code. go to location {4,3}";
8484
};
8585

8686
map.locations.at(Point{ 4, 3 }).enter_action = [](Game &game, Point, Direction) {
87-
game.last_message = "Hint: go to location {8,8}";
87+
game.last_message = "Hint: You need file game_hacking_lesson_00.cpp. go to location {8,8}";
8888
};
8989

9090
map.locations.at(Point{ 7, 7 }).enter_action// NOLINT
@@ -95,14 +95,26 @@ Game_Map make_map()// NOLINT cognitive complexity
9595
= [](Game &game, Point, Direction) { game.last_message = "Look for 'special_location' in the source code"; };
9696

9797
map.locations.at(special_location) = Flashing_Tile;
98-
map.locations.at(special_location).can_enter = [](const Game &, Point, Direction direction) {
98+
map.locations.at(special_location).can_enter = [](const Game &, Point, [[maybe_unused]] Direction direction) {
99+
// || means "or"
100+
// this means you can currently enter the code from either
101+
// the South or the East...
102+
// but you need to be able to enter from the North or the West!
103+
//
104+
// North
105+
// ---
106+
// West | | East
107+
// ---
108+
// South
109+
//
110+
// Try changing the code below and see how the game changes
99111
return direction == Direction::South || direction == Direction::East;
100112
};
101113

102114
map.locations.at(special_location).exit_action = [](Game &game, Point, Direction) { game.last_message = ""; };
103115

104116
map.locations.at(special_location).enter_action = [](Game &game, Point, Direction) {
105-
game.last_message = "You found the secret room!";
117+
game.last_message = "You found the secret room! Now change the call to `play_game` to start lesson 01";
106118
Menu menu;
107119
menu.items.push_back(
108120
Menu::MenuItem{ "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } });
@@ -115,7 +127,7 @@ Game_Map make_map()// NOLINT cognitive complexity
115127
return map;
116128
}
117129

118-
Game make_game()
130+
Game make_lesson()
119131
{
120132
Game retval{};
121133
retval.maps.emplace("main", make_map());
@@ -146,7 +158,7 @@ Game make_game()
146158
retval.player = player;
147159

148160
retval.popup_message =
149-
"Welcome to 'Learning C++ With Game Hacking!' Your job is to get into the special square in the bottom right "
161+
"Welcome to 'Learning C++ With Game Hacking Lesson 00!' Your job is to get into the special square in the bottom right "
150162
"corner of the map. But to do that you'll need to modify the source code!";
151163

152164
return retval;

src/game_hacking_lesson_00.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef MY_AWESOME_GAME_GAME_HACKING_LESSON_00_HPP
2+
#define MY_AWESOME_GAME_GAME_HACKING_LESSON_00_HPP
3+
4+
namespace lefticus::my_awesome_game {
5+
struct Game;
6+
7+
namespace game_hacking::lesson_00 {
8+
Game make_lesson();
9+
}
10+
}
11+
12+
13+
14+
#endif// MY_AWESOME_GAME_GAME_HACKING_LESSON_00_HPP

src/game_hacking_lesson_01.cpp

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#include <source_location>
2+
3+
#include "bitmap.hpp"
4+
#include "game_components.hpp"
5+
#include "game_hacking_lesson_00.hpp"
6+
7+
8+
namespace lefticus::my_awesome_game::game_hacking::lesson_01 {
9+
10+
bool button_pressed(const Game &game) { return get<bool>(game.variables.at("ButtonPressed")); }
11+
12+
bool &button_pressed(Game &game) { return get<bool>(game.variables.at("ButtonPressed")); }
13+
14+
15+
Game_Map make_map()// NOLINT cognitive complexity
16+
{
17+
Game_Map map{ Size{ 10, 10 } };// NOLINT magic numbers
18+
19+
auto button_draw =
20+
[](Vector2D_Span<Color> &pixels, [[maybe_unused]] const Game &game, [[maybe_unused]] Point map_location) {
21+
if (button_pressed(game)) {
22+
fill(pixels, Color{ 45, 45, 45, 255 });// NOLINT magic number
23+
} else {
24+
fill(pixels, Color{ 15, 15, 15, 255 });// NOLINT magic number
25+
}
26+
};
27+
28+
auto empty_draw =
29+
[](Vector2D_Span<Color> &pixels, [[maybe_unused]] const Game &game, [[maybe_unused]] Point map_location) {
30+
fill(pixels, Color{ 5, 5, 25, 255 });// NOLINT magic number
31+
};
32+
33+
auto cannot_enter = [](const Game &, Point, Direction) -> bool { return false; };
34+
35+
auto water = [](
36+
Vector2D_Span<Color> &pixels, [[maybe_unused]] const Game &game, [[maybe_unused]] Point map_location) {
37+
fill(pixels, Color{ 0, 0, 250, 255 });// NOLINT magic number
38+
};
39+
40+
41+
auto wall_draw = []([[maybe_unused]] Vector2D_Span<Color> &pixels,
42+
[[maybe_unused]] const Game &game,
43+
[[maybe_unused]] Point map_location) {
44+
static constexpr auto wall_color = Color{ 100, 100, 100, 128 };
45+
46+
47+
switch ((game.clock.count() / 1000) % 2) {// NOLINT magic number
48+
case 0:
49+
fill(pixels, Color{ 64, 128, 64, 255 });// NOLINT magic number
50+
break;
51+
case 1:
52+
fill(pixels, Color{ 128, 64, 64, 255 });// NOLINT magic number
53+
break;
54+
}
55+
56+
57+
if (!game.maps.at(game.current_map).can_enter_from(game, map_location, Direction::East)) {
58+
fill_line(pixels,
59+
Point{ pixels.size().width - 1, 0 },
60+
Point{ pixels.size().width - 1, pixels.size().height - 1 },
61+
wall_color);
62+
}
63+
64+
if (!game.maps.at(game.current_map).can_enter_from(game, map_location, Direction::West)) {
65+
fill_line(pixels, Point{ 0, 0 }, Point{ 0, pixels.size().height - 1 }, wall_color);
66+
}
67+
68+
if (!game.maps.at(game.current_map).can_enter_from(game, map_location, Direction::North)) {
69+
fill_line(pixels, Point{ 0, 0 }, Point{ pixels.size().width - 1, 0 }, wall_color);
70+
}
71+
72+
if (!game.maps.at(game.current_map).can_enter_from(game, map_location, Direction::South)) {
73+
fill_line(pixels,
74+
Point{ 0, pixels.size().height - 1 },
75+
Point{ pixels.size().width - 1, pixels.size().height - 1 },
76+
wall_color);
77+
}
78+
};
79+
80+
for (std::size_t cur_x = 0; cur_x < map.locations.size().width; ++cur_x) {
81+
for (std::size_t cur_y = 0; cur_y < map.locations.size().height; ++cur_y) {
82+
map.locations.at(Point{ cur_x, cur_y }).draw = empty_draw;
83+
}
84+
}
85+
86+
fill_border(map.locations, Location{ {}, {}, water, cannot_enter });
87+
88+
const auto Flashing_Tile = Location{ {}, {}, wall_draw, cannot_enter };
89+
90+
constexpr static auto special_location = Point{ 8, 8 };
91+
92+
map.locations.at(Point{ 3, 4 }) = Flashing_Tile;
93+
map.locations.at(Point{ 2, 5 }) = Flashing_Tile;// NOLINT magic numbers
94+
map.locations.at(Point{ 1, 2 }) = Flashing_Tile;
95+
map.locations.at(Point{ 8, 6 }) = Flashing_Tile;// NOLINT magic numbers
96+
map.locations.at(Point{ 5, 5 }) = Flashing_Tile;// NOLINT magic numbers
97+
98+
map.locations.at(Point{ 2, 1 }).enter_action = [](Game &game, Point, Direction) {
99+
game.last_message = "A hidden space will activate a button! Go find it!";
100+
};
101+
102+
map.locations.at(Point{ 5, 6 }).draw// NOLINT magic numbers
103+
= button_draw;
104+
105+
map.locations.at(Point{ 5, 6 }).enter_action// NOLINT magic numbers
106+
= [](Game &game, Point, Direction) {
107+
// TODO: Update this to use std::source_location once clang supports it
108+
game.last_message =
109+
fmt::format("You need to fix the \"ButtonPressed\" variable code! ({}:{})", __FILE__, __LINE__);
110+
// `!` means "Not"
111+
// so if you step on this tile then it will invert the state of the button
112+
// from true to false and from false to true.
113+
//
114+
// At least...that's the idea. Problem is there's a typo. Do you see it?!
115+
game.variables["BottonPressed"] = !std::get<bool>(game.variables["ButtonPressed"]);
116+
};
117+
118+
119+
map.locations.at(special_location) = Flashing_Tile;
120+
map.locations.at(special_location).can_enter = [](const Game &game, Point, [[maybe_unused]] Direction direction) {
121+
return direction == Direction::West && button_pressed(game);
122+
};
123+
124+
map.locations.at(special_location).enter_action = [](Game &game, Point, Direction) {
125+
game.last_message = "You opened the door! Now change the call to `play_game` to start lesson 02";
126+
Menu menu;
127+
menu.items.push_back(
128+
Menu::MenuItem{ "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } });
129+
menu.items.push_back(
130+
Menu::MenuItem{ "Exit Game", [](Game &menu_action_game) { menu_action_game.exit_game = true; } });
131+
game.set_menu(menu);
132+
};
133+
134+
135+
return map;
136+
}
137+
138+
Game make_lesson()
139+
{
140+
Game retval{};
141+
retval.maps.emplace("main", make_map());
142+
retval.current_map = "main";
143+
retval.tile_size = Size{ 8, 8 };// NOLINT Magic Number
144+
145+
retval.variables["Task"] = "Exit game";
146+
retval.display_variables.emplace_back("Task");
147+
148+
retval.variables["ButtonPressed"] = false;
149+
retval.display_variables.emplace_back("ButtonPressed");
150+
151+
Character player;
152+
player.map_location = { 1, 1 };
153+
player.draw =
154+
[](Vector2D_Span<Color> &pixels, [[maybe_unused]] const Game &game, [[maybe_unused]] Point map_location) {
155+
for (std::size_t cur_x = 2; cur_x < pixels.size().width - 2; ++cur_x) {
156+
for (std::size_t cur_y = 2; cur_y < pixels.size().height - 2; ++cur_y) {
157+
if ((cur_x == 2 && cur_y == 2) || (cur_x == 2 && cur_y == pixels.size().height - 3)
158+
|| (cur_x == pixels.size().width - 3 && cur_y == pixels.size().height - 3)
159+
|| (cur_x == pixels.size().width - 3 && cur_y == 2)) {
160+
pixels.at(Point{ cur_x, cur_y }) += Color{ 128, 128, 0, 64 };// NOLINT
161+
} else {
162+
pixels.at(Point{ cur_x, cur_y }) += Color{ 128, 128, 0, 255 };// NOLINT
163+
}
164+
}
165+
}
166+
};
167+
168+
169+
retval.player = player;
170+
171+
retval.popup_message =
172+
"Welcome to 'Learning C++ With Game Hacking Lesson 01!' Your job is to get into the special square in the bottom "
173+
"right corner of the map. But to do that you'll need to modify the source code!";
174+
175+
return retval;
176+
}
177+
}// namespace lefticus::my_awesome_game::game_hacking::lesson_01

src/game_hacking_lesson_01.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef MY_AWESOME_GAME_GAME_HACKING_LESSON_01_HPP
2+
#define MY_AWESOME_GAME_GAME_HACKING_LESSON_01_HPP
3+
4+
namespace lefticus::my_awesome_game {
5+
6+
struct Game;
7+
8+
namespace game_hacking::lesson_01 {
9+
10+
Game make_lesson();
11+
12+
}
13+
}// namespace lefticus::my_awesome_game
14+
15+
16+
#endif// MY_AWESOME_GAME_GAME_HACKING_LESSON_01_HPP

0 commit comments

Comments
 (0)