samengine 1.9.1 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +168 -0
- package/dist/config/buildconfig.d.ts +146 -0
- package/dist/config/buildconfig.js +115 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.js +1 -0
- package/dist/core.d.ts +17 -0
- package/dist/core.js +24 -0
- package/dist/html.d.ts +29 -0
- package/dist/html.js +20 -0
- package/dist/input.d.ts +51 -0
- package/dist/input.js +44 -3
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +6 -2
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +8 -1
- package/dist/nonbrowser/getversion.d.ts +13 -0
- package/dist/nonbrowser/getversion.js +35 -0
- package/dist/nonbrowser/ghresolver.d.ts +1 -0
- package/dist/nonbrowser/ghresolver.js +7 -0
- package/dist/nonbrowser/index.d.ts +9 -0
- package/dist/nonbrowser/index.js +9 -0
- package/dist/nonbrowser/internal/buildhelper.d.ts +42 -0
- package/dist/nonbrowser/internal/buildhelper.js +144 -0
- package/dist/nonbrowser/internal/cli/argparser.d.ts +18 -0
- package/dist/nonbrowser/internal/cli/argparser.js +36 -0
- package/dist/nonbrowser/internal/cli/main.d.ts +13 -0
- package/dist/nonbrowser/internal/cli/main.js +265 -0
- package/dist/nonbrowser/internal/config.d.ts +9 -0
- package/dist/nonbrowser/internal/config.js +40 -0
- package/dist/nonbrowser/internal/exporthtml.d.ts +37 -0
- package/dist/nonbrowser/internal/exporthtml.js +622 -0
- package/dist/nonbrowser/utils.d.ts +8 -0
- package/dist/nonbrowser/utils.js +18 -0
- package/dist/physics/collision.d.ts +33 -0
- package/dist/physics/collision.js +27 -0
- package/dist/physics/physicsEngine.d.ts +18 -0
- package/dist/physics/physicsEngine.js +18 -0
- package/dist/physics/physicsObject.d.ts +20 -0
- package/dist/physics/physicsObject.js +20 -0
- package/dist/renderer.d.ts +78 -0
- package/dist/renderer.js +72 -9
- package/dist/samegui/index.d.ts +29 -0
- package/dist/samegui/index.js +26 -0
- package/dist/save.d.ts +12 -0
- package/dist/save.js +10 -0
- package/dist/sound/audioplayer.d.ts +39 -0
- package/dist/sound/audioplayer.js +39 -5
- package/dist/storage/index.d.ts +40 -2
- package/dist/storage/index.js +34 -3
- package/dist/text/index.d.ts +14 -0
- package/dist/text/index.js +58 -0
- package/dist/texture.d.ts +100 -0
- package/dist/texture.js +75 -41
- package/dist/types/button.d.ts +25 -0
- package/dist/types/button.js +22 -0
- package/dist/types/circle.d.ts +26 -0
- package/dist/types/circle.js +21 -7
- package/dist/types/color.d.ts +17 -0
- package/dist/types/color.js +11 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/rectangle.d.ts +29 -0
- package/dist/types/rectangle.js +23 -7
- package/dist/types/triangle.d.ts +23 -0
- package/dist/types/triangle.js +20 -6
- package/dist/types/vector2d.d.ts +42 -0
- package/dist/types/vector2d.js +39 -11
- package/dist/types/vector3d.d.ts +38 -0
- package/dist/types/vector3d.js +35 -11
- package/dist/utils/index.d.ts +11 -4
- package/dist/utils/index.js +11 -4
- package/dist/utils/logger/index.d.ts +24 -0
- package/dist/utils/logger/index.js +44 -0
- package/dist/utils/math.d.ts +18 -0
- package/dist/utils/math.js +18 -4
- package/package.json +29 -11
- package/dist/utils/csv/index.d.ts +0 -3
- package/dist/utils/csv/index.js +0 -2
- package/dist/utils/csv/parser.d.ts +0 -25
- package/dist/utils/csv/parser.js +0 -212
- package/dist/utils/csv/stringifier.d.ts +0 -30
- package/dist/utils/csv/stringifier.js +0 -130
- package/dist/utils/csv/types.d.ts +0 -63
- package/dist/utils/csv/types.js +0 -1
- package/dist/utils/jsonc-parser.d.ts +0 -4
- package/dist/utils/jsonc-parser.js +0 -166
- package/dist/utils/markdown.d.ts +0 -41
- package/dist/utils/markdown.js +0 -699
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Audio based sound manager for short effects and looping music.
|
|
3
|
+
*
|
|
4
|
+
* Sounds are decoded once and cached by URL. In normal builds, audio files are
|
|
5
|
+
* loaded with `fetch(url)`. Single-file exports can provide `window.__loadResource`
|
|
6
|
+
* to resolve embedded resources before falling back to normal fetch.
|
|
7
|
+
*/
|
|
1
8
|
export class SoundSystem {
|
|
9
|
+
/**
|
|
10
|
+
* Creates or reuses an `AudioContext` and connects a master gain node.
|
|
11
|
+
*
|
|
12
|
+
* Browsers often require user interaction before audio can play. If playback
|
|
13
|
+
* is blocked or suspended, call `resume()` from a click/key handler.
|
|
14
|
+
*/
|
|
2
15
|
constructor() {
|
|
3
16
|
this.bufferCache = new Map();
|
|
4
17
|
this.activeSources = new Set();
|
|
@@ -8,7 +21,12 @@ export class SoundSystem {
|
|
|
8
21
|
this.masterGain.gain.value = 1;
|
|
9
22
|
this.masterGain.connect(this.ctx.destination);
|
|
10
23
|
}
|
|
11
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Loads, decodes, and caches an audio file.
|
|
26
|
+
*
|
|
27
|
+
* @param url Audio URL or embedded resource key.
|
|
28
|
+
* @returns Decoded `AudioBuffer`.
|
|
29
|
+
*/
|
|
12
30
|
async load(url) {
|
|
13
31
|
if (this.bufferCache.has(url)) {
|
|
14
32
|
return this.bufferCache.get(url);
|
|
@@ -38,7 +56,12 @@ export class SoundSystem {
|
|
|
38
56
|
this.bufferCache.set(url, buffer);
|
|
39
57
|
return buffer;
|
|
40
58
|
}
|
|
41
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Plays an audio file and returns the active source node.
|
|
61
|
+
*
|
|
62
|
+
* @param url Audio URL or embedded resource key.
|
|
63
|
+
* @param options Per-playback settings such as volume, loop, and speed.
|
|
64
|
+
*/
|
|
42
65
|
async play(url, options = {}) {
|
|
43
66
|
const buffer = await this.load(url);
|
|
44
67
|
const source = this.ctx.createBufferSource();
|
|
@@ -56,7 +79,9 @@ export class SoundSystem {
|
|
|
56
79
|
};
|
|
57
80
|
return source;
|
|
58
81
|
}
|
|
59
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Stops every source started through this `SoundSystem`.
|
|
84
|
+
*/
|
|
60
85
|
stopAll() {
|
|
61
86
|
for (const s of this.activeSources) {
|
|
62
87
|
try {
|
|
@@ -66,16 +91,25 @@ export class SoundSystem {
|
|
|
66
91
|
}
|
|
67
92
|
this.activeSources.clear();
|
|
68
93
|
}
|
|
69
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Sets the master volume for all current and future sounds.
|
|
96
|
+
*
|
|
97
|
+
* `1` is full volume, `0` is silent. Values above `1` amplify.
|
|
98
|
+
*/
|
|
70
99
|
setVolume(v) {
|
|
71
100
|
this.masterGain.gain.value = v;
|
|
72
101
|
}
|
|
73
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Resumes the underlying `AudioContext` if the browser suspended it.
|
|
104
|
+
*/
|
|
74
105
|
async resume() {
|
|
75
106
|
if (this.ctx.state === "suspended") {
|
|
76
107
|
await this.ctx.resume();
|
|
77
108
|
}
|
|
78
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Suspends the underlying `AudioContext` without clearing loaded buffers.
|
|
112
|
+
*/
|
|
79
113
|
async pause() {
|
|
80
114
|
if (this.ctx.state === "running") {
|
|
81
115
|
await this.ctx.suspend();
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,19 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrapper shape for values that may receive expiration metadata in the future.
|
|
3
|
+
*
|
|
4
|
+
* The current `StorageLib` methods store raw values directly, so this type is
|
|
5
|
+
* mainly useful when callers want to manage their own `expiresAt` convention.
|
|
6
|
+
*/
|
|
1
7
|
export type StoredValue<T> = {
|
|
2
8
|
value: T;
|
|
3
9
|
expiresAt?: number;
|
|
4
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* Small typed wrapper around `localStorage`.
|
|
13
|
+
*
|
|
14
|
+
* Values are serialized with `JSON.stringify` and read with `JSON.parse`. If a
|
|
15
|
+
* value cannot be parsed, `get` returns `null` instead of throwing.
|
|
16
|
+
*/
|
|
5
17
|
export declare class StorageLib {
|
|
18
|
+
/**
|
|
19
|
+
* Stores a JSON-serializable value under a key.
|
|
20
|
+
*/
|
|
6
21
|
static set<T>(key: string, value: T): void;
|
|
22
|
+
/**
|
|
23
|
+
* Reads and parses a value from localStorage.
|
|
24
|
+
*
|
|
25
|
+
* @returns The parsed value or `null` when the key is missing or invalid JSON.
|
|
26
|
+
*/
|
|
7
27
|
static get<T>(key: string): T | null;
|
|
28
|
+
/**
|
|
29
|
+
* Removes a single key from localStorage.
|
|
30
|
+
*/
|
|
8
31
|
static remove(key: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Clears the complete browser localStorage for the current origin.
|
|
34
|
+
*
|
|
35
|
+
* Be careful: this affects all keys on the same domain, not only samengine
|
|
36
|
+
* keys.
|
|
37
|
+
*/
|
|
9
38
|
static clear(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether a key exists in localStorage.
|
|
41
|
+
*/
|
|
10
42
|
static has(key: string): boolean;
|
|
11
43
|
/**
|
|
12
|
-
*
|
|
44
|
+
* Exports the complete localStorage content as a JSON string.
|
|
45
|
+
*
|
|
46
|
+
* Existing JSON values are parsed back into objects; non-JSON values are kept
|
|
47
|
+
* as strings.
|
|
13
48
|
*/
|
|
14
49
|
static exportToJson(pretty?: boolean): string;
|
|
15
50
|
/**
|
|
16
|
-
* JSON
|
|
51
|
+
* Imports a JSON object into localStorage.
|
|
52
|
+
*
|
|
53
|
+
* @param json JSON object string, usually created by `exportToJson`.
|
|
54
|
+
* @param overwrite If false, existing keys are preserved.
|
|
17
55
|
*/
|
|
18
56
|
static importFromJson(json: string, overwrite?: boolean): void;
|
|
19
57
|
}
|
package/dist/storage/index.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Small typed wrapper around `localStorage`.
|
|
3
|
+
*
|
|
4
|
+
* Values are serialized with `JSON.stringify` and read with `JSON.parse`. If a
|
|
5
|
+
* value cannot be parsed, `get` returns `null` instead of throwing.
|
|
6
|
+
*/
|
|
2
7
|
export class StorageLib {
|
|
8
|
+
/**
|
|
9
|
+
* Stores a JSON-serializable value under a key.
|
|
10
|
+
*/
|
|
3
11
|
static set(key, value) {
|
|
4
12
|
localStorage.setItem(key, JSON.stringify(value));
|
|
5
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Reads and parses a value from localStorage.
|
|
16
|
+
*
|
|
17
|
+
* @returns The parsed value or `null` when the key is missing or invalid JSON.
|
|
18
|
+
*/
|
|
6
19
|
static get(key) {
|
|
7
20
|
const item = localStorage.getItem(key);
|
|
8
21
|
if (!item) {
|
|
@@ -15,17 +28,32 @@ export class StorageLib {
|
|
|
15
28
|
return null;
|
|
16
29
|
}
|
|
17
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Removes a single key from localStorage.
|
|
33
|
+
*/
|
|
18
34
|
static remove(key) {
|
|
19
35
|
localStorage.removeItem(key);
|
|
20
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Clears the complete browser localStorage for the current origin.
|
|
39
|
+
*
|
|
40
|
+
* Be careful: this affects all keys on the same domain, not only samengine
|
|
41
|
+
* keys.
|
|
42
|
+
*/
|
|
21
43
|
static clear() {
|
|
22
44
|
localStorage.clear();
|
|
23
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Checks whether a key exists in localStorage.
|
|
48
|
+
*/
|
|
24
49
|
static has(key) {
|
|
25
50
|
return localStorage.getItem(key) !== null;
|
|
26
51
|
}
|
|
27
52
|
/**
|
|
28
|
-
*
|
|
53
|
+
* Exports the complete localStorage content as a JSON string.
|
|
54
|
+
*
|
|
55
|
+
* Existing JSON values are parsed back into objects; non-JSON values are kept
|
|
56
|
+
* as strings.
|
|
29
57
|
*/
|
|
30
58
|
static exportToJson(pretty = true) {
|
|
31
59
|
const data = {};
|
|
@@ -44,7 +72,10 @@ export class StorageLib {
|
|
|
44
72
|
return JSON.stringify(data, null, pretty ? 2 : 0);
|
|
45
73
|
}
|
|
46
74
|
/**
|
|
47
|
-
* JSON
|
|
75
|
+
* Imports a JSON object into localStorage.
|
|
76
|
+
*
|
|
77
|
+
* @param json JSON object string, usually created by `exportToJson`.
|
|
78
|
+
* @param overwrite If false, existing keys are preserved.
|
|
48
79
|
*/
|
|
49
80
|
static importFromJson(json, overwrite = true) {
|
|
50
81
|
const data = JSON.parse(json);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Rect } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* For Text Input: Use canvasinput-ts
|
|
4
|
+
*/
|
|
5
|
+
export interface TextStyle {
|
|
6
|
+
color: string;
|
|
7
|
+
font: string;
|
|
8
|
+
size: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Rendert Text innerhalb eines Rechtecks mit automatischem Umbruch.
|
|
12
|
+
* Lange Wörter werden ebenfalls umgebrochen.
|
|
13
|
+
*/
|
|
14
|
+
export declare function renderTextBox(ctx: CanvasRenderingContext2D, rect: Rect, text: string, style: TextStyle, padding?: number): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendert Text innerhalb eines Rechtecks mit automatischem Umbruch.
|
|
3
|
+
* Lange Wörter werden ebenfalls umgebrochen.
|
|
4
|
+
*/
|
|
5
|
+
export function renderTextBox(ctx, rect, text, style, padding = 4) {
|
|
6
|
+
ctx.fillStyle = style.color;
|
|
7
|
+
ctx.font = `${style.size}px ${style.font}`;
|
|
8
|
+
ctx.textBaseline = "top";
|
|
9
|
+
const maxWidth = rect.width - padding * 2;
|
|
10
|
+
const lineHeight = style.size * 1.2;
|
|
11
|
+
let y = rect.y + padding;
|
|
12
|
+
const paragraphs = text.split("\n");
|
|
13
|
+
for (const paragraph of paragraphs) {
|
|
14
|
+
let line = "";
|
|
15
|
+
const words = paragraph.split(" ");
|
|
16
|
+
for (let word of words) {
|
|
17
|
+
// Wort zu lang → innerhalb des Wortes umbrechen
|
|
18
|
+
while (ctx.measureText(word).width > maxWidth) {
|
|
19
|
+
let chunk = "";
|
|
20
|
+
for (const char of word) {
|
|
21
|
+
const test = chunk + char;
|
|
22
|
+
if (ctx.measureText(test).width > maxWidth) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
chunk = test;
|
|
26
|
+
}
|
|
27
|
+
// Höhenbegrenzung
|
|
28
|
+
if (y + lineHeight > rect.y + rect.height) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
ctx.fillText(chunk, rect.x + padding, y);
|
|
32
|
+
y += lineHeight;
|
|
33
|
+
word = word.substring(chunk.length);
|
|
34
|
+
}
|
|
35
|
+
const testLine = line.length > 0
|
|
36
|
+
? `${line} ${word}`
|
|
37
|
+
: word;
|
|
38
|
+
if (ctx.measureText(testLine).width > maxWidth) {
|
|
39
|
+
if (y + lineHeight > rect.y + rect.height) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
ctx.fillText(line, rect.x + padding, y);
|
|
43
|
+
y += lineHeight;
|
|
44
|
+
line = word;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
line = testLine;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (line.length > 0) {
|
|
51
|
+
if (y + lineHeight > rect.y + rect.height) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
ctx.fillText(line, rect.x + padding, y);
|
|
55
|
+
y += lineHeight;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/texture.d.ts
CHANGED
|
@@ -1,37 +1,137 @@
|
|
|
1
1
|
import { Rect } from "./types/rectangle.js";
|
|
2
|
+
/**
|
|
3
|
+
* Loaded texture image.
|
|
4
|
+
*
|
|
5
|
+
* `undefined` means the texture was not loaded or the cache key does not exist.
|
|
6
|
+
*/
|
|
2
7
|
export type Texture = HTMLImageElement | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Optional drawing settings used by texture, atlas, and animation rendering.
|
|
10
|
+
*/
|
|
3
11
|
export type DrawOptions = {
|
|
12
|
+
/** Target width before `scale` is applied. Defaults to source width. */
|
|
4
13
|
width?: number;
|
|
14
|
+
/** Target height before `scale` is applied. Defaults to source height. */
|
|
5
15
|
height?: number;
|
|
16
|
+
/** Rotation in radians around the rendered image center. */
|
|
6
17
|
rotation?: number;
|
|
18
|
+
/** Mirrors the image horizontally around its center. */
|
|
7
19
|
flipX?: boolean;
|
|
20
|
+
/** Mirrors the image vertically around its center. */
|
|
8
21
|
flipY?: boolean;
|
|
22
|
+
/** Multiplies the final rendered width and height. */
|
|
9
23
|
scale?: number;
|
|
10
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Loads an image and stores it in the texture cache.
|
|
27
|
+
*
|
|
28
|
+
* Normal builds load from `/resources/${src}`. Single-file exports can provide
|
|
29
|
+
* embedded resources through `window.__resources`; if a matching key exists,
|
|
30
|
+
* that data URL is used instead. The cache key always remains the original
|
|
31
|
+
* `src` string, so `getTexture(src)` works the same in both modes.
|
|
32
|
+
*
|
|
33
|
+
* @param src Resource path relative to the resources folder.
|
|
34
|
+
* @returns Promise resolving to the loaded `HTMLImageElement`.
|
|
35
|
+
*/
|
|
11
36
|
export declare function loadTextureAsync(src: string): Promise<HTMLImageElement>;
|
|
37
|
+
/**
|
|
38
|
+
* Reads a texture from the cache.
|
|
39
|
+
*
|
|
40
|
+
* This does not start loading by itself. Call `loadTextureAsync` first or handle
|
|
41
|
+
* the `undefined` result.
|
|
42
|
+
*/
|
|
12
43
|
export declare function getTexture(src: string): Texture;
|
|
44
|
+
/**
|
|
45
|
+
* Draws a texture to the canvas.
|
|
46
|
+
*
|
|
47
|
+
* The image is drawn around its center for rotation and flipping, but `x` and
|
|
48
|
+
* `y` still describe the top-left target position. If the texture is missing,
|
|
49
|
+
* a magenta fallback rectangle is drawn to make missing assets visible.
|
|
50
|
+
*/
|
|
13
51
|
export declare function drawTexture(ctx: CanvasRenderingContext2D, texture: Texture, x: number, y: number, options?: DrawOptions): void;
|
|
52
|
+
/**
|
|
53
|
+
* A spritesheet image plus named frame rectangles inside that image.
|
|
54
|
+
*/
|
|
14
55
|
export type TextureAtlas = {
|
|
56
|
+
/** Spritesheet image. */
|
|
15
57
|
image: HTMLImageElement;
|
|
58
|
+
/** Named source rectangles inside `image`. */
|
|
16
59
|
frames: Record<string, Rect>;
|
|
17
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Loads a texture atlas image and its JSON frame data.
|
|
63
|
+
*
|
|
64
|
+
* The JSON file is fetched from the resources folder and must contain a
|
|
65
|
+
* `frames` object with frame names mapped to rectangles:
|
|
66
|
+
*
|
|
67
|
+
* ```json
|
|
68
|
+
* {
|
|
69
|
+
* "frames": {
|
|
70
|
+
* "player_idle_0": { "x": 0, "y": 0, "width": 32, "height": 32 }
|
|
71
|
+
* }
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
18
75
|
export declare function loadAtlas(imageSrc: string, dataSrc: string): Promise<TextureAtlas>;
|
|
76
|
+
/**
|
|
77
|
+
* Draws one named frame from a texture atlas.
|
|
78
|
+
*
|
|
79
|
+
* Missing frame names are logged through `dlog` and skipped.
|
|
80
|
+
*/
|
|
19
81
|
export declare function drawAtlasFrame(ctx: CanvasRenderingContext2D, atlas: TextureAtlas, frameName: string, x: number, y: number, options?: DrawOptions): void;
|
|
82
|
+
/**
|
|
83
|
+
* Keeps track of time and current frame for a named-frame animation.
|
|
84
|
+
*
|
|
85
|
+
* The player only stores animation state. It does not draw anything by itself;
|
|
86
|
+
* call `drawAnimation` after `update(dt)` to render the current atlas frame.
|
|
87
|
+
*/
|
|
20
88
|
export declare class AnimationPlayer {
|
|
21
89
|
animation: Animation;
|
|
22
90
|
private time;
|
|
23
91
|
private currentFrameIndex;
|
|
24
92
|
private finished;
|
|
93
|
+
/**
|
|
94
|
+
* Creates a player for the given animation definition.
|
|
95
|
+
*/
|
|
25
96
|
constructor(animation: Animation);
|
|
97
|
+
/**
|
|
98
|
+
* Advances the animation by `deltaTime` seconds.
|
|
99
|
+
*
|
|
100
|
+
* Looping animations wrap back to the first frame. Non-looping animations
|
|
101
|
+
* stop on their final frame and then report `isFinished() === true`.
|
|
102
|
+
*/
|
|
26
103
|
update(deltaTime: number): void;
|
|
104
|
+
/**
|
|
105
|
+
* Returns the frame name that should currently be rendered from the atlas.
|
|
106
|
+
*/
|
|
27
107
|
getCurrentFrame(): string;
|
|
108
|
+
/**
|
|
109
|
+
* Restarts the animation from the first frame.
|
|
110
|
+
*/
|
|
28
111
|
reset(): void;
|
|
112
|
+
/**
|
|
113
|
+
* Returns whether a non-looping animation reached its final frame.
|
|
114
|
+
*/
|
|
29
115
|
isFinished(): boolean;
|
|
30
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Animation definition based on named frames inside a `TextureAtlas`.
|
|
119
|
+
*/
|
|
31
120
|
export type Animation = {
|
|
121
|
+
/** Frame names in playback order. */
|
|
32
122
|
frames: string[];
|
|
123
|
+
/** Playback speed in frames per second. */
|
|
33
124
|
fps: number;
|
|
125
|
+
/** Whether playback should wrap to the first frame. Defaults to false-like. */
|
|
34
126
|
loop?: boolean;
|
|
35
127
|
};
|
|
128
|
+
/**
|
|
129
|
+
* Draws the current frame of an `AnimationPlayer`.
|
|
130
|
+
*/
|
|
36
131
|
export declare function drawAnimation(ctx: CanvasRenderingContext2D, atlas: TextureAtlas, player: AnimationPlayer, x: number, y: number, options?: DrawOptions): void;
|
|
132
|
+
/**
|
|
133
|
+
* Helper for side-view sprites.
|
|
134
|
+
*
|
|
135
|
+
* Returns `true` when a negative direction should flip a sprite horizontally.
|
|
136
|
+
*/
|
|
37
137
|
export declare function getFlipFromDirection(dir: number): boolean;
|
package/dist/texture.js
CHANGED
|
@@ -3,7 +3,17 @@ const textures = {};
|
|
|
3
3
|
function getresourcepath(path) {
|
|
4
4
|
return "/resources/" + path;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Loads an image and stores it in the texture cache.
|
|
8
|
+
*
|
|
9
|
+
* Normal builds load from `/resources/${src}`. Single-file exports can provide
|
|
10
|
+
* embedded resources through `window.__resources`; if a matching key exists,
|
|
11
|
+
* that data URL is used instead. The cache key always remains the original
|
|
12
|
+
* `src` string, so `getTexture(src)` works the same in both modes.
|
|
13
|
+
*
|
|
14
|
+
* @param src Resource path relative to the resources folder.
|
|
15
|
+
* @returns Promise resolving to the loaded `HTMLImageElement`.
|
|
16
|
+
*/
|
|
7
17
|
export function loadTextureAsync(src) {
|
|
8
18
|
return new Promise((resolve, reject) => {
|
|
9
19
|
// Wenn schon geladen
|
|
@@ -32,11 +42,22 @@ export function loadTextureAsync(src) {
|
|
|
32
42
|
img.src = finalSrc;
|
|
33
43
|
});
|
|
34
44
|
}
|
|
35
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Reads a texture from the cache.
|
|
47
|
+
*
|
|
48
|
+
* This does not start loading by itself. Call `loadTextureAsync` first or handle
|
|
49
|
+
* the `undefined` result.
|
|
50
|
+
*/
|
|
36
51
|
export function getTexture(src) {
|
|
37
52
|
return textures[src];
|
|
38
53
|
}
|
|
39
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Draws a texture to the canvas.
|
|
56
|
+
*
|
|
57
|
+
* The image is drawn around its center for rotation and flipping, but `x` and
|
|
58
|
+
* `y` still describe the top-left target position. If the texture is missing,
|
|
59
|
+
* a magenta fallback rectangle is drawn to make missing assets visible.
|
|
60
|
+
*/
|
|
40
61
|
export function drawTexture(ctx, texture, x, y, options = {}) {
|
|
41
62
|
const { width, height, rotation = 0, flipX = false, flipY = false, scale = 1 } = options;
|
|
42
63
|
if (!texture) {
|
|
@@ -56,17 +77,20 @@ export function drawTexture(ctx, texture, x, y, options = {}) {
|
|
|
56
77
|
ctx.drawImage(texture, -w / 2, -h / 2, w, h);
|
|
57
78
|
ctx.restore();
|
|
58
79
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Loads a texture atlas image and its JSON frame data.
|
|
82
|
+
*
|
|
83
|
+
* The JSON file is fetched from the resources folder and must contain a
|
|
84
|
+
* `frames` object with frame names mapped to rectangles:
|
|
85
|
+
*
|
|
86
|
+
* ```json
|
|
87
|
+
* {
|
|
88
|
+
* "frames": {
|
|
89
|
+
* "player_idle_0": { "x": 0, "y": 0, "width": 32, "height": 32 }
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
70
94
|
export async function loadAtlas(imageSrc, dataSrc) {
|
|
71
95
|
const [image, data] = await Promise.all([
|
|
72
96
|
loadTextureAsync(imageSrc),
|
|
@@ -77,6 +101,11 @@ export async function loadAtlas(imageSrc, dataSrc) {
|
|
|
77
101
|
frames: data.frames
|
|
78
102
|
};
|
|
79
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Draws one named frame from a texture atlas.
|
|
106
|
+
*
|
|
107
|
+
* Missing frame names are logged through `dlog` and skipped.
|
|
108
|
+
*/
|
|
80
109
|
export function drawAtlasFrame(ctx, atlas, frameName, x, y, options = {}) {
|
|
81
110
|
const { width, height, rotation = 0, flipX = false, flipY = false, scale = 1 } = options;
|
|
82
111
|
const frame = atlas.frames[frameName];
|
|
@@ -95,40 +124,28 @@ export function drawAtlasFrame(ctx, atlas, frameName, x, y, options = {}) {
|
|
|
95
124
|
ctx.drawImage(atlas.image, frame.x, frame.y, frame.width, frame.height, -w / 2, -h / 2, w, h);
|
|
96
125
|
ctx.restore();
|
|
97
126
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// // Define Animation
|
|
105
|
-
// const walkAnimation: Animation = {
|
|
106
|
-
// frames: ["walk_0", "walk_1", "walk_2", "walk_3"],
|
|
107
|
-
// fps: 8,
|
|
108
|
-
// loop: true
|
|
109
|
-
// };
|
|
110
|
-
//
|
|
111
|
-
// const player = new AnimationPlayer(walkAnimation);
|
|
112
|
-
//
|
|
113
|
-
// // Game Loop
|
|
114
|
-
// function update(dt: number) {
|
|
115
|
-
// player.update(dt);
|
|
116
|
-
// }
|
|
117
|
-
//
|
|
118
|
-
// function render(ctx: CanvasRenderingContext2D) {
|
|
119
|
-
// drawAnimation(ctx, atlas, player, 100, 100, 64, 64);
|
|
120
|
-
// }
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// Animation Player Class
|
|
127
|
+
/**
|
|
128
|
+
* Keeps track of time and current frame for a named-frame animation.
|
|
129
|
+
*
|
|
130
|
+
* The player only stores animation state. It does not draw anything by itself;
|
|
131
|
+
* call `drawAnimation` after `update(dt)` to render the current atlas frame.
|
|
132
|
+
*/
|
|
124
133
|
export class AnimationPlayer {
|
|
125
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Creates a player for the given animation definition.
|
|
136
|
+
*/
|
|
126
137
|
constructor(animation) {
|
|
127
138
|
this.animation = animation;
|
|
128
139
|
this.time = 0;
|
|
129
140
|
this.currentFrameIndex = 0;
|
|
130
141
|
this.finished = false;
|
|
131
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Advances the animation by `deltaTime` seconds.
|
|
145
|
+
*
|
|
146
|
+
* Looping animations wrap back to the first frame. Non-looping animations
|
|
147
|
+
* stop on their final frame and then report `isFinished() === true`.
|
|
148
|
+
*/
|
|
132
149
|
update(deltaTime) {
|
|
133
150
|
if (this.finished)
|
|
134
151
|
return;
|
|
@@ -148,22 +165,39 @@ export class AnimationPlayer {
|
|
|
148
165
|
}
|
|
149
166
|
}
|
|
150
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Returns the frame name that should currently be rendered from the atlas.
|
|
170
|
+
*/
|
|
151
171
|
getCurrentFrame() {
|
|
152
172
|
return this.animation.frames[this.currentFrameIndex];
|
|
153
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Restarts the animation from the first frame.
|
|
176
|
+
*/
|
|
154
177
|
reset() {
|
|
155
178
|
this.time = 0;
|
|
156
179
|
this.currentFrameIndex = 0;
|
|
157
180
|
this.finished = false;
|
|
158
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns whether a non-looping animation reached its final frame.
|
|
184
|
+
*/
|
|
159
185
|
isFinished() {
|
|
160
186
|
return this.finished;
|
|
161
187
|
}
|
|
162
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Draws the current frame of an `AnimationPlayer`.
|
|
191
|
+
*/
|
|
163
192
|
export function drawAnimation(ctx, atlas, player, x, y, options = {}) {
|
|
164
193
|
const frame = player.getCurrentFrame();
|
|
165
194
|
drawAtlasFrame(ctx, atlas, frame, x, y, options);
|
|
166
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Helper for side-view sprites.
|
|
198
|
+
*
|
|
199
|
+
* Returns `true` when a negative direction should flip a sprite horizontally.
|
|
200
|
+
*/
|
|
167
201
|
export function getFlipFromDirection(dir) {
|
|
168
202
|
return dir < 0; // links = true
|
|
169
203
|
}
|
package/dist/types/button.d.ts
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import { Rect } from "./rectangle.js";
|
|
2
2
|
import { Mouse } from "../input.js";
|
|
3
|
+
/**
|
|
4
|
+
* Simple canvas button model.
|
|
5
|
+
*
|
|
6
|
+
* The button is not a DOM element. It is drawn on the canvas and checked with
|
|
7
|
+
* mouse hit testing.
|
|
8
|
+
*/
|
|
3
9
|
export type Button = {
|
|
10
|
+
/** Rectangle that defines the button position and size. */
|
|
4
11
|
form: Rect;
|
|
12
|
+
/** Text label drawn in the button center. */
|
|
5
13
|
text: string;
|
|
6
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Creates a canvas button description.
|
|
17
|
+
*/
|
|
7
18
|
export declare function makeButton(form: Rect, text: string): Button;
|
|
19
|
+
/**
|
|
20
|
+
* Returns true when the button rectangle was clicked during the current frame.
|
|
21
|
+
*/
|
|
8
22
|
export declare function clickedButton(btn: Button, mouse: Mouse): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Draws a simple filled rectangle button with centered text.
|
|
25
|
+
*/
|
|
9
26
|
export declare function drawButton(btn: Button, ctx: CanvasRenderingContext2D, color?: string, textColor?: string, font?: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if the Button is hovered
|
|
29
|
+
*/
|
|
30
|
+
export declare function isButtonhovered(btn: Button, mouse: Mouse): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Check if the Button is pressed!
|
|
33
|
+
*/
|
|
34
|
+
export declare function isButtonpressed(btn: Button, mouse: Mouse): boolean;
|
package/dist/types/button.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Button
|
|
2
|
+
import { isMouseInRect } from "./rectangle.js";
|
|
2
3
|
import { isRectClicked } from "./rectangle.js";
|
|
3
4
|
import { drawRect } from "../renderer.js";
|
|
4
5
|
import { renderText } from "../renderer.js";
|
|
5
6
|
// Function to create a new Button Type
|
|
7
|
+
/**
|
|
8
|
+
* Creates a canvas button description.
|
|
9
|
+
*/
|
|
6
10
|
export function makeButton(form, text) {
|
|
7
11
|
return {
|
|
8
12
|
form: form,
|
|
@@ -10,10 +14,16 @@ export function makeButton(form, text) {
|
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
// Prüft, ob der Button geklickt wurde
|
|
17
|
+
/**
|
|
18
|
+
* Returns true when the button rectangle was clicked during the current frame.
|
|
19
|
+
*/
|
|
13
20
|
export function clickedButton(btn, mouse) {
|
|
14
21
|
return isRectClicked(mouse, btn.form);
|
|
15
22
|
}
|
|
16
23
|
// Zeichnet den Button (Rechteck + Text)
|
|
24
|
+
/**
|
|
25
|
+
* Draws a simple filled rectangle button with centered text.
|
|
26
|
+
*/
|
|
17
27
|
export function drawButton(btn, ctx, color = "#444", textColor = "white", font = "20px Arial") {
|
|
18
28
|
drawRect(ctx, btn.form, color);
|
|
19
29
|
// Text mittig im Button platzieren
|
|
@@ -23,3 +33,15 @@ export function drawButton(btn, ctx, color = "#444", textColor = "white", font =
|
|
|
23
33
|
ctx.textBaseline = "middle";
|
|
24
34
|
renderText(ctx, btn.text, textX, textY, textColor, font);
|
|
25
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if the Button is hovered
|
|
38
|
+
*/
|
|
39
|
+
export function isButtonhovered(btn, mouse) {
|
|
40
|
+
return isMouseInRect(mouse, btn.form);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if the Button is pressed!
|
|
44
|
+
*/
|
|
45
|
+
export function isButtonpressed(btn, mouse) {
|
|
46
|
+
return isRectClicked(mouse, btn.form);
|
|
47
|
+
}
|