diff options
-rwxr-xr-x | gamestate.lua | 170 | ||||
-rw-r--r-- | ghost.lua | 212 | ||||
-rw-r--r-- | init.lua | 141 |
3 files changed, 265 insertions, 258 deletions
diff --git a/gamestate.lua b/gamestate.lua new file mode 100755 index 0000000..683ce3f --- /dev/null +++ b/gamestate.lua @@ -0,0 +1,170 @@ + +-- Array to hold all the running game states +mypacman.games = {} + +--------------------------------------------------------- +-- Public functions (these can be called from any other place) + +-- Start the game from the spawn block at position "pos" activated by "player" +function mypacman.game_start(pos, player) + -- create an id unique for the given position + local id = minetest.pos_to_string(pos) + + -- make sure any previous game with the same id has ended + if mypacman.games[id] then + mypacman.game_end(id) + end + + -- Create a new game state with that id and add it to the game list + local gamestate = { + id = id, + player_name = player:get_player_name(), + pos = pos, + start = {x=pos.x+14,y=pos.y+0.5,z=pos.z+16}, + pellet_count = 0, + level = 1, + speed = 2, + lives = 3, + } + mypacman.games[id] = gamestate + minetest.log("action","New pacman game started at " .. id .. " by " .. gamestate.player_name) + + -- place schematic + local schem = minetest.get_modpath("mypacman").."/schems/mypacman_3.mts" + minetest.place_schematic({x=pos.x,y=pos.y-1,z=pos.z-2},schem,0, "air", true) + + -- Set start positions + mypacman.game_reset(id, player) + minetest.sound_play("mypacman_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) +end + +-- Finish the game with the given id +function mypacman.game_end(id) + mypacman.remove_ghosts(id) + -- Clear the data + mypacman.games[id] = nil +end + +-- Resets the game to the start positions +function mypacman.game_reset(id, player) + local gamestate = mypacman.games[id] + minetest.log("action", "resetting game " .. id) + + -- Position the player + local player = player or minetest.get_player_by_name(gamestate.player_name) + player:setpos(gamestate.start) + + -- Spawn the ghosts and assign the game id to each ghost + minetest.after(2, function() + local pos = vector.add(gamestate.pos, {x=13,y=0.5,z=19}) + local ghost = minetest.add_entity(pos, "mypacman:inky") + ghost:get_luaentity().gameid = id + end) + minetest.after(12, function() + local pos = vector.add(gamestate.pos, {x=15,y=0.5,z=19}) + local ghost = minetest.add_entity(pos, "mypacman:pinky") + ghost:get_luaentity().gameid = id + end) + minetest.after(22, function() + local pos = vector.add(gamestate.pos, {x=13,y=0.5,z=18}) + local ghost = minetest.add_entity(pos, "mypacman:blinky") + ghost:get_luaentity().gameid = id + end) + minetest.after(32, function() + local pos = vector.add(gamestate.pos, {x=15,y=0.5,z=18}) + local ghost = minetest.add_entity(pos, "mypacman:clyde") + ghost:get_luaentity().gameid = id + end) +end + +-- Remove all the ghosts from the board with the given id +function mypacman.remove_ghosts(id) + local gamestate = mypacman.games[id] + if not gamestate then return end + + -- Remove all non-players (ghosts!) + local boardcenter = vector.add(gamestate.pos, {x=13,y=0.5,z=15}) + for index, object in ipairs(minetest.get_objects_inside_radius(boardcenter,20)) do + if object:is_player() ~= true then + object:remove() + end + end +end + +-- A player got a pellet, update the state +function mypacman.on_player_got_pellet(player) + local name = player:get_player_name() + local gamestate = mypacman.get_game_by_player(name) + if not gamestate then return end + + gamestate.pellet_count = gamestate.pellet_count + 1 + if gamestate.pellet_count >= 20 then -- 252 + minetest.chat_send_player(name, "You cleared the board!") + + mypacman.remove_ghosts(gamestate.id) + gamestate.pellet_count = 0 + gamestate.level = gamestate.level + 1 + gamestate.speed = gamestate.level + 1 + + minetest.after(3.0, function() + minetest.chat_send_player(name, "Starting Level "..gamestate.level) + -- place schematic + local schem = minetest.get_modpath("mypacman").."/schems/mypacman_3.mts" + minetest.place_schematic(vector.add(gamestate.pos, {x=0,y=-1,z=-2}),schem,0, "air", true) + + -- Set start positions + mypacman.game_reset(gamestate.id, player) + minetest.sound_play("mypacman_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) + end) + end + +end + +-- Get the game that the given player is playing +function mypacman.get_game_by_player(player_name) + for _,gamestate in pairs(mypacman.games) do + if gamestate.player_name == player_name then + return gamestate + end + end +end + +--------------------------------------------------------- +--- Private functions (only can be used inside this file) + +-- Save Table +local function gamestate_save() + local data = mypacman.games + local f, err = io.open(minetest.get_worldpath().."/mypacman_data", "w") + if err then return err end + f:write(minetest.serialize(data)) + f:close() +end + +--Read Table +local function gamestate_load() + local f, err = io.open(minetest.get_worldpath().."/mypacman_data", "r") + if f then + local data = minetest.deserialize(f:read("*a")) + f:close() + return data + else + return nil + end +end + +------------------- +--- Execution code + +-- load the gamestate from disk +mypacman.games = gamestate_load() or {} + +local tmr = 0 +--Save Table every 10 seconds +minetest.register_globalstep(function(dtime) + tmr = tmr + dtime; + if tmr >= 10 then + tmr = 0 + gamestate_save() + end +end) @@ -1,24 +1,3 @@ ---The code used for the ghosts was made by Tenplus1 - -local myghosts = {} -local deathcount = 0 -local gravity = -10 - -clear_ghosts = function() - local pos = mypacman.start - - for index, object in ipairs(minetest.get_objects_inside_radius({x=pos.x+13,y=pos.y+0.5,z=pos.z+15},20)) do - --local obj = object:get_luaentity() - if object:is_player() ~= true then - object:moveto({x=pos.x+13,y=pos.y+0.5,z=pos.z+19}) - end - end - --minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+19}, "mypacman:inky") - --minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+19}, "mypacman:pinky") - --minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+18}, "mypacman:blinky") - --minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+18}, "mypacman:clyde") - -end local ghosts = { {"pinky","Pinky"}, @@ -30,122 +9,97 @@ for i in ipairs(ghosts) do local itm = ghosts[i][1] local desc = ghosts[i][2] -minetest.register_entity("mypacman:"..itm, { - - hp_max = 1, - physical = true, - collide_with_objects = true, - visual = "cube", - visual_size = {x = 0.6, y = 1}, - textures = - {"mypacman_"..itm.."s.png", - "mypacman_"..itm.."s.png", - "mypacman_"..itm.."s.png", - "mypacman_"..itm.."s.png", - "mypacman_"..itm.."f.png", - "mypacman_"..itm.."s.png", + minetest.register_entity("mypacman:"..itm, { + hp_max = 1, + physical = true, + collide_with_objects = true, + visual = "cube", + visual_size = {x = 0.6, y = 1}, + textures = { + "mypacman_"..itm.."s.png", + "mypacman_"..itm.."s.png", + "mypacman_"..itm.."s.png", + "mypacman_"..itm.."s.png", + "mypacman_"..itm.."f.png", + "mypacman_"..itm.."s.png", }, - velocity = {x=math.random(-1,1), y=0, z=math.random(-1,1)}, - collisionbox = {-0.25, -1.0, -0.25, 0.25, 0.48, 0.25}, - weight = 5, -- ?? - is_visible = true, - automatic_rotate = true, - automatic_face_movement_dir = -90, -- set yaw direction in degrees, false to disable - stepheight = 1.1, - makes_footstep_sound = false, - floats = 1, - view_range = 40, - speed = mypacman.spd, - jump_height = 0, + groups = {immortal = 1}, + velocity = {x=math.random(-1,1), y=0, z=math.random(-1,1)}, + collisionbox = {-0.25, -1.0, -0.25, 0.25, 0.48, 0.25}, + is_visible = true, + automatic_rotate = true, + automatic_face_movement_dir = -90, -- set yaw direction in degrees, false to disable + makes_footstep_sound = false, - set_velocity = function(self, v) - if not v then v = 0 end - local yaw = self.object:getyaw() - self.object:setvelocity({x=math.sin(yaw) * -v, y=self.object:getvelocity().y, z=math.cos(yaw) * v}) - end, + set_velocity = function(self, v) + if not v then v = 0 end + local yaw = self.object:getyaw() + self.object:setvelocity({x=math.sin(yaw) * -v, y=self.object:getvelocity().y, z=math.cos(yaw) * v}) + end, - on_step = function(self, dtime) - - -- every 1 second - self.timer = (self.timer or 0) + dtime - if self.timer < 1 then return end - self.timer = 0 + on_step = function(self, dtime) + -- every 1 second + self.timer = (self.timer or 0) + dtime + if self.timer < 1 then return end + self.timer = 0 - -- make sure object floats (or not) when in water - if minetest.get_item_group(minetest.get_node(self.object:getpos()).name, "water") ~= 0 then - if self.floats == 1 then - self.object:setacceleration({x = self.object:getacceleration().x, y = 1.5, z = self.object:getacceleration().z}) - end - else - self.object:setacceleration({x = self.object:getacceleration().x, y = gravity, z = self.object:getacceleration().z}) - end + -- Do we have game state? if not just die + local gamestate = mypacman.games[self.gameid] + if not gamestate then + minetest.log("action", "Removing pacman ghost from finished game " .. (self.gameid or "")) + self.object:remove() + return + end - local s, p, dist, nod - -- find player to follow - for _,player in pairs(minetest.get_connected_players()) do - s = self.object:getpos() - p = player:getpos() + -- Make sure we have a targetted player + if not self.target then + self.target = minetest.get_player_by_name(gamestate.player_name) + end + local player = self.target - -- find distance - dist = ((p.x-s.x)^2 + (p.y-s.y)^2 + (p.z-s.z)^2)^0.5 - if dist < self.view_range then - local vec = {x=p.x-s.x, y=p.y-s.y, z=p.z-s.z} - local yaw = (math.atan(vec.z/vec.x)+math.pi/2) - if p.x > s.x then - yaw = yaw + math.pi - end - -- face player - self.object:setyaw(yaw) + local s = self.object:getpos() -- ghost + local p = player:getpos() -- player - -- find direction and show node facing - self.direction = {x = math.sin(yaw)*-1, y = 0, z = math.cos(yaw)} - nod = minetest.get_node_or_nil({x=s.x + self.direction.x,y=s.y+1,z=s.z + self.direction.z}) - - -- more than 2 nodes ahead then follow, otherwise stop - if dist > 0 then - if self.jump_height > 0 and self.object:getvelocity().y == 0 then - local v = self.object:getvelocity() - v.y = self.jump_height - self.object:setvelocity(v) - end - - self.set_velocity(self, self.speed) - else - self.set_velocity(self, 0) - end + -- find distance from ghost to player + local distance = ((p.x-s.x)^2 + (p.y-s.y)^2 + (p.z-s.z)^2)^0.5 + if distance < 1.5 then + -- player is so close it got catched!! + gamestate.lives = gamestate.lives - 1 + if gamestate.lives < 1 then + minetest.chat_send_player(gamestate.player_name,"Game Over") + player:moveto(vector.add(gamestate.pos,{x=0.5,y=0.5,z=-1.5})) + mypacman.game_end(self.gameid) + elseif gamestate.lives == 1 then + minetest.chat_send_player(gamestate.player_name,"This is your last life") + mypacman.game_reset(self.gameid, player) + else + minetest.chat_send_player(gamestate.player_name,"You have ".. gamestate.lives .." lives left") + mypacman.game_reset(self.gameid, player) + end - -- break look after player found - break - end - end - - -- if touches player then death - local s = self.object:getpos() - local obs = {} - for _,oir in ipairs(minetest.get_objects_inside_radius(s, 1.5)) do - local obj = oir:get_luaentity() - if oir:is_player() then - local player = oir - local pos = mypacman.start - local name = player:get_player_name() - if deathcount == 0 then - player:moveto({x=pos.x+13,y=pos.y+0.5,z=pos.z+15.5}) - minetest.chat_send_player(name,"You have 1 more life after this") - deathcount = 1 - clear_ghosts() - elseif deathcount == 1 then - player:moveto({x=pos.x+13,y=pos.y+0.5,z=pos.z+15.5}) - minetest.chat_send_player(name,"This is your last life") - deathcount = 2 - clear_ghosts() - elseif deathcount == 2 then - player:moveto({x=pos.x+0.5,y=pos.y+0.5,z=pos.z-1.5}) - minetest.chat_send_player(name,"Game Over") - deathcount = 0 - end - end + else + local vec = {x=p.x-s.x, y=p.y-s.y, z=p.z-s.z} + local yaw = (math.atan(vec.z/vec.x)+math.pi/2) + if p.x > s.x then + yaw = yaw + math.pi end - end, - }) + -- face player and move towards him + self.object:setyaw(yaw) + self.set_velocity(self, gamestate.speed) + end + end, + + -- This function should return the saved state of the entity in a string + get_staticdata = function(self) + return self.gameid or "" + end, + + -- This function should load the saved state of the entity from a string + on_activate = function(self, staticdata) + if staticdata and staticdata ~= "" then + self.gameid = staticdata + end + end + }) end @@ -1,71 +1,14 @@ ---Save Table -function save_table() - local data = mypacman - local f, err = io.open(minetest.get_worldpath().."/mypacman_data", "w") - if err then return err end - f:write(minetest.serialize(data)) - f:close() -end ---Read Table -local function read_table() - local f, err = io.open(minetest.get_worldpath().."/mypacman_data", "r") - local data = minetest.deserialize(f:read("*a")) - f:close() - return data -end -local tmr = 0 ---Save Table every 10 seconds -minetest.register_globalstep(function(dtime) - tmr = tmr + dtime; - if tmr >= 10 then - tmr = 0 - save_table() - end -end) ---removes the ghosts from the game -local function remove_ghosts() - local pos = mypacman.start - for index, object in ipairs(minetest.get_objects_inside_radius({x=pos.x+13,y=pos.y+0.5,z=pos.z+15},20)) do - if object:is_player() ~= true then - object:remove() - end - end -end +-- This variable will be exported to other mods when they "depend" on this mod +mypacman = {} -local function spawn_ghosts() - local pos = mypacman.start - minetest.after(2, function() - minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+19}, "mypacman:inky") - end) - minetest.after(12, function() - minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+19}, "mypacman:pinky") - end) - minetest.after(22, function() - minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+18}, "mypacman:blinky") - end) - minetest.after(32, function() - minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+18}, "mypacman:clyde") - end) -end ---Check to see if table exists. Need to see if there is a better way -local f, err = io.open(minetest.get_worldpath().."/mypacman_data", "r") -if f == nil then -mypacman = {} -mypacman.start = {} -mypacman.pellet_count = 0 -mypacman.level = 1 -mypacman.spd = 2 -mypacman.board_num = 1 -else -mypacman = read_table().start -mypacman.start = read_table().start -mypacman.pellet_count = read_table().pellet_count -mypacman.level = read_table().level -mypacman.spd = read_table().spd -mypacman.board_num = read_table().board_num -end +dofile(minetest.get_modpath("mypacman").."/craftitems.lua") +dofile(minetest.get_modpath("mypacman").."/ghost.lua") +dofile(minetest.get_modpath("mypacman").."/blocks.lua") +dofile(minetest.get_modpath("mypacman").."/portals.lua") +dofile(minetest.get_modpath("mypacman").."/gamestate.lua") + --Yellow Pellets minetest.register_node("mypacman:pellet_1", { @@ -92,25 +35,7 @@ minetest.register_node("mypacman:pellet_1", { }) end, after_dig_node = function(pos, oldnode, oldmetadata, digger) - local name = digger:get_player_name() - local pos = mypacman.start - local schem = minetest.get_modpath("mypacman").."/schems/mypacman_3.mts" - mypacman.pellet_count = mypacman.pellet_count + 1 - if mypacman.pellet_count >= 1 then --252 - remove_ghosts() - minetest.chat_send_player(name, "You cleared the board!") - mypacman.pellet_count = 0 - mypacman.level = mypacman.level + 1 - mypacman.spd = mypacman.level + 1 - minetest.after(3.0, function() - local pos = mypacman.start - digger:setpos({x=pos.x+13,y=pos.y+0.5,z=pos.z+15.5}) - minetest.chat_send_player(name, "Starting Level "..mypacman.level) - minetest.sound_play("mypacman_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) - spawn_ghosts() - end) - minetest.place_schematic({x=pos.x,y=pos.y-1,z=pos.z-2},schem,0, "air", true) - end + mypacman.on_player_got_pellet(digger) end, }) @@ -165,49 +90,7 @@ minetest.register_node("mypacman:block2",{ paramtype2 = "facedir", light_source = 8, groups = {cracky = 1}, - - -on_rightclick = function(pos, node, player, itemstack, pointed_thing) -local schem = minetest.get_modpath("mypacman").."/schems/mypacman_3.mts" - minetest.place_schematic({x=pos.x,y=pos.y-1,z=pos.z-2},schem,0, "air", true) - mypacman.start = {x=pos.x, y=pos.y, z=pos.z} - mypacman.pellet_count = 0 - mypacman.level = 1 - mypacman.spd = 2 - remove_ghosts() - player:setpos({x=pos.x+14,y=pos.y+0.5,z=pos.z+16}) - mypacman.pellet_count = 0 - if mypacman.pellet_count >= 252 then - remove_ghosts() - end - minetest.sound_play("mypacman_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) - - minetest.after(2, function() - minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+19}, "mypacman:inky") - end) - minetest.after(12, function() - minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+19}, "mypacman:pinky") - end) - minetest.after(22, function() - minetest.add_entity({x=pos.x+13,y=pos.y+0.5,z=pos.z+18}, "mypacman:blinky") - end) - minetest.after(32, function() - minetest.add_entity({x=pos.x+15,y=pos.y+0.5,z=pos.z+18}, "mypacman:clyde") - end) -end, + on_rightclick = function(pos, node, player, itemstack, pointed_thing) + mypacman.game_start(pos, player) + end, }) - - -dofile(minetest.get_modpath("mypacman").."/craftitems.lua") -dofile(minetest.get_modpath("mypacman").."/ghost.lua") -dofile(minetest.get_modpath("mypacman").."/blocks.lua") -dofile(minetest.get_modpath("mypacman").."/portals.lua") - - - - - - - - - |