Get rid of some todos / fixmes
This commit is contained in:
parent
1025bede1f
commit
8260edb373
|
@ -71,10 +71,6 @@ releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
|
||||||
const translations = require("./translations");
|
const translations = require("./translations");
|
||||||
translations.gulptasksTranslations($, gulp, buildFolder);
|
translations.gulptasksTranslations($, gulp, buildFolder);
|
||||||
|
|
||||||
// FIXME
|
|
||||||
// const cordova = require("./cordova");
|
|
||||||
// cordova.gulptasksCordova($, gulp, buildFolder);
|
|
||||||
|
|
||||||
///////////////////// BUILD TASKS /////////////////////
|
///////////////////// BUILD TASKS /////////////////////
|
||||||
|
|
||||||
// Cleans up everything
|
// Cleans up everything
|
||||||
|
|
|
@ -1,467 +1,465 @@
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { Signal } from "../core/signal";
|
import { Signal } from "../core/signal";
|
||||||
import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils";
|
import { fastArrayDelete, fastArrayDeleteValueIfContained } from "./utils";
|
||||||
import { Vector } from "./vector";
|
import { Vector } from "./vector";
|
||||||
import { IS_MOBILE, SUPPORT_TOUCH } from "./config";
|
import { IS_MOBILE, SUPPORT_TOUCH } from "./config";
|
||||||
import { SOUNDS } from "../platform/sound";
|
import { SOUNDS } from "../platform/sound";
|
||||||
import { GLOBAL_APP } from "./globals";
|
import { GLOBAL_APP } from "./globals";
|
||||||
|
|
||||||
const logger = createLogger("click_detector");
|
const logger = createLogger("click_detector");
|
||||||
|
|
||||||
export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80;
|
export const MAX_MOVE_DISTANCE_PX = IS_MOBILE ? 20 : 80;
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
const registerClickDetectors = G_IS_DEV && true;
|
const registerClickDetectors = G_IS_DEV && true;
|
||||||
if (registerClickDetectors) {
|
if (registerClickDetectors) {
|
||||||
/** @type {Array<ClickDetector>} */
|
/** @type {Array<ClickDetector>} */
|
||||||
window.activeClickDetectors = [];
|
window.activeClickDetectors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store active click detectors so we can cancel them
|
// Store active click detectors so we can cancel them
|
||||||
/** @type {Array<ClickDetector>} */
|
/** @type {Array<ClickDetector>} */
|
||||||
const ongoingClickDetectors = [];
|
const ongoingClickDetectors = [];
|
||||||
|
|
||||||
// Store when the last touch event was registered, to avoid accepting a touch *and* a click event
|
// Store when the last touch event was registered, to avoid accepting a touch *and* a click event
|
||||||
|
|
||||||
export let clickDetectorGlobals = {
|
export let clickDetectorGlobals = {
|
||||||
lastTouchTime: -1000,
|
lastTouchTime: -1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click detector creation payload typehints
|
* Click detector creation payload typehints
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* consumeEvents?: boolean,
|
* consumeEvents?: boolean,
|
||||||
* preventDefault?: boolean,
|
* preventDefault?: boolean,
|
||||||
* applyCssClass?: string,
|
* applyCssClass?: string,
|
||||||
* captureTouchmove?: boolean,
|
* captureTouchmove?: boolean,
|
||||||
* targetOnly?: boolean,
|
* targetOnly?: boolean,
|
||||||
* maxDistance?: number,
|
* maxDistance?: number,
|
||||||
* clickSound?: string,
|
* clickSound?: string,
|
||||||
* preventClick?: boolean,
|
* preventClick?: boolean,
|
||||||
* }} ClickDetectorConstructorArgs
|
* }} ClickDetectorConstructorArgs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Detects clicks
|
// Detects clicks
|
||||||
export class ClickDetector {
|
export class ClickDetector {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Element} element
|
* @param {Element} element
|
||||||
* @param {object} param1
|
* @param {object} param1
|
||||||
* @param {boolean=} param1.consumeEvents Whether to call stopPropagation
|
* @param {boolean=} param1.consumeEvents Whether to call stopPropagation
|
||||||
* (Useful for nested elements where the parent has a click handler as wel)
|
* (Useful for nested elements where the parent has a click handler as wel)
|
||||||
* @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster)
|
* @param {boolean=} param1.preventDefault Whether to call preventDefault (Usually makes the handler faster)
|
||||||
* @param {string=} param1.applyCssClass The css class to add while the element is pressed
|
* @param {string=} param1.applyCssClass The css class to add while the element is pressed
|
||||||
* @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well
|
* @param {boolean=} param1.captureTouchmove Whether to capture touchmove events as well
|
||||||
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
|
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
|
||||||
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
|
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
|
||||||
* @param {string=} param1.clickSound Sound key to play on touchdown
|
* @param {string=} param1.clickSound Sound key to play on touchdown
|
||||||
* @param {boolean=} param1.preventClick Whether to prevent click events
|
* @param {boolean=} param1.preventClick Whether to prevent click events
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
element,
|
element,
|
||||||
{
|
{
|
||||||
consumeEvents = false,
|
consumeEvents = false,
|
||||||
preventDefault = true,
|
preventDefault = true,
|
||||||
applyCssClass = "pressed",
|
applyCssClass = "pressed",
|
||||||
captureTouchmove = false,
|
captureTouchmove = false,
|
||||||
targetOnly = false,
|
targetOnly = false,
|
||||||
maxDistance = MAX_MOVE_DISTANCE_PX,
|
maxDistance = MAX_MOVE_DISTANCE_PX,
|
||||||
clickSound = SOUNDS.uiClick,
|
clickSound = SOUNDS.uiClick,
|
||||||
preventClick = false,
|
preventClick = false,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
assert(element, "No element given!");
|
assert(element, "No element given!");
|
||||||
this.clickDownPosition = null;
|
this.clickDownPosition = null;
|
||||||
|
|
||||||
this.consumeEvents = consumeEvents;
|
this.consumeEvents = consumeEvents;
|
||||||
this.preventDefault = preventDefault;
|
this.preventDefault = preventDefault;
|
||||||
this.applyCssClass = applyCssClass;
|
this.applyCssClass = applyCssClass;
|
||||||
this.captureTouchmove = captureTouchmove;
|
this.captureTouchmove = captureTouchmove;
|
||||||
this.targetOnly = targetOnly;
|
this.targetOnly = targetOnly;
|
||||||
this.clickSound = clickSound;
|
this.clickSound = clickSound;
|
||||||
this.maxDistance = maxDistance;
|
this.maxDistance = maxDistance;
|
||||||
this.preventClick = preventClick;
|
this.preventClick = preventClick;
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
this.click = new Signal();
|
this.click = new Signal();
|
||||||
this.rightClick = new Signal();
|
this.rightClick = new Signal();
|
||||||
this.touchstart = new Signal();
|
this.touchstart = new Signal();
|
||||||
this.touchmove = new Signal();
|
this.touchmove = new Signal();
|
||||||
this.touchend = new Signal();
|
this.touchend = new Signal();
|
||||||
this.touchcancel = new Signal();
|
this.touchcancel = new Signal();
|
||||||
|
|
||||||
// Simple signals which just receive the touch position
|
// Simple signals which just receive the touch position
|
||||||
this.touchstartSimple = new Signal();
|
this.touchstartSimple = new Signal();
|
||||||
this.touchmoveSimple = new Signal();
|
this.touchmoveSimple = new Signal();
|
||||||
this.touchendSimple = new Signal();
|
this.touchendSimple = new Signal();
|
||||||
|
|
||||||
// Store time of touch start
|
// Store time of touch start
|
||||||
this.clickStartTime = null;
|
this.clickStartTime = null;
|
||||||
|
|
||||||
// A click can be cancelled if another detector registers a click
|
// A click can be cancelled if another detector registers a click
|
||||||
this.cancelled = false;
|
this.cancelled = false;
|
||||||
|
|
||||||
this.internalBindTo(/** @type {HTMLElement} */ (element));
|
this.internalBindTo(/** @type {HTMLElement} */ (element));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up all event listeners of this detector
|
* Cleans up all event listeners of this detector
|
||||||
*/
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if (this.element) {
|
if (this.element) {
|
||||||
if (registerClickDetectors) {
|
if (registerClickDetectors) {
|
||||||
const index = window.activeClickDetectors.indexOf(this);
|
const index = window.activeClickDetectors.indexOf(this);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
logger.error("Click detector cleanup but is not active");
|
logger.error("Click detector cleanup but is not active");
|
||||||
} else {
|
} else {
|
||||||
window.activeClickDetectors.splice(index, 1);
|
window.activeClickDetectors.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const options = this.internalGetEventListenerOptions();
|
const options = this.internalGetEventListenerOptions();
|
||||||
|
|
||||||
if (SUPPORT_TOUCH) {
|
if (SUPPORT_TOUCH) {
|
||||||
this.element.removeEventListener("touchstart", this.handlerTouchStart, options);
|
this.element.removeEventListener("touchstart", this.handlerTouchStart, options);
|
||||||
this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
|
this.element.removeEventListener("touchend", this.handlerTouchEnd, options);
|
||||||
this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options);
|
this.element.removeEventListener("touchcancel", this.handlerTouchCancel, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.removeEventListener("mouseup", this.handlerTouchStart, options);
|
this.element.removeEventListener("mouseup", this.handlerTouchStart, options);
|
||||||
this.element.removeEventListener("mousedown", this.handlerTouchEnd, options);
|
this.element.removeEventListener("mousedown", this.handlerTouchEnd, options);
|
||||||
this.element.removeEventListener("mouseout", this.handlerTouchCancel, options);
|
this.element.removeEventListener("mouseout", this.handlerTouchCancel, options);
|
||||||
|
|
||||||
if (this.captureTouchmove) {
|
if (this.captureTouchmove) {
|
||||||
if (SUPPORT_TOUCH) {
|
if (SUPPORT_TOUCH) {
|
||||||
this.element.removeEventListener("touchmove", this.handlerTouchMove, options);
|
this.element.removeEventListener("touchmove", this.handlerTouchMove, options);
|
||||||
}
|
}
|
||||||
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
|
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.preventClick) {
|
if (this.preventClick) {
|
||||||
this.element.removeEventListener("click", this.handlerPreventClick, options);
|
this.element.removeEventListener("click", this.handlerPreventClick, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.click.removeAll();
|
this.click.removeAll();
|
||||||
this.touchstart.removeAll();
|
this.touchstart.removeAll();
|
||||||
this.touchmove.removeAll();
|
this.touchmove.removeAll();
|
||||||
this.touchend.removeAll();
|
this.touchend.removeAll();
|
||||||
this.touchcancel.removeAll();
|
this.touchcancel.removeAll();
|
||||||
|
|
||||||
// TODO: Remove pointer captures
|
this.element = null;
|
||||||
|
}
|
||||||
this.element = null;
|
}
|
||||||
}
|
|
||||||
}
|
// INTERNAL METHODS
|
||||||
|
|
||||||
// INTERNAL METHODS
|
/**
|
||||||
|
*
|
||||||
/**
|
* @param {Event} event
|
||||||
*
|
*/
|
||||||
* @param {Event} event
|
internalPreventClick(event) {
|
||||||
*/
|
window.focus();
|
||||||
internalPreventClick(event) {
|
event.preventDefault();
|
||||||
window.focus();
|
}
|
||||||
event.preventDefault();
|
|
||||||
}
|
/**
|
||||||
|
* Internal method to get the options to pass to an event listener
|
||||||
/**
|
*/
|
||||||
* Internal method to get the options to pass to an event listener
|
internalGetEventListenerOptions() {
|
||||||
*/
|
return {
|
||||||
internalGetEventListenerOptions() {
|
capture: this.consumeEvents,
|
||||||
return {
|
passive: !this.preventDefault,
|
||||||
capture: this.consumeEvents,
|
};
|
||||||
passive: !this.preventDefault,
|
}
|
||||||
};
|
|
||||||
}
|
/**
|
||||||
|
* Binds the click detector to an element
|
||||||
/**
|
* @param {HTMLElement} element
|
||||||
* Binds the click detector to an element
|
*/
|
||||||
* @param {HTMLElement} element
|
internalBindTo(element) {
|
||||||
*/
|
const options = this.internalGetEventListenerOptions();
|
||||||
internalBindTo(element) {
|
|
||||||
const options = this.internalGetEventListenerOptions();
|
this.handlerTouchStart = this.internalOnPointerDown.bind(this);
|
||||||
|
this.handlerTouchEnd = this.internalOnPointerEnd.bind(this);
|
||||||
this.handlerTouchStart = this.internalOnPointerDown.bind(this);
|
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
|
||||||
this.handlerTouchEnd = this.internalOnPointerEnd.bind(this);
|
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
|
||||||
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
|
|
||||||
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
|
if (this.preventClick) {
|
||||||
|
this.handlerPreventClick = this.internalPreventClick.bind(this);
|
||||||
if (this.preventClick) {
|
element.addEventListener("click", this.handlerPreventClick, options);
|
||||||
this.handlerPreventClick = this.internalPreventClick.bind(this);
|
}
|
||||||
element.addEventListener("click", this.handlerPreventClick, options);
|
|
||||||
}
|
if (SUPPORT_TOUCH) {
|
||||||
|
element.addEventListener("touchstart", this.handlerTouchStart, options);
|
||||||
if (SUPPORT_TOUCH) {
|
element.addEventListener("touchend", this.handlerTouchEnd, options);
|
||||||
element.addEventListener("touchstart", this.handlerTouchStart, options);
|
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
|
||||||
element.addEventListener("touchend", this.handlerTouchEnd, options);
|
}
|
||||||
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
|
|
||||||
}
|
element.addEventListener("mousedown", this.handlerTouchStart, options);
|
||||||
|
element.addEventListener("mouseup", this.handlerTouchEnd, options);
|
||||||
element.addEventListener("mousedown", this.handlerTouchStart, options);
|
element.addEventListener("mouseout", this.handlerTouchCancel, options);
|
||||||
element.addEventListener("mouseup", this.handlerTouchEnd, options);
|
|
||||||
element.addEventListener("mouseout", this.handlerTouchCancel, options);
|
if (this.captureTouchmove) {
|
||||||
|
if (SUPPORT_TOUCH) {
|
||||||
if (this.captureTouchmove) {
|
element.addEventListener("touchmove", this.handlerTouchMove, options);
|
||||||
if (SUPPORT_TOUCH) {
|
}
|
||||||
element.addEventListener("touchmove", this.handlerTouchMove, options);
|
element.addEventListener("mousemove", this.handlerTouchMove, options);
|
||||||
}
|
}
|
||||||
element.addEventListener("mousemove", this.handlerTouchMove, options);
|
|
||||||
}
|
if (registerClickDetectors) {
|
||||||
|
window.activeClickDetectors.push(this);
|
||||||
if (registerClickDetectors) {
|
}
|
||||||
window.activeClickDetectors.push(this);
|
this.element = element;
|
||||||
}
|
}
|
||||||
this.element = element;
|
|
||||||
}
|
/**
|
||||||
|
* Returns if the bound element is currently in the DOM.
|
||||||
/**
|
*/
|
||||||
* Returns if the bound element is currently in the DOM.
|
internalIsDomElementAttached() {
|
||||||
*/
|
return this.element && document.documentElement.contains(this.element);
|
||||||
internalIsDomElementAttached() {
|
}
|
||||||
return this.element && document.documentElement.contains(this.element);
|
|
||||||
}
|
/**
|
||||||
|
* Checks if the given event is relevant for this detector
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Checks if the given event is relevant for this detector
|
*/
|
||||||
* @param {TouchEvent|MouseEvent} event
|
internalEventPreHandler(event, expectedRemainingTouches = 1) {
|
||||||
*/
|
if (!this.element) {
|
||||||
internalEventPreHandler(event, expectedRemainingTouches = 1) {
|
// Already cleaned up
|
||||||
if (!this.element) {
|
return false;
|
||||||
// Already cleaned up
|
}
|
||||||
return false;
|
|
||||||
}
|
if (this.targetOnly && event.target !== this.element) {
|
||||||
|
// Clicked a child element
|
||||||
if (this.targetOnly && event.target !== this.element) {
|
return false;
|
||||||
// Clicked a child element
|
}
|
||||||
return false;
|
|
||||||
}
|
// Stop any propagation and defaults if configured
|
||||||
|
if (this.consumeEvents && event.cancelable) {
|
||||||
// Stop any propagation and defaults if configured
|
event.stopPropagation();
|
||||||
if (this.consumeEvents && event.cancelable) {
|
}
|
||||||
event.stopPropagation();
|
|
||||||
}
|
if (this.preventDefault && event.cancelable) {
|
||||||
|
event.preventDefault();
|
||||||
if (this.preventDefault && event.cancelable) {
|
}
|
||||||
event.preventDefault();
|
|
||||||
}
|
if (window.TouchEvent && event instanceof TouchEvent) {
|
||||||
|
clickDetectorGlobals.lastTouchTime = performance.now();
|
||||||
if (window.TouchEvent && event instanceof TouchEvent) {
|
|
||||||
clickDetectorGlobals.lastTouchTime = performance.now();
|
// console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches);
|
||||||
|
if (event.targetTouches.length !== expectedRemainingTouches) {
|
||||||
// console.log("Got touches", event.targetTouches.length, "vs", expectedRemainingTouches);
|
return false;
|
||||||
if (event.targetTouches.length !== expectedRemainingTouches) {
|
}
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
}
|
if (event instanceof MouseEvent) {
|
||||||
|
if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) {
|
||||||
if (event instanceof MouseEvent) {
|
return false;
|
||||||
if (performance.now() - clickDetectorGlobals.lastTouchTime < 1000.0) {
|
}
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
return true;
|
|
||||||
}
|
/**
|
||||||
|
* Extracts the mous position from an event
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Extracts the mous position from an event
|
* @returns {Vector} The client space position
|
||||||
* @param {TouchEvent|MouseEvent} event
|
*/
|
||||||
* @returns {Vector} The client space position
|
static extractPointerPosition(event) {
|
||||||
*/
|
if (window.TouchEvent && event instanceof TouchEvent) {
|
||||||
static extractPointerPosition(event) {
|
if (event.changedTouches.length !== 1) {
|
||||||
if (window.TouchEvent && event instanceof TouchEvent) {
|
logger.warn(
|
||||||
if (event.changedTouches.length !== 1) {
|
"Got unexpected target touches:",
|
||||||
logger.warn(
|
event.targetTouches.length,
|
||||||
"Got unexpected target touches:",
|
"->",
|
||||||
event.targetTouches.length,
|
event.targetTouches
|
||||||
"->",
|
);
|
||||||
event.targetTouches
|
return new Vector(0, 0);
|
||||||
);
|
}
|
||||||
return new Vector(0, 0);
|
|
||||||
}
|
const touch = event.changedTouches[0];
|
||||||
|
return new Vector(touch.clientX, touch.clientY);
|
||||||
const touch = event.changedTouches[0];
|
}
|
||||||
return new Vector(touch.clientX, touch.clientY);
|
|
||||||
}
|
if (event instanceof MouseEvent) {
|
||||||
|
return new Vector(event.clientX, event.clientY);
|
||||||
if (event instanceof MouseEvent) {
|
}
|
||||||
return new Vector(event.clientX, event.clientY);
|
|
||||||
}
|
assertAlways(false, "Got unknown event: " + event);
|
||||||
|
|
||||||
assertAlways(false, "Got unknown event: " + event);
|
return new Vector(0, 0);
|
||||||
|
}
|
||||||
return new Vector(0, 0);
|
|
||||||
}
|
/**
|
||||||
|
* Cacnels all ongoing events on this detector
|
||||||
/**
|
*/
|
||||||
* Cacnels all ongoing events on this detector
|
cancelOngoingEvents() {
|
||||||
*/
|
if (this.applyCssClass && this.element) {
|
||||||
cancelOngoingEvents() {
|
this.element.classList.remove(this.applyCssClass);
|
||||||
if (this.applyCssClass && this.element) {
|
}
|
||||||
this.element.classList.remove(this.applyCssClass);
|
this.clickDownPosition = null;
|
||||||
}
|
this.clickStartTime = null;
|
||||||
this.clickDownPosition = null;
|
this.cancelled = true;
|
||||||
this.clickStartTime = null;
|
fastArrayDeleteValueIfContained(ongoingClickDetectors, this);
|
||||||
this.cancelled = true;
|
}
|
||||||
fastArrayDeleteValueIfContained(ongoingClickDetectors, this);
|
|
||||||
}
|
/**
|
||||||
|
* Internal pointer down handler
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Internal pointer down handler
|
*/
|
||||||
* @param {TouchEvent|MouseEvent} event
|
internalOnPointerDown(event) {
|
||||||
*/
|
window.focus();
|
||||||
internalOnPointerDown(event) {
|
|
||||||
window.focus();
|
if (!this.internalEventPreHandler(event, 1)) {
|
||||||
|
return false;
|
||||||
if (!this.internalEventPreHandler(event, 1)) {
|
}
|
||||||
return false;
|
|
||||||
}
|
const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
||||||
|
|
||||||
const position = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
if (event instanceof MouseEvent) {
|
||||||
|
const isRightClick = event.button === 2;
|
||||||
if (event instanceof MouseEvent) {
|
if (isRightClick) {
|
||||||
const isRightClick = event.button === 2;
|
// Ignore right clicks
|
||||||
if (isRightClick) {
|
this.rightClick.dispatch(position, event);
|
||||||
// Ignore right clicks
|
this.cancelled = true;
|
||||||
this.rightClick.dispatch(position, event);
|
this.clickDownPosition = null;
|
||||||
this.cancelled = true;
|
return;
|
||||||
this.clickDownPosition = null;
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
if (this.clickDownPosition) {
|
||||||
|
logger.warn("Ignoring double click");
|
||||||
if (this.clickDownPosition) {
|
return false;
|
||||||
logger.warn("Ignoring double click");
|
}
|
||||||
return false;
|
|
||||||
}
|
this.cancelled = false;
|
||||||
|
this.touchstart.dispatch(event);
|
||||||
this.cancelled = false;
|
|
||||||
this.touchstart.dispatch(event);
|
// Store where the touch started
|
||||||
|
this.clickDownPosition = position;
|
||||||
// Store where the touch started
|
this.clickStartTime = performance.now();
|
||||||
this.clickDownPosition = position;
|
this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y);
|
||||||
this.clickStartTime = performance.now();
|
|
||||||
this.touchstartSimple.dispatch(this.clickDownPosition.x, this.clickDownPosition.y);
|
// If we are not currently within a click, register it
|
||||||
|
if (ongoingClickDetectors.indexOf(this) < 0) {
|
||||||
// If we are not currently within a click, register it
|
ongoingClickDetectors.push(this);
|
||||||
if (ongoingClickDetectors.indexOf(this) < 0) {
|
} else {
|
||||||
ongoingClickDetectors.push(this);
|
logger.warn("Click detector got pointer down of active pointer twice");
|
||||||
} else {
|
}
|
||||||
logger.warn("Click detector got pointer down of active pointer twice");
|
|
||||||
}
|
// If we should apply any classes, do this now
|
||||||
|
if (this.applyCssClass) {
|
||||||
// If we should apply any classes, do this now
|
this.element.classList.add(this.applyCssClass);
|
||||||
if (this.applyCssClass) {
|
}
|
||||||
this.element.classList.add(this.applyCssClass);
|
|
||||||
}
|
// If we should play any sound, do this
|
||||||
|
if (this.clickSound) {
|
||||||
// If we should play any sound, do this
|
GLOBAL_APP.sound.playUiSound(this.clickSound);
|
||||||
if (this.clickSound) {
|
}
|
||||||
GLOBAL_APP.sound.playUiSound(this.clickSound);
|
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
|
* Internal pointer move handler
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Internal pointer move handler
|
*/
|
||||||
* @param {TouchEvent|MouseEvent} event
|
internalOnPointerMove(event) {
|
||||||
*/
|
if (!this.internalEventPreHandler(event, 1)) {
|
||||||
internalOnPointerMove(event) {
|
return false;
|
||||||
if (!this.internalEventPreHandler(event, 1)) {
|
}
|
||||||
return false;
|
this.touchmove.dispatch(event);
|
||||||
}
|
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
||||||
this.touchmove.dispatch(event);
|
this.touchmoveSimple.dispatch(pos.x, pos.y);
|
||||||
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
return false;
|
||||||
this.touchmoveSimple.dispatch(pos.x, pos.y);
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
|
* Internal pointer end handler
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Internal pointer end handler
|
*/
|
||||||
* @param {TouchEvent|MouseEvent} event
|
internalOnPointerEnd(event) {
|
||||||
*/
|
window.focus();
|
||||||
internalOnPointerEnd(event) {
|
|
||||||
window.focus();
|
if (!this.internalEventPreHandler(event, 0)) {
|
||||||
|
return false;
|
||||||
if (!this.internalEventPreHandler(event, 0)) {
|
}
|
||||||
return false;
|
|
||||||
}
|
if (this.cancelled) {
|
||||||
|
// warn(this, "Not dispatching touchend on cancelled listener");
|
||||||
if (this.cancelled) {
|
return false;
|
||||||
// warn(this, "Not dispatching touchend on cancelled listener");
|
}
|
||||||
return false;
|
|
||||||
}
|
if (event instanceof MouseEvent) {
|
||||||
|
const isRightClick = event.button === 2;
|
||||||
if (event instanceof MouseEvent) {
|
if (isRightClick) {
|
||||||
const isRightClick = event.button === 2;
|
return;
|
||||||
if (isRightClick) {
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
const index = ongoingClickDetectors.indexOf(this);
|
||||||
|
if (index < 0) {
|
||||||
const index = ongoingClickDetectors.indexOf(this);
|
logger.warn("Got pointer end but click detector is not in pressed state");
|
||||||
if (index < 0) {
|
} else {
|
||||||
logger.warn("Got pointer end but click detector is not in pressed state");
|
fastArrayDelete(ongoingClickDetectors, index);
|
||||||
} else {
|
}
|
||||||
fastArrayDelete(ongoingClickDetectors, index);
|
|
||||||
}
|
let dispatchClick = false;
|
||||||
|
let dispatchClickPos = null;
|
||||||
let dispatchClick = false;
|
|
||||||
let dispatchClickPos = null;
|
// Check for correct down position, otherwise must have pinched or so
|
||||||
|
if (this.clickDownPosition) {
|
||||||
// Check for correct down position, otherwise must have pinched or so
|
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
||||||
if (this.clickDownPosition) {
|
const distance = pos.distance(this.clickDownPosition);
|
||||||
const pos = /** @type {typeof ClickDetector} */ (this.constructor).extractPointerPosition(event);
|
if (!IS_MOBILE || distance <= this.maxDistance) {
|
||||||
const distance = pos.distance(this.clickDownPosition);
|
dispatchClick = true;
|
||||||
if (!IS_MOBILE || distance <= this.maxDistance) {
|
dispatchClickPos = pos;
|
||||||
dispatchClick = true;
|
} else {
|
||||||
dispatchClickPos = pos;
|
console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")");
|
||||||
} else {
|
}
|
||||||
console.warn("[ClickDetector] Touch does not count as click:", "(was", distance, ")");
|
}
|
||||||
}
|
|
||||||
}
|
this.clickDownPosition = null;
|
||||||
|
this.clickStartTime = null;
|
||||||
this.clickDownPosition = null;
|
|
||||||
this.clickStartTime = null;
|
if (this.applyCssClass) {
|
||||||
|
this.element.classList.remove(this.applyCssClass);
|
||||||
if (this.applyCssClass) {
|
}
|
||||||
this.element.classList.remove(this.applyCssClass);
|
|
||||||
}
|
// Dispatch in the end to avoid the element getting invalidated
|
||||||
|
// Also make sure that the element is still in the dom
|
||||||
// Dispatch in the end to avoid the element getting invalidated
|
if (this.internalIsDomElementAttached()) {
|
||||||
// Also make sure that the element is still in the dom
|
this.touchend.dispatch(event);
|
||||||
if (this.internalIsDomElementAttached()) {
|
this.touchendSimple.dispatch();
|
||||||
this.touchend.dispatch(event);
|
|
||||||
this.touchendSimple.dispatch();
|
if (dispatchClick) {
|
||||||
|
const detectors = ongoingClickDetectors.slice();
|
||||||
if (dispatchClick) {
|
for (let i = 0; i < detectors.length; ++i) {
|
||||||
const detectors = ongoingClickDetectors.slice();
|
detectors[i].cancelOngoingEvents();
|
||||||
for (let i = 0; i < detectors.length; ++i) {
|
}
|
||||||
detectors[i].cancelOngoingEvents();
|
this.click.dispatch(dispatchClickPos, event);
|
||||||
}
|
}
|
||||||
this.click.dispatch(dispatchClickPos, event);
|
}
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
/**
|
||||||
|
* Internal touch cancel handler
|
||||||
/**
|
* @param {TouchEvent|MouseEvent} event
|
||||||
* Internal touch cancel handler
|
*/
|
||||||
* @param {TouchEvent|MouseEvent} event
|
internalOnTouchCancel(event) {
|
||||||
*/
|
if (!this.internalEventPreHandler(event, 0)) {
|
||||||
internalOnTouchCancel(event) {
|
return false;
|
||||||
if (!this.internalEventPreHandler(event, 0)) {
|
}
|
||||||
return false;
|
|
||||||
}
|
if (this.cancelled) {
|
||||||
|
// warn(this, "Not dispatching touchcancel on cancelled listener");
|
||||||
if (this.cancelled) {
|
return false;
|
||||||
// warn(this, "Not dispatching touchcancel on cancelled listener");
|
}
|
||||||
return false;
|
|
||||||
}
|
this.cancelOngoingEvents();
|
||||||
|
this.touchcancel.dispatch(event);
|
||||||
this.cancelOngoingEvents();
|
this.touchendSimple.dispatch(event);
|
||||||
this.touchcancel.dispatch(event);
|
return false;
|
||||||
this.touchendSimple.dispatch(event);
|
}
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
import { globalConfig } from "./config";
|
import { globalConfig } from "./config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("../game/root").GameRoot} GameRoot
|
* @typedef {import("../game/root").GameRoot} GameRoot
|
||||||
* @typedef {import("./rectangle").Rectangle} Rectangle
|
* @typedef {import("./rectangle").Rectangle} Rectangle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class DrawParameters {
|
export class DrawParameters {
|
||||||
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
|
constructor({ context, visibleRect, desiredAtlasScale, zoomLevel, root }) {
|
||||||
/** @type {CanvasRenderingContext2D} */
|
/** @type {CanvasRenderingContext2D} */
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
/** @type {Rectangle} */
|
/** @type {Rectangle} */
|
||||||
this.visibleRect = visibleRect;
|
this.visibleRect = visibleRect;
|
||||||
|
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
this.desiredAtlasScale = desiredAtlasScale;
|
this.desiredAtlasScale = desiredAtlasScale;
|
||||||
|
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this.zoomLevel = zoomLevel;
|
this.zoomLevel = zoomLevel;
|
||||||
|
|
||||||
// FIXME: Not really nice
|
/** @type {GameRoot} */
|
||||||
/** @type {GameRoot} */
|
this.root = root;
|
||||||
this.root = root;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ export class RestrictionManager extends ReadWriteProxy {
|
||||||
* @param {any} data
|
* @param {any} data
|
||||||
*/
|
*/
|
||||||
migrate(data) {
|
migrate(data) {
|
||||||
// Todo
|
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ const variantsCache = new Map();
|
||||||
export function registerBuildingVariant(
|
export function registerBuildingVariant(
|
||||||
code,
|
code,
|
||||||
meta,
|
meta,
|
||||||
variant = "default" /* FIXME: Circular dependency, actually its defaultBuildingVariant */,
|
variant = "default" /* @TODO: Circular dependency, actually its defaultBuildingVariant */,
|
||||||
rotationVariant = 0
|
rotationVariant = 0
|
||||||
) {
|
) {
|
||||||
assert(!gBuildingVariants[code], "Duplicate id: " + code);
|
assert(!gBuildingVariants[code], "Duplicate id: " + code);
|
||||||
|
|
|
@ -191,7 +191,6 @@ export class MetaUndergroundBeltBuilding extends MetaBuilding {
|
||||||
) {
|
) {
|
||||||
tile = tile.addScalars(searchVector.x, searchVector.y);
|
tile = tile.addScalars(searchVector.x, searchVector.y);
|
||||||
|
|
||||||
/* WIRES: FIXME */
|
|
||||||
const contents = root.map.getTileContent(tile, "regular");
|
const contents = root.map.getTileContent(tile, "regular");
|
||||||
if (contents) {
|
if (contents) {
|
||||||
const undergroundComp = contents.components.UndergroundBelt;
|
const undergroundComp = contents.components.UndergroundBelt;
|
||||||
|
|
|
@ -182,23 +182,6 @@ export class EntityManager extends BasicSerializableObject {
|
||||||
return this.componentToEntity[componentHandle.getId()] || [];
|
return this.componentToEntity[componentHandle.getId()] || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all of a given class. This is SLOW!
|
|
||||||
* @param {object} entityClass
|
|
||||||
* @returns {Array<Entity>} entities
|
|
||||||
*/
|
|
||||||
getAllOfClass(entityClass) {
|
|
||||||
// FIXME: Slow
|
|
||||||
const result = [];
|
|
||||||
for (let i = 0; i < this.entities.length; ++i) {
|
|
||||||
const entity = this.entities[i];
|
|
||||||
if (entity instanceof entityClass) {
|
|
||||||
result.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters all components of an entity from the component to entity mapping
|
* Unregisters all components of an entity from the component to entity mapping
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { ShapeItem } from "../../items/shape_item";
|
||||||
import { WireComponent } from "../../components/wire";
|
import { WireComponent } from "../../components/wire";
|
||||||
import { LeverComponent } from "../../components/lever";
|
import { LeverComponent } from "../../components/lever";
|
||||||
|
|
||||||
|
// @todo: Make dictionary
|
||||||
const tutorialsByLevel = [
|
const tutorialsByLevel = [
|
||||||
// Level 1
|
// Level 1
|
||||||
[
|
[
|
||||||
|
|
|
@ -225,8 +225,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||||
*/
|
*/
|
||||||
tryPassOverItem(item, receiver, slotIndex) {
|
tryPassOverItem(item, receiver, slotIndex) {
|
||||||
// Try figuring out how what to do with the item
|
// Try figuring out how what to do with the item
|
||||||
// TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
|
// @TODO: Kinda hacky. How to solve this properly? Don't want to go through inheritance hell.
|
||||||
// Also its just a few cases (hope it stays like this .. :x).
|
|
||||||
|
|
||||||
const beltComp = receiver.components.Belt;
|
const beltComp = receiver.components.Belt;
|
||||||
if (beltComp) {
|
if (beltComp) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const enumHubGoalRewardsToContentUnlocked = {
|
||||||
]),
|
]),
|
||||||
[enumHubGoalRewards.reward_logic_gates]: typed([[MetaLogicGateBuilding, defaultBuildingVariant]]),
|
[enumHubGoalRewards.reward_logic_gates]: typed([[MetaLogicGateBuilding, defaultBuildingVariant]]),
|
||||||
[enumHubGoalRewards.reward_filter]: typed([[MetaFilterBuilding, defaultBuildingVariant]]),
|
[enumHubGoalRewards.reward_filter]: typed([[MetaFilterBuilding, defaultBuildingVariant]]),
|
||||||
[enumHubGoalRewards.reward_virtual_processing]: null, // @TODO!
|
[enumHubGoalRewards.reward_virtual_processing]: null,
|
||||||
|
|
||||||
[enumHubGoalRewards.reward_wires_painter_and_levers]: typed([
|
[enumHubGoalRewards.reward_wires_painter_and_levers]: typed([
|
||||||
[MetaPainterBuilding, enumPainterVariants.quad],
|
[MetaPainterBuilding, enumPainterVariants.quad],
|
||||||
|
|
|
@ -41,7 +41,7 @@ export class SavegameManager extends ReadWriteProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(data) {
|
verify(data) {
|
||||||
// TODO / FIXME!!!!
|
// @TODO
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,6 @@ export class PreloadState extends GameState {
|
||||||
<div class="loadingImage"></div>
|
<div class="loadingImage"></div>
|
||||||
<div class="loadingStatus">
|
<div class="loadingStatus">
|
||||||
<span class="desc">Booting</span>
|
<span class="desc">Booting</span>
|
||||||
<span class="bar">
|
|
||||||
<span class="inner" style="width: 0%"></span>
|
|
||||||
<span class="status">0%</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="prefab_GameHint"></span>
|
<span class="prefab_GameHint"></span>
|
||||||
|
@ -56,10 +52,6 @@ export class PreloadState extends GameState {
|
||||||
|
|
||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
this.statusText = this.htmlElement.querySelector(".loadingStatus > .desc");
|
this.statusText = this.htmlElement.querySelector(".loadingStatus > .desc");
|
||||||
/** @type {HTMLElement} */
|
|
||||||
this.statusBar = this.htmlElement.querySelector(".loadingStatus > .bar > .inner");
|
|
||||||
/** @type {HTMLElement} */
|
|
||||||
this.statusBarText = this.htmlElement.querySelector(".loadingStatus > .bar > .status");
|
|
||||||
|
|
||||||
/** @type {HTMLElement} */
|
/** @type {HTMLElement} */
|
||||||
this.hintsText = this.htmlElement.querySelector(".prefab_GameHint");
|
this.hintsText = this.htmlElement.querySelector(".prefab_GameHint");
|
||||||
|
@ -67,7 +59,6 @@ export class PreloadState extends GameState {
|
||||||
this.nextHintDuration = 0;
|
this.nextHintDuration = 0;
|
||||||
|
|
||||||
this.currentStatus = "booting";
|
this.currentStatus = "booting";
|
||||||
this.currentIndex = 0;
|
|
||||||
|
|
||||||
this.startLoading();
|
this.startLoading();
|
||||||
}
|
}
|
||||||
|
@ -259,16 +250,8 @@ export class PreloadState extends GameState {
|
||||||
*/
|
*/
|
||||||
setStatus(text) {
|
setStatus(text) {
|
||||||
logger.log("✅ " + text);
|
logger.log("✅ " + text);
|
||||||
this.currentIndex += 1;
|
|
||||||
this.currentStatus = text;
|
this.currentStatus = text;
|
||||||
this.statusText.innerText = text;
|
this.statusText.innerText = text;
|
||||||
|
|
||||||
const numSteps = 10; // FIXME
|
|
||||||
|
|
||||||
const percentage = (this.currentIndex / numSteps) * 100.0;
|
|
||||||
this.statusBar.style.width = percentage + "%";
|
|
||||||
this.statusBarText.innerText = findNiceValue(percentage) + "%";
|
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ function match(originalObj, translatedObj, path = "/") {
|
||||||
if (typeof valueOriginal === "object") {
|
if (typeof valueOriginal === "object") {
|
||||||
match(valueOriginal, valueMatching, path + key + "/");
|
match(valueOriginal, valueMatching, path + key + "/");
|
||||||
} else if (typeof valueOriginal === "string") {
|
} else if (typeof valueOriginal === "string") {
|
||||||
// todo
|
// @todo
|
||||||
const originalPlaceholders = matchAll(valueOriginal, placeholderRegexp).toArray();
|
const originalPlaceholders = matchAll(valueOriginal, placeholderRegexp).toArray();
|
||||||
const translatedPlaceholders = matchAll(valueMatching, placeholderRegexp).toArray();
|
const translatedPlaceholders = matchAll(valueMatching, placeholderRegexp).toArray();
|
||||||
|
|
||||||
|
|
Reference in New Issue