/* * GDevelop JS Platform * Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved. * This project is released under the MIT License. */ /** * Tools related to objects, for events generated code. * @memberof gdjs.evtTools * @namespace object */ gdjs.evtTools.object = gdjs.evtTools.object || {}; /** * Keep only the specified object in the lists of picked objects. * * @param {Hashtable} objectsLists The lists of objects to trim * @param {gdjs.RuntimeObject} runtimeObject The object to keep in the lists */ gdjs.evtTools.object.pickOnly = function(objectsLists, runtimeObject) { for (var listName in objectsLists.items) { if (objectsLists.items.hasOwnProperty(listName)) { var list = objectsLists.items[listName]; if (list.indexOf(runtimeObject) === -1) { list.length = 0; //Be sure not to lose the reference to the original array } else { list.length = 0; //Be sure not to lose the reference to the original array list.push(runtimeObject); } } } }; /** * A predicate to be passed to `gdjs.evtTools.object.twoListsTest`. * @callback gdjsTwoListsTestPredicate * @param {gdjs.RuntimeObject} object1 First object * @param {gdjs.RuntimeObject} object2 Second object * @param {*} extraArg An optional extra argument * @return {boolean} true if the pair satisfy the predicate (for example,there is a collision), meaning that the objects will be picked, false otherwise (no collision). */ /** * Do a test on two tables of objects so as to pick only the pair of objects for which the test is true. * * Note that the predicate method is not called stricly for each pair: When considering a pair of objects, if * these objects have already been marked as picked, the predicate method won't be called again. * * Cost (Worst case, predicate being always false): * Cost(Setting property 'picked' of NbObjList1+NbObjList2 objects to false) * + Cost(predicate)*NbObjList1*NbObjList2 * + Cost(Testing NbObjList1+NbObjList2 booleans) * + Cost(Removing NbObjList1+NbObjList2 objects from all the lists) * * Cost (Best case, predicate being always true): * Cost(Setting property 'picked' of NbObjList1+NbObjList2 objects to false) * + Cost(predicate)*(NbObjList1+NbObjList2) * + Cost(Testing NbObjList1+NbObjList2 booleans) * * * @param {gdjsTwoListsTestPredicate} predicate The predicate function is called with the two objects to compare, and an optional argument `extraArg` * @param {Hashtable} objectsLists1 e.g. Hashtable.newFrom({ A: objects1 }); * @param {Hashtable} objectsLists2 e.g. Hashtable.newFrom({ B: objects2 }); * @param {boolean} inverted If `inverted` == true, only the objects of the first table are filtered. * @param {*} extraArg (optional) This argument should be used to avoid declaring the predicate as a closure that would be created and destroyed at each call to twoListsTest (potentially multiple time per frame). */ gdjs.evtTools.object.twoListsTest = function(predicate, objectsLists1, objectsLists2, inverted, extraArg) { var isTrue = false; var objects1Lists = gdjs.staticArray(gdjs.evtTools.object.twoListsTest); objectsLists1.values(objects1Lists); var objects2Lists = gdjs.staticArray2(gdjs.evtTools.object.twoListsTest); objectsLists2.values(objects2Lists); for(var i = 0, leni = objects1Lists.length;i= objectsCount) index = objectsCount-1; //Should never happen. // Find the object var startIndex = 0; var theChosenOne = null; for (var listName in objectsLists.items) { if (objectsLists.items.hasOwnProperty(listName)) { var list = objectsLists.items[listName]; if (index - startIndex < list.length) { theChosenOne = list[index - startIndex]; break; } startIndex += list.length; } } gdjs.evtTools.object.pickOnly(objectsLists, theChosenOne); return true; }; gdjs.evtTools.object.pickNearestObject = function(objectsLists, x, y, inverted) { var bestObject = null; var best = 0; var first = true; var lists = gdjs.staticArray(gdjs.evtTools.object.pickNearestObject); objectsLists.values(lists); for(var i = 0, len = lists.length;i= testSqDist) ) { testSqDist = result.farSqDist; matchObject = object; resultX = result.farX; resultY = result.farY; } } } } if ( !matchObject ) return false; gdjs.evtTools.object.pickOnly(objectsLists, matchObject); varX.setNumber(resultX); varY.setNumber(resultY); return true; }; /** * Do the work of creating a new object * @private */ gdjs.evtTools.object.doCreateObjectOnScene = function(objectsContext, objectName, objectsLists, x, y, layer) { // objectsContext will either be the gdjs.RuntimeScene or, in an events function, the // eventsFunctionContext. We can't directly use runtimeScene because the object name could // be different than the real object name (this is the case in a function. The eventsFunctionContext // will take care of this in createObject). var obj = objectsContext.createObject(objectName); if ( obj !== null ) { //Do some extra setup obj.setPosition(x,y); obj.setLayer(layer); //Let the new object be picked by next actions/conditions. if ( objectsLists.containsKey(objectName) ) { objectsLists.get(objectName).push(obj); } } }; /** * Allows events to create a new object on a scene. * @private */ gdjs.evtTools.object.createObjectOnScene = function(objectsContext, objectsLists, x, y, layer) { gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectsLists.firstKey(), objectsLists, x, y, layer); }; /** * Allows events to create a new object on a scene. * @private */ gdjs.evtTools.object.createObjectFromGroupOnScene = function(objectsContext, objectsLists, objectName, x, y, layer) { gdjs.evtTools.object.doCreateObjectOnScene(objectsContext, objectName, objectsLists, x, y, layer); }; /** * Allows events to get the number of objects picked. * @private */ gdjs.evtTools.object.pickedObjectsCount = function(objectsLists) { var size = 0; var lists = gdjs.staticArray(gdjs.evtTools.object.pickedObjectsCount); objectsLists.values(lists); for(var i = 0, len = lists.length;i