1class Legendary { 2 3 static init() { 4 5 // Needed to check against Q param. 6 this.quickParams = []; 7 8 this.animals = []; 9 this.layer = L.layerGroup(); 10 this.layer.addTo(MapBase.map); 11 this.context = document.querySelector('.menu-hidden[data-type=legendary_animals]'); 12 const pane = MapBase.map.createPane('animalSpawnPoint'); 13 pane.style.zIndex = 450; // markers on top of circle, but behind ânormalâ markers/shadows 14 pane.style.pointerEvents = 'none'; 15 16 this.onSettingsChanged(); 17 document.querySelectorAll('.menu-hidden[data-type="legendary_animals"] > *:first-child button').forEach((btn) => 18 btn.addEventListener('click', (e) => { 19 e.preventDefault(); 20 const showAll = e.target.getAttribute('data-text') === 'menu.show_all'; 21 Legendary.animals.forEach(animal => animal.onMap = showAll); 22 }) 23 ); 24 25 return Loader.promises['animal_legendary'].consumeJson(data => { 26 data.forEach(item => { 27 this.animals.push(new Legendary(item)); 28 this.quickParams.push(item.text); 29 }); 30 this.onLanguageChanged(); 31 console.info('%c[Legendary animals] Loaded!', 'color: #bada55; background: #242424'); 32 this.checkSpawnTime(); 33 }); 34 } 35 static onLanguageChanged() { 36 Menu.reorderMenu(this.context); 37 this.onSettingsChanged(); 38 } 39 static onSettingsChanged() { 40 this.animals.forEach(animal => animal.reinitMarker()); 41 } 42 43 // not idempotent (on the environment) 44 constructor(preliminary) { 45 Object.assign(this, preliminary); 46 this._shownKey = `shown.${this.text}`; 47 this.element = document.createElement('div'); 48 this.element.classList.add('collectible-wrapper'); 49 this.element.setAttribute('data-help', 'item'); 50 this.element.addEventListener('click', () => this.onMap = !this.onMap); 51 this.element.innerHTML = `<p class="collectible" data-text="${this.text}"></p>`; 52 Language.translateDom(this.element); 53 this.species = this.text.replace(/^mp_animal_|_legendary_\d+$/g, ''); 54 this.animalSpeciesKey = `rdo.Legendaries_category_time_${this.species}`; 55 this.preferred_weather = `map.weather.${this.preferred_weather}`; 56 this.trader_materials = this.trader_materials || 'map.cant_be_picked_up'; 57 this.trapper_value = this.trapper_value ? `$${this.trapper_value.toFixed(2)}` : 'map.cant_be_picked_up'; 58 this.trapper_pelt_value = `$${this.trapper_pelt_value.toFixed(2)}`; 59 this.trapper_part_value = `$${this.trapper_part_value.toFixed(2)}`; 60 this.sample_value = `$${this.sample_value.toFixed(2)}`; 61 this.reinitMarker(); 62 Legendary.context.appendChild(this.element); 63 } 64 // auto remove marker? from map, recreate marker, auto add? marker 65 reinitMarker() { 66 if (this.marker) Legendary.layer.removeLayer(this.marker); 67 this.marker = L.layerGroup(); 68 if (Settings.isLaBgEnabled) { 69 this.marker.addLayer(L.circle([this.x, this.y], { 70 color: this.isGreyedOut ? '#c4c4c4' : '#fdc607', 71 fillColor: this.isGreyedOut ? '#c4c4c4' : '#fdc607', 72 fillOpacity: linear(Settings.overlayOpacity, 0, 1, .1, .2), 73 radius: this.radius, 74 opacity: linear(Settings.overlayOpacity, 0, 1, .2, .6), 75 }) 76 .bindPopup(this.popupContent.bind(this), { minWidth: 400 }) 77 ); 78 } 79 const iconType = Settings.legendarySpawnIconType; 80 const spawnIconSize = Settings.legendarySpawnIconSize; 81 const isGold = MapBase.isDarkMode ? 'gold_' : ''; 82 83 this.spawnIcon = new L.Icon.TimedData({ 84 iconUrl: `./assets/images/icons/game/animals/legendaries/small/${iconType}_${isGold}${this.species}.png?nocache=${nocache}`, 85 iconSize: [16 * spawnIconSize, 16 * spawnIconSize], 86 iconAnchor: [8 * spawnIconSize, 8 * spawnIconSize], 87 time: this.spawn_time.reduce((acc, [start, end]) => [...acc, ...timeRange(start, end)], []), 88 }); 89 this.locations.forEach(point => 90 this.marker.addLayer(L.marker([point.x, point.y], { 91 icon: this.spawnIcon, 92 pane: 'animalSpawnPoint', 93 opacity: this.isGreyedOut ? .25 : 1, 94 }) 95 .bindPopup(this.popupContent.bind(this), { 96 minWidth: 400, 97 }) 98 ) 99 ); 100 if (!MapBase.isPreviewMode && Settings.isLaBgEnabled) {
101 const overlay = `assets/images/icons/game/animals/legendaries/${this.text}.svg?nocache=${nocache}`; 102 this.marker.addLayer(L.imageOverlay(overlay, [ 103 [this.x - this.radius, this.y - this.radius * 2], 104 [this.x + this.radius, this.y + this.radius * 2], 105 ], { 106 opacity: linear(Settings.overlayOpacity, 0, 1, 0.5, 1), 107 })); 108 } 109 this.onMap = this.onMap; 110 } 111 popupContent() { 112 const snippet = document.createElement('div'); 113 snippet.classList.add('handover-wrapper-with-no-influence'); 114 snippet.innerHTML = ` 115 <img class="legendary-animal-popup-image" src="assets/images/icons/game/animals/legendaries/${this.text}.svg" alt="Animal"> 116 <h1 data-text="${this.text}"></h1> 117 <div class="accordion accordion-flush" id="legendary-accordion"> 118 <div class="accordion-item"> 119 <h2 class="accordion-header"> 120 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#legendary-collapse-cooldown" aria-expanded="true" aria-controls="legendary-collapse-cooldown">Cooldown end</button> 121 </h2> 122 <div id="legendary-collapse-cooldown" class="accordion-collapse collapse show" data-bs-parent="#legendary-accordion"> 123 <div class="accordion-body"> 124 </div> 125 </div> 126 </div> 127 <div class="accordion-item"> 128 <h2 class="accordion-header"> 129 <button class="accordion-button" type="button" data-text="map.user_pins.placeholder_desc" data-bs-toggle="collapse" data-bs-target="#legendary-collapse-desc" aria-expanded="true" aria-controls="legendary-collapse-desc">Description</button> 130 </h2> 131 <div id="legendary-collapse-desc" class="accordion-collapse collapse" data-bs-parent="#legendary-accordion"> 132 <div class="accordion-body" data-text="${Language.get(this.text + '.desc')}"> 133 </div> 134 </div> 135 </div> 136 <div class="accordion-item"> 137 <h2 class="accordion-header"> 138 <button class="accordion-button collapsed" type="button" data-text="map.tips" data-bs-toggle="collapse" data-bs-target="#legendary-collapse-tips" aria-expanded="false" aria-controls="legendary-collapse-tips">Tips</button> 139 </h2> 140 <div id="legendary-collapse-tips" class="accordion-collapse collapse" data-bs-parent="#legendary-accordion"> 141 <div class="accordion-body" data-text="map.legendary_animal.desc"> 142 </div> 143 </div> 144 </div> 145 </div> 146 <span class="legendary-properties"> 147 <p class="legendary-spawn-time" data-text="map.legendary.spawn_time_string"></p> 148 <p class="legendary-preferred-weather" data-text="map.legendary.preferred_weather"></p> 149 <p class="legendary-trader-materials" data-text="map.legendary.trader_materials"></p> 150 <p class="legendary-trader-pelt-materials" data-text="map.legendary.trader_pelt_materials"></p> 151 <p class="legendary-trapper-value" data-text="map.legendary.trapper_value"></p> 152 <p class="legendary-trapper-pelt-value" data-text="map.legendary.trapper_pelt_value"></p> 153 <p class="legendary-trapper-part-value" data-text="map.legendary.trapper_part_value"></p> 154 <p class="legendary-sample-value" data-text="map.legendary.sample_value"></p> 155 </span> 156 <button type="button" class="btn btn-info remove-button remove-animal-category" data-text="map.remove.animal_category"></button> 157 <button type="button" class="btn btn-info remove-button reset-animal-timer ${this.isGreyedOut ? 'active' : ''}" data-text="map.reset_animal_timer" aria-pressed="${this.isGreyedOut ? 'true' : ''}"></button> 158 <button type="button" class="btn btn-info remove-button remove-animal" data-text="map.remove"></button> 159 `; 160 Language.translateDom(snippet); 161 162 this.spawn_time_string = this.spawn_time.map(timeRange => timeRange.map(hour => convertToTime(hour)).join(' - ')).join(', '); 163 164 snippet.querySelectorAll('span > p').forEach(p => { 165 const propertyText = Language.get(p.getAttribute('data-text')) 166 .replace(/{([a-z_]+)}/, (full, key) => Language.get(String(this[key]))); 167 p.textContent = propertyText; 168 }); 169 170 const cooldownBtn = snippet.querySelector('#legendary-collapse-cooldown').closest('.accordion-item').querySelector('button'); 171 if (this.isGreyedOut) { 172 cooldownBtn.textContent = Language.get('map.legendary_animal_cooldown_end_time').split(/[:ï¼]/)[0]; 173 snippet.querySelector('#legendary-collapse-cooldown .accordion-body').textContent = new Date( 174 +localStorage.getItem(this.animalSpeciesKey)
175 ).toLocaleString(Settings.language, { weekday: 'long', day: 'numeric', month: 'short', hour: 'numeric', minute: '2-digit' }); 176 } 177 178 snippet.querySelector('#legendary-collapse-desc').classList.toggle('show', !this.isGreyedOut); 179 cooldownBtn.style.display = this.isGreyedOut ? '' : 'none'; 180 const btnRemoveAnimalCategory = snippet.querySelector('button.remove-animal-category'); 181 btnRemoveAnimalCategory.style.display = !this.isGreyedOut ? '' : 'none'; 182 btnRemoveAnimalCategory.addEventListener('click', () => this.isGreyedOut = true); 183 const btnResetAnimalTimer = snippet.querySelector('button.reset-animal-timer'); 184 btnResetAnimalTimer.style.display = this.isGreyedOut ? '' : 'none'; 185 btnResetAnimalTimer.addEventListener('click', () => this.isGreyedOut = false); 186 snippet.querySelector('button.remove-animal').addEventListener('click', () => this.onMap = false); 187 188 return snippet; 189 } 190 191 static toggleAnimalSpecies(animalSpecies) { 192 Legendary.animals.forEach(animal => { 193 if (animal.species === animalSpecies) 194 animal.reinitMarker(); 195 }); 196 } 197 198 static checkSpawnTime() { 199 const animalSpeciesSet = new Set(); 200 Legendary.animals.forEach(animal => { 201 animalSpeciesSet.add(animal.species); 202 }); 203 204 setInterval(() => { 205 animalSpeciesSet.forEach(animalSpecies => { 206 const key = `rdo.Legendaries_category_time_${animalSpecies}`; 207 if (!(key in localStorage)) return; 208 209 const time = localStorage.getItem(key); 210 if (time <= Date.now()) { 211 localStorage.removeItem(key); 212 Legendary.toggleAnimalSpecies(animalSpecies); 213 } 214 }); 215 }, 2000); 216 } 217 218 set isGreyedOut(state) { 219 if (state) 220 localStorage.setItem(this.animalSpeciesKey, Date.now() + 259200000); // 259200000 ms = 72 hours 221 else 222 localStorage.removeItem(this.animalSpeciesKey); 223 224 Legendary.toggleAnimalSpecies(this.species); 225 } 226 227 get isGreyedOut() { 228 return !!localStorage.getItem(this.animalSpeciesKey); 229 } 230 231 set onMap(state) { 232 if (!this.marker) return; 233 if (state) { 234 Legendary.layer.addLayer(this.marker); 235 this.element.classList.remove('disabled'); 236 if (!MapBase.isPreviewMode) 237 localStorage.setItem(`rdo.${this._shownKey}`, 'true'); 238 } else { 239 Legendary.layer.removeLayer(this.marker); 240 this.element.classList.add('disabled'); 241 if (!MapBase.isPreviewMode) 242 localStorage.removeItem(`rdo.${this._shownKey}`); 243 } 244 } 245 246 get onMap() { 247 return !!localStorage.getItem(`rdo.${this._shownKey}`); 248 } 249 250 static onCategoryToggle() { 251 Legendary.animals.forEach(animal => animal.onMap = animal.onMap); 252 } 253}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.