summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/pacman/game.hpp312
-rw-r--r--include/pacman/globals.hpp67
-rw-r--r--include/pacman/graphics.hpp214
-rw-r--r--include/pacman/maze.hpp336
-rw-r--r--include/pacman/utils.hpp46
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_ */