UNPKG

ygocore-interface

Version:

[WIP] ygocore interface (message definitions, constants, api signatures)

362 lines 14.4 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); var coremsg_1 = require("./coremsg"); exports.DUEL_RULE_1 = 1 << 16; exports.DUEL_RULE_2 = 2 << 16; exports.DUEL_RULE_3 = 3 << 16; exports.DUEL_RULE_4 = 4 << 16; /** * @see DUEL */ exports.DEFAULT_DUEL_OPTIONS = exports.DUEL_RULE_4; var DEFAULT_LP = 8000; var DEFAULT_START_HAND = 5; var DEFAULT_DRAW_COUNT = 1; /** * single duel only * * (TAG duel is a TODO) */ var Duel = /** @class */ (function () { function Duel(engine, params) { this.state = createDuel(engine, params); } /** * feed player's response to this duel * @param response player's response * @returns false for invalid response, true otherwise */ Duel.prototype.feed = function (response) { return feed(this.state, response); }; /** * like engine.process, see: * * @see StepResult * * @see DispatchPacket * * @see AskQuestion * * @see DuelFinished */ Duel.prototype.step = function () { return step(this.state); }; /** * finish the duel */ Duel.prototype.release = function () { return this.state.engine.endDuel(this.state.duel); }; return Duel; }()); exports.Duel = Duel; /** * helps pumping message */ var MessageQueue = /** @class */ (function () { function MessageQueue(pump) { this.pump = pump; this.queue = []; this.index = 0; } MessageQueue.prototype.get = function () { this.fill(); return this.queue[this.index++]; }; MessageQueue.prototype.peek = function () { this.fill(); return this.queue[this.index]; }; MessageQueue.prototype.fill = function () { while (this.index === this.queue.length) { this.queue = this.pump(); this.index = 0; } }; return MessageQueue; }()); /** * create & prepare for a duel * @param engine the engine * @param params configurations about this duel */ function createDuel(engine, params) { var duel = engine.createDuel(params.seed); params.players.forEach(function (player, playerId) { var lp = player.lp || DEFAULT_LP; var draw = player.draw || DEFAULT_DRAW_COUNT; var start = player.start || DEFAULT_START_HAND; engine.setPlayerInfo(duel, { lp: lp, draw: draw, start: start, player: playerId }); prepareCards(playerId, player.main, coremsg_1.LOCATION.DECK); prepareCards(playerId, player.extra, coremsg_1.LOCATION.EXTRA); }); engine.startDuel(duel, params.options); var pump = function () { return coremsg_1.parseMessage(engine.process(duel).data); }; return { options: { replay: !!params.replay }, queue: new MessageQueue(pump), engine: engine, duel: duel }; function prepareCards(player, cards, location) { for (var _i = 0, cards_1 = cards; _i < cards_1.length; _i++) { var code = cards_1[_i]; engine.newCard(duel, { player: player, owner: player, sequence: 0, code: code, location: location, position: coremsg_1.POS.FACEDOWN }); } } } function feed(state, response) { state.engine.setResponse(state.duel, response); if (state.queue.peek().msgtype !== 'MSG_RETRY') { delete state.pendingQuestion; return true; } return false; } function step(state) { if (state.finished) { return state.finished; } if (state.pendingQuestion) { return { tag: 'ASK_QUESTION', question: state.pendingQuestion }; } var m = state.queue.get(); if (m.msgtype === 'MSG_WIN') { state.finished = { tag: 'DUEL_FINISHED', why: { tag: 'REASON_WIN', message: m } }; } if (coremsg_1.isQuestionMessage(m)) { state.pendingQuestion = m; } var packets = state.pendingQuestion ? handleQuestion(state, m) : handleMessage(state, m); return packets.length ? { tag: 'DISPATCH_PACKET', packets: packets, original: m } : step(state); } function refreshZone(state, player, location, queryFlags, useCache) { var qbuff = state.engine.queryFieldCard(state.duel, { player: player, location: location, queryFlags: queryFlags, useCache: useCache }); var header = [coremsg_1.MSG.UPDATE_DATA, player, location]; return coremsg_1.parseMessage(Buffer.concat([Buffer.from(header), qbuff]))[0]; } var REFRESH_FLAGS_DEFAULT = { HAND: 0x781FFF, MZONE: 0x881FFF, SZONE: 0x681FFF, SINGLE: 0xF81FFF }; var M = { location: coremsg_1.LOCATION.MZONE, queryFlags: REFRESH_FLAGS_DEFAULT.MZONE }; var S = { location: coremsg_1.LOCATION.SZONE, queryFlags: REFRESH_FLAGS_DEFAULT.SZONE }; var H = { location: coremsg_1.LOCATION.HAND, queryFlags: REFRESH_FLAGS_DEFAULT.HAND }; function refreshMany(state, player, where, useCache) { if (useCache === void 0) { useCache = true; } return where.map(function (_a) { var location = _a.location, queryFlags = _a.queryFlags; return refreshZone(state, player, location, queryFlags, useCache); }); } function hideCodeForUpdateData(m, replay) { if (replay) { return m; } var cards = m.cards.map(function (card) { if (!(card.query_flag & coremsg_1.QUERY.CODE) || !card.info) return __assign({}, card, { code: 0 }); if (!(card.info.position & coremsg_1.POS.FACEUP)) return __assign({}, card, { code: 0 }); return card; }); return __assign({}, m, { cards: cards }); } function refreshCard(state, player, location, sequence, flags, useCache) { var result = state.engine.queryCard(state.duel, { player: player, location: location, queryFlags: flags, useCache: useCache, sequence: sequence }); var header = [coremsg_1.MSG.UPDATE_CARD, player, location, sequence]; return coremsg_1.parseMessage(Buffer.concat([Buffer.from(header), result]))[0]; } function shouldResendRefreshSingle(m) { if (!('location' in m) || !('info' in m)) return false; if (m.location === coremsg_1.LOCATION.REMOVED && (m.info.position & coremsg_1.POS.FACEDOWN)) return false; if (m.location & coremsg_1.LOCATION.OVERLAY) return true; var positionAwareLoc = coremsg_1.LOCATION.MZONE + coremsg_1.LOCATION.SZONE + coremsg_1.LOCATION.ONFIELD + coremsg_1.LOCATION.REMOVED; return (m.location & positionAwareLoc) && (m.info.position & coremsg_1.POS.FACEUP); } var both = [0, 1]; function dispatch(whom, what) { return { whom: whom, what: what }; } function another(player) { return 1 - player; } function all(player, replay) { return function (u) { return [dispatch(player, u), dispatch(another(player), hideCodeForUpdateData(u, replay))]; }; } function handleQuestion(state, /* NOTE: will modify */ m) { var replay = state.options.replay; switch (m.msgtype) { case 'MSG_SELECT_BATTLECMD': case 'MSG_SELECT_IDLECMD': return both .map(function (player) { return refreshMany(state, player, [M, S, H]).map(all(player, replay)); }) .reduce(flatten, []) .reduce(flatten, []); case 'MSG_SELECT_TRIBUTE': case 'MSG_SELECT_CARD': for (var _i = 0, _a = m.selections; _i < _a.length; _i++) { var card = _a[_i]; if (card.controller !== m.player) { card.code = 0; } } break; case 'MSG_SELECT_UNSELECT_CARD': for (var _b = 0, _c = m.not_selected; _b < _c.length; _b++) { var card = _c[_b]; if (card.controller !== m.player) { card.code = 0; } } for (var _d = 0, _e = m.selected; _d < _e.length; _d++) { var card = _e[_d]; if (card.controller !== m.player) { card.code = 0; } } break; default: /* nothing to do */ } return []; } function handleMessage(state, m) { var packets = []; _handleMessage(state, m, packets); return packets; function _handleMessage(state, m, out) { var replay = state.options.replay; function tell(whom, what) { out.push(dispatch(whom, what)); } function yell(what) { tell(0, what); tell(1, what); } function secretlyTellMany(whom) { return function (what) { tell(whom, what); tell(another(whom), hideCodeForUpdateData(what, replay)); }; } function secretlyTell(whom, what) { tell(whom, what); if (shouldResendRefreshSingle(what)) tell(another(whom), what); } switch (m.msgtype) { case 'MSG_HINT': switch (m.type) { case 1: case 2: case 3: case 5: return tell(m.player, m); case 4: case 6: case 7: case 8: case 9: return tell(another(m.player), m); default: return yell(m); } case 'MSG_CONFIRM_CARDS': if (m.cards[0].location !== coremsg_1.LOCATION.DECK) { return yell(m); } else { return tell(m.player, m); } case 'MSG_SHUFFLE_HAND': case 'MSG_SHUFFLE_EXTRA': tell(m.player, m); return tell(another(m.player), __assign({}, m, { cards: m.cards.map(function () { return 0; }) })); case 'MSG_SHUFFLE_SET_CARD': for (var _i = 0, both_1 = both; _i < both_1.length; _i++) { var player = both_1[_i]; tell(player, m); refreshMany(state, player, [{ location: m.location, queryFlags: 0x181FFF }], false).forEach(secretlyTellMany(player)); } return; case 'MSG_NEW_PHASE': case 'MSG_NEW_TURN': for (var _a = 0, both_2 = both; _a < both_2.length; _a++) { var player = both_2[_a]; refreshMany(state, player, [M, S, H]).forEach(secretlyTellMany(player)); tell(player, m); } return; case 'MSG_MOVE': tell(m.current.controller, m); var graveOrOverlay = !!(m.current.location & (coremsg_1.LOCATION.GRAVE + coremsg_1.LOCATION.OVERLAY)); var deckOrHand = !!(m.current.location & (coremsg_1.LOCATION.DECK + coremsg_1.LOCATION.HAND)); var faceDown = !!(m.current.position & coremsg_1.POS.FACEDOWN); if (!graveOrOverlay && (deckOrHand || faceDown)) { tell(another(m.current.controller), __assign({}, m, { code: m.code })); } else { tell(another(m.current.controller), m); } if (m.current.location && !(m.current.location & coremsg_1.LOCATION.OVERLAY) && (m.current.location !== m.previous.location || m.current.controller !== m.previous.controller)) { var q = refreshCard(state, m.current.controller, m.current.location, m.current.sequence, REFRESH_FLAGS_DEFAULT.SINGLE, false); secretlyTell(m.current.controller, q); } return; case 'MSG_POS_CHANGE': yell(m); if ((m.previous_position & coremsg_1.POS.FACEDOWN) && (m.current_position & coremsg_1.POS.FACEUP)) { var q = refreshCard(state, m.current_controller, m.current_location, m.current_sequence, REFRESH_FLAGS_DEFAULT.SINGLE, false); secretlyTell(m.current_controller, q); } return; case 'MSG_SET': return yell(__assign({}, m, { code: 0 })); case 'MSG_SWAP': yell(m); for (var _b = 0, _c = [m.first, m.second]; _b < _c.length; _b++) { var info = _c[_b]; var q = refreshCard(state, info.controller, info.location, info.sequence, REFRESH_FLAGS_DEFAULT.SINGLE, false); secretlyTell(info.controller, q); } return; case 'MSG_SUMMONED': case 'MSG_SPSUMMONED': case 'MSG_FLIPSUMMONED': case 'MSG_CHAINED': case 'MSG_CHAIN_SOLVED': case 'MSG_CHAIN_END': for (var _d = 0, both_3 = both; _d < both_3.length; _d++) { var player = both_3[_d]; tell(player, m); var alsoRefreshHand = m.msgtype === 'MSG_CHAINED' || m.msgtype === 'MSG_CHAIN_SOLVED' || m.msgtype === 'MSG_CHAIN_END'; refreshMany(state, player, alsoRefreshHand ? [M, S, H] : [M, S]).forEach(secretlyTellMany(player)); } return; case 'MSG_CARD_SELECTED': return; case 'MSG_DRAW': tell(m.player, m); return tell(another(m.player), __assign({}, m, { cards: m.cards.map(function (code) { return (code & 0x80000000) ? code : 0; }) })); case 'MSG_DAMAGE_STEP_START': case 'MSG_DAMAGE_STEP_END': for (var _e = 0, both_4 = both; _e < both_4.length; _e++) { var player = both_4[_e]; tell(player, m); refreshMany(state, player, [M]).forEach(secretlyTellMany(player)); } return; case 'MSG_MISSED_EFFECT': return tell(m.controller, m); default: return yell(m); } } } function flatten(previous, current) { return previous.concat(current); } //# sourceMappingURL=duel.js.map