def __init__(self, game, name: str, meta: str): self._game = game self._original_metadata = meta self._metadata = MetaParser(type(self)).parse(meta) self.name = name self.team = self._metadata.team self.gender = self._metadata.gender self.height = SV.parse(self._metadata.height) if isinstance( self._metadata.height, str) else SV.parse(str(self._metadata.height) + "m") self.baseheight = copy(self.height) if not isURL(self._metadata.url): raise ValueError(f"{self._metadata.url} is not a URL.") self.url = self._metadata.url self.attributes = [] if self._metadata.attr is None else self._metadata.attr if self._metadata.nsfw is None or self._metadata.nsfw.lower( ) == "true": self.nsfw = True elif self._metadata.nsfw.lower() == "false": self.nsfw = False else: raise ParseError( f"{self._metadata.nsfw!r} is not a valid NSFW flag.") self.inventory = [] self.dead = False self.elims = 0
def parse(cls, s): prefixmatch = valid_prefixes + r"\s*(.*)" suffixmatch = r"(.*)\s*" + valid_suffixes ct = None v = None if m := re.match(prefixmatch, s): prefix = m.group(1) value = m.group(2) if prefix in add_prefixes: ct = "add" v = SV.parse(value) elif prefix in subtract_prefixes: ct = "add" v = SV.parse(value) * -1 elif prefix in multiply_prefixes: ct = "multiply" v = Decimal(value) elif prefix in divide_prefixes: ct = "multiply" v = Decimal(1) / Decimal(value) elif prefix in percent_prefixes: ct = "multiply" v = Decimal(value) / Decimal(100) elif prefix in power_prefixes: ct = "power" v = Decimal(value)
def __init__(self, *, lessthan: str = None, greaterthan: str = None, elimslessthan: str = None, elimsgreaterthan: str = None, elimsequal: str = None, team: str = None, items: list = None, gender: str = None, attributes: list = None, nsfw=None): self.lessthan = None if lessthan is None else SV.parse(lessthan) self.greaterthan = None if greaterthan is None else SV.parse( greaterthan) self.elimslessthan = None if elimslessthan is None else Decimal( elimslessthan) self.elimsgreaterthan = None if elimsgreaterthan is None else Decimal( elimsgreaterthan) self.elimsequal = None if elimsequal is None else Decimal(elimsequal) self.team = team self.items = items self.attributes = attributes self.gender = gender self.nsfw = nsfw self.realteam = None
def get_steps(start_inc: SV, diff: Diff, goal: SV): """Return the number of steps it would take to reach `goal` from 0, first by increasing by `start_inc`, then by `start_inc` operator(`diff.changetype`) `diff.amount`, repeating this process until `goal` is reached. Returns (steps, final increment, start inc. / final inc.)""" if (diff.changetype == "add" and diff.amount == 0) or (diff.changetype == "multiply" and diff.amount == 1): if start_inc <= 0: return (Decimal("inf"), SV(0), Decimal("inf")) # Calculate number of steps required to reach goal steps = math.ceil(goal / start_inc) # Calculate how far user got after those steps # current_pos = start_inc * steps return (Decimal(steps), start_inc, 1) elif diff.changetype == "add": if diff.amount < 0: # Calculate max distance travelled, if each step is getting shorter max_dist = ((diff.amount / 2) - start_inc) / diff.amount if max_dist < goal: return (Decimal("inf"), SV(0), Decimal("inf")) # Calculate number of steps required to reach goal # https://www.wolframalpha.com/input/?i=g+%3D+%28s+*+t%29+%2B+%28a+*+%28%28t+-+1%29+*+t%29+%2F+2%29+solve+for+t steps = math.ceil( Decimal( Decimal( math.sqrt( Decimal(diff.amount**2) - Decimal(4 * diff.amount * start_inc) + Decimal(8 * diff.amount * goal) + Decimal(4 * Decimal(start_inc**2)))) + diff.amount - Decimal(2 * start_inc)) / Decimal(2 * diff.amount)) # Calculate how far user got after those steps # current_pos = (start_inc * steps) + (diff.amount * ((steps - 1) * steps) / 2) # Calculate length of last step current_inc = start_inc + (diff.amount * (steps - 1)) return (Decimal(steps), current_inc, start_inc / current_inc) elif diff.changetype == "multiply": # https://en.wikipedia.org/wiki/Geometric_series if diff.amount < 1: # Calculate max distance travelled, if each step is getting shorter max_dist = start_inc / (1 - diff.amount) if max_dist < goal: return (Decimal("inf"), SV(0), Decimal("inf")) # Calculate number of steps required to reach goal steps = math.ceil( Decimal( math.log(-(goal * (1 - diff.amount) / start_inc) + 1, diff.amount))) # Calculate how far user got after those steps # current_pos = start_inc * ((1 - diff.amount ** (steps - 1)) / (1 - diff.amount)) # Calculate length of last step current_inc = start_inc * (diff.amount**(steps - 1)) return (Decimal(steps), current_inc, start_inc / current_inc) else: raise ChangeMethodInvalidException( "This change type is not yet supported for scale-walking.")
def liftstrength(self, value): if value is None: self._liftstrength = None return value = SV(value) if value < 0: value = SV(0) self._liftstrength = value
def change_height(self, diff: Diff): if diff.changetype == "add": self.height = SV(self.height + diff.amount) elif diff.changetype == "multiply": self.height = SV(self.height * diff.amount) else: raise GametimeError(f"Unsupported changetype {diff.changetype!r}.")
def hairlength(self, value): if value is None: self._hairlength = None return value = SV(value) if value < 0: value = SV(0) self._hairlength = value
def __init__(self, userid, guildid, *, addPerSec=0, mulPerSec=1, stopSV=None, stopTV=None, startTime=None, lastRan=None): self.userid = userid self.guildid = guildid self.addPerSec = addPerSec and SV(addPerSec) self.mulPerSec = mulPerSec and Decimal(mulPerSec) self.stopSV = stopSV and SV(stopSV) self.stopTV = stopTV and TV(stopTV) self.startTime = startTime and Decimal(startTime) self.lastRan = lastRan and Decimal(lastRan)
def get_dist(start_inc: SV, diff: Diff, steps: int): if diff.changetype == "add": current_pos = (start_inc * steps) + (diff.amount * ((steps - 1) * steps) / 2) return SV(current_pos) elif diff.changetype == "multiply": current_pos = start_inc * ((1 - diff.amount**(steps - 1)) / (1 - diff.amount)) return SV(current_pos) else: raise ChangeMethodInvalidException( "This change type is not yet supported for scale-walking.")
async def setear(self, ctx, *, newear): """Set your current ear heightear.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) newearsv = SV(SV.parse(newear) * userdata.viewscale) userdata.earheight = newearsv userdb.save(userdata) await ctx.send( f"{userdata.nickname}'s base ear height is now {userdata.earheight:mu} long, " f"or {SV(userdata.earheight):mu} currently.") await showNextStep(ctx, userdata)
async def setfoot(self, ctx, *, newfoot): """Set your current foot length.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) userdata.footlength = SV(SV.parse(newfoot) * userdata.viewscale) userdb.save(userdata) await ctx.send( f"{userdata.nickname}'s base foot length is now {userdata.footlength:mu} long ({formatShoeSize(userdata.footlength)}), " f"or {(SV(userdata.footlength * userdata.scale)):mu} currently. {formatShoeSize(SV(userdata.footlength * userdata.scale))}" ) await showNextStep(ctx, userdata)
async def settail(self, ctx, *, newtail): """Set your current tail length.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) newtailsv = SV(SV.parse(newtail) * userdata.viewscale) userdata.taillength = newtailsv userdb.save(userdata) await ctx.send( f"{userdata.nickname}'s base tail length is now {userdata.taillength:mu} long, " f"or {SV(userdata.taillength):mu} currently.") await showNextStep(ctx, userdata)
async def settail(self, ctx, *, newtail): """Set your current tail length.""" userdata = userdb.load(ctx.guild.id, ctx.author.id) newtailsv = SV(SV.parse(newtail) * userdata.viewscale) userdata.taillength = newtailsv userdb.save(userdata) logger.info( f"User {ctx.author.id} ({ctx.author.display_name})'s tail is now {userdata.taillength:m} long." ) await ctx.send( f"<@{ctx.author.id}>'s tail is now {userdata.taillength:mu} long.")
async def setfoot(self, ctx, *, newfoot): """Set your current foot length.""" userdata = userdb.load(ctx.guild.id, ctx.author.id) userdata.footlength = SV(SV.parse(newfoot) * userdata.viewscale) userdb.save(userdata) logger.info( f"User {ctx.author.id} ({ctx.author.display_name})'s foot is now {userdata.footlength:m} long." ) await ctx.send( f"<@{ctx.author.id}>'s foot is now {userdata.footlength:mu} long. ({formatShoeSize(userdata.footlength)})" )
def getUserSizes(g): # Find the largest and smallest current users. smallestuser = None smallestsize = SV.infinity largestuser = None largestsize = SV(0) allusers = {} for _, userid in userdb.listUsers(guildid=g.id): member = g.get_member(userid) if not (member and str(member.status) != "offline"): continue userdata = userdb.load(g.id, userid) if userdata.height == 0 or userdata.height == SV.infinity: continue if not userdata.is_active: continue if userdata.height > largestsize: largestuser = userid largestsize = userdata.height if userdata.height < smallestsize: smallestuser = userid smallestsize = userdata.height allusers[userid] = userdata.height return { "smallest": { "id": smallestuser, "size": smallestsize }, "largest": { "id": largestuser, "size": largestsize }, "users": allusers }
async def ruler(self, ctx, length: SV, *, who: typing.Union[discord.Member, SV] = None): """A distance to a user looks how long to everyone else? Examples: `&ruler 1mi` `&ruler 1ft @DigiDuncan`""" if who is None: who = ctx.message.author userdata = getUserdata(who) userstats = proportions.PersonStats(userdata) if userdata.height == 0: await ctx.send(f"{userdata.tag} doesn't exist...") return newlength = SV(length / userstats.viewscale) e = discord.Embed( title = f"{userstats.nickname}'s {length:,.3mu} to the world", description = ( f"To everyone else, {userstats.nickname}'s {length:,.3mu} would look to be **{newlength:,.3mu}.**" ) ) await ctx.send(embed = e)
def SV_parse(): s = request.args.get("s") try: val = SV.parse(s) except InvalidSizeValue: abort(404) return json.dumps({"SV": str(val)})
async def setbase(self, ctx, arg1: typing.Union[SV, WV], arg2: typing.Union[SV, WV] = None): """Set your base height and weight.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) # Don't allow a user to enter setbase(SV, SV) or setbase(WV, WV) if (isinstance(arg1, SV) and isinstance(arg2, SV)) or (isinstance(arg1, WV) and isinstance(arg2, WV)): raise errors.UserMessedUpException( "Please do not enter two heights or two weights.") newbaseheight = None newbaseweight = None for arg in [arg1, arg2]: if isinstance(arg, SV): newbaseheight = arg if isinstance(arg, WV): newbaseweight = arg completed_registration = False if newbaseheight is not None: if "setheight" in userdata.registration_steps_remaining: userdata.height = newbaseheight if not (SV.parse("4ft") < newbaseheight < SV.parse("8ft")): await ctx.send( f"{emojis.warning} **WARNING:** Your base height should probably be something more human-scale. This makes comparison math work out much nicer. If this was intended, you can ignore this warning, but it is ***highly recommended*** that you have a base height similar to the size of a normal human being." ) userdata.baseheight = newbaseheight completed_registration = userdata.complete_step( "setheight") or completed_registration if newbaseweight is not None: if "setweight" in userdata.registration_steps_remaining: if not (WV.parse("10lb") < newbaseheight < SV.parse("1000lb")): await ctx.send( f"{emojis.warning} **WARNING:** Your base weight should probably be something more human-scale. This makes comparison math work out much nicer. If this was intended, you can ignore this warning, but it is ***highly recommended*** that you have a base weight similar to that of a normal human being." ) userdata.baseweight = newbaseweight completed_registration = userdata.complete_step( "setweight") or completed_registration userdb.save(userdata) await ctx.send( f"{userdata.nickname} changed their base height and weight to {userdata.baseheight:,.3mu} and {userdata.baseweight:,.3mu}" ) await showNextStep(ctx, userdata, completed=completed_registration)
def SV_format(): value = Decimal(request.args.get("value")) system = request.args.get("system") try: val = SV(value) except InvalidSizeValue: abort(404) return json.dumps({"formatted": format(val, system)})
async def on_message(m): # non-guild messages if not isinstance(m.author, discord.Member): return edgedict = getEdgesFile(m.guild.id) sm = edgedict.get("smallest", None) lg = edgedict.get("largest", None) if m.author.id != sm and m.author.id != lg: return # The user is not set to be the smallest or the largest user. userdata = userdb.load(m.guild.id, m.author.id) usersizes = getUserSizes(m.guild) smallestuser = usersizes["smallest"]["id"] smallestsize = usersizes["smallest"]["size"] largestuser = usersizes["largest"]["id"] largestsize = usersizes["largest"]["size"] if edgedict.get("smallest", None) == m.author.id: if m.author.id == smallestuser: return elif userdata.height == SV(0): return else: userdata.height = smallestsize * Decimal(0.9) userdb.save(userdata) logger.info( f"User {m.author.id} ({m.author.display_name}) is now {userdata.height:m} tall, so that they stay the smallest." ) if edgedict.get("largest", None) == m.author.id: if m.author.id == largestuser: return elif userdata.height == SV(SV.infinity): return else: userdata.height = largestsize * Decimal(1.1) userdb.save(userdata) logger.info( f"User {m.author.id} ({m.author.display_name}) is now {userdata.height:m} tall, so that they stay the largest." ) if userdata.display: await proportions.nickUpdate(m.author)
def fromJSON(cls, jsondata): changetype = jsondata["changetype"] if changetype == "add": amount = SV(jsondata["amount"]) else: amount = Decimal(jsondata["amount"]) original = jsondata["original"] return cls(original, changetype, amount)
def addToUnits(self): if self.unitlength is not None: SV.addUnit( Unit(factor=self.unitlength, name=self.name, namePlural=self.namePlural, names=self.aliases, symbol=self.symbol)) SV.addSystemUnit("o", SystemUnit(self.name)) if self.weight is not None: WV.addUnit( Unit(factor=self.weight, name=self.name, namePlural=self.namePlural, names=self.aliases, symbol=self.symbol)) WV.addSystemUnit("o", SystemUnit(self.name))
def runperhour(self, value): if value is None: self._runperhour = None return if isinstance(value, ParseableRate): if value.diff.changetype != "add": raise ValueError("Invalid rate for speed parsing.") if value.diff.amount < 0: raise ValueError("Speed can not go backwards!") value = value.diff.amount / value.time * Decimal("3600") value = SV(value) if value < 0: value = SV(0) self._runperhour = value
def fromShoeSize(shoesize): shoesizenum = Decimal(re.search(r"(\d*,)*\d+(\.\d*)?", shoesize)[0]) if "w" in shoesize.lower(): shoesizenum -= 1 if "c" in shoesize.lower( ): # Intentional override, children's sizes have no women/men distinction. shoesizenum -= (12 + Decimal("1/3")) footlengthinches = ((shoesizenum + 24) / 3) - Decimal("2/3") return SV.parse(f"{footlengthinches}in")
async def load(self, data: str): self.parser = Parser(self.game, data) async for progress in self.parser.parse(): yield progress self.minsize = SV.parse( "1mm") if self.parser.minsize is None else SV.parse( self.parser.minsize) self.maxsize = SV.parse( "4mi") if self.parser.maxsize is None else SV.parse( self.parser.maxsize) self.autoelim = True if self.parser.autoelim is None else bool( self.parser.autoelim) self.deathrate = Decimal( 10) if self.parser.deathrate is None else Decimal( self.parser.deathrate) self.arenafreq = Decimal( 10) if self.parser.deathrate is None else Decimal( self.parser.deathrate) self.unitsystem = "m" if self.parser.unitsystem is None else self.parser.unitsystem self.teamwin = False if self.parser.teamwin is None else bool( self.parser.teamwin) self.players = self.parser.players self.original_player_count = len(self.players) self.arenas = self.parser.arenas self._bloodbath_events = self.parser.bloodbath_events self._day_events = self.parser.day_events self._night_events = self.parser.night_events self._fatalday_events = self.parser.fatalday_events self._fatalnight_events = self.parser.fatalnight_events self._feast_events = self.parser.feast_events eventsdict = { "bloodbath_events": self._bloodbath_events, "day_events": self._day_events, "night_events": self._night_events, "fatalday_events": self._fatalday_events, "fatalnight_events": self._fatalnight_events, "feast_events": self._feast_events } self.events = AttrDict(eventsdict)
def changeUser(guildid, userid, changestyle, amount): changestyle = changestyle.lower() if changestyle in ["add", "+", "a", "plus"]: changestyle = "add" if changestyle in ["subtract", "sub", "-", "minus"]: changestyle = "subtract" if changestyle in ["power", "exp", "pow", "exponent", "^", "**"]: changestyle = "power" if changestyle in ["multiply", "mult", "m", "x", "times", "*"]: changestyle = "multiply" if changestyle in ["divide", "d", "/", "div"]: changestyle = "divide" if changestyle in ["percent", "per", "perc", "%"]: changestyle = "percent" if changestyle not in [ "add", "subtract", "multiply", "divide", "power", "percent" ]: raise errors.ChangeMethodInvalidException(changestyle) amountSV = None amountVal = None newamount = None if changestyle in ["add", "subtract"]: amountSV = SV.parse(amount) elif changestyle in ["multiply", "divide", "power"]: amountVal = Decimal(amount) if amountVal == 1: raise errors.ValueIsOneException if amountVal == 0: raise errors.ValueIsZeroException elif changestyle in ["percent"]: amountVal = Decimal(amount) if amountVal == 0: raise errors.ValueIsZeroException userdata = userdb.load(guildid, userid) if changestyle == "add": newamount = userdata.height + amountSV elif changestyle == "subtract": newamount = userdata.height - amountSV elif changestyle == "multiply": newamount = userdata.height * amountVal elif changestyle == "divide": newamount = userdata.height / amountVal elif changestyle == "power": userdata = userdata**amountVal elif changestyle == "percent": newamount = userdata.height * (amountVal / 100) if changestyle != "power": userdata.height = newamount userdb.save(userdata)
async def setrandomheight(self, ctx, minheight, maxheight): """Change height to a random value. Sets your height to a height between `minheight` and `maxheight`. Weighted on a logarithmic curve.""" minheightSV = utils.clamp(0, SV.parse(minheight), SV._infinity) maxheightSV = utils.clamp(0, SV.parse(maxheight), SV._infinity) newheightSV = decimal.randRangeLog(minheightSV, maxheightSV) userdata = userdb.load(ctx.guild.id, ctx.author.id) userdata.height = newheightSV userdb.save(userdata) logger.info( f"User {ctx.author.id} ({ctx.author.display_name}) set a random height, and are now {userdata.height:m} tall." ) await ctx.send(f"<@{ctx.author.id}> is now {userdata.height:mu} tall.") await proportions.nickUpdate(ctx.author)
async def setinf(self, ctx): """Change height to infinity.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) userdata.height = SV("infinity") completed_registration = userdata.complete_step("setheight") userdb.save(userdata) await ctx.send(f"{userdata.nickname} is now infinitely tall.") await proportions.nickUpdate(ctx.author) await showNextStep(ctx, userdata, completed=completed_registration)
def fromJSON(cls, jsondata): rate = Rate.fromJSON(jsondata["rate"]) original = jsondata["original"] stoptype = jsondata["stoptype"] if stoptype == "SV": stop = SV(jsondata["stop"]) elif stoptype == "TV": stop = TV(jsondata["stop"]) else: raise ThisShouldNeverHappenException return cls(original, rate, stop)
async def setbaseear(self, ctx, *, newear): """Set a custom ear height.""" newearsv = SV.parse(newear) userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) userdata.earheight = newearsv userdb.save(userdata) await ctx.send( f"{userdata.nickname}'s ear is now {userdata.earheight:mu} long.") await showNextStep(ctx, userdata)