Files
granblue-party/Replicard.vue
2026-03-27 10:14:29 +03:00

340 lines
11 KiB
Vue

<template>
<div class="flex flex-col">
<h1 class="self-center mb-8">Replicard Sandbox Maps</h1>
<div class="flex flex-row flex-wrap mb-4 items-center gap-4">
<button class="btn" :class="show_help ? 'btn-blue' : 'btn-white'" @click="show_help = ! show_help">
<fa-icon :icon="['fas', 'info-circle']" class="text-xl"></fa-icon> Usage
</button>
<checkbox v-model="show_loot">Show loot</checkbox>
<checkbox v-model="show_progression">Show progression</checkbox>
<button class="btn btn-blue" v-if="show_progression && isUserLogged" @click="save()">
<fa-icon :icon="['fas', 'save']" class="text-xl"></fa-icon> Save progression
</button>
</div>
<!-- Usage -->
<div class="bg-secondary self-center rounded p-4 mb-2" v-if="show_help">
<h2>Number of fights</h2>
<p class="pb-4">
The number above each circle represents the number of fights needed to guarantee the appearance of a Defender.
</p>
<h2>To fight the Boss</h2>
<p class="pb-4">
To fight the Boss of each zone, you need components dropped by Defenders.<br>
Chain fights of a node until the Defender appears.<br>
Each component is indicated in parentheses after the color of its Defender.
</p>
<h2>Track your progression</h2>
<p class="pb-4">
Click on the stars to track your progression.<br>
Each node has 3 quests. Add a star when you complete the corresponding quest.<br>
Mouseover the stars to see the name of the monster.
</p>
</div>
<!-- Tabs -->
<span class="flex flex-col w-full">
<div class="self-start flex flex-row flex-wrap border-primary border-b font-bold w-full">
<a
v-for="(zone, letter) in getZones"
:key="letter"
@click="show_tab = letter"
class="px-4 py-2 text-primary cursor-pointer rounded-t"
:class="show_tab === letter ? 'bg-secondary' : ''"
>
{{ zone.name }}
</a>
</div>
<div v-if="currentZone.boss4">
<div><span class="text-pink-500">Pink:</span> {{ currentZone.boss1 }} (Gospel of Egeiro)</div>
<div><span class="text-emerald-500">Green:</span> {{ currentZone.boss2 }} (Gospel of Genea)</div>
<div><span class="text-amber-500">Yellow:</span> {{ currentZone.boss3 }} (Gospel of Thysia)</div>
<div><span class="text-blue-500">Blue:</span> {{ currentZone.boss4 }} (Gospel of Analipsis)</div>
</div>
<div v-else-if="currentZone.boss3">
<div><span class="text-pink-500">Pink:</span> {{ currentZone.boss1 }} (Organ)</div>
<div><span class="text-emerald-500">Green:</span> {{ currentZone.boss2 }} (Rib)</div>
<div><span class="text-amber-500">Yellow:</span> {{ currentZone.boss3 }} (Core)</div>
</div>
<div v-else>
<div><span class="text-pink-500">Pink:</span> {{ currentZone.boss1 }} (Invocation)</div>
<div><span class="text-emerald-500">Green:</span> {{ currentZone.boss2 }} (Masquerade)</div>
</div>
<div class="relative bg-primary overflow-x-auto">
<picture v-if="show_loot" class="block" :style="currentZoneWidth">
<source type="image/webp" :srcset="'/img/arcarum/replicard_' + show_tab + '_loot.webp'">
<img :src="'/img/arcarum/replicard_' + show_tab + '_loot.png'">
</picture>
<picture :class="show_loot ? 'absolute top-0 left-0' : ''" :style="currentZoneWidth">
<source type="image/webp" :srcset="'/img/arcarum/replicard_' + show_tab + '.webp'">
<img :src="'/img/arcarum/replicard_' + show_tab + '.png'">
</picture>
<span
v-if="show_progression"
class="absolute top-0 left-0"
:style="currentZoneWidth"
>
<div class="relative">
<stars-line
v-for="(star, index) in currentZone.stars"
:key="index"
class="absolute bg-black"
:style="'top: ' + star.top + 'px; left: ' + star.left + 'px;'"
:base="2"
:extra="3"
:current.sync="progression[show_tab][index]"
:max="3"
:title="star.name"
></stars-line>
</div>
</span>
</div>
</span>
</div>
</template>
<script>
import Utils from '@/js/utils.js'
import Checkbox from '@/components/common/Checkbox.vue'
import StarsLine from '@/components/StarsLine.vue'
import replicardMixin from '@/store/modules/replicard'
const lsMgt = new Utils.LocalStorageMgt('Replicard');
const ZONES = {
'E': {
name: 'Eletio',
boss1: 'The Devil',
boss2: 'The Sun',
boss3: 'The Star',
width: '1377',
stars: [
{top: 140, left: 45, name: 'Slithering Seductress'},
{top: 135, left: 322, name: 'Living Lightning Rod'},
{top: 265, left: 272, name: 'Eletion Drake'},
{top: 142, left: 553, name: 'Paradoxical Gate'},
{top: 128, left: 685, name: 'Blazing Everwing'},
{top: 208, left: 812, name: 'Death Seer'},
{top: 66, left: 965, name: 'Hundred-Armed Hulk'},
{top: 193, left: 1135, name: 'Terror Trifecta'},
{top: 265, left: 1125, name: 'Rageborn One'},
{top: 135, left: 1280, name: 'Eletion Glider'}
]
},
'F': {
name: 'Faym',
boss1: 'Justice',
boss2: 'The Moon',
boss3: 'Death',
width: '1377',
stars: [
{top: 146, left: 30, name: 'Trident Grandmaster'},
{top: 206, left: 167, name: 'Hoarfrost Icequeen'},
{top: 263, left: 352, name: 'Oceanic Archon'},
{top: 146, left: 508, name: 'Farsea Predator'},
{top: 206, left: 646, name: 'Faymian Fortress'},
{top: 146, left: 791, name: 'Draconic Simulacrum'},
{top: 146, left: 993, name: 'Azureflame Dragon'},
{top: 263, left: 1091, name: 'Eye of Sorrow'},
{top: 149, left: 1275, name: 'Mad Shearwielder'},
{top: 154, left: 1132, name: 'Faymian Gun'}
]
},
'G': {
name: 'Goliath',
boss1: 'The Hanged Man',
boss2: 'The Tower',
boss3: 'Death',
width: '1377',
stars: [
{top: 257, left: 164, name: 'Avatar of Avarice'},
{top: 29, left: 237, name: 'Temptation\'s Guide'},
{top: 143, left: 285, name: 'World\'s Veil'},
{top: 138, left: 592, name: 'Bloodstained Barbarian'},
{top: 257, left: 563, name: 'Goliath Keeper'},
{top: 120, left: 810, name: 'Frenzied Howler'},
{top: 141, left: 963, name: 'Vestige of Truth'},
{top: 257, left: 857, name: 'Goliath Vanguard'},
{top: 194, left: 1085, name: 'Writhing Despair'},
{top: 198, left: 1282, name: 'Goliath Triune'}
]
},
'H': {
name: 'Harbinger',
boss1: 'Temperance',
boss2: 'Judgement',
boss3: 'The Star',
width: '1377',
stars: [
{top: 174, left: 29, name: 'Dirgesinger'},
{top: 34, left: 208, name: 'Vengeful Demigod'},
{top: 250, left: 159, name: 'Wildwind Conjurer / Fullthunder Conjurer'},
{top: 146, left: 334, name: 'Harbinger Simurgh'},
{top: 260, left: 486, name: 'Harbinger Hardwood'},
{top: 188, left: 583, name: 'Demanding Stormgod'},
{top: 143, left: 960, name: 'Harbinger Tyrant'},
{top: 256, left: 1099, name: 'Phantasmagoric Aberration'},
{top: 186, left: 1220, name: 'Dimentional Riftwalker'},
{top: 48, left: 670, name: 'Harbinger Stormer'}
]
},
'I': {
name: 'Invidia',
boss1: 'The Sun / The Devil',
boss2: 'The Star',
width: '884',
stars: [
]
},
'J': {
name: 'Joculator',
boss1: 'The Moon / Justice',
boss2: 'Death',
width: '884',
stars: [
]
},
'K': {
name: 'Kalendae',
boss1: 'Death',
boss2: 'The Hanged Man / The Tower',
width: '884',
stars: [
]
},
'L': {
name: 'Liber',
boss1: 'Temperance / Judgment',
boss2: 'The Star',
width: '884',
stars: [
]
},
'M': {
name: 'Mundus',
boss1: ' ',
boss2: ' ',
boss3: ' ',
boss4: ' ',
width: 960,
stars: []
}
}
export default {
components: {
Checkbox,
StarsLine
},
mixins: [
replicardMixin
],
head: {
title: 'Granblue.Party - Replicard Sandbox Maps',
desc: 'View Replicard Sandbox Maps with loots, colors, and progression for each node',
image: 'https://www.granblue.party/img/card_replicard.jpg',
keywords: 'Replicard, Sandbox, Maps, Arcarum, Evoker, Eletio, Faym, Goliath, Harbinger, Invidia, Joculator, Kalendae, Liber, Mundus'
},
data() {
return {
show_help: false,
show_loot: true,
show_progression: true,
scrollbar: true,
show_tab: 'E',
};
},
methods: {
save() {
this.axios.post('/replicard/save', this.progression)
.then(response => this.$store.dispatch('addMessage', {message: 'Data saved successfully'}))
.catch(error => this.$store.dispatch('addAxiosErrorMessage', error));
},
loadServerData() {
if (this.data_fetched === false) {
return this.axios.get('/replicard/load')
.then(response => {
if (response.data !== null) {
this.$set(this, 'progression', response.data);
this.data_fetched = true;
}
})
.catch(error => this.$store.dispatch('addAxiosErrorMessage', error));
}
},
loadData() {
if (this.isUserLogged) {
this.loadServerData();
}
else {
this.$store.commit('replicard/resetReplicard');
const progression = lsMgt.fetchValue('progression');
if (progression !== undefined) {
this.progression = progression;
}
}
},
},
computed: {
isUserLogged() {
return this.$store.getters.getUserId !== null;
},
getZones() {
return ZONES;
},
currentZone() {
return this.getZones[this.show_tab];
},
currentZoneWidth() {
return "min-width: " + this.currentZone.width + "px;";
},
progression: {
get() { return this.$store.state.replicard.progression },
set(value) { this.$store.commit('replicard/setReplicardData', value) }
},
data_fetched: {
get() { return this.$store.state.replicard.data_fetched },
set(value) { this.$store.commit('replicard/setReplicardFetched', value) }
},
},
watch: {
'$store.getters.getUserId'() {
this.data_fetched = false;
this.loadData();
},
show_loot() {
lsMgt.setValue('show_loot', this);
},
show_tab() {
lsMgt.setValue('show_tab', this);
},
progression: {
handler() {
if ( ! this.isUserLogged) {
lsMgt.setValue('progression', this);
}
},
deep: true
},
},
serverPrefetch() {
if (this.isUserLogged) {
return this.loadServerData();
}
},
mounted() {
lsMgt.getValue(this, 'show_loot');
lsMgt.getValue(this, 'show_tab');
this.loadData();
},
};
</script>