Merge pull request #714 from nonameShijian/PR-Branch

增加异步content以及GameEvent#toPromise,郭嘉和二张作为测试
This commit is contained in:
Spmario233 2023-12-07 23:03:06 +08:00 committed by GitHub
commit 909d62982e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 361 additions and 234 deletions

View File

@ -3952,9 +3952,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
if(target.isMin()) return false; if(target.isMin()) return false;
return player!=target&&target.canEquip(card); return player!=target&&target.canEquip(card);
}, },
content:function(){ async content(event, trigger, player){
target.equip(cards[0]); await event.target.promises.equip(event.cards[0]);
player.draw(); await player.promises.draw();
}, },
discard:false, discard:false,
lose:false, lose:false,

View File

@ -586,77 +586,72 @@ game.import('character',function(lib,game,ui,get,ai,_status){
trigger:{player:'damageEnd'}, trigger:{player:'damageEnd'},
frequent:true, frequent:true,
filter:function(event){ filter:function(event){
return (event.num>0) return event.num>0;
}, },
content:function(){ async content(event, trigger, player) {
'step 0' event.count = trigger.num;
event.count=trigger.num; // event.goto -> while
'step 1' while (event.count > 0) {
event.count--; 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}); if (_status.connectMode) game.broadcastAll(function () { _status.noclearcountdown = true });
event.given_map={}; event.given_map = {};
'step 2' if (!cards.length) return;
if(event.cards.length>1){ // event.goto -> do while
player.chooseCardButton('遗计:请选择要分配的牌',true,event.cards,[1,event.cards.length]).set('ai',function(button){ do {
if(ui.selected.buttons.length==0) return 1; const { result: { bool, links } } =
return 0; cards.length == 1 ?
}); { result: { links: cards.slice(0), bool: true } } :
} await player.promises.chooseCardButton('遗计:请选择要分配的牌', true, cards, [1, cards.length])
else if(event.cards.length==1){ .set('ai', function (button) {
event._result={links:event.cards.slice(0),bool:true}; if (ui.selected.buttons.length == 0) return 1;
} return 0;
else{ });
event.finish(); if (!bool) return;
} cards.removeArray(links);
'step 3' event.togive = links.slice(0);
if(result.bool){ const { result: { targets } } = await player.promises.chooseTarget('选择一名角色获得' + get.translation(links), true)
event.cards.removeArray(result.links); .set('ai', function (target) {
event.togive=result.links.slice(0); var att = get.attitude(_status.event.player, target);
player.chooseTarget('选择一名角色获得'+get.translation(result.links),true).set('ai',function(target){ if (_status.event.enemy) {
var att=get.attitude(_status.event.player,target); return -att;
if(_status.event.enemy){ }
return -att; else if (att > 0) {
return att / (1 + target.countCards('h'));
}
else {
return att / 100;
}
})
.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);
} }
else if(att>0){ } while (cards.length > 0);
return att/(1+target.countCards('h')); if (_status.connectMode) {
} game.broadcastAll(function () { delete _status.noclearcountdown; game.stopCountChoose() });
else{ }
return att/100; const list = [];
} for (const i in event.given_map) {
}).set('enemy',get.value(event.togive[0],player,'raw')<0); const source = (_status.connectMode ? lib.playerOL : game.playerMap)[i];
} player.line(source, 'green');
'step 4' list.push([source, event.given_map[i]]);
if(result.targets.length){ }
var id=result.targets[0].playerid,map=event.given_map; await game.loseAsync({
if(!map[id]) map[id]=[]; gain_list: list,
map[id].addArray(event.togive); giver: player,
} animate: 'draw',
if(cards.length>0) event.goto(2); }).toPromise().setContent('gaincardMultiple');
'step 5' if (event.count > 0 && player.hasSkill(event.name) && !get.is.blocked(event.name, player)) {
if(_status.connectMode){ const { result: { bool: repeat } } = await player.promises.chooseBool(get.prompt2(event.name)).set('frequentSkill', event.name);
game.broadcastAll(function(){delete _status.noclearcountdown;game.stopCountChoose()}); if (repeat) {
} player.logSkill(event.name);
var list=[]; } else return;
for(var i in event.given_map){ }
var source=(_status.connectMode?lib.playerOL:game.playerMap)[i]; else return;
player.line(source,'green');
list.push([source,event.given_map[i]]);
}
game.loseAsync({
gain_list:list,
giver:player,
animate:'draw',
}).setContent('gaincardMultiple');
'step 6'
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){
player.logSkill(event.name);
event.goto(1);
} }
}, },
ai:{ ai:{

View File

@ -47,6 +47,7 @@ new Promise(resolve=>{
} }
} }
const GeneratorFunction=(function*(){}).constructor; const GeneratorFunction=(function*(){}).constructor;
const AsyncFunction=(async function(){}).constructor;
// gnc: GeNCoroutine // gnc: GeNCoroutine
const gnc={ const gnc={
of:fn=>gnc.is.generatorFunc(fn)?function genCoroutine(){ of:fn=>gnc.is.generatorFunc(fn)?function genCoroutine(){
@ -21030,6 +21031,22 @@ new Promise(resolve=>{
}; };
player.queueCount=0; player.queueCount=0;
player.outCount=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){ buildEventListener(noclick){
let player = this; let player = this;
@ -31288,6 +31305,14 @@ new Promise(resolve=>{
this._notrigger=[]; this._notrigger=[];
this._result={}; this._result={};
this._set=[]; 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; if(trigger!==false&&!game.online) this._triggered=0;
} }
static initialGameEvent(){ static initialGameEvent(){
@ -31517,7 +31542,10 @@ new Promise(resolve=>{
switch(typeof item){ switch(typeof item){
case "object": case "object":
case "function": case "function":
this.content=lib.init.parsex(item); if(item instanceof AsyncFunction){
this.content=item;
}
else this.content=lib.init.parsex(item);
break; break;
default: default:
try{ try{
@ -32073,6 +32101,114 @@ new Promise(resolve=>{
this.unreal; this.unreal;
throw new Error('Do not call this method'); 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{ Dialog:class extends HTMLDivElement{
constructor(){ constructor(){
@ -41263,218 +41399,214 @@ new Promise(resolve=>{
setTimeout(game.reload,15000) setTimeout(game.reload,15000)
} }
}, },
loop:function(){ /**
while(true){ * @param { GameEvent } [belongAsyncEvent]
var event=_status.event; */
var step=event.step; async loop(belongAsyncEvent){
var source=event.source; if(belongAsyncEvent){
var player=event.player; game.belongAsyncEvent=belongAsyncEvent;
var target=event.target; }else if(game.belongAsyncEvent){
var targets=event.targets; return game.loop(game.belongAsyncEvent);
var card=event.card; }
var cards=event.cards; while (true) {
var skill=event.skill; let event = (belongAsyncEvent && belongAsyncEvent.parent == _status.event) ? belongAsyncEvent : _status.event;
var forced=event.forced; let { step, source, player, target, targets, card, cards, skill, forced, num, _trigger: trigger, _result: result } = event;
var num=event.num; const _resolve = () => {
var trigger=event._trigger; if (event.async) {
var result=event._result; if (typeof event.resolve == 'function') {
if(_status.paused2||_status.imchoosing){ event.resolve(event);
if(!lib.status.dateDelaying){ } else {
lib.status.dateDelaying=new Date(); throw new TypeError('异步事件的event.resolve未赋值使用await时将会被永久等待');
}
}
};
if (_status.paused2 || _status.imchoosing) {
if (!lib.status.dateDelaying) {
lib.status.dateDelaying = new Date();
} }
} }
if(_status.paused||_status.paused2||_status.over){ if (_status.paused || _status.paused2 || _status.over) {
return; return;
} }
if(_status.paused3){ if (_status.paused3) {
_status.paused3='paused'; _status.paused3 = 'paused';
return; return;
} }
if(lib.status.dateDelaying){ if (lib.status.dateDelaying) {
lib.status.dateDelayed+=lib.getUTC(new Date())-lib.getUTC(lib.status.dateDelaying); lib.status.dateDelayed += lib.getUTC(new Date()) - lib.getUTC(lib.status.dateDelaying);
delete lib.status.dateDelaying; delete lib.status.dateDelaying;
} }
if(event.next.length>0){ if (event.next.length > 0) {
var next=event.next.shift(); var next = event.next.shift();
if(next.player&&next.player.skipList.contains(next.name)){ if (next.player && next.player.skipList.contains(next.name)) {
event.trigger(next.name+'Skipped'); event.trigger(next.name + 'Skipped');
next.player.skipList.remove(next.name); next.player.skipList.remove(next.name);
if(lib.phaseName.contains(next.name)) next.player.getHistory('skipped').add(next.name); if (lib.phaseName.contains(next.name)) next.player.getHistory('skipped').add(next.name);
} }
else{ else {
next.parent=event; next.parent = event;
_status.event=next; _status.event = next;
game.getGlobalHistory('everything').push(next); game.getGlobalHistory('everything').push(next);
} }
} }
else if(event.finished){ else if (event.finished) {
if(event._triggered==1){ if (event._triggered == 1) {
if(event.type=='card') event.trigger('useCardToOmitted'); if (event.type == 'card') event.trigger('useCardToOmitted');
event.trigger(event.name+'Omitted'); event.trigger(event.name + 'Omitted');
event._triggered=4; event._triggered = 4;
} }
else if(event._triggered==2){ else if (event._triggered == 2) {
if(event.type=='card') event.trigger('useCardToEnd'); if (event.type == 'card') event.trigger('useCardToEnd');
event.trigger(event.name+'End'); event.trigger(event.name + 'End');
event._triggered=3; event._triggered = 3;
} }
else if(event._triggered==3){ else if (event._triggered == 3) {
if(event.type=='card') event.trigger('useCardToAfter'); if (event.type == 'card') event.trigger('useCardToAfter');
event.trigger(event.name+'After'); event.trigger(event.name + 'After');
event._triggered++; event._triggered++;
} }
else if(event.after&&event.after.length){ else if (event.after && event.after.length) {
var next=event.after.shift(); var next = event.after.shift();
if(next.player&&next.player.skipList.contains(next.name)){ if (next.player && next.player.skipList.contains(next.name)) {
event.trigger(next.name+'Skipped'); event.trigger(next.name + 'Skipped');
next.player.skipList.remove(next.name); next.player.skipList.remove(next.name);
if(lib.phaseName.contains(next.name)) next.player.getHistory('skipped').add(next.name) if (lib.phaseName.contains(next.name)) next.player.getHistory('skipped').add(next.name)
} }
else{ else {
next.parent=event; next.parent = event;
_status.event=next; _status.event = next;
} }
} }
else{ else {
if(event.parent){ if (event.parent) {
if(event.result){ if (event.result) {
event.parent._result=event.result; 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;
} }
_status.event=event.parent;
} }
else{ else {
return; if (game.belongAsyncEvent == event) {
delete game.belongAsyncEvent;
//resolve();
}
return _resolve();
} }
} }
} }
else{ else {
if(event._triggered==0){ if (event._triggered == 0) {
if(event.type=='card') event.trigger('useCardToBefore'); if (event.type == 'card') event.trigger('useCardToBefore');
event.trigger(event.name+'Before'); event.trigger(event.name + 'Before');
event._triggered++; event._triggered++;
} }
else if(event._triggered==1){ else if (event._triggered == 1) {
if(event.type=='card') event.trigger('useCardToBegin'); if (event.type == 'card') event.trigger('useCardToBegin');
event.trigger(event.name+'Begin'); event.trigger(event.name + 'Begin');
event._triggered++; 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{ else {
event.callHandler(event.getDefaultHandlerType(),event,{ event.callHandler(event.getDefaultHandlerType(), event, {
state:'begin' state: 'begin'
}); });
if(player&&player.classList.contains('dead')&&!event.forceDie&&event.name!='phaseLoop'){ const after = () => {
game.broadcastAll(function(){ event.clearStepCache();
while(_status.dieClose.length){ 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) {
_status.dieClose.shift().close(); _status.dieClose.shift().close();
} }
}); });
if(event._oncancel){ if (event._oncancel) {
event._oncancel(); event._oncancel();
} }
event.finish(); event.finish();
after();
} }
else if(player&&player.removed&&event.name!='phaseLoop'){ else if (player && player.removed && event.name != 'phaseLoop') {
event.finish(); event.finish();
after();
} }
else if(player&&player.isOut()&&event.name!='phaseLoop'&&!event.includeOut){ else if (player && player.isOut() && event.name != 'phaseLoop' && !event.includeOut) {
if(event.name=='phase'&&player==_status.roundStart&&!event.skill){ if (event.name == 'phase' && player == _status.roundStart && !event.skill) {
_status.roundSkipped=true; _status.roundSkipped = true;
} }
event.finish(); event.finish();
after();
} }
else{ else {
if(_status.withError||lib.config.compatiblemode||(_status.connectMode&&!lib.config.debug)){ await game.runContent(belongAsyncEvent).catch(e => {
try{ if (_status.withError || lib.config.compatiblemode || (_status.connectMode && !lib.config.debug)) {
if(event.content instanceof GeneratorFunction){ game.print('游戏出错:' + event.name);
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()); game.print(e.toString());
console.log(e); console.log(e);
} }
} else throw e;
else{ }).then(after);
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);
}
}
} }
event.clearStepCache();
event.callHandler(event.getDefaultHandlerType(),event,{
state:'end'
});
if(typeof event.step=="number") ++event.step;
} }
} }
} }
}, },
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();
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();
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();
}
});
},
pause:function(){ pause:function(){
clearTimeout(_status.timeout); clearTimeout(_status.timeout);
_status.paused=true; _status.paused=true;