-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.js
More file actions
194 lines (173 loc) · 8 KB
/
Copy pathconfig.js
File metadata and controls
194 lines (173 loc) · 8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// ============================================================
// CONFIG — shared constants
// ============================================================
const _isElectron = navigator.userAgent.includes('Electron');
export const isMobile = !_isElectron && (
/Android|iPhone|iPad|iPod|webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|| (navigator.maxTouchPoints > 1));
export const isAndroid = /Android/i.test(navigator.userAgent);
export const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
export const BIKE_MODEL_PATH = 'tandem-3d/tandem_bicycle.glb';
// Protocol message types
export const MSG_PEDAL = 0x01;
export const MSG_STATE = 0x02;
export const MSG_EVENT = 0x03;
export const MSG_HEARTBEAT = 0x04;
export const MSG_LEAN = 0x05;
// Event subtypes
export const EVT_COUNTDOWN = 0x01;
export const EVT_START = 0x02;
export const EVT_CRASH = 0x03;
export const EVT_RESET = 0x04;
export const EVT_GAMEOVER = 0x05;
export const EVT_CHECKPOINT = 0x06;
export const EVT_FINISH = 0x07;
export const EVT_RETURN_ROOM = 0x08;
export const MSG_COLLECT = 0x06;
export const MSG_PROFILE = 0x07;
export const RELAY_URL = 'wss://tandemonium-relay.pete-872.workers.dev';
// Backend API (auth, scores, achievements, leaderboard, /me). Shared by the
// identity client and the achievements sync glue. (#318 Step 4)
export const API_BASE = 'https://tandemonium-api.pete-872.workers.dev';
// Production web URL — used for QR codes in Electron, share links, etc.
// Update this when the domain changes.
export const SITE_URL = 'https://tandemonium.jimandi.love';
export const TURN_CREDENTIALS_URL = 'https://tandemonium-relay.pete-872.workers.dev/turn-credentials';
// Display name shown to a partner when a player joins an invite without
// signing in (anonymous play). See Issue #312.
export const GUEST_NAME = 'Guest';
// Self-hosted PeerJS signaling server (Cloud Run)
export const PEERJS_HOST = 'peerjs-640682648249.us-central1.run.app';
export const PEERJS_PORT = 443;
export const PEERJS_PATH = '/';
export const PEERJS_SECURE = true;
// Shared defaults (platform-independent)
const SHARED_PHYSICS = {
calibSamples: 10,
// Controller gyro (WebHID) — unchanged, hardware is consistent
gyroSensitivity: 40,
gyroDeadzone: 4,
gyroOutputSmoothing: 0.5,
gyroResponseCurve: 1.5,
steeringFeel: 0.5,
gyroAccelCorrection: 0.02,
// Shared physics
leanForce: 12,
gravityForce: 2.5,
damping: 4.0,
turnRate: 0.50,
};
// Platform-specific tilt defaults
const PLATFORM_TILT_DEFAULTS = isAndroid ? {
// Android: wider range, higher noise floor, heavier filtering
sensitivity: 32, // Android gamma reports ~30-50% higher
deadzone: 5, // Android rest noise ±2–4° vs iOS ±1–2°
lowPassK: 0.08, // more aggressive low-pass on raw accel
responseCurve: 2.2, // gentler center zone hides micro-jitter
outputSmoothing: 0.50, // heavier EMA compensates for noise
} : {
// iOS (and desktop fallback): tighter, more responsive
sensitivity: 23,
deadzone: 4,
lowPassK: 0.1,
responseCurve: 2.0,
outputSmoothing: 0.38,
};
// Balance physics defaults — single source of truth
export const BALANCE_DEFAULTS = { ...SHARED_PHYSICS, ...PLATFORM_TILT_DEFAULTS };
// Mutable runtime tuning (initialized from defaults, adjustable by player)
export const TUNE = { ...BALANCE_DEFAULTS };
// Difficulty presets
export const DIFFICULTY_PRESETS = {
tutorial: {
crashThreshold: 2.2, // ~126° — nearly impossible to reach
gravityForce: 1.0, // very weak topple force
wobbleMultiplier: 0.0, // NO random wobble
dangerOnset: 0.85, // danger shaking only very close to edge
timeMultiplier: 2.0, // generous time (timer is hidden anyway)
maxSpeed: 12, // lower top speed = easier to control
scoreMultiplier: 0, // no scoring in tutorial
autoCorrection: true, // gentle return-to-center force
autoCorrectionStrength: 6.0, // strong self-righting (ramped per phase)
pedalLeanKickScale: 0.0, // no random lean impulse on pedal strokes
autoSpeed: true, // bike rolls forward automatically
},
chill: {
crashThreshold: 2.2, // same as tutorial — nearly impossible to crash
gravityForce: 1.0, // very weak topple force
wobbleMultiplier: 0.0, // no random wobble
dangerOnset: 0.85, // danger shaking only very close to edge
timeMultiplier: 1.3,
maxSpeed: 12, // same as tutorial
scoreMultiplier: 0.75,
autoCorrection: true,
autoCorrectionStrength: 6.0, // strong self-righting
pedalLeanKickScale: 0.0, // no random lean impulse
autoSpeed: true, // steady cruise speed for smooth, stable riding
},
adventurous: {
crashThreshold: 2.0, // forgiving but crashable
gravityForce: 1.2, // slightly more topple than chill
wobbleMultiplier: 0.1, // very light wobble
dangerOnset: 0.75, // moderate warning
timeMultiplier: 1.0,
maxSpeed: 14, // moderate speed
scoreMultiplier: 1.0,
autoCorrection: true, // still has auto-correction
autoCorrectionStrength: 5.0, // strong — bike helps a lot
pedalLeanKickScale: 0.1, // barely noticeable pedal kicks
},
daredevil: {
crashThreshold: 1.8, // tighter but still forgiving
gravityForce: 1.5, // moderate topple force
wobbleMultiplier: 0.3, // light wobble
dangerOnset: 0.60, // earlier danger warning
timeMultiplier: 0.9,
maxSpeed: 19,
scoreMultiplier: 1.5,
autoCorrection: true, // still has auto-correction
autoCorrectionStrength: 3.0, // gentle help
pedalLeanKickScale: 0.3, // light pedal kicks
},
};
export function applyDifficulty(presetName) {
const preset = DIFFICULTY_PRESETS[presetName] || DIFFICULTY_PRESETS.adventurous;
Object.assign(TUNE, preset);
}
// Snapshot of calibrated base values for steering feel scaling.
// Updated by tutorial completion and background adaptation saves.
export const TUNING_BASE = { ...BALANCE_DEFAULTS };
/** Capture current TUNE motion params as the base for steering feel scaling. */
export function snapshotTuningBase() {
for (const k of ['sensitivity', 'deadzone', 'outputSmoothing', 'responseCurve',
'gyroSensitivity', 'gyroDeadzone', 'gyroOutputSmoothing', 'gyroResponseCurve']) {
TUNING_BASE[k] = TUNE[k];
}
}
/**
* Apply a steering feel value (0 = Stable, 1 = Responsive) by scaling
* the calibrated base tuning parameters. Call AFTER loading saved tuning
* or after tutorial completion.
* @param {number} feel — 0..1 slider value
*/
export function applySteeringFeel(feel) {
TUNE.steeringFeel = feel;
// Deadzone: Stable = wider (×1.4), Responsive = tighter (×0.6)
const dzScale = 1.4 - 0.8 * feel;
// Smoothing: Stable = more smoothing (×0.6 output factor), Responsive = less (×1.5)
const smScale = 0.6 + 0.9 * feel;
// Sensitivity: Stable = less (×0.85), Responsive = more (×1.15)
const senScale = 0.85 + 0.3 * feel;
// Response curve: Stable = higher exponent (more gradual center), Responsive = lower
const rcShift = 0.3 - 0.6 * feel; // +0.3 at Stable, -0.3 at Responsive
// Apply to mobile tilt params (scaled from calibrated base)
TUNE.deadzone = Math.min(8, Math.max(2, TUNING_BASE.deadzone * dzScale));
TUNE.outputSmoothing = Math.min(0.8, Math.max(0.15, TUNING_BASE.outputSmoothing * smScale));
TUNE.sensitivity = Math.min(60, Math.max(15, TUNING_BASE.sensitivity * senScale));
TUNE.responseCurve = Math.min(2.5, Math.max(1.0, TUNING_BASE.responseCurve + rcShift));
// Apply to gyro params
TUNE.gyroDeadzone = Math.min(8, Math.max(2, TUNING_BASE.gyroDeadzone * dzScale));
TUNE.gyroOutputSmoothing = Math.min(0.8, Math.max(0.15, TUNING_BASE.gyroOutputSmoothing * smScale));
TUNE.gyroSensitivity = Math.min(60, Math.max(15, TUNING_BASE.gyroSensitivity * senScale));
TUNE.gyroResponseCurve = Math.min(2.0, Math.max(1.0, TUNING_BASE.gyroResponseCurve + rcShift));
}