Skip to content

Commit

Permalink
Add new VisionBlock type 'Behind'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kruptein committed Dec 15, 2023
1 parent 760f369 commit 8f9e85d
Show file tree
Hide file tree
Showing 28 changed files with 301 additions and 90 deletions.
7 changes: 6 additions & 1 deletion client/src/apiTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { AuraId } from "./game/systems/auras/models";
import type { CharacterId } from "./game/systems/characters/models";
import type { ClientId } from "./game/systems/client/models";
import type { PlayerId } from "./game/systems/players/models";
import type { VisionBlock } from "./game/systems/properties/types";
import type { TrackerId } from "./game/systems/trackers/models";

export type ApiShape = ApiAssetRectShape | ApiRectShape | ApiCircleShape | ApiCircularTokenShape | ApiPolygonShape | ApiTextShape | ApiLineShape | ApiToggleCompositeShape
Expand Down Expand Up @@ -149,7 +150,7 @@ export interface ApiCoreShape {
name_visible: boolean;
fill_colour: string;
stroke_colour: string;
vision_obstruction: boolean;
vision_obstruction: VisionBlock;
movement_obstruction: boolean;
is_token: boolean;
annotation: string;
Expand Down Expand Up @@ -659,6 +660,10 @@ export interface ShapeSetDoorToggleModeValue {
shape: GlobalId;
value: "movement" | "vision" | "both";
}
export interface ShapeSetIntegerValue {
shape: GlobalId;
value: number;
}
export interface ShapeSetOptionalStringValue {
shape: GlobalId;
value: string | null;
Expand Down
97 changes: 97 additions & 0 deletions client/src/core/components/ToggleGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script setup lang="ts" generic="T, MS extends boolean">
import { computed } from "vue";
const props = withDefaults(
defineProps<{
// data
options: readonly { label: string; value: T }[];
modelValue: MS extends true ? T[] : T;
// styles
id?: string;
activeColor?: string;
color?: string;
disabled?: boolean;
multiSelect: MS;
}>(),
{
id: undefined,
color: "rgba(236, 242, 255, 0.25)",
activeColor: "rgba(236, 242, 255, 1)",
disabled: false,
},
);
const emit = defineEmits<(e: "update:modelValue", s: MS extends true ? T[] : T) => void>();
const data = computed(() => (props.multiSelect ? props.modelValue : [props.modelValue]) as T[]);
function toggle(option: T): void {
if (props.disabled) return;
let data: MS extends true ? T[] : T;
if (props.multiSelect) {
const arr = props.modelValue as T[];
if (arr.includes(option)) {
data = arr.filter((d) => d !== option) as MS extends true ? T[] : T;
} else {
data = [...arr, option] as MS extends true ? T[] : T;
}
} else {
// idk eslint and typescript are not agreeing with eachother
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
data = option as MS extends true ? T[] : T;
}
emit("update:modelValue", data);
}
</script>

<template>
<div :id="id" class="toggle-group" :class="{ disabled }">
<div
v-for="option of options"
:key="option.label"
:class="{ selected: data.includes(option.value) }"
@click="toggle(option.value)"
>
{{ option.label }}
</div>
</div>
</template>

<style scoped lang="scss">
.toggle-group {
display: flex;
align-items: stretch;
flex-wrap: wrap;
width: fit-content;
overflow: hidden;
border-radius: 1rem;
background-color: v-bind("color");
border: solid 2px v-bind("activeColor");
> div {
padding: 0.5rem 0.7rem;
display: flex;
align-items: center;
&:hover {
cursor: pointer;
background-color: v-bind("activeColor");
}
&.selected {
background-color: v-bind("activeColor");
}
}
&.disabled > {
div:hover {
cursor: not-allowed;
}
:not(.selected):hover {
background-color: inherit;
}
}
}
</style>
9 changes: 7 additions & 2 deletions client/src/game/api/emits/shape/options.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { ShapeSetBooleanValue, ShapeSetOptionalStringValue, ShapeSetStringValue } from "../../../../apiTypes";
import type {
ShapeSetBooleanValue,
ShapeSetIntegerValue,
ShapeSetOptionalStringValue,
ShapeSetStringValue,
} from "../../../../apiTypes";
import { wrapSocket } from "../../helpers";

export const sendShapeSetInvisible = wrapSocket<ShapeSetBooleanValue>("Shape.Options.Invisible.Set");
export const sendShapeSetDefeated = wrapSocket<ShapeSetBooleanValue>("Shape.Options.Defeated.Set");
export const sendShapeSetLocked = wrapSocket<ShapeSetBooleanValue>("Shape.Options.Locked.Set");
export const sendShapeSetIsToken = wrapSocket<ShapeSetBooleanValue>("Shape.Options.Token.Set");
export const sendShapeSetBlocksMovement = wrapSocket<ShapeSetBooleanValue>("Shape.Options.MovementBlock.Set");
export const sendShapeSetBlocksVision = wrapSocket<ShapeSetBooleanValue>("Shape.Options.VisionBlock.Set");
export const sendShapeSetBlocksVision = wrapSocket<ShapeSetIntegerValue>("Shape.Options.VisionBlock.Set");
export const sendShapeSetNameVisible = wrapSocket<ShapeSetBooleanValue>("Shape.Options.NameVisible.Set");
export const sendShapeSetShowBadge = wrapSocket<ShapeSetBooleanValue>("Shape.Options.ShowBadge.Set");

Expand Down
9 changes: 7 additions & 2 deletions client/src/game/api/events/shape/options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { ShapeSetBooleanValue, ShapeSetOptionalStringValue, ShapeSetStringValue } from "../../../../apiTypes";
import type {
ShapeSetBooleanValue,
ShapeSetIntegerValue,
ShapeSetOptionalStringValue,
ShapeSetStringValue,
} from "../../../../apiTypes";
import { UI_SYNC } from "../../../../core/models/types";
import type { Sync } from "../../../../core/models/types";
import { getLocalId, getShape } from "../../../id";
Expand Down Expand Up @@ -64,7 +69,7 @@ socket.on(

socket.on(
"Shape.Options.VisionBlock.Set",
wrapSystemCall<ShapeSetBooleanValue>(propertiesSystem.setBlocksVision.bind(propertiesSystem)),
wrapSystemCall<ShapeSetIntegerValue>(propertiesSystem.setBlocksVision.bind(propertiesSystem)),
);

socket.on(
Expand Down
6 changes: 4 additions & 2 deletions client/src/game/operations/movement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { clientSystem } from "../systems/client";
import { gameState } from "../systems/game/state";
import { teleportZoneSystem } from "../systems/logic/tp";
import { getProperties } from "../systems/properties/state";
import { VisionBlock } from "../systems/properties/types";
import { selectedSystem } from "../systems/selected";
import { locationSettingsState } from "../systems/settings/location/state";
import { initiativeStore } from "../ui/initiative/state";
Expand Down Expand Up @@ -45,7 +46,7 @@ export async function moveShapes(shapes: readonly IShape[], delta: Vector, tempo
shape: shape.id,
});
}
if (props.blocksVision) {
if (props.blocksVision !== VisionBlock.No) {
recalculateVision = true;
visionState.deleteFromTriangulation({
target: TriangulationTarget.VISION,
Expand All @@ -66,7 +67,8 @@ export async function moveShapes(shapes: readonly IShape[], delta: Vector, tempo

if (props.blocksMovement && !temporary)
visionState.addToTriangulation({ target: TriangulationTarget.MOVEMENT, shape: shape.id });
if (props.blocksVision) visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });
if (props.blocksVision !== VisionBlock.No)
visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });

// todo: Fix again
// if (sel.refPoint.x % gridSize !== 0 || sel.refPoint.y % gridSize !== 0) sel.snapToGrid();
Expand Down
6 changes: 4 additions & 2 deletions client/src/game/operations/resize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { GlobalPoint } from "../../core/geometry";
import { sendShapeSizeUpdate } from "../api/emits/shape/core";
import type { IShape } from "../interfaces/shape";
import { getProperties } from "../systems/properties/state";
import { VisionBlock } from "../systems/properties/types";
import { TriangulationTarget, visionState } from "../vision/state";

export function resizeShape(
Expand All @@ -23,7 +24,7 @@ export function resizeShape(
shape: shape.id,
});
}
if (props.blocksVision) {
if (props.blocksVision !== VisionBlock.No) {
recalculateVision = true;
visionState.deleteFromTriangulation({
target: TriangulationTarget.VISION,
Expand All @@ -36,7 +37,8 @@ export function resizeShape(
// todo: think about calling deleteIntersectVertex directly on the corner point
if (props.blocksMovement && !temporary)
visionState.addToTriangulation({ target: TriangulationTarget.MOVEMENT, shape: shape.id });
if (props.blocksVision) visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });
if (props.blocksVision !== VisionBlock.No)
visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });

if (!shape.preventSync) sendShapeSizeUpdate({ shape, temporary });

Expand Down
6 changes: 4 additions & 2 deletions client/src/game/operations/rotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { GlobalPoint } from "../../core/geometry";
import { sendShapePositionUpdate } from "../api/emits/shape/core";
import type { IShape } from "../interfaces/shape";
import { getProperties } from "../systems/properties/state";
import { VisionBlock } from "../systems/properties/types";
import { TriangulationTarget, visionState } from "../vision/state";

export function rotateShapes(
Expand All @@ -24,7 +25,7 @@ export function rotateShapes(
shape: shape.id,
});
}
if (props.blocksVision) {
if (props.blocksVision !== VisionBlock.No) {
recalculateVision = true;
visionState.deleteFromTriangulation({
target: TriangulationTarget.VISION,
Expand All @@ -36,7 +37,8 @@ export function rotateShapes(

if (props.blocksMovement && !temporary)
visionState.addToTriangulation({ target: TriangulationTarget.MOVEMENT, shape: shape.id });
if (props.blocksVision) visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });
if (props.blocksVision !== VisionBlock.No)
visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: shape.id });

if (!shape.preventSync) sendShapePositionUpdate([shape], temporary);
}
Expand Down
8 changes: 6 additions & 2 deletions client/src/game/shapes/shape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { teleportZoneSystem } from "../systems/logic/tp";
import { propertiesSystem } from "../systems/properties";
import { getProperties } from "../systems/properties/state";
import type { ShapeProperties } from "../systems/properties/state";
import { VisionBlock } from "../systems/properties/types";
import { playerSettingsState } from "../systems/settings/players/state";
import { trackerSystem } from "../systems/trackers";
import { trackersFromServer, trackersToServer } from "../systems/trackers/conversion";
Expand Down Expand Up @@ -189,7 +190,10 @@ export abstract class Shape implements IShape {
this.floorId,
false,
);
this._lightBlockingNeighbours = shapeHits;
// Only the behind-blockers need to be tracked as these require extra effort
this._lightBlockingNeighbours = shapeHits.filter(
(s) => getProperties(s)?.blocksVision === VisionBlock.Behind,
);
this._visionPolygon = visibility;
this.visionIteration = visionIteration;
this.recalcVisionBbox();
Expand Down Expand Up @@ -482,7 +486,7 @@ export abstract class Shape implements IShape {

updateShapeVision(alteredMovement: boolean, alteredVision: boolean): void {
const props = getProperties(this.id)!;
if (props.blocksVision && !alteredVision) {
if (props.blocksVision !== VisionBlock.No && !alteredVision) {
visionState.deleteFromTriangulation({
target: TriangulationTarget.VISION,
shape: this.id,
Expand Down
3 changes: 2 additions & 1 deletion client/src/game/shapes/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { floorState } from "../systems/floors/state";
import { groupSystem } from "../systems/groups";
import { positionSystem } from "../systems/position";
import { getProperties } from "../systems/properties/state";
import { VisionBlock } from "../systems/properties/types";
import { selectedSystem } from "../systems/selected";
import type { TrackerId } from "../systems/trackers/models";
import { TriangulationTarget, VisibilityMode, visionState } from "../vision/state";
Expand Down Expand Up @@ -190,7 +191,7 @@ export function deleteShapes(shapes: readonly IShape[], sync: SyncMode): void {
const gId = getGlobalId(sel.id);
if (gId) removed.push(gId);
const props = getProperties(sel.id)!;
if (props.blocksVision) recalculateVision = true;
if (props.blocksVision !== VisionBlock.No) recalculateVision = true;
if (props.blocksMovement) recalculateMovement = true;
sel.layer?.removeShape(sel, { sync: SyncMode.NO_SYNC, recalculate: recalculateIterative, dropShapeId: true });
}
Expand Down
3 changes: 2 additions & 1 deletion client/src/game/shapes/variants/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { LayerName } from "../../models/floor";
import { loadSvgData } from "../../svg";
import { floorSystem } from "../../systems/floors";
import { getProperties } from "../../systems/properties/state";
import { VisionBlock } from "../../systems/properties/types";
import { TriangulationTarget, visionState } from "../../vision/state";
import type { SHAPE_TYPE } from "../types";

Expand Down Expand Up @@ -85,7 +86,7 @@ export class Asset extends BaseRect implements IAsset {
const svgs = await loadSvgData(`/static/assets/${this.options.svgAsset}`);
this.svgData = [...map(svgs.values(), (svg) => ({ svg, rp: this.refPoint, paths: undefined }))];
const props = getProperties(this.id)!;
if (props.blocksVision) {
if (props.blocksVision !== VisionBlock.No) {
if (this.floorId !== undefined) visionState.recalculateVision(this.floorId);
visionState.addToTriangulation({ target: TriangulationTarget.VISION, shape: this.id });
}
Expand Down
5 changes: 3 additions & 2 deletions client/src/game/shapes/variants/polygon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { AuraId } from "../../systems/auras/models";
import { positionState } from "../../systems/position/state";
import { getProperties } from "../../systems/properties/state";
import type { ShapeProperties } from "../../systems/properties/state";
import { VisionBlock } from "../../systems/properties/types";
import type { TrackerId } from "../../systems/trackers/models";
import { visionState } from "../../vision/state";
import { Shape } from "../shape";
Expand Down Expand Up @@ -275,7 +276,7 @@ export class Polygon extends Shape implements IShape {
this.layer?.addShape(
newPolygon,
SyncMode.FULL_SYNC,
props.blocksVision ? InvalidationMode.WITH_LIGHT : InvalidationMode.NORMAL,
props.blocksVision !== VisionBlock.No ? InvalidationMode.WITH_LIGHT : InvalidationMode.NORMAL,
);

this.invalidatePoints();
Expand Down Expand Up @@ -334,7 +335,7 @@ export class Polygon extends Shape implements IShape {
if (invalidate) {
const props = getProperties(this.id)!;
if (this.floorId !== undefined) {
if (props.blocksVision) visionState.recalculateVision(this.floorId);
if (props.blocksVision !== VisionBlock.No) visionState.recalculateVision(this.floorId);
if (props.blocksMovement) visionState.recalculateMovement(this.floorId);
}
if (!this.preventSync) sendShapePositionUpdate([this], false);
Expand Down
5 changes: 3 additions & 2 deletions client/src/game/shapes/variants/toggleComposite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { accessSystem } from "../../systems/access";
import { auraSystem } from "../../systems/auras";
import { getProperties } from "../../systems/properties/state";
import type { ShapeProperties } from "../../systems/properties/state";
import { VisionBlock } from "../../systems/properties/types";
import { selectedSystem } from "../../systems/selected";
import { TriangulationTarget, visionState } from "../../vision/state";
import { Shape } from "../shape";
Expand Down Expand Up @@ -115,7 +116,7 @@ export class ToggleComposite extends Shape implements IToggleComposite {
if (props.isToken) accessSystem.removeOwnedToken(variant.id);
if (props.blocksMovement)
visionState.removeBlocker(TriangulationTarget.MOVEMENT, variant.floorId, variant, true);
if (props.blocksVision)
if (props.blocksVision !== VisionBlock.No)
visionState.removeBlocker(TriangulationTarget.VISION, variant.floorId, variant, true);
if (auraSystem.getAll(variant.id, false).length > 0)
visionState.removeVisionSources(variant.floorId, variant.id);
Expand Down Expand Up @@ -155,7 +156,7 @@ export class ToggleComposite extends Shape implements IToggleComposite {
if (newVariant.floorId !== undefined) {
if (props.blocksMovement)
visionState.addBlocker(TriangulationTarget.MOVEMENT, newVariant.id, newVariant.floorId, true);
if (props.blocksVision)
if (props.blocksVision !== VisionBlock.No)
visionState.addBlocker(TriangulationTarget.VISION, newVariant.id, newVariant.floorId, true);

for (const au of auraSystem.getAll(newVariant.id, false)) {
Expand Down
Loading

0 comments on commit 8f9e85d

Please sign in to comment.