1- $ ( window ) . on ( 'load' , function ( ) {
2- var frameSpeed = 1000 ,
3- frameContainer = $ ( '#frame-container' ) ,
4- frames = $ ( '.frame' , frameContainer ) ,
5- frameCount = frames . length ,
6- messageContainer = $ ( '#message-container' ) ,
7- messages = $ ( '.message' , messageContainer )
8- messageCount = messages . length ,
9- t = null ,
10- start = $ ( '#start' ) ,
11- showFrame = function ( n ) {
12- if ( n != frameCount ) {
13- return frames . hide ( ) . eq ( n ) . show ( ) && messages . hide ( ) . eq ( n ) . show ( ) ;
14-
15- }
16- return frames . eq ( frameCount ) . show ( ) && messages . eq ( messageCount ) . show ( ) ;
17-
18- } ,
19- nextFrame = function ( ) {
20- if ( index == frameCount ) {
21- stopFrames ( ) ;
22- showFrame ( frameCount - 1 ) ;
23- }
24- else {
25- showFrame ( ++ index ) ;
26- t = setTimeout ( nextFrame , frameSpeed ) ;
27- }
28-
29- } ,
30- stopFrames = function ( ) {
31- clearInterval ( t ) ;
32- index = 0 ;
33- } ;
34- frameContainer
35- start . on ( 'click' , nextFrame )
36- stopFrames ( ) ;
37- showFrame ( 0 ) ;
38- } ) ;
1+ const SPECIAL_ROLL_USERNAMES = new Set ( [ 'shadukat' , 'gerni shadu' ] ) ;
2+
3+ function getCurrentUsername ( ) {
4+ const profileLink = document . querySelector ( 'a.profile' ) ;
5+ return profileLink ? profileLink . textContent . trim ( ) . toLowerCase ( ) : '' ;
6+ }
7+
8+ function isSpecialRollUser ( ) {
9+ return SPECIAL_ROLL_USERNAMES . has ( getCurrentUsername ( ) ) ;
10+ }
11+
12+ function shuffleArray ( items ) {
13+ const cloned = items . slice ( ) ;
14+ for ( let i = cloned . length - 1 ; i > 0 ; i -= 1 ) {
15+ const randomIndex = Math . floor ( Math . random ( ) * ( i + 1 ) ) ;
16+ [ cloned [ i ] , cloned [ randomIndex ] ] = [ cloned [ randomIndex ] , cloned [ i ] ] ;
17+ }
18+ return cloned ;
19+ }
20+
21+ function ensureRollModal ( ) {
22+ let modal = document . getElementById ( 'taskRollModal' ) ;
23+ if ( modal ) {
24+ return modal ;
25+ }
26+
27+ modal = document . createElement ( 'dialog' ) ;
28+ modal . id = 'taskRollModal' ;
29+ modal . className = 'task-roll-modal' ;
30+ modal . innerHTML = `
31+ <div class="task-roll-shell rsText">
32+ <div class="task-roll-title">Rolling Task</div>
33+ <div class="task-roll-content">
34+ <img id="taskRollModalImage" class="task-roll-image" src="/static/assets/Cake_of_guidance_detail.png" alt="Task roll image" />
35+ <p id="taskRollModalMessage" class="task-roll-message">Rolling...</p>
36+ <p id="taskRollModalSpecial" class="task-roll-special" hidden>Hi Youtube <3 Gerni Task</p>
37+ </div>
38+ <div class="task-roll-actions">
39+ <button id="taskRollModalClose" class="task-roll-button" type="button">Continue</button>
40+ </div>
41+ </div>
42+ ` ;
43+
44+ document . body . appendChild ( modal ) ;
45+
46+ const closeButton = modal . querySelector ( '#taskRollModalClose' ) ;
47+ closeButton . addEventListener ( 'click' , ( ) => {
48+ if ( typeof modal . close === 'function' ) {
49+ modal . close ( ) ;
50+ }
51+ } ) ;
52+
53+ return modal ;
54+ }
55+
56+ function buildFallbackRollCandidates ( ) {
57+ return [
58+ { name : 'Rolling...' , image : '/static/assets/Cake_of_guidance_detail.png' } ,
59+ { name : 'Rolling...' , image : '/static/assets/Tome_of_fire_(empty).png' } ,
60+ { name : 'Rolling...' , image : '/static/assets/Cockatrice_head.png' } ,
61+ { name : 'Rolling...' , image : '/static/assets/Dragon_sword.png' } ,
62+ { name : 'Rolling...' , image : '/static/assets/Guilded_smile_flag.png' } ,
63+ { name : 'Rolling...' , image : '/static/assets/Malediction_shard_2.png' }
64+ ] ;
65+ }
66+
67+ function getOfficialRollCandidates ( ) {
68+ const frameNodes = document . querySelectorAll ( '#frame-container .frame' ) ;
69+ const messageNodes = document . querySelectorAll ( '#message-container .message' ) ;
70+ const candidates = [ ] ;
71+
72+ frameNodes . forEach ( ( frameNode , index ) => {
73+ const imageSrc = frameNode . getAttribute ( 'src' ) ;
74+ const messageNode = messageNodes [ index ] ;
75+ const messageText = messageNode ? messageNode . textContent . trim ( ) : 'Rolling...' ;
76+ if ( imageSrc ) {
77+ candidates . push ( { name : messageText || 'Rolling...' , image : imageSrc } ) ;
78+ }
79+ } ) ;
80+
81+ return candidates . length > 0 ? candidates : buildFallbackRollCandidates ( ) ;
82+ }
83+
84+ function normalizeServerRollCandidates ( serverCandidates ) {
85+ if ( ! Array . isArray ( serverCandidates ) ) {
86+ return [ ] ;
87+ }
88+
89+ return serverCandidates
90+ . map ( ( candidate ) => ( {
91+ name : candidate && candidate . name ? candidate . name : 'Rolling...' ,
92+ image : candidate && candidate . image ? candidate . image : '/static/assets/Cake_of_guidance_detail.png'
93+ } ) )
94+ . filter ( ( candidate ) => Boolean ( candidate . image ) ) ;
95+ }
96+
97+ function resolveTaskImageSource ( imageValue ) {
98+ if ( ! imageValue ) {
99+ return '/static/assets/Cake_of_guidance_detail.png' ;
100+ }
101+ if ( imageValue . startsWith ( 'http://' ) || imageValue . startsWith ( 'https://' ) || imageValue . startsWith ( '/static/' ) ) {
102+ return imageValue ;
103+ }
104+ return `/static/assets/${ imageValue } ` ;
105+ }
106+
107+ function rollTaskModal ( options ) {
108+ const {
109+ finalName,
110+ finalImage,
111+ instant = false ,
112+ candidates = [ ]
113+ } = options ;
114+
115+ return new Promise ( ( resolve ) => {
116+ const modal = ensureRollModal ( ) ;
117+ const imageNode = modal . querySelector ( '#taskRollModalImage' ) ;
118+ const messageNode = modal . querySelector ( '#taskRollModalMessage' ) ;
119+ const specialNode = modal . querySelector ( '#taskRollModalSpecial' ) ;
120+ const closeButton = modal . querySelector ( '#taskRollModalClose' ) ;
121+
122+ let settled = false ;
123+
124+ const finish = ( ) => {
125+ if ( settled ) {
126+ return ;
127+ }
128+ settled = true ;
129+ closeButton . onclick = null ;
130+ resolve ( ) ;
131+ } ;
132+
133+ closeButton . onclick = ( ) => {
134+ if ( typeof modal . close === 'function' ) {
135+ modal . close ( ) ;
136+ }
137+ finish ( ) ;
138+ } ;
139+
140+ if ( typeof modal . showModal === 'function' ) {
141+ modal . showModal ( ) ;
142+ }
143+
144+ const finalImageSource = resolveTaskImageSource ( finalImage ) ;
145+ specialNode . hidden = ! instant ;
146+
147+ if ( instant ) {
148+ imageNode . src = finalImageSource ;
149+ messageNode . textContent = finalName ;
150+ closeButton . disabled = false ;
151+ return ;
152+ }
153+
154+ const baseCandidates = candidates . length > 0 ? candidates : buildFallbackRollCandidates ( ) ;
155+ const randomizedCandidates = shuffleArray ( baseCandidates ) . concat ( [ { name : finalName , image : finalImageSource } ] ) ;
156+ let pointer = 0 ;
157+ closeButton . disabled = true ;
158+
159+ const intervalId = window . setInterval ( ( ) => {
160+ const nextCandidate = randomizedCandidates [ pointer ] ;
161+ imageNode . src = resolveTaskImageSource ( nextCandidate . image ) ;
162+ messageNode . textContent = nextCandidate . name || 'Rolling...' ;
163+ pointer += 1 ;
164+
165+ if ( pointer >= randomizedCandidates . length ) {
166+ window . clearInterval ( intervalId ) ;
167+ imageNode . src = finalImageSource ;
168+ messageNode . textContent = finalName ;
169+ closeButton . disabled = false ;
170+ }
171+ } , 120 ) ;
172+ } ) ;
173+ }
39174
40175$ ( document ) . on ( 'click' , '#start' , function ( ) {
41176 req = $ . ajax ( {
42177 url : '/generate/' ,
43178 type : 'POST'
44- } )
179+ } ) ;
45180
46- req . done ( function ( data ) {
47- delay ( function ( ) {
48- const message = document . getElementById ( "message_target" ) ;
49- const image = document . getElementById ( "image_target" ) ;
50- const imageLink = document . getElementById ( "taskImage" ) ;
51- imageLink . href = data . link ;
52- imageLink . setAttribute ( 'data-tip' , data . tip ) ;
53- message . innerHTML = data . name ;
54- image . src = data . image ;
55- document . getElementById ( "start" ) . disabled = true ;
56- document . getElementById ( "complete" ) . disabled = false ;
57- } , 6000 ) ;
181+ req . done ( async function ( data ) {
182+ const serverCandidates = normalizeServerRollCandidates ( data . rollCandidates ) ;
183+ const rollCandidates = serverCandidates . length > 0 ? serverCandidates : getOfficialRollCandidates ( ) ;
184+ await rollTaskModal ( {
185+ finalName : data . name ,
186+ finalImage : data . image ,
187+ instant : isSpecialRollUser ( ) ,
188+ candidates : rollCandidates
189+ } ) ;
190+
191+ const message = document . getElementById ( "message_target" ) ;
192+ const image = document . getElementById ( "image_target" ) ;
193+ const imageLink = document . getElementById ( "taskImage" ) ;
194+ imageLink . href = data . link ;
195+ imageLink . setAttribute ( 'data-tip' , data . tip ) ;
196+ message . innerHTML = data . name ;
197+ image . src = resolveTaskImageSource ( data . image ) ;
198+ document . getElementById ( "start" ) . disabled = true ;
199+ document . getElementById ( "complete" ) . disabled = false ;
58200 } ) ;
59201} ) ;
60202
@@ -70,14 +212,6 @@ $(document).on('click', '#complete', function(){
70212} ) ;
71213
72214
73- var delay = ( function ( ) {
74- var timer = 0 ;
75- return function ( callback , ms ) {
76- clearTimeout ( timer ) ;
77- timer = setTimeout ( callback , ms ) ;
78- } ;
79- } ) ( ) ;
80-
81215// $(document).on('click', '#easy_generate', function(){
82216// $('form').submit(false);
83217// req = $.ajax({
@@ -103,7 +237,15 @@ $(document).on('click', '#generate_unofficial', function(){
103237 type : 'POST' ,
104238 data : { tier : tier + 'Tasks' }
105239 } ) ;
106- req . done ( function ( data ) {
240+ req . done ( async function ( data ) {
241+ const rollCandidates = normalizeServerRollCandidates ( data . rollCandidates ) ;
242+ await rollTaskModal ( {
243+ finalName : data . name ,
244+ finalImage : data . image ,
245+ instant : isSpecialRollUser ( ) ,
246+ candidates : rollCandidates . length > 0 ? rollCandidates : getOfficialRollCandidates ( )
247+ } ) ;
248+
107249 const task = document . getElementById ( tier + "_task" ) ;
108250 const image = document . getElementById ( tier + "_image" ) ;
109251 const imagePreview = document . getElementById ( tier + "_image_preview" ) ;
@@ -115,13 +257,9 @@ $(document).on('click', '#generate_unofficial', function(){
115257 imagePlaceholder . href = data . link
116258 imagePreview . name = data . name ;
117259 task . innerHTML = data . name ;
118- if ( data . image === 'Cake_of_guidance_detail.png' ) {
119- image . src = '/static/assets/Cake_of_guidance_detail.png' ;
120- imagePreview . src = '/static/assets/Cake_of_guidance_detail.png' ;
121- } else {
122- image . src = data . image ;
123- imagePreview . src = data . image ;
124- }
260+ const resolvedImageSource = resolveTaskImageSource ( data . image ) ;
261+ image . src = resolvedImageSource ;
262+ imagePreview . src = resolvedImageSource ;
125263 } ) ;
126264} ) ;
127265
0 commit comments