Jede Liste gehoert einem Spieler, CC-Guthaben wird umgewandelt
Ein Gruppen-CC wird fuer jeden Nutzer einzeln gerechnet: fuer den einen kann er der gekaufte Platz sein, fuer den anderen ein Redesign seines vorherigen CCs. Dafuer haengt die Liste jetzt an der Mitgliedschaft statt am CC, und verschieben ordnet nur die eigene Liste. Ein Redesign wird nie aus dem CC-Topf bezahlt. Wer sein CC-Guthaben dafuer einsetzt, wandelt es um: ein Platz weniger, ein bezahltes Redesign mehr. Damit entfaellt die Ausnahme, die sich bisher durch Slot-Pruefung und Kettenrechnung zog. Beim Start werden Buchungen einmalig an die Listen angeglichen.
This commit is contained in:
@@ -769,3 +769,106 @@ describe('Der eigene CC bleibt der eigene', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ein Gruppen-CC zählt für jeden Nutzer einzeln', () => {
|
||||
/// Womit ein bestimmter Spieler diesen CC bezahlt hat.
|
||||
async function bezahltMit(characterId: number, playerId: number): Promise<string | undefined> {
|
||||
const buchung = await db.prisma.creditLedger.findFirst({
|
||||
where: { characterId, playerId, source: 'CONSUME', reversedBy: null },
|
||||
orderBy: { id: 'desc' },
|
||||
});
|
||||
return buchung?.kind;
|
||||
}
|
||||
|
||||
/// Die Liste dieses Spielers, von unten nach oben.
|
||||
async function liste(playerId: number): Promise<string[]> {
|
||||
const eigene = await db.prisma.characterMember.findMany({
|
||||
where: { playerId },
|
||||
orderBy: [{ lineageId: 'asc' }, { chainPosition: 'asc' }],
|
||||
select: { lineageId: true, character: { select: { name: true } } },
|
||||
});
|
||||
return eigene.map((e) => `${e.lineageId.slice(0, 4)}:${e.character.name}`);
|
||||
}
|
||||
|
||||
it('ist für den einen ein Platz und für den anderen ein Redesign', async () => {
|
||||
const wirt = await spielerMitPaket('Zaehlwirt');
|
||||
const gast = await spielerMitPaket('Zaehlgast');
|
||||
const gruppe = await characters.createCharacter(wirt, { name: 'Gemeinsames Werk' }, ctx);
|
||||
const eigener = await characters.createCharacter(gast, { name: 'Sein Erster' }, ctx);
|
||||
|
||||
await credits.bookRedesignPackage(gast, ctx);
|
||||
await characters.addMember(
|
||||
gruppe.character.id,
|
||||
gast,
|
||||
{ useKind: 'REDESIGN_PAID', predecessorId: eigener.character.id },
|
||||
ctx,
|
||||
);
|
||||
|
||||
assert.equal(await bezahltMit(gruppe.character.id, wirt), 'CC', 'für den Wirt sein Platz');
|
||||
assert.equal(
|
||||
await bezahltMit(gruppe.character.id, gast),
|
||||
'REDESIGN_PAID',
|
||||
'für den Gast das Redesign seines ersten CCs',
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await characters.richteBuchungenAus(),
|
||||
0,
|
||||
'beide Sichten sind in sich stimmig und bleiben unangetastet',
|
||||
);
|
||||
|
||||
// Die Rechnung des Gastes: eine Liste mit zwei Einträgen.
|
||||
const stand = await credits.getBalances(gast);
|
||||
assert.equal(stand.CC, 0, 'ein Platz, eine Liste');
|
||||
assert.equal(stand.REDESIGN_PAID, 1, 'von zwei Redesigns eines verbraucht');
|
||||
assert.deepEqual(
|
||||
(await liste(gast)).map((e) => e.split(':')[1]),
|
||||
['Sein Erster', 'Gemeinsames Werk'],
|
||||
'in seiner Liste steht der Gruppen-CC über seinem ersten',
|
||||
);
|
||||
});
|
||||
|
||||
it('lässt jeden für sich verschieben, ohne den anderen anzufassen', async () => {
|
||||
const wirt = await spielerMitPaket('Schiebewirt', 2);
|
||||
const gast = await spielerMitPaket('Schiebegast', 2);
|
||||
const gruppe = await characters.createCharacter(wirt, { name: 'Die Bude' }, ctx);
|
||||
const eigener = await characters.createCharacter(gast, { name: 'Gasts Erster' }, ctx);
|
||||
|
||||
await characters.addMember(gruppe.character.id, gast, { useKind: 'CC' }, ctx);
|
||||
assert.equal(await bezahltMit(gruppe.character.id, gast), 'CC', 'erst ein eigener Platz');
|
||||
|
||||
// Der Gast reiht den Gruppen-CC in seine Liste ein: für ihn wird daraus
|
||||
// ein Redesign seines ersten CCs.
|
||||
await credits.bookRedesignPackage(gast, ctx);
|
||||
await characters.moveInChain(
|
||||
gruppe.character.id,
|
||||
{
|
||||
lineageId: (
|
||||
await db.prisma.characterMember.findFirstOrThrow({
|
||||
where: { playerId: gast, characterId: eigener.character.id },
|
||||
})
|
||||
).lineageId,
|
||||
afterId: eigener.character.id,
|
||||
forPlayerId: gast,
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await bezahltMit(gruppe.character.id, gast),
|
||||
'REDESIGN_PAID',
|
||||
'in seiner Liste ist es jetzt ein Redesign',
|
||||
);
|
||||
assert.equal(
|
||||
await bezahltMit(gruppe.character.id, wirt),
|
||||
'CC',
|
||||
'beim Wirt ändert sich dadurch nichts',
|
||||
);
|
||||
|
||||
// Und der CC selbst bleibt aktiv - der Wirt spielt ihn weiter.
|
||||
const danach = await db.prisma.character.findUniqueOrThrow({
|
||||
where: { id: gruppe.character.id },
|
||||
});
|
||||
assert.equal(danach.status, 'ACTIVE');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user