7 factions already implemented: - Norther Realms - Nilfgaard Empire - Scoia'Tael - Monsters - Skellige - Redenia - Toussaint In progress: - Velen To do: - Witchers - Wild Hunt
326 lines
11 KiB
JavaScript
326 lines
11 KiB
JavaScript
"use strict"
|
|
|
|
class GameTracker {
|
|
constructor(player1,player2) {
|
|
this.player1 = player1;
|
|
this.player2 = player2;
|
|
this.rounds = [];
|
|
this.winner = null;
|
|
}
|
|
|
|
startRound() {
|
|
this.currentRound = new Round(this.rounds.length + 1);
|
|
this.currentRound.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
|
|
}
|
|
|
|
endRound(winpl) {
|
|
this.currentRound.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
|
|
this.currentRound.endRound(winpl);
|
|
this.rounds.push(this.currentRound);
|
|
}
|
|
|
|
startTurn(pl) {
|
|
this.currentRound.startTurn(pl);
|
|
}
|
|
|
|
endTurn() {
|
|
this.currentRound.endTurn();
|
|
}
|
|
|
|
getCurrentTurn() {
|
|
return this.currentRound.currentTurn;
|
|
}
|
|
|
|
endGame(winpl) {
|
|
this.winner = winpl;
|
|
}
|
|
|
|
getScores() {
|
|
let scores = [];
|
|
this.rounds.forEach(r => {
|
|
scores.push([r.finalScores[0], r.finalScores[1]]);
|
|
});
|
|
return scores;
|
|
}
|
|
|
|
getCardImpacts() {
|
|
let data = {};
|
|
|
|
this.rounds.forEach(r => {
|
|
r.turns.forEach(t => {
|
|
if (t.action instanceof UnitCardAction) {
|
|
let ckey = t.action.card.key;
|
|
if (!data[ckey])
|
|
data[ckey] = [];
|
|
data[ckey].push(t.action.impact);
|
|
}
|
|
});
|
|
});
|
|
return data;
|
|
}
|
|
|
|
getAbilitiesImpact() {
|
|
let cimp = this.getCardImpacts();
|
|
let data = {"bond":[],"muster":[],"medic":[],"witcher":[],"whorshiped":[],"inspire":[],"scorch":[],"scorch_c":[],"morale":[],"horn":[], "witch_hunt":[]};
|
|
|
|
Object.keys(cimp).forEach(key => {
|
|
let abi = card_dict[key]["ability"].split(" ");
|
|
if (abi.includes("bond")) {
|
|
data["bond"] = data["bond"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
|
|
} else if (abi.includes("muster")) {
|
|
data["muster"] = data["muster"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
|
|
} else if (abi.includes("medic")) {
|
|
data["medic"] = data["medic"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.at(-1) && abi.at(-1).startsWith("witcher_")) {
|
|
data["witcher"] = data["witcher"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("whorshiped")) {
|
|
data["whorshiped"] = data["whorshiped"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("inspire")) {
|
|
data["inspire"] = data["inspire"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("scorch")) {
|
|
data["scorch"] = data["scorch"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("scorch_c")) {
|
|
data["scorch_c"] = data["scorch_c"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("morale")) {
|
|
data["morale"] = data["morale"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("horn")) {
|
|
data["horn"] = data["horn"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
} else if (abi.includes("witch_hunt")) {
|
|
data["witch_hunt"] = data["witch_hunt"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
|
|
}
|
|
});
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
class Round {
|
|
constructor(n) {
|
|
this.number = n;
|
|
this.turns = [];
|
|
this.winner = null;
|
|
this.finalScores = [0, 0];
|
|
this.start_hand_size = [0, 0];
|
|
this.end_hand_size = [0, 0];
|
|
}
|
|
|
|
startTurn(pl) {
|
|
this.currentTurn = new Turn(this.turns.length + 1, this, pl);
|
|
this.currentTurn.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
|
|
}
|
|
|
|
endTurn() {
|
|
this.currentTurn.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
|
|
this.turns.push(this.currentTurn);
|
|
}
|
|
|
|
setBeforeStats(scores, rows, handsize) {
|
|
this.startScores = scores;
|
|
this.startRows = [];
|
|
rows.forEach(r => {
|
|
this.startRows.push(Object.assign({}, r));
|
|
});
|
|
this.start_hand_size = handsize;
|
|
}
|
|
|
|
setAfterStats(scores, rows, handsize) {
|
|
this.endScores = scores;
|
|
this.endRows = [];
|
|
rows.forEach(r => {
|
|
this.endRows.push(Object.assign({}, r));
|
|
});
|
|
this.end_hand_size = handsize;
|
|
this.turns.forEach(t => {
|
|
if (t.action instanceof UnitCardAction && t.action.card.currentLocation instanceof Row) {
|
|
let pl = t.action.card.holder;
|
|
if (!t.action.card.isLocked() &&
|
|
(t.action.card.abilities.includes("bond")
|
|
|| t.action.card.abilities.includes("whorshipped")
|
|
|| t.action.card.abilities.includes("inspire")
|
|
|| (t.action.card.abilities.at(-1) && t.action.card.abilities.at(-1).startsWith("witcher_"))
|
|
)) {
|
|
t.action.impact = t.action.card.power;
|
|
} else if (!t.action.card.isLocked() &&
|
|
(t.action.card.abilities.includes("scorch")
|
|
|| t.action.card.abilities.includes("scorch_c")
|
|
|| t.action.card.abilities.includes("medic")
|
|
|| t.action.card.abilities.includes("witch_hunt")
|
|
)) {
|
|
t.action.impact = (t.endScores[getPlayerIndex(pl)] - t.startScores[getPlayerIndex(pl)]) + (t.startScores[getPlayerIndex(pl.opponent())] - t.endScores[getPlayerIndex(pl.opponent())]);
|
|
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("morale")) {
|
|
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).length;
|
|
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("horn")) {
|
|
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).reduce((a,c) => a + (c.power / 2),0);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
endRound(winpl) {
|
|
this.winner = winpl;
|
|
this.finalScores = [player_me.total, player_op.total];
|
|
}
|
|
}
|
|
|
|
class Turn {
|
|
constructor(n,rnd,pl) {
|
|
this.number = n;
|
|
this.round = rnd;
|
|
this.player = pl;
|
|
this.action = null;
|
|
this.start_hand_size = [0, 0];
|
|
this.end_hand_size = [0, 0];
|
|
}
|
|
|
|
setBeforeStats(scores, rows, handsize) {
|
|
this.startScores = scores;
|
|
this.startRows = [];
|
|
rows.forEach(r => {
|
|
this.startRows.push(Object.assign({}, r));
|
|
});
|
|
this.start_hand_size = handsize;
|
|
}
|
|
|
|
setAfterStats(scores, rows, handsize) {
|
|
this.endScores = scores;
|
|
this.endRows = [];
|
|
rows.forEach(r => {
|
|
this.endRows.push(Object.assign({}, r));
|
|
});
|
|
this.end_hand_size = handsize;
|
|
if (this.action instanceof UnitCardAction) {
|
|
var pl = this.action.card.holder;
|
|
if (this.action.card.abilities.length === 0) {
|
|
this.action.impact = this.action.card.power;
|
|
} else if (this.action.card.abilities.includes("muster")) {
|
|
// We devide the impact by the number of cards from hand it took
|
|
let handdiff = Math.max(1, (this.start_hand_size[getPlayerIndex(pl)] - this.end_hand_size[getPlayerIndex(pl)]));
|
|
this.action.impact = (this.endScores[getPlayerIndex(pl)] - this.startScores[getPlayerIndex(pl)]) / handdiff;
|
|
}
|
|
}
|
|
}
|
|
|
|
playUnitCard(card, row) {
|
|
this.action = new UnitCardAction(this, card, row.getRowType());
|
|
}
|
|
|
|
playWeatherCard(card) {
|
|
this.action = new WeatherCardAction(this, card.key, card.abilities.at(-1));
|
|
}
|
|
|
|
playSpecialCard(card, row) {
|
|
this.action = new SpecialCardAction(this, card.key, row.getRowType());
|
|
}
|
|
|
|
playSpecialCardBoard(card) {
|
|
this.action = new SpecialCardAction(this, card.key, "board");
|
|
}
|
|
|
|
passAction() {
|
|
this.action = new PassAction(this);
|
|
}
|
|
|
|
useLeader(card) {
|
|
this.action = new LeaderAction(this, card.key);
|
|
}
|
|
|
|
useLeaderOnRow(card,r) {
|
|
this.action = new LeaderAction(this, card.key, r.getRowType());
|
|
}
|
|
|
|
useLeaderOnCard(card,t) {
|
|
this.action = new LeaderAction(this, card.key, false, t.key);
|
|
}
|
|
|
|
summary() {
|
|
let action_string = "No action";
|
|
if (this.action)
|
|
action_string = this.action.summary();
|
|
return `[Round:${this.round.number}] [Turn:${this.number}] [ScoresBefore:${this.startScores[0]}/${this.startScores[1]}]\nPlayer ${this.player.tag}: ${action_string}]\n[ScoresAfter:${this.endScores[0]}/${this.endScores[1]}]`;
|
|
}
|
|
}
|
|
|
|
class Action {
|
|
constructor(turn) {
|
|
this.turn = turn;
|
|
this.targetRow = false;
|
|
this.targetCard = false;
|
|
this.impact = 0;
|
|
}
|
|
|
|
summary() {
|
|
return "No output";
|
|
}
|
|
}
|
|
|
|
class UnitCardAction extends Action {
|
|
constructor(turn, card, r) {
|
|
super(turn);
|
|
this.card = card;
|
|
this.key = card.key;
|
|
this.targetRow = r;
|
|
}
|
|
|
|
summary() {
|
|
return `Card '${this.key}' played in ${this.targetRow} row.`;
|
|
}
|
|
}
|
|
|
|
class SpecialCardAction extends Action {
|
|
constructor(turn, ckey, r) {
|
|
super(turn);
|
|
this.key = ckey;
|
|
this.targetRow = r;
|
|
}
|
|
|
|
summary() {
|
|
return `Special Card '${this.key}' played on ${this.targetRow}.`;
|
|
}
|
|
}
|
|
|
|
class WeatherCardAction extends Action {
|
|
constructor(turn, ckey, type) {
|
|
super(turn);
|
|
this.key = ckey;
|
|
this.type = type;
|
|
}
|
|
|
|
summary() {
|
|
return `Weather Card '${this.key}' played with ${this.type} effect.`;
|
|
}
|
|
}
|
|
|
|
class LeaderAction extends Action {
|
|
constructor(turn, ckey, targetRow=false, targetCard=false) {
|
|
super(turn);
|
|
this.key = ckey;
|
|
this.targetRow = targetRow;
|
|
this.targetCard = targetCard;
|
|
}
|
|
|
|
summary() {
|
|
return `Leader '${this.key}' ability used`;
|
|
}
|
|
}
|
|
|
|
class FactionAction extends Action {
|
|
constructor(turn, ckey) {
|
|
super(turn);
|
|
this.key = ckey;
|
|
}
|
|
}
|
|
|
|
class PassAction extends Action {
|
|
constructor(turn) {
|
|
super(turn);
|
|
}
|
|
|
|
summary() {
|
|
return "Passed";
|
|
}
|
|
}
|
|
|
|
// UTIL FUNCTIONS
|
|
function getPlayerIndex(pl) {
|
|
return pl.tag === "me" ? 0 : 1;
|
|
} |