Commit 58c95f1c authored by Roque's avatar Roque

erp5_officejs_drone_capture_flag: changes on the game

- add back the drone collision logic
- check drone out with map limits
- randomize seed library
- use seed to randomize values
- terrain texture change
- adapt default configuration
- refactor to allow map_size as parameter (instead of min-max lat-lon)
- new map function
- camera and size limits
- enemy team
- enemy AI script
- add flags and obstacles
- obstacle and flag collision
- allow dronemanager to take cartesian coordinates
- game finish rulls
- user score and result message
- refactor AI default script (drone dodges obstacles)
- new drone implementation for enemy drone (API)
- add getDroneViewInfo to API(s)
- restore drone color dot trace (improve color pick)
- allow random seed as url parameter
- handle fullscreen
- flag weight as parameter
- show default seed value, and make input mandatory
- do not show enemy drone logs on UI
- flag collision crashes the drone
- update score calculation
- drop flag weight (model and parameter)
- use geo coordinates in general
- refactor enemy drone to use cartesian
- add debug mode for test using console logs
- async obstacle detection (API changes)
parent a5c95ebd
/*global BABYLON, console*/
/*jslint nomen: true, indent: 2, maxlen: 80, todo: true */
/************************** ENEMY DRONE API ****************************/
var EnemyDroneAPI = /** @class */ (function () {
"use strict";
var DEFAULT_ACCELERATION = 1,
VIEW_SCOPE = 50,
DEFAULT_SPEED = 16.5,
MIN_SPEED = 12,
MAX_SPEED = 26;
//** CONSTRUCTOR
function EnemyDroneAPI(gameManager, drone_info, flight_parameters, id) {
this._gameManager = gameManager;
this._mapManager = this._gameManager._mapManager;
this._map_dict = this._mapManager.getMapInfo();
this._flight_parameters = flight_parameters;
this._id = id;
this._drone_info = drone_info;
this._drone_dict_list = [];
this._acceleration = DEFAULT_ACCELERATION;
}
/*
** Function called on start phase of the drone, just before onStart AI script
*/
EnemyDroneAPI.prototype.internal_start = function (drone) {
drone._targetCoordinates = drone.getCurrentPosition();
drone._maxAcceleration = this.getMaxAcceleration();
if (drone._maxAcceleration <= 0) {
throw new Error('max acceleration must be superior to 0');
}
drone._minSpeed = this.getMinSpeed();
if (drone._minSpeed <= 0) {
throw new Error('min speed must be superior to 0');
}
drone._maxSpeed = this.getMaxSpeed();
if (drone._minSpeed > drone._maxSpeed) {
throw new Error('min speed cannot be superior to max speed');
}
drone._speed = drone._targetSpeed = this.getInitialSpeed();
if (drone._speed < drone._minSpeed || drone._speed > drone._maxSpeed) {
throw new Error('Drone speed must be between min speed and max speed');
}
if (drone._maxSinkRate > drone._maxSpeed) {
throw new Error('max sink rate cannot be superior to max speed');
}
drone._maxOrientation = this.getMaxOrientation();
return;
};
/*
** Function called on every drone update, right before onUpdate AI script
*/
EnemyDroneAPI.prototype.internal_update = function (context, delta_time) {
context._speed += context._acceleration * delta_time / 1000;
if (context._speed > context._maxSpeed)
context._speed = context._maxSpeed;
if (context._speed < -context._maxSpeed)
context._speed = -context._maxSpeed;
var updateSpeed = context._speed * delta_time / 1000;
if (context._direction.x !== 0 ||
context._direction.y !== 0 ||
context._direction.z !== 0) {
context._controlMesh.position.addInPlace(
new BABYLON.Vector3(context._direction.x * updateSpeed,
context._direction.y * updateSpeed,
context._direction.z * updateSpeed));
}
var orientationValue = context._maxOrientation *
(context._speed / context._maxSpeed);
context._mesh.rotation = new BABYLON.Vector3(
orientationValue * context._direction.z, 0,
-orientationValue * context._direction.x);
context._controlMesh.computeWorldMatrix(true);
context._mesh.computeWorldMatrix(true);
return;
};
/*
** Function called on every drone update, right after onUpdate AI script
*/
EnemyDroneAPI.prototype.internal_post_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
if (drone_position) {
drone_info = {
'altitudeRel' : drone_position.z,
'altitudeAbs' : _this._mapManager.getMapInfo().start_AMSL +
drone_position.z,
'latitude' : drone_position.x,
'longitude' : drone_position.y
};
_this._drone_dict_list[_this._id] = drone_info;
//broadcast drone info using internal msg
_this._gameManager._droneList.forEach(function (drone) {
if (drone.id !== _this._id) {
drone.internal_getMsg(drone_info, _this._id);
}
});
}
};
EnemyDroneAPI.prototype.setAltitude = function (drone, altitude) {
drone._targetCoordinates.z = altitude;
};
EnemyDroneAPI.prototype.setStartingPosition = function (drone, x, y, z) {
if (!drone._canPlay) {
if (z <= 0.05) {
z = 0.05;
}
drone._controlMesh.position = new BABYLON.Vector3(x, z, y);
}
drone._controlMesh.computeWorldMatrix(true);
drone._mesh.computeWorldMatrix(true);
};
EnemyDroneAPI.prototype.internal_getMsg = function (msg, id) {
this._drone_dict_list[id] = msg;
};
EnemyDroneAPI.prototype.internal_setTargetCoordinates =
function (drone, coordinates) {
if (!drone._canPlay) return;
var x = coordinates.x, y = coordinates.y, z = coordinates.z;
if (isNaN(x) || isNaN(y) || isNaN(z)) {
throw new Error('Target coordinates must be numbers');
}
x -= drone._controlMesh.position.x;
y -= drone._controlMesh.position.z;
z -= drone._controlMesh.position.y;
drone.setDirection(x, y, z);
};
EnemyDroneAPI.prototype.sendMsg = function (msg, to) {
var _this = this,
droneList = _this._gameManager._droneList;
_this._gameManager.delay(function () {
if (to < 0) {
// Send to all drones
droneList.forEach(function (drone) {
if (drone.infosMesh) {
try {
drone.onGetMsg(msg);
} catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
drone._internal_crash();
}
}
});
} else {
// Send to specific drone
if (droneList[to].infosMesh) {
try {
droneList[to].onGetMsg(msg);
} catch (error) {
console.warn('Drone crashed on sendMsg due to error:', error);
droneList[to]._internal_crash();
}
}
}
}, _this._flight_parameters.latency.communication);
};
EnemyDroneAPI.prototype.log = function (msg) {
console.log("API say : " + msg);
};
EnemyDroneAPI.prototype.getGameParameter = function (name) {
if (["gameTime", "map"].includes(name)) {
return this._gameManager.gameParameter[name];
}
};
/*
** Enemy drone works with cartesian, no geo conversion
*/
EnemyDroneAPI.prototype.processCoordinates = function (x, y, z) {
if (isNaN(x) || isNaN(y) || isNaN(z)) {
throw new Error('Target coordinates must be numbers');
}
if (z > this._map_dict.start_AMSL) {
z -= this._map_dict.start_AMSL;
}
return {
'x': x,
'y': y,
'z': z
};
};
EnemyDroneAPI.prototype.getCurrentPosition = function (x, y, z) {
return this._mapManager.convertToGeoCoordinates(x, y, z);
};
EnemyDroneAPI.prototype.getDroneViewInfo = function (drone) {
var context = this, result = [], distance,
drone_position = drone.position, other_position;
function calculateDistance(a, b) {
return Math.sqrt(Math.pow((a.x - b.x), 2) + Math.pow((a.y - b.y), 2) +
Math.pow((a.z - b.z), 2));
}
context._gameManager._droneList_user.forEach(function (other) {
if (other.can_play) {
other_position = other.position;
distance = calculateDistance(drone_position, other_position);
if (distance <= VIEW_SCOPE) {
result.push({
position: other_position,
direction: other.direction,
rotation: other.rotation,
speed: other.speed,
target: other._targetCoordinates, //check
team: other.team
});
}
}
});
return result;
};
EnemyDroneAPI.prototype.getDroneAI = function () {
//interception math based on https://www.codeproject.com/Articles/990452/Interception-of-Two-Moving-Objects-in-D-Space
return 'var BASE_DISTANCE = 300;\n' +
'function calculateInterception(hunter_position, prey_position, hunter_speed, prey_speed, prey_velocity_vector) {\n' +
' var vector_from_drone, distance_to_prey, distance_to_prey_vector, a, b, c, t1, t2, interception_time, interception_point;\n' +
' function dot(a, b) {\n' +
' return a.map((x, i) => a[i] * b[i]).reduce((m, n) => m + n);\n' +
' }\n' +
' distance_to_prey_vector = [hunter_position.x - prey_position.x, hunter_position.y - prey_position.y, hunter_position.z - prey_position.z];\n' +
' distance_to_prey = distance(hunter_position, prey_position);\n' +
' a = hunter_speed * hunter_speed - prey_speed * prey_speed;\n' +
' b = 2 * dot(distance_to_prey_vector, prey_velocity_vector);\n' +
' c = - distance_to_prey * distance_to_prey;\n' +
' t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a);\n' +
' t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / (2 * a);\n' +
' if (t1 > 0 && t2 > 0) {\n' +
' interception_time = Math.min( t1, t2 );\n' +
' } else {\n' +
' interception_time = Math.max( t1, t2 );\n' +
' }\n' +
' interception_point = [prey_position.x + prey_velocity_vector[0] * interception_time, prey_position.y + prey_velocity_vector[1] * interception_time, prey_position.z + prey_velocity_vector[2] * interception_time];\n' +
' if (isNaN(interception_point[0]) || isNaN(interception_point[1]) || isNaN(interception_point[2])) {\n' +
' return;\n' +
' }\n' +
' return interception_point;\n' +
'}\n' +
'function distance(a, b) {\n' +
' return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2 + (a.z - b.z) ** 2);\n' +
'}\n' +
'\n' +
'me.onStart = function () {\n' +
' me.base = me.position;\n' +
' me.setDirection(0,0,0);\n' +
' return;\n' +
'\n' +
'};\n' +
'\n' +
'me.onUpdate = function (timestamp) {\n' +
' me.current_position = me.position;\n' +
' var drone_position, drone_velocity_vector, interception_point, drone_view,\n' +
' dist = distance(\n' +
' me.current_position,\n' +
' me.base\n' +
' );\n' +
// return to base point if drone is too far
' if (dist >= BASE_DISTANCE) {\n' +
' me.chasing = false;\n' +
' me.setTargetCoordinates(\n' +
' me.base.x,\n' +
' me.base.y,\n' +
' me.base.z\n' +
' );\n' +
' return;\n' +
' }\n' +
' drone_view = me.getDroneViewInfo();\n' +
' if (drone_view.length) {\n' +
' drone_position = drone_view[0].position;\n' +
' drone_velocity_vector = [drone_view[0].target.x - drone_position.x, drone_view[0].target.y - drone_position.y, drone_view[0].target.z - drone_position.z];\n' +
' interception_point = calculateInterception(me.current_position, drone_position, me.speed, drone_view[0].speed, drone_velocity_vector);\n' +
' if (!interception_point) {\n' +
' return;\n' +
' }\n' +
' me.chasing = true;\n' +
' me.setTargetCoordinates(interception_point[0], interception_point[1], interception_point[2]);\n' +
' }\n' +
// return to base point if drone is too far
' if (!me.chasing && dist <= 10) {\n' +
' me.setDirection(0,0,0);\n' +
' }\n' +
'};';
};
EnemyDroneAPI.prototype.getMinSpeed = function () {
return MIN_SPEED;
//return this._flight_parameters.drone.minSpeed;
};
EnemyDroneAPI.prototype.getMaxSpeed = function () {
return MAX_SPEED;
//return this._flight_parameters.drone.maxSpeed;
};
EnemyDroneAPI.prototype.getInitialSpeed = function () {
return DEFAULT_SPEED;
//return this._flight_parameters.drone.speed;
};
EnemyDroneAPI.prototype.getMaxDeceleration = function () {
return this._flight_parameters.drone.maxDeceleration;
};
EnemyDroneAPI.prototype.getMaxAcceleration = function () {
return this._flight_parameters.drone.maxAcceleration;
};
EnemyDroneAPI.prototype.getMaxOrientation = function () {
//TODO should be a game parameter (but how to force value to PI quarters?)
return Math.PI / 4;
};
EnemyDroneAPI.prototype.triggerParachute = function (drone) {
var drone_pos = drone.getCurrentPosition();
drone.setTargetCoordinates(drone_pos.x, drone_pos.y, 5);
};
EnemyDroneAPI.prototype.landed = function (drone) {
var drone_pos = drone.getCurrentPosition();
return Math.floor(drone_pos.z) < 10;
};
EnemyDroneAPI.prototype.exit = function () {
return;
};
EnemyDroneAPI.prototype.getInitialAltitude = function () {
return 0;
};
EnemyDroneAPI.prototype.getAltitudeAbs = function (altitude) {
return altitude;
};
EnemyDroneAPI.prototype.getMinHeight = function () {
return 0;
};
EnemyDroneAPI.prototype.getMaxHeight = function () {
return 800;
};
EnemyDroneAPI.prototype.getFlightParameters = function () {
return this._flight_parameters;
};
return EnemyDroneAPI;
}());
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_erp5_page_drone_capture_flag_enemydrone.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>drone_capture_flag_enemydrone_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Web Script</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Enemy Drone (API)</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1683915732.9</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1009.37360.49772.3874</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1688571911.82</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>detect_converted_file</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>converted</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1683913977.2</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -5,12 +5,9 @@
var FixedWingDroneAPI = /** @class */ (function () {
"use strict";
// var TAKEOFF_RADIUS = 60,
var DEFAULT_SPEED = 16,
EARTH_GRAVITY = 9.81,
LOITER_LIMIT = 30,
LOITER_RADIUS_FACTOR = 0.60,
LOITER_SPEED_FACTOR = 1.5,
MAX_ACCELERATION = 6,
MAX_DECELERATION = 1,
MIN_SPEED = 12,
......@@ -19,7 +16,8 @@ var FixedWingDroneAPI = /** @class */ (function () {
MIN_PITCH = -20,
MAX_PITCH = 25,
MAX_CLIMB_RATE = 8,
MAX_SINK_RATE = 3;
MAX_SINK_RATE = 3,
VIEW_SCOPE = 100;
//** CONSTRUCTOR
function FixedWingDroneAPI(gameManager, drone_info, flight_parameters, id) {
......@@ -29,10 +27,8 @@ var FixedWingDroneAPI = /** @class */ (function () {
this._flight_parameters = flight_parameters;
this._id = id;
this._drone_info = drone_info;
this._loiter_radius = 0;
this._last_loiter_point_reached = -1;
this._loiter_radius = 100;
//this._start_altitude = 0;
//this._last_altitude_point_reached = -1;
this._loiter_mode = false;
this._drone_dict_list = [];
}
......@@ -40,6 +36,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
** Function called on start phase of the drone, just before onStart AI script
*/
FixedWingDroneAPI.prototype.internal_start = function (drone) {
drone._targetCoordinates = drone.getCurrentPosition();
drone._maxDeceleration = this.getMaxDeceleration();
if (drone._maxDeceleration <= 0) {
throw new Error('max deceleration must be superior to 0');
......@@ -146,9 +143,6 @@ var FixedWingDroneAPI = /** @class */ (function () {
*/
FixedWingDroneAPI.prototype.internal_post_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
if (_this._loiter_mode) {
_this.loiter(drone);
}
/*if (_this._start_altitude > 0) { //TODO move start_altitude here
_this.reachAltitude(drone);
}*/
......@@ -158,7 +152,10 @@ var FixedWingDroneAPI = /** @class */ (function () {
'altitudeAbs' : _this._mapManager.getMapInfo().start_AMSL +
drone_position.z,
'latitude' : drone_position.x,
'longitude' : drone_position.y
'longitude' : drone_position.y,
'yaw': drone.getYaw(),
'speed': drone.getAirSpeed(),
'climbRate': drone.getClimbRate()
};
_this._drone_dict_list[_this._id] = drone_info;
//broadcast drone info using internal msg
......@@ -171,7 +168,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
};
FixedWingDroneAPI.prototype._updateSpeed = function (drone, delta_time) {
var speed = drone.getSpeed(), speedDiff, speedUpdate;
var speed = drone.getAirSpeed(), speedDiff, speedUpdate;
if (speed !== this._targetSpeed) {
speedDiff = this._targetSpeed - speed;
speedUpdate = drone._acceleration * delta_time / 1000;
......@@ -185,13 +182,30 @@ var FixedWingDroneAPI = /** @class */ (function () {
};
FixedWingDroneAPI.prototype._updateDirection = function (drone, delta_time) {
var horizontalCoeff, newX, newY, newZ;
var horizontalCoeff, newX, newY, newZ, tangentYaw;
if (this._loiter_mode && Math.sqrt(
Math.pow(drone._targetCoordinates.x - drone.position.x, 2) +
Math.pow(drone._targetCoordinates.y - drone.position.y, 2)
) <= this._loiter_radius) {
tangentYaw = this._computeBearing(
drone.position.x,
drone.position.y,
drone._targetCoordinates.x,
drone._targetCoordinates.y
) - 90;
// trigonometric circle is east oriented, yaw angle is clockwise
tangentYaw = this._toRad(-tangentYaw + 90);
newX = Math.cos(tangentYaw);
newZ = Math.sin(tangentYaw);
} else {
[newX, newZ] = this._getNewYaw(drone, delta_time);
}
newY = this._getNewAltitude(drone);
horizontalCoeff = Math.sqrt(
(
Math.pow(drone.getSpeed(), 2) - Math.pow(newY, 2)
Math.pow(drone.getAirSpeed(), 2) - Math.pow(newY, 2)
) / (
Math.pow(newX, 2) + Math.pow(newZ, 2)
)
......@@ -235,14 +249,14 @@ var FixedWingDroneAPI = /** @class */ (function () {
verticalSpeed = this._computeVerticalSpeed(
altitudeDiff,
this.getMaxClimbRate(),
drone.getSpeed(),
drone.getAirSpeed(),
this.getMaxPitchAngle()
);
} else {
verticalSpeed = -this._computeVerticalSpeed(
Math.abs(altitudeDiff),
this.getMaxSinkRate(),
drone.getSpeed(),
drone.getAirSpeed(),
-this.getMinPitchAngle()
);
}
......@@ -261,18 +275,14 @@ var FixedWingDroneAPI = /** @class */ (function () {
drone.rotation.z + y);
};
FixedWingDroneAPI.prototype.setAltitude = function (drone, altitude) {
drone._targetCoordinates.z = altitude;
};
FixedWingDroneAPI.prototype.setSpeed = function (drone, speed) {
this._targetSpeed = Math.max(
Math.min(speed, this.getMaxSpeed()),
this.getMinSpeed()
);
drone._acceleration = (this._targetSpeed > drone.getSpeed())
? this.getMaxAcceleration() : -this.getMaxDeceleration();
drone._acceleration = (this._targetSpeed > drone.getAirSpeed()) ?
this.getMaxAcceleration() : -this.getMaxDeceleration();
};
FixedWingDroneAPI.prototype.setStartingPosition = function (drone, x, y, z) {
......@@ -290,34 +300,18 @@ var FixedWingDroneAPI = /** @class */ (function () {
this._drone_dict_list[id] = msg;
};
FixedWingDroneAPI.prototype.set_loiter_mode = function (radius) {
FixedWingDroneAPI.prototype.internal_setTargetCoordinates =
function (drone, coordinates, radius) {
if (radius) {
this._loiter_mode = true;
if (radius && radius > LOITER_LIMIT) {
this._loiter_radius = radius * LOITER_RADIUS_FACTOR;
this._loiter_center = this._last_target;
this._loiter_coordinates = [];
this._last_loiter_point_reached = -1;
var x1, y1, angle;
//for (var angle = 0; angle <360; angle+=8){ //counter-clockwise
for (angle = 360; angle > 0; angle -= 8) { //clockwise
x1 = this._loiter_radius *
Math.cos(this._toRad(angle)) + this._loiter_center.x;
y1 = this._loiter_radius *
Math.sin(this._toRad(angle)) + this._loiter_center.y;
this._loiter_coordinates.push(
this.getCurrentPosition(x1, y1, this._loiter_center.z)
);
}
if (radius >= LOITER_LIMIT) {
this._loiter_radius = radius;
}
};
FixedWingDroneAPI.prototype.internal_setTargetCoordinates =
function (drone, coordinates, loiter) {
if (!loiter) {
} else {
this._loiter_mode = false;
//save last target point to use as next loiter center
this._last_target = coordinates;
}
};
FixedWingDroneAPI.prototype.sendMsg = function (msg, to) {
var _this = this,
droneList = _this._gameManager._droneList;
......@@ -362,79 +356,59 @@ var FixedWingDroneAPI = /** @class */ (function () {
if (isNaN(lat) || isNaN(lon) || isNaN(z)) {
throw new Error('Target coordinates must be numbers');
}
var x = this._mapManager.longitudToX(lon, this._map_dict.width),
y = this._mapManager.latitudeToY(lat, this._map_dict.depth),
position = this._mapManager.normalize(x, y, this._map_dict),
processed_coordinates;
var point = this._mapManager.toLocalCoordinates(lat, lon,
this._map_dict.map_size),
position = this._mapManager.normalize(point.x, point.y, this._map_dict);
if (z > this._map_dict.start_AMSL) {
z -= this._map_dict.start_AMSL;
}
processed_coordinates = {
return {
x: position[0],
y: position[1],
z: z
};
//this._last_altitude_point_reached = -1;
//this.takeoff_path = [];
return processed_coordinates;
};
FixedWingDroneAPI.prototype.getCurrentPosition = function (x, y, z) {
return this._mapManager.convertToGeoCoordinates(x, y, z, this._map_dict);
};
FixedWingDroneAPI.prototype.loiter = function (drone) {
if (this._loiter_radius > LOITER_LIMIT) {
var drone_pos = drone.getCurrentPosition(),
min = 9999,
min_i,
i,
d,
next_point;
//shift loiter circle to nearest point
if (this._last_loiter_point_reached === -1) {
if (!this.shifted) {
drone._maxSpeed = drone._maxSpeed * LOITER_SPEED_FACTOR;
for (i = 0; i < this._loiter_coordinates.length; i += 1) {
d = this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[this._loiter_coordinates[i].x,
this._loiter_coordinates[i].y]);
if (d < min) {
min = d;
min_i = i;
}
}
this._loiter_coordinates = this._loiter_coordinates.concat(
this._loiter_coordinates.splice(0, min_i)
);
this.shifted = true;
}
} else {
this.shifted = false;
}
//stop
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
if (drone._maxSpeed !== this.getMaxSpeed()) {
drone._maxSpeed = this.getMaxSpeed();
FixedWingDroneAPI.prototype.getDroneViewInfo = function (drone) {
var context = this, result = { "obstacles": [], "drones": [] }, distance,
other_position, drone_position = drone.getCurrentPosition();
function calculateDistance(a, b, _this) {
return _this._mapManager.latLonDistance([a.x, a.y], [b.x, b.y]);
}
context._gameManager._droneList.forEach(function (other) {
if (other.can_play && drone.id != other.id) {
other_position = other.getCurrentPosition();
distance = calculateDistance(drone_position, other_position, context);
if (distance <= VIEW_SCOPE) {
result.drones.push({
position: drone.getCurrentPosition(),
direction: drone.direction,
rotation: drone.rotation,
speed: drone.speed,
team: drone.team
});
}
drone.setDirection(0, 0, 0);
return;
}
//loiter
next_point =
this._loiter_coordinates[this._last_loiter_point_reached + 1];
this.internal_setTargetCoordinates(drone, next_point, true);
if (this._mapManager.latLonDistance([drone_pos.x, drone_pos.y],
[next_point.x, next_point.y]) < 1) {
this._last_loiter_point_reached += 1;
if (this._last_loiter_point_reached ===
this._loiter_coordinates.length - 1) {
return;
});
context._map_dict.geo_obstacle_list.forEach(function (obstacle) {
distance = calculateDistance(drone_position, obstacle.position, context);
if (distance <= VIEW_SCOPE) {
result.obstacles.push(obstacle);
}
next_point = this._loiter_coordinates[
this._last_loiter_point_reached + 1
];
this.internal_setTargetCoordinates(drone, next_point, true);
});
if (drone.__is_getting_drone_view !== true) {
drone.__is_getting_drone_view = true;
context._gameManager.delay(function () {
drone.__is_getting_drone_view = false;
try {
drone.onDroneViewInfo(result);
} catch (error) {
console.warn('Drone crashed on drone view due to error:', error);
drone._internal_crash();
}
}, 1000);
}
};
FixedWingDroneAPI.prototype.getDroneAI = function () {
......@@ -475,9 +449,9 @@ var FixedWingDroneAPI = /** @class */ (function () {
return Math.PI / 4;
};
FixedWingDroneAPI.prototype.getYawVelocity = function (drone) {
return 360 * EARTH_GRAVITY
* Math.tan(this._toRad(this.getMaxRollAngle()))
/ (2 * Math.PI * drone.getSpeed());
return 360 * EARTH_GRAVITY *
Math.tan(this._toRad(this.getMaxRollAngle())) /
(2 * Math.PI * drone.getAirSpeed());
};
FixedWingDroneAPI.prototype.getYaw = function (drone) {
var direction = drone.worldDirection;
......@@ -493,9 +467,10 @@ var FixedWingDroneAPI = /** @class */ (function () {
};
FixedWingDroneAPI.prototype._computeVerticalSpeed =
function (altitude_diff, max_climb_rate, speed, max_pitch) {
var maxVerticalSpeed = Math.min(altitude_diff, Math.min(max_climb_rate, speed));
return (this._toDeg(Math.asin(maxVerticalSpeed / speed)) > max_pitch)
? speed * Math.sin(this._toRad(max_pitch))
var maxVerticalSpeed =
Math.min(altitude_diff, Math.min(max_climb_rate, speed));
return (this._toDeg(Math.asin(maxVerticalSpeed / speed)) > max_pitch) ?
speed * Math.sin(this._toRad(max_pitch))
: maxVerticalSpeed;
};
FixedWingDroneAPI.prototype._toRad = function (angle) {
......@@ -505,13 +480,13 @@ var FixedWingDroneAPI = /** @class */ (function () {
return angle * 180 / Math.PI;
};
FixedWingDroneAPI.prototype.getClimbRate = function (drone) {
return drone.worldDirection.y * drone.getSpeed();
return drone.worldDirection.y * drone.getAirSpeed();
};
FixedWingDroneAPI.prototype.getGroundSpeed = function (drone) {
var direction = drone.worldDirection;
return Math.sqrt(
Math.pow(direction.x * drone.getSpeed(), 2)
+ Math.pow(direction.z * drone.getSpeed(), 2)
Math.pow(direction.x * drone.getAirSpeed(), 2) +
Math.pow(direction.z * drone.getAirSpeed(), 2)
);
};
FixedWingDroneAPI.prototype.triggerParachute = function (drone) {
......@@ -526,10 +501,10 @@ var FixedWingDroneAPI = /** @class */ (function () {
return;
};
FixedWingDroneAPI.prototype.getInitialAltitude = function () {
return 0;
return this._map_dict.start_AMSL;
};
FixedWingDroneAPI.prototype.getAltitudeAbs = function (altitude) {
return altitude;
return altitude + this._map_dict.start_AMSL;
};
FixedWingDroneAPI.prototype.getMinHeight = function () {
return 0;
......
......@@ -246,7 +246,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1007.63305.59225.51968</string> </value>
<value> <string>1010.5034.64121.33006</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -266,7 +266,7 @@
</tuple>
<state>
<tuple>
<float>1682437182.61</float>
<float>1690565260.64</float>
<string>UTC</string>
</tuple>
</state>
......
/*global BABYLON, RSVP, console, FixedWingDroneAPI, document*/
/*global BABYLON, RSVP, console, FixedWingDroneAPI, EnemyDroneAPI, document*/
/*jslint nomen: true, indent: 2, maxlen: 80, todo: true,
unparam: true */
var GAMEPARAMETERS = {};
var GAMEPARAMETERS = {}, TEAM_USER = "user", TEAM_ENEMY = "enemy";
//for DEBUG/TEST mode
var baseLogFunction = console.log, console_log = "";
/******************************* DRONE MANAGER ********************************/
var DroneManager = /** @class */ (function () {
"use strict";
//** CONSTRUCTOR
function DroneManager(scene, id, API) {
function DroneManager(scene, id, API, team) {
this._mesh = null;
this._controlMesh = null;
this._canPlay = false;
......@@ -31,8 +33,9 @@ var DroneManager = /** @class */ (function () {
this._scene = scene;
this._canUpdate = true;
this._id = id;
this._leader_id = 0;
this._team = team;
this._API = API; // var API created on AI evel
this._score = 0;
// Create the control mesh
this._controlMesh = BABYLON.Mesh.CreateBox(
"droneControl_" + id,
......@@ -61,11 +64,6 @@ var DroneManager = /** @class */ (function () {
// swap y and z axis so z axis represents altitude
return new BABYLON.Vector3(vector.x, vector.z, vector.y);
};
Object.defineProperty(DroneManager.prototype, "leader_id", {
get: function () { return this._leader_id; },
enumerable: true,
configurable: true
});
Object.defineProperty(DroneManager.prototype, "drone_dict", {
get: function () { return this._API._drone_dict_list; },
enumerable: true,
......@@ -73,6 +71,7 @@ var DroneManager = /** @class */ (function () {
});
Object.defineProperty(DroneManager.prototype, "can_play", {
get: function () { return this._canPlay; },
set: function (value) { this._canPlay = value; },
enumerable: true,
configurable: true
});
......@@ -81,6 +80,17 @@ var DroneManager = /** @class */ (function () {
enumerable: true,
configurable: true
});
Object.defineProperty(DroneManager.prototype, "score", {
get: function () { return this._score; },
set: function (value) { this._score = value; },
enumerable: true,
configurable: true
});
Object.defineProperty(DroneManager.prototype, "team", {
get: function () { return this._team; },
enumerable: true,
configurable: true
});
Object.defineProperty(DroneManager.prototype, "colliderMesh", {
get: function () { return this._mesh; },
enumerable: true,
......@@ -124,11 +134,10 @@ var DroneManager = /** @class */ (function () {
enumerable: true,
configurable: true
});
DroneManager.prototype.internal_start = function (initial_position) {
DroneManager.prototype.internal_start = function () {
this._API.internal_start(this);
this._canPlay = true;
this._canCommunicate = true;
this._targetCoordinates = initial_position;
try {
return this.onStart();
} catch (error) {
......@@ -140,6 +149,10 @@ var DroneManager = /** @class */ (function () {
* Set a target point to move
*/
DroneManager.prototype.setTargetCoordinates = function (x, y, z) {
this._internal_setTargetCoordinates(x, y, z);
};
DroneManager.prototype._internal_setTargetCoordinates =
function (x, y, z, radius) {
if (!this._canPlay) {
return;
}
......@@ -147,9 +160,20 @@ var DroneManager = /** @class */ (function () {
this._targetCoordinates = this._API.processCoordinates(x, y, z);
return this._API.internal_setTargetCoordinates(
this,
this._targetCoordinates
this._targetCoordinates,
radius
);
};
/**
* Returns the list of things a drone "sees"
*/
DroneManager.prototype.getDroneViewInfo = function () {
var context = this;
if (this._controlMesh) {
return context._API.getDroneViewInfo(context);
}
return;
};
DroneManager.prototype.internal_update = function (delta_time) {
var context = this;
if (this._controlMesh) {
......@@ -194,7 +218,7 @@ var DroneManager = /** @class */ (function () {
}
return this._API.setStartingPosition(this, x, y, z);
};
DroneManager.prototype.setSpeed = function (speed) {
DroneManager.prototype.setAirSpeed = function (speed) {
if (!this._canPlay) {
return;
}
......@@ -292,20 +316,11 @@ var DroneManager = /** @class */ (function () {
}
return null;
};
DroneManager.prototype.setAltitude = function (altitude) {
if (!this._canPlay) {
return;
}
return this._API.setAltitude(this, altitude);
};
/**
* Make the drone loiter (circle with a set radius)
*/
DroneManager.prototype.loiter = function (radius) {
if (!this._canPlay) {
return;
}
this._API.set_loiter_mode(radius);
DroneManager.prototype.loiter = function (x, y, z, radius) {
this._internal_setTargetCoordinates(x, y, z, radius);
};
DroneManager.prototype.getFlightParameters = function () {
if (this._API.getFlightParameters) {
......@@ -314,19 +329,31 @@ var DroneManager = /** @class */ (function () {
return null;
};
DroneManager.prototype.getYaw = function () {
if (typeof this._API.getYaw !== "undefined") {
return this._API.getYaw(this);
}
return;
};
DroneManager.prototype.getSpeed = function () {
DroneManager.prototype.getAirSpeed = function () {
return this._speed;
};
DroneManager.prototype.getGroundSpeed = function () {
if (typeof this._API.getGroundSpeed !== "undefined") {
return this._API.getGroundSpeed(this);
}
return;
};
DroneManager.prototype.getClimbRate = function () {
if (typeof this._API.getClimbRate !== "undefined") {
return this._API.getClimbRate(this);
}
return;
};
DroneManager.prototype.getSinkRate = function () {
return this._API.getSinkRate();
if (typeof this._API.getSinkRate !== "undefined") {
return this._API.getSinkRate(this);
}
return;
};
DroneManager.prototype.triggerParachute = function () {
return this._API.triggerParachute(this);
......@@ -364,6 +391,12 @@ var DroneManager = /** @class */ (function () {
* @param msg The message
*/
DroneManager.prototype.onGetMsg = function () { return; };
/**
* Function called when drone finished processing drone view
* (as result of getDroneViewInfo call)
*/
DroneManager.prototype.onDroneViewInfo = function (drone_view) { return; };
return DroneManager;
}());
......@@ -375,41 +408,70 @@ var DroneManager = /** @class */ (function () {
var MapManager = /** @class */ (function () {
"use strict";
function calculateMapInfo(map, map_dict, initial_position) {
var max_width = map.latLonDistance([map_dict.min_lat, map_dict.min_lon],
[map_dict.min_lat, map_dict.max_lon]),
max_height = map.latLonDistance([map_dict.min_lat, map_dict.min_lon],
[map_dict.max_lat, map_dict.min_lon]),
map_size = Math.ceil(Math.max(max_width, max_height)) * 0.6,
map_info = {
"depth": map_size,
//random geo-point:
var MIN_LAT = 45.64,
MIN_LON = 14.253,
EPSILON = 9.9,
START_Z = 15,
R = 6371e3;
function calculateMapInfo(map, map_dict) {
var min_lat = map_dict.min_lat || MIN_LAT,
min_lon = map_dict.min_lon || MIN_LON,
offset = map.latLonOffset(min_lat, min_lon, map_dict.map_size),
max_lat = offset[0],
max_lon = offset[1],
starting_point = map_dict.map_size / 2 * -0.75,
local_min = map.toLocalCoordinates(min_lat, min_lon, map_dict.map_size),
local_max = map.toLocalCoordinates(max_lat, max_lon, map_dict.map_size);
map.map_info = {
"depth": map_dict.map_size,
"width": map_dict.map_size,
"map_size": map_dict.map_size,
"min_lat": min_lat,
"min_lon": min_lon,
"max_lat": max_lat,
"max_lon": max_lon,
"min_x": local_min.x,
"min_y": local_min.y,
"max_x": local_max.x,
"max_y": local_max.y,
"height": map_dict.height,
"width": map_size,
"map_size": map_size,
"min_x": map.longitudToX(map_dict.min_lon, map_size),
"min_y": map.latitudeToY(map_dict.min_lat, map_size),
"max_x": map.longitudToX(map_dict.max_lon, map_size),
"max_y": map.latitudeToY(map_dict.max_lat, map_size),
"start_AMSL": map_dict.start_AMSL
},
position = map.normalize(
map.longitudToX(initial_position.longitude, map_size),
map.latitudeToY(initial_position.latitude, map_size),
map_info
);
map_info.initial_position = {
"x": position[0],
"y": position[1],
"z": initial_position.z
"start_AMSL": map_dict.start_AMSL,
"flag_list": map_dict.flag_list,
"geo_flag_list": [],
"flag_distance_epsilon": map_dict.flag_distance_epsilon || EPSILON,
"obstacle_list": map_dict.obstacle_list,
"geo_obstacle_list": [],
"initial_position": {
"x": 0,
"y": starting_point,
"z": START_Z
}
};
return map_info;
map_dict.flag_list.forEach(function (flag_info, index) {
map.map_info.geo_flag_list.push(map.convertToGeoCoordinates(
flag_info.position.x,
flag_info.position.y,
flag_info.position.z
));
});
map_dict.obstacle_list.forEach(function (obstacle_info, index) {
var geo_obstacle = {};
Object.assign(geo_obstacle, obstacle_info);
geo_obstacle.position = map.convertToGeoCoordinates(
obstacle_info.position.x,
obstacle_info.position.y,
obstacle_info.position.z
);
map.map_info.geo_obstacle_list.push(geo_obstacle);
});
}
//** CONSTRUCTOR
function MapManager(scene) {
var _this = this, max_sky, skybox, skyboxMat, largeGroundMat,
largeGroundBottom, width, depth, terrain, max;
_this.map_info = calculateMapInfo(_this, GAMEPARAMETERS.map,
GAMEPARAMETERS.initialPosition);
var _this = this, max_sky, skybox, skyboxMat, largeGroundMat, flag_material,
largeGroundBottom, width, depth, terrain, max, flag_a, flag_b, mast, flag,
count = 0, new_obstacle;
calculateMapInfo(_this, GAMEPARAMETERS.map);
max = _this.map_info.width;
if (_this.map_info.depth > max) {
max = _this.map_info.depth;
......@@ -419,18 +481,18 @@ var MapManager = /** @class */ (function () {
}
max = max < _this.map_info.depth ? _this.map_info.depth : max;
// Skybox
max_sky = (max * 10 < 20000) ? max * 10 : 20000;
skybox = BABYLON.Mesh.CreateBox("skyBox", max_sky, scene);
skybox.infiniteDistance = true;
skybox.renderingGroupId = 0;
max_sky = (max * 15 < 20000) ? max * 15 : 20000; //skybox scene limit
skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: max_sky }, scene);
skyboxMat = new BABYLON.StandardMaterial("skybox", scene);
skyboxMat.backFaceCulling = false;
skyboxMat.disableLighting = true;
skybox.material = skyboxMat;
skybox.infiniteDistance = true;
skyboxMat.disableLighting = true;
skyboxMat.reflectionTexture = new BABYLON.CubeTexture("./assets/skybox/sky",
scene);
skyboxMat.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skyboxMat.infiniteDistance = true;
skybox.material = skyboxMat;
skybox.renderingGroupId = 0;
// Plane from bottom
largeGroundMat = new BABYLON.StandardMaterial("largeGroundMat", scene);
largeGroundMat.specularColor = BABYLON.Color3.Black();
......@@ -441,8 +503,7 @@ var MapManager = /** @class */ (function () {
largeGroundBottom.rotation.x = -Math.PI / 2;
largeGroundBottom.rotation.y = Math.PI;
largeGroundBottom.material = largeGroundMat;
// Camera
scene.activeCamera.upperRadiusLimit = max * 4;
largeGroundBottom.renderingGroupId = 1;
// Terrain
// Give map some margin from the flight limits
width = _this.map_info.width * 1.10;
......@@ -453,19 +514,122 @@ var MapManager = /** @class */ (function () {
terrain.position = BABYLON.Vector3.Zero();
terrain.scaling = new BABYLON.Vector3(depth / 50000, depth / 50000,
width / 50000);
// Obstacles
_this._obstacle_list = [];
_this.map_info.obstacle_list.forEach(function (obstacle) {
switch (obstacle.type) {
case "box":
new_obstacle = BABYLON.MeshBuilder.CreateBox("obs_" + count,
{ 'size': 1 }, scene);
break;
case "cylinder":
new_obstacle = BABYLON.MeshBuilder.CreateCylinder("obs_" + count, {
'diameterBottom': obstacle.diameterBottom,
'diameterTop': obstacle.diameterTop,
'height': 1
}, scene);
break;
case "sphere":
new_obstacle = BABYLON.MeshBuilder.CreateSphere("obs_" + count, {
'diameterX': obstacle.scale.x,
'diameterY': obstacle.scale.z,
'diameterZ': obstacle.scale.y
}, scene);
break;
default:
return;
}
new_obstacle.type = obstacle.type;
var convertion = Math.PI / 180;
if ("position" in obstacle)
new_obstacle.position = new BABYLON.Vector3(obstacle.position.x,
obstacle.position.z,
obstacle.position.y);
if ("rotation" in obstacle)
new_obstacle.rotation =
new BABYLON.Vector3(obstacle.rotation.x * convertion,
obstacle.rotation.z * convertion,
obstacle.rotation.y * convertion);
if ("scale" in obstacle)
new_obstacle.scaling = new BABYLON.Vector3(obstacle.scale.x,
obstacle.scale.z,
obstacle.scale.y);
var obs_material = new BABYLON.StandardMaterial("obsmat_" + count, scene);
obs_material.alpha = 1;
obs_material.diffuseColor = new BABYLON.Color3(255, 153, 0);
new_obstacle.material = obs_material;
_this._obstacle_list.push(new_obstacle);
count++;
});
// Flags
_this._flag_list = [];
var FLAG_SIZE = {
'x': 1,
'y': 1,
'z': 6
};
_this.map_info.flag_list.forEach(function (flag_info, index) {
flag_material = new BABYLON.StandardMaterial("flag_mat_" + index, scene);
flag_material.alpha = 1;
flag_material.diffuseColor = BABYLON.Color3.Green();
flag_a = BABYLON.MeshBuilder.CreateDisc("flag_a_" + index,
{radius: 7, tessellation: 3},
scene);
flag_a.material = flag_material;
flag_a.position = new BABYLON.Vector3(
flag_info.position.x + 1,
FLAG_SIZE.z + 1, //swap
flag_info.position.y - 1
);
flag_a.rotation = new BABYLON.Vector3(0, 1, 0);
flag_b = BABYLON.MeshBuilder.CreateDisc("flag_b_" + index,
{radius: 3, tessellation: 3},
scene);
flag_b.material = flag_material;
flag_b.position = new BABYLON.Vector3(
flag_info.position.x - 1,
FLAG_SIZE.z + 1, //swap
flag_info.position.y + 1
);
flag_b.rotation = new BABYLON.Vector3(0, 4, 0);
mast = BABYLON.MeshBuilder.CreateBox("mast_" + index,
{ 'size': 1 }, scene);
mast.position = new BABYLON.Vector3(
flag_info.position.x,
FLAG_SIZE.z / 2, //swap
flag_info.position.y
);
mast.scaling = new BABYLON.Vector3(
FLAG_SIZE.x,
FLAG_SIZE.z, //swap
FLAG_SIZE.y);
mast.material = flag_material;
flag = BABYLON.Mesh.MergeMeshes([flag_a, flag_b, mast]);
flag.id = index;
//flag.weight = _this.map_info.flag_weight;
flag.location = flag_info.position;
flag.drone_collider_list = [];
_this._flag_list.push(flag);
});
}
MapManager.prototype.getMapInfo = function () {
return this.map_info;
};
MapManager.prototype.longitudToX = function (lon, map_size) {
return (map_size / 360.0) * (180 + lon);
MapManager.prototype.latLonOffset = function (lat, lon, offset_in_mt) {
var R = 6371e3, //Earth radius
lat_offset = offset_in_mt / R,
lon_offset = offset_in_mt / (R * Math.cos(Math.PI * lat / 180));
return [lat + lat_offset * 180 / Math.PI,
lon + lon_offset * 180 / Math.PI];
};
MapManager.prototype.toLocalCoordinates = function (lat, lon, map_size) {
return {
"x": (map_size / 360.0) * (180 + lon),
"y": (map_size / 180.0) * (90 - lat)
};
MapManager.prototype.latitudeToY = function (lat, map_size) {
return (map_size / 180.0) * (90 - lat);
};
MapManager.prototype.latLonDistance = function (c1, c2) {
var R = 6371e3,
q1 = c1[0] * Math.PI / 180,
var q1 = c1[0] * Math.PI / 180,
q2 = c2[0] * Math.PI / 180,
dq = (c2[0] - c1[0]) * Math.PI / 180,
dl = (c2[1] - c1[1]) * Math.PI / 180,
......@@ -481,8 +645,9 @@ var MapManager = /** @class */ (function () {
return [n_x * 1000 - map_dict.width / 2,
n_y * 1000 - map_dict.depth / 2];
};
MapManager.prototype.convertToGeoCoordinates = function (x, y, z, map_dict) {
var lon = x + map_dict.width / 2,
MapManager.prototype.convertToGeoCoordinates = function (x, y, z) {
var map_dict = this.map_info,
lon = x + map_dict.width / 2,
lat = y + map_dict.depth / 2;
lon = lon / 1000;
lon = lon * (map_dict.max_x - map_dict.min_x) +
......@@ -511,11 +676,15 @@ var GameManager = /** @class */ (function () {
"use strict";
// *** CONSTRUCTOR ***
function GameManager(canvas, game_parameters_json) {
var drone, header_list;
var drone, header_list, drone_count;
this._canvas = canvas;
this._canvas_width = canvas.width;
this._canvas_height = canvas.height;
this._scene = null;
this._engine = null;
this._droneList = [];
this._droneList_user = [];
this._droneList_enemy = [];
this._canUpdate = false;
this._max_step_animation_frame = game_parameters_json.simulation_speed;
if (!this._max_step_animation_frame) { this._max_step_animation_frame = 5; }
......@@ -524,19 +693,25 @@ var GameManager = /** @class */ (function () {
this._map_swapped = false;
this._log_count = [];
this._flight_log = [];
if (GAMEPARAMETERS.draw_flight_path) {
this._last_position_drawn = [];
this._trace_objects_per_drone = [];
this._result_message = "";
if (GAMEPARAMETERS.log_drone_flight) {
// ! Be aware that the following functions relies on this log format:
// - getLogEntries at Drone Simulator Log Page
// - getLogEntries at Dron Log Follower API
// - getLogEntries at Drone Log Follower API
header_list = ["timestamp (ms)", "latitude (°)", "longitude (°)",
"AMSL (m)", "rel altitude (m)", "yaw (°)",
"ground speed (m/s)", "climb rate (m/s)"];
for (drone = 0; drone < GAMEPARAMETERS.droneList.length; drone += 1) {
drone_count = GAMEPARAMETERS.map.drones.user.length +
GAMEPARAMETERS.map.drones.enemy.length;
for (drone = 0; drone < drone_count; drone += 1) {
this._flight_log[drone] = [];
this._flight_log[drone].push(header_list);
this._log_count[drone] = 0;
}
if (GAMEPARAMETERS.draw_flight_path) {
this._last_position_drawn = [];
this._trace_objects_per_drone = [];
for (drone = 0; drone < drone_count; drone += 1) {
this._last_position_drawn[drone] = null;
this._trace_objects_per_drone[drone] = [];
}
......@@ -553,10 +728,20 @@ var GameManager = /** @class */ (function () {
new BABYLON.Color3(0, 0, 128)
];
}
}
this.APIs_dict = {
FixedWingDroneAPI: FixedWingDroneAPI/*,
DroneLogAPI: DroneLogAPI*/
FixedWingDroneAPI: FixedWingDroneAPI,
EnemyDroneAPI: EnemyDroneAPI
};
if (this._game_parameters_json.debug_test_mode) {
console.log = function () {
baseLogFunction.apply(console, arguments);
var args = Array.prototype.slice.call(arguments);
for (var i = 0;i < args.length;i++) {
console_log += args[i] + "\n";
}
};
}
}
Object.defineProperty(GameManager.prototype, "gameParameter", {
......@@ -571,11 +756,15 @@ var GameManager = /** @class */ (function () {
var gadget = this;
return gadget._init()
.push(function () {
return gadget._flight_log;
return {
'message': gadget._result_message,
'content': gadget._flight_log,
'console_log': console_log
};
});
};
GameManager.prototype.update = function () {
GameManager.prototype.update = function (fullscreen) {
// time delta means that drone are updated every virtual second
// This is fixed and must not be modified
// otherwise, it will lead to different scenario results
......@@ -587,10 +776,8 @@ var GameManager = /** @class */ (function () {
function triggerUpdateIfPossible() {
if ((_this._canUpdate) && (_this.ongoing_update_promise === null) &&
(0 < _this.waiting_update_count)) {
_this.ongoing_update_promise = _this._update(
TIME_DELTA,
(_this.waiting_update_count === 1)
).push(function () {
_this.ongoing_update_promise = _this._update(TIME_DELTA, fullscreen)
.push(function () {
_this.waiting_update_count -= 1;
_this.ongoing_update_promise = null;
triggerUpdateIfPossible();
......@@ -617,16 +804,98 @@ var GameManager = /** @class */ (function () {
this._flight_log[drone._id].push(error.stack);
};
GameManager.prototype._checkDroneRules = function (drone) {
//TODO move this to API methods.
//each type of drone should define its rules
if (drone.getCurrentPosition()) {
return drone.getCurrentPosition().z > 1;
GameManager.prototype._checkDroneOut = function (drone) {
if (drone.position) {
var map_limit = this._mapManager.getMapInfo().map_size / 2;
return (drone.position.z > this._mapManager.getMapInfo().height) ||
(drone.position.x < -map_limit) ||
(drone.position.x > map_limit) ||
(drone.position.y < -map_limit) ||
(drone.position.y > map_limit);
}
};
GameManager.prototype._checkObstacleCollision = function (drone, obstacle) {
var closest = void 0, projected = BABYLON.Vector3.Zero();
if (drone.colliderMesh &&
drone.colliderMesh.intersectsMesh(obstacle, true)) {
drone._internal_crash(new Error('Drone ' + drone.id +
' touched an obstacle.'));
//Following workaround seems not needed with new babylonjs versions
/**
* Closest facet check is needed for sphere and cylinder,
* but just seemed bugged with the box
* So only need to check intersectMesh for the box
*/
/*if (obstacle.type == "box") {
closest = true;
} else {
obstacle.updateFacetData();
closest = obstacle.getClosestFacetAtCoordinates(
drone.infosMesh.position.x,
drone.infosMesh.position.y,
drone.infosMesh.position.z, projected);
}
if (closest !== null) {
drone._internal_crash(new Error('Drone ' + drone.id +
' touched an obstacle.'));
}*/
}
return false;
};
GameManager.prototype._update = function (delta_time) {
GameManager.prototype._checkFlagCollision = function (drone, flag) {
if (drone.team == TEAM_ENEMY) return;
function distance(a, b) {
return Math.sqrt(Math.pow((a.x - b.x), 2) + Math.pow((a.y - b.y), 2) +
Math.pow((a.z - b.z), 2));
}
if (drone.position) {
//TODO epsilon distance is 15 because of fixed wing loiter flights
//there is not a proper collision
if (distance(drone.position, flag.location) <=
this._mapManager.getMapInfo().flag_distance_epsilon) {
if (!flag.drone_collider_list.includes(drone.id)) {
//TODO notify the drone somehow? Or the AI script is in charge?
//console.log("flag " + flag.id + " hit by drone " + drone.id);
drone._internal_crash(new Error('Drone ' + drone.id +
' touched a flag.'));
if (flag.drone_collider_list.length === 0) {
drone.score++;
flag.drone_collider_list.push(drone.id);
}
}
}
}
};
GameManager.prototype._checkCollision = function (drone, other) {
if (drone.colliderMesh && other.colliderMesh &&
drone.colliderMesh.intersectsMesh(other.colliderMesh, false)) {
var angle = Math.acos(BABYLON.Vector3.Dot(drone.worldDirection,
other.worldDirection) /
(drone.worldDirection.length() *
other.worldDirection.length()));
//TODO is this parameter set? keep it or make 2 drones die when intersect?
if (angle < GAMEPARAMETERS.drone.collisionSector) {
if (drone.speed > other.speed) {
other._internal_crash(new Error('Drone ' + drone.id +
' bump drone ' + other.id + '.'));
}
else {
drone._internal_crash(new Error('Drone ' + other.id +
' bumped drone ' + drone.id + '.'));
}
}
else {
drone._internal_crash(new Error('Drone ' + drone.id +
' touched drone ' + other.id + '.'));
other._internal_crash(new Error('Drone ' + drone.id +
' touched drone ' + other.id + '.'));
}
}
};
GameManager.prototype._update = function (delta_time, fullscreen) {
var _this = this,
queue = new RSVP.Queue(),
i;
......@@ -642,14 +911,48 @@ var GameManager = /** @class */ (function () {
}
}
if (fullscreen) {
//Only resize if size changes
if (this._canvas.width !== GAMEPARAMETERS.fullscreen.width) {
this._canvas.width = GAMEPARAMETERS.fullscreen.width;
this._canvas.height = GAMEPARAMETERS.fullscreen.height;
}
} else {
if (this._canvas.width !== this._canvas_width) {
this._canvas.width = this._canvas_width;
this._canvas.height = this._canvas_height;
this._engine.resize(true);
}
}
this._droneList.forEach(function (drone) {
queue.push(function () {
var msg = '';
drone._tick += 1;
if (_this._checkDroneRules(drone)) {
return drone.internal_update(delta_time);
if (drone.can_play) {
if (drone.getCurrentPosition().z <= 0) {
drone._internal_crash(new Error('Drone ' + drone.id +
' touched the floor.'));
}
else if (_this._checkDroneOut(drone)) {
drone._internal_crash(new Error('Drone ' + drone.id +
' out of limits.'));
}
else {
_this._droneList.forEach(function (other) {
if (other.can_play && drone.id != other.id) {
_this._checkCollision(drone, other);
}
});
_this._mapManager._obstacle_list.forEach(function (obstacle) {
_this._checkObstacleCollision(drone, obstacle);
});
_this._mapManager._flag_list.forEach(function (flag) {
_this._checkFlagCollision(drone, flag);
});
}
}
//TODO error must be defined by the api?
drone._internal_crash('Drone touched the floor');
return drone.internal_update(delta_time);
});
});
......@@ -657,10 +960,17 @@ var GameManager = /** @class */ (function () {
.push(function () {
if (_this._timeOut()) {
console.log("TIMEOUT!");
_this._result_message += "TIMEOUT!";
return _this._finish();
}
if (_this._allDronesFinished()) {
console.log("ALL DRONES EXITED");
console.log("ALL DRONES DOWN");
_this._result_message += "ALL DRONES DOWN!";
return _this._finish();
}
if (_this._allFlagsCaptured()) {
console.log("ALL FLAGS CAPTURED");
_this._result_message += "ALL FLAGS CAPTURED!";
return _this._finish();
}
});
......@@ -674,7 +984,7 @@ var GameManager = /** @class */ (function () {
seconds = Math.floor(this._game_duration / 1000), trace_objects;
if (GAMEPARAMETERS.log_drone_flight || GAMEPARAMETERS.draw_flight_path) {
this._droneList.forEach(function (drone, index) {
this._droneList_user.forEach(function (drone, index) {
if (drone.can_play) {
drone_position = drone.position;
if (GAMEPARAMETERS.log_drone_flight) {
......@@ -687,8 +997,7 @@ var GameManager = /** @class */ (function () {
geo_coordinates = map_manager.convertToGeoCoordinates(
drone_position.x,
drone_position.y,
drone_position.z,
map_info
drone_position.z
);
game_manager._flight_log[index].push([
game_manager._game_duration, geo_coordinates.x,
......@@ -711,12 +1020,14 @@ var GameManager = /** @class */ (function () {
position_obj.position = new BABYLON.Vector3(drone_position.x,
drone_position.z,
drone_position.y);
//TODO base it on map_size
position_obj.scaling = new BABYLON.Vector3(4, 4, 4);
material = new BABYLON.StandardMaterial(game_manager._scene);
material.alpha = 1;
color = new BABYLON.Color3(255, 0, 0);
if (game_manager._colors[index]) {
color = game_manager._colors[index];
var color_index = index % 10;
if (game_manager._colors[color_index]) {
color = game_manager._colors[color_index];
}
material.diffuseColor = color;
position_obj.material = material;
......@@ -742,7 +1053,7 @@ var GameManager = /** @class */ (function () {
GameManager.prototype._allDronesFinished = function () {
var finish = true;
this._droneList.forEach(function (drone) {
this._droneList_user.forEach(function (drone) {
if (drone.can_play) {
finish = false;
}
......@@ -750,8 +1061,31 @@ var GameManager = /** @class */ (function () {
return finish;
};
GameManager.prototype._allFlagsCaptured = function () {
var finish = true;
this._mapManager._flag_list.forEach(function (flag) {
//do not use flag weight for now, just 1 hit is enough
if (flag.drone_collider_list.length === 0) {
//if (flag.drone_collider_list.length < flag.weight) {
finish = false;
}
});
return finish;
};
GameManager.prototype._calculateUserScore = function () {
var score = 0;
this._droneList_user.forEach(function (drone) {
//if (drone.can_play) {
score += drone.score;
//}
});
return score;
};
GameManager.prototype._finish = function () {
console.log("Simulation finished");
this._result_message += " User score: " + this._calculateUserScore();
this._canUpdate = false;
return this.finish_deferred.resolve();
};
......@@ -763,7 +1097,8 @@ var GameManager = /** @class */ (function () {
};
GameManager.prototype._init = function () {
var _this = this, canvas, hemi_north, hemi_south, camera, on3DmodelsReady;
var _this = this,
canvas, hemi_north, hemi_south, camera, cam_radius, on3DmodelsReady;
canvas = this._canvas;
this._delayed_defer_list = [];
this._dispose();
......@@ -775,6 +1110,9 @@ var GameManager = /** @class */ (function () {
audioEngine: false
});
this._scene = new BABYLON.Scene(this._engine);
//for DEBUG - fondo negro
//this._scene.clearColor = BABYLON.Color3.Black();
//deep ground color - light blue simile sky
this._scene.clearColor = new BABYLON.Color4(
88 / 255,
171 / 255,
......@@ -797,13 +1135,18 @@ var GameManager = /** @class */ (function () {
this._scene
);
hemi_south.intensity = 0.75;
camera = new BABYLON.ArcRotateCamera("camera", 0, 1.25, 800,
cam_radius = (GAMEPARAMETERS.map.map_size * 1.10 < 6000) ?
GAMEPARAMETERS.map.map_size * 1.10 : 6000; //skybox scene limit
camera = new BABYLON.ArcRotateCamera("camera", 0, 1.25, cam_radius,
BABYLON.Vector3.Zero(), this._scene);
camera.wheelPrecision = 10;
//zoom out limit
camera.upperRadiusLimit = GAMEPARAMETERS.map.map_size * 10;
//scene.activeCamera.upperRadiusLimit = max * 4;
//changed for event handling
//camera.attachControl(this._scene.getEngine().getRenderingCanvas()); //orig
camera.attachControl(canvas, true);
camera.maxz = 40000;
camera.maxz = 400000;
this._camera = camera;
// Render loop
......@@ -819,8 +1162,9 @@ var GameManager = /** @class */ (function () {
}
// Init the map
_this._mapManager = new MapManager(ctx._scene);
ctx._spawnDrones(_this._mapManager.map_info.initial_position,
GAMEPARAMETERS.droneList, ctx);
ctx._spawnDrones(_this._mapManager.getMapInfo().initial_position,
GAMEPARAMETERS.map.drones.user, TEAM_USER, ctx);
ctx._spawnDrones(null, GAMEPARAMETERS.map.drones.enemy, TEAM_ENEMY, ctx);
// Hide the drone prefab
DroneManager.Prefab.isVisible = false;
//Hack to make advanced texture work
......@@ -833,23 +1177,37 @@ var GameManager = /** @class */ (function () {
ctx._scene
);
document = documentTmp;
for (count = 0; count < GAMEPARAMETERS.droneList.length; count += 1) {
controlMesh = ctx._droneList[count].infosMesh;
function colourDrones(drone_list, colour) {
for (count = 0; count < drone_list.length; count += 1) {
controlMesh = drone_list[count].infosMesh;
rect = new BABYLON.GUI.Rectangle();
rect.width = "10px";
rect.height = "10px";
rect.cornerRadius = 20;
rect.color = "white";
rect.thickness = 0.5;
rect.background = "grey";
rect.background = colour;
advancedTexture.addControl(rect);
rect.linkWithMesh(controlMesh);
}
}
function colourFlags(flag_list) {
for (count = 0; count < flag_list.length; count += 1) {
controlMesh = flag_list[count].subMeshes[0]._mesh;
rect = new BABYLON.GUI.Rectangle();
rect.width = "15px";
rect.height = "10px";
rect.cornerRadius = 1;
rect.color = "white";
rect.thickness = 0.5;
rect.background = "green";
advancedTexture.addControl(rect);
label = new BABYLON.GUI.TextBlock();
label.text = count.toString();
label.fontSize = 7;
rect.addControl(label);
rect.linkWithMesh(controlMesh);
rect.linkOffsetY = 0;
}
}
colourFlags(_this._mapManager._flag_list);
colourDrones(ctx._droneList_user, "blue");
colourDrones(ctx._droneList_enemy, "red");
console.log("on3DmodelsReady - advaced textures added");
return ctx;
};
......@@ -862,6 +1220,8 @@ var GameManager = /** @class */ (function () {
})
.push(function () {
on3DmodelsReady(_this);
_this._droneList =
_this._droneList_user.concat(_this._droneList_enemy);
var result = new RSVP.Queue();
result.push(function () {
return RSVP.delay(1000);
......@@ -871,7 +1231,7 @@ var GameManager = /** @class */ (function () {
};
GameManager.prototype._start = function () {
var _this = this, promise_list;
var _this = this, promise_list, start_msg;
_this.waiting_update_count = 0;
_this.ongoing_update_promise = null;
_this.finish_deferred = RSVP.defer();
......@@ -882,11 +1242,17 @@ var GameManager = /** @class */ (function () {
return new RSVP.Queue()
.push(function () {
promise_list = [];
_this._droneList.forEach(function (drone) {
_this._droneList_user.forEach(function (drone) {
drone._tick = 0;
promise_list.push(drone.internal_start(
_this._mapManager.getMapInfo().initial_position
));
promise_list.push(drone.internal_start());
});
start_msg = {
'flag_positions': _this._mapManager.getMapInfo().geo_flag_list
};
promise_list.push(_this._droneList_user[0].sendMsg(start_msg));
_this._droneList_enemy.forEach(function (drone) {
drone._tick = 0;
promise_list.push(drone.internal_start());
});
return RSVP.all(promise_list);
})
......@@ -941,9 +1307,10 @@ var GameManager = /** @class */ (function () {
return parameter;
};
GameManager.prototype._spawnDrones = function (center, drone_list, ctx) {
GameManager.prototype._spawnDrones = function (init_position, drone_list,
team, ctx, drone_location) {
var position, i, position_list = [], max_collision = 10 * drone_list.length,
collision_nb = 0, api;
collision_nb = 0, api, center;
function checkCollision(position, list) {
var el;
for (el = 0; el < list.length; el += 1) {
......@@ -953,7 +1320,7 @@ var GameManager = /** @class */ (function () {
}
return false;
}
function spawnDrone(x, y, z, index, drone_info, api) {
function spawnDrone(x, y, z, index, drone_info, api, team) {
var default_drone_AI = api.getDroneAI(), code, base, code_eval;
if (default_drone_AI) {
code = default_drone_AI;
......@@ -961,9 +1328,9 @@ var GameManager = /** @class */ (function () {
code = drone_info.script_content;
}
code_eval = "let drone = new DroneManager(ctx._scene, " +
index + ', api);' +
index + ', api, team);' +
"let droneMe = function(NativeDate, me, Math, window, DroneManager," +
" GameManager, FixedWingDroneAPI, BABYLON, " +
" GameManager, FixedWingDroneAPI, EnemyDroneAPI, BABYLON, " +
"GAMEPARAMETERS) {" +
"var start_time = (new Date(2070, 0, 0, 0, 0, 0, 0)).getTime();" +
"Date.now = function () {" +
......@@ -980,8 +1347,8 @@ var GameManager = /** @class */ (function () {
}
base = code_eval;
code_eval += code + "}; droneMe(Date, drone, Math, {});";
base += "};ctx._droneList.push(drone)";
code_eval += "ctx._droneList.push(drone)";
base += "};ctx._droneList_" + team + ".push(drone)";
code_eval += "ctx._droneList_" + team + ".push(drone)";
/*jslint evil: true*/
try {
eval(code_eval);
......@@ -1001,6 +1368,11 @@ var GameManager = /** @class */ (function () {
return new BABYLON.Vector3(x, y, z);
}
for (i = 0; i < drone_list.length; i += 1) {
if (!init_position) {
center = drone_list[i].position;
} else {
center = init_position;
}
position = randomSpherePoint(center.x + i, center.y + i, center.z + i,
0, 0, 0);
if (checkCollision(position, position_list)) {
......@@ -1010,14 +1382,18 @@ var GameManager = /** @class */ (function () {
}
} else {
position_list.push(position);
var id_offset = 0;
if (team == TEAM_ENEMY) {
id_offset = GAMEPARAMETERS.map.drones.user.length;
}
api = new this.APIs_dict[drone_list[i].type](
this,
drone_list[i],
GAMEPARAMETERS,
i
i + id_offset
);
spawnDrone(position.x, position.y, position.z, i,
drone_list[i], api);
spawnDrone(position.x, position.y, position.z, i + id_offset,
drone_list[i], api, team);
}
}
};
......@@ -1043,15 +1419,15 @@ var runGame, updateGame;
return game_manager_instance.run();
};
updateGame = function () {
updateGame = function (fullscreen) {
if (game_manager_instance) {
return game_manager_instance.update();
return game_manager_instance.update(fullscreen);
}
};
/*// Resize canvas on window resize
window.addEventListener('resize', function () {
engine.resize();
game_manager_instance._engine.resize();
});*/
......
......@@ -246,7 +246,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1007.63308.6371.35532</string> </value>
<value> <string>1010.5086.1058.34440</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -266,7 +266,7 @@
</tuple>
<state>
<tuple>
<float>1682437964.37</float>
<float>1690567567.2</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -18,6 +18,7 @@
<script src="jiodev.js" type="text/javascript"></script>
<script src="gadget_global.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<script type="text/javascript" src="./libraries/seedrandom.min.js"></script>
<script src="gadget_erp5_page_drone_capture_flag_script_page.js" type="text/javascript"></script>
......
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1007.63297.49586.20838</string> </value>
<value> <string>1008.22253.2281.60074</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -264,7 +264,7 @@
</tuple>
<state>
<tuple>
<float>1682436612.47</float>
<float>1683820259.6</float>
<string>UTC</string>
</tuple>
</state>
......
/*jslint indent: 2, maxlen: 100*/
/*global window, rJS, domsugar, document, Blob*/
(function (window, rJS, domsugar, document, Blob) {
/*global window, rJS, domsugar, document, URLSearchParams, Blob*/
(function (window, rJS, domsugar, document, URLSearchParams, Blob) {
"use strict";
//Default values - TODO: get them from the drone API
var SIMULATION_SPEED = 200,
SIMULATION_TIME = 1500,
min_lat = 45.6364,
max_lat = 45.65,
min_lon = 14.2521,
max_lon = 14.2766,
map_height = 100,
//Drone default values - TODO: get them from the drone API
var SIMULATION_SPEED = 10,
SIMULATION_TIME = 270,
MAP_SIZE = 600,
map_height = 700,
start_AMSL = 595,
DEFAULT_SPEED = 16,
MAX_ACCELERATION = 6,
......@@ -22,65 +19,20 @@
MAX_PITCH = 25,
MAX_CLIMB_RATE = 8,
MAX_SINK_RATE = 3,
INITIAL_POSITION = {
"latitude": 45.6412,
"longitude": 14.2658,
"z": 15
},
NUMBER_OF_DRONES = 2,
NUMBER_OF_DRONES = 10,
FLAG_WEIGHT = 5,
SEED = '6',
// Non-inputs parameters
DEFAULT_SCRIPT_CONTENT =
'var ALTITUDE = 100,\n' +
' EPSILON = 9,\n' +
' CHECKPOINT_LIST = [\n' +
' {\n' +
' altitude: 585.1806861589965,\n' +
' latitude: 45.64492790560583,\n' +
' longitude: 14.25334942966329\n' +
' },\n' +
' {\n' +
' altitude: 589.8802607573035,\n' +
' latitude: 45.64316335436476,\n' +
' longitude: 14.26332880184475\n' +
' },\n' +
' {\n' +
' altitude: 608.6648153348965,\n' +
' latitude: 45.64911917196595,\n' +
' longitude: 14.26214792790128\n' +
' },\n' +
' {\n' +
' altitude: 606.1448368129072,\n' +
' latitude: 45.64122685351364,\n' +
' longitude: 14.26590493128597\n' +
' },\n' +
' {\n' +
' altitude: 630.0829598206344,\n' +
' latitude: 45.64543355564817,\n' +
' longitude: 14.27242391207985\n' +
' },\n' +
' {\n' +
' altitude: 616.1839898415284,\n' +
' latitude: 45.6372792927328,\n' +
' longitude: 14.27533492411138\n' +
' },\n' +
' {\n' +
' altitude: 598.0603137354178,\n' +
' latitude: 45.64061299543953,\n' +
' longitude: 14.26161958465814\n' +
' },\n' +
' {\n' +
' altitude: 607.1243119862851,\n' +
' latitude: 45.64032340702919,\n' +
' longitude: 14.2682896662383\n' +
' }\n' +
' ];\n' +
'var EPSILON = 15,\n' +
' DODGE_DISTANCE = 100;\n' +
'\n' +
'function distance(lat1, lon1, lat2, lon2) {\n' +
'function distance(a, b) {\n' +
' var R = 6371e3, // meters\n' +
' la1 = lat1 * Math.PI / 180, // lat, lon in radians\n' +
' la2 = lat2 * Math.PI / 180,\n' +
' lo1 = lon1 * Math.PI / 180,\n' +
' lo2 = lon2 * Math.PI / 180,\n' +
' la1 = a.x * Math.PI / 180, // lat, lon in radians\n' +
' la2 = b.x * Math.PI / 180,\n' +
' lo1 = a.y * Math.PI / 180,\n' +
' lo2 = b.y * Math.PI / 180,\n' +
' haversine_phi = Math.pow(Math.sin((la2 - la1) / 2), 2),\n' +
' sin_lon = Math.sin((lo2 - lo1) / 2),\n' +
' h = haversine_phi + Math.cos(la1) * Math.cos(la2) * sin_lon * sin_lon;\n' +
......@@ -89,49 +41,88 @@
'\n' +
'me.onStart = function () {\n' +
' me.direction_set = false;\n' +
' me.next_checkpoint = 0;\n' +
' me.dodging = false;\n' +
' me.ongoing_detection = false;\n' +
'};\n' +
'\n' +
'me.onGetMsg = function (msg) {\n' +
' if (msg && msg.flag_positions) {\n' +
' me.flag_positions = msg.flag_positions\n' +
' me.next_checkpoint = me.id % me.flag_positions.length;\n' +
' }\n' +
'};\n' +
'\n' +
'me.onUpdate = function (timestamp) {' +
'me.onUpdate = function (timestamp) {\n' +
' if (!me.flag_positions) return;\n' +
' if (me.dodging) {\n' +
' me.current_position = me.getCurrentPosition();\n' +
' var dist = distance(\n' +
' me.current_position,\n' +
' me.dodging.position\n' +
' );\n' +
' if (dist >= DODGE_DISTANCE) {\n' +
//' console.log("Good distance to obstacle. DODGED.");\n' +
' me.dodging = false;\n' +
' }\n' +
' return;\n' +
' }\n' +
' if (!me.direction_set) {\n' +
' if (me.next_checkpoint < CHECKPOINT_LIST.length) {\n' +
' if (me.next_checkpoint < me.flag_positions.length) {\n' +
' me.setTargetCoordinates(\n' +
' CHECKPOINT_LIST[me.next_checkpoint].latitude,\n' +
' CHECKPOINT_LIST[me.next_checkpoint].longitude,\n' +
' CHECKPOINT_LIST[me.next_checkpoint].altitude + ALTITUDE + ALTITUDE * me.id\n' +
' me.flag_positions[me.next_checkpoint].x,\n' +
' me.flag_positions[me.next_checkpoint].y,\n' +
' me.flag_positions[me.next_checkpoint].z + me.id\n' +
' );\n' +
' console.log("[DEMO] Going to Checkpoint %d", me.next_checkpoint);\n' +
//' console.log("[DEMO] Going to Checkpoint %d", me.next_checkpoint);\n' +
' }\n' +
' me.direction_set = true;\n' +
' return;\n' +
' }\n' +
' if (me.next_checkpoint < CHECKPOINT_LIST.length) {\n' +
' if (me.next_checkpoint < me.flag_positions.length) {\n' +
' if (!me.ongoing_detection) {\n' +
' me.getDroneViewInfo();\n' +
' me.ongoing_detection = true;\n' +
' }\n' +
' }\n' +
' if (me.next_checkpoint < me.flag_positions.length) {\n' +
' me.current_position = me.getCurrentPosition();\n' +
' me.distance = distance(\n' +
' me.current_position.x,\n' +
' me.current_position.y,\n' +
' CHECKPOINT_LIST[me.next_checkpoint].latitude,\n' +
' CHECKPOINT_LIST[me.next_checkpoint].longitude\n' +
' me.current_position,\n' +
' me.flag_positions[me.next_checkpoint]\n' +
' );\n' +
' if (me.distance <= EPSILON) {\n' +
' console.log("[DEMO] Reached Checkpoint %d", me.next_checkpoint);\n' +
//' console.log("[DEMO] Reached Checkpoint %d", me.next_checkpoint);\n' +
' me.next_checkpoint += 1;\n' +
' me.direction_set = false;\n' +
' }\n' +
' return;\n' +
' }\n' +
' me.exit(0);\n' +
'};\n' +
'\n' +
'me.onDroneViewInfo = function (drone_view) {\n' +
' me.ongoing_detection = false;\n' +
' if (drone_view && drone_view.obstacles && drone_view.obstacles.length) {\n' +
' me.dodging = drone_view.obstacles[0];\n' +
' me.direction_set = false;\n' +
' var random = Math.random() < 0.5, dodge_point = {};\n' +
' Object.assign(dodge_point, me.flag_positions[me.next_checkpoint]);\n' +
' if (random) {\n' +
' dodge_point.x = dodge_point.x * -1;\n' +
' } else {\n' +
' dodge_point.y = dodge_point.y * -1;\n' +
' }\n' +
' me.setTargetCoordinates(dodge_point.x, dodge_point.y, me.getCurrentPosition().z);\n' +
' return;\n' +
' }\n' +
'};',
DRAW = true,
LOG = true,
LOG_TIME = 1662.7915426540285,
DRONE_LIST = [],
WIDTH = 680,
HEIGHT = 340,
LOGIC_FILE_LIST = [
'gadget_erp5_page_drone_capture_flag_logic.js',
'gadget_erp5_page_drone_capture_flag_fixedwingdrone.js'/*,
'gadget_erp5_page_drone_capture_flag_dronelogfollower.js'*/
'gadget_erp5_page_drone_capture_flag_fixedwingdrone.js',
'gadget_erp5_page_drone_capture_flag_enemydrone.js'
];
rJS(window)
......@@ -161,7 +152,8 @@
})
.declareMethod('render', function render() {
var gadget = this;
var gadget = this, url_sp = new URLSearchParams(window.location.hash),
url_seed = url_sp.get("seed");
return gadget.getDeclaredGadget('form_view')
.push(function (form_gadget) {
return form_gadget.render({
......@@ -299,60 +291,38 @@
"hidden": 0,
"type": "FloatField"
},
"my_minimum_latitud": {
"description": "",
"title": "Minimum latitude",
"default": min_lat,
"css_class": "",
"required": 1,
"editable": 1,
"key": "min_lat",
"hidden": 0,
"type": "FloatField"
},
"my_maximum_latitud": {
"description": "",
"title": "Maximum latitude",
"default": max_lat,
"css_class": "",
"required": 1,
"editable": 1,
"key": "max_lat",
"hidden": 0,
"type": "FloatField"
},
"my_minimum_longitud": {
"my_map_size": {
"description": "",
"title": "Minimum longitude",
"default": min_lon,
"title": "Map size",
"default": MAP_SIZE,
"css_class": "",
"required": 1,
"editable": 1,
"key": "min_lon",
"key": "map_size",
"hidden": 0,
"type": "FloatField"
},
"my_maximum_longitud": {
"my_start_AMSL": {
"description": "",
"title": "Maximum longitude",
"default": max_lon,
"title": "Start AMSL",
"default": start_AMSL,
"css_class": "",
"required": 1,
"editable": 1,
"key": "max_lon",
"key": "start_AMSL",
"hidden": 0,
"type": "FloatField"
},
"my_start_AMSL": {
"description": "",
"title": "Start AMSL",
"default": start_AMSL,
"my_map_seed": {
"description": "Seed value to randomize the map",
"title": "Seed value",
"default": url_seed ? url_seed : SEED,
"css_class": "",
"required": 1,
"editable": 1,
"key": "start_AMSL",
"key": "map_seed",
"hidden": 0,
"type": "FloatField"
"type": "StringField"
},
"my_map_height": {
"description": "",
......@@ -365,39 +335,17 @@
"hidden": 0,
"type": "IntegerField"
},
"my_init_pos_lon": {
/*"my_flag_weight": {
"description": "",
"title": "Initial drone longitude",
"default": INITIAL_POSITION.longitude,
"title": "Flag Weight",
"default": FLAG_WEIGHT,
"css_class": "",
"required": 1,
"editable": 1,
"key": "init_pos_lon",
"key": "flag_weight",
"hidden": 0,
"type": "FloatField"
},
"my_init_pos_lat": {
"description": "",
"title": "Initial drone latitude",
"default": INITIAL_POSITION.latitude,
"css_class": "",
"required": 1,
"editable": 1,
"key": "init_pos_lat",
"hidden": 0,
"type": "FloatField"
},
"my_init_pos_z": {
"description": "",
"title": "Initial drone position Z",
"default": INITIAL_POSITION.z,
"css_class": "",
"required": 1,
"editable": 1,
"key": "init_pos_z",
"hidden": 0,
"type": "FloatField"
},
"type": "IntegerField"
},*/
"my_number_of_drones": {
"description": "",
"title": "Number of drones",
......@@ -432,13 +380,11 @@
group_list: [[
"left",
[["my_simulation_speed"], ["my_simulation_time"], ["my_number_of_drones"],
["my_minimum_latitud"], ["my_maximum_latitud"],
["my_minimum_longitud"], ["my_maximum_longitud"],
["my_init_pos_lat"], ["my_init_pos_lon"], ["my_init_pos_z"],
["my_map_height"]]
["my_map_size"], ["my_map_height"],// ["my_flag_weight"],
["my_start_AMSL"], ["my_map_seed"]]
], [
"right",
[["my_start_AMSL"], ["my_drone_min_speed"], ["my_drone_speed"], ["my_drone_max_speed"],
[["my_drone_min_speed"], ["my_drone_speed"], ["my_drone_max_speed"],
["my_drone_max_acceleration"], ["my_drone_max_deceleration"],
["my_drone_max_roll"], ["my_drone_min_pitch"], ["my_drone_max_pitch"],
["my_drone_max_sink_rate"], ["my_drone_max_climb_rate"]]
......@@ -460,14 +406,119 @@
.declareJob('runGame', function runGame(options) {
var gadget = this, i,
fragment = gadget.element.querySelector('.simulator_div'),
game_parameters_json;
game_parameters_json, map_json;
DRONE_LIST = [];
fragment = domsugar(gadget.element.querySelector('.simulator_div'),
[domsugar('div')]).firstElementChild;
DRONE_LIST = [];
for (i = 0; i < options.number_of_drones; i += 1) {
DRONE_LIST[i] = {"id": i, "type": "FixedWingDroneAPI",
"script_content": options.script};
}
function randomizeMap(json_map) {
function randomIntFromInterval(min, max, random_seed) {
return Math.floor(random_seed.quick() * (max - min + 1) + min);
}
function randomPosition(random_seed, map_size) {
var sign_x = random_seed.quick() < 0.5 ? -1 : 1,
sign_y = random_seed.quick() < 0.5 ? -1 : 1,
pos_x = sign_x * random_seed.quick() * map_size / 2,
pos_y = sign_y * random_seed.quick() * map_size / 2;
return [pos_x, pos_y];
}
var seed_value = options.map_seed,
random_seed = new Math.seedrandom(seed_value), i,
n_enemies = randomIntFromInterval(5, 10, random_seed),
n_flags = randomIntFromInterval(Math.floor(DRONE_LIST.length / 2),
DRONE_LIST.length, random_seed),
n_obstacles = randomIntFromInterval(5, 15, random_seed),
flag_list = [], obstacle_list = [], enemy_list = [], random_position,
obstacles_types = ["box"/*, "sphere"*/, "cylinder"], type,
obstacle_limit = [options.map_size / 6, options.map_size / 100,
options.map_size / 6, 30];
//enemies
for (i = 0; i < n_enemies; i += 1) {
random_position = randomPosition(random_seed, options.map_size);
enemy_list.push({
"id": i + parseInt(options.number_of_drones),
"type": "EnemyDroneAPI",
"position": {
"x": random_position[0],
"y": random_position[1],
"z": 15 //TODO random z?
}
});
}
//flags
for (i = 0; i < n_flags; i += 1) {
//avoid flags near the limits
random_position = randomPosition(random_seed, options.map_size * 0.75);
flag_list.push({
"position": {
"x": random_position[0],
"y": random_position[1],
"z": 10
}
});
}
function checkDistance(position, position_list) {
function distance(a, b) {
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}
var el;
for (el = 0; el < position_list.length; el += 1) {
if (distance(position, position_list[el].position) < options.map_size / 6) {
return true;
}
}
return false;
}
//obstacles
for (i = 0; i < n_obstacles; i += 1) {
random_position = randomPosition(random_seed, options.map_size);
if (checkDistance({ 'x': random_position[0],
'y': random_position[1]}, flag_list)) {
i -= 1;
} else {
type = randomIntFromInterval(0, 2, random_seed);
obstacle_list.push({
"type": obstacles_types[type],
"position": {
"x": random_position[0],
"y": random_position[1],
"z": 15 //TODO random z?
},
"scale": {
"x": randomIntFromInterval(20, obstacle_limit[type], random_seed),
"y": randomIntFromInterval(20, obstacle_limit[type], random_seed),
"z": randomIntFromInterval(5, obstacle_limit[3], random_seed)
},
"rotation": {
"x": 0,
"y": 0,
"z": 0
}
});
}
}
json_map.obstacle_list = obstacle_list;
json_map.drones.enemy = enemy_list;
json_map.flag_list = flag_list;
return json_map;
}
map_json = {
"map_size": parseFloat(options.map_size),
"height": parseInt(options.map_height, 10),
"start_AMSL": parseFloat(options.start_AMSL),
"flag_list": [],
"obstacle_list" : [],
"drones": {
"user": DRONE_LIST,
"enemy": []
}
};
game_parameters_json = {
"drone": {
"maxAcceleration": parseInt(options.drone_max_acceleration, 10),
......@@ -487,24 +538,11 @@
"information": 0,
"communication": 0
},
"map": {
"min_lat": parseFloat(options.min_lat),
"max_lat": parseFloat(options.max_lat),
"min_lon": parseFloat(options.min_lon),
"max_lon": parseFloat(options.max_lon),
"height": parseInt(options.map_height, 10),
"start_AMSL": parseFloat(options.start_AMSL)
},
"initialPosition": {
"longitude": parseFloat(options.init_pos_lon),
"latitude": parseFloat(options.init_pos_lat),
"z": parseFloat(options.init_pos_z)
},
"map": randomizeMap(map_json),
"draw_flight_path": DRAW,
"temp_flight_path": true,
"log_drone_flight": LOG,
"log_interval_time": LOG_TIME,
"droneList": DRONE_LIST
"log_interval_time": LOG_TIME
};
return gadget.declareGadget("babylonjs.gadget.html",
{element: fragment, scope: 'simulator'})
......@@ -525,8 +563,7 @@
"type": "GadgetField",
"url": "babylonjs.gadget.html",
"sandbox": "public",
"renderjs_extra": '{"autorun": false, "width": ' + WIDTH + ', ' +
'"height": ' + HEIGHT + ', ' +
"renderjs_extra": '{"autorun": false, ' +
'"logic_file_list": ' + JSON.stringify(LOGIC_FILE_LIST) + ', ' +
'"game_parameters": ' + JSON.stringify(game_parameters_json) +
'}'
......@@ -553,35 +590,44 @@
return form_gadget.getContent();
})
.push(function (result) {
var a, blob, div, key, log, log_content;
var a, blob, div, key, log, log_content, aux;
i = 0;
for (key in result) {
if (result.hasOwnProperty(key)) {
log_content = result[key].join('\n').replaceAll(",", ";");
div = domsugar('div', { text: result.message });
document.querySelector('.container').parentNode.appendChild(div);
for (key in result.content) {
if (result.content.hasOwnProperty(key)) {
log_content = result.content[key].join('\n').replaceAll(",", ";");
blob = new Blob([log_content], {type: 'text/plain'});
a = domsugar('a', {
text: 'Download Simulation LOG ' + i,
download: 'simulation_log_' + i
+ '_speed_' + game_parameters_json.drone.speed
+ '_min-speed_' + game_parameters_json.drone.minSpeed
+ '_max-speed_' + game_parameters_json.drone.maxSpeed
+ '_max-accel_' + game_parameters_json.drone.maxAcceleration
+ '_max-decel_' + game_parameters_json.drone.maxDeceleration
+ '_max-roll_' + game_parameters_json.drone.maxRoll
+ '_min-pitch_' + game_parameters_json.drone.minPitchAngle
+ '_max-pitch_' + game_parameters_json.drone.maxPitchAngle
+ '_max-sink_' + game_parameters_json.drone.maxSinkRate
+ '_max-climb_' + game_parameters_json.drone.maxClimbRate
+ '.txt',
download: 'simulation_log_' + i +
'_speed_' + game_parameters_json.drone.speed +
'_min-speed_' + game_parameters_json.drone.minSpeed +
'_max-speed_' + game_parameters_json.drone.maxSpeed +
'_max-accel_' + game_parameters_json.drone.maxAcceleration +
'_max-decel_' + game_parameters_json.drone.maxDeceleration +
'_max-roll_' + game_parameters_json.drone.maxRoll +
'_min-pitch_' + game_parameters_json.drone.minPitchAngle +
'_max-pitch_' + game_parameters_json.drone.maxPitchAngle +
'_max-sink_' + game_parameters_json.drone.maxSinkRate +
'_max-climb_' + game_parameters_json.drone.maxClimbRate +
'.txt',
href: window.URL.createObjectURL(blob)
});
log = domsugar('textarea', { value: log_content, id: 'log_result_' + i });
log = domsugar('textarea',
{ value: log_content, id: 'log_result_' + i });
div = domsugar('div', [a]);
a.dataset.downloadurl = ['text/plain', a.download,
a.href].join(':');
document.querySelector('.container').appendChild(div);
document.querySelector('.container').appendChild(log);
document.querySelector('.container').parentNode.appendChild(div);
document.querySelector('.container').parentNode.appendChild(log);
i += 1;
if (i === DRONE_LIST.length) {
break;
//Do not show enemy drone logs for now
/*aux = domsugar('div', { text: "Enemy drones logs:" });
document.querySelector('.container').parentNode.appendChild(aux);*/
}
}
}
}, function (error) {
......@@ -590,4 +636,4 @@
});
});
}(window, rJS, domsugar, document, Blob));
\ No newline at end of file
}(window, rJS, domsugar, document, URLSearchParams, Blob));
\ No newline at end of file
......@@ -246,7 +246,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1007.63299.40871.39799</string> </value>
<value> <string>1009.57725.14056.1911</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -266,7 +266,7 @@
</tuple>
<state>
<tuple>
<float>1682437874.39</float>
<float>1689793877.59</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -16,7 +16,7 @@
</item>
<item>
<key> <string>height</string> </key>
<value> <int>512</int> </value>
<value> <int>1024</int> </value>
</item>
<item>
<key> <string>precondition</string> </key>
......@@ -28,7 +28,7 @@
</item>
<item>
<key> <string>width</string> </key>
<value> <int>512</int> </value>
<value> <int>1024</int> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>libraries</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
!function(f,a,c){var s,l=256,p="random",d=c.pow(l,6),g=c.pow(2,52),y=2*g,h=l-1;function n(n,t,r){function e(){for(var n=u.g(6),t=d,r=0;n<g;)n=(n+r)*l,t*=l,r=u.g(1);for(;y<=n;)n/=2,t/=2,r>>>=1;return(n+r)/t}var o=[],i=j(function n(t,r){var e,o=[],i=typeof t;if(r&&"object"==i)for(e in t)try{o.push(n(t[e],r-1))}catch(n){}return o.length?o:"string"==i?t:t+"\0"}((t=1==t?{entropy:!0}:t||{}).entropy?[n,S(a)]:null==n?function(){try{var n;return s&&(n=s.randomBytes)?n=n(l):(n=new Uint8Array(l),(f.crypto||f.msCrypto).getRandomValues(n)),S(n)}catch(n){var t=f.navigator,r=t&&t.plugins;return[+new Date,f,r,f.screen,S(a)]}}():n,3),o),u=new m(o);return e.int32=function(){return 0|u.g(4)},e.quick=function(){return u.g(4)/4294967296},e.double=e,j(S(u.S),a),(t.pass||r||function(n,t,r,e){return e&&(e.S&&v(e,u),n.state=function(){return v(u,{})}),r?(c[p]=n,t):n})(e,i,"global"in t?t.global:this==c,t.state)}function m(n){var t,r=n.length,u=this,e=0,o=u.i=u.j=0,i=u.S=[];for(r||(n=[r++]);e<l;)i[e]=e++;for(e=0;e<l;e++)i[e]=i[o=h&o+n[e%r]+(t=i[e])],i[o]=t;(u.g=function(n){for(var t,r=0,e=u.i,o=u.j,i=u.S;n--;)t=i[e=h&e+1],r=r*l+i[h&(i[e]=i[o=h&o+t])+(i[o]=t)];return u.i=e,u.j=o,r})(l)}function v(n,t){return t.i=n.i,t.j=n.j,t.S=n.S.slice(),t}function j(n,t){for(var r,e=n+"",o=0;o<e.length;)t[h&o]=h&(r^=19*t[h&o])+e.charCodeAt(o++);return S(t)}function S(n){return String.fromCharCode.apply(0,n)}if(j(c.random(),a),"object"==typeof module&&module.exports){module.exports=n;try{s=require("crypto")}catch(n){}}else"function"==typeof define&&define.amd?define(function(){return n}):c["seed"+p]=n}("undefined"!=typeof self?self:this,[],Math);
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>seedrandom.min.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment