From ebc3d155de70467a270c576e8c78977cebb47f18 Mon Sep 17 00:00:00 2001 From: Rendo Date: Sat, 14 Mar 2026 20:37:34 +0500 Subject: [PATCH] Ghosts --- assets/atlas.png | Bin 1090 -> 1231 bytes assets/map | 62 ++++++++++++++++---------------- src/components.h | 28 ++++++++++++++- src/entity.cpp | 8 +++++ src/ghost.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- src/maploader.cpp | 27 +++++++++++++- src/pacman.cpp | 45 +++++++++++++++++------- src/world.cpp | 49 ++++++++++++++++++++++++-- src/world.h | 10 ++++++ 10 files changed, 270 insertions(+), 49 deletions(-) create mode 100644 src/ghost.cpp diff --git a/assets/atlas.png b/assets/atlas.png index b92a29645d05dc104d0417430d8e482cb9bb3e49..49b83967928a8124f679f0ef2a12493a7192fa19 100644 GIT binary patch delta 1150 zcmV-^1cCd)2+s+SKz{^zNkl%W~r&5Jkb2&15$D|F6s@nO&GgA_rrTfCkZs zbE;B~2^S=~)jSUdo>(ZC-RA8#|+>1azR$t1!u%n8PE(HBpeJ!^Z zg^ZxI!F*aYo}_+F+>EKa8DBF(pDOE!nDNi7KcP(v+HZwjKBS!a)Lt#kWO2|zn3`lG6oDH4Sv{#?>}>3qwijAA$X0H;*y(jk9^* zO!}5jv6lOnuz%!ApoZxnD}e)0j*G{o81L zScY`ts%Mw=GzQqKEayTb`lD%9@4mZ5{iF49;msL$i)!x?Ah+->U+pNir@kb_YP^1l zJ}vMJ?{3c6uLs>UvjFMSQuljRop^tV2G2?G(vvdJ18+YW1o70Jf{T)s} z5K@YUr%a+hl^x?d`(=#z89;jLKT~Z7!!!2{CG%@X8!Yhoe+%=d?E)a5(7-W+r@#Rg z=O$V9w102Lq93cz$`^DA4`YDsm(ly#=!N>7F~A<`X918$iO~byBcu1T(Q_UE{fXxR zm<22jUBJckr7#P)h`yG~bOED2gEu=WKG-1D`;?TW<@#IRe{mV3H-n^yCtEo|ZT+K> zI)SP#K)N*Qmou{dnK-=J(ot#4!AwqB+VrglbAP>O1>!D1`?N^AUW(KKn(qa)y5Z3$ zc~&_+EOvm_H{7DUGY`7XX7CUd(-@|K7TUz8QX6|#Wb+R%}7G= zYEwTSHiIsvFNN~}7u1)aq6>g=&;?vfUkc{|E~2l6E?{v~EM%Zx_&fl}xYT(7I3FBf zaesClpvQXsnPtpg{~1fvsw2Sc2Al!a4zMv|w@q6AEU5ke06FER^d)IM>d&QxzE-=@ z?L5Gimeatwjh&<4E2~CRT7Q_<>i&60vt9LmTszMuCg5|kMB)j2JZfCym9j33q1Jk?4f z0S*w5`nJuSZ{ak?829o0lbb%8six QaR2}S07*qoM6N<$f(Mm9l>h($ delta 1009 zcmVx!|5Ij{o#`gBWvm}yfEmR7 zR;}Uz3C18K(t5We{J5}p+`rRip7F^;@zyS9USm7>)R&vaalPe$V>%Vx;jBezZ)M41b+9I$t_;*Crj!zG|IF zs?r9OUZ{5bQ8_IWD1;E4bIyBzc<#*;K)c~bk1w@${`%*mB)Cif>9QsMd2WM*9~UHz zXd!<4_uR5tC(uy$0FpWUs3Em)Af?U{BxMR1Y8e=QTqteu8XYC6UxWX1`WffS8)wbF zk>YE9i>+M0#DA1e0xc{Dc@n5-q+z@@UIyJPK3CSA@hGoDXOGX3^cGhyR$0o4NaC-SS*_>p8ue?(?*}fyg=IJPIRc~>zJ9LsF1BTS&Jb(y z{3Y?n0h^8wOK|@27B|c)K>F>;`Ap3?RXZIXj|9#31AnZ?j>g5U&Ej*h3b;vpsoP8eTEBxgIupOxAdTP3Da*?F*S!AhGPGX?NjFc{ zN`lh*wMmn})D$3nG`cNEv;L7>UTp4NY0aCNlz+0U#n-->YrQHEPXX4KMb_h`qzO>{ zT|li1*51i8%kE)u0@S|X8s+ue=sH@5$4o3^SO)4X1DDc*wlUK95;d29()veizm<74Ed=3*+0Q4{g+$=s9`vGneUy3PUawZ;R5I^yL0La{G zKYsxFBLOCNngA`H*Y_-A^!j@|qBcDOjNXAGFr5G^jkj&m`bS~*-v>x3_b5K+Sd02o z)rc>RT{YVeupUck;MB%Wi9ai=Bqq21_*kv$zdD~%CZ_<&w(ee^xr^tXz|^NwR~g+p z{-}(cPCJX2*00rmr9MW0)FHIwGd1E{e@#23^=r3ZF7N&Y*`wg!^Z%6!wwb&KKtHeF z3&{N@OGgfvzz8ro5@2#9z~o4P$&mn)BLOBy0!)qsm>dZ(ITB!UB*5fe?*e$XKP5(! fkpv_dfX@B{@)?&ap|VA^00000NkvXXu0mjf{Yw9v diff --git a/assets/map b/assets/map index f6e4ade..ba38ccc 100644 --- a/assets/map +++ b/assets/map @@ -1,31 +1,31 @@ -#############--############## -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -- - -- - -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# P # -############--############### +############################ +# ## # +# #### ##### ## ##### #### # +#C#### ##### ## ##### ####C# +# #### ##### ## ##### #### # +# # +# #### ## ######## ## #### # +# #### ## ######## ## #### # +# ## ## ## # +###### ##### ## ##### ###### +###### ##### ## ##### ###### +###### ## ## ###### +###### ## #++# ## ###### +###### ## #__# ## ###### +- #GG# - +###### ## #GG# ## ###### +###### ## #### ## ###### +###### ## ## ###### +###### ## ######## ## ###### +###### ## ######## ## ###### +# ## # +# #### ##### ## ##### #### # +# #### ##### ## ##### #### # +#C ## P ## C# +### ## ## ######## ## ## ### +### ## ## ######## ## ## ### +# ## ## ## # +# ########## ## ########## # +# ########## ## ########## # +# # +############################ diff --git a/src/components.h b/src/components.h index d7a6e32..331f90a 100644 --- a/src/components.h +++ b/src/components.h @@ -6,6 +6,8 @@ #include class Entity{ + protected: + Vector2 project_position(int direction, int distance); public: virtual ~Entity(){} @@ -44,7 +46,10 @@ class Pacman : public Entity{ void collision(Entity* with) override; }; -class Wall : public Entity { +class PacmanWall{}; +class GhostWall{}; + +class Wall : public Entity,public PacmanWall,public GhostWall{ private: TextureAtlas texture; public: @@ -53,6 +58,9 @@ class Wall : public Entity { void draw() const override; }; +class Door : public Entity, public PacmanWall{}; +class Portal : public Entity, public GhostWall{}; + class Scorepoint : public Entity { private: TextureAtlas texture; @@ -61,4 +69,22 @@ class Scorepoint : public Entity { void draw() const override; }; +class Ghost : public Entity { + private: + static unsigned int color_decision; + TextureAtlas texture; + const int speed = 16; + Color color; + int direction = 1; + Vector2 start_position; + void recalculate_direction(); + void try_to_chase(); + public: + Ghost(); + void ready() override; + void tick() override; + void draw() const override; + void collision(Entity* with) override; +}; + #endif diff --git a/src/entity.cpp b/src/entity.cpp index 7a80291..7829ea4 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1,4 +1,5 @@ #include "components.h" +#include "world.h" void Entity::queue_free() { if (this->free_queued) @@ -10,3 +11,10 @@ void Entity::queue_free() { bool Entity::get_free_flag() { return this->free_queued; } + +Vector2 Entity::project_position(int direction, int distance) { + double angle = PI/2.0*direction; + Vector2 vector_direction = {(float)(cos(angle)),(float)(sin(-angle))}; + return Vector2Add(this->position,Vector2Scale(vector_direction, (float)distance*CELL_SIZE)); + +} diff --git a/src/ghost.cpp b/src/ghost.cpp new file mode 100644 index 0000000..6dc02c6 --- /dev/null +++ b/src/ghost.cpp @@ -0,0 +1,88 @@ +#include "components.h" +#include "world.h" +#include +#include + +unsigned int Ghost::color_decision = 0; + +Ghost::Ghost() { + this->texture = {get_world().get_atlas(),64,64,32,16}; + if (color_decision > 3) + color_decision = 0; + switch(this->color_decision++){ + case 0: + this->color = ORANGE; + break; + case 1: + this->color = PINK; + break; + case 2: + this->color = RED; + break; + case 3: + this->color = BLUE; + break; + } +} + +void Ghost::ready() { + this->start_position = position; +} + +void Ghost::tick() { + try_to_chase(); + Vector2 check_position = project_position(direction, 1); + + World& world = get_world(); + + if(dynamic_cast(world.grid[world.indexify_position(check_position)]) != nullptr){ + recalculate_direction(); + check_position = project_position(direction, 1); + } + + position = check_position; +} + +void Ghost::try_to_chase(){ + // Todo +} + +void Ghost::recalculate_direction(){ + World& world = get_world(); + + switch (rand()%3) { + case 0: + direction = (direction+2)%4; + break; + case 1: + if(dynamic_cast(world.grid[world.indexify_position(project_position((direction+1)%4, 1))]) == nullptr){ + direction = (direction+1)%4; + } + else if(dynamic_cast(world.grid[world.indexify_position(project_position((direction-1)%4,1))]) == nullptr){ + direction = (direction-1)%4; + } + else{ + direction = (direction-2)%4; + } + break; + case 2: + if(dynamic_cast(world.grid[world.indexify_position(project_position((direction-1)%4, 1))]) == nullptr){ + direction = (direction-1)%4; + } + else if(dynamic_cast(world.grid[world.indexify_position(project_position((direction+1)%4,1))]) == nullptr){ + direction = (direction+1)%4; + } + else{ + direction = (direction-2)%4; + } + break; + } +} + +void Ghost::draw() const { + DrawTextureRec(this->texture.get_texture(), this->texture.rect_view(0,0,16, 16), this->position, this->color); +} + +void Ghost::collision(Entity* with) { + +} diff --git a/src/main.cpp b/src/main.cpp index f05d420..438b56b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include "world.h" int main() { - create_world_with(1./10.); + create_world_with(1./5.); World& world = get_world(); Vector2i window_size = get_map_size("assets/map"); diff --git a/src/maploader.cpp b/src/maploader.cpp index 86a3ad2..afcf25a 100644 --- a/src/maploader.cpp +++ b/src/maploader.cpp @@ -32,6 +32,15 @@ void load_world(World& world,const char* path) { x++; } + break; + case 'G': + { + Ghost* ghost = new Ghost; + ghost->position = {(float)x*16,(float)y*16}; + world.entities.push_back(ghost); + + } + x++; break; case '\n': @@ -39,9 +48,22 @@ void load_world(World& world,const char* path) { y++; break; case '-': + { + Portal* portal = new Portal; + portal->position = {(float)x*16,(float)y*16}; + world.entities.push_back(portal); + } x++; break; - default: + case '+': + { + Door* door = new Door; + door->position = {(float)x*16,(float)y*16}; + world.entities.push_back(door); + } + x++; + break; + case ' ': { Scorepoint* point = new Scorepoint; point->position = {(float)x*16,(float)y*16}; @@ -49,6 +71,9 @@ void load_world(World& world,const char* path) { } x++; + break; + default: + x++; break; } diff --git a/src/pacman.cpp b/src/pacman.cpp index 38cd89c..22a3b9b 100644 --- a/src/pacman.cpp +++ b/src/pacman.cpp @@ -24,20 +24,35 @@ Pacman::Pacman(Vector2 position) { } void Pacman::tick() { - // Input handling - if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) {this->facing=1;} - if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) {this->facing=2;} - if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) {this->facing=3;} - if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) {this->facing=0;} - - // Movement in direction - double angle = PI/2.0*facing; - Vector2 direction = {(float)(cos(angle)),(float)(sin(-angle))}; - Vector2 new_position = Vector2Add(this->position,Vector2Scale(direction, (float)speed)); - World& world = get_world(); + // Input handling + if ( + (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) + && dynamic_cast(world.grid[world.indexify_position(project_position(1, 1))]) == nullptr + ) + this->facing=1; - if (dynamic_cast(world.grid[world.indexify_position(new_position)]) == nullptr){ + else if ( + (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) + && dynamic_cast(world.grid[world.indexify_position(project_position(2, 1))]) == nullptr + ) + this->facing=2; + + else if ((IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) + && dynamic_cast(world.grid[world.indexify_position(project_position(3, 1))]) == nullptr + ) + this->facing=3; + + else if ( + (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) + && dynamic_cast(world.grid[world.indexify_position(project_position(0, 1))]) == nullptr + ) + this->facing=0; + + // Movement in direction + Vector2 new_position = project_position(this->facing, 1); + + if (dynamic_cast(world.grid[world.indexify_position(new_position)]) == nullptr){ this->position=new_position; } @@ -79,7 +94,11 @@ void Pacman::collision(Entity* with){ score->queue_free(); get_world().points+=10; } - + Ghost* ghost = dynamic_cast(with); + if (ghost != nullptr){ + queue_free(); + get_world().lose(); + } } diff --git a/src/world.cpp b/src/world.cpp index 4442b7f..861dff1 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -4,8 +4,22 @@ #include #include +void init_world() { + create_world_with(1./5.); + + World& world = get_world(); + Vector2i window_size = get_map_size("assets/map"); + + world.load_atlas(); + world.set_size(window_size); + load_world(world,"assets/map"); + + world.setup(); +} + World::World(){ this->entities = {}; + this->grid = new Entity*[get_capacity()]; this->seconds_per_tick = 0; this->clock = 0; this->debug = false; @@ -16,7 +30,6 @@ World::~World(){ for(int i = 0; i < this->entities.size();i++) { delete this->entities[i]; } - delete [] this->grid; UnloadTexture(this->texture_atlas); } @@ -36,6 +49,14 @@ void World::process(){ this->seconds_per_tick-=0.025; if(IsKeyPressed(KEY_F3)) this->seconds_per_tick+=0.025; + + if(state != LevelState::NotFinished){ + if(IsKeyPressed(KEY_Q)) + CloseWindow(); + if(IsKeyPressed(KEY_R)) + init_world(); + return; + } this->update_grid(); @@ -75,6 +96,21 @@ void World::draw() const { for(int i = 0; i < this->entities.size(); i++) { this->entities[i]->draw(); } + + if(state != LevelState::NotFinished) { + if(state == LevelState::Won) + { + DrawText("You won!", 16, 16, 24, WHITE); + } + else { + DrawText("You lost!",16,16,24,WHITE); + } + DrawText("q to quit, r to restart",0,40,16,WHITE); + DrawText(TextFormat("Score: %i",this->points),16,56,16,WHITE); + + } + + if (this->debug == false) return; @@ -122,7 +158,7 @@ void World::set_size(Vector2i size){ this->width = size.x; this->height = size.y; if(this->grid != nullptr) - delete [] this->grid; + delete [] grid; this->grid = new Entity*[this->get_capacity()]; } @@ -138,11 +174,20 @@ int World::get_columns() const{ return this->width/CELL_SIZE; } +void World::lose(){ + state = LevelState::Lost; +} + +void World::win(){ + state = LevelState::Won; +} + Texture2D* World::get_atlas() { return &this->texture_atlas; } void create_world_with(float seconds_per_tick){ + get_world() = World(); get_world().seconds_per_tick = seconds_per_tick; } diff --git a/src/world.h b/src/world.h index ea94aca..7237d8a 100644 --- a/src/world.h +++ b/src/world.h @@ -14,6 +14,12 @@ struct Vector2i{ int y; }; +enum LevelState{ + NotFinished = 0, + Won = 1, + Lost = 2, +}; + /// Class that holds information about game world class World { public: @@ -40,16 +46,20 @@ class World { int get_columns() const; int get_rows() const; void load_atlas(); + void lose(); + void win(); Texture2D* get_atlas(); int indexify_position(Vector2 vector); int points = 0; private: float clock = 0.; + int state = 0; void update_grid(); Texture2D texture_atlas; }; void create_world_with(float seconds_per_tick); +void init_world(); World& get_world(); // Thanks, 2ndbeam, helps a lot Vector2i get_map_size(const char* path); void load_world(World& world, const char* path);