Merge pull request #815 from libccy/PR-Branch

v1.10.6.1
This commit is contained in:
Spmario233 2024-01-19 23:28:24 +08:00 committed by GitHub
commit 9698106cd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 17817 additions and 13569 deletions

View File

@ -184,8 +184,8 @@ game.import('card',function(lib,game,ui,get,ai,_status){
}
"step 3"
if((!result||!result.bool||!result.result||result.result!='shaned')&&!event.unhurt){
if (!event.directHit&&!event.directHit2&&lib.filter.cardEnabled(new lib.element.VCard('shan'), target, 'forceEnable')&&
target.hasCard(()=>true,'hs')&&get.damageEffect(target,player,target)<0) target.addGaintag(target.getCards('hs'),'sha_notshan');
if (!event.directHit&&!event.directHit2&&lib.filter.cardEnabled(new lib.element.VCard({name:'shan'}), target, 'forceEnable')&&
target.countCards('hs')>0&&get.damageEffect(target,player,target)<0) target.addGaintag(target.getCards('hs'),'sha_notshan');
target.damage(get.nature(event.card));
event.result={bool:true}
event.trigger('shaDamage');

View File

@ -411,7 +411,7 @@ game.import('card',function(lib,game,ui,get,ai,_status){
equipSkill:true,
forced:true,
trigger:{target:'gift'},
filter:(event,player)=>event.target!=player,
filter:(event,player)=>event.player!=player,
logTarget:'player',
content:()=>{
trigger.deniedGift.add(trigger.card);

View File

@ -155,8 +155,8 @@ game.import('character',function(lib,game,ui,get,ai,_status){
'虎:当你使用指定唯一目标的牌对目标角色造成伤害时,此伤害+1。',
'鹿①当你获得此效果时你回复1点体力并弃置判定区的所有牌。②你不能成为延时锦囊牌的目标。',
'熊:每回合限一次,当你受到伤害时,此伤害-1。',
'猿:出牌阶段开始时,你选择一名角色,随机获得其装备区里的一张牌。',
'鹤:出牌阶段开始时,你摸三张牌。',
'猿:当你获得此效果时,你选择一名其他角色,获得其装备区里的一张牌。',
'鹤:当你获得此效果时,你摸三张牌。',
],
updateMark:function(player){
var wuqinxi=player.storage.wuling_wuqinxi;
@ -177,11 +177,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
game.broadcastAll(function(player,curMark){
if(player.marks.wuling_wuqinxi) player.marks.wuling_wuqinxi.firstChild.innerHTML=curMark;
},player,curMark);
if(curMark=='鹿'){
player.logSkill('wuling_wuqinxi');
player.recover();
player.discard(player.getCards('j')).discarder=player;
}
var next=game.createEvent('wuling_change');
next.player=player;
next.setContent('emptyEvent');
},
ai:{
order:7,
@ -214,7 +212,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
trigger:{
source:'damageBegin1',
player:['phaseZhunbeiBegin','damageBegin4','phaseUseBegin'],
player:['phaseZhunbeiBegin','damageBegin4','wuling_change'],
},
filter:function(event,player,name){
const wuqinxi=player.storage.wuling_wuqinxi&&player.storage.wuling_wuqinxi[0];
@ -228,9 +226,17 @@ game.import('character',function(lib,game,ui,get,ai,_status){
case 'damageBegin4':
return wuqinxi=='熊'&&!player.hasSkill('wuling_xiong');
default:
if(wuqinxi=='鹤') return true;
if(wuqinxi!='猿') return false;
return game.hasPlayer(target=>target.countGainableCards(player,'e'));
switch(wuqinxi){
case '鹿':
return player.isDamaged()||player.countCards('j');
case '鹤':
return true;
case '猿':
return game.hasPlayer(target=>target!=playertarget.countGainableCards(player,'e'));
default:
return false;
}
break;
}
},
forced:true,
@ -256,31 +262,38 @@ game.import('character',function(lib,game,ui,get,ai,_status){
event.finish();
break;
default:
if(wuqinxi=='鹤'){
player.draw(3);
event.finish();
}
else{
player.chooseTarget('五禽戏:获得一名角色装备区里的一张装备牌',function(card,player,target){
return target.countGainableCards(player,'e');
}).set('ai',function(target){
var player=_status.event.player;
var att=get.attitude(player,target),eff=0;
target.getCards('e',function(card){
var val=get.value(card,target);
eff=Math.max(eff,-val*att);
switch(wuqinxi){
case '鹿':
player.recover();
player.discard(player.getCards('j')).discarder=player;
event.finish();
break;
case '鹤':
player.draw(3);
event.finish();
break;
case '猿':
player.chooseTarget('五禽戏:获得一名其他角色装备区里的一张装备牌',function(card,player,target){
return target!=player&&target.countGainableCards(player,'e');
}).set('ai',function(target){
var player=_status.event.player;
var att=get.attitude(player,target),eff=0;
target.getCards('e',function(card){
var val=get.value(card,target);
eff=Math.max(eff,-val*att);
});
return eff;
});
return eff;
});
break;
}
break;
}
}
'step 1'
if(result.bool){
var target=result.targets[0];
player.line(target,'green');
var cards=target.getGainableCards(player,'e');
player.gain(cards.randomGets(1),target,'give');
player.gainPlayerCard(target,'e',true);
}
},
ai:{
@ -7014,11 +7027,11 @@ game.import('character',function(lib,game,ui,get,ai,_status){
source:'damageSource'
},
filter:function(event,player){
if(get.attitude(_status.event.player,event.player)>=0) return false;
if(player.storage.drlt_duorui.length) return false;
return event.player.isIn()&&_status.currentPhase==player;
},
check:function(event,player){
if(get.attitude(_status.event.player,event.player)>=0) return false;
if(player.hasEnabledSlot()&&!player.hasEnabledSlot(5)) return false;
return true;
},
@ -8049,8 +8062,8 @@ game.import('character',function(lib,game,ui,get,ai,_status){
'<br><li>虎:当你使用指定唯一目标的牌对目标角色造成伤害时,此伤害+1。'+
'<br><li>鹿①当你获得此效果时你回复1点体力并弃置判定区的所有牌。②你不能成为延时锦囊牌的目标。'+
'<br><li>熊:每回合限一次,当你受到伤害时,此伤害-1。'+
'<br><li>猿:出牌阶段开始时,你选择一名角色,随机获得其装备区里的一张牌。'+
'<br><li>鹤:出牌阶段开始时,你摸三张牌。',
'<br><li>猿:当你获得此效果时,你选择一名其他角色,获得其装备区里的一张牌。'+
'<br><li>鹤:当你获得此效果时,你摸三张牌。',
youyi:'游医',
youyi_info:'①弃牌阶段结束时你可以将所有于此阶段弃置的牌置入仁区。②出牌阶段限一次。你可以将仁区的所有牌置入弃牌堆令所有角色各回复1点体力。',

View File

@ -107,7 +107,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
sp_jishi:['dc_jiben','zhenghun','dc_sunhanhua','liuchongluojun'],
sp_raoting:['dc_huanghao','dc_sunziliufang','dc_sunchen','dc_jiachong'],
sp_yijun:['gongsundu','mengyou'],
sp_zhengyin:['yue_caiwenji','yue_zhoufei','yue_caiyong'],
sp_zhengyin:['yue_caiwenji','yue_zhoufei','yue_caiyong','yue_xiaoqiao'],
}
},
skill:{
@ -658,7 +658,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
trigger:{global:'phaseEnd'},
forced:true,
filter:function(event,player){
return player.getHistory('useCard').length>player.getHp();
return player.getHistory('useCard').length>player.getHp()||player.getHistory('gain').reduce((sum,evt)=>sum+evt.cards.length,0)>player.getHp();
},
content:function*(event,map){
const player=map.player;
@ -1094,9 +1094,8 @@ game.import('character',function(lib,game,ui,get,ai,_status){
trigger:{player:['gainAfter','loseAsyncAfter']},
forced:true,
filter:(event,player)=>{
// if(player==_status.currentPhase) return false;
if(event.getParent('phaseDraw',true)) return false;
const evt=player.getHistory('gain')[0];
const evt=player.getHistory('gain',i=>!i.getParent('phaseDraw',true))[0];
if(!evt) return false;
if(event.name=='gain'){
if(evt!=event||event.getlx===false) return false;
@ -1109,8 +1108,8 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
content:function(){
var hs=player.getCards('h'),cards=trigger.getg(player);
var card=cards.filter(card=>hs.includes(card)).randomGet();
player.addGaintag(card,'dclingkong_tag');
cards=cards.filter(card=>hs.includes(card));
player.addGaintag(cards,'dclingkong_tag');
game.delayx();
},
},
@ -2254,7 +2253,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
logTarget:'player',
content:function(){
var num=trigger.player.countMark('dcshengdu');
player.draw(num);
player.draw(num*trigger.cards.length);
trigger.player.removeMark('dcshengdu',num);
},
}
@ -11093,6 +11092,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
zhangchu:['zhangchu','jsrg_zhangchu'],
xianglang:['xianglang','mb_xianglang'],
chengui:['chengui','mb_chengui'],
liuyong:['liuyong','jsrg_liuyong'],
zhangxuan:['zhangxuan','jsrg_zhangxuan'],
gaoxiang:['gaoxiang','jsrg_gaoxiang'],
},
translate:{
re_panfeng:'潘凤',
@ -11468,7 +11470,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
dcguangshi_info:'锁定技。准备阶段若所有其他角色均有“信众”你摸两张牌并失去1点体力。',
dongwan:'董绾',
dcshengdu:'生妒',
dcshengdu_info:'回合开始时你可以选择一名其他角色令其获得1枚“生妒”标记。有“生妒”标记的角色于摸牌阶段得到牌后你摸X张牌然后其移去所有“生妒”标记X为其拥有的“生妒”标记数)。',
dcshengdu_info:'回合开始时你可以选择一名其他角色令其获得1枚“生妒”标记。有“生妒”标记的角色于摸牌阶段得到牌后你摸X张牌然后其移去所有“生妒”标记X为摸牌数乘以其拥有的“生妒”标记数)。',
dcjieling:'介绫',
dcjieling_info:'出牌阶段每种花色限一次你可以将两张花色不同的手牌当无距离限制且无任何次数限制的【杀】使用。然后若此【杀】造成了伤害所有目标角色失去1点体力未造成伤害所有目标角色依次获得1枚“生妒”标记。',
yuanyin:'袁胤',
@ -11514,7 +11516,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
yue_zhoufei_prefix:'乐',
dclingkong:'灵箜',
dclingkong_tag:'箜篌',
dclingkong_info:'锁定技。①游戏开始时,你将所有手牌标记为“箜篌”。②你的“箜篌”牌不计入手牌上限。③当你于一回合内首次于摸牌阶段外得到牌后,系统随机将其中的一张牌标记为“箜篌”。',
dclingkong_info:'锁定技。①游戏开始时,你将所有手牌标记为“箜篌”。②你的“箜篌”牌不计入手牌上限。③当你于一回合内首次于摸牌阶段外得到牌后,你将这些牌标记为“箜篌”。',
dcxianshu:'贤淑',
dcxianshu_info:'出牌阶段你可以将一张“箜篌”正面向上交给一名其他角色然后你摸X张牌X为你与其的体力值之差且至多为5。若此牌为红色且该角色的体力值不大于你则其回复1点体力若此牌为黑色且该角色的体力值不小于你则其失去1点体力。',
dc_zhangmancheng:'张曼成',
@ -11531,7 +11533,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
dccaisi:'才思',
dccaisi_info:'当你于回合内/回合外使用基本牌结算结束后,若你本回合以此法得到的牌数小于你的体力上限,你可以从牌堆/弃牌堆随机获得一张非基本牌,然后本回合下次发动此技能获得的牌数+1。',
dczhuoli:'擢吏',
dczhuoli_info:'锁定技。一名角色的回合结束时,若你本回合使用的牌数大于体力值你加1点体力上限不能超过存活角色数回复1点体力。',
dczhuoli_info:'锁定技。一名角色的回合结束时,若你本回合使用或获得的牌数大于体力值你加1点体力上限不能超过存活角色数回复1点体力。',
yue_caiyong:'乐蔡邕',
yue_caiyong_prefix:'乐',
dcjiaowei:'焦尾',
@ -11552,9 +11554,10 @@ game.import('character',function(lib,game,ui,get,ai,_status){
yue_xiaoqiao:'乐小乔',
yue_xiaoqiao_prefix:'乐',
dcqiqin:'绮琴',
dcqiqin_info:'锁定技。①游戏开始时,你将所有手牌标记为“乐”。②你的“乐”牌不计入手牌上限。③准备阶段,你获得弃牌堆中所有你标记过的“乐”牌。',
dcqiqin_tag:'琴',
dcqiqin_info:'锁定技。①游戏开始时,你将所有手牌标记为“琴”。②你的“琴”牌不计入手牌上限。③准备阶段,你获得弃牌堆中所有你标记过的“琴”牌。',
dcweiwan:'媦婉',
dcweiwan_info:'出牌阶段限一次,你可以弃置一张“乐”并获得一名其他角色区域内花色与此牌不相同的牌各一张若你获得了一张牌其失去1点体力两张牌本回合你对其使用牌无距离和次数限制三张牌本回合你不能对其使用牌。',
dcweiwan_info:'出牌阶段限一次,你可以弃置一张“琴”并随机获得一名其他角色区域内花色与此牌不相同的牌各一张若你获得了一张牌其失去1点体力两张牌本回合你对其使用牌无距离和次数限制三张牌本回合你不能对其使用牌。',
sp_baigei:'无双上将',
sp_caizijiaren:'才子佳人',

File diff suppressed because it is too large Load Diff

View File

@ -15391,7 +15391,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
zhanggong:['zhanggong','re_zhanggong'],
baosanniang:['xin_baosanniang','re_baosanniang','baosanniang'],
heqi:['re_heqi','heqi'],
weiwenzhugezhi:['weiwenzhugezhi','re_weiwenzhugezhi'],
weiwenzhugezhi:['weiwenzhugezhi','re_weiwenzhugezhi','jsrg_weiwenzhugezhi'],
xugong:['xugong','re_xugong','jsrg_xugong'],
liuzan:['re_liuzan','liuzan'],
sufei:['yj_sufei','sp_sufei','xf_sufei'],

View File

@ -886,25 +886,39 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
pshengwu:{
audio:'hengwu',
mod:{
aiOrder:(player,card,num)=>{
if(num>0&&get.tag(card,'draw')&&ui.cardPile.childNodes.length+ui.discardPile.childNodes.length<20) return 0;
},
aiValue:(player,card,num)=>{
if(num>0&&card.name==='zhuge') return 20;
},
aiUseful:(player,card,num)=>{
if(num>0&&card.name==='zhuge') return 10;
}
},
trigger:{player:['useCard','respond']},
direct:true,
locked:false,
filter:function(event,player){
return game.hasPlayer(i=>i.countCards('ej',cardx=>get.type(cardx)=='equip'&&get.suit(event.card)==get.suit(cardx)));
},
content:function(){
'step 0'
var suit=get.suit(trigger.card);
var prompt2='弃置任意张'+get.translation(suit)+'手牌然后摸X张牌X为你弃置的牌数+'+game.filterPlayer().map(i=>i.countCards('ej',cardx=>get.type(cardx)=='equip'&&get.suit(trigger.card)==get.suit(cardx))).reduce((p,c)=>p+c)+'';
var suit=get.suit(trigger.card),extra=game.filterPlayer().map(i=>i.countCards('ej',cardx=>{
return get.type(cardx)=='equip'&&get.suit(trigger.card)==get.suit(cardx);
})).reduce((p,c)=>p+c);
var prompt2='弃置任意张'+get.translation(suit)+'手牌然后摸X张牌X为你弃置的牌数+'+extra+'';
player.chooseToDiscard('h',[1,player.countCards('h',{suit:suit})],{suit:suit}).set('prompt',get.prompt('pshengwu')).set('prompt2',prompt2).set('ai',card=>{
var player=_status.event.player;
if(_status.event.tie) return 0;
let player=_status.event.player;
if(_status.event.goon) return 12-get.value(card);
if(player.countCards('h')>50) return 0;
if(player==_status.currentPhase){
if(['shan','caochuan','tao','wuxie'].includes(card.name)) return 8-get.value(card);
return 6-get.value(card);
}
return 5.5-get.value(card);
}).set('goon',player.countCards('h',{suit:suit})==1).set('logSkill','pshengwu');
}).set('goon',player.countCards('h',{suit:suit})==1).set('tie',extra>ui.cardPile.childNodes.length+ui.discardPile.childNodes.length).set('logSkill','pshengwu');
'step 1'
if(result.bool){
var num=result.cards.length;
@ -2649,7 +2663,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
ai:{
order:function(item,player){
var num=player.getStorage('zyquanji').length;
var num=player.getExpansions('zyquanji').length;
if(num==1) return 8;
return 1;
},

View File

@ -987,6 +987,7 @@ window.noname_character_rank={
'libai',
'tw_gongsunfan',
'yue_caiwenji',
'yue_xiaoqiao',
'vtb_xiaole',
'vtb_xiaojiu',
'ol_zhangzhang',
@ -1817,6 +1818,7 @@ window.noname_character_rank={
'caoxian',
],
epic:[
'yue_xiaoqiao',
'mb_chengui',
'ol_pengyang',
'ol_luyusheng',

View File

@ -6656,7 +6656,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
ai:{
order:function(item,player){
var num=player.getStorage('gzquanji').length;
var num=player.getExpansions('gzquanji').length;
if(num==1) return 8;
return 1;
},

View File

@ -709,7 +709,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
audio:'sbkanpo',
trigger:{global:'useCard'},
filter:function(event,player){
return event.player!=player&&player.getStorage('sbkanpo').includes(event.card.name);
return event.player!=player&&player.storage.sbkanpo[1].includes(event.card.name);
},
prompt2:function(event,player){
return '移除'+get.translation(event.card.name)+'的记录,令'+get.translation(event.card)+'无效';

View File

@ -3667,7 +3667,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
target:function(card,player,target,current){
if(typeof card==='object'&&get.name(card)==='sha'&&target.mayHaveShan(player,'use')) return [0.6,0.75];
if(!target.hasFriend()&&!player.hasUnknown()) return;
if(_status.currentPhase==target) return;
if(_status.currentPhase==target||get.type(card)==='delay') return;
if(card.name!='shuiyanqijunx'&&get.tag(card,'loseCard')&&target.countCards('he')){
if(target.hasSkill('ziliang')) return 0.7;
return [0.5,Math.max(2,target.countCards('h'))];
@ -7714,7 +7714,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
dongzhuo:['dongzhuo','ol_dongzhuo','re_dongzhuo','sp_dongzhuo','yj_dongzhuo'],
dengai:['dengai','ol_dengai','re_dengai'],
sp_ol_zhanghe:['sp_ol_zhanghe','yj_zhanghe','sp_zhanghe','jsrg_zhanghe'],
jiangwei:['jiangwei','ol_jiangwei','re_jiangwei','sb_jiangwei'],
jiangwei:['jiangwei','ol_jiangwei','re_jiangwei','sb_jiangwei','jsrg_jiangwei'],
liushan:['liushan','ol_liushan','re_liushan'],
sunce:['sunce','re_sunce','re_sunben','sb_sunce'],
zhangzhang:['zhangzhang','ol_zhangzhang','re_zhangzhang'],

View File

@ -727,7 +727,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
var {result:{bool,targets}}=await player.chooseTarget(get.prompt2('olgongjie'),[1,num],lib.filter.notMe).set('ai',target=>get.attitude(_status.event.player,target));
if(!bool) return;
targets=targets.sortBySeat();
player.logSkill('gongjie',targets);
player.logSkill('olgongjie',targets);
for(var target of targets){
var {result:{bool,cards}}=await target.gainPlayerCard(player,true,'he');
if(bool) draws.add(get.suit(cards[0],player));
@ -788,6 +788,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
return player.countCards('he');
},
direct:true,
limited:true,
skillAnimation:true,
animationColor:'water',
async content(event,trigger,player){
var target=_status.currentPhase,num=player.countCards('he');
var {result:{bool,cards}}=await player.chooseToGive(get.prompt2('olxiangzuo',target),[1,num],'he').set('ai',card=>{
@ -951,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);
@ -13357,7 +13360,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
audio:2,
direct:true,
filter:function(event,player){
return player!=event.player&&!event.player.isDisabledJudge()&&event.player.countCards('he')&&!event.player.countCards('j');
return player!=event.player&&!event.player.isDisabledJudge()&&event.player.countCards('he')&&!event.player.countCards('j',{type:'delay'});
},
content:function(){
'step 0'
@ -26329,7 +26332,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
retuogu:'托孤',
retuogu_info:'一名角色死亡时,你可以令其选择其武将牌上的一个技能(主公技,限定技,觉醒技,隐匿技、使命技等特殊技能除外),然后你获得其选择的技能并失去上次因〖托孤〗获得的技能。',
shanzhuan:'擅专',
shanzhuan_info:'当你对其他角色造成伤害后,若其判定区没有牌,则你可以将其的一张牌置于其的判定区。若此牌不为延时锦囊牌且此牌为:红色,此牌视为【乐不思蜀】;黑色,此牌视为【兵粮寸断】。回合结束时,若你本回合内未造成伤害,你可摸一张牌。',
shanzhuan_info:'当你对其他角色造成伤害后,若其判定区没有延时类锦囊牌,则你可以将其的一张牌置于其的判定区。若此牌不为延时锦囊牌且此牌为:红色,此牌视为【乐不思蜀】;黑色,此牌视为【兵粮寸断】。回合结束时,若你本回合内未造成伤害,你可摸一张牌。',
spniluan:'逆乱',
spniluan_info:'出牌阶段,你可以将一张黑色牌当做【杀】使用。此【杀】使用结算完成后,若你未因此【杀】造成过伤害,则你令此【杀】不计入使用次数。',
spweiwu:'违忤',

View File

@ -451,6 +451,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
trigger:{player:'phaseEnd',global:'die'},
filter:function(event,player){
if(event.name=='phase') return player.hasMark('starpizhi');
if(!player.getStorage('starcanxi_wangsheng').includes(event.player.group)&&!player.getStorage('starcanxi_xiangsi').includes(event.player.group)) return false;
var groups=player.getSkills().filter(skill=>skill.indexOf('starcanxi_')==0);
groups=groups.map(group=>group.slice(10));
return groups.includes(event.player.group);
@ -11181,7 +11182,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
starcanxi_cancel:'向死',
starcanxi_info:'锁定技。游戏开始时,你获得场上所有角色的势力对应的“玺角”标记,然后选择一个“玺角”对应势力并选择以下一项;一轮开始时,你选择一个“玺角”对应势力并选择以下一项:①妄生:本轮被选择势力角色每回合首次造成的伤害+1且计算与其他角色间的距离-1②向死本轮其他被选择势力角色每回合首次回复体力后失去1点体力且每回合对你使用的第一张牌无效。',
starpizhi:'圮秩',
starpizhi_info:'锁定技。①一名角色死亡后,若你拥有该角色对应的“玺角”标记你失去之并摸X张牌。②结束阶段你摸X张牌。X为你本局游戏失去的“玺角”标记数',
starpizhi_info:'锁定技。①一名角色死亡后,若你拥有该角色对应的“玺角”标记且你本轮发动〖向死〗的势力与其相同你失去之并摸X张牌。②结束阶段你摸X张牌。X为你本局游戏失去的“玺角”标记数',
starzhonggu:'冢骨',
starzhonggu_info:'主公技,锁定技。摸牌阶段,若游戏轮数大于等于场上的群势力角色数,则你额外摸两张牌,否则你少摸一张牌。',
star_dongzhuo:'星董卓',

View File

@ -1,4 +1,4 @@
'use strict';
import { game } from '../noname.js';
game.import('character',function(lib,game,ui,get,ai,_status){
return {
name:'standard',
@ -93,6 +93,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
ganning:['lingtong','xf_sufei'],
guanyu:['zhangfei','liaohua'],
},
/**
* @type { { [key: string]: Skill } }
*/
skill:{
//标准版甘夫人
stdshushen:{
@ -304,7 +307,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
logTarget:'source',
preHidden:true,
filter(event,player){
return (event.source&&event.source.countGainableCards(player,event.source!=player?'he':'e')&&event.num>0);
return event.source&&event.source.countGainableCards(player,event.source!=player?'he':'e')>0&&event.num>0;
},
async content(event,trigger,player){
player.gainPlayerCard(true,trigger.source,trigger.source!=player?'he':'e');
@ -1761,9 +1764,9 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
async content(event,trigger,player){
event.count=trigger.getl(player).es.length;
do {
while(event.count-->0){
player.draw(2);
if(!player.hasSkill(event.name)) break;
if(!event.count||!player.hasSkill(event.name)) break;
if(!get.is.blocked(event.name,player)){
const chooseBoolEvent=player.chooseBool(get.prompt2('xiaoji')).set('frequentSkill','xiaoji');
chooseBoolEvent.ai=lib.filter.all;
@ -1771,7 +1774,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
if(bool) player.logSkill('xiaoji');
else break;
}
}while(event.count-->0);
}
},
ai:{
noe:true,
@ -2325,7 +2328,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
characterReplace:{
caocao:['caocao','re_caocao','sb_caocao','dc_caocao'],
guojia:['guojia','re_guojia','ps1059_guojia','ps2070_guojia'],
simayi:['simayi','re_simayi','ps_simayi','ps2068_simayi'],
simayi:['simayi','re_simayi','jsrg_simayi','ps_simayi','ps2068_simayi'],
jin_simayi:['jin_simayi','junk_simayi','ps_jin_simayi'],
zhenji:['zhenji','re_zhenji','sb_zhenji','yj_zhenji'],
xuzhu:['xuzhu','re_xuzhu'],
@ -2335,15 +2338,15 @@ game.import('character',function(lib,game,ui,get,ai,_status){
liubei:['liubei','re_liubei','sb_liubei','dc_liubei','junk_liubei'],
guanyu:['guanyu','re_guanyu','ps_guanyu','old_guanyu'],
zhangfei:['zhangfei','re_zhangfei','old_zhangfei','xin_zhangfei','sb_zhangfei','tw_zhangfei','jsrg_zhangfei','yj_zhangfei'],
zhaoyun:['zhaoyun','re_zhaoyun','old_zhaoyun','sb_zhaoyun','ps2063_zhaoyun','ps2067_zhaoyun'],
zhaoyun:['zhaoyun','re_zhaoyun','old_zhaoyun','sb_zhaoyun','jsrg_zhaoyun','ps2063_zhaoyun','ps2067_zhaoyun'],
sp_zhaoyun:['sp_zhaoyun','jsp_zhaoyun'],
machao:['machao','re_machao','sb_machao','ps_machao'],
sp_machao:['sp_machao','dc_sp_machao','jsrg_machao','old_machao'],
zhugeliang:['zhugeliang','re_zhugeliang','ps2066_zhugeliang','ps_zhugeliang','sb_zhugeliang'],
zhugeliang:['zhugeliang','re_zhugeliang','sb_zhugeliang','jsrg_zhugeliang','ps2066_zhugeliang','ps_zhugeliang'],
huangyueying:['huangyueying','re_huangyueying','junk_huangyueying','sb_huangyueying'],
sunquan:['sunquan','re_sunquan','sb_sunquan','dc_sunquan'],
zhouyu:['zhouyu','re_zhouyu','sb_zhouyu','ps1062_zhouyu','ps2080_zhouyu'],
luxun:['luxun','re_luxun'],
luxun:['luxun','re_luxun','jsrg_luxun'],
lvmeng:['lvmeng','re_lvmeng','sb_lvmeng'],
huanggai:['huanggai','re_huanggai','sb_huanggai'],
daqiao:['daqiao','re_daqiao','sb_daqiao'],

View File

@ -77,7 +77,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
tw_madai:['male','shu',4,['mashu','twqianxi']],
tw_niujin:['male','wei',4,['twcuorui','twliewei']],
tw_guanqiujian:['male','wei',3,['twzhengrong','twhongju']],
tw_daxiaoqiao:['female','wu',3,['twxingwu','twpingting']],
tw_daxiaoqiao:['female','wu',3,['twxingwu','twpingting'],['tempname:daxiaoqiao']],
tw_furong:['male','shu',4,['twxuewei','twliechi']],
tw_yl_luzhi:['male','qun',3,['twmingren','twzhenliang']],
tw_liuzhang:['male','qun',3,['jutu','twyaohu','rehuaibi']],
@ -364,12 +364,12 @@ game.import('character',function(lib,game,ui,get,ai,_status){
event.target=target;
var list=['cancel2'];
var choiceList=[
'弃置一张手牌,令此【杀】可以额外指定一个目标',
'令此【杀】可以额外指定一个目标',
'弃置其一张手牌,若此【杀】造成伤害,则你摸一张牌且本阶段可以额外使用一张【杀】',
];
if(target.countCards('h')) list.unshift('其弃置');
else choiceList[1]='<span style="opacity:0.5">'+choiceList[1]+'</span>';
if(game.hasPlayer(targetx=>!trigger.targets.includes(targetx)&&player.canUse(trigger.card,targetx))) list.unshift('你弃置');
if(game.hasPlayer(targetx=>!trigger.targets.includes(targetx)&&player.canUse(trigger.card,targetx))) list.unshift('多指');
else choiceList[0]='<span style="opacity:0.5">'+choiceList[0]+'</span>';
player.chooseControl(list).set('choiceList',choiceList).set('ai',()=>{
var controls=_status.event.controls;
@ -377,7 +377,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
var player=trigger.player;
var target=trigger.target;
if(controls.includes('其弃置')&&_status.event.goon) return '其弃置';
if(controls.includes('你弃置')){
if(controls.includes('多指')){
if(game.hasPlayer(targetx=>!trigger.targets.includes(targetx)&&player.canUse(trigger.card,targetx)&&get.effect(targetx,trigger.card,player,player)>0)) return '你弃置';
}
return 'cancel2';
@ -6240,7 +6240,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
audio:2,
trigger:{global:['roundStart','dying']},
init:function(player,skill){
if(player.getExpansions('twxingwu').length) player.addAdditionalSkill(skill,['tianxiang_daxiaoqiao','liuli_daxiaoqiao']);
if(player.getExpansions('twxingwu').length) player.addAdditionalSkill(skill,['tianxiang','liuli']);
else player.removeAdditionalSkill(skill);
},
filter:function(event,player){
@ -6269,18 +6269,13 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
subSkill:{
update:{
trigger:{
player:['loseAfter','loseAsyncAfter','addToExpansionAfter'],
trigger:{player:['loseAfter','loseAsyncAfter','addToExpansionAfter']},
filter:function(event,player){
var cards=player.getExpansions('twxingwu'),skills=player.additionalSkills.twpingting;
return !((cards.length&&skills&&skills.length)||(!cards.length&&(!skills||!skills.length)));
},
forced:true,
silent:true,
filter:function(event,player){
var cards=player.getExpansions('twxingwu'),skills=player.additionalSkills.twpingting;
if((cards.length&&skills&&skills.length)||(!cards.length&&(!skills||!skills.length))){
return false;
}
return true;
},
content:function(){
lib.skill.twpingting.init(player,'twpingting');
}

View File

@ -20,7 +20,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
dc_sp_machao:['male','qun',4,['zhuiji','dc_olshichou']],
old_huangfusong:['male','qun',4,['xinfenyue']],
dc_xiahouba:['male','shu',4,['rebaobian']],
dc_daxiaoqiao:['female','wu',3,['dcxingwu','dcluoyan']],
dc_daxiaoqiao:['female','wu',3,['dcxingwu','dcluoyan'],['tempname:daxiaoqiao']],
tianshangyi:['female','wei',3,['dcposuo','dcxiaoren']],
sunlingluan:['female','wu',3,['dclingyue','dcpandi']],
dc_wangjun:['male','qun',4,['dctongye','dcchangqu']],
@ -145,10 +145,6 @@ game.import('character',function(lib,game,ui,get,ai,_status){
return (dis?6:1)-get.useful(card);
}
if(_status.event.hvt.includes(card)){
if(_status.event.suits.length>=4){
if(cards.length>8) return 0;
return 4.5-get.value(card);
}
if(!_status.event.suits.includes(suit)) return 6-get.value(card);
if(card.name==='sha') return 3-get.value(card);
return 1-get.value(card);
@ -1306,6 +1302,17 @@ game.import('character',function(lib,game,ui,get,ai,_status){
},
dczhangcai:{
audio:2,
mod:{
aiOrder:(player,card,num)=>{
if(num>0&&get.tag(card,'draw')&&ui.cardPile.childNodes.length+ui.discardPile.childNodes.length<20) return 0;
},
aiValue:(player,card,num)=>{
if(num>0&&card.name==='zhuge') return 20;
},
aiUseful:(player,card,num)=>{
if(num>0&&card.name==='zhuge') return 10;
}
},
trigger:{
player:['useCard','respond'],
},
@ -1319,7 +1326,14 @@ game.import('character',function(lib,game,ui,get,ai,_status){
if(typeof num=='number') count=Math.max(1,player.countCards('h',card=>get.number(card)==num))
return '你可以摸'+get.cnNumber(count)+'张牌。';
},
check:(event,player)=>{
const num=player.hasSkill('dczhangcai_all')?get.number(event.card):8;
let count=1;
if(typeof num=='number') count=Math.max(1,player.countCards('h',card=>get.number(card)==num));
return ui.cardPile.childNodes.length+ui.discardPile.childNodes.length>=count;
},
frequent:true,
locked:false,
content:function(){
var num=player.hasSkill('dczhangcai_all')?get.number(trigger.card):8;
var count=1;
@ -1649,33 +1663,29 @@ game.import('character',function(lib,game,ui,get,ai,_status){
}
},
},
dcluoyan: {
derivation: ['retianxiang', 'liuli'],
init: function (player) {
if (player.getStorage('dcxingwu').length) player.addAdditionalSkill('dcluoyan', ['retianxiang_daxiaoqiao', 'liuli_daxiaoqiao']);
dcluoyan:{
derivation:['retianxiang','liuli'],
init:function(player){
if(player.getExpansions('dcxingwu').length) player.addAdditionalSkill('dcluoyan',['retianxiang','liuli']);
else player.removeAdditionalSkill('dcluoyan');
},
onremove: function (player) {
onremove:function(player){
player.removeAdditionalSkill('dcluoyan');
},
trigger: {
player: ['loseAfter', 'loseAsyncAfter', 'addToExpansionAfter'],
trigger:{player:['loseAfter','loseAsyncAfter','addToExpansionAfter']},
filter:function(event,player){
var cards=player.getExpansions('dcxingwu'),skills=player.additionalSkills.dcluoyan;
return !((cards.length&&skills&&skills.length)||(!cards.length&&(!skills||!skills.length)));
},
filter: function (event, player) {
var cards = player.getExpansions('dcxingwu'), skills = player.additionalSkills.dcluoyan;
if ((cards.length && skills && skills.length) || (!cards.length && (!skills || !skills.length))) {
return false;
}
return true;
},
forced: true,
content: function () {
lib.skill.twpingting.init(player, 'dcluoyan');
forced:true,
silent:true,
content:function(){
lib.skill.dcluoyan.init(player,'dcluoyan');
},
},
retianxiang_daxiaoqiao: {
audio: 'tianxiang_daxiaoqiao',
inherit: 'retianxiang',
retianxiang_daxiaoqiao:{
audio:'tianxiang_daxiaoqiao',
inherit:'retianxiang',
},
//田尚衣
dcposuo:{
@ -13514,7 +13524,7 @@ game.import('character',function(lib,game,ui,get,ai,_status){
fengfangnv:['fengfangnv','re_fengfangnv'],
luotong:['dc_luotong','luotong'],
dc_wangchang:['dc_wangchang','tw_wangchang'],
guozhao:['guozhao','xin_guozhao'],
guozhao:['guozhao','xin_guozhao','jsrg_guozhao'],
dingshangwan:['dingshangwan','ol_dingshangwan'],
},
translate:{

View File

@ -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',

View File

@ -91,7 +91,7 @@ new Promise(resolve => {
script.async = true
script.onerror = (event) => {
console.error(event)
const message = `您使用的浏览器或无名杀客户端加载内容失败!\n若您使用的客户端为自带内核的旧版“兼容版”,请及时更新客户端版本!\n若您使用的客户端为手机端的非兼容版无名杀请尝试更新手机端WebView内核或者更换为1.8.2版本以上的兼容版`;
const message = `您使用的浏览器或无名杀客户端加载内容失败!\n若您使用的客户端为自带内核的旧版“兼容版”,请及时更新客户端版本!\n若您使用的客户端为手机端的非兼容版《无名杀》请尝试更新手机的WebView内核或者更换为1.8.2版本及以上的兼容版!\n若您是直接使用浏览器加载index.html进行游戏请改为运行文件夹内的“noname-server.exe”或使用VSCode等工具启动Live Server以动态服务器的方式启动《无名杀》`;
console.error(message);
alert(message);
exit()

View File

@ -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',
]
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -7,7 +7,7 @@
<title>无名杀</title>
<script>
window.onerror = function (msg, src, line, column, err) {
var str = `错误文件: ${typeof src == 'string' && src.length > 0 ? decodeURI(src) : '未知文件'}`;
let str = `错误文件: ${typeof src == 'string' && src.length > 0 ? decodeURI(src) : '未知文件'}`;
str += `\n错误信息: ${msg}`;
str += `\n行号: ${line}`;
str += `\n列号: ${column}`;
@ -16,6 +16,123 @@
alert(str);
};
</script>
<script>
if (location.href.startsWith('http') && typeof window.initReadWriteFunction != 'function' && !window.require && !window.__dirname) {
window.initReadWriteFunction = function(game) {
/*game.download = function() {
// 暂不实现
};*/
game.createDir = function (dir, success = () => {}, error = () => {}) {
fetch(`./createDir?dir=${dir}`)
.then(response => {
return response.json();
})
.then(result => {
if (result && result.success) success();
else error();
})
.catch(error);
};
game.readFile = function (fileName, callback = () => {}, error = () => {}) {
fetch(`./readFile?fileName=${fileName}`)
.then(response => {
return response.json();
})
.then(result => {
if (result && result.success) callback(new Uint8Array(result.data).buffer);
else error(result && result.errorMsg);
})
.catch(error);
};
game.readFileAsText = function (fileName, callback = () => {}, error = () => {}) {
fetch(`./readFileAsText?fileName=${fileName}`)
.then(response => {
return response.json();
})
.then(result => {
if (result && result.success) callback(result.data);
else error(result && result.errorMsg);
})
.catch(error);
};
game.writeFile = function (data, path, name, callback = () => { }) {
game.ensureDirectory(path, function () {
if (Object.prototype.toString.call(data) == '[object File]') {
const fileReader = new FileReader();
fileReader.onload = function (e) {
game.writeFile(e.target.result, path, name, callback);
};
fileReader.readAsArrayBuffer(data, "UTF-8");
}
else {
let filePath = path;
if (path.endsWith('/')) {
filePath += name;
} else if (path == "") {
filePath += name;
} else {
filePath += '/' + name;
}
fetch(`./writeFile`, {
method: 'post',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: typeof data == 'string' ? data : Array.prototype.slice.call(new Uint8Array(data)),
path: filePath
})
})
.then(response => {
return response.json();
})
.then(result => {
if (result && result.success) {
callback();
} else {
callback(result.errorMsg);
}
});
}
});
};
game.removeFile = function (fileName, callback = () => {}) {
fetch(`./removeFile?fileName=${fileName}`)
.then(response => {
return response.json();
})
.then(result => {
callback(result.errorMsg);
})
.catch(error);
};
game.getFileList = function (dir, callback = () => {}) {
fetch(`./getFileList?dir=${dir}`)
.then(response => {
return response.json();
})
.then(result => {
if (result && result.success) {
callback(result.data.dirs, result.data.files);
}
});
};
game.ensureDirectory = function (list, callback = () => {}, file = false) {
let pathArray = typeof list == "string" ? list.split("/") : list;
if (file) {
pathArray = pathArray.slice(0, -1);
}
game.createDir(pathArray.join("/"), callback, console.error);
};
};
}
</script>
<script src="game/update.js"></script>
<script src="game/config.js"></script>
<script src="game/package.js"></script>

View File

@ -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;
}
}

23
node_modules/noname-typings/Card.d.ts generated vendored Normal file
View File

@ -0,0 +1,23 @@
declare type CardBaseUIData = {
name?: string;
suit?: string;
number?: number;
nature?: string;
//用于某些方法,用于过滤卡牌的额外结构
type?: string;
subtype?: string;
color?: string;
/**
*
*
* true,false/undefined
* useCard使用时next.cards,card.cards;
*
*/
isCard?: boolean;
/** 真实使用的卡牌 */
cards?: Card[];
}

43
node_modules/noname-typings/Result.d.ts generated vendored Normal file
View File

@ -0,0 +1,43 @@
declare interface Result {
/**
*
*
* ;
*
*
* game.check
*
* ok方法会有直接的boolgame.check;
*/
bool?: boolean;
//choose系
/** 记录返回当前事件操作过程中的卡牌 */
cards: Card[];
/** 记录返回当前事件操作过程中的目标 */
targets: Player[];
/** 记录返回当前事件操作过程中的按钮 */
buttons: Button[];
/** 记录buttons内所有button.link(即该按钮的类型link的类型很多参考按钮的item) */
links: any[];
//control系(直接control系列没有result.bool)
/** control操作面板的选中结果即该按钮的link即名字 */
control: string;
/** 既control的下标 */
index: number;
//ok系
/** 记录返回当前事件操作过程中面板按钮的确定ok取消cancel */
confirm: string;
/** 一般为触发的“视为”技能 */
skill: string;
/**
*
* card参数特供给视为牌cards[0]
* card.isCardfalse为视为牌
*/
card: Card;
[key: string]: any;
}

1820
node_modules/noname-typings/Skill.d.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,11 @@
/// <reference path="./ArrayEx.d.ts" />
/// <reference path="./Card.d.ts" />
/// <reference path="./DateEx.d.ts" />
/// <reference path="./HTMLDivElementEx.d.ts" />
/// <reference path="./HTMLTableELementEx.d.ts" />
/// <reference path="./windowEx.d.ts" />
/// <reference path="./Result.d.ts" />
/// <reference path="./Skill.d.ts" />
/// <reference path="./type.d.ts" />
/// <reference path="./MapEx.d.ts" />
/// <reference types="@types/cordova" />

View File

@ -1,6 +1,6 @@
{
"name": "noname-typings",
"version": "2023.12.31",
"version": "2024.01.16",
"description": "Noname typings, mainly for showing type hints when creating extensions of the Sanguosha-like game Noname.",
"repository": {
"type": "git",

View File

@ -17,15 +17,43 @@ interface NMap<V> {
[key: number]: V
}
declare type Game = import('../../noname/game/index.js').Game;
declare type Library = import('../../noname/library/index.js').Library;
//从0个参数到任意参数的方法结构声明
type NoneParmFum<T> = () => T;
type OneParmFun<U, T> = (arg0: U) => T;
type TwoParmFun<U1, U2, T> = (arg0: U1, arg1: U2) => T;
type ThreeParmFun<U1, U2, U3, T> = (arg0: U1, arg1: U2, arg2: U3) => T;
type FourParmFun<U1, U2, U3, U4, T> = (arg0: U1, arg1: U2, arg2: U3, arg3: U4) => T;
type RestParmFun<T> = (...args) => T;
type RestParmFun2<U, T> = (...args: U[]) => T;
//尝试增加的符合类型声明
/** SingleAndArrayType:单体与集合类型 */
type SAAType<T> = T | T[];
/** 再价格可以返回这种类型的方法 */
type SAAFType<T> = T | T[] | RestParmFun<T>;
/** 有name属性的对象 */
type NameType = { name: string };
/** 技能或者卡牌 */
type SkillOrCard = string | NameType | Card;
/** 卡牌或者卡牌集合 */
type CCards = SAAType<Card>;
/** 技能content */
declare type ContentFuncByAll = {
// (event: GameEventPromise, step: number, source: Player, player: Player, target: Player, targets: Player[], card: Card, cards: Card[], skill: string, forced: boolean, num: number, trigger: GameEventPromise, result: Result): any,
(event: GameEventPromise, trigger: GameEventPromise, player: Player): Promise<any>;
}
declare type Game = typeof import('../../noname/game/index.js').Game;
declare type Library = typeof import('../../noname/library/index.js').Library;
declare type Status = typeof import('../../noname/status/index.js').status;
declare type UI = import('../../noname/ui/index.js').UI;
declare type Get = import('../../noname/get/index.js').Get;
declare type AI = import('../../noname/ai/index.js').AI;
declare type UI = typeof import('../../noname/ui/index.js').UI;
declare type Get = typeof import('../../noname/get/index.js').Get;
declare type AI = typeof import('../../noname/ai/index.js').AI;
declare type Button = import('../../noname/library/index.js').Button;
declare type Card = import('../../noname/library/index.js').Card;
declare type VCard = import('../../noname/library/index.js').VCard;
declare type Dialog = import('../../noname/library/index.js').Dialog;
declare type GameEvent = import('../../noname/library/index.js').GameEvent;
declare type GameEventPromise = import('../../noname/library/index.js').GameEventPromise;
@ -40,3 +68,4 @@ declare type CodeMirror = typeof import('../../game/codemirror.js').default;
declare type Sex = 'male' | 'female' | 'dobule' | 'none';
declare type Character = [Sex, string, number | string, string[], string[]];
declare type Select = [number, number];

View File

@ -69,16 +69,7 @@ declare interface Window {
ai: AI;
}
initReadWriteFunction?(game = ({
download: () => any,
readFile: () => any,
readFileAsText: () => any,
writeFile: () => any,
removeFile: () => any,
getFileList: () => any,
ensureDirectory: () => any,
createDir: () => any,
})): void;
initReadWriteFunction?(game: Game): void;
bannedKeyWords: string[];
}

BIN
noname-server.exe Normal file

Binary file not shown.

View File

@ -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();
}
@ -6017,7 +6022,18 @@ export class Game extends Uninstantable {
if (info.usable && get.skillCount(skills2[i]) >= info.usable) enable = false;
if (info.chooseButton && _status.event.noButton) enable = false;
if (info.round && (info.round - (game.roundNumber - player.storage[skills2[i] + '_roundcount']) > 0)) enable = false;
if (player.storage[`temp_ban_${skills2[i]}`] === true) enable = false;
for (const item in player.storage) {
if (item.startsWith('temp_ban_')) {
if(player.storage[item] !== true) continue;
const skillName = item.slice(9);
if (lib.skill[skillName]) {
const skills=game.expandSkills([skillName]);
if(skills.includes(skills2[i])) {
enable = false; break;
}
}
}
}
}
if (enable) {
if (event.isMine() || !event._aiexclude.includes(skills2[i])) {
@ -7135,14 +7151,14 @@ export class Game extends Uninstantable {
event.avatars[i].classList.add('selecting');
}
}
let rand2 = [];
let rand = [];
for (let i = 0; i < event.config.width; i++) {
for (let j = 0; j < event.config.width - i; j++) {
rand2.push(i);
rand.push(i);
}
}
for (let i = 0; i < event.config.num; i++) {
let rand2 = rand2.randomGet();
let rand2 = rand.randomGet();
for (let j = 0; j < rand2.length; j++) {
if (rand2[j] == rand2) {
rand2.splice(j--, 1);

View File

@ -814,6 +814,7 @@ export class Get extends Uninstantable {
"[object Object]": true,
"[object Array]": true,
"[object Arguments]": true,
"[object Date]": true
};
if (typeof obj !== "object" || obj === null || !canTranverse[getType(obj)])
@ -827,16 +828,22 @@ export class Get extends Uninstantable {
const target =
constructor
? (
// 这类数据处理单独处理
// 这类数据处理单独处理
// 实际上需要处理的只有Map和Set
// 除此之外的就只能祝愿有拷贝构造函数了
(Array.isArray(obj) || obj instanceof Map || obj instanceof Set)
(Array.isArray(obj) || obj instanceof Map || obj instanceof Set || constructor === Object)
// @ts-ignore
? new constructor()
// @ts-ignore
: new constructor(obj)
: (
(constructor.name in window && /\[native code\]/.test(constructor.toString()))
// @ts-ignore
? new constructor(obj)
: obj
)
)
: Object.create(null);
if (target === obj) return target;
map.set(obj, target);
if (obj instanceof Map) {
@ -1360,11 +1367,13 @@ export class Get extends Uninstantable {
}
static infoFuncOL(info) {
var func;
const str = info.slice(13).trim();
try {
eval('func=(' + info.slice(13) + ');');
}
catch (e) {
return function () { };
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)) {
func = get.filter.apply(this, get.parsedResult(func));
@ -1954,8 +1963,8 @@ export class Get extends Uninstantable {
}
}
/**
* @param { number | [number, number] | (()=>[number, number]) } [select]
* @returns { [number, number] }
* @param { number | Select | (()=>Select) } [select]
* @returns { Select }
*/
static select(select) {
if (typeof select == 'function') return get.select(select());
@ -3007,7 +3016,16 @@ export class Get extends Uninstantable {
var js = node.getCards('j');
for (var i = 0; i < js.length; i++) {
if (js[i].viewAs && js[i].viewAs != js[i].name) {
uiintro.add('<div><div class="skill">' + js[i].outerHTML + '</div><div>' + lib.translate[js[i].viewAs] + '' + lib.translate[js[i].viewAs + '_info'] + '</div></div>');
let html = js[i].outerHTML;
let cardInfo = lib.card[js[i].viewAs], showCardIntro=true;
if (cardInfo.blankCard) {
var cardOwner = get.owner(js[i]);
if (cardOwner && !cardOwner.isUnderControl(true)) showCardIntro = false;
}
if (!showCardIntro) {
html=ui.create.button(js[i],'blank').outerHTML;
}
uiintro.add('<div><div class="skill">' + html + '</div><div>' + lib.translate[js[i].viewAs] + '' + lib.translate[js[i].viewAs + '_info'] + '</div></div>');
}
else {
uiintro.add('<div><div class="skill">' + js[i].outerHTML + '</div><div>' + lib.translate[js[i].name + '_info'] + '</div></div>');
@ -3477,7 +3495,7 @@ export class Get extends Uninstantable {
const defaultYingbianEffect = get.defaultYingbianEffect(node.link || node);
if (lib.yingbian.prompt.has(defaultYingbianEffect)) yingbianEffects.push(defaultYingbianEffect);
}
if (yingbianEffects.length) uiintro.add(`<div class="text" style="font-family: yuanli">应变:${yingbianEffects.map(value => lib.yingbian.prompt.get(value)).join('')}</div>`);
if (yingbianEffects.length && showCardIntro) uiintro.add(`<div class="text" style="font-family: yuanli">应变:${yingbianEffects.map(value => lib.yingbian.prompt.get(value)).join('')}</div>`);
}
if (lib.translate[name + '_append']) {
uiintro.add('<div class="text" style="display:inline">' + lib.translate[name + '_append'] + '</div>');

View File

@ -332,7 +332,7 @@ export class Is extends Uninstantable {
}
return true;
}
static altered() { return false; }
static altered(skillName) { return false; }
/*
skill=>{
return false;

View File

@ -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')) {

View File

@ -1,4 +1,5 @@
import { Game as game } from '../game/index.js';
import { lib } from '../library/index.js';
/**
* @param {string} name - 卡牌包名
@ -33,7 +34,20 @@ export const importMode = generateImportFunction('mode', (name) => `../../mode/$
*/
function generateImportFunction(type, pathParser) {
return async (name) => {
const modeContent = await import(pathParser(name));
const path = pathParser(name);
// 通过浏览器自带的script标签导入可直接获取报错信息且不会影响JS运行
// 此时代码内容也将缓存在浏览器中故再次import后将不会重新执行代码内容测试下来如此
const [status, script] = await new Promise((resolve) => {
const script = document.createElement('script');
script.type = 'module';
script.src = `${lib.assetURL}noname/init/${path}`;
script.onerror = () => resolve(['error', script]);
script.onload = () => resolve(['ok', script]);
document.head.appendChild(script);
});
script.remove();
if (status === 'error') return;
const modeContent = await import(path);
if (!modeContent.type) return;
if (modeContent.type !== type) throw new Error(`Loaded Content doesn't conform to "${type}" but "${modeContent.type}".`);
await game.import(type, modeContent.default);

View File

@ -58,7 +58,7 @@ export async function boot() {
_status.event = lib.element.GameEvent.initialGameEvent();
setWindowListener();
await setOnError();
const promiseErrorHandler = await setOnError();
// 无名杀更新日志
if (window.noname_update) {
@ -468,6 +468,7 @@ export async function boot() {
if (extensionlist.length && (config.get('mode') != 'connect' || show_splash)) {
_status.extensionLoading = [];
_status.extensionLoaded = [];
const bannedExtensions = Reflect.get(window, 'bannedExtensions');
@ -477,8 +478,25 @@ export async function boot() {
extensionsLoading.push(importExtension(name));
}
await Promise.allSettled(extensionsLoading);
await Promise.allSettled(_status.extensionLoading);
const extErrorList = [];
for (const promise of extensionsLoading) {
await promise.catch(async (error) => {
extErrorList.add(error);
if (!promiseErrorHandler || !promiseErrorHandler.onHandle) return;
// @ts-ignore
await promiseErrorHandler.onHandle({ promise });
});
}
for (const promise of _status.extensionLoading) {
await promise.catch(async (error) => {
if (extErrorList.includes(error)) return;
if (!promiseErrorHandler || !promiseErrorHandler.onHandle) return;
// @ts-ignore
await promiseErrorHandler.onHandle({ promise });
});
}
// await Promise.allSettled(_status.extensionLoading);
_status.extensionLoaded.filter(Boolean).forEach((name) => {
lib.announce.publish("Noname.Init.Extension.onLoad", name);
lib.announce.publish(`Noname.Init.Extension.${name}.onLoad`, void 0);
@ -843,7 +861,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");
@ -867,6 +886,8 @@ async function setOnError() {
game.loop();
}
};
return promiseErrorHandler;
}
function setWindowListener() {

View File

@ -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)) {
@ -476,6 +484,9 @@ export const Content = {
else black.push([target, card]);
}
event.red = red; event.black = black;
event.trigger('debateShowOpinion');
'step 2'
var red = event.red, black = event.black;
if (red.length) {
game.log(red.map(function (i) {
return i[0];
@ -528,7 +539,7 @@ export const Content = {
dialog.open();
}, get.translation(player), event.videoId, red, black);
game.delay(4);
'step 2';
'step 3';
game.broadcastAll('closeDialog', event.videoId);
var opinion = null;
if (event.red.length > event.black.length) opinion = 'red';
@ -542,7 +553,7 @@ export const Content = {
black: event.black,
targets: event.targets
};
'step 3';
'step 4';
if (event.callback) {
var next = game.createEvent('debateCallback', false);
next.player = player;
@ -1999,47 +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();
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;
return lib.skill.global.includes(info.skill) || info.player.hasSkill(info.skill, true);
});
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);
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);
await game.createTrigger(event.triggername, event.current.skill, event.current.player, trigger);
}
}
}
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 => {
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);
});
if (event.choice.length < 2) return event.goto(4);
'step 2';
const next = event.choice[0].player.chooseControl(event.choice.map(i => i.skill));
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;
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);
},
createTrigger: function () {
"step 0";
@ -2143,11 +2164,11 @@ export const Content = {
next._trigger = trigger;
next.triggername = event.triggername;
if ("contents" in info && Array.isArray(info.contents)) {
next.setContents(info.contents);
} else {
// if ("contents" in info && Array.isArray(info.contents)) {
// next.setContents(info.contents);
// } else {
next.setContent(info.content);
}
// }
next.skillHidden = event.skillHidden;
if (info.forceDie) next.forceDie = true;
@ -8163,7 +8184,12 @@ 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];
}
game.log(player, '被贴上了<span class="yellowtext">' + get.translation(cards[0].viewAs) + '</span>', cards, '');
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');

View File

@ -45,6 +45,10 @@ export class GameEvent {
};
this._aiexclude = [];
this._notrigger = [];
/**
* @type { Result }
*/
// @ts-ignore
this._result = {};
this._set = [];
/**
@ -738,7 +742,7 @@ export class GameEvent {
untrigger(all = true, player) {
const evt = this._triggering;
if (all) {
this._triggered = 5;
if(all !== 'currentOnly') this._triggered = 5;
if (evt && evt.doingList) {
evt.doingList.forEach(doing => doing.todoList = []);
}
@ -749,11 +753,6 @@ export class GameEvent {
// const doing=evt.doingList.find(doing=>doing.player==player);
// if(doing) doing.todoList=[];
}
else if (all==='currentOnly'){
if (evt && evt.doingList) {
evt.doingList.forEach(doing => doing.todoList = []);
}
}
return this;
}
/**
@ -862,6 +861,11 @@ export class GameEvent {
*/
// @ts-ignore
this.excludeButton;
/**
* @type { Result }
*/
// @ts-ignore
this.result;
throw new Error('Do not call this method');
}
}

View File

@ -16,7 +16,7 @@ import { AsyncFunction } from '../../util/index.js';
* 且Promise的原有属性无法被修改一切对这个类实例的属性修改删除
* 再配置等操作都会转发到事件对应的属性中
*
* @todo 需要完成异步事件的debugger方法
* @template { GameEvent } T
*
* @example
* 使用await xx()等待异步事件执行

View File

@ -13,11 +13,6 @@ export class VCard {
* @param { string } [nature]
*/
constructor(suitOrCard, numberOrCards, name, nature) {
if (suitOrCard instanceof VCard) {
const other = suitOrCard;
[suitOrCard, numberOrCards, name, nature] = other._args;
}
if (Array.isArray(suitOrCard)) {
/**
* @type {string}
@ -96,8 +91,6 @@ export class VCard {
if (typeof nature == 'string') this.nature = nature;
if (!this.storage) this.storage = {};
if (!this.cards) this.cards = [];
this._args = [suitOrCard, numberOrCards, name, nature];
}
sameSuitAs(card) {
return get.suit(this) == get.suit(card);

View File

@ -5,7 +5,7 @@
* @typedef { InstanceType<typeof lib.element.Button> } Button
* @typedef { InstanceType<typeof lib.element.Dialog> } Dialog
* @typedef { InstanceType<typeof lib.element.GameEvent> } GameEvent
* @typedef { InstanceType<typeof lib.element.GameEvent> & InstanceType<typeof lib.element.GameEventPromise> & typeof Promise<typeof lib.element.GameEvent> } GameEventPromise
* @typedef { InstanceType<typeof lib.element.GameEvent> & InstanceType<typeof lib.element.GameEventPromise & typeof Promise<InstanceType<typeof lib.element.GameEvent>> } GameEventPromise
* @typedef { InstanceType<typeof lib.element.NodeWS> } NodeWS
* @typedef { InstanceType<typeof lib.element.Control> } Control
*/
@ -9691,7 +9691,16 @@ export class Library extends Uninstantable {
if (typeof info.usable == 'number' && player.hasSkill('counttrigger') &&
player.storage.counttrigger && player.storage.counttrigger[skill] >= info.usable) return false;
if (info.round && (info.round - (game.roundNumber - player.storage[skill + '_roundcount']) > 0)) return false;
if (player.storage[`temp_ban_${skill}`] === true) return false;
for (const item in player.storage) {
if (item.startsWith('temp_ban_')) {
if(player.storage[item] !== true) continue;
const skillName = item.slice(9);
if (lib.skill[skillName]) {
const skills=game.expandSkills([skillName]);
if(skills.includes(skill)) return false;
}
}
}
return true;
},
characterDisabled: function (i, libCharacter) {
@ -10198,6 +10207,19 @@ export class Library extends Uninstantable {
return 0;
}
};
/**
* @type {{
* global: string[];
* globalmap: SMap<Player[]>;
* storage: SMap<any>;
* undist: SMap<any>;
* thers: SMap<any>;
* zhu: SMap<any>;
* zhuSkill: SMap<any>;
* land_used: SMap<any>;
* [key: string]: Skill;
* }}
*/
static skill = {
stratagem_fury: {
marktext: '🔥',
@ -12785,6 +12807,10 @@ export class Library extends Uninstantable {
color: '#c3f9ff',
nature: 'thundermm',
}],
['合', {
color: '#c3f9ff',
nature: 'thundermm',
}],
['梦', {
color: '#6affe2',
nature: 'watermm',
@ -13062,11 +13088,10 @@ export class Library extends Uninstantable {
static other = {
ignore: () => void 0
};
}
Library.config = undefined;
};
Library.config = undefined;
Library.configOL = undefined;
;
export const lib = Library;

View File

@ -4190,7 +4190,7 @@ class Create extends Uninstantable {
}
editnode.classList.remove('disabled');
};
var clickButton = async () => {
var clickButton = async function () {
if (currentButton == this) {
resetEditor();
return;
@ -8652,6 +8652,48 @@ class Create extends Uninstantable {
clickCapt.call(node[lib.config.character_dialog_tool]);
}
}
//仅仅下面是新加的by Curpond
let container = dialog.querySelector('.content-container>.content')
let Searcher = ui.create.div('.searcher.caption')
let input = document.createElement('input')
input.style.textAlign = 'center'
input.style.border = 'solid 2px #294510'
input.style.borderRadius = '6px'
input.style.fontWeight = 'bold'
input.style.fontSize = '21px'
let find = ui.create.button(['find', '搜索'], 'tdnodes')
find.style.display = 'inline'
let clickfind = function (e) {
e.stopPropagation()
let value = input.value
if (value == '') {
game.alert('搜索不能为空')
input.focus()
return
}
let list = []
for (let btn of dialog.buttons) {
if ((new RegExp(value, 'g').test(get.translation(btn.link)))) {
btn.classList.remove('nodisplay')
} else {
btn.classList.add('nodisplay')
}
}
}
input.addEventListener('keyup', (e) => {
if (e.key == 'Enter') clickfind(e)
})
find.listen(clickfind)
Searcher.appendChild(input)
Searcher.appendChild(find)
container.prepend(Searcher)
return dialog;
}
static dialog() {
@ -9008,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');
}
@ -9624,7 +9665,7 @@ class Create extends Uninstantable {
if (get.position(item) == 'j' && item.viewAs && lib.config.cardtempname != 'off') {
node.classList.add('infoflip');
node.classList.add('infohidden')
ui.create.cardTempName(item, node).style.setProperty('display','block','important')
ui.create.cardTempName(item, node).style.setProperty('display', 'block', 'important')
}
return node;
@ -13377,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);
})();
}
@ -13592,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);
})();
}
@ -14092,6 +14137,18 @@ export class UI extends Uninstantable {
* @type { Control | undefined }
*/
static skills3;
/**
* @type { HTMLDivElement }
*/
static window;
/**
* @type { HTMLDivElement }
*/
static pause;
/**
* @type { HTMLAudioElement }
*/
static backgroundMusic;
static refresh(node) {
void window.getComputedStyle(node, null).getPropertyValue("opacity");
}

View File

@ -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,66 @@ 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) {
// @ts-ignore
@ -71,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
*/

View File

@ -1,7 +1,7 @@
/**
* 关于除已实现浏览器外其余浏览器的异步错误处理
*
* 很遗憾对于这类浏览器因为标准未涉及报错栈堆或地址及行列号故我们只能直接暴力地throw出我们捕获道德错误
* 很遗憾对于这类浏览器因为标准未涉及报错栈堆或地址及行列号故我们只能直接暴力地throw出我们捕获到的错误
*
* 尽管我们还是会为了这类浏览器判断是不是捕获到了一个`Error`
*