diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index b3c073c..c2c7b48 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -7,7 +7,7 @@ on: jobs: deploy: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/src/main/menu.ts b/src/main/menu.ts index 6a6751f..c9a1f47 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -5,6 +5,7 @@ import { DemoPlayerUi } from "./ui"; import classes from "./menu.scss"; import { InspectorPanel } from "./inspector"; import { ChatPanel } from "./chat"; +import { SeeInvisibility } from "../misc/constants"; export class Menu extends Panel { constructor() { @@ -74,10 +75,13 @@ export class MainMenu extends Menu { this.ui.nerdy_stats.style.display = (this.ui.nerdy_stats.style.display == "block") ? "none" : "block"; this.close(); }); + let vision_button = this.add_basic_button("Set Vision", null, () => { + new SeeInvisibilityMenu(this.ui).put_to_right(vision_button).open(true); + }); this.add_basic_button("Toggle Darkness", null, () => { this.ui.player.toggle_darkness(); this.close(); - }) + }); } } @@ -237,3 +241,25 @@ export class ChatOptionsMenu extends Menu { }); } } + +///For setting the see_invisibility value of the demo player +export class SeeInvisibilityMenu extends Menu { + constructor(public ui : DemoPlayerUi) { + super(); + this.add_basic_button("Minimum Possible Vision", null, () => { + ui.player.set_see_invisible(SeeInvisibility.SEE_INVISIBLE_MINIMUM); + }); + this.add_basic_button("Regular Vision", null, () => { + ui.player.set_see_invisible(SeeInvisibility.SEE_INVISIBLE_LIVING); + }); + this.add_basic_button("Ghost vision", null, () => { + ui.player.set_see_invisible(SeeInvisibility.SEE_INVISIBLE_OBSERVER); + }); + this.add_basic_button("ALL vision", null, () => { + ui.player.set_see_invisible(SeeInvisibility.INVISIBILITY_MAXIMUM); + }); + this.add_basic_button("Debug vision", null, () => { + ui.player.set_see_invisible(SeeInvisibility.INVISIBILITY_ABSTRACT); + }); + } +} diff --git a/src/misc/appearance.ts b/src/misc/appearance.ts index 326fd4c..f044192 100644 --- a/src/misc/appearance.ts +++ b/src/misc/appearance.ts @@ -1,5 +1,5 @@ import { IconStateDir } from "../player/rendering/icon"; -import { RESET_ALPHA, RESET_COLOR, RESET_TRANSFORM } from "./constants"; +import { Planes, RESET_ALPHA, RESET_COLOR, RESET_TRANSFORM } from "./constants"; import { Matrix, matrix_invert, matrix_is_identity, matrix_multiply } from "./matrix"; export enum FilterType { @@ -176,13 +176,23 @@ export type TransitionalAppearance = BaseAppearance 10000) parent_plane = resolve_plane(parent_plane); - if(plane < -10000 || plane > 10000) { + if(parent_plane < Planes.LOWEST_EVER_PLANE || parent_plane > Planes.HIGHEST_EVER_PLANE) parent_plane = resolve_plane(parent_plane); + if(plane < Planes.LOWEST_EVER_PLANE || plane > Planes.HIGHEST_EVER_PLANE) { plane = ((parent_plane + plane + 32767) << 16) >> 16; } return plane; } + /** + * Used to determine if the appearance belongs to a plane that should be invisible when darkness is toggled off. Without darkness there's no reason to see sprites + * that exist to be an alpha mask of the dark. + * @param plane of the appearance as a number + * @returns TRUE if the plane value falls within the range of Byond lighting planes, FALSE if the plane is anything else + */ + export function is_lighting_plane(plane : number): boolean { + return (plane >= Planes.EMISSIVE_BLOCKER_PLANE && plane <= Planes.O_LIGHTING_VISUAL_PLANE) + } + export function get_appearance_parts(appearance : Appearance) { if(appearance.sorted_appearances) return appearance.sorted_appearances; let appearances : Appearance[] = []; @@ -298,7 +308,7 @@ export namespace Appearance { clone(); overlay.blend_mode = appearance.blend_mode; } - if(overlay.plane < -10000 || overlay.plane > 10000) { + if(overlay.plane < Planes.LOWEST_EVER_PLANE || overlay.plane > Planes.HIGHEST_EVER_PLANE) { clone(); overlay.plane = resolve_plane(overlay.plane, appearance.plane); } diff --git a/src/misc/constants.ts b/src/misc/constants.ts index 0497703..04b2fa8 100644 --- a/src/misc/constants.ts +++ b/src/misc/constants.ts @@ -35,3 +35,31 @@ export const SOUND_STREAM = 4; export const SOUND_UPDATE = 16; export const MAX_LAYER : number = 32; + + +///All relevant planes used by Yogstation categorized into an enum of their values +export const enum Planes{ + LOWEST_EVER_PLANE = -10000, + CLICKCATCHER_PLANE = -99, + SPACE_PLANE = -95, + FLOOR_PLANE = -2, + GAME_PLANE = -1, + BLACKNESS_PLANE = 0, + EMISSIVE_BLOCKER_PLANE = 12, + EMISSIVE_PLANE = 13, + EMISSIVE_UNBLOCKABLE_PLANE = 14, + LIGHTING_PLANE = 15, + O_LIGHTING_VISUAL_PLANE = 16, + FLOOR_OPENSPACE_PLANE = 17, + BYOND_LIGHTING_PLANE = 18, + CAMERA_STATIC_PLANE = 19, + HIGHEST_EVER_PLANE = 10000, +} + +export const enum SeeInvisibility{ + SEE_INVISIBLE_MINIMUM = 5, + SEE_INVISIBLE_LIVING = 25, + SEE_INVISIBLE_OBSERVER = 60, + INVISIBILITY_MAXIMUM = 100, + INVISIBILITY_ABSTRACT = 101, +} \ No newline at end of file diff --git a/src/parser/binary_parser.ts b/src/parser/binary_parser.ts index 3976ddf..4805cae 100644 --- a/src/parser/binary_parser.ts +++ b/src/parser/binary_parser.ts @@ -1,5 +1,5 @@ import { Filter, FilterType, ReaderAppearance } from "../misc/appearance"; -import { SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE } from "../misc/constants"; +import { Planes, SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE } from "../misc/constants"; import { Matrix } from "../misc/matrix"; import { DemoParser, ReaderDemoAnimation, ReaderDemoAnimationFrame } from "./base_parser"; import { RevData } from "./interface"; @@ -549,7 +549,7 @@ export class DemoParserBinary extends DemoParser { appearance.vis_flags = p.read_uint8(); } - if(appearance.plane == 15 && !appearance.screen_loc) appearance.blend_mode = 4; // This only exists because I CBA to implement plane masters right now + if(appearance.plane == Planes.LIGHTING_PLANE && !appearance.screen_loc) appearance.blend_mode = 4; // This only exists because I CBA to implement plane masters right now return this.appearance_refs[appearance_ref] = this.appearance_id(appearance); } else { if(appearance_ref == 0xFFFF) return null; diff --git a/src/parser/text_parser.ts b/src/parser/text_parser.ts index bac631c..fe3491b 100644 --- a/src/parser/text_parser.ts +++ b/src/parser/text_parser.ts @@ -1,6 +1,7 @@ import { normalize_ref } from "../misc/ref"; import { ReaderAppearance } from "../misc/appearance"; import { DemoParser } from "./base_parser"; +import { Planes } from "../misc/constants"; const MIN_VERSION = 1; const MAX_VERSION = 1; @@ -247,7 +248,7 @@ export class DemoParserText extends DemoParser { icon_state: `${(((x + y) ^ ~(x * y) + z) % 25 + 25) % 25}`, name: 'space', layer: 1.8, - plane: -95 + plane: Planes.SPACE_PLANE, }; } @@ -267,7 +268,7 @@ export class DemoParserText extends DemoParser { icon_state: '1', name: 'space', layer: 1.8, - plane: -95 + plane: Planes.SPACE_PLANE }; if(p.curr() == 't') { p.idx++; @@ -356,7 +357,7 @@ export class DemoParserText extends DemoParser { if(p.read_next_or_end()) return appearance; if(p.curr() != ';') { appearance.plane = p.read_number(); - if(appearance.plane == 15) appearance.blend_mode = 4; + if(appearance.plane == Planes.LIGHTING_PLANE) appearance.blend_mode = 4; else appearance.blend_mode = 0; } if(p.read_next_or_end()) return appearance; @@ -429,7 +430,7 @@ export class DemoParserText extends DemoParser { p.idx++; if(p.curr() == ']') break; let overlay = this.read_appearance(p, appearance, false); - //if(overlay?.plane == 15) continue; + //if(overlay?.plane == Planes.LIGHTING_PLANE) continue; if(overlay) appearance.underlays.push(this.appearance_id(overlay)); } if(p.curr() == '[' && !appearance.underlays.length) p.idx++; diff --git a/src/player/player.ts b/src/player/player.ts index 19260b3..7e43e7f 100644 --- a/src/player/player.ts +++ b/src/player/player.ts @@ -9,7 +9,7 @@ import { AtlasNode, DmiAtlas } from "./rendering/atlas"; import { IconState, IconStateDir } from "./rendering/icon"; import { CmdViewport, FollowDesc, RenderingCmd } from "./rendering/commands"; import { DrawBuffer } from "./rendering/buffer"; -import { LONG_GLIDE, RESET_ALPHA, RESET_COLOR, RESET_TRANSFORM, SEE_MOBS, SEE_OBJS, SEE_THRU, SEE_TURFS } from "../misc/constants"; +import { LONG_GLIDE, Planes, RESET_ALPHA, RESET_COLOR, RESET_TRANSFORM, SEE_MOBS, SEE_OBJS, SEE_THRU, SEE_TURFS, SeeInvisibility } from "../misc/constants"; import { matrix_is_identity, matrix_multiply } from "../misc/matrix"; import { despam_promise } from "../misc/promise_despammer"; import { view_turfs } from "./view"; @@ -44,7 +44,8 @@ export class DemoPlayer { icon_loader : IconLoader; z_level = 2; - use_index=0; + use_index = 0; + see_invisible = SeeInvisibility.SEE_INVISIBLE_OBSERVER; change_counter = 0; @@ -334,7 +335,7 @@ export class DemoPlayer { height: d_turf_window.top - d_turf_window.bottom } }); - this.draw_object_list(drawing_commands, objects, undefined, followview_window); + this.draw_object_list(drawing_commands, objects, this.see_invisible, followview_window); drawing_commands.push({cmd: "copytoviewport", follow_data, followview_window}); this.last_objects = objects; @@ -452,7 +453,7 @@ export class DemoPlayer { } let root_appearance = thing.get_appearance(this, see_invisible); if(!root_appearance || root_appearance.invisibility > see_invisible) continue; - if(root_appearance.plane == 15 && !this.show_darkness) continue; + if(Appearance.is_lighting_plane(root_appearance.plane) && !this.show_darkness) continue; for(let appearance of Appearance.get_appearance_parts(root_appearance)) { if(!appearance.icon_state_dir) { let dir = this.get_appearance_dir(appearance, buffer.atlas); @@ -965,6 +966,16 @@ export class DemoPlayer { this.show_darkness = !this.show_darkness; this.change_counter++; } + + /** + * Adjust the see_invisibility that you can see on the replay viewer through the vision menu. Invisibility flags is how some objects and items are invisible + to mobs. Like regular living players not being able to see ghosts, or cables under floor tiles + * @param vision_setting a number to determine the sight flag for the viewer. the SeeInvisibility enum has all the relevant vision flag settings + */ + set_see_invisible(vision_setting: number = SeeInvisibility.SEE_INVISIBLE_OBSERVER) { + this.see_invisible = vision_setting; + this.change_counter++; + } } export abstract class Renderable {