Содержание

Codes by GPS Coordinates

The engine can submit a code by itself once the player reaches the right spot on the ground. There are two ways to do it:

Important: geolocation is enabled only with the player's permission. If the player refuses, no zone will fire, so leave a fallback way to pass the level (an ordinary code, a hint that gives the code away) or warn the teams in advance that GPS is required.
The team's track. While tracking is on, the engine records the team's path — per device, as “stood” and “walked” segments, plus zone hits and rejected fixes. The game page gives the author a “GPS tracks” link: a report with signs of spoofed geolocation per team (the verdict is not a sentence — every sign has honest explanations), disputed moments such as “reached the zone, but the code was not entered”, and a map. A track is kept for 90 days from its last entry; it can be deleted earlier on the same page.

Turn tracking on: gps_start()

Both ways start the same — with a gps_start() call in the “Task script” field:

gps_start();

Without it the browser won't ask for coordinates and no zone will fire. If a task has triggers but no gps_start() anywhere, the editor warns about it on save.

The engine loads the geolocation library only for tasks that need it — there is nothing else to switch on.

The GPS icon in the task header

A task with GPS has a satellite icon next to its title. It shows the player whether geolocation works and turns it on as well: on a phone the browser hands out coordinates only after a tap, which is why the icon is there from the very start, before the first fix.

The GPS icon in the task header: green means you can go

Colour What it means What a tap does
grey tracking is off turns it on
red no position: searching for satellites, the player denied access, or geolocation is unavailable starts the search again or explains how to allow access
yellow there is a position, but its accuracy is worse than 30 metres — the engine doesn't count such a fix refreshes the position
green everything works, you can go refreshes the position

Hovering over the icon shows the state in words and the current accuracy.

Server trigger

A zone is declared with a marker in the task text:

{! on gps 51.5299 -0.1275 radius 30: code newton !}

The general marker rules — where it fires, what the save checks, how several actions are written — are on the Server Actions page.

A zone of any shape

A yard, a square, a block — a polygon zone. After the word polygon come the vertex coordinates in a row, in “latitude longitude” pairs, from 3 to 30 vertices. A polygon has no radius — its vertices set the boundary.

{! on gps /* square */ polygon 51.5305 -0.1280 51.5320 -0.1262 51.5313 -0.1230 51.5291 -0.1238: code square !}

How it works

While the player walks, the page sends their coordinates to the server — when they have moved at least a few metres, but no more often than every couple of seconds. The server answers which zone the player is standing in, and the page enters that zone's code — silently, as an ordinary answer. In the answer log such a code is recorded under the player who got there.

Switch and priority

A plain code is entered by a zone once. A field condition in square brackets lets it enter again — and this is where it matters most:

{! on gps /* north yard */ 51.5221 -0.1072 radius 30: code [c1 !=] north !}
{! on gps /* south yard */ 51.5201 -0.1072 radius 30: code [c1 !=] south !}

Two zones on one sector make a switch: the first sector holds the code of the zone the team was in last. A {% match code_1 %} template shows from it where the team is now. The zones of a switch must not overlap: standing in both at once, the player gets both codes one after another.

{! on gps /* far */      51.5190 -0.1072 radius 30: code [c2 <] 1far !}
{! on gps /* at door */  51.5215 -0.1072 radius 30: code [c2 <] 4door !}

A set of zones with < makes a priority: a code with a bigger leading number replaces a smaller one, but not the other way round. So the sector keeps the best spot reached.

More on field conditions — on the Server Actions page.

A zone without a code: zone

Sometimes a zone doesn't need to enter anything — you only want to show the player something while they stand by the gazebo. That is what the zone action with a name is for:

{! on gps /* gazebo */ 51.5241 -0.1072 radius 30: zone gazebo !}

Such a zone has no code. The page tells the task script that the player entered or left the zone — once per change, not on every fix:

QEng.Gps.onZone = function (name, inside) {
  if (name === 'gazebo') $('#gazebo').toggle(inside);
};

A zone in a bonus and in a hint

A marker works as long as the text it stands in is visible. So a zone in a bonus description exists while the bonus is unsolved, and a zone in a hint appears together with the hint. That way the set of zones changes over the level without a single line of script.

Finish the game by a zone

A zone doesn't finish the game by itself. It takes two steps: the zone enters a code, and finish_game stands in a template branch on that code:

{! on gps 51.5299 -0.1275: code finish !}
{% match code_1 }:{ {%= finish}:{ {! finish_game !} %} %}

Example in game: a switch, a polygon, a priority, a zone without a code, zones in a bonus and in a hint

Check it without leaving home: the "GPS" tab

The tester has a “GPS” tab in the testing panel at the bottom of the page — it appears on a task whose script calls gps_start().

The "GPS" tab of the testing panel: the level's zones on a map and the player's position

What the markers entered on this page load is shown by the neighbouring “Actions” tab — see Server Actions.

Points in the script: g_targets

The older way, and it works as it always did. The coordinates of the points sit in the page source, so it won't do for a “find the place” riddle — the player opens the source and sees the target. You need it when the browser itself has to know where the target is: to show the distance or draw the point on your own map.

Points are put into the g_targets array. Each point is an object with coordinates, a radius and actions:

gps_start();
 
window.target_lat1 = 51.5211367;   // Latitude of the point
window.target_lon1 = -0.10723867;  // Longitude of the point
 
g_targets.push({
  'lat': window.target_lat1,
  'lon': window.target_lon1,
  'r': 30,  // Radius in METRES
  'action_inside': "enter_silent('finish_code_123')",
  'action_outside': "$('#distance_to').text(gps_distance(window.target_lat1, window.target_lon1).toFixed(0))"
});

There can be as many points as you like — just push several objects into g_targets.

The actions run on every coordinate update, not once when the player enters the zone. As long as the player stands inside the radius, action_inside will fire again and again. For submitting a code that is harmless: enter() and enter_silent() send a code only once. Showing a message or playing a sound, however, is better guarded with a flag of your own:
'action_inside': "if (!window.was_inside) { window.was_inside = 1; play_sound('limit'); }"

Distance to a point

The function gps_distance(latitude, longitude) returns the distance from the player's current position to the given point in metres. It is usually shown to the player so they can tell whether they are getting warmer or colder:

<p>Distance to the point: <span id="distance_to">???</span> m.</p>

A level with a point in the script: the distance to it is computed by the player's browser

The function can only be called once the first coordinates have arrived — before that the player's position is unknown. The easiest way is to call it from action_outside, as in the example above: the engine will run it itself as soon as the coordinates appear.

Your own reaction to coordinate updates

If you need more than a radius check — drawing a map or your own indicator, say — override the on_update_position() function: the engine calls it every time new coordinates arrive. The current position is in g_position (the lat and lon fields, as well as accuracy in metres, timestamp, altitude, speed, heading):

gps_start();
 
on_update_position = function () {
  $('#my_coords').text(g_position.lat.toFixed(5) + ', ' + g_position.lon.toFixed(5) + ' (±' + Math.round(g_position.accuracy) + 'm)');
};

You can also monitor GPS sensor states via on_gps_status(status, details):

To stop tracking (e.g. between stages to save battery), use gps_stop(). Calling gps_start() again reuses the active watcher and immediately evaluates new targets without waiting for the next satellite tick.

The same functions are also available under the common name QEng.Gps: QEng.Gps.start() and stop(), distance(lat, lon), position, targets, addTarget({…}), onUpdatePosition, onStatus. The old names work and will keep working.

Debugging

The service function init_debug_map(latitude, longitude, zoom) draws a map with your points and radii on it and puts down a draggable player marker. Wherever you drag the marker is where the engine will believe the player to be. The function expects a <div id=“debug_map”> on the page and works on Leaflet + OpenStreetMap, no API key needed. Server triggers don't need it — the tester has the “GPS” tab for that.

A map on the level

The engine does not load any mapping libraries — the author adds the map to the task text personally. The example below uses the free Leaflet with OpenStreetMap tiles: the library's CSS and JS are included, a map container is created, and a marker moves along with the player's coordinates.

A Leaflet example: the level's map with the player's marker on it

Keep in mind that the task is redrawn on every auto-refresh, so it is convenient to keep the state of the map (centre, zoom, the map object) in window, so that it is not created anew and does not jump around under the player.

If the plot only needs to send the player to a spot rather than check that they arrived, an ordinary link is enough. Insert it in “Source” mode:

<a href="geo:?q=59.249,37.465">59.249, 37.465</a>

The geo: scheme opens the coordinates in whatever navigation app is installed on the phone. For desktop browsers, which have no such scheme, a link to a map service will do:

<a href="https://www.google.com/maps/search/?api=1&query=49.940333,36.39305" target="_blank">49.940333, 36.39305</a>

Example of server triggers | Example of a point in the script | Example with a map | Example of links to coordinates