lists
This commit is contained in:
@@ -5,7 +5,7 @@ const collection = 'scenarios';
|
|||||||
* Scenarios manager class, контролен клас за управление на игрови сценарии
|
* Scenarios manager class, контролен клас за управление на игрови сценарии
|
||||||
*/
|
*/
|
||||||
class ScenariosManager{
|
class ScenariosManager{
|
||||||
name = 'scenarios';
|
name = 'scenario';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class initializer, инициализация
|
* Class initializer, инициализация
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class ScenariosController{
|
|||||||
*/
|
*/
|
||||||
init(app){
|
init(app){
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { scenarios } = app;
|
const { scenario } = app;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API: PUT /api/scenario/ Create or update scenario, създаване/обновяване на игрови сценарий
|
* API: PUT /api/scenario/ Create or update scenario, създаване/обновяване на игрови сценарий
|
||||||
@@ -24,7 +24,7 @@ class ScenariosController{
|
|||||||
router.put('/', async (req, res)=>{
|
router.put('/', async (req, res)=>{
|
||||||
try{
|
try{
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
let object = await scenarios[data.id? 'update' : 'create'](req, data)
|
let object = await scenario[data.id? 'update' : 'create'](req, data)
|
||||||
res.json({status: 'OK', object});
|
res.json({status: 'OK', object});
|
||||||
}catch(err){
|
}catch(err){
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -39,7 +39,7 @@ class ScenariosController{
|
|||||||
* @memberof ScenariosController
|
* @memberof ScenariosController
|
||||||
*/
|
*/
|
||||||
router.post('/', async (req, res)=>{
|
router.post('/', async (req, res)=>{
|
||||||
let result = await scenarios.list(req.body);
|
let result = await scenario.list(req.body);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ class ScenariosController{
|
|||||||
* @memberof ScenariosController
|
* @memberof ScenariosController
|
||||||
*/
|
*/
|
||||||
router.get('/:id', async (req, res)=>{
|
router.get('/:id', async (req, res)=>{
|
||||||
let object = await scenarios.read(parseInt(req.params.id));
|
let object = await scenario.read(parseInt(req.params.id));
|
||||||
res.json(object);
|
res.json(object);
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ class ScenariosController{
|
|||||||
* @memberof ScenariosController
|
* @memberof ScenariosController
|
||||||
*/
|
*/
|
||||||
router.delete('/:id', async (req, res)=>{
|
router.delete('/:id', async (req, res)=>{
|
||||||
await gameObject.remove(req.params.id);
|
await scenario.remove(req.params.id);
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="(v, i) in items" :key="i" cols="12" xs="6" sm="4" md="3" xl="2"
|
<v-col v-for="(v, i) in items" :key="i" cols="12" xs="6" sm="4" md="3" xl="2"
|
||||||
class="position-relative">
|
class="position-relative">
|
||||||
<v-img :src="`/asset/thumb/${v.asset?.thumb}`" @click="select(v.id)"
|
<v-img :src="`/asset/thumb/${v.asset?.thumb}`" @click="select(v)"
|
||||||
class="cursor-pointer"></v-img>
|
class="cursor-pointer"></v-img>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<span class="flex-grow-1">{{ v.name }}</span>
|
<span class="flex-grow-1">{{ v.name }}</span>
|
||||||
@@ -38,10 +38,6 @@ export default {
|
|||||||
dialog: false
|
dialog: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted(){
|
|
||||||
console.log(this.activatorProps, this.cls)
|
|
||||||
},
|
|
||||||
|
|
||||||
async created(){
|
async created(){
|
||||||
this.items = (await this.$api.gameObject.search({
|
this.items = (await this.$api.gameObject.search({
|
||||||
@@ -50,8 +46,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods:{
|
methods:{
|
||||||
select(id){
|
select(v){
|
||||||
this.$emit('select', id);
|
this.$emit('select', { id: v.id, name: v.name });
|
||||||
this.dialog = false;
|
this.dialog = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<teleport to=".scene-designer" v-if="active">
|
<teleport to=".scene-designer .game-objects" v-if="active">
|
||||||
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{gameObject: true, selected}" v-show="visible && parent.visible">
|
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{gameObject: true, selected}" v-show="visible && parent.visible">
|
||||||
<line :x1="vd.x1" :y1="vd.y1" :x2="parent.vd.x1" :y2="parent.vd.y1"></line>
|
<line :x1="vd.x1" :y1="vd.y1" :x2="parent.vd.x1" :y2="parent.vd.y1"></line>
|
||||||
<svg-icon :src="`/asset/thumb/${modelValue.id||0}.webp`" :x="vd.x1" :y="vd.y1" :size="37"></svg-icon>
|
<svg-icon :src="`/asset/thumb/${modelValue.go||0}.webp`" :x="vd.x1" :y="vd.y1" :size="37"></svg-icon>
|
||||||
</g>
|
</g>
|
||||||
</teleport>
|
</teleport>
|
||||||
<v-card v-if="selected" class="py-4">
|
<v-card v-if="selected" class="py-4">
|
||||||
<asset-selector @select="modelValue.id = $event" type="GameObject">
|
<asset-selector @select="assignGameObject" type="GameObject">
|
||||||
<template v-slot:activator="props">
|
<template v-slot:activator="props">
|
||||||
<v-btn v-bind="props" prepend-icon="mdi-panorama-outline" color="success" block>Choose game object</v-btn>
|
<v-btn v-bind="props" prepend-icon="mdi-panorama-outline" color="success" block>Choose game object</v-btn>
|
||||||
</template>
|
</template>
|
||||||
@@ -47,6 +47,12 @@ export default {
|
|||||||
methods:{
|
methods:{
|
||||||
intersect(v){
|
intersect(v){
|
||||||
return Utils.intersectLineRect(this.vd, v);
|
return Utils.intersectLineRect(this.vd, v);
|
||||||
|
},
|
||||||
|
assignGameObject(e){
|
||||||
|
this.modelValue.go = e.id;
|
||||||
|
if (this.modelValue.id == this.modelValue.title){
|
||||||
|
this.modelValue.title = e.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<teleport to=".scene-designer" v-if="active">
|
<teleport to=".scene-designer .scenes" v-if="active">
|
||||||
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{scene: true, selected}" v-show="visible">
|
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{scene: true, selected}" v-show="visible">
|
||||||
<svg-icon :src="`/asset/thumb/${modelValue.environment||0}.webp`" :x="vd.x1" :y="vd.y1" :size="65"></svg-icon>
|
<svg-icon :src="`/asset/thumb/${modelValue.environment||0}.webp`" :x="vd.x1" :y="vd.y1" :size="65"></svg-icon>
|
||||||
</g>
|
</g>
|
||||||
</teleport>
|
</teleport>
|
||||||
<v-card title="Scene" v-if="selected">
|
<v-card title="Scene" v-if="selected">
|
||||||
<asset-selector @select="modelValue.environment = $event" type="Scene">
|
<asset-selector @select="assignGameObject" type="Scene">
|
||||||
<template v-slot:activator="props">
|
<template v-slot:activator="props">
|
||||||
<v-btn v-bind="props" prepend-icon="mdi-panorama-outline" block color="success" class="py-4">Choose environment</v-btn>
|
<v-btn v-bind="props" prepend-icon="mdi-panorama-outline" block color="success" class="py-4">Choose environment</v-btn>
|
||||||
</template>
|
</template>
|
||||||
@@ -49,6 +49,12 @@ export default {
|
|||||||
methods:{
|
methods:{
|
||||||
intersect(v){
|
intersect(v){
|
||||||
return Utils.intersectLineRect(this.vd, v);
|
return Utils.intersectLineRect(this.vd, v);
|
||||||
|
},
|
||||||
|
assignGameObject(e){
|
||||||
|
this.modelValue.environment = e.id;
|
||||||
|
if (this.modelValue.id == this.modelValue.title){
|
||||||
|
this.modelValue.title = e.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,16 @@
|
|||||||
:viewBox="`${vb.x} ${vb.y} ${vb.w} ${vb.h}`" x="0" y="0" xmlns="http://www.w3.org/2000/svg"
|
:viewBox="`${vb.x} ${vb.y} ${vb.w} ${vb.h}`" x="0" y="0" xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<SvgRectangle v-model="selector" class="selector"></SvgRectangle>
|
<SvgRectangle v-model="selector" class="selector"></SvgRectangle>
|
||||||
|
<g class="tasks"></g>
|
||||||
|
<g class="game-objects"></g>
|
||||||
|
<g class="scenes"></g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<v-navigation-drawer location="right" width="400">
|
<v-navigation-drawer location="right" width="400">
|
||||||
<v-list density="compact" nav v-model:selected="selectedItem"
|
<v-list density="compact" nav v-model:selected="selectedItem"
|
||||||
:select-strategy="mode == 'select' ? 'leaf' : 'single-leaf'">
|
:select-strategy="mode == 'select' ? 'leaf' : 'single-leaf'">
|
||||||
<v-list-item v-for="(item, i) in flatItems.toSorted((a,b)=>a.__path.localeCompare(b.__path))"
|
<v-list-item v-for="(item, i) in flatItems.toSorted((a,b)=>a.__path.localeCompare(b.__path))"
|
||||||
:key="i" :title="item.data.title" :value="item" :style="`padding-left:${item.__level}rem`">
|
:key="i" :title="item.data.title" :value="item" :style="`padding-left:${item.__level}rem`" v-show="!item.__parent || item.__parent.vd.expanded">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-btn variant="plain" density="comfortable" size="small"
|
<v-btn variant="plain" density="comfortable" size="small"
|
||||||
:icon="`mdi-eye${item.visible ? '' : '-off'}`"
|
:icon="`mdi-eye${item.visible ? '' : '-off'}`"
|
||||||
@@ -35,60 +38,13 @@
|
|||||||
<!-- <v-icon :icon="components[item.name].icon" size="small"></v-icon> -->
|
<!-- <v-icon :icon="components[item.name].icon" size="small"></v-icon> -->
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-btn variant="plain" density="compact" class="float-right" size="small" color="red-darken-4" icon="mdi-delete"
|
<v-btn variant="plain" density="compact" size="small" color="red-darken-4" icon="mdi-delete"
|
||||||
@click="items.splice(i, 1)"></v-btn>
|
@click="remove(item)"></v-btn>
|
||||||
|
<v-btn v-if="item.__type != 'Task'" :icon="item.vd.expanded ? 'mdi-chevron-up' : 'mdi-chevron-down'"
|
||||||
|
variant="plain" size="small" @click="item.vd.expanded = !item.vd.expanded"></v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
<v-list density="compact" v-model:selected="selectedItem"
|
|
||||||
:select-strategy="mode == 'select' ? 'independent' : 'single-independent'" selectable
|
|
||||||
open-strategy="multiple">
|
|
||||||
<v-list-group v-for="(item, i) in items" :key="i" :value="item" subgroup>
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-list-item v-bind="props" color="secondary">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-btn variant="plain" density="comfortable" size="small"
|
|
||||||
:icon="`mdi-eye${item.visible ? '' : '-off'}`"
|
|
||||||
@click.stop="item.visible = !item.visible"></v-btn>
|
|
||||||
<!-- <v-icon :icon="components[item.name].icon" size="small"></v-icon> -->
|
|
||||||
</template>
|
|
||||||
<v-list-item-title>
|
|
||||||
{{ item.data.title }}
|
|
||||||
<v-btn variant="plain" density="compact" class="float-right" size="small" color="red-darken-4" icon="mdi-delete"
|
|
||||||
@click="items.splice(i, 1)"></v-btn>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
<v-list-group v-for="(go, i) in item.data.gameObjects" :key="i" :value="go" subgroup>
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-list-item color="primary" v-bind="props" >
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-btn variant="plain" density="comfortable" size="small"
|
|
||||||
:icon="`mdi-eye${go.visible ? '' : '-off'}`"
|
|
||||||
@click.stop="go.visible = !go.visible"></v-btn>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title>
|
|
||||||
{{ go.data.title }}
|
|
||||||
<v-btn variant="plain" density="compact" class="float-right" size="small" color="red-darken-4" icon="mdi-delete"
|
|
||||||
@click="item.data.gameObjects.splice(i, 1)"></v-btn>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</template>
|
|
||||||
<v-list-item v-for="(task, i) in go.data.tasks" :key="i" :value="task" color="primary">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-btn variant="plain" density="comfortable" size="small"
|
|
||||||
:icon="`mdi-eye${task.visible ? '' : '-off'}`"
|
|
||||||
@click.stop="task.visible = !task.visible"></v-btn>
|
|
||||||
</template>
|
|
||||||
<v-list-item-title>
|
|
||||||
{{ task.data.title }}
|
|
||||||
<v-btn variant="plain" density="compact" class="float-right" size="small" color="red-darken-4" icon="mdi-delete"
|
|
||||||
@click="go.data.tasks.splice(i, 1)"></v-btn>
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list-group>
|
|
||||||
</v-list-group>
|
|
||||||
</v-list>
|
|
||||||
|
|
||||||
<template v-for="(item, i) in flatItems" :key="i">
|
<template v-for="(item, i) in flatItems" :key="i">
|
||||||
<component :is="components[item.__type]" :ref="'svg-'+item.id" :vd="item.vd" v-model="item.data"
|
<component :is="components[item.__type]" :ref="'svg-'+item.id" :vd="item.vd" v-model="item.data"
|
||||||
@@ -148,6 +104,7 @@ export default {
|
|||||||
mounted(){
|
mounted(){
|
||||||
window.addEventListener('resize', this.resize);
|
window.addEventListener('resize', this.resize);
|
||||||
this.resize();
|
this.resize();
|
||||||
|
this.object.scenes = this.object.scenes || [];
|
||||||
},
|
},
|
||||||
unmounted(){
|
unmounted(){
|
||||||
window,removeEventListener('resize', this.resize);
|
window,removeEventListener('resize', this.resize);
|
||||||
@@ -184,12 +141,12 @@ export default {
|
|||||||
i.__type = 'Scene';
|
i.__type = 'Scene';
|
||||||
i.__path = `scene-${ii.toString().padStart(4, '0')}`;
|
i.__path = `scene-${ii.toString().padStart(4, '0')}`;
|
||||||
i.__level = 1;
|
i.__level = 1;
|
||||||
i.data?.gameObjects?.forEach((go, igo)=>{
|
i.data?.items?.forEach((go, igo)=>{
|
||||||
go.__parent = i;
|
go.__parent = i;
|
||||||
go.__type = 'GameObject';
|
go.__type = 'GameObject';
|
||||||
go.__path = `${i.__path}.go-${igo.toString().padStart(4, '0')}`
|
go.__path = `${i.__path}.go-${igo.toString().padStart(4, '0')}`
|
||||||
go.__level = 2;
|
go.__level = 2;
|
||||||
go.data.tasks?.forEach((t, it)=>{
|
go.data.items?.forEach((t, it)=>{
|
||||||
fi.push(t);
|
fi.push(t);
|
||||||
t.__parent = go;
|
t.__parent = go;
|
||||||
t.__type = 'Task';
|
t.__type = 'Task';
|
||||||
@@ -318,22 +275,19 @@ export default {
|
|||||||
let id, nid = 1;
|
let id, nid = 1;
|
||||||
do {
|
do {
|
||||||
id = `${this.components[this.mode].name}-${nid++}`
|
id = `${this.components[this.mode].name}-${nid++}`
|
||||||
}while (this.flatItems.find(i=>i.id == id));
|
}while (this.flatItems.find(i=>i.data.id == id));
|
||||||
|
|
||||||
let targetContainer = this.selectedItem[0]?.data; //this.items;
|
let targetContainer = this.selectedItem[0]?.data; //this.items;
|
||||||
if (this.mode == 'GameObject'){
|
if (targetContainer){
|
||||||
targetContainer.gameObjects = targetContainer.gameObjects || [];
|
targetContainer.items = targetContainer.items || [];
|
||||||
targetContainer = targetContainer.gameObjects;
|
targetContainer = targetContainer.items;
|
||||||
}else if(this.mode == 'Task'){
|
|
||||||
targetContainer.tasks = targetContainer.tasks || [];
|
|
||||||
targetContainer = targetContainer.tasks;
|
|
||||||
}else{
|
}else{
|
||||||
targetContainer = this.items
|
targetContainer = this.items
|
||||||
}
|
}
|
||||||
targetContainer.push({
|
targetContainer.push({
|
||||||
//__type: this.mode,
|
//__type: this.mode,
|
||||||
vd: this.target.target,
|
vd: this.target.target,
|
||||||
data: { title: id },
|
data: { title: id, id },
|
||||||
visible: true
|
visible: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -375,6 +329,11 @@ export default {
|
|||||||
setTarget(t, item){
|
setTarget(t, item){
|
||||||
this.target = t;
|
this.target = t;
|
||||||
this.selectedItem = [item]
|
this.selectedItem = [item]
|
||||||
|
let i = item;
|
||||||
|
while (i){
|
||||||
|
i.vd.expanded = true;
|
||||||
|
i = i.__parent;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
select(){
|
select(){
|
||||||
let r = Utils.adjustMinMax(this.selector);
|
let r = Utils.adjustMinMax(this.selector);
|
||||||
@@ -386,6 +345,11 @@ export default {
|
|||||||
this.viewBox.h = r.clientHeight;
|
this.viewBox.h = r.clientHeight;
|
||||||
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
//this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h);
|
||||||
},
|
},
|
||||||
|
remove(item){
|
||||||
|
//console.log(item);
|
||||||
|
let p = item.__parent?.data?.items || this.items;
|
||||||
|
p.splice(p.indexOf(item), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<teleport to=".scene-designer" v-if="active">
|
<teleport to=".scene-designer .tasks" v-if="active">
|
||||||
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{task: true, selected}" v-show="visible && parent.visible">
|
<g @mousedown="$emit('target', {target:vd, attrs:['x1', 'y1'], delta: true})" :class="{task: true, selected}"
|
||||||
|
v-show="visible && parent.visible && parent.__parent.visible">
|
||||||
<line :x1="vd.x1" :y1="vd.y1" :x2="parent.vd.x1" :y2="parent.vd.y1"></line>
|
<line :x1="vd.x1" :y1="vd.y1" :x2="parent.vd.x1" :y2="parent.vd.y1"></line>
|
||||||
<svg-icon :src="`/asset/thumb/${modelValue.id||0}.webp`" :x="vd.x1" :y="vd.y1" :size="22"></svg-icon>
|
<svg-icon :src="`/asset/thumb/0.webp`" :x="vd.x1" :y="vd.y1" :size="22"></svg-icon>
|
||||||
</g>
|
</g>
|
||||||
</teleport>
|
</teleport>
|
||||||
<v-card v-if="selected">
|
<v-card v-if="selected">
|
||||||
|
|||||||
Reference in New Issue
Block a user