Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ best friend, lajbel, can put the correct version name here

### Added

- Added the `charsPerTile` option to the `level()` component, so that you can
make clearer maps without having to resort to Unicode - @dragoncoder047
Comment thread
dragoncoder047 marked this conversation as resolved.
Outdated
- Added `tileMode` option to 9-slice sprites with four tiling strategies:
`'none'` (stretch all), `'edges'` (tile edges only), `'center'` (tile center
only), and `'all'` (tile both edges and center) (#996) - @JustKira
Expand Down Expand Up @@ -64,6 +66,8 @@ best friend, lajbel, can put the correct version name here

### Added

- Added `AreaCompOpt.isSensor`. Areas without body or is sensor will no longer
be eligible for collisions - @mflerackers
- Added `floodFill()` for puzzle games - @mflerackers
- Added `AreaComp.isVisuallyColliding` to test collisions in screen space. This
can be used for fixed objects which do not necessarily collide in world space.
Expand Down
31 changes: 31 additions & 0 deletions examples/multicharLevel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @file Multi-Character Level
* @description Build levels from ASCII
* @difficulty 0
* @tags basics, game
* @minver 4000.0
* @category basics
*/

kaplay();

loadBean();
loadSprite("bobo", "/sprites/bobo.png");
loadSprite("dino", "/sprites/dino.png");
loadSprite("ghosty", "/sprites/ghosty.png");

addLevel([
"bean dino",
" ghos bobo",
], {
pos: vec2(100, 100),
tileWidth: 50,
tileHeight: 50,
charsPerTile: 4,
tiles: {
bean: () => [sprite("bean")],
bobo: () => [sprite("bobo")],
dino: () => [sprite("dino")],
ghos: () => [sprite("ghosty")],
},
});
44 changes: 36 additions & 8 deletions src/ecs/components/level/level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ export interface LevelCompOpt {
sym: string,
pos: Vec2,
) => CompList<Comp> | null | undefined;
/**
* The number of characters that specify one tile.
*
* If the line length isn't a multiple of this, the last tile will be padded
* with spaces to be the same length before things get looked up.
*
* @example
* ```js
* addLevel([
* "BABA IS YOU",
* ], {
* tileWidth: 24,
* tileHeight: 24,
* charsPerTile: 4,
* tiles: {
* "BABA": () => [sprite("text_baba")],
* " IS ": () => [sprite("text_is")],
* "YOU ": () => [sprite("text_you")],
* }
* });
* ```
*
* @default 1
*/
charsPerTile?: number;
}

/**
Expand Down Expand Up @@ -242,7 +267,7 @@ export function level(map: string[], opt: LevelCompOpt): LevelComp {
};

// The connectivity map is used to see whether two locations are connected
// -1: inaccesible n: connectivity group
// -1: inaccessible n: connectivity group
const createConnectivityMap = (level: GameObj<LevelComp>) => {
const size = level.numRows() * level.numColumns();
const traverse = (i: number, index: number) => {
Expand Down Expand Up @@ -344,13 +369,15 @@ export function level(map: string[], opt: LevelCompOpt): LevelComp {
id: "level",

add(this: GameObj<LevelComp>) {
map.forEach((row, i) => {
const keys = row.split("");
numColumns = Math.max(keys.length, numColumns);
keys.forEach((key, j) => {
this.spawn(key, vec2(j, i));
});
});
const charsPerTile = opt.charsPerTile ?? 1;
for (let y = 0; y < map.length; y++) {
const row = map[y]!;
for (let j = 0, x = 0; j < row.length; j += charsPerTile) {
this.spawn(row.slice(j, j + charsPerTile), vec2(x, y));
x++;
if (numColumns < x) numColumns = x;
}
}
},

tileWidth() {
Expand All @@ -370,6 +397,7 @@ export function level(map: string[], opt: LevelCompOpt): LevelComp {

const comps = (() => {
if (typeof key === "string") {
key = key.padEnd(opt.charsPerTile ?? 1);
if (opt.tiles[key]) {
if (typeof opt.tiles[key] !== "function") {
throw new Error(
Expand Down