diff --git a/assets/gamedata/wolfenstein/level-e1m1.json b/assets/gamedata/wolfenstein/level-e1m1.json
index c610450..65956f1 100644
--- a/assets/gamedata/wolfenstein/level-e1m1.json
+++ b/assets/gamedata/wolfenstein/level-e1m1.json
@@ -4,7 +4,7 @@
"name": "First Blood",
"campaignId": null,
"missionIndex": 0,
- "width": 19,
+ "width": 20,
"height": 21,
"cellSize": 64,
"walls": [
@@ -27,6 +27,7 @@
1,
1,
1,
+ 1,
1
],
[
@@ -48,6 +49,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -69,6 +71,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -90,6 +93,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -111,6 +115,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -132,6 +137,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -153,6 +159,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -174,6 +181,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -195,6 +203,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -216,6 +225,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -237,6 +247,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -258,6 +269,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -279,6 +291,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -300,6 +313,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -321,6 +335,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -342,6 +357,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -363,6 +379,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -384,6 +401,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -405,6 +423,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -426,6 +445,7 @@
0,
0,
0,
+ 1,
1
],
[
@@ -447,6 +467,7 @@
1,
1,
1,
+ 1,
1
]
],
@@ -544,5 +565,37 @@
"y": 15,
"frame": 1
}
+ ],
+ "objects": [
+ {
+ "x": 8,
+ "y": 17,
+ "frame": 1
+ },
+ {
+ "x": 8,
+ "y": 19,
+ "frame": 1
+ },
+ {
+ "x": 12,
+ "y": 17,
+ "frame": 1
+ },
+ {
+ "x": 12,
+ "y": 19,
+ "frame": 1
+ },
+ {
+ "x": 8,
+ "y": 9,
+ "frame": 0
+ },
+ {
+ "x": 6,
+ "y": 9,
+ "frame": 0
+ }
]
}
diff --git a/assets/images/wolfenstein/objects.png b/assets/images/wolfenstein/objects.png
new file mode 100644
index 0000000..7e20ca1
Binary files /dev/null and b/assets/images/wolfenstein/objects.png differ
diff --git a/assets/images/wolfenstein/objects.psd b/assets/images/wolfenstein/objects.psd
new file mode 100644
index 0000000..c7ff169
Binary files /dev/null and b/assets/images/wolfenstein/objects.psd differ
diff --git a/assets/images/wolfenstein/pickups.png b/assets/images/wolfenstein/pickups.png
new file mode 100644
index 0000000..705ffbc
Binary files /dev/null and b/assets/images/wolfenstein/pickups.png differ
diff --git a/assets/images/wolfenstein/pickups.psd b/assets/images/wolfenstein/pickups.psd
new file mode 100644
index 0000000..0aef2cc
Binary files /dev/null and b/assets/images/wolfenstein/pickups.psd differ
diff --git a/data/wolfenstein-artwork.json b/data/wolfenstein-artwork.json
index 88a996d..9be42df 100644
--- a/data/wolfenstein-artwork.json
+++ b/data/wolfenstein-artwork.json
@@ -4,7 +4,8 @@
"walls": { "key": "wolfenstein-walls", "path": "assets/images/wolfenstein/walls.png", "frameWidth": 64, "frameHeight": 64 },
"guard": { "key": "wolfenstein-guard-sheet", "path": "assets/images/wolfenstein/enemies.png", "frameWidth": 128, "frameHeight": 128 },
"doors": { "key": "wolfenstein-doors", "path": "assets/images/wolfenstein/doors.png", "frameWidth": 64, "frameHeight": 64 },
- "wallArt": { "key": "wolfenstein-wall-art", "path": "assets/images/wolfenstein/wall-art.png", "frameWidth": 64, "frameHeight": 64 }
+ "wallArt": { "key": "wolfenstein-wall-art", "path": "assets/images/wolfenstein/wall-art.png", "frameWidth": 64, "frameHeight": 64 },
+ "objects": { "key": "wolfenstein-objects", "path": "assets/images/wolfenstein/objects.png", "frameWidth": 64, "frameHeight": 64 }
},
"artwork": [
{ "key": "wolfenstein-item-pistol", "path": null },
diff --git a/data/wolfenstein-objects.json b/data/wolfenstein-objects.json
new file mode 100644
index 0000000..661e174
--- /dev/null
+++ b/data/wolfenstein-objects.json
@@ -0,0 +1,7 @@
+{
+ "_readme": "Frame registry for the objects decal sheet (assets/images/wolfenstein/objects.png, sheets.objects in wolfenstein-artwork.json, 64x64 frames, transparent PNGs). Objects are solid props placed on open floor in the editor — they block movement but not sight/bullets, and can't be picked up. To add a new object: paint the next 64x64 tile into objects.png, then append an entry here — WolfensteinEditor.js's Object dropdown reads this file directly, no code changes needed.",
+ "frames": [
+ { "frame": 0, "id": "tall-bush", "name": "Tall Bush" },
+ { "frame": 1, "id": "gold-eagle", "name": "Gold Eagle" }
+ ]
+}
diff --git a/src/games/wolfenstein/WolfensteinArt.js b/src/games/wolfenstein/WolfensteinArt.js
index 21e1427..23343aa 100644
--- a/src/games/wolfenstein/WolfensteinArt.js
+++ b/src/games/wolfenstein/WolfensteinArt.js
@@ -22,6 +22,7 @@ export function ensureSprites(scene) {
paintBullet(scene);
paintMuzzleFlash(scene);
paintWeaponPistol(scene);
+ paintObject(scene);
}
function paintGuard(scene) {
@@ -94,3 +95,18 @@ function paintWeaponPistol(scene) {
g.generateTexture('wolf-weapon-pistol', W, H);
g.destroy();
}
+
+// One flat placeholder for every object frame (same "single generic
+// stand-in, real art per-frame lands later" idiom as wolf-guard) — a
+// rounded block reads reasonably as "a solid obstacle" regardless of which
+// actual prop (bush, statue, ...) a given frame turns out to be.
+function paintObject(scene) {
+ const S = 64;
+ const g = scene.make.graphics({ x: 0, y: 0, add: false });
+ g.fillStyle(0x3a6b3a, 1);
+ g.fillRoundedRect(S * 0.12, S * 0.1, S * 0.76, S * 0.85, 6);
+ g.lineStyle(3, 0x1f3a1f, 0.8);
+ g.strokeRoundedRect(S * 0.12, S * 0.1, S * 0.76, S * 0.85, 6);
+ g.generateTexture('wolf-object', S, S);
+ g.destroy();
+}
diff --git a/src/games/wolfenstein/WolfensteinEditor.js b/src/games/wolfenstein/WolfensteinEditor.js
index 0cc5fad..8453ab2 100644
--- a/src/games/wolfenstein/WolfensteinEditor.js
+++ b/src/games/wolfenstein/WolfensteinEditor.js
@@ -63,6 +63,7 @@ const FACING_DIRS = {
// applyTool()/WALL_TOOLS is either the selected option's id, or — for a
// dropdown-less category — the category's own id.
const WALLART_PREFIX = 'wallart:';
+const OBJECT_PREFIX = 'object:';
const CATEGORIES = [
{ id: 'wall', label: 'Wall', options: [
@@ -81,6 +82,11 @@ const CATEGORIES = [
{ id: 'door', label: 'Door', options: [
{ id: 'door', label: 'Normal' },
] },
+ // Same dynamic-dropdown treatment as Wall Art (populated from
+ // data/wolfenstein-objects.json, see refreshObjectOptions), except an
+ // object is placed on open FLOOR (walls[y][x] === 0) rather than an
+ // existing wall — the exact complementary requirement to Wall Art's.
+ { id: 'object', label: 'Object', options: [] },
{ id: 'pickup', label: 'Pickup', options: [
{ id: 'health', label: 'Health' },
{ id: 'ammo', label: 'Ammo' },
@@ -150,6 +156,13 @@ export default class WolfensteinEditor extends Phaser.Scene {
this.refreshWallArtOptions();
}).catch(() => {});
+ this.objectFrames = [];
+ fetch('data/wolfenstein-objects.json').then((r) => r.json())
+ .then((d) => {
+ this.objectFrames = d.frames ?? [];
+ this.refreshObjectOptions();
+ }).catch(() => {});
+
this.buildToolbar();
this.buildLoadPanel();
this.buildMinimap();
@@ -169,7 +182,7 @@ export default class WolfensteinEditor extends Phaser.Scene {
campaignId: null, missionIndex: 0,
width: W, height: H, cellSize: 64,
walls, playerStart: { x: 1.5, y: 1.5, angle: 0 },
- doors: [], enemies: [], items: [], wallArt: [],
+ doors: [], enemies: [], items: [], wallArt: [], objects: [],
exit: { x: W - 1.5, y: H - 1.5, radius: 0.6 },
briefing: [],
};
@@ -334,6 +347,7 @@ export default class WolfensteinEditor extends Phaser.Scene {
enemies: this.level.enemies.filter((e) => e.x < w - 1 && e.y < h - 1),
items: this.level.items.filter((it) => it.x < w - 1 && it.y < h - 1),
wallArt: (this.level.wallArt ?? []).filter((wa) => wa.x < w - 1 && wa.y < h - 1),
+ objects: (this.level.objects ?? []).filter((o) => o.x < w - 1 && o.y < h - 1),
playerStart: this.level.playerStart && this.level.playerStart.x < w - 1 && this.level.playerStart.y < h - 1 ? this.level.playerStart : null,
exit: this.level.exit && this.level.exit.x < w - 1 && this.level.exit.y < h - 1 ? this.level.exit : null,
};
@@ -402,6 +416,13 @@ export default class WolfensteinEditor extends Phaser.Scene {
select.innerHTML = this.wallArtFrames.map((f) => ``).join('');
}
+ /** Patches the Object category's