EF2 Changelog

Builds / 1.15.91

Update 1.15.91

Compared with 1.15.88 · published Fri, 16 Oct 2026 02:01:12 GMT · live channel

Highlights

Changes

Fixes

Technical

AI summary (claude-sonnet-5-5) written from the diffs below. Check the evidence before quoting it.

Assets 25

FileChangeSizePreview
assets/chunks/ArcheryGamePopup.js changed 27.9 KB → 27.9 KB
assets/chunks/ConquestPopup.js changed 47.2 KB → 47.2 KB
assets/chunks/EventHubPopup.js changed 4.8 KB → 4.8 KB
assets/chunks/FishingFlow.js changed 880.1 KB → 911.0 KB
assets/chunks/FishingHuntPage.js changed 43.6 KB → 108.8 KB
assets/chunks/FishingMiniWindow.js changed 12.4 KB → 12.4 KB
assets/chunks/FriendPopup.js changed 40.4 KB → 40.4 KB
assets/chunks/GuildWarBattlePopupV3.js changed 113.9 KB → 113.9 KB
assets/chunks/MiniAscentFlow.js changed 42.4 KB → 42.4 KB
assets/chunks/index.js changed 1.7 KB → 1.7 KB
assets/chunks/vendor-pixi.js changed 644.3 KB → 644.3 KB
assets/images/fishing/topdown/hazard_icons/ghost.webp removed 13.1 KB → gone
assets/images/fishing/topdown/hazard_icons/gust.webp removed 11.9 KB → gone
assets/images/fishing/topdown/hazard_icons/hazard.webp removed 6.4 KB → gone
assets/images/fishing/topdown/hazard_icons/ice.webp removed 8.9 KB → gone
assets/images/fishing/topdown/hazard_icons/log.webp removed 11.4 KB → gone
assets/images/fishing/topdown/hazard_icons/monster.webp removed 10.6 KB → gone
assets/images/fishing/topdown/hazard_icons/rapid.webp removed 11.3 KB → gone
assets/images/fishing/topdown/hazard_icons/reef.webp removed 12.4 KB → gone
assets/images/fishing/topdown/hazard_icons/rock.webp removed 9.5 KB → gone
assets/images/fishing/topdown/hazard_icons/storm.webp removed 9.6 KB → gone
assets/images/fishing/topdown/hazard_icons/swell.webp removed 7.9 KB → gone
assets/images/fishing/topdown/hazard_icons/thunder.webp removed 7.2 KB → gone
assets/images/fishing/topdown/hazard_icons/vine.webp removed 13.5 KB → gone
assets/images/fishing/topdown/hazard_icons/wave.webp removed 12.0 KB → gone

Code 31 changes in 10 classes

Minified client code with short names blanked for diffing. Class names are real.

AutoLevelHold 2 hunks, 26 statements
- e(Dy,"maxAffordableLevel"),e(Py,"planEvenLevels"),e(xy,"planInOrder");
+ function By(t,s,i,h){
+ return t&&0===h&&s-i>=3e4}
+ function Uy(t,s){
+ return!t&&0===s}
+ function Hy(t){
+ const s=t.split("?")[0];
+ return s.endsWith("/hero/levelUp")||s.endsWith("/castle/upgrade")}
+ e(Dy,"maxAffordableLevel"),e(Py,"planEvenLevels"),e(xy,"planInOrder"),e(By,"shouldAutoFlush"),e(Uy,"shouldAutoBuy"),e(Hy,"isAutoLevelSaveUrl");
- n(this,"frameCount",0),n(this,"frameRegistered",!1),n(this,"_enabled",!1),n(this,"lastFlushAt",0),n(this,"loadPromise",null),n(this,"touched",!1)}
+ n(this,"until",new Map)}
+ hold(s,i=Date.now()){
+ this.until.set(s,i+t.HOLD_MS)}
+ release(t){
+ this.until.delete(t)}
+ active(t=Date.now()){
+ let s=!1;
+ return this.until.forEach((i,h)=>{
+ t<i?s=!0:this.until.delete(h)}
+ ),s}
+ }
+ ;
+ e($y,"AutoLevelHold"),n($y,"HOLD_MS",6e4);
+ const Fy=new $y,Wy=class t{
+ constructor(){
+ n(this,"frameCount",0),n(this,"frameRegistered",!1),n(this,"_enabled",!1),n(this,"lastFlushAt",0),n(this,"loadPromise",null),n(this,"touched",!1),Zi.addBeforeMutation(t=>{
+ !Hy(t)&&yy.hasPending&&(this.lastFlushAt=Date.now(),yy.cleanup())}
+ )}
BaseService 3 hunks, 19 statements
+ static get pendingMutations(){
+ return t.pendingMutationCount}
+ static addBeforeMutation(s){
+ t.beforeMutationHooks.includes(s)||t.beforeMutationHooks.push(s)}
- const s=t.mutationChain.then(a,a);
- t.mutationChain=s.then(()=>{
+ if(!t.runningBeforeMutation&&t.beforeMutationHooks.length>0){
+ t.runningBeforeMutation=!0;
+ try{
+ for(const i of t.beforeMutationHooks)i(s)}
+ catch(c){
+ }
+ finally{
+ t.runningBeforeMutation=!1}
+ }
+ t.pendingMutationCount++;
+ const i=e(()=>{
+ t.pendingMutationCount=Math.max(0,t.pendingMutationCount-1)}
+ ,"settled"),h=t.mutationChain.then(a,a);
+ h.then(i,i),t.mutationChain=h.then(()=>{
- e(Ji,"BaseService"),n(Ji,"pendingRequests",new Map),n(Ji,"DUPLICATE_THRESHOLD",700),n(Ji,"REQUEST_TIMEOUT_MS",15e3),n(Ji,"mutationChain",Promise.resolve()),n(Ji,"READONLY_EXCEPTIONS",["getHeroInfo"]);
+ e(Ji,"BaseService"),n(Ji,"pendingRequests",new Map),n(Ji,"DUPLICATE_THRESHOLD",700),n(Ji,"REQUEST_TIMEOUT_MS",15e3),n(Ji,"mutationChain",Promise.resolve()),n(Ji,"pendingMutationCount",0),n(Ji,"beforeMutationHooks",[]),n(Ji,"runningBeforeMutation",!1),n(Ji,"READONLY_EXCEPTIONS",["getHeroInfo"]);
CastleAutoController 4 hunks, 16 statements
- n(this,"frameCount",0),n(this,"frameRegistered",!1),n(this,"_enabled",!1),n(this,"loadPromise",null),n(this,"touched",!1),n(this,"lastFlushAt",0)}
+ n(this,"frameCount",0),n(this,"frameRegistered",!1),n(this,"_enabled",!1),n(this,"loadPromise",null),n(this,"touched",!1),n(this,"lastFlushAt",0),Zi.addBeforeMutation(t=>{
+ !Hy(t)&&jZ.hasPending&&(this.lastFlushAt=Date.now(),jZ.cleanup())}
+ )}
- if(!this._enabled)return;
- if(!lo.isIdleBattle)return;
- if(lo.pausedAt>0)return;
- if(!B2.getInstance().getPlayer())return;
+ this._enabled&&lo.isIdleBattle&&(lo.pausedAt>0||W2.getInstance().getPlayer()&&Uy(Fy.active(),Zi.pendingMutations)&&(this.buy(),this.maybeFlush()))}
+ flushNow(){
+ jZ.hasPending&&(this.lastFlushAt=Date.now(),jZ.cleanup())}
+ maybeFlush(){
+ const t=Date.now();
+ By(jZ.hasPending,t,this.lastFlushAt,Zi.pendingMutations)&&(this.lastFlushAt=t,jZ.cleanup())}
+ buy(){
- if(r>0){
- Oo.gold-=r,rd.getInstance().updateGold(),p0.getInstance().setData(),VZ.markDirtyAndSchedule();
- const s=Date.now();
- 0===this.lastFlushAt&&(this.lastFlushAt=s),s-this.lastFlushAt>=t.FLUSH_MS&&(this.lastFlushAt=s,VZ.cleanup())}
- }
+ o>0&&(Oo.gold-=o,rd.getInstance().updateGold(),m0.getInstance().setData(),jZ.markDirtyAuto())}
- e(KZ,"CastleAutoController"),n(KZ,"instance"),n(KZ,"TICK_FRAMES",180),n(KZ,"UNLOCK_WAVE",100),n(KZ,"FLUSH_MS",15e3);
+ e(qZ,"CastleAutoController"),n(qZ,"instance"),n(qZ,"TICK_FRAMES",180),n(qZ,"UNLOCK_WAVE",100);
HeroItemRenderer 6 hunks, 14 statements
+ static get hasPending(){
+ return t.isDirty}
+ static markDirtyAuto(){
+ t.isDirty=!0,-1===t.numReviveSnapshot&&(t.numReviveSnapshot=Oo.numRevive)}
- t.isDirty=!0,-1===t.numReviveSnapshot&&(t.numReviveSnapshot=Oo.numRevive),null!==t.updateTimer&&clearTimeout(t.updateTimer),t.updateTimer=setTimeout(()=>{
+ t.isDirty=!0,t.manualDirty=!0,-1===t.numReviveSnapshot&&(t.numReviveSnapshot=Oo.numRevive),null!==t.updateTimer&&clearTimeout(t.updateTimer),t.updateTimer=setTimeout(()=>{
- if(0===t.heroTargetLevels.size)return void(t.numReviveSnapshot=-1);
+ const s=t.manualDirty;
+ if(t.manualDirty=!1,0===t.heroTargetLevels.size)return void(t.numReviveSnapshot=-1);
- const i=[],h=[];
- s.forEach((t,s)=>{
- i.push(s),h.push(t)}
- );
+ const h=[],e=[],n=new Set(Xt.getInstance().list.filter(t=>t&&t.id>0).map(t=>t.id));
+ if(i.forEach((t,s)=>{
+ n.has(s)&&(h.push(s),e.push(t))}
+ ),0===h.length)return;
- return t.success&&(Oo.setUserInfo(t.body),rd.getInstance().updateAll(),_N.getInstance().setData()),void Ar.getInstance().showError(o)}
- Oo.setUserInfo(o.body),rd.getInstance().updateGold(),_N.getInstance().isMultiLevel=!1,_N.getInstance().setMultiLevel(!1)}
+ return t.success&&(Oo.setUserInfo(t.body),rd.getInstance().updateAll(),EN.getInstance().setData()),void(s&&Ar.getInstance().showError(a))}
+ Oo.setUserInfo(a.body),rd.getInstance().updateGold(),s&&(EN.getInstance().isMultiLevel=!1,EN.getInstance().setMultiLevel(!1))}
- e(Ry,"HeroItemRenderer"),n(Ry,"updateTimer",null),n(Ry,"DEBOUNCE_DELAY",3e3),n(Ry,"isDirty",!1),n(Ry,"heroTargetLevels",new Map),n(Ry,"numReviveSnapshot",-1),n(Ry,"autoCloseCounter",0),n(Ry,"AUTO_CLOSE_SECONDS",5),n(Ry,"lastCounterUpdateTime",0);
+ e(Ry,"HeroItemRenderer"),n(Ry,"updateTimer",null),n(Ry,"DEBOUNCE_DELAY",3e3),n(Ry,"isDirty",!1),n(Ry,"heroTargetLevels",new Map),n(Ry,"numReviveSnapshot",-1),n(Ry,"manualDirty",!1),n(Ry,"autoCloseCounter",0),n(Ry,"AUTO_CLOSE_SECONDS",5),n(Ry,"lastCounterUpdateTime",0);
HeroAutoController 3 hunks, 12 statements
- if(!this._enabled)return;
- if(!t.isUnlocked)return;
- if(!lo.isIdleBattle)return;
- if(lo.pausedAt>0)return;
- if(!B2.getInstance().getPlayer())return;
+ this._enabled&&t.isUnlocked&&lo.isIdleBattle&&(lo.pausedAt>0||W2.getInstance().getPlayer()&&Uy(Fy.active(),Zi.pendingMutations)&&(this.buy(),this.maybeFlush()))}
+ flushNow(){
+ yy.hasPending&&(this.lastFlushAt=Date.now(),yy.cleanup())}
+ maybeFlush(){
+ const t=Date.now();
+ By(yy.hasPending,t,this.lastFlushAt,Zi.pendingMutations)&&(this.lastFlushAt=t,yy.cleanup())}
+ buy(){
- if(a<=0)return;
- Oo.gold-=a,rd.getInstance().updateGold(),l&&Ii.getInstance().recalculate(),yy.markDirtyAndSchedule();
- const c=Date.now();
- 0===this.lastFlushAt&&(this.lastFlushAt=c),c-this.lastFlushAt>=t.FLUSH_MS&&(this.lastFlushAt=c,yy.cleanup()),_N.getInstance().refreshLevels(),pR.getInstance().onConditionChanged()}
+ r<=0||(Oo.gold-=r,rd.getInstance().updateGold(),a&&Ii.getInstance().recalculate(),yy.markDirtyAuto(),EN.getInstance().refreshLevels(),pR.getInstance().onConditionChanged())}
- e(By,"HeroAutoController"),n(By,"instance"),n(By,"TICK_FRAMES",180),n(By,"FLUSH_MS",15e3),n(By,"UNLOCK_WAVE",1e3);
+ e(Wy,"HeroAutoController"),n(Wy,"instance"),n(Wy,"TICK_FRAMES",180),n(Wy,"UNLOCK_WAVE",1e3);
CastleItemRenderer 5 hunks, 9 statements
+ static get hasPending(){
+ return t.isDirty}
+ static markDirtyAuto(){
+ t.isDirty=!0}
- t.isDirty=!0,null!==t.updateTimer&&clearTimeout(t.updateTimer),t.updateTimer=setTimeout(()=>{
+ t.isDirty=!0,t.manualDirty=!0,null!==t.updateTimer&&clearTimeout(t.updateTimer),t.updateTimer=setTimeout(()=>{
- if(!t.isDirty)return;
+ const s=t.manualDirty;
+ if(t.manualDirty=!1,!t.isDirty)return;
- return t.success&&(Oo.setUserInfo(t.body),rd.getInstance().updateAll(),p0.getInstance().setData(),FZ.getInstance().setData()),void Ar.getInstance().showError(e)}
+ return t.success&&(Oo.setUserInfo(t.body),rd.getInstance().updateAll(),m0.getInstance().setData(),zZ.getInstance().setData()),void(s&&Ar.getInstance().showError(n))}
- e(WZ,"CastleItemRenderer"),n(WZ,"updateTimer",null),n(WZ,"DEBOUNCE_DELAY",3e3),n(WZ,"isDirty",!1);
+ e(XZ,"CastleItemRenderer"),n(XZ,"updateTimer",null),n(XZ,"DEBOUNCE_DELAY",3e3),n(XZ,"isDirty",!1),n(XZ,"manualDirty",!1);
HeroDetailPopup 3 hunks, 3 statements
- if(1===s){
+ if(Fy.hold("heroDetail"),1===s){
- this.tapTrans.visible=h,!h&&this.tapTrans.selected?this.onTapDetailClick():this.tapTrans.selected&&this.updateTransInfo(),this.applyTabButtonVisibility(),this.applyTabResourceMode(),this.updateLayout(),this.updateDraggableContainer(),this.show2()}
+ this.tapTrans.visible=h,!h&&this.tapTrans.selected?this.onTapDetailClick():this.tapTrans.selected&&this.updateTransInfo(),this.applyTabButtonVisibility(),this.applyTabResourceMode(),this.updateLayout(),this.updateDraggableContainer(),"list"!==this.mode&&"roster"!==this.mode||Fy.hold("heroDetail"),this.show2()}
- this.enhanceTimer&&this.syncEnhanceToServer(),this.multiEnhanceContainer.visible=!1,super.hide()}
+ this.enhanceTimer&&this.syncEnhanceToServer(),this.multiEnhanceContainer.visible=!1,Fy.release("heroDetail"),super.hide()}
CastleDetailPopup 3 hunks, 3 statements
- this.title=Nr.getText("GAME_86","성 정보"),this.lblName.text=Nr.getText("GAME_530","발키온 성"),this.btnEnhance.text=Nr.getText("GAME_66","강화"),this.btnReset.text=Nr.getText("COMMON_368","초기화"),this.enhanceTimer||this.syncInFlight||this.resetEnhance(),this.multiEnhanceContainer.visible=!1,t||(t=js.getInstance().castleVO),this.vo=t,this.updateCastleInfo(),this.updateLayout(),super.show(),pR.getInstance().registerPopupButtonGetter(t=>"btnEnhance"===t&&this.btnEnhance.visible?{
+ this.title=Nr.getText("GAME_86","성 정보"),this.lblName.text=Nr.getText("GAME_530","발키온 성"),this.btnEnhance.text=Nr.getText("GAME_66","강화"),this.btnReset.text=Nr.getText("COMMON_368","초기화"),this.enhanceTimer||this.syncInFlight||this.resetEnhance(),this.multiEnhanceContainer.visible=!1,t||(t=js.getInstance().castleVO),this.vo=t,this.updateCastleInfo(),this.updateLayout(),super.show(),Fy.hold("castleDetail"),pR.getInstance().registerPopupButtonGetter(t=>"btnEnhance"===t&&this.btnEnhance.visible?{
- this.enhanceTimer&&(clearTimeout(this.enhanceTimer),this.enhanceTimer=null,this.syncEnhanceToServer()),this.multiEnhanceContainer.visible=!1,pR.getInstance().unregisterPopupButtonGetter(),super.hide()}
+ this.enhanceTimer&&(clearTimeout(this.enhanceTimer),this.enhanceTimer=null,this.syncEnhanceToServer()),this.multiEnhanceContainer.visible=!1,pR.getInstance().unregisterPopupButtonGetter(),Fy.release("castleDetail"),super.hide()}
- if(1===s){
+ if(Fy.hold("castleDetail"),1===s){
VersionManager 1 hunks, 1 statements
- e(Z,"VersionManager"),n(Z,"gameId","ef2"),n(Z,"appVersion","1.15.88");
+ e(Z,"VersionManager"),n(Z,"gameId","ef2"),n(Z,"appVersion","1.15.91");
(module) 1 hunks, 1 statements
- "hidden"===document.visibilityState?C0.cleanup():"visible"===document.visibilityState&&Yi.getInstance().checkDateRollover()}
+ "hidden"===document.visibilityState?(G0.cleanup(),Vy.getInstance().flushNow(),QZ.getInstance().flushNow()):"visible"===document.visibilityState&&Yi.getInstance().checkDateRollover()}