diff --git a/character/mobile/characterReplace.js b/character/mobile/characterReplace.js index 45a2cf5ad..6dacfc4f2 100644 --- a/character/mobile/characterReplace.js +++ b/character/mobile/characterReplace.js @@ -19,6 +19,7 @@ const characterReplaces = { zhoubuyi: ["zhoubuyi", "yj_zhoubuyi"], xianglang: ["xianglang", "mb_xianglang"], miheng: ["re_miheng", "miheng"], + peixiu: ["ol_peixiu", "peixiu"], }; export default characterReplaces; diff --git a/character/mobile/translate.js b/character/mobile/translate.js index d6e54587e..518c81e01 100644 --- a/character/mobile/translate.js +++ b/character/mobile/translate.js @@ -563,7 +563,8 @@ const translates = { miheng_prefix: "手杀", re_gaoshun: "手杀界高顺", re_gaoshun_prefix: "手杀界", - peixiu: "裴秀", + peixiu: "手杀裴秀", + peixiu_prefix: "手杀", xingtu: "行图", xingtu1: "倍数", xingtu2: "约数", diff --git a/character/offline/character.js b/character/offline/character.js index bef9d0355..b41fe4f88 100644 --- a/character/offline/character.js +++ b/character/offline/character.js @@ -1,4 +1,5 @@ const characters = { + yj_ehuan: ["male", "qun", 4, ["psdiwan", "pssuiluan", "psconghan"], ["doublegroup:shu:qun"]], yj_zhouji: ["female", "wu", 3, ["psyanmou", "pszhanyan", "psyuhuo"]], drag_guanyu: ["male", "shu", 4, ["dragchaojue", "dragjunshen"]], drag_caoren: ["male", "wei", 4, ["draglizhong", "dragjuesui"]], diff --git a/character/offline/intro.js b/character/offline/intro.js index a5ad13fa2..b754e5e77 100644 --- a/character/offline/intro.js +++ b/character/offline/intro.js @@ -1,4 +1,5 @@ const characterIntro = { + ehuan: "鄂焕,古典文学名著《三国演义》人物,为蜀将高定部将,身长九尺,面目狰狞,使一只方天戟,有万夫不当之勇。于孔明征朱褒、雍闿时粉墨登场,与魏延大战不分胜负,后中计被魏延、王平、张翼联手擒获,孔明以礼相待,成功离间高定与朱、雍二人。后高定派鄂焕斩朱褒、平雍闿,二人一起归蜀,鄂焕遂因其功而被封为牙门将。", zhouji: "三国杀集换式卡牌游戏《阵面对决》中的权倾系列卡牌。游卡桌游官方的三国时期女性角色,原型是周妃(又名周彻)。周瑜之女。", lvchang: "吕常(161—221年),荆州南阳博望(今河南省南阳市方城县博望镇)人,汉末至三国时期曹魏将领。吕常曾担任曹魏横海将军、章陵太守,为武猛都尉厉节中郎将裨将军,封关内侯。常以中勇,显名州司,试守雉长,执戈秉戎,慎守易,兵不顿于敌国,坠不侵于四邻,拜武猛都尉厉节中郎将裨将军,封关内侯。王师南征,与充军从,奄有江汉,舍爵册勋,封阴德亭侯,领郡。鸠集荒散,为民统纪,三考有成,转拜平狄将军,改封卢亭侯,莅国赋政,十有三年。会蜀将关羽猖獗为寇,常御之,羽不能克。文帝加其庸,转拜横海将军,徙封西鄂都乡侯,食邑并七百户。年六十一,黄初二年正月卒。", huangjinleishi: "黄巾军中负责施法的女祭司二人组。", diff --git a/character/offline/skill.js b/character/offline/skill.js index 5a9080185..3a62a7cbf 100644 --- a/character/offline/skill.js +++ b/character/offline/skill.js @@ -2,7 +2,114 @@ import { lib, game, ui, get, ai, _status } from "../../noname.js"; /** @type { importCharacterConfig['skill'] } */ const skills = { - //线下E系列肘击 + //线下E系列 + //鄂焕 + psdiwan: { + trigger: { player: "useCardToPlayered" }, + filter(event, player) { + return event.card.name == "sha" && event.isFirstTarget; + }, + frequent: true, + usable: 1, + content() { + player.draw(trigger.targets.length); + }, + }, + pssuiluan: { + trigger: { player: "useCard2" }, + filter(event, player) { + if (player.group != "qun" || event.card.name != "sha") return false; + return ( + game.countPlayer(target => { + return !event.targets.includes(target) && lib.filter.targetEnabled2(event.card, player, target) && lib.filter.targetInRange(event.card, player, target); + }) > 1 + ); + }, + groupSkill: true, + async cost(event, trigger, player) { + event.result = await player + .chooseTarget( + get.prompt2("pssuiluan"), + (card, player, target) => { + const event = get.event().getTrigger(); + return !event.targets.includes(target) && lib.filter.targetEnabled2(event.card, player, target) && lib.filter.targetInRange(event.card, player, target); + }, + 2 + ) + .set("ai", target => { + const player = get.event("player"), + event = get.event().getTrigger(); + return get.effect(target, event.card, player); + }) + .forResult(); + }, + async content(event, trigger, player) { + trigger.targets.addArray(event.targets); + player.addTempSkill("pssuiluan_effect"); + trigger.card.pssuiluan = true; + }, + subSkill: { + effect: { + charlotte: true, + trigger: { player: ["useCardAfter", "damageEnd"] }, + filter(event, player) { + if (event.name == "damage") { + return player.group != "shu" && event.getParent(4).name == "pssuiluan_effect"; + } + return event.card.pssuiluan && (event.targets || []).some(i => i.isIn()); + }, + forced: true, + popup: false, + forceDie: true, + async content(event, trigger, player) { + if (trigger.name == "damage") { + await player.changeGroup("shu"); + return; + } + const targets = trigger.targets.filter(i => i.isIn()).sortBySeat(); + for (const target of targets) { + await target + .chooseToUse(function (card, player, event) { + if (get.name(card) != "sha") return false; + return lib.filter.filterCard.apply(this, arguments); + }, "随乱:是否对" + get.translation(player) + "使用一张【杀】?") + .set("filterTarget", function (card, player, target) { + if (target != _status.event.sourcex && !ui.selected.targets.includes(_status.event.sourcex)) return false; + return lib.filter.filterTarget.apply(this, arguments); + }) + .set("targetRequired", true) + .set("complexSelect", true) + .set("sourcex", player); + } + }, + }, + }, + }, + psconghan: { + trigger: { global: "damageSource" }, + filter(event, player) { + if (player.group != "shu" || !event.source || !event.player.isIn()) return false; + return event.source.getSeatNum() == 1 && (player.hasSha() || (_status.connectMode && player.countCards("hs"))); + }, + direct: true, + groupSkill: true, + content() { + player + .chooseToUse(function (card, player, event) { + if (get.name(card) != "sha") return false; + return lib.filter.filterCard.apply(this, arguments); + }, get.prompt2("psconghan", trigger.player)) + .set("filterTarget", function (card, player, target) { + if (target != _status.event.sourcex && !ui.selected.targets.includes(_status.event.sourcex)) return false; + return lib.filter.filterTarget.apply(this, arguments); + }) + .set("targetRequired", true) + .set("complexSelect", true) + .set("logSkill", ["psconghan", trigger.player]) + .set("sourcex", trigger.player); + }, + }, + //肘击 psyanmou: { getCards(event, player) { let cards = []; diff --git a/character/offline/sort.js b/character/offline/sort.js index 850350669..896c3fa61 100644 --- a/character/offline/sort.js +++ b/character/offline/sort.js @@ -5,7 +5,7 @@ const characterSort = { offline_luanwu: ["ns_lijue", "ns_zhangji", "ns_fanchou"], offline_yongjian: ["ns_chendao", "yj_caoang", "yj_caocao", "yj_liru", "yj_caohong", "yj_zhangfei", "yongjian_ganning", "yj_dongzhuo", "yj_xuyou", "yj_jiaxu", "yj_zhenji"], offline_piracyE_zy: ["shen_jiaxu", "pe_wangyun", "pe_zhonghui", "pe_sunchen", "pe_mengda", "pe_wenqin", "ns_caoanmin", "jiangqing", "kongrong", "jiling", "tianfeng", "mateng"], - offline_piracyE: ["yj_zhouji"], + offline_piracyE: ["yj_zhouji", "yj_ehuan"], offline_piracyS: ["ns_jiaxu", "longyufei", "ps_guanyu", "ps1059_guojia", "ps2070_guojia", "ps2063_zhaoyun", "ps2067_zhaoyun", "ps1062_zhouyu", "ps2080_zhouyu", "ps_caozhi", "ps_jin_simayi", "ps_caopi", "ps_simayi", "ps2068_simayi", "ps_machao", "ps_zhugeliang", "ps2066_zhugeliang", "ps_jiaxu", "ps_lvbu", "ps_shen_machao", "jsp_liubei"], offline_piracyK: ["pk_sp_duyu"], offline_vtuber: ["vtb_xiaosha", "vtb_xiaoshan", "vtb_xiaotao", "vtb_xiaole", "vtb_xiaojiu"], diff --git a/character/offline/translate.js b/character/offline/translate.js index cf2711af4..44baa2304 100644 --- a/character/offline/translate.js +++ b/character/offline/translate.js @@ -404,8 +404,7 @@ const translates = { drag_guanyu_prefix: "龙", drag_caoren: "龙曹仁", drag_caoren_prefix: "龙", - drag_lvchang: "龙吕常", - drag_lvchang_prefix: "龙", + drag_lvchang: "吕常", dragchaojue: "超绝", dragchaojue_info: "准备阶段,你可以弃置一张手牌,然后令所有其他角色本回合不能使用或打出此花色的牌,然后这些角色依次选择一项:①正面朝上交给你一张牌;②本回合非锁定技失效。", dragjunshen: "军神", @@ -425,6 +424,13 @@ const translates = { pszhanyan_info: "出牌阶段限一次,你可以令你攻击范围内的所有角色依次选择一项:①受到你对其造成的1点火属性伤害;②将手牌或弃牌堆中的一张【火攻】或火【杀】置于牌堆顶。然后你摸X张牌(X为本次选择次数较小的选项的被选择次数)。", psyuhuo: "驭火", psyuhuo_info: "锁定技。①防止你受到的火属性伤害。②你的【火攻】和火【杀】不计入手牌上限。", + yj_ehuan: "鄂焕", + psdiwan: "敌万", + psdiwan_info: "每回合限一次,当你使用【杀】指定第一个目标后,你可以摸X张牌(X为此牌指定的目标数)。", + pssuiluan: "随乱", + pssuiluan_info: "群势力技。你使用【杀】可以额外指定两个目标,若如此做,此牌结算完毕后,所有目标角色可依次对你使用一张【杀】,你以此法受到伤害后,将势力变更至蜀。", + psconghan: "从汉", + psconghan_info: "蜀势力技。一号位造成伤害后,你可以对受伤角色使用一张【杀】。", }; export default translates; diff --git a/character/onlyOL/skill.js b/character/onlyOL/skill.js index 3f56be324..f32ba82e9 100644 --- a/character/onlyOL/skill.js +++ b/character/onlyOL/skill.js @@ -313,7 +313,7 @@ const skills = { .chooseToUse(function (card, player, event) { if (get.name(card) != "sha") return false; return lib.filter.filterCard.apply(this, arguments); - }, "眩惑:对" + get.translation(player) + "使用一张【杀】,或令" + get.translation(player) + "你的手牌并获得你的两张牌") + }, "眩惑:对" + get.translation(target2) + "使用一张【杀】,或令" + get.translation(player) + "你的手牌并获得你的两张牌") .set("filterTarget", function (card, player, target) { if (target != _status.event.sourcex && !ui.selected.targets.includes(_status.event.sourcex)) return false; return lib.filter.targetEnabled.apply(this, arguments); diff --git a/character/rank.js b/character/rank.js index ca698486f..37865c324 100644 --- a/character/rank.js +++ b/character/rank.js @@ -714,6 +714,9 @@ window.noname_character_rank = { "sb_gongsunzan", ], bp: [ + "ol_peixiu", + "caimao", + "yj_ehuan", "xin_huojun", "chess_diaochan", "chess_huangzhong", @@ -2287,6 +2290,8 @@ window.noname_character_rank = { "zhugemengxue", "ol_sb_taishici", "clan_wuqiao", + "caimao", + "yj_ehuan", "xin_huojun", "muludawang", "mb_huban", @@ -2782,6 +2787,7 @@ window.noname_character_rank = { "sb_gongsunzan", ], junk: [ + "ol_peixiu", "ol_sb_guanyu", "junk_guanyu", "sunshao", diff --git a/character/sp/character.js b/character/sp/character.js index c59422cd4..1ae7d0b15 100644 --- a/character/sp/character.js +++ b/character/sp/character.js @@ -1,4 +1,6 @@ const characters = { + caimao: ["male", "wei", 4, ["olzuolian", "oljingzhou"]], + ol_peixiu: ["male", "wei", 3, ["olmaozhu", "oljinlan"]], yadan: ["male", "qun", 4, ["olqingya", "oltielun"]], sp_sunce: ["male", "qun", 4, ["olliantao"]], ol_liupi: ["male", "qun", 4, ["olyicheng"]], diff --git a/character/sp/intro.js b/character/sp/intro.js index 5aac7efd8..8347f4eb3 100644 --- a/character/sp/intro.js +++ b/character/sp/intro.js @@ -1,4 +1,5 @@ const characterIntro = { + caimao: "蔡瑁,字德珪,生卒年不详。襄阳蔡州人,东汉末年荆州名士。少年时与曹操交好。初平元年(公元190年),刘表为荆州刺史。时宗贼猖獗,蔡瑁协助刘表诛杀宗贼,平定荆州之地,蔡瑁因此得刘表重用,并在刘表任镇南将军时担任他的军师。刘表病亡后,蔡瑁拥护刘表幼子刘琮继位,并逼迫他投降南征的曹操。蔡瑁在曹操麾下历任从事中郎、司马、长水校尉,受封汉阳亭侯。", yadan: "雅丹,《三国演义》虚构人物,西羌丞相。诸葛亮伐魏时,魏大都督曹真驰书赴羌,西羌国王彻里吉任命雅丹与元帅越吉起兵15万前去增援,中了诸葛亮之计,被伏兵所困。", liupan: "刘磐(生卒年不详),山阳高平人,荆州牧刘表从子。与南阳人黄忠共守长沙攸县。为人骁勇,数次为寇于艾、西安诸县。江东孙策于是分海昏、建昌为左右六县,以东莱太史慈为建昌都尉,治海昏,并督诸将共拒刘磐。于是刘磐绝迹不复为寇。", guotu: "郭图(?-205年),字公则,颍川(治今河南省禹州市)人。东汉末年袁绍帐下谋士。韩馥统冀州时,郭图与荀谌等人奉袁绍之命,说服韩馥让位。袁绍统一河北后,郭图与审配等人力劝袁绍统率大军攻打曹操。袁绍死后,袁尚继位。郭图与辛评为袁谭效力,挑唆袁谭攻击袁尚。建安十年(205年),郭图和袁谭一同被曹操所杀。", diff --git a/character/sp/skill.js b/character/sp/skill.js index abe7fb48a..a7e6503de 100644 --- a/character/sp/skill.js +++ b/character/sp/skill.js @@ -2,6 +2,197 @@ import { lib, game, ui, get, ai, _status } from "../../noname.js"; /** @type { importCharacterConfig['skill'] } */ const skills = { + //蔡瑁 + olzuolian: { + audio: 2, + enable: "phaseUse", + filter(event, player) { + return player.getHp() > 0 && game.hasPlayer(target => target.countCards("h")); + }, + filterTarget(card, player, target) { + return target.countCards("h"); + }, + selectTarget() { + return [1, get.event("player").getHp()]; + }, + usable: 1, + multitarget: true, + multiline: true, + async content(event, trigger, player) { + const targets = event.targets.sortBySeat(); + const cards = targets.slice().map(i => i.getCards("h").randomGet()); + const videoId = lib.status.videoId++; + game.broadcastAll( + (targets, cards, id, player) => { + let dialog = ui.create.dialog(get.translation(player) + "发动了【佐练】", cards); + dialog.videoId = id; + const getName = target => { + if (target._tempTranslate) return target._tempTranslate; + var name = target.name; + if (lib.translate[name + "_ab"]) return lib.translate[name + "_ab"]; + return get.translation(name); + }; + for (let i = 0; i < targets.length; i++) { + dialog.buttons[i].querySelector(".info").innerHTML = getName(targets[i]); + } + }, + targets, + cards, + videoId, + player + ); + await game.asyncDelay(3); + game.broadcastAll("closeDialog", videoId); + const cards_cardPile = Array.from(ui.cardPile.childNodes).filter(i => i.name == "sha" && get.nature(i, false)); + const cards_discardPile = Array.from(ui.discardPile.childNodes).filter(i => i.name == "sha" && get.nature(i, false)); + if (!Boolean(cards_cardPile.length + cards_discardPile.length)) { + player.popup("杯具"); + player.chat("我属性【杀】呢?!"); + game.log("但牌堆和弃牌堆都没有属性【杀】!"); + return; + } + const result = await player + .chooseToMove("佐练:选择交换展示牌和牌堆或弃牌堆中的属性【杀】") + .set( + "list", + (function (cards, cardPile, discardPile) { + let list = [["展示手牌", cards, "olzuolian_tag"]]; + if (cardPile.length) { + list.push(["牌堆", cardPile]); + } + if (discardPile.length) { + list.push(["弃牌堆", discardPile]); + } + return list; + })(cards, cards_cardPile, cards_discardPile) + ) + .set("filterMove", (from, to, moved) => { + if (typeof to == "number") return false; + const cards=get.event("cards"); + if(cards.includes(from.link)==cards.includes(to.link)) return false; + for(const pl of [[from.link,to.link],[to.link,from.link]]){ + if(cards.includes(pl[0])&&moved[0].includes(pl[1])&&cards.indexOf(pl[0])!=moved[0].indexOf(pl[1])) return false; + } + return true; + }) + .set("processAI", list => { + return list.map(i => i[1]); + }) + .set("cards", cards) + .forResult(); + if (result.bool) { + const cardsx = result.moved[0]; + for (let i = 0; i < cardsx.length; i++) { + const current = targets[i], + card = cardsx[i]; + if (!cards.includes(card)) { + if (cards_cardPile.includes(card)) { + current.$throw([cards[i]], 1000); + await current + .lose([cards[i]], ui.cardPile) + .set("insert_index", () => { + return ui.cardPile.childNodes[get.event("num")]; + }) + .set("num", cards_cardPile.indexOf(card)); + } else if (cards_discardPile.includes(card)) { + await current.loseToDiscardpile(cards[i]); + } + await current.gain(card, "gain2"); + } + } + } + }, + ai: { + order: 10, + result: { + target(player, target) { + //插个眼,等PZ157拯救此AI + return 0.5 - Math.random(); + }, + }, + }, + }, + oljingzhou: { + audio: 2, + trigger: { player: "damageBegin3" }, + filter(event, player) { + return player.getHp() > 0; + }, + async cost(event, trigger, player) { + event.result = await player + .chooseTarget(get.prompt2("oljingzhou"), [1, player.getHp()]) + .set("ai", target => { + const player = get.event("player"), + trigger = get.event().getTrigger(); + if (trigger.hasNature() && target == player) { + return -get.effect(target, { name: "tiesuo" }, player, player); + } + return get.effect(target, { name: "tiesuo" }, player, player); + }) + .forResult(); + }, + async content(event, trigger, player) { + for (const i of event.targets) { + await i.link(!i.isLinked()); + } + }, + }, + //裴秀 + //我们三国杀也有属于自己的爽文.jpg + olmaozhu: { + audio: 2, + trigger: { source: "damageBegin1" }, + filter(event, player) { + const count = lib.skill.olmaozhu.countSkill; + if (!player.isPhaseUsing() || count(player) <= count(event.player)) return false; + const evtx = event.getParent("phaseUse"); + return !player.getHistory("sourceDamage", evt => { + return evt.getParent("phaseUse") == evtx && count(player) > count(evt.player); + }).length; + }, + forced: true, + logTarget: "player", + content() { + trigger.increase("num"); + }, + countSkill(player) { + return player.getSkills(null, false, false).filter(i => { + const info = get.info(i); + return !info || !info.charlotte; + }).length; + }, + mod: { + cardUsable(card, player, num) { + if (card.name == "sha") return num + lib.skill.olmaozhu.countSkill(player); + }, + maxHandcard(player, num) { + return num + lib.skill.olmaozhu.countSkill(player); + }, + }, + }, + oljinlan: { + audio: 2, + enable: "phaseUse", + filter(event, player) { + const count = lib.skill.olmaozhu.countSkill; + return player.countCards("h") < Math.max(...game.filterPlayer().map(i => count(i))); + }, + filterCard: () => false, + selectCard: [0, 1], + prompt() { + const count = lib.skill.olmaozhu.countSkill; + return "将手牌数摸至" + get.cnNumber(Math.max(...game.filterPlayer().map(i => count(i)))) + "张"; + }, + usable: 1, + content() { + const count = lib.skill.olmaozhu.countSkill; + player.drawTo(Math.max(...game.filterPlayer().map(i => count(i)))); + }, + ai: { + order: 0.000000114514191981, + result: { player: 1 }, + }, + }, //鸭蛋 olqingya: { audio: 2, diff --git a/character/sp/sort.js b/character/sp/sort.js index d9fde9c58..0480445e1 100644 --- a/character/sp/sort.js +++ b/character/sp/sort.js @@ -12,7 +12,7 @@ const characterSort = { sp_zhongdan: ["cuiyan", "huangfusong"], sp_guozhan2: ["sp_dongzhuo", "liqueguosi", "zhangren"], sp_others: ["hanba", "caiyang"], - sp_waitforsort: ["ol_luyusheng", "ol_tw_zhangji", "ol_liwan", "ol_liuyan"], + sp_waitforsort: ["ol_luyusheng", "ol_tw_zhangji", "ol_liwan", "ol_liuyan", "ol_peixiu", "caimao"], }; const characterSortTranslate = { diff --git a/character/sp/translate.js b/character/sp/translate.js index e34f4c99a..f9dbb5e82 100644 --- a/character/sp/translate.js +++ b/character/sp/translate.js @@ -1244,6 +1244,17 @@ const translates = { olqingya_info: "当你使用【杀】指定唯一目标后,你可从逆时针方向和顺时针方向中选择一个你与其之间角色最少的方向。你弃置该方向下你与其之间的角色各一张手牌,然后你可以于本回合下个阶段结束时使用其中一张牌。", oltielun: "铁轮", oltielun_info: "锁定技。你计算与其他角色的距离-X(X为你本轮使用的牌数)。", + ol_peixiu: "裴秀", + olmaozhu: "茂著", + olmaozhu_info: "锁定技。①你的手牌上限和使用【杀】的额定次数+X(X为你拥有的技能数)。②当你于出牌阶段首次对技能数小于你的角色造成伤害时,此伤害+1。", + oljinlan: "尽览", + oljinlan_info: "出牌阶段限一次,你可以将手牌数摸至Y张(Y为场上技能数最多的角色的技能数)。", + caimao: "蔡瑁", + olzuolian: "佐练", + olzuolian_tag: "展示牌", + olzuolian_info: "出牌阶段限一次,你可以选择至多X名有牌的角色(X为你的体力值),你随机展示这些角色的各一张牌,然后你可以将这些牌与牌堆和弃牌堆中的属性【杀】进行交换。", + oljingzhou: "精舟", + oljingzhou_info: "当你受到伤害时,你可以选择至多X名角色(X为你的体力值)这些角色中处于/未处于连环状态的角色重置/横置武将牌。", }; export default translates; diff --git a/character/tw/skill.js b/character/tw/skill.js index 5517024d3..b7578dcd9 100644 --- a/character/tw/skill.js +++ b/character/tw/skill.js @@ -274,7 +274,7 @@ const skills = { let max = 0, names = ["huogong", "tiesuo", "wuzhong"].filter(name => { if (player.getStorage("twcairu_used").includes(name)) return false; - return event.filterCard({ name }, player, event); + return player.hasValueTarget(name ,true, true); }); if (!names.length) return 0; names = names.map(namex => ({ name: namex })); diff --git a/image/character/caimao.jpg b/image/character/caimao.jpg new file mode 100644 index 000000000..7d53ea667 Binary files /dev/null and b/image/character/caimao.jpg differ diff --git a/image/character/ol_peixiu.jpg b/image/character/ol_peixiu.jpg new file mode 100644 index 000000000..7ab4e9706 Binary files /dev/null and b/image/character/ol_peixiu.jpg differ diff --git a/image/character/yj_ehuan.jpg b/image/character/yj_ehuan.jpg new file mode 100644 index 000000000..0a55fd364 Binary files /dev/null and b/image/character/yj_ehuan.jpg differ