ソースを参照

We have almost everything.

Eiyeron Fulmincendii 9 年 前
コミット
728e3aedd0
共有16 個のファイルを変更した721 個の追加150 個の削除を含む
  1. 3 1
      data/blinky.lua
  2. 33 0
      data/p2.lua
  3. 1 0
      src/engine/State.h
  4. 4 3
      src/engine/StateMachine.cpp
  5. 59 0
      src/pacman/AIEntity.cpp
  6. 22 0
      src/pacman/AIEntity.h
  7. 46 0
      src/pacman/AIPlayer.cpp
  8. 23 0
      src/pacman/AIPlayer.h
  9. 96 0
      src/pacman/Entity.cpp
  10. 58 0
      src/pacman/Entity.h
  11. 233 82
      src/pacman/GameState.cpp
  12. 25 8
      src/pacman/GameState.h
  13. 73 0
      src/pacman/Ghost.cpp
  14. 27 0
      src/pacman/Ghost.h
  15. 9 21
      src/pacman/Player.cpp
  16. 9 35
      src/pacman/Player.h

+ 3 - 1
data/blinky.lua

@@ -1,9 +1,11 @@
 blinky = {
     t = 0,
     init = function(self)
-        print("Hello World!")
     end,
     update = function(self)
         self.t = self.t + 1
+        if self.t %60 == 0 then
+            self.intent = math.random(0, 4)
+        end
     end
 }

+ 33 - 0
data/p2.lua

@@ -0,0 +1,33 @@
+p2 = {
+    -- Pushed values to this object on update (self.xxx)
+    -- x,y (in) : top left corner of the entity
+    -- vx,vy (in) : Velocity
+    -- width (in), height : dimensions of the map<->entity hitbox
+    -- points (in) : scored points
+    -- intent (in-out) : Intended direction of the entity when it'll can.
+        -- Editing intent will impact the AI's deplacement.
+
+    -- The game structure
+    -- The map is stored as 8*8pixel tiles.
+    -- The map has a dimension of 224*248 pixels (borders included).
+    -- To push the IA to move to another direction when it can, edit self.
+    -- The game structure stores the players' and ghosts' position (in pixel) and velocity
+        -- gamestructure.p1.x
+        -- gamestructure.p2.y
+        -- gamestructure.blinky == red
+        -- gamestructure.inky == cyan
+        -- gamestructure.pinky == huh
+        -- gamestructure.clyde == orange
+
+    -- gamestate functions
+    -- Note : All of those functions needs gamestate.data as first argument
+    --  collide(gamestate.data, x,y,w,h) checks if a rectangle defined by its left corner and its dimensions collides with the walls.
+    --  get_gum(gamestate.data, x, y) returns the gum if there is one at this position. Gets pixel coordinates (not tile position)
+
+    init = function(self)
+    end,
+    update = function(self)
+        self.intent = math.random(0, 4)
+        -- print("p1.x : "..gamestate.p1.x)
+    end
+}

+ 1 - 0
src/engine/State.h

@@ -8,6 +8,7 @@ namespace WalrusRPG
         class State
         {
           public:
+            bool dead = false;
             virtual ~State(){};
             virtual void update(unsigned dt) = 0;
             virtual void render(unsigned dt) = 0;

+ 4 - 3
src/engine/StateMachine.cpp

@@ -108,7 +108,8 @@ void StateMachine::run()
         Input::key_poll();
         stack.back()->update(100 * update_time / TIMER_FREQ);
         last_update = update_stamp;
-
+        if(stack.back()->dead) pop();
+        if(stack.empty()) return;
         if (Timing::gettime() < loop_next)
         {
             frame_stamp = Timing::gettime();
@@ -121,8 +122,8 @@ void StateMachine::run()
             // Text::print_format(0, 0, "WRPG build %s", git_version);
             if (frame_time != 0 && update_time != 0)
             {
-                Text::print_format(0, 240 - 8, "%ufps, %uups", TIMER_FREQ / frame_time,
-                                   TIMER_FREQ / update_time);
+                // Text::print_format(0, 240 - 8, "%ufps, %uups", TIMER_FREQ / frame_time,
+                //                    TIMER_FREQ / update_time);
             }
             // TODO : use a boolean to show/hide and to avoid that frigging wanring.
             // draw_buttons();

+ 59 - 0
src/pacman/AIEntity.cpp

@@ -0,0 +1,59 @@
+#include "AIEntity.h"
+#include "Logger.h"
+#include <lua.hpp>
+
+using Pacman::AIEntity;
+using namespace WalrusRPG;
+
+void AIEntity::initAI() {
+    lua_State *L = g.get_lua_context();
+    int result = luaL_loadfile (L, filename) || lua_pcall(L,0,0,0);
+    if(result != LUA_OK)
+    {
+        Logger::error("Lua : Error while loading %s : %s", filename, lua_tostring(L, -1));
+        lua_pop(L, 1);
+        AI_ready = false;
+    }
+    else{
+        Logger::log("Lua : %s correctly loaded.", filename);
+        try {
+            lua_getglobal(L, ai_name);
+            luwra::Table t = luwra::read<luwra::Table>(L, -1);
+            t.get<luwra::NativeFunction<void>>("init")(t);
+            AI_ready = true;
+        } catch(std::exception e)
+        {
+            Logger::error("Error while loading %s : %s", ai_name);
+            AI_ready = false;
+        }
+    }
+}
+
+void AIEntity::updateAI() {
+    lua_State *L = g.get_lua_context();
+    if(AI_ready && ai_name != nullptr) {
+        try{
+            lua_getglobal(L, ai_name);
+            luwra::Table t = luwra::read<luwra::Table>(L, -1);
+            // Updating position
+            t.set("x", x);
+            t.set("y", y);
+            t.set("vx", x);
+            t.set("vy", y);
+            t.set("w", 8);
+            t.set("h", 8);
+            t.set("points", points);
+            t.set("intent", (int)intent);
+            t.get<luwra::NativeFunction<void>>("update")(t);
+
+            lua_getglobal(L, ai_name);
+            luwra::Table t2 = luwra::read<luwra::Table>(L, -1);
+            intent = (MoveIntent)t2.get<int>("intent");
+            apply_movement();
+
+        }catch(std::exception e) {
+            WalrusRPG::Logger::error("Error while executing %s: %s", ai_name, e.what());
+            AI_ready = false;
+        }
+    }
+}

+ 22 - 0
src/pacman/AIEntity.h

@@ -0,0 +1,22 @@
+#ifndef INCLUDE_AICOMPONENT_H
+#define INCLUDE_AICOMPONENT_H
+
+#include "GameState.h"
+#include "Entity.h"
+#include "Logger.h"
+#include <luwra.hpp>
+
+namespace Pacman {
+    class AIEntity : public Entity {
+    private:
+        const char* filename;
+    public:
+        bool AI_ready;
+        int points;
+        AIEntity(int x, int y, GameState& g, const char* ai_name, const char* filename): Entity(x, y, g, ai_name), filename(filename), points(0){}
+        void initAI();
+        void updateAI();
+    };
+}
+
+#endif

+ 46 - 0
src/pacman/AIPlayer.cpp

@@ -0,0 +1,46 @@
+#include "AIPlayer.h"
+#include "Graphics.h"
+#include "Logger.h"
+
+#include <luwra.hpp>
+
+using Pacman::Entity;
+using Pacman::AIPlayer;
+using Pacman::ButtonInput;
+using Pacman::MoveIntent;
+using WalrusRPG::Camera;
+using namespace WalrusRPG::Graphics;
+using namespace WalrusRPG::Input;
+using namespace WalrusRPG;
+
+
+AIPlayer::AIPlayer(int x, int y, GameState &g, const char* filename, const char* ai_name): AIEntity(x, y, g, ai_name, filename), dead(false)
+{
+    intent = LEFT;
+}
+
+AIPlayer::~AIPlayer()
+{
+
+}
+
+void AIPlayer::update(unsigned dt)
+{
+    if(dead) return;
+    int px = x, py = y;
+    updateAI();
+    if(g.is_there_collision(g.p1, g.p2)) {
+        x = px;
+        y = py;
+    }
+}
+
+void AIPlayer::render(unsigned dt, Camera& cam)
+{
+    if(dead) return;
+	put_rectangle({x-cam.get_x(), y-cam.get_y(),16,16}, Yellow);
+	if(x < 0)
+	put_rectangle({x-cam.get_x()+224, y-cam.get_y(),16,16}, Yellow);
+	if(x > 232)
+	put_rectangle({x-cam.get_x()-224, y-cam.get_y(),16,16}, Yellow);
+}

+ 23 - 0
src/pacman/AIPlayer.h

@@ -0,0 +1,23 @@
+#ifndef INCLUDE_AIPLAYER_H
+#define INCLUDE_AIPLAYER_H
+
+#include "input/Input.h"
+#include "render/Camera.h"
+#include "AIEntity.h"
+
+
+namespace Pacman
+{
+	class AIPlayer: public AIEntity {
+	private:
+	public:
+		bool dead;
+		AIPlayer(int x, int y, GameState &g, const char* filename, const char* ai_name);
+		~AIPlayer();
+		virtual void render(unsigned dt, WalrusRPG::Camera& cam) override;
+		virtual void update(unsigned dt) override;
+	};
+}
+
+#include "GameState.h"
+#endif

+ 96 - 0
src/pacman/Entity.cpp

@@ -0,0 +1,96 @@
+#include "Entity.h"
+#include "GameState.h"
+#include "Logger.h"
+
+#include <luwra.hpp>
+
+
+using Pacman::ButtonInput;
+using Pacman::Entity;
+using Pacman::GameState;
+using Pacman::MoveIntent;
+using  WalrusRPG::Utils::Rect;
+using namespace WalrusRPG::Input;
+using namespace WalrusRPG;
+
+ButtonInput::ButtonInput(Key key): key(key)
+{
+}
+ButtonInput::~ButtonInput()
+{
+}
+
+
+bool ButtonInput::operator()()
+{
+	return key_down(key);
+}
+
+Entity::Entity(int x, int y, GameState &g, const char* ai_name) : g(g), ai_name(ai_name), x(x), y(y),
+	vx(0), vy(0),
+	start_x(x), start_y(y),
+	intent(MoveIntent::NONE),
+	up(nullptr), down(nullptr), left(nullptr), right(nullptr)
+{
+
+}
+
+Entity::~Entity()
+{
+	if(up != nullptr)
+		delete up;
+	if(down != nullptr)
+		delete down;
+	if(left != nullptr)
+		delete left;
+	if(right != nullptr)
+		delete right;
+}
+
+void Entity::apply_movement()
+{
+	if(intent == UP) {
+		if(!g.does_collide_a_wall({x+4, y+4-1, 8, 8})){
+			vy = -1;
+			vx = 0;
+		}
+	}
+	if(intent == DOWN) {
+		if(!g.does_collide_a_wall({x+4, y+4+1, 8, 8})){
+			vy = 1;
+			vx = 0;
+		}
+	}
+	if(intent == LEFT) {
+		if(!g.does_collide_a_wall({x+4-1, y+4, 8, 8})){
+			vx = -1;
+			vy = 0;
+		}
+	}
+	if(intent == RIGHT) {
+		if(!g.does_collide_a_wall({x+4+1, y+4, 8, 8})){
+			vx = 1;
+			vy = 0;
+		}
+	}
+
+	if(g.does_collide_a_wall({x+4+vx, y+4+vy, 8, 8})){
+		vy = 0;
+		vx = 0;
+	}
+
+	x += vx;
+	y += vy;
+
+	if(x < -8)
+		x = 224-8;
+	if(x+16 > 232)
+		x = 0;
+}
+
+void Entity::reset()
+{
+	x = start_x;
+	y = start_y;
+	intent = MoveIntent::NONE;
+}

+ 58 - 0
src/pacman/Entity.h

@@ -0,0 +1,58 @@
+#ifndef INCLUDE_ENTITY_H
+#define INCLUDE_ENTITY_H
+
+#include "input/Input.h"
+#include "render/Camera.h"
+
+namespace Pacman{
+    class VButtonInput
+	{
+	public:
+		virtual bool operator()() = 0;
+		VButtonInput() {};
+		virtual ~VButtonInput() {};
+	};
+
+	class ButtonInput : public VButtonInput {
+		private:
+			WalrusRPG::Input::Key key;
+		public:
+		ButtonInput(WalrusRPG::Input::Key key);
+		~ButtonInput();
+		bool operator()() override;
+	};
+
+	enum MoveIntent{NONE, UP, DOWN, LEFT, RIGHT};
+
+    class GameState;
+	class Entity
+	{
+    protected:
+        GameState &g;
+        const char* ai_name;
+	public:
+    	int start_x, start_y;
+		int x;
+		int y;
+		int vx;
+		int vy;
+		MoveIntent intent;
+		VButtonInput *up;
+		VButtonInput *down;
+		VButtonInput *left;
+		VButtonInput *right;
+
+		Entity(int x, int y, GameState &g, const char *ai_name);
+		virtual ~Entity();
+		virtual void render(unsigned dt, WalrusRPG::Camera& cam) = 0;
+		virtual void update(unsigned dt) = 0;
+        void apply_movement();
+        void reset();
+
+	};
+}
+
+#include "GameState.h"
+
+
+#endif

+ 233 - 82
src/pacman/GameState.cpp

@@ -1,17 +1,22 @@
-#include "GameState.h"
 #include "piaf/Archive.h"
 #include "input/Input.h"
 #include "render/Text.h"
+#include "engine/StateMachine.h"
 #include "Graphics.h"
 #include "Logger.h"
 
+#include "GameState.h"
 #include "Player.h"
+#include "Ghost.h"
+#include "AIPlayer.h"
 #include "static_data.h"
 
 #include <luwra.hpp>
 
 using Pacman::GameState;
 using Pacman::Player;
+using Pacman::AIPlayer;
+using Pacman::Ghost;
 using Pacman::ButtonInput;
 using Pacman::MoveIntent;
 using WalrusRPG::PIAF::Archive;
@@ -90,37 +95,126 @@ const uint8_t gums_model[] = {
 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 };
 
-GameState::GameState() : level(1), map(28, 31, map_data, nullptr, map_texture), cam(-(320-28*8)/2,4),
-	p1(4,4), p2(204,228), L(luaL_newstate()), blinky_ready(false)
-{
-	p1.up = new ButtonInput(K_A);
-	p1.down = new ButtonInput(K_B);
-	p1.left = new ButtonInput(K_L);
-	p1.right = new ButtonInput(K_R);
+struct Corner{int x; int y;};
 
-	p2.up = new ButtonInput(K_UP);
-	p2.down = new ButtonInput(K_DOWN);
-	p2.left = new ButtonInput(K_LEFT);
-	p2.right = new ButtonInput(K_RIGHT);
 
+GameState::GameState() : level(1), map(28, 31, map_data, nullptr, map_texture), cam(-(320-28*8),4),
+	p1(nullptr),
+	p2(nullptr),
+	L(luaL_newstate())
+{
 	luaL_openlibs(L);
-	load_ghost_script("data/blinky.lua", "blinky", blinky_ready);
+
+	// Creating game state functions/mechanisms
+
+	lua_CFunction cfun_collide = LUWRA_WRAP(GameState::does_collide_long);
+	lua_CFunction cfun_getgum = LUWRA_WRAP(GameState::LH_get_gum_by_pixel_pos);
+	lua_CFunction cfun_ispixelsolid = LUWRA_WRAP(GameState::LH_is_pixel_solid);
+	lua_CFunction cfun_getTile = LUWRA_WRAP(GameState::LH_get_tile);
+
+
+	lua_createtable(L, 0,0);
+	luwra::Table gameState(L, -1);
+	gameState.set("data", this);
+	gameState.set("map", map);
+	gameState.set("collide", cfun_collide);
+	gameState.set("get_tile", cfun_getTile);
+
+	luwra::setGlobal(L, "gamestate", gameState);
+	p1 = new AIPlayer(4,4, *this, "data/p2.lua", "p2");
+	p2 = new AIPlayer(200,228, *this, "data/p2.lua", "p2");
+	blinky = new Ghost(68, 84, *this, "data/blinky.lua", "blinky");
+	inky = new Ghost(140, 84, *this, "data/inky.lua", "inky");
+	pinky = new Ghost(68, 132, *this, "data/pinky.lua", "pinky");
+	clyde = new Ghost(140, 132, *this, "data/clyde.lua", "clyde");
+	updateLuaState();
+	p1->initAI();
+	p2->initAI();
+	blinky->initAI();
+	inky->initAI();
+	pinky->initAI();
+	clyde->initAI();
+
 	set_gums();
 }
 
 GameState::~GameState()
 {
 	lua_close(L);
+	delete p1;
+	delete p2;
+	delete blinky;
+	delete pinky;
+	delete inky;
+	delete clyde;
+}
+
+luwra::Table GameState::registerEntity(Entity* e){
+	lua_createtable(L,0,0);
+	luwra::Table table(L, -1);
+	table.set("x", e->x);
+	table.set("y", e->y);
+	table.set("vx", e->vx);
+	table.set("vy", e->vy);
+
+	return table;
+}
+void GameState::updateLuaState() {
+	lua_getglobal(L, "gamestate");
+	luwra::Table gameState(L, -1);
+	gameState.set("p1", registerEntity(p1));
+	gameState.set("p2", registerEntity(p2));
+	gameState.set("blinky", registerEntity(blinky));
+	gameState.set("inky", registerEntity(inky));
+	gameState.set("pinky", registerEntity(pinky));
+	gameState.set("clyde", registerEntity(clyde));
 }
 
 void GameState::update(unsigned dt)
 {
+	int stack_size = lua_gettop(L);
+
+	if(remaining_pills == 0 || (p1->dead && p2->dead))
+	{
+		if(Input::key_down(Key::K_START))
+		{
+			dead = true;
+
+			// Uncomment and commend dead if you want to make it run endlessly
+			/*level++;
+			set_gums();
+			p1->reset();
+			p2->reset();
+			blinky->reset();
+			inky->reset();
+			pinky->reset();
+			clyde->reset();
+
+			p1->initAI();
+			p2->initAI();
+			blinky->initAI();
+			inky->initAI();
+			pinky->initAI();
+			clyde->initAI();
+			*/
+		}
+			return;
+	}
+
 	map.update(dt);
-	p1.update(dt);
-	p2.update(dt);
-	move_player(p1);
-	move_player(p2);
-	update_ghost("blinky", blinky_ready);
+	p1->update(dt);
+	p1->points += 10*collect_gums({p1->x, p1->y, 16, 16});
+	p2->update(dt);
+	p2->points += 10*collect_gums({p2->x, p2->y, 16, 16});
+	blinky->update(dt);
+	inky->update(dt);
+	pinky->update(dt);
+	clyde->update(dt);
+	updateLuaState();
+
+	lua_pop(L, stack_size);
+	//move_player(p1);
+	//move_player(p2);
 }
 
 void GameState::render(unsigned dt)
@@ -146,28 +240,101 @@ void GameState::render(unsigned dt)
 	// render_player(j1);
 	// render_player(j2);
 
-	p1.render(dt, cam);
-	p2.render(dt, cam);
+	p1->render(dt, cam);
+	p2->render(dt, cam);
+	blinky->render(dt, cam, Red);
+	inky->render(dt, cam, Cyan);
+	pinky->render(dt, cam, Pixel(255, 153, 204));
+	clyde->render(dt, cam, Pixel(255, 153, 51));
 	// Borders
 
 	put_rectangle({0, 0, 0u-cam.get_x(), 320}, DarkGray);
-	put_rectangle({320+cam.get_x(), 0, 0u-cam.get_x(), 320}, DarkGray);
 	put_rectangle({-cam.get_x(),0, 1, 240}, White);
-	put_rectangle({320+cam.get_x(),0, 1, 240}, White);
+
+	Text::print_format(0,0,"Level %6d", level);
+	Text::print_format(0,8,"P1 : %07d", p1->points);
+	Text::print_format(0,16,"P2 : %07d", p2->points);
+
+	if(remaining_pills == 0 || (p1->dead && p2->dead)) {
+		if(p1->points > p2-> points) {
+			Text::print_format(0, 48, "P1 Won");
+		} else if(p2->points > p1-> points) {
+			Text::print_format(0, 48, "P2 Won");
+		} else {
+			Text::print_format(0, 48, "It's a draw.");
+		}
+	}
 
 }
 
 void GameState::set_gums()
 {
+	remaining_pills = 0;
 	for (int y = 0; y < 31; ++y)
 	{
 		for (int x = 0; x < 28; ++x)
 		{
+			if(gums[y][x]) remaining_pills++;
 			gums[y][x] = (Pacgum)gums_model[28*y + x];
 		}
 	}
 }
 
+int GameState::collect_gums(Rect r)
+{
+	Corner left{r.x, r.y+(int)(r.height-1)/2};
+	Corner right{r.x+(int)(r.width-1), r.y+(int)(r.height-1)/2};
+	Corner top{r.x+(int)(r.width-1)/2, r.y};
+	Corner bottom{r.x+(int)(r.width-1)/2,  r.y+(int)(r.height-1)};
+	Corner center{r.x+(int)(r.width-1)/2,  r.y+(int)(r.height-1)/2};
+	int pt = 0;
+	if(LH_get_gum_by_pixel_pos(left.x/8, left.y/8) != 0){
+		gums[left.y/8][left.x/8] = NOTHING;
+		pt++;
+	}
+	if(LH_get_gum_by_pixel_pos(right.x/8, right.y/8) != 0){
+		if(LH_get_gum_by_pixel_pos(right.x/8, right.y/8) == 2) {
+			blinky->make_weak();
+			inky->make_weak();
+			pinky->make_weak();
+			clyde->make_weak();
+		}
+		gums[right.y/8][right.x/8] = NOTHING;
+		pt++;
+	}
+	if(LH_get_gum_by_pixel_pos(top.x/8, top.y/8) != 0){
+		if(LH_get_gum_by_pixel_pos(top.x/8, top.y/8) == 2) {
+			blinky->make_weak();
+			inky->make_weak();
+			pinky->make_weak();
+			clyde->make_weak();
+		}
+			gums[top.y/8][top.x/8] = NOTHING;
+		pt++;
+	}
+	if(LH_get_gum_by_pixel_pos(bottom.x/8, bottom.y/8) != 0){
+		if(LH_get_gum_by_pixel_pos(bottom.x/8, bottom.y/8) == 2) {
+			blinky->make_weak();
+			inky->make_weak();
+			pinky->make_weak();
+			clyde->make_weak();
+		}
+			gums[bottom.y/8][bottom.x/8] = NOTHING;
+		pt++;
+	}
+	if(LH_get_gum_by_pixel_pos(center.x/8, center.y/8) != 0){
+		if(LH_get_gum_by_pixel_pos(center.x/8, center.y/8) == 2) {
+			blinky->make_weak();
+			inky->make_weak();
+			pinky->make_weak();
+			clyde->make_weak();
+		}
+		gums[center.y/8][center.x/8] = NOTHING;
+		pt++;
+	}
+	return pt;
+}
+
 /**
  * Does a corner-based collision check for a small Rect.
  * Assumes that said Rect as big or smaller than a tile
@@ -177,8 +344,6 @@ void GameState::set_gums()
  */
 bool GameState::does_collide_a_wall(Rect r)
 {
-	struct Corner{int x; int y;};
-
 	Corner top_left{r.x, r.y};
 	Corner top_right{(r.x+(int)r.width-1), r.y};
 	Corner bottom_left{r.x, (r.y+(int)r.height-1)};
@@ -189,80 +354,66 @@ bool GameState::does_collide_a_wall(Rect r)
 			|| (is_inside_map(bottom_left.x, bottom_left.y) && (map.is_tile_solid(bottom_left.x/8, bottom_left.y/8) != 0));
 }
 
-void GameState::move_player(Player &p)
+bool GameState::does_collide_long(int x, int y, int width, int height)
+{
+	return does_collide_a_wall({x, y, (unsigned)width, (unsigned)height});
+}
+
+
+void GameState::move_player(Entity *p)
 {
-	if(p.intent == UP) {
-		if(!does_collide_a_wall({p.x+4, p.y+4-1, 8, 8})){
-			p.vy = -1;
-			p.vx = 0;
+	if(p->intent == UP) {
+		if(!does_collide_a_wall({p->x+4, p->y+4-1, 8, 8})){
+			p->vy = -1;
+			p->vx = 0;
 		}
 	}
-	if(p.intent == DOWN) {
-		if(!does_collide_a_wall({p.x+4, p.y+4+1, 8, 8})){
-			p.vy = 1;
-			p.vx = 0;
+	if(p->intent == DOWN) {
+		if(!does_collide_a_wall({p->x+4, p->y+4+1, 8, 8})){
+			p->vy = 1;
+			p->vx = 0;
 		}
 	}
-	if(p.intent == LEFT) {
-		if(!does_collide_a_wall({p.x+4-1, p.y+4, 8, 8})){
-			p.vx = -1;
-			p.vy = 0;
+	if(p->intent == LEFT) {
+		if(!does_collide_a_wall({p->x+4-1, p->y+4, 8, 8})){
+			p->vx = -1;
+			p->vy = 0;
 		}
 	}
-	if(p.intent == RIGHT) {
-		if(!does_collide_a_wall({p.x+4+1, p.y+4, 8, 8})){
-			p.vx = 1;
-			p.vy = 0;
+	if(p->intent == RIGHT) {
+		if(!does_collide_a_wall({p->x+4+1, p->y+4, 8, 8})){
+			p->vx = 1;
+			p->vy = 0;
 		}
 	}
 
-	if(does_collide_a_wall({p.x+4+p.vx, p.y+4+p.vy, 8, 8})){
-		p.vy = 0;
-		p.vx = 0;
+	if(does_collide_a_wall({p->x+4+p->vx, p->y+4+p->vy, 8, 8})){
+		p->vy = 0;
+		p->vx = 0;
 	}
 
-	p.x += p.vx;
-	p.y += p.vy;
+	p->x += p->vx;
+	p->y += p->vy;
 
-	if(p.x < -8)
-		p.x = 224-8;
-	if(p.x+16 > 232)
-		p.x = 0;
+	if(p->x < -8)
+		p->x = 224-8;
+	if(p->x+16 > 232)
+		p->x = 0;
 }
 
-void GameState::load_ghost_script(const char* filename, const char* ghost_name, bool& ready_flag)
+bool GameState::is_there_collision(Entity* a, Entity* b)
 {
-	int result = luaL_loadfile (L, filename) || lua_pcall(L,0,0,0);
-	if(result != LUA_OK)
-	{
-		Logger::error("Lua : Error while loading %s : %s", filename, lua_tostring(L, -1));
-		lua_pop(L, 1);
-		ready_flag = false;
-	}
-	else{
-		Logger::log("Lua : %s correctly loaded.", filename);
-		try {
-			lua_getglobal(L, ghost_name);
-			luwra::Table t = luwra::read<luwra::Table>(L, -1);
-			t.get<luwra::NativeFunction<void>>("init")(t);
-			ready_flag = true;
-		} catch(std::exception e)
-		{
-			Logger::error("Error while loading %s : %s", ghost_name);
-			ready_flag = false;
-		}
-	}
+	if ((a->x + 16 < b->x) || (a->x > b->x+16))
+   		return false;
+ 	else if ((a->y + 16 < b->y) || (a->y > b->y + 16))
+   		return false;
+	else return true;
 }
 
-void GameState::update_ghost(const char* ghost_name, bool& ready_flag) {
-	if(ready_flag) {
-		try{
-			lua_getglobal(L, ghost_name);
-			luwra::Table t = luwra::read<luwra::Table>(L, -1);
-			t.get<luwra::NativeFunction<void>>("update")(t);
-		}catch(std::exception e) {
-			Logger::error("Error while executing %s: %s", ghost_name, e.what());
-			ready_flag = false;
-		}
-	}
+luwra::Table GameState::LH_get_tile(int tx, int ty){
+	lua_createtable(L,0,0);
+	luwra::Table t(L, -1);
+	t.set("is_solid", LH_is_tile_solid(tx, ty)!=0);
+	t.set("gum", LH_does_tile_has_gum_solid(tx, ty));
+	return t;
 }

+ 25 - 8
src/pacman/GameState.h

@@ -4,24 +4,31 @@
 #include "map/Map.h"
 #include "render/Camera.h"
 #include "utility/Rect.h"
-#include "Player.h"
 #include <lua.hpp>
-
+#include <luwra.hpp>
 namespace Pacman
 {
 	enum Pacgum:uint8_t {NOTHING, NORMAL, SUPER};
 
+	class Ghost;
+	class AIPlayer;
+	class Entity;
 	class GameState : public WalrusRPG::States::State {
 	private:
 		unsigned level;
 		WalrusRPG::Map map;
 		Pacgum gums[31][28];
 		WalrusRPG::Camera cam;
-		Player p1;
-		Player p2;
 		lua_State *L;
-		bool blinky_ready;
+		int remaining_pills;
 	public:
+		AIPlayer *p1;
+		AIPlayer *p2;
+		Ghost *blinky;
+		Ghost *inky;
+		Ghost *pinky;
+		Ghost *clyde;
+
 		GameState();
 		~GameState();
         void update(unsigned dt) override;
@@ -29,11 +36,21 @@ namespace Pacman
         void set_gums();
         inline bool is_inside_map(int x, int y) { return x >= 0 && x < 224 && y >= 0 && y < 248;}
         bool does_collide_a_wall(WalrusRPG::Utils::Rect r);
-        void move_player(Player &p);
+		int collect_gums(WalrusRPG::Utils::Rect r);
+		bool does_collide_long(int x, int y, int width, int height);
+        void move_player(Entity *p);
         void render_player(WalrusRPG::Utils::Rect r);
-		void load_ghost_script(const char* filename, const char* ghost_name, bool& ready_flag);
-		void update_ghost(const char* ghost_name, bool& ready_flag);
+		inline lua_State* get_lua_context() {return L;}
+		void updateLuaState();
+		luwra::Table registerEntity(Entity* e);
+		bool is_there_collision(Entity* a, Entity* b);
 
+		// Lua helpers
+		luwra::Table LH_get_tile(int tx, int ty);
+		int LH_is_pixel_solid(int x, int y) {return does_collide_a_wall({x, y, 1, 1});}
+		int LH_get_gum_by_pixel_pos(int x, int y) { return is_inside_map(x*8, y*8) ? gums[y][x] : 0; }
+		int LH_is_tile_solid(int x, int y) {return does_collide_a_wall({x*8, y*8, 1,1});}
+		int LH_does_tile_has_gum_solid(int x, int y) {return LH_get_gum_by_pixel_pos(x*8, y*8);}
 	};
 }
 

+ 73 - 0
src/pacman/Ghost.cpp

@@ -0,0 +1,73 @@
+#include "AIPlayer.h"
+#include "Ghost.h"
+#include "Graphics.h"
+#include "Logger.h"
+
+#include <luwra.hpp>
+
+using Pacman::Entity;
+using Pacman::Ghost;
+using Pacman::ButtonInput;
+using Pacman::MoveIntent;
+using WalrusRPG::Camera;
+using WalrusRPG::Graphics::Pixel;
+using namespace WalrusRPG::Graphics;
+using namespace WalrusRPG::Input;
+using namespace WalrusRPG;
+
+
+Ghost::Ghost(int x, int y, GameState &g, const char* filename, const char* ghost_name): AIEntity(x, y, g, ghost_name, filename),
+    weak(false), dead_timer(0)
+{
+    intent = UP;
+}
+
+Ghost::~Ghost()
+{
+
+}
+
+void Ghost::update(unsigned dt)
+{
+    if(dead_timer > 0) {
+        dead_timer --;
+        return;
+    }
+    updateAI();
+    if(g.is_there_collision(this, g.p1)){
+        if(weak)
+            kill();
+        else
+            g.p1->dead = true;
+    }
+    if(g.is_there_collision(this, g.p2)){
+        if(weak)
+            kill();
+        else
+        g.p2->dead = true;
+
+    }
+    if(weak) {
+        weak_timer--;
+        if(weak_timer <= 0)
+        weak = false;
+    }
+}
+
+void Ghost::render(unsigned dt, Camera& cam, Pixel col)
+{
+    if(dead_timer) {
+        col = dead_timer % 2 ? col : DarkGray;
+    } else if(weak){
+        if(weak_timer > 60 || (weak_timer % 10 < 5) )
+        col = Blue;
+        else
+        col = White;
+    }
+
+	put_rectangle({x-cam.get_x(), y-cam.get_y(),16,16}, col);
+	if(x < 0)
+	put_rectangle({x-cam.get_x()+224, y-cam.get_y(),16,16}, col);
+	if(x > 232)
+	put_rectangle({x-cam.get_x()-224, y-cam.get_y(),16,16}, col);
+}

+ 27 - 0
src/pacman/Ghost.h

@@ -0,0 +1,27 @@
+#ifndef INCLUDE_GHOST_H
+#define INCLUDE_GHOST_H
+
+#include "input/Input.h"
+#include "render/Camera.h"
+#include "AIEntity.h"
+
+
+namespace Pacman
+{
+	class Ghost: public AIEntity {
+	public:
+		bool weak;
+		int weak_timer;
+		int dead_timer;
+		Ghost(int x, int y, GameState &g, const char* filename, const char* ai_name);
+		~Ghost();
+		virtual void render(unsigned dt, WalrusRPG::Camera& cam) override {};
+		virtual void render(unsigned dt, WalrusRPG::Camera& cam, WalrusRPG::Graphics::Pixel col);
+		virtual void update(unsigned dt) override;
+		inline void make_weak() {weak = true; weak_timer = 200;}
+		inline void kill() {x=start_x; y=start_y; dead_timer = 180; weak = false;}
+	};
+}
+
+#include "GameState.h"
+#endif

+ 9 - 21
src/pacman/Player.cpp

@@ -1,6 +1,7 @@
 #include "Player.h"
 #include "Graphics.h"
 
+using Pacman::Entity;
 using Pacman::Player;
 using Pacman::ButtonInput;
 using Pacman::MoveIntent;
@@ -8,34 +9,20 @@ using WalrusRPG::Camera;
 using namespace WalrusRPG::Graphics;
 using namespace WalrusRPG::Input;
 
-ButtonInput::ButtonInput(Key key): key(key)
-{
-}
-ButtonInput::~ButtonInput()
-{
-}
 
 
-bool ButtonInput::operator()()
+Player::Player(int x, int y, GameState &g, Key kup, Key kdown, Key kleft, Key kright) : Entity(x, y, g, nullptr)
 {
-	return key_down(key);
-}
+	up = new ButtonInput(kup);
+	down = new ButtonInput(kdown);
+	left = new ButtonInput(kleft);
+	right = new ButtonInput(kright);
 
-Player::Player(int x, int y) : x(x), y(y), up(nullptr), down(nullptr), left(nullptr), right(nullptr),
-	intent(MoveIntent::NONE), vx(0), vy(0)
-{
 }
 
 Player::~Player()
 {
-	if(up != nullptr)
-		delete up;
-	if(down != nullptr)
-		delete down;
-	if(left != nullptr)
-		delete left;
-	if(right != nullptr)
-		delete right;
+
 }
 
 void Player::update(unsigned dt)
@@ -52,6 +39,7 @@ void Player::update(unsigned dt)
 	if(right != nullptr && (*right)()){
 		intent = RIGHT;
 	}
+	apply_movement();
 }
 
 void Player::render(unsigned dt, Camera& cam)
@@ -61,4 +49,4 @@ void Player::render(unsigned dt, Camera& cam)
 	put_rectangle({x-cam.get_x()+224, y-cam.get_y(),16,16}, Yellow);
 	if(x > 232)
 	put_rectangle({x-cam.get_x()-224, y-cam.get_y(),16,16}, Yellow);
-}
+}

+ 9 - 35
src/pacman/Player.h

@@ -3,45 +3,19 @@
 
 #include "input/Input.h"
 #include "render/Camera.h"
+#include "Entity.h"
 
 namespace Pacman
 {
-	class VButtonInput
-	{
+	class Player : public Entity {
 	public:
-		virtual bool operator()() = 0;
-		VButtonInput() {};
-		virtual ~VButtonInput() {};
-	};
-
-	class ButtonInput : public VButtonInput {
-		private:
-			WalrusRPG::Input::Key key;
-		public:
-		ButtonInput(WalrusRPG::Input::Key key);
-		~ButtonInput();
-		bool operator()() override;
-	};
-
-	enum MoveIntent{NONE, UP, DOWN, LEFT, RIGHT};
-
-	class Player
-	{
-	public:
-		int x;
-		int y;
-		int vx;
-		int vy;
-		MoveIntent intent;
-		VButtonInput *up;
-		VButtonInput *down;
-		VButtonInput *left;
-		VButtonInput *right;
-
-		Player(int x, int y);
+		Player(int x, int y, GameState &g, WalrusRPG::Input::Key kup, WalrusRPG::Input::Key kdown, WalrusRPG::Input::Key kleft, WalrusRPG::Input::Key kright);
 		~Player();
-		void render(unsigned dt, WalrusRPG::Camera& cam);
-		void update(unsigned dt);
+		virtual void render(unsigned dt, WalrusRPG::Camera& cam) override;
+		virtual void update(unsigned dt) override;
 	};
 }
-#endif
+
+#include "GameState.h"
+
+#endif