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
0 commit comments