!! Inform Werewolf version 2 Copyright 1999 Evin Robertson. May be used, !! modified, and distributed without restriction. Absolutely no warranty of !! any type is provided for this game. !! !! For use with ifMUD's Floyd. requires multifloyd.h and array.h Constant Story "Werewolf"; Constant Headline "^An Interactive Lynching^Copyright 1999 Evin Robertson^"; Release 2; Replace SleepSub; Global IsNight = true; ! Start at night Global num_bots; Global need_sleep = true; ! require sleep? (prevents idletime cheating) Global protect_mode = 0; ! One of the following constants: Constant protect_any = 0; Constant protect_self = 1; Constant protect_other = 2; Include "Parser"; object multiobject with initial "^^^Inform implementation of werewolf using Floyd as the moderator by Evin Robertson. Uses the multifloyd library. This game may be played, modified, and distributed without restriction. Rules for this game are based on http://www.eblong.com/zarf/werewolf.html^", description [ x t; objectloop(x ofclass Role && x provides selecttext) { if(t) print ", "; t = 1; print (string) x.selecttext; } ": Set number of these^ PROTECT: Change who the wolfsbane carrier can protect^ SLEEP: Toggle requiring sleeping^ NIGHT: Toggle starting in day/starting in night^"; ], player_class Wereplayer, allow_join true, who_count [ c; print c; if(multiobject.allow_join && num_bots) print " + ", num_bots, "bots"; ], who_extra [ p; if((~~multiobject.allow_join) && (~~IsNight)) { if(p.voted_for >= 0) print "has ", p.voted_for, " vote", (cond_s) p.voted_for; else print "is protected from lynching"; print " and "; if(p.player_vote ~= nothing) print "votes to lynch ", (name) p.player_vote, ".^"; else print "hasn't yet voted.^"; } ], orders [ w; switch(w) { 'help': HelpSub(); rtrue; 'rules': RulesSub(); rtrue; } ]; Include "multifloyd"; Object LibraryMessages with before [; Prompt: rtrue; !! werewolf doesn't need a prompt ]; Include "Verblib"; Include "array"; !!!!!!!!!!!!!!! !! The roles !! !!!!!!!!!!!!!!! [ doreveal o; objectloop(o has ingame) if(o.player_type provides reveal) print (name) o, " ", (string) o.player_type.reveal, ".^"; ]; Class Role with name 'space monkey [BUG]', description "I like a space monkey. [BUG]", !! For help text plural "space monkeys [BUG]", number 0, !! How many of this role are in the game seeothers false, !! Does this class know all of its members? victim nothing, !! The proposed victim for a night of this role alignment Villager, !! On which side is this role? affects 0, !! What does this role protect from nocturnal 0; !! Which verb this role uses at night Role Werewolf "werewolf" with name 'werewolf' 'werewolves' 'wolf' 'wolves', plural "werewolves", reveal "was a werewolf", selecttext "WEREWOLF", description "At night, werewolves decide on a victim and one of them whispers KILL VICTIM to Floyd.", number 2, seeothers true, alignment Werewolf, nocturnal ##Kill ##Sleep; Role Seer "seer" with name 'seer' 'seers', plural "seers", reveal "was a seer", selecttext "SEER", description "The seer whispers IDENTIFY SUSPECT, trying to help the villagers convict the werewolves.", number 1, nocturnal ##Identify; Role Wolfsbane "wolfsbane carrier" with name 'wolfsbane' 'wolfsbanes' 'baner' 'baners' 'bane' 'banes', plural "wolfsbane carriers", reveal "carried wolfsbane", selecttext "WOLFSBANE", description "The wolfsbane carrier whispers PROTECT PERSON, which protects someone for a night from werewolf attack.", affects Werewolf, nocturnal ##Protect; Role Mason "Mason" with name 'mason' 'masons', plural "Masons", reveal "was a member of the Masons", selecttext "MASON", description "The Masons are a group of villagers who know each other to be innocent.", seeothers true, nocturnal ##Sleep; Role Devil "devil" with name 'devil' 'devils', plural "devils", reveal "was a devil", selecttext "DEVIL", description "The devil identifies like the seer, but is trying to help the werewolves and mafia. He doesn't count for daytime massacre calculation.", nocturnal ##Identify; Role Vigilante "vigilante" with name 'vigilante' 'vigilantes', plural "vigilantes", reveal "was a vigilante", selecttext "VIGILANTE", description "The vigilante is a villager who has the option of KILLing someone each night.", nocturnal ##Kill ##Sleep; Role Mafia "Mafia" with name 'mafia', plural "Mafia", reveal "was a Mafia member", selecttext "MAFIA", description "The Mafia KILL at night and are in competition with the werewolves in taking over the city.", seeothers true, alignment Mafia, nocturnal ##Kill ##Sleep; Role Archangel "archangel" with name 'archangel' 'archangels' 'arch' 'arches', plural "archangels", reveal "was an archangel", selecttext "ARCHANGEL", description "The archangel PROTECTs against mafia attack.", affects Mafia, nocturnal ##Protect; Role Magistrate "magistrate" with name 'magistrate' 'magistrates', plural "magistrates", reveal "was a magistrate", selecttext "MAGISTRATE", description "The magistrate may place someone in PROTECTive custody, and this person cannot be lynched the following day.", affects Villager, nocturnal ##Protect ##Sleep; Role Villager "villager" with name 'villager' 'villagers', plural "villagers", description "The villagers just SLEEP each night.", number 0, !! Will be calculated after everyone has joined nocturnal ##Sleep; [ rolename w x; w = NextWord(); objectloop(x ofclass Role && parray_contains(x, name, w)) return x; return -1; ]; !! Handle 'werewolf 3' commands Global ThisRole; [ rolecountSub; ; ]; Verb 'role#count' * number -> rolecount; [ UnknownVerb w x; objectloop(x ofclass Role && parray_contains(x, name, w)) { ThisRole = x; return 'role#count'; } ]; [ role_action o max n; if(~~max) max = o.#nocturnal / 2; for(n = 0: n < max: n++) { if(n) print " or "; switch(o.&nocturnal-->n) { ##Sleep: print "SLEEP"; ##Identify: print "IDENTIFY"; ##Kill: print "KILL"; ##Protect: print "PROTECT"; default: print "DANCE [BUG]"; } } ]; [ player_action o max; role_action(o.player_type, max); ]; !!!!!!!!!!!!!!!!!!!!!! !! Verb Definitions !! !!!!!!!!!!!!!!!!!!!!!! Include "Grammar"; [ HelpSub x; print "Based on rules available at http://www.eblong.com/zarf/werewolf.html. "; objectloop(x ofclass Role) print (string) x.description, " "; "When everyone has performed their task, the night ends. At sunrise, the death, if any, will be announced along with the type of the victim. Then the lynch mob votes on who to kill. When a majority of the players have voted for an individual, that person is killed, and their identity revealed. Then it goes back to night. This continues until the werewolves are either all killed or make up at least half the village. If killed, the villagers win. If the werewolves comprise half the village, then they can kill the villagers during the day, and win."; ]; Verb 'help' * -> Help; [ RulesSub x; objectloop(x ofclass Role) if(x.number) print (string) x.plural, ": ", x.number, "^"; print "sleep: ", (yesno) need_sleep, "^start night: ", (yesno) IsNight, "^protection: "; switch(protect_mode) { protect_any: print "any"; protect_self: print "self"; protect_other:print "others"; default: print "corn"; } ]; Verb 'rules' * -> Rules; [ setupact; if(~~multiobject.allow_join) { print "But the game has already begun!^"; rfalse; } woff(); !! All setup actions will be outloud rtrue; ]; [ BeginSub n x; if(~~setupact()) return; n = 0; objectloop(x ofclass Role) n = n + x.number; if(num_players + num_bots < n) "More special characters requested than there are players."; multiobject.allow_join = false; if(~~Startgame()) multiobject.allow_join = true; ]; Verb 'begin' * -> Begin; [ BotSub; if(~~setupact()) return; num_bots = noun; print_ret (name) actor, " requests ", num_bots, " bot", (cond_s) num_bots, "."; ]; Verb 'bot' * number -> Bot; [ requestSub; if(~~setupact()) return; if(~~(second provides selecttext)) { won(); "You can't adjust the number of those."; } second.number = noun; print_ret (name) actor, " requests ", second.number, " ", (smart_name) second, "."; ]; Verb 'request' * number rolename -> request; [ ProtectToggleSub; if(~~setupact()) return; if(++protect_mode > protect_other) protect_mode = 0; print (name) actor, " declares that the roles who PROTECT may protect "; switch(protect_mode) { protect_any: "anyone."; protect_self: "only themselves."; protect_other:"only others."; default: "corn husks [BUG]."; } ]; [ NightSub; if(~~setupact()) return; IsNight = ~~IsNight; print (name) actor, " declares the game will begin with "; if(IsNight) "night."; else "day."; ]; Verb 'night' * -> Night; [ SleepToggle; if(~~setupact()) return; need_sleep = ~~need_sleep; print (name) actor, " declares sleep is "; if(~~need_sleep) print "not "; "necessary."; ]; [ nightact f; if(multiobject.allow_join) { print "But the game hasn't yet begun!^"; rfalse; } if(~~IsNight) { print "Wait for night.^"; rfalse; } if(f) rtrue; if(~~parray_contains(actor.player_type, nocturnal, action)) { print "But you don't do that; you "; player_action(actor); print ".^"; rfalse; } rtrue; ]; [ SleepSub; if(multiobject.allow_join) return SleepToggle(); if(~~nightact()) return; if(actor.player_movedark) "You're already asleep. Wait 'till morning."; actor.player_movedark = true; switch(random(9)) { 1: print "You fall asleep, "; 2: print "You lie in bed, "; 3: print "You get into bed, "; 4: print "You sleep, "; 5: print "You collapse into bed, "; 6: print "You crawl into bed, "; 7: print "You toss and turn in bed, "; 8: "Now you sleep; if death before you wake,^one less lynching in which to partake."; 9: "You take two sleeping pills, and call it a day."; } switch(random(7)) { 1: "having locked your doors, knowing it won't help."; 2: "the cold deadly night outside."; 3: "fearing appliances lurking in the shadows."; 4: "the wind waking you every ten minutes."; 5: "hopefully not for the last time."; 6: "knowing tomorrow will be a long day of lynching."; 7: "the full moon throwing eerie shadows across your room."; } ]; Extend 'sleep' replace * -> Sleep; [ IdentifySub; if(~~nightact()) return; if(actor.player_movedark) "You've already identified tonight."; actor.player_movedark = true; print_ret (name) noun, " is a ", (name) noun.player_type, "."; ]; Verb 'identify' 'point' * creature -> Identify; [ KillSub o; if(~~nightact()) return; print "You decide to kill ", (name) noun, "^"; actor.player_type.victim = noun; objectloop(o has ingame) if(o.player_type == actor.player_type) { o.player_movedark = true; if(o ~= actor) { won(o); print (name) actor, " decides the ", (smart_name) actor.player_type, " will kill ", (name) noun, "^"; woff(); } } ]; Extend 'kill' replace * creature -> Kill; Verb 'waylay' * creature -> Kill; [ ProtectSub; if(~~nightact()) return; if(protect_mode == protect_self && noun ~= actor) "You can only protect yourself."; if(noun == actor && protect_mode == protect_other) "You must protect someone other than yourself."; actor.player_movedark = true; actor.protecting = noun; "You decide to protect ", (name) noun, " from the ", (smart_name) actor.player_type.affects, "."; ]; Verb 'protect' * -> ProtectToggle * creature -> Protect; [ RemindSub o; if(~~nightact(1)) return; woff(); objectloop(o has ingame && (~~o.player_movedark)) { print (won) o, "** You need to ", (player_action) o, " **"; woff(); } ]; Verb 'remind' * -> Remind; [ SignalSub o; if(~~nightact(1)) return; if(~~parray_contains_non(noun, nocturnal, ##Sleep)) "You can't signal a role who does nothing but sleep."; if(actor.player_signaled) "You've already sent or received a signal tonight."; print "You signal the ", (smart_name) noun, ".^"; actor.player_signaled = true; objectloop(o has ingame && o.player_type == noun && o ~= actor) { won(o); print (The) actor, " signals the ", (string) noun.plural, ".^"; noun.player_signaled = true; woff(); } ]; Verb 'signal' * rolename -> Signal; [ dayact; if(multiobject.allow_join) { print "But the game hasn't yet begun!^"; rfalse; } if(IsNight) { print "Wait for day.^"; rfalse; } rtrue; ]; [ VoteSub; if(~~dayact()) return; woff(); if(actor.player_vote == nothing) { print (name) actor, " votes to lynch "; } else { actor.player_vote.voted_for--; print (name) actor, " changes vote from ", (name) actor.player_vote, " to "; } print (name) noun, ".^"; noun.voted_for++; actor.player_vote = noun; ]; Verb 'vote' 'lynch' * creature -> Vote; [ UnVoteSub; if(~~dayact()) return; if(actor.player_vote == nothing) "But you haven't yet voted."; woff(); actor.player_vote.voted_for--; print (name) actor, " retracts a vote for ", (name) actor.player_vote, ".^"; actor.player_vote = nothing; ]; Verb 'unvote' 'retract' * -> Unvote * creature -> Unvote; !! This line for inky !!!!!!!!!!!!!!!!!! !! Player class !! !!!!!!!!!!!!!!!!!! Class Wereplayer(80) class GenericNewPlayer with description "A suspicious villager not unlike yourself.", player_type Villager, player_movedark false, ! The person has done something this night player_signaled false, ! Has sent or received a signal protecting 0, ! Who is being protected player_isbot false, last_vote nothing, ! Who the person voted for yesterday bot_heapstart, ! If a bot, start spot in heap of recent ID bot_heapitems, ! Number of items currently used in heap player_vote nothing, ! Who the person has voted to lynch voted_for 0, ! How many people voted for my lynching destroy [; if(~~multiobject.allow_join) self.player_type.number--; self.GenericNewPlayer::destroy(); ]; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! Stuff to handle state changes !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! [ InitNight o x; objectloop(x ofclass Role) x.victim = nothing; objectloop(o has InGame) { o.player_movedark = false; o.player_signaled = false; o.protecting = 0; } objectloop(o has InGame) if(o.player_isbot) BotNocturnal(o); ]; [ InitDay o; objectloop(o has InGame) { o.last_vote = o.player_vote; o.player_vote = nothing; o.voted_for = 0; if(o.protecting && o.player_type.affects == Villager) { print (name) o.protecting, " is protected from lynching today.^"; o.protecting.voted_for = -10000; } } objectloop(o has InGame) if(o.player_isbot) BotDiurnal(o); ]; [ CheckGameOver x; objectloop(x ofclass Role && x.alignment ~= Villager && parray_contains(x, nocturnal, ##Kill)) { if(x.number >= (num_players - x.number - Devil.number)) { print "there are as many ", (string) x.plural, " as villagers; they kill the remaining villager", (cond_s) (num_players - x.number - Devil.number), " during the day.^The ", (string) x.plural, " have won.^"; doreveal(); quit; } } if(Werewolf.number == 0 && Mafia.number == 0) { print "All the werewolves and Mafia have been killed.^The villagers win.^"; doreveal(); quit; } ]; Object gameroom with description "This is a boring room where you die and kill.", each_turn [ o f x; if(multiobject.allow_join) rtrue; woff(); if(IsNight) { IsNight = 0; objectloop(o has InGame) { if(~~o.player_movedark) { if(need_sleep || (parray_contains_non(o.player_type, nocturnal, ##Sleep))) { IsNight = 1; break; } } } if(~~IsNight) { f = false; objectloop(x ofclass Role && x.number && x.victim && x.victim has ingame) { objectloop(o has ingame && o.player_type.affects == x && o.protecting == x.victim) { x.victim = 0; break; } if(x.victim) { print (name) x.victim, ", a ", (name) x.victim.player_type, ", "; f = true; switch(random(12)) { 1: print "is dead."; 2: print "looks like the result of using a blender in entirely the wrong way."; 3: print "is found scattered around the village."; 4: print "is found as a heap of gnawed bones."; 5: print "grins up at you from a lifeless skull."; 6: print "wakes missing a few vital organs."; 7: print "mutters something about having been detected as a B4D MCH1N3 before collapsing into a pile of scrap metal."; 8: print "is conspicously missing from today's gathering."; 9: print "is discovered lounging by the town gates, behind the tavern, in the bushes, across the town square, and at home."; 10: print "is remembered at a special service held at daybreak."; 11: print "may still be with us in spirit, but not in flesh."; 12: print "doesn't wake up today."; } Wereplayer.destroy(x.victim); print "^"; } } if(~~f) print "No one died the previous night.^"; CheckGameOver(); print "It is now day. Time to VOTE on whom to lynch.^"; InitDay(); } } else { objectloop(o has InGame) { if(o.voted_for > (num_players / 2)) { print "A majority has voted to lynch ", (name) o, ", who is strung up and dies before sundown. ", (name) o, " turns out to have been a ", (name) o.player_type, ".^"; Wereplayer.destroy(o); IsNight = 1; break; } } if(IsNight) { CheckGameOver(); print "Night has fallen; time for some to sleep and others to kill.^"; InitNight(); } } ], has light; [ GameInitialise; location = gameroom; return(2); ]; Object dummy_room; [ select_players type num_left n x o; num_left = 0; objectloop(o in dummy_room) num_left++; if(type.number > num_left) "You finish drinking your ", type.number, " ", (name) type, " soup before it's poured ", num_left, " [BUG]"; for(x = 0: x < type.number: x++) { o = child(dummy_room); for(n = random(num_left) - 1: n: n--) o = sibling(o); o.player_type = type; remove o; num_left--; } ]; [ Startgame o x n; if(~~create_bots()) rfalse; objectloop(o has ingame) move o to dummy_room; n = num_players; objectloop(x ofclass Role) n = n - x.number; Villager.number = n; objectloop(x ofclass Role) select_players(x); objectloop(o has ingame) { print (won) o, "You are a ", (name) o.player_type; bot_remember(o, o); if(o.player_type.seeothers) { objectloop(x has ingame) if(x.player_type == o.player_type && x ~= o) { bot_remember(o, x); print " and so is ", (name) x; } } print "."; woff(); move o to gameroom; } print "A new game begins with ", num_players, " player", (cond_s) num_players, " composed of "; n = 0; objectloop(x ofclass Role) if(x.number) n++; objectloop(x ofclass Role) { if(x.number) { if(n == 1) print "and "; print x.number, " ", (smart_name) x; if(n > 1) print ", "; n--; } } print ". Now "; switch(IsNight) { 0: print "it is day; time to vote on whom to lynch.^"; InitDay(); 1: print "it is night; time for "; n = 0; objectloop(x ofclass Role && x.number && parray_contains_non(x, nocturnal, ##Sleep)) { if(n) print ", "; print "the ", (smart_name) x, " to "; role_action(x, 1); n = 1; } print " and the rest to SLEEP.^"; InitNight(); default: print "it is ALPACALAND. [BUG]^"; rfalse; } rtrue; ]; !!!!!!!!!!!!!! !! Bot code !! !!!!!!!!!!!!!! !! Array space for bots to keep track of who's on their side. ! Max of about 4000 objects, so we can use the top 4 bits as tag bits. Constant BotTags = $f000; Constant BotNoTags = $0fff; Constant BotFriend = $8000; !! Is the person our friend? Constant BotKILL = $4000; !! Does the person have KILL abilities? Constant BotIDENTIFY = $2000; !! Does the person have IDENTIFY abilites? Constant BotPROTECT = $1000; !! Does the person have PROTECT abilites? !! These constants are arranged so we can vote for the person which has !! the highest value - friends will have a negative value. Constant Heapspace = 2000; !! num_bots * Players must be less than this Array heap --> Heapspace; [ init_heap o n HeapPerBot; if(~~num_bots) return; HeapPerBot = Heapspace / num_bots; if(HeapPerBot < Num_Players) { print "Eeeagh - too many bots!^^"; rfalse; } n = 0; objectloop(o has ingame && o.player_isbot) { o.bot_heapstart = n; o.bot_heapitems = 0; n = n + HeapPerBot; } rtrue; ]; Array bot_name -> (PlayerNameMax + 3); [ create_bots n i x o; for(n = 0: n < num_bots: n++) { for(i = 0: i < PlayerNameMax: i++) bot_name->i = 0; @output_stream 3 bot_name; switch(n) { 0: print "wolfgang"; 1: print "lupy"; 2: print "vlad"; 3: print "harry"; 4: print "merlin"; 5: print "wilhelm"; 6: print "fido"; 7: print "oz"; 8: print "lycos"; 9: print "angua"; 10: print "carrie"; 11: print "silver"; 12: print "pluto"; 13: print "sirius"; 14: print "cudjo"; default: print "werebot", n - 14; } @output_stream -3; if((bot_name-->0) > PlayerNameMax) { print "Holy buffer overflow, batman! [BUG]^"; rfalse; } for(i = 0: i < PlayerNameMax: i++) text_buffer->i = (bot_name+2)->i; x = Wereplayer.create(); if(x) { move x to gameroom; x.player_isbot = true; } else { print "Sorry, too many bots!^"; objectloop(o ofclass Wereplayer && o.player_isbot) { o.player_isbot = false; o.destroy(); } rfalse; } } if(~~init_heap()) { objectloop(o ofclass Wereplayer && o.player_isbot) { o.player_isbot = false; o.destroy(); } rfalse; } rtrue; ]; [ bot_remember o x storeval; if(~~o.player_isbot) return; storeval = x; if(x.player_type.alignment == o.player_type.alignment) storeval = storeval | BotFriend; if(parray_contains(x.player_type, nocturnal, ##KILL)) storeval = storeval | BotKILL; if(parray_contains(x.player_type, nocturnal, ##IDENTIFY)) storeval = storeval | BotIDENTIFY; if(parray_contains(x.player_type, nocturnal, ##PROTECT)) storeval = storeval | BotPROTECT; heap-->(o.bot_heapstart + o.bot_heapitems) = storeval; o.bot_heapitems++; ]; [ BotFindPerson o favor isnot x n min max i j; objectloop(x has ingame) give x ~general; n = 0; min = 0; max = 0; for(i = 0: i < o.bot_heapitems: i++) { x = heap-->(o.bot_heapstart + i); j = x & BotNoTags; if(j has ingame) { give j general; if(j ~= isnot) { if(x & BotTags >= max) max = x; if(x & BotTags < min) min = x; } n++; } } if(favor < 0 && min) return min & BotNoTags; if(favor > 0 && max) return max & BotNoTags; if(favor ~= 1 or 0 or -1) return 0; n = random(Num_Players - n); !! Choose somone we don't know about objectloop(x has ingame && x hasnt general) if(~~--n) return x; print "Too much monkey business [BUG]^"; return o; ]; [ BotNocturnal o x; ! action = parray_random(o.player_type, nocturnal); action = o.player_type.&nocturnal-->0; actor = o; won(o); switch(action) { ##Sleep: indirect(#actions_table-->action); ##Identify: noun = BotFindPerson(o, 0); indirect(#actions_table-->action); bot_remember(o, noun); ##Kill: objectloop(x has ingame) if(x.player_type == o.player_type && (~~x.player_isbot)) return; noun = BotFindPerson(o, 1); indirect(#actions_table-->action); ##Protect: switch(protect_mode) { protect_any: noun = BotFindPerson(o, -1); protect_self: noun = o; protect_other: noun = BotFindPerson(o, -1, o); } indirect(#actions_table-->action); default: print "Bot does the funky monkey dance! [BUG]^"; } woff(); ]; [ BotDiurnal o; noun = BotFindPerson(o, 2); if((~~noun) && o.last_vote && o.last_vote has ingame && random(3) ~= 1) noun = o.last_vote; else noun = BotFindPerson(o, 1); action = ##Vote; actor = o; indirect(#actions_table-->action); ]; !!!!!!!!!!!!!!! !! Misc Junk !! !!!!!!!!!!!!!!! [ smart_name r; if(r.number == 1) print (name) r; else print (string) r.plural; ]; [ cond_s n; if(n ~= 1) print "s"; ]; [ yesno t; if(t) print "yes"; else print "no"; ];