-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlevelLength.py
executable file
·164 lines (126 loc) · 4.62 KB
/
levelLength.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
####################
# Reimplementation of the GDCF method for getting level length
# Values stolen from GDCF and GDBrowser
# (mostly) created by zmx
####################
from typing import Tuple, List, Dict
from enum import Enum
import math
from levelUtil import parseKeyVarArray
# stolen magic values from stadust
class Portals(Enum):
SLOW = 251.16
NORMAL = 311.58
MEDIUM = 387.42
FAST = 468.0
SUPER_FAST = 576.0
portalInfo = Tuple[float, Portals]
def getPortalFromId(objectId: int) -> Portals:
# lol also stolen from stadust
portalIds: Dict[int,
Portals] = {200: Portals.SLOW,
201: Portals.NORMAL,
202: Portals.MEDIUM,
203: Portals.FAST,
1334: Portals.SUPER_FAST}
# script takes care of error handlings
return portalIds[int(objectId)]
def getPortalInfo(portalIndex: int) -> Portals:
portalVals: List[Portals] = [
Portals.NORMAL,
Portals.SLOW,
Portals.MEDIUM,
Portals.FAST,
Portals.SUPER_FAST]
try:
portalInfo = portalVals[portalIndex]
except BaseException:
portalInfo = portalVals[0]
return portalInfo
# x val, portal id
def getPortalList(objects: List[Dict[str, str]]) -> List[portalInfo]:
portals: List[portalInfo] = []
for objec in objects:
try:
portalId: Portals = getPortalFromId(int(objec['1']))
if objec['13'] == '1': # checked portal
# id, x-pos
portals.append((float(objec['2']), portalId))
except BaseException:
pass
sPortals: List[portalInfo] = sorted(
portals, key=lambda x: x[0]) # sort by x pos
return sPortals
# reimplementation of stadust's function
def getSecondsFromxPos(
levelLength: float,
startSpeed: Portals,
portals: List[portalInfo]) -> float:
speed: float = startSpeed.value
if not portals:
return levelLength / speed
lastObjPos: float = 0.0
totalTime: float = 0.0
for (x, portal) in portals:
currentSegment: float = x - lastObjPos
if levelLength <= currentSegment:
break
totalTime += currentSegment / speed
speed = portal.value
lastObjPos = x
return (levelLength - lastObjPos) / speed + totalTime
def getLevelLength(levelString: str) -> float:
"""
- expects a level string lol
- returns seconds
"""
# first we want to get the starting portal, i guess
# this is found in the levle header
levelObjectsUnp: List[str] = levelString.split(';')
levelHeader: str = levelObjectsUnp.pop(0)
# ok next we get value
dictHeader: Dict[str, str] = parseKeyVarArray(levelHeader, ',')
levelSpeed: int
try:
levelSpeed = int(dictHeader['kA4'])
except BaseException:
# does rob just not put it ? no idea but let's not find out
levelSpeed = 0
# last object is gross
levelObjectsUnp.pop()
levelObjects: List[Dict[str, str]] = list(
map(lambda x: parseKeyVarArray(x, ','), levelObjectsUnp))
levelPortals: List[portalInfo] = getPortalList(levelObjects)
furthestX: float = 0.0
# probably could be optimized - we iterate over this list a lot
for objec in levelObjects:
furthestX = max(furthestX, float(objec['2']))
seconds: float = getSecondsFromxPos(
furthestX, getPortalInfo(levelSpeed), levelPortals)
return seconds
if __name__ == "__main__":
import sys
import os
import levelDownloader
print("~ Level Length Calculator by zmx (formula from stadust) ~")
levelFile: str = ''
levelString: bytes = b''
if os.getenv("MAIN", "false").lower() == "false":
levelDownloader.url = "https://absolllute.com/gdps/\
gdapi/downloadGJLevel19.php"
if len(sys.argv) != 2:
print(f"""Usage: {sys.argv[0]} <id>...
The following environment variables modify execution:
MAIN - download from 2.1""")
sys.exit()
try:
levelFile = sys.argv[1]
levelString, levelInfo = levelDownloader.downloadLevel(int(levelFile))
print(f'Downloaded level `{levelInfo["2"]}`')
except BaseException:
print("could not download level!")
sys.exit(1)
seconds = getLevelLength(levelString.decode('utf8'))
minutes, mSeconds = divmod(seconds, 60.0)
print(f'Length: {round(minutes)}m {math.ceil(mSeconds)}s')
# ceiling is more accurate to gd?