Skip to content

Commit

Permalink
feat(client): 🍪👈
Browse files Browse the repository at this point in the history
  • Loading branch information
syuilo committed Jan 8, 2023
1 parent fceeb1b commit 27c2ca5
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ You should also include the user name that made the change.
- Client: show bot warning on screen when logged in as bot account @syuilo
- Client: improve overall performance of client @syuilo
- Client: ui tweaks @syuilo
- Client: clicker game @syuilo

### Bugfixes
- Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,7 @@ _widgets:
userList: "ユーザーリスト"
_userList:
chooseList: "リストを選択"
clicker: "クリッカー"

_cw:
hide: "隠す"
Expand Down
Binary file added packages/frontend/assets/cookie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions packages/frontend/src/components/MkClickerGame.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<div>
<div v-if="game.ready" :class="$style.game">
<div :class="$style.count" class=""><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
<button v-click-anime class="_button" :class="$style.button" @click="onClick">
<img src="/client-assets/cookie.png" :class="$style.img">
</button>
</div>
<div v-else>
<MkLoading/>
</div>
</div>
</template>

<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
import * as os from '@/os';
import { useInterval } from '@/scripts/use-interval';
import * as game from '@/scripts/clicker-game';
import number from '@/filters/number';
defineProps<{
}>();
const saveData = game.saveData;
const cookies = computed(() => saveData.value?.cookies);
function onClick(ev: MouseEvent) {
saveData.value!.cookies++;
saveData.value!.clicked++;
const x = ev.clientX;
const y = ev.clientY;
os.popup(MkPlusOneEffect, { x, y }, {}, 'end');
}
useInterval(game.save, 1000 * 5, {
immediate: false,
afterMounted: true,
});
onMounted(async () => {
await game.load();
});
onUnmounted(() => {
game.save();
});
</script>

<style lang="scss" module>
.game {
padding: 16px;
text-align: center;
}
.count {
font-size: 1.3em;
margin-bottom: 6px;
}
.button {
}
.img {
max-width: 90px;
}
</style>
69 changes: 69 additions & 0 deletions packages/frontend/src/components/MkPlusOneEffect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<template>
<div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }">
<span class="text" :class="{ up }">+1</span>
</div>
</template>

<script lang="ts" setup>
import { onMounted } from 'vue';
import * as os from '@/os';
const props = withDefaults(defineProps<{
x: number;
y: number;
}>(), {
});
const emit = defineEmits<{
(ev: 'end'): void;
}>();
let up = $ref(false);
const zIndex = os.claimZIndex('middle');
const angle = (45 - (Math.random() * 90)) + 'deg';
onMounted(() => {
window.setTimeout(() => {
up = true;
}, 10);
window.setTimeout(() => {
emit('end');
}, 1100);
});
</script>

<style lang="scss" module>
.root {
pointer-events: none;
position: fixed;
width: 128px;
height: 128px;
&:global {
> .text {
display: block;
height: 1em;
text-align: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
color: #fff;
text-shadow: 0 0 6px #000;
font-size: 18px;
font-weight: bold;
transform: translateY(0px);
transition: transform 1s cubic-bezier(0,.5,0,1), opacity 1s cubic-bezier(.5,0,1,.5);
will-change: opacity, transform;
&.up {
opacity: 0;
transform: translateY(-50px) rotateZ(v-bind(angle));
}
}
}
}
</style>
3 changes: 3 additions & 0 deletions packages/frontend/src/directives/click-anime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export default {
target.classList.add('_anime_bounce_standBy');

el.addEventListener('mousedown', () => {
target.classList.remove('_anime_bounce_ready');
target.classList.remove('_anime_bounce');

target.classList.add('_anime_bounce_standBy');
target.classList.add('_anime_bounce_ready');

Expand Down
24 changes: 24 additions & 0 deletions packages/frontend/src/pages/clicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<MkStickyContainer>
<template #header><MkPageHeader/></template>
<MkSpacer :content-max="800">
<MkClickerGame/>
</MkSpacer>
</MkStickyContainer>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import MkClickerGame from '@/components/MkClickerGame.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
definePageMetadata({
title: '🍪👈',
icon: 'ti ti-cookie',
});
</script>

<style lang="scss" module>
</style>
4 changes: 4 additions & 0 deletions packages/frontend/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ export const routes = [{
path: '/timeline/antenna/:antennaId',
component: page(() => import('./pages/antenna-timeline.vue')),
loginRequired: true,
}, {
path: '/clicker',
component: page(() => import('./pages/clicker.vue')),
loginRequired: true,
}, {
name: 'index',
path: '/',
Expand Down
46 changes: 46 additions & 0 deletions packages/frontend/src/scripts/clicker-game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ref, computed } from 'vue';
import * as os from '@/os';

type SaveData = {
gameVersion: number;
cookies: number;
clicked: number;
};

export const saveData = ref<SaveData>();
export const ready = computed(() => saveData.value != null);

let prev = '';

export async function load() {
try {
saveData.value = await os.api('i/registry/get', {
scope: ['clickerGame'],
key: 'saveData',
});
} catch (err) {
if (err.code === 'NO_SUCH_KEY') {
saveData.value = {
gameVersion: 1,
cookies: 0,
clicked: 0,
};
save();
return;
}
throw err;
}
}

export async function save() {
const current = JSON.stringify(saveData.value);
if (current === prev) return;

await os.api('i/registry/set', {
scope: ['clickerGame'],
key: 'saveData',
value: saveData.value,
});

prev = current;
}
5 changes: 5 additions & 0 deletions packages/frontend/src/ui/_common_/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export function openInstanceMenu(ev: MouseEvent) {
to: '/api-console',
text: 'API Console',
icon: 'ti ti-terminal-2',
}, {
type: 'link',
to: '/clicker',
text: '🍪👈',
icon: 'ti ti-cookie',
}],
}, null, {
type: 'parent',
Expand Down
44 changes: 44 additions & 0 deletions packages/frontend/src/widgets/clicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<MkContainer :show-header="widgetProps.showHeader" class="mkw-clicker">
<template #header><i class="ti ti-cookie"></i>Clicker</template>
<MkClickerGame/>
</MkContainer>
</template>

<script lang="ts" setup>
import { onMounted, onUnmounted, Ref, ref, watch } from 'vue';
import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
import { GetFormResultType } from '@/scripts/form';
import { $i } from '@/account';
import MkContainer from '@/components/MkContainer.vue';
import MkClickerGame from '@/components/MkClickerGame.vue';
const name = 'clicker';
const widgetPropsDef = {
showHeader: {
type: 'boolean' as const,
default: true,
},
};
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない
//const props = defineProps<WidgetComponentProps<WidgetProps>>();
//const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
const props = defineProps<{ widget?: Widget<WidgetProps>; }>();
const emit = defineEmits<{ (ev: 'updateProps', props: WidgetProps); }>();
const { widgetProps, configure } = useWidgetPropsManager(name,
widgetPropsDef,
props,
emit,
);
defineExpose<WidgetComponentExpose>({
name,
configure,
id: props.widget ? props.widget.id : null,
});
</script>
2 changes: 2 additions & 0 deletions packages/frontend/src/widgets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default function(app: App) {
app.component('MkwAiscriptApp', defineAsyncComponent(() => import('./aiscript-app.vue')));
app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue')));
app.component('MkwUserList', defineAsyncComponent(() => import('./user-list.vue')));
app.component('MkwClicker', defineAsyncComponent(() => import('./clicker.vue')));
}

export const widgets = [
Expand Down Expand Up @@ -52,4 +53,5 @@ export const widgets = [
'aiscriptApp',
'aichan',
'userList',
'clicker',
];

0 comments on commit 27c2ca5

Please sign in to comment.