ygocore-interface
Version:
[WIP] ygocore interface (message definitions, constants, api signatures)
362 lines • 14.4 kB
JavaScript
"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