foundryvtt / foundryvtt

Public issue tracking and documentation for Foundry Virtual Tabletop - software connecting RPG gamers in a shared multiplayer environment with an intuitive interface and powerful API.
https://foundryvtt.com/
238 stars 9 forks source link

Better handling of region transitions #11168

Open caewok opened 3 months ago

caewok commented 3 months ago

User Experience

I would like to be able to create regions that are tied to specific properties, but have a way to transition the token between regions without having the property stripped and then re-added. Two examples I can think of:

  1. Tying an active effect or other property to a region. Ideally, if the token enters the region, the active effect gets added. If the token leaves the region, the active effect gets removed. The problem comes in when there are 2+ regions that are adjacent or overlap. If Region A is adjacent to Region B, I want to have Active Effect 1 removed when leaving Region A unless Active Effect 1 is added by Region B. In other words, I don't want to see: token has effect 1 --> remove effect 1 --> immediately add effect 1.
  2. Setting elevation when a token enters a region. I expand on this example below, but basically you see the same issue: the elevation gets changed and then changed back quite rapidly.

Proposed solution

Not sure I have the perfect answer here. But I am leaning towards some sort of delta check, such that if exit and enter are both triggered at the same point, the token state is resolved to combine the two in a reasonable fashion. For example:

Example

I set up a set of three regions, one at [0, 10] (Region 0), one at [0, 20] (Region 20), and one at [0,30] (Region 30). Each region sets the token to 0 upon exit, and to the maximum elevation of the region (0, 20, or 30) upon enter.

Screenshot 2024-06-09 at 7 17 38 AM

When you move a token at the border of two overlapping zones, the elevation of the token flickers as it gets set to the new elevation and then gets reset to the old elevation. And if you move the token from one region to another where they are adjacent but do not overlap, the token (unsurprisingly) first gets set to 0 and then back to the new elevation. This will also cause flickering if there is elevation specific lighting, walls, etc. Screen Recording 2024-06-09 at 7.44.06 AM.webm

What I think is happening

For example, if I move the token from Region 0 to Region 20 in an area where both overlap, the token's elevation goes from 0 --> 20 --> 0. I think you can break this down as:

  1. Token is at elevation 0.
  2. Token encounters new Region 20 [0, 20]. Token elevation is set to 20.
  3. Because token is moving from 0 -> 20 in elevation, it is exiting Region 0. Token elevation is set to 0.

If I move the token from Region 20 to Region 30 in an area where both are adjacent, the token's elevation goes from 20 --> 0 --> 30. I think you can break this down as:

  1. Token is at elevation 20.
  2. Token exists region 20. Token is set to elevation 0.
  3. Token enters region 30. Token is set to elevation 30.

Why other alternatives are insufficient

I have gamed this out, and none of the options I have thought of thus far really fix the issue of transitioning between regions.

Infinitely-high regions

The closest I have gotten is to set the regions to infinite height, and only use an enter behavior.

This works, in that a token encountering a new region will get set to the correct elevation without "flickering." But it is very constraining on users:

Exit vs non-exit behavior

If you want the token effect or elevation to change on exit, you really need an exit behavior. But this behavior is what causes the problems when transitioning to another region. Ideally, there would be some way to limit exit behavior such that it can be overridden by simultaneous enter behaviors.

Replication Steps

Construct at least 2 regions. Place the 2 regions so one overlaps the other. For example: Region A:

Region B:

Move the token from Region A to Region B in an area where the two overlap.

Here is the elevation macro I am using, which pulls the relevant elevation change from the name of the behavior:

// console.log({behavior, event, scene, region});

const tokenDoc = event.data.token;
if ( !tokenDoc ) return;

const re = / ([+-])?([0-9]+)/
const res = re.exec(behavior.name);
if ( !res ) return;
const modifier = res[1];
const value = Number(res[2]) ?? tokenDoc.elevation;

console.log(`${tokenDoc.name} elevating ${modifier ?? ""}${value}`);

let elevation = tokenDoc.elevation;
switch ( modifier ) {
  case "+": elevation += value; break;
  case "-": elevation -= value; break;
  default: elevation = value;
}
tokenDoc.update({ elevation })

Some more screenshots of the regions:

Screenshot 2024-06-09 at 7 20 05 AM Screenshot 2024-06-09 at 7 19 55 AM Screenshot 2024-06-09 at 7 19 40 AM
caewok commented 3 months ago

FYI, you can get close to handling the elevation case by adding a check to ignore exits if the token has already left the region in question:

// Add to the macro above before changing the elevation
// On Exit, if the token has already moved out of the region, ignore.
if ( event.name === "tokenExit" && !region.object.testPoint(tokenDoc.object.center, tokenDoc.elevation) ) {
  console.log(`Ignoring exit for ${tokenDoc.name}.`);
  console.groupEnd();
  return;
}

This simple version doesn't work where there are multiple overlaps, as in the example screenshot in my OP. You would probably have to run through all regions to accomplish that. And it cannot really handle my other use case: adding/removing properties or active effects, for which a merge would still be necessary.

aaclayton commented 3 months ago

@dev7355608 perhaps you can give a read through this and see if there are any suggestions here that you feel are appropriate for us to action in v12.