File indexing completed on 2024-05-12 15:21:09
0001 /* GCompris - balanceboxeditor.js 0002 * 0003 * SPDX-FileCopyrightText: 2015 Holger Kaelberer <holger.k@elberer.de> 0004 * 0005 * Authors: 0006 * Holger Kaelberer <holger.k@elberer.de> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 0011 .pragma library 0012 .import QtQuick 2.12 as Quick 0013 .import GCompris 1.0 as GCompris 0014 .import "qrc:/gcompris/src/core/core.js" as Core 0015 0016 Qt.include("../balancebox_common.js") 0017 0018 var TOOL_CLEAR = EMPTY 0019 var TOOL_H_WALL = SOUTH 0020 var TOOL_V_WALL = EAST 0021 var TOOL_HOLE = HOLE 0022 var TOOL_CONTACT = CONTACT 0023 var TOOL_GOAL = GOAL 0024 var TOOL_BALL = START 0025 0026 var levels; 0027 var level; 0028 var currentLevel; 0029 var numberOfLevel; 0030 var levelChanged = false; // whether current level has unsaved changes 0031 var props; 0032 var currentIsNewLevel; 0033 var targetList = []; 0034 0035 function initEditor(_props) 0036 { 0037 props = _props; 0038 console.log("init editor"); 0039 0040 currentLevel = 0; 0041 numberOfLevel = 0; 0042 props.lastBallIndex = -1; 0043 props.lastGoalIndex = -1; 0044 levels = []; 0045 if (props.file.exists(props.editor.filename)) { 0046 levels = props.parser.parseFromUrl(props.editor.filename, validateLevels); 0047 if (levels == null) { 0048 console.error("BalanceboxEditor: Error loading levels from " 0049 + props.editor.filename); 0050 levels = []; // restart with an empty level-set 0051 } 0052 } 0053 numberOfLevel = levels.length; 0054 0055 initLevel(); 0056 } 0057 0058 function createEmptyLevel() 0059 { 0060 var map = []; 0061 var num = currentLevel + 1; 0062 for (var row = 0; row < props.rows; row++) 0063 for (var col = 0; col < props.columns; col++) { 0064 if (col === 0) 0065 map[row] = []; 0066 map[row][col] = 0; 0067 } 0068 return { 0069 level: currentLevel + 1, 0070 map: map, 0071 targets: [] 0072 }; 0073 } 0074 0075 function initLevel() 0076 { 0077 props.editor.testBox.loading.start(); 0078 if (currentLevel >= numberOfLevel) { 0079 levels.push(createEmptyLevel()); 0080 levelChanged = false; 0081 numberOfLevel++; 0082 currentIsNewLevel = true; 0083 } else 0084 currentIsNewLevel = false; 0085 0086 level = levels[currentLevel]; 0087 props.bar.level = currentLevel + 1; 0088 // populate model in the worker-thread: 0089 props.editorWorker.sendMessage({ 0090 lastBallIndex: props.lastBallIndex, 0091 lastGoalIndex: props.lastGoalIndex, 0092 lastOrderNum: props.lastOrderNum, 0093 mapModel: props.mapModel, 0094 targetList: targetList, 0095 level: level 0096 }); 0097 } 0098 0099 function dec2hex(i) { 0100 return (i+0x10000).toString(16).substr(-4).toUpperCase(); 0101 } 0102 0103 function modelToLevel() 0104 { 0105 var map = new Array(); 0106 var targets = new Array(); 0107 targetList.sort(function(a,b) { return a - b;}) 0108 for (var i = 0; i < props.mapModel.count; i++) { 0109 var row = Math.floor(i / props.columns); 0110 var col = i % props.columns; 0111 if (col === 0) { 0112 map[row] = new Array(); 0113 } 0114 0115 var obj = props.mapModel.get(i); 0116 var value = obj.value; 0117 value &= ~(0xff00); // always clear order-number bits 0118 if (obj.value & CONTACT) { 0119 value |= ((targetList.indexOf(parseInt(obj.contactValue)) + 1) << 8); 0120 } 0121 map[row][col] = "0x" + dec2hex(value); 0122 } 0123 var level = { 0124 level: currentLevel + 1, 0125 map: map, 0126 targets: targetList 0127 } 0128 return level; 0129 } 0130 0131 function saveModel() 0132 { 0133 var l = modelToLevel(); 0134 levels[currentLevel] = l; 0135 // renumber levels before saving: 0136 for(var i = 0; i < levels.length; i++) 0137 levels[i].level = i + 1; 0138 0139 levelChanged = false; 0140 currentIsNewLevel = false; 0141 return levels 0142 } 0143 0144 function modifyMap(props, row, col) 0145 { 0146 var modelIndex = row * level.map.length + col; 0147 var obj = props.mapModel.get(modelIndex); 0148 var oldValue = obj.value; 0149 var newValue = oldValue; 0150 0151 // contact-tool: check for already existing value early 0152 if (props.currentTool === TOOL_CONTACT // have contact tool and ... 0153 && targetList.indexOf(parseInt(props.contactValue)) !== -1 // already have this contact value ... 0154 && !(obj.value & TOOL_CONTACT // which is not set at the same cell 0155 && obj.contactValue === props.contactValue)) 0156 { 0157 console.debug("Avoiding to set duplicate contact value " + props.contactValue 0158 + " current targets=" + JSON.stringify(targetList)); 0159 return; 0160 } 0161 0162 if (props.currentTool === TOOL_CLEAR) { 0163 newValue = 0; 0164 // remove contact stuff: 0165 if (obj.value & TOOL_CONTACT) { 0166 if (targetList.indexOf(parseInt(obj.contactValue)) !== -1) 0167 targetList.splice(targetList.indexOf(parseInt(obj.contactValue)), 1); 0168 props.mapModel.setProperty(row * level.map.length + col, 0169 "contactValue", ""); 0170 } 0171 } else { // all other tools 0172 0173 // special treatment for mutually exclusive ones: 0174 if (props.currentTool === TOOL_HOLE 0175 || props.currentTool === TOOL_GOAL 0176 || props.currentTool === TOOL_CONTACT 0177 || props.currentTool === TOOL_BALL) { 0178 // helper: 0179 var MUTEX_MASK = (START | GOAL | HOLE | CONTACT) ^ props.currentTool; 0180 newValue &= ~MUTEX_MASK; 0181 } 0182 0183 // special treatment for singletons: 0184 if (props.currentTool === TOOL_GOAL) { 0185 if ((obj.value & TOOL_GOAL) === 0) { 0186 // setting a new one 0187 if (props.lastGoalIndex > -1) { 0188 // clear last one first: 0189 props.mapModel.setProperty(props.lastGoalIndex, "value", 0190 props.mapModel.get(props.lastGoalIndex).value & 0191 (~TOOL_GOAL)); 0192 } 0193 // now memorize the new one: 0194 props.lastGoalIndex = modelIndex; 0195 } 0196 } else 0197 if (props.currentTool === TOOL_BALL) { 0198 if ((obj.value & TOOL_BALL) === 0) { 0199 // setting a new one 0200 if (props.lastBallIndex > -1) 0201 // clear last one first: 0202 props.mapModel.setProperty(props.lastBallIndex, "value", 0203 props.mapModel.get(props.lastBallIndex).value & (~TOOL_BALL)); 0204 // now memorize the new one: 0205 props.lastBallIndex = modelIndex; 0206 } 0207 } 0208 0209 // special treatment for contacts: 0210 if (props.currentTool === TOOL_CONTACT) { 0211 if (obj.value & TOOL_CONTACT && // have old contact value ... 0212 obj.contactValue === props.contactValue) { // ... which is == the new one 0213 // clear contact 0214 if (targetList.indexOf(parseInt(obj.contactValue)) !== -1) 0215 targetList.splice(targetList.indexOf(parseInt(obj.contactValue)), 1); 0216 props.mapModel.setProperty(row * level.map.length + col, 0217 "contactValue", ""); 0218 newValue &= ~(CONTACT); 0219 } else { 0220 if (obj.value & TOOL_CONTACT) { // have old contact that is different 0221 if (targetList.indexOf(parseInt(obj.contactValue)) !== -1) 0222 targetList.splice(targetList.indexOf(parseInt(obj.contactValue)), 1); 0223 // no change to newValue 0224 } 0225 // -> set new one: 0226 if (targetList.indexOf(parseInt(props.contactValue)) === -1) 0227 targetList.push(parseInt(props.contactValue)); 0228 props.mapModel.setProperty(row * level.map.length + col, 0229 "contactValue", props.contactValue); 0230 // the grid is 10*10 so 99 max goals 0231 var contactValue = Number(props.contactValue); 0232 if(contactValue < 99) { 0233 props.contactValue = Number(contactValue + 1).toString(); 0234 } 0235 newValue |= CONTACT; 0236 } 0237 } else { 0238 // for other than contact-tool: update value by current tool bit: 0239 newValue ^= props.currentTool; 0240 } 0241 } 0242 0243 if (oldValue !== newValue) 0244 levelChanged = true; 0245 props.mapModel.setProperty(modelIndex, "value", newValue); 0246 } 0247 0248 var warningVisible = false; 0249 function warnUnsavedChanges(yesFunc, noFunc) 0250 { 0251 if (!warningVisible) { 0252 warningVisible = true; 0253 Core.showMessageDialog(props.editor, 0254 qsTr("You have unsaved changes!<br/> " + 0255 "Do you really want to leave this level and lose your changes?"), 0256 qsTr("Yes"), function() { 0257 warningVisible = false; 0258 if (yesFunc !== undefined) 0259 yesFunc(); 0260 }, 0261 qsTr("No"), function() { 0262 warningVisible = false; 0263 if (noFunc !== undefined) 0264 noFunc(); 0265 }, 0266 function() { 0267 warningVisible = false; 0268 if (noFunc !== undefined) 0269 noFunc(); 0270 }); 0271 } 0272 } 0273 0274 function nextLevel() { 0275 if(numberOfLevel === currentLevel + 1 0276 && !levelChanged && currentIsNewLevel ) { 0277 console.log("BalanceboxEditor: Current level is new and unchanged, nogo!"); 0278 return; 0279 } 0280 0281 currentLevel++; 0282 levelChanged = false; 0283 initLevel(); 0284 } 0285 0286 function previousLevel() { 0287 if (currentLevel === 0) 0288 return; 0289 currentLevel--; 0290 levelChanged = false; 0291 initLevel(); 0292 }