M&6&/;5i7]>dzw4E{GskZ`!$PVEI]O(/m¶ M0PXYO *މb+iq+ fHx(VFuKvH[aѣqS^UA4m?:cv=CKYVZ|ZT(4p ti3GkzWy&+U մTh 'ʅ FAӣP>Deb*rrm3WdU>v}~[1/>u%4PyyᔫnPqtZ[Ip*0;>W_}}GcLff/N#^ LK pR[1BqIa9F.H3bfjM{aӾt'&[/gxAOt4^}v0Ii)b ]Ҡ-*mC*r/6m!os(舕=(`z(7B"jbB A•c0 5WDoPc'aEGFMD}y>܍pB؁[#2)^ETW>6A+rC9utr >!T8: ۙ 蘆8yf%k? Rd7  U_o/K/K{sEmo19/(uC`hυFU5"xoww~JO&_#Yֺ EL[nb#GHŗS@ɀxk(!ze,nє8D!Rb|@1M4@Zj~cNI;AW>mi6*MMF+͎]<)]{/Dt6+h+x?oU{Ng=̳ygL+_י`JqMtJS|襎s)=jZZE8^mN}tN x;iR5T-OqF.mDG63KsM Buv| )P@5fl8m3miϞje˱u&SEt"K0yWYwe~ŤM/eku@ԇ:Cgj~57eg^Z/K旱t,Jot?rYu^֋٩gqmRԒd8CD7 >Ztk{U: ؀ٮ{N?Rfb/,0jh^%b&;3nzy>̶FzzS&nz֏!T?'1 Fc%}0ҚE19w]|S(iD1Yr ulBܐԡg8{v?:1Jt=r8:-(ab* SQ)۔&OgB%QA^6_[d5)|0ZֽkwdsˈuE5;z(Aə׊Nmt e&K<^]57oȩ?sVp7wէpsM8z7ec ԋIBP@үd$jP'X 9< _|+0񸷩F-5Bd υgk)ll hSHDK񞀓*6>hkMŃYޟ_o@H*4 Ygx̎.kꕸAg hw{mE{R!xE6l:aԤZ,CnfּQ32H}$A(pjݹі$`i_P:/0A\<$Z}X,uFH9Ŧf4a6}B1xOY愳 tF tIz75Dc"#Xܭ`lt z~ZLLӃe4 H 5-b|6`/i}: `s4g[2 LsB8yo{^ݎ|KFl}k, [wsh43vF0Q7 ՠu.hywvp]h"Jtj}Y=^ų#B9x_Pj58<#$4yQͣ E+ԣrй$DΧ-4Ffѿϖ<(:2x$46iDHDPˤdm|,=W*‚X֥Un#o#&e3"~b߽To~\1j; S[T-퉢i`[:Zܦ/׮PD}178 !diޡ1yib!]75f̑`F#( nBn)%\LC_g"" IB[pE%A , DP@ Z4;;fXUr`mû+d>I`joO`gLOӇ_n:M^7Nu;9f"rn false; } return blocksMotion; } @override double getFloorHeight(Entity entity) { return -0.5; } @override double getWalkSpeed(Player player) { return 0.4; } } class TorchBlock extends Block { Sprite torchSprite; TorchBlock() { torchSprite = new Sprite(0.0, 0.0, 0.0, 3, 0xFFFF00); sprites.add(torchSprite); } @override void decorate(Level level, int x, int y) { var torchRand = new Math.Random((x + y * 1000) * 341871231); double r = 0.4; for (int i = 0; i < 1000; i++) { int face = torchRand.nextInt(4); if (face == 0 && level.getBlock(x - 1, y).solidRender) { torchSprite.x -= r; break; } if (face == 1 && level.getBlock(x, y - 1).solidRender) { torchSprite.z -= r; break; } if (face == 2 && level.getBlock(x + 1, y).solidRender) { torchSprite.x += r; break; } if (face == 3 && level.getBlock(x, y + 1).solidRender) { torchSprite.z += r; break; } } } @override void tick() { super.tick(); if (rand.nextInt(4) == 0) { torchSprite.tex = 3 + rand.nextInt(2); } } } class BarsBlock extends Block { Sprite sprite; bool open = false; BarsBlock() { sprite = new Sprite(0.0, 0.0, 0.0, 0, 0x606060); addSprite(sprite); blocksMotion = true; } @override bool use(Level level, Item item) { if (open) return false; if (item == Item.cutters) { Sound.cut.play(); sprite.tex = 1; open = true; } return true; } @override bool blocks(Entity entity) { if (open && entity is Player) return false; if (open && entity is Bullet) return false; return blocksMotion; } } class LadderBlock extends Block { static final int LADDER_COLOR = 0xDB8E53; bool wait = false; LadderBlock(bool down) { if (down) { floorTex = 1; addSprite(new Sprite(0.0, 0.0, 0.0, 8 + 3, LADDER_COLOR)); } else { ceilTex = 1; addSprite(new Sprite(0.0, 0.0, 0.0, 8 + 4, LADDER_COLOR)); } } @override void removeEntity(Entity entity) { super.removeEntity(entity); if (entity is Player) { wait = false; } } @override void addEntity(Entity entity) { super.addEntity(entity); if (!wait && entity is Player) { level.switchLevel(id); Sound.ladder.play(); } } } class LootBlock extends Block { bool taken = false; Sprite sprite; LootBlock() { sprite = new Sprite(0.0, 0.0, 0.0, 16 + 2, 0xFFFF80); addSprite(sprite); blocksMotion = true; } @override void addEntity(Entity entity) { super.addEntity(entity); if (!taken && entity is Player) { sprite.removed = true; taken = true; blocksMotion = false; entity.loot++; Sound.pickup.play(); } } @override bool blocks(Entity entity) { if (entity is Player) return false; return blocksMotion; } } class DoorBlock extends SolidBlock { bool open = false; double openness = 0.0; DoorBlock() { tex = 4; solidRender = false; } @override bool use(Level level, Item item) { open = !open; if (open) { Sound.click1.play(); } else { Sound.click2.play(); } return true; } @override void tick() { super.tick(); openness = Math.min(1.0, Math.max(0.0, openness + (open ? 0.2 : -0.2))); double openLimit = 7 / 8; if (openness < openLimit && !open && !blocksMotion) { if (level.containsBlockingEntity(x - 0.5, y - 0.5, x + 0.5, y + 0.5)) { openness = openLimit; return; } } blocksMotion = openness < openLimit; } @override bool blocks(Entity entity) { double openLimit = 7 / 8; if (openness >= openLimit && entity is Player) return blocksMotion; if (openness >= openLimit && entity is Bullet) return blocksMotion; if (openness >= openLimit && entity is OgreEntity) return blocksMotion; return true; } } class SwitchBlock extends SolidBlock { bool pressed = false; SwitchBlock() { tex = 2; } @override bool use(Level level, Item item) { pressed = !pressed; tex = pressed ? 3 : 2; level.trigger(id, pressed); if (pressed) { Sound.click1.play(); } else { Sound.click2.play(); } return true; } } class PressurePlateBlock extends Block { bool pressed = false; PressurePlateBlock() { floorTex = 2; } @override void tick() { super.tick(); double r = 0.2; bool steppedOn = level.containsBlockingNonFlyingEntity(x - r, y - r, x + r, y + r); if (steppedOn != pressed) { pressed = steppedOn; floorTex = pressed ? 3 : 2; level.trigger(id, pressed); if (pressed) { Sound.click1.play(); } else { Sound.click2.play(); } } } @override double getFloorHeight(Entity entity) { return pressed ? -0.02 : 0.02; } } class IceBlock extends Block { IceBlock() { blocksMotion = false; floorTex = 16; } @override void tick() { super.tick(); floorCol = 0x8080FF; } @override double getWalkSpeed(Player player) { return player.getSelectedItem() == Item.skates ? 0.05 : 1.4; } @override double getFriction(Player player) { return player.getSelectedItem() == Item.skates ? 0.98 : 1.0; } @override bool blocks(Entity entity) { if (entity is Player) return false; if (entity is Bullet) return false; if (entity is EyeBossEntity) return false; if (entity is EyeEntity) return false; return true; } } class LockedDoorBlock extends DoorBlock { LockedDoorBlock() { tex = 5; } @override bool use(Level level, Item item) => false; @override void trigger(bool pressed) { open = pressed; } } class AltarBlock extends Block { bool filled = false; Sprite sprite; AltarBlock() { blocksMotion = true; sprite = new Sprite(0.0, 0.0, 0.0, 16 + 4, 0xE2FFE4); addSprite(sprite); } @override void addEntity(Entity entity) { super.addEntity(entity); if (!filled && (entity is GhostEntity || entity is GhostBossEntity)) { entity.remove(); filled = true; blocksMotion = false; sprite.removed = true; for (int i = 0; i < 8; i++) { RubbleSprite sprite = new RubbleSprite(); sprite.col = this.sprite.col; addSprite(sprite); } if (entity is GhostBossEntity) { level.addEntity(new KeyEntity(x.toDouble(), y.toDouble())); Sound.bosskill.play(); } else { Sound.altar.play(); } } } } class SpiritWallBlock extends Block { SpiritWallBlock() { floorTex = 7; ceilTex = 7; blocksMotion = true; for (int i = 0; i < 6; i++) { var x = rand.nextDouble() - 0.5; var y = (rand.nextDouble() - 0.7) * 0.3; var z = rand.nextDouble() - 0.5; addSprite(new Sprite(x, y, z, 4 * 8 + 6 + rand.nextInt(2), 0x202020)); } } @override bool blocks(Entity entity) { if (entity is Bullet) return false; return super.blocks(entity); } } class FinalUnlockBlock extends SolidBlock { bool pressed = false; FinalUnlockBlock() { tex = 8 + 3; } @override bool use(Level level, Item item) { if (pressed) return false; if (level.player.keys < 4) return false; Sound.click1.play(); pressed = true; level.trigger(id, true); return true; } } class WinBlock extends Block { @override void addEntity(Entity entity) { super.addEntity(entity); if (entity is Player) { entity.win(); } } }