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.")
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_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 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 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)
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 scalewalk(self, ctx, change: Diff, dist: SV, flag=None): """Walk a certain distance, scaling by an amount each step you take. Accepts addition or subtraction of a certain height, or multiplication/division of a factor. Examples: `&scalewalk 2x 50m `&scalewalk -1mm 20ft""" guildid = ctx.guild.id userid = ctx.author.id userdata = userdb.load(guildid, userid) stats = proportions.PersonStats(userdata) stepcount, final_inc, final_ratio = get_steps(stats.walksteplength, change, dist) finalheight = SV(userdata.height / final_ratio) symbol = "" if change.changetype == "add": symbol = "+" if change.changetype == "multiply": symbol = "x" amountstring = "" if change.changetype == "add": amountstring = f"{symbol}{change.amount:,.3mu}" if change.changetype == "multiply": amountstring = f"{symbol}{change.amount:,.3}" if flag is None: e = discord.Embed( title= f"If {userdata.nickname} walked {dist:,.3mu}, scaling {amountstring} each step...", description= f"They would now be **{finalheight:,.3mu}** tall after **{stepcount}** steps." ) await ctx.send(embed=e) elif flag == "apply": userdata.height = finalheight userdb.save(userdata) e = discord.Embed( title= f"{userdata.nickname} walked {dist:,.3mu}, scaling {amountstring} each step...", description= f"They are now **{finalheight:,.3mu}** tall after **{stepcount}** steps." ) await ctx.send(embed=e) else: raise DigiContextException(f"Invalid flag {flag}.")
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 setrandomheight(self, ctx, minheight: SV, maxheight: SV): """Change height to a random value. Sets your height to a height between `minheight` and `maxheight`. Weighted on a logarithmic curve.""" if minheight < 0: minheight = SV(0) if maxheight < 0: maxheight = SV(0) newheightSV = decimal.randRangeLog(minheight, maxheight) userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) userdata.height = newheightSV userdb.save(userdata) await ctx.send(f"{userdata.nickname} is now {userdata.height:mu} tall." ) await proportions.nickUpdate(ctx.author) 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)
def speedcalc(self, dist: SV, *, speed=False, foot=False, include_relative=False): reldist = SV(dist * self.viewer.viewscale) reldist_print = f"{reldist:,.3mu}" climblength = Decimal(0.3048) / self.viewer.viewscale climbspeed = Decimal(4828) / self.viewer.viewscale SVclimbspeed = SV(climbspeed) _walktime = (dist / self.viewer.walkperhour) * 60 * 60 walksteps = math.ceil(dist / self.viewer.walksteplength) _runtime = (dist / self.viewer.runperhour) * 60 * 60 runsteps = math.ceil(dist / self.viewer.runsteplength) _climbtime = (dist / climbspeed) * 60 * 60 climbsteps = math.ceil(dist / climblength) walktime = prettyTimeDelta(_walktime, roundeventually=True) runtime = prettyTimeDelta(_runtime, roundeventually=True) climbtime = prettyTimeDelta(_climbtime, roundeventually=True) walkspeedstr = f"\n*{emojis.blank}{self.viewer.walkperhour:,.3mu} per hour*" runspeedstr = f"\n*{emojis.blank}{self.viewer.runperhour:,.3mu} per hour*" climbspeedstr = f"\n*{emojis.blank}{SVclimbspeed:,.3mu} per hour*" shoesize = " (" + formatShoeSize(dist) + ")" newline = "\n" return ( f"{emojis.ruler} {dist:,.3mu}{shoesize if foot else ''}\n" f"{emojis.eyes + reldist_print + newline if include_relative else ''}" f"{emojis.walk} {walktime} ({walksteps:,.3} steps){walkspeedstr if speed else ''}\n" f"{emojis.run} {runtime} ({runsteps:,.3} strides){runspeedstr if speed else ''}\n" f"{emojis.climb} {climbtime} ({climbsteps:,.3} pulls){climbspeedstr if speed else ''}" )
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)})" )
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.")
def getUserSizes(g): # Find the largest and smallest current users. # TODO: Check to see if these users are recently active, which would determine if they count towards the check. smallestuser = 000000000000000000 smallestsize = SV(SV.infinity) largestuser = 000000000000000000 largestsize = SV(0) allusers = {} for _, testid in userdb.listUsers(g.id): if testid in list( [m.id for m in g.members if str(m.status) != "offline"]): testdata = userdb.load(g.id, testid) allusers[testid] = testdata.height if testdata.height <= 0 or testdata.height >= SV.infinity: break if testdata.height > largestsize: largestuser = testid largestsize = testdata.height if testdata.height < smallestsize: smallestuser = testid smallestsize = testdata.height smallestuser = int(smallestuser) largestuser = int(largestuser) return { "smallest": { "id": smallestuser, "size": smallestsize }, "largest": { "id": largestuser, "size": largestsize }, "users": allusers }
async def on_message(m): # non-guild messages if not isinstance(m.author, discord.Member): return try: guilddata = guilddb.load(m.guild.id) except GuildNotFoundException: return # Guild does not have edges set sm = guilddata.small_edge lg = guilddata.large_edge if not (m.author.id == sm or m.author.id == lg): return # The user is not set to be the smallest or the largest user. try: userdata = userdb.load(m.guild.id, m.author.id) except UserNotFoundException: return usersizes = getUserSizes(m.guild) smallestuser = usersizes["smallest"]["id"] smallestsize = usersizes["smallest"]["size"] largestuser = usersizes["largest"]["id"] largestsize = usersizes["largest"]["size"] if sm == 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) if lg == m.author.id: if m.author.id == largestuser: return elif userdata.height == SV.infinity: return else: userdata.height = largestsize * Decimal(1.1) userdb.save(userdata) if userdata.display: await proportions.nickUpdate(m.author)
async def setshoe(self, ctx, *, newshoe): """Set your current shoe size. Accepts a US Shoe Size. If a W is in the shoe size anywhere, it is parsed as a Women's size. If a C is in the show size anywhere, it is parsed as a Children's size.""" userdata = userdb.load(ctx.guild.id, ctx.author.id, allow_unreg=True) newfoot = fromShoeSize(newshoe) userdata.footlength = SV(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 setshoe(self, ctx, *, newshoe): """Set your current shoe size. Accepts a US Shoe Size. If a W is in the shoe size anywhere, it is parsed as a Women's size. If a C is in the show size anywhere, it is parsed as a Children's size.""" userdata = userdb.load(ctx.guild.id, ctx.author.id) newfoot = fromShoeSize(newshoe) userdata.footlength = SV(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 __init__(self, name, dimension, aliases=[], symbol=None, height=None, length=None, width=None, diameter=None, depth=None, thickness=None, weight=None): self.name = name self.namePlural = getPlural(name) self.singularNames = aliases + [self.name] self.aliases = aliases + [getPlural(a) for a in aliases] self.article = getIndefiniteArticle(self.name).split(" ")[0] self.symbol = symbol or None self.height = height and SV(height) self.length = length and SV(length) self.width = width and SV(width) self.diameter = diameter and SV(diameter) self.depth = depth and SV(depth) self.thickness = thickness and SV(thickness) self.weight = weight and WV(weight) dimensionmap = { "h": "height", "l": "length", "w": "width", "d": "diameter", "p": "depth", "t": "thickness" } self.unitlength = getattr(self, dimensionmap[dimension])
def viewscale(self, viewscale): """Scale the user height to match the view scale""" self.height = SV(self.baseheight / viewscale)
def scale(self, scale): """Scale the user height to match the scale""" self.height = SV(self.baseheight * scale)
async def _run_event(self, event: Event, playerpool: Dict[str, Player]) -> dict: """Runs an event, returning the string describing what happened and an image.""" if event.tributes > len(playerpool): raise GametimeError("Not enough players to run this event!") players = event.get_players(playerpool) eventtext = event.fillin(players) deaths = [] logger.log(ROYALE, "[EVENT] " + eventtext) def player_by_id(pid): return self.players[players.getByIndex(pid - 1).name] if event.elims is not None: for i in event.elims: player_by_id(i).dead = True deaths.append(player_by_id(i)) if event.perps is not None: for i in event.perps: player_by_id(i).elims += 1 if event.gives is not None: for i, s in event.gives: player_by_id(i).give_item(s) if event.removes is not None: for i, s in event.removes: player_by_id(i).remove_item(s) if event.clears is not None: for i in event.clears: player_by_id(i).clear_inventory() if event.giveattrs is not None: for i, s in event.giveattrs: player_by_id(i).give_attribute(s) if event.removeattrs is not None: for i, s in event.removeattrs: player_by_id(i).remove_attribute(s) if event.setsizes is not None: for i, d in event.setsizes: player_by_id(i).height = SV(d) if event.sizes is not None: for i, d in event.sizes: player_by_id(i).change_height(d) if event.setsizeranges is not None: for i, d1, d2 in event.setsizeranges: small, large = minmax(d1, d2) d = randRangeLog(small, large) player_by_id(i).height = SV(d) if event.sizeranges is not None: for i, d1, d2 in event.sizeranges: da = randRangeLog(d1.amount, d2.amount) if d1.changetype == "add": ds = "+" elif d1.changetype == "multiply": ds = "x" else: raise ThisShouldNeverHappenException do = ds + da d = Diff(do, changetype=d1.changetype, amount=da) player_by_id(i).change_height(d) if len(players) == 0: eventimage = None else: eventimage = merge_images( [await self.players[p].get_image() for p in players]) return RunnableEvent(text=eventtext, image=eventimage, players=players, deaths=deaths)