PageSourceSearch

https://jeanropke.github.io/RDOMap/assets/js/legendary.js?nocache=1552

js jeanropke.github.io collected 2026-10-03 09:23:34 UTC 11,614 bytes, 253 lines download raw bytes

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.