This commit is contained in:
2026-04-12 21:55:31 +03:00
parent 0df683aa8a
commit 36bd33b1f9
15 changed files with 79 additions and 12 deletions
+2 -1
View File
@@ -112,7 +112,8 @@ class AccessManager {
lastName: dbUser.lastName,
displayName: dbUser.displayName,
profilePicture: dbUser.profilePicture,
status: dbUser.status
status: dbUser.status,
gameData: dbUser.gameData
}
}
+1 -1
View File
@@ -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}
});
}
+15
View File
@@ -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];
@@ -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);
}
}
+2 -1
View File
@@ -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);
@@ -116,7 +116,6 @@ class PuzzleGame1 extends EventManager {
s: 'PG2'
});
});
//engine.dashboard.addPoints(10);
}
};
@@ -160,7 +160,6 @@ class PuzzleGame2 extends EventManager {
s: 'PG2'
});
});
//engine.dashboard.addPoints(10);
}
};
engine.addEventListener('beforeRender', this.update)
@@ -117,7 +117,6 @@ var PuzzleGame4 = function(context, gltf, w, h){
})
})
this.dispatchEvent({type:'finish'})
//context.dashboard.addPoints(10);
}
}
}
+6
View File
@@ -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');
}
}
}
+11 -2
View File
@@ -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);
}
+3
View File
@@ -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){
+5 -2
View File
@@ -3,7 +3,7 @@
<h1 class="text-h5 my-2" style="border-bottom:1px solid grey">{{ g }}</h1>
<v-row>
<v-col v-for="(v, i) in items.filter(t=>t.group == g).sort((a, b)=>(a.order||0) - (b.order||0) )" :key="i" cols="12" xs="6" sm="6" md="4" xl="4">
<v-card :to="`/playground/game/${v.id}`" class="hover-effect">
<v-card :to="`/playground/game/${v.id}`" :class="'hover-effect' + (v.locked ? ' locked' : '')">
<v-img :src="`/asset/thumb/${v.thumb}.webp`" cover content-class="d-flex flex-column justify-end" gradient="to bottom, rgba(0,0,0,0), rgba(0,0,0,.5)">
<v-card-title class="text-white text-h6" v-text="v.name"></v-card-title>
<div class="text-white text-caption mx-4" v-text="v.description"></div>
@@ -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();
},
+6 -1
View File
@@ -15,6 +15,9 @@
<v-textarea :label="l.description" v-model="object.description"></v-textarea>
<v-text-field :label="l.group" v-model="object.group"></v-text-field>
<v-number-input density="compact" :label="l.order" v-model="object.order"></v-number-input>
<v-select density="compact" :label="l.completionElements" v-model="object.activationTriggers"
:items="games.filter(v=>v.id != id).map(v=>({title: v.name, value: v.id}))" multiple >
</v-select>
<v-select :label="l.scenario" :items="scenarios" :disabled="this.id != 'add'" v-model="object.scenario" item-title="name" item-value="id"></v-select>
</v-form>
</v-tabs-window-item>
@@ -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){
+1 -1
View File
@@ -1,6 +1,6 @@
<template>
<v-container max-width="1400">
<GamePlay :id="$route.params?.id"></GamePlay>
<GamePlay :id="parseInt($route.params?.id)"></GamePlay>
</v-container>
</template>
+18
View File
@@ -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{