-- require "cards" -- the above is taken care of with loadscript in init ------------------------------------------------------------------------------------------------ -- General functions with no class or objects, includes global variables -- ------------------------------------------------------------------------------------------------ in_session = false player_lookup={} local white_deck local black_deck local function load_cards() local blk=loadtbl("black_deck") local wht=loadtbl("white_deck") loadscript("aethyn", "cards") black_deck = blk and blk or black_cards_deck white_deck = wht and wht or white_cards_deck end local function save_cards() if white_deck then savetbl("white_deck", white_deck) end if black_deck then savetbl("black_deck", black_deck) end end function init(char_name) -- checks that no game is in session then initializes a new Player object with the player's name in the variable if in_session then tell(char_name, "Game is already in session.") else gecho("{MA game of Cards Against Humanity has been started.{x") gecho("{MTo join : \ttell gamemaster join\t (or click the link){x") gecho("{MSee \thelp cards\t for more information.{x") load_cards() players = {} -- Set the number of points required to win a game. 5 is default. winning_points = 5 local plr=Player.new(char_name) player_lookup[char_name]=plr table.insert(players,plr) in_session = true active_players = 1 explain_rules(char_name) -- How long the gamemaster waits to start the game, in seconds. delay(45, start_game) end end function add_player(char_name) -- Adds a player to the game if not players then tell(char_name, "There is no game in session! Maybe you should start one?") return end for _,v in ipairs(players) do if v.name == char_name then tell(char_name, "You've already joined the game!") else local plr=Player.new(char_name) player_lookup[char_name] = plr table.insert(players,plr) active_players = active_players + 1 explain_rules(char_name) return end end end function start_game() -- Require at least 3 players to start a game. if #players > 2 then -- Deals a hand to every player say("dealt a hand") start_hand() else for _,v in ipairs(players) do tell(v.name, "Not enough players.") end -- Clear values so the next game starts OK in_session = false players = nil end end function explain_rules(char_name) tell(char_name, "Welcome to Cards Against Humanity.") tell(char_name, "For licensing and website information about the original game please check \thelp cards\t.") tell(char_name, "These are the available commands. They can be repeated by telling the Gamemaster \t'help'\t:") tell(char_name, "To show the white cards in your hand : tell Gamemaster \t'show'\t in it.") tell(char_name, "To play a card : tell Gamemaster 'play <#>' , where # is the number of the card in your hand.") tell(char_name, "To vote on a card : tell Gamemaster 'vote <#>' , where # is the number of the card that has been played.") tell(char_name, "You can also click the link of the card you want to choose if your MUD client supports MXP.") tell(char_name, "Enjoy the game!") end function check_player_num() for _,v in ipairs(players) do if not(getpc(v.name)) then missing_players = {} table.insert(missing_players, v.name) active_players = #players - #missing_players end end if active_players > 2 then start_hand() else for _,j in ipairs(players) do tell(j.name, "We're missing "..util.format_list("and", missing_players).."! We require three players to play, and we currently have "..active_players.."!") tell(j.name, "There will be a 30 second break for another player to join, or for "..util.format_list("and", missing_players).." to return.") delay(30, final_check) end end end function final_check() for k,v in ipairs(missing_players) do if getpc(v) then active_players = active_players + 1 table.remove(missing_players, k) end end if active_players < 3 then for _,v in ipairs(players) do tell(v.name, "Not enough players to continue. Ending game.") in_session = false players = {} end else tell(v.name, "We have enough players to continue.") start_hand() end end function start_hand() for _,v in ipairs(players) do v:deal_hand() v:display_hand() end -- Starts a hand by playing a black card local card_id = randnum(1,#black_deck) black_card_in_play = black_deck[card_id] -- tell everyone what the black card is for _,v in ipairs(players) do tell(v.name, "{x ") tell(v.name, "A hand has started. The black card in play is:") tell(v.name, "{D"..black_card_in_play.."{x") tell(v.name, "Send \"play <#>\" with the card number you wish to play or click the link.") end -- create table for player's white cards cards_in_play = {} -- remove the black card from the deck table.remove(black_deck, card_id) if #black_deck<1 then loadscript("aethyn", "cards") black_deck=black_cards_deck end delay(30,display_played) -- 30 seconds to play a card end -- play_card() function is of class Player, see there for function function display_played() -- Displays all the cards currently played, allows for a vote for _,v in ipairs(players) do tell(v.name, "The black card currently in play is:{D "..black_card_in_play.."{x") tell(v.name, "The white cards currently played are:") for i,j in ipairs(cards_in_play) do if string.find(black_card_in_play, "__________") then tell(v.name, "\t"..i.." \t"..string.gsub(black_card_in_play, "__________", "_{w"..j.text.."{T_")) else tell(v.name, "\t"..i.." \t"..black_card_in_play.." {w"..j.text.."{x") end end tell(v.name, "Send \"vote <#>\" with the card number you wish to vote on or click the link.") end votes = 0 delay(20,declare_winner) -- 20 seconds to vote on a card end -- vote_card() is a function of class Player function declare_winner() -- Declares a winner, then starts a new hand with start_hand, unless point total is met -- sort to get the winner of the hand, that card will move to front of table -- check to see if a card has actually been played if #cards_in_play > 0 then table.sort(cards_in_play, function(a,b) return a.votes>b.votes end) -- this goes here, not sure if it's the right way to do it ... the tie code seems to work, but might need more testing winners = {} for i=1,#cards_in_play do if cards_in_play[1].votes == cards_in_play[i].votes then player_lookup[cards_in_play[i].owner]:add_points() table.insert(winners, cards_in_play[i].owner) end end for _,v in ipairs(players) do if #winners > 1 then tell(v.name, "We have a tie at "..cards_in_play[1].votes.." votes! The winners are ") for _,win in pairs(winners) do for _,crd in pairs(cards_in_play) do if crd.owner==win then tell(v.name, win.." with "..crd.text) end end end else tell(v.name, cards_in_play[1].owner.." is the winner with "..util.pluralize(cards_in_play[1].votes, "vote").."!") tell(v.name, "Winning card: "..cards_in_play[1].text) end v.voted = false v.played = false player_lookup[v.name]:deal_hand() end -- check to see if game is ended, delay the output delay(2, display_points) delay(4, end_game) else -- End game if no one plays a card say("no cards played") for _,v in ipairs(players) do tell(v.name, "No cards were played. Ending game.") end players = nil in_session = false end -- Make sure we always have enough cards to play, "reshuffles" the deck --if #white_cards_deck < 50 or #black_cards_deck < 5 then --loadscript("aethyn","cards") --end end function display_points() -- Displays everyone's current points for _,v in ipairs(players) do tell(v.name, "The current scores are:") for _,j in ipairs(players) do tell(v.name, j.name.." with "..util.pluralize(j.points, "point")..".") --tell(v.name, j.name.." with "..j.points..".") -- if j.point == 1 then -- mdo("tell "..v.name.." "..j.name.." with "..j.points.." point.") -- else -- mdo("tell "..v.name.." "..j.name.." with "..j.points.." points.") -- end end end end -- Need a better way to end the game. in_session goes to false, but we should reset the tables too (at least players) -- Also, need a way to end a game without a winner, either as a request or something else (too many players leave?) function end_game() winner = false -- Checks if someone has enough points, then ends the game for _,v in ipairs(players) do if v.points == winning_points then -- Use says for now, will use tells later --say("Game is over. "..v.name.." is the winner!") winning_char = v.name winner = true in_session = false end end if not(winner) then check_player_num() else for _,v in ipairs(players) do tell(v.name, "The game is over. "..winning_char.." is the winner!") end mob:reward(getpc(winning_char), "qp", 5) players = nil end save_cards() end ------------------------------------------------------------------------------------------------ -- The player class, which allows us to use lua hacks to make object-like tables. -- -- Allows each player to be it's own object with attributes. -- ------------------------------------------------------------------------------------------------ Player = {} Player.__index = Player function Player.new(char_name) -- Initializes a player local self = setmetatable({}, Player) self.name = char_name self.cards = {} self.points = 0 self.voted = false self.played = false return self end function Player.show_instructions(self) tell(self.name, "These are the available commands. They can be repeated at anytime by send the command 'help':") tell(self.name, "To show what cards are in your hand, send a tell to the Gamemaster with 'show' in it.") tell(self.name, "To show what black and white cards are currently in play, send a tell to the Gamemaster with 'display' in it.") tell(self.name, "To play a card, send a tell to Gamemaster with 'play <#>' where # is the number of the corresponding card in your hand.") tell(self.name, "To vote on a card, send a tell to Gamemaster with 'vote <#>', where # is the number of the corresponding card that has been played.") tell(self.name, "You can also click the link of the card you want to choose if your MUD client supports MXP.") end function Player.deal_hand(self) -- deal cards for i=#self.cards+1,10 do local card_id = randnum(1, #white_deck) local card=Card.new(self.name, white_deck[card_id]) table.insert(self.cards, card) -- remove the white card from the deck table.remove(white_deck, card_id) if #white_deck<1 then loadscript("aethyn", "cards") white_deck=white_cards_deck end end end function Player.add_points(self) self.points = self.points + 1 end function Player.display_hand(self) tell(self.name, "Your cards:") for k,v in ipairs(self.cards) do tell(self.name, "\t"..k.." \t"..v.text) end end function Player.play_card(self, card_num) -- Plays a white card by the player. For now can only play one card, may need second card if we move on to -- dual card black cards if card_num < 1 or card_num > #self.cards then tell(self.name, "That isn't a valid card number.") else if self.played then tell(self.name, "You already played a card.") else tell(self.name, "You've successfully played card "..card_num) table.insert(cards_in_play, self.cards[card_num]) table.remove(self.cards, card_num) self.played = true end if #cards_in_play == #players then say("all players have played a card") cancel() -- cancel the timer that's running display_played() end end end function Player.vote_card(self, hand_num) -- Votes on a card if hand_num < 1 or hand_num > #cards_in_play then tell(self.name, "That isn't a valid card number.") else if self.voted then tell(self.name, "You already voted.") elseif self.name == cards_in_play[hand_num].owner then tell(self.name, "You can't vote on your own card!") else cards_in_play[hand_num]:add_vote() self.voted = true votes = votes + 1 tell(self.name, "Vote recorded.") if votes == #players then cancel() -- cancel the timer that's running declare_winner() end end end end ------------------------------------------------------------------------------------------------ -- The card class, which allows us to use lua hacks to make object-like tables. -- -- Each card will have name, id, owner, votes -- ------------------------------------------------------------------------------------------------ Card = {} Card.__index = Card function Card.new(char_name, card_text) -- Initializes a new card local self = setmetatable({}, Card) self.owner = char_name self.text = card_text self.votes = 0 return self end function Card.add_vote(self) self.votes = self.votes + 1 end