update
This commit is contained in:
@@ -19,7 +19,7 @@ node --test tests/*.test.js
|
|||||||
- E یا دکمهٔ محصولات: باز کردن دستگاه در نزدیکی آن
|
- E یا دکمهٔ محصولات: باز کردن دستگاه در نزدیکی آن
|
||||||
- شروع دوباره: بازنشانی داستان، موجودی و زوم
|
- شروع دوباره: بازنشانی داستان، موجودی و زوم
|
||||||
|
|
||||||
گفتوگوها کوتاهاند و در کادر سفید پایین صفحه خودکار عوض میشوند. در طول تمام گفتوگوها، از جمله پل، حرکت آزاد است. هنگام خرید، تحویل محصول یا باز بودن دفترچه حرکت موقتاً متوقف میشود.
|
گفتوگوها کوتاهاند و در کادر سفید پایین صفحه خودکار عوض میشوند. هنگام گفتوگوها، خرید، تحویل محصول یا باز بودن دفترچه حرکت موقتاً متوقف میشود.
|
||||||
|
|
||||||
## دستگاه ۶۰ کانال و خرید با هتومان
|
## دستگاه ۶۰ کانال و خرید با هتومان
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ node --test tests/*.test.js
|
|||||||
## ساختار
|
## ساختار
|
||||||
|
|
||||||
- `src/main.js`: صحنه، دوربین، دستگاه، رابط و حلقهٔ بازی
|
- `src/main.js`: صحنه، دوربین، دستگاه، رابط و حلقهٔ بازی
|
||||||
- `src/controls.js`: جهت حرکت و شرایط مجاز بودن حرکت؛ دیالوگ مانع حرکت نیست
|
- `src/controls.js`: جهت حرکت و شرایط مجاز بودن حرکت؛ دیالوگ حرکت را موقتاً متوقف میکند
|
||||||
- `src/characters.js`: شخصیتهای مفصلی، تناسب بدن، لباس، چشمها، قدم زدن و تماس دستها
|
- `src/characters.js`: شخصیتهای مفصلی، تناسب بدن، لباس، چشمها، قدم زدن و تماس دستها
|
||||||
- `src/payment.js`: مراحل پرداخت، انصراف و جلوگیری از برداشت تکراری
|
- `src/payment.js`: مراحل پرداخت، انصراف و جلوگیری از برداشت تکراری
|
||||||
- `src/game-state.js`: قیمتهای هتومان، موجودی، اعتبارسنجی کد و خرید
|
- `src/game-state.js`: قیمتهای هتومان، موجودی، اعتبارسنجی کد و خرید
|
||||||
@@ -50,12 +50,12 @@ node --test tests/*.test.js
|
|||||||
|
|
||||||
## تعامل تکدکمهای
|
## تعامل تکدکمهای
|
||||||
|
|
||||||
گزینههای چندانتخابی داستان حذف شدهاند. هر رویداد یک کار مشخص دارد: تماشای خرگوش، برداشتن نقاشی، ادامه در مسیر آفتابی و کامل کردن نقاشی با آسمان طلایی. برای انجام آن، کلید **F** یا همان دکمهٔ روی صفحه را بزنید. متنهای معمولی خودکار عوض میشوند و حرکت آزاد میماند. **E** تعامل با دستگاه و **C** کشیدن کارت است. انتخاب شمارهٔ محصول و دکمهٔ پرداخت در پنجرهٔ دستگاه باقی است.
|
گزینههای چندانتخابی داستان حذف شدهاند. هر رویداد یک کار مشخص دارد: تماشای خرگوش، برداشتن نقاشی، ادامه در مسیر آفتابی و کامل کردن نقاشی با آسمان طلایی. برای انجام آن، کلید **F** یا همان دکمهٔ روی صفحه را بزنید. متنهای معمولی خودکار عوض میشوند و پس از پایان گفتوگو حرکت دوباره آزاد میشود. **E** تعامل با دستگاه و **C** کشیدن کارت است. انتخاب شمارهٔ محصول و دکمهٔ پرداخت در پنجرهٔ دستگاه باقی است.
|
||||||
|
|
||||||
## سفر از خانه به کوه
|
## سفر از خانه به کوه
|
||||||
|
|
||||||
بازی با خروج خانواده از خانه شروع میشود. با F یا دکمهٔ روی صفحه سوار ماشین شوید؛ با W یا ↑ گاز بدهید، با S یا ↓ ترمز کنید و با A/D فرمان بدهید؛ پس از رسیدن به ورودی جنگل، در ورودی جنگل دوباره F را بزنید تا پیاده شوید. در مسیر جنگل، جمعی از دخترها و پسرها کنار آتش میرقصند و خانواده آدرس دستگاه وندینگ را از آنها میپرسد. پس از خرید نوشیدنی و خوراکی، مسیر را تا پیکنیک بالای کوه ادامه دهید.
|
بازی با خروج خانواده از خانه شروع میشود. با F یا دکمهٔ روی صفحه سوار ماشین شوید؛ با W یا ↑ گاز بدهید، با S یا ↓ ترمز کنید و با A/D فرمان بدهید؛ پس از رسیدن به ورودی جنگل، در ورودی جنگل دوباره F را بزنید تا پیاده شوید. در مسیر جنگل، جمعی از دخترها و پسرها کنار آتش میرقصند و خانواده آدرس دستگاه وندینگ را از آنها میپرسد. پس از خرید نوشیدنی و خوراکی، مسیر را تا پیکنیک بالای کوه ادامه دهید.
|
||||||
|
|
||||||
دوربین پیشفرض سومشخص است؛ با V یا دکمهٔ تغییر دوربین بین نمای اولشخص و سومشخص جابهجا شوید. برای نگاه به اطراف روی صحنه بکشید. W/S یا چپ/راست حرکت در مسیر و Shift دویدن است. این نسخه از مدلهای هندسی استفاده میکند و کیفیت گرافیکی یک بازی AAA ندارد.
|
دوربین پیشفرض سومشخص است؛ با V یا دکمهٔ تغییر دوربین بین نمای اولشخص و سومشخص جابهجا شوید. برای نگاه به اطراف در نمای اولشخص یا چرخاندن دوربین در نمای سومشخص روی صحنه بکشید؛ هنگام تمرکز خودکار روی رویدادها و دستگاه، نمای دوربین ثابت است. W/S یا چپ/راست حرکت در مسیر و Shift دویدن است. این نسخه از مدلهای هندسی استفاده میکند و کیفیت گرافیکی یک بازی AAA ندارد.
|
||||||
|
|
||||||
گفتوگوی هر رویداد هنگام رسیدن به همان نقطه نمایش داده میشود و تا پایان آن حرکت متوقف میماند. دوربین نزدیک دستگاه خودکار روی آن تمرکز میکند. خانواده پس از رسیدن به قله روی فرش مینشینند.
|
گفتوگوی هر رویداد هنگام رسیدن به همان نقطه نمایش داده میشود و تا پایان آن حرکت متوقف میماند. دوربین نزدیک دستگاه خودکار روی آن تمرکز میکند. خانواده پس از رسیدن به قله روی فرش مینشینند.
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
// Dialogue deliberately never locks walking. Only transactional UI and delivery do.
|
// Dialogue, transactional UI and delivery pause walking.
|
||||||
export function movementDirection(pressed, held) {
|
export function movementDirection(pressed, held) {
|
||||||
return Number(pressed.has('arrowright') || pressed.has('d') || held.has(1))
|
return Number(pressed.has('arrowright') || pressed.has('d') || held.has(1))
|
||||||
- Number(pressed.has('arrowleft') || pressed.has('a') || held.has(-1));
|
- Number(pressed.has('arrowleft') || pressed.has('a') || held.has(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canWalk({shopOpen = false, notebookOpen = false, endingOpen = false, busy = false, finished = false}) {
|
export function canWalk({shopOpen = false, notebookOpen = false, endingOpen = false, busy = false, finished = false, chapterId = null}) {
|
||||||
return !shopOpen && !notebookOpen && !endingOpen && !busy && !finished;
|
return !shopOpen && !notebookOpen && !endingOpen && !busy && !finished && !chapterId;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -835,7 +835,8 @@ function frame(now) {
|
|||||||
shopOpen: shop.open,
|
shopOpen: shop.open,
|
||||||
notebookOpen: notebook.open,
|
notebookOpen: notebook.open,
|
||||||
endingOpen: ending.open,
|
endingOpen: ending.open,
|
||||||
busy: state.busy || Boolean(chapterId),
|
busy: state.busy,
|
||||||
|
chapterId,
|
||||||
finished: state.finished
|
finished: state.finished
|
||||||
}) ? direction : 0;
|
}) ? direction : 0;
|
||||||
x = THREE.MathUtils.clamp(x + moving * (pressed.has('shift') ? 3.7 : 2.2) * dt, -9, 22);
|
x = THREE.MathUtils.clamp(x + moving * (pressed.has('shift') ? 3.7 : 2.2) * dt, -9, 22);
|
||||||
@@ -881,7 +882,15 @@ function frame(now) {
|
|||||||
zoomDistance = THREE.MathUtils.clamp(zoomDistance + zoomInput * dt * .45, .6, 1.6);
|
zoomDistance = THREE.MathUtils.clamp(zoomDistance + zoomInput * dt * .45, .6, 1.6);
|
||||||
}
|
}
|
||||||
camTarget.sub(lookTarget).multiplyScalar(zoomDistance).add(lookTarget);
|
camTarget.sub(lookTarget).multiplyScalar(zoomDistance).add(lookTarget);
|
||||||
if (walking && Math.abs(x - stationX) >= 3.2 && firstPerson && !shopping && !state.finished && !encounterActive) {
|
const firstPersonActive = walking && Math.abs(x - stationX) >= 3.2 && firstPerson && !shopping && !state.finished && !encounterActive && chapterId !== 'camp';
|
||||||
|
if (walking && !firstPersonActive && !shopping && !state.finished && !encounterActive && chapterId !== 'camp' && Math.abs(x - stationX) >= 3.2) {
|
||||||
|
camTarget.sub(lookTarget);
|
||||||
|
const orbit = new THREE.Spherical().setFromVector3(camTarget);
|
||||||
|
orbit.theta += viewYaw;
|
||||||
|
orbit.phi = THREE.MathUtils.clamp(orbit.phi - viewPitch, .2, Math.PI / 2 - .05);
|
||||||
|
camTarget.setFromSpherical(orbit).add(lookTarget);
|
||||||
|
}
|
||||||
|
if (firstPersonActive) {
|
||||||
const bob = moving ? Math.sin(gait * 2) * .035 : 0;
|
const bob = moving ? Math.sin(gait * 2) * .035 : 0;
|
||||||
camTarget.set(x - .35, 1.7 + pathHeight(x) + bob, pathZ(x) - .7);
|
camTarget.set(x - .35, 1.7 + pathHeight(x) + bob, pathZ(x) - .7);
|
||||||
lookTarget.set(x + 5 * Math.cos(viewYaw), 1.65 + pathHeight(x) + viewPitch * 4, pathZ(x) + 5 * Math.sin(viewYaw));
|
lookTarget.set(x + 5 * Math.cos(viewYaw), 1.65 + pathHeight(x) + viewPitch * 4, pathZ(x) + 5 * Math.sin(viewYaw));
|
||||||
@@ -903,7 +912,7 @@ function frame(now) {
|
|||||||
lookTarget.set(cx + 3, 1, scenery.car.position.z);
|
lookTarget.set(cx + 3, 1, scenery.car.position.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
family.visible = journey.phase !== 'driving' && !(journey.phase === 'boarding' && journey.time > 1.8) && !(walking && firstPerson && !shopping && !state.finished && !encounterActive);
|
family.visible = journey.phase !== 'driving' && !(journey.phase === 'boarding' && journey.time > 1.8) && !firstPersonActive;
|
||||||
if (chapterId === 'camp' && walking) {
|
if (chapterId === 'camp' && walking) {
|
||||||
camTarget.set(1, 2.4, -1);
|
camTarget.set(1, 2.4, -1);
|
||||||
lookTarget.set(4, 1, 4.4);
|
lookTarget.set(4, 1, 4.4);
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import assert from 'node:assert/strict';
|
|||||||
import {canWalk,movementDirection} from '../src/controls.js';
|
import {canWalk,movementDirection} from '../src/controls.js';
|
||||||
import {products,createState,purchase,INITIAL_BALANCE,formatHetoman} from '../src/game-state.js';
|
import {products,createState,purchase,INITIAL_BALANCE,formatHetoman} from '../src/game-state.js';
|
||||||
import {createFamily} from '../src/characters.js';
|
import {createFamily} from '../src/characters.js';
|
||||||
test('Walking remains enabled during dialogue; arrows up/down only zoom',()=>{
|
test('Dialogue pauses walking; arrows up/down only zoom',()=>{
|
||||||
assert.ok(canWalk({chapterId:'bridge'}));assert.ok(canWalk({chapterId:'fish'}));
|
assert.equal(canWalk({chapterId:'bridge'}),false);assert.equal(canWalk({chapterId:'fish'}),false);
|
||||||
|
assert.ok(canWalk({chapterId:null}));
|
||||||
assert.equal(canWalk({busy:true}),false);assert.equal(canWalk({shopOpen:true}),false);
|
assert.equal(canWalk({busy:true}),false);assert.equal(canWalk({shopOpen:true}),false);
|
||||||
assert.equal(movementDirection(new Set(['arrowup']),new Set()),0);
|
assert.equal(movementDirection(new Set(['arrowup']),new Set()),0);
|
||||||
assert.equal(movementDirection(new Set(['arrowright']),new Set()),1);
|
assert.equal(movementDirection(new Set(['arrowright']),new Set()),1);
|
||||||
|
|||||||
Reference in New Issue
Block a user