(options, terrain)
| 143 | } |
| 144 | |
| 145 | constructor (options, terrain) { |
| 146 | super(options) |
| 147 | this.onMouseMovePortals = this.onMouseMovePortals.bind(this) |
| 148 | this.onWindowResize = this.onWindowResize.bind(this) |
| 149 | this.terrain = terrain |
| 150 | if (/^classCode/.test(this.terrain)) { |
| 151 | this.terrain = '' // Stop /play?classCode= from making us try to play a classCode campaign |
| 152 | } |
| 153 | |
| 154 | this.editorMode = options?.editorMode |
| 155 | this.requiresSubscription = !me.isPremium() |
| 156 | if (this.editorMode && !this.terrain) { |
| 157 | this.terrain = 'dungeon' |
| 158 | } |
| 159 | // Level completion by levelID (slug): used for UI (stars, locked state, header count). Key = session.levelID. |
| 160 | this.levelStatusMap = {} |
| 161 | // Level completion by level original id: use for lookups by original (e.g. levelToUnlock). Key = session.level.original. |
| 162 | this.levelOriginalStatusMap = {} |
| 163 | this.levelPlayCountMap = {} |
| 164 | this.levelDifficultyMap = {} |
| 165 | this.levelScoreMap = {} |
| 166 | this.moduleCampaignStatsMap = {} |
| 167 | this.courseLevelsLoaded = false |
| 168 | this.highlightedCampaign = null |
| 169 | this.highlightedConnectionIndex = null |
| 170 | |
| 171 | if (this.redirectMiddleware()) { |
| 172 | return |
| 173 | } |
| 174 | |
| 175 | if (userUtils.shouldShowLibraryLoginModal() && me.isAnonymous()) { |
| 176 | this.openModalView(new CreateAccountModal({ startOnPath: 'individual-basic' })) |
| 177 | } |
| 178 | if (!this.editorMode) { |
| 179 | this.sessions = this.supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', { cache: false }, 1).model |
| 180 | this.listenToOnce(this.sessions, 'sync', this.onSessionsLoaded) |
| 181 | } |
| 182 | if (!this.terrain) { |
| 183 | this.campaigns = this.supermodel.loadCollection(new CampaignsCollection(), 'campaigns', null, 1).model |
| 184 | this.listenToOnce(this.campaigns, 'sync', this.onCampaignsLoaded) |
| 185 | this.probablyCachedMusic = storage.load('loaded-menu-music') |
| 186 | const musicDelay = this.probablyCachedMusic ? 1000 : 10000 |
| 187 | const delayMusicStart = () => setTimeout(() => { |
| 188 | if (!this.destroyed) { |
| 189 | this.playMusic() |
| 190 | } |
| 191 | }, musicDelay) |
| 192 | this.playMusicTimeout = delayMusicStart() |
| 193 | return |
| 194 | } |
| 195 | if (this.terrain) { |
| 196 | this.campaign = new Campaign({ _id: this.terrain }) |
| 197 | this.campaign = this.supermodel.loadModel(this.campaign).model |
| 198 | |
| 199 | this.listenToOnce(this.campaign, 'sync', () => { |
| 200 | // Check for HackStack redirect immediately after campaign loads |
| 201 | const redirectInfo = this.checkHackstackRedirect() |
| 202 | if (redirectInfo) { |
nothing calls this directly
no test coverage detected