diff --git a/botconfig.example.yml b/botconfig.example.yml
index 423755e6..0a52ac60 100644
--- a/botconfig.example.yml
+++ b/botconfig.example.yml
@@ -164,6 +164,38 @@ gameplay:
notice: true
# Include which user is spectating in the above alert, if enabled.
include_user: false
+ # You can adjust how likely gamemodes are to appear by default. Setting a mode to 0 will require that players
+ # select the mode with a majority vote before the game starts, ensuring it will never randomly appear.
+ # If you have a lot of new players, you can uncomment the following to disable the hardest modes by default
+ # (e.g. modes with special mechanics that could confuse new players) while making simpler modes far more likely.
+# modes:
+# # Very simple modes with little complexity
+# default: 100
+# classic: 100
+# # Simple modes that introduce few twists, primarily team switching or win-stealing neutral roles
+# alpha: 50
+# charming: 50
+# foolish: 50
+# lycan: 50
+# # Moderate modes that have more complex roles or introduce minor mechanical changes, such as totem or gun chances
+# aleatoire: 10
+# drunkfire: 10
+# kaboom: 10
+# mad: 10
+# masquerade: 10
+# noreveal: 10
+# random: 10
+# # Difficult modes that feature more complex mechanical changes, such as modifying default win conditions
+# evilvillage: 6
+# guardian: 6
+# mudkip: 6
+# rapidfire: 6
+# valentines: 6
+# # Very difficult modes that feature significant mechanical changes
+# boreal: 0
+# pactbreaker: 0
+# maelstrom: 0
+# sleepy: 0
# Reaper. Users that leave during the game or are otherwise idle will be automatically removed from the game
# after a period of time (by killing them in-game). This allows a game to continue onwards with the active players
diff --git a/gendoc.py b/gendoc.py
index d239bd90..c07d411d 100644
--- a/gendoc.py
+++ b/gendoc.py
@@ -51,6 +51,9 @@ def add_values(section, indent, path):
section = t
t = section["_type"]
+ if isinstance(t, list):
+ t = set(t)
+
if "_name" in section and indent > 2:
return f"Instance of [[#{section['_name']}|{section['_name']}]].\n"
@@ -63,7 +66,7 @@ def add_values(section, indent, path):
default = f'"{default}"
'
elif t == "bool":
default = "true
" if default else "false
"
- elif t in ("int", "float", "enum"):
+ elif t in ("int", "float", "enum") or (isinstance(t, set) and t & {"int", "float", "enum"} == t):
default = f'{default}
'
elif t == "list":
if not default:
@@ -107,8 +110,6 @@ def yaml_dump(obj):
return f"\n{dump}\n"
def generate_config_page():
- file = Path(__file__).parent / "src" / "defaultsettings.yml"
- Conf.load_metadata(file)
# Initialize markup with our intro (as a template so we can easily edit it without needing to push code changes)
markup = "{{config intro}}\n"
# Add in the root
@@ -135,9 +136,10 @@ def generate_gamemodes_page():
"modes": {}
}
total_likelihood = 0
- for mode, min_players, max_players, likelihood in GAME_MODES.values():
+ for mode, min_players, max_players in GAME_MODES.values():
if mode.name in ("roles",):
continue
+ likelihood = Conf.get(f"gameplay.modes.{mode.name}.weight", 0)
mode_inst = mode()
role_guide = {}
default_role = mode_inst.CUSTOM_SETTINGS.default_role
@@ -238,6 +240,8 @@ def edit_page(session: Session,
return wiki_api(session, url, edit_params, edit_data, assert_bot=assert_bot)
if __name__ == "__main__":
+ file = Path(__file__).parent / "src" / "defaultsettings.yml"
+ Conf.load_metadata(file)
# make sure we always print a literal null when dumping None values
RoundTripRepresenter.add_representer(type(None), SafeRepresenter.represent_none)
# emit an empty string if the default is undefined
diff --git a/src/config.py b/src/config.py
index 51929e72..ab725d45 100644
--- a/src/config.py
+++ b/src/config.py
@@ -262,8 +262,8 @@ def merge(metadata: dict[str, Any], base, settings, *path: str,
if not path:
path = ("",)
- settings_type = metadata["_type"]
- assert settings_type is not None
+ settings_type = metadata.get("_type", None)
+ assert settings_type is not None, f"Path {path} does not define a metadata type"
if type_override is not None:
settings_type = type_override
@@ -284,13 +284,13 @@ def merge(metadata: dict[str, Any], base, settings, *path: str,
ctors = metadata.get("_ctors", [])
if ctors:
- assert settings_type == "dict" and "_default" in metadata
+ assert settings_type == "dict" and "_default" in metadata, f"Path {path} defines a constructor but is not a dict with a default"
if ctors and not isinstance(settings, dict):
for ctor in ctors:
# if a constructor fits our current data type, we'll merge that
# into the default object and return it
set_metadata = metadata["_default"][ctor["_set"]]
- assert ctor["_type"] == set_metadata["_type"]
+ assert ctor["_type"] == set_metadata["_type"], f"Path {path} constructor type doesn't match the type of value it is setting"
try:
value = {ctor["_set"]: merge(set_metadata, Empty, settings, *path, ctor["_set"])}
return merge(metadata, base, value, *path, strategy_override="replace")
diff --git a/src/defaultsettings.yml b/src/defaultsettings.yml
index afdeae82..03ea2386 100644
--- a/src/defaultsettings.yml
+++ b/src/defaultsettings.yml
@@ -881,6 +881,315 @@ gameplay: &gameplay
_default: []
_items:
_type: str
+ modes:
+ _desc: >
+ Allows you to adjust how likely it is for certain gamemodes to appear when there is not a majority vote.
+ Some gamemodes may have additional settings that can be adjusted here as well.
+ A mode set to have weight 0 will not appear unless there is a majority vote for the mode. To disable a mode
+ entirely, use gameplay.disable.gamemodes instead.
+ _type: dict
+ _extra: true
+ _default:
+ aleatoire:
+ _desc: Settings for the aleatoire game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ alpha:
+ _desc: Settings for the alpha game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ boreal:
+ _desc: Settings for the boreal game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ nights:
+ _desc: How many nights the village needs to survive to win the game even if wolf shamans remain alive.
+ _type: int
+ _default: 7
+ tribe:
+ _desc: Settings controlling how the tribe behaves with respect to needing to be fed and starvation.
+ _type: dict
+ _default:
+ base:
+ _desc: >
+ Percentage of alive players that determines the number of times the tribe needs to be fed.
+ This should be a number between 0 and 1 and is multiplied by the number of players.
+ Alive wolf shamans further reduce this multiplier; see gameplay.modes.boreal.tribe.adjust.
+ _type:
+ - float
+ - int
+ _default: 0.4
+ adjust:
+ _desc: >
+ The percentage in gameplay.modes.boreal.tribe.base is reduced by this amount for each alive
+ wolf shaman. This reduces the number of times village needs to be fed while wolf shamans are alive
+ to slightly counteract the fact wolf shamans can pass around hunger totems.
+ _type:
+ - float
+ - int
+ _default: 0.03
+ starve:
+ _desc: >
+ If the tribe is not sufficiently fed for this many nights, the wolves will win.
+ These nights need not be consecutive.
+ _type: int
+ _default: 3
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 1
+ charming:
+ _desc: Settings for the charming game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ classic:
+ _desc: Settings for the classic game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ default:
+ _desc: Settings for the default game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 40
+ drunkfire:
+ _desc: Settings for the drunkfire game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ evilvillage:
+ _desc: Settings for the evilvillage game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ foolish:
+ _desc: Settings for the foolish game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ guardian:
+ _desc: Settings for the guardian game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ kaboom:
+ _desc: Settings for the kaboom game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ lycan:
+ _desc: Settings for the lycan game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ mad:
+ _desc: Settings for the mad game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ maelstrom:
+ _desc: Settings for the maelstrom game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ masquerade:
+ _desc: Settings for the masquerade game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ mudkip:
+ _desc: Settings for the mudkip game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ noreveal:
+ _desc: Settings for the noreveal game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 1
+ pactbreaker:
+ _desc: Settings for the pactbreaker game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ random:
+ _desc: Settings for the random game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ rapidfire:
+ _desc: Settings for the rapidfire game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
+ sleepy:
+ _desc: Settings for the sleepy game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ nightmare:
+ _desc: Settings related to the nightmare mechanic in the sleepy mode.
+ _type: dict
+ _default:
+ chance:
+ _desc: >
+ How often a nightmare will occur. Set to 0 to disable nightmares. 1 or above will guarantee
+ a nightmare will occur each night. A value between 0 and 1 is the percentage chance a nightmare
+ will occur (e.g. 0.42 is a 42% chance a nightmare will occur). If multiple people are allowed to
+ have nightmares (see gameplay.modes.sleepy.nightmare.max), this is rolled once per allowed player.
+ _type:
+ - float
+ - int
+ _default: 0.2
+ max:
+ _desc: The maximum number of people that can have a nightmare each night.
+ _type: int
+ _default: 1
+ turn:
+ _desc: >
+ How likely certain roles (seer, harlot, cultist) are to change into other roles when priest dies.
+ A value between 0 and 1 is the percentage chance for these roles to turn (e.g. 0.42 is a 42% chance).
+ _type:
+ - float
+ - int
+ _default: 0.6
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 5
+ valentines:
+ _desc: Settings for the valentines game mode.
+ _type: dict
+ _ctors:
+ - _type: int
+ _set: weight
+ _default:
+ weight:
+ _desc: How likely this mode is to appear if there are no votes for gamemodes.
+ _type: int
+ _default: 0
ratelimits: &ratelimits
_name: ratelimits
diff --git a/src/gamemodes/__init__.py b/src/gamemodes/__init__.py
index 498e6629..bf70f34e 100644
--- a/src/gamemodes/__init__.py
+++ b/src/gamemodes/__init__.py
@@ -321,11 +321,11 @@ def custom_gun_chances(self, evt: Event, var: GameState, player: User, role: str
for key, value in self.GUN_CHANCES[role].items():
evt.data[key] += value
-GAME_MODES: dict[str, tuple[Type[GameMode], int, int, int]] = {}
+GAME_MODES: dict[str, tuple[Type[GameMode], int, int]] = {}
-def game_mode(name: str, minp: int, maxp: int, likelihood: int = 0):
+def game_mode(name: str, minp: int, maxp: int):
def decor(c: Type[GameMode]):
c.name = name
- GAME_MODES[name] = (c, minp, maxp, likelihood)
+ GAME_MODES[name] = (c, minp, maxp)
return c
return decor
diff --git a/src/gamemodes/aleatoire.py b/src/gamemodes/aleatoire.py
index 3f052e9c..f60e65c3 100644
--- a/src/gamemodes/aleatoire.py
+++ b/src/gamemodes/aleatoire.py
@@ -4,7 +4,7 @@
# Credits to Metacity for designing and current name
# Blame arkiwitect for the original name of KrabbyPatty
-@game_mode("aleatoire", minp=8, maxp=24, likelihood=5)
+@game_mode("aleatoire", minp=8, maxp=24)
class AleatoireMode(GameMode):
"""Game mode created by Metacity and balanced by woffle."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/alpha.py b/src/gamemodes/alpha.py
index ec16b3dd..3a7de882 100644
--- a/src/gamemodes/alpha.py
+++ b/src/gamemodes/alpha.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("alpha", minp=10, maxp=24, likelihood=5)
+@game_mode("alpha", minp=10, maxp=24)
class AlphaMode(GameMode):
"""Features the alpha wolf who can turn other people into wolves, be careful whom you trust!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/boreal.py b/src/gamemodes/boreal.py
index 09f0800f..d26b4369 100644
--- a/src/gamemodes/boreal.py
+++ b/src/gamemodes/boreal.py
@@ -3,7 +3,7 @@
import re
from collections import defaultdict
-from src import users
+from src import config, users
from src.cats import Wolfteam
from src.containers import DefaultUserDict
from src.decorators import command
@@ -17,7 +17,7 @@
from src.roles.helper.wolves import send_wolfchat_message
from src.status import add_dying
-@game_mode("boreal", minp=6, maxp=24, likelihood=5)
+@game_mode("boreal", minp=6, maxp=24)
class BorealMode(GameMode):
"""Some shamans are working against you. Exile them before you starve!"""
def __init__(self, arg=""):
@@ -80,14 +80,14 @@ def __init__(self, arg=""):
self.hunger_levels = DefaultUserDict(int)
self.totem_tracking = defaultdict(int) # no need to make a user container, this is only non-empty a very short time
self.phase = 1
- self.max_nights = 7
+ self.max_nights = config.Main.get("gameplay.modes.boreal.nights")
self.village_hunger = 0
- self.village_hunger_percent_base = 0.4
- self.village_hunger_percent_adj = 0.03
+ self.village_hunger_percent_base = config.Main.get("gameplay.modes.boreal.tribe.base")
+ self.village_hunger_percent_adj = config.Main.get("gameplay.modes.boreal.tribe.adjust")
self.ws_num_totem_percent = 0.5
self.ws_extra_totem = 0
self.village_starve = 0
- self.max_village_starve = 3
+ self.max_village_starve = config.Main.get("gameplay.modes.boreal.tribe.starve")
self.num_retribution = 0
self.saved_messages: dict[str, str] = {}
kwargs = dict(chan=False, pm=True, playing=True, silenced=True, phases=("night",),
diff --git a/src/gamemodes/charming.py b/src/gamemodes/charming.py
index 36522322..1fe9b0cf 100644
--- a/src/gamemodes/charming.py
+++ b/src/gamemodes/charming.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("charming", minp=6, maxp=24, likelihood=5)
+@game_mode("charming", minp=6, maxp=24)
class CharmingMode(GameMode):
"""Charmed players must band together to find the piper in this game mode."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/classic.py b/src/gamemodes/classic.py
index e143f12d..fe5be1e0 100644
--- a/src/gamemodes/classic.py
+++ b/src/gamemodes/classic.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("classic", minp=4, maxp=21, likelihood=0)
+@game_mode("classic", minp=4, maxp=21)
class ClassicMode(GameMode):
"""Classic game mode from before all the changes."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/default.py b/src/gamemodes/default.py
index 96b906c3..dbc9197e 100644
--- a/src/gamemodes/default.py
+++ b/src/gamemodes/default.py
@@ -3,7 +3,7 @@
from src.events import EventListener
from src import channels, users
-@game_mode("default", minp=6, maxp=24, likelihood=40)
+@game_mode("default", minp=6, maxp=24)
class DefaultMode(GameMode):
"""Default game mode."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/drunkfire.py b/src/gamemodes/drunkfire.py
index dfefa2cd..5a6e6296 100644
--- a/src/gamemodes/drunkfire.py
+++ b/src/gamemodes/drunkfire.py
@@ -3,7 +3,7 @@
from src.events import EventListener
from src import channels, users
-@game_mode("drunkfire", minp=8, maxp=17, likelihood=0)
+@game_mode("drunkfire", minp=8, maxp=17)
class DrunkFireMode(GameMode):
"""Most players get a gun, quickly shoot all the wolves!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/evilvillage.py b/src/gamemodes/evilvillage.py
index e25be99b..a2f1f0ff 100644
--- a/src/gamemodes/evilvillage.py
+++ b/src/gamemodes/evilvillage.py
@@ -6,7 +6,7 @@
from src import channels, users
from src.cats import Village
-@game_mode("evilvillage", minp=6, maxp=18, likelihood=5)
+@game_mode("evilvillage", minp=6, maxp=18)
class EvilVillageMode(GameMode):
"""Majority of the village is wolf aligned, safes must secretly try to kill the wolves."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/foolish.py b/src/gamemodes/foolish.py
index 7a406029..c0dfe093 100644
--- a/src/gamemodes/foolish.py
+++ b/src/gamemodes/foolish.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("foolish", minp=8, maxp=24, likelihood=5)
+@game_mode("foolish", minp=8, maxp=24)
class FoolishMode(GameMode):
"""Contains the fool, be careful not to lynch them!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/guardian.py b/src/gamemodes/guardian.py
index bab6e85c..7554efee 100644
--- a/src/gamemodes/guardian.py
+++ b/src/gamemodes/guardian.py
@@ -6,7 +6,7 @@
from src import channels, users
# original idea by Rossweisse, implemented by Vgr with help from woffle and jacob1
-@game_mode("guardian", minp=7, maxp=24, likelihood=5)
+@game_mode("guardian", minp=7, maxp=24)
class GuardianMode(GameMode):
"""Game mode full of guardian angels, wolves need to pick them apart!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/kaboom.py b/src/gamemodes/kaboom.py
index 62fe0747..b305be65 100644
--- a/src/gamemodes/kaboom.py
+++ b/src/gamemodes/kaboom.py
@@ -3,7 +3,7 @@
from src.gamestate import GameState
-@game_mode("kaboom", minp=6, maxp=24, likelihood=0)
+@game_mode("kaboom", minp=6, maxp=24)
class KaboomMode(GameMode):
"""All of these explosions are rather loud..."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/lycan.py b/src/gamemodes/lycan.py
index 8c2b8a80..80de7d2f 100644
--- a/src/gamemodes/lycan.py
+++ b/src/gamemodes/lycan.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("lycan", minp=7, maxp=24, likelihood=5)
+@game_mode("lycan", minp=7, maxp=24)
class LycanMode(GameMode):
"""Many lycans will turn into wolves. Hunt them down before the wolves overpower the village."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/mad.py b/src/gamemodes/mad.py
index caf31432..3ffd5a39 100644
--- a/src/gamemodes/mad.py
+++ b/src/gamemodes/mad.py
@@ -3,7 +3,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("mad", minp=7, maxp=24, likelihood=5)
+@game_mode("mad", minp=7, maxp=24)
class MadMode(GameMode):
"""This game mode has mad scientist and many things that may kill you."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/maelstrom.py b/src/gamemodes/maelstrom.py
index 7ea952f6..5c271313 100644
--- a/src/gamemodes/maelstrom.py
+++ b/src/gamemodes/maelstrom.py
@@ -10,7 +10,7 @@
from src import channels, users
from src.cats import All, Team_Switcher, Win_Stealer, Wolf, Wolf_Objective, Vampire_Objective, Killer
-@game_mode("maelstrom", minp=8, maxp=24, likelihood=0)
+@game_mode("maelstrom", minp=8, maxp=24)
class MaelstromMode(GameMode):
"""Some people just want to watch the world burn."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/masquerade.py b/src/gamemodes/masquerade.py
index c2b4aad2..0f961134 100644
--- a/src/gamemodes/masquerade.py
+++ b/src/gamemodes/masquerade.py
@@ -4,7 +4,7 @@
from src.events import EventListener
from src import channels, users
-@game_mode("masquerade", minp=6, maxp=24, likelihood=5)
+@game_mode("masquerade", minp=6, maxp=24)
class MasqueradeMode(GameMode):
"""Trouble is afoot at a masquerade ball when an attendee is found torn to shreds!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/mudkip.py b/src/gamemodes/mudkip.py
index 7051bfe1..2bc0eaba 100644
--- a/src/gamemodes/mudkip.py
+++ b/src/gamemodes/mudkip.py
@@ -5,7 +5,7 @@
from src import channels, users
# someone let woffle commit while drunk again... tsk tsk
-@game_mode("mudkip", minp=6, maxp=17, likelihood=5)
+@game_mode("mudkip", minp=6, maxp=17)
class MudkipMode(GameMode):
"""Why are all the professors named after trees?"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/noreveal.py b/src/gamemodes/noreveal.py
index b7576d6e..9e61bcf3 100644
--- a/src/gamemodes/noreveal.py
+++ b/src/gamemodes/noreveal.py
@@ -2,7 +2,7 @@
from src.messages import messages
from src import events, channels, users
-@game_mode("noreveal", minp=4, maxp=21, likelihood=1)
+@game_mode("noreveal", minp=4, maxp=21)
class NoRevealMode(GameMode):
"""Roles are not revealed when players die."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/pactbreaker.py b/src/gamemodes/pactbreaker.py
index e9d7d046..1c52e217 100644
--- a/src/gamemodes/pactbreaker.py
+++ b/src/gamemodes/pactbreaker.py
@@ -23,7 +23,7 @@
from src.roles.vampire import on_player_protected as vampire_drained
from src.roles.vigilante import vigilante_retract, vigilante_pass, vigilante_kill
-@game_mode("pactbreaker", minp=6, maxp=24, likelihood=0)
+@game_mode("pactbreaker", minp=6, maxp=24)
class PactBreakerMode(GameMode):
"""Help a rogue vigilante take down the terrors of the night or re-establish your pact with the werewolves!"""
def __init__(self, arg=""):
diff --git a/src/gamemodes/random.py b/src/gamemodes/random.py
index 93946674..886db969 100644
--- a/src/gamemodes/random.py
+++ b/src/gamemodes/random.py
@@ -7,7 +7,7 @@
from src import users
from src.cats import All, Wolf, Wolf_Objective, Vampire_Objective, Killer
-@game_mode("random", minp=8, maxp=24, likelihood=0)
+@game_mode("random", minp=8, maxp=24)
class RandomMode(GameMode):
"""Completely random and hidden roles."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/rapidfire.py b/src/gamemodes/rapidfire.py
index 672e7be1..da8b3953 100644
--- a/src/gamemodes/rapidfire.py
+++ b/src/gamemodes/rapidfire.py
@@ -3,7 +3,7 @@
from src.events import EventListener
from src import channels, users
-@game_mode("rapidfire", minp=6, maxp=24, likelihood=0)
+@game_mode("rapidfire", minp=6, maxp=24)
class RapidFireMode(GameMode):
"""Many roles that lead to multiple chain deaths."""
def __init__(self, arg=""):
diff --git a/src/gamemodes/sleepy.py b/src/gamemodes/sleepy.py
index c5ad97e7..d361c65b 100644
--- a/src/gamemodes/sleepy.py
+++ b/src/gamemodes/sleepy.py
@@ -10,9 +10,9 @@
from src.gamestate import GameState
from src.status import add_dying
from src.events import EventListener, Event
-from src import channels, locks
+from src import channels, config, locks
-@game_mode("sleepy", minp=10, maxp=24, likelihood=5)
+@game_mode("sleepy", minp=10, maxp=24)
class SleepyMode(GameMode):
"""A small village has become the playing ground for all sorts of supernatural beings."""
def __init__(self, arg=""):
@@ -24,9 +24,9 @@ def __init__(self, arg=""):
18: ["wolf(4)", "harlot", "monster"],
21: ["wolf(5)", "village drunk", "monster(2)", "gunner"],
}
- self.NIGHTMARE_CHANCE = 1/5
- self.NIGHTMARE_MAX = 1
- self.TURN_CHANCE = 3/5
+ self.NIGHTMARE_CHANCE = config.Main.get("gameplay.modes.sleepy.nightmare.chance")
+ self.NIGHTMARE_MAX = config.Main.get("gameplay.modes.sleepy.nightmare.max")
+ self.TURN_CHANCE = config.Main.get("gameplay.modes.sleepy.turn")
# Make sure priest is always prophet AND blessed, and that drunk is always gunner
self.SECONDARY_ROLES["blessed villager"] = {"priest"}
self.SECONDARY_ROLES["prophet"] = {"priest"}
diff --git a/src/gamemodes/valentines.py b/src/gamemodes/valentines.py
index c52f3acc..a8c4437e 100644
--- a/src/gamemodes/valentines.py
+++ b/src/gamemodes/valentines.py
@@ -1,7 +1,7 @@
from src.gamemodes import game_mode, GameMode
from src.events import EventListener
-@game_mode("valentines", minp=8, maxp=24, likelihood=0)
+@game_mode("valentines", minp=8, maxp=24)
class MatchmakerMode(GameMode):
"""Love is in the air!"""
def __init__(self, arg=""):
diff --git a/src/pregame.py b/src/pregame.py
index f4c641fb..224128a5 100644
--- a/src/pregame.py
+++ b/src/pregame.py
@@ -202,7 +202,7 @@ def start(wrapper: MessageDispatcher, *, forced: bool = False):
def _isvalid(mode, allow_vote_only):
x = GAME_MODES[mode]
- if not x[3] and not allow_vote_only:
+ if not config.Main.get(f"gameplay.modes.{mode}.weight", 0) and not allow_vote_only:
return False
min_players = config.Main.get("gameplay.player_limits.minimum")
max_players = config.Main.get("gameplay.player_limits.maximum")
@@ -233,7 +233,7 @@ def _isvalid(mode, allow_vote_only):
possiblegamemodes = []
for gamemode in GAME_MODES.keys() - set(config.Main.get("gameplay.disable.gamemodes")):
if _isvalid(gamemode, False):
- possiblegamemodes += [gamemode] * GAME_MODES[gamemode][3]
+ possiblegamemodes += [gamemode] * config.Main.get(f"gameplay.modes.{gamemode}.weight", 0)
gamemode = random.choice(possiblegamemodes)
set_gamemode(pregame_state, gamemode)
diff --git a/src/votes.py b/src/votes.py
index 29161bf3..29da2ef1 100644
--- a/src/votes.py
+++ b/src/votes.py
@@ -139,7 +139,7 @@ def show_votes(wrapper: MessageDispatcher, message: str):
# - No other game mode has a majority
if (GAME_MODES[gamemode][1] <= len(pl) <= GAME_MODES[gamemode][2] and
(not majority or num_votes >= len(pl) / 2) and
- (GAME_MODES[gamemode][3] > 0 or num_votes >= len(pl) / 2)):
+ (config.Main.get(f"gameplay.modes.{gamemode}.weight", 0) > 0 or num_votes >= len(pl) / 2)):
gamemode = messages["bold"].format(gamemode)
if num_votes >= len(pl) / 2:
majority = True