From f6c6f616dbabfc5d27e141ef8e4c405ee8e9b088 Mon Sep 17 00:00:00 2001 From: Kenneth VanderLinde Date: Thu, 11 May 2023 23:08:26 -0700 Subject: [PATCH] Flush aura cache when tokens with auras are changed The `drawableAura` cache was optionally flushed based on whether the token currently has light sources or contributions to an illumination. Auras don't interact with illumination, so this was missing tokens that used to have auras but now have no light sources. The new checks separate auras from normal lights. The aura cache is now cleared whenever the token is recognized as an aura source, or just been given an aura. --- .../maptool/client/ui/zone/ZoneView.java | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java index 1ffa4bc088..57ed3549e0 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java @@ -60,7 +60,10 @@ private record IlluminationKey(double multiplier) {} // region These fields track light sources and their illuminated areas. - /** Map light source type to all tokens with that type. */ + /** + * Map light source type to all tokens with that type. Will always have entries for each light + * type, so no need to check whether they exist. + */ private final Map> lightSourceMap = new HashMap<>(); // endregion @@ -135,6 +138,10 @@ private record IlluminationKey(double multiplier) {} */ public ZoneView(Zone zone) { this.zone = zone; + + for (final var type : LightSource.Type.values()) { + lightSourceMap.put(type, new HashSet<>()); + } findLightSources(); new MapToolEventBus().getMainEventBus().register(this); @@ -265,9 +272,7 @@ private IlluminationModel getIlluminationModel(IlluminationKey illuminationKey) // We need to get all lights ordered by lumens. From there, we can do darkness subtraction and // build drawable lights for each, and then build a lumens map. - final var lightSourceTokenGuids = - new ArrayList<>( - lightSourceMap.getOrDefault(LightSource.Type.NORMAL, Collections.emptySet())); + final var lightSourceTokenGuids = lightSourceMap.get(LightSource.Type.NORMAL); final var lightSourceTokens = lightSourceTokenGuids.stream() .map(zone::getToken) @@ -493,9 +498,7 @@ private Illumination getIllumination(IlluminationKey illuminationKey) { } if (token.hasLightSources() - && !lightSourceMap - .getOrDefault(LightSource.Type.NORMAL, Collections.emptySet()) - .contains(token.getId())) { + && !lightSourceMap.get(LightSource.Type.NORMAL).contains(token.getId())) { // This accounts for temporary tokens (such as during an Expose Last Path) personalLights.addAll(calculateLitAreas(token, sight.getMultiplier())); } @@ -642,62 +645,59 @@ public Area getVisibleArea(Token token, PlayerView view) { public List getDrawableAuras() { if (drawableAuras == null) { List lightList = new LinkedList(); - final var auraTokenGUIDs = lightSourceMap.get(LightSource.Type.AURA); - if (auraTokenGUIDs != null) { - for (GUID lightSourceToken : auraTokenGUIDs) { - Token token = zone.getToken(lightSourceToken); - if (token == null) { + for (GUID lightSourceToken : lightSourceMap.get(LightSource.Type.AURA)) { + Token token = zone.getToken(lightSourceToken); + if (token == null) { + continue; + } + Point p = FogUtil.calculateVisionCenter(token, zone); + + for (AttachedLightSource als : token.getLightSources()) { + LightSource lightSource = MapTool.getCampaign().getLightSource(als.getLightSourceId()); + if (lightSource == null) { + continue; + } + // Token can also have non-auras lights, we don't want those. + if (lightSource.getType() != LightSource.Type.AURA) { continue; } - Point p = FogUtil.calculateVisionCenter(token, zone); - for (AttachedLightSource als : token.getLightSources()) { - LightSource lightSource = MapTool.getCampaign().getLightSource(als.getLightSourceId()); - if (lightSource == null) { + Area lightSourceArea = lightSource.getArea(token, zone); + lightSourceArea.transform(AffineTransform.getTranslateInstance(p.x, p.y)); + Area visibleArea = + FogUtil.calculateVisibility( + p, + lightSourceArea, + getTopologyTree(Zone.TopologyType.WALL_VBL), + getTopologyTree(Zone.TopologyType.HILL_VBL), + getTopologyTree(Zone.TopologyType.PIT_VBL)); + + // This needs to be cached somehow + for (Light light : lightSource.getLightList()) { + // If there is no paint, it's a "bright aura" that just shows whatever is beneath it + // and doesn't need to be rendered. + if (light.getPaint() == null) { continue; } - // Token can also have non-auras lights, we don't want those. - if (lightSource.getType() != LightSource.Type.AURA) { + boolean isOwner = token.getOwners().contains(MapTool.getPlayer().getName()); + if ((light.isGM() && !MapTool.getPlayer().isEffectiveGM())) { continue; } - - Area lightSourceArea = lightSource.getArea(token, zone); - lightSourceArea.transform(AffineTransform.getTranslateInstance(p.x, p.y)); - Area visibleArea = - FogUtil.calculateVisibility( - p, - lightSourceArea, - getTopologyTree(Zone.TopologyType.WALL_VBL), - getTopologyTree(Zone.TopologyType.HILL_VBL), - getTopologyTree(Zone.TopologyType.PIT_VBL)); - - // This needs to be cached somehow - for (Light light : lightSource.getLightList()) { - // If there is no paint, it's a "bright aura" that just shows whatever is beneath it - // and doesn't need to be rendered. - if (light.getPaint() == null) { - continue; - } - boolean isOwner = token.getOwners().contains(MapTool.getPlayer().getName()); - if ((light.isGM() && !MapTool.getPlayer().isEffectiveGM())) { - continue; - } - if ((!token.isVisible()) && !MapTool.getPlayer().isEffectiveGM()) { - continue; - } - if (token.isVisibleOnlyToOwner() && !AppUtil.playerOwns(token)) { - continue; - } - if (light.isOwnerOnly() && !isOwner && !MapTool.getPlayer().isEffectiveGM()) { - continue; - } - - // Calculate the area covered by this particular range. - Area lightArea = lightSource.getArea(token, zone, light); - lightArea.transform(AffineTransform.getTranslateInstance(p.x, p.y)); - lightArea.intersect(visibleArea); - lightList.add(new DrawableLight(light.getPaint(), lightArea, light.getLumens())); + if ((!token.isVisible()) && !MapTool.getPlayer().isEffectiveGM()) { + continue; + } + if (token.isVisibleOnlyToOwner() && !AppUtil.playerOwns(token)) { + continue; + } + if (light.isOwnerOnly() && !isOwner && !MapTool.getPlayer().isEffectiveGM()) { + continue; } + + // Calculate the area covered by this particular range. + Area lightArea = lightSource.getArea(token, zone, light); + lightArea.transform(AffineTransform.getTranslateInstance(p.x, p.y)); + lightArea.intersect(visibleArea); + lightList.add(new DrawableLight(light.getPaint(), lightArea, light.getLumens())); } } } @@ -712,7 +712,9 @@ public List getDrawableAuras() { * Find the light sources from all appropriate tokens, and store them in {@link #lightSourceMap}. */ private void findLightSources() { - lightSourceMap.clear(); + for (final var set : lightSourceMap.values()) { + set.clear(); + } for (Token token : zone.getAllTokens()) { if (token.hasLightSources() && token.isVisible()) { @@ -722,9 +724,7 @@ private void findLightSources() { if (lightSource == null) { continue; } - Set lightSet = - lightSourceMap.computeIfAbsent(lightSource.getType(), k -> new HashSet<>()); - lightSet.add(token.getId()); + lightSourceMap.get(lightSource.getType()).add(token.getId()); } } } @@ -832,7 +832,6 @@ public void flush(Token token) { visibleAreaMap.clear(); // TODO Could we instead only clear those views that include the token? drawableLights.clear(); - drawableAuras = null; } else if (token.getHasSight()) { contributedPersonalLightsByToken.remove(token.getId()); // TODO Could we instead only clear those views that include the token? @@ -840,6 +839,12 @@ public void flush(Token token) { exposedAreaMap.clear(); visibleAreaMap.clear(); drawableLights.clear(); + } + + // If the token had auras as well, we'll need to recompute them. This could be more precise + // (i.e., only do the ones for this token) but that's more complicated than it's worth for now. + if (lightSourceMap.get(LightSource.Type.AURA).contains(token.getId()) + || token.hasLightSourceType(LightSource.Type.AURA)) { drawableAuras = null; } } @@ -900,10 +905,7 @@ private void onTokensRemoved(TokensRemoved event) { if (lightSource == null) { continue; } - Set lightSet = lightSourceMap.get(lightSource.getType()); - if (lightSet != null) { - lightSet.remove(token.getId()); - } + lightSourceMap.get(lightSource.getType()).remove(token.getId()); } } @@ -958,12 +960,10 @@ private boolean processTokenAddChangeEvent(List tokens) { if (lightSource != null) { Set lightSet = lightSourceMap.get(lightSource.getType()); if (hasLightSource) { - if (lightSet == null) { - lightSet = new HashSet(); - lightSourceMap.put(lightSource.getType(), lightSet); - } lightSet.add(token.getId()); - } else if (lightSet != null) lightSet.remove(token.getId()); + } else { + lightSet.remove(token.getId()); + } } } hasSight |= token.getHasSight();