1
0
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:
tobspr 2020-09-19 20:30:35 +02:00
parent d6f56da9e4
commit fbff0a0ad4
5 changed files with 484 additions and 412 deletions

View File

@ -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);
}
}

View File

@ -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)",

View File

@ -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

View File

@ -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();
} }
} }

View File

@ -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