From 36bd33b1f9930f88620b6d46e98151d2a139b1b7 Mon Sep 17 00:00:00 2001 From: goynov Date: Sun, 12 Apr 2026 21:55:31 +0300 Subject: [PATCH] #82 --- backend/app/AccessManager.js | 3 ++- backend/app/bl/GamesManager.js | 2 +- backend/app/bl/UserManager.js | 15 +++++++++++++++ backend/controllers/api/UsersController.js | 9 +++++++++ src/components/GamePlay/GamePlay.vue | 3 ++- .../PuzzleGame1/PuzzleGame1.js | 1 - .../PuzzleGame2/PuzzleGame2.js | 1 - .../InteractiveObjects/PuzzleGame4.js | 1 - src/lib/Api.js | 6 ++++++ src/lib/GameManager.js | 13 +++++++++++-- src/lib/Telemetry.js | 3 +++ src/pages/index.vue | 7 +++++-- src/pages/manage/games/[[id]].vue | 7 ++++++- src/pages/playground/game/[[id]].vue | 2 +- src/styles/style.scss | 18 ++++++++++++++++++ 15 files changed, 79 insertions(+), 12 deletions(-) diff --git a/backend/app/AccessManager.js b/backend/app/AccessManager.js index b9d6ebb..d156b1d 100644 --- a/backend/app/AccessManager.js +++ b/backend/app/AccessManager.js @@ -112,7 +112,8 @@ class AccessManager { lastName: dbUser.lastName, displayName: dbUser.displayName, profilePicture: dbUser.profilePicture, - status: dbUser.status + status: dbUser.status, + gameData: dbUser.gameData } } diff --git a/backend/app/bl/GamesManager.js b/backend/app/bl/GamesManager.js index 4a7e4d5..d7ffc9f 100644 --- a/backend/app/bl/GamesManager.js +++ b/backend/app/bl/GamesManager.js @@ -68,7 +68,7 @@ class GamesManager{ this.list = async function(query){ return await db.list(collection, { query, - project: { name:1, description:1, id:1, thumb: 1, group: 1, order: 1} + project: { name:1, description:1, id:1, thumb: 1, group: 1, order: 1, activationTriggers: 1} }); } diff --git a/backend/app/bl/UserManager.js b/backend/app/bl/UserManager.js index c420781..166ef5d 100644 --- a/backend/app/bl/UserManager.js +++ b/backend/app/bl/UserManager.js @@ -95,6 +95,21 @@ class UserManager { await db.update(collection, { '_id': db.ObjectId(user._id) }, user); } + this.addGameData = async function(ctx, data){ + if (ctx.user?._id){ + let user = await db.get(collection, { '_id': db.ObjectId(ctx.user._id) }); + let gameData = (user.gameData || []).filter(gd=>gd.id != data.id); + gameData.push(data); + ctx.user.gameData = user.gameData = gameData; + await db.update(collection, { '_id': db.ObjectId(user._id) }, user); + //ctx.user.gameData = gameData; + }else{ + let gameData = (ctx.session.gameData || []).filter(gd=>gd.id != data.id); + gameData.push(data); + ctx.session.gameData = gameData + } + } + this.assignSafeUserData = function (ctx, userObject, newData, isAdmin) { ['displayName', 'firstName', 'lastName', ...(isAdmin ? ['roles', 'groups', 'email', 'status'] : [])].forEach(e => { userObject[e] = newData[e]; diff --git a/backend/controllers/api/UsersController.js b/backend/controllers/api/UsersController.js index 904587c..956e6c3 100644 --- a/backend/controllers/api/UsersController.js +++ b/backend/controllers/api/UsersController.js @@ -167,6 +167,15 @@ class UsersController { } }) + router.get('/gameData', (req, res) => { + res.json(req.user?.gameData || req.session.gameData || []); + }) + + router.put('/gameData', async (req, res) => { + await user.addGameData(req, req.body); + res.json(req.user?.gameData || req.session.gameData || []); + }) + app.webServer.xapp.use(this.route, router); } } diff --git a/src/components/GamePlay/GamePlay.vue b/src/components/GamePlay/GamePlay.vue index b5ddf9a..56c1b9f 100644 --- a/src/components/GamePlay/GamePlay.vue +++ b/src/components/GamePlay/GamePlay.vue @@ -24,7 +24,8 @@ export default { mode: 'GamePlay' }); manager = await new GameManager(engine, this.id); - manager.addEventListener('the-end', _=>{ + manager.addEventListener('the-end', async e =>{ + await this.$api.user.addGameData({ id: this.id, score: e.score }); this.$router.push('/'); }) window.addEventListener('resize', this.resize); diff --git a/src/components/InteractiveObjects/PuzzleGame1/PuzzleGame1.js b/src/components/InteractiveObjects/PuzzleGame1/PuzzleGame1.js index 288f6b1..f9c75bc 100644 --- a/src/components/InteractiveObjects/PuzzleGame1/PuzzleGame1.js +++ b/src/components/InteractiveObjects/PuzzleGame1/PuzzleGame1.js @@ -116,7 +116,6 @@ class PuzzleGame1 extends EventManager { s: 'PG2' }); }); - //engine.dashboard.addPoints(10); } }; diff --git a/src/components/InteractiveObjects/PuzzleGame2/PuzzleGame2.js b/src/components/InteractiveObjects/PuzzleGame2/PuzzleGame2.js index 0039d6b..c629ef7 100644 --- a/src/components/InteractiveObjects/PuzzleGame2/PuzzleGame2.js +++ b/src/components/InteractiveObjects/PuzzleGame2/PuzzleGame2.js @@ -160,7 +160,6 @@ class PuzzleGame2 extends EventManager { s: 'PG2' }); }); - //engine.dashboard.addPoints(10); } }; engine.addEventListener('beforeRender', this.update) diff --git a/src/components/InteractiveObjects/PuzzleGame4.js b/src/components/InteractiveObjects/PuzzleGame4.js index 48db7e3..13ea40c 100644 --- a/src/components/InteractiveObjects/PuzzleGame4.js +++ b/src/components/InteractiveObjects/PuzzleGame4.js @@ -117,7 +117,6 @@ var PuzzleGame4 = function(context, gltf, w, h){ }) }) this.dispatchEvent({type:'finish'}) - //context.dashboard.addPoints(10); } } } diff --git a/src/lib/Api.js b/src/lib/Api.js index d28f2cd..c541402 100644 --- a/src/lib/Api.js +++ b/src/lib/Api.js @@ -85,6 +85,12 @@ const api = { }, async update(data){ return await $ax.post('/user/update', data); + }, + async addGameData(data){ + return await $ax.put('/user/gameData', data); + }, + async getGameData(){ + return await $ax.get('/user/gameData'); } } } diff --git a/src/lib/GameManager.js b/src/lib/GameManager.js index ad1f7fe..d76e36e 100644 --- a/src/lib/GameManager.js +++ b/src/lib/GameManager.js @@ -59,7 +59,7 @@ class GameManager extends EventManager{ } let expectToFinish = 0, finished = 0, iobjs = []; if (scene.data.items){ - let loaded = 0; + let loaded = 0, maxPoints = 0; for (let i of scene.data.items) { //this.debug('Loading', i.data.id); if (engine.opts.mode != 'GameDesigner'){ @@ -75,6 +75,7 @@ class GameManager extends EventManager{ } this.setObjectAttributes(sceneObjects, i.data, io.object, io.source, 1); engine.activeObjects.add(io.object); + maxPoints += io.maxPoints; if (engine.opts.mode != 'GameDesigner'){ if (i.data.$go?.type == 'player3d'){ let hero = new Hero(engine, io); @@ -115,7 +116,15 @@ class GameManager extends EventManager{ //this.scenesList = [this.scenes.find(s=>s.data.id == e.scene)] //switch to next scene: if (e.scene == '__end__'){ - this.dispatchEvent({ type: 'the-end' }); + let score = { + value: engine.dashboard.points, + max: maxPoints + } + this.dispatchEvent({ + type: 'the-end', + score + }); + engine.tm?.completeGame(gameData.id, score); }else{ this.loadScene(e.scene); } diff --git a/src/lib/Telemetry.js b/src/lib/Telemetry.js index 90830a9..5bde1dd 100644 --- a/src/lib/Telemetry.js +++ b/src/lib/Telemetry.js @@ -27,6 +27,9 @@ class Telemetry { this.game = game; this.gameStart = Math.round(performance.now()/1000); } + completeGame(game, score){ + this.#af('game:complete', game, { mode: this.mode, game, score }); + } setScene(scene){ if (this.scene == scene) { return; } if (this.scene){ diff --git a/src/pages/index.vue b/src/pages/index.vue index 12a9367..0d7158f 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -3,7 +3,7 @@

{{ g }}

- +
@@ -19,16 +19,19 @@ export default { data(){ return { items: [], - groups: [] + groups: [], + gameData: [] } }, async created(){ + this.gameData = (await this.$api.user.getGameData()).data this.items = (await this.$api.game.search()).data.data; this.items.forEach(i=>{ if (this.groups.indexOf(i.group) == -1){ this.groups.push(i.group); } + i.locked = (i.activationTriggers||[]).filter(at=>!this.gameData.find(gd=>gd.id == at)).length }) this.groups.sort(); }, diff --git a/src/pages/manage/games/[[id]].vue b/src/pages/manage/games/[[id]].vue index 2db1724..ae124c7 100644 --- a/src/pages/manage/games/[[id]].vue +++ b/src/pages/manage/games/[[id]].vue @@ -15,6 +15,9 @@ + + @@ -39,7 +42,8 @@ export default { }, loading: false, panel: [], - scenarios: [] + scenarios: [], + games: [] } }, async mounted(){ @@ -47,6 +51,7 @@ export default { this.object = (await this.$api.game.load(this.id)).data; } this.scenarios = (await this.$api.scenario.search()).data.data; + this.games = (await this.$api.game.search()).data.data; }, watch:{ 'object.scenario'(v){ diff --git a/src/pages/playground/game/[[id]].vue b/src/pages/playground/game/[[id]].vue index 29e8346..89b4dbb 100644 --- a/src/pages/playground/game/[[id]].vue +++ b/src/pages/playground/game/[[id]].vue @@ -1,6 +1,6 @@ diff --git a/src/styles/style.scss b/src/styles/style.scss index af1946e..b1725b3 100644 --- a/src/styles/style.scss +++ b/src/styles/style.scss @@ -84,6 +84,24 @@ video{ } } +.locked { + pointer-events: none; + opacity: 0.5; + position: relative; + &::after{ + content:"\F033E"; + text-align: center; + font-family: "Material Design Icons"; + font-size: 111px; + opacity: 0.5; + position: absolute; + width: 100%; + height: 100%; + left:0; + top:0; + } +} + .svg-container{ svg{ image{