mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Allow to pan the map with the mouse by moving the cursor to the edges of the screen
This commit is contained in:
parent
d6f56da9e4
commit
fbff0a0ad4
@ -1,408 +1,411 @@
|
|||||||
import { AnimationFrame } from "./core/animation_frame";
|
import { AnimationFrame } from "./core/animation_frame";
|
||||||
import { BackgroundResourcesLoader } from "./core/background_resources_loader";
|
import { BackgroundResourcesLoader } from "./core/background_resources_loader";
|
||||||
import { IS_MOBILE } from "./core/config";
|
import { IS_MOBILE } from "./core/config";
|
||||||
import { GameState } from "./core/game_state";
|
import { GameState } from "./core/game_state";
|
||||||
import { GLOBAL_APP, setGlobalApp } from "./core/globals";
|
import { GLOBAL_APP, setGlobalApp } from "./core/globals";
|
||||||
import { InputDistributor } from "./core/input_distributor";
|
import { InputDistributor } from "./core/input_distributor";
|
||||||
import { Loader } from "./core/loader";
|
import { Loader } from "./core/loader";
|
||||||
import { createLogger, logSection } from "./core/logging";
|
import { createLogger, logSection } from "./core/logging";
|
||||||
import { StateManager } from "./core/state_manager";
|
import { StateManager } from "./core/state_manager";
|
||||||
import { TrackedState } from "./core/tracked_state";
|
import { TrackedState } from "./core/tracked_state";
|
||||||
import { getPlatformName, waitNextFrame } from "./core/utils";
|
import { getPlatformName, waitNextFrame } from "./core/utils";
|
||||||
import { Vector } from "./core/vector";
|
import { Vector } from "./core/vector";
|
||||||
import { AdProviderInterface } from "./platform/ad_provider";
|
import { AdProviderInterface } from "./platform/ad_provider";
|
||||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||||
import { AnalyticsInterface } from "./platform/analytics";
|
import { AnalyticsInterface } from "./platform/analytics";
|
||||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||||
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
||||||
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
|
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
|
||||||
import { PlatformWrapperInterface } from "./platform/wrapper";
|
import { PlatformWrapperInterface } from "./platform/wrapper";
|
||||||
import { ApplicationSettings } from "./profile/application_settings";
|
import { ApplicationSettings } from "./profile/application_settings";
|
||||||
import { SavegameManager } from "./savegame/savegame_manager";
|
import { SavegameManager } from "./savegame/savegame_manager";
|
||||||
import { AboutState } from "./states/about";
|
import { AboutState } from "./states/about";
|
||||||
import { ChangelogState } from "./states/changelog";
|
import { ChangelogState } from "./states/changelog";
|
||||||
import { InGameState } from "./states/ingame";
|
import { InGameState } from "./states/ingame";
|
||||||
import { KeybindingsState } from "./states/keybindings";
|
import { KeybindingsState } from "./states/keybindings";
|
||||||
import { MainMenuState } from "./states/main_menu";
|
import { MainMenuState } from "./states/main_menu";
|
||||||
import { MobileWarningState } from "./states/mobile_warning";
|
import { MobileWarningState } from "./states/mobile_warning";
|
||||||
import { PreloadState } from "./states/preload";
|
import { PreloadState } from "./states/preload";
|
||||||
import { SettingsState } from "./states/settings";
|
import { SettingsState } from "./states/settings";
|
||||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||||
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||||
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const logger = createLogger("application");
|
const logger = createLogger("application");
|
||||||
|
|
||||||
// Set the name of the hidden property and the change event for visibility
|
// Set the name of the hidden property and the change event for visibility
|
||||||
let pageHiddenPropName, pageVisibilityEventName;
|
let pageHiddenPropName, pageVisibilityEventName;
|
||||||
if (typeof document.hidden !== "undefined") {
|
if (typeof document.hidden !== "undefined") {
|
||||||
// Opera 12.10 and Firefox 18 and later support
|
// Opera 12.10 and Firefox 18 and later support
|
||||||
pageHiddenPropName = "hidden";
|
pageHiddenPropName = "hidden";
|
||||||
pageVisibilityEventName = "visibilitychange";
|
pageVisibilityEventName = "visibilitychange";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} else if (typeof document.msHidden !== "undefined") {
|
} else if (typeof document.msHidden !== "undefined") {
|
||||||
pageHiddenPropName = "msHidden";
|
pageHiddenPropName = "msHidden";
|
||||||
pageVisibilityEventName = "msvisibilitychange";
|
pageVisibilityEventName = "msvisibilitychange";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} else if (typeof document.webkitHidden !== "undefined") {
|
} else if (typeof document.webkitHidden !== "undefined") {
|
||||||
pageHiddenPropName = "webkitHidden";
|
pageHiddenPropName = "webkitHidden";
|
||||||
pageVisibilityEventName = "webkitvisibilitychange";
|
pageVisibilityEventName = "webkitvisibilitychange";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Application {
|
export class Application {
|
||||||
constructor() {
|
constructor() {
|
||||||
assert(!GLOBAL_APP, "Tried to construct application twice");
|
assert(!GLOBAL_APP, "Tried to construct application twice");
|
||||||
logger.log("Creating application, platform =", getPlatformName());
|
logger.log("Creating application, platform =", getPlatformName());
|
||||||
setGlobalApp(this);
|
setGlobalApp(this);
|
||||||
|
|
||||||
this.unloaded = false;
|
this.unloaded = false;
|
||||||
|
|
||||||
// Global stuff
|
// Global stuff
|
||||||
this.settings = new ApplicationSettings(this);
|
this.settings = new ApplicationSettings(this);
|
||||||
this.ticker = new AnimationFrame();
|
this.ticker = new AnimationFrame();
|
||||||
this.stateMgr = new StateManager(this);
|
this.stateMgr = new StateManager(this);
|
||||||
this.savegameMgr = new SavegameManager(this);
|
this.savegameMgr = new SavegameManager(this);
|
||||||
this.inputMgr = new InputDistributor(this);
|
this.inputMgr = new InputDistributor(this);
|
||||||
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
||||||
|
|
||||||
// Platform dependent stuff
|
// Platform dependent stuff
|
||||||
|
|
||||||
/** @type {StorageInterface} */
|
/** @type {StorageInterface} */
|
||||||
this.storage = null;
|
this.storage = null;
|
||||||
|
|
||||||
/** @type {SoundInterface} */
|
/** @type {SoundInterface} */
|
||||||
this.sound = null;
|
this.sound = null;
|
||||||
|
|
||||||
/** @type {PlatformWrapperInterface} */
|
/** @type {PlatformWrapperInterface} */
|
||||||
this.platformWrapper = null;
|
this.platformWrapper = null;
|
||||||
|
|
||||||
/** @type {AdProviderInterface} */
|
/** @type {AdProviderInterface} */
|
||||||
this.adProvider = null;
|
this.adProvider = null;
|
||||||
|
|
||||||
/** @type {AnalyticsInterface} */
|
/** @type {AnalyticsInterface} */
|
||||||
this.analytics = null;
|
this.analytics = null;
|
||||||
|
|
||||||
/** @type {GameAnalyticsInterface} */
|
/** @type {GameAnalyticsInterface} */
|
||||||
this.gameAnalytics = null;
|
this.gameAnalytics = null;
|
||||||
|
|
||||||
this.initPlatformDependentInstances();
|
this.initPlatformDependentInstances();
|
||||||
|
|
||||||
// Track if the window is focused (only relevant for browser)
|
// Track if the window is focused (only relevant for browser)
|
||||||
this.focused = true;
|
this.focused = true;
|
||||||
|
|
||||||
// Track if the window is visible
|
// Track if the window is visible
|
||||||
this.pageVisible = true;
|
this.pageVisible = true;
|
||||||
|
|
||||||
// Track if the app is paused (cordova)
|
// Track if the app is paused (cordova)
|
||||||
this.applicationPaused = false;
|
this.applicationPaused = false;
|
||||||
|
|
||||||
/** @type {TypedTrackedState<boolean>} */
|
/** @type {TypedTrackedState<boolean>} */
|
||||||
this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this);
|
this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this);
|
||||||
|
|
||||||
// Dimensions
|
// Dimensions
|
||||||
this.screenWidth = 0;
|
this.screenWidth = 0;
|
||||||
this.screenHeight = 0;
|
this.screenHeight = 0;
|
||||||
|
|
||||||
// Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova
|
// Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova
|
||||||
this.lastResizeCheck = null;
|
this.lastResizeCheck = null;
|
||||||
|
|
||||||
// Store the mouse position, or null if not available
|
// Store the mouse position, or null if not available
|
||||||
/** @type {Vector|null} */
|
/** @type {Vector|null} */
|
||||||
this.mousePosition = null;
|
this.mousePosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all platform instances
|
* Initializes all platform instances
|
||||||
*/
|
*/
|
||||||
initPlatformDependentInstances() {
|
initPlatformDependentInstances() {
|
||||||
logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")");
|
logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")");
|
||||||
|
|
||||||
if (G_IS_STANDALONE) {
|
if (G_IS_STANDALONE) {
|
||||||
this.platformWrapper = new PlatformWrapperImplElectron(this);
|
this.platformWrapper = new PlatformWrapperImplElectron(this);
|
||||||
} else {
|
} else {
|
||||||
this.platformWrapper = new PlatformWrapperImplBrowser(this);
|
this.platformWrapper = new PlatformWrapperImplBrowser(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with empty ad provider
|
// Start with empty ad provider
|
||||||
this.adProvider = new NoAdProvider(this);
|
this.adProvider = new NoAdProvider(this);
|
||||||
this.sound = new SoundImplBrowser(this);
|
this.sound = new SoundImplBrowser(this);
|
||||||
this.analytics = new GoogleAnalyticsImpl(this);
|
this.analytics = new GoogleAnalyticsImpl(this);
|
||||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers all game states
|
* Registers all game states
|
||||||
*/
|
*/
|
||||||
registerStates() {
|
registerStates() {
|
||||||
/** @type {Array<typeof GameState>} */
|
/** @type {Array<typeof GameState>} */
|
||||||
const states = [
|
const states = [
|
||||||
PreloadState,
|
PreloadState,
|
||||||
MobileWarningState,
|
MobileWarningState,
|
||||||
MainMenuState,
|
MainMenuState,
|
||||||
InGameState,
|
InGameState,
|
||||||
SettingsState,
|
SettingsState,
|
||||||
KeybindingsState,
|
KeybindingsState,
|
||||||
AboutState,
|
AboutState,
|
||||||
ChangelogState,
|
ChangelogState,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i = 0; i < states.length; ++i) {
|
for (let i = 0; i < states.length; ++i) {
|
||||||
this.stateMgr.register(states[i]);
|
this.stateMgr.register(states[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers all event listeners
|
* Registers all event listeners
|
||||||
*/
|
*/
|
||||||
registerEventListeners() {
|
registerEventListeners() {
|
||||||
window.addEventListener("focus", this.onFocus.bind(this));
|
window.addEventListener("focus", this.onFocus.bind(this));
|
||||||
window.addEventListener("blur", this.onBlur.bind(this));
|
window.addEventListener("blur", this.onBlur.bind(this));
|
||||||
|
|
||||||
window.addEventListener("resize", () => this.checkResize(), true);
|
window.addEventListener("resize", () => this.checkResize(), true);
|
||||||
window.addEventListener("orientationchange", () => this.checkResize(), true);
|
window.addEventListener("orientationchange", () => this.checkResize(), true);
|
||||||
|
|
||||||
if (!G_IS_MOBILE_APP && !IS_MOBILE) {
|
if (!G_IS_MOBILE_APP && !IS_MOBILE) {
|
||||||
window.addEventListener("mousemove", this.handleMousemove.bind(this));
|
window.addEventListener("mousemove", this.handleMousemove.bind(this));
|
||||||
}
|
window.addEventListener("mouseout", this.handleMousemove.bind(this));
|
||||||
|
window.addEventListener("mouseover", this.handleMousemove.bind(this));
|
||||||
// Unload events
|
window.addEventListener("mouseleave", this.handleMousemove.bind(this));
|
||||||
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
|
}
|
||||||
window.addEventListener("unload", this.onUnload.bind(this), true);
|
|
||||||
|
// Unload events
|
||||||
document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false);
|
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
|
||||||
|
window.addEventListener("unload", this.onUnload.bind(this), true);
|
||||||
// Track touches so we can update the focus appropriately
|
|
||||||
document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
|
document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false);
|
||||||
document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true);
|
|
||||||
}
|
// Track touches so we can update the focus appropriately
|
||||||
|
document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||||
/**
|
document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||||
* Checks the focus after a touch
|
}
|
||||||
* @param {TouchEvent} event
|
|
||||||
*/
|
/**
|
||||||
updateFocusAfterUserInteraction(event) {
|
* Checks the focus after a touch
|
||||||
const target = /** @type {HTMLElement} */ (event.target);
|
* @param {TouchEvent} event
|
||||||
if (!target || !target.tagName) {
|
*/
|
||||||
// Safety check
|
updateFocusAfterUserInteraction(event) {
|
||||||
logger.warn("Invalid touchstart/touchend event:", event);
|
const target = /** @type {HTMLElement} */ (event.target);
|
||||||
return;
|
if (!target || !target.tagName) {
|
||||||
}
|
// Safety check
|
||||||
|
logger.warn("Invalid touchstart/touchend event:", event);
|
||||||
// When clicking an element which is not the currently focused one, defocus it
|
return;
|
||||||
if (target !== document.activeElement) {
|
}
|
||||||
// @ts-ignore
|
|
||||||
if (document.activeElement.blur) {
|
// When clicking an element which is not the currently focused one, defocus it
|
||||||
// @ts-ignore
|
if (target !== document.activeElement) {
|
||||||
document.activeElement.blur();
|
// @ts-ignore
|
||||||
}
|
if (document.activeElement.blur) {
|
||||||
}
|
// @ts-ignore
|
||||||
|
document.activeElement.blur();
|
||||||
// If we click an input field, focus it now
|
}
|
||||||
if (target.tagName.toLowerCase() === "input") {
|
}
|
||||||
// We *really* need the focus
|
|
||||||
waitNextFrame().then(() => target.focus());
|
// If we click an input field, focus it now
|
||||||
}
|
if (target.tagName.toLowerCase() === "input") {
|
||||||
}
|
// We *really* need the focus
|
||||||
|
waitNextFrame().then(() => target.focus());
|
||||||
/**
|
}
|
||||||
* Handles a page visibility change event
|
}
|
||||||
* @param {Event} event
|
|
||||||
*/
|
/**
|
||||||
handleVisibilityChange(event) {
|
* Handles a page visibility change event
|
||||||
window.focus();
|
* @param {Event} event
|
||||||
const pageVisible = !document[pageHiddenPropName];
|
*/
|
||||||
if (pageVisible !== this.pageVisible) {
|
handleVisibilityChange(event) {
|
||||||
this.pageVisible = pageVisible;
|
window.focus();
|
||||||
logger.log("Visibility changed:", this.pageVisible);
|
const pageVisible = !document[pageHiddenPropName];
|
||||||
this.trackedIsRenderable.set(this.isRenderable());
|
if (pageVisible !== this.pageVisible) {
|
||||||
}
|
this.pageVisible = pageVisible;
|
||||||
}
|
logger.log("Visibility changed:", this.pageVisible);
|
||||||
|
this.trackedIsRenderable.set(this.isRenderable());
|
||||||
/**
|
}
|
||||||
* Handles a mouse move event
|
}
|
||||||
* @param {MouseEvent} event
|
|
||||||
*/
|
/**
|
||||||
handleMousemove(event) {
|
* Handles a mouse move event
|
||||||
this.mousePosition = new Vector(event.clientX, event.clientY);
|
* @param {MouseEvent} event
|
||||||
}
|
*/
|
||||||
|
handleMousemove(event) {
|
||||||
/**
|
this.mousePosition = new Vector(event.clientX, event.clientY);
|
||||||
* Internal on focus handler
|
}
|
||||||
*/
|
|
||||||
onFocus() {
|
/**
|
||||||
this.focused = true;
|
* Internal on focus handler
|
||||||
}
|
*/
|
||||||
|
onFocus() {
|
||||||
/**
|
this.focused = true;
|
||||||
* Internal blur handler
|
}
|
||||||
*/
|
|
||||||
onBlur() {
|
/**
|
||||||
this.focused = false;
|
* Internal blur handler
|
||||||
}
|
*/
|
||||||
|
onBlur() {
|
||||||
/**
|
this.focused = false;
|
||||||
* Returns if the app is currently visible
|
}
|
||||||
*/
|
|
||||||
isRenderable() {
|
/**
|
||||||
return !this.applicationPaused && this.pageVisible;
|
* Returns if the app is currently visible
|
||||||
}
|
*/
|
||||||
|
isRenderable() {
|
||||||
onAppRenderableStateChanged(renderable) {
|
return !this.applicationPaused && this.pageVisible;
|
||||||
logger.log("Application renderable:", renderable);
|
}
|
||||||
window.focus();
|
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
onAppRenderableStateChanged(renderable) {
|
||||||
if (!renderable) {
|
logger.log("Application renderable:", renderable);
|
||||||
if (currentState) {
|
window.focus();
|
||||||
currentState.onAppPause();
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
}
|
if (!renderable) {
|
||||||
} else {
|
if (currentState) {
|
||||||
if (currentState) {
|
currentState.onAppPause();
|
||||||
currentState.onAppResume();
|
}
|
||||||
}
|
} else {
|
||||||
this.checkResize();
|
if (currentState) {
|
||||||
}
|
currentState.onAppResume();
|
||||||
|
}
|
||||||
this.sound.onPageRenderableStateChanged(renderable);
|
this.checkResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
this.sound.onPageRenderableStateChanged(renderable);
|
||||||
* Internal unload handler
|
}
|
||||||
*/
|
|
||||||
onUnload(event) {
|
/**
|
||||||
if (!this.unloaded) {
|
* Internal unload handler
|
||||||
logSection("UNLOAD HANDLER", "#f77");
|
*/
|
||||||
this.unloaded = true;
|
onUnload(event) {
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
if (!this.unloaded) {
|
||||||
if (currentState) {
|
logSection("UNLOAD HANDLER", "#f77");
|
||||||
currentState.onBeforeExit();
|
this.unloaded = true;
|
||||||
}
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
this.deinitialize();
|
if (currentState) {
|
||||||
}
|
currentState.onBeforeExit();
|
||||||
}
|
}
|
||||||
|
this.deinitialize();
|
||||||
/**
|
}
|
||||||
* Internal before-unload handler
|
}
|
||||||
*/
|
|
||||||
onBeforeUnload(event) {
|
/**
|
||||||
logSection("BEFORE UNLOAD HANDLER", "#f77");
|
* Internal before-unload handler
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
*/
|
||||||
|
onBeforeUnload(event) {
|
||||||
if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) {
|
logSection("BEFORE UNLOAD HANDLER", "#f77");
|
||||||
if (!G_IS_STANDALONE) {
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
// Need to show a "Are you sure you want to exit"
|
|
||||||
event.preventDefault();
|
if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) {
|
||||||
event.returnValue = "Are you sure you want to exit?";
|
if (!G_IS_STANDALONE) {
|
||||||
}
|
// Need to show a "Are you sure you want to exit"
|
||||||
}
|
event.preventDefault();
|
||||||
}
|
event.returnValue = "Are you sure you want to exit?";
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Boots the application
|
}
|
||||||
*/
|
|
||||||
boot() {
|
/**
|
||||||
console.log("Booting ...");
|
* Boots the application
|
||||||
this.registerStates();
|
*/
|
||||||
this.registerEventListeners();
|
boot() {
|
||||||
|
console.log("Booting ...");
|
||||||
Loader.linkAppAfterBoot(this);
|
this.registerStates();
|
||||||
|
this.registerEventListeners();
|
||||||
// Check for mobile
|
|
||||||
if (IS_MOBILE) {
|
Loader.linkAppAfterBoot(this);
|
||||||
this.stateMgr.moveToState("MobileWarningState");
|
|
||||||
} else {
|
// Check for mobile
|
||||||
this.stateMgr.moveToState("PreloadState");
|
if (IS_MOBILE) {
|
||||||
}
|
this.stateMgr.moveToState("MobileWarningState");
|
||||||
|
} else {
|
||||||
// Starting rendering
|
this.stateMgr.moveToState("PreloadState");
|
||||||
this.ticker.frameEmitted.add(this.onFrameEmitted, this);
|
}
|
||||||
this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this);
|
|
||||||
this.ticker.start();
|
// Starting rendering
|
||||||
|
this.ticker.frameEmitted.add(this.onFrameEmitted, this);
|
||||||
window.focus();
|
this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this);
|
||||||
}
|
this.ticker.start();
|
||||||
|
|
||||||
/**
|
window.focus();
|
||||||
* Deinitializes the application
|
}
|
||||||
*/
|
|
||||||
deinitialize() {
|
/**
|
||||||
return this.sound.deinitialize();
|
* Deinitializes the application
|
||||||
}
|
*/
|
||||||
|
deinitialize() {
|
||||||
/**
|
return this.sound.deinitialize();
|
||||||
* Background frame update callback
|
}
|
||||||
* @param {number} dt
|
|
||||||
*/
|
/**
|
||||||
onBackgroundFrame(dt) {
|
* Background frame update callback
|
||||||
if (this.isRenderable()) {
|
* @param {number} dt
|
||||||
return;
|
*/
|
||||||
}
|
onBackgroundFrame(dt) {
|
||||||
|
if (this.isRenderable()) {
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
return;
|
||||||
if (currentState) {
|
}
|
||||||
currentState.onBackgroundTick(dt);
|
|
||||||
}
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
}
|
if (currentState) {
|
||||||
|
currentState.onBackgroundTick(dt);
|
||||||
/**
|
}
|
||||||
* Frame update callback
|
}
|
||||||
* @param {number} dt
|
|
||||||
*/
|
/**
|
||||||
onFrameEmitted(dt) {
|
* Frame update callback
|
||||||
if (!this.isRenderable()) {
|
* @param {number} dt
|
||||||
return;
|
*/
|
||||||
}
|
onFrameEmitted(dt) {
|
||||||
|
if (!this.isRenderable()) {
|
||||||
const time = performance.now();
|
return;
|
||||||
|
}
|
||||||
// Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!)
|
|
||||||
if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
|
const time = performance.now();
|
||||||
this.checkResize();
|
|
||||||
this.lastResizeCheck = time;
|
// Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!)
|
||||||
}
|
if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
|
||||||
|
this.checkResize();
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
this.lastResizeCheck = time;
|
||||||
if (currentState) {
|
}
|
||||||
currentState.onRender(dt);
|
|
||||||
}
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
}
|
if (currentState) {
|
||||||
|
currentState.onRender(dt);
|
||||||
/**
|
}
|
||||||
* Checks if the app resized. Only does this once in a while
|
}
|
||||||
* @param {boolean} forceUpdate Forced update of the dimensions
|
|
||||||
*/
|
/**
|
||||||
checkResize(forceUpdate = false) {
|
* Checks if the app resized. Only does this once in a while
|
||||||
const w = window.innerWidth;
|
* @param {boolean} forceUpdate Forced update of the dimensions
|
||||||
const h = window.innerHeight;
|
*/
|
||||||
if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
|
checkResize(forceUpdate = false) {
|
||||||
this.screenWidth = w;
|
const w = window.innerWidth;
|
||||||
this.screenHeight = h;
|
const h = window.innerHeight;
|
||||||
const currentState = this.stateMgr.getCurrentState();
|
if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
|
||||||
if (currentState) {
|
this.screenWidth = w;
|
||||||
currentState.onResized(this.screenWidth, this.screenHeight);
|
this.screenHeight = h;
|
||||||
}
|
const currentState = this.stateMgr.getCurrentState();
|
||||||
|
if (currentState) {
|
||||||
const scale = this.getEffectiveUiScale();
|
currentState.onResized(this.screenWidth, this.screenHeight);
|
||||||
waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`));
|
}
|
||||||
window.focus();
|
|
||||||
}
|
const scale = this.getEffectiveUiScale();
|
||||||
}
|
waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`));
|
||||||
|
window.focus();
|
||||||
/**
|
}
|
||||||
* Returns the effective ui sclae
|
}
|
||||||
*/
|
|
||||||
getEffectiveUiScale() {
|
/**
|
||||||
return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue();
|
* Returns the effective ui sclae
|
||||||
}
|
*/
|
||||||
|
getEffectiveUiScale() {
|
||||||
/**
|
return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue();
|
||||||
* Callback after ui scale has changed
|
}
|
||||||
*/
|
|
||||||
updateAfterUiScaleChanged() {
|
/**
|
||||||
this.checkResize(true);
|
* Callback after ui scale has changed
|
||||||
}
|
*/
|
||||||
}
|
updateAfterUiScaleChanged() {
|
||||||
|
this.checkResize(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ export const CHANGELOG = [
|
|||||||
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
||||||
"Added confirmation when deleting a savegame",
|
"Added confirmation when deleting a savegame",
|
||||||
"Fixed tunnels entrances connecting to exits sometimes when they shouldn't",
|
"Fixed tunnels entrances connecting to exits sometimes when they shouldn't",
|
||||||
|
"You can now pan the map with your mouse by moving the cursor to the edges of the screen!",
|
||||||
"Added setting to auto select the extractor when pipetting a resource patch (by Exund)",
|
"Added setting to auto select the extractor when pipetting a resource patch (by Exund)",
|
||||||
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
||||||
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
||||||
|
@ -769,6 +769,7 @@ export class Camera extends BasicSerializableObject {
|
|||||||
this.cameraUpdateTimeBucket -= physicsStepSizeMs;
|
this.cameraUpdateTimeBucket -= physicsStepSizeMs;
|
||||||
|
|
||||||
this.internalUpdatePanning(now, physicsStepSizeMs);
|
this.internalUpdatePanning(now, physicsStepSizeMs);
|
||||||
|
this.internalUpdateMousePanning(now, physicsStepSizeMs);
|
||||||
this.internalUpdateZooming(now, physicsStepSizeMs);
|
this.internalUpdateZooming(now, physicsStepSizeMs);
|
||||||
this.internalUpdateCentering(now, physicsStepSizeMs);
|
this.internalUpdateCentering(now, physicsStepSizeMs);
|
||||||
this.internalUpdateShake(now, physicsStepSizeMs);
|
this.internalUpdateShake(now, physicsStepSizeMs);
|
||||||
@ -855,6 +856,61 @@ export class Camera extends BasicSerializableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal screen panning handler
|
||||||
|
* @param {number} now
|
||||||
|
* @param {number} dt
|
||||||
|
*/
|
||||||
|
internalUpdateMousePanning(now, dt) {
|
||||||
|
if (!this.root.app.settings.getAllSettings().enableMousePan) {
|
||||||
|
// Not enabled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mousePos = this.root.app.mousePosition;
|
||||||
|
if (!mousePos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.desiredCenter || this.desiredZoom || this.currentlyMoving || this.currentlyPinching) {
|
||||||
|
// Performing another method of movement right now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
mousePos.x < 0 ||
|
||||||
|
mousePos.y < 0 ||
|
||||||
|
mousePos.x > this.root.gameWidth ||
|
||||||
|
mousePos.y > this.root.gameHeight
|
||||||
|
) {
|
||||||
|
// Out of screen
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const panAreaPixels = Math.min(this.root.gameWidth, this.root.gameHeight) * 0.015;
|
||||||
|
|
||||||
|
const panVelocity = new Vector();
|
||||||
|
if (mousePos.x < panAreaPixels) {
|
||||||
|
panVelocity.x -= 1;
|
||||||
|
}
|
||||||
|
if (mousePos.x > this.root.gameWidth - panAreaPixels) {
|
||||||
|
panVelocity.x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mousePos.y < panAreaPixels) {
|
||||||
|
panVelocity.y -= 1;
|
||||||
|
}
|
||||||
|
if (mousePos.y > this.root.gameHeight - panAreaPixels) {
|
||||||
|
panVelocity.y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.center = this.center.add(
|
||||||
|
panVelocity.multiplyScalar(
|
||||||
|
((0.5 * dt) / this.zoomLevel) * this.root.app.settings.getMovementSpeed()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the non user interaction zooming
|
* Updates the non user interaction zooming
|
||||||
* @param {number} now Time now in seconds
|
* @param {number} now Time now in seconds
|
||||||
|
@ -253,6 +253,7 @@ export const allApplicationSettings = [
|
|||||||
changeCb: (app, id) => {},
|
changeCb: (app, id) => {},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
new BoolSetting("enableMousePan", enumCategories.advanced, (app, value) => {}),
|
||||||
new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}),
|
new BoolSetting("alwaysMultiplace", enumCategories.advanced, (app, value) => {}),
|
||||||
new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}),
|
new BoolSetting("clearCursorOnDeleteWhilePlacing", enumCategories.advanced, (app, value) => {}),
|
||||||
new BoolSetting("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}),
|
new BoolSetting("enableTunnelSmartplace", enumCategories.advanced, (app, value) => {}),
|
||||||
@ -308,6 +309,7 @@ class SettingsStorage {
|
|||||||
this.clearCursorOnDeleteWhilePlacing = true;
|
this.clearCursorOnDeleteWhilePlacing = true;
|
||||||
this.displayChunkBorders = false;
|
this.displayChunkBorders = false;
|
||||||
this.pickMinerOnPatch = true;
|
this.pickMinerOnPatch = true;
|
||||||
|
this.enableMousePan = true;
|
||||||
|
|
||||||
this.enableColorBlindHelper = false;
|
this.enableColorBlindHelper = false;
|
||||||
|
|
||||||
@ -525,7 +527,7 @@ export class ApplicationSettings extends ReadWriteProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCurrentVersion() {
|
getCurrentVersion() {
|
||||||
return 27;
|
return 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {{settings: SettingsStorage, version: number}} data */
|
/** @param {{settings: SettingsStorage, version: number}} data */
|
||||||
@ -653,6 +655,11 @@ export class ApplicationSettings extends ReadWriteProxy {
|
|||||||
data.version = 27;
|
data.version = 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.version < 28) {
|
||||||
|
data.settings.enableMousePan = true;
|
||||||
|
data.version = 28;
|
||||||
|
}
|
||||||
|
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -785,7 +785,7 @@ settings:
|
|||||||
movementSpeed:
|
movementSpeed:
|
||||||
title: Movement speed
|
title: Movement speed
|
||||||
description: >-
|
description: >-
|
||||||
Changes how fast the view moves when using the keyboard.
|
Changes how fast the view moves when using the keyboard or moving the mouse to the screen borders.
|
||||||
speeds:
|
speeds:
|
||||||
super_slow: Super slow
|
super_slow: Super slow
|
||||||
slow: Slow
|
slow: Slow
|
||||||
@ -840,7 +840,7 @@ settings:
|
|||||||
refreshRate:
|
refreshRate:
|
||||||
title: Tick Rate
|
title: Tick Rate
|
||||||
description: >-
|
description: >-
|
||||||
The game will automatically adjust the tickrate to be between this target tickrate and half of it. For example, with a tickrate of 60hz, the game will try to stay at 60hz, and if your computer can't handle it it will go down until it eventually reaches 30hz.
|
This determines how many game ticks happen per second. In general, a higher tick rate means better precision but also worse performance. On lower tickrates, the throughput may not be exact.
|
||||||
|
|
||||||
alwaysMultiplace:
|
alwaysMultiplace:
|
||||||
title: Multiplace
|
title: Multiplace
|
||||||
@ -911,7 +911,12 @@ settings:
|
|||||||
simplifiedBelts:
|
simplifiedBelts:
|
||||||
title: Simplified Belts (Ugly)
|
title: Simplified Belts (Ugly)
|
||||||
description: >-
|
description: >-
|
||||||
Does not render belt items except when hovering the belt, to save performance.
|
Does not render belt items except when hovering the belt to save performance. I do not recommend to play with this setting if you do not absolutely need the performance.
|
||||||
|
|
||||||
|
enableMousePan:
|
||||||
|
title: Enable Mouse Pan
|
||||||
|
description: >-
|
||||||
|
Allows to move the map by moving the cursor to the edges of the screen. The speed depends on the Movement Speed setting.
|
||||||
|
|
||||||
keybindings:
|
keybindings:
|
||||||
title: Keybindings
|
title: Keybindings
|
||||||
|
Loading…
Reference in New Issue
Block a user