Merge branch 'libccy:PR-Branch' into PR-Branch
|
@ -1,3 +1,9 @@
|
|||
noname-server.exe的源码见以下仓库:
|
||||
|
||||
https://github.com/nonameShijian/noname-server
|
||||
|
||||
---
|
||||
|
||||
在线试玩:
|
||||
|
||||
https://spmario233.github.io/noname/index.html (图片素材加载速度较慢,不推荐)
|
||||
|
|
|
@ -272,7 +272,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
game.cardsGotoOrdering(cards);
|
||||
const color=event.result.card.name=='wuxie'?'black':'red';
|
||||
if(get.color(cards,false)!=color){
|
||||
player.tempBanSkill('jsrgwentian');
|
||||
player.tempBanSkill('jsrgwentian','roundStart');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,12 +284,12 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
usable:1,
|
||||
filter(event,player){
|
||||
const zhu=get.zhu(player);
|
||||
if(!zhu||!zhu.isZhu2()) return false;
|
||||
return !player.isZhu2();
|
||||
if(!zhu||!zhu.isZhu2()||!zhu.countCards('h')) return false;
|
||||
return !player.isZhu2()&&player.countCards('h');
|
||||
},
|
||||
async content(event,trigger,player){
|
||||
player.chooseToDebate(game.filterPlayer(current=>{
|
||||
return current==player||current.isZhu2();
|
||||
return (current==player||current.isZhu2())&¤t.countCards('h');
|
||||
})).set('callback',async event=>{
|
||||
const result=event.debateResult;
|
||||
if(result.bool&&result.opinion){
|
||||
|
@ -574,7 +574,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
if(!get.event('goon')) return 0;
|
||||
return -get.attitude(get.player(),target);
|
||||
}).set('goon',player.countCards('hs',['shan','caochuan'])||player.getHp()>=3);
|
||||
if(!result.bool) return event.finish();
|
||||
if(!result.bool) return;
|
||||
const {targets}=result,target=targets[0];
|
||||
player.logSkill('jsrgyoujin',target);
|
||||
const {result:result2}=await player.chooseToCompare(target).set('small',true);
|
||||
|
@ -701,8 +701,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
return p+c;
|
||||
},0)<-4).set('logSkill',['jsrglonglin',trigger.player]);
|
||||
if(result.bool){
|
||||
trigger.targets.length=0;
|
||||
trigger.all_excluded=true;
|
||||
trigger.excluded.addArray(trigger.targets);
|
||||
game.asyncDelayx();
|
||||
if(trigger.player.canUse(juedou,player)){
|
||||
const {result}=await trigger.player.chooseBool(`是否视为对${get.translation(player)}使用一张【决斗】?`).set('choice',get.effect(player,juedou,trigger.player,trigger.player)>=0);
|
||||
|
@ -880,7 +879,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
]);
|
||||
next.set('prompt','鹰眎:点击将牌移动到牌堆顶或牌堆底');
|
||||
next.processAI=list=>{
|
||||
const cards=list[0][1],player=_status.event.player;
|
||||
const cards=list[1][1],player=_status.event.player;
|
||||
const top=[];
|
||||
const judges=player.getCards('j');
|
||||
let stopped=false;
|
||||
|
@ -977,6 +976,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
global:['gainAfter','equipAfter','addJudgeAfter','loseAsyncAfter','addToExpansionAfter'],
|
||||
},
|
||||
filter(event,player){
|
||||
if(player.isHealthy()) return false;
|
||||
const evt=event.getl(player);
|
||||
return evt&&evt.es&&evt.es.length>0;
|
||||
},
|
||||
|
@ -1011,7 +1011,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
charlotte:true,
|
||||
forced:true,
|
||||
silent:true,
|
||||
async content(event,trigger,player){
|
||||
content(){
|
||||
trigger.player.addGaintag(trigger.cards,'jsrgtuigu');
|
||||
trigger.player.addTempSkill('jsrgtuigu_blocked',{player:'phaseAfter'});
|
||||
}
|
||||
|
@ -1213,6 +1213,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
enable:'chooseToUse',
|
||||
viewAs:{name:'lebu'},
|
||||
position:'hes',
|
||||
viewAsFilter(player){
|
||||
return player.countCards('hes');
|
||||
},
|
||||
filterCard(card,player){
|
||||
return get.color(card)=='red'&&get.type2(card)!='trick';
|
||||
},
|
||||
|
@ -1506,22 +1509,29 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
effect:{
|
||||
audio:'jsrgdanxin',
|
||||
trigger:{
|
||||
player:['loseAfter','gainAfter'],
|
||||
global:'gainAfter',
|
||||
},
|
||||
filter(event,player){
|
||||
if(event.getParent(2).name!='tuixinzhifu') return false;
|
||||
const card=event.getParent(3).card;
|
||||
const level=event.player!=player?1:2;
|
||||
if(event.player!=player&&event.getParent(level).name!='tuixinzhifu') return false;
|
||||
if(event.player==player&&event.getParent(level).name!='tuixinzhifu') return false;
|
||||
const card=event.getParent(level+1).card;
|
||||
return card&&card.storage&&card.storage.jsrgdanxin;
|
||||
},
|
||||
forced:true,
|
||||
popup:false,
|
||||
charlotte:true,
|
||||
async content(event,trigger,player){
|
||||
const {targets}=trigger.getParent(3);
|
||||
const level=trigger.player!=player?1:2;
|
||||
const {targets}=trigger.getParent(level+1);
|
||||
await player.showCards(trigger.cards);
|
||||
if(trigger.cards.some(card=>get.suit(card)=='heart')){
|
||||
await get.owner(trigger.cards.find(card=>get.suit(card)=='heart')).recover();
|
||||
const owners=trigger.cards.filter((card=>get.suit(card)=='heart')).map(card=>get.owner(card)).toUniqued();
|
||||
for(const owner of owners){
|
||||
if(owner&&owner.isIn()) await owner.recover();
|
||||
}
|
||||
}
|
||||
if(trigger.player==player) return;
|
||||
player.addTempSkill('jsrgdanxin_distance');
|
||||
if(!player.storage.jsrgdanxin_distance) player.storage.jsrgdanxin_distance={};
|
||||
const id=targets[0].playerid;
|
||||
|
@ -1570,7 +1580,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
},true).set('ai',target=>{
|
||||
const player=get.player();
|
||||
const att=get.attitude(player,target);
|
||||
const delta=get.value(target.getCards('e'),player)-get.value(player.getCards('e'),player);
|
||||
let delta=get.value(target.getCards('e'),player)-get.value(player.getCards('e'),player);
|
||||
if(att>0){
|
||||
if(delta<0) delta+=att/3;
|
||||
}
|
||||
|
@ -1754,7 +1764,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
async content(event,trigger,player){
|
||||
const target=event.target;
|
||||
const targets=game.filterPlayer(current=>target.inRange(current)&¤t!=player).sortBySeat(player);
|
||||
if(!targets.length) return event.finish();
|
||||
if(!targets.length) return;
|
||||
while(targets.length){
|
||||
const current=targets.shift();
|
||||
if(current.countCards('he')) await current.chooseToDiscard('驰应:请弃置一张牌','he',true);
|
||||
|
@ -1777,7 +1787,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
const targets=game.filterPlayer(current=>target.inRange(current)&¤t!=player);
|
||||
let eff=0;
|
||||
for(const targetx of targets){
|
||||
const effx=get.effect(targetx,{name:'guohe_copy2'},player,target);
|
||||
let effx=get.effect(targetx,{name:'guohe_copy2'},player,target);
|
||||
if(get.attitude(player,targetx)<0) effx/=2;
|
||||
eff+=effx;
|
||||
}
|
||||
|
@ -8200,7 +8210,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
jsrg_zhugeliang:'梦诸葛亮',
|
||||
jsrg_zhugeliang_prefix:'梦',
|
||||
jsrgwentian:'问天',
|
||||
jsrgwentian_info:'①你可以将牌堆顶的牌当【无懈可击】/【火攻】使用,若此牌不为黑色/红色,〖问天〗于本回合失效。②每回合限一次。你的一个阶段开始时,你可以观看牌堆顶的五张牌,然后将其中一张牌交给一名其他角色,将其余牌以任意顺序置于牌堆顶或牌堆底。',
|
||||
jsrgwentian_info:'①你可以将牌堆顶的牌当【无懈可击】/【火攻】使用,若此牌不为黑色/红色,〖问天〗于本轮失效。②每回合限一次。你的一个阶段开始时,你可以观看牌堆顶的五张牌,然后将其中一张牌交给一名其他角色,将其余牌以任意顺序置于牌堆顶或牌堆底。',
|
||||
jsrgchushi:'出师',
|
||||
jsrgchushi_info:'出牌阶段限一次。若你不为主公,你可以与主公议事。若结果为:红色,你与其各摸一张牌,若你与其手牌数之和小于7,重复此流程;黑色,当你于本轮内造成属性伤害时,此伤害+1。',
|
||||
jsrgyinlve:'隐略',
|
||||
|
|
|
@ -954,7 +954,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
|
|||
forced:true,
|
||||
content:function(){
|
||||
'step 0'
|
||||
player.addTempSkill('olsilv'+(trigger.getg?'gain':'lose'));
|
||||
player.addTempSkill('olsilv_'+(trigger.getg?'gain':'lose'));
|
||||
if(!trigger.visible){
|
||||
var cards,name=player.storage.ollianju;
|
||||
if(trigger.getg) cards=trigger.getg(player).filter(card=>card.name==name);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
window.noname_asset_list=[
|
||||
'v1.10.6',
|
||||
'v1.10.6.1',
|
||||
/*audio start*/
|
||||
'audio/background/aozhan_chaoming.mp3',
|
||||
'audio/background/aozhan_online.mp3',
|
||||
|
@ -7237,30 +7237,41 @@ window.noname_asset_list=[
|
|||
'image/character/jsp_liubei.jpg',
|
||||
'image/character/jsp_zhaoyun.jpg',
|
||||
'image/character/jsrg_caocao.jpg',
|
||||
'image/character/jsrg_caofang.jpg',
|
||||
'image/character/jsrg_chendeng.jpg',
|
||||
'image/character/jsrg_chunyuqiong.jpg',
|
||||
'image/character/jsrg_dongbai.jpg',
|
||||
'image/character/jsrg_fanjiangzhangda.jpg',
|
||||
'image/character/jsrg_gaoxiang.jpg',
|
||||
'image/character/jsrg_guanyu.jpg',
|
||||
'image/character/jsrg_guojia.jpg',
|
||||
'image/character/jsrg_guoxun.jpg',
|
||||
'image/character/jsrg_guozhao.jpg',
|
||||
'image/character/jsrg_hansui.jpg',
|
||||
'image/character/jsrg_hejin.jpg',
|
||||
'image/character/jsrg_huangfusong.jpg',
|
||||
'image/character/jsrg_huangzhong.jpg',
|
||||
'image/character/jsrg_jiangwei.jpg',
|
||||
'image/character/jsrg_kongrong.jpg',
|
||||
'image/character/jsrg_liubei.jpg',
|
||||
'image/character/jsrg_liuhong.jpg',
|
||||
'image/character/jsrg_liuyan.jpg',
|
||||
'image/character/jsrg_liuyong.jpg',
|
||||
'image/character/jsrg_lougui.jpg',
|
||||
'image/character/jsrg_luxun.jpg',
|
||||
'image/character/jsrg_lvbu.jpg',
|
||||
'image/character/jsrg_machao.jpg',
|
||||
'image/character/jsrg_nanhualaoxian.jpg',
|
||||
'image/character/jsrg_pangtong.jpg',
|
||||
'image/character/jsrg_qiaoxuan.jpg',
|
||||
'image/character/jsrg_simayi.jpg',
|
||||
'image/character/jsrg_sunce.jpg',
|
||||
'image/character/jsrg_sunjian.jpg',
|
||||
'image/character/jsrg_sunjun.jpg',
|
||||
'image/character/jsrg_sunlubansunluyu.jpg',
|
||||
'image/character/jsrg_sunshangxiang.jpg',
|
||||
'image/character/jsrg_wangyun.jpg',
|
||||
'image/character/jsrg_weiwenzhugezhi.jpg',
|
||||
'image/character/jsrg_xiahouen.jpg',
|
||||
'image/character/jsrg_xiahourong.jpg',
|
||||
'image/character/jsrg_xugong.jpg',
|
||||
|
@ -7272,7 +7283,10 @@ window.noname_asset_list=[
|
|||
'image/character/jsrg_zhanghe.jpg',
|
||||
'image/character/jsrg_zhangliao.jpg',
|
||||
'image/character/jsrg_zhangren.jpg',
|
||||
'image/character/jsrg_zhangxuan.jpg',
|
||||
'image/character/jsrg_zhaoyun.jpg',
|
||||
'image/character/jsrg_zhenji.jpg',
|
||||
'image/character/jsrg_zhugeliang.jpg',
|
||||
'image/character/jsrg_zhujun.jpg',
|
||||
'image/character/jsrg_zoushi.jpg',
|
||||
'image/character/jun_caocao.jpg',
|
||||
|
|
101
game/update.js
|
@ -1,137 +1,56 @@
|
|||
window.noname_update={
|
||||
version:'1.10.6',
|
||||
update:'1.10.5',
|
||||
version:'1.10.6.1',
|
||||
update:'1.10.6',
|
||||
changeLog:[
|
||||
'整合@nonameShijian @mengxinzxz @PZ157 @Ansolve @Rintim @S-N-O-R-L-A-X @universe-st @copcap @kuangshen04 的Pull Request',
|
||||
'拆分game.js,优化代码逻辑与可读性',
|
||||
'孙策(十周年斗地主)、乐小乔、手杀陈珪、手杀胡班、OL界凌统、OL界曹彰、OL界简雍、OL谋姜维、谋诸葛亮、谋关羽、曹宇、星董卓、曹宪、新杀谋鲁肃、新杀谋周瑜',
|
||||
'整合@mengxinzxz @PZ157 @universe-st @Ansolve @Rintim @nonameShijian @copcap @kuangshen04 的Pull Request',
|
||||
'《江山如故·合》武将包',
|
||||
'其他AI优化与bug修复',
|
||||
],
|
||||
files:[
|
||||
'card/extra.js',
|
||||
'card/gujian.js',
|
||||
'card/guozhan.js',
|
||||
'card/gwent.js',
|
||||
'card/hearth.js',
|
||||
'card/huanlekapai.js',
|
||||
'card/mtg.js',
|
||||
'card/sp.js',
|
||||
'card/standard.js',
|
||||
'card/swd.js',
|
||||
'card/yingbian.js',
|
||||
'card/yongjian.js',
|
||||
'card/yunchou.js',
|
||||
'card/zhenfa.js',
|
||||
'card/zhulu.js',
|
||||
|
||||
'character/clan.js',
|
||||
'character/collab.js',
|
||||
'character/ddd.js',
|
||||
'character/diy.js',
|
||||
'character/extra.js',
|
||||
'character/gujian.js',
|
||||
'character/gwent.js',
|
||||
'character/hearth.js',
|
||||
'character/huicui.js',
|
||||
'character/jiange.js',
|
||||
'character/jsrg.js',
|
||||
'character/mobile.js',
|
||||
'character/mtg.js',
|
||||
'character/offline.js',
|
||||
'character/old.js',
|
||||
'character/onlyOL.js',
|
||||
'character/ow.js',
|
||||
'character/rank.js',
|
||||
'character/refresh.js',
|
||||
'character/sb.js',
|
||||
'character/shenhua.js',
|
||||
'character/shiji.js',
|
||||
'character/sp.js',
|
||||
'character/sp2.js',
|
||||
'character/standard.js',
|
||||
'character/swd.js',
|
||||
'character/tw.js',
|
||||
'character/xiake.js',
|
||||
'character/xianding.js',
|
||||
'character/xianjian.js',
|
||||
'character/xinghuoliaoyuan.js',
|
||||
'character/yijiang.js',
|
||||
'character/yingbian.js',
|
||||
'character/yxs.js',
|
||||
|
||||
'extension/boss/extension.js',
|
||||
|
||||
'game/codemirror.js',
|
||||
'game/config.js',
|
||||
'game/core-js-bundle.js',
|
||||
'game/entry.js',
|
||||
'game/game.js',
|
||||
'game/package.js',
|
||||
'game/source.js',
|
||||
|
||||
'mode/boss.js',
|
||||
'mode/brawl.js',
|
||||
'mode/chess.js',
|
||||
'mode/doudizhu.js',
|
||||
'mode/guozhan.js',
|
||||
'mode/identity.js',
|
||||
'mode/realtime.js',
|
||||
'mode/single.js',
|
||||
'mode/stone.js',
|
||||
'mode/tafang.js',
|
||||
'mode/versus.js',
|
||||
|
||||
'noname.js',
|
||||
|
||||
'noname/ai/basic.js',
|
||||
'noname/ai/index.js',
|
||||
'noname/game/index.js',
|
||||
'noname/game/promises.js',
|
||||
'noname/game/dynamic-style/index.js',
|
||||
|
||||
'noname/get/index.js',
|
||||
'noname/get/is.js',
|
||||
'noname/gnc/index.js',
|
||||
'noname/gnc/is.js',
|
||||
|
||||
'noname/init/cordova.js',
|
||||
'noname/init/import.js',
|
||||
'noname/init/index.js',
|
||||
'noname/init/node.js',
|
||||
'noname/init/onload.js',
|
||||
'noname/init/polyfill.js',
|
||||
|
||||
'noname/library/index.js',
|
||||
'noname/library/path.js',
|
||||
'noname/library/announce/index.d.ts',
|
||||
'noname/library/announce/index.js',
|
||||
'noname/library/channel/index.js',
|
||||
'noname/library/element/button.js',
|
||||
'noname/library/element/card.js',
|
||||
'noname/library/element/client.js',
|
||||
|
||||
'noname/library/element/content.js',
|
||||
'noname/library/element/contents.js',
|
||||
'noname/library/element/control.js',
|
||||
'noname/library/element/dialog.js',
|
||||
'noname/library/element/gameEvent.js',
|
||||
'noname/library/element/gameEventPromise.js',
|
||||
'noname/library/element/index.js',
|
||||
'noname/library/element/nodeWS.js',
|
||||
'noname/library/element/player.js',
|
||||
'noname/library/element/vcard.js',
|
||||
'noname/library/experimental/index.js',
|
||||
'noname/library/experimental/symbol.js',
|
||||
'noname/library/init/index.js',
|
||||
'noname/library/init/promises.js',
|
||||
'noname/status/index.js',
|
||||
|
||||
'noname/ui/index.js',
|
||||
'noname/util/browser.js',
|
||||
'noname/util/config.js',
|
||||
'noname/util/index.js',
|
||||
'noname/util/mutex.js',
|
||||
'noname/util/struct/index.js',
|
||||
'noname/util/struct/interface/index.d.ts',
|
||||
'noname/util/struct/interface/promise-error-handler.d.ts',
|
||||
|
||||
'noname/util/struct/promise-error-handler/chrome.js',
|
||||
'noname/util/struct/promise-error-handler/firefox.js',
|
||||
'noname/util/struct/promise-error-handler/index.js',
|
||||
'noname/util/struct/promise-error-handler/unknown.js',
|
||||
]
|
||||
};
|
||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
20
index.html
|
@ -18,11 +18,19 @@
|
|||
</script>
|
||||
<script>
|
||||
if (location.href.startsWith('http') && typeof window.initReadWriteFunction != 'function' && !window.require && !window.__dirname) {
|
||||
window.initReadWriteFunction = function(game) {
|
||||
/*game.download = function() {
|
||||
// 暂不实现
|
||||
};*/
|
||||
window.initReadWriteFunction = async function(game) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`./readFile?fileName=noname.js`)
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then(result => {
|
||||
if (result && result.success) callback();
|
||||
else reject(result.errorMsg)
|
||||
})
|
||||
.catch(reject);
|
||||
|
||||
function callback() {
|
||||
game.createDir = function (dir, success = () => { }, error = () => { }) {
|
||||
fetch(`./createDir?dir=${dir}`)
|
||||
.then(response => {
|
||||
|
@ -130,6 +138,10 @@
|
|||
}
|
||||
game.createDir(pathArray.join("/"), callback, console.error);
|
||||
};
|
||||
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -8818,7 +8818,7 @@ game.import('mode',function(lib,game,ui,get,ai,_status){
|
|||
}
|
||||
function isDefined(opd) {
|
||||
if(opd!=undefined){
|
||||
if (opd.get||opd.set||opd.writable!=true||opd.configurable!=true||opd.enumerable!=true){
|
||||
if (opd.get||opd.set||opd.writable!=true||opd.configurable!=true){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ declare interface Window {
|
|||
get: Get;
|
||||
ai: AI;
|
||||
}
|
||||
|
||||
initReadWriteFunction?(game: Game): void;
|
||||
/** 为其他自定义平台提供文件读写函数赋值的一种方式 */
|
||||
initReadWriteFunction?(game: Game): Promise<void>;
|
||||
|
||||
bannedKeyWords: string[];
|
||||
}
|
||||
|
|
|
@ -1364,19 +1364,21 @@ export class Game extends Uninstantable {
|
|||
else if (!path.startsWith('db:')) path = `audio/${path}`;
|
||||
if (!lib.config.repeat_audio && _status.skillaudio.includes(path)) return;
|
||||
}
|
||||
_status.skillaudio.add(path);
|
||||
game.addVideo('playAudio', null, path);
|
||||
setTimeout(() => _status.skillaudio.remove(path), 1000);
|
||||
const audio = document.createElement('audio');
|
||||
audio.autoplay = true;
|
||||
audio.volume = lib.config.volumn_audio / 8;
|
||||
audio.addEventListener('ended', () => audio.remove());
|
||||
audio.onerror = event => {
|
||||
//Some browsers do not support "autoplay", so "oncanplay" listening has been added
|
||||
audio.oncanplay = () => Promise.resolve(audio.play()).catch(() => void 0);
|
||||
audio.onplay = () => {
|
||||
_status.skillaudio.add(path);
|
||||
setTimeout(() => _status.skillaudio.remove(path), 1000);
|
||||
game.addVideo("playAudio", null, path);
|
||||
};
|
||||
audio.onended = (event) => audio.remove();
|
||||
audio.onerror = (event) => {
|
||||
audio.remove();
|
||||
if (onError) onError(event);
|
||||
};
|
||||
//Some browsers do not support "autoplay", so "oncanplay" listening has been added
|
||||
audio.oncanplay = () => Promise.resolve(audio.play()).catch(() => void 0);
|
||||
new Promise((resolve, reject) => {
|
||||
if (path.startsWith('db:')) game.getDB('image', path.slice(3)).then(octetStream => resolve(get.objectURL(octetStream)), reject);
|
||||
else if (lib.path.extname(path)) resolve(`${lib.assetURL}${path}`);
|
||||
|
@ -4213,6 +4215,7 @@ export class Game extends Uninstantable {
|
|||
* @param { string } skill
|
||||
* @param { Player } player
|
||||
* @param { GameEventPromise } event
|
||||
* @returns { GameEventPromise }
|
||||
*/
|
||||
static createTrigger(name, skill, player, event) {
|
||||
let info = get.info(skill);
|
||||
|
@ -4227,6 +4230,7 @@ export class Game extends Uninstantable {
|
|||
next.includeOut = true;
|
||||
next._trigger = event;
|
||||
next.setContent('createTrigger');
|
||||
return next;
|
||||
}
|
||||
/**
|
||||
* @legacy Use {@link lib.element.GameEvent.constructor} instead.
|
||||
|
@ -5362,10 +5366,11 @@ export class Game extends Uninstantable {
|
|||
* @param { GameEventPromise } [belongAsyncEvent]
|
||||
*/
|
||||
static async loop(belongAsyncEvent) {
|
||||
if (!game.belongAsyncEventList) game.belongAsyncEventList = [];
|
||||
if (belongAsyncEvent) {
|
||||
game.belongAsyncEvent = belongAsyncEvent;
|
||||
} else if (game.belongAsyncEvent) {
|
||||
return game.loop(game.belongAsyncEvent);
|
||||
game.belongAsyncEventList.push(belongAsyncEvent);
|
||||
} else if (game.belongAsyncEventList.length) {
|
||||
belongAsyncEvent = game.belongAsyncEventList.at(-1);
|
||||
}
|
||||
while (true) {
|
||||
let event = (belongAsyncEvent && belongAsyncEvent.parent == _status.event) ? belongAsyncEvent : _status.event;
|
||||
|
@ -5443,8 +5448,8 @@ export class Game extends Uninstantable {
|
|||
event.parent._result = event.result;
|
||||
}
|
||||
_status.event = event.parent;
|
||||
if (game.belongAsyncEvent == event) {
|
||||
delete game.belongAsyncEvent;
|
||||
if (game.belongAsyncEventList.includes(event)) {
|
||||
game.belongAsyncEventList.remove(event);
|
||||
}
|
||||
_resolve();
|
||||
// 此时应该退出了
|
||||
|
@ -5453,8 +5458,8 @@ export class Game extends Uninstantable {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (game.belongAsyncEvent == event) {
|
||||
delete game.belongAsyncEvent;
|
||||
if (game.belongAsyncEventList.includes(event)) {
|
||||
game.belongAsyncEventList.remove(event);
|
||||
}
|
||||
return _resolve();
|
||||
}
|
||||
|
|
|
@ -1367,10 +1367,12 @@ export class Get extends Uninstantable {
|
|||
}
|
||||
static infoFuncOL(info) {
|
||||
var func;
|
||||
const str = info.slice(13).trim();
|
||||
try {
|
||||
eval('func=(' + info.slice(13) + ');');
|
||||
}
|
||||
catch (e) {
|
||||
if (str.startsWith("function") || str.startsWith("(")) eval(`func=(${str});`);
|
||||
else eval(`func=(function ${str});`);
|
||||
} catch (e) {
|
||||
console.error(`${e} in \n${str}`);
|
||||
return function () {};
|
||||
}
|
||||
if (Array.isArray(func)) {
|
||||
|
|
|
@ -17,7 +17,7 @@ export async function cordovaReady() {
|
|||
}
|
||||
});
|
||||
document.addEventListener("resume", () => {
|
||||
if (ui.backgroundMusic) ui.backgroundMusic.play();
|
||||
if (ui.backgroundMusic && !isNaN(ui.backgroundMusic.duration)) ui.backgroundMusic.play();
|
||||
});
|
||||
document.addEventListener("backbutton", function () {
|
||||
if (ui.arena && ui.arena.classList.contains('menupaused')) {
|
||||
|
|
|
@ -106,7 +106,7 @@ export async function boot() {
|
|||
else if (!Reflect.has(lib, 'device')) {
|
||||
Reflect.set(lib, 'path', (await import('../library/path.js')).default);
|
||||
//为其他自定义平台提供文件读写函数赋值的一种方式。
|
||||
//但这种方式只能修改game的文件读写函数。
|
||||
//但这种方式只允许修改game的文件读写函数。
|
||||
if (typeof window.initReadWriteFunction == 'function') {
|
||||
const g = {};
|
||||
const ReadWriteFunctionName = ['download', 'readFile', 'readFileAsText', 'writeFile', 'removeFile', 'getFileList', 'ensureDirectory', 'createDir'];
|
||||
|
@ -123,7 +123,9 @@ export async function boot() {
|
|||
});
|
||||
});
|
||||
// @ts-ignore
|
||||
window.initReadWriteFunction(g);
|
||||
await window.initReadWriteFunction(g).catch(e => {
|
||||
console.error('文件读写函数初始化失败:', e);
|
||||
});
|
||||
}
|
||||
window.onbeforeunload = function () {
|
||||
if (config.get('confirm_exit') && !_status.reloading) {
|
||||
|
@ -861,7 +863,8 @@ async function setOnError() {
|
|||
}
|
||||
}
|
||||
//解析parsex里的content fun内容(通常是技能content)
|
||||
else if (err && err.stack && err.stack.split('\n')[1].trim().startsWith('at Object.eval [as content]')) {
|
||||
// @ts-ignore
|
||||
else if (err && err.stack && ['at Object.eval [as content]', 'at Proxy.content'].some(str => err.stack.split('\n')[1].trim().startsWith(str))) {
|
||||
const codes = _status.event.content;
|
||||
if (typeof codes == 'function') {
|
||||
const lines = codes.toString().split("\n");
|
||||
|
|
|
@ -393,6 +393,14 @@ export const Content = {
|
|||
event.swapped = true;
|
||||
}
|
||||
"step 5";
|
||||
if (get.itemtype(result) == 'cards') {
|
||||
for (let card of result){
|
||||
if (card.willBeDestroyed('discardPile', player, event)) {
|
||||
card.selfDestroy(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
"step 6";
|
||||
//if(player.isMin() || player.countCards('e',{subtype:get.subtype(card)})){
|
||||
if (player.isMin() || !player.canEquip(card)) {
|
||||
event.finish();
|
||||
|
@ -411,7 +419,7 @@ export const Content = {
|
|||
game.addVideo('equip', player, get.cardInfo(card));
|
||||
if (event.log != false) game.log(player, '装备了', card);
|
||||
if (event.updatePile) game.updateRoundNumber();
|
||||
"step 6";
|
||||
"step 7";
|
||||
var info = get.info(card, false);
|
||||
if (info.onEquip && (!info.filterEquip || info.filterEquip(card, player))) {
|
||||
if (Array.isArray(info.onEquip)) {
|
||||
|
@ -2002,48 +2010,57 @@ export const Content = {
|
|||
event.callback();
|
||||
}
|
||||
},
|
||||
arrangeTrigger: function () {
|
||||
'step 0';
|
||||
event.doing = event.doingList[0];
|
||||
if (event.doing && event.doing.todoList.length) return;
|
||||
if (event.doingList.length) {
|
||||
event.doingList.shift();
|
||||
return event.redo();
|
||||
}
|
||||
event.finish();
|
||||
'step 1';
|
||||
if (trigger.filterStop && trigger.filterStop()) return event.finish();
|
||||
event.current = event.doing.todoList.find(info => lib.filter.filterTrigger(trigger, info.player, event.triggername, info.skill));
|
||||
if (!event.current) {
|
||||
event.doing.todoList = [];
|
||||
return event.goto(0);
|
||||
}
|
||||
event.doing.todoList = event.doing.todoList.filter(i => i.priority <= event.current.priority);
|
||||
|
||||
const directUse = info => lib.skill[info.skill].silent || !lib.translate[info.skill];//是否不触发同顺序选择
|
||||
if (directUse(event.current)) return event.goto(4);
|
||||
event.choice = event.doing.todoList.filter(info => {
|
||||
arrangeTrigger: async function (event,trigger,player) {
|
||||
while(event.doingList.length>0){
|
||||
event.doing = event.doingList.shift();
|
||||
while(true){
|
||||
if (trigger.filterStop && trigger.filterStop()) return;
|
||||
const usableSkills = event.doing.todoList.filter(info => {
|
||||
if (!lib.filter.filterTrigger(trigger, info.player, event.triggername, info.skill)) return false;
|
||||
if (directUse(info)) return false;
|
||||
if (event.current.player !== info.player) return false;
|
||||
return lib.skill.global.includes(info.skill) || event.current.player.hasSkill(info.skill, true);
|
||||
return lib.skill.global.includes(info.skill) || info.player.hasSkill(info.skill, true);
|
||||
});
|
||||
event.choice = event.choice.filter(n=>n.priority == event.choice[0].priority);
|
||||
if (event.choice.length < 2) return event.goto(4);
|
||||
'step 2';
|
||||
const next = event.choice[0].player.chooseControl(event.choice.map(i => i.skill));
|
||||
if (usableSkills.length == 0){
|
||||
break;
|
||||
}
|
||||
else {
|
||||
event.doing.todoList = event.doing.todoList.filter(i => i.priority <= usableSkills[0].priority);
|
||||
//firstDo时机和lastDo时机不进行技能优先级选择
|
||||
if (get.itemtype(event.doing.player) !== 'player'){
|
||||
event.current = usableSkills[0];
|
||||
}
|
||||
else {
|
||||
event.choice = usableSkills.filter(n => n.priority == usableSkills[0].priority);
|
||||
//现在只要找到一个同优先度技能为silent 便优先执行该技能
|
||||
const silentSkill = event.choice.find(item => {
|
||||
const skillInfo = lib.skill[item.skill];
|
||||
return (skillInfo && skillInfo.silent);
|
||||
})
|
||||
if (silentSkill){
|
||||
event.current = silentSkill;
|
||||
}
|
||||
else {
|
||||
const currentChoice = event.choice[0];
|
||||
if (event.choice.length == 1) {
|
||||
event.current = currentChoice;
|
||||
}
|
||||
else{
|
||||
const currentPlayer = currentChoice.player , skillsToChoose = event.choice.map(i => i.skill);
|
||||
const next = currentPlayer.chooseControl(skillsToChoose);
|
||||
next.set('prompt', '选择下一个触发的技能');
|
||||
next.set('forceDie', true);
|
||||
next.set('arrangeSkill', true);
|
||||
next.set('includeOut', true);
|
||||
'step 3';
|
||||
if (result.control) event.current = event.doing.todoList.find(info => info.skill == result.control && info.player == event.choice[0].player);
|
||||
'step 4';
|
||||
if (!event.current || !event.doing.todoList.includes(event.current)) return;
|
||||
const {result} = await next;
|
||||
event.current = event.doing.todoList.find(info => info.skill == result.control);
|
||||
}
|
||||
}
|
||||
}
|
||||
event.doing.doneList.push(event.current);
|
||||
event.doing.todoList.remove(event.current);
|
||||
game.createTrigger(event.triggername, event.current.skill, event.current.player, trigger);
|
||||
event.goto(0);
|
||||
await game.createTrigger(event.triggername, event.current.skill, event.current.player, trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
createTrigger: function () {
|
||||
"step 0";
|
||||
|
@ -8167,8 +8184,13 @@ export const Content = {
|
|||
cards[0].classList.add('fakejudge');
|
||||
cards[0].node.background.innerHTML = lib.translate[cards[0].viewAs + '_bg'] || get.translation(cards[0].viewAs)[0];
|
||||
}
|
||||
if(lib.card[viewAs].blankCard){
|
||||
game.log(player, '被扣置了<span class="yellowtext">' + get.translation(cards[0].viewAs) + '</span>');
|
||||
}
|
||||
else {
|
||||
game.log(player, '被贴上了<span class="yellowtext">' + get.translation(cards[0].viewAs) + '</span>(', cards, ')');
|
||||
}
|
||||
}
|
||||
else {
|
||||
cards[0].classList.remove('fakejudge');
|
||||
game.log(player, '被贴上了', cards);
|
||||
|
|
|
@ -9050,10 +9050,9 @@ class Create extends Uninstantable {
|
|||
ui.backgroundMusic.autoplay = true;
|
||||
ui.backgroundMusic.addEventListener('ended', game.playBackgroundMusic);
|
||||
ui.window.appendChild(ui.backgroundMusic);
|
||||
ui.window.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', function playMusic() {
|
||||
ui.window.removeEventListener(lib.config.touchscreen ? 'touchend' : 'click', playMusic, false);
|
||||
if (!ui.backgroundMusic.played.length && lib.config.background_music != 'music_off') ui.backgroundMusic.play();
|
||||
}, false);
|
||||
ui.window.addEventListener(lib.config.touchscreen ? 'touchend' : 'click', () => {
|
||||
if (!ui.backgroundMusic.played.length && lib.config.background_music != 'music_off' && !isNaN(ui.backgroundMusic.duration)) ui.backgroundMusic.play();
|
||||
}, {once:true});
|
||||
if (lib.config.cursor_style == 'pointer') {
|
||||
ui.window.classList.add('nopointer');
|
||||
}
|
||||
|
@ -13419,12 +13418,14 @@ class Click extends Uninstantable {
|
|||
// 有bug,先用旧版
|
||||
if (lib.config.background_speak && e !== 'init') {
|
||||
let audio, skillnode = this;
|
||||
const playedAudios = [];
|
||||
(function play() {
|
||||
if (!skillnode.audioList || !skillnode.audioList.length) {
|
||||
skillnode.audioList = game.parseSkillAudio(skillnode.link, playername);
|
||||
if (!skillnode.audioList.length) return;
|
||||
if (!skillnode.audioList.length||skillnode.audioList.length==playedAudios.length) return;
|
||||
}
|
||||
audio = skillnode.audioList.shift();
|
||||
playedAudios.push(audio);
|
||||
game.playAudio(audio, play);
|
||||
})();
|
||||
}
|
||||
|
@ -13634,12 +13635,14 @@ class Click extends Uninstantable {
|
|||
// 有bug,先用旧版
|
||||
if (lib.config.background_speak && e !== 'init') {
|
||||
let audio, skillnode = this;
|
||||
const playedAudios = [];
|
||||
(function play() {
|
||||
if (!skillnode.audioList || !skillnode.audioList.length) {
|
||||
skillnode.audioList = game.parseSkillAudio(skillnode.link, playername);
|
||||
if (!skillnode.audioList.length) return;
|
||||
if (!skillnode.audioList.length||skillnode.audioList.length==playedAudios.length) return;
|
||||
}
|
||||
audio = skillnode.audioList.shift();
|
||||
playedAudios.push(audio);
|
||||
game.playAudio(audio, play);
|
||||
})();
|
||||
}
|
||||
|
@ -14142,6 +14145,10 @@ export class UI extends Uninstantable {
|
|||
* @type { HTMLDivElement }
|
||||
*/
|
||||
static pause;
|
||||
/**
|
||||
* @type { HTMLAudioElement }
|
||||
*/
|
||||
static backgroundMusic;
|
||||
static refresh(node) {
|
||||
void window.getComputedStyle(node, null).getPropertyValue("opacity");
|
||||
}
|
||||
|
|
|
@ -1,36 +1,49 @@
|
|||
/**
|
||||
* 关于`Google Chrome`的异步错误处理
|
||||
*
|
||||
* `Chrome`所用的`v8`引擎为`Error`提供了特有的报错栈堆处理函数,用于用户自定义报错栈堆的内容。
|
||||
* 由于`v8`提供的`Error.prepareStackTrace(error, structuredStackTrace)`接口存在一些限制,导致不适合无名杀
|
||||
*
|
||||
* 我们用到了`Error.prepareStackTrace(error, structuredStackTrace)`这个函数,这个函数的信息可参考[这里](https://v8.dev/docs/stack-trace-api#customizing-stack-traces)
|
||||
* 故我们直接解析`Error`的栈堆信息,来获取相关内容
|
||||
*
|
||||
* 该函数提供了结构化的栈堆信息,很幸运的是,这个结构化的栈堆能直接告诉我们报错的文件以及位置,故我们使用该函数,让异步报错能直接定位原始位置
|
||||
* ~~`Chrome`所用的`v8`引擎为`Error`提供了特有的报错栈堆处理接口,用于用户自定义报错栈堆的内容。~~
|
||||
*
|
||||
* ~~我们用到了`Error.prepareStackTrace(error, structuredStackTrace)`这个接口,这个接口的信息可参考[这里](https://v8.dev/docs/stack-trace-api#customizing-stack-traces)~~
|
||||
*
|
||||
* ~~该接口提供了结构化的栈堆信息,很幸运的是,这个结构化的栈堆能直接告诉我们报错的文件以及位置,故我们使用该接口,让异步报错能直接定位原始位置~~
|
||||
*
|
||||
* @implements {PromiseErrorHandler}
|
||||
*/
|
||||
export class ChromePromiseErrorHandler {
|
||||
|
||||
/**
|
||||
* 用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表
|
||||
* ~~用于临时记录报错信息的列表,通过`Error.prepareStackTrace`更新该列表~~
|
||||
*
|
||||
* @type {[Error, NodeJS.CallSite[]][]}
|
||||
* 现在用于存储报错过的错误信息
|
||||
*
|
||||
* @type {Error[]}
|
||||
*/
|
||||
#errorList;
|
||||
|
||||
/**
|
||||
* @type {typeof Error.prepareStackTrace}
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
#originErrorPrepareStackTrace;
|
||||
#_originErrorPrepareStackTrace;
|
||||
|
||||
/**
|
||||
* 初始化`Error.prepareStackTrace`,将该值赋值成我们需要的函数
|
||||
* 判断是否是v8错误栈堆用到的正则
|
||||
*/
|
||||
#STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
|
||||
|
||||
/**
|
||||
* ~~初始化`Error.prepareStackTrace`,将该值赋值成我们需要的函数~~
|
||||
*
|
||||
* 未防止本来Error.prepareStackTrace便存在赋值的行为,我们将原始值存储,并在需要的函数中调用
|
||||
* ~~未防止本来Error.prepareStackTrace便存在赋值的行为,我们将原始值存储,并在需要的函数中调用~~
|
||||
*
|
||||
* > 这或许就是本体扩展化的第一步(小声)
|
||||
* 初始化存储报错信息的列表
|
||||
*/
|
||||
onLoad() {
|
||||
/*
|
||||
this.#errorList = [];
|
||||
this.#originErrorPrepareStackTrace = Error.prepareStackTrace;
|
||||
Error.prepareStackTrace = (error, stackTraces) => {
|
||||
|
@ -42,13 +55,19 @@ export class ChromePromiseErrorHandler {
|
|||
this.#errorList.push([error, stackTraces]);
|
||||
return result;
|
||||
};
|
||||
*/
|
||||
this.#errorList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将原来可能的`Error.prepareStackTrace`赋值回去
|
||||
* ~~将原来可能的`Error.prepareStackTrace`赋值回去~~
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
onUnload() {
|
||||
/*
|
||||
Error.prepareStackTrace = this.#originErrorPrepareStackTrace;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,6 +79,65 @@ export class ChromePromiseErrorHandler {
|
|||
*/
|
||||
onHandle(event) {
|
||||
event.promise.catch((error) => {
|
||||
// 如果`error`是个错误,则继续处理
|
||||
if (error instanceof Error) {
|
||||
// 如果已经处理过该错误,则不再处理
|
||||
if (this.#errorList.includes(error)) return;
|
||||
this.#errorList.push(error);
|
||||
// 如果`error`拥有字符串形式的报错栈堆,且报错栈堆确实符合v8的stack
|
||||
if (typeof error.stack === 'string' && this.#STACK_REGEXP.test(error.stack)) {
|
||||
// 获取符合栈堆信息的字符串,一般来说就是从第二行开始的所有行
|
||||
// 为了处理eval的情况,故必须获取完行数
|
||||
let lines = error.stack.split('\n').filter((line) =>
|
||||
this.#STACK_REGEXP.test(line));
|
||||
|
||||
// 提供类型信息防止vscode报错
|
||||
/**
|
||||
* @type {string | undefined}
|
||||
*/
|
||||
let fileName = void 0;
|
||||
|
||||
/**
|
||||
* @type {number | undefined}
|
||||
*/
|
||||
let line = void 0;
|
||||
|
||||
/**
|
||||
* @type {number | undefined}
|
||||
*/
|
||||
let column = void 0;
|
||||
|
||||
// 从第一条开始遍历,一直遍历到不存在eval的位置
|
||||
for (let currentLine = 0; currentLine < lines.length; ++currentLine) {
|
||||
if (/\(eval /.test(lines[currentLine])) continue;
|
||||
|
||||
let formatedLine = lines[currentLine].replace(/^\s+/, '').replace(/\(eval code/g, '(').replace(/^.*?\s+/, '');
|
||||
|
||||
const location = formatedLine.match(/ (\(.+\)$)/);
|
||||
if (location) formatedLine = formatedLine.replace(location[0], '');
|
||||
|
||||
const locationParts = extractLocation(location ? location[1] : formatedLine);
|
||||
|
||||
fileName = ['eval', '<anonymous>'].includes(locationParts[0]) ? void 0 : locationParts[0];
|
||||
line = Number(locationParts[1]);
|
||||
column = Number(locationParts[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
window.onerror(error.message, fileName, line, column, error);
|
||||
}
|
||||
// 反之我们只能不考虑报错文件信息,直接调用onerror
|
||||
else {
|
||||
// @ts-ignore
|
||||
let [_, src = void 0, line = void 0, column = void 0] = /at\s+.*\s+\((.*):(\d*):(\d*)\)/i.exec(error.stack.split('\n')[1])
|
||||
if (typeof line == 'string') line = Number(line);
|
||||
if (typeof column == 'string') column = Number(column);
|
||||
// @ts-ignore
|
||||
window.onerror(error.message, src, line, column, error);
|
||||
}
|
||||
}
|
||||
/*
|
||||
console.error(error)
|
||||
const result = this.#errorList.find(savedError => savedError[0] === error);
|
||||
if (result) {
|
||||
|
@ -72,17 +150,42 @@ export class ChromePromiseErrorHandler {
|
|||
result[0]
|
||||
);
|
||||
}
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 正式报错时便不再需要报错信息了,故直接清空列表,释放内存
|
||||
* ~~正式报错时便不再需要报错信息了,故直接清空列表,释放内存~~
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
onErrorPrepare() {
|
||||
/*
|
||||
this.#errorList.length = 0;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简易的解析报错栈堆位置信息的函数
|
||||
*
|
||||
* @param {string} urlLike
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function extractLocation(urlLike) {
|
||||
// 不存在地址信息的字符串
|
||||
if (!/:/.test(urlLike)) {
|
||||
return [urlLike];
|
||||
}
|
||||
|
||||
// 捕获位置用到的正则
|
||||
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
|
||||
const parts = regExp.exec(urlLike.replace(/[()]/g, ''));
|
||||
|
||||
// @ts-ignore
|
||||
return [parts[1], parts[2] || void 0, parts[3] || void 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('../interface/promise-error-handler').PromiseErrorHandler} PromiseErrorHandler
|
||||
*/
|
||||
|
|