@bot-shiki/koishi-plugin-werewolf
Version:
Touhou werewolf game
231 lines (230 loc) • 9.03 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WerewolfGame = void 0;
const koishi_1 = require("koishi");
const koishi_plugin_lobby_1 = require("koishi-plugin-lobby");
const utils_1 = require("./utils");
const iku_1 = __importDefault(require("./action/iku"));
const yukari_1 = __importDefault(require("./action/yukari"));
const remilia_1 = __importDefault(require("./action/remilia"));
const mamizou_1 = __importDefault(require("./action/mamizou"));
const doremy_1 = __importDefault(require("./action/doremy"));
const ningen_1 = __importDefault(require("./action/ningen"));
const sage_1 = __importDefault(require("./action/sage"));
const exile_1 = __importDefault(require("./action/exile"));
const death_1 = __importDefault(require("./action/death"));
class WerewolfGame extends koishi_plugin_lobby_1.Game {
dayCount = 0;
weather = null;
seats;
chars;
winner;
iku = new iku_1.default(this);
sage = new sage_1.default(this);
exile = new exile_1.default(this);
death = new death_1.default(this);
ningen = new ningen_1.default(this);
remilia = new remilia_1.default(this);
mamizou = new mamizou_1.default(this);
yukari = new yukari_1.default(this);
doremy = new doremy_1.default(this);
getChar(identity) {
return this.seats.find(c => c.identity === identity);
}
async validate() {
if (this.room.size < 6 || this.room.size > 16) {
throw new Error('lobby.game.th-werewolf.invalid-size');
}
}
leave(player) {
const char = this.chars.get(player);
if (char.isOut)
return;
char.isOut = true;
char.killer = 'offline';
this.check();
}
async init() {
const [ningenCount, youkaiCount, ...extra] = koishi_1.Random.pick(utils_1.Preset.default[this.room.size]);
const identities = [
koishi_1.Random.pick(utils_1.Identity.Ningen.Normal, ningenCount),
koishi_1.Random.pick(utils_1.Identity.Youkai.Normal, youkaiCount),
...extra.map(([count, ...options]) => {
if (!this.options.weather) {
options = options.filter(i => i !== 'iku');
}
return koishi_1.Random.pick(options, count);
}),
].flat();
const parties = { ningen: [], youkai: [], neutral: [] };
for (const identity of identities) {
const party = utils_1.Identity.getParty(identity);
parties[party].push(identity);
}
const output = [(0, utils_1.t)('init.parties')];
for (const party in parties) {
if (!parties[party].length)
continue;
const elements = [];
parties[party].forEach((identity, index) => {
if (index)
elements.push(', ');
elements.push((0, utils_1.t)(`character.${identity}.name`));
});
output.push((0, koishi_1.h)('p', [(0, utils_1.t)(`party.${party}`), ': ', ...elements]));
}
this.seats = [];
this.chars = new Map();
await this.room.broadcast(output);
const players = Object.values(this.room.players);
await Promise.all(players.map(async (player, index) => {
const identity = identities[index];
const char = new utils_1.Character(this, player, identity);
utils_1.logger.debug('init %s [%s]', char, char.party);
this.chars.set(player, char);
this.seats.push(char);
const output = [
(0, utils_1.t)('init.character', [(0, utils_1.t)(`character.${identity}.name`)]),
(0, utils_1.t)('init.party', [(0, utils_1.t)(`party.${char.party}`)]),
];
if (char.isExpert) {
output.push((0, utils_1.t)(`character.${identity}.skill`));
}
await player.send(output.map(el => (0, koishi_1.h)('p', el)));
await player.pause(60000, null, true);
}));
this.seats = koishi_1.Random.shuffle(this.seats);
}
async start() {
this.room.allowSpeech = false;
try {
await this.init();
while (true) {
await this.dayAction();
await this.iku.action();
await this.remilia.action();
await this.mamizou.action();
await this.ningen.action();
await this.yukari.action();
await this.doremy.action();
await this.sage.action();
await this.nightAction();
await this.death.action();
await this.exile.action();
await this.death.action();
}
}
catch (e) {
if (!this.winner)
throw e;
}
await this.announce();
}
isNingenWinner() {
const alive = this.seats.filter(c => !c.isDead || c.identity === 'utsuho' && c.killer === 'vote');
if (this.room.size <= 7) {
return alive.every(c => c.party === 'ningen');
}
else {
return alive.every(c => c.party !== 'youkai' || c.isExpert)
|| alive.every(c => c.party !== 'youkai' || !c.isExpert);
}
}
check() {
const alive = this.seats.filter(c => !c.isOut);
if (alive.length === 0) {
const parsee = this.getChar('parsee');
if (parsee && !parsee.isDead) {
parsee.killer = null;
this.winner = 'parsee';
}
else {
this.winner = 'draw';
}
}
else if (alive.length === 1 && alive[0].identity === 'parsee') {
this.winner = 'parsee';
}
else if (alive.every(c => c.nightmare || c.identity === 'doremy')) {
alive.forEach((char) => {
if (char.identity !== 'doremy') {
char.killer = 'doremy';
}
});
this.winner = 'doremy';
}
else if (alive.every(c => c.party !== 'ningen')) {
this.winner = 'youkai';
}
else if (this.isNingenWinner()) {
this.winner = 'ningen';
}
if (this.winner)
throw new Error('game over');
}
async announce() {
utils_1.logger.debug('winner:', this.winner);
if (this.winner === 'draw') {
await this.room.broadcast((0, utils_1.t)('winner.draw'));
}
else if (this.winner === 'ningen' || this.winner === 'youkai') {
await this.room.broadcast((0, utils_1.t)('winner.party', [
(0, utils_1.t)(`party.${this.winner}`),
this.seats.filter(c => c.party === this.winner).map(c => c.player.name).join(', '),
]));
}
else {
await this.room.broadcast((0, utils_1.t)('winner.single', [
(0, utils_1.t)(`character.${this.winner}`),
this.getChar(this.winner).player.name,
]));
}
const output = this.seats.map((char) => {
const content = [char.player.name, ' '];
if (!char.killer) {
content.push((0, utils_1.t)('killer.survive'));
}
else if (utils_1.Identity.Expert.includes(char.killer)) {
content.push((0, utils_1.t)(`character.${char.killer}.name`));
}
else {
content.push((0, utils_1.t)(`killer.${char.killer}`));
}
return (0, koishi_1.h)('p', content);
});
output.unshift((0, utils_1.t)('killer.header'));
await this.room.broadcast(output);
}
async dayAction() {
this.dayCount += 1;
utils_1.logger.debug('day %s', this.dayCount);
await this.room.broadcast((0, utils_1.t)('general.day', [this.dayCount]));
}
async nightAction() {
const deaths = this.seats.filter(c => c.killer && !c.isDead);
if (deaths.length) {
await this.room.broadcast((0, utils_1.t)('general.night-death', [this.dayCount, deaths.map(c => c.player.name).join(', ')]));
}
else {
await this.room.broadcast((0, utils_1.t)('general.night-peace', [this.dayCount]));
}
}
}
exports.WerewolfGame = WerewolfGame;
class RPSCorridor extends koishi_plugin_lobby_1.Corridor {
config;
factory = WerewolfGame;
constructor(ctx, config) {
super(ctx, 'th-werewolf');
this.config = config;
ctx.i18n.define('zh-CN', require('./locales/zh-CN'));
this.cmd.option('weather', '-W, --no-weather', { fallback: true });
}
}
(function (RPSCorridor) {
RPSCorridor.Config = koishi_1.z.object({});
})(RPSCorridor || (RPSCorridor = {}));
exports.default = RPSCorridor;