成就界面

This commit is contained in:
unanmed 2023-02-22 20:23:37 +08:00
parent 17e282a835
commit 2687a0e50b
9 changed files with 378 additions and 30 deletions

2
components.d.ts vendored
View File

@ -8,6 +8,7 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ADivider: typeof import('ant-design-vue/es')['Divider']
AProgress: typeof import('ant-design-vue/es')['Progress']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
@ -17,5 +18,6 @@ declare module '@vue/runtime-core' {
Colomn: typeof import('./src/components/colomn.vue')['default']
EnemyOne: typeof import('./src/components/enemyOne.vue')['default']
Scroll: typeof import('./src/components/scroll.vue')['default']
TextedProgress: typeof import('./src/components/textedProgress.vue')['default']
}
}

View File

@ -502,7 +502,7 @@ control.prototype.showStartAnimate = function (noAnimate, callback) {
};
control.prototype._showStartAnimate_resetDom = function () {
core.plugin.loaded.value = true;
if (main.mode === 'play') core.plugin.loaded.value = true;
core.status.played = false;
core.dom.gameGroup.style.display = 'none';
core.clearStatus();

View File

@ -245,7 +245,6 @@ loader.prototype._loadTilesets_sync = function (callback) {
core.tilesets,
core.material.images.tilesets,
function () {
core.loader._loadTilesets_afterLoad();
callback();
}
);
@ -259,25 +258,11 @@ loader.prototype._loadTilesets_async = function (onprogress, onfinished) {
core.material.images.tilesets,
onprogress,
function () {
core.loader._loadTilesets_afterLoad();
onfinished();
}
);
};
loader.prototype._loadTilesets_afterLoad = function () {
// 检查宽高是32倍数如果出错在控制台报错
for (var imgName in core.material.images.tilesets) {
var img = core.material.images.tilesets[imgName];
if (img.width % 32 != 0 || img.height % 32 != 0) {
console.warn('警告!' + imgName + '的宽或高不是32的倍数');
}
if (img.width * img.height > 32 * 32 * 3000) {
console.warn('警告!' + imgName + '上的图块素材个数大于3000');
}
}
};
// ------ 实际加载一系列图片 ------ //
loader.prototype.loadImages = function (dir, names, toSave, callback) {

View File

@ -1,5 +1,5 @@
{
"challenge": [
"normal": [
{
"name": "虚惊一场",
"text": [
@ -7,6 +7,15 @@
],
"point": 30
},
{
"name": "真能刷",
"text": [
"勇气之路的刷血怪刷到 <span style=\"color: gold\">15w</span> 以上的血"
],
"point": 30
}
],
"challenge": [
{
"name": "逃出生天",
"text": [
@ -14,13 +23,6 @@
],
"point": 20
},
{
"name": "真能刷",
"text": [
"勇气之路的刷血怪刷到 <span style=\"color: gold\">15w<span> 以上的血"
],
"point": 30
},
{
"name": "冰与火之舞",
"text": [
@ -35,7 +37,8 @@
"text": [
"第一章完成度达到100%"
],
"progress": "",
"progress": "100 / 100",
"percent": true,
"point": 50
},
{
@ -43,6 +46,7 @@
"text": [
"与山路上的若干个神秘木牌对话"
],
"progress": "5 / 5",
"hide": "该探索成就需要你自己探索如何达成",
"point": 25
},
@ -51,7 +55,8 @@
"text": [
"第二章完成度达到100%"
],
"progress": "",
"progress": "100 / 100",
"percent": true,
"point": 50
},
{
@ -59,6 +64,7 @@
"text": [
"学习电摇嘲讽技能"
],
"hide": "该探索成就需要你自己探索如何达成",
"point": 20
},
{

View File

@ -0,0 +1,14 @@
import list from '../../data/achievement.json';
type AchievementList = typeof list;
type AchievementType = keyof AchievementList;
export default function init() {
return {};
}
export function completeAchievement(type: AchievementType, index: number) {}
export function hasCompletedAchievement(type: AchievementType, index: number) {
return true;
}

View File

@ -10,6 +10,7 @@ import SkillTree from '../ui/skillTree.vue';
import Fly from '../ui/fly.vue';
import FixedDetail from '../ui/fixedDetail.vue';
import Shop from '../ui/shop.vue';
import Achievement from '../ui/achievement.vue';
export const bookOpened = ref(false);
export const toolOpened = ref(false);
@ -24,6 +25,7 @@ export const showStudiedSkill = ref(false);
export const fixedDetailOpened = ref(false);
export const shopOpened = ref(false);
export const startOpened = ref(false);
export const achievementOpened = ref(false);
export const transition = ref(true);
export const noClosePanel = ref(false);
@ -51,7 +53,8 @@ const UI_LIST: [Ref<boolean>, Component][] = [
[skillTreeOpened, SkillTree],
[flyOpened, Fly],
[fixedDetailOpened, FixedDetail],
[shopOpened, Shop]
[shopOpened, Shop],
[achievementOpened, Achievement]
];
/** ui栈 */

View File

@ -209,6 +209,9 @@ interface PluginUis {
/** 开始界面是否打开 */
readonly startOpened: Ref<boolean>;
/** 成就界面是否打开 */
readonly achievementOpened: Ref<boolean>;
/** ui栈 */
readonly uiStack: Ref<any[]>;

View File

@ -1,14 +1,335 @@
<template>
<div id="achievement"></div>
<div id="achievement">
<div id="tools">
<span id="back" class="button-text tools" @click="exit"
><left-outlined />返回游戏</span
>
</div>
<div id="column">
<div class="achievement-column" v-for="c of column">
<span
class="column-text button-text"
:active="selectedColumn === c"
@click="selectedColumn = c"
>{{ columnName[c] }}</span
>
</div>
</div>
<a-divider dashed id="divider"></a-divider>
<div id="list">
<div id="achievement-list" :style="{ left: `-${offset}%` }">
<div v-for="t of column" class="achievement-one">
<Scroll class="list-scroll">
<div class="list-div">
<div
v-for="a of getAllAchievements(t)"
class="list-one"
>
<div
class="list-content"
:complete="a.complete"
>
<span class="list-name">{{ a.name }}</span>
<span
class="list-text"
v-html="a.text"
></span>
<div class="list-end">
<div class="end-info">
<span
class="complete"
:complete="a.complete"
>完成情况:
{{
a.complete
? '已完成'
: '未完成'
}}</span
>
<span class="point"
>成就点数: {{ a.point }}</span
>
</div>
<div
v-if="a.progress"
class="list-progress"
>
<a-progress
:percent="a.percent"
:strokeColor="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:strokeWidth="height / 150"
:format="
() =>
a.usePercent
? `${a.percent}%`
: a.progress
"
></a-progress>
</div>
</div>
</div>
<a-divider id="divider" dashed></a-divider>
</div>
</div>
</Scroll>
</div>
</div>
</div>
<div id="total-progress">
<a-progress
id="point-progress"
:percent="(nowPoint / totalPoint) * 100"
:strokeColor="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:strokeWidth="height / 150"
:showInfo="false"
></a-progress>
<span id="point-number"
>成就点: {{ nowPoint }} / {{ totalPoint }}</span
>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { achievementOpened, noClosePanel } from '../plugin/uiController';
import { LeftOutlined } from '@ant-design/icons-vue';
import list from '../data/achievement.json';
import { hasCompletedAchievement } from '../plugin/ui/achievement';
import Scroll from '../components/scroll.vue';
import { has } from '../plugin/utils';
interface Achievement {
name: string;
text: string[];
point: number;
hide?: string;
progress?: string;
percent?: boolean;
}
type AchievementList = typeof list;
type AchievementType = keyof AchievementList;
interface ResolvedAchievement {
name: string;
text: string;
complete: boolean;
point: number;
/** number / number */
progress?: string;
percent?: number;
usePercent?: boolean;
}
const column: AchievementType[] = ['normal', 'challenge', 'explore'];
const columnName = {
normal: '普通成就',
challenge: '挑战成就',
explore: '探索成就'
};
const selectedColumn = ref<AchievementType>('normal');
const offset = computed(() => {
return column.indexOf(selectedColumn.value) * 100;
});
const height = window.innerHeight;
const width = window.innerWidth;
const totalPoint = Object.values(list)
.map((v: Achievement[]) =>
v.reduce((prev, curr) => {
return curr.point + prev;
}, 0)
)
.reduce((prev, curr) => prev + curr);
const nowPoint = (function () {
let res = 0;
for (const [type, achi] of Object.entries(list)) {
achi.forEach((v, i) => {
if (hasCompletedAchievement(type as AchievementType, i)) {
res += v.point;
}
});
}
return res;
})();
/**
* 获取一个类型的所有成就
* @param type 成就类型
*/
function getAllAchievements(type: AchievementType): ResolvedAchievement[] {
return list[type].map<ResolvedAchievement>((v: Achievement, i) => {
const complete = hasCompletedAchievement(type, i);
const text = v.hide && !complete ? v.hide : v.text.join('');
const res: ResolvedAchievement = {
text,
name: v.name,
point: v.point,
complete
};
if (v.progress) {
const p = eval('`' + v.progress + '`') as string;
res.progress = p;
res.percent = Math.floor(eval(p) * 100);
if (v.percent) res.usePercent = true;
}
return res;
});
}
function exit() {
noClosePanel.value = true;
achievementOpened.value = false;
}
</script>
<style lang="less" scoped>
#achievement {
width: 90vh;
height: 90vh;
font-family: 'normal';
font-size: 2.8vh;
display: flex;
flex-direction: column;
user-select: none;
}
#divider {
margin: 1vh 0 1vh 0;
border-color: #ddd4;
}
#tools {
height: 5vh;
font-size: 3.2vh;
}
#column {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-top: 3vh;
font-size: 3.5vh;
}
.list-scroll {
width: 100%;
height: 100%;
}
#list {
overflow: hidden;
width: 100%;
height: 69vh;
}
#achievement-list {
position: relative;
width: 300%;
height: 100%;
display: flex;
flex-direction: row;
transition: left 0.4s ease;
}
.achievement-one {
width: 90vh;
}
.list-div {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.list-one {
width: 70%;
.list-content {
height: 18vh;
display: flex;
flex-direction: column;
align-items: center;
border: 2px double rgba(132, 132, 132, 0.17);
border-radius: 1vh;
margin: 2vh 0 2.5vh 0;
background-color: rgba(59, 59, 59, 0.281);
}
.list-content[complete='true'] {
background-color: rgba(239, 255, 63, 0.205);
}
.list-name {
border-bottom: 1px solid #ddd4;
}
.list-text {
font-size: 2.5vh;
}
.list-end {
width: 90%;
height: 95%;
display: flex;
flex-direction: column-reverse;
font-size: 2.3vh;
.end-info {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: end;
font-size: 2.3vh;
}
.complete {
color: lightcoral;
}
.complete[complete='true'] {
color: lightgreen;
}
}
.list-progress {
display: flex;
flex-direction: row;
align-items: center;
.progress {
width: 100%;
height: 2.6vh;
font-size: 1vh;
}
}
}
#total-progress {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
#point-progress {
width: 100%;
}
#point-number {
font-size: 2vh;
margin-left: 2vh;
white-space: nowrap;
}
}
</style>

View File

@ -52,6 +52,7 @@ import { sleep } from 'mutate-animate';
import { Matrix4 } from '../plugin/webgl/matrix';
import { doByInterval, keycode } from '../plugin/utils';
import { KeyCode } from '../plugin/keyCodes';
import { achievementOpened } from '../plugin/uiController';
let startdiv: HTMLDivElement;
let start: HTMLDivElement;
@ -119,6 +120,9 @@ async function clickStartButton(id: string) {
core.load();
}
if (id === 'replay') core.chooseReplayFile();
if (id === 'achievement') {
achievementOpened.value = true;
}
}
function onmove(e: MouseEvent) {
@ -324,6 +328,7 @@ onUnmounted(() => {
#000 100%
);
animation: gradient 4s ease-out 0.5s 1 normal forwards;
pointer-events: none;
}
#listen {
@ -345,7 +350,6 @@ onUnmounted(() => {
#title {
margin-top: 7%;
text-align: center;
color: transparent;
font: 4em 'normal';
font-weight: 200;
background-image: linear-gradient(
@ -363,6 +367,7 @@ onUnmounted(() => {
5px 5px 5px rgba(0, 0, 0, 0.4);
filter: brightness(1.8);
user-select: none;
animation: opacity 3s ease-out 0.5s 1 normal forwards;
}
#buttons {
@ -547,6 +552,15 @@ onUnmounted(() => {
}
}
@keyframes opacity {
from {
color: #bbb;
}
to {
color: transparent;
}
}
.start-enter-active {
transition: all 1.2s ease-out;
}