0ŖĪ]4iuŅ(ué%Œ}}}e0’Ęq’ī0}Ā Sä÷q²īźīīī °’’’’’’’’2Tv˜ŗÜžļĶ«‰gE#2Tv˜ŗÜž2Tv˜ŗÜž§Üī’’ļŽ›X$!S0†ĖÜīĪgUvˆŹž’Žje‡ŗ»«šxw‡˜ŖŖšxV4§Üī’’ļŽ›X$!Sww‡ˆ™Ŗŗ»ĢĢ̼»«š‰UUUUõ’’’’’’’effwVD3"2D PPPPUUUUUPUUPUPUUUUUUUUUUUUUUUUPUUUUUUUUUPUPUUPUUPUUUˆˆ€ˆˆˆˆˆˆ€€ˆˆˆˆˆˆˆ€PUPUPUUUUPUUUUUUPUUPPUUUUPUUPUUUUUUPUUUPuUUPUUUUPPUPUUUUUUUUUUUUUUUUPUUUUUUPUPUPPPUUUUUUUPUUPUPPPPUU€™™™p©™™)™€™™)" "r€)’)"ˆ˜"‰™™™™™)")’")‚)™"™))")r)")‚€˜€ ˜’)"™"'"" ' €™™™)™""™"™’€™’™’™"™™™"""’("’)’)’UUUPUPUUP€UUUUUUPUUPU…PPPXˆˆˆˆˆˆ…ˆUPUUUUUPPUUPUUPPUUPUUUUUUPUPUUUUPUUUUUUUUUUUUUUUUUUUUPPUPUPPPPPUupUuUu€™"™"™™"™™*™"™"™"™")‚)" ™    ™)™" ™" ™" €™" ™" €™" ™" ™)‚™")"")™"ˆ™"™’™’™’)’™’™’™’™’)’)’)’)")")’)’)’PUUUUUUUUPUPUˆUˆP€ˆˆUP…PPXXUPPP€X€U€XP…€XP…€ˆXPUUPUUUUPUUUUPUPUPUUUUUWUUUUUUPUUUUUUUUUPUPUUUUUUUUUPPPPPPPPUpUpUpU€U™"™"™"™"™"™"""€™™™       ™ —"‰"’"’ "’™"’p"" ""™"™"—™"™™")""""""""p™’"’"’p"  ’p"" )’)’" ")™"™r"€ˆ€ˆˆP…ˆPˆˆˆ€€€€ˆˆPˆˆˆ€€ˆˆ€ˆˆ€ˆˆ€ˆˆˆˆPUPUUUUPUXPXPUUWUWUWUWUUUUUUUUUUUUUUUUUUUUUUUUUUUPPPPPPPP€UUpUpUpPUUPUUUPUUUPUUUPUUUUUUUUUuUUUUUUUUUUUUp€ˆw€P€€€€ˆˆ€83€€P€€PPˆ€ˆPUPU€UˆˆXˆˆUUˆUUUPUUPUPUPUPPP€…X€€UPUPUUUUUUUUUPUPWUWUWUUUUUUUUUUUUUUUUUUUUUUUUUUPPPPPPPPUpUpUpUpUpUpUpUpPUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU…UUUUUUUUUUUUUUUUUUUUUUUUUuwUuwWUUwwUuwWuuwuUUuUUUuwUuUU€ˆˆ€€ˆwx™€™ˆ™Æ€x™Ŗ—©’xw—™w™™™™™Ŗ ™©™Ŗ)’’)’’p’’p€ˆU€ˆUUUUUUUUUUUUUUUUUPUPUPUPPUPUPUUUUUUUUUUUUUUUUWUWUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUPUUPUUUUPPPPPPPPUpUpUpUpUpUpUpUpUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUuUUUUUUWwUUUwUUuwUUuwUUuUUu€€€€€—™p)ˆ—)š—’€ˆł€—ł€pš’™’™™’™™©™š©™™™’‰©)‰(™™’)p)(‰UUUUUU…UˆˆUˆˆUUˆˆU…ˆUUˆ€ˆPˆP…PXˆPXˆPUˆxU€xU€UUUUUUUUUUUUUUUUUUUWUUUWUWUWUWUPUUPUUPUUPUUPUUPUUPUUPUPPUPUPPPPPUpUpUpUpUpUpUpUpUUUUUUUUUUUUUUUUUUUUPUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWUUWUUUUUWUUu©’€€Æ™©€ų™™x™š€™"’’’‰’’˜ś’)ś’ "©’"™ł)—™’UˆUˆUˆUˆUUUˆUˆXUˆˆUU…UUPUUU€UUXUUUPˆPUUUUUUUPPPxPUPwUUuUUUUUUUUPUUUUUPUPUUUPUUUUUUUUUUUPUPUUUUUUUUUUUPPPPPPPPUpUpUpUpUpUpUpUpUUUUUUUUUPUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPp—™p—™p™™wwwwwwUUUPUU™™™ ™™™y™™™™www—wwwwUUUUUUUUUUUUUUUUUUUUUUUuUUUPPPUPUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPPUPUUUUpUPPuUUUUUUUUUUUUUUUUPUPUUUUUUUUUUUUUUUUUUUUUUUPUUUPUUUUUUUUUUUUUUPUUUUUPPUUUUPUUPUUPPUUUUUUUUUUUUUUUPUPUPPUPpUPpUUpUUpUUpUUUUUUUUUUUUUUUUUUUUUUUUUUUUPPPPPPPPUUUUUUUUPUUUPUUUPUUUPUUUPUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUPUUUPUUUPUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUPUUUUUUUUUPX…ˆUXXxUUUUUUUUUUU…Uˆ…U………X…xwuxWwwwuwwUUU…XXXU‡…‡ˆX‡uXUuwWwwwwwwwwwwwUUX…Xˆˆ‡…uˆˆxXw…WwUˆww‡xww…xwwXUXˆXU…ˆˆˆ‡‡‡wwUwWuwwUwwwwwWwwwwX…pXˆpˆ‡pwwpwpwpUwpPUwpPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPPUPUPPUUUUUUPUUUUPUUUUPUUUUUUUUPUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU…PuXUUPUU……UU……€……PUwUUXwU…X‡ˆxwwXuwwxuˆwuWwwwxwwxwwwuwwWˆˆˆˆwwwwwwwwwuwwWuwwwwwwWwwwuwwWˆˆˆˆwxw‡www‡wwWˆwwwxww…xuwˆwwWxwˆˆˆˆxwwwwwwwwwwwwwWwwwUwwWuwwwwwUUuwˆˆˆˆwwwwwwwwwwwwwwwwwwUwwwuwuwwuwwWUUWUUUUUww‡wwwwwwwwwUUUUUUUUUUUUwwwwPUPPPUPPUUUUUPUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUPUUUUUUUUUUPUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUPUUUUUUUUPUUPUUUUPUUPUUPUUUPUPUU……ˆU…X‡UU…wUU……UUUˆUUUUUUUUUUUUwwwwˆˆˆxWwxˆxwwwWˆww…uˆuˆXˆ‡U……Uwwwwwwwwˆˆˆwww‡ˆwwwwwwwwˆXwWxˆˆˆw‡xww…ww‡ˆˆˆˆˆˆˆ…uwwˆxwwxwwwxwwwwwwwwwwwˆˆˆˆˆˆˆˆwwwwwwwwwwwwwwwwwwwwwwwwˆˆˆˆXUUWwwwwwwwwWwwwWwwwww…wwwˆwˆXxwWˆˆˆwwXwwwXwwwxww‡xwwwwwwwwwˆˆˆˆwwwwwwwwwwwwwwwwwWuwUUUPUUPUUUUUPPUUPUPUUUUPUUUUUUUUUUUUUUPUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUPPUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUPUUUUUUUUPUUUPUUUUUUUUUUUUUUUPUUUUUUUUUPUUPUPUUUUUUU……UXUUUUUUUUUUUUUUUUUUUUUUUUUUuˆUXUXXUUU…UPUUUUUUUUUUUUUUUU‡‡‡wˆxU‡ˆˆˆxUUUˆUUUUUUUUUUUUUUUUwwww‡xuw…U…Xˆ……UUU…UUUUUUUUUUUUUwwwwwwwwxxwUˆ…xxˆ…X…U…UˆUUUUUUUUw‡uww‡wwwWwwxwwˆWˆ‡ˆXXXˆ…ˆXUPUUwwwwwWwwwwwWwwww…Uˆ‡…ˆˆxUXXˆUUUUUUUPUUUPUUUUUUUUPUUUPUUPUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUPUUUUUUPUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU„PUUUˆˆˆˆˆˆp€€wˆˆˆˆˆˆˆˆˆUUPUUUUUUUPUUUUUPUUUUUPUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUˆ€€ €€""€p"'’p"™"’ "’"’""""’"™)r€ ™")")")" '"'’ " " """""™r"™"™) "")™™’ ™™™™™™)™™"™™"™)r™)’€™)y€™) WuUUPPPPPPPUUUUUUUUUUUUUUUUUUUPUUUPUUUUUUUUUUUPPUUUUUUUUUUPUUUPUPUU€PPUPUUUUPPUUPPUUUUUUUUUUPUUUUU€™™‡"""—""r€ˆˆˆ™’’ " " """ ’™’)™) """""") )™) ) ) ) ) ™) ) ) ))))))))’ ’ ’ ’ ’ "r’ ’ ™"™"™"""" ’€’PPUPUPUPPPPPPPPUUUUUUPUUUUUUUUUUPUUUUUPUUPUPUPPUUPUUU€UˆˆPUˆˆˆˆˆˆˆˆˆUUPUUPUUUPUUPPUPUPPUUˆ"’"’"’ p"™'’'’p"€")")")" ™"™)r€™)r€’""" " " '’"'' )" "™)"™""™""™r""’  ™™™™™r)"€€"PUPUWPPPPPPUUPUUPPUPPUPUUUUPUUPPUUPUUPPPUUPPUUPUUPUUPUPUPPUPU€ˆUˆP€P€…ˆˆˆˆˆˆPˆUUUUUUPUUPUPUPPUPˆUU€ˆˆˆ€€€€ˆˆˆwp€ˆ€€€p€€wPUPUUUUUUUUUUUUUUUU€ˆUUUUUUUUUU€PPUPUUUUUUUUUUUUUUUUUUUUUUUPUuPUuPPUUPPUUPPUUPUUUUUPPUUPUUPU€PUPPU€PUPP…P…€XXˆˆˆ€€ˆPˆˆPˆˆXPˆˆˆˆˆˆˆˆˆˆˆˆˆXˆPˆ€ˆP€ˆPP™™wˆš™™w©™™™Æ™™™ ™r’𒙐š’yš’šˆ€—ų™w™wˆŖŸś™€’šyšUUUUwUUUwWUUWUUUuUUUWUUwUUUUUUUUUUUUUUUUUUUUUUwUUUuUUUwUUUUUUUUUUUUUUPUUUPUUUUUUUUUUUPPUPUPUPUPUPUPUUUUPUUPPPPPPUUUPUUUPUUUUUPUUUUUUPUuPUuPUuPUPP…PP€UˆUPPUPPUPUUPˆˆUPˆUUUUUPUUPUUPUUP€P€…P€PU€UPU€™™)™*™™©™)™™ˆ™’™"ˆ"©(")©ˆ™‚™ ™™"™ ) ™ ™ ™ ƙƉšŸ™€WUWUUWUUWUUUwWUUwWUUwWUUpWUUpwWUWUUUuUUUWUUUwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUPUPUPUPUPUPUUUUUUUUUUUUUUUUUPUUUPUUUPUUUPUUUUuUuPUuPUuPUUUUPUuPPUPPUUPUUPUUPUUPUUPUUPUUUPUUPPPUPUUUUUUPUUPUPPUˆUˆUPUPUUPUUUPUUUPUUUPU™(’ ’("’(’š"‰’)"š’"’š’" ’™š Ŗ™’š ’™˜€’™‡šł™y€š™™wˆpWUUpWUUpWUUpWUUpWUUPUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUPUPUPUPUPUPUUPPUPUPPUUPPUUPPUUPPUUPUUPPUPUUUUPPUuPUUPUuPUuUuUuPPUUPUUPUUPUUPUUPUUPPUUPPUUˆˆ€ˆP‡ˆP‡ˆP‡ˆP‡XPU€XPUUUPUUPU…PUˆˆPUˆ‡PUˆ‡XU…ˆUˆU ™™™p™™™™™™™™wwwwwwwUUUUUUUU™y™™y™™ywwwwwwwwUUUUUUUUPUUPUUPUUPUUpUUPUUPUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUPUPUPUPUPUPUPUUPUUPUPPPUUUUUUUUUuPUuPUUPPUPUUUUPUUUPUUPUUPUUPpUU…UUUPUU€XPU€UUˆUU‡UUwUUˆUU€ˆPUˆPUˆPUˆPUˆPUˆPU€ˆPUˆˆPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUPUPuPuPUPuPUPuPUUUUUUUUUUUPUUUUUU…UUUUUUUUUUUUUUUPˆUUU…UUPUUPUUPUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUPPUUPPUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUUUUUUUUUUUUUUUUUUUUUUUUPUUPUUUUUUUPUUPPUUUUUUUUUUUUUUUUPUUUUUUUUUUUUPu€ˆPu€ˆPu€uPupwPupUupUUpUUp…UUUˆˆˆˆ…wX‡wwuwwwwwwwwwwwwwwwwwUUUUUˆUUˆXˆˆwwˆ‡uwwˆwu7wwwwwwwwwUUUUUUUUXˆ……XˆxˆuWwuwwwwwwwwww‡ˆUUUUUUUUU…XUˆ…ˆˆ‡xX…wwUˆwwwxˆˆˆˆUUUUUUUUUUUU…XUU…UˆU‡…ˆˆˆ…‡ˆˆˆˆ…UUUUUUUUUUUUUUUUUUUUUXUUUˆ…ˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUPUPUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUPUUUPUUUPwwwwwwwwUUUUUUUUUUUUwwwwwwwWwwwwUUUuUUUuUUUUwwwx€ˆˆˆpww‡pwww€www€wwWpwwWpwwwwwwwˆˆˆˆUUXUwwwwwwwwwwwwwwwUuwwwwwwwˆˆˆˆuw‡ˆwwwwwwwWwwwwwwwwwwwwwwwwˆˆˆxXUU‡wwwwwuwwuwwwuwwwuwWwWwWwU…xxWˆ‡ˆW…xwwwˆwwwˆxwwwˆww‡‡www‡ˆ…XˆˆXUUw‡ˆXww‡ˆwwwˆwwW…xwwwˆwuwP…UUU…UUUX…UUˆUUUˆˆXUWˆUUUUUUUUUUUPUUUUUUUUUUUUUUUUPUUUUUUUPUPPUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUPUUwwwwwwwwˆˆˆˆwwwwwwwuwwwWwwwwwwwWww‡xwwwˆˆˆˆˆwwwXwww‡www‡www‡www‡wwwwwwwwˆˆˆˆwwwwxwwwuwwwuwwwuwwwwwwwwwwwˆˆˆˆwwwwwwwwwwwwwwwwwWwwwwwwˆˆˆˆXXU…wwwwwwwwwwwwwwwwwwwwwwwwwwwwˆˆˆˆwwwwwwwwwwwwwWwwwwwuwww‡ˆˆˆxXUUXwwwwwwwwwwu‡ww‡ˆˆx…ˆ‡ˆW‡w‡ˆ…UUXˆwXˆ…wˆˆˆX…XUUˆUUUˆUUU…UUUXUUUUUUUUUUUUUUUPUUUUUUUUUUUPUUUPUUUPUUUUPUUUUUUPUUUUUPPUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUPUPUUUUUUUUUUUUUwwwwwwwwwuwuwuwUUwxˆXˆˆXˆUUXUUUUwww‡wwwwwwwwwWww‡……‡X…uˆ……ˆXUUUUUwwwXwwwXWwwXuwUx…ˆˆˆ…………UˆˆUPUUwWwwwwuuwwwW‡Xˆ…ˆUxˆ…XUˆUUUUUUUUwwwwwwwww‡ˆXˆWUUUˆˆ…XˆXUUUUUUUUUwwˆ‡‡…xX…Xˆˆ…ˆXUˆUXUUUUUUUUUUUUUxˆXX…XˆˆXXUXUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUPUPUPUUUUUUUUUUUUPPUUUUPUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU {753211100000001134567777788888jhfcbaaaaabcdefghhiiijjjjjjjkk76543222222233445556677888999:…YXVUTSSRRRSSTTUUVVWWXXXXXXYYYY @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    ‘k‘Y“H”7•&–—™äšŌ›Äœ³£ž“ŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸŸń#4$ćį11#ōä#2°  °°ĄŠš @P`PšššaQB#ōŌó£‚  000@P`pp€€```P@0€€ ˆ3        ¤pp ```@ ppppqrrsttuvwwxyyzz{{||||}||{zyx­¢¬Ā«ó©#Ø4§T¦u¦e¦V§'Øē©ø©Ø©Ŗ©ŗØė¦„\¤}¢m¢~”?”ļ”ϔϢ߄ØO¬Æć(('óćó':[[[I& =N^m{y¤¶Ēéū.Oo~}  II}}†‡ˆ‰‚TGvExC{A}OOOOOOOOOOOOOOOOOOOOOOOOOOT"‚ ‚ ‚ ‚ ‚‚¢ ‚ ‚ ‚ ‚ ‚ ‚k¢¢ ¢ ¢¢¢¢¢¢ ¢ ¢¢ ¢ ¢¢¢¢¢¢ ¢ ¢¢ ¢ ¢¢¢¢¢¢‚¢¢¢‚ ‚ ‚f‚ ‚ ‚‚¢¢¢‚ ‚ ‚ ‚ ‚‚` ‚ ‚‚¢¢¢‚ ‚ ‚ ‚ ‚¢¢ ¢ ` € ` ` ``````€``€ € € € ` ` ` ` ` `````@``@`€ €€ € € € € € € € €€€ €€€€ €€ € €€€€ka a a a aa a a a a a aaaaaka a a a aa a a a aaaaaaaaaaaaaa a a a` aaaaaaaaaaaaaaaaa```` ` `aaAAAAAAA A AAA A AaaaaA A AA A A   a a a a a aa`````dd„ d„D„ d„d„ d„„„„„`„ d„„„d„„„d„„„ „ „ „ „d„ d d d„„„„„ d d d d d„„„ d d dd di„„„`d d„„ d d d d d d„„„„ „ „ „ „„`„ d d „ d„„„ „„„` „„„„d„ „ d „¤DDDDDDDD` dd „„ D D D D D D D D„ „d„fDD„ d„dd„ d„„ d d„„„„„iD D„„„ d d„„„„„„ „ „ „ „„kd„ d„dd„ d„„ d d„„„ dd„„„„ d d„„„„„„ „ „ „„id „„ „„ „„ „„ „ d „ d „ d „ d „ „ „„„„„ dd„d„ d d d d„„„„ „ „ „ „ „ „d„„ dd„ d dd„„„„„„„`„„„ „¤ „ „`DDDDD„„„d„„„„` D DD D„„ d„„„d„ d„„„„„„md„ „ „ „ „„ d™GˆÉ •ĄC„CC„C€™Ž`# „ ' © + ­ ŲžĄ?™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUUUUUUUUUUUUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUQPUPUPUPUPUPUPUQUPUPQPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUUUUUQQUUQUUUUUUQUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUQUUUQQUQUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUQUUUQQUQUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUXUPUPQPUPUPQPQUPUPUPXUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUUUUUUUUUUUUUXUUUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUXUUXUUUUUUU…XˆXXUˆXUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUXUUUUUQQXUUU”ŖQUUUUˆˆˆXUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP”ŖŖŖŖ ” PUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU”ŖŖŖŖ ” €UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU wwwwwwUUU”   ”Ŗ” wwQUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUQUUŖwwUUUŖw’ w”wwww Ŗw”’wQUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUQUPŖzwPUP pww÷  ’ˆ wwwwŖŠ’”wwXPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUXUU ’QUUU”pwp÷  ’ ’šz÷’”wUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUXUUUŖUUUUUŖpš  ’š Ŗ’ŖwQUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUQŖQU” p ’š ’Ŗ’QUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUˆUPUXŖP” p ’š ’Ŗ’QPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUXUś” š ’š ’”’QUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUX ’ š ’š ’”’UUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUXˆ św wš špw ’š ’” ’€UUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUQP  wš š÷zww ’š ’ ’ˆUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUQ Ŗšpw špwzww ’š w÷’ ’ˆˆˆˆUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUXQUQ Ŗš šwz ’š wwz wUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUˆUXUQ Ŗš šz ’š w§Ŗ wUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPU€ˆPUU Ŗš š’z ’š w Ø wPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU Ŗš€”šwpz w wØw UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU Ŗš€” šwpz w wØw UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU Ŗwš”’pz  wØ UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPQP śwš”z” pw wØŠš’UPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUU…UQU śš”z” pw wØŠš’UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUU šwŖzX” wŖUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUU śUU” XŠ  ŖUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUˆšˆUPˆ” P Š  €UPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU…” UU  QUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUU€UUU…”Ŗ”QUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUQU؁UU…UUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUˆˆˆˆˆˆQPŖˆXUP…PUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUU…XUUUUUUˆˆUˆˆˆˆXUˆUUU…ˆˆˆˆˆˆUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUˆXUUUUUUUUUUU…XUUUUUUˆˆˆUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUQUXUUUUUUUUUUUUU…UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP…XPˆXUPUPUPUPUPUPU€€XPUPUPUPUPUPˆPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUˆUUUUUUUUUUUUUUUUU…XU…ˆUUUUUUUUUUUUU…ˆXUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUˆUUUUUUUUUUUUUUUUUUUXUUUUUUUUUUUUUUUUUUUˆUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU…ˆUUUUUUUUUUUUUUUUUUUXUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUˆˆˆˆXUPUPUPUPUPUPUPUPUPUPUˆˆPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆPUPUPUPU""""""""""""""""""PUPUPUPU""""""""""""""""""PUPUPUPU""""""""""""""""""PUPUPUPUPUP•YUUUUUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™™™™™™™™™™™™™™™™™™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™ŖŖŖŖŖŖŖŖŖŖŖŖŖŖ™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™ŖŖŖŖŖŖŖŖŖŖŖŖŖŖ™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™Ŗ’’’’’’’’’’’’’’Ŗ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™Ŗ’’’’’’’’’’’’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’ww’’’’’’ww’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’ww’’’’’’ww’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™Ŗ’’www’’’’www’’Ŗ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™Ŗ’’www’’’’www’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’’www’’www’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’’www’’www’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™Ŗ’’’’wwwwww’’’’Ŗ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™Ŗ’’’’wwwwww’’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’’’’ww’w’’’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗ’’’’’ww’w’’’’’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™Ŗ’’’’’w’ww’’’’’Ŗ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUU„UUˆ™Ŗ’’’’’w’ww’’’’’Ŗ™ˆUUZUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUZUˆ™Ŗ’’’’wwwwww’’’’Ŗ™ˆU„UUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUU„Uˆ™Ŗ’’’’wwwwww’’’’Ŗ™ˆUZUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPZPˆ™Ŗ’’’www’’www’’’Ŗ™ˆU UPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUU„UUˆ™Ŗ’’’www’’www’’’Ŗ™ˆUUZUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗˆwwww’’’’wwwwˆŖ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗˆwwww’’’’wwwwˆŖ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™Ŗ’ˆww’’’’’’wwˆ’Ŗ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™Ŗ’ˆww’’’’’’wwˆ’Ŗ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗˆˆˆw’’’’’’wˆˆˆŖ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™Ŗˆˆˆw’’’’’’wˆˆˆŖ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™ˆˆˆ’ˆ’’’’’’ˆ’ˆˆˆ™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™ˆˆˆ’ˆ’’’’’’ˆ’ˆˆˆ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™ˆˆ’’’’’’’’’’’’ˆˆ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™ˆˆ’’’’’’’’’’’’ˆˆ™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™™ŖŖŖŖŖŖŖŖŖŖŖŖŖŖ™™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™™ŖŖŖŖŖŖŖŖŖŖŖŖŖŖ™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™™™™™™™™™™™™™™™™™™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUPUPUPUPUPˆ™™™™™™™™™™™™™™™™™™ˆUPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUP"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UPUPUPUPUP•YUUUUUUUUUUˆ™™™™™™™™™™™™™™™™™™ˆUUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUU"ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"UUUUUUUUUU•YUUUUUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUUUUU•YUUUUUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆUUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUU""""""""""""""""""UUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUP""U"UR""R"UPUPUPUPUPUPUš’_’’Uš’_õ’U’UPõ’_PUPUPUPUPUP’’U’’_õ’U’U_UPUPUPUPUP’_õ’_P’’U’’P’_õ_õP’’UPUPUPUPUP•YUUUUUUUUUUUUUU"R"P""PUUUUUUUUUUUUU’’_U’’_’PU’UUUUUUUUUUU’_’’_’šUUUUUUUUUõšõšUõPõššõ’õš’PUUUUUUUUU•YUUUUUUUUUUUUUU"P"P""U"PUUUUUUUUUUUUU’P’PU’P’P’PUõ’UUUUUUUUUUUU’P’’U’P’_PUUUUUUUUUõõšõPõUõõõš’’P’_UUUUUUUUUU•YUUUUUUUUUUUUUU"P"P"P"PUUUUUUUUUUUUU’P’’U’P’’’PUU’_UUUUUUUUUUU’P’P’P_’šUUUUUUUUUUõõš’_PõUõõõš’Põ’UUUUUUUUUU•YUPUPUPUPUPUPUP"" """R""RUPUPUPUPUPUP’P’_š’_’’’_’’PUPUPUPUPUP’’’’_õ’’P_UPUPUPUPUP’_šPõU’’P’_šõš’PPUPUPUPUP•YUUUUUUUUUUUUUUPUPUUUUUUUUUUUUPPUPPUUUUUUUUUUUPUPPUUUUUUUUUUUUUUUPUUPUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUuUwUwUuUWWuWUUUUUUwUUUUUuUUUUUUUuUUUUUUUUUUWuUUUUUUUUUUUwUUUWUUUUUWUUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUWWWWWWWWWWWUuUuWuWuUuWuWwWUUUUUUWWuUuWuUwUuUUUWWwWUUUUUUWWuUwUwUuWWWUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUwWwWwWWWWWuUUUwUWWuUWWWUuUUUUUUUwWUUWUWWWWwWuUwUwWUUUUUUwUUUWWuWWUwUUUUUUUUUUUUUUUUUUUUUUUUUU•YUPUPUPUPUPUPUPUPUPUPUPUPUWWwUwUWWwWPWpUPWwUpUwUWUpUPUPUPUWWpUWUWWWWpUpUWUWWPUPUPUWWpUWWWWWUwUPUPUPUPUPUPUPUPUPUPUPUPUP•YUUUUUUUUUUUUUUUUUUUUUUUUUWWWWWWuUwWwUUUwUuWwWuWuWUWUUUUUUWWUUuWuUWWuUuUWUWWUUUUUUwUUUwUwWuWWWUUUUUUUUUUUUUUUUUUUUUUUUU•YUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU•™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™,0ŖĪ]4iuŅ(ué%Œ}}}e0’Ęq’ī0}Ā Sä÷q²īźīīī!ł®ŖŖŖś’’’ś÷’św’ś’w÷ś’wś’’wś’’÷ŖŖŖź’’’Æ’÷Æ’w÷Æw’Æw÷’Æ’’Æw’’Æ®ŖŖŖś’’’ś’ś’ń’ś’’śń’śń’ńśńŖŖŖź’’’ÆńÆ’€Æ’ųÆń𯒯šńÆ®ŖŖŖś’’’ś’’’*"""z‚ˆˆz'ˆ€zw‚św'ˆŖŖŖź’’’Æ’’’Æņ’’Æ(’’ƈņ’ƈ(’ƀˆņÆ®ŖŖŖś’’’ś’’’śš’ w’pww wwś’š÷ŖŖŖź’’’Æ’’’Æ’Æ’pw wwww ’Æś’wś’w÷Šww’śx÷’Šˆ÷’ˆųų’ˆ’’’®ŖŖŖw÷’Æw’Æ’wwØ’‡Æ’ˆØ’ˆ’’’ˆŖŖŖźśńśń’ńśń’ś’’ś’ń’ś’ś’’’®ŖŖŖńńÆ’Æń’Æ’’ńÆ’’Æń’Æ’’’ÆŖŖŖźśw‚ś’w'ś’wś’’wś’’ś’’’ś’’’®ŖŖŖˆˆ(Æ"""¢www§www§www§’’’Æ’’’ÆŖŖŖź wwpww w’śš’ś’’’ś’’’ś’’’®ŖŖŖww ww’pw ’Æ’’’Æ’’’Æ’’’ÆŖŖŖź/"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ˆˆˆˆ"ņ’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆˆˆų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ˆˆˆˆ’’’’’’’’’’’’’’’’’’’’’’’’’’’’ˆų’’’’’’’’’’’’’’’’’’’’’’’€ˆ˜‰€™˜‰€™˜‰€™˜‰€™˜‰ˆ€™˜‰™˜‰™˜‰™˜‰ˆˆˆ€ˆˆ˜‰™˜‰€™˜‰€™˜‰€™˜‰€™˜™ˆ™ˆ™˜‰™˜‰™˜‰™˜‰™˜‰™™‰˜˜€ˆ˜‰€™˜‰€™˜‰€™˜‰ ˜‰‰˜ ™˜ ™€™™™™˜‰™˜‰™˜‰š’’ššššššš’’š’’ššššššššš’’š÷’÷’ww÷šwwww÷÷÷’š’šŸł’Ÿł’Ÿ™™łš™™Ÿ™™łŸłŸł’š’‚ˆˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆˆˆˆ€ˆ€ˆ€ˆ€ˆ€ˆ€ˆˆˆˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’€™˜‰€ˆ‰ˆ€™™™€™™™€™™™˜™™˜™™€™™™™™™™™ˆˆ™™™™™™‰™™‰™™™‰€™˜‰€™™‰€™™‰€™™™€™™™€™™™˜™™˜™™ˆˆˆ™™™™™™ˆˆ™™™™™™‰™™‰™™€™˜‰€™˜‰€™‰ˆ€™™™€™™™€™™™˜™™˜™™ˆˆˆ™™™™™™ˆˆ™™™™™™‰™™‰™™‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ/"""ˆˆ€ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’"ņ’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ˆˆˆ’’’’’’’’’’’’’’’’’’’’’’’’’’’’ˆˆˆˆ’’’’’’’’’’’’’’’’’’’’’ˆų’’””””’’’’’’’’’’’’’’’’’’’’’’’’%"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ˆˆˆˆ"RUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU…ˆˆˆø»»»ø»»»ø»»»ø»»»ø»»»ø»»»ø»»»ˆˆˆˆ»»»»»»»»»»»»»»»»»»»»;³»»33»»ˆXUU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UUÕŻŻŻķīīīķīīīķīīīķīīīķīīīķīīīķīīīŻŻŻŻīīīīīīīīīīīīīīīīīīīīŽķīīŻŻīīŻ]UUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUU%"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ™™ˆˆ"RUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU…ˆˆˆ˜ ") "’")"€("ˆ("€ˆ("€€ˆˆˆˆ) ‚‚(((‚‚"ˆ(‚€"€ ˆXUU™‰UU‰UU‰UU‰UU"€UU"€UU"‚UU‚ˆˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆˆˆ€ˆ€ˆ€ˆ€ˆˆˆˆˆ€ˆˆˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUø»»»ø»»;ø»»;ø»»;ø»»;ø»»»ø»»»ø»»»33»»33³»33³»33³»33³»33»»33»»;³»»»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UUķīīīķīīŽķīīŽķīīŽķīīŽķīīīķīīīķīīīŻŻīīŻŻķīŻŻķīŻŻķīŻŻķīŻŻīīŻŻīīŽķīīīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUU‚ˆˆ˜‚ˆ˜ ‚ˆˆˆ‚ˆ˜ ‚ˆˆ˜‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‰ˆŖŖ‰  ˆˆŖŖ‰‰ˆ™™ˆˆˆˆˆˆˆˆˆˆˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU("€ˆ(ˆ(ˆ(ˆ"€("€€("€ˆ("ˆˆ€€€€€ˆˆ€€ ‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU‚UU‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ%"""UUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""UUUUUUUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU"RUUUUUUUUUUø»»»ø»»»ø»»»ø»»»ø»»»…ˆˆˆUUUUUUUU»»»»»»»»»»»»»»»»»»»»ˆˆˆˆUUUUUUUU»‹UU»‹UU»‹UU»‹UU»‹UUˆXUUUUUUUUUUķīīīķīīīķīīīķīīīķīīīÕŻŻŻUUUUUUUUīīīīīīīīīīīīīīīīīīīīŻŻŻŻUUUUUUUUīŽUUīŽUUīŽUUīŽUUīŽUUŻ]UUUUUUUUUU‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ%"""UUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""UUUUUUUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU"RUUUUUUUUUU(""ˆ"""" "˜ ""…ˆˆˆUUUUUUUUˆˆˆ ˆ"ˆ"" """" "ˆˆˆˆUUUUUUUU"‚UU"‚UU"€UU‰UU‰UUˆXUUUUUUUUUU "!   !"!  "" QUUUUUUUUUUUUUU!UU%"""‚˜‰ˆ‚)ˆ("ˆˆ™’ˆ˜ˆ‚ˆ‰ˆ‚˜ˆˆ‚˜ˆ""""ˆˆ’ˆ™ˆˆ‰ˆ‰ˆ’ˆ˜ˆˆ€‰ˆ€ ˆ˜ˆ"RUUˆ(UUˆ(UUˆ(UU‰(UU‰(UU‰(UU’(UU…ˆˆˆ("""("""("""("""(""ņ(""’("ņ’ˆˆˆˆ""""""""""""""""’ "’"𒠈XUU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU" !   "" """"""""""  UU UUUUUUUUUUUUUU‚‚ˆ‚‰ˆ ‚ˆ˜‚ˆ˜‚ˆ˜ˆ"ˆ(‰"ˆˆ "ˆˆ˜˜ˆ ˆ€€€ˆˆˆ˜€€)‚˜ˆ˜(UU˜(UU)(UU‰(UU‰(UUˆ(UUˆ(UUˆ(UU("’("’’("’’("’’("’’("’’("ņ’(""’’š’š’š ""‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU!   UUUUUUUU"  UUUUUUUUUUUUUUUU UUQUUUUUUUUUU"‰ˆˆ‚‰ˆ‰‚’ˆ˜‚˜‰ˆ‚)˜‚%"""UUUUUUUU™ˆ‰ˆˆˆˆ˜‚ˆˆ‰ˆ˜ˆˆˆˆˆ˜""""UUUUUUUU‚(UUˆ(UUˆ)UUˆ"UU˜(UU"RUUUUUUUUUU(""ņ("""("""("""("""…ˆˆˆUUUUUUUU’ """""""""""""""""ˆˆˆˆUUUUUUUU"‚UU"‚UU"‚UU"‚UU"‚UUˆXUUUUUUUUUUuUUUWWUUuuUUUWWUUuuUUUWWUUuuUUUWUUUUUUUUUUUUUUUuUUUWUUuuPWWPuUUWUUuuUUWWUUuUUUWUUUUUUUUUUUUUUUššš’š÷’šw’’’p÷’pw÷÷w’wš’’w÷’’wš’÷wš÷÷’’w’’ww÷’wųwwxwwwwwwwpwšw’ww’Pš’’’w’w’w’UUUuUUUwUUUwUUUwUUUwUUUwUUUwUUuuwwWUwwwUpwUpwUpwUwwwUwwwUUUWWUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUww’w÷’pww÷wwwwpwwwww’’w’÷w÷÷wš’w’’w’’’p’’p’pww÷’w÷w’xww’’p’’÷’÷÷÷’wp÷wpšww÷’wwwpww÷wwwww’pwUUWWUuuUUWWUuuUUWWUUuUUUUUUUUUUUUUuuUUUWUUUuUUUUUUUUUUUUUUUUUUUUUUUUWUUUuUUUWWUUuuUUUWUUUUUUUUUU’’’’’’š÷ššš÷pwšš’’xw’÷w’€p((pˆ Ŗxš¢šżˆ‚"ˆˆw÷÷w’ ’‚‚’Ŗ ˆ*ˆßˆˆ‚(’÷’’÷÷÷w÷wpšw’wšw€ˆ€˜˜‰€ ˜€ ˜˜€ €ˆ‰˜‰‰‰‰€€˜˜™€™™‰™‰™™ˆ€™€ˆˆ‰Ø˜™™€™™ˆ‰‰ˆˆ™™Ø˜™‰’wwpwwpww’wppwŻ "" ""§""w "§pˆ( š õPõÕŻ "r "p""z"wp‚w pWŻWwww’wpwww ˜˜©€ˆˆ€ ˜ €˜ € š‰‰ˆˆ‰€‰   €™ˆ˜‰Ŗ˜™ €‰Ŗ€™ˆ˜™€˜€ˆ™Ŗ˜‰ ™‰Ŗ˜ˆ™™‰‰€™ˆŖ˜™ €‰Ŗ€™ˆ˜™€‰Ø€Š˜‰š™Љ˜‰™‰"žDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADADHDADHDHDDDDDDDDDDADDADDDDDDDDDDDDDDDDDDDDDAAADHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDDDD„HADAAAAAAADADDˆHADDDDDDDDDHDHDˆHDDDDDDDADDDADDDADDDADDDDDDDHDDD”ŖADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDˆˆDDDDADDDDHDHDHDHDˆHDDDDDDDDDADADHDHDDDDD DDDDAwwwŖwwŖzw ’ŖŖwwwADDDDDDDDDDDDDDDDDDDDDDDAD””Ŗ”Ŗ” Ŗw pw”pwŖp p  ’ w÷  p÷  š w’ˆ’ŖŖŖŖŖŖ””w  ’ ’ ’  wwwwwwwšššŖ ŖwŖŠ’z÷’ Ŗ’ ’” ” ” ”””ŖŖww’wwwww’DD€DDADDADDHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDADDADDˆDDDDDDDDADDADAHˆDHHDHHDDADHDŖś ˆ AAA’św w  w Ŗ Ŗ ŖšššššD”” pw p ššššššwšp÷pwwzwzwzz ’ ’ ’ ’w ’w ’ ’ ’šššššššš ’ ’ ’ ’ ’ w÷ ww w§Ŗ”””’zŖ’’’ ’€ ’ ’ w wˆˆˆˆDDDDDDDDADDADDDDDDDDˆDDDDDDDDDDDDDDDDD„ˆDDDDDDDDDD„DDDDDDDAADDDDDDDDD  Ŗ Ŗ Ŗ Ŗ   śšššwšśwšśšš €”€”wDš’šw šw”’””ŖD” pppzzzzzzz ’ w w ” ” H”HŠšpwpw   w  w w w w w wØØwØwØØŠØŠŖŖ w   š’š’DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDˆˆ„HDDDDDDDDDDˆˆˆˆDDDDDDDDDDDDˆšˆˆDDDDDˆDDD€DADADDDADDˆ”D„”ØŖˆˆHDHDDD  ˆˆˆHDDDDDDDDDDDˆHDDˆDDDDDDDDDDDDD„D„D„D„DD„DD„ Š ”ŖˆˆHDDDDDD € ”ˆˆˆˆDDDDDDDDˆˆˆDDDDAADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD„DDDDDDDDDDDDDDDDDDDDDDDDDDDDDˆD„DDˆˆDDDDDDDDDDDDˆˆˆHDDDDDDDDDDDDDHDˆˆDDDDDDDDDDDDDDDDDDDDDDDDDDDHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD„DD„DDDDDDDDDDDDDDDDDDDDDDHHDHDˆˆDDDDDDDDDDDDDD„HD„ˆDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDˆDDDDDDDDDDDDDDDDDDDDDDDDDDDD„ˆHDDˆDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD````ŖØ €Ŗ  `     Ŗ   Ŗ Ŗ z`zz`z`zfx—xŖ x šŖ`f`§ § §ŠP©ŖŖŖŖŖØŖŠ ŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖ ŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖ ŖŖŖŖŠŖŖŖŖ ŖŖ € """"""" """"""""""" ’"""""’""" """™’™™˜ ššš’š’’p’’÷’’’š’’÷’’’’÷pšł’’’’’÷’’’’’’Ÿ’w‡ `pؐ‡ Pp wpŖ§ Ŗzzzˆ©ŖpŖŖŖ€§  pwŖ pw§zŖ§Ŗ‡™Ŗz€™Ŗ€˜™x§Ŗ xŖŖ x Ø §pØ  Ø ™yØ ™yØŖ™‰§™©z‰`  `fŖ©ŖŖŖ€ Ŗz ‡ŖŖŖŖŖŖŖŖŖ ŖŖŖ ŖŖŖ ŖØŖŖŠŖŖŖŠˆŠŖŖŖŖŖŖŖØŖØŖˆŖØŖŠŠˆŠŠŠŠŠØŠŠØˆŠŠˆˆŖŖŖŖŖŖŖŖŖˆŖŖØŠØŖˆØˆØˆˆˆˆˆˆˆˆ(€ŖŖŖŖŖŖŖŖŖˆŖŖŠˆˆ ؈ˆØØˆˆˆˆŠˆ("€"€"ˆ"€ˆ(€(€ˆ"")’""""(""(((""((("ˆ€‚"ˆ(("‚ ‚’)™™"""‚""(‚""((""((""ˆ(("ˆ(("(()"" ((ˆ(˜‚‚÷’š’÷÷’÷÷š’’’÷’’’š’’’’ł’’’÷÷’’’’’w÷‡’÷’÷’÷’’’’’’’÷’’w÷÷š÷’ųš’www’y÷—w’ww § § §f§f§`§§ wŸ™öŸö˜oö™™™™™™™™™™™™™p™oö ``’™šf™™™™ł™™y—™™px ųpØųx`fxf`f ŠˆŠ ŠˆŠ ŠˆŠ ŠˆŠŠˆˆ ˆˆˆ ˆŠØˆˆŠØˆˆŠˆŠų™ˆŠŸ™ˆˆ™ˆØ™ ˆˆ™ˆų‚ų‚(’™"˜™™)™™™)™™™)™™™™™"Ŗˆˆ"€ˆˆ"ˆŠ€ˆŠ  ˆˆ"ˆˆ"€"„€€’’ ˆ€€ˆ O€ ™’‰ ™™"ł’™p’™ˆˆˆ ˆˆˆō)’™™™’™™ ™ ™™‰ˆ‰ šp’’’÷’’’š÷÷pš÷ł’ų'’’'’‡"'ņ ļŻ(xų’ (""‡ ""(÷šw"ņųx"x"Šp"Ųķ/"š’x"""ˆ"""Ø÷’÷w’w÷÷w÷p÷prww zwšš™y Ÿ™ ų™ `‰ w’’WpWw™y™™™ fy€`pp÷pwwPuwUUpUUwff÷W__P €™ DDDDD@@DD™™) ™™ "”™"@D(DD"DD"DD)D’’DD¤DDDDD DDDDDDDDDD@DDODDDDšwppwp𒐠w’™÷™÷ ÷w÷ww𒐙€™ ÷™ x÷™p÷pwpwšpw’p÷’’’’÷wš’p’špŸąīīī§ ""Ŗ""§ "pw€"py§)šp§™Ż ™Ż ™(""§""‚§ "p""pp™ ~™ Ż™ ŻŽś÷ššŻ (" """"("""(""‚"""ˆ"" """""(""ˆ"""‚"""‚(""šš’š’šwwwwwwWšww÷www’ww’’ww÷’w÷wwwwwwwP÷÷÷÷pwpwpw Ŗś ©ŖšŖŖŖšŖŖŖŖśśśśŖŖŖŖ©Ŗš šŖ™šŖšš™śłłłššš™™©™ pwPwwwPpwwwwwwwwuwwWuwwwwwpwwwwwwwwPwwpuwwwUwwwWUˆ"€ˆ"ˆˆ"ˆˆ(€ˆˆˆˆUˆ€ˆXˆ€ˆX…"ˆ""ˆˆ‚"ˆˆ((ˆˆ((ˆˆˆˆˆˆ…ˆˆX…ˆXU˜ˆ"‚(""ˆˆ""ˆˆ"(ˆˆ"ˆˆˆˆˆˆˆˆXXUˆˆYUX‚‚‚"ˆ‚ˆˆˆˆˆˆˆˆˆUUXUU’UUuUUõwUUwwwwšwwwwwwUwwUõwWUwww÷wwwwwwwwwwwwwwwwwwwwwWwwwUwwwwwwwwwwwwwwwwwuwwwWwwWWUwWUUuwUuwUu÷UuWUu_uUUUwUUpwŖ ŖŖŖŖŖ ŖŖŖŖŖ ŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖŖšŠŖŖŠˆØŖ("‰Ŗų/Ŗ˜™'š©©©ššš™ˆ©©™ˆšš©‚©©™‚’"˜"_š˜™’™˜ł ™™™™™™ ™™™™™™ ™™™ ™™™pWpuWPpUUwUpUpUUpUUWwwwuwwwuwwuWUwUUUWPUUWUUU UPW wwwWwwwWWwwwWwUuuUUuUU"UPU"UUUPuUPUWUWuUWUWUW€XU…U…U……€€€˜ˆų‰˜ų‰˜łŸ™X™™ X™™™X™™™XU™™U÷ˆUłˆŸU™’™U™™™U™™)X™™YX™™UXXUUUU…UUUUUUUUUwWõwwWwWuwWõwWõwWUwuUUpUuwWUwWUUwUU%wU "wU w%w%ł’W%™™÷wUUUwU"RW%"RW"" U)%/"ł’)’™™™UUwUUuUURUU"UU"U"Uw’uWUUUU Ŗ  ą ąķīķŽķŽīŖ˜™"¢˜™)("™)‚‚™) ’™™p—™Żp—™é'™™™™™ˆ™™™(™™)")™"’’™"‚Ż"wī™"w™™ē˜™’’ ©ź ŖŚŖŚŽīķŽ pUUpUUpUUpUUpUpuUpuUPWU% U %%p( š"'™™™™™)W™™™W—™™"%U"€‚p""šY™™R™™R’™™P™™™UUWUUUUPuUuuWuuWUuWUPųQU’™Q!’Q!"Q!)™™€™’™YU)RQ"""""ų’’’’ˆpUppUšpPWššwwwU™™™U•™™PU™)š_•™ww'™ww'"wwׯwwׯ’™™R™™)U"™RU™)U™"ww)"wwŻŻwwŻŻwwUU’w÷÷ķŽīŠīīķŠīīķąīīŠīŽķŽŠŽ wē’™ē"™wē"’īī)"īī)"īī)"īī)"ķī)"™)éw™"ā™"āw""āī""āī")āŽ")āŽ’"āķēķŽ ēķŽŖ×īŽŖŽīī­ķīŽŖķī­ŖīŻŖŖŽÆŖŖPPWUWWuWDw@EDDDD’ODD’’@’w•™™p™™uu’™uu"™õ’"U…’"õww’ww™™ U™™U™)ru™'ņu ’ņP"’‡w‡’wˆˆ’UWUUUUUUDTEU’’Eõ’šDõODEDL0ŖĪ]4iuŅ(ué%Œ}}}e0’Ęq’ī0}Ā Sä÷q²īźīīīA`ššššššššpww ŖŖŖŖŖ Ŗ  ŖŖ Ŗ Ŗ ŖŖŖ  Ŗ      @DD@@ĄDD Ą ĄĄĄĄĄDDD@Ģ D Ą@Ą@™™  ™™™™™™»» ° °»»»»»»ff``ffffffššššššš’ššššššššpšpp‘‘šššˆ€€ˆšw€x€€ Ŗ  ŖŖŖ ŖŖŖ ŖŖ ŖŖ Ŗ  Ŗ ŖŖŖ ĄĄ DĢ@ĄĢD@D@DĄ@ĢD Ģ@D@D@DĄĄĄĄ Ą Ą Ą šš’’’’’’’wwp’’’’’’/"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ˆˆˆˆ"ņ’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆˆˆų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ˆˆˆˆ’’’’’’’’’’’’’’’’’’’’’’’’’’’’ˆų’’’’’’’’’’’’’’’’’’’’’’’€ˆ˜‰€™˜‰€™˜‰€™˜‰€™˜‰ˆ€™˜‰™˜‰™˜‰™˜‰ˆˆˆ€ˆˆ˜‰™˜‰€™˜‰€™˜‰€™˜‰€™˜™ˆ™ˆ™˜‰™˜‰™˜‰™˜‰™˜‰™™‰˜˜€ˆ˜‰€™˜‰€™˜‰€™˜‰ ˜‰‰˜ ‰˜ ‰€™™€™™˜‰™˜‰™˜‰ˆˆ€ŖŖؚ©ŠØ¢šŠØ¢šŠØ*¢Š€ŖŖˆˆˆˆ€ŖŖؙ™ŠØ""Š€ŖŖˆˆ€ˆˆؙ™ŠØ""Š€ˆˆˆˆ€ŖŖØ""ŠØ™™Š€ŖŖˆˆ‚ˆˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆˆˆ€ˆ€ˆ€ˆ€ˆˆˆˆˆ€ˆˆˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’ų’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’€™˜‰€ˆ‰ˆ€™™™€™™™€™™™˜™™˜™™€™™™™™™™™ˆˆ™™™™™™‰™™‰™™™‰€™˜‰€™™‰€™™‰€™™™€™™™€™™™˜™™˜™™ˆˆˆ™™™™™™ˆˆ™™™™™™‰™™‰™™€™˜‰€™˜‰€™‰ˆ€™™™€™™™€™™™˜™™˜™™ˆˆˆ™™™™™™ˆˆ™™™™™™‰™™‰™™ˆˆ€ŖŖØ¢*ŠØŖŖŠØ""ŠØ¢*Š€ŖŖˆˆˆˆ€ŖŖØ¢*ŠØŖŖŠØ¢*ŠØ*¢Š€ŖŖˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ/"""’’’’’’’’ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""’’’’’’’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’ˆ(’’"ņ’’’’’’’’’’ų’’’ų’’’ų’’’ų’’’ų’’’ˆˆˆ’’’’’’’’’’’’’’’’’’’’’’’’’’’’ˆˆˆˆ’’’’’’’’’’’’’’’’’’’’’’’ˆų’’’’’’’’’’’’’’’’’’’’’’’š’’š’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’š’’’’’’’’’’’’’’’’š’’š’’’’’’’’š’’’’’’’’’’š’’’’’’’’’’’’’’’’’’š’’’’’’’’’’š’’’’’š’’š’’’’’’’š’’’’’’’’šš’’’’’’’’’’’’’’’’šš’’’’””””ŻŻŠnf ķ’oÖŻļöÖŻßžŽŻ’ļŻŠŻŻ ŻŻˆˆ€(‚ˆņ/ˆ(ņˆˆ(ņˆ(ųˆˆ€ųˆˆ!ņ/!ņ!ņ!ńń"" ™™’ł™)’ł™)"ł™)"ņ’" ņ/""UUPÄĢõÄü\õõü\õõōTU_OUPUUUU330ĖĢóĖü<óóü<óóū;3?æ303333UUPETUõ’TU_DTEōOTUEōTP’OUUŻŻŠff mö’ŻmfoŽmf߯mf߯Šķß ŻŻ"" ™™’’Ÿ)"Ÿł)"/ł)"’Ÿ" """"’’ššš’šš’šššš’’’’špšš÷š÷pšš’’’’ššpšpš’w÷šwššp’’’’šppš’’’’w÷pššp’’’’šppššw÷wššš’’’’šp÷špš÷špšš÷’’’’šš÷šw’špš÷ššp’’%"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ€ˆˆˆˆ"RUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU…ˆˆˆø»»»ø»»»ø»»»ø»»»ø»»»ø»»»ø»»»ˆˆˆˆ»»»»»»»»»»»»»»»»»»»»;³»»33»»ˆXUU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UUÕŻŻŻķīīīķīīīķīīīķīīīķīīīķīīīķīīīŻŻŻŻīīīīīīīīīīīīīīīīīīīīŽķīīŻŻīīŻ]UUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUU%"""‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ""""ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ™™ˆˆ"RUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU…ˆˆˆ˜ ") "’")"€("ˆ("€ˆ("€€ˆˆˆˆ) ‚‚(((‚‚"ˆ(‚€"€ ˆXUU™‰UU‰UU‰UU‰UU"€UU"€UU"‚UU‚ˆˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆˆˆ€ˆ€ˆ€ˆ€ˆˆˆˆˆ€ˆˆˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUø»»»ø»»;ø»»;ø»»;ø»»;ø»»»ø»»»ø»»»33»»33³»33³»33³»33³»33»»33»»;³»»»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UU»‹UUķīīīķīīŽķīīŽķīīŽķīīŽķīīīķīīīķīīīŻŻīīŻŻķīŻŻķīŻŻķīŻŻķīŻŻīīŻŻīīŽķīīīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUUīŽUU‚ˆˆ˜‚ˆ˜ ‚ˆˆˆ‚ˆ˜ ‚ˆˆ˜‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‰ˆŖŖ‰  ˆˆŖŖ‰‰ˆ™™ˆˆˆˆˆˆˆˆˆˆˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU("€ˆ(ˆ(ˆ(ˆ"€("€€("€ˆ("ˆˆ€€€€€ˆˆ€€ ‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU‚UU‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ%"""UUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""UUUUUUUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU"RUUUUUUUUUUø»»»ø»»»ø»»»ø»»»ø»»»…ˆˆˆUUUUUUUU»»»»»»»»»»»»»»»»»»»»ˆˆˆˆUUUUUUUU»‹UU»‹UU»‹UU»‹UU»‹UUˆXUUUUUUUUUUķīīīķīīīķīīīķīīīķīīīÕŻŻŻUUUUUUUUīīīīīīīīīīīīīīīīīīīīŻŻŻŻUUUUUUUUīŽUUīŽUUīŽUUīŽUUīŽUUŻ]UUUUUUUUUU‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ‚ˆˆˆ%"""UUUUUUUUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ""""UUUUUUUUˆ(UUˆ(UUˆ(UUˆ(UUˆ(UU"RUUUUUUUUUU(""ˆ"""" "˜ ""…ˆˆˆUUUUUUUUˆˆˆ ˆ"ˆ"" """" "ˆˆˆˆUUUUUUUU"‚UU"‚UU"€UU‰UU‰UUˆXUUUUUUUUUU "!   !"!  "" QUUUUUUUUUUUUUU!UU%"""‚˜‰ˆ‚)ˆ("ˆˆ™’ˆ˜ˆ‚ˆ‰ˆ‚˜ˆˆ‚˜ˆ""""ˆˆ’ˆ™ˆˆ‰ˆ‰ˆ’ˆ˜ˆˆ€‰ˆ€ ˆ˜ˆ"RUUˆ(UUˆ(UUˆ(UU‰(UU‰(UU‰(UU’(UU…ˆˆˆ("""("""("""("""(""ņ(""’("ņ’ˆˆˆˆ""""""""""""""""’ "’"𒠈XUU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU" !   "" """"""""""  UU UUUUUUUUUUUUUU‚‚ˆ‚‰ˆ ‚ˆ˜‚ˆ˜‚ˆ˜ˆ"ˆ(‰"ˆˆ "ˆˆ˜˜ˆ ˆ€€€ˆˆˆ˜€€)‚˜ˆ˜(UU˜(UU)(UU‰(UU‰(UUˆ(UUˆ(UUˆ(UU("’("’’("’’("’’("’’("’’("ņ’(""’’š’š’š ""‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU"‚UU!   UUUUUUUU"  UUUUUUUUUUUUUUUU UUQUUUUUUUUUU"‰ˆˆ‚‰ˆ‰‚’ˆ˜‚˜‰ˆ‚)˜‚%"""UUUUUUUU™ˆ‰ˆˆˆˆ˜‚ˆˆ‰ˆ˜ˆˆˆˆˆ˜""""UUUUUUUU‚(UUˆ(UUˆ)UUˆ"UU˜(UU"RUUUUUUUUUU(""ņ("""("""("""("""…ˆˆˆUUUUUUUU’ """""""""""""""""ˆˆˆˆUUUUUUUU"‚UU"‚UU"‚UU"‚UU"‚UUˆXUUUUUUUUUUuUUUWWUUuuUUUWWUUuuUUUWWUUuuUUUWUUUUUUUUUUUUUUUuUUUWUUuuPWWPuUUWUUuuUUWWUUuUUUWUUUUUUUUUUUUUUUUUUuUUUwUUUwUUUwUUUwUUUwUUUwUUuuwwWUwwwUpwUpwUpwUwwwUwwwUUUWWUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUWWUuuUUWWUuuUUWWUUuUUUUUUUUUUUUUuuUUUWUUUuUUUUUUUUUUUUUUUUUUUUUUUUWUUUuUUUWWUUuuUUUWUUUUUUUUUUl0ŖĪ]4iuŅ(ué%Œ}}}e0’Ęq’ī0}Ā Sä÷q²īźīīīa wwwp’’’xwwwxŸ™łxŸ™łpww÷’’÷’’www÷÷™™÷™™www‡’’’’wŖw§’w’’Ŗ’?ńÆ9‘Ŗ÷ŸŸŖń’’z’’÷śł’x’’ó÷Ÿ’£wšś’’’UUU•UUTyUTD”DDD™UD—ŠUD©Ŗō˜‰Ŗ•˜Ÿ™)"—!)!™xŖy'"ŖŸ'"™ųÆ"©’™ÕŽŽŽķķˆ€w€ˆ’‡ˆ‚’ˆˆ(’ˆˆˆ’ˆų‡’ˆ€ŽŽÕˆˆą]ˆˆ~‚‚ˆ÷(ˆˆ’ˆˆˆ’ų‡ˆųˆšEDUõUETōUUÄōUUÄTUDUEETUUwUUwUU’’w’ww÷÷’÷U’pTUpEE §UDp³33+³3³2;³+²;³"";»" ++"²" "²"+333333³3»+»3"²Ż»";ķ=»»Ū="»Ż=²»ķ½ˆ’’Ŗ‚”Ɓ"”Ŗˆ”Ŗ”Ŗ‚”Ŗ"‚"ŖŠØŖˆˆŖŠˆŖŠŖˆŖ""ˆwwwˆ˜ˆˆˆ™)ˆ˜™™"™™’™™’™™’™™’(™’™ˆˆˆˆˆxwwˆ™÷ˆw™w‚w÷)ˆˆ™™‚(™™"˜™”Ŗ1ƚ÷Ÿšwńłwńwńwńqńq‘’’‘’qń’ńńńńń’ńņń”˜I™”™D˜ō™”wDO˜yD3™˜33Ÿ˜3³™˜3»™ų("Ÿ()"’)'"ņ)™"ņŸ""Ÿ ™Ÿ™™0ˆˆ³ƒˆß©‰ˆķˆÕŽ€ķķÕµ]Y]ˆˆ³šˆˆ»ūˆˆ©Ł€PŽķ]ƒŽÕķ›Z]zUUwUW §UwwPw P÷zPwwPwPwu w§pwzpw pww§p §wpw§#+"+3w²»{w’²w"+?ó+‚ū33ø»»‹»33ƒˆ»Ūķ=»Ūī=‹ˆˆøø»»8KB‹3"“ø;»‹;3ˆ8»3"”ŖŖ”ŖØ”Ŗ"”Ŗ"”Ŗ"ØŖˆ‚ŖŖŖˆˆŖ!""ŖˆˆŖ"ˆ”!"”Ŗ!"!Ŗ!!ØŖ!ˆDōDDDDODODDDōDOōDDpDšDļŠDßDDDDDDAŃŻķēīž’ßŻDōķīD’ąNōOUUUUUU’’U’U’ššU’šU’ššU’U’’’UUUU_UUU_UUU’UUU’UUU’UUU’UUUĻUUU3³»»3Ū@³» M»»}»°» Ż» »Ū Ż»»;3»»»3Ä»»;»»»·»»°»»»°ŠĶ°ˆ˜šéŁš™’€™ˆ˜ˆ˜ˆ‰žžų™ų™’‰ˆ‰ˆ<š33£¢3™#Ŗ#’“šJ$#ŖJ¢£ŖŖ*£”*"£Ŗ"33":Ć4©:<„#Ŗ3Ŗ:Ŗ:©I¢:ŖJ$2%:#:"2#:ūw÷»ūw÷ ’’w“’’y’—’’Ÿ™’÷y™—yšwæ°w÷9sw’—q’’™÷™™7÷™—9s‰w3÷{·»{» z§»»pŖ»»w*»{'ƒ»"{"( {‚·»»»«»»»ŗ«ŗ»šŖŗ·3šz» šz»’’» zæˆ"""" š(õš‚(’u""w"UPU UPq""qW"ˆšU "õ’""wpš/ņqńPšŃķNDįō’’’ßDDDAODDDõ’Dõ’DD_@DU’DDō’NDODADōDDDōOUU’’UUÅĢUUÅUUUUUU‡ˆWW Ģ\UUĢ\Ģ Ģ™UUW™ ĖŠ ° » »»Ū°»³°»» »»»3³»»ĶŠ»ĄĄ» ° °»°»°»°»½Ū»» »»Ó»»;3 "™2#’“™#“™Y3“™33“033™03™9™))2#2™9”™9™93™33“933™93£"ŖC3Ŗ:£3:3D<3£šĆ“Ŗ23S*3ĆŖ%ĆŻŻ>ŻŻ>ÖŻķwff6qff3fmÖŻcŻŻŻćÖŻŻć`ŻŻć`ŻŻwŽmŻÓfm3cffPwŻUpwŅpW p9pPÓ"PÓWpp’30÷w w/ w/ w/3;33;3Ó33Ó3 Ż£­Ż Óڐ ŻAŻō33;38Ó3;Ż33”Š=³ŖŠ3 ™Š=DŻŠōЈ"‚"ˆ""(""ˆ"r‡ˆ"‚ˆ0((°"DDOw’’’ ’ō’‰DDD)DOD ōDD D44 D43ÕUU[ÕUU•Ż]ŻIŻŻŻŻŻŻŻŻ™Ż›™Ż›ŪŻ•µ»Ż•Y»Ż‘•ŁŻ¹»ŻŻ½™ŁŻ™™Ł™ŻŻŻłŻŻŻDD™DDō™DD”YDD—•DDXUDDPUDUUDTUE•UUEUUUDUUDDUDDUYUDUUUE™UUUDTUUwpUUŠ݈€€€€ˆˆ…€ˆˆ€ˆ‡xwwpqˆpˆ€xˆˆˆ€ˆˆ€ˆˆńTDBĮ\ "Ä "ĢP\Š ĢŻ Ä ÅAĢĢĢBBBE$DHŻDfLmALFŃLf†DFmf¦>Żf6 Żfv£mf:§m`¢ 7w7*7s3 w3w ćjff cfÖ:gfŻz£fŻ *Ż¢sws 37s w3w " )Ł"03U0030ŠŻ==Ó30S2= "r?3)r_5)ņÓ3Ó30 D3? 33”33Ŗ3=­:;ŻŻ03Š 3³333A3Ŗš?Ŗó3”33£Ó³ŻŻ33ŻŻ;;3;3°ˆ° ˆ ø D33DDHōO„ō/DŌ-Aōż!Ņ-Ņ-UUUÕUUU}UUUUUßUŠ}UżŻU wÕßw]UUU]UÕwU}wpwwŻpwpŻwwwwp×UUUUåå īU^UUµīµwšU{w§µwpww—PUUU°^U^ PåU™UUU w[Uzw·Uwp[ywąggwpvw`g gw wgŖ wg/wwwwwwvww`vfw`wv`ŻUUŖ]DTŠUDT˜UĄˆ…ˆˆ™…3 ˆ…3 BU8ŖUUŻØՉUˆU™ˆˆXˆ¢Z$ (Z"ˆUUUUUUUU„UUUśUUU ’’_Ÿ’™ÆÆuył’ł’§ŖUUUUZUÅ\’U\Å U\Å©UÅ\Ł_ŖŖŻł„ZŖ§UZ Ż ŻŠ ĶĢŻŻü’ Ķw üĒÅŻüĢĢĶ| uĄĻšÜ ŻŻŻĶŠõĶŻĒ\Ļ ĢĢĢÜWÅõÜÜ]wŻĄuݰ [ [µ¹°[U—›™uy°™Y™°  °µ›[µyUµ —W™¹™•™ ęęęęnnī’ęęīĻnnīŸęīīžnīņęęžīīššnnn’īęļnnnˆ€ā€ˆāˆ!ÕppŻw ŻpwŻ}wwwpqwwqw pŻp ŁŻŻ  ŠŃŻŻŃŻŻŠŻŻµw"™Uwp U[pw^^pķUUpwåUp}^U×wUUww™"w[×w[wµUŽ^^wUå×uUw}wWwwU^wgšpggwwpwpwwgf`v vŖw`w`vffwU…ˆU؈˜™ ˜™ Æ…™@$… * ¢"‚XU €UJ €ˆŖ ™‰ *™‰™Xw§X€Ųõ’ ÷_’Ÿyõ_ ÆUõłÆUU’Uõ’÷Uś’U  ’’ś_Z÷ÆU’šÆUśśÆU Z„U÷’UU’’UU’ÆZUĶĢ\ߥüÜĢĄŻü|ĶŻŒĻŻ}|ųŻĄĢUŠĢÜ}ż’ ĻŻ|WŻĢuüßĒWĢ\ĢŻ U߯ŻuüŻwÕŻ Ģ’ŠŠ°˜˜‰[ˆˆ)ۈˆ‹ˆ… ÕUX™ŠÕUÕՅ—]Õ™˜‰‰µ’ˆˆµ ˆˆøˆX™Õ…U ÕUPyŲU ™€]]īą!īįžšęīnžę’ń’ń’ą’’ļšąīęšnn3333³;33[“33Pµ3»UE;»UU»[UU[UUUU3333»3“3»»_;ū [»õ[µUU[UU™KUY@UUUUUUUUõūUµ°U»“ūU› ¹UUU¹UUعUUUU_UUUUUUUPšUUPšP’UP[˜r)‚(""‚")"‚Ļ’"‚Ļ™‚ˆ"((™"÷÷÷÷'÷÷‰"‚(’’"(")ü("™ü("‚"ˆ("™‚EEEDTDDDEDDODD™’E”ł’D”’÷D”’’DDł’EEEEDDTTtDDE™DT’—ID÷IT’ID’—DT!"/"!ņ/!ńw/ńw"ń÷!ń÷ńw/"""!"ń!!ņw/÷!ņw/’÷""3303€3033;33ŗ0 ƒ00°3 0000030³3ƒ3«03°330 3°ƒUUUŻUUŻżUŻżóUŻ3óÕß3?Õß3Żļ>“ŻžīśŻUUUżŻUUžÓŻUóŻUł3Ž]3Ž]9ļ’Ż©š3ßÄDDDōDüĢDD’wGtwwwww³;»;;»;»»»»»DĢÄĢ’LDD÷ODDwGtGwwww»³;;»»»»³;;»TUUSU™³U•U»»YU³›»T3¹;C33333333™ŸUłŸ UłŸ Uµ U“[ µ“»“³ 03333Uõ U_ūUš šUŖšæ5Ŗ_õ:£UõŖ„Z…õU_»’æ°[’±[;[»°P»°0»[;0»µ PµS± ;S("o("(fwˆ""˜ˆ'"‚™/ņ/ų'’/˜""ųö"‚f‚"‚"ˆ÷wˆ‰"÷™("rņ/ņ‰ņ)r""ųDDä’D”īīD™īķDéīīE™žīT”éīEDäīTTDDMDDīŽIDŽī™DīīDīé™DīžITīMDEDTTTw/’ ÷w/ńwš’w÷pw"ńw!’’w""÷!"ppš'w""w""r 333³0ĢĢÄĢĄLLĢ€ĢĢLĢĢĀĢĢĄ;3003330ĢÄĢĢLLĢ@ĢĢ ÄĢĢĢĢĢŻ> Żļ ՝ī£Õż’ļUżóļUõóķU3߯U;UŻ śŖß©š3ß>ī’]ī?ß]ī3UŽ?SUŻ03UŻU³U³³»»»»»³»»»»»³»»»»»»;»;»3;333333»»»;»;»»³»»‹»‹ˆ8»;3³»³»»33;33333w™)w™)w™)w™)w˜)p™)w™‰w™""’w""’""’"" """y¢*y*’y"yUUUUUUUU™UU™UUwUU™ŻŻw]]™UUUUPUUU PUU™ UU™ UUy UU™ ÕÕy ]]™™))’""™")))"ņ.™ņļī™łon_’ž’’™U"™)™’™)™"/™’’’™’UUU™EPU•UUP•UUUP °°D »»°DKD“LD“DDKÄKDĖÄ °D »K»D“L“DDLKLD“LLD» °[Uµ"[U«³U š9» ™ š"µ !» "µµ[Uµ»UUµ²UU›[U[²U»»³ »" ³U‰9U»""""ˆ""ˆ˜)"ˆ33#333333»»3»»»3»·»"""ˆ""€’"€"" €™""+""»+°»» ąīč™ Ž™™™š™™™ń—™ś™”zŽīīŽčīčˆčīŽˆ€īžč™Ž™łī™ń™™DUˆUaELPĢVPüEōĢ’L’Ģ’’Å’’ĘüĢĢĘ’ĢTüφU’ fEĻD`X’_V’D`w™™pw—™w‡™wˆwˆˆˆwwˆˆww€pww""’y""‚y ’ ˆˆ™wˆˆ™wˆˆ™pˆˆ˜ˆˆxÕՐ—ŻŻ™ŻŻ—Ņݐ™¢Ŗ™"*™""™ˆˆˆˆ— UUy ŻŻ™©ŻŻ™wׯ™w'"™w""™y‡ˆˆˆˆ’U™‘Q)""’")ņ’™™ł"™"™)™"’"UEUUUUU•E™TPš™’’/"’’)’’)™™™)’™»DKD@»ĖÄD»D“DK°@K °° °DDD»ÄD»D“DDKD“D °° ° *¢  +" €ŗK¤ UDUŗ¤DUµEDUŗDEŖU8[UU‚[UU"³[µ(2»» (‚ŗ€€KU»ŖUUKET3²w"+/‚’÷’‚’÷’÷ww’÷ww÷wpwpw»ˆ3‚ˆ3/ /3š’pwš’pw÷’wwwwŖ”‘™™™™™™™™™33z™””Ŗ”‘™™™™™™™™™™™U’’Aō’dEĻDÅeaõfĮDUÅüEDÅü’_`’LĻEaAĻA’laQĻlQĻQüLDUPPPPPõPUPPwWPPPPĒuĻpUU%ņUU%śUU%śRUUņUUśRUņ"UUõUUõ/UU/U%UZU%UZU%UZUU"UUR*UU"U%UUUUUUUUUUUUUUUEEDDDDD$DĢĢ ĢĢ̜_UUUXõUU"UD"HÄ"̜"œ™’™ €PPUPˆr ¢‚)"URUU"‚UU"€ˆˆ (""("põUUUõU\ÅõU\ÅU\ÅĢUĢÅĢUÅĢĢUÅĢÉYUŸÉ\õUUU_UUUU_UU\_õĢĢU\Ģ\\UĢĢ\UĢ\U_ĢĢ̈ü™Ģ œŸĢŒü’̌ĢĢ̈ĢĢĢČĢˆ€ˆˆˆ€€™˜™‰ˆy‰ł‰‰Żī×}ķŽķīŠŻķīķīŻīīŲķīPÕUUUŻīīīīpīīīīwēīīŻ}ŽŽŠ}ŽīķŻēīŻēīUU'§÷wwrśw÷ww÷wwwwwwxw÷’Ŗ—ID €śww'wwwwww÷w÷§wwzwww ww’ˆ(‚™PĄ’UPwPPPUUPUpPĻPUpUPUUPPWuuURUņP%Uņ%R%ÆUU„/U%„/UUŖ/UUņ’U¢śÆ%U%U%R%U%URUUUUURR"¢RUŖ/"U’*UUĢĢ™™Ģ™™ə™ ’  €€"r¢ " ¢("€Š €"" "*"("(""(("(" "( ˆˆ (•YÆŹ™©śÆ%"™É"™’Ā’)™R™"•Yy"•YUR•YĻĢĢUĀĢ_UĢ\UUĢÅ\õü\UUUüUUUUĻĢUUUUˆˆ€˜™€ ˆˆˆ™ ˆˆ€ˆˆˆ€ˆˆ€ˆˆˆˆˆˆUU}wUUķīPUÕķuu÷÷÷’÷’ļ’’’īīUU]UUU]UUU~U÷UUż~_’’’’’’EŖDYŖEDDwWDDw‡EEw™•w÷—Y’÷wwz÷ww”™ ˆ„’™—”Hxwpwwwwwww÷wwwr÷üĄDÄOĢ’ĢL@ OO@üōÄOĄĢDLĄDĢDDD@ @DÄL@L@ŻŻŠŻŻŠŠŻŠŠŻŻ Š Š ŠŠŠŻŠŻŻŻŻ Ż Ż Š Š ŠŠ]v‡ŻPe(ŲŠŻ†ŅŠfŻ`ŻŻPvÕŻef÷ÕefVw×ŻŻżŠ wšŻwŻ€‚ŻŠ-ŠŻŽŻīķŻķŻķŻŽßķŻŽżąšąĢąĄģąĢĄīīŻķŻŻŽŻŽżķŻŽßķŻŽąšąĢĪ ī ĢˆˆˆUUX…83UU::X££…PUU…P3…50€€UUˆ€3ƒ€€::€££ˆUU30…ˆ0SˆüōLĢÄOĢĢĻĢĢLōĢüōÄüĢōÄüĻDōĻĢOüODDĻĢLÄĢĻLLÄĻĢüOĢüLOĢĻüDüüĢō’ĢĻDDÄL@@UDUŅETMŁDÕŌŃTM]$TM] TEM""E"UŻDŌ’$Ż-%AUÕR DT@ ŻD@™˜™™‰™™™˜™™‰™™™˜™™‰™™™˜™™‰™€™˜™y—™™˜w™‰™˜™w˜xń™ĢĄO ’@ Ģ@LĢĢDD@DĄ@@ DĄOÄĢÄL@ĢĢ ŠUU»ÓU3µˆU³UU53UUUU Š3ŪUUŃ3Uˆ;[1;U3SUUUUUUŻfŻ] ŻŻ Ż]p÷p÷Õ wwÖŻŻŻŻŻŻŻŻŻŻŻ'ww pw  Ż ĄĄģĢĄīĪĢĄ šĄ ĄĄĄ ĄĢĪ ģī Ģ Ą  XS33US3X3™ˆ33‘ˆU30U3ˆX35XUU335€35€™Q3€33ˆ13U3US3…UU€ÄLōOüĢ’OÄĻĻOĻüĢOÄĢĢLĻōüDLOüĢDĻĻĻōOōĻō’’ĻōüĻLÄĢ’ĢōĻĢĻDüĢōĢÄüLÄÄĻĻBŻ)YD™ PU™’ŌM)@Ż™""$MEB”ŌDŻD@BDA"UTŻU‰™˜™™w‚ˆ—™(p˜‰™y™ń‡™p™x™™w˜™‰‡™™˜™‰™™™˜™™‰™™Œ0ŖĪ]4iuŅ(ué%Œ}}}e0’Ęq’ī0}Ā Sä÷q²īźīīī ”Ŗ)”Ļ!ŹĮ)"Ź"*¢Ģ)Ŗ"Ź" 'Ģ" §*)Ŗ’!¬™¬™))œ’),!™"¢"""rŖŖŖUÕŻUUÕŻõUÕŻuÕU÷wUŻwU}wUww_ņ’_’’U×]U÷’]U׊U×}UšwUõŠŻU’’’’’ˆ’’(ˆ’((’ˆ’ ‚’€( €ų’’’ųų’’š’÷‡š’÷ppųxų €'ųpš'ų’’’’’’’/’’’Ą/"ł‚/"‰ ’"’"rų€r‡’’ ’’/"’//"÷/’w’"w ņrˆ ņ' ņ "(ų’’ļī’’īž’īž’’~’’ļ~÷īwwžw~šīī’’’ļž’’wīīw’ļwš’w÷š’p’pDDDD$˜DD”'ID(ˆDD„ˆ‰D"D”)’D”r"DD“»DD“»DD“»DD“»DD»»""²»'ˆ»‚ˆ‚»ˆˆqˆ§pwwˆ˜ˆw˜ˆń™łˆqyˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆńˆ—ł—łUUUUUUUUUUUUPUUUPUUPUU•UUU™YUU•UUUUUUUwˆUU’ĒWUUxPUw‡pUŖ w§Ŗ’w" Ŗ’p   pwwz§ØŖ "¢ p""”"’pw—p——Õ÷wu÷÷u÷ßUu’UÕwŻwwŻ wwŻ ww’߯]’Ż]’’×]Õ×]÷pwŻwŻ×ŻŻŻ( "'’r ’(ņ(ųx€’’’’’’€’’’€'÷ųššp’pˆų‚ų’‡ų’‡pš€’š’ˆ"ww‚x’"ņ’"ņ’‚ˆ’"‚’/‚’’ˆ(‚‚ų€‚ų ‚ˆ’ ‚’(‚’’(‚’’ ‚ų’"‚ų’’’’ī’’’ļppļppļ’īē’~÷ē’pē’wp’pwwww’~www~wwp~wpDt(ˆK„"‚»D‚ˆ»»‹{»»+{»»‹»»»»8»;38ˆˆ(²øˆ»²»‹»²»‹»ø»‹»8»‹3333333333ˆ‘y€ˆ‘€ˆ‘€ˆŸˆq‘ˆqqˆqpqw÷ywł—ˆš‘ˆxp÷ń’pu÷÷wwwpUrwp%"pp("w"wp("pw("ppwĒĄXČ"wČ  Œ(wŒ(ˆ(wˆ(X33s733sw3www3w÷3w’ńsws÷3÷’333w733w73ų’?3’73x3q’3q’3ŻŻŻŻŻŻŻ=Ż}»»Ż·»wŻ·{³Ż}7G½s·[³Ū·;ÓŻÓŻ±Ż{Ż»»{×s»»×;73×»3sݻ۽ݳ{=Ó333w330w33ww3p§wsw§Ŗ3wŖ"rwŖ²sw *033w03ww3Ŗw3Ŗzw0¢zw7¢zw7Ŗ w3  Ŗz }9Ł»»{»»»03pŻ×w’} Ż{»»{ Ż »sQDEōńõOQōD÷_DDQO„QD÷_D„ADDDDDDDADōDQUDTUETUEXUQEDU’’wwww źŖ ­-ˆ÷’÷’wwwwąīŖ ą®īڈāīīppppp‰‰‰ppppypxw˜p˜šŖ™ł’šłww™ww— ww)ywwˆyw(‚y‚"yw(xŸ©ŖŖ™™ŖŖ™™ŖŖ’™©Ŗ,™©Ŗ(™™©ˆ˜w™wˆw™3w3w‡ˆ3333‡33333133ƒ13ų?3ų73ń333338333ˆ13ƒ33ˆ8³Ū=;ŻŻ}³ŻŻŻwŻŻ}ÓŻ½»7ݽ»ÓŻ»»ŻŻŻŻŻ³Ó³Ū;×=Ż×ŻŻŻ}ÓŻŻŻ»ŪŻŻ»{ŻŻ»»ŻŻŻ³Ż3Ŗ3—™£Ŗw™£*w#:s3£Ŗ33ŖŖ33Ŗ:3šyp3™y¢:Ŗ:p7Ŗ:77#30Ŗ:33ŖŖ33£Ŗ300»Ż0»{Ż0»»3»»00» 3° ppwpwŻ»Ż { 3° 3» »3p·pwGTEDGDE’sōUDsOTE3GDU3sōD33wD333wDDDwDDwDDt@ōODDEDDDUUUUōDDDwwwwī.ˆ™Ŗ‚™™ī‚)łī‚™’Š-ˆ™ŠŖ"ˆŻāīźāź™ˆ¢ī™™( Ÿ’(™™(™ˆŅ ˆ(īŻ®®źī®"ąīpp ÷ˆw€ˆ÷wˆw’€€ww€€xwš÷w÷’w§wwwww w‚™"™™€™ š š€Ŗš‰ŖŖ™™wpw™wwww™pw—™pp™™™y©™ŖUUUUUU•UU•UUUUuUUu‡UU•ˆUU…‰Q™XUP™xUPyˆW™ˆ™ˆ™ˆˆ™˜™‰™˜™‰ˆˆˆˆXˆ…ˆˆ…EˆˆXEXXUU……TU„ˆHU„ˆˆTEˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆŲp pp€²wp {²+ ww "»+·  ''UU  UUZ UUU„UUUŖUU„PU„U„¢U%¢    Ŗ” ,Ŗ*ZU*RZU߯ż]’Żß}’’Żß’’’ŻĢü’ßżĢü’żżĶĢżżŻĶ×Ļ’’׿’’żż’’Ż’’ß’ĻŻß’üŻßÜŻßß\ŻßßŻŻŻŻŻŻŻŻŻŻwŻ}}WÕŻWQÕ}twpU×EŻwww}wP}wÕŻwÕQwUU}õ]qõŻ"YUUU™U%"•™ršU%'wUupUUUpUUWõUUUUUU•UUU™™™Y_UU÷UU pUUwpUUp÷U’’’wwDDq’’q’w’’÷÷’Oō’Aō’š÷’p÷’U…u™Uˆ‰U ™™y™ˆ™ŖŖˆ™™Ŗˆ™‰ˆˆ™ˆˆ™˜™‰˜™™™™˜™™š™š˜™ˆ˜™™ˆ˜™™ˆˆAUˆˆUˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ„ŲŻ…ŃYQ•ˆÕˆˆˆˆˆˆˆˆˆˆˆ"( PP·UWUUUWUUµ[U„µUUUµU·" [{UuPPUµUUµUUUU[U„UUZUUUUPUUŖPU¢p*Ŗ""ppwwwwppwwwŖ„UU*ŖUUŖ"R""PwwwwwwwżżŻßżżŻŻżŻßŻŻßżŻŻß-"Żż-’ŻŻ//ŻŻ-"߯ß߯Żß߯żŻßŻßżŻ"ŻżŻņŅ߯’ņŻŻ"ŅŻŻ}QU}}Ż]}wwW-rw"""r'""r""''"rww'WŻ""uŻ"'UŻ"'x‡"'"w(""r(""r'""rw'U’÷U’wpu÷’uww' %RUUwp_/ _ Uš wW P RUR%óów’ww’’’pšpw÷w’’’’w’p÷÷wpš÷w÷rņōw’ww’wwwwńw^UUUåõUõ÷’Põ_uWņ_uW^WW^’WUPU^UååUõU^U’_^õ’åõWU’÷šņ’W’PUSu53’÷_3eų?Ufx÷swWųSU5s33ós3‡x3SUU0SUU3SU33WU?wPU÷WUUų÷UŠųׯŻwˆˆrwˆ"’ww""w(r÷wrw÷wpˆˆx÷ˆxw/xq’"xw"!pp'wzwwˆˆˆˆˆˆˆ’ˆˆųˆˆ(ˆˆšˆˆˆw‡ˆx‡ˆx‡ˆˆˆˆˆˆˆ'ˆˆxw‚ˆ"pˆˆˆw‡ˆˆ‡ˆˆp‡ˆLDÄDÄDtLDLD÷DÄDwLDōÄL’ĘDDDaDDDwDÄDÄDLDLļOÄDw~ODpēwDļēGpĒwąppŻīģĪŻķ||ŻĢĢīĪüüīĶĢīŻīīķķīīŻ}ŽīŻīŻżŻŽż×Żwß×÷’ß’üĒ×’ļ~üżw}÷ŻĶ÷’÷DŖ÷DDwO¤wwO”÷’wœ’ŸĢ’'Ģ’ÉĢJō÷Dw÷D’’D’w’œw÷,yw’|’y’|w÷’ˆˆHˆˆųOųˆU÷ˆW‡ˆWõˆˆˆuˆˆˆx™™™Wˆˆˆˆˆˆˆrwwwˆˆˆˆˆˆˆˆˆˆˆˆˆ‡‡ˆˆ‡õUUpU’’žõ÷w’p’UåõUåU÷U^Uõ÷wWUw^W^’åWuUwšpUwwUw_^÷’Uåšwˆxƒ‡uu÷WU’_õ’’÷ż’ˆˆ‡Ų݈ˆŻ׿Żx݇x÷ˆˆ÷߈ń’ׇˆ…X݈ˆŻq÷www!÷! ¢÷( ¢ńŖń¢¢Ŗwńpńw"w"! "Ŗ " "ŖŖ*ˆ(‚(’(€(’(€(ų/‚‚/ˆų'(š((ˆ‚(ˆ‡ˆˆˆˆˆˆˆˆˆ÷’ˆˆˆ€ˆˆ’ˆwww‡Ģ|DDtē’GGw÷D ąw tpLÄDÄDLDÄDDtptGwp’ōē@~wwGpwDtGĒDDLDLDÄDÄż’’ww’wwp÷pw’wwww pŻ×Ķ÷÷Ķ÷÷wĒ÷w’|÷w’wüwĒĒ×wpŻ}Ż’’ŸĢ’÷’Ģ’’wĢ’÷÷Ģ’wŸō’w/DOODDDD$|w÷Lw’’Lw’÷Lō’Lō’OIōOOIDODIDDD™™)x™w"Ww—)W™™ˆw˜)'w™(wr"r)r™™™™—™™—•™™w—‰™™u(""u™™ww’ww’’wxxwUUDETU$"D%B$DņBDD"/"%ņB$$"D¤$"DśTEDTTEDT“DDUTK“UB»U[²“»K»K»Kæ»»»ŻĶ]åŻ\UfĶUn]\^UPUÖUPÕīÅŻīUķ’fÖŻmÅÜŻUŻUPÕÜUPÜŻĶU߯U’nfVÖŻmÖŻŻmŻŻŻÖŻŲŻÖŻ(ŻŻ}‚(Żp((݈ˆxÖoŻmŻßŻfżŻm߯fmÖŃŅÖmwŻŻaׯŻÖ °0 °0;°000°00°0°°0°0°° °0 °0 000°° ° ° fffffffīfļīīīēīīīēwīw|īšĒ|÷ īfpīīīī~īīī~ēīpww~wpwĒĄw īīīīīīīīīīī>īćī3ī333ī>33ī338ž7s7ž™™™ž“™™33™™?“™xŸ™ˆw—™ƒƒćī™xćīUUUUuwUUwwU˜up…ˆupUˆUw™Uwˆ…UuW•UUUU˜UU_UõW)łpW‰WwW™˜w˜uwWˆUwWQńDQOQDQDŃDEDUDDōTDDDT%"DśU"B¤E""“D""BTBBE%EBµ%DRE%D$Bæ»»»ŗ»»»»“»KD»»Kæ[»KūD»UU“[UUKTUUķ’UŻīUŻęVPnÖnffUīVĢPÅ PUUļnęĪ߯eęŻ]UęŻUeUUPeU\`Ą`Ģ\f‚ˆwwˆˆwwˆxwwˆ÷wˆˆ((ˆˆˆ(ˆˆˆˆˆˆˆˆ×ŻmŻ×ŻŻŻ‡"ŻŻˆˆbŻŻŻŻŻŻŻŻfŅŻff(Żfݰ°¹°°y°°0“03«Ŗ3£Ŗ~03£Ŗ33 ° 00 3³0 Ŗ:³ ēŖ:Ŗ:333~÷ąp~w~ĄˆĢwLjˆˆĢ"ˆˆˆ*"‚ˆŖŖ""wĄpąp|ēš €|÷p‡’ˆČ’ˆˆˆ’"ˆų’~8x3Žīx™=Ż3ļŻŻ3ÓŻŻ}ŻŻŻ33ŻŻó’ŻŻŻŻ™xćī™~īīī>ćīŻ=ēīŻŻ=ÓŻŻŻßŻŻŻŻŻŻŻŻU•U‡U—ˆ)U™‰˜U™ˆ—U…‡UX—XU‰)‚U™)‚˜XuW˜YuUyXUU……XUUUˆWU%‚UU"XUR‰XUQUUQaQAUUdVQQAwquuwuq÷wwwwwwwŖæ”Ŗ»”j»±»»±ū÷;ß1»»±»»ó;»÷;Ż»Ŗ”ŖŖ‘©ŖŖŖ‘š©”ŖŖŖš™™”šš”šŖŖŖŖ33ó733’3“ 3#’™3s’™3##"333w3333333w333w373y7'3 w23rp3333w'33D”DDD Ił™I„II™šIō”™wI”ųIDDIŸ™IIš Iw÷˜DĒH™I”™D‰I™]UUUŻUU’U]uUUUUõW]õWWŻ_ßUUUUŻõUU]w_ÕUWU]PUUPPUp’Uw€UU”ńŖw”w”qqwwÆŖUw„wpuwŚ­±”’ń’’’ń’’’’įīīń’ń’÷’q÷’’’īīppīQqqwUwwwwīw1×ī±ėī³ćŽ;㾻㾻ć¾ī¾įŽ;»;3»;3»+33»»»»»³3ifń’af‘”–ŖŖ‘Ŗ‘””””53%’US•’UU•’'rUU/xUU•rUUuPUUUUx"?S"WSł"WU‰rPU'xUUUUURUUUWUUUDōD  w w‡ō’w÷™’łŸD™™™šAIˆ—Iˆ÷™qq„q—IDų’”™”™™ _] PPPuwwpp€wuqxPUUpQUwXQÕ€xU€qUU€XWswWsw17sf30sg31371wŻŻwŻŻ3vŚ­3w§:37”:1pŖ”įwwįš’óīī1ó’’13īīńļ’’ww’īī?’’3īī3ž’īfvręfvyn~w™ę™f™ŽŻŻ™pׇ  ŖŖnęwŖŖn™ŖęŖf¢¢ŖfŅŅŖķŲŅŖmwØŚķ"!"™’UE""RU"łÆD™™_U"UUD"RUE™™UUD"’ˆDˆˆUTTUUUEDUDD…DDDDD3”33138£™Yˆš‘”ؚ’©ˆˆˆˆˆˆˆŠŖŖ„„ˆUŖUUZU„  ™š™Ŗ³ó© ;3™³³»õ933_O;3ššDEOOD»»KD£ŗD¤ ŖŖ0šŖŖ£¤ 3¤ š ’’’’PUQPUPÅ5ĢU5ĢPRüU%ĮĢĢ’Ģü’Ģ’ü’’Į’Ģ’Ļ’/’Ģ’ņĢĻ,Ģ Ü Żpww×pwwwww|—w7|—ĢĄ ĢĄ ĢĄ ĶpÜŻŻ×Ż}ŻŲŻw‰˜yw—™™wō’Ÿ™ō’’’ō"""$™™™$™™™$™™™$if™ŁŻÓ=ł’’’’’’’""’™™™ņ™™™ņ™™™ņf–ņ3Ż"  Š  €Ż€ˆfŠˆfŻŲnÖŲŻfnŲŻęfhęˆŅŻķˆŻŻˆˆˆķˆˆˆf€ˆčˆh ˆīˆī™DDłUU™©UU)"DU’"BE""TD!T)AD„UEUUUUUUˆˆUˆˆ‰ˆˆ˜’‘™©”:£ˆŖˆŖˆˆˆˆˆˆˆˆ3U„ŖSŖ:33333131ƒˆEE4;DT;³ōDKōDOEDōDTDp_TPEšU“:Dó3DTETEODDEš9 æ’™ł»’ˆųŖłų’’’’’’’’’’’š"ņ’™0’"™™’'™™š/rw""ņ/wP"ĢĮU"ĀĢU"RĢ'"UUw"RU'"wu™"yw™™™™ĮUPÕ]UPÕ ]}wŻww™yww|37w733w733÷vvgfwfvgföff’’’fwwwww™wwŁ×Ęł–żwoŸoffffwwwwwvwwwgvwwłŻ3ŚŻ3©­ż3©Ŗó©*Żį©bfęyŸffyŸ™™:ӝ"1Ó}w3Ówp’ÓwwīŃw÷īföīnvw™™pppxwpˆ}€XUՇˆw‡wwwßU ˆ×U ˆWU €WU}WU}ż’Ż’’Ż?’ Š3óy™9óyИ™™Š™™™ŻˆˆˆŻ’߯ś’ŠŖ’×Ŗ™ י˜ ™™Ł ‰™Ł ˆˆˆŻ""wr"ņww"ņ’"?š"ņų"ņš""pšrr€p'w"" p"w÷'p’w€’šwšw€pˆˆwfflfffVflffdVffFfdeffFjff¦dZf¦ČTffffffefffefffšflfŖf„dŖf„UŖi•UšIDDDDDDDDDDDDDDDDDDtDUUuUUĢYWÅŖZŖDDDDDDDDDDDDDDDDGDDDWUUUĒŹ\UšŖĢUfffffEfffŖuffŖŖffŖ¤tf§¤fDDTf¤j¦ffffffFffV¦ff©ŖfUŖŖjW™ŖdeDJdjfzjŻŻŻŻŻŻŻ­ŻŻŻŠŻŻŻ‰ŻŻ©™ŻŻŖŻŻ™ŻŻŠŖŻŻŻŻ©ŚŻŻšŻŻŻ©ŻŻŻ©šŻŻ©ŁŻŻŠØŻŻ™˜ŚŻUPUUÅ’Å’üĄ’Ļ’ĻUUõĻ%Åü’PPUPUüUPšĻ_U’Ļ ’Ļ \ĻĢPüĢUU’üĢ\wwwwwwŻwwׯswUUs×]Us×]U3ׯU3SUUpWÕwż_Uw’ßUw’ŻUwUÕ]wUÕ}wUÕ}wUŻ}7ŻŻq­ŻŚwŻŻŻpŻwŃ”Ńr‚p€p"p"""x/qw’ˆ ły€ųw€‡‘Ÿx ™x™™q™wq™ˆ™f¦„•f¤U„f”UefTeff¤eff„offV’fffefTDšVJTYTVeJeDUJjDT•eDU’fffffffffŖŖš:ŖŖšŖŖz™UŖz‡ˆØwˆˆ‰ˆˆˆˆˆˆˆˆˆˆˆšŖŹUš©ŖZu™ŖŖˆ—§ŖˆˆwŠˆˆˆ˜ˆˆˆˆˆˆˆˆfuwvfWeD¦UjŖ¦Ŗyŗ¦Ŗ…Ŗ¦ŖgI¦„gffDffg¦wjD¦§fŖvŖj«—ŖjŖxŖjdvŖjfv¤jffDfŻØŻØ݊J’­ŠH’­ŹI’©šI’Ŗš‰’Ŗ™‰’‘˜ŚŻ”šŖŻ”š©ŻŸų©ŚÄœ™Śō¬™ŚOł™ŖŖ©™Ŗ#33’#2Ļ3#ā?»#޳³#ø»»38;;#»»»3³3;’333<8;=³1»³31»»;3;13‚ œ™ÉY@™ÄYLDA\ĄTÉETIDTLETUIꙙ™” •™A\Ģ™\UUĢPUĜEDL”\TP”DT@Uƒˆˆˆˆˆ03€؈ˆˆńńƒˆ1XE1ˆˆ š *¢"Ŗ*¢ Ø* Š""Ŗ"" "¢ Š¢؊( ؈‰Š‚’‰Ŗ ˆŖ”Ŗ)”Ļ!ŹĮ)"Ź"*¢Ģ)Ŗ"Ź" 'Ģ" §*)Ŗ’!¬™¬™))œ’),!™"¢"""rŖŖŖUUUYU•@ÉT YT”YE™E”™U™ UDPET@™U\™DLPEP•@”P”P”ƒƒńńP3ˆõ3ƒ3ƒ333333wˆwˆ€ˆw€ˆw ˆ €)€‚* ‚‚€€ˆ()‚’)ˆŖ"ˆŖ(ˆŖŠ((Ŗ"(Š‚¢*ˆ‚Ŗˆˆ"*ˆŖ w§Ŗ’w" Ŗ’p   pwwz§ØŖ "¢ p""”"’pw—p——„-- title: YGO80 -- author: draovar -- desc: Yu-gi-oh Speed duel for tic80 -- version: 1.1 -- script: lua -- saveid: draovar_ygo80 do SCREEN_W=240 SCREEN_H=136 ZONE_H=22 ZONE_W_SPECIAL=20 ZONE_W_MAIN=22 ZONE_GAP=2 function colW(c)return(c==0 or c==4)and ZONE_W_SPECIAL or ZONE_W_MAIN end PANEL_W=85 SEPARATOR_X=85 FIELD_X=86 FIELD_W=154 do local x=FIELD_X+20 COL_X={} for c=0,4 do COL_X[c]=x x=x+colW(c)+ZONE_GAP end end HAND_W=20 HAND_H_PLR=22 HAND_H_OPP=11 HAND_GAP=1 MAX_HAND=7 OPP_HAND_Y=1 OPP_ST_Y=13 OPP_MON_Y=36 DIVIDER_Y=60 PLR_MON_Y=63 PLR_ST_Y=86 PLR_HAND_Y=109 BLACK=0 RED=1 ORANGE=2 DARK_GREEN=3 BLUE=4 DARK_BLUE=5 MAGENTA=6 GRAY=7 BROWN=8 PEACH=9 YELLOW=10 LIGHT_GREEN=11 LIGHT_BLUE=12 VIOLET=13 PINK=14 WHITE=15 C_BG=BLACK C_DIVIDER=WHITE C_TEXT=WHITE C_HINT=GRAY C_HIGHLIGHT=GRAY C_ZONE_PLR=GRAY C_ZONE_OPP=GRAY C_ZONE_GY=DARK_BLUE C_ZONE_DECK=BROWN C_ZONE_FIELD=DARK_GREEN C_ZONE_EXTRA=ORANGE C_FACE_NORMAL=PEACH C_FACE_EFFECT=ORANGE C_FACE_SPELL=LIGHT_GREEN C_FACE_TRAP=MAGENTA C_FACE_FUSION=VIOLET C_LP=BLUE C_ACCENT=ORANGE C_CURSOR=LIGHT_BLUE C_TARGET=DARK_GREEN C_TRIBUTE=DARK_GREEN C_PLACE=BLUE C_MENU_SEL=BROWN C_ATTACK=RED C_STAT_UP=DARK_GREEN C_STAT_DOWN=RED C_PLAYMAT=DARK_BLUE C_FRAME=PEACH NAME_SCROLL_PAUSE=300 WIN_SCREEN_DELAY=60 WIN_SCENE_FADE=40 WINBOX_X,WINBOX_Y,WINBOX_W,WINBOX_H=40,30,160,76 SPR_TRIB=6 SPR_FUSE=8 SPR_CARDBACK=32 SPR_BACK_LOCK=192 SPR_FRAME=35 SPR_STAR=76 SPR_SEALED=62 SPR_CHECK_OFF=44 SPR_CHECK_ON=45 SPR_TRIAL_OFF=46 SPR_TRIAL_ON=47 SPR_MENU_LOGO=256 ATTR_SPR={dark=80,earth=81,fire=82,light=83,water=84,wind=85, spell=86,trap=87,divine=88} KIND_SPR={normal=89,continuous=90,counter=91,equip=92,field=93,quickplay=94,ritual=95} CARD_BACKS={ {key="normal",label="NORMAL",spr=96}, {key="basics",label="YGO BASICS",spr=99,group="YGO BASICS",how="CLEAR ALL YGO BASICS PUZZLES"}, {key="hero",label="HERO MATH",spr=102,group="HERO MATH",how="CLEAR ALL HERO MATH PUZZLES"}, {key="legend",label="LEGENDARY",spr=105,group="LEGENDARY",how="CLEAR ALL LEGENDARY PUZZLES"}, {key="theme",label="THEME DUEL",spr=108,how="WIN EVERY THEME DUEL"}, {key="survival",label="SURVIVAL",spr=144,how="WIN 5 ROUNDS IN SURVIVAL"}, {key="yami",label="YAMI MARIK",spr=147,how="DEFEAT YAMI MARIK",secret=true}, {key="collector",label="COLLECTOR",spr=150,how="UNLOCK EVERY CARD"}, } CARD_BACK_KEY=DARK_BLUE TITLE_ITEMS={"DUEL","TRIALS","DECK","OPTIONS"} SCENE="title" TITLE_SEL=1 ANIM={} DB={} CARD_BACK_SEL=1 function handX(n,i) local total=n*HAND_W+(n-1)*HAND_GAP local startX=FIELD_X+(FIELD_W-total)//2 return startX+i*(HAND_W+HAND_GAP) end function zoneXY(plr,row,col) local px=(plr==1) local monY=px and PLR_MON_Y or OPP_MON_Y local stY=px and PLR_ST_Y or OPP_ST_Y if row=="mon"then return col and COL_X[px and col or 4-col],monY elseif row=="st"then return col and COL_X[px and col or 4-col],stY elseif row=="fs"then return COL_X[px and 0 or 4],monY elseif row=="gy"then return COL_X[px and 4 or 0],monY elseif row=="ed"then return COL_X[px and 0 or 4],stY elseif row=="dk"then return COL_X[px and 4 or 0],stY elseif row=="hand"then return nil,px and PLR_HAND_Y or OPP_HAND_Y end end function zoneColor(plr)return(plr==1)and C_ZONE_PLR or C_ZONE_OPP end end do SAVE_HEADER=0 SAVE_VER_PMEM=0 SAVE_SLOT_PMEM=1 SAVE_FLAGS_PMEM=2 SAVE_F_SEEDED=1 SAVE_F_REVEALED=2 SAVE_OPT=4 SAVE_COLL=8 SAVE_COLL_N=8 SAVE_TRIALS=16 SAVE_PROGRESS=24 SAVE_SURV_BEST=24 SAVE_CARD_BACK=25 SAVE_BEATEN=26 SAVE_BEATEN_DARK=8 SAVE_DECKS=32 SAVE_DECK_WORDS=20 SAVE_DECK_MAIN=15 SAVE_DECK_EXTRA=4 SAVE_DECK_SLOTS=3 SAVE_MAGIC=0x59474F00 SAVE_VERSION=3 function saveDeckBase(slot) return SAVE_DECKS+(slot-1)*SAVE_DECK_WORDS end function saveVersion() local v=pmem(SAVE_VER_PMEM) if(v&0xFFFFFF00)~=SAVE_MAGIC then return 0 end return v&0xFF end function saveStamp() pmem(SAVE_VER_PMEM,SAVE_MAGIC|SAVE_VERSION) end DB_SLOT=1 function saveSlotLoad() local s=pmem(SAVE_SLOT_PMEM) DB_SLOT=(s>=1 and s<=SAVE_DECK_SLOTS)and s or 1 end function saveSlotSet(slot) DB_SLOT=slot pmem(SAVE_SLOT_PMEM,slot) end local V2_DECK_MAGIC=0xD80D0002 local V2_COLL_MAGIC=0xC0110001 local function migrateV2() local hadDeck=(pmem(6)==V2_DECK_MAGIC) local hadColl=(pmem(16)==V2_COLL_MAGIC) local main,extra,opt,trials={},{},pmem(7),pmem(18) if hadDeck then for i=0,4 do main[i+1]=pmem(i)end extra[1],extra[2]=pmem(5),pmem(17) end for i=0,SAVE_DECKS+SAVE_DECK_SLOTS*SAVE_DECK_WORDS-1 do if i=SAVE_COLL+SAVE_COLL_N then pmem(i,0)end end local base=saveDeckBase(1) for i=1,5 do pmem(base+i-1,main[i]or 0)end for i=1,2 do pmem(base+SAVE_DECK_MAIN+i-1,extra[i]or 0)end pmem(SAVE_OPT,opt) pmem(SAVE_TRIALS,trials) pmem(SAVE_SLOT_PMEM,1) if hadColl then pmem(SAVE_FLAGS_PMEM,pmem(SAVE_FLAGS_PMEM)|SAVE_F_SEEDED) else for i=0,SAVE_COLL_N-1 do pmem(SAVE_COLL+i,0)end end saveStamp() return hadDeck or hadColl end function saveMigrate() local v=saveVersion() if v==SAVE_VERSION then saveSlotLoad();return false end local carried=false if v==0 then carried=migrateV2()end saveStamp() saveSlotLoad() return carried end end do ART_LIB=0x8000 ART_MAP_END=ART_LIB+32640 ART_VRAM=0x4000 ART_BANKS={3,4} local SLOT_N=64 local slotSpr,slotArt,slotUse={},{},{} for s=1,SLOT_N do slotSpr[s]=256+((s-1)//8)*32+((s-1)%8)*2 slotUse[s]=0 end local artSlot,stamp={},0 function artReset() artSlot,slotArt,stamp={},{},0 for s=1,SLOT_N do slotUse[s]=0 end end function sprOf(card) local a=card.spr if not a then return nil end stamp=stamp+1 local s=artSlot[a] if s then slotUse[s]=stamp;return slotSpr[s]end s=1 for i=2,SLOT_N do if slotUse[i]>9)*16384+(a&511)*32 local dst=ART_VRAM+slotSpr[s]*32 memcpy(dst,src,64) memcpy(dst+512,src+512,64) return slotSpr[s] end function artPrime(ids) for _,id in ipairs(ids or{})do local cd=CARDS[id] if cd then sprOf(cd)end end end local function artLoadBank(n) local b=ART_BANKS[n] if not b then return false end local base=ART_LIB+(n-1)*16384 sync(1,b,false) memcpy(base,ART_VRAM,math.min(8192,ART_MAP_END-base)) base=base+8192 if base0 do if#rest<=maxCh then lines[#lines+1]=rest;break end local cut=maxCh if rest:sub(cut+1,cut+1)==" "then cut=cut+1 else while cut>1 and rest:sub(cut,cut)~=" "do cut=cut-1 end if cut<=1 then cut=maxCh end end lines[#lines+1]=rest:sub(1,cut) rest=rest:sub(cut+1) end end end return lines end function printWrap(text,x,y,maxW,col,maxY) local lineH=7 for _,ln in ipairs(wrapLines(text,maxW))do if maxY and y+lineH>maxY then return y end print(ln,x,y,col,true,1,true) y=y+lineH end return y end function printCentre(text,x,w,y,col,small) print(text,x+(w-#text*(small and 4 or 6))//2,y,col,true,1,small) end function shadowText(text,x,y,col) print(text,x+1,y+1,C_BG,true,1,false) print(text,x,y,col,true,1,false) end function shadowCentre(text,x,w,y,col) printCentre(text,x+1,w,y+1,C_BG,false) printCentre(text,x,w,y,col,false) end function tribsNeeded(lvl) if lvl<=4 then return 0 end if lvl<=6 then return 1 end if lvl<=8 then return 2 end return 3 end function turnMod(card,key) return(card.turnMods and card.turnMods[key])or 0 end function addTurnMod(card,key,delta) card.turnMods=card.turnMods or{} card.turnMods[key]=(card.turnMods[key]or 0)+delta end function getMonLvl(card) return math.max(1,(card.lvl or 0)+turnMod(card,"lvl")) end function clearTurnMods() local function clear(c)if c then c.turnMods=nil end end for p=1,2 do for i=1,3 do clear(G.mon[p][i])end for _,c in ipairs(G.hand[p])do clear(c)end for _,c in ipairs(G.gy[p])do clear(c)end end end function tributeValueOf(summonCard,m) if not m then return 0 end local mb=behaviorOf(m) if mb and mb.tributeValue then local v=mb.tributeValue(m,summonCard) if v then return v end end local sb=behaviorOf(summonCard) if sb and sb.tributeValue then local v=sb.tributeValue(m,summonCard) if v then return v end end return 1 end function tributeValueSelected(p) local n=p.soul and tributeValueOf(p.card,p.soul)or 0 for _,col in ipairs(p.tributes)do n=n+tributeValueOf(p.card,G.mon[1][col])end return n end function tributeValueAvailable(summonCard,plr) local _,soul=soulTarget(plr) local n=soul and tributeValueOf(summonCard,soul)or 0 for c=1,3 do n=n+tributeValueOf(summonCard,G.mon[plr][c])end return n end function hasMonsters(plr) return(G.mon[plr][1]or G.mon[plr][2]or G.mon[plr][3])~=nil end function countMonsters(plr) local n=0 for c=1,3 do if G.mon[plr][c]then n=n+1 end end return n end local function handNames(plr) local n={} for _,c in ipairs(G.hand[plr])do n[#n+1]=c.name or"?"end return(#n>0)and table.concat(n,", ")or"(empty)" end function traceDuelStart() if not AI_TRACE or G.sim then return end trace(string.format("=== DUEL START vs %s | first=p%d | lp %d/%d | deck %d/%d", tostring(G.oppName or"?"),G.firstPlayer or 1, G.lp[1]or 0,G.lp[2]or 0,#G.deck[1],#G.deck[2])) trace("=== p1 hand: "..handNames(1)) trace("=== p2 hand: "..handNames(2)) end function traceDuelEnd() if not AI_TRACE or G.sim then return end trace(string.format("=== DUEL END on turn %d | p%d WINS | lp %d/%d", G.turn or 0,G.winner or 0,G.lp[1]or 0,G.lp[2]or 0)) end function copyCard(card) local c={} for k,v in pairs(card)do c[k]=v end return c end local function equipTarget(eq) local t=eq.equippedTo return t and G.mon[t.plr]and G.mon[t.plr][t.col] end function getEquipBonus(card) local ab,db=0,0 for p=1,2 do for c=1,3 do local eq=G.st[p][c] if eq and not eq.facedown and eq.subtype=="equip"and equipTarget(eq)==card then local b=behaviorOf(eq) if b and b.equipBonus then local a,d=b.equipBonus(card,eq,p);ab=ab+a;db=db+d end end end end return ab,db end function getFieldBonus(card) local ab,db=0,0 if cardImmune(card)then return ab,db end local function ask(src) local b=src and not src.facedown and behaviorOf(src) if b and b.fieldBonus then local a,d=b.fieldBonus(card);ab=ab+a;db=db+d end end for p=1,2 do ask(G.fs[p]) for c=1,3 do ask(G.st[p][c])end end return ab,db end local function monStat(card,isAtk) local b=behaviorOf(card) local hook,base,mod,key if isAtk then hook=b and b.atkBonus base,mod,key=card.atk,card.atkMod,"atk" else hook=b and b.defBonus base,mod,key=card.def,card.defMod,"def" end local bonus=0 if hook then bonus=hook(card)or 0 end local ea,ed=getEquipBonus(card) local fa,fd=getFieldBonus(card) local equip=isAtk and ea or ed local field=isAtk and fa or fd return math.max(0,base+bonus+equip+field+(mod or 0)+turnMod(card,key)) end function getMonAtk(card)return monStat(card,true)end function getMonDef(card)return monStat(card,false)end function retargetEquips(fromPlr,fromCol,toPlr,toCol) for p=1,2 do for c=1,3 do local eq=G.st[p][c] local e=eq and eq.subtype=="equip"and eq.equippedTo if e and e.plr==fromPlr and e.col==fromCol then e.plr,e.col=toPlr,toCol end end end end function checkEquips() for p=1,2 do for c=1,3 do local eq=G.st[p][c] if eq and not eq.facedown and eq.subtype=="equip"and eq.equippedTo then local target=equipTarget(eq) if not target or target.facedown then sendSpellTrapToGY(p,c,"rule") end end end end end function firstEmpty(arr) local mon=G and G.mon local plr=mon and((arr==mon[1]and 1)or(arr==mon[2]and 2))or nil if plr and zonesAllowed(plr)<=0 then return nil end for i=1,3 do if not arr[i]then return i end end return nil end function firstOccupied(arr) for i=1,3 do if arr[i]then return i end end return nil end function monsterCol(plr,card) for c=1,3 do if G.mon[plr][c]==card then return c end end end end do function addAnim(frames,fn,onDone) if G.sim then if onDone then onDone()end return end table.insert(ANIM,{frames=frames,t=frames,fn=fn,onDone=onDone}) end function tickAnims() for i=#ANIM,1,-1 do local a=ANIM[i] a.t=a.t-1 if a.t<=0 then local cb=a.onDone table.remove(ANIM,i) if cb then cb()end end end end function drawAnims() for _,a in ipairs(ANIM)do a.fn(a.t,a.frames)end end function animDrawCard(p) local n=#G.hand[p] local sx,sy=zoneXY(p,"dk") local ex=handX(n,n-1) local _,ey=zoneXY(p,"hand") addAnim(18,function(t,f) local prog=(f-t)/f local cx=math.floor(sx+(ex-sx)*prog) local cy=math.floor(sy+(ey-sy)*prog) drawCardBack(cx,cy,HAND_W,HAND_H_PLR) end) end function destroyFlash(x,y) addAnim(24,function(t,f)if t//4%2==0 then rect(x,y,ZONE_W_MAIN,ZONE_H,C_ACCENT)end end) end function swordParams(dx,dy) local adx,ady=math.abs(dx),math.abs(dy) if adx0 and 2 or 0,0 else local fl if dx>=0 and dy<=0 then fl=0 elseif dx<0 and dy<=0 then fl=1 elseif dx>=0 and dy>0 then fl=2 else fl=3 end return 4,fl,0 end end function animSwordSlash(ax,ay,tx,ty,onDone) local sp,sf,sr=swordParams(tx-ax,ty-ay) addAnim(18,function(t,f) local p=1-t/f spr(sp,ax+(tx-ax)*p,ay+(ty-ay)*p,0,1,sf,sr,2,2) end,onDone) end function animSwordSlashBox(ax,ay,tx,ty) local box={value=false} animSwordSlash(ax,ay,tx,ty,function()box.value=true end) return box end local function drawSpinOverZones(sprId,zones,t) local rot=(4-(t//8)%4)%4 for _,z in ipairs(zones)do spr(sprId,z.x+3,z.y+3,0,1,0,rot,2,2) end end local TRIBUTE_FRAMES=30 function animTribute(zones,onDone) playSfx("tribute",TRIBUTE_FRAMES) addAnim(TRIBUTE_FRAMES,function(t)drawSpinOverZones(SPR_TRIB,zones,t)end,onDone) end local FUSE_FRAMES=45 function playMaterialTributeAnim(plr,picks) local zones={} local hand=G.hand[plr] local _,handY=zoneXY(plr,"hand") if plr==2 then handY=-3 end for _,p in ipairs(picks)do if p.kind=="field"then local x,y=zoneXY(plr,"mon",p.fc) zones[#zones+1]={x=x,y=y} else zones[#zones+1]={x=handX(#hand,p.hi-1),y=handY} end end if#zones==0 then return end playSfx("special",FUSE_FRAMES) waitAnim(playAnim(FUSE_FRAMES,function(t)drawSpinOverZones(SPR_FUSE,zones,t)end)) end function animPeekST(plr,col) local card=G.st[plr][col] if not card then return end local x,y=zoneXY(plr,"st",col) local zc=zoneColor(plr) waitAnim(playAnim(60,function(t) drawCardSpell(x,y,card,zc) if(t//6)%2==0 then rectb(x,y,ZONE_W_MAIN,ZONE_H,C_TEXT)end end)) end local function drawRevealed(e,t) local w,h=ZONE_W_MAIN,ZONE_H if e.kind=="hand"then drawHandOppDebug(e.x,e.y,e.card) w,h=HAND_W,HAND_H_OPP elseif e.kind=="st"then drawCardSpell(e.x,e.y,e.card,e.zc) elseif e.card.pos==2 then drawCardDef(e.x,e.y,e.card,e.zc) else drawCardAtk(e.x,e.y,e.card,e.zc)end if(t//6)%2==0 then rectb(e.x,e.y,w,h,C_TEXT)end end local function addHidden(list,plr,row,col) local card=G[row][plr][col] if not(card and card.facedown)then return end local x,y=zoneXY(plr,row,col) list[#list+1]={card=card,kind=row,zc=zoneColor(plr),x=x,y=y} end function animRevealHidden(plr) if plr~=2 then return nil end local list={} local n=math.min(#G.hand[plr],MAX_HAND) local _,hy=zoneXY(plr,"hand") for i=0,n-1 do list[#list+1]={card=G.hand[plr][i+1],kind="hand",x=handX(n,i),y=hy} end for c=1,3 do addHidden(list,plr,"mon",c)end if#list==0 then return nil end return playAnim(90,function(t) for _,e in ipairs(list)do drawRevealed(e,t)end end) end function animFlashFaceDown() local list={} for p=1,2 do for c=1,3 do addHidden(list,p,"mon",c) addHidden(list,p,"st",c) end end if#list==0 then return end addAnim(90,function(t) for _,e in ipairs(list)do drawRevealed(e,t)end end) end end do SFX_CHANNEL=3 SFX_DUR=30 SFX={ tribute={id=57,note=36}, special={id=58,note=36,prio=2}, activate={id=59,note=38}, destroy={id=28,note=12,prio=2}, damage={id=55,note=56,speed=1,prio=3}, heal={id=53,prio=3}, win={id=63,note=48,speed=-2,prio=4}, lose={id=62,note=48,speed=-2,prio=4}, } local frameTick,framePrio=-1,0 function playSfx(name,dur) if G.sim or OPT.nosfx then return end local s=SFX[name] if not s then return end local prio=s.prio or 1 if G.tick==frameTick and prio<=framePrio then return end frameTick,framePrio=G.tick,prio sfx(s.id,s.note or-1,dur or s.dur or SFX_DUR,SFX_CHANNEL,15,s.speed or 0) end end do START_LP=4000 G={} function newGame() G={ turn=1,ph=PH_DRAW,active=1,firstPlayer=1,tick=0, lp={START_LP,START_LP}, dispLp={START_LP,START_LP}, mon={{nil,nil,nil},{nil,nil,nil}}, st={{nil,nil,nil},{nil,nil,nil}}, fs={nil,nil}, extra={{},{}}, hand={{},{}}, gy={{},{}}, banish={{},{}}, deck={{},{}}, cur={side=1,row=1,col=2}, menu={open=false,items={},sel=1}, mode="free", infoCard=nil, infoScroll=0, nameScroll={card=nil,offset=0,pause=NAME_SCROLL_PAUSE,atEnd=false}, pending=nil, popup=nil, normalSummoned=false, aiTimer=0, autoTimer=50, } ANIM={} procInit() end function getHoveredCard() local c=G.cur if c.row==3 then return G.hand[c.side][c.col+1]end if c.row==1 then if c.side==1 and c.col==0 then return G.fs[1]end if c.side==2 and c.col==4 then return G.fs[2]end end if c.col==0 or c.col==4 then return nil end local col=c.col if c.side==2 then col=4-col end if c.row==1 then return G.mon[c.side][col]end return G.st[c.side][col] end function clampToHand(side) G.cur.col=math.min(G.cur.col,math.max(0,#G.hand[side]-1)) end local function altWinner() for p=1,2 do for _,c in ipairs(G.hand[p])do local b=behaviorOf(c) if b and b.winCondition and b.winCondition(p)then return p,c end end end end function winElapsed()return G.tick-(G.winTick or 0)end function winSceneT() if not G.winner then return nil end local b=behaviorOf(G.winCard) if not(b and b.winAnim)then return nil end return winElapsed(),b.winAnim end function winScenePlr() local t=winSceneT() return(t and t>=WIN_SCENE_FADE)and G.winner or nil end function winHoldOver() local b=behaviorOf(G.winCard) return winElapsed()>=((b and b.winHold)or WIN_SCREEN_DELAY) end function endDuel(winner,card) if G.winner then return end G.winner=winner G.winCard=card G.winTick=G.tick traceDuelEnd() end function checkWin() if G.winner then return end local alt,card=altWinner() if alt then endDuel(alt,card) elseif G.lp[1]<=0 then endDuel(2) elseif G.lp[2]<=0 then endDuel(1)end end function tickWinSfx() if not G.winner or G.winSfxDone or not winHoldOver()then return end G.winSfxDone=true playSfx(G.winner==1 and"win"or"lose") end function drawCard(p,noAnim) if#G.deck[p]==0 then endDuel(3-p);return end if#G.hand[p]0 then G.deckTop[p]=G.deckTop[p]-1 end addToHand(p,makeCard(table.remove(G.deck[p]))) if not noAnim then animDrawCard(p)end end end local function revealFlash(zx,zy,onDone) addAnim(24,function(t,f)if t//4%2==0 then rect(zx,zy,ZONE_W_MAIN,ZONE_H,C_ACCENT)end end,onDone) end function revealAndDestroyST(plr,col) local card=G.st[plr][col] if not card then return end card.wasSet=card.facedown local zx,zy=zoneXY(plr,"st",col) playSfx("destroy") if card.facedown then card.facedown=false revealFlash(zx,zy,function()sendSpellTrapToGY(plr,col,"effect");checkEquips()end) else destroyFlash(zx,zy) sendSpellTrapToGY(plr,col,"effect") checkEquips() end end function revealAndDestroyMon(plr,col,reason) local m=G.mon[plr][col] if not m then return end if reason=="effect"and(effectImmune(m)or cannotEffectDestroy(m))then return end if m.facedown and DESTROY_REASONS[reason]then m.facedown=false local zx,zy=zoneXY(plr,"mon",col) revealFlash(zx,zy,function()sendMonsterToGY(plr,col,reason);checkEquips()end) return end sendMonsterToGY(plr,col,reason) end function negateSummon(plr,col) local m=G.mon[plr][col] if not m or summonUnnegatable(m)then return nil end destroyFlash(zoneXY(plr,"mon",col)) playSfx("destroy") return sendMonsterToGY(plr,col,"negate") end function changeLp(plr,delta) G.lp[plr]=math.max(0,G.lp[plr]+delta) checkWin() end function applyDamage(plr,dmg) if dmg<=0 then return end local ctx={plr=plr,dmg=dmg,actor=3-plr,negated=false} procPushFrame(function() if not ctx.negated then changeLp(plr,-ctx.dmg)end end) raiseNow(EV_BEFORE_DMG,ctx) end DESTROY_REASONS={battle=true,effect=true,rule=true,send=true} local function unlinkPartner(card) local partner=card.linkedTrap or card.linkedMon if not partner then return nil end local releasing=card.binds==true card.linkedTrap=nil;card.linkedMon=nil partner.linkedTrap=nil;partner.linkedMon=nil if staticActive("blocksTraps")then return nil end if releasing then return nil end return partner end local function destroyLinked(zones,card,send,reason) if not card then return end for p=1,2 do for c=1,3 do if zones[p][c]==card then send(p,c,reason);return end end end end function clearFieldMods(card) if not card then return end card.atkMod=nil;card.defMod=nil end local function intoGY(plr,card) card.fusionMaterial=nil card.noEffectDestroy=nil;card.madeFrom=nil card.borrowedFrom=nil;card.borrowedAtTurn=nil clearFieldMods(card) table.insert(G.gy[plr],card) end local function destroyLinkedTrap(t)destroyLinked(G.st,t,sendSpellTrapToGY,"rule")end local function destroyLinkedMon(m)destroyLinked(G.mon,m,sendMonsterToGY,"effect")end function sendMonsterToGY(plr,col,reason) local m=G.mon[plr][col] if not m then return nil end if(reason=="effect"or reason=="send")and effectImmune(m)then return nil end if reason=="effect"and cannotEffectDestroy(m)then return nil end if(reason=="battle"or reason=="effect")and destroyReplaced(m,plr)then return nil end G.mon[plr][col]=nil if not m.isToken then intoGY(ownerOf(m,plr),m)end if DESTROY_REASONS[reason]then destroyFlash(zoneXY(plr,"mon",col)) playSfx("destroy") end destroyLinkedTrap(unlinkPartner(m)) checkEquips() destroyEvent(m,plr,reason) return m end local function resumeTrapsIfFreed(c) local b=behaviorOf(c) if not(b and b.static and b.static.blocksTraps)then return end if staticActive("blocksTraps")then return end sweepTrapHook("onResume") end function sendSpellTrapToGY(plr,col,reason) local c=G.st[plr][col] if not c then return nil end G.st[plr][col]=nil local m=unlinkPartner(c) intoGY(plr,c) local b=behaviorOf(c) if b and b.onSentToGY then b.onSentToGY(c,plr,reason)end destroyLinkedMon(m) resumeTrapsIfFreed(c) return c end function returnSTToHand(plr,col) local c=G.st[plr][col] if not c then return nil end G.st[plr][col]=nil destroyLinkedMon(unlinkPartner(c)) c.facedown=false;c.setThisTurn=false if c.subtype=="equip"then c.equippedTo=nil end addToHand(plr,c) resumeTrapsIfFreed(c) return c end function ownerOf(m,controller) return(m and(m.borrowedFrom or m.owner))or controller end function returnMonsterToHand(plr,col) local m=G.mon[plr][col] if not m then return nil end local owner=ownerOf(m,plr) G.mon[plr][col]=nil destroyLinkedTrap(unlinkPartner(m)) m.borrowedFrom=nil;m.borrowedAtTurn=nil;m.noEffectDestroy=nil addToHand(owner,m) checkEquips() return m end function returnFSToHand(plr) local f=G.fs[plr] if not f then return nil end G.fs[plr]=nil addToHand(plr,f) return f end function discardFromHand(plr,handIdx,reason) local c=G.hand[plr][handIdx] if not c then return nil end table.remove(G.hand[plr],handIdx) intoGY(plr,c) local b=behaviorOf(c) if b and b.onSentToGY then b.onSentToGY(c,plr,reason)end return c end function addToHand(plr,card) if not card then return nil end card.effectsNegated=nil clearFieldMods(card) card.pos=1;card.facedown=false card.attacked=false;card.summoned=false;card.posChanged=false if card.subtype=="fusion"then table.insert(G.extra[ownerOf(card,plr)],card) return card end table.insert(G.hand[plr],card) checkWin() return card end function banishMonster(plr,col) local m=G.mon[plr][col] if not m then return nil end G.mon[plr][col]=nil m.noEffectDestroy=nil clearFieldMods(m) local owner=ownerOf(m,plr) m.borrowedFrom=nil;m.borrowedAtTurn=nil if not m.isToken then table.insert(G.banish[owner],m)end destroyLinkedTrap(unlinkPartner(m)) checkEquips() return m end function banishFromHand(plr,idx) local c=G.hand[plr][idx] if not c then return nil end table.remove(G.hand[plr],idx) table.insert(G.banish[plr],c) return c end function banishFromGY(plr,idx) local c=G.gy[plr][idx] if not c then return nil end table.remove(G.gy[plr],idx) table.insert(G.banish[plr],c) return c end function addToGY(plr,card,reason) if not card then return end intoGY(plr,card) end function sendFieldSpellToGY(plr,reason,actor) local f=G.fs[plr] if not f then return nil end if reason=="effect"then if actor and actor~=plr and staticActive("blocksFieldSpells",plr)then return nil end playSfx("destroy") end G.fs[plr]=nil intoGY(plr,f) return f end end do function chainSpeed(card) if not card then return 1 end local b=behaviorOf(card) if b and b.speed then return b.speed end if card.cat=="trap"then return card.subtype=="counter"and 3 or 2 end if card.cat=="spell"then return card.subtype=="quickplay"and 2 or 1 end return 1 end EV_PHASE="PHASE" EV_SUMMON="SUMMON" EV_FLIP="FLIP" EV_ATTACK="ATTACK" EV_BEFORE_DMG="BEFORE_DAMAGE" EV_DESTROYED="DESTROYED" EV_TRIBUTED="TRIBUTED" EV_ACTIVATE="ACTIVATE" function procInit() G.proc={ frames={}, chain={links={}}, pending={}, currentChoice=nil, source=nil, } end function procPushFrame(body) table.insert(G.proc.frames,{co=coroutine.create(body),wait=nil}) end function procTopFrame()return G.proc.frames[#G.proc.frames]end function procIdle() return#G.proc.frames==0 and#G.proc.pending==0 end function procBusy() if not G.proc then return false end return not procIdle()or G.proc.currentChoice~=nil end function procBusyReason() if not G.proc then return"no proc"end if#G.proc.frames>0 then local tag="frame#"..#G.proc.frames local w=G.proc.frames[#G.proc.frames].wait if not w then return tag.." running"end if w.kind=="anim"then return tag.." wait:anim done="..tostring(w.done and w.done.value) end if w.kind=="choose"then local req=w.req or{} return tag.." wait:choose kind="..tostring(req.kind).. " plr="..tostring(req.plr).." result="..tostring(w.result) end return tag.." wait:"..tostring(w.kind) end if#G.proc.pending>0 then return"pending#"..#G.proc.pending end if G.proc.currentChoice then local cc=G.proc.currentChoice return"currentChoice kind="..tostring(cc.kind).." plr="..tostring(cc.plr) end return"idle?" end function procSweepStranded() for p=1,2 do for c=1,3 do local card=G.st[p][c] if card and card.pendingActivation then card.pendingActivation=nil sendSpellTrapToGY(p,c,"effect") end end end end local function dropFrame(target) for i=#G.proc.frames,1,-1 do if G.proc.frames[i]==target then table.remove(G.proc.frames,i);return end end end function procTick() for _=1,64 do local f=procTopFrame() if not f then if#G.proc.pending>0 then procFlushPending();return end G.proc.source=nil procSweepStranded() return end local fed local w=f.wait if w then if w.kind=="anim"and not(w.done and w.done.value)then return end if w.kind=="choose"then if w.result==nil then return end if w.result~=false then fed=w.result end end end f.wait=nil local ok,sig=coroutine.resume(f.co,fed) if not ok then trace("PROC ERROR: "..tostring(sig)) dropFrame(f) elseif coroutine.status(f.co)=="dead"then dropFrame(f) elseif type(sig)=="table"then if sig.kind=="anim"then f.wait=sig elseif sig.kind=="choose"then f.wait=sig;procRouteChoice(sig) elseif sig.kind=="push"then procPushFrame(sig.body) end end end end function waitAnim(doneBox) return coroutine.yield{kind="anim",done=doneBox} end function choose(req) return coroutine.yield{kind="choose",req=req} end function subroutine(body) return coroutine.yield{kind="push",body=body} end function playAnim(frames,tickFn) local box={value=false} addAnim(frames,tickFn,function()box.value=true end) return box end function cardChoiceItems(req) if req.items then return req.items end local pool if req.from=="hand"then pool=G.hand[req.plr] elseif req.from=="gy"then pool=G.gy[req.plr] elseif req.from=="deck"then pool=G.deck[req.plr]end local items={} if pool then for i,c in ipairs(pool)do local card=(type(c)=="table")and c or CARDS[c] if not req.filter or req.filter(card,i)then table.insert(items,{ deckIdx=i,card=card,name=card.name,atk=card.atk,def=card.def, lvl=card.lvl,desc=card.desc, }) end end end return items end local function routeRandom(req,waitObj) local isCoin=(req.kind=="coin") local result=isCoin and math.random(0,1)or math.random(1,6) G.popup={type=req.kind,result=result,t=180,frames=180,displayed=result} addAnim(180, function(t,f) G.popup.t=t if isCoin then return end local elapsed=f-t if elapsed<60 and elapsed%4==0 then G.popup.displayed=math.random(1,6) elseif elapsed>=60 then G.popup.displayed=result end end, function()G.popup=nil;waitObj.result=result end) end local function bridgeResponse(req) if not(req.options and#req.options>0)then return true end G.mode="choose_response" for _,opt in ipairs(req.options)do local s=opt.self if s.plr==1 then local row=2 if s.zone=="mon"then row=1 elseif s.zone=="hand"then row=3 end local col=(s.zone=="hand")and(s.col-1)or s.col G.cur={side=1,row=row,col=col} break end end return true end local function bridgeCard(req) local items=cardChoiceItems(req) if#items==0 then return false end G.mode="sel_deck" G.deckSel={ items=items,sel=1,title=req.title or"PICK CARD", forced=(req.forced~=false), onPick=function(deckIdx,item)procAnswerChoice({idx=deckIdx,item=item})end, onCancel=function()procAnswerChoice(false)end, } return true end local function bridgeMenu(req) G.mode="free" G.menu={ open=true,sel=1,items=req.items,forced=(req.forced~=false), onChoose=function(key)procAnswerChoice(key)end, } return true end local function bridgeZone(req) local valid=zoneChoiceTargets(req) if#valid==0 then return false end G.mode="sel_target" G.targetSel={ valid=valid,idx=1,title=req.title,forced=(req.forced~=false), onPick=function(side,col,row) if side then procAnswerChoice({plr=side,col=col,row=row}) else procAnswerChoice(false)end end, } syncTargetCursor() return true end local PLAYER_BRIDGE={response=bridgeResponse,card=bridgeCard, menu=bridgeMenu,zone=bridgeZone} function procRouteChoice(waitObj) local req=waitObj.req if G.sim then waitObj.result=simAnswer(req) G.proc.currentChoice=nil return end if req.kind=="coin"or req.kind=="dice"then return routeRandom(req,waitObj)end if req.plr==2 then return aiRouteChoice(req,waitObj)end G.proc.currentChoice=req local bridge=PLAYER_BRIDGE[req.kind] if bridge and not bridge(req)then waitObj.result=false G.proc.currentChoice=nil end end function procAnswerChoice(answer) local f=procTopFrame() if not f or not f.wait or f.wait.kind~="choose"then return end if answer==nil then answer=false end local cc=G.proc.currentChoice if cc and cc.kind~="response"then simNoteLiveChoice(answer)end f.wait.result=answer G.proc.currentChoice=nil end function raise(event,ctx) ctx=ctx or{};ctx.event=event if#G.proc.frames>0 then table.insert(G.proc.pending,{event=event,ctx=ctx}) return end raiseNow(event,ctx) end function raiseNow(event,ctx) local mand,opt=collectListeners(event,ctx) if#mand>0 or#opt>0 then procPushFrame(function()runResponseWindow(event,ctx)end) end end function procFlushPending() local q=G.proc.pending;G.proc.pending={} for _,e in ipairs(q)do raiseNow(e.event,e.ctx)end end local function _alreadyOnChain(chain,card) if not chain then return false end for _,L in ipairs(chain.links)do if L.source==card then return true end end return false end local function _stillHeld(card) for p=1,2 do if G.fs[p]==card then return true end for c=1,3 do if G.mon[p][c]==card or G.st[p][c]==card then return true end end for _,h in ipairs(G.hand[p])do if h==card then return true end end end return false end local function _sealSpellTraps(list,plr) for i=#list,1,-1 do local c=list[i].self.card if list[i].plr==plr and(c.cat=="spell"or c.cat=="trap")then table.remove(list,i)end end end local function _consider(m,o,event,ctx,chain,card,plr,zone,col) if not card or _alreadyOnChain(chain,card)then return end if card.simHidden then return end if card.effectsNegated then return end local b=BEHAVIORS[card.effect] if not b or not b.listens then return end local L=b.listens[event] if not L then return end if zone=="hand"and(card.cat=="trap"or card.subtype=="quickplay")then return end local self={card=card,plr=plr,controller=plr,zone=zone,col=col} if L.when and not L.when(self,ctx)then return end local entry={ self=self,react=L.react,plr=plr, speed=L.speed or chainSpeed(card), targetCard=L.targetCard, targetPick=L.targetPick, } if L.optional then table.insert(o,entry)else table.insert(m,entry)end end function collectListeners(event,ctx) local m,o={},{} local chain=G.proc.chain local trapsBlocked=staticActive("blocksTraps")or trapsLocked() local gyBlocked=staticActive("blocksGYEffects") local function gyOK(card) return not(gyBlocked and card and card.cat=="monster"and not isGravekeeper(card)) end local hidden=G.sim and(3-G.simActor)or nil for p=1,2 do for c=1,3 do local mon=G.mon[p][c] if mon and not mon.facedown then _consider(m,o,event,ctx,chain,mon,p,"mon",c)end local st=G.st[p][c] if st then local concealed=(p==hidden)and not simIsRevealed(p,c) local skip=st.facedown and(st.setThisTurn or(st.cat=="trap"and trapsBlocked) or concealed) if not skip then _consider(m,o,event,ctx,chain,st,p,"st",c)end end end local fs=G.fs[p] if fs and not fs.facedown then _consider(m,o,event,ctx,chain,fs,p,"fs",nil)end if p~=hidden then for i,h in ipairs(G.hand[p])do _consider(m,o,event,ctx,chain,h,p,"hand",i)end end for _,gc in ipairs(G.gy[p])do local b=BEHAVIORS[gc.effect] if b and b.listensInGY and gc~=ctx.card and gyOK(gc)then _consider(m,o,event,ctx,chain,gc,p,"gy",nil) end end end if ctx and ctx.card and not _stillHeld(ctx.card)and gyOK(ctx.card)then _consider(m,o,event,ctx,chain,ctx.card,ctx.plr or 1,"gy",nil) end if event==EV_ATTACK and ctx and sealsAttackST(ctx.attacker)then _sealSpellTraps(m,3-ctx.plr);_sealSpellTraps(o,3-ctx.plr) end return m,o end function summonEvent(card,plr,col,kind,tributed) if not card or card.facedown then return end kind=kind or"normal" if kind=="special"and card.subtype~="fusion"then playSfx("special")end raise(EV_SUMMON,{ card=card,plr=plr,col=col,kind=kind, facedown=false,tributed=tributed or{},actor=plr, }) end function flipEvent(card,plr,col) if not card or card.facedown then return end raise(EV_FLIP,{card=card,plr=plr,col=col,actor=plr}) end function destroyEvent(card,plr,reason) if not card or not DESTROY_REASONS[reason]then return end raise(EV_DESTROYED,{card=card,plr=plr,reason=reason,actor=plr}) end function runResponseWindow(event,ctx) local passes=0 local offering=3-(ctx.actor or G.active) local curEvent,curCtx=event,ctx local function fireListener(picked) local pEv,pCtx=picked.srcEvent or curEvent,picked.srcCtx or curCtx subroutine(function()activateListener(picked,pEv,pCtx)end) local newLink=G.proc.chain.links[#G.proc.chain.links] return EV_ACTIVATE,{card=picked.self.card,controller=picked.plr, actor=picked.plr,link=newLink} end while passes<2 do local mand,opt=collectListeners(curEvent,curCtx) if curEvent~=event then local m2,o2=collectListeners(event,ctx) local seen={} for _,e in ipairs(mand)do seen[e.self.card]=true end for _,e in ipairs(opt)do seen[e.self.card]=true end local function carryOver(dst,src) for _,e in ipairs(src)do if not seen[e.self.card]then seen[e.self.card]=true e.srcEvent,e.srcCtx=event,ctx dst[#dst+1]=e end end end carryOver(mand,m2) carryOver(opt,o2) end if#mand>0 then table.sort(mand,function(a,b) if a.plr==b.plr then return false end return a.plr==G.active end) local picked=mand[1] passes=0 curEvent,curCtx=fireListener(picked) offering=3-picked.plr else local top=chainTopSpeed() local mine={} for _,e in ipairs(opt)do if e.plr==offering and e.speed>=math.max(top,2)then table.insert(mine,e) end end if#mine==0 then passes=passes+1 else local ans=choose{ kind="response",plr=offering, event=curEvent,ctx=curCtx,options=mine, } local pick=ans and mine[ans.optionIdx or 1] if not pick then passes=passes+1 else passes=0 curEvent,curCtx=fireListener(pick) end end offering=3-offering end end resolveChainStack() end function chainTopSpeed() local L=G.proc.chain.links return(#L>0)and L[#L].speed or 0 end function pushLink(link) link.negated=link.negated or false link.speed=link.speed or chainSpeed(link.source) if link.source then link.source.pendingActivation=nil end table.insert(G.proc.chain.links,link) end function playActivateAnim(card,plr,zone,col) playSfx("activate") local msg=(card.name or"?"):upper() local w=#msg*4+8 local bx=FIELD_X+(FIELD_W-w)//2 local by=DIVIDER_Y-4 local function drawBanner() rect(bx,by,w,9,C_ACCENT);rectb(bx,by,w,9,C_TEXT) print(msg,bx+4,by+2,C_TEXT,true,1,true) end if zone=="hand"or zone=="gy"then waitAnim(playAnim(60,drawBanner)) return end local zx,zy=zoneXY(plr,zone,col) local sc=cardFaceColor(card) local zw=(zone=="mon"or zone=="st")and ZONE_W_MAIN or ZONE_W_SPECIAL waitAnim(playAnim(60,function(t,f) if(t//6)%2==0 then rect(zx,zy,zw,ZONE_H,sc);rectb(zx,zy,zw,ZONE_H,C_TEXT)end drawBanner() end)) end function chainActivation(card,plr,loc,reactFn,target) procPushFrame(function() card.facedown=false playActivateAnim(card,plr,loc and loc.zone or"hand",loc and loc.col) local link={ source=card,controller=plr,speed=chainSpeed(card), sourceLoc=loc,target=target, } link.react=function() if reactFn then reactFn(link)else applyResolve(card,plr,nil)end end pushLink(link) runResponseWindow(EV_ACTIVATE,{card=card,controller=plr,actor=plr,link=link}) end) end function procActivate(card,plr,zone,col,reactFn,target) chainActivation(card,plr,{zone=zone,plr=plr,col=col},reactFn,target) end function pushActivationLink(opts,resolveFn) local card,col,zone=opts.card,opts.col,opts.zone local plr=opts.plr or 1 if zone=="hand"and card.cat=="spell"then local stCol=firstEmpty(G.st[plr]) if stCol then G.st[plr][stCol]=card for i,h in ipairs(G.hand[plr])do if h==card then table.remove(G.hand[plr],i);break end end procActivate(card,plr,"st",stCol,resolveFn) else addToGY(plr,card,"effect") chainActivation(card,plr,nil,resolveFn) end else procActivate(card,plr,zone,col,resolveFn) end end function activateListener(listener,event,ctx) local s=listener.self local card,zone,plr,col=s.card,s.zone,s.plr,s.col if card.facedown and zone=="st"then card.facedown=false card.pendingActivation=true end local picked,params if listener.targetPick then picked=listener.targetPick(s,ctx) if not picked then return end params=picked.params end local target=(picked and picked.target) or(listener.targetCard and listener.targetCard(s,ctx)) playActivateAnim(card,plr,zone,col) pushLink{ source=card, controller=listener.plr, speed=listener.speed, sourceLoc={zone=zone,plr=plr,col=col}, target=target, react=function()listener.react(s,ctx,params)end, } end function resolveChainStack() local links=G.proc.chain.links for i=#links,1,-1 do local L=links[i] if linkTargetNegates(L)then L.negated=true end if not L.negated then local outer=G.proc.source G.proc.source={card=L.source,plr=L.controller} subroutine(L.react) G.proc.source=outer end spendLink(L) end G.proc.chain.links={} end function spendLink(link) local loc=link.sourceLoc;if not loc then return end if loc.zone=="st"then local c=link.source if c.subtype=="continuous"or c.subtype=="equip"then c.facedown=false else sendSpellTrapToGY(loc.plr,loc.col,"effect")end elseif loc.zone=="fs"then link.source.facedown=false elseif loc.zone=="hand"then for i,h in ipairs(G.hand[loc.plr])do if h==link.source then discardFromHand(loc.plr,i,"effect");break end end end end function negateLinkBelow(selfCard,actorPlr) local links=G.proc.chain.links local myIdx for i=#links,1,-1 do if links[i].source==selfCard then myIdx=i;break end end if not myIdx or myIdx<=1 then return end local target=links[myIdx-1] target.negated=true local loc=target.sourceLoc local z=loc and loc.zone if z=="st"then revealAndDestroyST(loc.plr,loc.col) elseif z=="fs"then sendFieldSpellToGY(loc.plr,"effect",actorPlr) elseif z=="mon"then sendMonsterToGY(loc.plr,loc.col,"effect")end end end do function gyMonsterItems(plr) local items={} for p=(plr or 1),(plr or 2)do for i,c in ipairs(G.gy[p])do if c.cat=="monster"and canSpecialSummon(c)then table.insert(items,{gyPlr=p,gyIdx=i,deckIdx=i,card=c, name=c.name,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc}) end end end return items end function sweepTrapHook(name) for p=1,2 do for c=1,3 do local t=G.st[p][c] if t and t.cat=="trap"and not t.facedown then local b=BEHAVIORS[t.effect] if b and b[name]then b[name](t,p,c)end end end end end function listensOn(events,entry) local t={} for _,e in ipairs(events)do t[e]=entry end return t end function quickPlayListens(entry) return listensOn({EV_SUMMON,EV_FLIP,EV_ATTACK,EV_ACTIVATE},entry) end local function isSelf(self,ctx)return ctx.card==self.card end local function diedInBattle(self,ctx)return ctx.card==self.card and ctx.reason=="battle"end local function gadgetSearch(wantedId) return{ listens={ SUMMON={ when=isSelf, react=function(self,ctx) local plr=self.plr if#G.hand[plr]>=MAX_HAND then return end for i,id in ipairs(G.deck[plr])do if id==wantedId then addToHand(plr,makeCard(table.remove(G.deck[plr],i))) return end end end, }, }, } end local function sideOf(card) for p=1,2 do for c=1,3 do if G.mon[p][c]==card then return p end end end end local function halfAtkBurn(card,plr,atkCol,target,tgtCol,opp) if target and opp and tgtCol and G.mon[opp][tgtCol]~=target and not hasUnknownStats(target)then changeLp(opp,-((target.atk or 0)//2)) end end local function oppActed(self,ctx)return ctx.actor~=self.controller end function togglePosition(m) m.pos=(m.pos==1)and 2 or 1 m.posChanged=true end local function takeControl(plr,col,card) local opp=3-plr if G.mon[opp][col]~=card then return end G.mon[opp][col]=nil local landCol=firstEmpty(G.mon[plr]) if not landCol then addToGY(opp,card,"effect");return end card.borrowedFrom=opp;card.borrowedAtTurn=G.turn G.mon[plr][landCol]=card retargetEquips(opp,col,plr,landCol) end local function activateInFrame(body) return function(opts) local card,col,zone=opts.card,opts.col,opts.zone local plr=opts.plr or 1 procPushFrame(function()body(card,plr,zone,col)end) end end local function targetMonsterActivate(spec,title,apply,stillLegal) return activateInFrame(function(card,plr,zone,col) local t=choose{kind="zone",plr=plr,spec=spec,title=title} if not t then return end local m=G.mon[t.plr][t.col] if not m then return end procActivate(card,plr,zone,col,function() if G.mon[t.plr][t.col]~=m then return end if stillLegal and not stillLegal(m)then return end apply(plr,t.plr,t.col,m) end,m) end) end local function equipActivate(spec,title,stillLegal) return activateInFrame(function(card,plr,zone,col) local t=choose{kind="zone",plr=plr,spec=spec,title=title} if not t then return end local m=G.mon[t.plr][t.col] if not m or m.facedown then return end if stillLegal and not stillLegal(m)then return end card.equippedTo={plr=t.plr,col=t.col} procActivate(card,plr,zone,col,nil,m) end) end function chooseSummonPosition(plr,body) local key=choose{kind="menu",plr=plr,forced=true,summonPosFor=body, items={{"ATK POSITION","atk"},{"DEF POSITION","def"}}} return(key=="def")and 2 or 1 end local function pickFieldMonster(plr,title,forced) if not(hasTargetableMon(1)or hasTargetableMon(2))then return nil end local t=choose{kind="zone",plr=plr,spec={zone="mon"},title=title,forced=forced} if not t then return nil end if not(G.mon[t.plr]and G.mon[t.plr][t.col])then return nil end return t.plr,t.col end local function specialSummon(plr,col,m,pos) m.pos=pos;m.facedown=false;m.attacked=false;m.posChanged=false;m.summoned=true m.effectsNegated=nil m.borrowedFrom=nil;m.borrowedAtTurn=nil G.mon[plr][col]=m summonEvent(m,plr,col,"special",nil) end local function summonFromDeck(plr,title,filter,anyCond) if not firstEmpty(G.mon[plr])then return end local function offered(cd,idx) return(anyCond or canSpecialSummon(cd))and(filter==nil or filter(cd,idx)) end local ans=choose{kind="card",plr=plr,from="deck",title=title,filter=offered} if not ans then return end local pos=chooseSummonPosition(plr,CARDS[G.deck[plr][ans.idx]]) local emptyCol=firstEmpty(G.mon[plr]) if not emptyCol then return end local pick=G.deck[plr][ans.idx] local cd=pick and CARDS[pick] if not(cd and(cd.cat or"monster")=="monster"and offered(cd,ans.idx))then return end specialSummon(plr,emptyCol,makeCard(table.remove(G.deck[plr],ans.idx)),pos) end local function summonPicked(plr,it) local pos=chooseSummonPosition(plr,it.card) local col=firstEmpty(G.mon[plr]) if not col then return end local pile=G[it.source][plr] local at=pile[it.realIdx] if it.source=="deck"then if not at or CARDS[at]~=it.card then return end local m=makeCard(table.remove(pile,it.realIdx)) specialSummon(plr,col,m,pos) return m end if at~=it.card then return end specialSummon(plr,col,table.remove(pile,it.realIdx),pos) return at end local function handSpecialSummon(card,plr,handIdx,pos) procPushFrame(function() pos=pos or chooseSummonPosition(plr,card) local col=firstEmpty(G.mon[plr]) if not col or G.hand[plr][handIdx]~=card then return end table.remove(G.hand[plr],handIdx) specialSummon(plr,col,card,pos) end) end local function isCyberDragonName(c) return c~=nil and c.name~=nil and c.name:find("Cyber Dragon",1,true)~=nil end local function isCyberST(c) return c~=nil and(c.cat=="spell"or c.cat=="trap") and c.name~=nil and c.name:find("^Cyber")~=nil end local function cyberTributeSummon(n,label,entry) local function cyberCount(plr) local k=0 for c=1,3 do if matIsLiteral(G.mon[plr][c],"cyber_dragon")then k=k+1 end end return k end entry.noNormalSummon=true entry.noSpecialSummon=true entry.handIgnition={ label=label,summonsSelf=true, canActivate=function(_,plr)return cyberCount(plr)>=n end, activate=function(card,plr,handIdx) procPushFrame(function() local taken,cols,mons={},{},{} local sp={zone="mon",side="own",condition=true, where=function(m)return matIsLiteral(m,"cyber_dragon")and not taken[m]end} for i=1,n do if not hasTarget(sp,plr)then return end local t=choose{kind="zone",plr=plr,spec=sp,title=label.." "..i.."/"..n} if not t then return end local m=G.mon[t.plr][t.col] taken[m]=true;cols[#cols+1]=t.col;mons[#mons+1]=m end local pos=chooseSummonPosition(plr,card) if G.hand[plr][handIdx]~=card then return end for i,c in ipairs(cols)do if G.mon[plr][c]~=mons[i]then return end end for _,c in ipairs(cols)do sendMonsterToGY(plr,c,"tribute")end local col=firstEmpty(G.mon[plr]) if not col then return end table.remove(G.hand[plr],handIdx) specialSummon(plr,col,card,pos) end) end, } return entry end function stOrFsSpecs(card) return{{zone="st",exclude=card},{zone="fs",exclude=card}} end local function hasAnySpec(specs,plr) for _,sp in ipairs(specs)do if hasTarget(sp,plr)then return true end end return false end local function stOrFsHasTarget(card,plr) return hasAnySpec(stOrFsSpecs(card),plr) end function destroyPickedST(ans,actor) if ans.row=="fs"then if not G.fs[ans.plr]then return false end sendFieldSpellToGY(ans.plr,"effect",actor) return true end if not G.st[ans.plr][ans.col]then return false end revealAndDestroyST(ans.plr,ans.col) return true end local function searchGY(plr,title,filter,forced) local ans=choose{kind="card",plr=plr,from="gy",title=title,filter=filter,forced=forced} if ans then addToHand(plr,table.remove(G.gy[plr],ans.idx))end end local function isPoly(c)return c.id=="polymerization"end local function recoverPoly(plr) for i,c in ipairs(G.gy[plr])do if isPoly(c)then addToHand(plr,table.remove(G.gy[plr],i));return end end end local function discardThenDestroy(name,label,specsOf,destroy,costFilter,verb) local function canPay(plr) if not costFilter then return#G.hand[plr]>0 end for _,c in ipairs(G.hand[plr])do if costFilter(c)then return true end end return false end return{ label=label, canActivate=function(card,plr) return card.ignitionUsed~=G.turn and canPay(plr) and hasAnySpec(specsOf(card),plr) end, activate=function(card,plr,col) card.ignitionUsed=G.turn procPushFrame(function() local cost=choose{kind="card",plr=plr,from="hand",purpose="cost", title=name..": DISCARD",filter=costFilter} if not cost then return end discardFromHand(plr,cost.idx,"effect") local specs=specsOf(card) if not hasAnySpec(specs,plr)then return end local ans=choose{kind="zone",plr=plr,specs=specs,title=name..": "..(verb or"DESTROY")} if ans then destroy(ans,plr)end end) end, } end local function armedEvolve(nextId) local IN_HAND={zone="hand",side="own",condition=true,id=nextId} local IN_DECK={zone="deck",side="own",condition=true,id=nextId} local function takeNext(plr) for i,id in ipairs(G.deck[plr])do if id==nextId then return makeCard(table.remove(G.deck[plr],i))end end for i,c in ipairs(G.hand[plr])do if c.id==nextId then return table.remove(G.hand[plr],i)end end end return{ PHASE={ when=function(self,ctx) return self.zone=="mon"and ctx.phase==PH_STBY and ctx.actor==self.plr and(hasTarget(IN_HAND,self.plr)or hasTarget(IN_DECK,self.plr)) end, react=function(self,ctx) local plr=self.plr if G.mon[plr][self.col]~=self.card then return end if not(hasTarget(IN_HAND,plr)or hasTarget(IN_DECK,plr))then return end if not sendMonsterToGY(plr,self.col,"send")then return end local pos=chooseSummonPosition(plr,CARDS[nextId]) local col=firstEmpty(G.mon[plr]) if not col then return end local up=takeNext(plr) if up then specialSummon(plr,col,up,pos)end end, }, } end local function armedBlast(name,label,sweep) local function isMon(c)return(c.cat or"monster")=="monster"and not hasUnknownStats(c)end local function capSpecs(cap) return{{zone="mon",side="opp",facing="up", where=function(c)return getMonAtk(c)<=cap end}} end return{ label=label, canActivate=function(card,plr) if card.ignitionUsed==G.turn then return false end local best=-1 for _,c in ipairs(G.hand[plr])do if isMon(c)and(c.atk or 0)>best then best=c.atk or 0 end end return best>=0 and hasAnySpec(capSpecs(best),plr) end, activate=function(card,plr,col) card.ignitionUsed=G.turn procPushFrame(function() local cost=choose{kind="card",plr=plr,from="hand", title=name..": SEND MONSTER",filter=isMon,purpose="fuel"} if not cost then return end local sent=G.hand[plr][cost.idx] local cap=(sent and sent.atk)or 0 discardFromHand(plr,cost.idx,"effect") local opp=3-plr if sweep then for c=3,1,-1 do local m=G.mon[opp][c] if m and not m.facedown and getMonAtk(m)<=cap then sendMonsterToGY(opp,c,"effect") end end return end local specs=capSpecs(cap) if not hasAnySpec(specs,plr)then return end local ans=choose{kind="zone",plr=plr,specs=specs,title=name..": DESTROY"} local m=ans and G.mon[ans.plr][ans.col] if m and not m.facedown and getMonAtk(m)<=cap then sendMonsterToGY(ans.plr,ans.col,"effect") end end) end, } end local function stBreaker(cat,title) local function specs(card) local out={{zone="st",facing="up",cat=cat,exclude=card}, {zone="st",facing="down",exclude=card}} if cat=="spell"then out[#out+1]={zone="fs",exclude=card}end return out end local function occupant(ans) if ans.row=="fs"then return G.fs[ans.plr]end return G.st[ans.plr][ans.col] end return{ canActivate=function(card,plr)return hasAnySpec(specs(card),plr or 1)end, activate=activateInFrame(function(card,plr,zone,col) local ans=choose{kind="zone",plr=plr,specs=specs(card),title=title} if not ans then return end local picked=occupant(ans) if not picked then return end procActivate(card,plr,zone,col,function() if occupant(ans)~=picked then return end if picked.cat==cat then destroyPickedST(ans,plr) elseif picked.facedown then animPeekST(ans.plr,ans.col) end end,picked) end), } end local function dragonCount(p) local n=0 for i=1,3 do local m=G.mon[p][i] if m and not m.facedown and m.type=="dragon"then n=n+1 end end for _,c in ipairs(G.gy[p])do if c.type=="dragon"then n=n+1 end end return n end function tickSwords() local controller=3-G.active for c=1,3 do local s=G.st[controller][c] if s and not s.facedown and s.effect=="swords"and s.swordsCounter then s.swordsCounter=s.swordsCounter-1 if s.swordsCounter<=0 then sendSpellTrapToGY(controller,c,"rule")end end end end local function reflectAttack(ctx) if not ctx.attacker then return end ctx.attacker.attackNegated=true changeLp(ctx.plr,-getMonAtk(ctx.attacker)) end MAT_GROUPS={ ["@ancient_gear"]={label="ANCIENT GEAR", match=function(c)return c~=nil and(c.cat or"monster")=="monster"and isAncientGear(c)end}, ["@ojama"]={label="OJAMA", match=function(c)return c~=nil and(c.cat or"monster")=="monster"and isOjama(c)end}, } local function treatedAs(card,nm) if card.treatAs~=nm then return false end for p=1,2 do if monsterCol(p,card)then return true end end if not card.treatAsGY then return false end for p=1,2 do for _,c in ipairs(G.gy[p])do if c==card then return true end end end return false end function matIsLiteral(card,nm) local g=MAT_GROUPS[nm] if g then return g.match(card)end if card==nil then return false end return card.id==nm or treatedAs(card,nm) end function matLabel(nm) local g=MAT_GROUPS[nm] if g then return g.label end return CARDS[nm]and CARDS[nm].name:upper()or nm:upper() end function fusionMaterialsAvailable(fusion,plr,extraId) if not(fusion and fusion.materials)then return false end local order,groups={},{} for _,nm in ipairs(fusion.materials)do if MAT_GROUPS[nm]then groups[#groups+1]=nm else order[#order+1]=nm end end for _,nm in ipairs(groups)do order[#order+1]=nm end local pool,subs,fieldSub={},0,0 local function add(c,onField) if not(c and c.cat=="monster"and c.id)then return end pool[#pool+1]={card=c,field=onField} if fusionSub(c)then subs=subs+1 if onField then fieldSub=fieldSub+1 end end end for _,c in ipairs(G.hand[plr])do add(c,false)end if extraId then add(makeCard(extraId),false)end for col=1,3 do add(G.mon[plr][col],true)end local fromField,short=0,0 for _,nm in ipairs(order)do local pick for _,e in ipairs(pool)do if not e.taken and matIsLiteral(e.card,nm)then if e.field then pick=e;break end if not pick then pick=e end end end if pick then pick.taken=true if pick.field then fromField=fromField+1 end elseif MAT_GROUPS[nm]then return false else short=short+1 end end if short>1 then return false end if short==1 then if subs<1 then return false end fromField=fromField+math.min(1,fieldSub) end return firstEmpty(G.mon[plr])~=nil or fromField>0 end local function fusionAssemble(plr,fusionIdx,title,commit) local fusionCard=G.extra[plr][fusionIdx] if not fusionCard then return end local picks={} local consumed={hand={},field={}} local needField=(firstEmpty(G.mon[plr])==nil) local fieldPicked=false local subUsed=false local function matches(c,nm) if not c then return false end if matIsLiteral(c,nm)then return true end return MAT_GROUPS[nm]==nil and not subUsed and fusionSub(c) end local function fieldCopyExists(nm) for fc=1,3 do if matches(G.mon[plr][fc],nm)and not consumed.field[fc]then return true end end return false end local function literalExists(nm) for hi,c in ipairs(G.hand[plr])do if matIsLiteral(c,nm)and not consumed.hand[hi]then return true end end for fc=1,3 do if matIsLiteral(G.mon[plr][fc],nm)and not consumed.field[fc]then return true end end return false end local function laterMaterial(mi,pred) for j=mi+1,#fusionCard.materials do if pred(fusionCard.materials[j])then return true end end return false end local function narrow(items,keep) local out={} for _,it in ipairs(items)do if keep(it)then out[#out+1]=it end end return(#out>0)and out or items end for mi,matName in ipairs(fusionCard.materials)do local mItems={} for hi,c in ipairs(G.hand[plr])do if matches(c,matName)and not consumed.hand[hi]then mItems[#mItems+1]={kind="hand",hi=hi,card=c, name=c.name.." (H)",atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end for fc=1,3 do local m=G.mon[plr][fc] if matches(m,matName)and not consumed.field[fc]then local tag=m.facedown and(" (F"..fc.." SET)")or(" (F"..fc..")") mItems[#mItems+1]={kind="field",fc=fc,card=m, name=m.name..tag,atk=m.atk,def=m.def,lvl=m.lvl,desc=m.desc} end end if not subUsed and literalExists(matName) and laterMaterial(mi,function(nm)return not literalExists(nm)end)then mItems=narrow(mItems,function(it)return not fusionSub(it.card)end) end if#mItems==0 then return end if needField and not fieldPicked and not laterMaterial(mi,fieldCopyExists)then mItems=narrow(mItems,function(it)return it.kind=="field"end) end local mAns=choose{kind="card",plr=plr,items=mItems, title=title..matLabel(matName)} if not mAns then return end if fusionSub(mAns.item.card)and not matIsLiteral(mAns.item.card,matName)then subUsed=true end local it=mAns.item picks[#picks+1]={kind=it.kind,hi=it.hi,fc=it.fc, card=(it.kind=="hand")and G.hand[plr][it.hi]or G.mon[plr][it.fc]} if mAns.item.kind=="hand"then consumed.hand[mAns.item.hi]=true else consumed.field[mAns.item.fc]=true;fieldPicked=true end end local pos=chooseSummonPosition(plr,fusionCard) commit(function() for _,p in ipairs(picks)do local at=(p.kind=="hand")and G.hand[plr][p.hi]or G.mon[plr][p.fc] if at~=p.card then return end end playMaterialTributeAnim(plr,picks) local handIdxs,fieldCols={},{} for _,p in ipairs(picks)do if p.kind=="hand"then handIdxs[#handIdxs+1]=p.hi else fieldCols[#fieldCols+1]=p.fc end end table.sort(handIdxs,function(a,b)return a>b end) local used={} for _,hi in ipairs(handIdxs)do local c=discardFromHand(plr,hi,"effect") if c then c.fusionMaterial=true;used[#used+1]=c end end for _,fc in ipairs(fieldCols)do local m=G.mon[plr][fc] sendMonsterToGY(plr,fc,"send") if m then m.fusionMaterial=true;used[#used+1]=m end end local landCol=firstEmpty(G.mon[plr]) if not landCol then return end local m=table.remove(G.extra[plr],fusionIdx) if not m then return end specialSummon(plr,landCol,m,pos) m.madeFrom=used end) end local function unionFusion(targetId,label,title) local function extraSlot(plr) for i,c in ipairs(G.extra[plr]or{})do if c.id==targetId then return i end end end return{ label=label, canActivate=function(card,plr) if card.ignitionUsed==G.turn then return false end local i=extraSlot(plr) return i~=nil and fusionMaterialsAvailable(G.extra[plr][i],plr) end, activate=function(card,plr,col) card.ignitionUsed=G.turn procPushFrame(function() local i=extraSlot(plr) if i then fusionAssemble(plr,i,title,function(fn)fn()end)end end) end, } end local function battleFetch(attr,title) return{listens={DESTROYED={when=diedInBattle,react=function(self) summonFromDeck(self.plr,title,function(c) return(c.cat or"monster")=="monster"and c.attr==attr and(c.atk or 0)<=1500 end) end}}} end BEHAVIORS={ darkhole={ resolve=function(plr) for i=1,3 do for p=1,2 do if G.mon[p][i]then revealAndDestroyMon(p,i,"effect")end end end checkEquips() end, }, ojamadelta=(function() local TRIO={"ojama_green","ojama_yellow","ojama_black"} local function controls(plr,id) for c=1,3 do local m=G.mon[plr][c] if m and not m.facedown and m.id==id then return true end end return false end return{ canActivate=function(card,plr) plr=plr or 1 for _,id in ipairs(TRIO)do if not controls(plr,id)then return false end end return true end, resolve=function(plr) local opp=3-plr for i=1,3 do if G.mon[opp][i]then revealAndDestroyMon(opp,i,"effect")end end for i=1,3 do if G.st[opp][i]then revealAndDestroyST(opp,i)end end if G.fs[opp]then sendFieldSpellToGY(opp,"effect",plr)end checkEquips() end, } end)(), triwight=(function() local function lowNormal(c) return c~=nil and c.cat=="monster"and not c.effect and not c.treatAs and c.subtype~="fusion"and(c.lvl or 0)<=2 end local function eligible(c)return lowNormal(c)and canSpecialSummon(c)end local function pool(plr) local n=0 for _,c in ipairs(G.gy[plr])do if eligible(c)then n=n+1 end end return n end local function freeZones(plr) local n=0 for c=1,3 do if not G.mon[plr][c]and not zoneSealed(plr,c)then n=n+1 end end return n end return{ canActivate=function(card,plr) plr=plr or 1 return not staticActive("blocksGYMoves")and freeZones(plr)>=3 and pool(plr)>=3 end, resolve=function(plr) if staticActive("blocksGYMoves")then return end local pos=chooseSummonPosition(plr) for i=1,3 do local col=firstEmpty(G.mon[plr]) if not col then return end local ans=choose{kind="card",plr=plr,from="gy", title="TRI-WIGHT "..i.."/3",filter=eligible} if not ans then return end local m=G.gy[plr][ans.idx] if not eligible(m)then return end table.remove(G.gy[plr],ans.idx) specialSummon(plr,col,m,pos) end end, } end)(), ojamapajama=(function() local function protects(m) if not m then return false end if m.name and m.name:find("Armed Dragon",1,true)then return true end return m.subtype=="fusion"and m.type=="machine"and m.attr=="light" end local function ojamaItems(plr,exclude) local out={} for i,c in ipairs(G.hand[plr])do if isOjama(c)then out[#out+1]={kind="hand",at=i,card=c,name=c.name.." (H)", atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end for col=1,3 do local m=G.mon[plr][col] if m and m~=exclude and not m.facedown and isOjama(m)then out[#out+1]={kind="field",at=col,card=m,name=m.name.." (F"..col..")", atk=m.atk,def=m.def,lvl=m.lvl,desc=m.desc} end end return out end local function spendOjama(plr,exclude) local items=ojamaItems(plr,exclude) if#items==0 then return false end local ans=choose{kind="card",plr=plr,items=items,forced=false, title="PAJAMA: SEND OJAMA?",purpose="cost"} local it=ans and ans.item if not it then return false end if it.kind=="hand"then if G.hand[plr][it.at]~=it.card then return false end discardFromHand(plr,it.at,"effect") else if G.mon[plr][it.at]~=it.card then return false end if not sendMonsterToGY(plr,it.at,"send")then return false end end return true end local function decked(plr) for _,e in ipairs(G.deck[plr])do if isOjama(CARDS[e])then return true end end return false end return{ listens=listensOn({EV_ACTIVATE,EV_ATTACK},{ optional=true, speed=2, when=function(self,ctx) if not self.card.facedown then return false end if(ctx.controller or ctx.actor)==self.plr then return false end for c=1,3 do if protects(G.mon[self.plr][c])then return true end end return false end, react=function()end, }), replaceDestroy=function(card,plr) if not protects(card)or not coroutine.isyieldable()then return false end if simHandHidden(plr)then for c=1,3 do local m=G.mon[plr][c] if m and m~=card and not m.facedown and isOjama(m)then sendMonsterToGY(plr,c,"send");return true end end if#G.hand[plr]>0 then discardFromHand(plr,1,"effect");return true end return false end return spendOjama(plr,card) end, stIgnition={ label="ACTIVATE", canActivate=function(card,plr) return G.ph==PH_MAIN and G.active==plr and card.pajamaUsed~=G.turn and decked(plr) end, activate=function(card,plr,col) card.pajamaUsed=G.turn procPushFrame(function() local ans=choose{kind="card",plr=plr,from="deck", title="PAJAMA: ADD OJAMA",filter=isOjama} if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) if#G.hand[plr]==0 then return end local d=choose{kind="card",plr=plr,from="hand",title="PAJAMA: DISCARD",purpose="cost"} if d then discardFromHand(plr,d.idx,"effect")end end) end, }, } end)(), ojamatch=(function() local function ojamaMon(c)return c~=nil and c.cat=="monster"and isOjama(c)end local function armedMon(c) return c~=nil and c.cat=="monster"and c.name~=nil and c.name:find("Armed Dragon",1,true)~=nil end local function tag(c,label) return{card=c,name=c.name..label,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end local function costItems(plr,self) local out={} for i,c in ipairs(G.hand[plr])do if c~=self and isOjama(c)then local it=tag(c," (H)");it.kind="hand";it.at=i;out[#out+1]=it end end for col=1,3 do local m=G.mon[plr][col] if m and not m.facedown and isOjama(m)then local it=tag(m," (F"..col..")");it.kind="field";it.at=col;out[#out+1]=it end end return out end local function searchItems(plr,pred) local out={} for i,e in ipairs(G.deck[plr])do local cd=CARDS[e] if pred(cd)then local it=tag(cd," (D)");it.kind="deck";it.at=i;it.id=e;out[#out+1]=it end end for i,c in ipairs(G.gy[plr])do if pred(c)then local it=tag(c," (GY)");it.kind="gy";it.at=i;out[#out+1]=it end end return out end local function takeSearched(plr,pred,title,got) if#G.hand[plr]>=MAX_HAND then return end local items=searchItems(plr,pred) if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title=title,forced=false} local it=ans and ans.item if not it then return end local card if it.kind=="deck"then if G.deck[plr][it.at]~=it.id then return end card=makeCard(table.remove(G.deck[plr],it.at)) else if G.gy[plr][it.at]~=it.card then return end card=table.remove(G.gy[plr],it.at) end addToHand(plr,card) got[#got+1]=card end return{ canActivate=function(card,plr) plr=plr or 1 if#costItems(plr,card)==0 then return false end return#searchItems(plr,ojamaMon)>0 or#searchItems(plr,armedMon)>0 end, resolve=function(plr) local items=costItems(plr) if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title="OJAMATCH: SEND",purpose="cost"} local it=ans and ans.item if not it then return end local sentName if it.kind=="hand"then local c=G.hand[plr][it.at] if not(c==it.card and isOjama(c))then return end sentName=c.name discardFromHand(plr,it.at,"effect") else local m=G.mon[plr][it.at] if not(m==it.card and isOjama(m))then return end sentName=m.name if not sendMonsterToGY(plr,it.at,"send")then return end end local got={} takeSearched(plr,function(c)return ojamaMon(c)and c.name~=sentName end, "OJAMATCH: OJAMA",got) takeSearched(plr,armedMon,"OJAMATCH: ARMED DRAGON",got) if#got>0 then for _,c in ipairs(got)do c.ojamatchSummon=G.turn end G.extraSummon="ojamatch" end end, gyIgnition={ label="RECYCLE", canActivate=function(card,plr) if staticActive("blocksGYMoves")or#G.deck[plr]==0 then return false end local n=0 for _,c in ipairs(G.gy[plr])do if ojamaMon(c)then n=n+1 end end return n>=3 end, acetivate=function(card,plr,gyIdx) if G.gy[plr][gyIdx]~=card then return end banishFromGY(plr,gyIdx) procPushFrame(function() for i=1,3 do local ans=choose{kind="card",plr=plr,from="gy", title="OJAMATCH: SHUFFLE "..i.."/3",filter=ojamaMon} if not ans then return end local m=G.gy[plr][ans.idx] if not ojamaMon(m)then return end table.remove(G.gy[plr],ans.idx) table.insert(G.deck[plr],m.id) end shuffle(G.deck[plr]) if G.deckTop then G.deckTop[plr]=0 end drawCard(plr) end) end, }, } end)(), ojamagic={ responseOnly=true, onSentToGY=function(card,plr) for _,id in ipairs({"ojama_green","ojama_yellow","ojama_black"})do if#G.hand[plr]>=MAX_HAND then return end for i,e in ipairs(G.deck[plr])do if e==id then addToHand(plr,makeCard(table.remove(G.deck[plr],i)));break end end end end, }, raigeki={ resolve=function(plr) local opp=3-plr for i=1,3 do if G.mon[opp][i]then revealAndDestroyMon(opp,i,"effect")end end checkEquips() end, }, fissure={ canActivate=function(card,plr)return hasTarget({zone="mon",side="opp"},plr or 1)end, resolve=function(plr) local opp=3-plr local low,lowI=math.huge,nil for i=1,3 do local m=G.mon[opp][i] if m and not m.facedown and canTargetMon(m)then local a=getMonAtk(m) if aCOST and firstEmpty(G.mon[plr])~=nil and not staticActive("blocksGYMoves") and hasTarget({zone="gy",side="own",cat="monster",condition=true, where=eligible},plr) end return{ canActivate=function(card,plr)return live(plr or 1)end, activate=activateInFrame(function(card,plr,zone,col) if not live(plr)then return end local ans=choose{kind="card",plr=plr,from="gy",title="PREMATURE BURIAL", filter=eligible} if not ans then return end local m=G.gy[plr][ans.idx] if not(m and eligible(m))then return end changeLp(plr,-COST) procActivate(card,plr,zone,col,function() if staticActive("blocksGYMoves")then return end local emptyCol,idx=firstEmpty(G.mon[plr]),gyIndex(plr,m) if not(emptyCol and idx)then return end table.remove(G.gy[plr],idx) card.equippedTo={plr=plr,col=emptyCol} card.buried=m specialSummon(plr,emptyCol,m,1) end,m) end), onSentToGY=function(card,plr) local m=card.buried card.buried=nil if not m then return end for p=1,2 do local c=monsterCol(p,m) if c then sendMonsterToGY(p,c,"effect");return end end end, } end)(), unitedwestand=(function() local TGT={zone="mon",side="any",facing="up"} return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, activate=equipActivate(TGT,"UNITED WE STAND"), equipBonus=function(target,equip,controller) local tp=controller or equip.equippedTo.plr local n=0 for i=1,3 do if G.mon[tp][i]and not G.mon[tp][i].facedown then n=n+1 end end return n*800,n*800 end, } end)(), cyberemergency=(function() local function target(c) if(c.cat or"monster")~="monster"then return false end return isCyberDragonName(c) or(c.attr=="light"and c.type=="machine"and not canNormalSummon(c)) end local TGT={zone="deck",side="own",condition=true,where=target} return{ canActivate=function(card,plr) plr=plr or 1 return G.cyberEmergency~=G.turn and#G.hand[plr]=MAX_HAND then return end local ans=choose{kind="card",plr=plr,from="deck", title="CYBER EMERGENCY",filter=target} if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) end, } end)(), cyberloadfusion=(function() local function namesCyber(cd) if not(cd and cd.subtype=="fusion"and cd.materials)then return false end for _,nm in ipairs(cd.materials)do if isCyberDragonName(CARDS[nm])then return true end end return false end local function sources(plr) local out={} for c=1,3 do local m=G.mon[plr][c] if m and m.id then out[#out+1]={kind="mon",card=m}end end for _,c in ipairs(G.banish[plr])do if c.id and(c.cat or"monster")=="monster"then out[#out+1]={kind="ban",card=c}end end return out end local function payable(fus,plr) local need,have={},{} for _,nm in ipairs(fus.materials or{})do need[nm]=(need[nm]or 0)+1 end local onField=0 for _,s in ipairs(sources(plr))do for nm in pairs(need)do if matIsLiteral(s.card,nm)then have[nm]=(have[nm]or 0)+1 if s.kind=="mon"then onField=onField+1 end break end end end for nm,n in pairs(need)do if(have[nm]or 0)0 or firstEmpty(G.mon[plr])~=nil end local function offers(plr) local out={} for idx,c in ipairs(G.extra[plr])do if namesCyber(c)and payable(c,plr)then out[#out+1]={extraIdx=idx,card=c,name=c.name,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end return out end local function plan(plr) local fItems=offers(plr) if#fItems==0 then return nil end local fAns=choose{kind="card",plr=plr,items=fItems,title="CYBERLOAD FUSION"} if not fAns then return nil end local fus=G.extra[plr][fAns.item.extraIdx] if not fus then return nil end local taken,picks={},{} for _,nm in ipairs(fus.materials)do local src,items=sources(plr),{} for i,s in ipairs(src)do if not taken[s.card]and matIsLiteral(s.card,nm)then items[#items+1]={srcIdx=i,card=s.card,kind=s.kind, name=s.card.name..((s.kind=="mon")and" (F)"or" (BAN)"), atk=s.card.atk,def=s.card.def,lvl=s.card.lvl,desc=s.card.desc} end end if#items==0 then return nil end local a=choose{kind="card",plr=plr,items=items,title="CYBERLOAD: "..matLabel(nm)} if not a then return nil end local s=src[a.item.srcIdx] if not s then return nil end taken[s.card]=true picks[#picks+1]=s end return{extraIdx=fAns.item.extraIdx,picks=picks,pos=chooseSummonPosition(plr,G.extra[plr][fAns.item.extraIdx])} end local function apply(plr,p) for _,it in ipairs(p.picks)do local here=(it.kind=="mon")and monsterCol(plr,it.card) if it.kind~="mon"then for _,c in ipairs(G.banish[plr])do if c==it.card then here=true;break end end end if not here then return end end for _,it in ipairs(p.picks)do if it.kind=="mon"then local col=monsterCol(plr,it.card) if col then G.mon[plr][col]=nil end else for i,c in ipairs(G.banish[plr])do if c==it.card then table.remove(G.banish[plr],i);break end end end table.insert(G.deck[plr],it.card.id) end shuffle(G.deck[plr]) if G.deckTop then G.deckTop[plr]=0 end checkEquips() local col=firstEmpty(G.mon[plr]) if not col then return end local m=table.remove(G.extra[plr],p.extraIdx) if not m then return end specialSummon(plr,col,m,p.pos) G.cyberload={turn=G.turn,plr=plr,only=m} end return{ canActivate=function(card,plr)return#offers(plr or 1)>0 end, activate=activateInFrame(function(card,plr,zone,col) local p=plan(plr) if not p then return end procActivate(card,plr,zone,col,function()apply(plr,p)end) end), } end)(), defusion=(function() local TGT={zone="mon",side="any",facing="up", where=function(c)return c.subtype=="fusion"end} local function isFusion(c) return c~=nil and not c.facedown and c.subtype=="fusion" end local function pick(plr) local t=choose{kind="zone",plr=plr,spec=TGT,title="DE-FUSION"} if not t then return nil end local m=G.mon[t.plr][t.col] return isFusion(m)and m or nil end local function split(plr,m) local mp,mc for p=1,2 do local c=monsterCol(p,m);if c then mp,mc=p,c end end if not(mp and isFusion(m))then return end local used=m.madeFrom G.mon[mp][mc]=nil m.madeFrom=nil table.insert(G.extra[ownerOf(m,mp)],m) checkEquips() if not used then return end local gy,idx=G.gy[plr],{} for _,c in ipairs(used)do local at for i,g in ipairs(gy)do if g==c and not idx[i]then at=i;break end end if not at then return end idx[at]=true end local free=0 for c=1,3 do if not G.mon[plr][c]then free=free+1 end end if free<#used then return end local order={} for i in pairs(idx)do order[#order+1]=i end table.sort(order,function(a,b)return a>b end) for _,i in ipairs(order)do local col=firstEmpty(G.mon[plr]) if not col then return end specialSummon(plr,col,table.remove(gy,i),1) end end local function chained(self,ctx) local m=pick(self.plr) if m then split(self.plr,m)end end return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, activate=activateInFrame(function(card,plr,zone,col) local m=pick(plr) if not m then return end pushActivationLink({card=card,col=col,zone=zone,plr=plr},function() split(plr,m) end) end), listens={ ATTACK={ optional=true,speed=2, when=function(self,ctx)return isFusion(ctx.attacker)or isFusion(ctx.target)end, react=chained, }, ACTIVATE={ optional=true,speed=2, when=function(self,ctx) return ctx.controller~=self.plr and hasTarget(TGT,self.plr) end, react=chained, }, }, } end)(), mst=(function() local function mstWhen(self,ctx) return G.active==(3-self.plr)and stOrFsHasTarget(self.card,self.plr) end local function mstReact(self,ctx) local ans=choose{kind="zone",plr=self.plr,specs=stOrFsSpecs(self.card), title="MST (CHAINED)"} if not ans then return end destroyPickedST(ans,self.plr) end return{ canActivate=function(card,plr)return stOrFsHasTarget(card,plr or 1)end, activate=activateInFrame(function(card,plr,zone,col) local ans=choose{kind="zone",plr=plr,specs=stOrFsSpecs(card),title="MST"} if not ans then return end pushActivationLink({card=card,col=col,zone=zone,plr=plr},function() destroyPickedST(ans,plr) end) end), listens=quickPlayListens{optional=true,speed=2,when=mstWhen,react=mstReact}, } end)(), stampingdestruction={ canActivate=function(card,plr) plr=plr or 1 return hasTarget({zone="mon",side="own",type="dragon",facing="up",condition=true},plr) and stOrFsHasTarget(card,plr) end, activate=activateInFrame(function(card,plr,zone,col) local ans=choose{kind="zone",plr=plr,title="STAMPING DESTRUCTION", specs=stOrFsSpecs(card)} if not ans then return end pushActivationLink({card=card,col=col,zone=zone,plr=plr},function() if destroyPickedST(ans,plr)then changeLp(ans.plr,-500)end end) end), }, potofgreed={ canActivate=function(card,plr)plr=plr or 1;return#G.deck[plr]>=2 and#G.hand[plr]0 end, resolve=function(plr) local items=pool(plr) if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title="MAGNET CIRCLE LV2"} if not ans or not ans.item then return end summonPicked(plr,ans.item) end, } end)(), limiterremoval=(function() local function isOwnMachine(m) return m and not m.facedown and m.type=="machine" end local function overload(plr) for c=1,3 do local m=G.mon[plr][c] if isOwnMachine(m)and not effectImmune(m)then addTurnMod(m,"atk",getMonAtk(m)) m.destroyAtEnd=G.turn end end end local function hasMachine(plr) for c=1,3 do if isOwnMachine(G.mon[plr][c])then return true end end return false end return{ canActivate=function(card,plr)return hasMachine(plr or 1)end, resolve=function(plr)overload(plr)end, listens=quickPlayListens{optional=true,speed=2, when=function(self,ctx)return G.active==(3-self.plr)and hasMachine(self.plr)end, react=function(self,ctx)overload(self.plr)end}, } end)(), gracefuldice=(function() local function applyBuff(plr) local r=choose{kind="dice"} if not r then return end local boost=r*100 for c=1,3 do local m=G.mon[plr][c] if m and not m.facedown then addTurnMod(m,"atk",boost);addTurnMod(m,"def",boost) end end end local function gdWhen(self,ctx) return G.active==(3-self.plr)and hasMonsters(self.plr) end return{ canActivate=function(card,plr) return hasTarget({zone="mon",side="own",condition=true},plr or 1) end, resolve=function(plr)applyBuff(plr)end, listens=quickPlayListens{optional=true,speed=2,when=gdWhen, react=function(self,ctx)applyBuff(self.plr)end}, } end)(), enemycontroller=(function() local EC_TGT={zone="mon",side="opp",facing="up"} local EC_TRIB={zone="mon",side="own",condition=true,where=function(m)return tributable(m)end} local function ecPick(plr) local opp=3-plr local items={{"CHANGE POS","pos"}} if hasTarget(EC_TRIB,plr)then items[#items+1]={"TAKE CONTROL","control"}end local effect=choose{kind="menu",plr=plr,forced=true,items=items} if not effect then return nil end if effect=="pos"then local target=choose{ kind="zone",plr=plr,spec=EC_TGT,title="EC: CHANGE POS", } if not target then return nil end local targetCard=G.mon[target.plr][target.col] if not targetCard or targetCard.facedown then return nil end return effect,{targetPlr=target.plr,targetCol=target.col,targetCard=targetCard} else local trib=choose{ kind="zone",plr=plr,title="EC: TRIBUTE",spec=EC_TRIB, } if not trib then return nil end local target=choose{ kind="zone",plr=plr,spec=EC_TGT,title="EC: TAKE CONTROL", } if not target then return nil end local targetCard=G.mon[opp][target.col] if not targetCard or targetCard.facedown then return nil end return effect,{tribCol=trib.col,targetCol=target.col,targetCard=targetCard} end end local function ecApply(plr,effect,p) if effect=="pos"then local m=G.mon[p.targetPlr]and G.mon[p.targetPlr][p.targetCol] if m==p.targetCard and not m.facedown then togglePosition(m)end else local tribCard=G.mon[plr][p.tribCol] if tribCard then sendMonsterToGY(plr,p.tribCol,"tribute") raise(EV_TRIBUTED,{card=tribCard,plr=plr,actor=plr}) end if not p.targetCard.facedown then takeControl(plr,p.targetCol,p.targetCard)end end end local function ecWhen(self,ctx) return G.active==(3-self.plr)and hasTarget(EC_TGT,self.plr) end local function ecTargetPick(self,ctx) local effect,p=ecPick(self.plr) if not effect then return nil end return{target=p.targetCard,params={effect=effect,p=p}} end local function ecReact(self,ctx,params)ecApply(self.plr,params.effect,params.p)end return{ canActivate=function(card,plr)return hasTarget(EC_TGT,plr or 1)end, activate=activateInFrame(function(card,plr,zone,col) local effect,p=ecPick(plr) if not effect then return end procActivate(card,plr,zone,col,function() ecApply(plr,effect,p) end,p.targetCard) end), listens=quickPlayListens{optional=true,speed=2,when=ecWhen, targetPick=ecTargetPick,react=ecReact}, } end)(), gianttrunade={ resolve=function(plr,ctx) local selfCard=ctx and ctx.source for p=1,2 do for c=1,3 do if G.st[p][c]and G.st[p][c]~=selfCard then returnSTToHand(p,c)end end returnFSToHand(p) end checkEquips() end, }, changeofheart=(function() local TGT={zone="mon",side="opp"} return{ canActivate=function(card,plr) plr=plr or 1 return firstEmpty(G.mon[plr])and hasTarget(TGT,plr) end, activate=targetMonsterActivate(TGT,"CHANGE OF HEART", function(plr,tp,tc,m)takeControl(plr,tc,m)end), } end)(), reinforcements=(function() local function isWarrior(c) return c.cat=="monster"and c.type=="warrior"and(c.lvl or 0)<=4 end local TGT={zone="deck",side="own",condition=true,where=isWarrior} return{ canActivate=function(card,plr) plr=plr or 1 return#G.hand[plr]=MAX_HAND then return end local ans=choose{ kind="card",plr=plr,from="deck",title="REINFORCEMENTS: WARRIOR", filter=isWarrior, } if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) end, } end)(), fusionrecovery=(function() local function isMaterial(c)return c.fusionMaterial==true end local POLY={zone="gy",side="own",cat="spell",condition=true,where=isPoly} local MAT={zone="gy",side="own",cat="monster",condition=true,where=isMaterial} return{ canActivate=function(card,plr) plr=plr or 1 return not staticActive("blocksGYMoves")and hasTarget(POLY,plr) end, resolve=function(plr) if staticActive("blocksGYMoves")then return end recoverPoly(plr) if not hasTarget(MAT,plr)then return end searchGY(plr,"FUSION RECOVERY: MATERIAL",isMaterial,true) end, } end)(), emergencyprovisions=(function() local function specs(card) return{{zone="st",side="own",exclude=card},{zone="fs",side="own",exclude=card}} end return{ canActivate=function(card,plr)return hasAnySpec(specs(card),plr or 1)end, resolve=function(plr,ctx) local card=ctx and ctx.source local sent=0 for i=1,4 do local sp=specs(card) if not hasAnySpec(sp,plr)then break end local ans=choose{kind="zone",plr=plr,specs=sp, title="PROVISIONS "..(sent+1),forced=(i==1)} if not ans then break end if ans.row=="fs"then if not G.fs[ans.plr]then break end sendFieldSpellToGY(ans.plr,"rule") else local c=G.st[ans.plr][ans.col] if not c then break end c.facedown=false sendSpellTrapToGY(ans.plr,ans.col,"effect") end sent=sent+1 end if sent>0 then changeLp(plr,sent*1000)end end, } end)(), righteousjustice=(function() local function heroCount(plr) local n=0 for c=1,3 do if isEHero(G.mon[plr][c])then n=n+1 end end return n end return{ canActivate=function(card,plr) plr=plr or 1 return heroCount(plr)>0 and stOrFsHasTarget(card,plr) end, resolve=function(plr,ctx) local card=ctx and ctx.source local n=heroCount(plr) local taken={} local function free(c)return not taken[c]end local specs={{zone="st",exclude=card,where=free}, {zone="fs",exclude=card,where=free}} for i=1,n do if not hasAnySpec(specs,plr)then return end local ans=choose{ kind="zone",plr=plr,specs=specs, title="R-JUSTICE "..i.."/"..n, forced=(i==1), } if not ans then return end local picked=(ans.row=="fs")and G.fs[ans.plr]or G.st[ans.plr][ans.col] if not picked then return end taken[picked]=true destroyPickedST(ans,plr) end end, } end)(), despell=stBreaker("spell","DE-SPELL"), removetrap=stBreaker("trap","REMOVE TRAP"), nobleman=(function() local TGT={zone="mon",side="any",facing="down"} return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, activate=targetMonsterActivate(TGT,"NOBLEMAN OF CROSSOUT", function(plr,tp,tc)revealAndDestroyMon(tp,tc,"effect")end, function(m)return m.facedown end), } end)(), straylambs={ canActivate=function(card,plr) return countMonsters(plr or 1)<=1 end, resolve=function(plr) for _=1,2 do local col=firstEmpty(G.mon[plr]) if not col then return end local tok=makeToken("Lamb Token","beast","earth",1,0,0,390) tok.pos=2 G.mon[plr][col]=tok summonEvent(tok,plr,col,"special",nil) end end, }, costdown={ canActivate=function(card,plr) plr=plr or 1 if#G.hand[plr]<2 then return false end for _,c in ipairs(G.hand[plr])do if c.cat=="monster"and tribsNeeded(getMonLvl(c)-2)0 end, activate=activateInFrame(function(card,plr,zone,col) local targets=validTargets(plr) if#targets==0 then return end local fItems={} for _,idx in ipairs(targets)do local c=G.extra[plr][idx] fItems[#fItems+1]={extraIdx=idx,card=c,name=c.name,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end local fAns=choose{kind="card",plr=plr,items=fItems,title="POLYMERIZATION"} if not fAns then return end fusionAssemble(plr,fAns.item.extraIdx,"POLY MATERIAL: ", function(fn)procActivate(card,plr,zone,col,fn)end) end), } end)(), fluteofdragon=(function() local function isDragon(c)return c.type=="dragon"end local TGT={zone="hand",side="own",type="dragon",condition=true} return{ canActivate=function(card,plr) plr=plr or 1 return staticActive("dragonProtect",plr)and firstEmpty(G.mon[plr]) and hasTarget(TGT,plr) end, resolve=function(plr) if not staticActive("dragonProtect",plr)then return end for i=1,2 do local emptyCol=firstEmpty(G.mon[plr]) if not emptyCol then return end if not hasTarget(TGT,plr)then return end local ans=choose{ kind="card",plr=plr,from="hand", title="FLUTE: PICK DRAGON ("..(3-i).." LEFT)", filter=isDragon, forced=(i==1), } if not ans then return end local pos=chooseSummonPosition(plr,G.hand[plr][ans.idx]) emptyCol=firstEmpty(G.mon[plr]) if not emptyCol then return end specialSummon(plr,emptyCol,table.remove(G.hand[plr],ans.idx),pos) end end, } end)(), aheroemerges={ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=function(self,ctx) return oppActed(self,ctx)and#G.hand[self.plr]>0 end, react=function(self,ctx) local plr=self.plr local items={} for i=1,#G.hand[plr]do items[i]={realIdx=i,deckIdx=i,name="CARD "..i}end local ans=choose{kind="card",plr=3-plr,items=items,forced=true, title="A HERO EMERGES"} local idx=ans and ans.item and ans.item.realIdx local c=idx and G.hand[plr][idx] if not c then return end if c.cat=="monster"then if not canSpecialSummon(c)then return end local col=firstEmpty(G.mon[plr]) if not col then return end local pos=chooseSummonPosition(plr,c) col=firstEmpty(G.mon[plr]) if not col then return end specialSummon(plr,col,table.remove(G.hand[plr],idx),pos) else discardFromHand(plr,idx,"effect") end end, }, }, }, herosignal=(function() local function eligible(c) return isEHero(c)and(c.cat or"monster")=="monster"and(c.lvl or 0)<=4 and canSpecialSummon(c) end return{ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=function(self,ctx) return oppActed(self,ctx)and ctx.attacker~=nil and firstEmpty(G.mon[self.plr])~=nil end, react=function(self,ctx) local plr=self.plr local items={} local function addItem(source,card,realIdx) if eligible(card)then table.insert(items,{source=source,realIdx=realIdx,deckIdx=realIdx,card=card, name=card.name,atk=card.atk,def=card.def,lvl=card.lvl,desc=card.desc}) end end for i,c in ipairs(G.hand[plr])do addItem("hand",c,i)end for i,id in ipairs(G.deck[plr])do addItem("deck",CARDS[id],i)end if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title="HERO SIGNAL"} if not ans then return end summonPicked(plr,ans.item) end, }, }, } end)(), statuewicked={ responseOnly=true, onSentToGY=function(card,plr) if not card.wasSet then return end local col=firstEmpty(G.mon[plr]) if not col then return end local tok=makeToken("Wicked Token","fiend","dark",4,1000,1000,582) tok.summoned=true G.mon[plr][col]=tok summonEvent(tok,plr,col,"special",nil) end, }, zerogravity=(function() local function eachFaceUp(fn) for p=1,2 do for c=1,3 do local m=G.mon[p][c] if m and not m.facedown and fn(m)then return true end end end return false end local function anyFaceUp()return eachFaceUp(function()return true end)end local function flipAll() eachFaceUp(function(m)if not effectImmune(m)then togglePosition(m)end end) end return{ canActivate=function(card,plr)return anyFaceUp()end, resolve=function(plr)flipAll()end, listens=listensOn({EV_SUMMON,EV_ATTACK},{ optional=true,speed=2, when=function(self,ctx)return oppActed(self,ctx)and anyFaceUp()end, react=function(self,ctx)flipAll()end, }), } end)(), walldisruption={ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=oppActed, react=function(self,ctx) local opp=3-self.plr local n=0 for i=1,3 do if G.mon[opp][i]then n=n+1 end end for i=1,3 do local m=G.mon[opp][i] if m and m.pos==1 and not m.facedown and not trapImmune(m)and not effectImmune(m)then m.atkMod=(m.atkMod or 0)-800*n end end end, }, }, }, mirrorforce={ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=oppActed, react=function(self,ctx) local opp=3-self.plr G.battleAnim=nil for i=1,3 do local m=G.mon[opp][i] if m and m.pos==1 and not m.facedown and not trapImmune(m)then sendMonsterToGY(opp,i,"effect")end end end, }, }, }, kunaichain=(function() local KUNAI_TGT={zone="mon",side="own",facing="up", where=function(c)return not trapImmune(c)end} return{ responseOnly=true, equipBonus=function()return 500,0 end, listens={ ATTACK={ optional=true, speed=2, when=function(self,ctx) return self.card.subtype=="normal"and ctx.actor~=self.controller and(not trapImmune(ctx.attacker)or hasTarget(KUNAI_TGT,self.plr)) end, react=function(self,ctx) local hasOwn=hasTarget(KUNAI_TGT,self.plr) local canPos=ctx.attacker and not trapImmune(ctx.attacker) local items={} if hasOwn and canPos then table.insert(items,{"BOTH","both"})end if canPos then table.insert(items,{"CHANGE POS","pos"})end if hasOwn then table.insert(items,{"EQUIP","equip"})end if#items==0 then return end local key=choose{kind="menu",plr=self.plr,forced=true,items=items} if key=="pos"or key=="both"then if ctx.attacker then ctx.attacker.pos=2 end end if key=="equip"or key=="both"then local target=choose{ kind="zone",plr=self.plr,spec=KUNAI_TGT, title="KUNAI: EQUIP TARGET", } if target and G.st[self.plr][self.col]==self.card then self.card.subtype="equip" self.card.equippedTo={plr=target.plr,col=target.col} end end end, }, }, } end)(), magnetforce=(function() local function shieldable(m)return m and(m.type=="machine"or m.type=="rock")end return{ canActivate=function(card,plr) for p=1,2 do for c=1,3 do if shieldable(G.mon[p][c])then return true end end end return false end, resolve=function(plr)G.magnetForce={turn=G.turn,plr=plr}end, } end)(), waboku={ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=oppActed, react=function(self,ctx) G.battleShield=G.battleShield or{} G.battleShield[self.plr]=G.turn end, }, }, }, magiccylinder={ responseOnly=true, listens={ ATTACK={ optional=true, speed=2, when=function(self,ctx)return oppActed(self,ctx)and not trapImmune(ctx.attacker)end, targetCard=function(self,ctx)return ctx.attacker end, react=function(self,ctx)reflectAttack(ctx)end, }, }, }, solemnjudgement=(function() local function canPay(self)return G.lp[self.plr]>=2 end local function pay(self)changeLp(self.plr,-(G.lp[self.plr]//2))end return{ responseOnly=true, listens={ [EV_SUMMON]={ optional=true, speed=3, when=function(self,ctx) return ctx.actor~=self.controller and canPay(self) and not summonUnnegatable(ctx.card) end, targetCard=function(self,ctx)return ctx.card end, react=function(self,ctx) pay(self) if G.mon[ctx.plr]and G.mon[ctx.plr][ctx.col]==ctx.card then negateSummon(ctx.plr,ctx.col) end end, }, [EV_ACTIVATE]={ optional=true, speed=3, when=function(self,ctx) return ctx.card and(ctx.card.cat=="spell"or ctx.card.cat=="trap") and ctx.controller~=self.plr and canPay(self) end, react=function(self,ctx) pay(self) negateLinkBelow(self.card,self.plr) end, }, }, } end)(), traphole={ responseOnly=true, listens={ SUMMON={ optional=true, speed=2, when=function(self,ctx) return ctx.kind~="special"and not ctx.facedown and getMonAtk(ctx.card)>=1000 and ctx.actor~=self.controller and canTargetMon(ctx.card)and not trapImmune(ctx.card) end, targetCard=function(self,ctx)return ctx.card end, react=function(self,ctx) if G.mon[ctx.plr]and G.mon[ctx.plr][ctx.col]==ctx.card then sendMonsterToGY(ctx.plr,ctx.col,"effect") end end, }, }, }, callhaunted=(function() local function revive(self) local plr=self.plr local target=choose{ kind="card",from="gy",plr=plr, filter=function(c)return c.cat=="monster"and canSpecialSummon(c)end, title="CALL OF THE HAUNTED", } if not target then return end local emptyCol=firstEmpty(G.mon[plr]) if not emptyCol then return end local m=G.gy[plr][target.idx] if not(m and m.cat=="monster")then return end table.remove(G.gy[plr],target.idx) self.card.linkedMon=m;m.linkedTrap=self.card specialSummon(plr,emptyCol,m,1) end local entry={ optional=true, speed=2, when=function(self,ctx) return self.card.facedown and oppActed(self,ctx)and canReviveMonster(self.plr) end, react=function(self,ctx)revive(self)end, } return{ onNegate=function(card) if not card.linkedMon then return end local m=card.linkedMon for mp=1,2 do for mc=1,3 do if G.mon[mp][mc]==m then sendMonsterToGY(mp,mc,"effect")end end end card.linkedMon=nil end, onResume=function(card,p,c) if not card.linkedMon then sendSpellTrapToGY(p,c,"rule")end end, listens=listensOn({EV_ATTACK,EV_PHASE},entry), canActivate=function(card,plr)return canReviveMonster(plr or 1)end, activate=function(opts) local card,col=opts.card,opts.col local plr=opts.plr or 1 procActivate(card,plr,"st",col,function() revive{card=card,plr=plr,zone="st",col=col} end) end, } end)(), magicjammer={ responseOnly=true, listens={ ACTIVATE={ optional=true, speed=3, when=function(self,ctx) return ctx.card and ctx.card.cat=="spell"and ctx.controller~=self.plr and#G.hand[self.plr]>=1 end, react=function(self,ctx) local ans=choose{kind="card",plr=self.plr,from="hand",title="MAGIC JAMMER COST"} if not ans then return end discardFromHand(self.plr,ans.idx,"cost") negateLinkBelow(self.card,self.plr) end, }, }, }, cyberneticoverflow=(function() local function sources(plr) local out={} for _,c in ipairs(G.hand[plr])do if matIsLiteral(c,"cyber_dragon")then out[#out+1]={kind="hand",card=c}end end for col=1,3 do local m=G.mon[plr][col] if m and not m.facedown and matIsLiteral(m,"cyber_dragon")then out[#out+1]={kind="mon",card=m} end end for _,c in ipairs(G.gy[plr])do if matIsLiteral(c,"cyber_dragon")then out[#out+1]={kind="gy",card=c}end end return out end local function oppHasCard(plr) local opp=3-plr for c=1,3 do if G.mon[opp][c]or G.st[opp][c]then return true end end return G.fs[opp]~=nil end local function banishOne(plr,it) if it.kind=="mon"then local col=monsterCol(plr,it.card) if col then banishMonster(plr,col)end elseif it.kind=="gy"then for i,c in ipairs(G.gy[plr])do if c==it.card then banishFromGY(plr,i);return end end else for i,c in ipairs(G.hand[plr])do if c==it.card then banishFromHand(plr,i);return end end end end local DEST={{zone="mon",side="opp"},{zone="st",side="opp"}} local function fire(plr) G.overflowUsed=G.turn local usedLv,n={},0 for i=1,3 do local items={} for _,s in ipairs(sources(plr))do local lv=s.card.lvl or 0 if not usedLv[lv]then items[#items+1]={card=s.card,kind=s.kind,lv=lv,name=s.card.name, atk=s.card.atk,def=s.card.def,lvl=lv,desc=s.card.desc} end end if#items==0 then break end local a=choose{kind="card",plr=plr,items=items,forced=(i==1), title="OVERFLOW: BANISH "..i} if not a then break end usedLv[a.item.lv]=true banishOne(plr,a.item) n=n+1 end checkEquips() for i=1,n do if not hasAnySpec(DEST,plr)then break end local a=choose{kind="zone",plr=plr,specs=DEST, title="OVERFLOW: DESTROY "..i.."/"..n} if not a then break end if a.row=="mon"then if G.mon[a.plr][a.col]then revealAndDestroyMon(a.plr,a.col,"effect")end else destroyPickedST(a,plr)end end checkEquips() end return{ canActivate=function(card,plr) plr=plr or 1 return G.overflowUsed~=G.turn and#sources(plr)>0 and oppHasCard(plr) end, resolve=fire, onSentToGY=function(card,plr,reason) if reason~="effect"or not card.wasSet then return end procPushFrame(function() if#G.hand[plr]>=MAX_HAND then return end local ans=choose{kind="card",plr=plr,from="deck", title="OVERFLOW: CYBER S/T",filter=isCyberST} if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) end) end, listens=quickPlayListens{optional=true,speed=2, when=function(self,ctx) return oppActed(self,ctx)and G.overflowUsed~=G.turn and#sources(self.plr)>0 and oppHasCard(self.plr) end, react=function(self,ctx)fire(self.plr)end}, } end)(), trapjammer={ responseOnly=true, listens={ ACTIVATE={ optional=true, speed=3, when=function(self,ctx) return G.ph==PH_BATTLE and ctx.card and ctx.card.cat=="trap" and ctx.controller~=self.plr end, react=function(self,ctx)negateLinkBelow(self.card,self.plr)end, }, }, }, seventools={ responseOnly=true, listens={ ACTIVATE={ optional=true, speed=3, when=function(self,ctx) return ctx.card and ctx.card.cat=="trap"and ctx.controller~=self.plr and G.lp[self.plr]>=1000 end, react=function(self,ctx) changeLp(self.plr,-1000) negateLinkBelow(self.card,self.plr) end, }, }, }, negateattack={ responseOnly=true, listens={ ATTACK={ optional=true, speed=3, when=function(self,ctx)return oppActed(self,ctx)and not trapImmune(ctx.attacker)end, react=function()doAdvancePhase(PH_END)end, }, }, }, ringdestruction=(function() local function ringSpec(plr) local cap=G.lp[3-plr] return{zone="mon",side="opp",facing="up", where=function(c)return getMonAtk(c)<=cap and not trapImmune(c)and not hasUnknownStats(c)end} end local function when(self,ctx) return G.active==(3-self.plr)and hasTarget(ringSpec(self.plr),self.plr) end local function targetPick(self,ctx) local t=choose{ kind="zone",plr=self.plr,spec=ringSpec(self.plr),title="RING OF DESTRUCTION", } if not t then return nil end local m=G.mon[t.plr][t.col] if not m or m.facedown then return nil end return{target=m,params={plr=t.plr,col=t.col,card=m,dmg=(m.atk or 0)//2}} end local function react(self,ctx,params) local opp=3-self.plr if G.mon[params.plr][params.col]~=params.card then return end sendMonsterToGY(params.plr,params.col,"effect") changeLp(self.plr,-params.dmg) if G.lp[self.plr]>0 then changeLp(opp,-params.dmg)end end return{ responseOnly=true, listens=quickPlayListens{optional=true,speed=2,when=when, targetPick=targetPick,react=react}, } end)(), darkillusion={ responseOnly=true, listens={ ACTIVATE={ optional=true, speed=3, when=function(self,ctx) local t=ctx.link and ctx.link.target return t and not t.facedown and t.attr=="dark"and ctx.controller~=self.plr end, react=function(self,ctx)negateLinkBelow(self.card,self.plr)end, }, }, }, crushcard=(function() local TRIB={zone="mon",side="own",attr="dark",condition=true, where=function(m)return getMonAtk(m)<=1000 end} local CRUSH_ATK=1500 local function body(plr) local opp=3-plr local t=choose{kind="zone",plr=plr,spec=TRIB,title="CRUSH: TRIBUTE DARK"} if not t then return end sendMonsterToGY(t.plr,t.col,"tribute") local shown=animRevealHidden(opp) if shown then waitAnim(shown)end for c=1,3 do local m=G.mon[opp][c] if m and getMonAtk(m)>=CRUSH_ATK and not trapImmune(m)then m.facedown=false sendMonsterToGY(opp,c,"send") end end for i=#G.hand[opp],1,-1 do local h=G.hand[opp][i] if h.cat=="monster"and getMonAtk(h)>=CRUSH_ATK then discardFromHand(opp,i,"effect") end end checkEquips() end local entry={ optional=true, speed=2, when=function(self,ctx) return ctx.actor~=self.controller and hasTarget(TRIB,self.plr) end, react=function(self,ctx)body(self.plr)end, } return{ canActivate=function(card,plr)return hasTarget(TRIB,plr or 1)end, resolve=function(plr,ctx)body(plr)end, listens=listensOn({EV_SUMMON,EV_ATTACK},entry), } end)(), royaldecree={ static={blocksTraps=true}, resolve=function()sweepTrapHook("onNegate")end, listens={ ACTIVATE={ optional=true, speed=2, when=function(self,ctx) return self.card.facedown and ctx.card and ctx.card.cat=="trap" and ctx.controller~=self.plr end, react=function(self,ctx) sweepTrapHook("onNegate") negateLinkBelow(self.card,self.plr) end, }, }, }, spellbindingcircle=(function() local TGT={zone="mon",side="opp",where=function(c)return not trapImmune(c)end} local function bind(card,plr) local t=choose{kind="zone",plr=plr,spec=TGT,title="SPELLBINDING CIRCLE"} if not t then return nil end local m=G.mon[t.plr][t.col] if not m then return nil end card.binds=true card.linkedMon=m;m.linkedTrap=card return m end return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, activate=function(opts) local card,col=opts.card,opts.col local plr=opts.plr or 1 procActivate(card,plr,"st",col,function() bind(card,plr) end) end, listens={ ATTACK={ optional=true, speed=2, when=function(self,ctx) return self.card.facedown and ctx.actor~=self.controller and hasTarget(TGT,self.plr) end, react=function(self,ctx) local m=bind(self.card,self.plr) if m and m==ctx.attacker then m.attackNegated=true end end, }, }, } end)(), gravitybind={ static={gravityBind=true}, listens={ PHASE={ optional=true, speed=2, when=function(self,ctx) return self.card.facedown and ctx.toPhase==PH_BATTLE and ctx.actor~=self.controller end, react=function(self,ctx)end, }, }, }, kuriboh={ speed=2, handValue=900, listens={ BEFORE_DAMAGE={ optional=true, speed=2, when=function(self,ctx) return self.zone=="hand"and ctx.plr==self.plr and(ctx.dmg or 0)>0 end, react=function(self,ctx) ctx.negated=true end, }, }, }, exodia=(function() local PIECES=EXODIA_PIECES local PART_OF={} for _,id in ipairs(PIECES)do PART_OF[id]=true end local FIGURE={{"right_leg_forbidden",-8,20},{"left_leg_forbidden",8,20}, {"right_arm_forbidden",-13,4},{"left_arm_forbidden",13,4},{"exodia",0,0}} local SCALE=2 local BODY_W,BODY_H,BODY_TIP=16,20,-4 local GLOW_STEP=20 local PART_STEP=6 local PIECE_N=#PIECES local FIGURE_T=GLOW_STEP*PIECE_N local ox,oy do local x0,y0,x1,y1=math.huge,math.huge,-math.huge,-math.huge for _,p in ipairs(FIGURE)do x0=math.min(x0,p[2]);x1=math.max(x1,p[2]+16) y0=math.min(y0,p[3]);y1=math.max(y1,p[3]+16) end ox,oy=-(((x0+x1)*SCALE)//2),-(((y0+y1)*SCALE)//2) end local function isPart(c)return PART_OF[c.id]end return{ winCondition=function(plr) local have={} for _,c in ipairs(G.hand[plr])do if c.id then have[c.id]=true end end for _,id in ipairs(PIECES)do if not have[id]then return false end end return true end, winHold=210, winAnim=function(plr,t) glowHandCards(plr,isPart,math.min(PIECE_N,t//GLOW_STEP+1)) if t=MAX_HAND then return end local ans=choose{ kind="card",plr=plr,from="deck",title="SANGAN ATK<=1500", filter=function(card) return(card.cat or"monster")=="monster"and card.atk and card.atk<=1500 and not card.unknownStats end, } if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) end, }, }, }, thunderdragon={ handIgnition={ label="DISC: SEARCH", canActivate=function(card,plr) return hasTarget({zone="deck",side="own",id="thunder_dragon",condition=true},plr) end, activate=function(card,plr,handIdx) discardFromHand(plr,handIdx,"effect") local added=0 for i=#G.deck[plr],1,-1 do if added>=2 or#G.hand[plr]>=MAX_HAND then break end if G.deck[plr][i]=="thunder_dragon"then addToHand(plr,makeCard(table.remove(G.deck[plr],i))) added=added+1 end end end, }, }, maneater={ listens={ FLIP={ when=isSelf, react=function(self,ctx) local tp,tc=pickFieldMonster(self.plr,"MAN-EATER") if not tp then return end revealAndDestroyMon(tp,tc,"effect") checkEquips() end, }, }, }, magicianfaith={ listens={ FLIP={ when=isSelf, react=function(self,ctx) local plr=self.plr if staticActive("blocksGYMoves")then return end local ans=choose{ kind="card",plr=plr,from="gy", title="FAITH: SPELL TO HAND", filter=function(c)return c.cat=="spell"end, } if not ans then return end addToHand(plr,table.remove(G.gy[plr],ans.idx)) end, }, }, }, legion=(function() local function search(plr) G.legionSearchUsed=G.legionSearchUsed or{} if G.legionSearchUsed[plr]then return end G.legionSearchUsed[plr]=true local items={} local function addItem(source,card,realIdx) if card.type=="spellcaster"and not card.effect then table.insert(items,{source=source,realIdx=realIdx,deckIdx=realIdx,card=card, name=card.name,atk=card.atk,def=card.def,lvl=card.lvl,desc=card.desc}) end end for i,id in ipairs(G.deck[plr])do addItem("deck",CARDS[id],i)end if not staticActive("blocksGYMoves")then for i,card in ipairs(G.gy[plr])do addItem("gy",card,i)end end if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title="LEGION SPELLCASTER"} if not ans then return end local it=ans.item if it.source=="deck"then addToHand(plr,makeCard(table.remove(G.deck[plr],it.realIdx))) else addToHand(plr,table.remove(G.gy[plr],it.realIdx)) end end return{ ignition={ label="EXTRA SUMMON", canActivate=function(_,plr) return not G.legionSummonUsed and not G.extraSummon end, activate=function()G.extraSummon="spellcaster";G.legionSummonUsed=true end, }, listens=listensOn({EV_DESTROYED,EV_TRIBUTED},{ when=isSelf, react=function(self,ctx)search(self.plr)end, }), } end)(), goddess={ fusionSub=true, }, ehelectrum={ atkBonus=function(card) local n=0 for p=1,2 do for c=1,3 do local m=G.mon[p][c] if m and not m.facedown then n=n+1 end end end return n*300 end, }, ehflamewingman={ onAfterAttack=halfAtkBurn, }, ehshiningwingman={ onAfterAttack=halfAtkBurn, atkBonus=function(card) local p=sideOf(card) if not p then return 0 end local n=0 for _,c in ipairs(G.gy[p])do if isEHero(c)then n=n+1 end end return n*300 end, }, ehthundergiant={ ignition=discardThenDestroy("THUNDER GIANT","THUNDERBLAST", function(card) local cap=getMonAtk(card)or 0 return{{zone="mon",side="any",facing="up", where=function(c)return(c.atk or 0)0 end return{ noNormalSummon=true, noSpecialSummon=true, handIgnition={ label="TRIBUTE LV7",summonsSelf=true, canActivate=function(_,plr) for _,t in ipairs(listTargets(LV7,plr))do if landsAfter(plr,t.col)then return true end end return false end, activate=function(card,plr,handIdx) procPushFrame(function() if not hasTarget(LV7,plr)then return end local t=choose{kind="zone",plr=plr,spec=LV7,title="ARMED LV10: TRIBUTE"} if not t then return end local m=G.mon[t.plr][t.col] local pos=chooseSummonPosition(plr,card) if G.hand[plr][handIdx]~=card or G.mon[t.plr][t.col]~=m then return end if not landsAfter(plr,t.col)then return end sendMonsterToGY(t.plr,t.col,"tribute") local col=firstEmpty(G.mon[plr]) if not col then return end table.remove(G.hand[plr],handIdx) specialSummon(plr,col,card,pos) end) end, }, ignition={ label="BLAST ALL", canActivate=function(card,plr) return card.ignitionUsed~=G.turn and#G.hand[plr]>0 and hasTarget({zone="mon",side="opp",facing="up"},plr) end, activate=function(card,plr,col) card.ignitionUsed=G.turn procPushFrame(function() local cost=choose{kind="card",plr=plr,from="hand",title="ARMED LV10: SEND",purpose="cost"} if not cost then return end discardFromHand(plr,cost.idx,"effect") local opp=3-plr for c=3,1,-1 do local mm=G.mon[opp][c] if mm and not mm.facedown then sendMonsterToGY(opp,c,"effect")end end end) end, }, } end)(), armedlv7={ noNormalSummon=true, noSpecialSummon=true, ignition=armedBlast("ARMED LV7","BLAST ALL",true), }, ojamablue=(function() local function ojamaNamed(c)return isOjama(c)end local OJAMA={zone="deck",side="own",condition=true,where=ojamaNamed} return{ listens={ DESTROYED={ when=function(self,ctx) return diedInBattle(self,ctx) and#G.hand[self.plr]=MAX_HAND then return end local ans=choose{ kind="card",plr=plr,from="deck", title="OJAMA BLUE: ADD ("..(3-i).." LEFT)", filter=ojamaNamed, forced=(i==1), } if not ans then return end addToHand(plr,makeCard(table.remove(G.deck[plr],ans.idx))) end end, }, }, } end)(), dmgirl={ atkBonus=function(card) local b=0 for p=1,2 do for _,c in ipairs(G.gy[p])do if c.id=="dark_magician"then b=b+300 end end end return b end, }, busterblader={ atkBonus=function(card) for p=1,2 do if monsterCol(p,card)then return dragonCount(3-p)*500 end end return 0 end, }, darkpaladin={ atkBonus=function(card) return(dragonCount(1)+dragonCount(2))*500 end, listens={ ACTIVATE={ optional=true, speed=2, when=function(self,ctx) return ctx.card and ctx.card.cat=="spell" and ctx.actor and ctx.actor~=self.plr and#G.hand[self.plr]>=1 end, react=function(self,ctx) local ans=choose{kind="card",plr=self.plr,from="hand",title="DARK PALADIN COST"} if not ans then return end discardFromHand(self.plr,ans.idx,"cost") local links=G.proc.chain.links local myIdx=nil for i=#links,1,-1 do if links[i].source==self.card then myIdx=i;break end end if not myIdx or myIdx<=1 then return end local target=links[myIdx-1] target.negated=true local loc=target.sourceLoc if loc and loc.zone=="st"then revealAndDestroyST(loc.plr,loc.col) elseif loc and loc.zone=="fs"then sendFieldSpellToGY(loc.plr,"effect",self.plr)end end, }, }, }, sternmystic={ listens={ FLIP={ when=isSelf, react=function(self,ctx)animFlashFaceDown()end, }, }, }, doublecoston={ tributeValue=function(m,summonCard) return(summonCard and summonCard.attr=="dark")and 2 or nil end, }, ancientgearcastle={ counterOn="summon", fieldBonus=function(card) return isAncientGear(card)and 300 or 0,0 end, tributeSub={ label="TRIB CASTLE", value=function(card,summonCard) return isAncientGear(summonCard)and(card.counters or 0)or nil end, }, resolve=function()end, }, trojanhorse={ tributeValue=function(m,summonCard) return(summonCard and summonCard.attr=="earth")and 2 or nil end, }, redgadget=gadgetSearch("yellow_gadget"), greengadget=gadgetSearch("red_gadget"), yellowgadget=gadgetSearch("green_gadget"), ancientgearchimera=(function() local MODE={green_gadget="green",red_gadget="red",yellow_gadget="yellow"} return{ listens={ SUMMON={ when=isSelf, react=function(self,ctx) local card=self.card card.gadj=nil if ctx.kind~="tribute"then return end local m={} for _,t in ipairs(ctx.tributed)do local key=MODE[t.card.id] if key then m[key]=true end end card.gadj=m end, }, }, atkBonus=function(card) return(card.gadj and card.gadj.green)and 300 or 0 end, onAfterAttack=function(card,plr,atkCol,target,tgtCol,opp) local m=card.gadj if not m then return end if not target then if m.red then changeLp(3-plr,-500)end elseif m.yellow and opp and tgtCol and G.mon[opp][tgtCol]~=target then changeLp(opp,-700) end end, } end)(), lordofd={ static={dragonProtect=true}, }, ancientgear={ handIgnition={ label="SS ANC GEAR",summonsSelf=true, canActivate=function(_,plr) return firstEmpty(G.mon[plr])~=nil and hasTarget({zone="mon",side="own",condition=true, where=isAncientGear},plr) end, activate=function(card,plr,handIdx)handSpecialSummon(card,plr,handIdx,1)end, }, }, ancientgearcannon={ ignition={ label="BURN 500", activate=function(card,plr,col) procPushFrame(function() sendMonsterToGY(plr,col,"tribute") changeLp(3-plr,-500) G.trapLock=G.turn end) end, }, }, aghuntinghound=(function() local function sources(plr) local out={} for i,c in ipairs(G.hand[plr])do if(c.cat or"monster")=="monster"and c.id then out[#out+1]={kind="hand",hi=i,card=c}end end for col=1,3 do local m=G.mon[plr][col] if m and m.id then out[#out+1]={kind="field",fc=col,card=m}end end return out end local function offers(plr) local out={} for i,c in ipairs(G.extra[plr]or{})do if c.subtype=="fusion"and isAncientGear(c)and fusionMaterialsAvailable(c,plr)then out[#out+1]={extraIdx=i,card=c,name=c.name,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end return out end return{ sealsAttackST=true, listens={ SUMMON={ when=function(self,ctx)return ctx.card==self.card and ctx.kind~="special"end, react=function(self,ctx)changeLp(3-self.plr,-600)end, }, }, ignition={ label="AG FUSION", canActivate=function(card,plr) return card.houndUsed~=G.turn and#offers(plr or 1)>0 end, activate=function(card,plr) procPushFrame(function() local fItems=offers(plr) if#fItems==0 then return end local fAns=choose{kind="card",plr=plr,items=fItems,title="AG FUSION"} if not fAns then return end local fus=G.extra[plr][fAns.item.extraIdx] if not fus then return end local src,taken,picks=sources(plr),{},{} for _,nm in ipairs(fus.materials)do local items={} for i,s in ipairs(src)do if not taken[i]and matIsLiteral(s.card,nm)then items[#items+1]={srcIdx=i,card=s.card, name=s.card.name..((s.kind=="hand")and" (H)"or" (F)"), atk=s.card.atk,def=s.card.def,lvl=s.card.lvl,desc=s.card.desc} end end if#items==0 then return end local a=choose{kind="card",plr=plr,items=items,title="HOUND: "..matLabel(nm)} if not a then return end taken[a.item.srcIdx]=true picks[#picks+1]=src[a.item.srcIdx] end card.houndUsed=G.turn local handIdxs,fieldCols={},{} for _,p in ipairs(picks)do if p.kind=="hand"then handIdxs[#handIdxs+1]=p.hi else fieldCols[#fieldCols+1]=p.fc end end table.sort(handIdxs,function(a,b)return a>b end) local used={} for _,hi in ipairs(handIdxs)do local c=discardFromHand(plr,hi,"effect") if c then c.fusionMaterial=true;used[#used+1]=c end end for _,fc in ipairs(fieldCols)do local m=G.mon[plr][fc] sendMonsterToGY(plr,fc,"send") if m then m.fusionMaterial=true;used[#used+1]=m end end local landCol=firstEmpty(G.mon[plr]) if not landCol then return end local m=table.remove(G.extra[plr],fAns.item.extraIdx) specialSummon(plr,landCol,m,chooseSummonPosition(plr,m)) m.madeFrom=used end) end, }, } end)(), ultimateaggolem=(function() local function golemIdx(plr) for i,c in ipairs(G.gy[plr])do if c.id=="ancient_gear_golem"then return i end end end return{ piercing=true, sealsAttackST=true, noSpecialSummon=true, listens={ DESTROYED={ when=function(self,ctx) return ctx.card==self.card and golemIdx(self.plr)~=nil end, react=function(self,ctx) local plr=self.plr local idx=golemIdx(plr) if not(idx and firstEmpty(G.mon[plr]))then return end local pos=chooseSummonPosition(plr,G.gy[plr][idx]) local col=firstEmpty(G.mon[plr]) idx=golemIdx(plr) if not(col and idx)then return end specialSummon(plr,col,table.remove(G.gy[plr],idx),pos) end, }, }, } end)(), ancientgearhowitzer={ cardImmune=true, ignition={ label="HOWITZER", canActivate=function(card)return card.howitzerUsed~=G.turn end, activate=function(card,plr) card.howitzerUsed=G.turn changeLp(3-plr,-1000) end, }, listens={ DESTROYED={ when=diedInBattle, react=function(self,ctx) summonFromDeck(self.plr,"ANCIENT GEAR",function(c) return(c.cat or"monster")=="monster"and isAncientGear(c) end,true) end, }, }, }, ancientgearbeast={ noSpecialSummon=true, sealsAttackST=true, negatesBattleKill=true, }, ancientgeargolem={ noSpecialSummon=true, sealsAttackST=true, piercing=true, }, ancientgearsoldier={ sealsAttackST=true, }, speardragon={ piercing=true, onAfterAttack=function(card)card.pos=2 end, }, bladedge={ piercing=true, }, wildheart={ trapImmune=true, }, gearfried={ listens={ ACTIVATE={ speed=2, when=function(self,ctx) return ctx.card and ctx.card.subtype=="equip" and ctx.link and ctx.link.target==self.card end, react=function(self,ctx)negateLinkBelow(self.card,self.plr)end, }, }, }, rocketwarrior={ battleImmune=function(card,ctrl)return G.active==ctrl end, noBattleDamage=function(card,ctrl)return G.active==ctrl end, onAfterAttack=function(card,plr,atkCol,target,tgtCol,opp) if target and opp and tgtCol and G.mon[opp][tgtCol]==target then addTurnMod(target,"atk",-500) end end, }, reflectbounder={ listens={ ATTACK={ when=function(self,ctx) return ctx.target==self.card and self.card.pos==1 end, react=function(self,ctx) local col=monsterCol(self.plr,self.card) if not col then return end reflectAttack(ctx) sendMonsterToGY(self.plr,col,"effect") end, }, }, }, timewizard={ ignition={ label="ROULETTE", canActivate=function(card)return card.timeWizardUsed~=G.turn end, activate=function(card,plr,col) card.timeWizardUsed=G.turn procPushFrame(function() local r=choose{kind="coin"} if r==1 then for c=1,3 do if G.mon[3-plr][c]then sendMonsterToGY(3-plr,c,"effect")end end else local total=0 for c=1,3 do local m=G.mon[plr][c] if m then total=total+getMonAtk(m)end end for c=1,3 do if G.mon[plr][c]then sendMonsterToGY(plr,c,"effect")end end changeLp(plr,-math.floor(total/2)) end checkEquips() end) end, }, }, kaibaman={ ignition={ label="SS BLUE-EYES", canActivate=function(_,plr) return hasTarget({zone="hand",side="own",id="blue_eyes_w_dragon",condition=true},plr) end, activate=function(_,plr,col) procPushFrame(function() local bewIdx=nil for i,c in ipairs(G.hand[plr])do if c.id=="blue_eyes_w_dragon"then bewIdx=i;break end end if not bewIdx then return end local pos=chooseSummonPosition(plr,G.hand[plr][bewIdx]) sendMonsterToGY(plr,col,"tribute") specialSummon(plr,col,table.remove(G.hand[plr],bewIdx),pos) end) end, }, }, fiendmegacyber={ handIgnition={ label="SS MEGACYBER",summonsSelf=true, canActivate=function(_,plr) return countMonsters(3-plr)>=countMonsters(plr)+2 end, activate=handSpecialSummon, }, }, cyberdragon={ handIgnition={ label="SS CYBER",summonsSelf=true, canActivate=function(_,plr) return countMonsters(plr)==0 and countMonsters(3-plr)>=1 end, activate=handSpecialSummon, }, }, cybertwin={ doubleAttack=true, onAfterAttack=function(card) if card.dblTurn~=G.turn then card.dblTurn=G.turn;card.attacked=false end end, }, cyberend={ piercing=true, }, cyberbarrier=cyberTributeSummon(1,"SS BARRIER",{ listens={ ATTACK={ when=function(self,ctx) return self.zone=="mon"and ctx.actor~=self.plr and ctx.attacker~=nil and self.card.pos==1 and self.card.barrierTurn~=G.turn end, react=function(self,ctx) self.card.barrierTurn=G.turn ctx.attacker.attackNegated=true end, }, }, }), cyberlaser=cyberTributeSummon(2,"SS LASER",{ ignition=(function() local function specs(card) local cap=getMonAtk(card)or 0 return{{zone="mon",side="any",facing="up",exclude=card, where=function(c)return getMonAtk(c)>=cap or getMonDef(c)>=cap end}} end return{ label="LASER", canActivate=function(card,plr) return card.ignitionUsed~=G.turn and hasAnySpec(specs(card),plr) end, activate=function(card,plr,col) card.ignitionUsed=G.turn procPushFrame(function() local sp=specs(card) if not hasAnySpec(sp,plr)then return end local ans=choose{kind="zone",plr=plr,specs=sp,title="LASER: DESTROY"} if ans and G.mon[ans.plr][ans.col]then sendMonsterToGY(ans.plr,ans.col,"effect")end end) end, } end)(), }), cyberrevsystem=(function() local function eligible(c) return c~=nil and c.cat=="monster"and matIsLiteral(c,"cyber_dragon") and canSpecialSummon(c) end local function sources(plr) local items={} local function add(src,list,tag) for i,c in ipairs(list)do if eligible(c)then items[#items+1]={source=src,realIdx=i,deckIdx=i,card=c, name=c.name..tag,atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end end add("hand",G.hand[plr]," (H)") if not staticActive("blocksGYMoves")then add("gy",G.gy[plr]," (GY)")end return items end return{ canActivate=function(card,plr) plr=plr or 1 return firstEmpty(G.mon[plr])~=nil and#sources(plr)>0 end, resolve=function(plr) local items=sources(plr) if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,title="REVSYSTEM"} if not ans then return end local m=summonPicked(plr,ans.item) if m then m.noEffectDestroy=true end end, } end)(), cybervalley={ listens={ ATTACK={ when=function(self,ctx)return ctx.target==self.card end, react=function(self,ctx) local col=monsterCol(self.plr,self.card) if not col then return end banishMonster(self.plr,col) drawCard(self.plr) doAdvancePhase(PH_END) end, }, }, }, cyberdragoncore=(function() local function cyberMon(c) return(c.cat or"monster")=="monster"and isCyberDragonName(c) end local ST={zone="deck",side="own",condition=true,where=isCyberST} local MON={zone="deck",side="own",condition=true,where=cyberMon} return{ listens={ SUMMON={ when=function(self,ctx) return ctx.card==self.card and ctx.kind~="special" and#G.hand[self.plr]=1 and not staticActive("blocksGYMoves") and hasTarget(MON,plr) end, activate=function(card,plr,gyIdx) if G.gy[plr][gyIdx]~=card then return end banishFromGY(plr,gyIdx) procPushFrame(function() summonFromDeck(plr,"CORE: CYBER DRAGON",cyberMon) end) end, }, } end)(), cyberphoenix={ static={negatesMachineTarget=function(c)return c.pos==1 end}, listens={ DESTROYED={ when=diedInBattle, react=function(self,ctx)if#G.deck[self.plr]>0 then drawCard(self.plr)end end, }, }, }, jinzo=(function() local negate={when=isSelf,react=function()sweepTrapHook("onNegate")end} local resume={when=isSelf,react=function() if not staticActive("blocksTraps")then sweepTrapHook("onResume")end end} return{ static={blocksTraps=true}, listens={ [EV_SUMMON]=negate,[EV_FLIP]=negate, [EV_DESTROYED]=resume,[EV_TRIBUTED]=resume, }, } end)(), gkcurse={ listens={ SUMMON={ when=isSelf, react=function(self,ctx) if not monsterCol(self.plr,self.card)then return end changeLp(3-self.plr,-800) end, }, }, }, gkassailant={ listens={ ATTACK={ when=function(self,ctx) return ctx.attacker==self.card and staticActive("necrovalley")and hasTargetableMon(3-self.plr) end, react=function(self,ctx) local plr=self.plr local key=choose{ kind="menu",plr=plr,forced=true, items={{"EFFECT","yes"},{"NORMAL","no"}}, } if key~="yes"then return end local target=choose{ kind="zone",plr=plr,spec={zone="mon",side="opp"},title="ASSAILANT: CHANGE POS", } if not target then return end local m=G.mon[target.plr]and G.mon[target.plr][target.col] if not m then return end if m.facedown then m.facedown=false;m.pos=1;m.posChanged=true flipEvent(m,target.plr,target.col) else togglePosition(m) end checkEquips() end, }, }, }, gkspy={ listens={ FLIP={ when=isSelf, react=function(self,ctx) summonFromDeck(self.plr,"GRAVEKEEPER'S SPY",function(c) return(c.cat or"monster")=="monster"and isGravekeeper(c)and(c.atk or 0)<=1500 end) end, }, }, }, gkguard={ listens={ FLIP={ when=isSelf, react=function(self,ctx) local tp,tc=pickFieldMonster(self.plr,"GK GUARD") if not tp then return end returnMonsterToHand(tp,tc) checkEquips() end, }, }, }, revivaljam=(function() local COST=1000 local function gyIndex(plr,card) for i,c in ipairs(G.gy[plr])do if c==card then return i end end end return{ listensInGY=true, listens={ DESTROYED={ when=function(self,ctx) return diedInBattle(self,ctx) end, react=function(self)self.card.revivalReady=true end, }, PHASE={ when=function(self,ctx) return ctx.phase==PH_STBY and ctx.actor==self.plr and self.card.revivalReady==true end, react=function(self,ctx) local plr,card=self.plr,self.card card.revivalReady=nil if not gyIndex(plr,card)or not firstEmpty(G.mon[plr])then return end if G.lp[plr]<=COST or staticActive("blocksGYMoves")then return end local key=choose{kind="menu",plr=plr,forced=true, items={{"PAY 1000 LP","yes"},{"DECLINE","no"}}} if key~="yes"then return end local col,idx=firstEmpty(G.mon[plr]),gyIndex(plr,card) if not(col and idx)then return end changeLp(plr,-COST) table.remove(G.gy[plr],idx) specialSummon(plr,col,card,2) end, }, }, } end)(), penguinsoldier={ listens={ FLIP={ when=isSelf, react=function(self,ctx) for i=1,2 do local tp,tc=pickFieldMonster(self.plr,"PENGUIN BOUNCE "..i.."/2",i==1) if not tp then break end returnMonsterToHand(tp,tc) end checkEquips() end, }, }, }, necrovalley={ static={necrovalley=true,blocksGYMoves=true}, fieldBonus=function(card) if not isGravekeeper(card)then return 0,0 end return 500,500 end, resolve=function()end, }, skyscraper={ static={heroBoost=true}, resolve=function()end, }, sogen={ fieldBonus=function(card) if card.type~="warrior"and card.type~="beast-warr"then return 0,0 end return 400,400 end, resolve=function()end, }, yami={ fieldBonus=function(card) if card.type~="fiend"and card.type~="spellcaster"then return 0,0 end return 300,300 end, resolve=function()end, }, gkshaman={ static={blocksFieldSpells=function()return staticActive("necrovalley")end, blocksGYEffects=true}, defBonus=function(card) local n=0 for p=1,2 do for _,c in ipairs(G.gy[p])do if isGravekeeper(c)then n=n+200 end end end return n end, }, catillomen={ listens={ FLIP={ when=isSelf, react=function(self,ctx) local plr=self.plr local nv=staticActive("necrovalley") local ans=choose{ kind="card",plr=plr,fromE="deck", title=nv and"CAT: ADD TRAP TO HAND"or"CAT: TRAP ON TOP OF DECK", filter=function(c)return c.cat=="trap"end, } if not ans then return end local slug=table.remove(G.deck[plr],ans.idx) if nv then addToHand(plr,makeCard(slug)) else table.insert(G.deck[plr],slug) G.deckTop=G.deckTop or{0,0} G.deckTop[plr]=(G.deckTop[plr]or 0)+1 end end, }, }, }, gkstele=(function() local function isGkMonster(c)return isGravekeeper(c)and c.cat=="monster"end local TGT={zone="gy",side="own",cat="monster",condition=true,where=isGkMonster} return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, resolve=function(plr) for i=1,2 do if not hasTarget(TGT,plr)then return end local ans=choose{ kind="card",plr=plr,from="gy", title="STELE: PICK GK ("..(3-i).." LEFT)", filter=isGkMonster, } if not ans then return end addToHand(plr,table.remove(G.gy[plr],ans.idx)) end end, } end)(), gkoracle=(function() local EFFECT_OF={e1=1,e2=2,e3=3} local function pickLoop(card,plr,n,lvlSum) local opp=3-plr local used={} while n>0 do local items={} if not used[1]then items[#items+1]={"add "..lvlSum*100 .." ATK","e1"}end if not used[2]then items[#items+1]={"dst set ms","e2"}end if not used[3]then items[#items+1]={"-2000 stats","e3"}end items[#items+1]={"DONE","done"} local key=choose{kind="menu",plr=plr,items=items} if not key or key=="done"then return end local eff=EFFECT_OF[key] used[eff]=true if eff==1 then card.atkMod=(card.atkMod or 0)+lvlSum*100 elseif eff==2 then for i=1,3 do if G.mon[opp][i]and G.mon[opp][i].facedown then revealAndDestroyMon(opp,i,"effect") end end checkEquips() else for i=1,3 do local m=G.mon[opp][i] if m and not effectImmune(m)then m.atkMod=(m.atkMod or 0)-2000 m.defMod=(m.defMod or 0)-2000 end end end n=n-1 end end return{ tributeValue=function(m)return isGravekeeper(m)and 3 or nil end, listens={ SUMMON={ when=function(self,ctx) return ctx.card==self.card and ctx.kind=="tribute" end, react=function(self,ctx) if not monsterCol(self.plr,self.card)then return end local gkN,lvlS=0,0 for _,t in ipairs(ctx.tributed)do local m=t.card lvlS=lvlS+(m.lvl or 0) if isGravekeeper(m)then gkN=gkN+1 end end if gkN==0 then return end pickLoop(self.card,self.plr,gkN,lvlS) end, }, }, } end)(), obelisk=(function() local function fodder(card) return{zone="mon",side="own",condition=true,exclude=card,where=function(m)return tributable(m)end} end return{ noSpecialSummon=true,noEffectDestroy=true,unnegatableSummon=true, ignition={ label="DESTROY ALL", canActivate=function(card,plr) plr=plr or 1 return card.obeliskUsed~=G.turn and countMonsters(3-plr)>0 and hasTarget(fodder(card),plr) end, activate=function(card,plr) card.obeliskUsed=G.turn card.noAttackTurn=G.turn procPushFrame(function() local t=choose{kind="zone",plr=plr,spec=fodder(card),title="OBELISK: TRIBUTE"} if not(t and G.mon[t.plr][t.col])then return end sendMonsterToGY(t.plr,t.col,"tribute") for c=1,3 do if G.mon[3-plr][c]then revealAndDestroyMon(3-plr,c,"effect")end end checkEquips() end) end, }, } end)(), slifer=(function() local function handBonus(card) if card.facedown then return 0 end for p=1,2 do if monsterCol(p,card)then return 1000*#G.hand[p]end end return 0 end return{ noSpecialSummon=true,noEffectDestroy=true,unnegatableSummon=true, atkBonus=handBonus,defBonus=handBonus, listens={ SUMMON={ when=function(self,ctx) return self.zone=="mon"and ctx.plr~=self.plr and ctx.card~=nil and ctx.card.pos==1 end, react=function(self,ctx) local m=ctx.card if G.mon[ctx.plr][ctx.col]~=m or cardImmune(m)then return end local before=getMonAtk(m) m.atkMod=(m.atkMod or 0)-2000 if before>0 and getMonAtk(m)==0 then revealAndDestroyMon(ctx.plr,ctx.col,"effect") checkEquips() end end, }, }, } end)(), ra=(function() local COST=1000 local TGT={zone="mon",where=function(m)return m.facedown or not cannotEffectDestroy(m)end} return{ noSpecialSummon=true,noEffectDestroy=true,unnegatableSummon=true, onTributeSummon=function(card,tributed) local a,d=0,0 for _,t in ipairs(tributed)do a=a+(t.card.atk or 0);d=d+(t.card.def or 0)end card.atkMod=a;card.defMod=d end, ignition={ label="DESTROY 1", canActivate=function(card,plr) plr=plr or 1 return card.raUsed~=G.turn and G.lp[plr]>COST and hasTarget(TGT,plr) end, activate=function(card,plr) card.raUsed=G.turn procPushFrame(function() local t=choose{kind="zone",plr=plr,spec=TGT,title="RA: DESTROY"} if not(t and G.mon[t.plr][t.col])then return end changeLp(plr,-COST) revealAndDestroyMon(t.plr,t.col,"effect") checkEquips() end) end, }, } end)(), rasdisciple=(function() local ID="ras_disciple" local function sources(plr) local items={} for i,c in ipairs(G.hand[plr])do if c.id==ID then items[#items+1]={source="hand",realIdx=i,deckIdx=i,card=c, name=c.name.." (H)",atk=c.atk,def=c.def,lvl=c.lvl,desc=c.desc} end end for i,id in ipairs(G.deck[plr])do if id==ID then local cd=CARDS[id] items[#items+1]={source="deck",realIdx=i,deckIdx=i,card=cd, name=cd.name.." (D)",atk=cd.atk,def=cd.def,lvl=cd.lvl,desc=cd.desc} end end return items end return{ godTributeOnly=true, tributeValue=function(m,summonCard) if not tributable(m,summonCard)then return 0 end end, listens={ SUMMON={ when=function(self,ctx) return ctx.card==self.card and firstEmpty(G.mon[self.plr])~=nil and#sources(self.plr)>0 end, react=function(self,ctx) local plr=self.plr for _=1,2 do if not firstEmpty(G.mon[plr])then return end local items=sources(plr) if#items==0 then return end local ans=choose{kind="card",plr=plr,items=items,forced=false,title="RA'S DISCIPLE"} if not ans then return end if not summonPicked(plr,ans.item)then return end end end, }, }, } end)(), soulexchange=(function() local TGT={zone="mon",side="opp"} return{ canActivate=function(card,plr)return hasTarget(TGT,plr or 1)end, activate=targetMonsterActivate(TGT,"SOUL EXCHANGE",function(plr,tp,tc,m) G.soulExchange={plr=plr,card=m,turn=G.turn} G.battleBan={plr=plr,turn=G.turn} end), } end)(), jambreedingmachine={ resolve=function()end, listens={ PHASE={ when=function(self,ctx) return self.zone=="st"and not self.card.facedown and ctx.phase==PH_STBY and ctx.actor==self.plr and firstEmpty(G.mon[self.plr])~=nil end, react=function(self) local plr=self.plr if G.st[plr][self.col]~=self.card then return end local col=firstEmpty(G.mon[plr]) if not col then return end specialSummon(plr,col,makeToken("Slime Token","aqua","water",1,500,500,CARDS.revival_jam.spr),1) end, }, }, }, } end do function behaviorOf(card)return card and card.effect and BEHAVIORS[card.effect]end function applyResolve(card,plr,ctx) local b=behaviorOf(card) if b and b.resolve then b.resolve(plr,ctx or{source=card})end end function canReviveMonster(plr) if staticActive("blocksGYMoves")then return false end if not firstEmpty(G.mon[plr])then return false end for _,c in ipairs(G.gy[plr])do if c.cat=="monster"and canSpecialSummon(c)then return true end end return false end local function grantsStatic(card,flag,skipTraps) if not card or card.facedown then return false end if skipTraps and card.cat=="trap"then return false end local b=behaviorOf(card) if not(b and b.static)then return false end local v=b.static[flag] if type(v)=="function"then v=v(card)end return v and true or false end local function scanStatic(flag,plr,skipTraps) for p=(plr or 1),(plr or 2)do for c=1,3 do if grantsStatic(G.mon[p][c],flag,skipTraps) or grantsStatic(G.st[p][c],flag,skipTraps)then return true end end if grantsStatic(G.fs[p],flag,skipTraps)then return true end end return false end function staticActive(flag,plr) local trapsNegated=flag~="blocksTraps"and scanStatic("blocksTraps") return scanStatic(flag,plr,trapsNegated) end function canActivateSetST(card,plr) if not card then return false,"no card in zone"end if not card.facedown then return false,"card already face-up"end if card.setThisTurn and(card.cat=="trap"or card.subtype=="quickplay")then return false,"set this turn" end local b=behaviorOf(card) if b and b.responseOnly then return false,"response-only"end if card.cat=="trap"and(staticActive("blocksTraps")or trapsLocked())then return false,"traps blocked" end if b and b.canActivate and not b.canActivate(card,plr)then return false,"canActivate=false"end return true end function spellTimingOK(card,plr) if not card or card.cat~="spell"or card.subtype=="quickplay"then return true end return G.ph==PH_MAIN and G.active==plr end function canTargetMon(card) if not card then return false end if not card.facedown and card.type=="dragon"and staticActive("dragonProtect")then return false end return not effectImmune(card) end function linkTargetNegates(link) local src=link.source if not(src and(src.cat=="spell"or src.cat=="trap"))then return false end local t=link.target if not(t and(t.cat or"monster")=="monster"and t.type=="machine")then return false end for p=1,2 do if monsterCol(p,t)then return staticActive("negatesMachineTarget",p)end end return false end function cardImmune(card) local b=behaviorOf(card) return(b~=nil and b.cardImmune==true) end function effectImmune(card) local s=G.proc and G.proc.source if not(s and s.card)or s.card==card then return false end if cardImmune(card)then return true end local mf=G.magnetForce return mf~=nil and mf.turn==G.turn and s.card.cat=="monster"and s.plr==3-mf.plr and(card.type=="machine"or card.type=="rock") end function hasTargetableMon(plr) for c=1,3 do if canTargetMon(G.mon[plr][c])then return true end end return false end function trapImmune(card) local b=behaviorOf(card) return(b~=nil and b.trapImmune==true) end function gyIgnitionReady(card,plr) local b=behaviorOf(card) local ign=b and b.gyIgnition if not(ign and ign.activate)then return false end if ign.canActivate and not ign.canActivate(card,plr)then return false end return true end function canNormalSummon(card) local b=behaviorOf(card) return not(b~=nil and b.noNormalSummon==true) end function canSummonSelf(card,plr) local b=behaviorOf(card) local hi=b and b.handIgnition return hi~=nil and hi.summonsSelf==true and(not hi.canActivate or hi.canActivate(card,plr)) end function extraSummonAllows(card,plr) local kind=G.extraSummon if not kind or not card or card.cat~="monster"then return false end if not canNormalSummon(card)then return false end local trib=tribsNeeded(getMonLvl(card)) if kind=="spellcaster"then return card.type=="spellcaster"and trib>=1 and countMonsters(plr)>=trib end if kind=="ojamatch"then if card.ojamatchSummon~=G.turn then return false end if trib==0 then return firstEmpty(G.mon[plr])~=nil end return tributeValueAvailable(card,plr)>=trib end return false end function canSpecialSummon(card) local b=behaviorOf(card) return not(b~=nil and b.noSpecialSummon==true) end function cannotEffectDestroy(card) if card.noEffectDestroy then return true end local b=behaviorOf(card) return b~=nil and b.noEffectDestroy==true end function summonUnnegatable(card) local b=behaviorOf(card) return b~=nil and b.unnegatableSummon==true end function isEgyptianGod(card)return card~=nil and card.type=="divine"end function tributable(m,summonCard) local b=behaviorOf(m) return not(b~=nil and b.godTributeOnly==true)or isEgyptianGod(summonCard) end function soulTarget(plr) local s=G.soulExchange if not(s and s.plr==plr and s.turn==G.turn)then return nil end local col=monsterCol(3-plr,s.card) if col then return col,s.card end end function battleBanned(plr) if G.turn==1 and plr==G.firstPlayer then return true end local b=G.battleBan return b~=nil and b.plr==plr and b.turn==G.turn end function sealsAttackST(card) local b=behaviorOf(card) return(b~=nil and b.sealsAttackST==true) end function negatesBattleKill(card) local b=behaviorOf(card) return(b~=nil and b.negatesBattleKill==true) end function sweepDestroyAtEnd() for p=1,2 do for c=1,3 do local m=G.mon[p][c] if m and m.destroyAtEnd==G.turn then sendMonsterToGY(p,c,"effect")end end end end function tributeSubValue(st,summonCard) local b=(st and not st.facedown)and behaviorOf(st) local sub=b and b.tributeSub local n=sub and sub.value(st,summonCard) if n and n>0 then return n,sub.label end end function tributeSubFor(summonCard,plr) for c=1,3 do local n,label=tributeSubValue(G.st[plr][c],summonCard) if n then return c,n,label end end end function addCounters(trigger) for p=1,2 do for c=1,3 do local st=G.st[p][c] local b=(st and not st.facedown)and behaviorOf(st) if b and b.counterOn==trigger then st.counters=(st.counters or 0)+1 end end end end function fusionSub(card) local b=behaviorOf(card) return(b~=nil and b.fusionSub==true) end local LIST_ZONES={hand=true,gy=true,deck=true,extra=true} local function eachZoneCard(spec,p,fn) local zone=spec.zone or"mon" if zone=="mon"or zone=="st"then local arr=G[zone][p] for c=1,3 do local card=arr[c] if card then local r=fn(card,p,c,card.id) if r then return r end end end elseif zone=="fs"then local card=G.fs[p] if card then return fn(card,p,nil,card.id)end elseif LIST_ZONES[zone]then for i,e in ipairs(G[zone][p])do local isTable=(type(e)=="table") local card=isTable and e or CARDS[e] if card then local r=fn(card,p,i,isTable and e.id or e) if r then return r end end end end end local function eachSpecCard(spec,plr,fn) local side=spec.side or"any" if side=="own"then return eachZoneCard(spec,plr,fn)end if side=="opp"then return eachZoneCard(spec,3-plr,fn)end return eachZoneCard(spec,plr,fn)or eachZoneCard(spec,3-plr,fn) end function specMatches(spec,card,side,col,id) if not card then return false end if spec.exclude and card==spec.exclude then return false end if spec.facing=="up"and card.facedown then return false end if spec.facing=="down"and not card.facedown then return false end if spec.cat and card.cat~=spec.cat then return false end if spec.type and card.type~=spec.type then return false end if spec.attr and card.attr~=spec.attr then return false end if spec.subtype and card.subtype~=spec.subtype then return false end if spec.name and card.name~=spec.name then return false end if spec.id and(id or card.id)~=spec.id then return false end if(spec.zone or"mon")=="mon"and not spec.condition and not canTargetMon(card)then return false end if spec.where and not spec.where(card,side,col,id)then return false end return true end function listTargets(spec,plr) local out={} eachSpecCard(spec,plr,function(card,side,col,id) if specMatches(spec,card,side,col,id)then out[#out+1]={plr=side,col=col,card=card,id=id} end end) return out end function hasTarget(spec,plr) local need=spec.count or 1 local n=0 return eachSpecCard(spec,plr,function(card,side,col,id) if specMatches(spec,card,side,col,id)then n=n+1 if n>=need then return true end end end)or false end function monsterBound(card) local t=card and card.linkedTrap return t~=nil and t.binds==true and not t.facedown and not staticActive("blocksTraps") end function attackBlocked(card,plr) if staticActive("blocksAttack",3-plr)then return true end if card.noAttackTurn==G.turn then return true end local cl=G.cyberload if cl and cl.turn==G.turn and cl.plr==plr and card~=cl.only then return true end if trapImmune(card)then return false end return(getMonLvl(card)>=4 and staticActive("gravityBind")) or monsterBound(card) end function canChangePosition(m) return m~=nil and not m.summoned and not m.posChanged and not monsterBound(m) end function canDeclareAttack(m,plr) return m~=nil and m.pos==1 and not m.facedown and not m.attacked and not attackBlocked(m,plr) end function struckThisTurn(m,tgtCol) return tgtCol~=nil and m~=nil and m.wildedgeTurn==G.turn and m.wildedgeHit[tgtCol]==true end function canDefDirect(m,plr) if m==nil or m.facedown or m.pos~=2 or m.attacked then return false end local b=behaviorOf(m) return(b~=nil and b.defDirect==true)and not attackBlocked(m,plr) end function battleShielded(plr) return G.battleShield~=nil and G.battleShield[plr]==G.turn end function trapsLocked() return G.trapLock==G.turn and G.ph==PH_BATTLE end function damageShielded(plr) return G.damageShield~=nil and G.damageShield[plr]==G.turn end local function battleProtected(card,ctrl,hook,vs) if battleShielded(ctrl)and not trapImmune(card)and not trapImmune(vs)then return true end if hook=="noBattleDamage"and damageShielded(ctrl)then return true end local b=behaviorOf(card) local fn=b and b[hook] return(fn and fn(card,ctrl))or false end function destroyReplaced(card,plr) for c=1,3 do local st=G.st[plr][c] local b=(st and not st.facedown)and behaviorOf(st)or nil local rep=b and b.replaceDestroy if rep and rep(card,plr)then return true end end return false end function battleIndestructible(card,ctrl,vs) return battleProtected(card,ctrl,"battleImmune",vs) end function battleDamageImmune(card,ctrl,vs) return battleProtected(card,ctrl,"noBattleDamage",vs) end function isGravekeeper(card) return(card and card.name and card.name:find("^Gravekeeper's"))~=nil end function isAncientGear(card) return(card and card.name and card.name:find("Ancient Gear",1,true))~=nil end function isOjama(card) return(card and card.name and card.name:find("Ojama",1,true))~=nil end function zonesSealed(plr) local n=0 for c=1,3 do local m=G.mon[3-plr][c] if m and not m.facedown then local b=behaviorOf(m) local v=b and b.sealsZones if v and v>n then n=v end end end return n end function zonesAllowed(plr,freed) local n=zonesSealed(plr) if n<=0 then return 3 end local held=0 for c=1,3 do if G.mon[plr][c]then held=held+1 end end return math.max(0,(3-n)-(held-(freed or 0))) end function zoneSealed(plr,col) if zonesSealed(plr)<=0 or G.mon[plr][col]then return false end local allowance,rank=zonesAllowed(plr),0 for c=1,3 do if not G.mon[plr][c]then rank=rank+1 if c==col then return rank>allowance end end end return false end function isEHero(card) return(card and card.name and card.name:find("^E%-HERO"))~=nil end function battleAtk(att,tgt,plr) local a=getMonAtk(att)or 0 if tgt and isEHero(att)and(getMonAtk(tgt)or 0)>a and staticActive("heroBoost",plr)then return a+1000 end return a end function battleOutcome(att,tgt,plr) local opp=3-plr local atkV=battleAtk(att,tgt,plr) local o={killTarget=false,killAttacker=false,toDefender=0,toAttacker=0} if tgt.pos==2 then local def=getMonDef(tgt) local b=behaviorOf(att) if atkV>def then o.killTarget=true if b and b.piercing then o.toDefender=atkV-def end elseif atkVtv then o.killTarget=true;o.toDefender=atkV-tv elseif atkV0 then o.killTarget=true;o.killAttacker=true end end if o.killTarget and battleIndestructible(tgt,opp,att)then o.killTarget=false end if o.killAttacker and battleIndestructible(att,plr,tgt)then o.killAttacker=false end if o.toDefender>0 and battleDamageImmune(tgt,opp,att)then o.toDefender=0 end if o.toAttacker>0 and battleDamageImmune(att,plr,tgt)then o.toAttacker=0 end return o end end do PH_DRAW=1;PH_STBY=2;PH_MAIN=3;PH_BATTLE=4;PH_END=5 local function returnBorrowedMonsters() for p=1,2 do for c=1,3 do local m=G.mon[p][c] if m and m.borrowedFrom then local owner=m.borrowedFrom G.mon[p][c]=nil m.borrowedFrom=nil;m.borrowedAtTurn=nil local destCol=firstEmpty(G.mon[owner]) if destCol then G.mon[owner][destCol]=m retargetEquips(p,c,owner,destCol) else addToGY(owner,m,"rule") end end end end checkEquips() end local function enforceHandLimit(plr) if#G.hand[plr]<=6 then return end procPushFrame(function() while#G.hand[plr]>6 do local ans=choose{ kind="card",plr=plr,from="hand",title="END PHASE - DISCARD", purpose="handLimit", } if not ans then return end discardFromHand(plr,ans.idx,"rule") end end) end local function endTurnFlip(plr) if G.puzzle and plr==1 then endDuel(2);return end returnBorrowedMonsters() tickSwords() clearTurnMods() G.turn=G.turn+1 G.active=3-plr G.normalSummoned=false drawCard(G.active) resetTurnFlags() if G.active==1 then G.autoTimer=50 else G.aiTimer=AI_DELAY end if AI_TRACE and not G.sim then trace(string.format("-- turn %d END -> %d p%d | lp %d/%d | hand %d/%d | mon %d/%d", G.turn-1,G.turn,G.active,G.lp[1],G.lp[2],#G.hand[1],#G.hand[2], countMonsters(1),countMonsters(2))) end end PHASE_TABLE={ [PH_DRAW]={ next=PH_STBY, legal={ACTIVATE=true,ADVANCE_PHASE=true}, }, [PH_STBY]={ next=PH_MAIN, legal={ACTIVATE=true,ADVANCE_PHASE=true}, }, [PH_MAIN]={ next=PH_BATTLE, legal={SUMMON=true,CAST=true,SET_ST=true,ACTIVATE=true, CHANGE_POS=true,ADVANCE_PHASE=true, IGNITION=true,HAND_IGNITION=true,GY_IGNITION=true}, }, [PH_BATTLE]={ next=PH_END, legal={DECLARE_ATTACK=true,ACTIVATE=true,ADVANCE_PHASE=true}, }, [PH_END]={ next=PH_DRAW, onEnter=function(plr)sweepDestroyAtEnd();enforceHandLimit(plr)end, onExit=endTurnFlip, legal={ACTIVATE=true,ADVANCE_PHASE=true}, }, } function resetTurnFlags() for p=1,2 do for i=1,3 do local m=G.mon[p][i] if m then m.attacked=false;m.summoned=false;m.posChanged=false end local s=G.st[p][i] if s then s.setThisTurn=false end end end G.legionSummonUsed=false G.legionSearchUsed={} G.extraSummon=nil end ACTIVE_ONLY={SUMMON=true,CAST=true,SET_ST=true,DECLARE_ATTACK=true, CHANGE_POS=true,IGNITION=true,HAND_IGNITION=true, GY_IGNITION=true, ADVANCE_PHASE=true} function doAdvancePhase(target) local cur=PHASE_TABLE[G.ph] if not cur then return end target=target or cur.next if target==PH_BATTLE and battleBanned(G.active)then target=PH_END end local nxt=PHASE_TABLE[target] if not nxt then return end if target==G.ph then return end if cur.onExit then cur.onExit(G.active)end local prev=G.ph G.ph=target raise(EV_PHASE,{phase=target,prevPhase=prev,toPhase=target,actor=G.active}) if nxt.onEnter then nxt.onEnter(G.active)end end local function clearSelectionUI(plr) if plr==1 then G.mode="free";G.pending=nil end end local function dispatchActivate(b,opts) if not(b and b.activate)then return false end b.activate(opts) return true end local fireIgnition INTENTS={} local function commitSummon(plr,a,card,tribCols,subCol,soul,soulCol,position) local tributedMonsters={} for _,tcol in ipairs(tribCols)do local tm=G.mon[plr][tcol] if tm then table.insert(tributedMonsters,{card=tm,plr=plr,col=tcol})end end if soul and G.mon[3-plr][soulCol]==soul then table.insert(tributedMonsters,{card=soul,plr=3-plr,col=soulCol}) sendMonsterToGY(3-plr,soulCol,"tribute") end for _,tcol in ipairs(tribCols)do sendMonsterToGY(plr,tcol,"tribute") end if subCol then sendSpellTrapToGY(plr,subCol,"tribute")end if a.handIdx then table.remove(G.hand[plr],a.handIdx)end local placed=card if position=="SET"then placed=copyCard(card);placed.pos=2;placed.facedown=true else placed.pos=1;placed.facedown=false end G.mon[plr][a.col]=placed placed.attacked=false;placed.posChanged=false;placed.summoned=true if a.extra then G.extraSummon=nil else G.normalSummoned=true end addCounters("summon") local pb=behaviorOf(placed) if pb and pb.onTributeSummon and#tributedMonsters>0 then pb.onTributeSummon(placed,tributedMonsters) end summonEvent(placed,plr,a.col,#tributedMonsters>0 and"tribute"or"normal",tributedMonsters) for _,t in ipairs(tributedMonsters)do raise(EV_TRIBUTED,{card=t.card,plr=t.plr,actor=plr}) end end INTENTS.SUMMON=function(plr,a) local card=a.card if not card or card.cat~="monster"then return false,"not a monster"end if not canNormalSummon(card)then return false,"cannot be Normal Summoned"end if a.extra then if not extraSummonAllows(card,plr)then return false,"no extra summon avail"end elseif G.normalSummoned then return false,"already summoned" end if not a.col or a.col<1 or a.col>3 then return false,"bad col"end if zoneSealed(plr,a.col)then return false,"zone sealed"end local tribCols=a.tributes or{} if zonesAllowed(plr,#tribCols)<=0 then return false,"zone sealed"end if G.mon[plr][a.col]then local isTrib=false for _,tcol in ipairs(tribCols)do if tcol==a.col then isTrib=true;break end end if not isTrib then return false,"zone occupied"end end local trib=tribsNeeded(getMonLvl(card)) local subCol,subVal=a.stTribute,0 if subCol then if trib<=0 then return false,"no tribute to substitute"end if subCol<1 or subCol>3 then return false,"bad stTribute"end subVal=tributeSubValue(G.st[plr][subCol],card) if not subVal then return false,"bad tribute substitute"end end local soulCol,soul=soulTarget(plr) if trib<=0 or a.stTribute then soulCol,soul=nil,nil end if soul and not a.soul then return false,"must tribute the Soul Exchange target"end if a.soul and not soul then return false,"no Soul Exchange target"end if trib>0 then local total=subVal if soul then if not tributable(soul,card)then return false,"cannot be tributed"end total=total+tributeValueOf(card,soul) end for _,tcol in ipairs(tribCols)do local m=G.mon[plr][tcol] if not m then return false,"tribute zone empty"end if not tributable(m,card)then return false,"cannot be tributed"end total=total+tributeValueOf(card,m) end if total0 or soul then local zones={} for _,tcol in ipairs(tribCols)do local x,y=zoneXY(plr,"mon",tcol) table.insert(zones,{x=x,y=y}) end if soul then local x,y=zoneXY(3-plr,"mon",soulCol) table.insert(zones,{x=x,y=y}) end animTribute(zones,commit) else commit() end return true end INTENTS.CAST=function(plr,a) local card=a.card if not card or card.cat~="spell"then return false,"not a spell"end local rb=behaviorOf(card) if rb and rb.responseOnly then return false,"response-only"end if card.subtype=="field"then if staticActive("blocksFieldSpells",3-plr)then return false,"field spell blocked"end clearSelectionUI(plr) local placed=copyCard(card) sendFieldSpellToGY(plr,"rule") sendFieldSpellToGY(3-plr,"rule") G.fs[plr]=placed table.remove(G.hand[plr],a.handIdx) procActivate(placed,plr,"fs",nil) return true end if a.col and(a.col<1 or a.col>3 or G.st[plr][a.col])then return false,"st zone occupied" end clearSelectionUI(plr) local stCol=a.col or firstEmpty(G.st[plr]) table.remove(G.hand[plr],a.handIdx) if stCol then local placed=copyCard(card) placed.facedown=false G.st[plr][stCol]=placed placed.pendingActivation=true local b=behaviorOf(placed) if not dispatchActivate(b,{col=stCol,card=placed,zone="st",plr=plr})then procActivate(placed,plr,"st",stCol) end else addToGY(plr,card,"effect") chainActivation(card,plr,nil) end return true end INTENTS.SET_ST=function(plr,a) local card=a.card if not card or(card.cat~="spell"and card.cat~="trap")then return false,"not a spell/trap"end if card.subtype=="field"then return false,"field spell cannot be set"end if not a.col or a.col<1 or a.col>3 or G.st[plr][a.col]then return false,"st zone occupied"end clearSelectionUI(plr) local placed=copyCard(card) placed.facedown=true placed.setThisTurn=true G.st[plr][a.col]=placed table.remove(G.hand[plr],a.handIdx) return true end INTENTS.ACTIVATE=function(plr,a) if not a.col or a.col<1 or a.col>3 then return false,"bad col"end local card=G.st[plr][a.col] local bh=behaviorOf(card) if card and not card.facedown and bh and bh.stIgnition then return fireIgnition(plr,card,"stIgnition",a.col) end local ok,err=canActivateSetST(card,plr) if not ok then return false,err end if not spellTimingOK(card,plr)then return false,"spell outside own Main Phase"end local b=behaviorOf(card) clearSelectionUI(plr) card.pendingActivation=true if not dispatchActivate(b,{col=a.col,card=card,zone="st",plr=plr})then procActivate(card,plr,"st",a.col) end return true end INTENTS.RESPONSE=function(plr,a) if a.pass then G.mode="free" simNoteDecision(false) procAnswerChoice(false) return true end local req=G.proc and G.proc.currentChoice local opt=a.optionIdx and req and req.options and req.options[a.optionIdx] if not opt then return false,"bad optionIdx"end G.mode="free" simNoteDecision({optionIdx=a.optionIdx},req) procAnswerChoice({optionIdx=a.optionIdx}) return true end local function zoneCentre(plr,row,col) local x,y=zoneXY(plr,row,col) return x+ZONE_W_MAIN//2-8,y+ZONE_H//2-8 end function runAttackBattle(plr,attacker,atkCol,target,tgtCol) local opp=3-plr local ax,ay=zoneCentre(plr,"mon",atkCol) local tx,ty=zoneCentre(opp,"mon",tgtCol) local wasFlipped=target.facedown if wasFlipped then target.facedown=false local zx,zy=zoneXY(opp,"mon",tgtCol) local zc=zoneColor(opp) waitAnim(playAnim(24,function(t,f) if(t//4)%2==0 then rect(zx,zy,ZONE_W_MAIN,ZONE_H,zc);rectb(zx,zy,ZONE_W_MAIN,ZONE_H,C_TEXT)end end)) end waitAnim(animSwordSlashBox(ax,ay,tx,ty)) local atkB=behaviorOf(attacker) local o=battleOutcome(attacker,target,plr) if o.killTarget then if negatesBattleKill(attacker)then target.effectsNegated=true end sendMonsterToGY(opp,tgtCol,"battle") end if o.toDefender>0 then applyDamage(opp,o.toDefender)end if o.killAttacker then sendMonsterToGY(plr,atkCol,"battle")end if o.toAttacker>0 then changeLp(plr,-o.toAttacker)end G.battleAnim=nil checkWin() checkEquips() if atkB and atkB.onAfterAttack and G.mon[plr][atkCol]==attacker then atkB.onAfterAttack(attacker,plr,atkCol,target,tgtCol,opp) end if wasFlipped then flipEvent(target,opp,tgtCol)end end function runDirectAttack(plr,attacker,atkCol,halve) local opp=3-plr local ax,ay=zoneCentre(plr,"mon",atkCol) local _,rowY=zoneXY(opp,"st") local tx,ty=FIELD_X+FIELD_W//2-8,rowY+ZONE_H//2-8 local dmg=getMonAtk(attacker) if halve then dmg=dmg//2 end animSwordSlash(ax,ay,tx,ty,function() G.battleAnim=nil if not battleDamageImmune(nil,opp)then applyDamage(opp,dmg)end local b=behaviorOf(attacker) if b and b.onAfterAttack and G.mon[plr][atkCol]==attacker then b.onAfterAttack(attacker,plr,atkCol) end end) end INTENTS.DECLARE_ATTACK=function(plr,a) local atkCol=a.atkCol if not atkCol or atkCol<1 or atkCol>3 then return false,"bad atkCol"end local attacker=G.mon[plr][atkCol] if not attacker then return false,"no attacker"end if attacker.facedown then return false,"attacker face-down"end local defDirect=(a.defDirect==true) if defDirect then if not canDefDirect(attacker,plr)then return false,"no DEF direct attack"end if a.tgtCol then return false,"DEF direct attack cannot target"end elseif attacker.pos~=1 then return false,"attacker not face-up ATK" end if attacker.attacked then return false,"already attacked"end if attackBlocked(attacker,plr)then return false,"attack blocked"end local tgtCol=a.tgtCol local target if tgtCol then if tgtCol<1 or tgtCol>3 then return false,"bad tgtCol"end target=G.mon[3-plr][tgtCol] if not target then return false,"no target"end if struckThisTurn(attacker,tgtCol)then return false,"already struck that monster"end elseif not defDirect then if hasMonsters(3-plr)then return false,"target required"end end attacker.attacked=true clearSelectionUI(plr) if plr==1 then G.cur={side=1,row=1,col=atkCol}end procPushFrame(function() local negated=attacker.attackNegated attacker.attackNegated=nil if negated or G.ph~=PH_BATTLE or G.mon[plr][atkCol]~=attacker or attacker.facedown or attacker.pos~=(defDirect and 2 or 1)then G.battleAnim=nil return end if defDirect then runDirectAttack(plr,attacker,atkCol,true);return end local gone=tgtCol and G.mon[3-plr][tgtCol]~=target if gone or(not tgtCol and hasMonsters(3-plr))then tgtCol=nil if hasMonsters(3-plr)then local t=choose{kind="zone",plr=plr,forced=false,replay=true,attacker=attacker, title="NEW TARGET? B:cancel", spec={zone="mon",side="opp",condition=true, where=function(_,_,col)return not struckThisTurn(attacker,col)end}} if not(t and G.mon[3-plr][t.col])or G.mon[plr][atkCol]~=attacker then G.battleAnim=nil return end tgtCol=t.col end end if tgtCol then runAttackBattle(plr,attacker,atkCol,G.mon[3-plr][tgtCol],tgtCol) else runDirectAttack(plr,attacker,atkCol) end end) raiseNow(EV_ATTACK,{attacker=attacker,plr=plr,actor=plr,target=target,tgtCol=tgtCol}) return true end INTENTS.CHANGE_POS=function(plr,a) if not a.col or a.col<1 or a.col>3 then return false,"bad col"end local card=G.mon[plr][a.col] if not card then return false,"no monster"end if card.summoned then return false,"summoned this turn"end if card.posChanged then return false,"already changed pos this turn"end if monsterBound(card)then return false,"held by a trap"end if card.facedown then card.facedown=false;card.pos=1 flipEvent(card,plr,a.col) else card.pos=(card.pos==1)and 2 or 1 end card.posChanged=true checkEquips() return true end fireIgnition=function(plr,card,hook,locator) if not card then return false,"no card"end local b=behaviorOf(card) local ign=b and b[hook] if not(ign and ign.activate)then return false,"no "..hook end if ign.canActivate and not ign.canActivate(card,plr)then return false,"canActivate=false" end clearSelectionUI(plr) G.proc.source={card=card,plr=plr} ign.activate(card,plr,locator) if procIdle()then G.proc.source=nil end return true end INTENTS.IGNITION=function(plr,a) if not a.col or a.col<1 or a.col>3 then return false,"bad col"end local card=G.mon[plr][a.col] if card and card.facedown then return false,"monster face-down"end return fireIgnition(plr,card,"ignition",a.col) end INTENTS.HAND_IGNITION=function(plr,a) return fireIgnition(plr,a.handIdx and G.hand[plr][a.handIdx],"handIgnition",a.handIdx) end INTENTS.GY_IGNITION=function(plr,a) return fireIgnition(plr,a.gyIdx and G.gy[plr][a.gyIdx],"gyIgnition",a.gyIdx) end INTENTS.ADVANCE_PHASE=function(plr,a) if G.winner then return false,"game over"end doAdvancePhase(a.to) return true end function submitIntent(plr,t,a) a=a or{} local fn=INTENTS[t] if not fn then trace("intent: unknown type "..tostring(t)) return false,"unknown intent" end if t=="RESPONSE"then local cc=G.proc and G.proc.currentChoice if not(cc and cc.kind=="response"and cc.plr==plr)then trace("intent: RESPONSE outside response window") return false,"no response window" end else if procBusy()then trace("intent: "..t.." rejected (procBusy: "..procBusyReason()..")") return false,"busy" end local ph=PHASE_TABLE[G.ph] if not(ph and ph.legal[t])then trace("intent: "..t.." illegal in phase "..tostring(G.ph)) return false,"illegal phase" end if ACTIVE_ONLY[t]and plr~=G.active then trace("intent: "..t.." rejected (not active player)") return false,"not active player" end end simRecordOrigin(plr,t,a) local ok,err=fn(plr,a) if ok then simNoteAction(plr,t,a)end return ok,err end end do function startMonsterPlacement(handIdx,action,stTribute) local card=G.hand[1][handIdx] if not card then return end local tribNeeded=tribsNeeded(getMonLvl(card)) local soul=(tribNeeded>0 and not stTribute)and select(2,soulTarget(1))or nil G.pending={handIdx=handIdx,card=card,action=action,tribNeeded=tribNeeded, tributes={},stTribute=stTribute,soul=soul} if tribNeeded>0 and not stTribute and tributeValueSelected(G.pending)0 and subCol and subN>=tribNeeded and firstEmpty(G.mon[1])then add(items,subLabel,"summon_sub") end local canSummon=(tribNeeded==0 and firstEmpty(G.mon[1])~=nil) or(tribNeeded>=1 and tributeValueAvailable(card,1)>=tribNeeded) if canSummon then add(items,"SUMMON","summon") add(items,"SET","set") end end if extraSummonAllows(card,1)then add(items,"EXTRA SUMMON","summon_extra") end if card.cat=="monster"then local ign=readyIgnition(card,"handIgnition",1) if ign then add(items,ign.label,"hand_ignition")end end end local function monsterMenuItems(items,card,isMain) if isMain and canChangePosition(card)then add(items,"CHG POS","chgpos") end if isMain and not card.facedown then local ign=readyIgnition(card,"ignition",1) if ign then add(items,ign.label,"ignition")end end if G.ph==PH_BATTLE then if canDeclareAttack(card,1)then add(items,"ATTACK","attack")end if canDefDirect(card,1)then add(items,"DIRECT ATK","def_direct")end end end local function stMenuItems(items,card,isMain,ownTurn) local b=card and behaviorOf(card) if b and b.stIgnition and card and not card.facedown then if not b.stIgnition.canActivate or b.stIgnition.canActivate(card,1)then add(items,"ACTIVATE","activate") end return end if not canActivateSetST(card,1)then return end if not ownTurn then if card.cat=="spell"and card.subtype=="quickplay"then add(items,"ACTIVATE","activate") end return end if(card.cat=="spell"and(isMain or card.subtype=="quickplay")) or card.cat=="trap"then add(items,"ACTIVATE","activate") end end function buildMenu() local c=G.cur local isMain=(G.ph==PH_MAIN) local items={} local onOwnSide=(c.side==1) local inCols=(c.col>=1 and c.col<=3) if onOwnSide and c.row==2 and c.col==4 then add(items,"SURRENDER","surrender") elseif onOwnSide and c.row==2 and inCols then stMenuItems(items,G.st[1][c.col],isMain,G.active==1) elseif onOwnSide and G.active==1 then if c.row==3 then local card=G.hand[1][c.col+1] if card and isMain then handMenuItems(items,card)end elseif c.row==1 and inCols then local card=G.mon[1][c.col] if card then monsterMenuItems(items,card,isMain)end end end local hovCard=getHoveredCard() if hovCard and hovCard.desc and not(c.side==2 and hovCard.facedown and not oppRevealed())then add(items,"INFO","info") end if#items==0 then return nil end add(items,"CANCEL","cancel") return items end function execAction(key) local c=G.cur G.menu.open=false if key=="cancel"then elseif key=="info"then G.infoCard=getHoveredCard() elseif key=="chgpos"then submitIntent(1,"CHANGE_POS",{col=c.col}) elseif key=="summon"or key=="summon_extra"or key=="set"then startMonsterPlacement(c.col+1,key) elseif key=="summon_sub"then startMonsterPlacement(c.col+1,"summon",(tributeSubFor(G.hand[1][c.col+1],1))) elseif key=="ignition"then submitIntent(1,"IGNITION",{col=c.col}) elseif key=="hand_ignition"then submitIntent(1,"HAND_IGNITION",{handIdx=c.col+1}) elseif key=="attack"then local attacker=G.mon[1][c.col] if attacker then G.pending={attacker=attacker,atkCol=c.col,action="attack"} G.mode="sel_atk" local tc=firstOccupied(G.mon[2]) for t=1,3 do if G.mon[2][t]and not struckThisTurn(attacker,t)then tc=t;break end end G.cur={side=2,row=1,col=4-(tc or 2)} end elseif key=="def_direct"then submitIntent(1,"DECLARE_ATTACK",{atkCol=c.col,defDirect=true}) elseif key=="set_st"then startSTPlacement(c.col+1,"set_st") elseif key=="cast_hand"then local handIdx=c.col+1 local card=G.hand[1][handIdx] if card and card.subtype=="field"then if submitIntent(1,"CAST",{card=card,handIdx=handIdx})then G.cur={side=1,row=1,col=0} end else startSTPlacement(handIdx,"cast_hand") end elseif key=="activate"then if G.st[1][c.col]then submitIntent(1,"ACTIVATE",{col=c.col})end elseif key=="surrender"then endDuel(2) elseif key=="nextphase"then submitIntent(1,"ADVANCE_PHASE",{}) elseif key=="endturn"then if submitIntent(1,"ADVANCE_PHASE",{to=PH_END})then G.autoTimer=1 end end end end do DEBUG_OPP_HAND=false function oppRevealed() return DEBUG_OPP_HAND or G.puzzle~=nil end PHASES={"DRAW","STBY","MAIN","BATTLE","END"} function drawZone(x,y,w,h,c,lbl,cnt) rect(x,y,w,h,c) rectb(x,y,w,h,C_DIVIDER) if lbl then local ly=cnt and y+3 or y+(h-6)//2 print(lbl,x+(w-#lbl*6)//2,ly,C_TEXT,true,1,false) if cnt then local cs=tostring(cnt) print(cs,x+(w-#cs*6)//2,y+13,C_TEXT,true,1,false) end end end function cardTypeInfo(card) if card.cat=="spell"then return"SPELL",C_FACE_SPELL end if card.cat=="trap"then return"TRAP",C_FACE_TRAP end local d=card.id and CARDS[card.id] local face=card.face or(d and d.face) if card.subtype=="fusion"then return"FUSION MONSTER",face or C_FACE_FUSION end if card.effect or card.treatAs then return"EFFECT MONSTER",face or C_FACE_EFFECT end return"NORMAL MONSTER",face or C_FACE_NORMAL end function cardFaceColor(card) local _,col=cardTypeInfo(card) return col end local function drawZoneCounter(card,x,y) local n=card and not card.facedown and(card.swordsCounter or card.counters) if n then print(tostring(n),x+ZONE_W_MAIN-7,y+2,C_ACCENT,true,1,false)end end function drawCardAtk(x,y,card,zc) rect(x,y,ZONE_W_MAIN,ZONE_H,zc) rect(x+2,y+1,ZONE_W_MAIN-4,ZONE_H-2,cardFaceColor(card)) if card.spr then spr(sprOf(card),x+3,y+2,-1,1,0,0,2,2)end clip(x+1,y,ZONE_W_MAIN-2,ZONE_H) spr(SPR_FRAME,x+1,y,15,1,0,0,3,3) clip() end function drawCardDef(x,y,card,zc) rect(x,y,ZONE_W_MAIN,ZONE_H,zc) rect(x+1,y+2,ZONE_W_MAIN-2,ZONE_H-4,cardFaceColor(card)) if card.spr then spr(sprOf(card),x+4,y+3,-1,1,0,1,2,2)end clip(x,y+1,ZONE_W_MAIN,ZONE_H-2) spr(SPR_FRAME,x-2,y+1,15,1,0,1,3,3) clip() end function drawCardSpell(x,y,card,zc) drawCardAtk(x,y,card,zc) end function cardAttrSpr(card) local key=(card.cat=="monster")and card.attr or card.cat return key and ATTR_SPR[key] end OVERLAY_X,OVERLAY_Y,OVERLAY_W,OVERLAY_H=20,10,200,116 function drawOverlayBox(x,y,w,h) rect(x,y,w,h,C_BG) rectb(x,y,w,h,C_DIVIDER) rectb(x+1,y+1,w-2,h-2,C_DIVIDER) end function drawCardInfo(cd) local bx,by,bw,bh=OVERLAY_X,OVERLAY_Y,OVERLAY_W,OVERLAY_H drawOverlayBox(bx,by,bw,bh) print(string.sub(cd.name or"?",1,28),bx+5,by+5,C_TEXT,true,1,false) local attrSpr=cardAttrSpr(cd) if attrSpr then spr(attrSpr,bx+bw-21,by+4,0,2,0,0,1,1)end if cd.cat=="monster"and cd.lvl then for i=1,cd.lvl do spr(SPR_STAR,bx+5+(i-1)*6,by+13,0,1,0,0,1,1)end end local artY=(cd.cat=="monster")and by+22 or by+19 if cd.spr then spr(sprOf(cd),bx+5,artY,-1,2,0,0,2,2)end if cd.cat=="monster"then print("ATK: "..printedStat(cd,"atk"),bx+42,by+20,C_TEXT,true,1,false) print("DEF: "..printedStat(cd,"def"),bx+42,by+30,C_TEXT,true,1,false) print("LV : "..cd.lvl,bx+42,by+40,C_TEXT,true,1,false) if cd.type then print(cd.type:upper(),bx+42,by+50,C_TEXT,true,1,false)end elseif cd.cat=="spell"or cd.cat=="trap"then local k=cd.subtype or"normal" local sp=KIND_SPR[k] if sp then spr(sp,bx+42,artY+2,0,2,0,0,1,1)end print(k:upper(),bx+62,artY+8,C_TEXT,true,1,false) end line(bx+2,by+58,bx+bw-3,by+58,C_DIVIDER) local lbl,col=cardTypeInfo(cd) print(lbl,bx+5,by+62,col,true,1,false) local scrollable=false if cd.desc then local dx,dy,dmaxY,lineH=bx+5,by+71,by+bh-10,7 local visN=math.floor((dmaxY-dy)/lineH) local lines=wrapLines(cd.desc,bw-16) local maxScroll=math.max(0,#lines-visN) scrollable=maxScroll>0 local off=math.max(0,math.min(G.infoScroll or 0,maxScroll)) G.infoScroll=off for i=1,visN do local li=off+i if li>#lines then break end print(lines[li],dx,dy+(i-1)*lineH,C_TEXT,true,1,true) end local ax=bx+bw-9 if off>0 then tri(ax,dy+1,ax+5,dy+1,ax+2,dy-2,C_TEXT)end if off",leftX,y,YELLOW,true,1,false) print("<",rightX,y,YELLOW,true,1,false) end function cardBackSpr() return(CARD_BACKS[CARD_BACK_SEL]or CARD_BACKS[1]).spr end function drawCardBack(x,y,w,h,rot,fl,syOff,s) rot=rot or 0;fl=fl or 0;syOff=syOff or 0;s=s or 1 local sx=x+(w-20*s)//2-(rot==1 and 3 or 0) clip(x,y,w,h) spr(cardBackSpr(),sx,y+syOff,CARD_BACK_KEY,s,fl,rot,3,3) clip() end function drawHandPlr(x,y,card) rect(x+1,y+1,HAND_W-2,HAND_H_PLR-2,cardFaceColor(card)) if card.spr then spr(sprOf(card),x+2,y+2,-1,1,0,0,2,2)end clip(x,y,HAND_W,HAND_H_PLR) spr(SPR_FRAME,x,y,15,1,0,0,3,3) clip() end function drawHandOppDebug(x,y,card) rect(x+1,y+1,HAND_W-2,HAND_H_OPP-2,cardFaceColor(card)) if card.spr then clip(x+2,y+1,HAND_W-4,HAND_H_OPP-2) spr(sprOf(card),x+2,y+2,-1,1,0,0,2,2) clip() end clip(x,y,HAND_W,HAND_H_OPP) spr(SPR_FRAME,x,y,15,1,0,0,3,3) clip() end function drawDotBorder(x,y,w,h,col) col=col or C_CURSOR local o=G.tick//15%2 for i=0,w-1 do if(i+o)%2==0 then pix(x+i,y,col);pix(x+i,y+h-1,col)end end for i=1,h-2 do if(i+o)%2==0 then pix(x,y+i,col);pix(x+w-1,y+i,col)end end end local function drawSealMark(plr,col,x,y) if G.mon[plr][col]or not zoneSealed(plr,col)then return end spr(SPR_SEALED,x+3,y+3,0,1,0,0,2,2) end function drawFieldSlot(x,y,card,facedown,zc) if not card then drawZone(x,y,ZONE_W_MAIN,ZONE_H,zc) elseif facedown then rect(x,y,ZONE_W_MAIN,ZONE_H,zc) if card.pos==2 then drawCardBack(x,y+1,ZONE_W_MAIN,ZONE_H-2,1) else drawCardBack(x,y,ZONE_W_MAIN,ZONE_H) end elseif card.cat=="spell"or card.cat=="trap"then drawCardSpell(x,y,card,zc) elseif card.pos==2 then drawCardDef(x,y,card,zc) else drawCardAtk(x,y,card,zc) end end function drawFieldSpellSlot(x,y,card) if not card then drawZone(x,y,ZONE_W_SPECIAL,ZONE_H,C_ZONE_FIELD,"FS") return end rect(x,y,ZONE_W_SPECIAL,ZONE_H,C_ZONE_FIELD) if card.facedown then drawCardBack(x,y+1,ZONE_W_SPECIAL,ZONE_H-2,0) else rect(x+2,y+1,ZONE_W_SPECIAL-4,ZONE_H-2,C_FACE_SPELL) if card.spr then spr(sprOf(card),x+2,y+2,-1,1,0,0,2,2)end end rectb(x,y,ZONE_W_SPECIAL,ZONE_H,C_DIVIDER) end function drawCursorRect(x,y,w,h,col) drawDotBorder(x-1,y-1,w+2,h+2,col) end function drawLPBar(x,y,w,lp) rect(x,y,w,9,C_BG) rect(x,y,math.min(w,w*lp//(G.lpFull or START_LP)),9,C_LP) rectb(x,y,w,9,C_DIVIDER) print(lp,x+2,y+2,C_TEXT,true,1,true) end local function drawPileZone(plr,row,col,lbl,pile) local x,y=zoneXY(plr,row) drawZone(x,y,ZONE_W_SPECIAL,ZONE_H,col,lbl,#pile) end local function drawTargetOutline(plr,c,row,x,y,w) if G.mode=="sel_target"and isTargetHighlighted(plr,c,row)then drawDotBorder(x,y,w,ZONE_H,C_TARGET) end end function drawOppSide() drawHandRow(2) drawPileZone(2,"dk",C_ZONE_DECK,"DK",G.deck[2]) for c=1,3 do local x,y=zoneXY(2,"st",c) local card=G.st[2][c] drawFieldSlot(x,y,card,card and card.facedown,C_ZONE_OPP) drawTargetOutline(2,c,"st",x,y,ZONE_W_MAIN) drawZoneCounter(card,x,y) end drawPileZone(2,"ed",C_ZONE_EXTRA,"ED",G.extra[2]) drawPileZone(2,"gy",C_ZONE_GY,"GY",G.gy[2]) for c=1,3 do local x,y=zoneXY(2,"mon",c) local card=G.mon[2][c] drawFieldSlot(x,y,card,card and card.facedown,C_ZONE_OPP) drawSealMark(2,c,x,y) if G.mode=="sel_atk"and card and not struckThisTurn(G.pending and G.mon[1][G.pending.atkCol],c)then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H,C_TARGET) end drawTargetOutline(2,c,"mon",x,y,ZONE_W_MAIN) if G.battleAnim and G.battleAnim.atkCol==c then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H,C_ATTACK) end end local fx,fy=zoneXY(2,"fs") drawFieldSpellSlot(fx,fy,G.fs[2]) drawTargetOutline(2,nil,"fs",fx,fy,ZONE_W_SPECIAL) end function drawPlrSide() local fx,fy=zoneXY(1,"fs") drawFieldSpellSlot(fx,fy,G.fs[1]) drawTargetOutline(1,nil,"fs",fx,fy,ZONE_W_SPECIAL) local tribCol={} if(G.mode=="sel_tribute"or G.mode=="sel_mon")and G.pending and G.pending.tributes then for _,t in ipairs(G.pending.tributes)do tribCol[t]=true end end for c=1,3 do local x,y=zoneXY(1,"mon",c) local card=G.mon[1][c] drawFieldSlot(x,y,card,card and card.facedown,tribCol[c]and C_TRIBUTE or C_ZONE_PLR) drawSealMark(1,c,x,y) if G.mode=="sel_tribute"and card and not tribCol[c]then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H) elseif G.mode=="sel_mon"and(not card or tribCol[c])then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H) elseif G.mode=="sel_target"and isTargetHighlighted(1,c,"mon")then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H,C_TARGET) elseif G.mode=="sel_atk"and G.pending and c==G.pending.atkCol then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H,C_ACCENT) end if G.battleAnim and G.battleAnim.tgtCol==c then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H,C_ATTACK) end end drawPileZone(1,"gy",C_ZONE_GY,"GY",G.gy[1]) drawPileZone(1,"ed",C_ZONE_EXTRA,"ED",G.extra[1]) for c=1,3 do local x,y=zoneXY(1,"st",c) local card=G.st[1][c] drawFieldSlot(x,y,card,card and card.facedown,C_ZONE_PLR) drawZoneCounter(card,x,y) if G.mode=="sel_st"and not card then drawDotBorder(x,y,ZONE_W_MAIN,ZONE_H)end drawTargetOutline(1,c,"st",x,y,ZONE_W_MAIN) end drawPileZone(1,"dk",C_ZONE_DECK,"DK",G.deck[1]) drawHandRow(1) end function handRowOpen(plr) return plr==1 or winScenePlr()==plr end function handRowH(plr) return handRowOpen(plr)and HAND_H_PLR or HAND_H_OPP end function drawHandRow(plr) local n=math.min(#G.hand[plr],MAX_HAND) local _,hy=zoneXY(plr,"hand") local open=handRowOpen(plr) for i=0,n-1 do local x,card=handX(n,i),G.hand[plr][i+1] if open then drawHandPlr(x,hy,card) elseif oppRevealed()then drawHandOppDebug(x,hy,card) else drawCardBack(x,hy,HAND_W,HAND_H_OPP,0,0,-11)end end end function dissolveFieldToMat(t,plr) local step=(t*3)//WIN_SCENE_FADE local gap=1 if step<1 then gap=4 elseif step<2 then gap=2 end local _,hy=zoneXY(plr,"hand") local h=handRowH(plr) for y=0,SCREEN_H-1,gap do if y=hy+h then rect(FIELD_X+1,y,FIELD_W-2,1,C_PLAYMAT) if y%4==0 then for px=FIELD_X,FIELD_X+FIELD_W-1,4 do pix(px,y,C_BG)end end end end end function winSceneCentre(plr) local _,hy=zoneXY(plr,"hand") local top,bot=0,hy if plr~=1 then top,bot=hy+handRowH(plr),SCREEN_H end return FIELD_X+FIELD_W//2,(top+bot)//2 end function drawDuelField() drawPlaymat(FIELD_X,FIELD_W) local t=winSceneT() if winScenePlr()then drawHandRow(G.winner);return end drawOppSide() line(COL_X[0],DIVIDER_Y,COL_X[4]+ZONE_W_SPECIAL-1,DIVIDER_Y,C_DIVIDER) drawPlrSide() if t then dissolveFieldToMat(t,G.winner)end end function drawPopup() local p=G.popup if not p then return end local cx=FIELD_X+FIELD_W//2 local cy=SCREEN_H//2 local w,h=56,56 local bx,by=cx-w//2,cy-h//2 rect(bx,by,w,h,C_BG);rectb(bx,by,w,h,C_TEXT) local label=(p.type=="coin")and"COIN FLIP"or"DICE ROLL" print(label,cx-#label*2,by+4,C_TEXT,true,1,true) local elapsed=(p.frames or 180)-(p.t or 180) local settled=elapsed>=60 local sprId if p.type=="coin"and settled then sprId=(p.result==1)and 61 or 60 elseif p.type=="coin"then sprId=44+(elapsed//3)%4 elseif settled then sprId=69+p.result else sprId=69+(p.displayed or p.result)end spr(sprId,cx-12,by+18,0,3,0,0,1,1) end function drawCursor() local c=G.cur local cx,cy,cw,ch if c.row==3 then local n=#G.hand[c.side] if n==0 or c.col>=n then return end local _,handY=zoneXY(c.side,"hand") cx,cy=handX(math.min(n,MAX_HAND),c.col),handY cw,ch=HAND_W,(c.side==1)and HAND_H_PLR or HAND_H_OPP else local _,rowY=zoneXY(c.side,(c.row==1)and"mon"or"st") cx,cy=COL_X[c.col],rowY cw,ch=colW(c.col),ZONE_H end drawCursorRect(cx,cy,cw,ch,(G.mode=="sel_atk")and C_ATTACK or C_CURSOR) end function drawChain() if not G.proc or not G.proc.chain then return end local links=G.proc.chain.links local n=#links if n==0 then return end local rowH,w=7,70 local h=10+n*rowH local x=FIELD_X+(FIELD_W-w)//2 local y=DIVIDER_Y-h//2 rect(x,y,w,h,C_BG) rectb(x,y,w,h,C_TEXT) print("CHAIN "..n,x+4,y+2,C_ACCENT,true,1,true) for i=1,n do local lk=links[i] local nm=(lk.source and lk.source.name)or"?" if#nm>13 then nm=nm:sub(1,13)end print(i..":"..nm,x+4,y+9+(i-1)*rowH,C_TEXT,true,1,true) end end function drawModeBanner() local txt if G.mode=="sel_target"and G.targetSel and G.targetSel.title then txt=G.targetSel.title elseif G.mode=="choose_response"then txt="RESPOND? A:yes B:pass" else txt=puzzleBannerText() end if not txt then return end local w=#txt*4+8 local x=FIELD_X+(FIELD_W-w)//2 local y=DIVIDER_Y+24 rect(x,y,w,9,C_ACCENT) rectb(x,y,w,9,C_TEXT) print(txt,x+4,y+2,C_TEXT,true,1,true) end local function lpRollStep(gap)return math.max(60,math.floor(gap*0.10))end local function lpRollFrames(gap) local n=0 while gap>0 do gap=gap-lpRollStep(gap);n=n+1 end return n end LP_ROLL_DELAY=15 local lpHold={0,0} local lpGap={0,0} function tickDispLp() for p=1,2 do local gap=G.dispLp[p]-G.lp[p] if gap<=0 then lpHold[p]=0 elseif gap>lpGap[p]then lpHold[p]=LP_ROLL_DELAY end if gap<0 and gap0 then lpHold[p]=lpHold[p]-1 if lpHold[p]==0 then playSfx("damage",lpRollFrames(gap))end end if lpHold[p]==0 and gap~=0 then local step=lpRollStep(math.abs(gap)) if gap<0 then G.dispLp[p]=math.min(G.lp[p],G.dispLp[p]+step) else G.dispLp[p]=math.max(G.lp[p],G.dispLp[p]-step)end end end end local function panelCardName(card) local nameStr=card.name or"?" local avail=PANEL_W-12 local w=#nameStr*4 if w<=avail then print(nameStr,2,11,C_TEXT,true,1,true) return end local ns=G.nameScroll if ns.card~=card then ns.card=card;ns.offset=0;ns.pause=NAME_SCROLL_PAUSE;ns.atEnd=false end if ns.pause>0 then ns.pause=ns.pause-1 elseif ns.atEnd then ns.offset=0;ns.atEnd=false;ns.pause=NAME_SCROLL_PAUSE elseif G.tick%3==0 then ns.offset=ns.offset+1 if ns.offset>w-avail then ns.offset=w-avail;ns.atEnd=true;ns.pause=NAME_SCROLL_PAUSE//2 end end clip(2,11,avail,7) print(nameStr,2-ns.offset,11,C_TEXT,true,1,true) clip() end local function panelCardInfo(card,onField) panelCardName(card) local attrSpr=cardAttrSpr(card) if attrSpr then spr(attrSpr,PANEL_W-10,10,0,1,0,0,1,1)end if card.cat=="monster"and card.lvl then for i=1,getMonLvl(card)do spr(SPR_STAR,2+(i-1)*6,18,0,1,0,0,1,1)end end local artY=(card.cat=="monster")and 24 or 21 if card.spr then spr(sprOf(card),2,artY,-1,2,0,0,2,2)end if card.cat=="monster"then local function statColor(eff,base) if not onField or eff==base or hasUnknownStats(card)then return C_TEXT end return(eff>base)and C_STAT_UP or C_STAT_DOWN end local effAtk=onField and getMonAtk(card)or card.atk local effDef=onField and getMonDef(card)or card.def print("ATK "..(onField and effAtk or printedStat(card,"atk")),38,27,statColor(effAtk,card.atk),true,1,true) print("DEF "..(onField and effDef or printedStat(card,"def")),38,34,statColor(effDef,card.def),true,1,true) print((card.pos==2)and"DEF POS"or"ATK POS",38,41,C_TEXT,true,1,true) if card.type then print(card.type:upper(),38,48,C_TEXT,true,1,true)end else local k=card.subtype or"normal" local sp=KIND_SPR[k] if sp then spr(sp,51,artY+2,0,2,0,0,1,1)end local lbl=k:upper() print(lbl,38+(45-#lbl*4)//2,artY+22,C_TEXT,true,1,true) end end local MODE_HINTS={ sel_atk={col=C_ACCENT,a="A: attack",b="B: cancel", title=function()return hasMonsters(2)and"SELECT TARGET"or"DIRECT ATK"end}, sel_tribute={col=C_TRIBUTE,a="A: pick",b="B: cancel", title=function() local p=G.pending return"TRIBUTE "..(p and tributeValueSelected(p)or 0).."/"..(p and p.tribNeeded or 0) end}, sel_mon={col=C_PLACE,a="A: place",b="B: cancel",title=function()return"SELECT ZONE"end}, sel_st={col=C_PLACE,a="A: place",b="B: cancel",title=function()return"SET ZONE"end}, } local function panelModeHint(card,facedown,pw) local hint=MODE_HINTS[G.mode] if hint then print(hint.title(),2,71,hint.col,true,1,true) print(hint.a,2,79,C_TEXT,true,1,true) print(hint.b,2,86,C_TEXT,true,1,true) elseif G.mode=="free"and card and not facedown then local label,col=cardTypeInfo(card) print(label,2,71,col,true,1,true) if card.desc then printWrap(card.desc,2,79,pw,C_TEXT,SCREEN_H-10)end elseif G.menu.hint then print("no action",2,71,C_TEXT,true,1,true) G.menu.hint=nil end end local function panelMenu() rect(0,70,PANEL_W,57,C_BG) for i,item in ipairs(G.menu.items)do local iy=72+(i-1)*8 if i==G.menu.sel then rect(0,iy-1,PANEL_W-1,8,C_MENU_SEL) print(">"..item[1],2,iy,C_ACCENT,true,1,false) else print(" "..item[1],2,iy,C_TEXT,true,1,false) end end local divY=72+#G.menu.items*8 line(0,divY,PANEL_W-1,divY,C_DIVIDER) end function drawPanel() line(SEPARATOR_X,0,SEPARATOR_X,SCREEN_H-1,C_DIVIDER) local pw=PANEL_W-4 local c=G.cur local card=getHoveredCard() local facedown=not oppRevealed()and c.side==2 and(c.row==3 or(card~=nil and card.facedown)) drawLPBar(2,0,pw,G.dispLp[2]) if card and not facedown then panelCardInfo(card,c.row==1) elseif facedown and card then print("???",2,11,C_TEXT,true,1,true) end line(0,57,PANEL_W-1,57,C_DIVIDER) if G.puzzle then print("PUZZLE",2,60,C_ACCENT,true,1,false) else print("TURN "..G.turn,2,60,C_TEXT,true,1,false)end local phase=PHASES[G.ph] print(phase,PANEL_W-2-#phase*6,60,(G.active==1)and C_ACCENT or C_TEXT,true,1,false) line(0,68,PANEL_W-1,68,C_DIVIDER) panelModeHint(card,facedown,pw) if G.menu.open then panelMenu()end drawLPBar(2,SCREEN_H-9,pw,G.dispLp[1]) end local LIST_NUM_X=OVERLAY_X+4 local LIST_NAME_X=OVERLAY_X+18 local LIST_ATK_X=OVERLAY_X+OVERLAY_W-52 local function drawListRow(iy,isSel,disp,name,nameLen,atk,nameCol) local tc=isSel and C_BG or(nameCol or C_TEXT) local dc=isSel and C_BG or C_TEXT print(disp..".",LIST_NUM_X,iy,dc,true,1,false) print(string.sub(name or"?",1,nameLen),LIST_NAME_X,iy,tc,true,1,false) if atk then print("ATK "..atk,LIST_ATK_X,iy,dc,true,1,false)end end local function drawCardStatLine(c) if not c then return end local y=OVERLAY_Y+OVERLAY_H-16 if c.cat=="monster"then print("ATK:"..printedStat(c,"atk").." DEF:"..printedStat(c,"def").." LV:"..(c.lvl or 0),OVERLAY_X+4,y,C_TEXT,true,1,false) else print(((c.cat or""):upper()).." - "..((c.subtype or"normal"):upper()),OVERLAY_X+4,y,C_TEXT,true,1,false) end end function drawListBox(o) local bx,by,bw,bh=OVERLAY_X,OVERLAY_Y,OVERLAY_W,OVERLAY_H drawOverlayBox(bx,by,bw,bh) print(o.title,bx+4,by+4,o.titleCol or C_ACCENT,true,1,false) local cnt="("..o.n..")" print(cnt,bx+bw-4-#cnt*6,by+4,C_TEXT,true,1,false) line(bx+2,by+12,bx+bw-3,by+12,C_DIVIDER) if o.n==0 then print("Empty",bx+(bw-30)//2,by+bh//2-3,C_TEXT,true,1,false) print("B:close",bx+4,by+bh-8,C_HINT,true,1,true) return end local listY,rowH=by+15,10 local botDiv=by+bh-19 local maxVis=math.floor((botDiv-listY)/rowH) local dispSel=o.reverse and(o.n-o.sel+1)or o.sel local scrollTop=math.max(1,math.min(dispSel-maxVis//2,math.max(1,o.n-maxVis+1))) for r=1,maxVis do local disp=scrollTop+r-1 if disp>o.n then break end local i=o.reverse and(o.n-disp+1)or disp local iy=listY+(r-1)*rowH local isSel=(i==o.sel) if isSel then rect(bx+2,iy-1,bw-4,9,C_HIGHLIGHT)end o.row(i,iy,isSel,disp) end if o.n>maxVis then local barH=maxVis*rowH local pct=(scrollTop-1)/math.max(1,o.n-maxVis) local markY=listY+math.floor(pct*(barH-4)) rect(bx+bw-4,listY,2,barH,C_BG);rect(bx+bw-4,markY,2,4,C_DIVIDER) end line(bx+2,botDiv,bx+bw-3,botDiv,C_DIVIDER) if o.footer then o.footer(o.sel)end print(o.hint,bx+4,by+bh-8,C_HINT,true,1,true) end function drawGYView() local gv=G.gyView local gy=G.gy[gv.plr] local gyCanFire=false if gv.plr==1 then for _,c in ipairs(gy)do if gyIgnitionReady(c,1)then gyCanFire=true;break end end end drawListBox{ title=(gv.plr==1)and"YOUR GRAVEYARD"or"OPP GRAVEYARD", n=#gy,sel=gv.sel,reverse=true, hint=gyCanFire and"UP/DN:browse A:activate B:close X:info" or"UP/DN:browse B:close X:info", row=function(i,iy,isSel,disp) local card=gy[i] local isMon=card.cat=="monster" drawListRow(iy,isSel,disp,card.name,isMon and 21 or 24,isMon and printedStat(card,"atk"), gv.plr==1 and gyIgnitionReady(card,1)and C_ACCENT or nil) if card.cat=="spell"then print("SPELL",OVERLAY_X+OVERLAY_W-34,iy,isSel and C_BG or C_FACE_SPELL,true,1,false) elseif card.cat=="trap"then print("TRAP",OVERLAY_X+OVERLAY_W-28,iy,isSel and C_BG or C_FACE_TRAP,true,1,false) end end, footer=function(i)drawCardStatLine(gy[i])end, } end function drawEDView() local ev=G.edView local ex=G.extra[ev.plr] drawListBox{ title="YOUR EXTRA DECK", titleCol=C_FACE_FUSION, n=#ex,sel=ev.sel, hint="UP/DN:browse B:close X:info", row=function(i,iy,isSel,disp) local card=ex[i] drawListRow(iy,isSel,disp,card.name,21,printedStat(card,"atk")) end, footer=function(i)drawCardStatLine(ex[i])end, } end function drawDeckSelect() local ds=G.deckSel drawListBox{ title=string.sub(ds.title or"?",1,26), n=#ds.items,sel=ds.sel, hint=(not ds.forced and ds.onCancel)and"UP/DN:browse A:pick B:skip X:info" or"UP/DN:browse A:pick X:info", row=function(i,iy,isSel,disp) local item=ds.items[i] drawListRow(iy,isSel,disp,item.name,item.atk and 21 or 28,item.atk and printedStat(item.card or item,"atk")) end, footer=function(i)drawCardStatLine(ds.items[i]and ds.items[i].card)end, } end function glowHandCards(plr,pred,n) local hand=G.hand[plr] local cnt=math.min(#hand,MAX_HAND) local _,hy=zoneXY(plr,"hand") local h=handRowH(plr) local lit=0 for i=1,cnt do if pred(hand[i])then lit=lit+1 if lit>n then return end rectb(handX(cnt,i-1)-1,hy-1,HAND_W+2,h+2,YELLOW) end end end function dimScreen() for y=0,SCREEN_H-1,2 do rect(0,y,SCREEN_W,1,C_BG)end end function drawGameOver() dimScreen() drawOverlayBox(WINBOX_X,WINBOX_Y,WINBOX_W,WINBOX_H) local win=(G.winner==1) local msg=win and"YOU WIN!"or"YOU LOSE!" local col=win and C_ACCENT or C_ATTACK print(msg,(SCREEN_W-#msg*12)//2,WINBOX_Y+8,col,true,2,false) local lp1="YOU "..G.lp[1].." LP" local lp2=(G.oppName or"OPP").." "..G.lp[2].." LP" printCentre(lp1,0,SCREEN_W,WINBOX_Y+28,win and C_LP or C_ATTACK,false) printCentre(lp2,0,SCREEN_W,WINBOX_Y+38,win and C_ATTACK or C_LP,false) local run=themeGameOverLine(win)or survivalGameOverLine(win) if run then printCentre(run,0,SCREEN_W,WINBOX_Y+50,C_TEXT,true)end if winHoldOver()and(winElapsed()//30)%2==0 then printCentre("A: CONTINUE",0,SCREEN_W,WINBOX_Y+(run and 62 or 56),C_TEXT,false) end end end do function zoneChoiceTargets(req) local out={} local specs=req.specs or(req.spec and{req.spec}) if specs then for _,sp in ipairs(specs)do local row=sp.zone or"mon" if row~="st"and row~="fs"then row="mon"end for _,e in ipairs(listTargets(sp,req.plr))do e.row=row;out[#out+1]=e end end return out end local sides=(req.side==1 or req.side==2)and{req.side}or{1,2} local row=req.row or"mon" for _,p in ipairs(sides)do local arr=(row=="st")and G.st[p]or G.mon[p] for c=1,3 do local card=arr[c] if card and(not req.filter or req.filter(card,p,c))then out[#out+1]={plr=p,col=c,card=card,row=row} end end end return out end function syncTargetCursor() local ts=G.targetSel local e=ts and ts.valid[ts.idx] if not e then return end if e.row=="fs"then G.cur={side=e.plr,row=1,col=(e.plr==1)and 0 or 4} else G.cur={side=e.plr,row=(e.row=="st")and 2 or 1, col=(e.plr==2)and(4-e.col)or e.col} end end function cursorTargetKey() local c=G.cur if c.row==1 and c.side==1 and c.col==0 then return 1,nil,"fs"end if c.row==1 and c.side==2 and c.col==4 then return 2,nil,"fs"end if c.row~=1 and c.row~=2 then return nil end if c.col<1 or c.col>3 then return nil end return c.side,(c.side==2)and(4-c.col)or c.col,(c.row==1)and"mon"or"st" end function isTargetHighlighted(plr,col,row) local ts=G.targetSel if not ts then return false end for _,e in ipairs(ts.valid)do if e.plr==plr and e.col==col and e.row==row then return true end end return false end local function listenerRow(s) return(s.zone=="mon"and 1)or(s.zone=="hand"and 3)or 2 end local function listenerCol(s) return(s.zone=="hand")and(s.col-1)or s.col end local function optionAtCursor(options,c) for i,opt in ipairs(options)do local s=opt.self if s.plr==c.side and c.col==listenerCol(s)and c.row==listenerRow(s)then return i end end end function handleChooseResponse() local req=G.proc and G.proc.currentChoice if not(req and req.options and#req.options>0)then G.mode="free" if G.proc then G.proc.currentChoice=nil end return end local c=G.cur if btnp(2)or btnp(3)then local n=#req.options local cur=optionAtCursor(req.options,c)or 1 local nxt=btnp(3)and(cur%n+1)or((cur-2)%n+1) local s=req.options[nxt].self G.cur={side=s.plr,row=listenerRow(s),col=listenerCol(s)} elseif btnp(4)then local idx=optionAtCursor(req.options,c) if idx then submitIntent(1,"RESPONSE",{optionIdx=idx})end elseif btnp(5)then submitIntent(1,"RESPONSE",{pass=true}) end end local function stepCursorRow(c,toward) local wasHand=(c.row==3) if c.side~=toward then if c.row==3 then c.row=2 elseif c.row==2 then c.row=1 else c.side=toward;c.row=1 end else if c.row==1 then c.row=2 elseif c.row==2 then c.row=3 end end if c.row==3 then clampToHand(c.side) elseif wasHand then c.col=math.min(c.col,4)end end function moveCursorFree(c) if btnp(0)then stepCursorRow(c,2)end if btnp(1)then stepCursorRow(c,1)end if btnp(2)then c.col=math.max(0,c.col-1)end if btnp(3)then if c.row==3 then c.col=math.min(math.max(0,#G.hand[c.side]-1),c.col+1) else c.col=math.min(4,c.col+1) end end end function handleDeckSelectInput() local ds=G.deckSel if btnp(0)then ds.sel=math.max(1,ds.sel-1) elseif btnp(1)then ds.sel=math.min(#ds.items,ds.sel+1) elseif btnp(4)then local item=ds.items[ds.sel] if item then G.mode="free";G.deckSel=nil;ds.onPick(item.deckIdx,item)end elseif btnp(5)then if not ds.forced and ds.onCancel then G.mode="free";G.deckSel=nil;ds.onCancel() end elseif btnp(6)then local item=ds.items[ds.sel] if item and item.card then G.infoCard=item.card end end end local function cancelToHand() local idx=G.pending and G.pending.handIdx-1 or 0 G.mode="free";G.pending=nil G.cur={side=1,row=3,col=idx} end local function stepToCol(c,dir,ok) for col=c.col+dir,(dir<0)and 1 or 3,dir do if ok(col)then c.col=col;return end end end local function zoneMode(ok,confirm,cancel) return function() local c=G.cur if btnp(2)then stepToCol(c,-1,ok) elseif btnp(3)then stepToCol(c,1,ok) elseif btnp(4)then confirm(c.col) elseif b%tnp(5)then cancel()end end end local function listViewMode(field,pile,newestFirst,activate) return function() local view=G[field] if not view then return false end local items=G[pile][view.plr] local step=newestFirst and 1 or-1 local function move(d)view.sel=math.max(1,math.min(#items,view.sel+d))end if btnp(0)then move(step) elseif btnp(1)then move(-step) elseif btnp(4)then if activate then activate(view,items)end elseif btnp(6)then if items[view.sel]then G.infoCard=items[view.sel]end elseif btnp(5)then G.mode="free";G[field]=nil end end end local function modeDeckSelect() if not G.deckSel then return false end handleDeckSelectInput() end local modeGYView=listViewMode("gyView","gy",true,function(view,items) if view.plr~=1 or not gyIgnitionReady(items[view.sel],1)then return end local idx=view.sel G.mode="free";G.gyView=nil submitIntent(1,"GY_IGNITION",{gyIdx=idx}) end) local modeEDView=listViewMode("edView","extra",false) local function oppMonAt(col) local atk=G.pending and G.mon[1][G.pending.atkCol] return G.mon[2][4-col]~=nil and not struckThisTurn(atk,4-col) end local function confirmAttack(col) local tgtCol=hasMonsters(2)and(4-col)or nil submitIntent(1,"DECLARE_ATTACK",{atkCol=G.pending.atkCol,tgtCol=tgtCol}) end local function cancelAttack() local col=G.pending and G.pending.atkCol or 2 G.mode="free";G.pending=nil G.cur={side=1,row=1,col=col} end local modeAttack=zoneMode(oppMonAt,confirmAttack,cancelAttack) local function myMonAt(col)return G.mon[1][col]~=nil end local function confirmTribute(col) if col<1 or col>3 or not myMonAt(col)then return end local p=G.pending if not tributable(G.mon[1][col],p.card)then return end local found=false for i,t in ipairs(p.tributes)do if t==col then table.remove(p.tributes,i);found=true;break end end if not found and tributeValueSelected(p)=p.tribNeeded then G.mode="sel_mon" G.cur={side=1,row=1,col=firstEmpty(G.mon[1])or p.tributes[1]or 1} end end local modeTribute=zoneMode(myMonAt,confirmTribute,cancelToHand) local function monZoneFree(col) if zoneSealed(1,col)then return false end if not G.mon[1][col]then return true end if G.pending and G.pending.tributes then for _,t in ipairs(G.pending.tributes)do if t==col then return true end end end return false end local function confirmMonZone(col) if col<1 or col>3 or not monZoneFree(col)then return end local p=G.pending local ok=submitIntent(1,"SUMMON",{ card=p.card,col=col,position=(p.action=="set")and"SET"or"ATK", tributes=p.tributes,handIdx=p.handIdx,stTribute=p.stTribute,soul=p.soul~=nil, extra=(p.action=="summon_extra"), }) if ok then G.mode="free";G.pending=nil G.cur={side=1,row=1,col=col} end end local modeMonZone=zoneMode(monZoneFree,confirmMonZone,cancelToHand) local function stZoneFree(col)return G.st[1][col]==nil end local function confirmSTZone(col) if col<1 or col>3 or not stZoneFree(col)then return end local p=G.pending local t=(p.action=="cast_hand")and"CAST"or"SET_ST" if submitIntent(1,t,{card=p.card,col=col,handIdx=p.handIdx})then G.cur={side=1,row=2,col=col} end end local modeSTZone=zoneMode(stZoneFree,confirmSTZone,cancelToHand) local function modeTarget() local ts=G.targetSel if not ts then return false end moveCursorFree(G.cur) if btnp(4)then local plr,col,row=cursorTargetKey() if plr and isTargetHighlighted(plr,col,row)then G.mode="free";G.targetSel=nil ts.onPick(plr,col,row) end elseif btnp(5)and not ts.forced then G.mode="free";G.targetSel=nil ts.onPick(nil) end end local MODE_HANDLERS={ choose_response=handleChooseResponse, sel_deck=modeDeckSelect, gy_view=modeGYView, ed_view=modeEDView, sel_atk=modeAttack, sel_tribute=modeTribute, sel_mon=modeMonZone, sel_st=modeSTZone, sel_target=modeTarget, } local function answerMenu(key) local cb=G.menu.onChoose G.menu={open=false,sel=1,items={}} cb(key) end local function handleActionMenu() if btnp(0)then G.menu.sel=math.max(1,G.menu.sel-1) elseif btnp(1)then G.menu.sel=math.min(#G.menu.items,G.menu.sel+1) elseif btnp(4)then local key=G.menu.items[G.menu.sel][2] if G.menu.onChoose then answerMenu(key)else execAction(key)end elseif btnp(5)and not G.menu.forced then if G.menu.onChoose then answerMenu(nil)else G.menu.open=false end end end local function handleFreeRoam() local c=G.cur moveCursorFree(c) if btnp(4)then local p=c.side local gyCol=(p==1)and 4 or 0 local edCol=(p==1)and 0 or 4 if c.row==1 and c.col==gyCol then if#G.gy[p]>0 then G.mode="gy_view";G.gyView={plr=p,sel=#G.gy[p]}end elseif c.row==2 and c.col==edCol then if p==1 and#G.extra[p]>0 then G.mode="ed_view";G.edView={plr=p,sel=1}end else local items=buildMenu() G.menu=items and{open=true,items=items,sel=1} or{open=false,items={},sel=1,hint=true} end end if btnp(5)and G.active==1 and not G.menu.open then G.menu={open=true,sel=1,items={ {"NEXT PHASE","nextphase"}, {"END TURN","endturn"}, }} end end function handleInfoOverlay() if not G.infoCard then return false end if btnp(0)then G.infoScroll=(G.infoScroll or 0)-1 elseif btnp(1)then G.infoScroll=(G.infoScroll or 0)+1 elseif btnp(4)or btnp(5)then G.infoCard=nil;G.infoScroll=0 end return true end function leaveDuel() sync(3,1,false);playMusic(0) SCENE="menu";TITLE_SEL=1;G={tick=G.tick} end function handleInput() if G.winner then if G.reward then rewardInput() elseif winHoldOver()and btnp(4)then if G.puzzle then puzzleLeave() elseif G.theme then themeRoundOver() elseif G.survival then survivalRoundOver() elseif G.winner==1 then oppMarkBeaten(G.oppIdx);rewardStart() else leaveDuel()end end return end if#ANIM>0 then return end if handleInfoOverlay()then return end local h=MODE_HANDLERS[G.mode] if h and h()~=false then return end if G.menu.open then handleActionMenu();return end handleFreeRoam() end function autoPhase() if G.active~=1 or G.winner or#ANIM>0 then return end if procBusy()then return end if G.ph~=PH_DRAW and G.ph~=PH_STBY and G.ph~=PH_END then return end G.autoTimer=G.autoTimer-1 if G.autoTimer<=0 then G.autoTimer=50 submitIntent(1,"ADVANCE_PHASE",{}) end end end do AI_DELAY=40 AI_DEPTH=5 AI_BEAM=16 AI_SIMS_PER_FRAME=10 AI_RNG_BRANCH_MAX=4 AI_HOLD_SET_CARD=4000 AI_PICK_UNCASTABLE=2000 AI_PICK_UNLOCKS_FUSION=3000 AI_TRACE=false AI_TRACE_BOARD=false AI_CHOICE_POINTS=5 AI_TAIL_POINTS=2 AI_PASS_TOLERANCE=0 AI_TURN_SEARCH=true AI_BATTLE_ACTIVATE=true AI_SUICIDE_ATTACK=false local function targetValue(c) if not c or c.cat~="monster"then return 0 end if c.facedown then return SIM_UNKNOWN_ATK end return(c.pos==2)and(c.def or 0)or(c.atk or 0) end local function aiPickReplay(req) local att,best,bestK=req.attacker,-1,nil for _,e in ipairs(zoneChoiceTargets(req))do local s=0 if not e.card.facedown then local o=battleOutcome(att,e.card,req.plr) s=(o.killTarget and targetValue(e.card)or 0)+o.toDefender -(o.killAttacker and targetValue(att)or 0)-o.toAttacker end if s>best then best=s;bestK={plr=e.plr,col=e.col}end end return bestK end function aiPickZone(req) if req.replay and req.attacker then return aiPickReplay(req)end local best,bestK=-1,nil for _,e in ipairs(zoneChoiceTargets(req))do local s=((e.plr~=req.plr)and 10000 or 0)+targetValue(e.card) if s>best then best=s;bestK={plr=e.plr,col=e.col}end end return bestK end local function pickRank(card,plr) if not(card and(card.cat or"monster")=="monster")then return 0 end local v=card.atk or 0 if tribsNeeded(getMonLvl(card))>countMonsters(plr)and not canSummonSelf(card,plr)then v=v-AI_PICK_UNCASTABLE end for _,f in ipairs(G.extra[plr]or{})do if fusionMaterialsAvailable(f,plr,card.id) and not fusionMaterialsAvailable(f,plr)then return v+AI_PICK_UNLOCKS_FUSION end end return v end function pickRankProbe(card,plr)return pickRank(card,plr)end function aiPickCard(req) local items=cardChoiceItems(req) local plr=req.plr or 2 if req.purpose=="fuel"then local pickI for i,it in ipairs(items)do local isMon=(it.card==nil)or(it.card.cat=="monster") if isMon and(pickI==nil or(it.atk or 0)<(items[pickI].atk or 0))then pickI=i end end local p=pickI and items[pickI] return p and{idx=p.deckIdx,item=p}or nil end local best,bestI=nil,nil for i,it in ipairs(items)do local s if req.purpose=="cost"or req.purpose=="handLimit"or req.purpose=="fuel"then s=it.atk or 0 else s=pickRank(it.card,plr)end local isMonster=(it.card==nil)or(it.card.cat=="monster") local better if req.purpose=="cost"then if not isMonster then s=-1 end better=(best==nil or sbest) end if better then best=s;bestI=i end end if not bestI then return nil end local pick=items[bestI] return{idx=pick.deckIdx,item=pick} end local function holdPenalty(req,i) if req.event==EV_ATTACK then return 0 end local o=req.options and req.options[i] if not o or o.self.zone~="st"then return 0 end local b=behaviorOf(o.self.card) if b and b.responseOnly then return 0 end return AI_HOLD_SET_CARD end function aiAnswer(req) if req.kind=="zone"then return aiPickZone(req)end if req.kind=="card"then return aiPickCard(req)end if req.kind=="menu"then local b=req.summonPosFor if b then return((b.def or 0)>(b.atk or 0))and"def"or"atk"end return req.items[1][2] end if req.kind=="response"then if not req.options or#req.options==0 then return false end local simAns=simScoreResponse(req) local scored=(simAns~=nil) if simAns==nil then simAns=false end local sc=simResponseScores() local held={} if scored and sc and sc[1]then local best=sc[1] simAns=false for i=1,#req.options do held[i]=holdPenalty(req,i) local v=sc[i+1] if v and v-held[i]>best then best,simAns=v-held[i],{optionIdx=i}end end end if AI_TRACE then local names={} for i,o in ipairs(req.options)do names[i]=tostring(o.self.card and o.self.card.name or"?") end local pick=simAns and names[simAns.optionIdx or 1] local parts={} if scored and sc then parts[1]=string.format("pass=%s",sc[1]and string.format("%.0f",sc[1])or"?") for i,n in ipairs(names)do parts[#parts+1]=string.format("%s=%s%s",n, sc[i+1]and string.format("%.0f",sc[i+1])or"?", (held[i]or 0)>0 and string.format("(hold -%d)",held[i])or"") end end trace(string.format("ai response(%s): [%s] -> %s%s", tostring(req.event),table.concat(names,", "), pick and("ACTIVATE "..pick)or"pass", scored and(" | "..table.concat(parts," ")) or" (NO FORK POINT -- not evaluated)")) end simNoteDecision(simAns,req) return simAns end return nil end function aiRouteChoice(req,waitObj) local ok,ans=true,nil if req.kind~="response"then ans=simScoreLiveChoice(req,req.plr or 2) end if ans==nil then ok,ans=pcall(aiAnswer,req) end if not ok then trace("aiAnswer ERROR kind="..tostring(req.kind).." err="..tostring(ans)) ans=nil end if ans==nil then ans=false end if req.kind~="response"then simNoteLiveChoice(ans)end waitObj.result=ans G.proc.currentChoice=nil end local function tributeCombos(card,plr,need) if need<=0 then return{{}},false end local _,soul=soulTarget(plr) if soul then if not tributable(soul,card)then return{},true end need=need-tributeValueOf(card,soul) if need<=0 then return{{}},true end end local mon=G.mon[plr] local cols,val={},{} for c=1,3 do local v=mon[c]and tributeValueOf(card,mon[c])or 0 if v>0 then cols[#cols+1]=c;val[c]=v end end local out={} for i=1,#cols do local a=cols[i] if val[a]>=need then out[#out+1]={a}end for j=i+1,#cols do local b=cols[j] if val[a]+val[b]>=need then out[#out+1]={a,b}end end end if#cols==3 and#out==0 and val[1]+val[2]+val[3]>=need then out[1]={1,2,3}end return out,soul~=nil end function aiEnumerateMain(plr) local moves={} local function add(t,args,label)moves[#moves+1]={type=t,args=args,label=label}end local mon,stz=G.mon[plr],G.st[plr] local monCol,stCol=firstEmpty(mon),firstEmpty(stz) local function eachSummonSlot(card,fn) local combos,soul=tributeCombos(card,plr,tribsNeeded(getMonLvl(card))) for _,tr in ipairs(combos)do local dest=monCol or tr[1] if dest and zonesAllowed(plr,#tr)>0 then fn(dest,tr,soul)end end end for i,card in ipairs(G.hand[plr])do local b=behaviorOf(card) if card.cat=="monster"then if not G.normalSummoned and canNormalSummon(card)then eachSummonSlot(card,function(dest,tr,soul) add("SUMMON",{handIdx=i,col=dest,position="ATK",tributes=tr,soul=soul},"SUMMON "..card.name) add("SUMMON",{handIdx=i,col=dest,position="SET",tributes=tr,soul=soul},"SET "..card.name) end) local subCol,subN=tributeSubFor(card,plr) local need=tribsNeeded(getMonLvl(card)) if monCol and need>0 and subCol and subN>=need then add("SUMMON",{handIdx=i,col=monCol,position="ATK",tributes={},stTribute=subCol}, "SUBSUMMON "..card.name) end end if extraSummonAllows(card,plr)then eachSummonSlot(card,function(dest,tr,soul) add("SUMMON",{handIdx=i,col=dest,position="ATK",tributes=tr,extra=true,soul=soul}, "XSUMMON "..card.name) end) end if b and b.handIgnition and(not b.handIgnition.canActivate or b.handIgnition.canActivate(card,plr))then add("HAND_IGNITION",{handIdx=i},"HANDIGN "..card.name) end elseif card.cat=="spell"then local ok=not(b and b.responseOnly) if ok and b and b.canActivate then ok=b.canActivate(card,plr)end if ok and(stCol or card.subtype=="field")then add("CAST",{handIdx=i},"CAST "..card.name) end if stCol and card.subtype=="quickplay"then add("SET_ST",{handIdx=i,col=stCol},"SET "..card.name) end elseif card.cat=="trap"then if stCol then add("SET_ST",{handIdx=i,col=stCol},"SET "..card.name)end end end for c=1,3 do local m=mon[c] if m then if canChangePosition(m)then add("CHANGE_POS",{col=c},"CHGPOS "..c) end if not m.facedown then local b=behaviorOf(m) if b and b.ignition and(not b.ignition.canActivate or b.ignition.canActivate(m,plr))then add("IGNITION",{col=c},"IGNITION "..m.name) end end end local st=stz[c] local sb=st and behaviorOf(st) if st and not st.facedown and sb and sb.stIgnition and(not sb.stIgnition.canActivate or sb.stIgnition.canActivate(st,plr))then add("ACTIVATE",{col=c},"STIGN "..st.name) elseif canActivateSetST(st,plr)then add("ACTIVATE",{col=c},"ACTIVATE "..st.name) end end for i,c in ipairs(G.gy[plr])do if gyIgnitionReady(c,plr)then add("GY_IGNITION",{gyIdx=i},"GYIGN "..c.name)end end return moves end local function rngValues(kind) if kind=="coin"then return{0,1}end if kind=="dice"then return{1,2,3,4,5,6}end return nil end local function aiFrontier(plr,prefix,snap,enum) if#prefix==0 then return{snap},enum(plr)end local function replay(force) local inner,moves simForceRNG(force) simRun(plr,function() for _,mv in ipairs(prefix)do if not submitIntent(plr,mv.type,simReboundArgs(plr,mv.args))then return end simDrain() end inner=simSnapshot() moves=enum(plr) end,snap) local kind=simRNGKind() simForceRNG(nil) return inner,moves,kind end local inner,moves,kind=replay(nil) local vals=inner and rngValues(kind) if not vals then return{inner},moves end local outs={} for _,v in ipairs(vals)do local s=replay(v) if s then outs[#outs+1]=s end end return(#outs>0)and outs or{inner},moves end local SIM_BUDGET=0 local SEARCH_SIMS,SEARCH_FRAMES=0,0 local function aiYield() SEARCH_SIMS=SEARCH_SIMS+1 SIM_BUDGET=SIM_BUDGET-1 if SIM_BUDGET<=0 then coroutine.yield()end end local function moveLabel(mv) if mv.label then return mv.label end if mv.type=="DECLARE_ATTACK"then return"ATK "..tostring(mv.args.atkCol).."->"..tostring(mv.args.tgtCol or"direct") end return mv.type end local function traceBoard(plr) if not(AI_TRACE and AI_TRACE_BOARD)then return end local function side(p,label) local mon,st={},{} for c=1,3 do local m=G.mon[p][c] if not m then mon[c]="-" else mon[c]=tostring(getMonAtk(m)or 0).. (m.facedown and"S"or((m.pos==2)and"D"or"A"))end local s=G.st[p][c] st[c]=(not s)and"-"or(s.facedown and"X"or"O") end trace(string.format("ai %-3s lp=%d mon=[%s] st=[%s] fs=%s hand=%d", label,G.lp[p]or 0,table.concat(mon,","),table.concat(st,","), G.fs[p]and"Y"or"-",#G.hand[p])) end side(plr,"me:") side(3-plr,"opp:") end local function traceTerms() if not(AI_TRACE and AI_TRACE_BOARD)then return end trace("ai terms: "..simExplain()) end local function traceRoot(plr,nodes,baseline) if not AI_TRACE then return end local parts={} for i=1,math.min(4,#nodes)do parts[#parts+1]=string.format("%s=%.0f",moveLabel(nodes[i].moves[1]),nodes[i].score) end trace(string.format("ai root: base=%.0f n=%d hand=%d%s | %s", baseline,#nodes,#G.hand[plr],G.normalSummoned and" NS"or"", table.concat(parts," "))) end local function traceNoMoves(plr) if not AI_TRACE then return end if G.ph==PH_BATTLE then trace(string.format( "ai: PASS -- no legal battle action | no attacker left, set cards from col %s" .." (hand is not playable this phase)",tostring(firstEmpty(G.st[plr])))) return end local held={} for _,c in ipairs(G.hand[plr])do held[#held+1]=c.name end trace(string.format("ai: PASS -- nothing enumerated | ph=%d ns=%s mon=%s st=%s | hand: %s", G.ph,tostring(G.normalSummoned), tostring(firstEmpty(G.mon[plr])),tostring(firstEmpty(G.st[plr])), #held>0 and table.concat(held,", ")or"(empty)")) end local function searchRoot(plr,enum) local real=0 for _,mv in ipairs(enum(plr))do if mv.type~="ADVANCE_PHASE"then real=real+1 end end if real==0 then traceNoMoves(plr) return nil end simClearFailReport() traceBoard(plr) local snap=simSnapshot() local _,baseline=simScore(plr,function()end,snap) traceTerms() aiYield() if baseline==nil then return nil end return snap,baseline end local function beamCut(open,baseline) local beam={} local keptFlat=false for i=1,math.min(AI_BEAM,#open)do beam[i]=open[i] if open[i].score<=baseline then keptFlat=true end end if not keptFlat then for i=1,#open do if open[i].score<=baseline then beam[#beam+1]=open[i];break end end end return beam end local function searchPick(best,bestFirst,nearBest,nearFirst,baseline) if not bestFirst and G.ph==PH_MAIN and nearFirst and nearBest>=baseline-AI_PASS_TOLERANCE then best,bestFirst=nearBest,nearFirst if AI_TRACE then trace(string.format("ai: within tolerance -- %s at %.0f vs base %.0f", moveLabel(bestFirst),nearBest,baseline)) end end if not bestFirst then if AI_TRACE then trace(string.format("ai: PASS -- nothing beat baseline %.0f",baseline)) end return nil end if AI_TRACE then trace(string.format("ai: play %s (%.0f vs base %.0f)",moveLabel(bestFirst),best,baseline)) end return{move=bestFirst} end local function aiSearchRun(plr) local enum=aiEnumerateTurn local snap,baseline=searchRoot(plr,enum) if not snap then return nil end local best,bestFirst=baseline,nil local nearBest,nearFirst=nil,nil local beam={{moves={}}} for depth=1,AI_DEPTH do local nodes={} for _,prefix in ipairs(beam)do local froms,moves=aiFrontier(plr,prefix.moves,snap,enum) aiYield() for _,mv in ipairs(moves or{})do local body=function() return submitIntent(plr,mv.type,simReboundArgs(plr,mv.args)) end local ok,sc,kind if depth==1 then ok,sc=simScoreChoices(plr,body,froms[1]) kind=simRNGKind() aiYield() else local total,n=0,0 for _,f in ipairs(froms)do local o,s=simScore(plr,body,f) kind=kind or simRNGKind() if o and s then total=total+s;n=n+1 end aiYield() end ok=(n>0) if ok then sc=total/n end end if ok and sc then local seq={} for i,m in ipairs(prefix.moves)do seq[i]=m end seq[#seq+1]=mv local branches=#(rngValues(kind)or{1}) nodes[#nodes+1]={moves=seq,score=sc,branches=branches,ord=#nodes+1} end end end if#nodes==0 then break end table.sort(nodes,function(a,b) if a.score~=b.score then return a.score>b.score end return a.ordbest then best=nodes[1].score bestFirst=nodes[1].moves[1] end for _,n in ipairs(nodes)do if n.moves[1].type=="SUMMON"then if nearBest==nil or n.score>nearBest then nearBest,nearFirst=n.score,n.moves[1] end break end end local open={} for _,n in ipairs(nodes)do if n.branches<=AI_RNG_BRANCH_MAX then open[#open+1]=n end end if depth==1 then beam=open else beam=beamCut(open,baseline) end end return searchPick(best,bestFirst,nearBest,nearFirst,baseline) end local function aiReplay(plr,moves,forced,snap,enum) local inner,list,key simForceRNG(forced) simRun(plr,function() for _,mv in ipairs(moves)do if not submitIntent(plr,mv.type,simReboundArgs(plr,mv.args))then return end simDrain() end inner=simSnapshot() list=enum(plr) key=simStateKey() end,snap) simForceRNG(nil) return inner,list,key end local function aiSearchTree(plr) local enum=aiEnumerateTurn local snap,baseline=searchRoot(plr,enum) if not snap then return nil end local SEEN={} local root={moves={},forced={},score=baseline} root.branch={stay=baseline,children={}} local frontier={root} for depth=1,AI_DEPTH do local nodes={} for _,node in ipairs(frontier)do local outs=node.chance and simChanceValues(node.chance)or{false} if node.chance then node.branches={}end for oi,v in ipairs(outs)do local forced={} for i,x in ipairs(node.forced)do forced[i]=x end if v~=false then forced[#forced+1]=v end local inner,moves,key,branch if node==root then inner,moves,branch=snap,enum(plr),root.branch else inner,moves,key=aiReplay(plr,node.moves,forced,snap,enum) aiYield() branch=key and SEEN[key] if branch then moves=nil elseif inner then local stay=node.score if node.chance then local _,s=simScore(plr,function()end,inner) stay=s or node.score aiYield() end branch={stay=stay,children={}} SEEN[key]=branch end end if node.chance then node.branches[oi]=branch else node.branch=branch end for _,mv in ipairs(moves or{})do local body=function() return submitIntent(plr,mv.type,simReboundArgs(plr,mv.args)) end local ok,sc if depth==1 then ok,sc=simScoreChoices(plr,body,inner) else ok,sc=simScore(plr,body,inner)end local kind=simRNGFreeKind() aiYield() if ok and sc then local seq={} for i,m in ipairs(node.moves)do seq[i]=m end seq[#seq+1]=mv local child={moves=seq,forced=forced,score=sc,chance=kind,ord=#nodes+1} branch.children[#branch.children+1]=child nodes[#nodes+1]=child end end end end if#nodes==0 then break end table.sort(nodes,function(a,b) if a.score~=b.score then return a.score>b.score end return a.ord=target then nxt=ch;break end end if not nxt then break end line[#line+1]=nxt.moves[#nxt.moves] n=nxt end return line end local best,bestFirst,nearBest,nearFirst=baseline,nil,nil,nil local bestChance,bestNode=false,nil for _,c in ipairs(root.branch.children)do local v=value(c) local tied=bestFirst and math.abs(v-best)<=1e-9*math.max(1,math.abs(best)) if(v>best and not tied)or(tied and c.chance and not bestChance)then best,bestFirst,bestChance,bestNode=v,c.moves[1],c.chance~=nil,c end if c.moves[1].type=="SUMMON"and(nearBest==nil or v>nearBest)then nearBest,nearFirst=v,c.moves[1] end end local pick=searchPick(best,bestFirst,nearBest,nearFirst,baseline) if pick and bestNode and pick.move==bestNode.moves[1]then pick.line=bestLine(bestNode) pick.line.plr=plr end return pick end local SEARCH_CO,SEARCH_DONE,SEARCH_RESULT=nil,false,nil function aiSearchAbort() SEARCH_CO,SEARCH_DONE,SEARCH_RESULT=nil,false,nil end local SEARCH_PLAYOUT=true function aiSearchStep(plr) if SEARCH_DONE then return true end if not SEARCH_CO then SEARCH_CO=coroutine.create(function() if AI_HIDDEN_DECK then return aiSearchTree(plr)end return aiSearchRun(plr) end) SEARCH_SIMS,SEARCH_FRAMES=0,0 SEARCH_PLAYOUT=(G.ph~=PH_BATTLE) end SEARCH_FRAMES=SEARCH_FRAMES+1 SIM_BUDGET=AI_SIMS_PER_FRAME simSetOwnPlayout(SEARCH_PLAYOUT) local ok,res=coroutine.resume(SEARCH_CO) simSetOwnPlayout(true) if not ok then trace("AI SEARCH ERROR: "..tostring(res)) SEARCH_DONE,SEARCH_RESULT=true,nil elseif coroutine.status(SEARCH_CO)=="dead"then SEARCH_DONE,SEARCH_RESULT=true,res if AI_TRACE then trace(string.format("ai: %d sims over %d frames (delay %d)", SEARCH_SIMS,SEARCH_FRAMES,AI_DELAY)) end end return SEARCH_DONE end function aiSearchResult() local r=SEARCH_RESULT aiSearchAbort() simSetPlannedLine(r and r.line) return r end local function losingAttack(att,tgt,plr) if tgt.pos~=1 then return false end if(getMonAtk(tgt)or 0)<=battleAtk(att,tgt,plr)then return false end return not battleIndestructible(att,plr) end function aiEnumerateBattle(plr) local moves={} local mon,oppMon=G.mon[plr],G.mon[3-plr] local oppHasMons=hasMonsters(3-plr) local order={} for ac=1,3 do local att=mon[ac] if canDeclareAttack(att,plr)then order[#order+1]={col=ac,atk=getMonAtk(att)or 0} end end table.sort(order,function(a,b) if a.atk~=b.atk then return a.atk>b.atk end return a.col0)and(" ("..skipped.." losing suppressed)")or"", (#parts>0 and table.concat(parts," ")or"no monsters on field"))) end return moves end function aiMarkAttacksDone(plr) for ac=1,3 do local att=G.mon[plr][ac] if canDeclareAttack(att,plr)then att.attacked=true end end end function aiEnumerateTurn(plr) if G.ph==PH_BATTLE then return aiEnumerateBattle(plr)end if G.ph~=PH_MAIN then return{}end local moves=aiEnumerateMain(plr) if AI_TURN_SEARCH then moves[#moves+1]={type="ADVANCE_PHASE",args={},label="TO BATTLE"} end return moves end local function aiBlocked() return G.active~=2 or G.winner or#ANIM>0 or G.menu.open or G.infoCard or G.mode=="sel_deck"or G.mode=="sel_target" end function aiTick() if aiBlocked()then return end if procBusy()then aiSearchAbort();return end G.aiTimer=G.aiTimer-1 if G.ph==PH_MAIN or G.ph==PH_BATTLE then if not aiSearchStep(2)then return end if G.aiTimer>0 then return end G.aiTimer=AI_DELAY local pick=aiSearchResult() if not pick then if G.ph==PH_BATTLE then aiMarkAttacksDone(2)end submitIntent(2,"ADVANCE_PHASE",{}) return end local args=simReboundArgs(2,pick.move.args) if pick.move.type=="DECLARE_ATTACK"then G.battleAnim={atkCol=args.atkCol,tgtCol=args.tgtCol} addAnim(30,function()end,function() if not submitIntent(2,"DECLARE_ATTACK",args)then G.battleAnim=nil end end) return end submitIntent(2,pick.move.type,args) return end aiSearchAbort() if G.aiTimer>0 then return end G.aiTimer=AI_DELAY submitIntent(2,"ADVANCE_PHASE",{}) end end do SIM_MAX_TICKS=400 SIM_COIN=1 SIM_DICE=3 SIM_UNKNOWN_ATK=1000 SIM_UNKNOWN_DEF=1000 SIM_HIDDEN_TARGETING=true SIM_SWING_FIRST=true SIM_PRICE_ENDPHASE_LOSS=true SIM_OUTCLASS_BEST_STAT=true SIM_SWING_PLAN=true SIM_TRADE_LOSS=true SIM_REPLAY_CHAIN=true SIM_OWN_CHOICES=true SIM_HAND_QUEUE=true AI_HIDDEN_DECK=true SIM_ENGINE_INCOMING=true SIM_SUMMON_CHANCE=true SIM_DEAD_IS_DEAD=true SIM_IDLE_TRIBUTE=true AI_DRAW_SAMPLES=4 SIM_EXPECTED_SUMMON=1000 SIM_W={ stat=0.25, presence=400, backrow=500, handCard=450, handQueue=0.75, idleTribute=450, handMonsterShare=0.5, fieldSpell=450, swing=0.8, incoming=1.0, incomingBody=0.7, lethal=4000, danger=0.5, win=1000000, winStepOdds=0.9, unusedKill=700, outclassed=900, } local function deepCopy(v,memo) if type(v)~="table"then return v end local hit=memo[v] if hit then return hit end local out={} memo[v]=out for k,val in pairs(v)do out[deepCopy(k,memo)]=deepCopy(val,memo)end return out end local function clearState() for k in pairs(G)do if k~="proc"then G[k]=nil end end end local function holdState() local live={} for k,v in pairs(G)do if k~="proc"then live[k]=v end end return live end local function putBackState(live) clearState() for k,v in pairs(live)do G[k]=v end end function simHandHidden(plr) return G.sim==true and plr~=G.simActor end function simSnapshot() local memo={} local snap={} for k,v in pairs(G)do if k~="proc"then snap[k]=deepCopy(v,memo)end end return snap end function simRestore(snap) local memo={} clearState() for k,v in pairs(snap)do G[k]=deepCopy(v,memo)end procInit() end local ORIGIN=nil local DECISIONS={} local REVEALED={} local PLAN=nil local PLANNED_LINE=nil local RNG_FORCE=nil local RNG_KIND=nil local RNG_FORCE_I=0 local RNG_FREE_KIND=nil local function nextForced(kind) RNG_FORCE_I=RNG_FORCE_I+1 local v=RNG_FORCE if type(v)=="table"then v=v[RNG_FORCE_I]end if v==nil and not RNG_FREE_KIND then RNG_FREE_KIND=kind end return v end local KEY_SKIP={proc=true,sim=true,simActor=true,mode=true,cur=true,menu=true, pending=true,infoCard=true,infoScroll=true,nameScroll=true,popup=true,deckSel=true, targetSel=true,gyView=true,edView=true,battleAnim=true,aiTimer=true,autoTimer=true, tick=true,dispLp=true,winTick=true,winSfxDone=true,theme=true,reward=true, puzzleBannerT=true,oppName=true} function simStateKey() local out,seen,n={},{},0 local function put(v) local t=type(v) if t=="table"then if seen[v]then out[#out+1]="@"..seen[v];return end n=n+1;seen[v]=n local base=v.id and CARDS[v.id] local keys={} for k,x in pairs(v)do if type(x)~="function"and not(base and base[k]==x)then keys[#keys+1]=k end end table.sort(keys,function(a,b)return tostring(a)1 then CHOICE_LOG[#CHOICE_LOG+1]={at=at,alts=alts}end end local ok,ans=pcall(aiAnswer,req) if not ok or ans==nil then return false end return ans end function simDrain() local n=0 while not procIdle()and n0)or a.extra then return false end for _,c in ipairs(G.hand[plr])do if c.cat=="monster"and canNormalSummon(c)and tribsNeeded(getMonLvl(c))==0 then return false end end return true end function simNoteAction(plr,t,a) if not(G.sim and plr==G.simActor)then return end if t=="SUMMON"and SIM_IDLE_TRIBUTE and idleTribute(plr,a)then G.simIdleTribute=true end if t=="ADVANCE_PHASE"or t=="RESPONSE"or t=="SET_ST"then return end if t=="SUMMON"and a and a.position=="SET"then return end G.simActions=(G.simActions or 0)+1 end local SIM_RNG_STATE=1 local function simRandom(m,n) SIM_RNG_STATE=(SIM_RNG_STATE*1103515245+12345)%2147483648 local r=SIM_RNG_STATE/2147483648 if m==nil then return r end if n==nil then m,n=1,m end return m+math.floor(r*(n-m+1)) end function simRun(plr,body,snap) snap=snap or simSnapshot() local liveProc=G.proc local live=holdState() local liveRandom=math.random math.random=simRandom SIM_RNG_STATE=1 simRestore(snap) G.sim=true G.simActor=plr RNG_KIND=nil RNG_FORCE_I,RNG_FREE_KIND=0,nil CHOICE_N=0 local hidden=3-plr for c=1,3 do local m=G.mon[hidden][c] if m and m.facedown then m.simHidden=true m.atk=SIM_UNKNOWN_ATK m.def=SIM_UNKNOWN_DEF end end local drained,score=false,nil local rejected=false local moveKind=nil local ok,err=pcall(function() if body()==false then rejected=true;return end drained=simDrain() moveKind=RNG_FREE_KIND if drained and OWN_PLAYOUT and AI_TURN_SEARCH and G.active==plr and not G.winner and(G.ph==PH_MAIN or G.ph==PH_BATTLE)then simPlayMyTurn(plr) drained=simDrain() end if drained then score=simEvaluate(plr)end end) RNG_FREE_KIND=moveKind putBackState(live) G.proc=liveProc math.random=liveRandom local why=nil if not ok then why=tostring(err) elseif rejected then why="move rejected" elseif not drained then why="drain cap hit"end if not why then return true,score,nil end if AI_TRACE and why~=LAST_FAIL then LAST_FAIL=why trace("sim FAILED: "..why) end return false,nil,why end function simRNGKind()return RNG_KIND end function simRNGFreeKind()return RNG_FREE_KIND end function simForceRNG(v)RNG_FORCE=v end function simClearFailReport()LAST_FAIL=nil end function simScore(plr,body,snap) local ok,score=simRun(plr,body,snap) if AI_HIDDEN_DECK then local kind=RNG_FREE_KIND local vals=ok and kind and simChanceValues(kind) if not vals then return ok,score end local total,n=0,0 for _,v in ipairs(vals)do RNG_FORCE={v} local ok2,s2=simRun(plr,body,snap) RNG_FORCE=nil if ok2 and s2 then total=total+s2;n=n+1 end end RNG_FREE_KIND=kind if n==0 then return ok,score end return true,total/n end local kind=RNG_KIND if not ok or kind==nil then return ok,score end local vals=(kind=="coin")and{0,1}or{1,2,3,4,5,6} local total,n=0,0 for _,v in ipairs(vals)do RNG_FORCE=v local ok2,s2=simRun(plr,body,snap) RNG_FORCE=nil if ok2 and s2 then total=total+s2;n=n+1 end end RNG_KIND=kind if n==0 then return ok,score end return true,total/n end function simScoreChoices(plr,body,snap) CHOICE_LOG={} local ok,best=simScore(plr,body,snap) local log=CHOICE_LOG CHOICE_LOG=nil if not ok or#log==0 then return ok,best end local plan=nil for i,pt in ipairs(log)do if i>AI_CHOICE_POINTS then break end for _,alt in ipairs(pt.alts)do local try={} if plan then for k,v in pairs(plan)do try[k]=v end end try[pt.at]=alt CHOICE_PLAN=try local ok2,s2=simScore(plr,body,snap) CHOICE_PLAN=nil if ok2 and s2 and(best==nil or s2>best)then best=s2;plan=try end end end return ok,best end function simRecordOrigin(plr,t,a) if G.sim then return end if t=="RESPONSE"then return end DECISIONS={} REVEALED={} LIVE_CHOICES={} local line=PLANNED_LINE PLANNED_LINE=nil if not procIdle()or#ANIM>0 then ORIGIN=nil;return end local cont=nil if line and line.plr==plr and line[1].type==t then cont={table.unpack(line,2)}end ORIGIN={plr=plr,type=t,args=a,snap=simSnapshot(),cont=cont} end function simSetPlannedLine(line)PLANNED_LINE=line end local function noteAnswer(list,ans) if ans==nil then ans=false end list[#list+1]=ans end function simNoteDecision(ans,req) if G.sim then return end noteAnswer(DECISIONS,ans) local opt=ans and req and req.options and req.options[ans.optionIdx] if opt and opt.self.zone=="st"then REVEALED[#REVEALED+1]={plr=opt.self.plr,col=opt.self.col} end end function simNoteLiveChoice(ans) if not G.sim then noteAnswer(LIVE_CHOICES,ans)end end local function simScoreTail(plr,basePlan,at,body,snap) CHOICE_LOG={} CHOICE_PLAN=basePlan local ok,best=simRun(plr,body,snap) local log=CHOICE_LOG CHOICE_LOG=nil;CHOICE_PLAN=nil if not ok or not best then return ok,best end local plan,seen=basePlan,0 for _,pt in ipairs(log)do if pt.at>at then seen=seen+1 if seen>AI_TAIL_POINTS then break end for _,alt in ipairs(pt.alts)do local try={} for k,v in pairs(plan)do try[k]=v end try[pt.at]=alt CHOICE_PLAN=try local ok2,s2=simRun(plr,body,snap) CHOICE_PLAN=nil if ok2 and s2 and s2>best then best=s2;plan=try end end end end return ok,best end function simScoreLiveChoice(req,plr) if G.sim or not ORIGIN then return nil end local alts=choiceAlts(req) if not alts or#alts<2 then return nil end local dflt=aiAnswer(req) if dflt~=nil then for i,alt in ipairs(alts)do if sameAlt(alt,dflt)then table.remove(alts,i);table.insert(alts,1,alt);break end end end local at=#LIVE_CHOICES+1 local best,bestAns=nil,nil for _,alt in ipairs(alts)do local plan={} for i,v in ipairs(LIVE_CHOICES)do plan[i]=v end plan[at]=alt local o=ORIGIN local ok,score=simScoreTail(plr,plan,at,function() if SIM_REPLAY_CHAIN then PLAN={i=0} for j,d in ipairs(DECISIONS)do PLAN[j]=d end end submitIntent(o.plr,o.type,simReboundArgs(o.plr,o.args)) for _,mv in ipairs(o.cont or{})do simDrain() if not submitIntent(o.plr,mv.type,simReboundArgs(o.plr,mv.args))then break end end end,o.snap) PLAN=nil if ok and score and(best==nil or score>best)then best,bestAns=score,alt end end return bestAns end function simIsRevealed(plr,col) for _,r in ipairs(REVEALED)do if r.plr==plr and r.col==col then return true end end return false end function simReboundArgs(plr,a) local r={} for k,v in pairs(a)do r[k]=v end if r.handIdx and G.hand[plr]then r.card=G.hand[plr][r.handIdx]end return r end local function simPlayOutBattle(plr) local opp=3-plr for _=1,6 do if G.ph~=PH_BATTLE or G.active~=opp or G.winner then return end local bestCol,bestAtk for c=1,3 do local m=G.mon[opp][c] if canDeclareAttack(m,opp)then local a=getMonAtk(m)or 0 if bestAtk==nil or a>bestAtk then bestCol,bestAtk=c,a end end end if not bestCol then return end local tgtCol for c=1,3 do if G.mon[plr][c]and not struckThisTurn(G.mon[opp][bestCol],c)then tgtCol=c;break end end if not submitIntent(opp,"DECLARE_ATTACK",{atkCol=bestCol,tgtCol=tgtCol})then return end simDrain() end end local RESP_SCORES=nil function simResponseScores()return RESP_SCORES end function simScoreResponse(req) if G.sim or not ORIGIN then return nil end local plr=req.plr local cands={false} for i=1,#(req.options or{})do cands[#cands+1]={optionIdx=i}end local best,bestAns=nil,nil RESP_SCORES={} for ci,cand in ipairs(cands)do local plan={i=0} for j,d in ipairs(DECISIONS)do plan[j]=d end plan[#DECISIONS+1]=cand if cand==false then plan[#DECISIONS+2]={optionIdx=1}end PLAN=plan if SIM_REPLAY_CHAIN then CHOICE_PLAN={} for i,v in ipairs(LIVE_CHOICES)do CHOICE_PLAN[i]=v end end local o=ORIGIN local ok,score=simRun(plr,function() submitIntent(o.plr,o.type,simReboundArgs(o.plr,o.args)) simDrain() simPlayOutBattle(plr) end,o.snap) PLAN=nil CHOICE_PLAN=nil RESP_SCORES[ci]=ok and score or nil if ok and(best==nil or score>best)then best,bestAns=score,cand end end return bestAns end local function bodyStat(m) if m.simHidden then return SIM_UNKNOWN_DEF end if m.facedown or m.pos==2 then return(getMonDef(m)or 0)-turnMod(m,"def")end return(getMonAtk(m)or 0)-turnMod(m,"atk") end local function blockValue(m) if m.facedown or m.pos==2 then return getMonDef(m)or 0 end return getMonAtk(m)or 0 end local function simOutclassed(plr,gone) local best=0 for c=1,3 do local m=G.mon[3-plr][c] if m and not(gone and gone[m])then local v=blockValue(m) if SIM_OUTCLASS_BEST_STAT and not m.facedown then v=math.max(getMonAtk(m)or 0,getMonDef(m)or 0) end best=math.max(best,v) end end if best==0 then return false end for c=1,3 do local m=G.mon[plr][c] if m and not m.facedown and not(gone and gone[m]) and(getMonAtk(m)or 0)>best then return false end end return true end local function bodyValue(m)return SIM_W.presence+bodyStat(m)*SIM_W.stat end local function diesThisTurn(m) return SIM_PRICE_ENDPHASE_LOSS and m and m.destroyAtEnd==G.turn end local function handValue(c) local b=behaviorOf(c) return(b and b.handValue)or SIM_W.handCard end local function handTotal(p) local s,queued=0,0 for _,c in ipairs(G.hand[p])do if SIM_HAND_QUEUE and simHandHidden(p)then s=s+SIM_W.handCard else local b=behaviorOf(c) if SIM_HAND_QUEUE and c.cat=="monster"and canNormalSummon(c)and not(b and b.handValue)then s=s+SIM_W.handCard*SIM_W.handQueue^queued queued=queued+1 else s=s+handValue(c) end end end return s end local function simSideValue(p) local s=G.lp[p]or 0 for c=1,3 do local m=G.mon[p][c] if m and not m.borrowedFrom and not diesThisTurn(m)then s=s+bodyValue(m)end local lent=G.mon[3-p][c] if lent and lent.borrowedFrom==p then s=s+bodyValue(lent)end if G.st[p][c]then s=s+SIM_W.backrow end end if G.fs[p]then s=s+SIM_W.fieldSpell end return s+handTotal(p) end local function simSwingDamage(victim,attacker,anticipate,doomed,judge) local thisTurn=not anticipate if thisTurn and attacker==G.active and battleBanned(attacker)then return 0,0 end local nMon=0 for c=1,3 do if G.mon[victim][c]then nMon=nMon+1 end end local direct=0 if thisTurn then for c=1,3 do local m=G.mon[attacker][c] if canDefDirect(m,attacker)then direct=direct+((getMonAtk(m)or 0)//2)end end end local atkers={} for c=1,3 do local m=G.mon[attacker][c] local spent=thisTurn and attacker==G.active and m and m.attacked local canSwing=m and not m.facedown and(m.pos==1 or anticipate) if canSwing and not spent and not(doomed and doomed[m]) and not attackBlocked(m,attacker)then local b=behaviorOf(m) local swings=1 if b then if b.attacksAll then swings=math.max(1,nMon) elseif b.doubleAttack and m.dblTurn~=G.turn then swings=2 end end for _=1,swings do atkers[#atkers+1]={atk=getMonAtk(m)or 0,card=m}end end end if anticipate and#G.hand[attacker]>0 and firstEmpty(G.mon[attacker]) and not staticActive("blocksAttack",attacker)then atkers[#atkers+1]={atk=SIM_EXPECTED_SUMMON} end local killed={} if#atkers==0 then return direct,0,killed end table.sort(atkers,function(a,b)return a.atk>b.atk end) local blockers={} for c=1,3 do local m=G.mon[victim][c] if m and not(anticipate and diesThisTurn(m))and not(doomed and doomed[m])then local v=blockValue(m) blockers[#blockers+1]={def=(m.facedown or m.pos==2),val=v,body=bodyValue(m), card=m,hidden=(SIM_HIDDEN_TARGETING and m.facedown)or false, pick=(SIM_HIDDEN_TARGETING and m.facedown)and SIM_UNKNOWN_DEF or v} end end local function clash(a,val,isDef) if isDef then if a>val then return 0,true end return-(val-a),false end if a>val then return a-val,true end if a0 end local function eff(a,b,real) if a.card and b.card and(real or not b.hidden)then return battleAtk(a.card,b.card,attacker) end return a.atk end local trades=SIM_TRADE_LOSS and not anticipate local function traded(a,b,gain,kill) return trades and kill and not b.def and gain==0 and a.card~=nil and not battleIndestructible(a.card,attacker,b.card) end if judge then local gone,killedNow={},{} local bestS,bestD,bestA,bestK local function plan(i,dmg,attr) if i>#atkers then local s=judge(dmg+direct,attr,killedNow) if bestS==nil or s>bestS then bestS,bestD,bestA,bestK=s,dmg+direct,attr,{} for k in pairs(killedNow)do bestK[k]=true end end return end local a=atkers[i] if a.card and killedNow[a.card]then return plan(i+1,dmg,attr)end local open,swung,tied=false,false,false for j,b in ipairs(blockers)do if not gone[j]then open=true local pGain,pKill=clash(eff(a,b,false),b.pick,b.def) if pGain>=0 or pKill then swung=true local gain,kill=clash(eff(a,b,true),b.val,b.def) local lost=traded(a,b,gain,kill) if kill then gone[j]=true;killedNow[b.card]=true end if lost then tied=true;killedNow[a.card]=true end plan(i+1,dmg+math.max(gain,0), attr+(kill and b.body or 0)-(lost and bodyValue(a.card)or 0)) if kill then gone[j]=nil;killedNow[b.card]=nil end if lost then killedNow[a.card]=nil end end end end if not open then plan(i+1,dmg+a.atk,attr) elseif tied or not swung then plan(i+1,dmg,attr)end end plan(1,0,0) return bestD,bestA,bestK end local dmg,attr=0,0 for _,a in ipairs(atkers)do if a.card and killed[a.card]then elseif#blockers==0 then dmg=dmg+a.atk else local bestRank,bestI for i,b in ipairs(blockers)do local gain,kill=clash(eff(a,b,false),b.pick,b.def) local rank=gain+(kill and 1 or 0) if bestRank==nil or rank>bestRank then bestRank,bestI=rank,i end end local b=blockers[bestI] local pGain,pKill=clash(eff(a,b,false),b.pick,b.def) if pGain>=0 or pKill then local gain,kill=clash(eff(a,b,true),b.val,b.def) dmg=dmg+math.max(gain,0) if kill then attr=attr+b.body killed[b.card]=true table.remove(blockers,bestI) end if traded(a,b,gain,kill)then attr=attr-bodyValue(a.card) killed[a.card]=true end end end end return dmg+direct,attr,killed end local function simUnusedKill(plr) if G.ph~=PH_BATTLE or G.active~=plr then return false end local opp=3-plr for c=1,3 do local m=G.mon[plr][c] if canDeclareAttack(m,plr)then for t=1,3 do local tg=G.mon[opp][t] if tg and battleAtk(m,tg,plr)>blockValue(tg)then return true end end end end return false end local EXPLAIN={} function simExplain() local e=EXPLAIN return string.format( "mat=%+.0f swing=%+.0f in=%+.0f lethal=%+.0f kill=%+.0f oc=%+.0f trib=%+.0f => %+.0f", e.mat or 0,e.swing or 0,e.inc or 0,e.lethal or 0,e.kill or 0,e.oc or 0, e.trib or 0,e.total or 0) end local function lethalPressure(dmg,lp) if(lp or 0)<=0 or dmg>=lp then return SIM_W.lethal end local r=dmg/lp if r<=SIM_W.danger then return 0 end return SIM_W.lethal*(r-SIM_W.danger)/(1-SIM_W.danger) end local function attackGain(att,tgt,plr) local opp=3-plr if not tgt then local d=battleDamageImmune(nil,opp)and 0 or(getMonAtk(att)or 0) return d,d end if tgt.facedown and not tgt.simHidden and plr~=G.simActor then tgt={name="?",type="",attr="",pos=2,facedown=true,simHidden=true, atk=SIM_UNKNOWN_ATK,def=SIM_UNKNOWN_DEF} end local o=battleOutcome(att,tgt,plr) local v=o.toDefender-o.toAttacker if o.killTarget then v=v+bodyValue(tgt)end if o.killAttacker then v=v-bodyValue(att)end return v,o.toDefender end function simPlayBattle(plr) local opp=3-plr for _=1,8 do if G.winner or G.ph~=PH_BATTLE or G.active~=plr then return end local best,bestWin,bestArgs=nil,false,nil local function consider(v,dmg,args) local win=dmg>0 and dmg>=(G.lp[opp]or 0) if(win and not bestWin)or(win==bestWin and(best==nil or v>best))then best,bestWin,bestArgs=v,win,args end end for ac=1,3 do local att=G.mon[plr][ac] if canDeclareAttack(att,plr)then if hasMonsters(opp)then for tc=1,3 do local tgt=G.mon[opp][tc] if tgt and not struckThisTurn(att,tc)then local v,dmg=attackGain(att,tgt,plr) consider(v,dmg,{atkCol=ac,tgtCol=tc}) end end else local v,dmg=attackGain(att,nil,plr) consider(v,dmg,{atkCol=ac}) end elseif canDefDirect(att,plr)then local d=battleDamageImmune(nil,opp)and 0 or((getMonAtk(att)or 0)//2) consider(d,d,{atkCol=ac,defDirect=true}) end end if not bestArgs or(not bestWin and best<=0)then return end if not submitIntent(plr,"DECLARE_ATTACK",bestArgs)then return end simDrain() end end function simPlayMyTurn(plr) if G.ph==PH_MAIN then if not submitIntent(plr,"ADVANCE_PHASE",{})then return end simDrain() end if G.ph==PH_BATTLE and G.active==plr and not G.winner then simPlayBattle(plr) if G.winner or G.ph~=PH_BATTLE then return end if not submitIntent(plr,"ADVANCE_PHASE",{})then return end simDrain() end end local function onField(p,m) for c=1,3 do if G.mon[p][c]==m then return true end end return false end local function simOpponentTurn(plr,summon) local opp=3-plr local lp0=G.lp[plr]or 0 local side0=simSideValue(plr) local mine={} for c=1,3 do local m=G.mon[plr][c] if m then mine[m]=bodyValue(m)end end local function advance(p) if G.winner or not submitIntent(p,"ADVANCE_PHASE",{})then return false end return simDrain() end if G.active==plr and G.ph==PH_BATTLE then advance(plr)end if G.active==plr and G.ph==PH_END then advance(plr)end for _=1,4 do if G.winner or G.active~=opp or G.ph==PH_MAIN then break end if not advance(opp)then break end end local theirLoss=nil if not G.winner and G.active==opp and G.ph==PH_MAIN then if summon and#G.hand[opp]>0 and not staticActive("blocksAttack",opp)then for c=1,3 do if not G.mon[opp][c]and not zoneSealed(opp,c)then G.mon[opp][c]=makeToken("?","","",4,SIM_EXPECTED_SUMMON,SIM_EXPECTED_SUMMON) break end end end for c=1,3 do local m=G.mon[opp][c] if m and not m.facedown and m.pos==2 and not m.posChanged and not monsterBound(m)then submitIntent(opp,"CHANGE_POS",{col=c}) simDrain() end end local their0=simSideValue(opp) if advance(opp)and G.ph==PH_BATTLE then simPlayBattle(opp)end theirLoss=their0-simSideValue(opp) end local dmg=math.max(0,lp0-(G.lp[plr]or 0)) local lost=0 for m,v in pairs(mine)do if not onField(plr,m)then lost=lost+v end end return dmg,lost,simSideValue(plr)-side0,theirLoss or 0,lp0 end function simEvaluate(plr) local opp=3-plr if G.winner==plr then if not AI_TURN_SEARCH then return SIM_W.win end local p=SIM_W.winStepOdds^(G.simActions or 0) return SIM_W.win*p+(1-p)*(simSideValue(plr)-simSideValue(opp)) end if G.winner==opp then return-SIM_W.win end local mat=simSideValue(plr)-simSideValue(opp) local swing,lethalOut,doomed=0,0,nil local first=SIM_SWING_FIRST and G.active==plr and G.ph=PH_BATTLE then oc=simOutclassed(plr)and-SIM_W.outclassed or 0 local function played(summon) local dmg,lost,change,theirLoss,lp0=simOpponentTurn(plr,summon) if SIM_DEAD_IS_DEAD and G.winner==3-plr then return-lp0,-SIM_W.lethal end return change+dmg*(1-SIM_W.incoming)+lost*(1-SIM_W.incomingBody)+theirLoss*SIM_W.swing, -lethalPressure(dmg,lp0) end if SIM_SUMMON_CHANCE then local p=1-(1-SIM_W.handMonsterShare)^(#G.hand[opp]+1) local snap=simSnapshot() local i1,l1=played(true) simRestore(snap) local i2,l2=played(false) inc,lethalIn=p*i1+(1-p)*i2,p*l1+(1-p)*l2 else inc,lethalIn=played(true) end else inc,lethalIn,oc=after(doomed) end local trib=G.simIdleTribute and SIM_W.idleTribute or 0 local s=mat+swing+lethalOut+inc+lethalIn+kill+oc+trib EXPLAIN.mat,EXPLAIN.swing,EXPLAIN.inc=mat,swing,inc EXPLAIN.lethal,EXPLAIN.kill,EXPLAIN.oc=lethalOut+lethalIn,kill,oc EXPLAIN.trib=trib EXPLAIN.total=s return s end local function benchFirstSummon(plr) for i,card in ipairs(G.hand[plr])do if card.cat=="monster"and tribsNeeded(getMonLvl(card))==0 then return function() local c=G.hand[plr][i] local col=firstEmpty(G.mon[plr]) if not c or not col then return false end return submitIntent(plr,"SUMMON",{card=c,col=col,position="ATK",handIdx=i}) end,"SUMMON "..tostring(card.name) end end return nil,"no summonable monster in hand" end local function benchFirstCast(plr) for i,card in ipairs(G.hand[plr])do if card.cat=="spell"and card.subtype~="equip"and card.subtype~="field"then local b=behaviorOf(card) if not(b and b.canActivate)or b.canActivate(card,plr)then return function() local c=G.hand[plr][i] if not c then return false end return submitIntent(plr,"CAST",{card=c,handIdx=i}) end,"CAST "..tostring(card.name) end end end return nil,"no castable spell in hand" end local function benchOne(plr,make,iters) local body,label=make(plr) if not body then return label end local committed=0 local wrapped=function() G.active=plr G.ph=PH_MAIN if body()then committed=committed+1 end end local snap=simSnapshot() simRun(plr,wrapped,snap) committed=0 local t0=time() local errs=0 for _=1,iters do local ok=simRun(plr,wrapped,snap) if not ok then errs=errs+1 end end local dt=time()-t0 return string.format("%s: %.2f ms/sim over %d (%.1f/frame) committed=%d errored=%d", label,dt/iters,iters,(dt>0)and(16.6/(dt/iters))or 999,committed,errs) end function simBench(iters) iters=iters or 200 local plr=2 if not procIdle()then trace("simBench: proc busy");return end if#ANIM>0 then trace("simBench: anim in flight");return end local function countMon(p) local n=0 for c=1,3 do if G.mon[p][c]then n=n+1 end end return n end trace(string.format("simBench: turn=%d gy=%d/%d hand=%d/%d mon=%d/%d deck=%d/%d", G.turn,#G.gy[1],#G.gy[2],#G.hand[1],#G.hand[2], countMon(1),countMon(2),#G.deck[1],#G.deck[2])) local snap=simSnapshot() local live=holdState() simRestore(snap) local t0=time() for _=1,iters do simRestore(snap)end local cloneMs=(time()-t0)/iters putBackState(live) trace(string.format("simBench: snapshot restore %.3f ms",cloneMs)) trace("simBench: "..tostring(benchOne(plr,benchFirstSummon,iters))) trace("simBench: "..tostring(benchOne(plr,benchFirstCast,iters))) end end do local function rowStart(n,w,gap) return(SCREEN_W-(n*w+(n-1)*gap))//2 end function drawTitle() cls(C_BG) sync(3,0,false) spr(1,0,-2,-1,1,0,0,15,16) spr(256,120,-2,-1,1,0,0,15,16) if(G.tick//30)%2==0 then printCentre("PRESS A TO CONTINUE",0,SCREEN_W,SCREEN_H-8,C_TEXT,false) end end CURTAIN_D=60 CURTAIN_TOP=160 CURTAIN_EDGE=BROWN CURTAIN_FILL=ORANGE function startCurtain(scene) CURTAIN={t=0,scene=scene} SCENE="curtain" end function drawCurtain() CURTAIN.t=CURTAIN.t+1 local d,e=CURTAIN_D,CURTAIN.t if e>2*d then SCENE=CURTAIN.scene;return end local y if e=revealSettled()then return true end local k=math.max(2,12-(t-f)//10) return((t-f)//k)%2==1 end function oppRevealCheck() if not oppDarkUnlocked()or(pmem(SAVE_FLAGS_PMEM)&SAVE_F_REVEALED)~=0 then return end for i,o in ipairs(OPPONENTS)do if o.dark then OPP_SEL=i;REVEAL={t=0};return end end end local function revealEnd() REVEAL=nil OPP_DARK=true pmem(SAVE_FLAGS_PMEM,pmem(SAVE_FLAGS_PMEM)|SAVE_F_REVEALED) end local function revealInput() if btnp(4)or btnp(5)then revealEnd();return end REVEAL.t=REVEAL.t+1 local t=REVEAL.t if t>=revealSettled()+REVEAL_HOLD then revealEnd() elseif t==revealSettled()then playSfx("destroy") elseif t==revealFlick()then playSfx("special") elseif t%REVEAL_STAR==0 and t//REVEAL_STAR<=#OPPONENTS then playSfx("activate")end end function drawOppSelect() drawPlaymat(0,SCREEN_W) local t=REVEAL and REVEAL.t if t and t>=revealFlick()then shadowCentre("THE DARKNESS AWAKENS...",0,SCREEN_W,8,RED) else shadowCentre("CHOOSE YOUR OPPONENT",0,SCREEN_W,8,C_ACCENT) end local ps,pad=32,4 local bw=ps+pad*2 local gap=12 local x0=rowStart(OPP_COLS,bw,gap) local rh=bw+14 for i in ipairs(OPPONENTS)do local opp=oppForm(i) local x=x0+((i-1)%OPP_COLS)*(bw+gap) local cy=20+((i-1)//OPP_COLS)*rh if t and OPPONENTS[i].dark and t>=revealFlick()and t=i*REVEAL_STAR and t=CONFIRM_DUR then startGame()end end function handleOppConfirmInput() if btnp(4)then startGame() elseif btnp(5)then CONFIRM=nil;SCENE="oppselect"end end function drawOppConfirm() local opp=CONFIRM.opp drawPlaymat(0,SCREEN_W) local bw,bh=184,82 local bx,by=(SCREEN_W-bw)//2,(SCREEN_H-bh)//2 rect(bx,by,bw,bh,C_FRAME) rect(bx+2,by+2,bw-4,bh-4,C_BG) local px,py=bx+8,by+8 spr(opp.spr,px,py,-1,1,0,0,4,4) rectb(px-1,py-1,34,34,C_ACCENT) local tx=px+40 local tw=bx+bw-tx-8 print(opp.name,tx,py+1,C_ACCENT,true,1,true) printWrap(CONFIRM.quote,tx,py+11,tw,C_TEXT,by+bh-18) if(G.tick//30)%2==0 then local d=opp.boss and"Your soul is on the line!"or"It's time to duel!" local dw=print(d,0,-20,BLACK,false,1,false) print(d,bx+(bw-dw)//2,by+bh-26,opp.boss and RED or YELLOW,false,1,false) end printCentre("A:duel B:cancel",0,SCREEN_W,by+bh-10,C_TEXT,true) end end do MAX_DECK=20 MAX_EXTRA=8 MAX_COPIES=2 MAX_LIMITED=3 function copyLimit(id) return BANLIST[id]or MAX_COPIES end DB_COLS=4;DB_ROWS=5 DB_FUSION_ROWS=MAX_EXTRA//DB_COLS DB_CW=HAND_W;DB_CH=HAND_H_PLR;DB_CG=1 DB_LX=87 DB_LIST_RH=10 DB_LIST_VIS=12 function dbGridX(c)return 2+c*(DB_CW+DB_CG)end function dbGridY(r)return 10+r*(DB_CH+DB_CG)end function dbSectionArr(section) return(section=="fusion")and DB.extra or DB.deck end function dbCountIn(arr,id) local n=0 for _,v in ipairs(arr)do if v==id then n=n+1 end end return n end function dbIsFusion(cd) return cd~=nil and cd.cat=="monster"and cd.subtype=="fusion" end local EXODIA_PART={} for _,id in ipairs(EXODIA_PIECES)do EXODIA_PART[id]=true end function dbLimitedCount(arr,with) local n,exodia=0,false local function count(id) if BANLIST[id]~=1 then return end if EXODIA_PART[id]then exodia=true else n=n+1 end end for _,id in ipairs(arr)do count(id)end if with then count(with)end return n+(exodia and 1 or 0) end function dbOverCap()return not OPT.nolimitcap and dbLimitedCount(DB.deck)>MAX_LIMITED end function dbCapBlocks(id) if OPT.nolimitcap then return false end local after=dbLimitedCount(DB.deck,id) return after>MAX_LIMITED and after>dbLimitedCount(DB.deck) end DB_TOOLS={ {spr=31,kind="owned"}, {spr=10,kind="filter",val="monster"}, {spr=11,kind="filter",val="spell"}, {spr=12,kind="filter",val="trap"}, {spr=13,kind="sort",val="name"}, {spr=14,kind="sort",val="atk"}, } DB_TOOL_X={177,190,199,208,221,230} DB_TOOL_CUR=26 DB_SLOTS={{spr=27},{spr=28}} DB_SLOT_X={66,76} DB_HINTS={ "ARROWS:move A:action B:exit X:sort", "ARROWS:move A:action B:back X:sort", "ARROWS:move A:toggle B:exit X:sort", "ARROWS:move A:switch deck B:exit X:sort", } local DB_TOOL_ON={ owned=function()return DB.owned end, filter=function(t)return DB.filter==t.val end, sort=function(t)return DB.sort==t.val end, } local DB_TOOL_TOGGLE={ owned=function()DB.owned=not DB.owned end, filter=function(t)DB.filter=(DB.filter~=t.val)and t.val or nil end, sort=function(t)DB.sort=(DB.sort~=t.val)and t.val or nil end, } function dbToolShown(i) return DB.cur.section~="fusion"or DB_TOOLS[i].kind~="filter" end function dbToolStep(i,dir) local j=i+dir while j>=1 and j<=#DB_TOOLS do if dbToolShown(j)then return j end j=j+dir end end function dbToolFirst()return dbToolStep(0,1)end local CAT_RANK={monster=1,spell=2,trap=3} local DB_SORTS={ name=function(a,b)return CARDS[a].namecb.atk end return ca.namecb.lvl end return ca.name>(b*8))&0xff] local cd=slug and CARDS[slug] if cd and dbIsFusion(cd)==wantFusion then table.insert(arr,slug)end end end local function dbLoadDefault() DB.deck={} for i=1,math.min(#DECK1,MAX_DECK)do DB.deck[i]=DECK1[i]end if#DB.extra==0 then for i=1,math.min(#DECK1_EXTRA,MAX_EXTRA)do DB.extra[i]=DECK1_EXTRA[i]end end end local function dbReadSlot(slot) local deck,extra={},{} local base=saveDeckBase(slot) for w=0,SAVE_DECK_MAIN-1 do dbUnpackSlot(pmem(base+w),4,deck,false)end for w=0,SAVE_DECK_EXTRA-1 do dbUnpackSlot(pmem(base+SAVE_DECK_MAIN+w),4,extra,true) end return{deck=deck,extra=extra} end function dbLoad() local s=dbReadSlot(DB_SLOT) DB.deck,DB.extra=s.deck,s.extra if#DB.deck#visible then break end local slug=visible[ci] local cd=CARDS[slug] local iy=9+(i-1)*DB_LIST_RH local isSel=(DB.cur.panel==2 and DB.listSel==ci) if isSel then rect(DB_LX,iy,SCREEN_W-DB_LX,DB_LIST_RH-1,C_HIGHLIGHT)end if cd.spr then spr(sprOf(cd),lx,iy,-1,1,0,0,1,1)end local nm=#cd.name>18 and string.sub(cd.name,1,18)..".."or cd.name local nc=C_TEXT if isSel then nc=C_BG elseif not cardUnlocked(slug)then nc=GRAY end print(nm,lx+9,iy+1,nc,true,1,false) local cnt=dbCountIn(arr,slug) local cc=C_TEXT if cnt>=copyLimit(slug)then cc=C_ATTACK elseif cnt>0 then cc=C_ACCENT end print("x"..cnt,SCREEN_W-18,iy+1,cc,true,1,false) end if#visible>DB_LIST_VIS then local bh=DB_LIST_VIS*DB_LIST_RH local pct=DB.listScr/math.max(1,#visible-DB_LIST_VIS) rect(SCREEN_W-3,9,2,bh,C_BG) rect(SCREEN_W-3,9+math.floor(pct*(bh-4)),2,4,C_DIVIDER) end end local function drawDBSlots() for i,t in ipairs(DB_SLOTS)do local x=DB_SLOT_X[i] spr(t.spr,x,0,0,1,0,0,1,1) if DB_SLOT==i then line(x,8,x+7,8,C_ACCENT)end if DB.cur.panel==4 and DB.slotSel==i then spr(DB_TOOL_CUR,x,0,0,1,0,0,1,1)end end end local function drawDBTools() for i,t in ipairs(DB_TOOLS)do if dbToolShown(i)then local x=DB_TOOL_X[i] spr(t.spr,x,0,0,1,0,0,1,1) if DB_TOOL_ON[t.kind](t)then line(x,8,x+7,8,C_ACCENT)end if DB.cur.panel==3 and DB.tool==i then spr(DB_TOOL_CUR,x,0,0,1,0,0,1,1)end end end end local function drawDBMenu() local mw=84 local note=DB.menu.note local mh=8+#DB.menu.items*12+(note and 10 or 0) local mx,my=(SCREEN_W-mw)//2,(SCREEN_H-mh)//2 rect(mx,my,mw,mh,C_BG) rectb(mx,my,mw,mh,C_DIVIDER) for i,item in ipairs(DB.menu.items)do local iy=my+4+(i-1)*12 local tc=C_TEXT if i==DB.menu.sel then rect(mx+1,iy-1,mw-2,10,C_ACCENT) tc=C_BG end print(item[1],mx+4,iy+1,tc,true,1,false) end if note then printCentre(note,mx,mw,my+mh-8,C_ATTACK,true)end end function drawDeckBuild() cls(C_BG) local section=DB.cur.section if section=="fusion"then print("FUSION "..(#DB.extra).."/"..MAX_EXTRA,2,1,C_FACE_FUSION,true,1,false) else print("DECK "..(#DB.deck).."/"..MAX_DECK,2,1,#DB.deck>=MAX_DECK and C_TEXT or C_ATTACK,true,1,false) end line(0,8,DB_LX-2,8,C_DIVIDER) drawDBSlots() drawDBGrid(section) if section~="fusion"then tri(42,125,45,125,43,127,C_TEXT)end line(DB_LX-1,0,DB_LX-1,SCREEN_H-8,C_DIVIDER) print(section=="fusion"and"FUSION POOL"or"CARDS",DB_LX+2,1,C_ACCENT,true,1,false) line(DB_LX,8,SCREEN_W-1,8,C_DIVIDER) drawDBTools() drawDBList(section) line(0,SCREEN_H-7,SCREEN_W-1,SCREEN_H-7,C_DIVIDER) if DB.menu then drawDBMenu() else print(DB_HINTS[DB.cur.panel],2,SCREEN_H-5,C_TEXT,true,1,true) end if not OPT.nolimitcap then local n=dbLimitedCount(DB.deck) local s="LIM "..n.."/"..MAX_LIMITED local col=(n>MAX_LIMITED and C_ATTACK)or(n==MAX_LIMITED and C_ACCENT)or C_TEXT print(s,SCREEN_W-2-#s*4,SCREEN_H-5,col,true,1,true) end if G.infoCard then drawCardInfo(G.infoCard)end end local function dbLeave(save) if save then dbSave()else saveSlotLoad()end sync(3,1,false) SCENE="menu";TITLE_SEL=3 end local DB_ACTIONS={ cancel=function()end, remove=function(ctx) if ctx.slotIdx then table.remove(dbSectionArr(ctx.section),ctx.slotIdx)end end, addtodeck=function(ctx) if not ctx.cardId or not cardUnlocked(ctx.cardId)then return end local toExtra=(ctx.section=="fusion") if dbIsFusion(CARDS[ctx.cardId])~=toExtra then return end local arr=dbSectionArr(ctx.section) local cap=toExtra and MAX_EXTRA or MAX_DECK if#arr=MAX_DECK and not dbOverCap()end local function openSaveMenu() local canSave=dbCanSave() local items={{"EXIT","exit"},{"CANCEL","cancel"}} if canSave then table.insert(items,1,{"SAVE & EXIT","saveexit"})end dbOpenMenu({type="save"},items) if not canSave then DB.menu.note=(#DB.deckDB.listScr+DB_LIST_VIS then DB.listScr=DB.listScr+1 end elseif btnp(2)then c.panel=1 elseif btnp(4)then local id=visible[DB.listSel] if id then local items={{"INFO","info"},{"CANCEL","cancel"}} local full=cardUnlocked(id)and dbCapBlocks(id) if cardUnlocked(id)and not full then table.insert(items,1,{"ADD TO DECK","addtodeck"})end dbOpenMenu({type="list",cardId=id,section=c.section},items) if full then DB.menu.note="LIMITED CARDS FULL"end end elseif btnp(5)then c.panel=3;DB.tool=dbToolFirst()end end local function dbMenuInput() if btnp(0)then DB.menu.sel=math.max(1,DB.menu.sel-1) elseif btnp(1)then DB.menu.sel=math.min(#DB.menu.items,DB.menu.sel+1) elseif btnp(4)then local item=DB.menu.items[DB.menu.sel] local ctx=DB.menu.ctx DB.menu=nil dbExecAction(item[2],ctx) elseif btnp(5)then DB.menu=nil end end function handleDeckBuildInput() if handleInfoOverlay()then return end if DB.menu then dbMenuInput();return end if btnp(6)then dbSortDeck(DB.deck);dbSortDeck(DB.extra);return end if DB.cur.panel==1 then dbGridInput(DB.cur) elseif DB.cur.panel==3 then dbToolInput(DB.cur) elseif DB.cur.panel==4 then dbSlotInput(DB.cur) else dbListInput(DB.cur)end end end do SETTINGS={ {group="GAMEPLAY",key="nomusic",bit=1,label="DISABLE MUSIC", toggle=function(on)if on then music()else playMusic(0)end end}, {group="GAMEPLAY",key="nosfx",bit=4,label="DISABLE SOUND FX"}, {group="GAMEPLAY",key="lp8000",bit=2,label="8000 LIFE POINTS", apply=function(on)START_LP=on and 8000 or 4000 end}, {group="GAMEPLAY",key="nolimitcap",bit=7,label="DISABLE LIMIT CAP"}, {group="DEBUG",key="opphand",bit=3,label="SHOW OPPONENT HAND", apply=function(on)DEBUG_OPP_HAND=on end}, {group="DEBUG",key="unlockall",bit=5,label="UNLOCK ALL CARDS"}, } OPT={} OPT_SEL=1 OPT_RULES=false OPT_RULES_SCR=0 OPT_DEBUG=false OPT_CHEAT=0 OPT_ROWS={} function optRebuildRows() OPT_ROWS={} for i,s in ipairs(SETTINGS)do if OPT_DEBUG or s.group~="DEBUG"then OPT_ROWS[#OPT_ROWS+1]=i end end end OPT_BTN_SEL=0 OPT_BTN_Y=20 OPT_BTN_H=11 OPT_ROW_H=12 OPT_HDR_H=11 OPT_LIST_Y=36 OPT_BLOCK_W=120 OPT_X=(SCREEN_W-OPT_BLOCK_W)//2 OPT_LABEL_X=OPT_X+12 local function startMusic(track,chain) if OPT.nomusic then return end music(track,-1,-1,not chain,true) end function playMusic(track) MUSIC_ALT=nil startMusic(track) end DUEL_TRACKS={1,2} MUSIC_ALT=nil MUSIC_LIVE=false function playDuelMusic() MUSIC_ALT=math.random(#DUEL_TRACKS) MUSIC_LIVE=false startMusic(DUEL_TRACKS[MUSIC_ALT],true) end function tickMusic() if not MUSIC_ALT or OPT.nomusic then return end if peek(0x13FFC)~=255 then MUSIC_LIVE=true elseif MUSIC_LIVE then MUSIC_ALT=MUSIC_ALT%#DUEL_TRACKS+1 MUSIC_LIVE=false startMusic(DUEL_TRACKS[MUSIC_ALT],true) end end OPT_PMEM=SAVE_OPT OPT_DEBUG_BIT=6 function optSave() local v=0 for _,s in ipairs(SETTINGS)do if OPT[s.key]then v=v|(1<<(s.bit-1))end end if OPT_DEBUG then v=v|(1<<(OPT_DEBUG_BIT-1))end pmem(OPT_PMEM,v) end local function optSet(s,on) OPT[s.key]=on if s.apply then s.apply(on)end end function optLoad() local v=pmem(OPT_PMEM) for _,s in ipairs(SETTINGS)do optSet(s,((v>>(s.bit-1))&1)==1) end OPT_DEBUG=((v>>(OPT_DEBUG_BIT-1))&1)==1 optRebuildRows() end function optToggle(i) local s=SETTINGS[i] if not s then return end local on=not OPT[s.key] optSet(s,on) if s.toggle then s.toggle(on)end optSave() end function startOptions() OPT_SEL=1 OPT_RULES=false OPT_CHEAT=0 SCENE="options" end local RULES_INTRO=[[ YGO80 is a duel engine for the TIC-80 fantasy console, designed for fast-paced, handheld-style gameplay. Its ruleset adapts standard Yu-Gi-Oh into a format similar to Speed Duels, balancing for platform limitations while maximizing deck variety. Match Rules:]] local function buildRules() local t={RULES_INTRO, "Life Points: "..START_LP.." LP", "Field: 3 Monster & 3 Spell/Trap Zones", "Five Phases: No second Main Phase", "", "Deck Construction:", "Main Deck: Exactly "..MAX_DECK.." cards", "Extra Deck: Up to "..MAX_EXTRA.." cards", "Card Limit: Max "..MAX_COPIES.." copies per card", OPT.nolimitcap and"Limited Cards: No cap" or"Limited Cards: Max "..MAX_LIMITED.." per deck, the 5 Exodia pieces count as 1", "", "Banlist:"} local low,high={},{} for _,id in ipairs(CARD_ORDER)do local n=BANLIST[id] if n and nMAX_COPIES then high[#high+1]=CARDS[id].name end end local function section(hdr,names) if#names==0 then return end t[#t+1]="" t[#t+1]=hdr for _,nm in ipairs(names)do t[#t+1]=nm end end section("Limited to 1 copy:",low) section("Allowed at 3 copies:",high) return table.concat(t,"\n") end local RULES_LH=7 local rulesLines,rulesKey local function drawOptRules() local bx,by,bw,bh=OVERLAY_X,OVERLAY_Y,OVERLAY_W,OVERLAY_H drawOverlayBox(bx,by,bw,bh) print("RULES AND BANLIST",bx+4,by+4,C_ACCENT,true,1,false) line(bx+2,by+12,bx+bw-3,by+12,C_DIVIDER) local key=START_LP..tostring(OPT.nolimitcap) if rulesKey~=key then rulesKey=key rulesLines=wrapLines(buildRules(),bw-16) end local dx,dy,dmaxY=bx+5,by+16,by+bh-11 local visN=(dmaxY-dy)//RULES_LH OPT_RULES_SCR=math.max(0,math.min(OPT_RULES_SCR,#rulesLines-visN)) for i=1,visN do local ln=rulesLines[OPT_RULES_SCR+i] if not ln then break end print(ln,dx,dy+(i-1)*RULES_LH,ln:sub(-1)==":"and C_ACCENT or C_TEXT,true,1,true) end local ax=bx+bw-9 if OPT_RULES_SCR>0 then tri(ax,dy+1,ax+5,dy+1,ax+2,dy-2,C_TEXT)end if OPT_RULES_SCR<#rulesLines-visN then tri(ax,dmaxY-3,ax+5,dmaxY-3,ax+2,dmaxY,C_TEXT)end print("UP/DN:scroll B:close",bx+4,by+bh-8,C_HINT,true,1,true) end local function drawOptButton() local isSel=(OPT_SEL==OPT_BTN_SEL) local ty=OPT_BTN_Y+(OPT_BTN_H-5)//2 rect(OPT_X,OPT_BTN_Y,OPT_BLOCK_W,OPT_BTN_H,C_BG) rectb(OPT_X,OPT_BTN_Y,OPT_BLOCK_W,OPT_BTN_H,isSel and C_ACCENT or C_FRAME) printCentre("RULES AND BANLIST",OPT_X,OPT_BLOCK_W,ty,isSel and C_ACCENT or C_TEXT,false) if isSel then drawChevrons(OPT_X-9,OPT_X+OPT_BLOCK_W+2,ty)end end function drawOptions() drawPlaymat(0,SCREEN_W) shadowCentre("SETTINGS AND INFO",0,SCREEN_W,10,C_ACCENT) drawOptButton() local y=OPT_LIST_Y local group=nil for r,i in ipairs(OPT_ROWS)do local s=SETTINGS[i] if s.group~=group then group=s.group shadowText(group,OPT_X,y,C_FRAME) y=y+OPT_HDR_H end local isSel=(r==OPT_SEL) spr(OPT[s.key]and SPR_CHECK_ON or SPR_CHECK_OFF,OPT_X,y-1,0,1,0,0,1,1) shadowText(s.label,OPT_LABEL_X,y,isSel and C_ACCENT or C_TEXT) if isSel then drawChevrons(OPT_X-9,OPT_X+OPT_BLOCK_W+2,y)end y=y+OPT_ROW_H end printCentre("ARROWS:move A:select B:back",0,SCREEN_W,SCREEN_H-7,C_HINT,true) if OPT_RULES then drawOptRules()end end local CHEAT={2,3,2,3} local function optCheatStep() for b=0,7 do if btnp(b)then if b==CHEAT[OPT_CHEAT+1]then OPT_CHEAT=OPT_CHEAT+1 elseif b==CHEAT[1]then OPT_CHEAT=1 else OPT_CHEAT=0 end if OPT_CHEAT==#CHEAT then OPT_CHEAT=0 if not OPT_DEBUG then OPT_DEBUG=true optRebuildRows() optSave() playSfx("win") end end return end end end function handleOptionsInput() if OPT_RULES then if btnp(0,20,4)then OPT_RULES_SCR=OPT_RULES_SCR-1 elseif btnp(1,20,4)then OPT_RULES_SCR=OPT_RULES_SCR+1 elseif btnp(4)or btnp(5)then OPT_RULES=false end return end optCheatStep() if btnp(0,20,4)then OPT_SEL=math.max(OPT_BTN_SEL,OPT_SEL-1) elseif btnp(1,20,4)then OPT_SEL=math.min(#OPT_ROWS,OPT_SEL+1) elseif btnp(4)then if OPT_SEL==OPT_BTN_SEL then OPT_RULES=true;OPT_RULES_SCR=0 else optToggle(OPT_ROWS[OPT_SEL])end elseif btnp(5)then SCENE="menu";TITLE_SEL=4 end end end do TRIAL_MODES={ {key="trophies",label="TROPHIES",view="trophies", desc="Card backs won in the trials. Pick yours."}, {key="puzzle",label="DUEL PUZZLES", desc="Set boards with one winning line. Find it.", subs={{group="YGO BASICS",key="basics1",label="BASICS 1",bit=1}, {group="YGO BASICS",key="basics2",label="BASICS 2",bit=2}, {group="YGO BASICS",key="basics3",label="BASICS 3",bit=3}, {group="HERO MATH",key="heromath1",label="HERO 1",bit=4}, {group="HERO MATH",key="heromath2",label="HERO 2",bit=5}, {group="HERO MATH",key="heromath3",label="HERO 3",bit=6}, {group="LEGENDARY",key="legend1",label="LEGEND 1",bit=7}, {group="LEGENDARY",key="legend2",label="LEGEND 2",bit=8}, {group="LEGENDARY",key="legend3",label="LEGEND 3",bit=9}}}, {key="theme",label="THEME DUEL", desc="A duel fought under one deck restriction.", subs={{key="nost",label="NO SPELL/TRAPS",bit=10}, {key="noeffect",label="NO EFFECT MONSTERS",bit=11}, {key="spellcaster",label="SPELLCASTERS ONLY",bit=12}, {key="warrior",label="WARRIORS ONLY",bit=13}, {key="nomonster",label="NO MONSTERS",bit=14}}}, {key="survival",label="SURVIVAL",bit=15, desc="Face every duelist in a row on one life bar."}, } SURV_BACK_ROUNDS=5 local function trialMode(key) for _,m in ipairs(TRIAL_MODES)do if m.key==key then return m end end end local function subsDone(key,group) for _,s in ipairs(trialMode(key).subs)do if(not group or s.group==group)and not TRIALS_DONE[key.."/"..s.key]then return false end end return true end function puzzleGroupDone(group)return subsDone("puzzle",group)end local BACK_RULES={ theme=function()return subsDone("theme")end, survival=function()return pmem(SAVE_SURV_BEST)>=SURV_BACK_ROUNDS end, yami=function() for i,o in ipairs(OPPONENTS)do if o.dark then return oppBeaten(oppSlot(i,true))end end return false end, collector=function()return collectionComplete()end, } function cardBackUnlocked(b) if b.soon then return false end if b.group then return puzzleGroupDone(b.group)end local rule=BACK_RULES[b.key] return rule==nil or rule() end function cardBackWonBy(change) local had={} for i,b in ipairs(CARD_BACKS)do had[i]=cardBackUnlocked(b)end change() for i,b in ipairs(CARD_BACKS)do if not had[i]and cardBackUnlocked(b)then return b end end end TRIALS={mode=1,sub=1,view="modes"} TRI_MODE_Y,TRI_MODE_BAND=20,96 TRI_SUB_Y,TRI_SUB_BAND=26,98 TRI_MODE_W,TRI_MODE_H,TRI_MODE_PITCH=100,15,19 TRI_ROW_H=12 TRI_TEXT_H=5 TRI_CHAR_W=6 TRI_MARK_W=12 TRI_DECO={ {s=234,x=14,y=10}, {s=236,x=26,y=56}, {s=238,x=10,y=92}, {s=238,x=210,y=10}, {s=234,x=198,y=56}, {s=236,x=214,y=92}, } function startTrials() TRIALS={mode=1,sub=1,view="modes"} SCENE="trials" end local function trialKey(mode,sub) return mode.key..(sub and("/"..sub.key)or"") end TRIALS_DONE={} TRIALS_PMEM=SAVE_TRIALS local function eachTrial(fn) for _,m in ipairs(TRIAL_MODES)do if m.subs then for _,s in ipairs(m.subs)do fn(trialKey(m,s),s.bit)end else fn(trialKey(m),m.bit) end end end function trialsLoad() local v=pmem(TRIALS_PMEM) TRIALS_DONE={} eachTrial(function(key,bit) if bit and((v>>(bit-1))&1)==1 then TRIALS_DONE[key]=true end end) local b=CARD_BACKS[pmem(SAVE_CARD_BACK)] CARD_BACK_SEL=(b and cardBackUnlocked(b))and pmem(SAVE_CARD_BACK)or 1 end function cardBackUse(i) CARD_BACK_SEL=i pmem(SAVE_CARD_BACK,i) end function trialsSave() local v=0 eachTrial(function(key,bit) if bit and TRIALS_DONE[key]then v=v|(1<<(bit-1))end end) pmem(TRIALS_PMEM,v) end function trialsMarkDone(key) if TRIALS_DONE[key]then return end TRIALS_DONE[key]=true trialsSave() end function trialsReturn() sync(3,1,false) playMusic(0) G={tick=G.tick} SCENE="trials" end function trialsLaunch(mode,sub) if sub and mode.key=="puzzle"and startPuzzle(sub.key)then return end if sub and mode.key=="theme"and THEMES[sub.key]then local why=themeStart(sub.key) if not why then return end TRIALS.info={title=sub.label,need=why} playSfx("destroy") return end if mode.key=="survival"then survivalStart();return end local node=sub or mode TRIALS.msg=node.label.." - COMING SOON" TRIALS.msgT=G.tick+120 TRIALS.launched=trialKey(mode,sub) playSfx("activate") end local function drawButton(y,label,sel,note) local x=(SCREEN_W-TRI_MODE_W)//2 local ty=y+(TRI_MODE_H-TRI_TEXT_H)//2 rect(x,y,TRI_MODE_W,TRI_MODE_H,C_BG) rectb(x,y,TRI_MODE_W,TRI_MODE_H,sel and C_ACCENT or C_DIVIDER) local col=sel and C_ACCENT or C_TEXT if note then local lw=#label*TRI_CHAR_W local lx=x+(TRI_MODE_W-(lw+TRI_CHAR_W+#note*4))//2 print(label,lx,ty,col,true,1,false) print(note,lx+lw+TRI_CHAR_W,ty,C_HINT,true,1,true) else printCentre(label,x,TRI_MODE_W,ty,col,false) end if sel then drawChevrons(x-9,x+TRI_MODE_W+3,ty)end end local function listWidth(list) local w,group=0,nil for _,s in ipairs(list)do if s.group and s.group~=group then group=s.group w=math.max(w,#s.group*TRI_CHAR_W) end w=math.max(w,TRI_MARK_W+#s.label*TRI_CHAR_W) end return w end local function trialLines(list) local lines,lineOf,group={},{},nil for i,s in ipairs(list)do if s.group and s.group~=group then group=s.group lines[#lines+1]={hdr=s.group} end lines[#lines+1]={idx=i,row=s} lineOf[i]=#lines end return lines,lineOf,group~=nil end local function trialsScroll(lines,li,vis) local scr=TRIALS.scr or 0 local top=(lines[li-1]and lines[li-1].hdr)and li-1 or li if top<=scr then scr=top-1 elseif li>scr+vis then scr=li-vis end scr=math.max(0,math.min(scr,#lines-vis)) TRIALS.scr=scr return scr end local function drawModeView(sel) shadowCentre("TRIALS",0,SCREEN_W,14,C_ACCENT) local blockH=(#TRIAL_MODES-1)*TRI_MODE_PITCH+TRI_MODE_H local y=TRI_MODE_Y+(TRI_MODE_BAND-blockH)//2 for i,m in ipairs(TRIAL_MODES)do drawButton(y,m.label,i==sel,(m.key=="survival")and survivalBestLabel()or nil) y=y+TRI_MODE_PITCH end printCentre(TRIAL_MODES[sel].desc,0,SCREEN_W,SCREEN_H-20,C_TEXT,true) end local function drawListView(mode,sel) shadowCentre(mode.label,0,SCREEN_W,14,C_ACCENT) local list=mode.subs local w=listWidth(list) local x=(SCREEN_W-w)//2 local lines,lineOf,grouped=trialLines(list) local n=#lines local pitch=grouped and TRI_ROW_H or TRI_ROW_H+2 local vis=(TRI_SUB_BAND-TRI_TEXT_H)//pitch+1 local blockH=(math.min(n,vis)-1)*pitch+TRI_TEXT_H local top=TRI_SUB_Y+(TRI_SUB_BAND-blockH)//2 local scr=trialsScroll(lines,lineOf[sel],vis) local y=top for k=scr+1,math.min(scr+vis,n)do local ln=lines[k] if ln.hdr then shadowText(ln.hdr,x,y,C_FRAME) else spr(TRIALS_DONE[trialKey(mode,ln.row)]and SPR_TRIAL_ON or SPR_TRIAL_OFF, x,y-1,0,1,0,0,1,1) shadowText(ln.row.label,x+TRI_MARK_W,y,(ln.idx==sel)and C_ACCENT or C_TEXT) if ln.idx==sel then drawChevrons(x-9,x+w+2,y)end end y=y+pitch end local ax,bot=x+w+10,top+blockH if scr>0 then tri(ax,top-1,ax+5,top-1,ax+2,top-4,C_TEXT)end if scr+visTRO_COLS then i=i-TRO_COLS elseif btnp(1,20,4)and i+TRO_COLS<=n then i=i+TRO_COLS elseif btnp(4)then if not cardBackUnlocked(CARD_BACKS[i])then playSfx("destroy") elseif i~=CARD_BACK_SEL then cardBackUse(i);playSfx("activate")end elseif btnp(5)then TRIALS.view="modes"end TRIALS.back=i end function handleTrialsInput() if TRIALS.info then if btnp(4)or btnp(5)then TRIALS.info=nil end return end if TRIALS.view=="trophies"then trophyInput();return end local mode=TRIAL_MODES[TRIALS.mode] if TRIALS.view=="subs"then if btnp(0,20,4)then TRIALS.sub=math.max(1,TRIALS.sub-1) elseif btnp(1,20,4)then TRIALS.sub=math.min(#mode.subs,TRIALS.sub+1) elseif btnp(4)then trialsLaunch(mode,mode.subs[TRIALS.sub]) elseif btnp(5)then TRIALS.view="modes"end return end if btnp(0,20,4)then TRIALS.mode=math.max(1,TRIALS.mode-1) elseif btnp(1,20,4)then TRIALS.mode=math.min(#TRIAL_MODES,TRIALS.mode+1) elseif btnp(4)then if mode.subs then TRIALS.view="subs";TRIALS.sub=1;TRIALS.scr=0 elseif mode.view then TRIALS.view=mode.view;TRIALS.back=CARD_BACK_SEL else trialsLaunch(mode)end elseif btnp(5)then SCENE="menu";TITLE_SEL=2 end end end do PUZZLES={ basics1={ goal="WIN THIS TURN", lp={4000,1600}, mon={{"rogue_doll","feral_imp"}, {{id="lord_of_d",pos=2}}}, }, basics2={ goal="WIN THIS TURN", lp={4000,100}, hand={{"buster_blader","mystical_typhoon","summoned_skull"}}, mon={{"dark_magician","dark_magician_girl"}, {"blue_eyes_w_dragon"}}, st={{},{{id="negate_attack",fd=true}}}, }, basics3={ goal="WIN THIS TURN", lp={4000,3800}, hand={{"stamping_destruction","jinzo","alligators_sword","summoned_skull"}}, mon={{"flame_manipulator","vorse_raider"}, {{id="red_eyes_b_dragon",pos=2}}}, st={{{id="call_of_the_haunted",fd=true}}, {{id="united_we_stand",equip={plr=2,col=1}},{id="mirror_force",fd=true}}}, fs={"sogen"}, gy={{"baby_dragon"}}, }, heromath1={ goal="WIN THIS TURN", lp={4000,2000}, hand={{"polymerization"}}, mon={{"e_hero_avian","e_hero_burstinatrix","e_hero_clayman"}, {"la_jinn"}}, extra={{"eh_flame_wingman","eh_rampart_blaster"}}, }, heromath2={ goal="WIN THIS TURN", lp={4000,2600}, hand={{"e_hero_clayman","e_hero_burstinatrix","polymerization","fusion_recovery","r_righteous_justice"}}, mon={{"e_hero_avian"},{{id="blue_eyes_w_dragon"},{id="aqua_madoor",pos=2}}}, st={{},{{id="mirror_force",fd=true},{id="negate_attack",fd=true},{id="magic_cylinder",fd=true}}}, fs={"skyscraper"}, extra={{"eh_flame_wingman","eh_wild_wingman","eh_plasma_vice","eh_wildedge", "eh_rampart_blaster","eh_thunder_giant","eh_electrum","eh_shining_wingman"}}, }, heromath3={ goal="WIN THIS TURN", lp={4000,5000}, hand={{"polymerization","e_hero_avian","e_hero_burstinatrix","e_hero_sparkman"}}, mon={{"eh_thunder_giant","wroughtweiler"}, {"blue_eyes_ultimate_dragon","winged_kuriboh"}}, gy={{"e_hero_clayman","e_hero_sparkman"}}, fs={"skyscraper"}, extra={{"eh_flame_wingman","eh_shining_wingman","eh_rampart_blaster","eh_electrum", "eh_wild_wingman","eh_plasma_vice","eh_wildedge"}}, }, legend1={ goal="WIN THIS TURN", lp={300,4000}, hand={{"exodia","left_arm_forbidden","sangan","monster_reborn"}}, mon={{"right_leg_forbidden","legion_fiend_jester",{id="penguin_soldier",pos=2,fd=true}}, {"e_hero_avian","feral_imp","reflect_bounder"}}, gy={{"left_leg_forbidden"}}, deck={{"kuriboh","giant_soldier","right_arm_forbidden"}}, }, legend2={ goal="WIN THIS TURN", lp={4000,3100}, hand={{"dark_magician","dark_magician_girl","thousand_knives","monster_reborn"}}, mon={{"legion_fiend_jester","double_coston"}, {"blue_eyes_w_dragon","dark_magician",{id="mystical_elf",pos=2}}}, gy={{"dark_magician"}}, deck={{"rogue_doll"}}, }, legend3={ goal="WIN THIS TURN", lp={4000,6000}, hand={{"flute_of_dragon","blue_eyes_w_dragon","blue_eyes_w_dragon","blue_eyes_w_dragon", "polymerization","mystical_typhoon"}}, mon={{"lord_of_d"}, {"dark_magician","dark_magician_girl",{id="mystical_elf",pos=2}}}, st={{{id="de_fusion",fd=true}}, {{id="magic_cylinder",fd=true}}}, extra={{"blue_eyes_ultimate_dragon"}}, }, } PUZZLE_BANNER=320 local function puzzleCard(spec) if type(spec)=="string"then spec={id=spec}end local c=makeCard(spec.id) c.pos=spec.pos or 1 c.facedown=spec.fd or false if spec.equip then c.equippedTo={plr=spec.equip.plr,col=spec.equip.col}end return c end local function specId(spec) return(type(spec)=="string")and spec or spec.id end local function zoneList(def,key,plr) return(def[key]or{})[plr]or{} end local function puzzleIds(def) local ids={} for p=1,2 do for _,key in ipairs{"mon","st","hand","gy","extra"}do for _,spec in ipairs(zoneList(def,key,p))do ids[#ids+1]=specId(spec)end end for _,id in ipairs(zoneList(def,"deck",p))do ids[#ids+1]=id end local f=(def.fs or{})[p] if f then ids[#ids+1]=specId(f)end end return ids end function startPuzzle(key) local def=PUZZLES[key] if not def then return false end sync(3,2,false) artReset() newGame() G.puzzle=key G.puzzleDef=def G.oppName="PUZZLE" G.turn=3 G.ph=PH_MAIN G.lp={def.lp[1],def.lp[2]} G.dispLp={def.lp[1],def.lp[2]} G.lpFull=math.max(def.lp[1],def.lp[2]) for p=1,2 do for c,spec in ipairs(zoneList(def,"mon",p))do G.mon[p][c]=puzzleCard(spec)end for c,spec in ipairs(zoneList(def,"st",p))do G.st[p][c]=puzzleCard(spec)end for _,spec in ipairs(zoneList(def,"hand",p))do addToHand(p,puzzleCard(spec))end for _,spec in ipairs(zoneList(def,"gy",p))do table.insert(G.gy[p],puzzleCard(spec))end for _,spec in ipairs(zoneList(def,"extra",p))do table.insert(G.extra[p],puzzleCard(spec))end for _,id in ipairs(zoneList(def,"deck",p))do table.insert(G.deck[p],id)end local f=(def.fs or{})[p] if f then G.fs[p]=puzzleCard(f)end end checkEquips() artPrime(puzzleIds(def)) G.puzzleBannerT=G.tick+PUZZLE_BANNER playDuelMusic() TRANS={t=0} SCENE="trans" return true end function puzzleLeave() if G.winner==1 then local back=cardBackWonBy(function()trialsMarkDone("puzzle/"..G.puzzle)end) if back then backRewardStart(back);return end end trialsReturn() end function puzzleBannerText() if not(G.puzzle and G.tick<(G.puzzleBannerT or 0))then return nil end return G.puzzleDef.goal end end do THEMES={ nost={ need="DECK MUST BE MONSTERS ONLY", allows=function(cd)return cd.cat=="monster"end, }, noeffect={ need="NO EFFECT MONSTERS ALLOWED", allows=function(cd)return cd.cat~="monster"or cd.effect==nil end, }, spellcaster={ need="SPELLCASTER MONSTERS ONLY", allows=function(cd)return cd.cat~="monster"or cd.type=="spellcaster"end, }, warrior={ need="WARRIOR MONSTERS ONLY", allows=function(cd)return cd.cat~="monster"or cd.type=="warrior"end, }, nomonster={ need="DECK MUST HAVE NO MONSTERS", allows=function(cd)return cd.cat~="monster"end, }, } THEME_WINS=2 THEME_ROUNDS=3 THEME=nil local function themeOffender(t) for _,list in ipairs{DB.deck,DB.extra}do for _,id in ipairs(list)do local cd=CARDS[id] if cd and not t.allows(cd)then return cd end end end end function themeMatchLabel() if not THEME then return nil end return("BEST OF THREE: %d - %d"):format(THEME.wins,THEME.losses) end function themeGameOverLine(win) if not THEME then return nil end return("ROUND %d OF %d %d-%d"):format(THEME.round,THEME_ROUNDS, THEME.wins+(win and 1 or 0),THEME.losses+(win and 0 or 1)) end local function themeNextRound() OPP_SEL=math.random(#OPPONENTS) startGame() G.theme=THEME.key end function themeStart(key) local t=THEMES[key] if not t then return end dbLoad() if themeOffender(t)then return t.need end THEME={key=key,wins=0,losses=0,round=1,oppSel=OPP_SEL} themeNextRound() end local function themeLeave(won) local back=won and cardBackWonBy(function()trialsMarkDone("theme/"..THEME.key)end) OPP_SEL=THEME.oppSel THEME=nil if back then backRewardStart(back)else trialsReturn()end end function themeRoundOver() if G.winner==1 then THEME.wins=THEME.wins+1 else THEME.losses=THEME.losses+1 end if THEME.wins>=THEME_WINS then themeLeave(true) elseif THEME.losses>=THEME_WINS then themeLeave(false) else THEME.round=THEME.round+1 themeNextRound() end end end do SURVIVAL=nil SURV_BEST=0 function survivalLoad() SURV_BEST=pmem(SAVE_SURV_BEST) end local function survivalKeep(n) if n<=SURV_BEST then return end SURV_BEST=n pmem(SAVE_SURV_BEST,n) end local function survivalRound()return SURVIVAL.wins+1 end function survivalMatchLabel() if not SURVIVAL then return nil end return("ROUND %d"):format(survivalRound()) end function survivalBestLabel() if SURV_BEST<1 then return nil end return("BEST %d"):format(SURV_BEST) end function survivalGameOverLine(win) if not SURVIVAL then return nil end if win then return("ROUND %d CLEARED"):format(survivalRound())end local head=(SURVIVAL.wins>SURVIVAL.best)and"NEW RECORD"or"RUN OVER" return("%s: %d IN A ROW"):format(head,SURVIVAL.wins) end local function survivalNextRound() OPP_SEL=math.random(#OPPONENTS) startGame() G.survival=true G.lp[1]=SURVIVAL.lp G.dispLp[1]=SURVIVAL.lp end function survivalStart() SURVIVAL={wins=0,lp=START_LP,oppSel=OPP_SEL,best=SURV_BEST} survivalNextRound() end local function survivalLeave() OPP_SEL=SURVIVAL.oppSel SURVIVAL=nil trialsReturn() end function survivalRoundOver() if G.winner~=1 then survivalLeave();return end SURVIVAL.wins=SURVIVAL.wins+1 SURVIVAL.lp=G.lp[1] local back=cardBackWonBy(function() survivalKeep(SURVIVAL.wins) if SURVIVAL.wins>=#OPPONENTS then trialsMarkDone("survival")end end) if back then backRewardStart(back,survivalNextRound)else survivalNextRound()end end end do COLL_PMEM=SAVE_COLL COLL_SLOTS=SAVE_COLL_N COLL={} local function collBit(id) local n=CARD_NUM[id] if not n then return nil end return(n-1)//32+1,(n-1)%32 end local function collSet(id) local s,b=collBit(id) if s then COLL[s]=COLL[s]|(1<>b)&1)==1 end function collectionComplete() for _,id in ipairs(CARD_ORDER)do local s,b=collBit(id) if not s or(((COLL[s]or 0)>>b)&1)==0 then return false end end return true end function collectionSave() for i=1,COLL_SLOTS do pmem(COLL_PMEM+i-1,COLL[i])end pmem(SAVE_FLAGS_PMEM,pmem(SAVE_FLAGS_PMEM)|SAVE_F_SEEDED) end function unlockCard(id) collSet(id) collectionSave() end function collectionLoad() if(pmem(SAVE_FLAGS_PMEM)&SAVE_F_SEEDED)~=0 then for i=1,COLL_SLOTS do COLL[i]=pmem(COLL_PMEM+i-1)end return end for i=1,COLL_SLOTS do COLL[i]=0 end for _,id in ipairs(DECK1)do collSet(id)end for _,id in ipairs(DECK1_EXTRA)do collSet(id)end collectionSave() end function oppBeaten(i) return((pmem(SAVE_BEATEN)>>(i-1))&1)==1 end function oppSlot(i,dark) return dark and i+SAVE_BEATEN_DARK or i end function oppMarkBeaten(i) if i then pmem(SAVE_BEATEN,pmem(SAVE_BEATEN)|(1<<(i-1)))end end function oppDarkUnlocked() for i=1,#OPPONENTS do if not oppBeaten(i)then return false end end return true end REWARD_DROPS=3 REWARD_CW=40 REWARD_CH=44 REWARD_GAP=8 REWARD_Y=WINBOX_Y+14 REWARD_NAME=23 REWARD_FLASH=45 function rewardStart() local n=G.oppDropN or REWARD_DROPS local pool=G.oppDrops if not(pool and#pool>=n)then pool=CARD_ORDER end local ids,seen={},{} while#ids0 then if(r.flash//4)%2==0 then rect(x+2,y+2,REWARD_CW-4,REWARD_CH-4,WHITE) else rectb(x+1,y+1,REWARD_CW-2,REWARD_CH-2,YELLOW)end printCentre("NEW CARD BACK!",WINBOX_X,WINBOX_W,WINBOX_Y+60,YELLOW,false) return end printCentre(r.back.label,WINBOX_X,WINBOX_W,WINBOX_Y+60,C_TEXT,false) printCentre("A:continue",WINBOX_X,WINBOX_W,WINBOX_Y+68,C_HINT,true) end function drawRewards() local r=G.reward if r.back then drawBackReward();return end dimScreen() drawOverlayBox(WINBOX_X,WINBOX_Y,WINBOX_W,WINBOX_H) printCentre(#r.ids==1 and"CARD WON"or"CARDS WON",WINBOX_X,WINBOX_W,WINBOX_Y+5,C_ACCENT,false) for i=1,#r.ids do drawRewardCard(i)end if r.flash>0 then drawRewardFlash(r.flash) printCentre("NEW CARD!",WINBOX_X,WINBOX_W,WINBOX_Y+60,YELLOW,false) elseif r.up[r.sel]then local nm=CARDS[r.ids[r.sel]].name if#nm>REWARD_NAME then nm=string.sub(nm,1,REWARD_NAME)..".."end printCentre(nm,WINBOX_X,WINBOX_W,WINBOX_Y+60,C_TEXT,false) end printCentre(rewardAllRevealed()and"A:info B:menu"or"A:reveal", WINBOX_X,WINBOX_W,WINBOX_Y+68,C_TEXT,true) end local function rewardReveal(i) local r=G.reward local id=r.ids[i] if r.up[i]then G.infoCard=CARDS[id];return end r.up[i]=true r.new[i]=not cardUnlocked(id) unlockCard(id) if r.new[i]then r.flash=REWARD_FLASH end playSfx(r.new[i]and"win"or"lose") end function rewardInput() local r=G.reward if r.flash>0 then r.flash=r.flash-1;return end if r.back then if btnp(4)then(r.after or trialsReturn)()end return end if handleInfoOverlay()then return end if btnp(2)then r.sel=math.max(1,r.sel-1) elseif btnp(3)then r.sel=math.min(#r.ids,r.sel+1) elseif btnp(4)then rewardReveal(r.sel) elseif btnp(5)and rewardAllRevealed()then leaveDuel()end end end do RPS_NAMES={[1]="ROCK",[2]="PAPER",[3]="SCISSORS"} RPS_SPRS={[1]=38,[2]=40,[3]=42} RPS_COLS={[1]=C_FACE_EFFECT,[2]=C_FACE_SPELL,[3]=C_FACE_TRAP} local RPS_BEATS={[1]=3,[2]=1,[3]=2} function rpsResult(p,ai) if p==ai then return 0 end return(RPS_BEATS[p]==ai)and 1 or 2 end function startRPS() RPS={sel=2,phase="select",playerChoice=nil,aiChoice=nil,winner=0,timer=0} end function handleRPSInput() if RPS.phase=="select"then if btnp(2)then RPS.sel=math.max(1,RPS.sel-1) elseif btnp(3)then RPS.sel=math.min(3,RPS.sel+1) elseif btnp(4)then RPS.playerChoice=RPS.sel RPS.aiChoice=math.random(1,3) RPS.winner=rpsResult(RPS.playerChoice,RPS.aiChoice) RPS.phase="reveal";RPS.timer=50 end elseif RPS.phase=="result"then if btnp(4)then if RPS.winner==0 then RPS.phase="select";RPS.sel=2 else G.active=RPS.winner;G.firstPlayer=RPS.winner traceDuelStart() if RPS.winner==2 then G.aiTimer=AI_DELAY end playDuelMusic() TRANS={t=0} SCENE="trans" end end end end function tickRPS() if RPS.phase=="reveal"then RPS.timer=RPS.timer-1 if RPS.timer<=0 then RPS.phase="result"end end end local function drawRpsCard(choice,x,y,s) s=s or 1 rect(x+s,y+s,(HAND_W-2)*s,(HAND_H_PLR-2)*s,RPS_COLS[choice]) spr(RPS_SPRS[choice],x+2*s,y+2*s,14,s,0,0,2,2) clip(x,y,HAND_W*s,HAND_H_PLR*s) spr(SPR_FRAME,x,y,15,s,0,0,3,3) clip() end local RPS_PICK_S,RPS_PICK_Y,RPS_SPREAD=2,30,8 local function drawRpsSelect() printCentre("WHO GOES FIRST?",0,SCREEN_W,21,C_TEXT,false) drawRpsCard(RPS.sel,(SCREEN_W-HAND_W*RPS_PICK_S)//2,RPS_PICK_Y,RPS_PICK_S) if(G.tick//18)%2==0 then printCentre(RPS_NAMES[RPS.sel],0,SCREEN_W,77,C_ACCENT,false) end end local function drawRpsReveal() local ly=RPS_PICK_Y local cw,ch=HAND_W*RPS_PICK_S,HAND_H_PLR*RPS_PICK_S local lx=SCREEN_W//4-cw//2-RPS_SPREAD drawRpsCard(RPS.playerChoice,lx,ly,RPS_PICK_S) printCentre("YOU",lx,cw,ly+ch+2,C_ACCENT,true) printCentre(RPS_NAMES[RPS.playerChoice],lx,cw,ly+ch+9,C_ACCENT,true) printCentre("VS",0,SCREEN_W,ly+ch//2-3,C_TEXT,false) local rx=3*SCREEN_W//4-cw//2+RPS_SPREAD if RPS.phase=="result"then drawRpsCard(RPS.aiChoice,rx,ly,RPS_PICK_S) else drawCardBack(rx,ly,cw,ch,0,0,0,RPS_PICK_S) end printCentre("CPU",rx,cw,ly+ch+2,C_TEXT,true) if RPS.phase~="result"then return end printCentre(RPS_NAMES[RPS.aiChoice],rx,cw,ly+ch+9,C_TEXT,true) local msg,sub,col if RPS.winner==1 then msg,sub,col="YOU WIN!","YOU GO FIRST!",C_ACCENT elseif RPS.winner==2 then msg,sub,col="CPU WINS!","CPU GOES FIRST!",C_TEXT else msg,sub,col="TIE!","PICK AGAIN",PEACH end if(G.tick//15)%2==0 or RPS.winner==0 then printCentre(msg,0,SCREEN_W,76,col,false) end printCentre(sub,0,SCREEN_W,88,col,true) if RPS.winner==0 and(G.tick//20)%2==0 then printCentre("A: TRY AGAIN",0,SCREEN_W,99,C_TEXT,true) end local match=themeMatchLabel()or survivalMatchLabel() if match then printCentre(match,0,SCREEN_W,ly-7,C_TEXT,true)end end function drawRPS() drawPlaymat(0,SCREEN_W) local cx0=(SCREEN_W-(3*HAND_W+2*HAND_GAP))//2 for i=0,2 do drawCardBack(cx0+i*(HAND_W+HAND_GAP),OPP_HAND_Y,HAND_W,HAND_H_OPP,0,0,-11) end for i=0,2 do local sel=(i+1==RPS.sel and RPS.phase=="select") drawRpsCard(i+1,cx0+i*(HAND_W+HAND_GAP),sel and PLR_HAND_Y-5 or PLR_HAND_Y) end if RPS.phase=="select"then drawRpsSelect()else drawRpsReveal()end end end DEBUG_HAND={nil,nil} function forceHand(plr,ids) if not ids or#ids==0 then return end local hand,deck=G.hand[plr],G.deck[plr] for i=#hand,1,-1 do table.insert(deck,hand[i].id) table.remove(hand,i) end for _,id in ipairs(ids)do if CARDS[id]then for i,d in ipairs(deck)do if d==id then table.remove(deck,i);break end end table.insert(hand,makeCard(id)) else trace("forceHand: unknown card id "..tostring(id)) end end shuffle(deck) end function startGame() sync(3,2,false) artReset() newGame() dbLoad() local opp,beat=oppForDuel() G.oppName=opp.name G.oppDrops=opp.drops G.oppDropN=opp.dropCount G.oppIdx=beat for i,id in ipairs(DB.deck)do G.deck[1][i]=id end for i,id in ipairs(opp.deck)do G.deck[2][i]=id end shuffle(G.deck[1]);shuffle(G.deck[2]) for _,id in ipairs(DB.extra)do G.extra[1][#G.extra[1]+1]=makeCard(id)end for _,id in ipairs(opp.extra or{})do G.extra[2][#G.extra[2]+1]=makeCard(id)end artPrime(DB.deck);artPrime(DB.extra);artPrime(opp.deck);artPrime(opp.extra) for _=1,4 do drawCard(1,true);drawCard(2,true)end forceHand(1,DEBUG_HAND[1]);forceHand(2,DEBUG_HAND[2]) startRPS() SCENE="rps" end function BOOT() G={tick=0} SCENE="boot" TITLE_SEL=1 saveMigrate() optLoad() collectionLoad() trialsLoad() survivalLoad() playMusic(0) end local function duelInput() if OPT_DEBUG and keyp(2)then simBench()end handleInput() end local function duelTick() autoPhase() aiTick() tickAnims() procTick() tickDispLp() tickWinSfx() end local function duelDraw() cls(C_BG) drawDuelField() local sceneT,winAnim=winSceneT() if not sceneT then drawCursor();drawAnims()end drawPanel() if not sceneT then drawChain();drawModeBanner()end if G.mode=="gy_view"and G.gyView then drawGYView()end if G.mode=="ed_view"and G.edView then drawEDView()end if G.mode=="sel_deck"and G.deckSel then drawDeckSelect()end if G.winner then if winAnim then winAnim(G.winner,sceneT)end if G.reward then drawRewards() elseif winHoldOver()then drawGameOver()end end if G.infoCard then drawCardInfo(G.infoCard)end drawPopup() end local SCENES={ boot={tick=artBootTick,draw=drawArtBoot}, title={input=handleTitleInput,draw=drawTitle}, menu={input=handleMenuInput,draw=drawMenu}, curtain={draw=drawCurtain}, oppselect={input=handleOppSelectInput,draw=drawOppSelect}, oppconfirm={input=handleOppConfirmInput,tick=tickOppConfirm,draw=drawOppConfirm}, deckbuild={input=handleDeckBuildInput,draw=drawDeckBuild}, options={input=handleOptionsInput,draw=drawOptions}, trials={input=handleTrialsInput,draw=drawTrials}, rps={input=handleRPSInput,tick=tickRPS,draw=drawRPS}, trans={draw=drawTrans}, game={input=duelInput,tick=duelTick,draw=duelDraw}, } function TIC() G.tick=G.tick+1 tickMusic() local s=SCENES[SCENE] if not s then return end local at=SCENE if s.input then s.input();if SCENE~=at then return end end if s.tick then s.tick();if SCENE~=at then return end end if s.draw then s.draw()end end