Add HP toggle, new conditions, fix turn order sync bug
- Add DM toggle (default on) to hide player HP bars on player display; persisted in activeDisplay Firestore doc for real-time sync - Add Alchemist Fire and Bardic Inspiration conditions; sort all conditions alphabetically - Fix turn order skipping when participants are deleted, deactivated, or killed mid-combat: turnOrderIds was never updated, causing handleNextTurn to resolve currentIndex as -1 and snap back to the first participant. Now all mutation paths (delete, toggle active, HP death/resurrection) keep turnOrderIds in sync and advance the turn pointer correctly when the current participant is removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+55
-6
@@ -152,6 +152,29 @@ const sortParticipantsByInitiative = (participants, originalOrder) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns turnOrderIds/currentTurnParticipantId updates when a participant leaves active combat.
|
||||||
|
const computeTurnOrderAfterRemoval = (encounter, removedId, updatedParticipants) => {
|
||||||
|
if (!encounter.isStarted) return {};
|
||||||
|
const currentIds = encounter.turnOrderIds || [];
|
||||||
|
const newIds = currentIds.filter(id => id !== removedId);
|
||||||
|
const updates = { turnOrderIds: newIds };
|
||||||
|
if (encounter.currentTurnParticipantId === removedId) {
|
||||||
|
const removedPos = currentIds.indexOf(removedId);
|
||||||
|
const candidates = [...currentIds.slice(removedPos + 1), ...currentIds.slice(0, removedPos)];
|
||||||
|
const nextId = candidates.find(id => updatedParticipants.find(p => p.id === id && p.isActive)) ?? null;
|
||||||
|
updates.currentTurnParticipantId = nextId;
|
||||||
|
}
|
||||||
|
return updates;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns turnOrderIds update when a participant re-enters active combat mid-encounter.
|
||||||
|
const computeTurnOrderAfterAddition = (encounter, addedId) => {
|
||||||
|
if (!encounter.isStarted) return {};
|
||||||
|
const currentIds = encounter.turnOrderIds || [];
|
||||||
|
if (currentIds.includes(addedId)) return {};
|
||||||
|
return { turnOrderIds: [...currentIds, addedId] };
|
||||||
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// CUSTOM HOOKS
|
// CUSTOM HOOKS
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -956,7 +979,10 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters }) {
|
|||||||
const updatedParticipants = participants.filter(p => p.id !== itemToDelete.id);
|
const updatedParticipants = participants.filter(p => p.id !== itemToDelete.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateDoc(doc(db, encounterPath), { participants: updatedParticipants });
|
await updateDoc(doc(db, encounterPath), {
|
||||||
|
participants: updatedParticipants,
|
||||||
|
...computeTurnOrderAfterRemoval(encounter, itemToDelete.id, updatedParticipants)
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error deleting participant:", err);
|
console.error("Error deleting participant:", err);
|
||||||
alert("Failed to delete participant. Please try again.");
|
alert("Failed to delete participant. Please try again.");
|
||||||
@@ -969,12 +995,20 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters }) {
|
|||||||
const toggleParticipantActive = async (participantId) => {
|
const toggleParticipantActive = async (participantId) => {
|
||||||
if (!db) return;
|
if (!db) return;
|
||||||
|
|
||||||
|
const participant = participants.find(p => p.id === participantId);
|
||||||
|
if (!participant) return;
|
||||||
|
const newIsActive = !participant.isActive;
|
||||||
|
|
||||||
const updatedParticipants = participants.map(p =>
|
const updatedParticipants = participants.map(p =>
|
||||||
p.id === participantId ? { ...p, isActive: !p.isActive } : p
|
p.id === participantId ? { ...p, isActive: newIsActive } : p
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const turnUpdates = newIsActive
|
||||||
|
? computeTurnOrderAfterAddition(encounter, participantId)
|
||||||
|
: computeTurnOrderAfterRemoval(encounter, participantId, updatedParticipants);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateDoc(doc(db, encounterPath), { participants: updatedParticipants });
|
await updateDoc(doc(db, encounterPath), { participants: updatedParticipants, ...turnUpdates });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error toggling active state:", err);
|
console.error("Error toggling active state:", err);
|
||||||
}
|
}
|
||||||
@@ -1030,8 +1064,14 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters }) {
|
|||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const turnUpdates = (isDead && !wasDead)
|
||||||
|
? computeTurnOrderAfterRemoval(encounter, participantId, updatedParticipants)
|
||||||
|
: wasResurrected
|
||||||
|
? computeTurnOrderAfterAddition(encounter, participantId)
|
||||||
|
: {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateDoc(doc(db, encounterPath), { participants: updatedParticipants });
|
await updateDoc(doc(db, encounterPath), { participants: updatedParticipants, ...turnUpdates });
|
||||||
setHpChangeValues(prev => ({ ...prev, [participantId]: '' }));
|
setHpChangeValues(prev => ({ ...prev, [participantId]: '' }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error applying HP change:", err);
|
console.error("Error applying HP change:", err);
|
||||||
@@ -1594,10 +1634,19 @@ function InitiativeControls({ campaignId, encounter, encounterPath }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = activePsInOrder.findIndex(p => p.id === encounter.currentTurnParticipantId);
|
let currentIndex = activePsInOrder.findIndex(p => p.id === encounter.currentTurnParticipantId);
|
||||||
let nextIndex = (currentIndex + 1) % activePsInOrder.length;
|
|
||||||
let nextRound = encounter.round;
|
let nextRound = encounter.round;
|
||||||
|
|
||||||
|
// Current participant was removed; find the next one after their old position in turnOrderIds
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
const rawPos = (encounter.turnOrderIds || []).indexOf(encounter.currentTurnParticipantId);
|
||||||
|
const candidateIds = [...(encounter.turnOrderIds || []).slice(rawPos + 1), ...(encounter.turnOrderIds || []).slice(0, rawPos)];
|
||||||
|
const nextP = candidateIds.map(id => activePsInOrder.find(p => p.id === id)).find(Boolean);
|
||||||
|
currentIndex = nextP ? activePsInOrder.findIndex(p => p.id === nextP.id) - 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nextIndex = (currentIndex + 1) % activePsInOrder.length;
|
||||||
|
|
||||||
if (nextIndex === 0 && currentIndex !== -1) {
|
if (nextIndex === 0 && currentIndex !== -1) {
|
||||||
nextRound += 1;
|
nextRound += 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user