Implemented merging/levels

This commit is contained in:
thepaperpilot 2023-02-18 18:09:06 -06:00
parent 17ffc977fb
commit b0e0f8aeac
5 changed files with 175 additions and 37 deletions

View file

@ -10,6 +10,10 @@
<link rel="alternate icon" type="image/png" sizes="48x48" href="/favicon.ico"> <link rel="alternate icon" type="image/png" sizes="48x48" href="/favicon.ico">
<meta name="theme-color" content="#2E3440"> <meta name="theme-color" content="#2E3440">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Mynerve&display=swap" rel="stylesheet">
<title>Profectus</title> <title>Profectus</title>
<meta name="description" content="A project made in Profectus"/> <meta name="description" content="A project made in Profectus"/>

View file

@ -1,9 +1,26 @@
<template> <template>
<Tooltip <Tooltip
:display="character ? characters[character.type].nickname : ''" :display="character && selected == null ? characters[character.type].nickname : ''"
:direction="Direction.Up" :direction="Direction.Up"
> >
<div class="character" :class="{ selected, empty: character == null }"> <div
class="character"
:class="{ selected: isSelected, empty: character == null && selected == null }"
>
<span class="move-indicator" v-if="character == null && selected != null">
<span class="material-icons">straight</span></span
>
<span
class="move-indicator"
v-if="
character != null &&
selected != null &&
!isSelected &&
character.type === selected.type &&
character.exp < 6
"
><span class="material-icons">merge</span></span
>
<span class="character-display" v-if="character != null"> <span class="character-display" v-if="character != null">
<img :src="characters[character.type].display" /> <img :src="characters[character.type].display" />
</span> </span>
@ -15,6 +32,18 @@
<span class="material-icons"> extension </span> <span class="material-icons"> extension </span>
{{ character?.presence }} {{ character?.presence }}
</span> </span>
<span class="level-display" v-if="character != null">
<span class="level">{{ level }}</span>
<span class="segments">
<span
v-for="i in segments"
:key="i"
class="segment"
:class="{ filled: filledSegments >= i }"
>
</span>
</span>
</span>
</div> </div>
</Tooltip> </Tooltip>
</template> </template>
@ -22,16 +51,39 @@
<script setup lang="ts"> <script setup lang="ts">
import Tooltip from "features/tooltips/Tooltip.vue"; import Tooltip from "features/tooltips/Tooltip.vue";
import { Direction } from "util/common"; import { Direction } from "util/common";
import { computed, watch } from "vue";
import { characters } from "./projEntry"; import { characters } from "./projEntry";
defineProps<{ const props = defineProps<{
character?: { character?: Character | null;
type: string; isSelected?: boolean;
relevancy: number; selected?: Character | null;
presence: number;
} | null;
selected?: boolean;
}>(); }>();
const level = computed(() => {
const exp = props.character?.exp ?? 0;
if (exp < 3) {
return 1;
}
if (exp < 6) {
return 2;
}
return 3;
});
const segments = computed(() => {
const exp = props.character?.exp ?? 0;
if (exp < 3) {
return 2;
}
return 3;
});
const filledSegments = computed(() => {
const exp = props.character?.exp ?? 0;
if (exp < 3) {
return exp - 1;
}
return exp - 3;
});
</script> </script>
<style scoped> <style scoped>
@ -81,6 +133,7 @@ defineProps<{
left: 0; left: 0;
right: 0; right: 0;
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%2388C0D0' stroke-width='8' stroke-dasharray='10%25%2c90%25' stroke-dashoffset='5' stroke-linecap='square'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%2388C0D0' stroke-width='8' stroke-dasharray='10%25%2c90%25' stroke-dashoffset='5' stroke-linecap='square'/%3e%3c/svg%3e");
transform: scale(1.5);
} }
.relevancy-display { .relevancy-display {
@ -116,4 +169,71 @@ defineProps<{
font-size: 200%; font-size: 200%;
z-index: -1; z-index: -1;
} }
.level-display {
position: absolute;
bottom: -10%;
right: -20%;
color: var(--accent2);
font-size: xx-large;
text-shadow: -1px 1px 0 var(--outline), 1px 1px 0 var(--outline), 1px -1px 0 var(--outline),
-1px -1px 0 var(--outline);
}
.level {
background: var(--locked);
border-radius: 4px;
border-top-left-radius: 50%;
border-bottom-left-radius: 0;
border: solid 2px var(--raised-background);
padding-left: 4px;
padding-right: 4px;
}
.level,
.level::before {
font-family: "Mynerve", cursive;
}
.level::before {
content: "lv";
font-size: large;
}
.segments {
position: absolute;
right: calc(100% - 2px);
width: 100%;
height: 25%;
bottom: -2px;
background: var(--locked);
border-radius: 20px 0 0 0;
border: solid 2px var(--raised-background);
display: flex;
overflow: hidden;
}
.segment {
width: 100%;
height: 100%;
}
.segment:not(:last-child) {
border-right: solid 3px var(--raised-background);
}
.segment.filled {
background-color: var(--accent2);
}
.move-indicator {
position: absolute;
transform: translateX(-50%) rotate(180deg);
top: -75%;
left: 50%;
font-size: xxx-large;
}
.move-indicator .material-icons {
font-size: xxx-large;
}
</style> </style>

View file

@ -78,7 +78,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
const turn = ref<number>(0); const turn = ref<number>(0);
const gold = ref<number>(0); const gold = ref<number>(0);
const team = ref<(Character | null)[]>([null, null, null]); const team = ref<(Character | null)[]>([null, null, null]);
const shop = ref<(string | null)[]>([]); const shop = ref<(Character | null)[]>([]);
const selectedCharacter = ref<number | null>(null); const selectedCharacter = ref<number | null>(null);
const selectedShopItem = ref<number | null>(null); const selectedShopItem = ref<number | null>(null);
@ -123,37 +123,34 @@ export const main = createLayer("main", function (this: BaseLayer) {
<h2>{nickname.value}</h2> <h2>{nickname.value}</h2>
<Spacer height="10vh" /> <Spacer height="10vh" />
<Row> <Row>
<CharacterSlot {new Array(3).fill(0).map((_, i) => (
character={team.value[0]} <CharacterSlot
selected={selectedCharacter.value === 0} character={team.value[i]}
onClick={clickCharacter(0)} isSelected={selectedCharacter.value === i}
/> selected={
<CharacterSlot selectedCharacter.value == null
character={team.value[1]} ? selectedShopItem.value == null ||
selected={selectedCharacter.value === 1} (team.value[i] != null &&
onClick={clickCharacter(1)} shop.value[selectedShopItem.value]?.type !==
/> team.value[i]?.type)
<CharacterSlot ? null
character={team.value[2]} : shop.value[selectedShopItem.value]
selected={selectedCharacter.value === 2} : team.value[selectedCharacter.value]
onClick={clickCharacter(2)} }
/> onClick={clickCharacter(i)}
/>
))}
</Row> </Row>
<Spacer height="10vh" /> <Spacer height="10vh" />
<Row> <Row>
{shop.value.map((item, i) => ( {shop.value.map((item, i) => (
<CharacterSlot <CharacterSlot
character={ character={item == null ? undefined : item}
item == null isSelected={selectedShopItem.value === i}
? undefined
: {
type: item,
relevancy: characters[item].initialRelevancy,
presence: characters[item].initialPresence
}
}
selected={selectedShopItem.value === i}
onClick={(e: MouseEvent) => { onClick={(e: MouseEvent) => {
if (item == null) {
return;
}
selectedShopItem.value = selectedShopItem.value === i ? null : i; selectedShopItem.value = selectedShopItem.value === i ? null : i;
selectedCharacter.value = null; selectedCharacter.value = null;
e.stopPropagation(); e.stopPropagation();

View file

@ -102,10 +102,20 @@ function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>)
socket.on("newTurn", shop => { socket.on("newTurn", shop => {
main.gold.value = 10; main.gold.value = 10;
main.turn.value++; main.turn.value++;
main.shop.value = shop; main.shop.value = shop.map(item => ({
type: item,
relevancy: characters[item].initialRelevancy,
presence: characters[item].initialPresence,
exp: 1
}));
}); });
socket.on("reroll", shop => { socket.on("reroll", shop => {
main.shop.value = shop; main.shop.value = shop.map(item => ({
type: item,
relevancy: characters[item].initialRelevancy,
presence: characters[item].initialPresence,
exp: 1
}));
}); });
socket.on("buy", (shopIndex, teamIndex, char) => { socket.on("buy", (shopIndex, teamIndex, char) => {
main.team.value[teamIndex] = char; main.team.value[teamIndex] = char;
@ -117,6 +127,10 @@ function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>)
main.team.value[index] = main.team.value[otherIndex]; main.team.value[index] = main.team.value[otherIndex];
main.team.value[otherIndex] = temp; main.team.value[otherIndex] = temp;
}); });
socket.on("merge", (index, otherIndex, char) => {
main.team.value[index] = null;
main.team.value[otherIndex] = char;
});
} }
declare module "game/settings" { declare module "game/settings" {

3
src/data/types.d.ts vendored
View file

@ -7,6 +7,7 @@ interface CharacterInfo {
interface Character { interface Character {
type: string; type: string;
exp: number;
relevancy: number; relevancy: number;
presence: number; presence: number;
} }
@ -19,9 +20,11 @@ interface ServerToClientEvents {
reroll: (shop: string[]) => void; reroll: (shop: string[]) => void;
buy: (shopIndex: number, teamIndex: number, char: Character) => void; buy: (shopIndex: number, teamIndex: number, char: Character) => void;
move: (index: number, otherIndex: number) => void; move: (index: number, otherIndex: number) => void;
merge: (shopIndex: number, teamIndex: number, char: Character) => void;
} }
interface ClientToServerEvents { interface ClientToServerEvents {
buy: (shopIndex: number, teamIndex: number) => void; buy: (shopIndex: number, teamIndex: number) => void;
move: (index: number, otherIndex: number) => void; move: (index: number, otherIndex: number) => void;
merge: (index: number, otherIndex: number) => void;
} }