/**
* @license GPL-3.0-only
*
* @author Mark Mayes / mm-dev
*
*
* @module PD/GAME
*
* @description
* ## Lots of configuration for the game, including:
* - Portrait/landscape layout details for UI elements
* - Frame rate/timings
* - Tweakable controls affecting difficulty, playability etc
*/
import { HORIZ_ALIGN, VERT_ALIGN } from "./ENUM.js";
import * as STRING from "./STRING.js";
/*
*
*
* General
*
*/
/** @constant
@type {object}
@default
*/
export const BITMAPIMAGE_DEFAULT_OPTIONS = { resizeQuality: "pixelated" };
/** @constant
@type {number}
@default
*/
// TODO Decide if this is going to be a constant or not
//export const PIXEL_SCALE = Math.ceil(window.devicePixelRatio);
//export const PIXEL_SCALE = 4;
//export const PIXEL_SCALE = 2;
//export const PIXEL_SCALE = window.devicePixelRatio;
export const PIXEL_SCALE = Math.max(
Math.min(Math.ceil(window.devicePixelRatio), 2),
1
);
/** @constant
@type {number}
@default
*/
export const SCALING_TARGET_SIZE = 1000;
/** @constant
@type {number}
@default
*/
export const TARGET_FPS = 60;
/** @constant
@type {number}
@default
*/
export const FRAME_MS = Math.floor(1000 / TARGET_FPS);
export const PIx2 = Math.PI * 2;
export const ONRESIZE_UPDATE_DELAY_MS = 200;
// Affects speed that background moves laterally compared to foreground objects (suggests distance/parallax)
export const BACKGROUND_LATERAL_MULTIPLIER = 0.2;
export const FLOATING_LATERAL_MULTIPLIER = 1;
export const MAINTITLE_WIDTH_PX = 800;
export const MAINTITLE_HEIGHT_PX = 200;
export const MAIN_PADDING_PX = 30;
export const VERSIONNUMBER_COLOR = "#8a0500";
// For FPS counter, how many recent FPS counts are stored and averaged
export const FPSDISPLAY_AVERAGE_FRAMES_SPAN = TARGET_FPS * 7;
export const UI_LANDSCAPE = {
controller: {
alignV: VERT_ALIGN.CENTER,
alignH: HORIZ_ALIGN.RIGHT,
mouseWidthPercent: 35,
mouseHeightPercent: 50,
touchWidthPercent: 20,
touchHeightPercent: 30,
},
healthMeter: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.LEFT,
//offsetByCharsV: 1,
},
levelText: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.LEFT,
},
timer: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.LEFT,
offsetByCharsV: 1,
},
collectText: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.LEFT,
offsetByCharsH: 3,
},
soundToggle: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.RIGHT,
},
fullscreenToggle: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.RIGHT,
},
mainTitle: {
sizeRatio: 0.6,
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.CENTER,
offsetByCharsV: 1,
},
fps: {
alignV: VERT_ALIGN.CENTER,
alignH: HORIZ_ALIGN.RIGHT,
},
versionInfoLevelIntro: {
alignH: HORIZ_ALIGN.RIGHT,
alignV: VERT_ALIGN.BOTTOM,
offsetByCharsH: -3,
},
};
export const UI_PORTRAIT = {
controller: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.CENTER,
mouseWidthPercent: 50,
mouseHeightPercent: 35,
touchWidthPercent: 30,
touchHeightPercent: 20,
},
healthMeter: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.RIGHT,
//offsetByCharsV: 1,
},
levelText: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.LEFT,
},
timer: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.RIGHT,
offsetByCharsV: 1,
},
collectText: {
alignV: VERT_ALIGN.TOP,
alignH: HORIZ_ALIGN.LEFT,
offsetByCharsV: 1,
},
soundToggle: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.LEFT,
},
fullscreenToggle: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.RIGHT,
},
mainTitle: {
sizeRatio: 0.85,
alignV: VERT_ALIGN.CENTER,
alignH: HORIZ_ALIGN.CENTER,
offsetByCharsV: -2,
},
fps: {
alignV: VERT_ALIGN.BOTTOM,
alignH: HORIZ_ALIGN.CENTER,
offsetByCharsV: -1,
},
versionInfoLevelIntro: {
alignH: HORIZ_ALIGN.CENTER,
alignV: VERT_ALIGN.BOTTOM,
},
};
/*
*
*
* Scoring
*
*/
export const SCORE_PER_SEC_REMAINING = 10;
export const SCORE_PER_LEVEL_MULTIPLIER = 1.1;
/*
*
*
* Timer
*
*/
export const TIME_LOW_SECONDS = 10;
/*
*
*
* Player
*
*/
export const PLAYER_ORIGIN_LONGITUDINAL = 230;
export const PLAYER_DAMPSPEED_MULTIPLIER = 7;
// length of 'player eats' animation in frames
export const PLAYEREATS_SECS_TOTAL = 0.8;
export const PLAYEREATS_FLASH_ON_SECS = 0.3;
export const PLAYEREATS_FLASH_OFF_SECS = 0.1;
export const PLAYER_LOSSOFCONTROL_MAGNITUDE_DIVISOR = 3000;
export const PLAYER_LOSSOFCONTROL_MAX_SECS = 4;
export const DAMAGED_SECS_TOTAL = 0.8;
// size of throb
export const PLAYEREATS_RADIUS_GROWTH = 0.1;
export const EATEN_OBSTACLE_INITIAL_GROWTH = 1.33;
export const EATEN_OBSTACLE_ALPHA = 0.33;
// Tail shows above this speed
export const PLAYER_TAIL_MINSPEED = 1;
export const PLAYER_ELLIPSE_STRETCH_MULTIPLIER = 0.01;
// Used to calculate distance between tail segments
export const PLAYER_TAILLENGTH_MULTIPLIER = 0.005;
export const PLAYER_TAIL_ALPHA = 0.13;
export const PLAYER_TAIL_ALPHA_DROP = 0.023;
export const PLAYER_TAIL_RADIUS_GROWTH = 1.08;
// Used to convert player speed to number of tail segments
export const PLAYER_SPEED_TO_TAILSEGMENTS_MULTIPLIER = 0.7;
export const PLAYER_HEAD_ALPHA = 1;
export const PLAYER_OUTLINE_ALPHA = 0.5;
export const PLAYER_OUTLINE_THICKNESS = 5;
export const PLAYER_TAILCOLOR_DARKLEVEL = "#ffffff";
export const PLAYER_TAILCOLOR_LIGHTLEVEL = "#dddddd";
export const PLAYER_GRIN_INCREASERATE = 1.333;
export const DAMAGE_FLASH_ON_SECS = 0.3;
export const DAMAGE_FLASH_OFF_SECS = 0.1;
/*
*
*
* Obstacles
*
*/
// For non-moving obstacles (eg trees), setting speed to 0 can cause errors/bugs relating to dividing/multiplying by 0, so use this value instead
export const STATIC_OBSTACLE_SPEED = Number.MIN_VALUE;
export const OBSTACLE_FADE_GRADIENT_STOP = 0.7;
// Prevent player getting stuck inside obstacle by rendering it unable to inflict damage for a few frames
export const DAMAGE_SAFETY_SECS_TOTAL = DAMAGED_SECS_TOTAL;
export const EXPLODING_FRAMES_TOTAL = 50;
export const EXPLODING_FRAMES_REDUCE_SPEED_MULTIPLIER_MIN = 0.7;
export const EXPLODING_FRAMES_REDUCE_SPEED_MULTIPLIER_MAX = 0.9;
export const EXPLODING_FRAMES_REDUCE_RADIUS_MULTIPLIER_MIN = 0.7;
export const EXPLODING_FRAMES_REDUCE_RADIUS_MULTIPLIER_MAX = 0.99;
export const EXPLODING_FRAMES_ANGLE_INCREASE_MIN = 0.05;
export const EXPLODING_FRAMES_ANGLE_INCREASE_MAX = 0.15;
export const EXPLODING_OBSTACLE_ALPHA = 1;
export const SHAPE_STROKE_THICKNESS = 7;
export const SHAPE_STROKE_ALPHA = 0.6;
export const INTRO_OBSTACLE_GROUP_COLS = 7;
export const INTRO_OBSTACLE_DEFAULT_ROTATION = 315;
export const OBSTACLE_RETURNTONORMALSPEED_RATE = 0.1;
export const FLOATING_OBSTACLE_LEVELOUTRO_ACCEL_RATE = 0.1;
/*
*
*
* Pipe wall
*
*/
export const PIPE_WALL_SEGMENT_AR = [
{
thickness: 8,
zoomFactor: 0.4,
perspectiveOffsetDivisor: 1.01,
},
{
thickness: 32,
zoomFactor: 0.49,
perspectiveOffsetDivisor: 1.001,
},
{
thickness: 8,
zoomFactor: 0.4,
perspectiveOffsetDivisor: 1.01,
},
];
export const PIPE_WALL_THICKNESS = 48;
export const PIPE_WALL_EDGE_THICKNESS = 8;
// Amount pipe wall glass zooms stuff behind it
// MUST BE ABOVE 0 AND BELOW 0.5 --- NOT 0.5!
export const PIPE_WALL_ZOOM_FACTOR = 0.49;
// Whether visual distortions in the wall occur on the main or the cross axis
export const PIPE_WALL_DISTORT_CROSSAXIS = true;
// Approximates something like the vertical angle of refraction, so how close
// the refracted version of the background appears offset from the normal
// version
export const PIPE_WALL_PERSPECTIVE_OFFSET_DIVISOR = 1.001;
export const PIPE_GRADIENTCOLOR_1 = "rgba(255, 255, 255, 0.01)";
export const PIPE_GRADIENTCOLOR_2 = "rgba(255, 255, 255, 0.017)";
export const PIPE_GRADIENTCOLOR_3 = "rgba(0, 0, 0, 0.03)";
export const TEXT_BACKGROUND_FILL_ALPHA = 0.66;
export const INSTRUCTIONTEXT_COLOR = "#ff4444";
export const OVERLAY_ACCENT_COLOR = "rgba(128, 128, 128, 1)";
export const TITLE_COLOR = "#00ffff";
export const OVERLAY_TEXT_ALIGN_H = HORIZ_ALIGN.CENTER;
export const OVERLAY_TEXT_ALIGN_V = VERT_ALIGN.CENTER;
export const LEVEL_INTRO_CIRCLE_RADIUS_PX = 30;
/*
*
*
* Controller
*
*/
// active padding is invisible border around controller where activity is detected
// Lower values (NOT BELOW 1, 3 > works best): More precise / instant / can feel choppy with big movements
// Higher values: Less instantaneous / more of a glide / smoother
export const CONTROLLER_SLIPPERINESS = 15;
export const CONTROLLER_SPEED_DAMP = 24;
export const CONTROLLER_ACTIVE_PADDING_PX = 0;
export const CONTROLLER_CORNER_RADIUS = 20;
export const CONTROLLER_ARROW_WIDTH = 60;
export const CONTROLLER_SPEED_ARROW_MULTIPLIER = 0.5;
// As speed increases, controller colour fades up
export const CONTROLLER_SPEED_ALPHA_MULTIPLIER = 0.2;
export const CONTROLLER_STICK_COLOR_INACTIVE = "#888888";
export const CONTROLLER_STICK_COLOR_ACTIVE_BACKWARDS = "#880000";
export const CONTROLLER_STICK_COLOR_ACTIVE_FORWARDS = "#008800";
export const CONTROLLER_STICK_THICKNESS = 18;
export const CONTROLLER_OUTLINE_THICKNESS = 12;
export const CONTROLLER_STICK_LINECAP = "square";
// midpos of control stick can be offset based on fractions eg 0.5 is the centre
// Anything above zero for `CONTROLLER_STICK_MIDPOS_SPEED` will allow backwards
// movement
// Negative values mean stopping is impossible (lower = faster minimum speed)
export const CONTROLLER_STICK_MIDPOS_SPEED = -0.02;
export const CONTROLLER_STICK_MIDPOS_LATERAL = 0.5;
//export const CONTROLAREA_OVERLAY_COLOR = "rgba(128, 128, 128, 0.05)";
export const CONTROLAREA_OVERLAY_COLOR = "rgba(128, 128, 128, 0.13";
//export const CONTROLAREA_OVERLAY_COLOR = "rgba(128, 128, 128, 0.175)";
export const CONTROLAREA_OVERLAY_SOLID_COLOR = "#888888";
export const CONTROLAREA_OVERLAY_ALPHA_MIN = 0.001;
export const CONTROLAREA_OVERLAY_ALPHA_MAX = 0.3;
/*
*
*
* Health meter
*
*/
export const HEALTHMETER_SIZE_CHARS = 5;
export const HEALTHMETER_BAR_COLOR = "#ffff00";
export const HEALTHMETER_DAMAGED_BAR_COLOR = "#ff0000";
/*
*
*
* Text
*
*/
export const TEXT_BLANKLINE = { text: " ", color: "transparent" };
// Best not to have this lower than 2 as it causes some padding values to end up as low fractions below 1 and get rounded weirdly, then layout issues
export const TEXT_BG_PADDING_TO_CHARWIDTH_RATIO = 2;
export const TEXT_DEFAULT_SHADOW_COLOR = "#000000";
export const TEXT_FLASH_ON_SECS = 1.9;
export const TEXT_FLASH_OFF_SECS = 0.2;
export const SCALED_TEXT_SPACINGROWTH_DIVISOR = 1;
export const BITMAPFONT_SPRITESHEET_CHAR_WIDTH = 128;
export const BITMAPFONT_SPRITESHEET_CHAR_HEIGHT = 128;
export const BITMAPFONT_SPRITESHEET_COLS = 8;
// Used to calculate font sizes ie we want to fit this many characters in the
// width of the canvas
export const CHARS_IN_CANVAS_WIDTH = 23;
export const CHARS_IN_CANVAS_HEIGHT = 21;
// Characters in the order they appear in the spritesheet
export const CHARS_IN_SPRITESHEET_AR = [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"-",
STRING.HEART,
"!",
"©",
"/",
STRING.SOUND_DISABLED,
STRING.SOUND_ENABLED,
STRING.FULLSCREEN,
STRING.CLOCK,
".",
];
// TODO unused?
export const CHARS_NARROW_AR = ["I", "!", "."];