Files
pronature-platform/src/components/InteractiveObjects/MazeQuizGame/MazeQuizGame.js
T
2026-03-23 19:56:37 +02:00

194 lines
7.6 KiB
JavaScript

import { MazeObject } from "./MazeObject";
import Utils from "#/app/Utils";
import { EventManager } from '@/lib/EventManager';
const params = {
scale: 3,
wallSize: 3 * 1.2,
tubeSize: 3 * 1.2,
}
const imgParams = {
type: 'ImageObject',
width: params.wallSize*0.033,
height: params.wallSize*0.033,
value: 'arrow.webp',
path: '/static/meshes/quiz/', distance: params.wallSize*3
}
const textParams = {
type: 'TextObject', maxWidth:0.44,
fontPath:'/static/fonts/Montserrat-Regular.ttf',
fontSize:0.025, distance: params.wallSize*3
}
const defaults = {
arrows:{
r: len => ({ position:[-.5,.25,len+.9], rotation:[0,Math.PI, 0], ...imgParams }),
l: len => ({ position:[.5,.25,len+.9], rotation:[0,Math.PI, Math.PI], ...imgParams }),
f: len => ({ position:[0,.37,len+.96], rotation:[0,Math.PI, Math.PI/2], ...imgParams })
},
answers:{
r: (len, text) => ({ ...textParams, text, position:[-.5,.3,len+.9], rotation:[0,Math.PI, 0] }),
l: (len, text) => ({ ...textParams, text, position:[.5,.3,len+.9], rotation:[0,Math.PI, 0] }),
f: (len, text) => ({ ...textParams, anchorY:'top', text, position:[0,.3,len+.9], rotation:[0,Math.PI, 0] })
}
}
const tl = 4;
class MazeQuizGame extends EventManager {
emits = ['finish', 'sceneSwitch', 'interaction']
constructor(engine, data) {
super();
this.data = data;
data.noPhysics = true;
this.params = { ...params, mazeFile: data.style || 'quiz-s2.gltf', io: this }
this.maxPoints = (data.points || 0) + (data.questionPoints||0) * data.questions.length;
this.minPoints = this.maxPoints - (data.questionPenalty||0) * data.questions.length;
return new Promise(async (resolve, reject)=>{
let questions = data.shuffle ? Utils.shuffleArray(data.questions) : data.questions;
let def = this.generate(questions);
this.mazeObject = new MazeObject(engine, def, this.params)
engine.addEventListener('collision', async e=>{
let ud1 = engine.physics.world.getCollider(e.handle1)?.parent()?.userData,
ud2 = engine.physics.world.getCollider(e.handle2)?.parent()?.userData;
let ud = {...ud1, ...ud2}
//console.log('collision', ud)
if (ud.finish){
if (e.started){
engine.dashboard.levelProgress.update(1)
engine.hero.animationsMap._idle = engine.hero.animationsMap.idle
if ( engine.hero.animationsMap?.win){
engine.hero.animationsMap.idle = engine.hero.animationsMap.win
}
// await Utils.wait(1000);
// engine.hero.cameraDelta = Math.PI;
// engine.hero.direction += Math.PI;
//engine.hero.model.rotation.y += Math.PI;
engine.motionQueue.add({
o: engine.hero,
a:{cameraDelta: k=>k*Math.PI*4 },
t: 12
});
this.dispatchEvent({type:'finish'})
}else{
engine.hero.animationsMap.idle = engine.hero.animationsMap._idle
engine.hero.cameraDelta = 0
}
}
if (ud.qid !== undefined && e.started){
engine.dashboard.updateText(ud.question.q)
engine.dashboard.levelProgress.update(ud.qid / questions.length)
this.dispatchEvent({type:'interaction'});
}
if (ud.corner && e.started){
let q = ud.corner.question;
if (!q.pointsAdded){
let qid = data.questions.indexOf(q);
if (!ud.corner.penalty){
q.pointsAdded = true;
engine.dashboard.addPoints(data.questionPoints - !!q.applyPenalty * data.questionPenalty)
engine.tm?.post('answer', null, { question: qid, correct: true });
}else{
q.applyPenalty = true;
engine.tm?.post('answer', null, { question: qid, correct: false });
}
}
}
//console.log(e, ud, engine.hero?.animationsMap);
})
await this.mazeObject.load();
this.object = this.mazeObject.object;
resolve(this);
})
}
generate(questions, qid = 0, len){
let question = questions[qid]
len = len || Math.round(Math.random()*tl) + 2;
if (!question) return {
len,
userData: { finish: true },
objects:[
// {
// type: 'GltfObject',
// position:[0,.22,len + .52], scale: [0.33, 0.33, 0.33], rotation: [0, Math.PI/4, 0],
// value: 'trophy.glb', path: imgParams.path, distance: params.wallSize*2,
// motion: { a:{rotation: { y: k=>k*Math.PI*2 }}, r: true, t: 4 }
// }
{
type: 'SceneSwitcher', switchScene: this.data.switchScene, switchType: this.data.switchType,
position:[0,.22,len + .52], scale: [0.33, 0.33, 0.33],
distance: this.params.wallSize*4
}
]
};
let directions = Utils.shuffleArray( ['l', 'r', 'f'] )
let mo = {
len, userData: { question, qid },
objects:[
{
...textParams, text: question.q, fontSize:0.033, maxWidth:0.55, position:[0,.55,len + .96], rotation:[0,Math.PI, 0]
}
]
}
question.a.filter(a=>!!a).forEach((a, i)=>{
let d = directions[i];
mo.objects.push(
defaults.arrows[d](len),
defaults.answers[d](len, a)
)
let dd;
if (d == 'f'){
dd = Math.random() > 0.5 ? 'l' : 'r';
}else {
dd = d == 'l' ? 'r' : 'l'
}
if (i == 0){
let next = this.generate(questions, qid + 1, 5)
next.userData.corner = { question };
mo[d] = {
len: 5,
[dd]: next
}
}else{
mo[d] = {
userData: { question, qid, answer: i },
len: 5,
[dd]: {
userData: { corner: { question, penalty: true } },
len: 3,
objects:[
{
...textParams, color:0xc71414, text: question.h, fontSize:0.033, maxWidth:0.66, position:[0,.44,3+.96], rotation:[0,Math.PI, 0]
},{
...imgParams, value:'x.webp', position:[0,.33,3+.96], rotation:[0,Math.PI, 0]
}
]
}
}
}
if (d == 'f'){
mo[d].len = 2;
if (i == 0){
let path = mo[d][dd];
mo[d][dd] = {
len: 1,
[dd == 'r' ? 'l' : 'r']: path
}
}
}
})
return mo;
}
}
export { MazeQuizGame }