Merge pull request #714 from nonameShijian/PR-Branch
增加异步content以及GameEvent#toPromise,郭嘉和二张作为测试
This commit is contained in:
commit
909d62982e
|
@ -3952,9 +3952,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
if(target.isMin()) return false;
|
||||
return player!=target&&target.canEquip(card);
|
||||
},
|
||||
content:function(){
|
||||
target.equip(cards[0]);
|
||||
player.draw();
|
||||
async content(event, trigger, player){
|
||||
await event.target.promises.equip(event.cards[0]);
|
||||
await player.promises.draw();
|
||||
},
|
||||
discard:false,
|
||||
lose:false,
|
||||
|
|
|
@ -586,34 +586,32 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
trigger:{player:'damageEnd'},
|
||||
frequent:true,
|
||||
filter:function(event){
|
||||
return (event.num>0)
|
||||
return event.num>0;
|
||||
},
|
||||
content:function(){
|
||||
'step 0'
|
||||
async content(event, trigger, player) {
|
||||
event.count = trigger.num;
|
||||
'step 1'
|
||||
// event.goto -> while
|
||||
while (event.count > 0) {
|
||||
event.count--;
|
||||
event.cards=game.cardsGotoOrdering(get.cards(2)).cards;
|
||||
const { cards } = await game.cardsGotoOrdering(get.cards(2)).toPromise();
|
||||
if (_status.connectMode) game.broadcastAll(function () { _status.noclearcountdown = true });
|
||||
event.given_map = {};
|
||||
'step 2'
|
||||
if(event.cards.length>1){
|
||||
player.chooseCardButton('遗计:请选择要分配的牌',true,event.cards,[1,event.cards.length]).set('ai',function(button){
|
||||
if (!cards.length) return;
|
||||
// event.goto -> do while
|
||||
do {
|
||||
const { result: { bool, links } } =
|
||||
cards.length == 1 ?
|
||||
{ result: { links: cards.slice(0), bool: true } } :
|
||||
await player.promises.chooseCardButton('遗计:请选择要分配的牌', true, cards, [1, cards.length])
|
||||
.set('ai', function (button) {
|
||||
if (ui.selected.buttons.length == 0) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
else if(event.cards.length==1){
|
||||
event._result={links:event.cards.slice(0),bool:true};
|
||||
}
|
||||
else{
|
||||
event.finish();
|
||||
}
|
||||
'step 3'
|
||||
if(result.bool){
|
||||
event.cards.removeArray(result.links);
|
||||
event.togive=result.links.slice(0);
|
||||
player.chooseTarget('选择一名角色获得'+get.translation(result.links),true).set('ai',function(target){
|
||||
if (!bool) return;
|
||||
cards.removeArray(links);
|
||||
event.togive = links.slice(0);
|
||||
const { result: { targets } } = await player.promises.chooseTarget('选择一名角色获得' + get.translation(links), true)
|
||||
.set('ai', function (target) {
|
||||
var att = get.attitude(_status.event.player, target);
|
||||
if (_status.event.enemy) {
|
||||
return -att;
|
||||
|
@ -624,39 +622,36 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
else {
|
||||
return att / 100;
|
||||
}
|
||||
}).set('enemy',get.value(event.togive[0],player,'raw')<0);
|
||||
}
|
||||
'step 4'
|
||||
if(result.targets.length){
|
||||
var id=result.targets[0].playerid,map=event.given_map;
|
||||
})
|
||||
.set('enemy', get.value(event.togive[0], player, 'raw') < 0);
|
||||
if (targets.length) {
|
||||
const id = targets[0].playerid,
|
||||
map = event.given_map;
|
||||
if (!map[id]) map[id] = [];
|
||||
map[id].addArray(event.togive);
|
||||
}
|
||||
if(cards.length>0) event.goto(2);
|
||||
'step 5'
|
||||
} while (cards.length > 0);
|
||||
if (_status.connectMode) {
|
||||
game.broadcastAll(function () { delete _status.noclearcountdown; game.stopCountChoose() });
|
||||
}
|
||||
var list=[];
|
||||
for(var i in event.given_map){
|
||||
var source=(_status.connectMode?lib.playerOL:game.playerMap)[i];
|
||||
const list = [];
|
||||
for (const i in event.given_map) {
|
||||
const source = (_status.connectMode ? lib.playerOL : game.playerMap)[i];
|
||||
player.line(source, 'green');
|
||||
list.push([source, event.given_map[i]]);
|
||||
}
|
||||
game.loseAsync({
|
||||
await game.loseAsync({
|
||||
gain_list: list,
|
||||
giver: player,
|
||||
animate: 'draw',
|
||||
}).setContent('gaincardMultiple');
|
||||
'step 6'
|
||||
}).toPromise().setContent('gaincardMultiple');
|
||||
if (event.count > 0 && player.hasSkill(event.name) && !get.is.blocked(event.name, player)) {
|
||||
player.chooseBool(get.prompt2(event.name)).set('frequentSkill',event.name);
|
||||
}
|
||||
else event.finish();
|
||||
'step 7'
|
||||
if(result.bool){
|
||||
const { result: { bool: repeat } } = await player.promises.chooseBool(get.prompt2(event.name)).set('frequentSkill', event.name);
|
||||
if (repeat) {
|
||||
player.logSkill(event.name);
|
||||
event.goto(1);
|
||||
} else return;
|
||||
}
|
||||
else return;
|
||||
}
|
||||
},
|
||||
ai:{
|
||||
|
|
274
game/game.js
274
game/game.js
|
@ -47,6 +47,7 @@ new Promise(resolve=>{
|
|||
}
|
||||
}
|
||||
const GeneratorFunction=(function*(){}).constructor;
|
||||
const AsyncFunction=(async function(){}).constructor;
|
||||
// gnc: GeNCoroutine
|
||||
const gnc={
|
||||
of:fn=>gnc.is.generatorFunc(fn)?function genCoroutine(){
|
||||
|
@ -21030,6 +21031,22 @@ new Promise(resolve=>{
|
|||
};
|
||||
player.queueCount=0;
|
||||
player.outCount=0;
|
||||
/**
|
||||
* 这部分应该用d.ts写。目前只给出大概类型
|
||||
* @type { {[key in keyof Player]: (...args) => Promise<GameEvent> & GameEvent} }
|
||||
*/
|
||||
player.promises=new Proxy({},{
|
||||
get(target,prop){
|
||||
const eventKeys=Object.keys(lib.element.player).filter(key=>typeof lib.element.player[key]=='function');
|
||||
if (eventKeys.includes(prop)){
|
||||
return function (...args) {
|
||||
/** @type { GameEvent } */
|
||||
const event=player[prop](...args);
|
||||
return event.toPromise();
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
buildEventListener(noclick){
|
||||
let player = this;
|
||||
|
@ -31288,6 +31305,14 @@ new Promise(resolve=>{
|
|||
this._notrigger=[];
|
||||
this._result={};
|
||||
this._set=[];
|
||||
/**
|
||||
* @type {boolean} 这个事件是否使用异步函数处理
|
||||
**/
|
||||
this.async=false;
|
||||
/**
|
||||
* @type {null|(event: GameEvent)=>any} 这个异步事件对应Promise的resolve函数
|
||||
**/
|
||||
this.resolve=null;
|
||||
if(trigger!==false&&!game.online) this._triggered=0;
|
||||
}
|
||||
static initialGameEvent(){
|
||||
|
@ -31517,7 +31542,10 @@ new Promise(resolve=>{
|
|||
switch(typeof item){
|
||||
case "object":
|
||||
case "function":
|
||||
this.content=lib.init.parsex(item);
|
||||
if(item instanceof AsyncFunction){
|
||||
this.content=item;
|
||||
}
|
||||
else this.content=lib.init.parsex(item);
|
||||
break;
|
||||
default:
|
||||
try{
|
||||
|
@ -32073,6 +32101,114 @@ new Promise(resolve=>{
|
|||
this.unreal;
|
||||
throw new Error('Do not call this method');
|
||||
}
|
||||
/**
|
||||
* 事件转为Promise化
|
||||
*
|
||||
* @returns { Promise<GameEvent> & GameEvent }
|
||||
*/
|
||||
toPromise(){
|
||||
if(this.async&&this.resolve){
|
||||
throw new TypeError('This event has been converted into a promise');
|
||||
}
|
||||
return new lib.element.GameEventPromise(this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 将事件Promise化以使用async异步函数来执行事件。
|
||||
*
|
||||
* 事件Promise化后,需要既能使用await等待事件完成,
|
||||
* 又需要在执行之前对事件进行配置。
|
||||
*
|
||||
* 所以这个类的实例集成了事件和Promise二者的所有属性,
|
||||
* 且Promise的原有属性无法被修改,一切对这个类实例的属性修改,删除,
|
||||
* 再配置等操作都会转发到事件对应的属性中。
|
||||
*
|
||||
* @todo 需要完成异步事件的debugger方法
|
||||
*
|
||||
* @example
|
||||
* 使用toPromise()函数将普通事件转换为异步事件:
|
||||
* ```js
|
||||
* await game.xxx().toPromise().setContent('yyy').set(zzz, 'i');
|
||||
* ```
|
||||
* 使用player.promises.xxx()函数将对于player的普通事件转换为异步事件并执行:
|
||||
* ```js
|
||||
* await player.promises.draw(2);
|
||||
* game.log('等待', player, '摸牌完成执行log');
|
||||
* ```
|
||||
*/
|
||||
GameEventPromise:class extends Promise{
|
||||
// 我谢谢你,这里是必须有的
|
||||
// 否则Promise的方法对其子类无效
|
||||
static get [Symbol.species]() {
|
||||
return Promise;
|
||||
}
|
||||
/**
|
||||
* @param { GameEvent } event
|
||||
* @returns { Promise<GameEvent> & GameEvent }
|
||||
*/
|
||||
constructor(event){
|
||||
super(resolve=>{
|
||||
// 设置为异步事件
|
||||
event.async=true;
|
||||
// 事件结束后触发resolve
|
||||
event.resolve=resolve;
|
||||
// 如果父级事件也是一个异步的话,那应该立即执行这个事件的
|
||||
// 如果在AsyncFunction执行过程中在别的位置新建了一个异步事件,那也直接(等会set配置完)执行
|
||||
if(_status.event.next.includes(event)&&_status.event.content instanceof AsyncFunction){
|
||||
if (_status.event!=event) {
|
||||
event.parent=_status.event;
|
||||
_status.event=event;
|
||||
game.getGlobalHistory('everything').push(event);
|
||||
}
|
||||
// 异步执行game.loop
|
||||
// 不直接game.loop(event)是因为需要让别人可以手动set()和setContent()
|
||||
// 再执行game.loop是因为原有的game.loop被await卡住了,
|
||||
// 得新执行一个只执行这个异步事件的game.loop
|
||||
Promise.resolve().then(()=>game.loop(event));
|
||||
}
|
||||
});
|
||||
return new Proxy(this,{
|
||||
get(target,prop,receiver){
|
||||
const thisValue=Reflect.get(target,prop);
|
||||
if(thisValue){
|
||||
if(typeof thisValue=='function'){
|
||||
return thisValue.bind(target);
|
||||
}
|
||||
return thisValue;
|
||||
}
|
||||
const eventValue=Reflect.get(event,prop);
|
||||
// 返回值如果是event,则修改为GameEventPromise类实例
|
||||
if(typeof eventValue=='function') return (function(...args){
|
||||
const returnValue=eventValue.call(event,...args);
|
||||
return returnValue==event?receiver:returnValue;
|
||||
}).bind(event);
|
||||
return eventValue;
|
||||
},
|
||||
set(target,prop,newValue){
|
||||
return Reflect.set(event,prop,newValue);
|
||||
},
|
||||
deleteProperty(target,prop){
|
||||
return Reflect.deleteProperty(event,prop);
|
||||
},
|
||||
defineProperty(target,prop,attributes){
|
||||
return Reflect.defineProperty(event,prop,attributes);
|
||||
},
|
||||
has(target,prop){
|
||||
return Reflect.has(event,prop);
|
||||
},
|
||||
ownKeys(target,prop){
|
||||
return Reflect.ownKeys(event,prop);
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* TODO: 实现debugger
|
||||
*/
|
||||
async debugger(){
|
||||
return new Promise(resolve=>{
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
},
|
||||
Dialog:class extends HTMLDivElement{
|
||||
constructor(){
|
||||
|
@ -41263,21 +41399,27 @@ new Promise(resolve=>{
|
|||
setTimeout(game.reload,15000)
|
||||
}
|
||||
},
|
||||
loop:function(){
|
||||
/**
|
||||
* @param { GameEvent } [belongAsyncEvent]
|
||||
*/
|
||||
async loop(belongAsyncEvent){
|
||||
if(belongAsyncEvent){
|
||||
game.belongAsyncEvent=belongAsyncEvent;
|
||||
}else if(game.belongAsyncEvent){
|
||||
return game.loop(game.belongAsyncEvent);
|
||||
}
|
||||
while (true) {
|
||||
var event=_status.event;
|
||||
var step=event.step;
|
||||
var source=event.source;
|
||||
var player=event.player;
|
||||
var target=event.target;
|
||||
var targets=event.targets;
|
||||
var card=event.card;
|
||||
var cards=event.cards;
|
||||
var skill=event.skill;
|
||||
var forced=event.forced;
|
||||
var num=event.num;
|
||||
var trigger=event._trigger;
|
||||
var result=event._result;
|
||||
let event = (belongAsyncEvent && belongAsyncEvent.parent == _status.event) ? belongAsyncEvent : _status.event;
|
||||
let { step, source, player, target, targets, card, cards, skill, forced, num, _trigger: trigger, _result: result } = event;
|
||||
const _resolve = () => {
|
||||
if (event.async) {
|
||||
if (typeof event.resolve == 'function') {
|
||||
event.resolve(event);
|
||||
} else {
|
||||
throw new TypeError('异步事件的event.resolve未赋值,使用await时将会被永久等待');
|
||||
}
|
||||
}
|
||||
};
|
||||
if (_status.paused2 || _status.imchoosing) {
|
||||
if (!lib.status.dateDelaying) {
|
||||
lib.status.dateDelaying = new Date();
|
||||
|
@ -41341,9 +41483,22 @@ new Promise(resolve=>{
|
|||
event.parent._result = event.result;
|
||||
}
|
||||
_status.event = event.parent;
|
||||
if (game.belongAsyncEvent == event) {
|
||||
delete game.belongAsyncEvent;
|
||||
//resolve();
|
||||
}
|
||||
_resolve();
|
||||
// 此时应该退出了
|
||||
if (belongAsyncEvent && belongAsyncEvent.parent == _status.event) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
if (game.belongAsyncEvent == event) {
|
||||
delete game.belongAsyncEvent;
|
||||
//resolve();
|
||||
}
|
||||
return _resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41357,22 +41512,18 @@ new Promise(resolve=>{
|
|||
if (event.type == 'card') event.trigger('useCardToBegin');
|
||||
event.trigger(event.name + 'Begin');
|
||||
event._triggered++;
|
||||
/*if(event.name=='phase'&&!event._begun){
|
||||
var next=game.createEvent('phasing',false,event);
|
||||
next.player=event.player;
|
||||
next.skill=event.skill;
|
||||
next.setContent('phasing');
|
||||
event._begun=true;
|
||||
}
|
||||
else{
|
||||
event.trigger(event.name+'Begin');
|
||||
event._triggered++;
|
||||
}*/
|
||||
}
|
||||
else {
|
||||
event.callHandler(event.getDefaultHandlerType(), event, {
|
||||
state: 'begin'
|
||||
});
|
||||
const after = () => {
|
||||
event.clearStepCache();
|
||||
event.callHandler(event.getDefaultHandlerType(), event, {
|
||||
state: 'end'
|
||||
});
|
||||
if (typeof event.step == "number") ++event.step;
|
||||
};
|
||||
if (player && player.classList.contains('dead') && !event.forceDie && event.name != 'phaseLoop') {
|
||||
game.broadcastAll(function () {
|
||||
while (_status.dieClose.length) {
|
||||
|
@ -41383,57 +41534,37 @@ new Promise(resolve=>{
|
|||
event._oncancel();
|
||||
}
|
||||
event.finish();
|
||||
after();
|
||||
}
|
||||
else if (player && player.removed && event.name != 'phaseLoop') {
|
||||
event.finish();
|
||||
after();
|
||||
}
|
||||
else if (player && player.isOut() && event.name != 'phaseLoop' && !event.includeOut) {
|
||||
if (event.name == 'phase' && player == _status.roundStart && !event.skill) {
|
||||
_status.roundSkipped = true;
|
||||
}
|
||||
event.finish();
|
||||
after();
|
||||
}
|
||||
else {
|
||||
await game.runContent(belongAsyncEvent).catch(e => {
|
||||
if (_status.withError || lib.config.compatiblemode || (_status.connectMode && !lib.config.debug)) {
|
||||
try{
|
||||
if(event.content instanceof GeneratorFunction){
|
||||
if(!event.debugging){
|
||||
if(event.generatorContent) event.generatorContent.return();
|
||||
event.generatorContent=event.content(event,step,source,player,target,targets,
|
||||
card,cards,skill,forced,num,trigger,result,
|
||||
_status,lib,game,ui,get,ai);
|
||||
}else{
|
||||
delete event.debugging;
|
||||
}
|
||||
var next=event.generatorContent.next();
|
||||
if(typeof next.value=='function'&&next.value.toString()=='code=>eval(code)'){
|
||||
//触发debugger
|
||||
var inputCallback=inputResult=>{
|
||||
if(inputResult===false){
|
||||
event.debugging=true;
|
||||
game.resume2();
|
||||
}else{
|
||||
alert(get.stringify(next.value(inputResult)));
|
||||
game.prompt('','debugger调试',inputCallback);
|
||||
}
|
||||
}
|
||||
game.prompt('','debugger调试',inputCallback);
|
||||
return game.pause2();
|
||||
}
|
||||
if(event.finished) event.generatorContent.return();
|
||||
}else{
|
||||
event.content(event,step,source,player,target,targets,
|
||||
card,cards,skill,forced,num,trigger,result,
|
||||
_status,lib,game,ui,get,ai);
|
||||
}
|
||||
}
|
||||
catch(e){
|
||||
game.print('游戏出错:' + event.name);
|
||||
game.print(e.toString());
|
||||
console.log(e);
|
||||
}
|
||||
else throw e;
|
||||
}).then(after);
|
||||
}
|
||||
else{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
runContent(belongAsyncEvent) {
|
||||
return new Promise(resolve=>{
|
||||
let event = (belongAsyncEvent && belongAsyncEvent.parent == _status.event) ? belongAsyncEvent : _status.event;
|
||||
let { step, source, player, target, targets, card, cards, skill, forced, num, _trigger: trigger, _result: result } = event;
|
||||
if (event.content instanceof GeneratorFunction) {
|
||||
if (!event.debugging) {
|
||||
if (event.generatorContent) event.generatorContent.return();
|
||||
|
@ -41459,21 +41590,22 @@ new Promise(resolve=>{
|
|||
return game.pause2();
|
||||
}
|
||||
if (event.finished) event.generatorContent.return();
|
||||
}else{
|
||||
resolve();
|
||||
}
|
||||
else if (event.content instanceof AsyncFunction) {
|
||||
// _status,lib,game,ui,get,ai六个变量由game.import提供
|
||||
event.content(event, trigger, player).then(() => {
|
||||
event.finish();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
else {
|
||||
event.content(event, step, source, player, target, targets,
|
||||
card, cards, skill, forced, num, trigger, result,
|
||||
_status, lib, game, ui, get, ai);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
event.clearStepCache();
|
||||
event.callHandler(event.getDefaultHandlerType(),event,{
|
||||
state:'end'
|
||||
});
|
||||
if(typeof event.step=="number") ++event.step;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pause:function(){
|
||||
clearTimeout(_status.timeout);
|
||||
|
|
Loading…
Reference in New Issue