Add basic Splatfest support

This commit is contained in:
Matt Isenhower
2022-09-23 09:09:45 -04:00
parent 544bd008be
commit e0de8b4c3f
7 changed files with 163 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

View File

@@ -60,7 +60,7 @@
<script setup>
import { computed } from '@vue/reactivity';
import { useAnarchyOpenSchedulesStore, useAnarchySeriesSchedulesStore, useRegularSchedulesStore } from '../stores/schedules';
import { useAnarchyOpenSchedulesStore, useAnarchySeriesSchedulesStore, useRegularSchedulesStore, useSplatfestSchedulesStore } from '../stores/schedules';
import ProductContainer from './ProductContainer.vue';
import StageImage from './StageImage.vue';
import ScheduleRow from './ScheduleRow.vue';
@@ -100,6 +100,13 @@ const types = {
img: battleBankaraSvg,
bg: 'bg-splatoon-battle-ranked bg-tapes',
},
splatfest: {
name: 'Splatfest Battle',
badge: null,
store: useSplatfestSchedulesStore(),
img: battleRegularSvg,
bg: 'bg-splatoon-battle-regular bg-tapes',
},
};
const type = computed(() => types[props.type]);

View File

@@ -0,0 +1,73 @@
<template>
<ProductContainer class="pt-10 pb-4" bg="bg-zinc-500 bg-camo-purple">
<div class="space-y-2">
<div class="font-splatoon1 text-3xl mx-2">
{{ title }}
</div>
<div class="flex justify-center mx-2">
<div class="font-splatoon2 text-zinc-200 text-lg text-center text-shadow bg-zinc-700 px-4 py-1 rounded-full bg-opacity-50 backdrop-blur-sm">
{{ festival.title }}
</div>
</div>
<div>
<img :src="props.festival.image.url" />
<div class="flex -mt-3 mb-4">
<template v-for="team in festival.teams" :key="team.id">
<div class="flex-1 flex justify-center items-center">
<SquidTape class="font-splatoon2 text-shadow -rotate-3" bg="" :style="`background-color: ${toRgba(team.color)};`">
<div class="px-2">
{{ team.teamName }}
</div>
</SquidTape>
</div>
</template>
</div>
</div>
<div class="font-splatoon2 text-splatoon-yellow text-center mx-2">
{{ formatDateTime(festival.startTime) }}
&ndash;
{{ formatDateTime(festival.endTime) }}
</div>
</div>
</ProductContainer>
</template>
<script setup>
import { computed } from 'vue';
import ProductContainer from './ProductContainer.vue';
import { STATUS_PAST, STATUS_ACTIVE, STATUS_UPCOMING } from '@/stores/splatfests';
import SquidTape from './SquidTape.vue';
import { formatDateTime } from '../common/time';
const props = defineProps({
festival: Object,
});
const title = computed(() => {
switch (props.festival.status) {
case STATUS_PAST:
return 'Recent Splatfest';
case STATUS_UPCOMING:
return 'Upcoming Splatfest';
case STATUS_ACTIVE:
default:
return 'Splatfest';
}
});
function toRgba(color) {
return `rgba(${color.r * 255}, ${color.g * 255}, ${color.b * 255}, ${color.a})`;
}
</script>
<style scoped>
:deep(.bg-camo-purple) {
background-image: url('@/assets/img/camo-transparent-bg.png'),
linear-gradient(180deg, rgba(2, 0, 36, 0.10) 0%, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, 0.25) 100%);
background-size: cover;
}
</style>

View File

@@ -41,11 +41,13 @@ function defineEndpointStore(id, endpoint, transform = null) {
export const useSchedulesDataStore = defineEndpointStore('schedules', '/data/schedules.json', d => d.data);
export const useGearDataStore = defineEndpointStore('gear', '/data/gear.json', d => d.data);
export const useFestivalsDataStore = defineEndpointStore('festivals', '/data/festivals.json');
export const useDataStore = defineStore('data', () => {
const stores = {
schedules: useSchedulesDataStore(),
gear: useGearDataStore(),
festivals: useFestivalsDataStore(),
};
let updateDataTimer;
@@ -93,6 +95,7 @@ export const useDataStore = defineStore('data', () => {
stopUpdating,
schedules: computed(() => stores.schedules.data),
gear: computed(() => stores.gear.data),
festivals: computed(() => stores.festivals.data),
}
});

View File

@@ -28,17 +28,24 @@ export const useRegularSchedulesStore = defineScheduleStore('regular', {
settings: node => node.regularMatchSetting,
});
// Anarchy Battle
// Anarchy Battle (Series)
export const useAnarchySeriesSchedulesStore = defineScheduleStore('anarchy/series', {
nodes: () => useSchedulesDataStore().data?.bankaraSchedules.nodes,
settings: node => node.bankaraMatchSettings?.find(s => s.mode === 'CHALLENGE'),
});
// Anarchy Battle (Open)
export const useAnarchyOpenSchedulesStore = defineScheduleStore('anarchy/open', {
nodes: () => useSchedulesDataStore().data?.bankaraSchedules.nodes,
settings: node => node.bankaraMatchSettings?.find(s => s.mode === 'OPEN'),
});
// Splatfest Battle
export const useSplatfestSchedulesStore = defineScheduleStore('splatfest', {
nodes: () => useSchedulesDataStore().data?.festSchedules.nodes,
settings: node => node.festMatchSetting,
});
// Salmon Run
export const useSalmonRunSchedulesStore = defineScheduleStore('salmonRun', {
nodes: () => useSchedulesDataStore().data?.coopGroupingSchedule.regularSchedules.nodes,
@@ -49,5 +56,6 @@ if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useRegularSchedulesStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useAnarchySeriesSchedulesStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useAnarchyOpenSchedulesStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useSplatfestSchedulesStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useSalmonRunSchedulesStore, import.meta.hot));
}

51
src/stores/splatfests.mjs Normal file
View File

@@ -0,0 +1,51 @@
import { acceptHMRUpdate, defineStore } from "pinia";
import { computed } from "vue";
import { useFestivalsDataStore } from "./data.mjs";
import { useTimeStore } from "./time.mjs";
export const STATUS_PAST = 'past';
export const STATUS_ACTIVE = 'active';
export const STATUS_UPCOMING = 'upcoming';
function defineSplatfestRegionStore(region) {
return defineStore(`splatfests/${region}`, () => {
const time = useTimeStore();
const store = useFestivalsDataStore();
function getStatus(node) {
switch (true) {
case time.isActive(node.startTime, node.endTime):
return STATUS_ACTIVE;
case time.isUpcoming(node.startTime):
return STATUS_UPCOMING;
default:
return STATUS_PAST;
}
}
const festivals = computed(() => store.data?.[region]?.data.festRecords.nodes.map(node => {
return {
...node,
status: getStatus(node),
};
}));
const previousFestivals = computed(() => festivals.value?.filter(f => f.status === STATUS_PAST));
const activeFestival = computed(() => festivals.value?.find(f => f.status === STATUS_ACTIVE));
const upcomingFestival = computed(() => festivals.value?.find(f => f.status === STATUS_UPCOMING))
return { festivals, previousFestivals, activeFestival, upcomingFestival };
});
}
export const useUSSplatfestsStore = defineSplatfestRegionStore('US');
export const useEUSplatfestsStore = defineSplatfestRegionStore('EU');
export const useJPSplatfestsStore = defineSplatfestRegionStore('JP');
export const useAPSplatfestsStore = defineSplatfestRegionStore('AP');
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useUSSplatfestsStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useEUSplatfestsStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useJPSplatfestsStore, import.meta.hot));
import.meta.hot.accept(acceptHMRUpdate(useAPSplatfestsStore, import.meta.hot));
}

View File

@@ -1,12 +1,25 @@
<template>
<MainLayout title="Map Schedules">
<div class="grow flex items-center justify-center">
<div class="mx-4 md:mx-12 max-w-screen-2xl w-full">
<div class="flex flex-col space-y-6 md:flex-row md:space-x-6 md:space-y-0">
<div class="mx-4 md:mx-12 max-w-screen-2xl w-full space-y-10">
<div class="flex flex-col space-y-6 md:flex-row md:space-x-6 md:space-y-0 justify-center" v-if="usSplatfests.activeFestival">
<div>
<SplatfestBox :festival="usSplatfests.activeFestival" class="flex-1 max-w-md md:-rotate-1" />
</div>
<ScheduleBox type="splatfest" class="flex-1 max-w-lg md:rotate-1" />
</div>
<div class="flex flex-col space-y-6 md:flex-row md:space-x-6 md:space-y-0" v-else>
<ScheduleBox type="regular" class="flex-1 md:-rotate-1" />
<ScheduleBox type="anarchySeries" class="flex-1 md:rotate-1" />
<ScheduleBox type="anarchyOpen" class="flex-1 md:-rotate-1" />
</div>
<div class="flex justify-center" v-if="usSplatfests.upcomingFestival">
<SplatfestBox
:festival="usSplatfests.upcomingFestival"
class="flex-1 max-w-md md:-rotate-1"
/>
</div>
</div>
</div>
</MainLayout>
@@ -15,4 +28,8 @@
<script setup>
import MainLayout from '@/layouts/MainLayout.vue'
import ScheduleBox from '../components/ScheduleBox.vue';
import { useUSSplatfestsStore } from '@/stores/splatfests';
import SplatfestBox from '../components/SplatfestBox.vue';
const usSplatfests = useUSSplatfestsStore();
</script>