traveling

This commit is contained in:
Bastian Wagner
2026-08-23 18:47:11 +02:00
parent 5fa344dbe2
commit bbd5dbbd1f
12 changed files with 251 additions and 9 deletions

View File

@@ -123,6 +123,8 @@ export interface CurrentLocationResponse {
encounterPreview: EncounterPreview[];
rewardPreview: RewardPreview[];
connections: CurrentLocationConnection[];
/** Known region locations that aren't a direct connection from here. */
otherLocations: LocationSummary[];
possibleMonsters: string[];
}

View File

@@ -131,6 +131,7 @@ export function southGateFixture(
danger: 'LOW',
},
],
otherLocations: [],
possibleMonsters: [],
...overrides,
};

View File

@@ -3,7 +3,8 @@
class="location-node"
[class.location-node--current]="current"
[class.location-node--selected]="selected"
[disabled]="disabled"
[class.location-node--unreachable]="!reachable"
[disabled]="disabled || !reachable"
[attr.aria-current]="current ? 'location' : null"
[attr.aria-pressed]="current ? null : selected"
[attr.data-location-key]="location.key"
@@ -12,6 +13,12 @@
<span class="location-node__marker" aria-hidden="true"></span>
<span class="location-node__name">{{ location.name }}</span>
<span class="location-node__state">{{
current ? 'Current Location' : selected ? 'Selected Destination' : 'Reachable'
!reachable
? 'Not Directly Reachable'
: current
? 'Current Location'
: selected
? 'Selected Destination'
: 'Reachable'
}}</span>
</button>

View File

@@ -54,6 +54,19 @@
color: var(--ar-success);
}
.location-node--unreachable {
opacity: 0.55;
}
.location-node--unreachable .location-node__marker {
border-color: var(--ar-border);
box-shadow: 0 0 0 0.15rem rgb(6 8 9 / 0.75);
}
.location-node--unreachable .location-node__state {
color: var(--ar-text-muted);
}
.location-node--selected .location-node__marker,
.location-node:not(:disabled):hover .location-node__marker {
border-color: #e1bd72;

View File

@@ -11,5 +11,6 @@ export class LocationNodeComponent {
@Input() current = false;
@Input() selected = false;
@Input() disabled = false;
@Input() reachable = true;
@Output() readonly choose = new EventEmitter<void>();
}

View File

@@ -31,6 +31,14 @@
(choose)="selectConnection(connection)"
/>
}
@for (otherLocation of location.otherLocations; track otherLocation.id) {
<app-location-node
[class]="'world-page__node world-page__node--' + otherLocation.key"
[location]="otherLocation"
[reachable]="false"
/>
}
</section>
<app-travel-panel

View File

@@ -89,6 +89,35 @@ describe('WorldPageComponent', () => {
expect(element.textContent).toContain('Low');
});
it('shows known region locations that are not directly reachable as distinct, non-interactive nodes', () => {
store.currentLocation.set(
southGateFixture({
connections: [burnedRoadConnection],
otherLocations: [
{
id: 'abandoned-watchpost-id',
key: 'abandoned-watchpost',
name: 'Abandoned Watchpost',
},
],
}),
);
const fixture = TestBed.createComponent(WorldPageComponent);
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
const node = element.querySelector<HTMLButtonElement>(
'[data-location-key="abandoned-watchpost"]',
);
expect(node).not.toBeNull();
expect(node?.disabled).toBe(true);
expect(node?.textContent).toContain('Not Directly Reachable');
node?.click();
expect(store.selectConnection).not.toHaveBeenCalled();
});
it('keeps each location at its geographic position when the authoritative current location swaps', () => {
const fixture = TestBed.createComponent(WorldPageComponent);
fixture.detectChanges();