Added a new auto-fix for actors atop bombs in CCL levels

This fixes CCLP5 level 25 (and I think one or two others) by introducing
a new item, the dormant bomb, which turns into a regular bomb when
something moves off of it.

Other accumulated tileset touchups that snuck in:

- Recolored the canopy to hopefully look more like a tent
- Lightened slime by one shade
- Made the custom green floor colors clash less, and lightened the
  yellow floor grout
- Removed the shadow from the pause stopwatch, and lightened the other
  two, to better distinguish their different behavior
- Added little rivets to steel walls
- Made the symbols on the sokoban blocks easier to see
- Lightened the blue and red keys to match the shade of the other two,
  and also hopefully make them easier to see atop water and thieves
- Shrank the railroad crossing sign slightly
- Added laces to the hiking boots
- Added shading to the bowling ball
- Removed the shadows from the actor versions of the bowling ball and
  dynamite
- Darkened the dynamite item to better distinguish it from active
  dynamite
- Lightened the blue teleporter exit so it doesn't look too much like an
  inactive red teleporter
- Improved the gradient on the beetle
- Made the hint tile look recessed like CC2, to better convey that it
  blocks some actors
- Added art for some possible tiles: rainbow teleporter, toll gate, nega
  heart, phantom ring, feather
This commit is contained in:
Eevee (Evelyn Woods) 2024-05-04 12:09:48 -06:00
parent e33c35bbe0
commit f6ee09b6c7
5 changed files with 65 additions and 23 deletions

View File

@ -150,6 +150,10 @@ export const COMPAT_FLAG_CATEGORIES = [{
key: 'no_auto_convert_ccl_blue_walls',
label: "Blue walls under blocks are not auto-converted in CCL levels",
rulesets: new Set(['steam-strict', 'lynx', 'ms']),
}, {
key: 'no_auto_convert_ccl_bombs',
label: "Mines under actors are not auto-converted in CCL levels",
rulesets: new Set(['steam-strict', 'lynx', 'ms']),
}],
}, {
title: "Actor behavior",
@ -300,6 +304,7 @@ export const COMPAT_FLAG_CATEGORIES = [{
label: "Bowling balls on cloners are destroyed when fired at point blank",
rulesets: new Set(['steam-strict']),
}, {
// XXX is this necessary, with the addition of the dormant bomb?
key: 'bombs_immediately_detonate_under_players',
label: "Mines under players detonate when the level starts",
rulesets: new Set(['steam-strict']),

View File

@ -142,6 +142,7 @@ export const CC2_TILESET_LAYOUT = {
bomb: [5, 4],
fuse: [7, 4],
},
dormant_bomb: [5, 4], // compat tile, so needs a fallback
green_bomb: {
__special__: 'bomb-fuse',
bomb: [6, 4],
@ -309,7 +310,7 @@ export const CC2_TILESET_LAYOUT = {
},
},
popwall: [8, 10],
popwall2: [8, 10],
popwall2: [8, 10], // compat tile, so needs a fallback
gravel: [9, 10],
ball: {
__special__: 'animated',
@ -897,6 +898,7 @@ export const TILE_WORLD_TILESET_LAYOUT = {
button_blue: [2, 8],
teleport_blue: [2, 9],
bomb: [2, 10],
dormant_bomb: [2, 10], // compat tile, so needs a fallback
trap: {
__special__: 'visual-state',
closed: [2, 11],
@ -905,7 +907,7 @@ export const TILE_WORLD_TILESET_LAYOUT = {
wall_appearing: [2, 12],
gravel: [2, 13],
popwall: [2, 14],
popwall2: [2, 14],
popwall2: [2, 14], // compat tile, so needs a fallback
hint: [2, 15],
cloner: [3, 1],
@ -1335,9 +1337,13 @@ export const LL_TILESET_LAYOUT = {
foil: [2, 17],
xray_eye: [3, 17],
helmet: [4, 17],
phantom_ring: [5, 17],
feather: [6, 17],
dormant_bomb: [7, 17],
skeleton_key: [0, 18],
ankh: [1, 18],
floor_ankh: [2, 18],
toll_gate: [5, 18],
no_sign: [6, 18],
gift_bow: [7, 18],
score_10: [0, 19],
@ -3070,6 +3076,10 @@ export function parse_tile_world_large_tileset(canvas) {
}
ctx.putImageData(image_data, 0, 0);
// These are compat tiles, which need to have a fallback
layout['popwall2'] = layout['popwall'];
layout['dormant_bomb'] = layout['bomb'];
return new Tileset(canvas, layout, tw, th);
}

View File

@ -352,8 +352,8 @@ const TILE_TYPES = {
level.stored_level.format === 'ccl' &&
me.cell.get_actor())
{
// Fix blocks and other actors on top of popwalls by turning them into double
// popwalls, which preserves CC2 popwall behavior
// CCL: Actors who start on popwalls are not intended to activate them when they
// leave, so preserve CC2 behavior by changing them to double popwalls
me.type = TILE_TYPES['popwall2'];
}
},
@ -430,8 +430,8 @@ const TILE_TYPES = {
level.stored_level.format === 'ccl' &&
me.cell.get_actor())
{
// Blocks can be pushed off of blue walls in TW Lynx, which only works due to a tiny
// quirk of the engine that I don't want to replicate, so replace them with popwalls
// CCL: Blocks can be pushed off of blue walls in TW Lynx, but we can replicate the
// behavior with CC2 rules by replacing them with popwalls
// TODO this also works with invis walls apparently. maybe only for blocks?
me.type = TILE_TYPES['popwall'];
}
@ -1062,6 +1062,17 @@ const TILE_TYPES = {
},
bomb: {
layer: LAYERS.item,
on_ready(me, level) {
if (! level.compat.no_auto_convert_ccl_bombs &&
level.stored_level.format === 'ccl' &&
me.cell.get_actor())
{
// CCL: A number of custom levels start an actor on top of a bomb (I guess to
// conserve space), relying on the CC1 behavior that bombs only detonate when
// stepped onto. Replace those with the custom "dormant bomb" tile.
me.type = TILE_TYPES['dormant_bomb'];
}
},
on_arrive(me, level, other) {
if (level.compat.bombs_detonate_on_arrive) {
me.type._detonate(me, level, other);
@ -1083,6 +1094,18 @@ const TILE_TYPES = {
level.kill_actor(other, me, 'explosion', 'bomb', 'exploded');
},
},
// Bomb variant originally added as a CC1 autofix, but which doubles as an experimental item --
// it's dormant until you drop it and move off of it, at which point it becomes a normal bomb
dormant_bomb: {
...COMMON_TOOL,
on_depart(me, level, other) {
// Unlike dynamite, anyone can activate this (important to make it work as CCL compat)
if (me.cell.get_item_mod())
return;
level.transmute_tile(me, 'bomb');
},
},
hole: {
layer: LAYERS.terrain,
on_ready(me, level) {
@ -2800,24 +2823,28 @@ const TILE_TYPES = {
dynamite: {
...COMMON_TOOL,
on_depart(me, level, other) {
if (other.type.is_real_player && ! me.cell.get_item_mod()) {
// FIXME wiki just says about 4.3 seconds; more likely this is exactly 255 frames
level._set_tile_prop(me, 'timer', 85);
level.transmute_tile(me, 'dynamite_lit');
// Actors are expected to have this, so populate it
level._set_tile_prop(me, 'movement_cooldown', 0);
level.add_actor(me);
// Dynamite inherits a copy of the player's inventory, which largely doesn't matter
// except for suction boots, helmet, or lightning bolt; keys can't matter because
// dynamite is blocked by doors
if (other.toolbelt) {
level._set_tile_prop(me, 'toolbelt', [...other.toolbelt]);
}
// Dynamite that lands on a force floor is moved by it, and dynamite that lands on a
// button holds it down
// TODO is there anything this should NOT activate?
level.step_on_cell(me, me.cell);
if (! other.type.is_real_player)
return;
if (me.cell.get_item_mod())
return;
// XXX wiki just says about 4.3 seconds; more likely this is exactly 255 frames (and
// there haven't been any compat problems so far...)
level._set_tile_prop(me, 'timer', 85);
level.transmute_tile(me, 'dynamite_lit');
// Actors are expected to have this, so populate it
level._set_tile_prop(me, 'movement_cooldown', 0);
level.add_actor(me);
// Dynamite inherits a copy of the player's inventory, which largely doesn't matter
// except for suction boots, helmet, or lightning bolt; keys can't matter because
// dynamite is blocked by doors
if (other.toolbelt) {
level._set_tile_prop(me, 'toolbelt', [...other.toolbelt]);
}
// Dynamite that lands on a force floor is moved by it, and dynamite that lands on a
// button holds it down
// TODO is there anything this should NOT activate?
level.step_on_cell(me, me.cell);
},
},
dynamite_lit: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.