diff options
Diffstat (limited to 'pacmine/gamestate.lua')
-rwxr-xr-x | pacmine/gamestate.lua | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/pacmine/gamestate.lua b/pacmine/gamestate.lua new file mode 100755 index 0000000..76c8994 --- /dev/null +++ b/pacmine/gamestate.lua @@ -0,0 +1,296 @@ + +-- Array to hold all the running game states +pacmine.games = {} + +-- Store all the currently playing players +pacmine.players = {} + +-- Duration of the power pellet effect (in seconds) +local power_pellet_duration = 10 + +--------------------------------------------------------- +-- Public functions (these can be called from any other place) + +-- Start the game from the spawn block at position "pos" activated by "player" +function pacmine.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 pacmine.games[id] then + pacmine.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, + score = 0, + } + pacmine.games[id] = gamestate + pacmine.players[id] = player + + minetest.log("action","New pacman game started at " .. id .. " by " .. gamestate.player_name) + + -- place schematic + local schem = minetest.get_modpath("pacmine").."/schems/pacmine_3.mts" + minetest.place_schematic({x=pos.x,y=pos.y-1,z=pos.z-2},schem,0, "air", true) + + -- Set start positions + pacmine.game_reset(id, player) + pacmine.update_hud(id, player) + minetest.sound_play("pacmine_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) +end + +-- Finish the game with the given id +function pacmine.game_end(id) + pacmine.remove_ghosts(id) + pacmine.remove_hud(pacmine.players[id], pacmine.games[id].player_name) + -- Clear the data + pacmine.games[id] = nil + pacmine.players[id] = nil +end + +-- Resets the game to the start positions +function pacmine.game_reset(id, player) + local gamestate = pacmine.games[id] + minetest.log("action", "resetting game " .. id) + + -- Save the time when the game was last resetted (to solve timing issues) + local last_reset = os.time() + + gamestate.power_pellet = false + gamestate.last_reset = last_reset + + -- 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() + if pacmine.games[id] and last_reset == pacmine.games[id].last_reset then + local pos = vector.add(gamestate.pos, {x=13,y=0.5,z=19}) + local ghost = minetest.add_entity(pos, "pacmine:inky") + ghost:get_luaentity().gameid = id + end + end) + minetest.after(12, function() + if pacmine.games[id] and last_reset == pacmine.games[id].last_reset then + local pos = vector.add(gamestate.pos, {x=15,y=0.5,z=19}) + local ghost = minetest.add_entity(pos, "pacmine:pinky") + ghost:get_luaentity().gameid = id + end + end) + minetest.after(22, function() + if pacmine.games[id] and last_reset == pacmine.games[id].last_reset then + local pos = vector.add(gamestate.pos, {x=13,y=0.5,z=18}) + local ghost = minetest.add_entity(pos, "pacmine:blinky") + ghost:get_luaentity().gameid = id + end + end) + minetest.after(32, function() + if pacmine.games[id] and last_reset == pacmine.games[id].last_reset then + local pos = vector.add(gamestate.pos, {x=15,y=0.5,z=18}) + local ghost = minetest.add_entity(pos, "pacmine:clyde") + ghost:get_luaentity().gameid = id + end + end) +end + +-- Remove all the ghosts from the board with the given id +function pacmine.remove_ghosts(id) + local gamestate = pacmine.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 pacmine.on_player_got_pellet(player) + local name = player:get_player_name() + local gamestate = pacmine.get_game_by_player(name) + if not gamestate then return end + + gamestate.pellet_count = gamestate.pellet_count + 1 + gamestate.score = gamestate.score + 10 + pacmine.update_hud(gamestate.id, player) + + if gamestate.pellet_count >= 252 then -- 252 + minetest.chat_send_player(name, "You cleared the board!") + + pacmine.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("pacmine").."/schems/pacmine_3.mts" + minetest.place_schematic(vector.add(gamestate.pos, {x=0,y=-1,z=-2}),schem,0, "air", true) + + -- Set start positions + pacmine.game_reset(gamestate.id, player) + minetest.sound_play("pacmine_beginning", {pos = pos,max_hear_distance = 40,gain = 10.0,}) + end) + end +end + +-- A player got a power pellet, update the state +function pacmine.on_player_got_power_pellet(player) + local name = player:get_player_name() + local gamestate = pacmine.get_game_by_player(name) + if not gamestate then return end + + minetest.chat_send_player(name, "You got a POWER PELLET") + gamestate.power_pellet = os.time() + power_pellet_duration + gamestate.score = gamestate.score + 50 + pacmine.update_hud(gamestate.id, player) + + local boardcenter = vector.add(gamestate.pos, {x=13,y=0.5,z=15}) + local powersound = minetest.sound_play("pacmine_powerup", {pos = boardcenter,max_hear_distance = 20, object=player, loop=true}) + + minetest.after(power_pellet_duration, function() + minetest.sound_stop(powersound) + if os.time() >= (gamestate.power_pellet or 0) then + gamestate.power_pellet = false + minetest.chat_send_player(name, "POWER PELLET wore off") + end + end) +end + +-- Get the game that the given player is playing +function pacmine.get_game_by_player(player_name) + for _,gamestate in pairs(pacmine.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 = pacmine.games + local f, err = io.open(minetest.get_worldpath().."/pacmine_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().."/pacmine_data", "r") + if f then + local data = minetest.deserialize(f:read("*a")) + f:close() + return data + else + return nil + end +end + +-- Called every 0.5 seconds for each player that is currently playing pacman +local function on_player_gamestep(player, gameid) + local player_pos = player:getpos() + local positions = { + {x=0.5,y=0.5,z=0.5}, + {x=-0.5,y=0.5,z=-0.5}, + } + for _,pos in pairs(positions) do + pos = vector.add(player_pos, pos) + local node = minetest.get_node(pos) + if node.name == "pacmine:pellet_1" then + minetest.remove_node(pos) + pacmine.on_player_got_pellet(player) + elseif node.name == "pacmine:pellet_2" then + minetest.remove_node(pos) + pacmine.on_player_got_power_pellet(player) + + minetest.sound_play("pacmine_eatfruit", { + pos = pos, + max_hear_distance = 100, + gain = 10.0, + }) + end + end +end + +------------------- +--- Execution code + +-- load the gamestate from disk +pacmine.games = gamestate_load() or {} + +-- Time counters +local tmr_gamestep = 0 +local tmr_savestep = 0 +minetest.register_globalstep(function(dtime) + tmr_gamestep = tmr_gamestep + dtime; + if tmr_gamestep > 0.2 then + for id,player in pairs(pacmine.players) do + on_player_gamestep(player, id) + end + tmr_savestep = tmr_savestep + tmr_gamestep + if tmr_savestep > 10 then + gamestate_save() + tmr_savestep = 0 + end + tmr_gamestep = 0 + end +end) + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + for id,game in pairs(pacmine.games) do + if game.player_name == name then + pacmine.players[id] = player + pacmine.update_hud(id, player) + end + end +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + for id,game in pairs(pacmine.games) do + if game.player_name == name then + pacmine.players[id] = nil + pacmine.remove_hud(player, name) + end + end +end) + +-- Chatcommand to end the game for the current player +minetest.register_chatcommand("pacmine_exit", { + params = "", + description = "Loads and saves all rooms", + func = function(name, param) + local gamestate = pacmine.get_game_by_player(name) + if gamestate then + pacmine.game_end(gamestate.id) + minetest.get_player_by_name(name):moveto(vector.add(gamestate.pos,{x=0.5,y=0.5,z=-1.5})) + minetest.chat_send_player(name, "You are no longer playing pacman") + else + minetest.chat_send_player(name, "You are not currently in a pacman game") + end + end +}) + +minetest.register_on_shutdown(function() + minetest.log("action", "Server shuts down. Saving pacman data") + gamestate_save() +end) |