Implement replay format v3

This stores deck data in a more efficient manner, and includes deck names, sleeves and (currently unused) upgrade level fields.
This commit is contained in:
Andrio Celos 2023-10-10 21:58:59 +11:00
parent 3a1df937f4
commit 4a2bd4d5f9
3 changed files with 99 additions and 32 deletions

View File

@ -39,6 +39,7 @@ let currentReplay: {
}[],
turns: Move[][],
}[],
decks: Deck[],
turns: Move[][],
placements: PlacementResults[],
watchingPlayer: number

View File

@ -15,7 +15,7 @@ class ReplayLoader {
throw new Error('Game data not loaded');
const version = this.readUint8();
const players = [ ];
const players: Player[] = [ ];
let goalWinCount = null;
switch (version) {
case 1: {
@ -35,7 +35,7 @@ class ReplayLoader {
const initialDrawOrder = [ ];
const drawOrder = [ ];
for (let j = 0; j < 15; j++) {
cards.push(this.loadCardFromReplay());
cards.push(this.readCard());
}
for (let j = 0; j < 2; j++) {
const n = this.readUint8();
@ -68,7 +68,7 @@ class ReplayLoader {
playerData.push({ deck: new Deck("Deck", 0, cards, new Array(15)), initialDrawOrder, drawOrder, won: false });
}
const turns = this.replayLoadTurns(numPlayers);
const turns = this.readTurns(numPlayers);
currentReplay.games.push({ stage, playerData, turns });
break;
}
@ -79,27 +79,7 @@ class ReplayLoader {
if (goalWinCount == 0) goalWinCount = null;
currentReplay = { gameNumber: 0, decks: [ ], games: [ ], turns: [ ], placements: [ ], watchingPlayer: 0 };
for (let i = 0; i < numPlayers; i++) {
const colour = this.readColour();
const specialColour = this.readColour();
const specialAccentColour = this.readColour();
const n2 = this.readUint8();
const len = n2 & 0x7F;
const player = {
name: this.readString(len),
specialPoints: 0,
isReady: false,
colour,
specialColour,
specialAccentColour,
uiBaseColourIsSpecialColour: (n2 & 0x80) != 0,
sleeves: 0,
totalSpecialPoints: 0,
passes: 0,
gamesWon: 0
};
players.push(player);
}
this.readPlayers(numPlayers, players);
while (this.pos < this.dataView.byteLength) {
const stage = stageDatabase.stages[this.readUint8()];
@ -110,7 +90,7 @@ class ReplayLoader {
const drawOrder = [ ];
let won = false;
for (let j = 0; j < 15; j++) {
cards.push(this.loadCardFromReplay());
cards.push(this.readCard());
}
for (let j = 0; j < 2; j++) {
const n = this.readUint8();
@ -127,7 +107,64 @@ class ReplayLoader {
}
playerData.push({ deck: new Deck("Deck", 0, cards, new Array(15)), initialDrawOrder, drawOrder, won });
}
const turns = this.replayLoadTurns(numPlayers);
const turns = this.readTurns(numPlayers);
currentReplay.games.push({ stage, playerData, turns });
}
break;
}
case 3: {
const n = this.readUint8();
const numPlayers = n & 0x0F;
goalWinCount = n >> 4;
if (goalWinCount == 0) goalWinCount = null;
currentReplay = { gameNumber: 0, decks: [ ], games: [ ], turns: [ ], placements: [ ], watchingPlayer: 0 };
this.readPlayers(numPlayers, players);
// Decks
const decks = [ ];
const numDecks = this.read7BitEncodedInt();
for (let i = 0; i < numDecks; i++) {
const name = this.readString();
const sleeves = this.readUint8();
const cards = [ ];
for (let i = 0; i < 15; i++) cards.push(this.readCard());
const upgrades = [ ];
for (let i = 0; i < 4; i++) {
const b = this.readUint8();
upgrades.push(b & 3);
upgrades.push(b >> 2 & 3);
upgrades.push(b >> 4 & 3);
if (i < 3) upgrades.push(b >> 6 & 3);
}
decks.push(new Deck(name, sleeves, cards, upgrades));
}
// Games
while (this.pos < this.dataView.byteLength) {
const stage = stageDatabase.stages[this.readUint8()];
const playerData = [ ];
for (let i = 0; i < numPlayers; i++) {
const deck = decks[this.read7BitEncodedInt()];
const initialDrawOrder = [ ];
const drawOrder = [ ];
let won = false;
for (let j = 0; j < 2; j++) {
const n = this.readUint8();
initialDrawOrder.push(n & 0xF);
initialDrawOrder.push(n >> 4 & 0xF);
}
for (let j = 0; j < 8; j++) {
const n = this.readUint8();
drawOrder.push(n & 0xF);
if (j == 7)
won = (n & 0x80) != 0;
else
drawOrder.push(n >> 4 & 0xF);
}
playerData.push({ deck, initialDrawOrder, drawOrder, won });
}
const turns = this.readTurns(numPlayers);
currentReplay.games.push({ stage, playerData, turns });
}
break;
@ -178,12 +215,36 @@ class ReplayLoader {
return n;
}
private replayLoadTurns(numPlayers: number) {
private readPlayers(numPlayers: number, players: Player[]) {
for (let i = 0; i < numPlayers; i++) {
const colour = this.readColour();
const specialColour = this.readColour();
const specialAccentColour = this.readColour();
const n2 = this.readUint8();
const len = n2 & 0x7F;
const player = {
name: this.readString(len),
specialPoints: 0,
isReady: false,
colour,
specialColour,
specialAccentColour,
uiBaseColourIsSpecialColour: (n2 & 0x80) != 0,
sleeves: 0,
totalSpecialPoints: 0,
passes: 0,
gamesWon: 0
};
players.push(player);
}
}
private readTurns(numPlayers: number) {
const turns = [ ];
for (let i = 0; i < 12; i++) {
const turn = [ ];
for (let j = 0; j < numPlayers; j++) {
const card = this.loadCardFromReplay();
const card = this.readCard();
const b = this.readUint8();
const x = this.readInt8();
const y = this.readInt8();
@ -199,7 +260,7 @@ class ReplayLoader {
return turns;
}
private loadCardFromReplay() {
private readCard() {
const num = this.readUint8();
return cardDatabase.get(num > cardDatabase.lastOfficialCardNumber ? num - 256 : num);
}

View File

@ -70,9 +70,14 @@ public class Game {
return false;
}
public Deck GetDeck(string name, int sleeves, IEnumerable<int> cardNumbers, IEnumerable<int> cardUpgrades)
=> this.deckCache.FirstOrDefault(d => d.Name == name && d.Sleeves == sleeves && cardNumbers.SequenceEqual(from c in d.Cards select c.Number) && cardUpgrades.SequenceEqual(d.Upgrades))
?? new(name, sleeves, (from i in cardNumbers select CardDatabase.GetCard(i)).ToArray(), cardUpgrades.ToArray());
public Deck GetDeck(string name, int sleeves, IEnumerable<int> cardNumbers, IEnumerable<int> cardUpgrades) {
var deck = this.deckCache.FirstOrDefault(d => d.Name == name && d.Sleeves == sleeves && cardNumbers.SequenceEqual(from c in d.Cards select c.Number) && cardUpgrades.SequenceEqual(d.Upgrades));
if (deck == null) {
deck = new(name, sleeves, (from i in cardNumbers select CardDatabase.GetCard(i)).ToArray(), cardUpgrades.ToArray());
this.deckCache.Add(deck);
}
return deck;
}
public bool CanPlay(int playerIndex, Card card, int x, int y, int rotation, bool isSpecialAttack) {
if (card is null) throw new ArgumentNullException(nameof(card));