diff --git a/packages/backend/src/server/api/endpoints/bubble-game/register.ts b/packages/backend/src/server/api/endpoints/bubble-game/register.ts index f092d16a70..8eb90fdbf9 100644 --- a/packages/backend/src/server/api/endpoints/bubble-game/register.ts +++ b/packages/backend/src/server/api/endpoints/bubble-game/register.ts @@ -63,8 +63,8 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.invalidSeed); } - // シードが古すぎる(1時間以上前)のも弾く - if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60) { + // シードが古すぎる(5時間以上前)のも弾く + if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60 * 5) { throw new ApiError(meta.errors.invalidSeed); } diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index a8fa953c38..1fc0c7cd9c 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -496,7 +496,7 @@ const SWEETS_MONOS: FrontendMonoDefinition[] = [{ }]; const props = defineProps<{ - gameMode: 'normal' | 'square' | 'yen' | 'sweets'; + gameMode: 'normal' | 'square' | 'yen' | 'sweets' | 'space'; mute: boolean; }>(); @@ -509,6 +509,7 @@ const monoDefinitions = computed(() => { props.gameMode === 'square' ? SQUARE_MONOS : props.gameMode === 'yen' ? YEN_MONOS : props.gameMode === 'sweets' ? SWEETS_MONOS : + props.gameMode === 'space' ? NORAML_MONOS : [] as never; }); diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 18d3f56ca2..dd3b189c9d 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only + {{ i18n.ts.start }} @@ -94,7 +95,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; -const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets'>('normal'); +const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets' | 'space'>('normal'); const gameStarted = ref(false); const mute = ref(false); const ranking = ref(null); @@ -108,6 +109,7 @@ function getScoreUnit(gameMode: string) { gameMode === 'square' ? 'pt' : gameMode === 'yen' ? '円' : gameMode === 'sweets' ? 'kcal' : + gameMode === 'space' ? 'pt' : '' as never; } diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts index 7c75822a20..b45aa591d1 100644 --- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts +++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts @@ -31,7 +31,7 @@ type Log = { operation: 'surrender'; }; -const NORMAL_BASE_SIZE = 30; +const NORMAL_BASE_SIZE = 32; const NORAML_MONOS: Mono[] = [{ id: '9377076d-c980-4d83-bdaf-175bc58275b7', level: 10, @@ -114,7 +114,7 @@ const NORAML_MONOS: Mono[] = [{ dropCandidate: true, }]; -const YEN_BASE_SIZE = 30; +const YEN_BASE_SIZE = 32; const YEN_SATSU_BASE_SIZE = 70; const YEN_MONOS: Mono[] = [{ id: '880f9bd9-802f-4135-a7e1-fd0e0331f726', @@ -1003,7 +1003,7 @@ export class DropAndFusionGame extends EventEmitter<{ private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; private overflowCollider: Matter.Body; private isGameOver = false; - private gameMode: 'normal' | 'yen' | 'square' | 'sweets'; + private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space'; private rng: () => number; private logs: Log[] = []; @@ -1031,6 +1031,7 @@ export class DropAndFusionGame extends EventEmitter<{ case 'yen': return YEN_MONOS; case 'square': return SQUARE_MONOS; case 'sweets': return SWEETS_MONOS; + case 'space': return NORAML_MONOS; } } @@ -1071,13 +1072,15 @@ export class DropAndFusionGame extends EventEmitter<{ this.getMonoRenderOptions = env.getMonoRenderOptions ?? null; this.rng = seedrandom(env.seed); + // sweetsモードは重いため + const physicsQualityFactor = this.gameMode === 'sweets' ? 4 : this.PHYSICS_QUALITY_FACTOR; this.engine = Matter.Engine.create({ - constraintIterations: 2 * this.PHYSICS_QUALITY_FACTOR, - positionIterations: 6 * this.PHYSICS_QUALITY_FACTOR, - velocityIterations: 4 * this.PHYSICS_QUALITY_FACTOR, + constraintIterations: 2 * physicsQualityFactor, + positionIterations: 6 * physicsQualityFactor, + velocityIterations: 4 * physicsQualityFactor, gravity: { x: 0, - y: 1, + y: this.gameMode === 'space' ? 0.0125 : 1, }, timing: { timeScale: 2, @@ -1092,7 +1095,7 @@ export class DropAndFusionGame extends EventEmitter<{ label: '_wall_', isStatic: true, friction: 0.7, - slop: 1.0, + slop: this.gameMode === 'space' ? 0.01 : 0.7, render: { strokeStyle: 'transparent', fillStyle: 'transparent', @@ -1130,13 +1133,12 @@ export class DropAndFusionGame extends EventEmitter<{ private createBody(mono: Mono, x: number, y: number) { const options: Matter.IBodyDefinition = { label: mono.id, - //density: 0.0005, - density: ((mono.sizeX + mono.sizeY) / 2) / 1000, - restitution: 0.2, - frictionAir: 0.01, - friction: 0.7, - frictionStatic: 5, - slop: 1.0, + density: this.gameMode === 'space' ? 0.01 : ((mono.sizeX * mono.sizeY) / 10000), + restitution: this.gameMode === 'space' ? 0.5 : 0.2, + frictionAir: this.gameMode === 'space' ? 0 : 0.01, + friction: this.gameMode === 'space' ? 0.5 : 0.7, + frictionStatic: this.gameMode === 'space' ? 0 : 5, + slop: this.gameMode === 'space' ? 0.01 : 0.7, //mass: 0, render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined, }; @@ -1327,6 +1329,15 @@ export class DropAndFusionGame extends EventEmitter<{ operation: 'drop', x: inputX, }); + + // add force + if (this.gameMode === 'space') { + Matter.Body.applyForce(body, body.position, { + x: 0, + y: (Math.PI * head.mono.sizeX * head.mono.sizeY) / 65536, + }); + } + Matter.Composite.add(this.engine.world, body); this.fusionReadyBodyIds.push(body.id);