diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/pacman/game.hpp | 312 | ||||
-rw-r--r-- | include/pacman/globals.hpp | 67 | ||||
-rw-r--r-- | include/pacman/graphics.hpp | 214 | ||||
-rw-r--r-- | include/pacman/maze.hpp | 336 | ||||
-rw-r--r-- | include/pacman/utils.hpp | 46 |
5 files changed, 975 insertions, 0 deletions
diff --git a/include/pacman/game.hpp b/include/pacman/game.hpp new file mode 100644 index 0000000..b084487 --- /dev/null +++ b/include/pacman/game.hpp @@ -0,0 +1,312 @@ +/* + * Author: Sven Gothel <[email protected]> and Svenson Han Gothel + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef PACMAN_GAME_HPP_ +#define PACMAN_GAME_HPP_ + +#include <pacman/maze.hpp> +#include <pacman/graphics.hpp> + +// +// score_t +// + +enum class score_t : int { + NONE = 0, + PELLET = 10, + PELLET_POWER = 50, + GHOST_1 = 200, + GHOST_2 = 400, + GHOST_3 = 800, + GHOST_4 = 1600, + CHERRY = 100, + STRAWBERRY = 300, + ORANGE = 500, + APPLE = 700, + MELON = 1000, + GALAXIAN = 2000, + BELL = 300, + KEY = 5000 +}; +constexpr int number(const score_t item) noexcept { + return static_cast<int>(item); +} +score_t tile_to_score(const tile_t tile); + +// +// global_tex_t +// + +class global_tex_t { + private: + std::shared_ptr<texture_t> all_images; + std::vector<std::shared_ptr<texture_t>> textures; + animtex_t atex_pellet_power; + + /** + * @param tile + * @return -1 if tile not handled, otherwise a valid textures index + */ + int tile_to_texidx(const tile_t tile) const; + + /** + * @param idx + * @return -1 if wrong idx, otherwise a valid textures index + */ + int validate_texidx(const int idx) const; + + public: + enum class special_idx : int { + GHOST_SCARED_BLUE = 10, + GHOST_SCARED_PINK = 11 + }; + static constexpr int number(const special_idx item) noexcept { + return static_cast<int>(item); + } + + global_tex_t(SDL_Renderer* rend); + + ~global_tex_t() { + destroy(); + } + + void destroy(); + + std::shared_ptr<texture_t> get_all_images() { return all_images; } + + std::shared_ptr<texture_t> get_tex(const tile_t tile); + std::shared_ptr<const texture_t> get_tex(const tile_t tile) const; + + std::shared_ptr<texture_t> get_tex(const int idx); + std::shared_ptr<const texture_t> get_tex(const int idx) const; + + bool tick() { + atex_pellet_power.tick(); + return true; + } + + void draw_tile(const tile_t tile, SDL_Renderer* rend, const int x, const int y); + + std::string toString() const; +}; + +// +// ghost_t +// + +/** + * See https://www.gamedeveloper.com/design/the-pac-man-dossier + */ +class ghost_t { + public: + enum class personality_t { + /** Red */ + BLINKY, + /** Orange */ + CLYDE, + /** Cyan or blue */ + INKY, + /** Pink or mangenta */ + PINKY + }; + + enum class mode_t { + AWAY, + HOME, + LEAVE_HOME, + CHASE, + SCATTER, + SCARED, + PHANTOM + }; + + /** mode durations in ms */ + enum class mode_duration_t : int { + HOMESTAY = 4000, + CHASING = 20000, + SCATTERING = 7000, + SCARED = 10000, + PHANTOM = 20000 + }; + static constexpr int number(const mode_duration_t item) noexcept { + return static_cast<int>(item); + } + private: + const int ms_per_atex = 500; + + const float fields_per_sec; + + personality_t id; // not necessarily unique + mode_t mode; + int mode_ms_left; + direction_t dir_, last_dir; + int frame_count; + + animtex_t atex_normal; + animtex_t atex_scared; + animtex_t atex_phantom; + animtex_t * atex; + + acoord_t pos; + acoord_t target; + + bool log_moves = false; + + static int id_to_yoff(ghost_t::personality_t id); + + animtex_t& get_tex(); + + animtex_t& get_phantom_tex() { + return atex_phantom; + } + + acoord_t get_personal_target() const; + + public: + ghost_t(const personality_t id_, SDL_Renderer* rend, const float fields_per_sec_=8); + + ~ghost_t() { + destroy(); + } + + void destroy(); + + void set_log_moves(const bool v) { log_moves = v; } + + mode_t get_mode() const { return mode; } + void set_mode(const mode_t m); + + void set_next_dir(); + + direction_t get_dir() const { return dir_; } + + const acoord_t& get_pos() const { return pos; } + const acoord_t& get_target() const { return target; } + + /** + * A game engine tick needs to: + * - adjust speed (acceleration?) + * - adjust position, taking direction, speed and collision into account. + * + * @return true if object is still alive, otherwise false + */ + bool tick(); + + void draw(SDL_Renderer* rend); + + std::string toString() const; +}; + +std::string to_string(ghost_t::personality_t id); +std::string to_string(ghost_t::mode_t m); + +// +// pacman_t +// + +class pacman_t { + public: + enum class mode_t { + HOME, + NORMAL, + POWERED, + DEAD + }; + + private: + /** mode durations in ms */ + enum class mode_duration_t : int { + HOMESTAY = 2000, + INPOWER = ghost_t::number(ghost_t::mode_duration_t::SCARED), + DEADANIM = 2000 + }; + static constexpr int number(const mode_duration_t item) noexcept { + return static_cast<int>(item); + } + const int ms_per_tex = 167; + + const float fields_per_sec; + + const bool auto_move; + + mode_t mode; + int mode_ms_left; + int lives; + direction_t dir_, last_dir; + int frame_count; + int steps_left; + uint64_t score; + + animtex_t atex_left; + animtex_t atex_right; + animtex_t atex_up; + animtex_t atex_down; + animtex_t atex_dead; + animtex_t atex_home; + animtex_t * atex; + + acoord_t pos; + + uint64_t perf_fields_walked_t0 =0; + + animtex_t& get_tex(); + + public: + pacman_t(SDL_Renderer* rend, const float fields_per_sec_=8, bool auto_move_=true); + + ~pacman_t() { + destroy(); + } + + void destroy(); + + void set_mode(const mode_t m); + + /** + * Set direction + */ + void set_dir(direction_t dir); + + direction_t get_dir() const { return dir_; } + + const acoord_t& get_pos() const { return pos; } + + uint64_t get_score() const { return score; } + + /** + * A game engine tick needs to: + * - adjust speed (acceleration?) + * - adjust position, taking direction, speed and collision into account. + * + * @return true if object is still alive, otherwise false + */ + bool tick(); + + void draw(SDL_Renderer* rend); + + std::string toString() const; +}; + +std::string to_string(pacman_t::mode_t m); + +#endif /* PACMAN_GAME_HPP_ */ diff --git a/include/pacman/globals.hpp b/include/pacman/globals.hpp new file mode 100644 index 0000000..faf0fc5 --- /dev/null +++ b/include/pacman/globals.hpp @@ -0,0 +1,67 @@ +/* + * Author: Sven Gothel <[email protected]> and Svenson Han Gothel + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef PACMAN_GLOBALS_HPP_ +#define PACMAN_GLOBALS_HPP_ + +#include <pacman/maze.hpp> +#include <pacman/graphics.hpp> +#include <pacman/game.hpp> + +#include <cmath> +#include <memory> + +// +// globals +// + +extern int win_pixel_width; +extern int win_pixel_scale; + +extern int get_frames_per_sec(); +inline int get_ms_per_frame() { return (int)std::round(1000.0 / (float)get_frames_per_sec()); } + +extern std::unique_ptr<maze_t> pacman_maze; + +extern std::shared_ptr<global_tex_t> global_tex; + +typedef std::shared_ptr<ghost_t> ghost_ref; +extern std::vector<ghost_ref> ghosts; + +typedef std::shared_ptr<pacman_t> pacman_ref; +extern pacman_ref pacman; + +/** + * By default the original pacman behavior is being implemented: + * - weighted (round) tile position for collision tests + * - pinky's up-target not 4 ahead, but 4 ahead and 4 to the left + * - ... + * + * If false, a more accurate implementation, the pacman bugfix, is used: + * - pixel accurate tile position for collision tests + * - pinky's up-traget to be 4 ahead as intended + * - ... + */ +extern bool use_original_pacman_behavior(); + +#endif /* PACMAN_GLOBALS_HPP_ */ diff --git a/include/pacman/graphics.hpp b/include/pacman/graphics.hpp new file mode 100644 index 0000000..a6b34b4 --- /dev/null +++ b/include/pacman/graphics.hpp @@ -0,0 +1,214 @@ +/* + * Author: Sven Gothel <[email protected]> and Svenson Han Gothel + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef PACMAN_GRAPHICS_HPP_ +#define PACMAN_GRAPHICS_HPP_ + +#include <atomic> +#include <memory> +#include <vector> +#include <string> +#include <inttypes.h> + +#include <SDL2/SDL.h> +#include <SDL2/SDL_image.h> +#include <SDL2/SDL_timer.h> +#include <SDL2/SDL_ttf.h> + +class texture_t { + private: + static std::atomic<int> counter; + int id; + SDL_Texture* tex; + int x, y, width, height; + bool owner; + + public: + /** + * Empty texture + */ + texture_t() + : id(counter++), tex(nullptr), x(0), y(0), width(0), height(0), owner(false) {} + + texture_t(SDL_Renderer* rend, const std::string& fname); + + texture_t(SDL_Renderer* rend, SDL_Surface* surface); + + texture_t(SDL_Texture* t, int x_, int y_, int w_, int h_, bool owner=true) + : id(counter++), tex(t), x(x_), y(y_), width(w_), height(h_), owner(owner) {} + + texture_t(SDL_Texture* t, int w_, int h_, bool owner=true) + : id(counter++), tex(t), x(0), y(0), width(w_), height(h_), owner(owner) {} + + texture_t(const texture_t&) = delete; + void operator=(const texture_t&) = delete; + + ~texture_t() { + destroy(); + } + + void destroy(); + + bool is_owner() const { return owner; } + void disown() { owner = false; } + + int get_id() const { return id; } + int get_x() const { return x; } + int get_y() const { return y; } + int get_width() const { return width; } + int get_height() const { return height; } + SDL_Texture* get_sdltex() { return tex; } + + void draw_scaled_dimpos(SDL_Renderer* rend, const int x_pos, const int y_pos); + void draw_scaled_dim(SDL_Renderer* rend, const int x_pos, const int y_pos); + void draw(SDL_Renderer* rend, const int x_pos, const int y_pos, const bool maze_offset); + + void draw(SDL_Renderer* rend, const float x_pos, const float y_pos, const bool maze_offset); + + std::string toString() const; +}; + +struct tex_sub_coord_t { + int x; + int y; +}; + +/** + * Add sub-textures to the storage list of texture_t from file, + * where the last one is the sole owner of the common SDL_Texture instance. + * @param storage + * @param rend + * @param filename + * @param w + * @param h + * @param x_off + * @return number of added sub-textures, last one is owner of the SDL_Texture instance + */ +int add_sub_textures(std::vector<std::shared_ptr<texture_t>>& storage, SDL_Renderer* rend, + const std::string& filename, int w, int h, int x_off); + +/** + * Add sub-textures to the storage list of texture_t from given global texture owner. + * + * None of the sub-textures is the owner of the common SDL_Texture instance. + * @param storage + * @param rend + * @param global_texture + * @param x_off + * @param y_off + * @param w + * @param h + * @param tex_positions + * @return number of added sub-textures + */ +int add_sub_textures(std::vector<std::shared_ptr<texture_t>>& storage, SDL_Renderer* rend, + const std::shared_ptr<texture_t>& global_texture, int x_off, int y_off, int w, int h, + const std::vector<tex_sub_coord_t>& tex_positions); + +class animtex_t { + private: + std::string name; + std::vector<std::shared_ptr<texture_t>> textures; + + int ms_per_atex; + int atex_ms_left; + size_t animation_index; + bool paused; + + public: + animtex_t(std::string name_, int ms_per_atex_, const std::vector<std::shared_ptr<texture_t>>& textures_); + + animtex_t(std::string name_, SDL_Renderer* rend, int ms_per_atex_, const std::vector<const char*>& filenames); + + animtex_t(std::string name_, SDL_Renderer* rend, int ms_per_atex_, const std::string& filename, int w, int h, int x_off); + + animtex_t(std::string name_, SDL_Renderer* rend, int ms_per_atex_, const std::shared_ptr<texture_t>& global_texture, + int x_off, int y_off, int w, int h, const std::vector<tex_sub_coord_t>& tex_positions); + + ~animtex_t() { + destroy(); + } + + void destroy(); + + std::shared_ptr<texture_t> get_tex(const size_t idx) { return idx < textures.size() ? textures[idx] : nullptr; } + std::shared_ptr<const texture_t> get_tex(const size_t idx) const { return idx < textures.size() ? textures[idx] : nullptr; } + + std::shared_ptr<texture_t> get_tex() { return animation_index < textures.size() ? textures[animation_index] : nullptr; } + std::shared_ptr<const texture_t> get_tex() const { return animation_index < textures.size() ? textures[animation_index] : nullptr; } + + int get_width() { std::shared_ptr<texture_t> tex = get_tex(); return nullptr!=tex ? tex->get_width() : 0; } + int get_height() { std::shared_ptr<texture_t> tex = get_tex(); return nullptr!=tex ? tex->get_height() : 0; } + + void reset(); + void pause(bool enable); + void tick(); + + void draw(SDL_Renderer* rend, const float x, const float y, const bool maze_offset) { + std::shared_ptr<texture_t> tex = get_tex(); + if( nullptr != tex ) { + tex->draw(rend, x, y, maze_offset); + } + } + + std::string toString() const; +}; + +/** + * Storage for a rendered text allowing caching for performance. + */ +struct text_texture_t { + std::string text; + texture_t texture; + bool scaled_pos; + int x_pos; + int y_pos; + + text_texture_t(const std::string text_, SDL_Renderer* rend, SDL_Surface* surface, bool scaled_pos_, int x_, int y_) + : text(text_), texture(rend, surface), scaled_pos(scaled_pos_), x_pos(x_), y_pos(y_) {} + + void redraw(SDL_Renderer* rend) { + if( scaled_pos ) { + texture.draw_scaled_dimpos(rend, x_pos, y_pos); + } else { + texture.draw_scaled_dim(rend, x_pos, y_pos); + } + } +}; + +/** + * + * @param rend + * @param font + * @param text + * @param x + * @param y + * @param r + * @param g + * @param b + */ +std::shared_ptr<text_texture_t> draw_text(SDL_Renderer* rend, TTF_Font* font, const std::string& text, int x, int y, uint8_t r, uint8_t g, uint8_t b); + +std::shared_ptr<text_texture_t> draw_text_scaled(SDL_Renderer* rend, TTF_Font* font, const std::string& text, uint8_t r, uint8_t g, uint8_t b, std::function<void(const texture_t& texture, int &x, int&y)> scaled_coord); + +#endif /* PACMAN_GRAPHICS_HPP_ */ diff --git a/include/pacman/maze.hpp b/include/pacman/maze.hpp new file mode 100644 index 0000000..5ac7691 --- /dev/null +++ b/include/pacman/maze.hpp @@ -0,0 +1,336 @@ +/* + * Author: Sven Gothel <[email protected]> and Svenson Han Gothel + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef PACMAN_MAZE_HPP_ +#define PACMAN_MAZE_HPP_ + +#include <pacman/utils.hpp> + +#include <string> +#include <vector> +#include <functional> +#include <cmath> + +// +// misc +// +inline constexpr int round_to_int(const float f) { + return (int)std::round(f); +} +inline constexpr int floor_to_int(const float f) { + return (int)std::floor(f); +} +inline constexpr int ceil_to_int(const float f) { + return (int)std::ceil(f); +} + +// +// direction_t +// +enum class direction_t : int { + UP = 0, + LEFT = 1, + DOWN = 2, + RIGHT = 3 +}; +constexpr int number(const direction_t d) noexcept { + return static_cast<int>(d); +} +std::string to_string(direction_t dir); +direction_t inverse(direction_t dir); +direction_t rot_left(direction_t dir); +direction_t rot_right(direction_t dir); + +// +// tile_t +// +enum class tile_t : int { + EMPTY = 0, + WALL = 1, + GATE = 2, + PELLET = 3, + PELLET_POWER = 4, + CHERRY = 5, + STRAWBERRY = 6, + ORANGE = 7, + APPLE = 8, + MELON = 9, + GALAXIAN = 10, + BELL = 11, + KEY = 12 +}; +constexpr int number(const tile_t item) noexcept { + return static_cast<int>(item); +} +std::string to_string(tile_t tile); + +// +// box_t +// +class box_t { + private: + int x_pos; + int y_pos; + int width; + int height; + + public: + box_t(const int x, const int y, const int w, const int h) + : x_pos(x), y_pos(y), width(w), height(h) {} + + void set_dim(const int x, const int y, const int w, const int h) { + x_pos = x; y_pos = y; width = w; height = h; + } + int get_x() const { return x_pos; } + int get_y() const { return y_pos; } + int get_width() const { return width; } + int get_height() const { return height; } + + std::string toString() const; +}; + +// +// acoord_t +// +class maze_t; // fwd + +class acoord_t { + public: + typedef std::function<bool(direction_t d, int x_pos, int y_pos, tile_t)> collisiontest_t; + + private: + static constexpr const bool DEBUG_BOUNDS = false; + + int x_pos_i, y_pos_i; + float x_pos_f, y_pos_f; + direction_t last_dir; + bool last_collided; + int fields_walked_i; + float fields_walked_f; + + bool step_impl(const maze_t& maze, direction_t dir, const bool test_only, const float fields_per_frame, collisiontest_t ct); + + public: + acoord_t(const int x, const int y); + + void reset_stats(); + + void set_pos(const int x, const int y); + + int get_x_i() const { return x_pos_i; } + int get_y_i() const { return y_pos_i; } + int get_fields_walked_i() const { return fields_walked_i; } + float get_x_f() const { return x_pos_f; } + float get_y_f() const { return y_pos_f; } + float get_fields_walked_f() const { return fields_walked_f; } + + /** + * Almost pixel accurate collision test. + * + * Note: This is not used in orig pacman game, + * since it uses tile weighted (rounded) tile position test only. + */ + bool intersects_f(const acoord_t& other) const; + + /** + * Weighted tile (rounded) test, i.e. simply comparing the tile position. + * + * This is used in orig pacman game. + * + * The weighted tile position is determined in step(..) implementation. + */ + bool intersects_i(const acoord_t& other) const; + + /** + * Intersection test using either the pixel accurate float method + * or the original pacman game weighted int method + * depending on use_original_pacman_behavior(). + */ + bool intersects(const acoord_t& other) const; + + /** + * Pixel accurate position test for intersection. + */ + bool intersects_f(const box_t& other) const; + + float distance(const float x, const float y) const; + + float distance(const acoord_t& other) const { + return distance(other.x_pos_f, other.y_pos_f); + } + + float sq_distance(const float x, const float y) const; + + float sq_distance(const acoord_t& other) const { + return sq_distance(other.x_pos_f, other.y_pos_f); + } + + void incr_fwd(const maze_t& maze, const direction_t dir, const int tile_count); + void incr_fwd(const maze_t& maze, const int tile_count) { + incr_fwd(maze, last_dir, tile_count); + } + void incr_left(const maze_t& maze, const int tile_count) { + incr_fwd(maze, rot_left(last_dir), tile_count); + } + void incr_right(const maze_t& maze, const int tile_count) { + incr_fwd(maze, rot_right(last_dir), tile_count); + } + + void step(const maze_t& maze, direction_t dir, const float fields_per_sec, const int frames_per_sec) { + step_impl(maze, dir, false, fields_per_sec / frames_per_sec, nullptr); + } + + /** + * + * @param maze + * @param dir + * @param fields_per_sec + * @param frames_per_sec + * @param ct + * @return true if successful, otherwise false for collision + */ + bool step(const maze_t& maze, direction_t dir, const float fields_per_sec, const int frames_per_sec, collisiontest_t ct) { + return step_impl(maze, dir, false, fields_per_sec / frames_per_sec, ct); + } + + bool test(const maze_t& maze, direction_t dir, collisiontest_t ct) { + return step_impl(maze, dir, true, 0.50, ct); + } + + bool is_transitioning(const float fields_per_sec, const int frames_per_sec); + + /** + * Returns whether the last step has collided according to the given collistiontest_t or not. + */ + bool has_collided() const { return last_collided; } + + direction_t get_last_dir() const { return last_dir; } + + std::string toString() const; + std::string toShortString() const; +}; + +// +// maze_t +// + +class maze_t { + public: + class field_t { + private: + int width, height; + std::vector<tile_t> tiles; + int count[13]; + + public: + field_t(); + + void set_dim(const int w, const int h) { width=w; height=h; } + void add_tile(const tile_t tile); + + void clear(); + bool validate_size() const { return tiles.size() == (size_t)width * (size_t)height; } + + int get_width() const { return width; } + int get_height() const { return height; } + + int get_count(const tile_t tile) const { return count[number(tile)]; } + + tile_t get_tile(const int x, const int y) const; + tile_t get_tile_nc(const int x, const int y) const { return tiles[y*width+x]; } + void set_tile(const int x, const int y, tile_t tile); + + std::string toString() const; + }; + private: + static constexpr const bool DEBUG = false; + std::string filename; + acoord_t top_left_pos; + acoord_t bottom_left_pos; + acoord_t bottom_right_pos; + acoord_t top_right_pos; + acoord_t pacman_start_pos; + box_t ghost_home; + acoord_t ghost_home_pos; + acoord_t ghost_start_pos; + int ppt_x, ppt_y; + std::string texture_file; + field_t active; + field_t original; + + bool digest_position_line(const std::string& name, acoord_t& dest, const std::string& line); + bool digest_box_line(const std::string& name, box_t& dest, const std::string& line); + + public: + maze_t(const std::string& fname); + + bool is_ok() const { return active.get_width() > 0 && active.get_height() > 0; }; + + int get_width() const { return active.get_width(); } + int get_height() const { return active.get_height(); } + const acoord_t& get_top_left_corner() const { return top_left_pos; } + const acoord_t& get_bottom_left_corner() const { return bottom_left_pos; } + const acoord_t& get_bottom_right_corner() const { return bottom_right_pos; } + const acoord_t& get_top_right_corner() const { return top_right_pos; } + const acoord_t& get_pacman_start_pos() const { return pacman_start_pos; } + const box_t& get_ghost_home_box() const { return ghost_home; } + const acoord_t& get_ghost_home_pos() const { return ghost_home_pos; } + const acoord_t& get_ghost_start_pos() const { return ghost_start_pos; } + + int get_ppt_x() const { return ppt_x; } + int get_ppt_y() const { return ppt_y; } + int x_to_pixel(const int x, const int win_scale, const bool maze_offset) const { + return x * ppt_x * win_scale - ( maze_offset ? ppt_x/4 * win_scale : 0 ); + } + int y_to_pixel(const int y, const int win_scale, const bool maze_offset) const { + return y * ppt_y * win_scale - ( maze_offset ? ppt_y/4 * win_scale : 0 ); + } + int x_to_pixel(const float x, const int win_scale, const bool maze_offset) const { + return round_to_int(x * ppt_x * win_scale) - ( maze_offset ? ( ppt_x/4 * win_scale ) : 0 ); + } + int y_to_pixel(const float y, const int win_scale, const bool maze_offset) const { + return round_to_int(y * ppt_y * win_scale) - ( maze_offset ? ( ppt_y/4 * win_scale ) : 0 ); + } + std::string get_texture_file() const { return texture_file; } + int get_pixel_width() const { return get_width() * ppt_x; } + int get_pixel_height() const { return get_height() * ppt_x; } + + int clip_pos_x(const int x) const { + return std::max(0, std::min(get_width()-1, x)); + } + int clip_pos_y(const int y) const { + return std::max(0, std::min(get_height()-1, y)); + } + + int get_count(const tile_t tile) const { return active.get_count(tile); } + tile_t get_tile(const int x, const int y) const { return active.get_tile(x, y); } + void set_tile(const int x, const int y, tile_t tile) { active.set_tile(x, y, tile); } + + void draw(std::function<void(const int x_pos, const int y_pos, tile_t tile)> draw_pixel); + + void reset(); + + std::string toString() const; +}; + +#endif /* PACMAN_MAZE_HPP_ */ diff --git a/include/pacman/utils.hpp b/include/pacman/utils.hpp new file mode 100644 index 0000000..c0fcf5b --- /dev/null +++ b/include/pacman/utils.hpp @@ -0,0 +1,46 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef PACMAN_UTILS_HPP_ +#define PACMAN_UTILS_HPP_ + +#include <cstdint> +#include <cstdarg> + +/** + * See <http://man7.org/linux/man-pages/man2/clock_gettime.2.html> + * <p> + * Regarding avoiding kernel via VDSO, + * see <http://man7.org/linux/man-pages/man7/vdso.7.html>, + * clock_gettime seems to be well supported at least on kernel >= 4.4. + * Only bfin and sh are missing, while ia64 seems to be complicated. + */ +uint64_t getCurrentMilliseconds() noexcept; + +uint64_t getElapsedMillisecond() noexcept; + +float get_fps(const uint64_t t0, const uint64_t t1, const float event_count); + +void log_print(const char * format, ...) noexcept; + +#endif /* PACMAN_UTILS_HPP_ */ |