add carcadr level
Some checks failed
continuous-integration/drone Build is failing

This commit is contained in:
Elizabeth Hunt 2025-03-01 15:31:27 -07:00
parent 309ed20627
commit 07f508365f
Signed by: simponic
GPG Key ID: 2909B9A7FF6213EE
10 changed files with 118 additions and 25 deletions

View File

@ -21,7 +21,7 @@ export const App = () => {
<div className="footer"> <div className="footer">
<span> <span>
built by{" "} built by{" "}
<a href="https://github.com/simponic" target="_blank" className="tf"> <a href="https://git.simponic.xyz/simponic" target="_blank" className="tf">
simponic simponic
</a>{" "} </a>{" "}
| inspired by{" "} | inspired by{" "}

View File

@ -11,7 +11,9 @@ export interface GameCanvasProps {
export const GameCanvas = ({ width, height }: GameCanvasProps) => { export const GameCanvas = ({ width, height }: GameCanvasProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
const [game, setGame] = useState<TheAbstractionEngine>(); const [game, setGame] = useState<TheAbstractionEngine>();
const [ready, setReady] = useState(false); // TODO: go back to this after done
// const [ready, setReady] = useState(false);
const [ready, setReady] = useState(document.location.hostname.includes("localhost"));
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {

View File

@ -55,7 +55,7 @@ export class Sprite extends Component implements Renderable {
ctx.save(); ctx.save();
ctx.translate(center.x, center.y); ctx.translate(center.x, center.y);
if (rotation != undefined && rotation != 0) { if (typeof rotation !== "undefined" && rotation != 0) {
ctx.rotate(rotation * (Math.PI / 180)); ctx.rotate(rotation * (Math.PI / 180));
} }
ctx.translate(-center.x, -center.y); ctx.translate(-center.x, -center.y);
@ -64,6 +64,12 @@ export class Sprite extends Component implements Renderable {
ctx.globalAlpha = opacity; ctx.globalAlpha = opacity;
} }
ctx.drawImage(
this.sheet,
...this.getSpriteArgs(),
...this.getDrawArgs(drawArgs),
);
if (backgroundText) { if (backgroundText) {
// draw text // draw text
const { fillStyle, font, textAlign, text } = backgroundText; const { fillStyle, font, textAlign, text } = backgroundText;
@ -75,12 +81,6 @@ export class Sprite extends Component implements Renderable {
ctx.fillText(text, center.x, center.y + height / 2); ctx.fillText(text, center.x, center.y + height / 2);
} }
ctx.drawImage(
this.sheet,
...this.getSpriteArgs(),
...this.getDrawArgs(drawArgs),
);
if (tint) { if (tint) {
ctx.globalAlpha = 0.5; ctx.globalAlpha = 0.5;
ctx.globalCompositeOperation = "source-atop"; ctx.globalCompositeOperation = "source-atop";

View File

@ -155,7 +155,6 @@ export class FunctionApplication extends Entity {
return; return;
} }
const { dimension } = gridSystem;
const nextPosition = gridSystem.getNewGridPosition( const nextPosition = gridSystem.getNewGridPosition(
grid.gridPosition, grid.gridPosition,
entityGrid.previousDirection entityGrid.previousDirection
@ -194,6 +193,7 @@ export class FunctionApplication extends Entity {
} }
SOUNDS.get(LambdaTransformSound.name)!.play(); SOUNDS.get(LambdaTransformSound.name)!.play();
const { dimension } = gridSystem;
const particles = new Particles({ const particles = new Particles({
center: gridSystem.gridToScreenPosition(nextPosition), center: gridSystem.gridToScreenPosition(nextPosition),
spawnerDimensions: { spawnerDimensions: {

View File

@ -0,0 +1,60 @@
import { Level, LevelNames } from ".";
import { Game } from "..";
import {
Curry,
FunctionApplication,
Grass,
LambdaFactory,
LockedDoor,
Player,
Wall,
} from "../entities";
import { Grid, SystemNames } from "../systems";
import { normalRandom } from "../utils";
export class CarCadr extends Level {
constructor() {
super(LevelNames.CarCadr);
}
public init(game: Game) {
const grid = game.getSystem<Grid>(SystemNames.Grid);
const dimensions = grid.getGridDimensions();
const grasses = Array.from({ length: dimensions.width })
.fill(0)
.map(() => {
// random grass
return new Grass({
x: Math.floor(
normalRandom(dimensions.width / 2, dimensions.width / 4, 1.5),
),
y: Math.floor(
normalRandom(dimensions.height / 2, dimensions.height / 4, 1.5),
),
});
});
const entities = [
...grasses,
new Player({ x: 9, y: 5 }),
new Wall({ x: 4, y: 3 }),
new Wall({ x: 4, y: 4 }),
new LambdaFactory({ x: 6, y: 4 }, "(\\ (x) . x)", 1),
new FunctionApplication({ x: 5, y: 5 }, "(_INPUT _KEY)"),
new Wall({ x: 4, y: 5 }),
new Wall({ x: 6, y: 5 }),
new FunctionApplication({ x: 4, y: 6 }, "(_INPUT _NULL)"),
new Wall({ x: 6, y: 7 }),
new Wall({ x: 5, y: 7 }),
new Wall({ x: 4, y: 7 }),
new LockedDoor({ x: 3, y: 8 }),
new Curry({ x: 3, y: 9 }),
new Wall({ x: 2, y: 9 }),
new Wall({ x: 4, y: 9 }),
new Wall({ x: 3, y: 10 }),
];
entities.forEach((entity) => game.addEntity(entity));
}
}

View File

@ -1,4 +1,5 @@
export namespace LevelNames { export namespace LevelNames {
export const Tutorial = "0"; export const Tutorial = "0";
export const CarCadr = "1";
export const LevelSelection = "LevelSelection"; export const LevelSelection = "LevelSelection";
} }

View File

@ -1,9 +1,12 @@
import { LEVELS, Level, LevelNames } from "."; import { LEVELS, Level, LevelNames } from ".";
import { Game } from ".."; import { Game } from "..";
import { Player, Portal } from "../entities"; import { Grass, Player, Portal } from "../entities";
import { Grid, Level as LevelSystem, SystemNames } from "../systems"; import { Grid, Level as LevelSystem, SystemNames } from "../systems";
import { normalRandom } from "../utils";
export class LevelSelection extends Level { export class LevelSelection extends Level {
public static RADIUS = 5;
constructor() { constructor() {
super(LevelNames.LevelSelection); super(LevelNames.LevelSelection);
} }
@ -11,23 +14,43 @@ export class LevelSelection extends Level {
public init(game: Game): void { public init(game: Game): void {
const gridSystem = game.getSystem<Grid>(SystemNames.Grid); const gridSystem = game.getSystem<Grid>(SystemNames.Grid);
const center = gridSystem.getCenterGrid(); const center = gridSystem.getCenterGrid();
const dimensions = gridSystem.getGridDimensions();
const levelSystem = game.getSystem<LevelSystem>(SystemNames.Level); const levelSystem = game.getSystem<LevelSystem>(SystemNames.Level);
const unlocked = levelSystem.getUnlockedLevels(); const unlocked = levelSystem.getUnlockedLevels();
LEVELS.forEach((level, i) => { const renderableLevels = LEVELS.filter(
if ( ({ name }) => name !== LevelNames.LevelSelection
!unlocked.has(level.name) || );
level.name === LevelNames.LevelSelection const radiansPerLevel = (2 * Math.PI) / renderableLevels.length;
) { renderableLevels
return; .filter(({ name }) => unlocked.has(name))
} .map((level, i) => {
const radians = i * radiansPerLevel;
const portal = new Portal(level.name, { x: i, y: 7 }); const coords = {
game.addEntity(portal); x: Math.floor(Math.cos(radians) * LevelSelection.RADIUS + center.x),
}); y: Math.floor(Math.sin(radians) * LevelSelection.RADIUS + center.y),
};
return new Portal(level.name, coords);
})
.forEach((e) => game.addEntity(e));
const player = new Player(center); const player = new Player(center);
game.addEntity(player); game.addEntity(player);
Array.from({ length: dimensions.width })
.fill(0)
.map(() => {
// random grass
return new Grass({
x: Math.floor(
normalRandom(dimensions.width / 2, dimensions.width / 4, 1.5)
),
y: Math.floor(
normalRandom(dimensions.height / 2, dimensions.height / 4, 1.5)
),
});
})
.forEach((e) => game.addEntity(e));
} }
} }

View File

@ -43,7 +43,7 @@ export class Tutorial extends Level {
{ x: 4, y: 3 }, { x: 4, y: 3 },
), ),
new Sign( new Sign(
"this is a Term Application; interact to view its code<br><br>push the term ➡️ created by the factory any direction into the Application to produce a new one 💭<br><br>note that:<br><br>+ _INPUT is the term replaced by the pushed term<br><br>+ in this case _KEY is applied to the function to make a new KEY! 🔑", "<div>this is a Term Application; interact to view its code<br><br>push the term ➡️ created by the factory any direction into the Application to produce a new one 💭<br><br>note that:<br><br>+ _INPUT is the term replaced by the pushed term<br><br>+ in this case _KEY is applied to the function to make a new KEY! 🔑</div>",
{ x: 4, y: 6 }, { x: 4, y: 6 },
), ),
new Wall({ x: 10, y: 9 }), new Wall({ x: 10, y: 9 }),

View File

@ -2,11 +2,17 @@ export * from "./LevelNames";
export * from "./Level"; export * from "./Level";
export * from "./LevelSelection"; export * from "./LevelSelection";
export * from "./Tutorial"; export * from "./Tutorial";
export * from "./CarCadr";
import { LevelNames } from "."; import { LevelNames } from ".";
import { LevelSelection, Tutorial, Level } from "."; import { CarCadr, LevelSelection, Tutorial, Level } from ".";
export const LEVELS: Level[] = [new LevelSelection(), new Tutorial()]; export const LEVELS: Level[] = [
new LevelSelection(),
new Tutorial(),
new CarCadr(),
];
export const LEVEL_PROGRESSION: Record<string, string[]> = { export const LEVEL_PROGRESSION: Record<string, string[]> = {
[LevelNames.LevelSelection]: [LevelNames.Tutorial], [LevelNames.LevelSelection]: [LevelNames.Tutorial],
[LevelNames.Tutorial]: [LevelNames.CarCadr],
}; };

View File

@ -3,6 +3,7 @@ import { Game } from "..";
import { type Level as LevelType, LEVELS, LEVEL_PROGRESSION } from "../levels"; import { type Level as LevelType, LEVELS, LEVEL_PROGRESSION } from "../levels";
export class Level extends System { export class Level extends System {
// TODO: read from localstorage
private unlockedLevels: Set<string> = new Set(); private unlockedLevels: Set<string> = new Set();
private currentLevel: LevelType | null = null; private currentLevel: LevelType | null = null;
private moveToLevel: string | null; private moveToLevel: string | null;