12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720 |
- BattleCore:
- ; These are move effects (second value from the Moves table in bank $E).
- ResidualEffects1:
- ; most non-side effects
- db CONVERSION_EFFECT
- db HAZE_EFFECT
- db SWITCH_AND_TELEPORT_EFFECT
- db MIST_EFFECT
- db FOCUS_ENERGY_EFFECT
- db CONFUSION_EFFECT
- db HEAL_EFFECT
- db TRANSFORM_EFFECT
- db LIGHT_SCREEN_EFFECT
- db REFLECT_EFFECT
- db POISON_EFFECT
- db PARALYZE_EFFECT
- db SUBSTITUTE_EFFECT
- db MIMIC_EFFECT
- db LEECH_SEED_EFFECT
- db SPLASH_EFFECT
- db -1
- SetDamageEffects:
- ; moves that do damage but not through normal calculations
- ; e.g., Super Fang, Psywave
- db SUPER_FANG_EFFECT
- db SPECIAL_DAMAGE_EFFECT
- db -1
- ResidualEffects2:
- ; non-side effects not included in ResidualEffects1
- ; stat-affecting moves, sleep-inflicting moves, and Bide
- ; e.g., Meditate, Bide, Hypnosis
- db $01
- db ATTACK_UP1_EFFECT
- db DEFENSE_UP1_EFFECT
- db SPEED_UP1_EFFECT
- db SPECIAL_UP1_EFFECT
- db ACCURACY_UP1_EFFECT
- db EVASION_UP1_EFFECT
- db ATTACK_DOWN1_EFFECT
- db DEFENSE_DOWN1_EFFECT
- db SPEED_DOWN1_EFFECT
- db SPECIAL_DOWN1_EFFECT
- db ACCURACY_DOWN1_EFFECT
- db EVASION_DOWN1_EFFECT
- db BIDE_EFFECT
- db SLEEP_EFFECT
- db ATTACK_UP2_EFFECT
- db DEFENSE_UP2_EFFECT
- db SPEED_UP2_EFFECT
- db SPECIAL_UP2_EFFECT
- db ACCURACY_UP2_EFFECT
- db EVASION_UP2_EFFECT
- db ATTACK_DOWN2_EFFECT
- db DEFENSE_DOWN2_EFFECT
- db SPEED_DOWN2_EFFECT
- db SPECIAL_DOWN2_EFFECT
- db ACCURACY_DOWN2_EFFECT
- db EVASION_DOWN2_EFFECT
- db -1
- AlwaysHappenSideEffects:
- ; Attacks that aren't finished after they faint the opponent.
- db DRAIN_HP_EFFECT
- db EXPLODE_EFFECT
- db DREAM_EATER_EFFECT
- db PAY_DAY_EFFECT
- db TWO_TO_FIVE_ATTACKS_EFFECT
- db $1E
- db ATTACK_TWICE_EFFECT
- db RECOIL_EFFECT
- db TWINEEDLE_EFFECT
- db RAGE_EFFECT
- db -1
- SpecialEffects:
- ; Effects from arrays 2, 4, and 5B, minus Twineedle and Rage.
- ; Includes all effects that do not need to be called at the end of
- ; ExecutePlayerMove (or ExecuteEnemyMove), because they have already been handled
- db DRAIN_HP_EFFECT
- db EXPLODE_EFFECT
- db DREAM_EATER_EFFECT
- db PAY_DAY_EFFECT
- db SWIFT_EFFECT
- db TWO_TO_FIVE_ATTACKS_EFFECT
- db $1E
- db CHARGE_EFFECT
- db SUPER_FANG_EFFECT
- db SPECIAL_DAMAGE_EFFECT
- db FLY_EFFECT
- db ATTACK_TWICE_EFFECT
- db JUMP_KICK_EFFECT
- db RECOIL_EFFECT
- ; fallthrough to Next EffectsArray
- SpecialEffectsCont:
- ; damaging moves whose effect is executed prior to damage calculation
- db THRASH_PETAL_DANCE_EFFECT
- db TRAPPING_EFFECT
- db -1
- SlidePlayerAndEnemySilhouettesOnScreen:
- call LoadPlayerBackPic
- ld a, MESSAGE_BOX ; the usual text box at the bottom of the screen
- ld [wTextBoxID], a
- call DisplayTextBoxID
- coord hl, 1, 5
- lb bc, 3, 7
- call ClearScreenArea
- call DisableLCD
- call LoadFontTilePatterns
- call LoadHudAndHpBarAndStatusTilePatterns
- ld hl, vBGMap0
- ld bc, $400
- .clearBackgroundLoop
- ld a, " "
- ld [hli], a
- dec bc
- ld a, b
- or c
- jr nz, .clearBackgroundLoop
- ; copy the work RAM tile map to VRAM
- coord hl, 0, 0
- ld de, vBGMap0
- ld b, 18 ; number of rows
- .copyRowLoop
- ld c, 20 ; number of columns
- .copyColumnLoop
- ld a, [hli]
- ld [de], a
- inc e
- dec c
- jr nz, .copyColumnLoop
- ld a, 12 ; number of off screen tiles to the right of screen in VRAM
- add e ; skip the off screen tiles
- ld e, a
- jr nc, .noCarry
- inc d
- .noCarry
- dec b
- jr nz, .copyRowLoop
- call EnableLCD
- ld a, $90
- ld [hWY], a
- ld [rWY], a
- xor a
- ld [hTilesetType], a
- ld [hSCY], a
- dec a
- ld [wUpdateSpritesEnabled], a
- call Delay3
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- ld b, $70
- ld c, $90
- ld a, c
- ld [hSCX], a
- call DelayFrame
- ld a, %11100100 ; inverted palette for silhouette effect
- ld [rBGP], a
- ld [rOBP0], a
- ld [rOBP1], a
- .slideSilhouettesLoop ; slide silhouettes of the player's pic and the enemy's pic onto the screen
- ld h, b
- ld l, $40
- call SetScrollXForSlidingPlayerBodyLeft ; begin background scrolling on line $40
- inc b
- inc b
- ld h, $0
- ld l, $60
- call SetScrollXForSlidingPlayerBodyLeft ; end background scrolling on line $60
- call SlidePlayerHeadLeft
- ld a, c
- ld [hSCX], a
- dec c
- dec c
- jr nz, .slideSilhouettesLoop
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- ld a, $31
- ld [hStartTileID], a
- coord hl, 1, 5
- predef CopyUncompressedPicToTilemap
- xor a
- ld [hWY], a
- ld [rWY], a
- inc a
- ld [H_AUTOBGTRANSFERENABLED], a
- call Delay3
- ld b, SET_PAL_BATTLE
- call RunPaletteCommand
- call HideSprites
- jpab PrintBeginningBattleText
- ; when a battle is starting, silhouettes of the player's pic and the enemy's pic are slid onto the screen
- ; the lower of the player's pic (his body) is part of the background, but his head is a sprite
- ; the reason for this is that it shares Y coordinates with the lower part of the enemy pic, so background scrolling wouldn't work for both pics
- ; instead, the enemy pic is part of the background and uses the scroll register, while the player's head is a sprite and is slid by changing its X coordinates in a loop
- SlidePlayerHeadLeft:
- push bc
- ld hl, wOAMBuffer + $01
- ld c, $15 ; number of OAM entries
- ld de, $4 ; size of OAM entry
- .loop
- dec [hl] ; decrement X
- dec [hl] ; decrement X
- add hl, de ; next OAM entry
- dec c
- jr nz, .loop
- pop bc
- ret
- SetScrollXForSlidingPlayerBodyLeft:
- ld a, [rLY]
- cp l
- jr nz, SetScrollXForSlidingPlayerBodyLeft
- ld a, h
- ld [rSCX], a
- .loop
- ld a, [rLY]
- cp h
- jr z, .loop
- ret
- StartBattle:
- xor a
- ld [wPartyGainExpFlags], a
- ld [wPartyFoughtCurrentEnemyFlags], a
- ld [wActionResultOrTookBattleTurn], a
- inc a
- ld [wFirstMonsNotOutYet], a
- ld hl, wEnemyMon1HP
- ld bc, wEnemyMon2 - wEnemyMon1 - 1
- ld d, $3
- .findFirstAliveEnemyMonLoop
- inc d
- ld a, [hli]
- or [hl]
- jr nz, .foundFirstAliveEnemyMon
- add hl, bc
- jr .findFirstAliveEnemyMonLoop
- .foundFirstAliveEnemyMon
- ld a, d
- ld [wSerialExchangeNybbleReceiveData], a
- ld a, [wIsInBattle]
- dec a ; is it a trainer battle?
- call nz, EnemySendOutFirstMon ; if it is a trainer battle, send out enemy mon
- ld c, 40
- call DelayFrames
- call SaveScreenTilesToBuffer1
- .checkAnyPartyAlive
- call AnyPartyAlive
- ld a, d
- and a
- jp z, HandlePlayerBlackOut ; jump if no mon is alive
- call LoadScreenTilesFromBuffer1
- ld a, [wBattleType]
- and a ; is it a normal battle?
- jp z, .playerSendOutFirstMon ; if so, send out player mon
- ; safari zone battle
- .displaySafariZoneBattleMenu
- call DisplayBattleMenu
- ret c ; return if the player ran from battle
- ld a, [wActionResultOrTookBattleTurn]
- and a ; was the item used successfully?
- jr z, .displaySafariZoneBattleMenu ; if not, display the menu again; XXX does this ever jump?
- ld a, [wNumSafariBalls]
- and a
- jr nz, .notOutOfSafariBalls
- call LoadScreenTilesFromBuffer1
- ld hl, .outOfSafariBallsText
- jp PrintText
- .notOutOfSafariBalls
- callab PrintSafariZoneBattleText
- ld a, [wEnemyMonSpeed + 1]
- add a
- ld b, a ; init b (which is later compared with random value) to (enemy speed % 256) * 2
- jp c, EnemyRan ; if (enemy speed % 256) > 127, the enemy runs
- ld a, [wSafariBaitFactor]
- and a ; is bait factor 0?
- jr z, .checkEscapeFactor
- ; bait factor is not 0
- ; divide b by 4 (making the mon less likely to run)
- srl b
- srl b
- .checkEscapeFactor
- ld a, [wSafariEscapeFactor]
- and a ; is escape factor 0?
- jr z, .compareWithRandomValue
- ; escape factor is not 0
- ; multiply b by 2 (making the mon more likely to run)
- sla b
- jr nc, .compareWithRandomValue
- ; cap b at 255
- ld b, $ff
- .compareWithRandomValue
- call Random
- cp b
- jr nc, .checkAnyPartyAlive
- jr EnemyRan ; if b was greater than the random value, the enemy runs
- .outOfSafariBallsText
- TX_FAR _OutOfSafariBallsText
- db "@"
- .playerSendOutFirstMon
- xor a
- ld [wWhichPokemon], a
- .findFirstAliveMonLoop
- call HasMonFainted
- jr nz, .foundFirstAliveMon
- ; fainted, go to the next one
- ld hl, wWhichPokemon
- inc [hl]
- jr .findFirstAliveMonLoop
- .foundFirstAliveMon
- ld a, [wWhichPokemon]
- ld [wPlayerMonNumber], a
- inc a
- ld hl, wPartySpecies - 1
- ld c, a
- ld b, 0
- add hl, bc
- ld a, [hl] ; species
- ld [wcf91], a
- ld [wBattleMonSpecies2], a
- call LoadScreenTilesFromBuffer1
- coord hl, 1, 5
- ld a, $9
- call SlideTrainerPicOffScreen
- call SaveScreenTilesToBuffer1
- ld a, [wWhichPokemon]
- ld c, a
- ld b, FLAG_SET
- push bc
- ld hl, wPartyGainExpFlags
- predef FlagActionPredef
- ld hl, wPartyFoughtCurrentEnemyFlags
- pop bc
- predef FlagActionPredef
- call LoadBattleMonFromParty
- call LoadScreenTilesFromBuffer1
- call SendOutMon
- jr MainInBattleLoop
- ; wild mon or link battle enemy ran from battle
- EnemyRan:
- call LoadScreenTilesFromBuffer1
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- ld hl, WildRanText
- jr nz, .printText
- ; link battle
- xor a
- ld [wBattleResult], a
- ld hl, EnemyRanText
- .printText
- call PrintText
- ld a, SFX_RUN
- call PlaySoundWaitForCurrent
- xor a
- ld [H_WHOSETURN], a
- jpab AnimationSlideEnemyMonOff
- WildRanText:
- TX_FAR _WildRanText
- db "@"
- EnemyRanText:
- TX_FAR _EnemyRanText
- db "@"
- MainInBattleLoop:
- call ReadPlayerMonCurHPAndStatus
- ld hl, wBattleMonHP
- ld a, [hli]
- or [hl] ; is battle mon HP 0?
- jp z, HandlePlayerMonFainted ; if battle mon HP is 0, jump
- ld hl, wEnemyMonHP
- ld a, [hli]
- or [hl] ; is enemy mon HP 0?
- jp z, HandleEnemyMonFainted ; if enemy mon HP is 0, jump
- call SaveScreenTilesToBuffer1
- xor a
- ld [wFirstMonsNotOutYet], a
- ld a, [wPlayerBattleStatus2]
- and (1 << NEEDS_TO_RECHARGE) | (1 << USING_RAGE) ; check if the player is using Rage or needs to recharge
- jr nz, .selectEnemyMove
- ; the player is not using Rage and doesn't need to recharge
- ld hl, wEnemyBattleStatus1
- res FLINCHED, [hl] ; reset flinch bit
- ld hl, wPlayerBattleStatus1
- res FLINCHED, [hl] ; reset flinch bit
- ld a, [hl]
- and (1 << THRASHING_ABOUT) | (1 << CHARGING_UP) ; check if the player is thrashing about or charging for an attack
- jr nz, .selectEnemyMove ; if so, jump
- ; the player is neither thrashing about nor charging for an attack
- call DisplayBattleMenu ; show battle menu
- ret c ; return if player ran from battle
- ld a, [wEscapedFromBattle]
- and a
- ret nz ; return if pokedoll was used to escape from battle
- ld a, [wBattleMonStatus]
- and (1 << FRZ) | SLP ; is mon frozen or asleep?
- jr nz, .selectEnemyMove ; if so, jump
- ld a, [wPlayerBattleStatus1]
- and (1 << STORING_ENERGY) | (1 << USING_TRAPPING_MOVE) ; check player is using Bide or using a multi-turn attack like wrap
- jr nz, .selectEnemyMove ; if so, jump
- ld a, [wEnemyBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; check if enemy is using a multi-turn attack like wrap
- jr z, .selectPlayerMove ; if not, jump
- ; enemy is using a multi-turn attack like wrap, so player is trapped and cannot execute a move
- ld a, $ff
- ld [wPlayerSelectedMove], a
- jr .selectEnemyMove
- .selectPlayerMove
- ld a, [wActionResultOrTookBattleTurn]
- and a ; has the player already used the turn (e.g. by using an item, trying to run or switching pokemon)
- jr nz, .selectEnemyMove
- ld [wMoveMenuType], a
- inc a
- ld [wAnimationID], a
- xor a
- ld [wMenuItemToSwap], a
- call MoveSelectionMenu
- push af
- call LoadScreenTilesFromBuffer1
- call DrawHUDsAndHPBars
- pop af
- jr nz, MainInBattleLoop ; if the player didn't select a move, jump
- .selectEnemyMove
- call SelectEnemyMove
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .noLinkBattle
- ; link battle
- ld a, [wSerialExchangeNybbleReceiveData]
- cp LINKBATTLE_RUN
- jp z, EnemyRan
- cp LINKBATTLE_STRUGGLE
- jr z, .noLinkBattle
- cp LINKBATTLE_NO_ACTION
- jr z, .noLinkBattle
- sub 4
- jr c, .noLinkBattle
- ; the link battle enemy has switched mons
- ld a, [wPlayerBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; check if using multi-turn move like Wrap
- jr z, .specialMoveNotUsed
- ld a, [wPlayerMoveListIndex]
- ld hl, wBattleMonMoves
- ld c, a
- ld b, 0
- add hl, bc
- ld a, [hl]
- cp METRONOME ; a MIRROR MOVE check is missing, might lead to a desync in link battles
- ; when combined with multi-turn moves
- jr nz, .specialMoveNotUsed
- ld [wPlayerSelectedMove], a
- .specialMoveNotUsed
- callab SwitchEnemyMon
- .noLinkBattle
- ld a, [wPlayerSelectedMove]
- cp QUICK_ATTACK
- jr nz, .playerDidNotUseQuickAttack
- ld a, [wEnemySelectedMove]
- cp QUICK_ATTACK
- jr z, .compareSpeed ; if both used Quick Attack
- jp .playerMovesFirst ; if player used Quick Attack and enemy didn't
- .playerDidNotUseQuickAttack
- ld a, [wEnemySelectedMove]
- cp QUICK_ATTACK
- jr z, .enemyMovesFirst ; if enemy used Quick Attack and player didn't
- ld a, [wPlayerSelectedMove]
- cp COUNTER
- jr nz, .playerDidNotUseCounter
- ld a, [wEnemySelectedMove]
- cp COUNTER
- jr z, .compareSpeed ; if both used Counter
- jr .enemyMovesFirst ; if player used Counter and enemy didn't
- .playerDidNotUseCounter
- ld a, [wEnemySelectedMove]
- cp COUNTER
- jr z, .playerMovesFirst ; if enemy used Counter and player didn't
- .compareSpeed
- ld de, wBattleMonSpeed ; player speed value
- ld hl, wEnemyMonSpeed ; enemy speed value
- ld c, $2
- call StringCmp ; compare speed values
- jr z, .speedEqual
- jr nc, .playerMovesFirst ; if player is faster
- jr .enemyMovesFirst ; if enemy is faster
- .speedEqual ; 50/50 chance for both players
- ld a, [hSerialConnectionStatus]
- cp USING_INTERNAL_CLOCK
- jr z, .invertOutcome
- call BattleRandom
- cp $80
- jr c, .playerMovesFirst
- jr .enemyMovesFirst
- .invertOutcome
- call BattleRandom
- cp $80
- jr c, .enemyMovesFirst
- jr .playerMovesFirst
- .enemyMovesFirst
- ld a, $1
- ld [H_WHOSETURN], a
- callab TrainerAI
- jr c, .AIActionUsedEnemyFirst
- call ExecuteEnemyMove
- ld a, [wEscapedFromBattle]
- and a ; was Teleport, Road, or Whirlwind used to escape from battle?
- ret nz ; if so, return
- ld a, b
- and a
- jp z, HandlePlayerMonFainted
- .AIActionUsedEnemyFirst
- call HandlePoisonBurnLeechSeed
- jp z, HandleEnemyMonFainted
- call DrawHUDsAndHPBars
- call ExecutePlayerMove
- ld a, [wEscapedFromBattle]
- and a ; was Teleport, Road, or Whirlwind used to escape from battle?
- ret nz ; if so, return
- ld a, b
- and a
- jp z, HandleEnemyMonFainted
- call HandlePoisonBurnLeechSeed
- jp z, HandlePlayerMonFainted
- call DrawHUDsAndHPBars
- call CheckNumAttacksLeft
- jp MainInBattleLoop
- .playerMovesFirst
- call ExecutePlayerMove
- ld a, [wEscapedFromBattle]
- and a ; was Teleport, Road, or Whirlwind used to escape from battle?
- ret nz ; if so, return
- ld a, b
- and a
- jp z, HandleEnemyMonFainted
- call HandlePoisonBurnLeechSeed
- jp z, HandlePlayerMonFainted
- call DrawHUDsAndHPBars
- ld a, $1
- ld [H_WHOSETURN], a
- callab TrainerAI
- jr c, .AIActionUsedPlayerFirst
- call ExecuteEnemyMove
- ld a, [wEscapedFromBattle]
- and a ; was Teleport, Road, or Whirlwind used to escape from battle?
- ret nz ; if so, return
- ld a, b
- and a
- jp z, HandlePlayerMonFainted
- .AIActionUsedPlayerFirst
- call HandlePoisonBurnLeechSeed
- jp z, HandleEnemyMonFainted
- call DrawHUDsAndHPBars
- call CheckNumAttacksLeft
- jp MainInBattleLoop
- HandlePoisonBurnLeechSeed:
- ld hl, wBattleMonHP
- ld de, wBattleMonStatus
- ld a, [H_WHOSETURN]
- and a
- jr z, .playersTurn
- ld hl, wEnemyMonHP
- ld de, wEnemyMonStatus
- .playersTurn
- ld a, [de]
- and (1 << BRN) | (1 << PSN)
- jr z, .notBurnedOrPoisoned
- push hl
- ld hl, HurtByPoisonText
- ld a, [de]
- and 1 << BRN
- jr z, .poisoned
- ld hl, HurtByBurnText
- .poisoned
- call PrintText
- xor a
- ld [wAnimationType], a
- ld a, BURN_PSN_ANIM
- call PlayMoveAnimation ; play burn/poison animation
- pop hl
- call HandlePoisonBurnLeechSeed_DecreaseOwnHP
- .notBurnedOrPoisoned
- ld de, wPlayerBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .playersTurn2
- ld de, wEnemyBattleStatus2
- .playersTurn2
- ld a, [de]
- add a
- jr nc, .notLeechSeeded
- push hl
- ld a, [H_WHOSETURN]
- push af
- xor $1
- ld [H_WHOSETURN], a
- xor a
- ld [wAnimationType], a
- ld a, ABSORB
- call PlayMoveAnimation ; play leech seed animation (from opposing mon)
- pop af
- ld [H_WHOSETURN], a
- pop hl
- call HandlePoisonBurnLeechSeed_DecreaseOwnHP
- call HandlePoisonBurnLeechSeed_IncreaseEnemyHP
- push hl
- ld hl, HurtByLeechSeedText
- call PrintText
- pop hl
- .notLeechSeeded
- ld a, [hli]
- or [hl]
- ret nz ; test if fainted
- call DrawHUDsAndHPBars
- ld c, 20
- call DelayFrames
- xor a
- ret
- HurtByPoisonText:
- TX_FAR _HurtByPoisonText
- db "@"
- HurtByBurnText:
- TX_FAR _HurtByBurnText
- db "@"
- HurtByLeechSeedText:
- TX_FAR _HurtByLeechSeedText
- db "@"
- ; decreases the mon's current HP by 1/16 of the Max HP (multiplied by number of toxic ticks if active)
- ; note that the toxic ticks are considered even if the damage is not poison (hence the Leech Seed glitch)
- ; hl: HP pointer
- ; bc (out): total damage
- HandlePoisonBurnLeechSeed_DecreaseOwnHP:
- push hl
- push hl
- ld bc, $e ; skip to max HP
- add hl, bc
- ld a, [hli] ; load max HP
- ld [wHPBarMaxHP+1], a
- ld b, a
- ld a, [hl]
- ld [wHPBarMaxHP], a
- ld c, a
- srl b
- rr c
- srl b
- rr c
- srl c
- srl c ; c = max HP/16 (assumption: HP < 1024)
- ld a, c
- and a
- jr nz, .nonZeroDamage
- inc c ; damage is at least 1
- .nonZeroDamage
- ld hl, wPlayerBattleStatus3
- ld de, wPlayerToxicCounter
- ld a, [H_WHOSETURN]
- and a
- jr z, .playersTurn
- ld hl, wEnemyBattleStatus3
- ld de, wEnemyToxicCounter
- .playersTurn
- bit BADLY_POISONED, [hl]
- jr z, .noToxic
- ld a, [de] ; increment toxic counter
- inc a
- ld [de], a
- ld hl, $0000
- .toxicTicksLoop
- add hl, bc
- dec a
- jr nz, .toxicTicksLoop
- ld b, h ; bc = damage * toxic counter
- ld c, l
- .noToxic
- pop hl
- inc hl
- ld a, [hl] ; subtract total damage from current HP
- ld [wHPBarOldHP], a
- sub c
- ld [hld], a
- ld [wHPBarNewHP], a
- ld a, [hl]
- ld [wHPBarOldHP+1], a
- sbc b
- ld [hl], a
- ld [wHPBarNewHP+1], a
- jr nc, .noOverkill
- xor a ; overkill: zero HP
- ld [hli], a
- ld [hl], a
- ld [wHPBarNewHP], a
- ld [wHPBarNewHP+1], a
- .noOverkill
- call UpdateCurMonHPBar
- pop hl
- ret
- ; adds bc to enemy HP
- ; bc isn't updated if HP subtracted was capped to prevent overkill
- HandlePoisonBurnLeechSeed_IncreaseEnemyHP:
- push hl
- ld hl, wEnemyMonMaxHP
- ld a, [H_WHOSETURN]
- and a
- jr z, .playersTurn
- ld hl, wBattleMonMaxHP
- .playersTurn
- ld a, [hli]
- ld [wHPBarMaxHP+1], a
- ld a, [hl]
- ld [wHPBarMaxHP], a
- ld de, wBattleMonHP - wBattleMonMaxHP
- add hl, de ; skip back from max hp to current hp
- ld a, [hl]
- ld [wHPBarOldHP], a ; add bc to current HP
- add c
- ld [hld], a
- ld [wHPBarNewHP], a
- ld a, [hl]
- ld [wHPBarOldHP+1], a
- adc b
- ld [hli], a
- ld [wHPBarNewHP+1], a
- ld a, [wHPBarMaxHP]
- ld c, a
- ld a, [hld]
- sub c
- ld a, [wHPBarMaxHP+1]
- ld b, a
- ld a, [hl]
- sbc b
- jr c, .noOverfullHeal
- ld a, b ; overfull heal, set HP to max HP
- ld [hli], a
- ld [wHPBarNewHP+1], a
- ld a, c
- ld [hl], a
- ld [wHPBarNewHP], a
- .noOverfullHeal
- ld a, [H_WHOSETURN]
- xor $1
- ld [H_WHOSETURN], a
- call UpdateCurMonHPBar
- ld a, [H_WHOSETURN]
- xor $1
- ld [H_WHOSETURN], a
- pop hl
- ret
- UpdateCurMonHPBar:
- coord hl, 10, 9 ; tile pointer to player HP bar
- ld a, [H_WHOSETURN]
- and a
- ld a, $1
- jr z, .playersTurn
- coord hl, 2, 2 ; tile pointer to enemy HP bar
- xor a
- .playersTurn
- push bc
- ld [wHPBarType], a
- predef UpdateHPBar2
- pop bc
- ret
- CheckNumAttacksLeft:
- ld a, [wPlayerNumAttacksLeft]
- and a
- jr nz, .checkEnemy
- ; player has 0 attacks left
- ld hl, wPlayerBattleStatus1
- res USING_TRAPPING_MOVE, [hl] ; player not using multi-turn attack like wrap any more
- .checkEnemy
- ld a, [wEnemyNumAttacksLeft]
- and a
- ret nz
- ; enemy has 0 attacks left
- ld hl, wEnemyBattleStatus1
- res USING_TRAPPING_MOVE, [hl] ; enemy not using multi-turn attack like wrap any more
- ret
- HandleEnemyMonFainted:
- xor a
- ld [wInHandlePlayerMonFainted], a
- call FaintEnemyPokemon
- call AnyPartyAlive
- ld a, d
- and a
- jp z, HandlePlayerBlackOut ; if no party mons are alive, the player blacks out
- ld hl, wBattleMonHP
- ld a, [hli]
- or [hl] ; is battle mon HP zero?
- call nz, DrawPlayerHUDAndHPBar ; if battle mon HP is not zero, draw player HD and HP bar
- ld a, [wIsInBattle]
- dec a
- ret z ; return if it's a wild battle
- call AnyEnemyPokemonAliveCheck
- jp z, TrainerBattleVictory
- ld hl, wBattleMonHP
- ld a, [hli]
- or [hl] ; does battle mon have 0 HP?
- jr nz, .skipReplacingBattleMon ; if not, skip replacing battle mon
- call DoUseNextMonDialogue ; this call is useless in a trainer battle. it shouldn't be here
- ret c
- call ChooseNextMon
- .skipReplacingBattleMon
- ld a, $1
- ld [wActionResultOrTookBattleTurn], a
- call ReplaceFaintedEnemyMon
- jp z, EnemyRan
- xor a
- ld [wActionResultOrTookBattleTurn], a
- jp MainInBattleLoop
- FaintEnemyPokemon:
- call ReadPlayerMonCurHPAndStatus
- ld a, [wIsInBattle]
- dec a
- jr z, .wild
- ld a, [wEnemyMonPartyPos]
- ld hl, wEnemyMon1HP
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- xor a
- ld [hli], a
- ld [hl], a
- .wild
- ld hl, wPlayerBattleStatus1
- res ATTACKING_MULTIPLE_TIMES, [hl]
- ; Bug. This only zeroes the high byte of the player's accumulated damage,
- ; setting the accumulated damage to itself mod 256 instead of 0 as was probably
- ; intended. That alone is problematic, but this mistake has another more severe
- ; effect. This function's counterpart for when the player mon faints,
- ; RemoveFaintedPlayerMon, zeroes both the high byte and the low byte. In a link
- ; battle, the other player's Game Boy will call that function in response to
- ; the enemy mon (the player mon from the other side's perspective) fainting,
- ; and the states of the two Game Boys will go out of sync unless the damage
- ; was congruent to 0 modulo 256.
- xor a
- ld [wPlayerBideAccumulatedDamage], a
- ld hl, wEnemyStatsToDouble ; clear enemy statuses
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hl], a
- ld [wEnemyDisabledMove], a
- ld [wEnemyDisabledMoveNumber], a
- ld [wEnemyMonMinimized], a
- ld hl, wPlayerUsedMove
- ld [hli], a
- ld [hl], a
- coord hl, 12, 5
- coord de, 12, 6
- call SlideDownFaintedMonPic
- coord hl, 0, 0
- lb bc, 4, 11
- call ClearScreenArea
- ld a, [wIsInBattle]
- dec a
- jr z, .wild_win
- xor a
- ld [wFrequencyModifier], a
- ld [wTempoModifier], a
- ld a, SFX_FAINT_FALL
- call PlaySoundWaitForCurrent
- .sfxwait
- ld a, [wChannelSoundIDs + Ch4]
- cp SFX_FAINT_FALL
- jr z, .sfxwait
- ld a, SFX_FAINT_THUD
- call PlaySound
- call WaitForSoundToFinish
- jr .sfxplayed
- .wild_win
- call EndLowHealthAlarm
- ld a, MUSIC_DEFEATED_WILD_MON
- call PlayBattleVictoryMusic
- .sfxplayed
- ; bug: win sfx is played for wild battles before checking for player mon HP
- ; this can lead to odd scenarios where both player and enemy faint, as the win sfx plays yet the player never won the battle
- ld hl, wBattleMonHP
- ld a, [hli]
- or [hl]
- jr nz, .playermonnotfaint
- ld a, [wInHandlePlayerMonFainted]
- and a ; was this called by HandlePlayerMonFainted?
- jr nz, .playermonnotfaint ; if so, don't call RemoveFaintedPlayerMon twice
- call RemoveFaintedPlayerMon
- .playermonnotfaint
- call AnyPartyAlive
- ld a, d
- and a
- ret z
- ld hl, EnemyMonFaintedText
- call PrintText
- call PrintEmptyString
- call SaveScreenTilesToBuffer1
- xor a
- ld [wBattleResult], a
- ld b, EXP_ALL
- call IsItemInBag
- push af
- jr z, .giveExpToMonsThatFought ; if no exp all, then jump
- ; the player has exp all
- ; first, we halve the values that determine exp gain
- ; the enemy mon base stats are added to stat exp, so they are halved
- ; the base exp (which determines normal exp) is also halved
- ld hl, wEnemyMonBaseStats
- ld b, $7
- .halveExpDataLoop
- srl [hl]
- inc hl
- dec b
- jr nz, .halveExpDataLoop
- ; give exp (divided evenly) to the mons that actually fought in battle against the enemy mon that has fainted
- ; if exp all is in the bag, this will be only be half of the stat exp and normal exp, due to the above loop
- .giveExpToMonsThatFought
- xor a
- ld [wBoostExpByExpAll], a
- callab GainExperience
- pop af
- ret z ; return if no exp all
- ; the player has exp all
- ; now, set the gain exp flag for every party member
- ; half of the total stat exp and normal exp will divided evenly amongst every party member
- ld a, $1
- ld [wBoostExpByExpAll], a
- ld a, [wPartyCount]
- ld b, 0
- .gainExpFlagsLoop
- scf
- rl b
- dec a
- jr nz, .gainExpFlagsLoop
- ld a, b
- ld [wPartyGainExpFlags], a
- jpab GainExperience
- EnemyMonFaintedText:
- TX_FAR _EnemyMonFaintedText
- db "@"
- EndLowHealthAlarm:
- ; This function is called when the player has the won the battle. It turns off
- ; the low health alarm and prevents it from reactivating until the next battle.
- xor a
- ld [wLowHealthAlarm], a ; turn off low health alarm
- ld [wChannelSoundIDs + Ch4], a
- inc a
- ld [wLowHealthAlarmDisabled], a ; prevent it from reactivating
- ret
- AnyEnemyPokemonAliveCheck:
- ld a, [wEnemyPartyCount]
- ld b, a
- xor a
- ld hl, wEnemyMon1HP
- ld de, wEnemyMon2 - wEnemyMon1
- .nextPokemon
- or [hl]
- inc hl
- or [hl]
- dec hl
- add hl, de
- dec b
- jr nz, .nextPokemon
- and a
- ret
- ; stores whether enemy ran in Z flag
- ReplaceFaintedEnemyMon:
- ld hl, wEnemyHPBarColor
- ld e, $30
- call GetBattleHealthBarColor
- callab DrawEnemyPokeballs
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .notLinkBattle
- ; link battle
- call LinkBattleExchangeData
- ld a, [wSerialExchangeNybbleReceiveData]
- cp LINKBATTLE_RUN
- ret z
- call LoadScreenTilesFromBuffer1
- .notLinkBattle
- call EnemySendOut
- xor a
- ld [wEnemyMoveNum], a
- ld [wActionResultOrTookBattleTurn], a
- ld [wAILayer2Encouragement], a
- inc a ; reset Z flag
- ret
- TrainerBattleVictory:
- call EndLowHealthAlarm
- ld b, MUSIC_DEFEATED_GYM_LEADER
- ld a, [wGymLeaderNo]
- and a
- jr nz, .gymleader
- ld b, MUSIC_DEFEATED_TRAINER
- .gymleader
- ld a, [wTrainerClass]
- cp SONY3 ; final battle against rival
- jr nz, .notrival
- ld b, MUSIC_DEFEATED_GYM_LEADER
- ld hl, wFlags_D733
- set 1, [hl]
- .notrival
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- ld a, b
- call nz, PlayBattleVictoryMusic
- ld hl, TrainerDefeatedText
- call PrintText
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- ret z
- call ScrollTrainerPicAfterBattle
- ld c, 40
- call DelayFrames
- call PrintEndBattleText
- ; win money
- ld hl, MoneyForWinningText
- call PrintText
- ld de, wPlayerMoney + 2
- ld hl, wAmountMoneyWon + 2
- ld c, $3
- predef_jump AddBCDPredef
- MoneyForWinningText:
- TX_FAR _MoneyForWinningText
- db "@"
- TrainerDefeatedText:
- TX_FAR _TrainerDefeatedText
- db "@"
- PlayBattleVictoryMusic:
- push af
- ld a, $ff
- ld [wNewSoundID], a
- call PlaySoundWaitForCurrent
- ld c, BANK(Music_DefeatedTrainer)
- pop af
- call PlayMusic
- jp Delay3
- HandlePlayerMonFainted:
- ld a, 1
- ld [wInHandlePlayerMonFainted], a
- call RemoveFaintedPlayerMon
- call AnyPartyAlive ; test if any more mons are alive
- ld a, d
- and a
- jp z, HandlePlayerBlackOut
- ld hl, wEnemyMonHP
- ld a, [hli]
- or [hl] ; is enemy mon's HP 0?
- jr nz, .doUseNextMonDialogue ; if not, jump
- ; the enemy mon has 0 HP
- call FaintEnemyPokemon
- ld a, [wIsInBattle]
- dec a
- ret z ; if wild encounter, battle is over
- call AnyEnemyPokemonAliveCheck
- jp z, TrainerBattleVictory
- .doUseNextMonDialogue
- call DoUseNextMonDialogue
- ret c ; return if the player ran from battle
- call ChooseNextMon
- jp nz, MainInBattleLoop ; if the enemy mon has more than 0 HP, go back to battle loop
- ; the enemy mon has 0 HP
- ld a, $1
- ld [wActionResultOrTookBattleTurn], a
- call ReplaceFaintedEnemyMon
- jp z, EnemyRan ; if enemy ran from battle rather than sending out another mon, jump
- xor a
- ld [wActionResultOrTookBattleTurn], a
- jp MainInBattleLoop
- ; resets flags, slides mon's pic down, plays cry, and prints fainted message
- RemoveFaintedPlayerMon:
- ld a, [wPlayerMonNumber]
- ld c, a
- ld hl, wPartyGainExpFlags
- ld b, FLAG_RESET
- predef FlagActionPredef ; clear gain exp flag for fainted mon
- ld hl, wEnemyBattleStatus1
- res 2, [hl] ; reset "attacking multiple times" flag
- ld a, [wLowHealthAlarm]
- bit 7, a ; skip sound flag (red bar (?))
- jr z, .skipWaitForSound
- ld a, $ff
- ld [wLowHealthAlarm], a ;disable low health alarm
- call WaitForSoundToFinish
- .skipWaitForSound
- ; a is 0, so this zeroes the enemy's accumulated damage.
- ld hl, wEnemyBideAccumulatedDamage
- ld [hli], a
- ld [hl], a
- ld [wBattleMonStatus], a
- call ReadPlayerMonCurHPAndStatus
- coord hl, 9, 7
- lb bc, 5, 11
- call ClearScreenArea
- coord hl, 1, 10
- coord de, 1, 11
- call SlideDownFaintedMonPic
- ld a, $1
- ld [wBattleResult], a
- ; When the player mon and enemy mon faint at the same time and the fact that the
- ; enemy mon has fainted is detected first (e.g. when the player mon knocks out
- ; the enemy mon using a move with recoil and faints due to the recoil), don't
- ; play the player mon's cry or show the "[player mon] fainted!" message.
- ld a, [wInHandlePlayerMonFainted]
- and a ; was this called by HandleEnemyMonFainted?
- ret z ; if so, return
- ld a, [wBattleMonSpecies]
- call PlayCry
- ld hl, PlayerMonFaintedText
- jp PrintText
- PlayerMonFaintedText:
- TX_FAR _PlayerMonFaintedText
- db "@"
- ; asks if you want to use next mon
- ; stores whether you ran in C flag
- DoUseNextMonDialogue:
- call PrintEmptyString
- call SaveScreenTilesToBuffer1
- ld a, [wIsInBattle]
- and a
- dec a
- ret nz ; return if it's a trainer battle
- ld hl, UseNextMonText
- call PrintText
- .displayYesNoBox
- coord hl, 13, 9
- lb bc, 10, 14
- ld a, TWO_OPTION_MENU
- ld [wTextBoxID], a
- call DisplayTextBoxID
- ld a, [wMenuExitMethod]
- cp CHOSE_SECOND_ITEM ; did the player choose NO?
- jr z, .tryRunning ; if the player chose NO, try running
- and a ; reset carry
- ret
- .tryRunning
- ld a, [wCurrentMenuItem]
- and a
- jr z, .displayYesNoBox ; xxx when does this happen?
- ld hl, wPartyMon1Speed
- ld de, wEnemyMonSpeed
- jp TryRunningFromBattle
- UseNextMonText:
- TX_FAR _UseNextMonText
- db "@"
- ; choose next player mon to send out
- ; stores whether enemy mon has no HP left in Z flag
- ChooseNextMon:
- ld a, BATTLE_PARTY_MENU
- ld [wPartyMenuTypeOrMessageID], a
- call DisplayPartyMenu
- .checkIfMonChosen
- jr nc, .monChosen
- .goBackToPartyMenu
- call GoBackToPartyMenu
- jr .checkIfMonChosen
- .monChosen
- call HasMonFainted
- jr z, .goBackToPartyMenu ; if mon fainted, you have to choose another
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .notLinkBattle
- inc a
- ld [wActionResultOrTookBattleTurn], a
- call LinkBattleExchangeData
- .notLinkBattle
- xor a
- ld [wActionResultOrTookBattleTurn], a
- call ClearSprites
- ld a, [wWhichPokemon]
- ld [wPlayerMonNumber], a
- ld c, a
- ld hl, wPartyGainExpFlags
- ld b, FLAG_SET
- push bc
- predef FlagActionPredef
- pop bc
- ld hl, wPartyFoughtCurrentEnemyFlags
- predef FlagActionPredef
- call LoadBattleMonFromParty
- call GBPalWhiteOut
- call LoadHudTilePatterns
- call LoadScreenTilesFromBuffer1
- call RunDefaultPaletteCommand
- call GBPalNormal
- call SendOutMon
- ld hl, wEnemyMonHP
- ld a, [hli]
- or [hl]
- ret
- ; called when player is out of usable mons.
- ; prints appropriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
- HandlePlayerBlackOut:
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr z, .notSony1Battle
- ld a, [wCurOpponent]
- cp OPP_SONY1
- jr nz, .notSony1Battle
- coord hl, 0, 0 ; sony 1 battle
- lb bc, 8, 21
- call ClearScreenArea
- call ScrollTrainerPicAfterBattle
- ld c, 40
- call DelayFrames
- ld hl, Sony1WinText
- call PrintText
- ld a, [wCurMap]
- cp OAKS_LAB
- ret z ; starter battle in oak's lab: don't black out
- .notSony1Battle
- ld b, SET_PAL_BATTLE_BLACK
- call RunPaletteCommand
- ld hl, PlayerBlackedOutText2
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .noLinkBattle
- ld hl, LinkBattleLostText
- .noLinkBattle
- call PrintText
- ld a, [wd732]
- res 5, a
- ld [wd732], a
- call ClearScreen
- scf
- ret
- Sony1WinText:
- TX_FAR _Sony1WinText
- db "@"
- PlayerBlackedOutText2:
- TX_FAR _PlayerBlackedOutText2
- db "@"
- LinkBattleLostText:
- TX_FAR _LinkBattleLostText
- db "@"
- ; slides pic of fainted mon downwards until it disappears
- ; bug: when this is called, [H_AUTOBGTRANSFERENABLED] is non-zero, so there is screen tearing
- SlideDownFaintedMonPic:
- ld a, [wd730]
- push af
- set 6, a
- ld [wd730], a
- ld b, 7 ; number of times to slide
- .slideStepLoop ; each iteration, the mon is slid down one row
- push bc
- push de
- push hl
- ld b, 6 ; number of rows
- .rowLoop
- push bc
- push hl
- push de
- ld bc, $7
- call CopyData
- pop de
- pop hl
- ld bc, -SCREEN_WIDTH
- add hl, bc
- push hl
- ld h, d
- ld l, e
- add hl, bc
- ld d, h
- ld e, l
- pop hl
- pop bc
- dec b
- jr nz, .rowLoop
- ld bc, SCREEN_WIDTH
- add hl, bc
- ld de, SevenSpacesText
- call PlaceString
- ld c, 2
- call DelayFrames
- pop hl
- pop de
- pop bc
- dec b
- jr nz, .slideStepLoop
- pop af
- ld [wd730], a
- ret
- SevenSpacesText:
- db " @"
- ; slides the player or enemy trainer off screen
- ; a is the number of tiles to slide it horizontally (always 9 for the player trainer or 8 for the enemy trainer)
- ; if a is 8, the slide is to the right, else it is to the left
- ; bug: when this is called, [H_AUTOBGTRANSFERENABLED] is non-zero, so there is screen tearing
- SlideTrainerPicOffScreen:
- ld [hSlideAmount], a
- ld c, a
- .slideStepLoop ; each iteration, the trainer pic is slid one tile left/right
- push bc
- push hl
- ld b, 7 ; number of rows
- .rowLoop
- push hl
- ld a, [hSlideAmount]
- ld c, a
- .columnLoop
- ld a, [hSlideAmount]
- cp 8
- jr z, .slideRight
- .slideLeft ; slide player sprite off screen
- ld a, [hld]
- ld [hli], a
- inc hl
- jr .nextColumn
- .slideRight ; slide enemy trainer sprite off screen
- ld a, [hli]
- ld [hld], a
- dec hl
- .nextColumn
- dec c
- jr nz, .columnLoop
- pop hl
- ld de, 20
- add hl, de
- dec b
- jr nz, .rowLoop
- ld c, 2
- call DelayFrames
- pop hl
- pop bc
- dec c
- jr nz, .slideStepLoop
- ret
- ; send out a trainer's mon
- EnemySendOut:
- ld hl, wPartyGainExpFlags
- xor a
- ld [hl], a
- ld a, [wPlayerMonNumber]
- ld c, a
- ld b, FLAG_SET
- push bc
- predef FlagActionPredef
- ld hl, wPartyFoughtCurrentEnemyFlags
- xor a
- ld [hl], a
- pop bc
- predef FlagActionPredef
- ; don't change wPartyGainExpFlags or wPartyFoughtCurrentEnemyFlags
- EnemySendOutFirstMon:
- xor a
- ld hl, wEnemyStatsToDouble ; clear enemy statuses
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hl], a
- ld [wEnemyDisabledMove], a
- ld [wEnemyDisabledMoveNumber], a
- ld [wEnemyMonMinimized], a
- ld hl, wPlayerUsedMove
- ld [hli], a
- ld [hl], a
- dec a
- ld [wAICount], a
- ld hl, wPlayerBattleStatus1
- res 5, [hl]
- coord hl, 18, 0
- ld a, 8
- call SlideTrainerPicOffScreen
- call PrintEmptyString
- call SaveScreenTilesToBuffer1
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .next
- ld a, [wSerialExchangeNybbleReceiveData]
- sub 4
- ld [wWhichPokemon], a
- jr .next3
- .next
- ld b, $FF
- .next2
- inc b
- ld a, [wEnemyMonPartyPos]
- cp b
- jr z, .next2
- ld hl, wEnemyMon1
- ld a, b
- ld [wWhichPokemon], a
- push bc
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- pop bc
- inc hl
- ld a, [hli]
- ld c, a
- ld a, [hl]
- or c
- jr z, .next2
- .next3
- ld a, [wWhichPokemon]
- ld hl, wEnemyMon1Level
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- ld a, [hl]
- ld [wCurEnemyLVL], a
- ld a, [wWhichPokemon]
- inc a
- ld hl, wEnemyPartyCount
- ld c, a
- ld b, 0
- add hl, bc
- ld a, [hl]
- ld [wEnemyMonSpecies2], a
- ld [wcf91], a
- call LoadEnemyMonData
- ld hl, wEnemyMonHP
- ld a, [hli]
- ld [wLastSwitchInEnemyMonHP], a
- ld a, [hl]
- ld [wLastSwitchInEnemyMonHP + 1], a
- ld a, 1
- ld [wCurrentMenuItem], a
- ld a, [wFirstMonsNotOutYet]
- dec a
- jr z, .next4
- ld a, [wPartyCount]
- dec a
- jr z, .next4
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr z, .next4
- ld a, [wOptions]
- bit 6, a
- jr nz, .next4
- ld hl, TrainerAboutToUseText
- call PrintText
- coord hl, 0, 7
- lb bc, 8, 1
- ld a, TWO_OPTION_MENU
- ld [wTextBoxID], a
- call DisplayTextBoxID
- ld a, [wCurrentMenuItem]
- and a
- jr nz, .next4
- ld a, BATTLE_PARTY_MENU
- ld [wPartyMenuTypeOrMessageID], a
- call DisplayPartyMenu
- .next9
- ld a, 1
- ld [wCurrentMenuItem], a
- jr c, .next7
- ld hl, wPlayerMonNumber
- ld a, [wWhichPokemon]
- cp [hl]
- jr nz, .next6
- ld hl, AlreadyOutText
- call PrintText
- .next8
- call GoBackToPartyMenu
- jr .next9
- .next6
- call HasMonFainted
- jr z, .next8
- xor a
- ld [wCurrentMenuItem], a
- .next7
- call GBPalWhiteOut
- call LoadHudTilePatterns
- call LoadScreenTilesFromBuffer1
- .next4
- call ClearSprites
- coord hl, 0, 0
- lb bc, 4, 11
- call ClearScreenArea
- ld b, SET_PAL_BATTLE
- call RunPaletteCommand
- call GBPalNormal
- ld hl, TrainerSentOutText
- call PrintText
- ld a, [wEnemyMonSpecies2]
- ld [wcf91], a
- ld [wd0b5], a
- call GetMonHeader
- ld de, vFrontPic
- call LoadMonFrontSprite
- ld a, -$31
- ld [hStartTileID], a
- coord hl, 15, 6
- predef AnimateSendingOutMon
- ld a, [wEnemyMonSpecies2]
- call PlayCry
- call DrawEnemyHUDAndHPBar
- ld a, [wCurrentMenuItem]
- and a
- ret nz
- xor a
- ld [wPartyGainExpFlags], a
- ld [wPartyFoughtCurrentEnemyFlags], a
- call SaveScreenTilesToBuffer1
- jp SwitchPlayerMon
- TrainerAboutToUseText:
- TX_FAR _TrainerAboutToUseText
- db "@"
- TrainerSentOutText:
- TX_FAR _TrainerSentOutText
- db "@"
- ; tests if the player has any pokemon that are not fainted
- ; sets d = 0 if all fainted, d != 0 if some mons are still alive
- AnyPartyAlive:
- ld a, [wPartyCount]
- ld e, a
- xor a
- ld hl, wPartyMon1HP
- ld bc, wPartyMon2 - wPartyMon1 - 1
- .partyMonsLoop
- or [hl]
- inc hl
- or [hl]
- add hl, bc
- dec e
- jr nz, .partyMonsLoop
- ld d, a
- ret
- ; tests if player mon has fainted
- ; stores whether mon has fainted in Z flag
- HasMonFainted:
- ld a, [wWhichPokemon]
- ld hl, wPartyMon1HP
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- ld a, [hli]
- or [hl]
- ret nz
- ld a, [wFirstMonsNotOutYet]
- and a
- jr nz, .done
- ld hl, NoWillText
- call PrintText
- .done
- xor a
- ret
- NoWillText:
- TX_FAR _NoWillText
- db "@"
- ; try to run from battle (hl = player speed, de = enemy speed)
- ; stores whether the attempt was successful in carry flag
- TryRunningFromBattle:
- call IsGhostBattle
- jp z, .canEscape ; jump if it's a ghost battle
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jp z, .canEscape ; jump if it's a safari battle
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jp z, .canEscape
- ld a, [wIsInBattle]
- dec a
- jr nz, .trainerBattle ; jump if it's a trainer battle
- ld a, [wNumRunAttempts]
- inc a
- ld [wNumRunAttempts], a
- ld a, [hli]
- ld [H_MULTIPLICAND + 1], a
- ld a, [hl]
- ld [H_MULTIPLICAND + 2], a
- ld a, [de]
- ld [hEnemySpeed], a
- inc de
- ld a, [de]
- ld [hEnemySpeed + 1], a
- call LoadScreenTilesFromBuffer1
- ld de, H_MULTIPLICAND + 1
- ld hl, hEnemySpeed
- ld c, 2
- call StringCmp
- jr nc, .canEscape ; jump if player speed greater than enemy speed
- xor a
- ld [H_MULTIPLICAND], a
- ld a, 32
- ld [H_MULTIPLIER], a
- call Multiply ; multiply player speed by 32
- ld a, [H_PRODUCT + 2]
- ld [H_DIVIDEND], a
- ld a, [H_PRODUCT + 3]
- ld [H_DIVIDEND + 1], a
- ld a, [hEnemySpeed]
- ld b, a
- ld a, [hEnemySpeed + 1]
- ; divide enemy speed by 4
- srl b
- rr a
- srl b
- rr a
- and a
- jr z, .canEscape ; jump if enemy speed divided by 4, mod 256 is 0
- ld [H_DIVISOR], a ; ((enemy speed / 4) % 256)
- ld b, $2
- call Divide ; divide (player speed * 32) by ((enemy speed / 4) % 256)
- ld a, [H_QUOTIENT + 2]
- and a ; is the quotient greater than 256?
- jr nz, .canEscape ; if so, the player can escape
- ld a, [wNumRunAttempts]
- ld c, a
- ; add 30 to the quotient for each run attempt
- .loop
- dec c
- jr z, .compareWithRandomValue
- ld b, 30
- ld a, [H_QUOTIENT + 3]
- add b
- ld [H_QUOTIENT + 3], a
- jr c, .canEscape
- jr .loop
- .compareWithRandomValue
- call BattleRandom
- ld b, a
- ld a, [H_QUOTIENT + 3]
- cp b
- jr nc, .canEscape ; if the random value was less than or equal to the quotient
- ; plus 30 times the number of attempts, the player can escape
- ; can't escape
- ld a, $1
- ld [wActionResultOrTookBattleTurn], a ; you lose your turn when you can't escape
- ld hl, CantEscapeText
- jr .printCantEscapeOrNoRunningText
- .trainerBattle
- ld hl, NoRunningText
- .printCantEscapeOrNoRunningText
- call PrintText
- ld a, 1
- ld [wForcePlayerToChooseMon], a
- call SaveScreenTilesToBuffer1
- and a ; reset carry
- ret
- .canEscape
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- ld a, $2
- jr nz, .playSound
- ; link battle
- call SaveScreenTilesToBuffer1
- xor a
- ld [wActionResultOrTookBattleTurn], a
- ld a, LINKBATTLE_RUN
- ld [wPlayerMoveListIndex], a
- call LinkBattleExchangeData
- call LoadScreenTilesFromBuffer1
- ld a, [wSerialExchangeNybbleReceiveData]
- cp LINKBATTLE_RUN
- ld a, $2
- jr z, .playSound
- dec a
- .playSound
- ld [wBattleResult], a
- ld a, SFX_RUN
- call PlaySoundWaitForCurrent
- ld hl, GotAwayText
- call PrintText
- call WaitForSoundToFinish
- call SaveScreenTilesToBuffer1
- scf ; set carry
- ret
- CantEscapeText:
- TX_FAR _CantEscapeText
- db "@"
- NoRunningText:
- TX_FAR _NoRunningText
- db "@"
- GotAwayText:
- TX_FAR _GotAwayText
- db "@"
- ; copies from party data to battle mon data when sending out a new player mon
- LoadBattleMonFromParty:
- ld a, [wWhichPokemon]
- ld bc, wPartyMon2 - wPartyMon1
- ld hl, wPartyMon1Species
- call AddNTimes
- ld de, wBattleMonSpecies
- ld bc, wBattleMonDVs - wBattleMonSpecies
- call CopyData
- ld bc, wPartyMon1DVs - wPartyMon1OTID
- add hl, bc
- ld de, wBattleMonDVs
- ld bc, NUM_DVS
- call CopyData
- ld de, wBattleMonPP
- ld bc, NUM_MOVES
- call CopyData
- ld de, wBattleMonLevel
- ld bc, wBattleMonPP - wBattleMonLevel
- call CopyData
- ld a, [wBattleMonSpecies2]
- ld [wd0b5], a
- call GetMonHeader
- ld hl, wPartyMonNicks
- ld a, [wPlayerMonNumber]
- call SkipFixedLengthTextEntries
- ld de, wBattleMonNick
- ld bc, NAME_LENGTH
- call CopyData
- ld hl, wBattleMonLevel
- ld de, wPlayerMonUnmodifiedLevel ; block of memory used for unmodified stats
- ld bc, 1 + NUM_STATS * 2
- call CopyData
- call ApplyBurnAndParalysisPenaltiesToPlayer
- call ApplyBadgeStatBoosts
- ld a, $7 ; default stat modifier
- ld b, NUM_STAT_MODS
- ld hl, wPlayerMonAttackMod
- .statModLoop
- ld [hli], a
- dec b
- jr nz, .statModLoop
- ret
- ; copies from enemy party data to current enemy mon data when sending out a new enemy mon
- LoadEnemyMonFromParty:
- ld a, [wWhichPokemon]
- ld bc, wEnemyMon2 - wEnemyMon1
- ld hl, wEnemyMons
- call AddNTimes
- ld de, wEnemyMonSpecies
- ld bc, wEnemyMonDVs - wEnemyMonSpecies
- call CopyData
- ld bc, wEnemyMon1DVs - wEnemyMon1OTID
- add hl, bc
- ld de, wEnemyMonDVs
- ld bc, NUM_DVS
- call CopyData
- ld de, wEnemyMonPP
- ld bc, NUM_MOVES
- call CopyData
- ld de, wEnemyMonLevel
- ld bc, wEnemyMonPP - wEnemyMonLevel
- call CopyData
- ld a, [wEnemyMonSpecies]
- ld [wd0b5], a
- call GetMonHeader
- ld hl, wEnemyMonNicks
- ld a, [wWhichPokemon]
- call SkipFixedLengthTextEntries
- ld de, wEnemyMonNick
- ld bc, NAME_LENGTH
- call CopyData
- ld hl, wEnemyMonLevel
- ld de, wEnemyMonUnmodifiedLevel ; block of memory used for unmodified stats
- ld bc, 1 + NUM_STATS * 2
- call CopyData
- call ApplyBurnAndParalysisPenaltiesToEnemy
- ld hl, wMonHBaseStats
- ld de, wEnemyMonBaseStats
- ld b, NUM_STATS
- .copyBaseStatsLoop
- ld a, [hli]
- ld [de], a
- inc de
- dec b
- jr nz, .copyBaseStatsLoop
- ld a, $7 ; default stat modifier
- ld b, NUM_STAT_MODS
- ld hl, wEnemyMonStatMods
- .statModLoop
- ld [hli], a
- dec b
- jr nz, .statModLoop
- ld a, [wWhichPokemon]
- ld [wEnemyMonPartyPos], a
- ret
- SendOutMon:
- callab PrintSendOutMonMessage
- ld hl, wEnemyMonHP
- ld a, [hli]
- or [hl] ; is enemy mon HP zero?
- jp z, .skipDrawingEnemyHUDAndHPBar; if HP is zero, skip drawing the HUD and HP bar
- call DrawEnemyHUDAndHPBar
- .skipDrawingEnemyHUDAndHPBar
- call DrawPlayerHUDAndHPBar
- predef LoadMonBackPic
- xor a
- ld [hStartTileID], a
- ld hl, wBattleAndStartSavedMenuItem
- ld [hli], a
- ld [hl], a
- ld [wBoostExpByExpAll], a
- ld [wDamageMultipliers], a
- ld [wPlayerMoveNum], a
- ld hl, wPlayerUsedMove
- ld [hli], a
- ld [hl], a
- ld hl, wPlayerStatsToDouble
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hl], a
- ld [wPlayerDisabledMove], a
- ld [wPlayerDisabledMoveNumber], a
- ld [wPlayerMonMinimized], a
- ld b, SET_PAL_BATTLE
- call RunPaletteCommand
- ld hl, wEnemyBattleStatus1
- res USING_TRAPPING_MOVE, [hl]
- ld a, $1
- ld [H_WHOSETURN], a
- ld a, POOF_ANIM
- call PlayMoveAnimation
- coord hl, 4, 11
- predef AnimateSendingOutMon
- ld a, [wcf91]
- call PlayCry
- call PrintEmptyString
- jp SaveScreenTilesToBuffer1
- ; show 2 stages of the player mon getting smaller before disappearing
- AnimateRetreatingPlayerMon:
- coord hl, 1, 5
- lb bc, 7, 7
- call ClearScreenArea
- coord hl, 3, 7
- lb bc, 5, 5
- xor a
- ld [wDownscaledMonSize], a
- ld [hBaseTileID], a
- predef CopyDownscaledMonTiles
- ld c, 4
- call DelayFrames
- call .clearScreenArea
- coord hl, 4, 9
- lb bc, 3, 3
- ld a, 1
- ld [wDownscaledMonSize], a
- xor a
- ld [hBaseTileID], a
- predef CopyDownscaledMonTiles
- call Delay3
- call .clearScreenArea
- ld a, $4c
- Coorda 5, 11
- .clearScreenArea
- coord hl, 1, 5
- lb bc, 7, 7
- jp ClearScreenArea
- ; reads player's current mon's HP into wBattleMonHP
- ReadPlayerMonCurHPAndStatus:
- ld a, [wPlayerMonNumber]
- ld hl, wPartyMon1HP
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- ld d, h
- ld e, l
- ld hl, wBattleMonHP
- ld bc, $4 ; 2 bytes HP, 1 byte unknown (unused?), 1 byte status
- jp CopyData
- DrawHUDsAndHPBars:
- call DrawPlayerHUDAndHPBar
- jp DrawEnemyHUDAndHPBar
- DrawPlayerHUDAndHPBar:
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- coord hl, 9, 7
- lb bc, 5, 11
- call ClearScreenArea
- callab PlacePlayerHUDTiles
- coord hl, 18, 9
- ld [hl], $73
- ld de, wBattleMonNick
- coord hl, 10, 7
- call CenterMonName
- call PlaceString
- ld hl, wBattleMonSpecies
- ld de, wLoadedMon
- ld bc, wBattleMonDVs - wBattleMonSpecies
- call CopyData
- ld hl, wBattleMonLevel
- ld de, wLoadedMonLevel
- ld bc, wBattleMonPP - wBattleMonLevel
- call CopyData
- coord hl, 14, 8
- push hl
- inc hl
- ld de, wLoadedMonStatus
- call PrintStatusConditionNotFainted
- pop hl
- jr nz, .doNotPrintLevel
- call PrintLevel
- .doNotPrintLevel
- ld a, [wLoadedMonSpecies]
- ld [wcf91], a
- coord hl, 10, 9
- predef DrawHP
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- ld hl, wPlayerHPBarColor
- call GetBattleHealthBarColor
- ld hl, wBattleMonHP
- ld a, [hli]
- or [hl]
- jr z, .fainted
- ld a, [wLowHealthAlarmDisabled]
- and a ; has the alarm been disabled because the player has already won?
- ret nz ; if so, return
- ld a, [wPlayerHPBarColor]
- cp HP_BAR_RED
- jr z, .setLowHealthAlarm
- .fainted
- ld hl, wLowHealthAlarm
- bit 7, [hl] ;low health alarm enabled?
- ld [hl], $0
- ret z
- xor a
- ld [wChannelSoundIDs + Ch4], a
- ret
- .setLowHealthAlarm
- ld hl, wLowHealthAlarm
- set 7, [hl] ;enable low health alarm
- ret
- DrawEnemyHUDAndHPBar:
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- coord hl, 0, 0
- lb bc, 4, 12
- call ClearScreenArea
- callab PlaceEnemyHUDTiles
- ld de, wEnemyMonNick
- coord hl, 1, 0
- call CenterMonName
- call PlaceString
- coord hl, 4, 1
- push hl
- inc hl
- ld de, wEnemyMonStatus
- call PrintStatusConditionNotFainted
- pop hl
- jr nz, .skipPrintLevel ; if the mon has a status condition, skip printing the level
- ld a, [wEnemyMonLevel]
- ld [wLoadedMonLevel], a
- call PrintLevel
- .skipPrintLevel
- ld hl, wEnemyMonHP
- ld a, [hli]
- ld [H_MULTIPLICAND + 1], a
- ld a, [hld]
- ld [H_MULTIPLICAND + 2], a
- or [hl] ; is current HP zero?
- jr nz, .hpNonzero
- ; current HP is 0
- ; set variables for DrawHPBar
- ld c, a
- ld e, a
- ld d, $6
- jp .drawHPBar
- .hpNonzero
- xor a
- ld [H_MULTIPLICAND], a
- ld a, 48
- ld [H_MULTIPLIER], a
- call Multiply ; multiply current HP by 48
- ld hl, wEnemyMonMaxHP
- ld a, [hli]
- ld b, a
- ld a, [hl]
- ld [H_DIVISOR], a
- ld a, b
- and a ; is max HP > 255?
- jr z, .doDivide
- ; if max HP > 255, scale both (current HP * 48) and max HP by dividing by 4 so that max HP fits in one byte
- ; (it needs to be one byte so it can be used as the divisor for the Divide function)
- ld a, [H_DIVISOR]
- srl b
- rr a
- srl b
- rr a
- ld [H_DIVISOR], a
- ld a, [H_PRODUCT + 2]
- ld b, a
- srl b
- ld a, [H_PRODUCT + 3]
- rr a
- srl b
- rr a
- ld [H_PRODUCT + 3], a
- ld a, b
- ld [H_PRODUCT + 2], a
- .doDivide
- ld a, [H_PRODUCT + 2]
- ld [H_DIVIDEND], a
- ld a, [H_PRODUCT + 3]
- ld [H_DIVIDEND + 1], a
- ld a, $2
- ld b, a
- call Divide ; divide (current HP * 48) by max HP
- ld a, [H_QUOTIENT + 3]
- ; set variables for DrawHPBar
- ld e, a
- ld a, $6
- ld d, a
- ld c, a
- .drawHPBar
- xor a
- ld [wHPBarType], a
- coord hl, 2, 2
- call DrawHPBar
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- ld hl, wEnemyHPBarColor
- GetBattleHealthBarColor:
- ld b, [hl]
- call GetHealthBarColor
- ld a, [hl]
- cp b
- ret z
- ld b, SET_PAL_BATTLE
- jp RunPaletteCommand
- ; center's mon's name on the battle screen
- ; if the name is 1 or 2 letters long, it is printed 2 spaces more to the right than usual
- ; (i.e. for names longer than 4 letters)
- ; if the name is 3 or 4 letters long, it is printed 1 space more to the right than usual
- ; (i.e. for names longer than 4 letters)
- CenterMonName:
- push de
- inc hl
- inc hl
- ld b, $2
- .loop
- inc de
- ld a, [de]
- cp "@"
- jr z, .done
- inc de
- ld a, [de]
- cp "@"
- jr z, .done
- dec hl
- dec b
- jr nz, .loop
- .done
- pop de
- ret
- DisplayBattleMenu:
- call LoadScreenTilesFromBuffer1 ; restore saved screen
- ld a, [wBattleType]
- and a
- jr nz, .nonstandardbattle
- call DrawHUDsAndHPBars
- call PrintEmptyString
- call SaveScreenTilesToBuffer1
- .nonstandardbattle
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- ld a, BATTLE_MENU_TEMPLATE
- jr nz, .menuselected
- ld a, SAFARI_BATTLE_MENU_TEMPLATE
- .menuselected
- ld [wTextBoxID], a
- call DisplayTextBoxID
- ld a, [wBattleType]
- dec a
- jp nz, .handleBattleMenuInput ; handle menu input if it's not the old man tutorial
- ; the following happens for the old man tutorial
- ld hl, wPlayerName
- ld de, wGrassRate
- ld bc, NAME_LENGTH
- call CopyData ; temporarily save the player name in unused space,
- ; which is supposed to get overwritten when entering a
- ; map with wild Pokémon. Due to an oversight, the data
- ; may not get overwritten (cinnabar) and the infamous
- ; Missingno. glitch can show up.
- ld hl, .oldManName
- ld de, wPlayerName
- ld bc, NAME_LENGTH
- call CopyData
- ; the following simulates the keystrokes by drawing menus on screen
- coord hl, 9, 14
- ld [hl], "▶"
- ld c, 80
- call DelayFrames
- ld [hl], " "
- coord hl, 9, 16
- ld [hl], "▶"
- ld c, 50
- call DelayFrames
- ld [hl], "▷"
- ld a, $2 ; select the "ITEM" menu
- jp .upperLeftMenuItemWasNotSelected
- .oldManName
- db "OLD MAN@"
- .handleBattleMenuInput
- ld a, [wBattleAndStartSavedMenuItem]
- ld [wCurrentMenuItem], a
- ld [wLastMenuItem], a
- sub 2 ; check if the cursor is in the left column
- jr c, .leftColumn
- ; cursor is in the right column
- ld [wCurrentMenuItem], a
- ld [wLastMenuItem], a
- jr .rightColumn
- .leftColumn ; put cursor in left column of menu
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- ld a, " "
- jr z, .safariLeftColumn
- ; put cursor in left column for normal battle menu (i.e. when it's not a Safari battle)
- Coorda 15, 14 ; clear upper cursor position in right column
- Coorda 15, 16 ; clear lower cursor position in right column
- ld b, $9 ; top menu item X
- jr .leftColumn_WaitForInput
- .safariLeftColumn
- Coorda 13, 14
- Coorda 13, 16
- coord hl, 7, 14
- ld de, wNumSafariBalls
- lb bc, 1, 2
- call PrintNumber
- ld b, $1 ; top menu item X
- .leftColumn_WaitForInput
- ld hl, wTopMenuItemY
- ld a, $e
- ld [hli], a ; wTopMenuItemY
- ld a, b
- ld [hli], a ; wTopMenuItemX
- inc hl
- inc hl
- ld a, $1
- ld [hli], a ; wMaxMenuItem
- ld [hl], D_RIGHT | A_BUTTON ; wMenuWatchedKeys
- call HandleMenuInput
- bit 4, a ; check if right was pressed
- jr nz, .rightColumn
- jr .AButtonPressed ; the A button was pressed
- .rightColumn ; put cursor in right column of menu
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- ld a, " "
- jr z, .safariRightColumn
- ; put cursor in right column for normal battle menu (i.e. when it's not a Safari battle)
- Coorda 9, 14 ; clear upper cursor position in left column
- Coorda 9, 16 ; clear lower cursor position in left column
- ld b, $f ; top menu item X
- jr .rightColumn_WaitForInput
- .safariRightColumn
- Coorda 1, 14 ; clear upper cursor position in left column
- Coorda 1, 16 ; clear lower cursor position in left column
- coord hl, 7, 14
- ld de, wNumSafariBalls
- lb bc, 1, 2
- call PrintNumber
- ld b, $d ; top menu item X
- .rightColumn_WaitForInput
- ld hl, wTopMenuItemY
- ld a, $e
- ld [hli], a ; wTopMenuItemY
- ld a, b
- ld [hli], a ; wTopMenuItemX
- inc hl
- inc hl
- ld a, $1
- ld [hli], a ; wMaxMenuItem
- ld a, D_LEFT | A_BUTTON
- ld [hli], a ; wMenuWatchedKeys
- call HandleMenuInput
- bit 5, a ; check if left was pressed
- jr nz, .leftColumn ; if left was pressed, jump
- ld a, [wCurrentMenuItem]
- add $2 ; if we're in the right column, the actual id is +2
- ld [wCurrentMenuItem], a
- .AButtonPressed
- call PlaceUnfilledArrowMenuCursor
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- ld a, [wCurrentMenuItem]
- ld [wBattleAndStartSavedMenuItem], a
- jr z, .handleMenuSelection
- ; not Safari battle
- ; swap the IDs of the item menu and party menu (this is probably because they swapped the positions
- ; of these menu items in first generation English versions)
- cp $1 ; was the item menu selected?
- jr nz, .notItemMenu
- ; item menu was selected
- inc a ; increment a to 2
- jr .handleMenuSelection
- .notItemMenu
- cp $2 ; was the party menu selected?
- jr nz, .handleMenuSelection
- ; party menu selected
- dec a ; decrement a to 1
- .handleMenuSelection
- and a
- jr nz, .upperLeftMenuItemWasNotSelected
- ; the upper left menu item was selected
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jr z, .throwSafariBallWasSelected
- ; the "FIGHT" menu was selected
- xor a
- ld [wNumRunAttempts], a
- jp LoadScreenTilesFromBuffer1 ; restore saved screen and return
- .throwSafariBallWasSelected
- ld a, SAFARI_BALL
- ld [wcf91], a
- jr UseBagItem
- .upperLeftMenuItemWasNotSelected ; a menu item other than the upper left item was selected
- cp $2
- jp nz, PartyMenuOrRockOrRun
- ; either the bag (normal battle) or bait (safari battle) was selected
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .notLinkBattle
- ; can't use items in link battles
- ld hl, ItemsCantBeUsedHereText
- call PrintText
- jp DisplayBattleMenu
- .notLinkBattle
- call SaveScreenTilesToBuffer2
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jr nz, BagWasSelected
- ; bait was selected
- ld a, SAFARI_BAIT
- ld [wcf91], a
- jr UseBagItem
- BagWasSelected:
- call LoadScreenTilesFromBuffer1
- ld a, [wBattleType]
- and a ; is it a normal battle?
- jr nz, .next
- ; normal battle
- call DrawHUDsAndHPBars
- .next
- ld a, [wBattleType]
- dec a ; is it the old man tutorial?
- jr nz, DisplayPlayerBag ; no, it is a normal battle
- ld hl, OldManItemList
- ld a, l
- ld [wListPointer], a
- ld a, h
- ld [wListPointer + 1], a
- jr DisplayBagMenu
- OldManItemList:
- db 1 ; # items
- db POKE_BALL, 50
- db -1
- DisplayPlayerBag:
- ; get the pointer to player's bag when in a normal battle
- ld hl, wNumBagItems
- ld a, l
- ld [wListPointer], a
- ld a, h
- ld [wListPointer + 1], a
- DisplayBagMenu:
- xor a
- ld [wPrintItemPrices], a
- ld a, ITEMLISTMENU
- ld [wListMenuID], a
- ld a, [wBagSavedMenuItem]
- ld [wCurrentMenuItem], a
- call DisplayListMenuID
- ld a, [wCurrentMenuItem]
- ld [wBagSavedMenuItem], a
- ld a, $0
- ld [wMenuWatchMovingOutOfBounds], a
- ld [wMenuItemToSwap], a
- jp c, DisplayBattleMenu ; go back to battle menu if an item was not selected
- UseBagItem:
- ; either use an item from the bag or use a safari zone item
- ld a, [wcf91]
- ld [wd11e], a
- call GetItemName
- call CopyStringToCF4B ; copy name
- xor a
- ld [wPseudoItemID], a
- call UseItem
- call LoadHudTilePatterns
- call ClearSprites
- xor a
- ld [wCurrentMenuItem], a
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jr z, .checkIfMonCaptured
- ld a, [wActionResultOrTookBattleTurn]
- and a ; was the item used successfully?
- jp z, BagWasSelected ; if not, go back to the bag menu
- ld a, [wPlayerBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; is the player using a multi-turn move like wrap?
- jr z, .checkIfMonCaptured
- ld hl, wPlayerNumAttacksLeft
- dec [hl]
- jr nz, .checkIfMonCaptured
- ld hl, wPlayerBattleStatus1
- res USING_TRAPPING_MOVE, [hl] ; not using multi-turn move any more
- .checkIfMonCaptured
- ld a, [wCapturedMonSpecies]
- and a ; was the enemy mon captured with a ball?
- jr nz, .returnAfterCapturingMon
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jr z, .returnAfterUsingItem_NoCapture
- ; not a safari battle
- call LoadScreenTilesFromBuffer1
- call DrawHUDsAndHPBars
- call Delay3
- .returnAfterUsingItem_NoCapture
- call GBPalNormal
- and a ; reset carry
- ret
- .returnAfterCapturingMon
- call GBPalNormal
- xor a
- ld [wCapturedMonSpecies], a
- ld a, $2
- ld [wBattleResult], a
- scf ; set carry
- ret
- ItemsCantBeUsedHereText:
- TX_FAR _ItemsCantBeUsedHereText
- db "@"
- PartyMenuOrRockOrRun:
- dec a ; was Run selected?
- jp nz, BattleMenu_RunWasSelected
- ; party menu or rock was selected
- call SaveScreenTilesToBuffer2
- ld a, [wBattleType]
- cp BATTLE_TYPE_SAFARI
- jr nz, .partyMenuWasSelected
- ; safari battle
- ld a, SAFARI_ROCK
- ld [wcf91], a
- jp UseBagItem
- .partyMenuWasSelected
- call LoadScreenTilesFromBuffer1
- xor a ; NORMAL_PARTY_MENU
- ld [wPartyMenuTypeOrMessageID], a
- ld [wMenuItemToSwap], a
- call DisplayPartyMenu
- .checkIfPartyMonWasSelected
- jp nc, .partyMonWasSelected ; if a party mon was selected, jump, else we quit the party menu
- .quitPartyMenu
- call ClearSprites
- call GBPalWhiteOut
- call LoadHudTilePatterns
- call LoadScreenTilesFromBuffer2
- call RunDefaultPaletteCommand
- call GBPalNormal
- jp DisplayBattleMenu
- .partyMonDeselected
- coord hl, 11, 11
- ld bc, 6 * SCREEN_WIDTH + 9
- ld a, " "
- call FillMemory
- xor a ; NORMAL_PARTY_MENU
- ld [wPartyMenuTypeOrMessageID], a
- call GoBackToPartyMenu
- jr .checkIfPartyMonWasSelected
- .partyMonWasSelected
- ld a, SWITCH_STATS_CANCEL_MENU_TEMPLATE
- ld [wTextBoxID], a
- call DisplayTextBoxID
- ld hl, wTopMenuItemY
- ld a, $c
- ld [hli], a ; wTopMenuItemY
- ld [hli], a ; wTopMenuItemX
- xor a
- ld [hli], a ; wCurrentMenuItem
- inc hl
- ld a, $2
- ld [hli], a ; wMaxMenuItem
- ld a, B_BUTTON | A_BUTTON
- ld [hli], a ; wMenuWatchedKeys
- xor a
- ld [hl], a ; wLastMenuItem
- call HandleMenuInput
- bit 1, a ; was A pressed?
- jr nz, .partyMonDeselected ; if B was pressed, jump
- ; A was pressed
- call PlaceUnfilledArrowMenuCursor
- ld a, [wCurrentMenuItem]
- cp $2 ; was Cancel selected?
- jr z, .quitPartyMenu ; if so, quit the party menu entirely
- and a ; was Switch selected?
- jr z, .switchMon ; if so, jump
- ; Stats was selected
- xor a ; PLAYER_PARTY_DATA
- ld [wMonDataLocation], a
- ld hl, wPartyMon1
- call ClearSprites
- ; display the two status screens
- predef StatusScreen
- predef StatusScreen2
- ; now we need to reload the enemy mon pic
- ld a, [wEnemyBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a ; does the enemy mon have a substitute?
- ld hl, AnimationSubstitute
- jr nz, .doEnemyMonAnimation
- ; enemy mon doesn't have substitute
- ld a, [wEnemyMonMinimized]
- and a ; has the enemy mon used Minimise?
- ld hl, AnimationMinimizeMon
- jr nz, .doEnemyMonAnimation
- ; enemy mon is not minimised
- ld a, [wEnemyMonSpecies]
- ld [wcf91], a
- ld [wd0b5], a
- call GetMonHeader
- ld de, vFrontPic
- call LoadMonFrontSprite
- jr .enemyMonPicReloaded
- .doEnemyMonAnimation
- ld b, BANK(AnimationSubstitute) ; BANK(AnimationMinimizeMon)
- call Bankswitch
- .enemyMonPicReloaded ; enemy mon pic has been reloaded, so return to the party menu
- jp .partyMenuWasSelected
- .switchMon
- ld a, [wPlayerMonNumber]
- ld d, a
- ld a, [wWhichPokemon]
- cp d ; check if the mon to switch to is already out
- jr nz, .notAlreadyOut
- ; mon is already out
- ld hl, AlreadyOutText
- call PrintText
- jp .partyMonDeselected
- .notAlreadyOut
- call HasMonFainted
- jp z, .partyMonDeselected ; can't switch to fainted mon
- ld a, $1
- ld [wActionResultOrTookBattleTurn], a
- call GBPalWhiteOut
- call ClearSprites
- call LoadHudTilePatterns
- call LoadScreenTilesFromBuffer1
- call RunDefaultPaletteCommand
- call GBPalNormal
- ; fall through to SwitchPlayerMon
- SwitchPlayerMon:
- callab RetreatMon
- ld c, 50
- call DelayFrames
- call AnimateRetreatingPlayerMon
- ld a, [wWhichPokemon]
- ld [wPlayerMonNumber], a
- ld c, a
- ld b, FLAG_SET
- push bc
- ld hl, wPartyGainExpFlags
- predef FlagActionPredef
- pop bc
- ld hl, wPartyFoughtCurrentEnemyFlags
- predef FlagActionPredef
- call LoadBattleMonFromParty
- call SendOutMon
- call SaveScreenTilesToBuffer1
- ld a, $2
- ld [wCurrentMenuItem], a
- and a
- ret
- AlreadyOutText:
- TX_FAR _AlreadyOutText
- db "@"
- BattleMenu_RunWasSelected:
- call LoadScreenTilesFromBuffer1
- ld a, $3
- ld [wCurrentMenuItem], a
- ld hl, wBattleMonSpeed
- ld de, wEnemyMonSpeed
- call TryRunningFromBattle
- ld a, 0
- ld [wForcePlayerToChooseMon], a
- ret c
- ld a, [wActionResultOrTookBattleTurn]
- and a
- ret nz ; return if the player couldn't escape
- jp DisplayBattleMenu
- MoveSelectionMenu:
- ld a, [wMoveMenuType]
- dec a
- jr z, .mimicmenu
- dec a
- jr z, .relearnmenu
- jr .regularmenu
- .loadmoves
- ld de, wMoves
- ld bc, NUM_MOVES
- call CopyData
- callab FormatMovesString
- ret
- .writemoves
- ld de, wMovesString
- ld a, [hFlags_0xFFF6]
- set 2, a
- ld [hFlags_0xFFF6], a
- call PlaceString
- ld a, [hFlags_0xFFF6]
- res 2, a
- ld [hFlags_0xFFF6], a
- ret
- .regularmenu
- call AnyMoveToSelect
- ret z
- ld hl, wBattleMonMoves
- call .loadmoves
- coord hl, 4, 12
- ld b, 4
- ld c, 14
- di ; out of pure coincidence, it is possible for vblank to occur between the di and ei
- ; so it is necessary to put the di ei block to not cause tearing
- call TextBoxBorder
- coord hl, 4, 12
- ld [hl], $7a
- coord hl, 10, 12
- ld [hl], $7e
- ei
- coord hl, 6, 13
- call .writemoves
- ld b, $5
- ld a, $c
- jr .menuset
- .mimicmenu
- ld hl, wEnemyMonMoves
- call .loadmoves
- coord hl, 0, 7
- ld b, 4
- ld c, 14
- call TextBoxBorder
- coord hl, 2, 8
- call .writemoves
- ld b, $1
- ld a, $7
- jr .menuset
- .relearnmenu
- ld a, [wWhichPokemon]
- ld hl, wPartyMon1Moves
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- call .loadmoves
- coord hl, 4, 7
- ld b, 4
- ld c, 14
- call TextBoxBorder
- coord hl, 6, 8
- call .writemoves
- ld b, $5
- ld a, $7
- .menuset
- ld hl, wTopMenuItemY
- ld [hli], a ; wTopMenuItemY
- ld a, b
- ld [hli], a ; wTopMenuItemX
- ld a, [wMoveMenuType]
- cp $1
- jr z, .selectedmoveknown
- ld a, $1
- jr nc, .selectedmoveknown
- ld a, [wPlayerMoveListIndex]
- inc a
- .selectedmoveknown
- ld [hli], a ; wCurrentMenuItem
- inc hl ; wTileBehindCursor untouched
- ld a, [wNumMovesMinusOne]
- inc a
- inc a
- ld [hli], a ; wMaxMenuItem
- ld a, [wMoveMenuType]
- dec a
- ld b, D_UP | D_DOWN | A_BUTTON
- jr z, .matchedkeyspicked
- dec a
- ld b, D_UP | D_DOWN | A_BUTTON | B_BUTTON
- jr z, .matchedkeyspicked
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr z, .matchedkeyspicked
- ld a, [wFlags_D733]
- bit BIT_TEST_BATTLE, a
- ld b, D_UP | D_DOWN | A_BUTTON | B_BUTTON | SELECT
- jr z, .matchedkeyspicked
- ld b, $ff
- .matchedkeyspicked
- ld a, b
- ld [hli], a ; wMenuWatchedKeys
- ld a, [wMoveMenuType]
- cp $1
- jr z, .movelistindex1
- ld a, [wPlayerMoveListIndex]
- inc a
- .movelistindex1
- ld [hl], a
- ; fallthrough
- SelectMenuItem:
- ld a, [wMoveMenuType]
- and a
- jr z, .battleselect
- dec a
- jr nz, .select
- coord hl, 1, 14
- ld de, WhichTechniqueString
- call PlaceString
- jr .select
- .battleselect
- ld a, [wFlags_D733]
- bit BIT_TEST_BATTLE, a
- jr nz, .select
- call PrintMenuItem
- ld a, [wMenuItemToSwap]
- and a
- jr z, .select
- coord hl, 5, 13
- dec a
- ld bc, SCREEN_WIDTH
- call AddNTimes
- ld [hl], "▷"
- .select
- ld hl, hFlags_0xFFF6
- set 1, [hl]
- call HandleMenuInput
- ld hl, hFlags_0xFFF6
- res 1, [hl]
- bit 6, a
- jp nz, SelectMenuItem_CursorUp ; up
- bit 7, a
- jp nz, SelectMenuItem_CursorDown ; down
- bit 2, a
- jp nz, SwapMovesInMenu ; select
- bit 1, a ; B, but was it reset above?
- push af
- xor a
- ld [wMenuItemToSwap], a
- ld a, [wCurrentMenuItem]
- dec a
- ld [wCurrentMenuItem], a
- ld b, a
- ld a, [wMoveMenuType]
- dec a ; if not mimic
- jr nz, .notB
- pop af
- ret
- .notB
- dec a
- ld a, b
- ld [wPlayerMoveListIndex], a
- jr nz, .moveselected
- pop af
- ret
- .moveselected
- pop af
- ret nz
- ld hl, wBattleMonPP
- ld a, [wCurrentMenuItem]
- ld c, a
- ld b, $0
- add hl, bc
- ld a, [hl]
- and $3f
- jr z, .noPP
- ld a, [wPlayerDisabledMove]
- swap a
- and $f
- dec a
- cp c
- jr z, .disabled
- ld a, [wPlayerBattleStatus3]
- bit 3, a ; transformed
- jr nz, .dummy ; game freak derp
- .dummy
- ld a, [wCurrentMenuItem]
- ld hl, wBattleMonMoves
- ld c, a
- ld b, $0
- add hl, bc
- ld a, [hl]
- ld [wPlayerSelectedMove], a
- xor a
- ret
- .disabled
- ld hl, MoveDisabledText
- jr .print
- .noPP
- ld hl, MoveNoPPText
- .print
- call PrintText
- call LoadScreenTilesFromBuffer1
- jp MoveSelectionMenu
- MoveNoPPText:
- TX_FAR _MoveNoPPText
- db "@"
- MoveDisabledText:
- TX_FAR _MoveDisabledText
- db "@"
- WhichTechniqueString:
- db "WHICH TECHNIQUE?@"
- SelectMenuItem_CursorUp:
- ld a, [wCurrentMenuItem]
- and a
- jp nz, SelectMenuItem
- call EraseMenuCursor
- ld a, [wNumMovesMinusOne]
- inc a
- ld [wCurrentMenuItem], a
- jp SelectMenuItem
- SelectMenuItem_CursorDown:
- ld a, [wCurrentMenuItem]
- ld b, a
- ld a, [wNumMovesMinusOne]
- inc a
- inc a
- cp b
- jp nz, SelectMenuItem
- call EraseMenuCursor
- ld a, $1
- ld [wCurrentMenuItem], a
- jp SelectMenuItem
- AnyMoveToSelect:
- ; return z and Struggle as the selected move if all moves have 0 PP and/or are disabled
- ld a, STRUGGLE
- ld [wPlayerSelectedMove], a
- ld a, [wPlayerDisabledMove]
- and a
- ld hl, wBattleMonPP
- jr nz, .handleDisabledMove
- ld a, [hli]
- or [hl]
- inc hl
- or [hl]
- inc hl
- or [hl]
- and $3f
- ret nz
- jr .noMovesLeft
- .handleDisabledMove
- swap a
- and $f ; get disabled move
- ld b, a
- ld d, NUM_MOVES + 1
- xor a
- .handleDisabledMovePPLoop
- dec d
- jr z, .allMovesChecked
- ld c, [hl] ; get move PP
- inc hl
- dec b ; is this the disabled move?
- jr z, .handleDisabledMovePPLoop ; if so, ignore its PP value
- or c
- jr .handleDisabledMovePPLoop
- .allMovesChecked
- and a ; any PP left?
- ret nz ; return if a move has PP left
- .noMovesLeft
- ld hl, NoMovesLeftText
- call PrintText
- ld c, 60
- call DelayFrames
- xor a
- ret
- NoMovesLeftText:
- TX_FAR _NoMovesLeftText
- db "@"
- SwapMovesInMenu:
- ld a, [wMenuItemToSwap]
- and a
- jr z, .noMenuItemSelected
- ld hl, wBattleMonMoves
- call .swapBytes ; swap moves
- ld hl, wBattleMonPP
- call .swapBytes ; swap move PP
- ; update the index of the disabled move if necessary
- ld hl, wPlayerDisabledMove
- ld a, [hl]
- swap a
- and $f
- ld b, a
- ld a, [wCurrentMenuItem]
- cp b
- jr nz, .next
- ld a, [hl]
- and $f
- ld b, a
- ld a, [wMenuItemToSwap]
- swap a
- add b
- ld [hl], a
- jr .swapMovesInPartyMon
- .next
- ld a, [wMenuItemToSwap]
- cp b
- jr nz, .swapMovesInPartyMon
- ld a, [hl]
- and $f
- ld b, a
- ld a, [wCurrentMenuItem]
- swap a
- add b
- ld [hl], a
- .swapMovesInPartyMon
- ld hl, wPartyMon1Moves
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- push hl
- call .swapBytes ; swap moves
- pop hl
- ld bc, wPartyMon1PP - wPartyMon1Moves
- add hl, bc
- call .swapBytes ; swap move PP
- xor a
- ld [wMenuItemToSwap], a ; deselect the item
- jp MoveSelectionMenu
- .swapBytes
- push hl
- ld a, [wMenuItemToSwap]
- dec a
- ld c, a
- ld b, 0
- add hl, bc
- ld d, h
- ld e, l
- pop hl
- ld a, [wCurrentMenuItem]
- dec a
- ld c, a
- ld b, 0
- add hl, bc
- ld a, [de]
- ld b, [hl]
- ld [hl], a
- ld a, b
- ld [de], a
- ret
- .noMenuItemSelected
- ld a, [wCurrentMenuItem]
- ld [wMenuItemToSwap], a ; select the current menu item for swapping
- jp MoveSelectionMenu
- PrintMenuItem:
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- coord hl, 0, 8
- ld b, 3
- ld c, 9
- call TextBoxBorder
- ld a, [wPlayerDisabledMove]
- and a
- jr z, .notDisabled
- swap a
- and $f
- ld b, a
- ld a, [wCurrentMenuItem]
- cp b
- jr nz, .notDisabled
- coord hl, 1, 10
- ld de, DisabledText
- call PlaceString
- jr .moveDisabled
- .notDisabled
- ld hl, wCurrentMenuItem
- dec [hl]
- xor a
- ld [H_WHOSETURN], a
- ld hl, wBattleMonMoves
- ld a, [wCurrentMenuItem]
- ld c, a
- ld b, $0 ; which item in the menu is the cursor pointing to? (0-3)
- add hl, bc ; point to the item (move) in memory
- ld a, [hl]
- ld [wPlayerSelectedMove], a ; update wPlayerSelectedMove even if the move
- ; isn't actually selected (just pointed to by the cursor)
- ld a, [wPlayerMonNumber]
- ld [wWhichPokemon], a
- ld a, BATTLE_MON_DATA
- ld [wMonDataLocation], a
- callab GetMaxPP
- ld hl, wCurrentMenuItem
- ld c, [hl]
- inc [hl]
- ld b, $0
- ld hl, wBattleMonPP
- add hl, bc
- ld a, [hl]
- and $3f
- ld [wcd6d], a
- ; print TYPE/<type> and <curPP>/<maxPP>
- coord hl, 1, 9
- ld de, TypeText
- call PlaceString
- coord hl, 7, 11
- ld [hl], "/"
- coord hl, 5, 9
- ld [hl], "/"
- coord hl, 5, 11
- ld de, wcd6d
- lb bc, 1, 2
- call PrintNumber
- coord hl, 8, 11
- ld de, wMaxPP
- lb bc, 1, 2
- call PrintNumber
- call GetCurrentMove
- coord hl, 2, 10
- predef PrintMoveType
- .moveDisabled
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- jp Delay3
- DisabledText:
- db "disabled!@"
- TypeText:
- db "TYPE@"
- SelectEnemyMove:
- ld a, [wLinkState]
- sub LINK_STATE_BATTLING
- jr nz, .noLinkBattle
- ; link battle
- call SaveScreenTilesToBuffer1
- call LinkBattleExchangeData
- call LoadScreenTilesFromBuffer1
- ld a, [wSerialExchangeNybbleReceiveData]
- cp LINKBATTLE_STRUGGLE
- jp z, .linkedOpponentUsedStruggle
- cp LINKBATTLE_NO_ACTION
- jr z, .unableToSelectMove
- cp 4
- ret nc
- ld [wEnemyMoveListIndex], a
- ld c, a
- ld hl, wEnemyMonMoves
- ld b, 0
- add hl, bc
- ld a, [hl]
- jr .done
- .noLinkBattle
- ld a, [wEnemyBattleStatus2]
- and (1 << NEEDS_TO_RECHARGE) | (1 << USING_RAGE) ; need to recharge or using rage
- ret nz
- ld hl, wEnemyBattleStatus1
- ld a, [hl]
- and (1 << CHARGING_UP) | (1 << THRASHING_ABOUT) ; using a charging move or thrash/petal dance
- ret nz
- ld a, [wEnemyMonStatus]
- and SLP | 1 << FRZ ; sleeping or frozen
- ret nz
- ld a, [wEnemyBattleStatus1]
- and (1 << USING_TRAPPING_MOVE) | (1 << STORING_ENERGY) ; using a trapping move like wrap or bide
- ret nz
- ld a, [wPlayerBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; caught in player's trapping move (e.g. wrap)
- jr z, .canSelectMove
- .unableToSelectMove
- ld a, $ff
- jr .done
- .canSelectMove
- ld hl, wEnemyMonMoves+1 ; 2nd enemy move
- ld a, [hld]
- and a
- jr nz, .atLeastTwoMovesAvailable
- ld a, [wEnemyDisabledMove]
- and a
- ld a, STRUGGLE ; struggle if the only move is disabled
- jr nz, .done
- .atLeastTwoMovesAvailable
- ld a, [wIsInBattle]
- dec a
- jr z, .chooseRandomMove ; wild encounter
- callab AIEnemyTrainerChooseMoves
- .chooseRandomMove
- push hl
- call BattleRandom
- ld b, $1
- cp $3f ; select move 1, [0,3e] (63/256 chance)
- jr c, .moveChosen
- inc hl
- inc b
- cp $7f ; select move 2, [3f,7e] (64/256 chance)
- jr c, .moveChosen
- inc hl
- inc b
- cp $be ; select move 3, [7f,bd] (63/256 chance)
- jr c, .moveChosen
- inc hl
- inc b ; select move 4, [be,ff] (66/256 chance)
- .moveChosen
- ld a, b
- dec a
- ld [wEnemyMoveListIndex], a
- ld a, [wEnemyDisabledMove]
- swap a
- and $f
- cp b
- ld a, [hl]
- pop hl
- jr z, .chooseRandomMove ; move disabled, try again
- and a
- jr z, .chooseRandomMove ; move non-existant, try again
- .done
- ld [wEnemySelectedMove], a
- ret
- .linkedOpponentUsedStruggle
- ld a, STRUGGLE
- jr .done
- ; this appears to exchange data with the other gameboy during link battles
- LinkBattleExchangeData:
- ld a, $ff
- ld [wSerialExchangeNybbleReceiveData], a
- ld a, [wPlayerMoveListIndex]
- cp LINKBATTLE_RUN ; is the player running from battle?
- jr z, .doExchange
- ld a, [wActionResultOrTookBattleTurn]
- and a ; is the player switching in another mon?
- jr nz, .switching
- ; the player used a move
- ld a, [wPlayerSelectedMove]
- cp STRUGGLE
- ld b, LINKBATTLE_STRUGGLE
- jr z, .next
- dec b ; LINKBATTLE_NO_ACTION
- inc a ; does move equal -1 (i.e. no action)?
- jr z, .next
- ld a, [wPlayerMoveListIndex]
- jr .doExchange
- .switching
- ld a, [wWhichPokemon]
- add 4
- ld b, a
- .next
- ld a, b
- .doExchange
- ld [wSerialExchangeNybbleSendData], a
- callab PrintWaitingText
- .syncLoop1
- call Serial_ExchangeNybble
- call DelayFrame
- ld a, [wSerialExchangeNybbleReceiveData]
- inc a
- jr z, .syncLoop1
- ld b, 10
- .syncLoop2
- call DelayFrame
- call Serial_ExchangeNybble
- dec b
- jr nz, .syncLoop2
- ld b, 10
- .syncLoop3
- call DelayFrame
- call Serial_SendZeroByte
- dec b
- jr nz, .syncLoop3
- ret
- ExecutePlayerMove:
- xor a
- ld [H_WHOSETURN], a ; set player's turn
- ld a, [wPlayerSelectedMove]
- inc a
- jp z, ExecutePlayerMoveDone ; for selected move = FF, skip most of player's turn
- xor a
- ld [wMoveMissed], a
- ld [wMonIsDisobedient], a
- ld [wMoveDidntMiss], a
- ld a, $a
- ld [wDamageMultipliers], a
- ld a, [wActionResultOrTookBattleTurn]
- and a ; has the player already used the turn (e.g. by using an item, trying to run or switching pokemon)
- jp nz, ExecutePlayerMoveDone
- call PrintGhostText
- jp z, ExecutePlayerMoveDone
- call CheckPlayerStatusConditions
- jr nz, .playerHasNoSpecialCondition
- jp hl
- .playerHasNoSpecialCondition
- call GetCurrentMove
- ld hl, wPlayerBattleStatus1
- bit CHARGING_UP, [hl] ; charging up for attack
- jr nz, PlayerCanExecuteChargingMove
- call CheckForDisobedience
- jp z, ExecutePlayerMoveDone
- CheckIfPlayerNeedsToChargeUp:
- ld a, [wPlayerMoveEffect]
- cp CHARGE_EFFECT
- jp z, JumpMoveEffect
- cp FLY_EFFECT
- jp z, JumpMoveEffect
- jr PlayerCanExecuteMove
- ; in-battle stuff
- PlayerCanExecuteChargingMove:
- ld hl, wPlayerBattleStatus1
- res CHARGING_UP, [hl] ; reset charging up and invulnerability statuses if mon was charging up for an attack
- ; being fully paralyzed or hurting oneself in confusion removes charging up status
- ; resulting in the Pokemon being invulnerable for the whole battle
- res INVULNERABLE, [hl]
- PlayerCanExecuteMove:
- call PrintMonName1Text
- ld hl, DecrementPP
- ld de, wPlayerSelectedMove ; pointer to the move just used
- ld b, BANK(DecrementPP)
- call Bankswitch
- ld a, [wPlayerMoveEffect] ; effect of the move just used
- ld hl, ResidualEffects1
- ld de, 1
- call IsInArray
- jp c, JumpMoveEffect ; ResidualEffects1 moves skip damage calculation and accuracy tests
- ; unless executed as part of their exclusive effect functions
- ld a, [wPlayerMoveEffect]
- ld hl, SpecialEffectsCont
- ld de, 1
- call IsInArray
- call c, JumpMoveEffect ; execute the effects of SpecialEffectsCont moves (e.g. Wrap, Thrash) but don't skip anything
- PlayerCalcMoveDamage:
- ld a, [wPlayerMoveEffect]
- ld hl, SetDamageEffects
- ld de, 1
- call IsInArray
- jp c, .moveHitTest ; SetDamageEffects moves (e.g. Seismic Toss and Super Fang) skip damage calculation
- call CriticalHitTest
- call HandleCounterMove
- jr z, handleIfPlayerMoveMissed
- call GetDamageVarsForPlayerAttack
- call CalculateDamage
- jp z, playerCheckIfFlyOrChargeEffect ; for moves with 0 BP, skip any further damage calculation and, for now, skip MoveHitTest
- ; for these moves, accuracy tests will only occur if they are called as part of the effect itself
- call AdjustDamageForMoveType
- call RandomizeDamage
- .moveHitTest
- call MoveHitTest
- handleIfPlayerMoveMissed:
- ld a, [wMoveMissed]
- and a
- jr z, getPlayerAnimationType
- ld a, [wPlayerMoveEffect]
- sub EXPLODE_EFFECT
- jr z, playPlayerMoveAnimation ; don't play any animation if the move missed, unless it was EXPLODE_EFFECT
- jr playerCheckIfFlyOrChargeEffect
- getPlayerAnimationType:
- ld a, [wPlayerMoveEffect]
- and a
- ld a, 4 ; move has no effect other than dealing damage
- jr z, playPlayerMoveAnimation
- ld a, 5 ; move has effect
- playPlayerMoveAnimation:
- push af
- ld a, [wPlayerBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a
- ld hl, HideSubstituteShowMonAnim
- ld b, BANK(HideSubstituteShowMonAnim)
- call nz, Bankswitch
- pop af
- ld [wAnimationType], a
- ld a, [wPlayerMoveNum]
- call PlayMoveAnimation
- call HandleExplodingAnimation
- call DrawPlayerHUDAndHPBar
- ld a, [wPlayerBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a
- ld hl, ReshowSubstituteAnim
- ld b, BANK(ReshowSubstituteAnim)
- call nz, Bankswitch
- jr MirrorMoveCheck
- playerCheckIfFlyOrChargeEffect:
- ld c, 30
- call DelayFrames
- ld a, [wPlayerMoveEffect]
- cp FLY_EFFECT
- jr z, .playAnim
- cp CHARGE_EFFECT
- jr z, .playAnim
- jr MirrorMoveCheck
- .playAnim
- xor a
- ld [wAnimationType], a
- ld a, STATUS_AFFECTED_ANIM
- call PlayMoveAnimation
- MirrorMoveCheck:
- ld a, [wPlayerMoveEffect]
- cp MIRROR_MOVE_EFFECT
- jr nz, .metronomeCheck
- call MirrorMoveCopyMove
- jp z, ExecutePlayerMoveDone
- xor a
- ld [wMonIsDisobedient], a
- jp CheckIfPlayerNeedsToChargeUp ; if Mirror Move was successful go back to damage calculation for copied move
- .metronomeCheck
- cp METRONOME_EFFECT
- jr nz, .next
- call MetronomePickMove
- jp CheckIfPlayerNeedsToChargeUp ; Go back to damage calculation for the move picked by Metronome
- .next
- ld a, [wPlayerMoveEffect]
- ld hl, ResidualEffects2
- ld de, 1
- call IsInArray
- jp c, JumpMoveEffect ; done here after executing effects of ResidualEffects2
- ld a, [wMoveMissed]
- and a
- jr z, .moveDidNotMiss
- call PrintMoveFailureText
- ld a, [wPlayerMoveEffect]
- cp EXPLODE_EFFECT ; even if Explosion or Selfdestruct missed, its effect still needs to be activated
- jr z, .notDone
- jp ExecutePlayerMoveDone ; otherwise, we're done if the move missed
- .moveDidNotMiss
- call ApplyAttackToEnemyPokemon
- call PrintCriticalOHKOText
- callab DisplayEffectiveness
- ld a, 1
- ld [wMoveDidntMiss], a
- .notDone
- ld a, [wPlayerMoveEffect]
- ld hl, AlwaysHappenSideEffects
- ld de, 1
- call IsInArray
- call c, JumpMoveEffect ; not done after executing effects of AlwaysHappenSideEffects
- ld hl, wEnemyMonHP
- ld a, [hli]
- ld b, [hl]
- or b
- ret z ; don't do anything else if the enemy fainted
- call HandleBuildingRage
- ld hl, wPlayerBattleStatus1
- bit ATTACKING_MULTIPLE_TIMES, [hl]
- jr z, .executeOtherEffects
- ld a, [wPlayerNumAttacksLeft]
- dec a
- ld [wPlayerNumAttacksLeft], a
- jp nz, getPlayerAnimationType ; for multi-hit moves, apply attack until PlayerNumAttacksLeft hits 0 or the enemy faints.
- ; damage calculation and accuracy tests only happen for the first hit
- res ATTACKING_MULTIPLE_TIMES, [hl] ; clear attacking multiple times status when all attacks are over
- ld hl, MultiHitText
- call PrintText
- xor a
- ld [wPlayerNumHits], a
- .executeOtherEffects
- ld a, [wPlayerMoveEffect]
- and a
- jp z, ExecutePlayerMoveDone
- ld hl, SpecialEffects
- ld de, 1
- call IsInArray
- call nc, JumpMoveEffect ; move effects not included in SpecialEffects or in either of the ResidualEffect arrays,
- ; which are the effects not covered yet. Rage effect will be executed for a second time (though it's irrelevant).
- ; Includes side effects that only need to be called if the target didn't faint.
- ; Responsible for executing Twineedle's second side effect (poison).
- jp ExecutePlayerMoveDone
- MultiHitText:
- TX_FAR _MultiHitText
- db "@"
- ExecutePlayerMoveDone:
- xor a
- ld [wActionResultOrTookBattleTurn], a
- ld b, 1
- ret
- PrintGhostText:
- ; print the ghost battle messages
- call IsGhostBattle
- ret nz
- ld a, [H_WHOSETURN]
- and a
- jr nz, .Ghost
- ld a, [wBattleMonStatus] ; player’s turn
- and SLP | (1 << FRZ)
- ret nz
- ld hl, ScaredText
- call PrintText
- xor a
- ret
- .Ghost ; ghost’s turn
- ld hl, GetOutText
- call PrintText
- xor a
- ret
- ScaredText:
- TX_FAR _ScaredText
- db "@"
- GetOutText:
- TX_FAR _GetOutText
- db "@"
- IsGhostBattle:
- ld a, [wIsInBattle]
- dec a
- ret nz
- ld a, [wCurMap]
- cp POKEMONTOWER_1
- jr c, .next
- cp LAVENDER_HOUSE_1
- jr nc, .next
- ld b, SILPH_SCOPE
- call IsItemInBag
- ret z
- .next
- ld a, 1
- and a
- ret
- ; checks for various status conditions affecting the player mon
- ; stores whether the mon cannot use a move this turn in Z flag
- CheckPlayerStatusConditions:
- ld hl, wBattleMonStatus
- ld a, [hl]
- and SLP ; sleep mask
- jr z, .FrozenCheck
- ; sleeping
- dec a
- ld [wBattleMonStatus], a ; decrement number of turns left
- and a
- jr z, .WakeUp ; if the number of turns hit 0, wake up
- ; fast asleep
- xor a
- ld [wAnimationType], a
- ld a, SLP_ANIM - 1
- call PlayMoveAnimation
- ld hl, FastAsleepText
- call PrintText
- jr .sleepDone
- .WakeUp
- ld hl, WokeUpText
- call PrintText
- .sleepDone
- xor a
- ld [wPlayerUsedMove], a
- ld hl, ExecutePlayerMoveDone ; player can't move this turn
- jp .returnToHL
- .FrozenCheck
- bit FRZ, [hl] ; frozen?
- jr z, .HeldInPlaceCheck
- ld hl, IsFrozenText
- call PrintText
- xor a
- ld [wPlayerUsedMove], a
- ld hl, ExecutePlayerMoveDone ; player can't move this turn
- jp .returnToHL
- .HeldInPlaceCheck
- ld a, [wEnemyBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; is enemy using a mult-turn move like wrap?
- jp z, .FlinchedCheck
- ld hl, CantMoveText
- call PrintText
- ld hl, ExecutePlayerMoveDone ; player can't move this turn
- jp .returnToHL
- .FlinchedCheck
- ld hl, wPlayerBattleStatus1
- bit FLINCHED, [hl]
- jp z, .HyperBeamCheck
- res FLINCHED, [hl] ; reset player's flinch status
- ld hl, FlinchedText
- call PrintText
- ld hl, ExecutePlayerMoveDone ; player can't move this turn
- jp .returnToHL
- .HyperBeamCheck
- ld hl, wPlayerBattleStatus2
- bit NEEDS_TO_RECHARGE, [hl]
- jr z, .AnyMoveDisabledCheck
- res NEEDS_TO_RECHARGE, [hl] ; reset player's recharge status
- ld hl, MustRechargeText
- call PrintText
- ld hl, ExecutePlayerMoveDone ; player can't move this turn
- jp .returnToHL
- .AnyMoveDisabledCheck
- ld hl, wPlayerDisabledMove
- ld a, [hl]
- and a
- jr z, .ConfusedCheck
- dec a
- ld [hl], a
- and $f ; did Disable counter hit 0?
- jr nz, .ConfusedCheck
- ld [hl], a
- ld [wPlayerDisabledMoveNumber], a
- ld hl, DisabledNoMoreText
- call PrintText
- .ConfusedCheck
- ld a, [wPlayerBattleStatus1]
- add a ; is player confused?
- jr nc, .TriedToUseDisabledMoveCheck
- ld hl, wPlayerConfusedCounter
- dec [hl]
- jr nz, .IsConfused
- ld hl, wPlayerBattleStatus1
- res CONFUSED, [hl] ; if confused counter hit 0, reset confusion status
- ld hl, ConfusedNoMoreText
- call PrintText
- jr .TriedToUseDisabledMoveCheck
- .IsConfused
- ld hl, IsConfusedText
- call PrintText
- xor a
- ld [wAnimationType], a
- ld a, CONF_ANIM - 1
- call PlayMoveAnimation
- call BattleRandom
- cp $80 ; 50% chance to hurt itself
- jr c, .TriedToUseDisabledMoveCheck
- ld hl, wPlayerBattleStatus1
- ld a, [hl]
- and 1 << CONFUSED ; if mon hurts itself, clear every other status from wPlayerBattleStatus1
- ld [hl], a
- call HandleSelfConfusionDamage
- jr .MonHurtItselfOrFullyParalysed
- .TriedToUseDisabledMoveCheck
- ; prevents a disabled move that was selected before being disabled from being used
- ld a, [wPlayerDisabledMoveNumber]
- and a
- jr z, .ParalysisCheck
- ld hl, wPlayerSelectedMove
- cp [hl]
- jr nz, .ParalysisCheck
- call PrintMoveIsDisabledText
- ld hl, ExecutePlayerMoveDone ; if a disabled move was somehow selected, player can't move this turn
- jp .returnToHL
- .ParalysisCheck
- ld hl, wBattleMonStatus
- bit PAR, [hl]
- jr z, .BideCheck
- call BattleRandom
- cp $3F ; 25% to be fully paralyzed
- jr nc, .BideCheck
- ld hl, FullyParalyzedText
- call PrintText
- .MonHurtItselfOrFullyParalysed
- ld hl, wPlayerBattleStatus1
- ld a, [hl]
- ; clear bide, thrashing, charging up, and trapping moves such as warp (already cleared for confusion damage)
- and $ff ^ ((1 << STORING_ENERGY) | (1 << THRASHING_ABOUT) | (1 << CHARGING_UP) | (1 << USING_TRAPPING_MOVE))
- ld [hl], a
- ld a, [wPlayerMoveEffect]
- cp FLY_EFFECT
- jr z, .FlyOrChargeEffect
- cp CHARGE_EFFECT
- jr z, .FlyOrChargeEffect
- jr .NotFlyOrChargeEffect
- .FlyOrChargeEffect
- xor a
- ld [wAnimationType], a
- ld a, STATUS_AFFECTED_ANIM
- call PlayMoveAnimation
- .NotFlyOrChargeEffect
- ld hl, ExecutePlayerMoveDone
- jp .returnToHL ; if using a two-turn move, we need to recharge the first turn
- .BideCheck
- ld hl, wPlayerBattleStatus1
- bit STORING_ENERGY, [hl] ; is mon using bide?
- jr z, .ThrashingAboutCheck
- xor a
- ld [wPlayerMoveNum], a
- ld hl, wDamage
- ld a, [hli]
- ld b, a
- ld c, [hl]
- ld hl, wPlayerBideAccumulatedDamage + 1
- ld a, [hl]
- add c ; accumulate damage taken
- ld [hld], a
- ld a, [hl]
- adc b
- ld [hl], a
- ld hl, wPlayerNumAttacksLeft
- dec [hl] ; did Bide counter hit 0?
- jr z, .UnleashEnergy
- ld hl, ExecutePlayerMoveDone
- jp .returnToHL ; unless mon unleashes energy, can't move this turn
- .UnleashEnergy
- ld hl, wPlayerBattleStatus1
- res STORING_ENERGY, [hl] ; not using bide any more
- ld hl, UnleashedEnergyText
- call PrintText
- ld a, 1
- ld [wPlayerMovePower], a
- ld hl, wPlayerBideAccumulatedDamage + 1
- ld a, [hld]
- add a
- ld b, a
- ld [wDamage + 1], a
- ld a, [hl]
- rl a ; double the damage
- ld [wDamage], a
- or b
- jr nz, .next
- ld a, 1
- ld [wMoveMissed], a
- .next
- xor a
- ld [hli], a
- ld [hl], a
- ld a, BIDE
- ld [wPlayerMoveNum], a
- ld hl, handleIfPlayerMoveMissed ; skip damage calculation, DecrementPP and MoveHitTest
- jp .returnToHL
- .ThrashingAboutCheck
- bit THRASHING_ABOUT, [hl] ; is mon using thrash or petal dance?
- jr z, .MultiturnMoveCheck
- ld a, THRASH
- ld [wPlayerMoveNum], a
- ld hl, ThrashingAboutText
- call PrintText
- ld hl, wPlayerNumAttacksLeft
- dec [hl] ; did Thrashing About counter hit 0?
- ld hl, PlayerCalcMoveDamage ; skip DecrementPP
- jp nz, .returnToHL
- push hl
- ld hl, wPlayerBattleStatus1
- res THRASHING_ABOUT, [hl] ; no longer thrashing about
- set CONFUSED, [hl] ; confused
- call BattleRandom
- and 3
- inc a
- inc a ; confused for 2-5 turns
- ld [wPlayerConfusedCounter], a
- pop hl ; skip DecrementPP
- jp .returnToHL
- .MultiturnMoveCheck
- bit USING_TRAPPING_MOVE, [hl] ; is mon using multi-turn move?
- jp z, .RageCheck
- ld hl, AttackContinuesText
- call PrintText
- ld a, [wPlayerNumAttacksLeft]
- dec a ; did multi-turn move end?
- ld [wPlayerNumAttacksLeft], a
- ld hl, getPlayerAnimationType ; if it didn't, skip damage calculation (deal damage equal to last hit),
- ; DecrementPP and MoveHitTest
- jp nz, .returnToHL
- jp .returnToHL
- .RageCheck
- ld a, [wPlayerBattleStatus2]
- bit USING_RAGE, a ; is mon using rage?
- jp z, .checkPlayerStatusConditionsDone ; if we made it this far, mon can move normally this turn
- ld a, RAGE
- ld [wd11e], a
- call GetMoveName
- call CopyStringToCF4B
- xor a
- ld [wPlayerMoveEffect], a
- ld hl, PlayerCanExecuteMove
- jp .returnToHL
- .returnToHL
- xor a
- ret
- .checkPlayerStatusConditionsDone
- ld a, $1
- and a
- ret
- FastAsleepText:
- TX_FAR _FastAsleepText
- db "@"
- WokeUpText:
- TX_FAR _WokeUpText
- db "@"
- IsFrozenText:
- TX_FAR _IsFrozenText
- db "@"
- FullyParalyzedText:
- TX_FAR _FullyParalyzedText
- db "@"
- FlinchedText:
- TX_FAR _FlinchedText
- db "@"
- MustRechargeText:
- TX_FAR _MustRechargeText
- db "@"
- DisabledNoMoreText:
- TX_FAR _DisabledNoMoreText
- db "@"
- IsConfusedText:
- TX_FAR _IsConfusedText
- db "@"
- HurtItselfText:
- TX_FAR _HurtItselfText
- db "@"
- ConfusedNoMoreText:
- TX_FAR _ConfusedNoMoreText
- db "@"
- SavingEnergyText:
- TX_FAR _SavingEnergyText
- db "@"
- UnleashedEnergyText:
- TX_FAR _UnleashedEnergyText
- db "@"
- ThrashingAboutText:
- TX_FAR _ThrashingAboutText
- db "@"
- AttackContinuesText:
- TX_FAR _AttackContinuesText
- db "@"
- CantMoveText:
- TX_FAR _CantMoveText
- db "@"
- PrintMoveIsDisabledText:
- ld hl, wPlayerSelectedMove
- ld de, wPlayerBattleStatus1
- ld a, [H_WHOSETURN]
- and a
- jr z, .removeChargingUp
- inc hl
- ld de, wEnemyBattleStatus1
- .removeChargingUp
- ld a, [de]
- res CHARGING_UP, a ; end the pokemon's
- ld [de], a
- ld a, [hl]
- ld [wd11e], a
- call GetMoveName
- ld hl, MoveIsDisabledText
- jp PrintText
- MoveIsDisabledText:
- TX_FAR _MoveIsDisabledText
- db "@"
- HandleSelfConfusionDamage:
- ld hl, HurtItselfText
- call PrintText
- ld hl, wEnemyMonDefense
- ld a, [hli]
- push af
- ld a, [hld]
- push af
- ld a, [wBattleMonDefense]
- ld [hli], a
- ld a, [wBattleMonDefense + 1]
- ld [hl], a
- ld hl, wPlayerMoveEffect
- push hl
- ld a, [hl]
- push af
- xor a
- ld [hli], a
- ld [wCriticalHitOrOHKO], a ; self-inflicted confusion damage can't be a Critical Hit
- ld a, 40 ; 40 base power
- ld [hli], a
- xor a
- ld [hl], a
- call GetDamageVarsForPlayerAttack
- call CalculateDamage ; ignores AdjustDamageForMoveType (type-less damage), RandomizeDamage,
- ; and MoveHitTest (always hits)
- pop af
- pop hl
- ld [hl], a
- ld hl, wEnemyMonDefense + 1
- pop af
- ld [hld], a
- pop af
- ld [hl], a
- xor a
- ld [wAnimationType], a
- inc a
- ld [H_WHOSETURN], a
- call PlayMoveAnimation
- call DrawPlayerHUDAndHPBar
- xor a
- ld [H_WHOSETURN], a
- jp ApplyDamageToPlayerPokemon
- PrintMonName1Text:
- ld hl, MonName1Text
- jp PrintText
- ; this function wastes time calling DetermineExclamationPointTextNum
- ; and choosing between Used1Text and Used2Text, even though
- ; those text strings are identical and both continue at PrintInsteadText
- ; this likely had to do with Japanese grammar that got translated,
- ; but the functionality didn't get removed
- MonName1Text:
- TX_FAR _MonName1Text
- TX_ASM
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveNum]
- ld hl, wPlayerUsedMove
- jr z, .playerTurn
- ld a, [wEnemyMoveNum]
- ld hl, wEnemyUsedMove
- .playerTurn
- ld [hl], a
- ld [wd11e], a
- call DetermineExclamationPointTextNum
- ld a, [wMonIsDisobedient]
- and a
- ld hl, Used2Text
- ret nz
- ld a, [wd11e]
- cp 3
- ld hl, Used2Text
- ret c
- ld hl, Used1Text
- ret
- Used1Text:
- TX_FAR _Used1Text
- TX_ASM
- jr PrintInsteadText
- Used2Text:
- TX_FAR _Used2Text
- TX_ASM
- ; fall through
- PrintInsteadText:
- ld a, [wMonIsDisobedient]
- and a
- jr z, PrintMoveName
- ld hl, InsteadText
- ret
- InsteadText:
- TX_FAR _InsteadText
- TX_ASM
- ; fall through
- PrintMoveName:
- ld hl, _PrintMoveName
- ret
- _PrintMoveName:
- TX_FAR _CF4BText
- TX_ASM
- ld hl, ExclamationPointPointerTable
- ld a, [wd11e] ; exclamation point num
- add a
- push bc
- ld b, $0
- ld c, a
- add hl, bc
- pop bc
- ld a, [hli]
- ld h, [hl]
- ld l, a
- ret
- ExclamationPointPointerTable:
- dw ExclamationPoint1Text
- dw ExclamationPoint2Text
- dw ExclamationPoint3Text
- dw ExclamationPoint4Text
- dw ExclamationPoint5Text
- ExclamationPoint1Text:
- TX_FAR _ExclamationPoint1Text
- db "@"
- ExclamationPoint2Text:
- TX_FAR _ExclamationPoint2Text
- db "@"
- ExclamationPoint3Text:
- TX_FAR _ExclamationPoint3Text
- db "@"
- ExclamationPoint4Text:
- TX_FAR _ExclamationPoint4Text
- db "@"
- ExclamationPoint5Text:
- TX_FAR _ExclamationPoint5Text
- db "@"
- ; this function does nothing useful
- ; if the move being used is in set [1-4] from ExclamationPointMoveSets,
- ; use ExclamationPoint[1-4]Text
- ; otherwise, use ExclamationPoint5Text
- ; but all five text strings are identical
- ; this likely had to do with Japanese grammar that got translated,
- ; but the functionality didn't get removed
- DetermineExclamationPointTextNum:
- push bc
- ld a, [wd11e] ; move ID
- ld c, a
- ld b, $0
- ld hl, ExclamationPointMoveSets
- .loop
- ld a, [hli]
- cp $ff
- jr z, .done
- cp c
- jr z, .done
- and a
- jr nz, .loop
- inc b
- jr .loop
- .done
- ld a, b
- ld [wd11e], a ; exclamation point num
- pop bc
- ret
- ExclamationPointMoveSets:
- db SWORDS_DANCE, GROWTH
- db $00
- db RECOVER, BIDE, SELFDESTRUCT, AMNESIA
- db $00
- db MEDITATE, AGILITY, TELEPORT, MIMIC, DOUBLE_TEAM, BARRAGE
- db $00
- db POUND, SCRATCH, VICEGRIP, WING_ATTACK, FLY, BIND, SLAM, HORN_ATTACK, BODY_SLAM
- db WRAP, THRASH, TAIL_WHIP, LEER, BITE, GROWL, ROAR, SING, PECK, COUNTER
- db STRENGTH, ABSORB, STRING_SHOT, EARTHQUAKE, FISSURE, DIG, TOXIC, SCREECH, HARDEN
- db MINIMIZE, WITHDRAW, DEFENSE_CURL, METRONOME, LICK, CLAMP, CONSTRICT, POISON_GAS
- db LEECH_LIFE, BUBBLE, FLASH, SPLASH, ACID_ARMOR, FURY_SWIPES, REST, SHARPEN, SLASH, SUBSTITUTE
- db $00
- db $FF ; terminator
- PrintMoveFailureText:
- ld de, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- jr z, .playersTurn
- ld de, wEnemyMoveEffect
- .playersTurn
- ld hl, DoesntAffectMonText
- ld a, [wDamageMultipliers]
- and $7f
- jr z, .gotTextToPrint
- ld hl, AttackMissedText
- ld a, [wCriticalHitOrOHKO]
- cp $ff
- jr nz, .gotTextToPrint
- ld hl, UnaffectedText
- .gotTextToPrint
- push de
- call PrintText
- xor a
- ld [wCriticalHitOrOHKO], a
- pop de
- ld a, [de]
- cp JUMP_KICK_EFFECT
- ret nz
- ; if you get here, the mon used jump kick or hi jump kick and missed
- ld hl, wDamage ; since the move missed, wDamage will always contain 0 at this point.
- ; Thus, recoil damage will always be equal to 1
- ; even if it was intended to be potential damage/8.
- ld a, [hli]
- ld b, [hl]
- srl a
- rr b
- srl a
- rr b
- srl a
- rr b
- ld [hl], b
- dec hl
- ld [hli], a
- or b
- jr nz, .applyRecoil
- inc a
- ld [hl], a
- .applyRecoil
- ld hl, KeptGoingAndCrashedText
- call PrintText
- ld b, $4
- predef PredefShakeScreenHorizontally
- ld a, [H_WHOSETURN]
- and a
- jr nz, .enemyTurn
- jp ApplyDamageToPlayerPokemon
- .enemyTurn
- jp ApplyDamageToEnemyPokemon
- AttackMissedText:
- TX_FAR _AttackMissedText
- db "@"
- KeptGoingAndCrashedText:
- TX_FAR _KeptGoingAndCrashedText
- db "@"
- UnaffectedText:
- TX_FAR _UnaffectedText
- db "@"
- PrintDoesntAffectText:
- ld hl, DoesntAffectMonText
- jp PrintText
- DoesntAffectMonText:
- TX_FAR _DoesntAffectMonText
- db "@"
- ; if there was a critical hit or an OHKO was successful, print the corresponding text
- PrintCriticalOHKOText:
- ld a, [wCriticalHitOrOHKO]
- and a
- jr z, .done ; do nothing if there was no critical hit or successful OHKO
- dec a
- add a
- ld hl, CriticalOHKOTextPointers
- ld b, $0
- ld c, a
- add hl, bc
- ld a, [hli]
- ld h, [hl]
- ld l, a
- call PrintText
- xor a
- ld [wCriticalHitOrOHKO], a
- .done
- ld c, 20
- jp DelayFrames
- CriticalOHKOTextPointers:
- dw CriticalHitText
- dw OHKOText
- CriticalHitText:
- TX_FAR _CriticalHitText
- db "@"
- OHKOText:
- TX_FAR _OHKOText
- db "@"
- ; checks if a traded mon will disobey due to lack of badges
- ; stores whether the mon will use a move in Z flag
- CheckForDisobedience:
- xor a
- ld [wMonIsDisobedient], a
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .checkIfMonIsTraded
- ld a, $1
- and a
- ret
- ; compare the mon's original trainer ID with the player's ID to see if it was traded
- .checkIfMonIsTraded
- ld hl, wPartyMon1OTID
- ld bc, wPartyMon2 - wPartyMon1
- ld a, [wPlayerMonNumber]
- call AddNTimes
- ld a, [wPlayerID]
- cp [hl]
- jr nz, .monIsTraded
- inc hl
- ld a, [wPlayerID + 1]
- cp [hl]
- jp z, .canUseMove
- ; it was traded
- .monIsTraded
- ; what level might disobey?
- ld hl, wObtainedBadges
- bit 7, [hl]
- ld a, 101
- jr nz, .next
- bit 5, [hl]
- ld a, 70
- jr nz, .next
- bit 3, [hl]
- ld a, 50
- jr nz, .next
- bit 1, [hl]
- ld a, 30
- jr nz, .next
- ld a, 10
- .next
- ld b, a
- ld c, a
- ld a, [wBattleMonLevel]
- ld d, a
- add b
- ld b, a
- jr nc, .noCarry
- ld b, $ff ; cap b at $ff
- .noCarry
- ld a, c
- cp d
- jp nc, .canUseMove
- .loop1
- call BattleRandom
- swap a
- cp b
- jr nc, .loop1
- cp c
- jp c, .canUseMove
- .loop2
- call BattleRandom
- cp b
- jr nc, .loop2
- cp c
- jr c, .useRandomMove
- ld a, d
- sub c
- ld b, a
- call BattleRandom
- swap a
- sub b
- jr c, .monNaps
- cp b
- jr nc, .monDoesNothing
- ld hl, WontObeyText
- call PrintText
- call HandleSelfConfusionDamage
- jp .cannotUseMove
- .monNaps
- call BattleRandom
- add a
- swap a
- and SLP ; sleep mask
- jr z, .monNaps ; keep trying until we get at least 1 turn of sleep
- ld [wBattleMonStatus], a
- ld hl, BeganToNapText
- jr .printText
- .monDoesNothing
- call BattleRandom
- and $3
- ld hl, LoafingAroundText
- and a
- jr z, .printText
- ld hl, WontObeyText
- dec a
- jr z, .printText
- ld hl, TurnedAwayText
- dec a
- jr z, .printText
- ld hl, IgnoredOrdersText
- .printText
- call PrintText
- jr .cannotUseMove
- .useRandomMove
- ld a, [wBattleMonMoves + 1]
- and a ; is the second move slot empty?
- jr z, .monDoesNothing ; mon will not use move if it only knows one move
- ld a, [wPlayerDisabledMoveNumber]
- and a
- jr nz, .monDoesNothing
- ld a, [wPlayerSelectedMove]
- cp STRUGGLE
- jr z, .monDoesNothing ; mon will not use move if struggling
- ; check if only one move has remaining PP
- ld hl, wBattleMonPP
- push hl
- ld a, [hli]
- and $3f
- ld b, a
- ld a, [hli]
- and $3f
- add b
- ld b, a
- ld a, [hli]
- and $3f
- add b
- ld b, a
- ld a, [hl]
- and $3f
- add b
- pop hl
- push af
- ld a, [wCurrentMenuItem]
- ld c, a
- ld b, $0
- add hl, bc
- ld a, [hl]
- and $3f
- ld b, a
- pop af
- cp b
- jr z, .monDoesNothing ; mon will not use move if only one move has remaining PP
- ld a, $1
- ld [wMonIsDisobedient], a
- ld a, [wMaxMenuItem]
- ld b, a
- ld a, [wCurrentMenuItem]
- ld c, a
- .chooseMove
- call BattleRandom
- and $3
- cp b
- jr nc, .chooseMove ; if the random number is greater than the move count, choose another
- cp c
- jr z, .chooseMove ; if the random number matches the move the player selected, choose another
- ld [wCurrentMenuItem], a
- ld hl, wBattleMonPP
- ld e, a
- ld d, $0
- add hl, de
- ld a, [hl]
- and a ; does the move have any PP left?
- jr z, .chooseMove ; if the move has no PP left, choose another
- ld a, [wCurrentMenuItem]
- ld c, a
- ld b, $0
- ld hl, wBattleMonMoves
- add hl, bc
- ld a, [hl]
- ld [wPlayerSelectedMove], a
- call GetCurrentMove
- .canUseMove
- ld a, $1
- and a; clear Z flag
- ret
- .cannotUseMove
- xor a ; set Z flag
- ret
- LoafingAroundText:
- TX_FAR _LoafingAroundText
- db "@"
- BeganToNapText:
- TX_FAR _BeganToNapText
- db "@"
- WontObeyText:
- TX_FAR _WontObeyText
- db "@"
- TurnedAwayText:
- TX_FAR _TurnedAwayText
- db "@"
- IgnoredOrdersText:
- TX_FAR _IgnoredOrdersText
- db "@"
- ; sets b, c, d, and e for the CalculateDamage routine in the case of an attack by the player mon
- GetDamageVarsForPlayerAttack:
- xor a
- ld hl, wDamage ; damage to eventually inflict, initialise to zero
- ldi [hl], a
- ld [hl], a
- ld hl, wPlayerMovePower
- ld a, [hli]
- and a
- ld d, a ; d = move power
- ret z ; return if move power is zero
- ld a, [hl] ; a = [wPlayerMoveType]
- cp FIRE ; types >= FIRE are all special
- jr nc, .specialAttack
- .physicalAttack
- ld hl, wEnemyMonDefense
- ld a, [hli]
- ld b, a
- ld c, [hl] ; bc = enemy defense
- ld a, [wEnemyBattleStatus3]
- bit HAS_REFLECT_UP, a ; check for Reflect
- jr z, .physicalAttackCritCheck
- ; if the enemy has used Reflect, double the enemy's defense
- sla c
- rl b
- .physicalAttackCritCheck
- ld hl, wBattleMonAttack
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .scaleStats
- ; in the case of a critical hit, reset the player's attack and the enemy's defense to their base values
- ld c, 3 ; defense stat
- call GetEnemyMonStat
- ld a, [H_PRODUCT + 2]
- ld b, a
- ld a, [H_PRODUCT + 3]
- ld c, a
- push bc
- ld hl, wPartyMon1Attack
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- pop bc
- jr .scaleStats
- .specialAttack
- ld hl, wEnemyMonSpecial
- ld a, [hli]
- ld b, a
- ld c, [hl] ; bc = enemy special
- ld a, [wEnemyBattleStatus3]
- bit HAS_LIGHT_SCREEN_UP, a ; check for Light Screen
- jr z, .specialAttackCritCheck
- ; if the enemy has used Light Screen, double the enemy's special
- sla c
- rl b
- ; reflect and light screen boosts do not cap the stat at 999, so weird things will happen during stats scaling if
- ; a Pokemon with 512 or more Defense has used Reflect, or if a Pokemon with 512 or more Special has used Light Screen
- .specialAttackCritCheck
- ld hl, wBattleMonSpecial
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .scaleStats
- ; in the case of a critical hit, reset the player's and enemy's specials to their base values
- ld c, 5 ; special stat
- call GetEnemyMonStat
- ld a, [H_PRODUCT + 2]
- ld b, a
- ld a, [H_PRODUCT + 3]
- ld c, a
- push bc
- ld hl, wPartyMon1Special
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- pop bc
- ; if either the offensive or defensive stat is too large to store in a byte, scale both stats by dividing them by 4
- ; this allows values with up to 10 bits (values up to 1023) to be handled
- ; anything larger will wrap around
- .scaleStats
- ld a, [hli]
- ld l, [hl]
- ld h, a ; hl = player's offensive stat
- or b ; is either high byte nonzero?
- jr z, .next ; if not, we don't need to scale
- ; bc /= 4 (scale enemy's defensive stat)
- srl b
- rr c
- srl b
- rr c
- ; defensive stat can actually end up as 0, leading to a division by 0 freeze during damage calculation
- ; hl /= 4 (scale player's offensive stat)
- srl h
- rr l
- srl h
- rr l
- ld a, l
- or h ; is the player's offensive stat 0?
- jr nz, .next
- inc l ; if the player's offensive stat is 0, bump it up to 1
- .next
- ld b, l ; b = player's offensive stat (possibly scaled)
- ; (c already contains enemy's defensive stat (possibly scaled))
- ld a, [wBattleMonLevel]
- ld e, a ; e = level
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .done
- sla e ; double level if it was a critical hit
- .done
- ld a, 1
- and a
- ret
- ; sets b, c, d, and e for the CalculateDamage routine in the case of an attack by the enemy mon
- GetDamageVarsForEnemyAttack:
- ld hl, wDamage ; damage to eventually inflict, initialise to zero
- xor a
- ld [hli], a
- ld [hl], a
- ld hl, wEnemyMovePower
- ld a, [hli]
- ld d, a ; d = move power
- and a
- ret z ; return if move power is zero
- ld a, [hl] ; a = [wEnemyMoveType]
- cp FIRE ; types >= FIRE are all special
- jr nc, .specialAttack
- .physicalAttack
- ld hl, wBattleMonDefense
- ld a, [hli]
- ld b, a
- ld c, [hl] ; bc = player defense
- ld a, [wPlayerBattleStatus3]
- bit HAS_REFLECT_UP, a ; check for Reflect
- jr z, .physicalAttackCritCheck
- ; if the player has used Reflect, double the player's defense
- sla c
- rl b
- .physicalAttackCritCheck
- ld hl, wEnemyMonAttack
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .scaleStats
- ; in the case of a critical hit, reset the player's defense and the enemy's attack to their base values
- ld hl, wPartyMon1Defense
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- ld a, [hli]
- ld b, a
- ld c, [hl]
- push bc
- ld c, 2 ; attack stat
- call GetEnemyMonStat
- ld hl, H_PRODUCT + 2
- pop bc
- jr .scaleStats
- .specialAttack
- ld hl, wBattleMonSpecial
- ld a, [hli]
- ld b, a
- ld c, [hl]
- ld a, [wPlayerBattleStatus3]
- bit HAS_LIGHT_SCREEN_UP, a ; check for Light Screen
- jr z, .specialAttackCritCheck
- ; if the player has used Light Screen, double the player's special
- sla c
- rl b
- ; reflect and light screen boosts do not cap the stat at 999, so weird things will happen during stats scaling if
- ; a Pokemon with 512 or more Defense has used Reflect, or if a Pokemon with 512 or more Special has used Light Screen
- .specialAttackCritCheck
- ld hl, wEnemyMonSpecial
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .scaleStats
- ; in the case of a critical hit, reset the player's and enemy's specials to their base values
- ld hl, wPartyMon1Special
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- ld a, [hli]
- ld b, a
- ld c, [hl]
- push bc
- ld c, 5 ; special stat
- call GetEnemyMonStat
- ld hl, H_PRODUCT + 2
- pop bc
- ; if either the offensive or defensive stat is too large to store in a byte, scale both stats by dividing them by 4
- ; this allows values with up to 10 bits (values up to 1023) to be handled
- ; anything larger will wrap around
- .scaleStats
- ld a, [hli]
- ld l, [hl]
- ld h, a ; hl = enemy's offensive stat
- or b ; is either high byte nonzero?
- jr z, .next ; if not, we don't need to scale
- ; bc /= 4 (scale player's defensive stat)
- srl b
- rr c
- srl b
- rr c
- ; defensive stat can actually end up as 0, leading to a division by 0 freeze during damage calculation
- ; hl /= 4 (scale enemy's offensive stat)
- srl h
- rr l
- srl h
- rr l
- ld a, l
- or h ; is the enemy's offensive stat 0?
- jr nz, .next
- inc l ; if the enemy's offensive stat is 0, bump it up to 1
- .next
- ld b, l ; b = enemy's offensive stat (possibly scaled)
- ; (c already contains player's defensive stat (possibly scaled))
- ld a, [wEnemyMonLevel]
- ld e, a
- ld a, [wCriticalHitOrOHKO]
- and a ; check for critical hit
- jr z, .done
- sla e ; double level if it was a critical hit
- .done
- ld a, $1
- and a
- and a
- ret
- ; get stat c of enemy mon
- ; c: stat to get (HP=1,Attack=2,Defense=3,Speed=4,Special=5)
- GetEnemyMonStat:
- push de
- push bc
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .notLinkBattle
- ld hl, wEnemyMon1Stats
- dec c
- sla c
- ld b, $0
- add hl, bc
- ld a, [wEnemyMonPartyPos]
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- ld a, [hli]
- ld [H_MULTIPLICAND + 1], a
- ld a, [hl]
- ld [H_MULTIPLICAND + 2], a
- pop bc
- pop de
- ret
- .notLinkBattle
- ld a, [wEnemyMonLevel]
- ld [wCurEnemyLVL], a
- ld a, [wEnemyMonSpecies]
- ld [wd0b5], a
- call GetMonHeader
- ld hl, wEnemyMonDVs
- ld de, wLoadedMonSpeedExp
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hl]
- ld [de], a
- pop bc
- ld b, $0
- ld hl, wLoadedMonSpeedExp - $b ; this base address makes CalcStat look in [wLoadedMonSpeedExp] for DVs
- call CalcStat
- pop de
- ret
- CalculateDamage:
- ; input:
- ; b: attack
- ; c: opponent defense
- ; d: base power
- ; e: level
- ld a, [H_WHOSETURN] ; whose turn?
- and a
- ld a, [wPlayerMoveEffect]
- jr z, .effect
- ld a, [wEnemyMoveEffect]
- .effect
- ; EXPLODE_EFFECT halves defense.
- cp EXPLODE_EFFECT
- jr nz, .ok
- srl c
- jr nz, .ok
- inc c ; ...with a minimum value of 1 (used as a divisor later on)
- .ok
- ; Multi-hit attacks may or may not have 0 bp.
- cp TWO_TO_FIVE_ATTACKS_EFFECT
- jr z, .skipbp
- cp $1e
- jr z, .skipbp
- ; Calculate OHKO damage based on remaining HP.
- cp OHKO_EFFECT
- jp z, JumpToOHKOMoveEffect
- ; Don't calculate damage for moves that don't do any.
- ld a, d ; base power
- and a
- ret z
- .skipbp
- xor a
- ld hl, H_DIVIDEND
- ldi [hl], a
- ldi [hl], a
- ld [hl], a
- ; Multiply level by 2
- ld a, e ; level
- add a
- jr nc, .nc
- push af
- ld a, 1
- ld [hl], a
- pop af
- .nc
- inc hl
- ldi [hl], a
- ; Divide by 5
- ld a, 5
- ldd [hl], a
- push bc
- ld b, 4
- call Divide
- pop bc
- ; Add 2
- inc [hl]
- inc [hl]
- inc hl ; multiplier
- ; Multiply by attack base power
- ld [hl], d
- call Multiply
- ; Multiply by attack stat
- ld [hl], b
- call Multiply
- ; Divide by defender's defense stat
- ld [hl], c
- ld b, 4
- call Divide
- ; Divide by 50
- ld [hl], 50
- ld b, 4
- call Divide
- ld hl, wDamage
- ld b, [hl]
- ld a, [H_QUOTIENT + 3]
- add b
- ld [H_QUOTIENT + 3], a
- jr nc, .asm_3dfd0
- ld a, [H_QUOTIENT + 2]
- inc a
- ld [H_QUOTIENT + 2], a
- and a
- jr z, .asm_3e004
- .asm_3dfd0
- ld a, [H_QUOTIENT]
- ld b, a
- ld a, [H_QUOTIENT + 1]
- or a
- jr nz, .asm_3e004
- ld a, [H_QUOTIENT + 2]
- cp 998 / $100
- jr c, .asm_3dfe8
- cp 998 / $100 + 1
- jr nc, .asm_3e004
- ld a, [H_QUOTIENT + 3]
- cp 998 % $100
- jr nc, .asm_3e004
- .asm_3dfe8
- inc hl
- ld a, [H_QUOTIENT + 3]
- ld b, [hl]
- add b
- ld [hld], a
- ld a, [H_QUOTIENT + 2]
- ld b, [hl]
- adc b
- ld [hl], a
- jr c, .asm_3e004
- ld a, [hl]
- cp 998 / $100
- jr c, .asm_3e00a
- cp 998 / $100 + 1
- jr nc, .asm_3e004
- inc hl
- ld a, [hld]
- cp 998 % $100
- jr c, .asm_3e00a
- .asm_3e004
- ; cap at 997
- ld a, 997 / $100
- ld [hli], a
- ld a, 997 % $100
- ld [hld], a
- .asm_3e00a
- ; add 2
- inc hl
- ld a, [hl]
- add 2
- ld [hld], a
- jr nc, .done
- inc [hl]
- .done
- ; minimum damage is 1
- ld a, 1
- and a
- ret
- JumpToOHKOMoveEffect:
- call JumpMoveEffect
- ld a, [wMoveMissed]
- dec a
- ret
- UnusedHighCriticalMoves:
- db KARATE_CHOP
- db RAZOR_LEAF
- db CRABHAMMER
- db SLASH
- db $FF
- ; determines if attack is a critical hit
- ; azure heights claims "the fastest pokémon (who are,not coincidentally,
- ; among the most popular) tend to CH about 20 to 25% of the time."
- CriticalHitTest:
- xor a
- ld [wCriticalHitOrOHKO], a
- ld a, [H_WHOSETURN]
- and a
- ld a, [wEnemyMonSpecies]
- jr nz, .handleEnemy
- ld a, [wBattleMonSpecies]
- .handleEnemy
- ld [wd0b5], a
- call GetMonHeader
- ld a, [wMonHBaseSpeed]
- ld b, a
- srl b ; (effective (base speed/2))
- ld a, [H_WHOSETURN]
- and a
- ld hl, wPlayerMovePower
- ld de, wPlayerBattleStatus2
- jr z, .calcCriticalHitProbability
- ld hl, wEnemyMovePower
- ld de, wEnemyBattleStatus2
- .calcCriticalHitProbability
- ld a, [hld] ; read base power from RAM
- and a
- ret z ; do nothing if zero
- dec hl
- ld c, [hl] ; read move id
- ld a, [de]
- bit GETTING_PUMPED, a ; test for focus energy
- jr nz, .focusEnergyUsed ; bug: using focus energy causes a shift to the right instead of left,
- ; resulting in 1/4 the usual crit chance
- sla b ; (effective (base speed/2)*2)
- jr nc, .noFocusEnergyUsed
- ld b, $ff ; cap at 255/256
- jr .noFocusEnergyUsed
- .focusEnergyUsed
- srl b
- .noFocusEnergyUsed
- ld hl, HighCriticalMoves ; table of high critical hit moves
- .Loop
- ld a, [hli] ; read move from move table
- cp c ; does it match the move about to be used?
- jr z, .HighCritical ; if so, the move about to be used is a high critical hit ratio move
- inc a ; move on to the next move, FF terminates loop
- jr nz, .Loop ; check the next move in HighCriticalMoves
- srl b ; /2 for regular move (effective (base speed / 2))
- jr .SkipHighCritical ; continue as a normal move
- .HighCritical
- sla b ; *2 for high critical hit moves
- jr nc, .noCarry
- ld b, $ff ; cap at 255/256
- .noCarry
- sla b ; *4 for high critical move (effective (base speed/2)*8))
- jr nc, .SkipHighCritical
- ld b, $ff
- .SkipHighCritical
- call BattleRandom ; generates a random value, in "a"
- rlc a
- rlc a
- rlc a
- cp b ; check a against calculated crit rate
- ret nc ; no critical hit if no borrow
- ld a, $1
- ld [wCriticalHitOrOHKO], a ; set critical hit flag
- ret
- ; high critical hit moves
- HighCriticalMoves:
- db KARATE_CHOP
- db RAZOR_LEAF
- db CRABHAMMER
- db SLASH
- db $FF
- ; function to determine if Counter hits and if so, how much damage it does
- HandleCounterMove:
- ; The variables checked by Counter are updated whenever the cursor points to a new move in the battle selection menu.
- ; This is irrelevant for the opponent's side outside of link battles, since the move selection is controlled by the AI.
- ; However, in the scenario where the player switches out and the opponent uses Counter,
- ; the outcome may be affected by the player's actions in the move selection menu prior to switching the Pokemon.
- ; This might also lead to desync glitches in link battles.
- ld a, [H_WHOSETURN] ; whose turn
- and a
- ; player's turn
- ld hl, wEnemySelectedMove
- ld de, wEnemyMovePower
- ld a, [wPlayerSelectedMove]
- jr z, .next
- ; enemy's turn
- ld hl, wPlayerSelectedMove
- ld de, wPlayerMovePower
- ld a, [wEnemySelectedMove]
- .next
- cp COUNTER
- ret nz ; return if not using Counter
- ld a, $01
- ld [wMoveMissed], a ; initialize the move missed variable to true (it is set to false below if the move hits)
- ld a, [hl]
- cp COUNTER
- ret z ; miss if the opponent's last selected move is Counter.
- ld a, [de]
- and a
- ret z ; miss if the opponent's last selected move's Base Power is 0.
- ; check if the move the target last selected was Normal or Fighting type
- inc de
- ld a, [de]
- and a ; normal type
- jr z, .counterableType
- cp FIGHTING
- jr z, .counterableType
- ; if the move wasn't Normal or Fighting type, miss
- xor a
- ret
- .counterableType
- ld hl, wDamage
- ld a, [hli]
- or [hl]
- ret z ; If we made it here, Counter still misses if the last move used in battle did no damage to its target.
- ; wDamage is shared by both players, so Counter may strike back damage dealt by the Counter user itself
- ; if the conditions meet, even though 99% of the times damage will come from the target.
- ; if it did damage, double it
- ld a, [hl]
- add a
- ldd [hl], a
- ld a, [hl]
- adc a
- ld [hl], a
- jr nc, .noCarry
- ; damage is capped at 0xFFFF
- ld a, $ff
- ld [hli], a
- ld [hl], a
- .noCarry
- xor a
- ld [wMoveMissed], a
- call MoveHitTest ; do the normal move hit test in addition to Counter's special rules
- xor a
- ret
- ApplyAttackToEnemyPokemon:
- ld a, [wPlayerMoveEffect]
- cp OHKO_EFFECT
- jr z, ApplyDamageToEnemyPokemon
- cp SUPER_FANG_EFFECT
- jr z, .superFangEffect
- cp SPECIAL_DAMAGE_EFFECT
- jr z, .specialDamage
- ld a, [wPlayerMovePower]
- and a
- jp z, ApplyAttackToEnemyPokemonDone ; no attack to apply if base power is 0
- jr ApplyDamageToEnemyPokemon
- .superFangEffect
- ; set the damage to half the target's HP
- ld hl, wEnemyMonHP
- ld de, wDamage
- ld a, [hli]
- srl a
- ld [de], a
- inc de
- ld b, a
- ld a, [hl]
- rr a
- ld [de], a
- or b
- jr nz, ApplyDamageToEnemyPokemon
- ; make sure Super Fang's damage is always at least 1
- ld a, $01
- ld [de], a
- jr ApplyDamageToEnemyPokemon
- .specialDamage
- ld hl, wBattleMonLevel
- ld a, [hl]
- ld b, a ; Seismic Toss deals damage equal to the user's level
- ld a, [wPlayerMoveNum]
- cp SEISMIC_TOSS
- jr z, .storeDamage
- cp NIGHT_SHADE
- jr z, .storeDamage
- ld b, SONICBOOM_DAMAGE ; 20
- cp SONICBOOM
- jr z, .storeDamage
- ld b, DRAGON_RAGE_DAMAGE ; 40
- cp DRAGON_RAGE
- jr z, .storeDamage
- ; Psywave
- ld a, [hl]
- ld b, a
- srl a
- add b
- ld b, a ; b = level * 1.5
- ; loop until a random number in the range [1, b) is found
- .loop
- call BattleRandom
- and a
- jr z, .loop
- cp b
- jr nc, .loop
- ld b, a
- .storeDamage ; store damage value at b
- ld hl, wDamage
- xor a
- ld [hli], a
- ld a, b
- ld [hl], a
- ApplyDamageToEnemyPokemon:
- ld hl, wDamage
- ld a, [hli]
- ld b, a
- ld a, [hl]
- or b
- jr z, ApplyAttackToEnemyPokemonDone ; we're done if damage is 0
- ld a, [wEnemyBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a ; does the enemy have a substitute?
- jp nz, AttackSubstitute
- ; subtract the damage from the pokemon's current HP
- ; also, save the current HP at wHPBarOldHP
- ld a, [hld]
- ld b, a
- ld a, [wEnemyMonHP + 1]
- ld [wHPBarOldHP], a
- sub b
- ld [wEnemyMonHP + 1], a
- ld a, [hl]
- ld b, a
- ld a, [wEnemyMonHP]
- ld [wHPBarOldHP+1], a
- sbc b
- ld [wEnemyMonHP], a
- jr nc, .animateHpBar
- ; if more damage was done than the current HP, zero the HP and set the damage (wDamage)
- ; equal to how much HP the pokemon had before the attack
- ld a, [wHPBarOldHP+1]
- ld [hli], a
- ld a, [wHPBarOldHP]
- ld [hl], a
- xor a
- ld hl, wEnemyMonHP
- ld [hli], a
- ld [hl], a
- .animateHpBar
- ld hl, wEnemyMonMaxHP
- ld a, [hli]
- ld [wHPBarMaxHP+1], a
- ld a, [hl]
- ld [wHPBarMaxHP], a
- ld hl, wEnemyMonHP
- ld a, [hli]
- ld [wHPBarNewHP+1], a
- ld a, [hl]
- ld [wHPBarNewHP], a
- coord hl, 2, 2
- xor a
- ld [wHPBarType], a
- predef UpdateHPBar2 ; animate the HP bar shortening
- ApplyAttackToEnemyPokemonDone:
- jp DrawHUDsAndHPBars
- ApplyAttackToPlayerPokemon:
- ld a, [wEnemyMoveEffect]
- cp OHKO_EFFECT
- jr z, ApplyDamageToPlayerPokemon
- cp SUPER_FANG_EFFECT
- jr z, .superFangEffect
- cp SPECIAL_DAMAGE_EFFECT
- jr z, .specialDamage
- ld a, [wEnemyMovePower]
- and a
- jp z, ApplyAttackToPlayerPokemonDone
- jr ApplyDamageToPlayerPokemon
- .superFangEffect
- ; set the damage to half the target's HP
- ld hl, wBattleMonHP
- ld de, wDamage
- ld a, [hli]
- srl a
- ld [de], a
- inc de
- ld b, a
- ld a, [hl]
- rr a
- ld [de], a
- or b
- jr nz, ApplyDamageToPlayerPokemon
- ; make sure Super Fang's damage is always at least 1
- ld a, $01
- ld [de], a
- jr ApplyDamageToPlayerPokemon
- .specialDamage
- ld hl, wEnemyMonLevel
- ld a, [hl]
- ld b, a
- ld a, [wEnemyMoveNum]
- cp SEISMIC_TOSS
- jr z, .storeDamage
- cp NIGHT_SHADE
- jr z, .storeDamage
- ld b, SONICBOOM_DAMAGE
- cp SONICBOOM
- jr z, .storeDamage
- ld b, DRAGON_RAGE_DAMAGE
- cp DRAGON_RAGE
- jr z, .storeDamage
- ; Psywave
- ld a, [hl]
- ld b, a
- srl a
- add b
- ld b, a ; b = attacker's level * 1.5
- ; loop until a random number in the range [0, b) is found
- ; this differs from the range when the player attacks, which is [1, b)
- ; it's possible for the enemy to do 0 damage with Psywave, but the player always does at least 1 damage
- .loop
- call BattleRandom
- cp b
- jr nc, .loop
- ld b, a
- .storeDamage
- ld hl, wDamage
- xor a
- ld [hli], a
- ld a, b
- ld [hl], a
- ApplyDamageToPlayerPokemon:
- ld hl, wDamage
- ld a, [hli]
- ld b, a
- ld a, [hl]
- or b
- jr z, ApplyAttackToPlayerPokemonDone ; we're done if damage is 0
- ld a, [wPlayerBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a ; does the player have a substitute?
- jp nz, AttackSubstitute
- ; subtract the damage from the pokemon's current HP
- ; also, save the current HP at wHPBarOldHP and the new HP at wHPBarNewHP
- ld a, [hld]
- ld b, a
- ld a, [wBattleMonHP + 1]
- ld [wHPBarOldHP], a
- sub b
- ld [wBattleMonHP + 1], a
- ld [wHPBarNewHP], a
- ld b, [hl]
- ld a, [wBattleMonHP]
- ld [wHPBarOldHP+1], a
- sbc b
- ld [wBattleMonHP], a
- ld [wHPBarNewHP+1], a
- jr nc, .animateHpBar
- ; if more damage was done than the current HP, zero the HP and set the damage (wDamage)
- ; equal to how much HP the pokemon had before the attack
- ld a, [wHPBarOldHP+1]
- ld [hli], a
- ld a, [wHPBarOldHP]
- ld [hl], a
- xor a
- ld hl, wBattleMonHP
- ld [hli], a
- ld [hl], a
- ld hl, wHPBarNewHP
- ld [hli], a
- ld [hl], a
- .animateHpBar
- ld hl, wBattleMonMaxHP
- ld a, [hli]
- ld [wHPBarMaxHP+1], a
- ld a, [hl]
- ld [wHPBarMaxHP], a
- coord hl, 10, 9
- ld a, $01
- ld [wHPBarType], a
- predef UpdateHPBar2 ; animate the HP bar shortening
- ApplyAttackToPlayerPokemonDone:
- jp DrawHUDsAndHPBars
- AttackSubstitute:
- ; Unlike the two ApplyAttackToPokemon functions, Attack Substitute is shared by player and enemy.
- ; Self-confusion damage as well as Hi-Jump Kick and Jump Kick recoil cause a momentary turn swap before being applied.
- ; If the user has a Substitute up and would take damage because of that,
- ; damage will be applied to the other player's Substitute.
- ; Normal recoil such as from Double-Edge isn't affected by this glitch,
- ; because this function is never called in that case.
- ld hl, SubstituteTookDamageText
- call PrintText
- ; values for player turn
- ld de, wEnemySubstituteHP
- ld bc, wEnemyBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .applyDamageToSubstitute
- ; values for enemy turn
- ld de, wPlayerSubstituteHP
- ld bc, wPlayerBattleStatus2
- .applyDamageToSubstitute
- ld hl, wDamage
- ld a, [hli]
- and a
- jr nz, .substituteBroke ; damage > 0xFF always breaks substitutes
- ; subtract damage from HP of substitute
- ld a, [de]
- sub [hl]
- ld [de], a
- ret nc
- .substituteBroke
- ; If the target's Substitute breaks, wDamage isn't updated with the amount of HP
- ; the Substitute had before being attacked.
- ld h, b
- ld l, c
- res HAS_SUBSTITUTE_UP, [hl] ; unset the substitute bit
- ld hl, SubstituteBrokeText
- call PrintText
- ; flip whose turn it is for the next function call
- ld a, [H_WHOSETURN]
- xor $01
- ld [H_WHOSETURN], a
- callab HideSubstituteShowMonAnim ; animate the substitute breaking
- ; flip the turn back to the way it was
- ld a, [H_WHOSETURN]
- xor $01
- ld [H_WHOSETURN], a
- ld hl, wPlayerMoveEffect ; value for player's turn
- and a
- jr z, .nullifyEffect
- ld hl, wEnemyMoveEffect ; value for enemy's turn
- .nullifyEffect
- xor a
- ld [hl], a ; zero the effect of the attacker's move
- jp DrawHUDsAndHPBars
- SubstituteTookDamageText:
- TX_FAR _SubstituteTookDamageText
- db "@"
- SubstituteBrokeText:
- TX_FAR _SubstituteBrokeText
- db "@"
- ; this function raises the attack modifier of a pokemon using Rage when that pokemon is attacked
- HandleBuildingRage:
- ; values for the player turn
- ld hl, wEnemyBattleStatus2
- ld de, wEnemyMonStatMods
- ld bc, wEnemyMoveNum
- ld a, [H_WHOSETURN]
- and a
- jr z, .next
- ; values for the enemy turn
- ld hl, wPlayerBattleStatus2
- ld de, wPlayerMonStatMods
- ld bc, wPlayerMoveNum
- .next
- bit USING_RAGE, [hl] ; is the pokemon being attacked under the effect of Rage?
- ret z ; return if not
- ld a, [de]
- cp $0d ; maximum stat modifier value
- ret z ; return if attack modifier is already maxed
- ld a, [H_WHOSETURN]
- xor $01 ; flip turn for the stat modifier raising function
- ld [H_WHOSETURN], a
- ; temporarily change the target pokemon's move to $00 and the effect to the one
- ; that causes the attack modifier to go up one stage
- ld h, b
- ld l, c
- ld [hl], $00 ; null move number
- inc hl
- ld [hl], ATTACK_UP1_EFFECT
- push hl
- ld hl, BuildingRageText
- call PrintText
- call StatModifierUpEffect ; stat modifier raising function
- pop hl
- xor a
- ldd [hl], a ; null move effect
- ld a, RAGE
- ld [hl], a ; restore the target pokemon's move number to Rage
- ld a, [H_WHOSETURN]
- xor $01 ; flip turn back to the way it was
- ld [H_WHOSETURN], a
- ret
- BuildingRageText:
- TX_FAR _BuildingRageText
- db "@"
- ; copy last move for Mirror Move
- ; sets zero flag on failure and unsets zero flag on success
- MirrorMoveCopyMove:
- ; Mirror Move makes use of ccf1 (wPlayerUsedMove) and ccf2 (wEnemyUsedMove) addresses,
- ; which are mainly used to print the "[Pokemon] used [Move]" text.
- ; Both are set to 0 whenever a new Pokemon is sent out
- ; ccf1 is also set to 0 whenever the player is fast asleep or frozen solid.
- ; ccf2 is also set to 0 whenever the enemy is fast asleep or frozen solid.
- ld a, [H_WHOSETURN]
- and a
- ; values for player turn
- ld a, [wEnemyUsedMove]
- ld hl, wPlayerSelectedMove
- ld de, wPlayerMoveNum
- jr z, .next
- ; values for enemy turn
- ld a, [wPlayerUsedMove]
- ld de, wEnemyMoveNum
- ld hl, wEnemySelectedMove
- .next
- ld [hl], a
- cp MIRROR_MOVE ; did the target Pokemon last use Mirror Move, and miss?
- jr z, .mirrorMoveFailed
- and a ; has the target selected any move yet?
- jr nz, ReloadMoveData
- .mirrorMoveFailed
- ld hl, MirrorMoveFailedText
- call PrintText
- xor a
- ret
- MirrorMoveFailedText:
- TX_FAR _MirrorMoveFailedText
- db "@"
- ; function used to reload move data for moves like Mirror Move and Metronome
- ReloadMoveData:
- ld [wd11e], a
- dec a
- ld hl, Moves
- ld bc, MoveEnd - Moves
- call AddNTimes
- ld a, BANK(Moves)
- call FarCopyData ; copy the move's stats
- call IncrementMovePP
- ; the follow two function calls are used to reload the move name
- call GetMoveName
- call CopyStringToCF4B
- ld a, $01
- and a
- ret
- ; function that picks a random move for metronome
- MetronomePickMove:
- xor a
- ld [wAnimationType], a
- ld a, METRONOME
- call PlayMoveAnimation ; play Metronome's animation
- ; values for player turn
- ld de, wPlayerMoveNum
- ld hl, wPlayerSelectedMove
- ld a, [H_WHOSETURN]
- and a
- jr z, .pickMoveLoop
- ; values for enemy turn
- ld de, wEnemyMoveNum
- ld hl, wEnemySelectedMove
- ; loop to pick a random number in the range [1, $a5) to be the move used by Metronome
- .pickMoveLoop
- call BattleRandom
- and a
- jr z, .pickMoveLoop
- cp NUM_ATTACKS + 1 ; max normal move number + 1 (this is Struggle's move number)
- jr nc, .pickMoveLoop
- cp METRONOME
- jr z, .pickMoveLoop
- ld [hl], a
- jr ReloadMoveData
- ; this function increments the current move's PP
- ; it's used to prevent moves that run another move within the same turn
- ; (like Mirror Move and Metronome) from losing 2 PP
- IncrementMovePP:
- ld a, [H_WHOSETURN]
- and a
- ; values for player turn
- ld hl, wBattleMonPP
- ld de, wPartyMon1PP
- ld a, [wPlayerMoveListIndex]
- jr z, .next
- ; values for enemy turn
- ld hl, wEnemyMonPP
- ld de, wEnemyMon1PP
- ld a, [wEnemyMoveListIndex]
- .next
- ld b, $00
- ld c, a
- add hl, bc
- inc [hl] ; increment PP in the currently battling pokemon memory location
- ld h, d
- ld l, e
- add hl, bc
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMonNumber] ; value for player turn
- jr z, .updatePP
- ld a, [wEnemyMonPartyPos] ; value for enemy turn
- .updatePP
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- inc [hl] ; increment PP in the party memory location
- ret
- ; function to adjust the base damage of an attack to account for type effectiveness
- AdjustDamageForMoveType:
- ; values for player turn
- ld hl, wBattleMonType
- ld a, [hli]
- ld b, a ; b = type 1 of attacker
- ld c, [hl] ; c = type 2 of attacker
- ld hl, wEnemyMonType
- ld a, [hli]
- ld d, a ; d = type 1 of defender
- ld e, [hl] ; e = type 2 of defender
- ld a, [wPlayerMoveType]
- ld [wMoveType], a
- ld a, [H_WHOSETURN]
- and a
- jr z, .next
- ; values for enemy turn
- ld hl, wEnemyMonType
- ld a, [hli]
- ld b, a ; b = type 1 of attacker
- ld c, [hl] ; c = type 2 of attacker
- ld hl, wBattleMonType
- ld a, [hli]
- ld d, a ; d = type 1 of defender
- ld e, [hl] ; e = type 2 of defender
- ld a, [wEnemyMoveType]
- ld [wMoveType], a
- .next
- ld a, [wMoveType]
- cp b ; does the move type match type 1 of the attacker?
- jr z, .sameTypeAttackBonus
- cp c ; does the move type match type 2 of the attacker?
- jr z, .sameTypeAttackBonus
- jr .skipSameTypeAttackBonus
- .sameTypeAttackBonus
- ; if the move type matches one of the attacker's types
- ld hl, wDamage + 1
- ld a, [hld]
- ld h, [hl]
- ld l, a ; hl = damage
- ld b, h
- ld c, l ; bc = damage
- srl b
- rr c ; bc = floor(0.5 * damage)
- add hl, bc ; hl = floor(1.5 * damage)
- ; store damage
- ld a, h
- ld [wDamage], a
- ld a, l
- ld [wDamage + 1], a
- ld hl, wDamageMultipliers
- set 7, [hl]
- .skipSameTypeAttackBonus
- ld a, [wMoveType]
- ld b, a
- ld hl, TypeEffects
- .loop
- ld a, [hli] ; a = "attacking type" of the current type pair
- cp $ff
- jr z, .done
- cp b ; does move type match "attacking type"?
- jr nz, .nextTypePair
- ld a, [hl] ; a = "defending type" of the current type pair
- cp d ; does type 1 of defender match "defending type"?
- jr z, .matchingPairFound
- cp e ; does type 2 of defender match "defending type"?
- jr z, .matchingPairFound
- jr .nextTypePair
- .matchingPairFound
- ; if the move type matches the "attacking type" and one of the defender's types matches the "defending type"
- push hl
- push bc
- inc hl
- ld a, [wDamageMultipliers]
- and $80
- ld b, a
- ld a, [hl] ; a = damage multiplier
- ld [H_MULTIPLIER], a
- add b
- ld [wDamageMultipliers], a
- xor a
- ld [H_MULTIPLICAND], a
- ld hl, wDamage
- ld a, [hli]
- ld [H_MULTIPLICAND + 1], a
- ld a, [hld]
- ld [H_MULTIPLICAND + 2], a
- call Multiply
- ld a, 10
- ld [H_DIVISOR], a
- ld b, $04
- call Divide
- ld a, [H_QUOTIENT + 2]
- ld [hli], a
- ld b, a
- ld a, [H_QUOTIENT + 3]
- ld [hl], a
- or b ; is damage 0?
- jr nz, .skipTypeImmunity
- .typeImmunity
- ; if damage is 0, make the move miss
- ; this only occurs if a move that would do 2 or 3 damage is 0.25x effective against the target
- inc a
- ld [wMoveMissed], a
- .skipTypeImmunity
- pop bc
- pop hl
- .nextTypePair
- inc hl
- inc hl
- jp .loop
- .done
- ret
- ; function to tell how effective the type of an enemy attack is on the player's current pokemon
- ; this doesn't take into account the effects that dual types can have
- ; (e.g. 4x weakness / resistance, weaknesses and resistances canceling)
- ; the result is stored in [wTypeEffectiveness]
- ; ($05 is not very effective, $10 is neutral, $14 is super effective)
- ; as far is can tell, this is only used once in some AI code to help decide which move to use
- AIGetTypeEffectiveness:
- ld a, [wEnemyMoveType]
- ld d, a ; d = type of enemy move
- ld hl, wBattleMonType
- ld b, [hl] ; b = type 1 of player's pokemon
- inc hl
- ld c, [hl] ; c = type 2 of player's pokemon
- ld a, $10
- ld [wTypeEffectiveness], a ; initialize to neutral effectiveness
- ld hl, TypeEffects
- .loop
- ld a, [hli]
- cp $ff
- ret z
- cp d ; match the type of the move
- jr nz, .nextTypePair1
- ld a, [hli]
- cp b ; match with type 1 of pokemon
- jr z, .done
- cp c ; or match with type 2 of pokemon
- jr z, .done
- jr .nextTypePair2
- .nextTypePair1
- inc hl
- .nextTypePair2
- inc hl
- jr .loop
- .done
- ld a, [hl]
- ld [wTypeEffectiveness], a ; store damage multiplier
- ret
- INCLUDE "data/type_effects.asm"
- ; some tests that need to pass for a move to hit
- MoveHitTest:
- ; player's turn
- ld hl, wEnemyBattleStatus1
- ld de, wPlayerMoveEffect
- ld bc, wEnemyMonStatus
- ld a, [H_WHOSETURN]
- and a
- jr z, .dreamEaterCheck
- ; enemy's turn
- ld hl, wPlayerBattleStatus1
- ld de, wEnemyMoveEffect
- ld bc, wBattleMonStatus
- .dreamEaterCheck
- ld a, [de]
- cp DREAM_EATER_EFFECT
- jr nz, .swiftCheck
- ld a, [bc]
- and SLP ; is the target pokemon sleeping?
- jp z, .moveMissed
- .swiftCheck
- ld a, [de]
- cp SWIFT_EFFECT
- ret z ; Swift never misses (interestingly, Azure Heights lists this is a myth, but it appears to be true)
- call CheckTargetSubstitute ; substitute check (note that this overwrites a)
- jr z, .checkForDigOrFlyStatus
- ; this code is buggy. it's supposed to prevent HP draining moves from working on substitutes.
- ; since $7b79 overwrites a with either $00 or $01, it never works.
- cp DRAIN_HP_EFFECT
- jp z, .moveMissed
- cp DREAM_EATER_EFFECT
- jp z, .moveMissed
- .checkForDigOrFlyStatus
- bit INVULNERABLE, [hl]
- jp nz, .moveMissed
- ld a, [H_WHOSETURN]
- and a
- jr nz, .enemyTurn
- .playerTurn
- ; this checks if the move effect is disallowed by mist
- ld a, [wPlayerMoveEffect]
- cp ATTACK_DOWN1_EFFECT
- jr c, .skipEnemyMistCheck
- cp HAZE_EFFECT + 1
- jr c, .enemyMistCheck
- cp ATTACK_DOWN2_EFFECT
- jr c, .skipEnemyMistCheck
- cp REFLECT_EFFECT + 1
- jr c, .enemyMistCheck
- jr .skipEnemyMistCheck
- .enemyMistCheck
- ; if move effect is from $12 to $19 inclusive or $3a to $41 inclusive
- ; i.e. the following moves
- ; GROWL, TAIL WHIP, LEER, STRING SHOT, SAND-ATTACK, SMOKESCREEN, KINESIS,
- ; FLASH, CONVERSION*, HAZE*, SCREECH, LIGHT SCREEN*, REFLECT*
- ; the moves that are marked with an asterisk are not affected since this
- ; function is not called when those moves are used
- ld a, [wEnemyBattleStatus2]
- bit PROTECTED_BY_MIST, a ; is mon protected by mist?
- jp nz, .moveMissed
- .skipEnemyMistCheck
- ld a, [wPlayerBattleStatus2]
- bit USING_X_ACCURACY, a ; is the player using X Accuracy?
- ret nz ; if so, always hit regardless of accuracy/evasion
- jr .calcHitChance
- .enemyTurn
- ld a, [wEnemyMoveEffect]
- cp ATTACK_DOWN1_EFFECT
- jr c, .skipPlayerMistCheck
- cp HAZE_EFFECT + 1
- jr c, .playerMistCheck
- cp ATTACK_DOWN2_EFFECT
- jr c, .skipPlayerMistCheck
- cp REFLECT_EFFECT + 1
- jr c, .playerMistCheck
- jr .skipPlayerMistCheck
- .playerMistCheck
- ; similar to enemy mist check
- ld a, [wPlayerBattleStatus2]
- bit PROTECTED_BY_MIST, a ; is mon protected by mist?
- jp nz, .moveMissed
- .skipPlayerMistCheck
- ld a, [wEnemyBattleStatus2]
- bit USING_X_ACCURACY, a ; is the enemy using X Accuracy?
- ret nz ; if so, always hit regardless of accuracy/evasion
- .calcHitChance
- call CalcHitChance ; scale the move accuracy according to attacker's accuracy and target's evasion
- ld a, [wPlayerMoveAccuracy]
- ld b, a
- ld a, [H_WHOSETURN]
- and a
- jr z, .doAccuracyCheck
- ld a, [wEnemyMoveAccuracy]
- ld b, a
- .doAccuracyCheck
- ; if the random number generated is greater than or equal to the scaled accuracy, the move misses
- ; note that this means that even the highest accuracy is still just a 255/256 chance, not 100%
- call BattleRandom
- cp b
- jr nc, .moveMissed
- ret
- .moveMissed
- xor a
- ld hl, wDamage ; zero the damage
- ld [hli], a
- ld [hl], a
- inc a
- ld [wMoveMissed], a
- ld a, [H_WHOSETURN]
- and a
- jr z, .playerTurn2
- .enemyTurn2
- ld hl, wEnemyBattleStatus1
- res USING_TRAPPING_MOVE, [hl] ; end multi-turn attack e.g. wrap
- ret
- .playerTurn2
- ld hl, wPlayerBattleStatus1
- res USING_TRAPPING_MOVE, [hl] ; end multi-turn attack e.g. wrap
- ret
- ; values for player turn
- CalcHitChance:
- ld hl, wPlayerMoveAccuracy
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMonAccuracyMod]
- ld b, a
- ld a, [wEnemyMonEvasionMod]
- ld c, a
- jr z, .next
- ; values for enemy turn
- ld hl, wEnemyMoveAccuracy
- ld a, [wEnemyMonAccuracyMod]
- ld b, a
- ld a, [wPlayerMonEvasionMod]
- ld c, a
- .next
- ld a, $0e
- sub c
- ld c, a ; c = 14 - EVASIONMOD (this "reflects" the value over 7, so that an increase in the target's evasion
- ; decreases the hit chance instead of increasing the hit chance)
- ; zero the high bytes of the multiplicand
- xor a
- ld [H_MULTIPLICAND], a
- ld [H_MULTIPLICAND + 1], a
- ld a, [hl]
- ld [H_MULTIPLICAND + 2], a ; set multiplicand to move accuracy
- push hl
- ld d, $02 ; loop has two iterations
- ; loop to do the calculations, the first iteration multiplies by the accuracy ratio and
- ; the second iteration multiplies by the evasion ratio
- .loop
- push bc
- ld hl, StatModifierRatios ; stat modifier ratios
- dec b
- sla b
- ld c, b
- ld b, $00
- add hl, bc ; hl = address of stat modifier ratio
- pop bc
- ld a, [hli]
- ld [H_MULTIPLIER], a ; set multiplier to the numerator of the ratio
- call Multiply
- ld a, [hl]
- ld [H_DIVISOR], a ; set divisor to the the denominator of the ratio
- ; (the dividend is the product of the previous multiplication)
- ld b, $04 ; number of bytes in the dividend
- call Divide
- ld a, [H_QUOTIENT + 3]
- ld b, a
- ld a, [H_QUOTIENT + 2]
- or b
- jp nz, .nextCalculation
- ; make sure the result is always at least one
- ld [H_QUOTIENT + 2], a
- ld a, $01
- ld [H_QUOTIENT + 3], a
- .nextCalculation
- ld b, c
- dec d
- jr nz, .loop
- ld a, [H_QUOTIENT + 2]
- and a ; is the calculated hit chance over 0xFF?
- ld a, [H_QUOTIENT + 3]
- jr z, .storeAccuracy
- ; if calculated hit chance over 0xFF
- ld a, $ff ; set the hit chance to 0xFF
- .storeAccuracy
- pop hl
- ld [hl], a ; store the hit chance in the move accuracy variable
- ret
- ; multiplies damage by a random percentage from ~85% to 100%
- RandomizeDamage:
- ld hl, wDamage
- ld a, [hli]
- and a
- jr nz, .DamageGreaterThanOne
- ld a, [hl]
- cp 2
- ret c ; return if damage is equal to 0 or 1
- .DamageGreaterThanOne
- xor a
- ld [H_MULTIPLICAND], a
- dec hl
- ld a, [hli]
- ld [H_MULTIPLICAND + 1], a
- ld a, [hl]
- ld [H_MULTIPLICAND + 2], a
- ; loop until a random number greater than or equal to 217 is generated
- .loop
- call BattleRandom
- rrca
- cp 217
- jr c, .loop
- ld [H_MULTIPLIER], a
- call Multiply ; multiply damage by the random number, which is in the range [217, 255]
- ld a, 255
- ld [H_DIVISOR], a
- ld b, $4
- call Divide ; divide the result by 255
- ; store the modified damage
- ld a, [H_QUOTIENT + 2]
- ld hl, wDamage
- ld [hli], a
- ld a, [H_QUOTIENT + 3]
- ld [hl], a
- ret
- ; for more detailed commentary, see equivalent function for player side (ExecutePlayerMove)
- ExecuteEnemyMove:
- ld a, [wEnemySelectedMove]
- inc a
- jp z, ExecuteEnemyMoveDone
- call PrintGhostText
- jp z, ExecuteEnemyMoveDone
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .executeEnemyMove
- ld b, $1
- ld a, [wSerialExchangeNybbleReceiveData]
- cp LINKBATTLE_STRUGGLE
- jr z, .executeEnemyMove
- cp 4
- ret nc
- .executeEnemyMove
- ld hl, wAILayer2Encouragement
- inc [hl]
- xor a
- ld [wMoveMissed], a
- ld [wMoveDidntMiss], a
- ld a, $a
- ld [wDamageMultipliers], a
- call CheckEnemyStatusConditions
- jr nz, .enemyHasNoSpecialConditions
- jp hl
- .enemyHasNoSpecialConditions
- ld hl, wEnemyBattleStatus1
- bit CHARGING_UP, [hl] ; is the enemy charging up for attack?
- jr nz, EnemyCanExecuteChargingMove ; if so, jump
- call GetCurrentMove
- CheckIfEnemyNeedsToChargeUp:
- ld a, [wEnemyMoveEffect]
- cp CHARGE_EFFECT
- jp z, JumpMoveEffect
- cp FLY_EFFECT
- jp z, JumpMoveEffect
- jr EnemyCanExecuteMove
- EnemyCanExecuteChargingMove:
- ld hl, wEnemyBattleStatus1
- res CHARGING_UP, [hl] ; no longer charging up for attack
- res INVULNERABLE, [hl] ; no longer invulnerable to typical attacks
- ld a, [wEnemyMoveNum]
- ld [wd0b5], a
- ld a, BANK(MoveNames)
- ld [wPredefBank], a
- ld a, MOVE_NAME
- ld [wNameListType], a
- call GetName
- ld de, wcd6d
- call CopyStringToCF4B
- EnemyCanExecuteMove:
- xor a
- ld [wMonIsDisobedient], a
- call PrintMonName1Text
- ld a, [wEnemyMoveEffect]
- ld hl, ResidualEffects1
- ld de, $1
- call IsInArray
- jp c, JumpMoveEffect
- ld a, [wEnemyMoveEffect]
- ld hl, SpecialEffectsCont
- ld de, $1
- call IsInArray
- call c, JumpMoveEffect
- EnemyCalcMoveDamage:
- call SwapPlayerAndEnemyLevels
- ld a, [wEnemyMoveEffect]
- ld hl, SetDamageEffects
- ld de, $1
- call IsInArray
- jp c, EnemyMoveHitTest
- call CriticalHitTest
- call HandleCounterMove
- jr z, handleIfEnemyMoveMissed
- call SwapPlayerAndEnemyLevels
- call GetDamageVarsForEnemyAttack
- call SwapPlayerAndEnemyLevels
- call CalculateDamage
- jp z, EnemyCheckIfFlyOrChargeEffect
- call AdjustDamageForMoveType
- call RandomizeDamage
- EnemyMoveHitTest:
- call MoveHitTest
- handleIfEnemyMoveMissed:
- ld a, [wMoveMissed]
- and a
- jr z, .moveDidNotMiss
- ld a, [wEnemyMoveEffect]
- cp EXPLODE_EFFECT
- jr z, handleExplosionMiss
- jr EnemyCheckIfFlyOrChargeEffect
- .moveDidNotMiss
- call SwapPlayerAndEnemyLevels
- GetEnemyAnimationType:
- ld a, [wEnemyMoveEffect]
- and a
- ld a, $1
- jr z, playEnemyMoveAnimation
- ld a, $2
- jr playEnemyMoveAnimation
- handleExplosionMiss:
- call SwapPlayerAndEnemyLevels
- xor a
- playEnemyMoveAnimation:
- push af
- ld a, [wEnemyBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a ; does mon have a substitute?
- ld hl, HideSubstituteShowMonAnim
- ld b, BANK(HideSubstituteShowMonAnim)
- call nz, Bankswitch
- pop af
- ld [wAnimationType], a
- ld a, [wEnemyMoveNum]
- call PlayMoveAnimation
- call HandleExplodingAnimation
- call DrawEnemyHUDAndHPBar
- ld a, [wEnemyBattleStatus2]
- bit HAS_SUBSTITUTE_UP, a ; does mon have a substitute?
- ld hl, ReshowSubstituteAnim
- ld b, BANK(ReshowSubstituteAnim)
- call nz, Bankswitch ; slide the substitute's sprite out
- jr EnemyCheckIfMirrorMoveEffect
- EnemyCheckIfFlyOrChargeEffect:
- call SwapPlayerAndEnemyLevels
- ld c, 30
- call DelayFrames
- ld a, [wEnemyMoveEffect]
- cp FLY_EFFECT
- jr z, .playAnim
- cp CHARGE_EFFECT
- jr z, .playAnim
- jr EnemyCheckIfMirrorMoveEffect
- .playAnim
- xor a
- ld [wAnimationType], a
- ld a, STATUS_AFFECTED_ANIM
- call PlayMoveAnimation
- EnemyCheckIfMirrorMoveEffect:
- ld a, [wEnemyMoveEffect]
- cp MIRROR_MOVE_EFFECT
- jr nz, .notMirrorMoveEffect
- call MirrorMoveCopyMove
- jp z, ExecuteEnemyMoveDone
- jp CheckIfEnemyNeedsToChargeUp
- .notMirrorMoveEffect
- cp METRONOME_EFFECT
- jr nz, .notMetronomeEffect
- call MetronomePickMove
- jp CheckIfEnemyNeedsToChargeUp
- .notMetronomeEffect
- ld a, [wEnemyMoveEffect]
- ld hl, ResidualEffects2
- ld de, $1
- call IsInArray
- jp c, JumpMoveEffect
- ld a, [wMoveMissed]
- and a
- jr z, .moveDidNotMiss
- call PrintMoveFailureText
- ld a, [wEnemyMoveEffect]
- cp EXPLODE_EFFECT
- jr z, .handleExplosionMiss
- jp ExecuteEnemyMoveDone
- .moveDidNotMiss
- call ApplyAttackToPlayerPokemon
- call PrintCriticalOHKOText
- callab DisplayEffectiveness
- ld a, 1
- ld [wMoveDidntMiss], a
- .handleExplosionMiss
- ld a, [wEnemyMoveEffect]
- ld hl, AlwaysHappenSideEffects
- ld de, $1
- call IsInArray
- call c, JumpMoveEffect
- ld hl, wBattleMonHP
- ld a, [hli]
- ld b, [hl]
- or b
- ret z
- call HandleBuildingRage
- ld hl, wEnemyBattleStatus1
- bit ATTACKING_MULTIPLE_TIMES, [hl] ; is mon hitting multiple times? (example: double kick)
- jr z, .notMultiHitMove
- push hl
- ld hl, wEnemyNumAttacksLeft
- dec [hl]
- pop hl
- jp nz, GetEnemyAnimationType
- res ATTACKING_MULTIPLE_TIMES, [hl] ; mon is no longer hitting multiple times
- ld hl, HitXTimesText
- call PrintText
- xor a
- ld [wEnemyNumHits], a
- .notMultiHitMove
- ld a, [wEnemyMoveEffect]
- and a
- jr z, ExecuteEnemyMoveDone
- ld hl, SpecialEffects
- ld de, $1
- call IsInArray
- call nc, JumpMoveEffect
- jr ExecuteEnemyMoveDone
- HitXTimesText:
- TX_FAR _HitXTimesText
- db "@"
- ExecuteEnemyMoveDone:
- ld b, $1
- ret
- ; checks for various status conditions affecting the enemy mon
- ; stores whether the mon cannot use a move this turn in Z flag
- CheckEnemyStatusConditions:
- ld hl, wEnemyMonStatus
- ld a, [hl]
- and SLP ; sleep mask
- jr z, .checkIfFrozen
- dec a ; decrement number of turns left
- ld [wEnemyMonStatus], a
- and a
- jr z, .wokeUp ; if the number of turns hit 0, wake up
- ld hl, FastAsleepText
- call PrintText
- xor a
- ld [wAnimationType], a
- ld a, SLP_ANIM
- call PlayMoveAnimation
- jr .sleepDone
- .wokeUp
- ld hl, WokeUpText
- call PrintText
- .sleepDone
- xor a
- ld [wEnemyUsedMove], a
- ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
- jp .enemyReturnToHL
- .checkIfFrozen
- bit FRZ, [hl]
- jr z, .checkIfTrapped
- ld hl, IsFrozenText
- call PrintText
- xor a
- ld [wEnemyUsedMove], a
- ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
- jp .enemyReturnToHL
- .checkIfTrapped
- ld a, [wPlayerBattleStatus1]
- bit USING_TRAPPING_MOVE, a ; is the player using a multi-turn attack like warp
- jp z, .checkIfFlinched
- ld hl, CantMoveText
- call PrintText
- ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
- jp .enemyReturnToHL
- .checkIfFlinched
- ld hl, wEnemyBattleStatus1
- bit FLINCHED, [hl] ; check if enemy mon flinched
- jp z, .checkIfMustRecharge
- res FLINCHED, [hl]
- ld hl, FlinchedText
- call PrintText
- ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
- jp .enemyReturnToHL
- .checkIfMustRecharge
- ld hl, wEnemyBattleStatus2
- bit NEEDS_TO_RECHARGE, [hl] ; check if enemy mon has to recharge after using a move
- jr z, .checkIfAnyMoveDisabled
- res NEEDS_TO_RECHARGE, [hl]
- ld hl, MustRechargeText
- call PrintText
- ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
- jp .enemyReturnToHL
- .checkIfAnyMoveDisabled
- ld hl, wEnemyDisabledMove
- ld a, [hl]
- and a
- jr z, .checkIfConfused
- dec a ; decrement disable counter
- ld [hl], a
- and $f ; did disable counter hit 0?
- jr nz, .checkIfConfused
- ld [hl], a
- ld [wEnemyDisabledMoveNumber], a
- ld hl, DisabledNoMoreText
- call PrintText
- .checkIfConfused
- ld a, [wEnemyBattleStatus1]
- add a ; check if enemy mon is confused
- jp nc, .checkIfTriedToUseDisabledMove
- ld hl, wEnemyConfusedCounter
- dec [hl]
- jr nz, .isConfused
- ld hl, wEnemyBattleStatus1
- res CONFUSED, [hl] ; if confused counter hit 0, reset confusion status
- ld hl, ConfusedNoMoreText
- call PrintText
- jp .checkIfTriedToUseDisabledMove
- .isConfused
- ld hl, IsConfusedText
- call PrintText
- xor a
- ld [wAnimationType], a
- ld a, CONF_ANIM
- call PlayMoveAnimation
- call BattleRandom
- cp $80
- jr c, .checkIfTriedToUseDisabledMove
- ld hl, wEnemyBattleStatus1
- ld a, [hl]
- and 1 << CONFUSED ; if mon hurts itself, clear every other status from wEnemyBattleStatus1
- ld [hl], a
- ld hl, HurtItselfText
- call PrintText
- ld hl, wBattleMonDefense
- ld a, [hli]
- push af
- ld a, [hld]
- push af
- ld a, [wEnemyMonDefense]
- ld [hli], a
- ld a, [wEnemyMonDefense + 1]
- ld [hl], a
- ld hl, wEnemyMoveEffect
- push hl
- ld a, [hl]
- push af
- xor a
- ld [hli], a
- ld [wCriticalHitOrOHKO], a
- ld a, 40
- ld [hli], a
- xor a
- ld [hl], a
- call GetDamageVarsForEnemyAttack
- call CalculateDamage
- pop af
- pop hl
- ld [hl], a
- ld hl, wBattleMonDefense + 1
- pop af
- ld [hld], a
- pop af
- ld [hl], a
- xor a
- ld [wAnimationType], a
- ld [H_WHOSETURN], a
- ld a, POUND
- call PlayMoveAnimation
- ld a, $1
- ld [H_WHOSETURN], a
- call ApplyDamageToEnemyPokemon
- jr .monHurtItselfOrFullyParalysed
- .checkIfTriedToUseDisabledMove
- ; prevents a disabled move that was selected before being disabled from being used
- ld a, [wEnemyDisabledMoveNumber]
- and a
- jr z, .checkIfParalysed
- ld hl, wEnemySelectedMove
- cp [hl]
- jr nz, .checkIfParalysed
- call PrintMoveIsDisabledText
- ld hl, ExecuteEnemyMoveDone ; if a disabled move was somehow selected, player can't move this turn
- jp .enemyReturnToHL
- .checkIfParalysed
- ld hl, wEnemyMonStatus
- bit PAR, [hl]
- jr z, .checkIfUsingBide
- call BattleRandom
- cp $3f ; 25% to be fully paralysed
- jr nc, .checkIfUsingBide
- ld hl, FullyParalyzedText
- call PrintText
- .monHurtItselfOrFullyParalysed
- ld hl, wEnemyBattleStatus1
- ld a, [hl]
- ; clear bide, thrashing about, charging up, and multi-turn moves such as warp
- and $ff ^ ((1 << STORING_ENERGY) | (1 << THRASHING_ABOUT) | (1 << CHARGING_UP) | (1 << USING_TRAPPING_MOVE))
- ld [hl], a
- ld a, [wEnemyMoveEffect]
- cp FLY_EFFECT
- jr z, .flyOrChargeEffect
- cp CHARGE_EFFECT
- jr z, .flyOrChargeEffect
- jr .notFlyOrChargeEffect
- .flyOrChargeEffect
- xor a
- ld [wAnimationType], a
- ld a, STATUS_AFFECTED_ANIM
- call PlayMoveAnimation
- .notFlyOrChargeEffect
- ld hl, ExecuteEnemyMoveDone
- jp .enemyReturnToHL ; if using a two-turn move, enemy needs to recharge the first turn
- .checkIfUsingBide
- ld hl, wEnemyBattleStatus1
- bit STORING_ENERGY, [hl] ; is mon using bide?
- jr z, .checkIfThrashingAbout
- xor a
- ld [wEnemyMoveNum], a
- ld hl, wDamage
- ld a, [hli]
- ld b, a
- ld c, [hl]
- ld hl, wEnemyBideAccumulatedDamage + 1
- ld a, [hl]
- add c ; accumulate damage taken
- ld [hld], a
- ld a, [hl]
- adc b
- ld [hl], a
- ld hl, wEnemyNumAttacksLeft
- dec [hl] ; did Bide counter hit 0?
- jr z, .unleashEnergy
- ld hl, ExecuteEnemyMoveDone
- jp .enemyReturnToHL ; unless mon unleashes energy, can't move this turn
- .unleashEnergy
- ld hl, wEnemyBattleStatus1
- res STORING_ENERGY, [hl] ; not using bide any more
- ld hl, UnleashedEnergyText
- call PrintText
- ld a, $1
- ld [wEnemyMovePower], a
- ld hl, wEnemyBideAccumulatedDamage + 1
- ld a, [hld]
- add a
- ld b, a
- ld [wDamage + 1], a
- ld a, [hl]
- rl a ; double the damage
- ld [wDamage], a
- or b
- jr nz, .next
- ld a, $1
- ld [wMoveMissed], a
- .next
- xor a
- ld [hli], a
- ld [hl], a
- ld a, BIDE
- ld [wEnemyMoveNum], a
- call SwapPlayerAndEnemyLevels
- ld hl, handleIfEnemyMoveMissed ; skip damage calculation, DecrementPP and MoveHitTest
- jp .enemyReturnToHL
- .checkIfThrashingAbout
- bit THRASHING_ABOUT, [hl] ; is mon using thrash or petal dance?
- jr z, .checkIfUsingMultiturnMove
- ld a, THRASH
- ld [wEnemyMoveNum], a
- ld hl, ThrashingAboutText
- call PrintText
- ld hl, wEnemyNumAttacksLeft
- dec [hl] ; did Thrashing About counter hit 0?
- ld hl, EnemyCalcMoveDamage ; skip DecrementPP
- jp nz, .enemyReturnToHL
- push hl
- ld hl, wEnemyBattleStatus1
- res THRASHING_ABOUT, [hl] ; mon is no longer using thrash or petal dance
- set CONFUSED, [hl] ; mon is now confused
- call BattleRandom
- and $3
- inc a
- inc a ; confused for 2-5 turns
- ld [wEnemyConfusedCounter], a
- pop hl ; skip DecrementPP
- jp .enemyReturnToHL
- .checkIfUsingMultiturnMove
- bit USING_TRAPPING_MOVE, [hl] ; is mon using multi-turn move?
- jp z, .checkIfUsingRage
- ld hl, AttackContinuesText
- call PrintText
- ld hl, wEnemyNumAttacksLeft
- dec [hl] ; did multi-turn move end?
- ld hl, GetEnemyAnimationType ; if it didn't, skip damage calculation (deal damage equal to last hit),
- ; DecrementPP and MoveHitTest
- jp nz, .enemyReturnToHL
- jp .enemyReturnToHL
- .checkIfUsingRage
- ld a, [wEnemyBattleStatus2]
- bit USING_RAGE, a ; is mon using rage?
- jp z, .checkEnemyStatusConditionsDone ; if we made it this far, mon can move normally this turn
- ld a, RAGE
- ld [wd11e], a
- call GetMoveName
- call CopyStringToCF4B
- xor a
- ld [wEnemyMoveEffect], a
- ld hl, EnemyCanExecuteMove
- jp .enemyReturnToHL
- .enemyReturnToHL
- xor a ; set Z flag
- ret
- .checkEnemyStatusConditionsDone
- ld a, $1
- and a ; clear Z flag
- ret
- GetCurrentMove:
- ld a, [H_WHOSETURN]
- and a
- jp z, .player
- ld de, wEnemyMoveNum
- ld a, [wEnemySelectedMove]
- jr .selected
- .player
- ld de, wPlayerMoveNum
- ld a, [wFlags_D733]
- bit BIT_TEST_BATTLE, a
- ld a, [wTestBattlePlayerSelectedMove]
- jr nz, .selected
- ld a, [wPlayerSelectedMove]
- .selected
- ld [wd0b5], a
- dec a
- ld hl, Moves
- ld bc, MoveEnd - Moves
- call AddNTimes
- ld a, BANK(Moves)
- call FarCopyData
- ld a, BANK(MoveNames)
- ld [wPredefBank], a
- ld a, MOVE_NAME
- ld [wNameListType], a
- call GetName
- ld de, wcd6d
- jp CopyStringToCF4B
- LoadEnemyMonData:
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jp z, LoadEnemyMonFromParty
- ld a, [wEnemyMonSpecies2]
- ld [wEnemyMonSpecies], a
- ld [wd0b5], a
- call GetMonHeader
- ld a, [wEnemyBattleStatus3]
- bit TRANSFORMED, a ; is enemy mon transformed?
- ld hl, wTransformedEnemyMonOriginalDVs ; original DVs before transforming
- ld a, [hli]
- ld b, [hl]
- jr nz, .storeDVs
- ld a, [wIsInBattle]
- cp $2 ; is it a trainer battle?
- ; fixed DVs for trainer mon
- ld a, $98
- ld b, $88
- jr z, .storeDVs
- ; random DVs for wild mon
- call BattleRandom
- ld b, a
- call BattleRandom
- .storeDVs
- ld hl, wEnemyMonDVs
- ld [hli], a
- ld [hl], b
- ld de, wEnemyMonLevel
- ld a, [wCurEnemyLVL]
- ld [de], a
- inc de
- ld b, $0
- ld hl, wEnemyMonHP
- push hl
- call CalcStats
- pop hl
- ld a, [wIsInBattle]
- cp $2 ; is it a trainer battle?
- jr z, .copyHPAndStatusFromPartyData
- ld a, [wEnemyBattleStatus3]
- bit TRANSFORMED, a ; is enemy mon transformed?
- jr nz, .copyTypes ; if transformed, jump
- ; if it's a wild mon and not transformed, init the current HP to max HP and the status to 0
- ld a, [wEnemyMonMaxHP]
- ld [hli], a
- ld a, [wEnemyMonMaxHP+1]
- ld [hli], a
- xor a
- inc hl
- ld [hl], a ; init status to 0
- jr .copyTypes
- ; if it's a trainer mon, copy the HP and status from the enemy party data
- .copyHPAndStatusFromPartyData
- ld hl, wEnemyMon1HP
- ld a, [wWhichPokemon]
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- ld a, [hli]
- ld [wEnemyMonHP], a
- ld a, [hli]
- ld [wEnemyMonHP + 1], a
- ld a, [wWhichPokemon]
- ld [wEnemyMonPartyPos], a
- inc hl
- ld a, [hl]
- ld [wEnemyMonStatus], a
- jr .copyTypes
- .copyTypes
- ld hl, wMonHTypes
- ld de, wEnemyMonType
- ld a, [hli] ; copy type 1
- ld [de], a
- inc de
- ld a, [hli] ; copy type 2
- ld [de], a
- inc de
- ld a, [hli] ; copy catch rate
- ld [de], a
- inc de
- ld a, [wIsInBattle]
- cp $2 ; is it a trainer battle?
- jr nz, .copyStandardMoves
- ; if it's a trainer battle, copy moves from enemy party data
- ld hl, wEnemyMon1Moves
- ld a, [wWhichPokemon]
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- ld bc, NUM_MOVES
- call CopyData
- jr .loadMovePPs
- .copyStandardMoves
- ; for a wild mon, first copy default moves from the mon header
- ld hl, wMonHMoves
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hl]
- ld [de], a
- dec de
- dec de
- dec de
- xor a
- ld [wLearningMovesFromDayCare], a
- predef WriteMonMoves ; get moves based on current level
- .loadMovePPs
- ld hl, wEnemyMonMoves
- ld de, wEnemyMonPP - 1
- predef LoadMovePPs
- ld hl, wMonHBaseStats
- ld de, wEnemyMonBaseStats
- ld b, NUM_STATS
- .copyBaseStatsLoop
- ld a, [hli]
- ld [de], a
- inc de
- dec b
- jr nz, .copyBaseStatsLoop
- ld hl, wMonHCatchRate
- ld a, [hli]
- ld [de], a
- inc de
- ld a, [hl] ; base exp
- ld [de], a
- ld a, [wEnemyMonSpecies2]
- ld [wd11e], a
- call GetMonName
- ld hl, wcd6d
- ld de, wEnemyMonNick
- ld bc, NAME_LENGTH
- call CopyData
- ld a, [wEnemyMonSpecies2]
- ld [wd11e], a
- predef IndexToPokedex
- ld a, [wd11e]
- dec a
- ld c, a
- ld b, FLAG_SET
- ld hl, wPokedexSeen
- predef FlagActionPredef ; mark this mon as seen in the pokedex
- ld hl, wEnemyMonLevel
- ld de, wEnemyMonUnmodifiedLevel
- ld bc, 1 + NUM_STATS * 2
- call CopyData
- ld a, $7 ; default stat mod
- ld b, NUM_STAT_MODS ; number of stat mods
- ld hl, wEnemyMonStatMods
- .statModLoop
- ld [hli], a
- dec b
- jr nz, .statModLoop
- ret
- ; calls BattleTransition to show the battle transition animation and initializes some battle variables
- DoBattleTransitionAndInitBattleVariables:
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .next
- ; link battle
- xor a
- ld [wMenuJoypadPollCount], a
- callab DisplayLinkBattleVersusTextBox
- ld a, $1
- ld [wUpdateSpritesEnabled], a
- call ClearScreen
- .next
- call DelayFrame
- predef BattleTransition
- callab LoadHudAndHpBarAndStatusTilePatterns
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- ld a, $ff
- ld [wUpdateSpritesEnabled], a
- call ClearSprites
- call ClearScreen
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- ld [hWY], a
- ld [rWY], a
- ld [hTilesetType], a
- ld hl, wPlayerStatsToDouble
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hli], a
- ld [hl], a
- ld [wPlayerDisabledMove], a
- ret
- ; swaps the level values of the BattleMon and EnemyMon structs
- SwapPlayerAndEnemyLevels:
- push bc
- ld a, [wBattleMonLevel]
- ld b, a
- ld a, [wEnemyMonLevel]
- ld [wBattleMonLevel], a
- ld a, b
- ld [wEnemyMonLevel], a
- pop bc
- ret
- ; loads either red back pic or old man back pic
- ; also writes OAM data and loads tile patterns for the Red or Old Man back sprite's head
- ; (for use when scrolling the player sprite and enemy's silhouettes on screen)
- LoadPlayerBackPic:
- ld a, [wBattleType]
- dec a ; is it the old man tutorial?
- ld de, RedPicBack
- jr nz, .next
- ld de, OldManPic
- .next
- ld a, BANK(RedPicBack)
- call UncompressSpriteFromDE
- predef ScaleSpriteByTwo
- ld hl, wOAMBuffer
- xor a
- ld [hOAMTile], a ; initial tile number
- ld b, $7 ; 7 columns
- ld e, $a0 ; X for the left-most column
- .loop ; each loop iteration writes 3 OAM entries in a vertical column
- ld c, $3 ; 3 tiles per column
- ld d, $38 ; Y for the top of each column
- .innerLoop ; each loop iteration writes 1 OAM entry in the column
- ld [hl], d ; OAM Y
- inc hl
- ld [hl], e ; OAM X
- ld a, $8 ; height of tile
- add d ; increase Y by height of tile
- ld d, a
- inc hl
- ld a, [hOAMTile]
- ld [hli], a ; OAM tile number
- inc a ; increment tile number
- ld [hOAMTile], a
- inc hl
- dec c
- jr nz, .innerLoop
- ld a, [hOAMTile]
- add $4 ; increase tile number by 4
- ld [hOAMTile], a
- ld a, $8 ; width of tile
- add e ; increase X by width of tile
- ld e, a
- dec b
- jr nz, .loop
- ld de, vBackPic
- call InterlaceMergeSpriteBuffers
- ld a, $a
- ld [$0], a
- xor a
- ld [$4000], a
- ld hl, vSprites
- ld de, sSpriteBuffer1
- ld a, [H_LOADEDROMBANK]
- ld b, a
- ld c, 7 * 7
- call CopyVideoData
- xor a
- ld [$0], a
- ld a, $31
- ld [hStartTileID], a
- coord hl, 1, 5
- predef_jump CopyUncompressedPicToTilemap
- ; does nothing since no stats are ever selected (barring glitches)
- DoubleOrHalveSelectedStats:
- callab DoubleSelectedStats
- jpab HalveSelectedStats
- ScrollTrainerPicAfterBattle:
- jpab _ScrollTrainerPicAfterBattle
- ApplyBurnAndParalysisPenaltiesToPlayer:
- ld a, $1
- jr ApplyBurnAndParalysisPenalties
- ApplyBurnAndParalysisPenaltiesToEnemy:
- xor a
- ApplyBurnAndParalysisPenalties:
- ld [H_WHOSETURN], a
- call QuarterSpeedDueToParalysis
- jp HalveAttackDueToBurn
- QuarterSpeedDueToParalysis:
- ld a, [H_WHOSETURN]
- and a
- jr z, .playerTurn
- .enemyTurn ; quarter the player's speed
- ld a, [wBattleMonStatus]
- and 1 << PAR
- ret z ; return if player not paralysed
- ld hl, wBattleMonSpeed + 1
- ld a, [hld]
- ld b, a
- ld a, [hl]
- srl a
- rr b
- srl a
- rr b
- ld [hli], a
- or b
- jr nz, .storePlayerSpeed
- ld b, 1 ; give the player a minimum of at least one speed point
- .storePlayerSpeed
- ld [hl], b
- ret
- .playerTurn ; quarter the enemy's speed
- ld a, [wEnemyMonStatus]
- and 1 << PAR
- ret z ; return if enemy not paralysed
- ld hl, wEnemyMonSpeed + 1
- ld a, [hld]
- ld b, a
- ld a, [hl]
- srl a
- rr b
- srl a
- rr b
- ld [hli], a
- or b
- jr nz, .storeEnemySpeed
- ld b, 1 ; give the enemy a minimum of at least one speed point
- .storeEnemySpeed
- ld [hl], b
- ret
- HalveAttackDueToBurn:
- ld a, [H_WHOSETURN]
- and a
- jr z, .playerTurn
- .enemyTurn ; halve the player's attack
- ld a, [wBattleMonStatus]
- and 1 << BRN
- ret z ; return if player not burnt
- ld hl, wBattleMonAttack + 1
- ld a, [hld]
- ld b, a
- ld a, [hl]
- srl a
- rr b
- ld [hli], a
- or b
- jr nz, .storePlayerAttack
- ld b, 1 ; give the player a minimum of at least one attack point
- .storePlayerAttack
- ld [hl], b
- ret
- .playerTurn ; halve the enemy's attack
- ld a, [wEnemyMonStatus]
- and 1 << BRN
- ret z ; return if enemy not burnt
- ld hl, wEnemyMonAttack + 1
- ld a, [hld]
- ld b, a
- ld a, [hl]
- srl a
- rr b
- ld [hli], a
- or b
- jr nz, .storeEnemyAttack
- ld b, 1 ; give the enemy a minimum of at least one attack point
- .storeEnemyAttack
- ld [hl], b
- ret
- CalculateModifiedStats:
- ld c, 0
- .loop
- call CalculateModifiedStat
- inc c
- ld a, c
- cp NUM_STATS - 1
- jr nz, .loop
- ret
- ; calculate modified stat for stat c (0 = attack, 1 = defense, 2 = speed, 3 = special)
- CalculateModifiedStat:
- push bc
- push bc
- ld a, [wCalculateWhoseStats]
- and a
- ld a, c
- ld hl, wBattleMonAttack
- ld de, wPlayerMonUnmodifiedAttack
- ld bc, wPlayerMonStatMods
- jr z, .next
- ld hl, wEnemyMonAttack
- ld de, wEnemyMonUnmodifiedAttack
- ld bc, wEnemyMonStatMods
- .next
- add c
- ld c, a
- jr nc, .noCarry1
- inc b
- .noCarry1
- ld a, [bc]
- pop bc
- ld b, a
- push bc
- sla c
- ld b, 0
- add hl, bc
- ld a, c
- add e
- ld e, a
- jr nc, .noCarry2
- inc d
- .noCarry2
- pop bc
- push hl
- ld hl, StatModifierRatios
- dec b
- sla b
- ld c, b
- ld b, 0
- add hl, bc
- xor a
- ld [H_MULTIPLICAND], a
- ld a, [de]
- ld [H_MULTIPLICAND + 1], a
- inc de
- ld a, [de]
- ld [H_MULTIPLICAND + 2], a
- ld a, [hli]
- ld [H_MULTIPLIER], a
- call Multiply
- ld a, [hl]
- ld [H_DIVISOR], a
- ld b, $4
- call Divide
- pop hl
- ld a, [H_DIVIDEND + 3]
- sub 999 % $100
- ld a, [H_DIVIDEND + 2]
- sbc 999 / $100
- jp c, .storeNewStatValue
- ; cap the stat at 999
- ld a, 999 / $100
- ld [H_DIVIDEND + 2], a
- ld a, 999 % $100
- ld [H_DIVIDEND + 3], a
- .storeNewStatValue
- ld a, [H_DIVIDEND + 2]
- ld [hli], a
- ld b, a
- ld a, [H_DIVIDEND + 3]
- ld [hl], a
- or b
- jr nz, .done
- inc [hl] ; if the stat is 0, bump it up to 1
- .done
- pop bc
- ret
- ApplyBadgeStatBoosts:
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- ret z ; return if link battle
- ld a, [wObtainedBadges]
- ld b, a
- ld hl, wBattleMonAttack
- ld c, $4
- ; the boost is applied for badges whose bit position is even
- ; the order of boosts matches the order they are laid out in RAM
- ; Boulder (bit 0) - attack
- ; Thunder (bit 2) - defense
- ; Soul (bit 4) - speed
- ; Volcano (bit 6) - special
- .loop
- srl b
- call c, .applyBoostToStat
- inc hl
- inc hl
- srl b
- dec c
- jr nz, .loop
- ret
- ; multiply stat at hl by 1.125
- ; cap stat at 999
- .applyBoostToStat
- ld a, [hli]
- ld d, a
- ld e, [hl]
- srl d
- rr e
- srl d
- rr e
- srl d
- rr e
- ld a, [hl]
- add e
- ld [hld], a
- ld a, [hl]
- adc d
- ld [hli], a
- ld a, [hld]
- sub 999 % $100
- ld a, [hl]
- sbc 999 / $100
- ret c
- ld a, 999 / $100
- ld [hli], a
- ld a, 999 % $100
- ld [hld], a
- ret
- LoadHudAndHpBarAndStatusTilePatterns:
- call LoadHpBarAndStatusTilePatterns
- LoadHudTilePatterns:
- ld a, [rLCDC]
- add a ; is LCD disabled?
- jr c, .lcdEnabled
- .lcdDisabled
- ld hl, BattleHudTiles1
- ld de, vChars2 + $6d0
- ld bc, BattleHudTiles1End - BattleHudTiles1
- ld a, BANK(BattleHudTiles1)
- call FarCopyDataDouble
- ld hl, BattleHudTiles2
- ld de, vChars2 + $730
- ld bc, BattleHudTiles3End - BattleHudTiles2
- ld a, BANK(BattleHudTiles2)
- jp FarCopyDataDouble
- .lcdEnabled
- ld de, BattleHudTiles1
- ld hl, vChars2 + $6d0
- lb bc, BANK(BattleHudTiles1), (BattleHudTiles1End - BattleHudTiles1) / $8
- call CopyVideoDataDouble
- ld de, BattleHudTiles2
- ld hl, vChars2 + $730
- lb bc, BANK(BattleHudTiles2), (BattleHudTiles3End - BattleHudTiles2) / $8
- jp CopyVideoDataDouble
- PrintEmptyString:
- ld hl, .emptyString
- jp PrintText
- .emptyString
- db "@"
- BattleRandom:
- ; Link battles use a shared PRNG.
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jp nz, Random
- push hl
- push bc
- ld a, [wLinkBattleRandomNumberListIndex]
- ld c, a
- ld b, 0
- ld hl, wLinkBattleRandomNumberList
- add hl, bc
- inc a
- ld [wLinkBattleRandomNumberListIndex], a
- cp 9
- ld a, [hl]
- pop bc
- pop hl
- ret c
- ; if we picked the last seed, we need to recalculate the nine seeds
- push hl
- push bc
- push af
- ; point to seed 0 so we pick the first number the next time
- xor a
- ld [wLinkBattleRandomNumberListIndex], a
- ld hl, wLinkBattleRandomNumberList
- ld b, 9
- .loop
- ld a, [hl]
- ld c, a
- ; multiply by 5
- add a
- add a
- add c
- ; add 1
- inc a
- ld [hli], a
- dec b
- jr nz, .loop
- pop af
- pop bc
- pop hl
- ret
- HandleExplodingAnimation:
- ld a, [H_WHOSETURN]
- and a
- ld hl, wEnemyMonType1
- ld de, wEnemyBattleStatus1
- ld a, [wPlayerMoveNum]
- jr z, .player
- ld hl, wBattleMonType1
- ld de, wEnemyBattleStatus1
- ld a, [wEnemyMoveNum]
- .player
- cp SELFDESTRUCT
- jr z, .isExplodingMove
- cp EXPLOSION
- ret nz
- .isExplodingMove
- ld a, [de]
- bit INVULNERABLE, a ; fly/dig
- ret nz
- ld a, [hli]
- cp GHOST
- ret z
- ld a, [hl]
- cp GHOST
- ret z
- ld a, [wMoveMissed]
- and a
- ret nz
- ld a, 5
- ld [wAnimationType], a
- PlayMoveAnimation:
- ld [wAnimationID], a
- call Delay3
- predef_jump MoveAnimation
- InitBattle:
- ld a, [wCurOpponent]
- and a
- jr z, DetermineWildOpponent
- InitOpponent:
- ld a, [wCurOpponent]
- ld [wcf91], a
- ld [wEnemyMonSpecies2], a
- jr InitBattleCommon
- DetermineWildOpponent:
- ld a, [wd732]
- bit 1, a
- jr z, .asm_3ef2f
- ld a, [hJoyHeld]
- bit 1, a ; B button pressed?
- ret nz
- .asm_3ef2f
- ld a, [wNumberOfNoRandomBattleStepsLeft]
- and a
- ret nz
- callab TryDoWildEncounter
- ret nz
- InitBattleCommon:
- ld a, [wMapPalOffset]
- push af
- ld hl, wLetterPrintingDelayFlags
- ld a, [hl]
- push af
- res 1, [hl]
- callab InitBattleVariables
- ld a, [wEnemyMonSpecies2]
- sub 200
- jp c, InitWildBattle
- ld [wTrainerClass], a
- call GetTrainerInformation
- callab ReadTrainer
- call DoBattleTransitionAndInitBattleVariables
- call _LoadTrainerPic
- xor a
- ld [wEnemyMonSpecies2], a
- ld [hStartTileID], a
- dec a
- ld [wAICount], a
- coord hl, 12, 0
- predef CopyUncompressedPicToTilemap
- ld a, $ff
- ld [wEnemyMonPartyPos], a
- ld a, $2
- ld [wIsInBattle], a
- jp _InitBattleCommon
- InitWildBattle:
- ld a, $1
- ld [wIsInBattle], a
- call LoadEnemyMonData
- call DoBattleTransitionAndInitBattleVariables
- ld a, [wCurOpponent]
- cp MAROWAK
- jr z, .isGhost
- call IsGhostBattle
- jr nz, .isNoGhost
- .isGhost
- ld hl, wMonHSpriteDim
- ld a, $66
- ld [hli], a ; write sprite dimensions
- ld bc, GhostPic
- ld a, c
- ld [hli], a ; write front sprite pointer
- ld [hl], b
- ld hl, wEnemyMonNick ; set name to "GHOST"
- ld a, "G"
- ld [hli], a
- ld a, "H"
- ld [hli], a
- ld a, "O"
- ld [hli], a
- ld a, "S"
- ld [hli], a
- ld a, "T"
- ld [hli], a
- ld [hl], "@"
- ld a, [wcf91]
- push af
- ld a, MON_GHOST
- ld [wcf91], a
- ld de, vFrontPic
- call LoadMonFrontSprite ; load ghost sprite
- pop af
- ld [wcf91], a
- jr .spriteLoaded
- .isNoGhost
- ld de, vFrontPic
- call LoadMonFrontSprite ; load mon sprite
- .spriteLoaded
- xor a
- ld [wTrainerClass], a
- ld [hStartTileID], a
- coord hl, 12, 0
- predef CopyUncompressedPicToTilemap
- ; common code that executes after init battle code specific to trainer or wild battles
- _InitBattleCommon:
- ld b, SET_PAL_BATTLE_BLACK
- call RunPaletteCommand
- call SlidePlayerAndEnemySilhouettesOnScreen
- xor a
- ld [H_AUTOBGTRANSFERENABLED], a
- ld hl, .emptyString
- call PrintText
- call SaveScreenTilesToBuffer1
- call ClearScreen
- ld a, $98
- ld [H_AUTOBGTRANSFERDEST + 1], a
- ld a, $1
- ld [H_AUTOBGTRANSFERENABLED], a
- call Delay3
- ld a, $9c
- ld [H_AUTOBGTRANSFERDEST + 1], a
- call LoadScreenTilesFromBuffer1
- coord hl, 9, 7
- lb bc, 5, 10
- call ClearScreenArea
- coord hl, 1, 0
- lb bc, 4, 10
- call ClearScreenArea
- call ClearSprites
- ld a, [wIsInBattle]
- dec a ; is it a wild battle?
- call z, DrawEnemyHUDAndHPBar ; draw enemy HUD and HP bar if it's a wild battle
- call StartBattle
- callab EndOfBattle
- pop af
- ld [wLetterPrintingDelayFlags], a
- pop af
- ld [wMapPalOffset], a
- ld a, [wSavedTilesetType]
- ld [hTilesetType], a
- scf
- ret
- .emptyString
- db "@"
- _LoadTrainerPic:
- ; wd033-wd034 contain pointer to pic
- ld a, [wTrainerPicPointer]
- ld e, a
- ld a, [wTrainerPicPointer + 1]
- ld d, a ; de contains pointer to trainer pic
- ld a, [wLinkState]
- and a
- ld a, Bank(TrainerPics) ; this is where all the trainer pics are (not counting Red's)
- jr z, .loadSprite
- ld a, Bank(RedPicFront)
- .loadSprite
- call UncompressSpriteFromDE
- ld de, vFrontPic
- ld a, $77
- ld c, a
- jp LoadUncompressedSpriteData
- ; unreferenced
- ResetCryModifiers:
- xor a
- ld [wFrequencyModifier], a
- ld [wTempoModifier], a
- jp PlaySound
- ; animates the mon "growing" out of the pokeball
- AnimateSendingOutMon:
- ld a, [wPredefRegisters]
- ld h, a
- ld a, [wPredefRegisters + 1]
- ld l, a
- ld a, [hStartTileID]
- ld [hBaseTileID], a
- ld b, $4c
- ld a, [wIsInBattle]
- and a
- jr z, .notInBattle
- add b
- ld [hl], a
- call Delay3
- ld bc, -(SCREEN_WIDTH * 2 + 1)
- add hl, bc
- ld a, 1
- ld [wDownscaledMonSize], a
- lb bc, 3, 3
- predef CopyDownscaledMonTiles
- ld c, 4
- call DelayFrames
- ld bc, -(SCREEN_WIDTH * 2 + 1)
- add hl, bc
- xor a
- ld [wDownscaledMonSize], a
- lb bc, 5, 5
- predef CopyDownscaledMonTiles
- ld c, 5
- call DelayFrames
- ld bc, -(SCREEN_WIDTH * 2 + 1)
- jr .next
- .notInBattle
- ld bc, -(SCREEN_WIDTH * 6 + 3)
- .next
- add hl, bc
- ld a, [hBaseTileID]
- add $31
- jr CopyUncompressedPicToHL
- CopyUncompressedPicToTilemap:
- ld a, [wPredefRegisters]
- ld h, a
- ld a, [wPredefRegisters + 1]
- ld l, a
- ld a, [hStartTileID]
- CopyUncompressedPicToHL:
- lb bc, 7, 7
- ld de, SCREEN_WIDTH
- push af
- ld a, [wSpriteFlipped]
- and a
- jr nz, .flipped
- pop af
- .loop
- push bc
- push hl
- .innerLoop
- ld [hl], a
- add hl, de
- inc a
- dec c
- jr nz, .innerLoop
- pop hl
- inc hl
- pop bc
- dec b
- jr nz, .loop
- ret
- .flipped
- push bc
- ld b, 0
- dec c
- add hl, bc
- pop bc
- pop af
- .flippedLoop
- push bc
- push hl
- .flippedInnerLoop
- ld [hl], a
- add hl, de
- inc a
- dec c
- jr nz, .flippedInnerLoop
- pop hl
- dec hl
- pop bc
- dec b
- jr nz, .flippedLoop
- ret
- LoadMonBackPic:
- ; Assumes the monster's attributes have
- ; been loaded with GetMonHeader.
- ld a, [wBattleMonSpecies2]
- ld [wcf91], a
- coord hl, 1, 5
- ld b, 7
- ld c, 8
- call ClearScreenArea
- ld hl, wMonHBackSprite - wMonHeader
- call UncompressMonSprite
- predef ScaleSpriteByTwo
- ld de, vBackPic
- call InterlaceMergeSpriteBuffers ; combine the two buffers to a single 2bpp sprite
- ld hl, vSprites
- ld de, vBackPic
- ld c, (2*SPRITEBUFFERSIZE)/16 ; count of 16-byte chunks to be copied
- ld a, [H_LOADEDROMBANK]
- ld b, a
- jp CopyVideoData
- JumpMoveEffect:
- call _JumpMoveEffect
- ld b, $1
- ret
- _JumpMoveEffect:
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveEffect]
- jr z, .next1
- ld a, [wEnemyMoveEffect]
- .next1
- dec a ; subtract 1, there is no special effect for 00
- add a ; x2, 16bit pointers
- ld hl, MoveEffectPointerTable
- ld b, 0
- ld c, a
- add hl, bc
- ld a, [hli]
- ld h, [hl]
- ld l, a
- jp hl ; jump to special effect handler
- MoveEffectPointerTable:
- dw SleepEffect ; unused effect
- dw PoisonEffect ; POISON_SIDE_EFFECT1
- dw DrainHPEffect ; DRAIN_HP_EFFECT
- dw FreezeBurnParalyzeEffect ; BURN_SIDE_EFFECT1
- dw FreezeBurnParalyzeEffect ; FREEZE_SIDE_EFFECT
- dw FreezeBurnParalyzeEffect ; PARALYZE_SIDE_EFFECT1
- dw ExplodeEffect ; EXPLODE_EFFECT
- dw DrainHPEffect ; DREAM_EATER_EFFECT
- dw $0000 ; MIRROR_MOVE_EFFECT
- dw StatModifierUpEffect ; ATTACK_UP1_EFFECT
- dw StatModifierUpEffect ; DEFENSE_UP1_EFFECT
- dw StatModifierUpEffect ; SPEED_UP1_EFFECT
- dw StatModifierUpEffect ; SPECIAL_UP1_EFFECT
- dw StatModifierUpEffect ; ACCURACY_UP1_EFFECT
- dw StatModifierUpEffect ; EVASION_UP1_EFFECT
- dw PayDayEffect ; PAY_DAY_EFFECT
- dw $0000 ; SWIFT_EFFECT
- dw StatModifierDownEffect ; ATTACK_DOWN1_EFFECT
- dw StatModifierDownEffect ; DEFENSE_DOWN1_EFFECT
- dw StatModifierDownEffect ; SPEED_DOWN1_EFFECT
- dw StatModifierDownEffect ; SPECIAL_DOWN1_EFFECT
- dw StatModifierDownEffect ; ACCURACY_DOWN1_EFFECT
- dw StatModifierDownEffect ; EVASION_DOWN1_EFFECT
- dw ConversionEffect ; CONVERSION_EFFECT
- dw HazeEffect ; HAZE_EFFECT
- dw BideEffect ; BIDE_EFFECT
- dw ThrashPetalDanceEffect ; THRASH_PETAL_DANCE_EFFECT
- dw SwitchAndTeleportEffect ; SWITCH_AND_TELEPORT_EFFECT
- dw TwoToFiveAttacksEffect ; TWO_TO_FIVE_ATTACKS_EFFECT
- dw TwoToFiveAttacksEffect ; unused effect
- dw FlinchSideEffect ; FLINCH_SIDE_EFFECT1
- dw SleepEffect ; SLEEP_EFFECT
- dw PoisonEffect ; POISON_SIDE_EFFECT2
- dw FreezeBurnParalyzeEffect ; BURN_SIDE_EFFECT2
- dw FreezeBurnParalyzeEffect ; unused effect
- dw FreezeBurnParalyzeEffect ; PARALYZE_SIDE_EFFECT2
- dw FlinchSideEffect ; FLINCH_SIDE_EFFECT2
- dw OneHitKOEffect ; OHKO_EFFECT
- dw ChargeEffect ; CHARGE_EFFECT
- dw $0000 ; SUPER_FANG_EFFECT
- dw $0000 ; SPECIAL_DAMAGE_EFFECT
- dw TrappingEffect ; TRAPPING_EFFECT
- dw ChargeEffect ; FLY_EFFECT
- dw TwoToFiveAttacksEffect ; ATTACK_TWICE_EFFECT
- dw $0000 ; JUMP_KICK_EFFECT
- dw MistEffect ; MIST_EFFECT
- dw FocusEnergyEffect ; FOCUS_ENERGY_EFFECT
- dw RecoilEffect ; RECOIL_EFFECT
- dw ConfusionEffect ; CONFUSION_EFFECT
- dw StatModifierUpEffect ; ATTACK_UP2_EFFECT
- dw StatModifierUpEffect ; DEFENSE_UP2_EFFECT
- dw StatModifierUpEffect ; SPEED_UP2_EFFECT
- dw StatModifierUpEffect ; SPECIAL_UP2_EFFECT
- dw StatModifierUpEffect ; ACCURACY_UP2_EFFECT
- dw StatModifierUpEffect ; EVASION_UP2_EFFECT
- dw HealEffect ; HEAL_EFFECT
- dw TransformEffect ; TRANSFORM_EFFECT
- dw StatModifierDownEffect ; ATTACK_DOWN2_EFFECT
- dw StatModifierDownEffect ; DEFENSE_DOWN2_EFFECT
- dw StatModifierDownEffect ; SPEED_DOWN2_EFFECT
- dw StatModifierDownEffect ; SPECIAL_DOWN2_EFFECT
- dw StatModifierDownEffect ; ACCURACY_DOWN2_EFFECT
- dw StatModifierDownEffect ; EVASION_DOWN2_EFFECT
- dw ReflectLightScreenEffect ; LIGHT_SCREEN_EFFECT
- dw ReflectLightScreenEffect ; REFLECT_EFFECT
- dw PoisonEffect ; POISON_EFFECT
- dw ParalyzeEffect ; PARALYZE_EFFECT
- dw StatModifierDownEffect ; ATTACK_DOWN_SIDE_EFFECT
- dw StatModifierDownEffect ; DEFENSE_DOWN_SIDE_EFFECT
- dw StatModifierDownEffect ; SPEED_DOWN_SIDE_EFFECT
- dw StatModifierDownEffect ; SPECIAL_DOWN_SIDE_EFFECT
- dw StatModifierDownEffect ; unused effect
- dw StatModifierDownEffect ; unused effect
- dw StatModifierDownEffect ; unused effect
- dw StatModifierDownEffect ; unused effect
- dw ConfusionSideEffect ; CONFUSION_SIDE_EFFECT
- dw TwoToFiveAttacksEffect ; TWINEEDLE_EFFECT
- dw $0000 ; unused effect
- dw SubstituteEffect ; SUBSTITUTE_EFFECT
- dw HyperBeamEffect ; HYPER_BEAM_EFFECT
- dw RageEffect ; RAGE_EFFECT
- dw MimicEffect ; MIMIC_EFFECT
- dw $0000 ; METRONOME_EFFECT
- dw LeechSeedEffect ; LEECH_SEED_EFFECT
- dw SplashEffect ; SPLASH_EFFECT
- dw DisableEffect ; DISABLE_EFFECT
- SleepEffect:
- ld de, wEnemyMonStatus
- ld bc, wEnemyBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jp z, .sleepEffect
- ld de, wBattleMonStatus
- ld bc, wPlayerBattleStatus2
- .sleepEffect
- ld a, [bc]
- bit NEEDS_TO_RECHARGE, a ; does the target need to recharge? (hyper beam)
- res NEEDS_TO_RECHARGE, a ; target no longer needs to recharge
- ld [bc], a
- jr nz, .setSleepCounter ; if the target had to recharge, all hit tests will be skipped
- ; including the event where the target already has another status
- ld a, [de]
- ld b, a
- and $7
- jr z, .notAlreadySleeping ; can't affect a mon that is already asleep
- ld hl, AlreadyAsleepText
- jp PrintText
- .notAlreadySleeping
- ld a, b
- and a
- jr nz, .didntAffect ; can't affect a mon that is already statused
- push de
- call MoveHitTest ; apply accuracy tests
- pop de
- ld a, [wMoveMissed]
- and a
- jr nz, .didntAffect
- .setSleepCounter
- ; set target's sleep counter to a random number between 1 and 7
- call BattleRandom
- and $7
- jr z, .setSleepCounter
- ld [de], a
- call PlayCurrentMoveAnimation2
- ld hl, FellAsleepText
- jp PrintText
- .didntAffect
- jp PrintDidntAffectText
- FellAsleepText:
- TX_FAR _FellAsleepText
- db "@"
- AlreadyAsleepText:
- TX_FAR _AlreadyAsleepText
- db "@"
- PoisonEffect:
- ld hl, wEnemyMonStatus
- ld de, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- jr z, .poisonEffect
- ld hl, wBattleMonStatus
- ld de, wEnemyMoveEffect
- .poisonEffect
- call CheckTargetSubstitute
- jr nz, .noEffect ; can't poison a substitute target
- ld a, [hli]
- ld b, a
- and a
- jr nz, .noEffect ; miss if target is already statused
- ld a, [hli]
- cp POISON ; can't poison a poison-type target
- jr z, .noEffect
- ld a, [hld]
- cp POISON ; can't poison a poison-type target
- jr z, .noEffect
- ld a, [de]
- cp POISON_SIDE_EFFECT1
- ld b, $34 ; ~20% chance of poisoning
- jr z, .sideEffectTest
- cp POISON_SIDE_EFFECT2
- ld b, $67 ; ~40% chance of poisoning
- jr z, .sideEffectTest
- push hl
- push de
- call MoveHitTest ; apply accuracy tests
- pop de
- pop hl
- ld a, [wMoveMissed]
- and a
- jr nz, .didntAffect
- jr .inflictPoison
- .sideEffectTest
- call BattleRandom
- cp b ; was side effect successful?
- ret nc
- .inflictPoison
- dec hl
- set 3, [hl] ; mon is now poisoned
- push de
- dec de
- ld a, [H_WHOSETURN]
- and a
- ld b, ANIM_C7
- ld hl, wPlayerBattleStatus3
- ld a, [de]
- ld de, wPlayerToxicCounter
- jr nz, .ok
- ld b, ANIM_A9
- ld hl, wEnemyBattleStatus3
- ld de, wEnemyToxicCounter
- .ok
- cp TOXIC
- jr nz, .normalPoison ; done if move is not Toxic
- set BADLY_POISONED, [hl] ; else set Toxic battstatus
- xor a
- ld [de], a
- ld hl, BadlyPoisonedText
- jr .continue
- .normalPoison
- ld hl, PoisonedText
- .continue
- pop de
- ld a, [de]
- cp POISON_EFFECT
- jr z, .regularPoisonEffect
- ld a, b
- call PlayBattleAnimation2
- jp PrintText
- .regularPoisonEffect
- call PlayCurrentMoveAnimation2
- jp PrintText
- .noEffect
- ld a, [de]
- cp POISON_EFFECT
- ret nz
- .didntAffect
- ld c, 50
- call DelayFrames
- jp PrintDidntAffectText
- PoisonedText:
- TX_FAR _PoisonedText
- db "@"
- BadlyPoisonedText:
- TX_FAR _BadlyPoisonedText
- db "@"
- DrainHPEffect:
- jpab DrainHPEffect_
- ExplodeEffect:
- ld hl, wBattleMonHP
- ld de, wPlayerBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .faintUser
- ld hl, wEnemyMonHP
- ld de, wEnemyBattleStatus2
- .faintUser
- xor a
- ld [hli], a ; set the mon's HP to 0
- ld [hli], a
- inc hl
- ld [hl], a ; set mon's status to 0
- ld a, [de]
- res SEEDED, a ; clear mon's leech seed status
- ld [de], a
- ret
- FreezeBurnParalyzeEffect:
- xor a
- ld [wAnimationType], a
- call CheckTargetSubstitute ; test bit 4 of d063/d068 flags [target has substitute flag]
- ret nz ; return if they have a substitute, can't effect them
- ld a, [H_WHOSETURN]
- and a
- jp nz, opponentAttacker
- ld a, [wEnemyMonStatus]
- and a
- jp nz, CheckDefrost ; can't inflict status if opponent is already statused
- ld a, [wPlayerMoveType]
- ld b, a
- ld a, [wEnemyMonType1]
- cp b ; do target type 1 and move type match?
- ret z ; return if they match (an ice move can't freeze an ice-type, body slam can't paralyze a normal-type, etc.)
- ld a, [wEnemyMonType2]
- cp b ; do target type 2 and move type match?
- ret z ; return if they match
- ld a, [wPlayerMoveEffect]
- cp PARALYZE_SIDE_EFFECT1 + 1 ; 10% status effects are 04, 05, 06 so 07 will set carry for those
- ld b, $1a ; 0x1A/0x100 or 26/256 = 10.2%~ chance
- jr c, .next1 ; branch ahead if this is a 10% chance effect..
- ld b, $4d ; else use 0x4D/0x100 or 77/256 = 30.1%~ chance
- sub $1e ; subtract $1E to map to equivalent 10% chance effects
- .next1
- push af
- call BattleRandom ; get random 8bit value for probability test
- cp b
- pop bc
- ret nc ; do nothing if random value is >= 1A or 4D [no status applied]
- ld a, b ; what type of effect is this?
- cp BURN_SIDE_EFFECT1
- jr z, .burn
- cp FREEZE_SIDE_EFFECT
- jr z, .freeze
- ; .paralyze
- ld a, 1 << PAR
- ld [wEnemyMonStatus], a
- call QuarterSpeedDueToParalysis ; quarter speed of affected mon
- ld a, ANIM_A9
- call PlayBattleAnimation
- jp PrintMayNotAttackText ; print paralysis text
- .burn
- ld a, 1 << BRN
- ld [wEnemyMonStatus], a
- call HalveAttackDueToBurn ; halve attack of affected mon
- ld a, ANIM_A9
- call PlayBattleAnimation
- ld hl, BurnedText
- jp PrintText
- .freeze
- call ClearHyperBeam ; resets hyper beam (recharge) condition from target
- ld a, 1 << FRZ
- ld [wEnemyMonStatus], a
- ld a, ANIM_A9
- call PlayBattleAnimation
- ld hl, FrozenText
- jp PrintText
- opponentAttacker:
- ld a, [wBattleMonStatus] ; mostly same as above with addresses swapped for opponent
- and a
- jp nz, CheckDefrost
- ld a, [wEnemyMoveType]
- ld b, a
- ld a, [wBattleMonType1]
- cp b
- ret z
- ld a, [wBattleMonType2]
- cp b
- ret z
- ld a, [wEnemyMoveEffect]
- cp PARALYZE_SIDE_EFFECT1 + 1
- ld b, $1a
- jr c, .next1
- ld b, $4d
- sub $1e
- .next1
- push af
- call BattleRandom
- cp b
- pop bc
- ret nc
- ld a, b
- cp BURN_SIDE_EFFECT1
- jr z, .burn
- cp FREEZE_SIDE_EFFECT
- jr z, .freeze
- ld a, 1 << PAR
- ld [wBattleMonStatus], a
- call QuarterSpeedDueToParalysis
- jp PrintMayNotAttackText
- .burn
- ld a, 1 << BRN
- ld [wBattleMonStatus], a
- call HalveAttackDueToBurn
- ld hl, BurnedText
- jp PrintText
- .freeze
- ; hyper beam bits aren't reseted for opponent's side
- ld a, 1 << FRZ
- ld [wBattleMonStatus], a
- ld hl, FrozenText
- jp PrintText
- BurnedText:
- TX_FAR _BurnedText
- db "@"
- FrozenText:
- TX_FAR _FrozenText
- db "@"
- CheckDefrost:
- ; any fire-type move that has a chance inflict burn (all but Fire Spin) will defrost a frozen target
- and 1 << FRZ ; are they frozen?
- ret z ; return if so
- ld a, [H_WHOSETURN]
- and a
- jr nz, .opponent
- ;player [attacker]
- ld a, [wPlayerMoveType]
- sub FIRE
- ret nz ; return if type of move used isn't fire
- ld [wEnemyMonStatus], a ; set opponent status to 00 ["defrost" a frozen monster]
- ld hl, wEnemyMon1Status
- ld a, [wEnemyMonPartyPos]
- ld bc, wEnemyMon2 - wEnemyMon1
- call AddNTimes
- xor a
- ld [hl], a ; clear status in roster
- ld hl, FireDefrostedText
- jr .common
- .opponent
- ld a, [wEnemyMoveType] ; same as above with addresses swapped
- sub FIRE
- ret nz
- ld [wBattleMonStatus], a
- ld hl, wPartyMon1Status
- ld a, [wPlayerMonNumber]
- ld bc, wPartyMon2 - wPartyMon1
- call AddNTimes
- xor a
- ld [hl], a
- ld hl, FireDefrostedText
- .common
- jp PrintText
- FireDefrostedText:
- TX_FAR _FireDefrostedText
- db "@"
- StatModifierUpEffect:
- ld hl, wPlayerMonStatMods
- ld de, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- jr z, .statModifierUpEffect
- ld hl, wEnemyMonStatMods
- ld de, wEnemyMoveEffect
- .statModifierUpEffect
- ld a, [de]
- sub ATTACK_UP1_EFFECT
- cp EVASION_UP1_EFFECT + $3 - ATTACK_UP1_EFFECT ; covers all +1 effects
- jr c, .incrementStatMod
- sub ATTACK_UP2_EFFECT - ATTACK_UP1_EFFECT ; map +2 effects to equivalent +1 effect
- .incrementStatMod
- ld c, a
- ld b, $0
- add hl, bc
- ld b, [hl]
- inc b ; increment corresponding stat mod
- ld a, $d
- cp b ; can't raise stat past +6 ($d or 13)
- jp c, PrintNothingHappenedText
- ld a, [de]
- cp ATTACK_UP1_EFFECT + $8 ; is it a +2 effect?
- jr c, .ok
- inc b ; if so, increment stat mod again
- ld a, $d
- cp b ; unless it's already +6
- jr nc, .ok
- ld b, a
- .ok
- ld [hl], b
- ld a, c
- cp $4
- jr nc, UpdateStatDone ; jump if mod affected is evasion/accuracy
- push hl
- ld hl, wBattleMonAttack + 1
- ld de, wPlayerMonUnmodifiedAttack
- ld a, [H_WHOSETURN]
- and a
- jr z, .pointToStats
- ld hl, wEnemyMonAttack + 1
- ld de, wEnemyMonUnmodifiedAttack
- .pointToStats
- push bc
- sla c
- ld b, $0
- add hl, bc ; hl = modified stat
- ld a, c
- add e
- ld e, a
- jr nc, .checkIf999
- inc d ; de = unmodified (original) stat
- .checkIf999
- pop bc
- ld a, [hld]
- sub 999 % $100 ; check if stat is already 999
- jr nz, .recalculateStat
- ld a, [hl]
- sbc 999 / $100
- jp z, RestoreOriginalStatModifier
- .recalculateStat ; recalculate affected stat
- ; paralysis and burn penalties, as well as badge boosts are ignored
- push hl
- push bc
- ld hl, StatModifierRatios
- dec b
- sla b
- ld c, b
- ld b, $0
- add hl, bc
- pop bc
- xor a
- ld [H_MULTIPLICAND], a
- ld a, [de]
- ld [H_MULTIPLICAND + 1], a
- inc de
- ld a, [de]
- ld [H_MULTIPLICAND + 2], a
- ld a, [hli]
- ld [H_MULTIPLIER], a
- call Multiply
- ld a, [hl]
- ld [H_DIVISOR], a
- ld b, $4
- call Divide
- pop hl
- ; cap at 999
- ld a, [H_PRODUCT + 3]
- sub 999 % $100
- ld a, [H_PRODUCT + 2]
- sbc 999 / $100
- jp c, UpdateStat
- ld a, 999 / $100
- ld [H_MULTIPLICAND + 1], a
- ld a, 999 % $100
- ld [H_MULTIPLICAND + 2], a
- UpdateStat:
- ld a, [H_PRODUCT + 2]
- ld [hli], a
- ld a, [H_PRODUCT + 3]
- ld [hl], a
- pop hl
- UpdateStatDone:
- ld b, c
- inc b
- call PrintStatText
- ld hl, wPlayerBattleStatus2
- ld de, wPlayerMoveNum
- ld bc, wPlayerMonMinimized
- ld a, [H_WHOSETURN]
- and a
- jr z, .asm_3f4e6
- ld hl, wEnemyBattleStatus2
- ld de, wEnemyMoveNum
- ld bc, wEnemyMonMinimized
- .asm_3f4e6
- ld a, [de]
- cp MINIMIZE
- jr nz, .asm_3f4f9
- ; if a substitute is up, slide off the substitute and show the mon pic before
- ; playing the minimize animation
- bit HAS_SUBSTITUTE_UP, [hl]
- push af
- push bc
- ld hl, HideSubstituteShowMonAnim
- ld b, BANK(HideSubstituteShowMonAnim)
- push de
- call nz, Bankswitch
- pop de
- .asm_3f4f9
- call PlayCurrentMoveAnimation
- ld a, [de]
- cp MINIMIZE
- jr nz, .applyBadgeBoostsAndStatusPenalties
- pop bc
- ld a, $1
- ld [bc], a
- ld hl, ReshowSubstituteAnim
- ld b, BANK(ReshowSubstituteAnim)
- pop af
- call nz, Bankswitch
- .applyBadgeBoostsAndStatusPenalties
- ld a, [H_WHOSETURN]
- and a
- call z, ApplyBadgeStatBoosts ; whenever the player uses a stat-up move, badge boosts get reapplied again to every stat,
- ; even to those not affected by the stat-up move (will be boosted further)
- ld hl, MonsStatsRoseText
- call PrintText
- ; these shouldn't be here
- call QuarterSpeedDueToParalysis ; apply speed penalty to the player whose turn is not, if it's paralyzed
- jp HalveAttackDueToBurn ; apply attack penalty to the player whose turn is not, if it's burned
- RestoreOriginalStatModifier:
- pop hl
- dec [hl]
- PrintNothingHappenedText:
- ld hl, NothingHappenedText
- jp PrintText
- MonsStatsRoseText:
- TX_FAR _MonsStatsRoseText
- TX_ASM
- ld hl, GreatlyRoseText
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveEffect]
- jr z, .playerTurn
- ld a, [wEnemyMoveEffect]
- .playerTurn
- cp ATTACK_DOWN1_EFFECT
- ret nc
- ld hl, RoseText
- ret
- GreatlyRoseText:
- TX_DELAY
- TX_FAR _GreatlyRoseText
- ; fallthrough
- RoseText:
- TX_FAR _RoseText
- db "@"
- StatModifierDownEffect:
- ld hl, wEnemyMonStatMods
- ld de, wPlayerMoveEffect
- ld bc, wEnemyBattleStatus1
- ld a, [H_WHOSETURN]
- and a
- jr z, .statModifierDownEffect
- ld hl, wPlayerMonStatMods
- ld de, wEnemyMoveEffect
- ld bc, wPlayerBattleStatus1
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr z, .statModifierDownEffect
- call BattleRandom
- cp $40 ; 1/4 chance to miss by in regular battle
- jp c, MoveMissed
- .statModifierDownEffect
- call CheckTargetSubstitute ; can't hit through substitute
- jp nz, MoveMissed
- ld a, [de]
- cp ATTACK_DOWN_SIDE_EFFECT
- jr c, .nonSideEffect
- call BattleRandom
- cp $55 ; 85/256 chance for side effects
- jp nc, CantLowerAnymore
- ld a, [de]
- sub ATTACK_DOWN_SIDE_EFFECT ; map each stat to 0-3
- jr .decrementStatMod
- .nonSideEffect ; non-side effects only
- push hl
- push de
- push bc
- call MoveHitTest ; apply accuracy tests
- pop bc
- pop de
- pop hl
- ld a, [wMoveMissed]
- and a
- jp nz, MoveMissed
- ld a, [bc]
- bit INVULNERABLE, a ; fly/dig
- jp nz, MoveMissed
- ld a, [de]
- sub ATTACK_DOWN1_EFFECT
- cp EVASION_DOWN1_EFFECT + $3 - ATTACK_DOWN1_EFFECT ; covers all -1 effects
- jr c, .decrementStatMod
- sub ATTACK_DOWN2_EFFECT - ATTACK_DOWN1_EFFECT ; map -2 effects to corresponding -1 effect
- .decrementStatMod
- ld c, a
- ld b, $0
- add hl, bc
- ld b, [hl]
- dec b ; dec corresponding stat mod
- jp z, CantLowerAnymore ; if stat mod is 1 (-6), can't lower anymore
- ld a, [de]
- cp ATTACK_DOWN2_EFFECT - $16 ; $24
- jr c, .ok
- cp EVASION_DOWN2_EFFECT + $5 ; $44
- jr nc, .ok
- dec b ; stat down 2 effects only (dec mod again)
- jr nz, .ok
- inc b ; increment mod to 1 (-6) if it would become 0 (-7)
- .ok
- ld [hl], b ; save modified mod
- ld a, c
- cp $4
- jr nc, UpdateLoweredStatDone ; jump for evasion/accuracy
- push hl
- push de
- ld hl, wEnemyMonAttack + 1
- ld de, wEnemyMonUnmodifiedAttack
- ld a, [H_WHOSETURN]
- and a
- jr z, .pointToStat
- ld hl, wBattleMonAttack + 1
- ld de, wPlayerMonUnmodifiedAttack
- .pointToStat
- push bc
- sla c
- ld b, $0
- add hl, bc ; hl = modified stat
- ld a, c
- add e
- ld e, a
- jr nc, .noCarry
- inc d ; de = unmodified stat
- .noCarry
- pop bc
- ld a, [hld]
- sub $1 ; can't lower stat below 1 (-6)
- jr nz, .recalculateStat
- ld a, [hl]
- and a
- jp z, CantLowerAnymore_Pop
- .recalculateStat
- ; recalculate affected stat
- ; paralysis and burn penalties, as well as badge boosts are ignored
- push hl
- push bc
- ld hl, StatModifierRatios
- dec b
- sla b
- ld c, b
- ld b, $0
- add hl, bc
- pop bc
- xor a
- ld [H_MULTIPLICAND], a
- ld a, [de]
- ld [H_MULTIPLICAND + 1], a
- inc de
- ld a, [de]
- ld [H_MULTIPLICAND + 2], a
- ld a, [hli]
- ld [H_MULTIPLIER], a
- call Multiply
- ld a, [hl]
- ld [H_DIVISOR], a
- ld b, $4
- call Divide
- pop hl
- ld a, [H_PRODUCT + 3]
- ld b, a
- ld a, [H_PRODUCT + 2]
- or b
- jp nz, UpdateLoweredStat
- ld [H_MULTIPLICAND + 1], a
- ld a, $1
- ld [H_MULTIPLICAND + 2], a
- UpdateLoweredStat:
- ld a, [H_PRODUCT + 2]
- ld [hli], a
- ld a, [H_PRODUCT + 3]
- ld [hl], a
- pop de
- pop hl
- UpdateLoweredStatDone:
- ld b, c
- inc b
- push de
- call PrintStatText
- pop de
- ld a, [de]
- cp $44
- jr nc, .ApplyBadgeBoostsAndStatusPenalties
- call PlayCurrentMoveAnimation2
- .ApplyBadgeBoostsAndStatusPenalties
- ld a, [H_WHOSETURN]
- and a
- call nz, ApplyBadgeStatBoosts ; whenever the player uses a stat-down move, badge boosts get reapplied again to every stat,
- ; even to those not affected by the stat-up move (will be boosted further)
- ld hl, MonsStatsFellText
- call PrintText
- ; These where probably added given that a stat-down move affecting speed or attack will override
- ; the stat penalties from paralysis and burn respectively.
- ; But they are always called regardless of the stat affected by the stat-down move.
- call QuarterSpeedDueToParalysis
- jp HalveAttackDueToBurn
- CantLowerAnymore_Pop:
- pop de
- pop hl
- inc [hl]
- CantLowerAnymore:
- ld a, [de]
- cp ATTACK_DOWN_SIDE_EFFECT
- ret nc
- ld hl, NothingHappenedText
- jp PrintText
- MoveMissed:
- ld a, [de]
- cp $44
- ret nc
- jp ConditionalPrintButItFailed
- MonsStatsFellText:
- TX_FAR _MonsStatsFellText
- TX_ASM
- ld hl, FellText
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveEffect]
- jr z, .playerTurn
- ld a, [wEnemyMoveEffect]
- .playerTurn
- ; check if the move's effect decreases a stat by 2
- cp BIDE_EFFECT
- ret c
- cp ATTACK_DOWN_SIDE_EFFECT
- ret nc
- ld hl, GreatlyFellText
- ret
- GreatlyFellText:
- TX_DELAY
- TX_FAR _GreatlyFellText
- ; fallthrough
- FellText:
- TX_FAR _FellText
- db "@"
- PrintStatText:
- ld hl, StatsTextStrings
- ld c, "@"
- .findStatName_outer
- dec b
- jr z, .foundStatName
- .findStatName_inner
- ld a, [hli]
- cp c
- jr z, .findStatName_outer
- jr .findStatName_inner
- .foundStatName
- ld de, wcf4b
- ld bc, $a
- jp CopyData
- StatsTextStrings:
- db "ATTACK@"
- db "DEFENSE@"
- db "SPEED@"
- db "SPECIAL@"
- db "ACCURACY@"
- db "EVADE@"
- StatModifierRatios:
- ; first byte is numerator, second byte is denominator
- db 25, 100 ; 0.25
- db 28, 100 ; 0.28
- db 33, 100 ; 0.33
- db 40, 100 ; 0.40
- db 50, 100 ; 0.50
- db 66, 100 ; 0.66
- db 1, 1 ; 1.00
- db 15, 10 ; 1.50
- db 2, 1 ; 2.00
- db 25, 10 ; 2.50
- db 3, 1 ; 3.00
- db 35, 10 ; 3.50
- db 4, 1 ; 4.00
- BideEffect:
- ld hl, wPlayerBattleStatus1
- ld de, wPlayerBideAccumulatedDamage
- ld bc, wPlayerNumAttacksLeft
- ld a, [H_WHOSETURN]
- and a
- jr z, .bideEffect
- ld hl, wEnemyBattleStatus1
- ld de, wEnemyBideAccumulatedDamage
- ld bc, wEnemyNumAttacksLeft
- .bideEffect
- set STORING_ENERGY, [hl] ; mon is now using bide
- xor a
- ld [de], a
- inc de
- ld [de], a
- ld [wPlayerMoveEffect], a
- ld [wEnemyMoveEffect], a
- call BattleRandom
- and $1
- inc a
- inc a
- ld [bc], a ; set Bide counter to 2 or 3 at random
- ld a, [H_WHOSETURN]
- add XSTATITEM_ANIM
- jp PlayBattleAnimation2
- ThrashPetalDanceEffect:
- ld hl, wPlayerBattleStatus1
- ld de, wPlayerNumAttacksLeft
- ld a, [H_WHOSETURN]
- and a
- jr z, .thrashPetalDanceEffect
- ld hl, wEnemyBattleStatus1
- ld de, wEnemyNumAttacksLeft
- .thrashPetalDanceEffect
- set THRASHING_ABOUT, [hl] ; mon is now using thrash/petal dance
- call BattleRandom
- and $1
- inc a
- inc a
- ld [de], a ; set thrash/petal dance counter to 2 or 3 at random
- ld a, [H_WHOSETURN]
- add ANIM_B0
- jp PlayBattleAnimation2
- SwitchAndTeleportEffect:
- ld a, [H_WHOSETURN]
- and a
- jr nz, .handleEnemy
- ld a, [wIsInBattle]
- dec a
- jr nz, .notWildBattle1
- ld a, [wCurEnemyLVL]
- ld b, a
- ld a, [wBattleMonLevel]
- cp b ; is the player's level greater than the enemy's level?
- jr nc, .playerMoveWasSuccessful ; if so, teleport will always succeed
- add b
- ld c, a
- inc c ; c = sum of player level and enemy level
- .rejectionSampleLoop1
- call BattleRandom
- cp c ; get a random number between 0 and c
- jr nc, .rejectionSampleLoop1
- srl b
- srl b ; b = enemyLevel / 4
- cp b ; is rand[0, playerLevel + enemyLevel) >= (enemyLevel / 4)?
- jr nc, .playerMoveWasSuccessful ; if so, allow teleporting
- ld c, 50
- call DelayFrames
- ld a, [wPlayerMoveNum]
- cp TELEPORT
- jp nz, PrintDidntAffectText
- jp PrintButItFailedText_
- .playerMoveWasSuccessful
- call ReadPlayerMonCurHPAndStatus
- xor a
- ld [wAnimationType], a
- inc a
- ld [wEscapedFromBattle], a
- ld a, [wPlayerMoveNum]
- jr .playAnimAndPrintText
- .notWildBattle1
- ld c, 50
- call DelayFrames
- ld hl, IsUnaffectedText
- ld a, [wPlayerMoveNum]
- cp TELEPORT
- jp nz, PrintText
- jp PrintButItFailedText_
- .handleEnemy
- ld a, [wIsInBattle]
- dec a
- jr nz, .notWildBattle2
- ld a, [wBattleMonLevel]
- ld b, a
- ld a, [wCurEnemyLVL]
- cp b
- jr nc, .enemyMoveWasSuccessful
- add b
- ld c, a
- inc c
- .rejectionSampleLoop2
- call BattleRandom
- cp c
- jr nc, .rejectionSampleLoop2
- srl b
- srl b
- cp b
- jr nc, .enemyMoveWasSuccessful
- ld c, 50
- call DelayFrames
- ld a, [wEnemyMoveNum]
- cp TELEPORT
- jp nz, PrintDidntAffectText
- jp PrintButItFailedText_
- .enemyMoveWasSuccessful
- call ReadPlayerMonCurHPAndStatus
- xor a
- ld [wAnimationType], a
- inc a
- ld [wEscapedFromBattle], a
- ld a, [wEnemyMoveNum]
- jr .playAnimAndPrintText
- .notWildBattle2
- ld c, 50
- call DelayFrames
- ld hl, IsUnaffectedText
- ld a, [wEnemyMoveNum]
- cp TELEPORT
- jp nz, PrintText
- jp ConditionalPrintButItFailed
- .playAnimAndPrintText
- push af
- call PlayBattleAnimation
- ld c, 20
- call DelayFrames
- pop af
- ld hl, RanFromBattleText
- cp TELEPORT
- jr z, .printText
- ld hl, RanAwayScaredText
- cp ROAR
- jr z, .printText
- ld hl, WasBlownAwayText
- .printText
- jp PrintText
- RanFromBattleText:
- TX_FAR _RanFromBattleText
- db "@"
- RanAwayScaredText:
- TX_FAR _RanAwayScaredText
- db "@"
- WasBlownAwayText:
- TX_FAR _WasBlownAwayText
- db "@"
- TwoToFiveAttacksEffect:
- ld hl, wPlayerBattleStatus1
- ld de, wPlayerNumAttacksLeft
- ld bc, wPlayerNumHits
- ld a, [H_WHOSETURN]
- and a
- jr z, .twoToFiveAttacksEffect
- ld hl, wEnemyBattleStatus1
- ld de, wEnemyNumAttacksLeft
- ld bc, wEnemyNumHits
- .twoToFiveAttacksEffect
- bit ATTACKING_MULTIPLE_TIMES, [hl] ; is mon attacking multiple times?
- ret nz
- set ATTACKING_MULTIPLE_TIMES, [hl] ; mon is now attacking multiple times
- ld hl, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- jr z, .setNumberOfHits
- ld hl, wEnemyMoveEffect
- .setNumberOfHits
- ld a, [hl]
- cp TWINEEDLE_EFFECT
- jr z, .twineedle
- cp ATTACK_TWICE_EFFECT
- ld a, $2 ; number of hits it's always 2 for ATTACK_TWICE_EFFECT
- jr z, .saveNumberOfHits
- ; for TWO_TO_FIVE_ATTACKS_EFFECT 3/8 chance for 2 and 3 hits, and 1/8 chance for 4 and 5 hits
- call BattleRandom
- and $3
- cp $2
- jr c, .gotNumHits
- ; if the number of hits was greater than 2, re-roll again for a lower chance
- call BattleRandom
- and $3
- .gotNumHits
- inc a
- inc a
- .saveNumberOfHits
- ld [de], a
- ld [bc], a
- ret
- .twineedle
- ld a, POISON_SIDE_EFFECT1
- ld [hl], a ; set Twineedle's effect to poison effect
- jr .saveNumberOfHits
- FlinchSideEffect:
- call CheckTargetSubstitute
- ret nz
- ld hl, wEnemyBattleStatus1
- ld de, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- jr z, .flinchSideEffect
- ld hl, wPlayerBattleStatus1
- ld de, wEnemyMoveEffect
- .flinchSideEffect
- ld a, [de]
- cp FLINCH_SIDE_EFFECT1
- ld b, $1a ; ~10% chance of flinch
- jr z, .gotEffectChance
- ld b, $4d ; ~30% chance of flinch
- .gotEffectChance
- call BattleRandom
- cp b
- ret nc
- set FLINCHED, [hl] ; set mon's status to flinching
- call ClearHyperBeam
- ret
- OneHitKOEffect:
- jpab OneHitKOEffect_
- ChargeEffect:
- ld hl, wPlayerBattleStatus1
- ld de, wPlayerMoveEffect
- ld a, [H_WHOSETURN]
- and a
- ld b, XSTATITEM_ANIM
- jr z, .chargeEffect
- ld hl, wEnemyBattleStatus1
- ld de, wEnemyMoveEffect
- ld b, ANIM_AF
- .chargeEffect
- set CHARGING_UP, [hl]
- ld a, [de]
- dec de ; de contains enemy or player MOVENUM
- cp FLY_EFFECT
- jr nz, .notFly
- set INVULNERABLE, [hl] ; mon is now invulnerable to typical attacks (fly/dig)
- ld b, TELEPORT ; load Teleport's animation
- .notFly
- ld a, [de]
- cp DIG
- jr nz, .notDigOrFly
- set INVULNERABLE, [hl] ; mon is now invulnerable to typical attacks (fly/dig)
- ld b, ANIM_C0
- .notDigOrFly
- xor a
- ld [wAnimationType], a
- ld a, b
- call PlayBattleAnimation
- ld a, [de]
- ld [wChargeMoveNum], a
- ld hl, ChargeMoveEffectText
- jp PrintText
- ChargeMoveEffectText:
- TX_FAR _ChargeMoveEffectText
- TX_ASM
- ld a, [wChargeMoveNum]
- cp RAZOR_WIND
- ld hl, MadeWhirlwindText
- jr z, .gotText
- cp SOLARBEAM
- ld hl, TookInSunlightText
- jr z, .gotText
- cp SKULL_BASH
- ld hl, LoweredItsHeadText
- jr z, .gotText
- cp SKY_ATTACK
- ld hl, SkyAttackGlowingText
- jr z, .gotText
- cp FLY
- ld hl, FlewUpHighText
- jr z, .gotText
- cp DIG
- ld hl, DugAHoleText
- .gotText
- ret
- MadeWhirlwindText:
- TX_FAR _MadeWhirlwindText
- db "@"
- TookInSunlightText:
- TX_FAR _TookInSunlightText
- db "@"
- LoweredItsHeadText:
- TX_FAR _LoweredItsHeadText
- db "@"
- SkyAttackGlowingText:
- TX_FAR _SkyAttackGlowingText
- db "@"
- FlewUpHighText:
- TX_FAR _FlewUpHighText
- db "@"
- DugAHoleText:
- TX_FAR _DugAHoleText
- db "@"
- TrappingEffect:
- ld hl, wPlayerBattleStatus1
- ld de, wPlayerNumAttacksLeft
- ld a, [H_WHOSETURN]
- and a
- jr z, .trappingEffect
- ld hl, wEnemyBattleStatus1
- ld de, wEnemyNumAttacksLeft
- .trappingEffect
- bit USING_TRAPPING_MOVE, [hl]
- ret nz
- call ClearHyperBeam ; since this effect is called before testing whether the move will hit,
- ; the target won't need to recharge even if the trapping move missed
- set USING_TRAPPING_MOVE, [hl] ; mon is now using a trapping move
- call BattleRandom ; 3/8 chance for 2 and 3 attacks, and 1/8 chance for 4 and 5 attacks
- and $3
- cp $2
- jr c, .setTrappingCounter
- call BattleRandom
- and $3
- .setTrappingCounter
- inc a
- ld [de], a
- ret
- MistEffect:
- jpab MistEffect_
- FocusEnergyEffect:
- jpab FocusEnergyEffect_
- RecoilEffect:
- jpab RecoilEffect_
- ConfusionSideEffect:
- call BattleRandom
- cp $19 ; ~10% chance
- ret nc
- jr ConfusionSideEffectSuccess
- ConfusionEffect:
- call CheckTargetSubstitute
- jr nz, ConfusionEffectFailed
- call MoveHitTest
- ld a, [wMoveMissed]
- and a
- jr nz, ConfusionEffectFailed
- ConfusionSideEffectSuccess:
- ld a, [H_WHOSETURN]
- and a
- ld hl, wEnemyBattleStatus1
- ld bc, wEnemyConfusedCounter
- ld a, [wPlayerMoveEffect]
- jr z, .confuseTarget
- ld hl, wPlayerBattleStatus1
- ld bc, wPlayerConfusedCounter
- ld a, [wEnemyMoveEffect]
- .confuseTarget
- bit CONFUSED, [hl] ; is mon confused?
- jr nz, ConfusionEffectFailed
- set CONFUSED, [hl] ; mon is now confused
- push af
- call BattleRandom
- and $3
- inc a
- inc a
- ld [bc], a ; confusion status will last 2-5 turns
- pop af
- cp CONFUSION_SIDE_EFFECT
- call nz, PlayCurrentMoveAnimation2
- ld hl, BecameConfusedText
- jp PrintText
- BecameConfusedText:
- TX_FAR _BecameConfusedText
- db "@"
- ConfusionEffectFailed:
- cp CONFUSION_SIDE_EFFECT
- ret z
- ld c, 50
- call DelayFrames
- jp ConditionalPrintButItFailed
- ParalyzeEffect:
- jpab ParalyzeEffect_
- SubstituteEffect:
- jpab SubstituteEffect_
- HyperBeamEffect:
- ld hl, wPlayerBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .hyperBeamEffect
- ld hl, wEnemyBattleStatus2
- .hyperBeamEffect
- set NEEDS_TO_RECHARGE, [hl] ; mon now needs to recharge
- ret
- ClearHyperBeam:
- push hl
- ld hl, wEnemyBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .playerTurn
- ld hl, wPlayerBattleStatus2
- .playerTurn
- res NEEDS_TO_RECHARGE, [hl] ; mon no longer needs to recharge
- pop hl
- ret
- RageEffect:
- ld hl, wPlayerBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .player
- ld hl, wEnemyBattleStatus2
- .player
- set USING_RAGE, [hl] ; mon is now in "rage" mode
- ret
- MimicEffect:
- ld c, 50
- call DelayFrames
- call MoveHitTest
- ld a, [wMoveMissed]
- and a
- jr nz, .mimicMissed
- ld a, [H_WHOSETURN]
- and a
- ld hl, wBattleMonMoves
- ld a, [wPlayerBattleStatus1]
- jr nz, .enemyTurn
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- jr nz, .letPlayerChooseMove
- ld hl, wEnemyMonMoves
- ld a, [wEnemyBattleStatus1]
- .enemyTurn
- bit INVULNERABLE, a
- jr nz, .mimicMissed
- .getRandomMove
- push hl
- call BattleRandom
- and $3
- ld c, a
- ld b, $0
- add hl, bc
- ld a, [hl]
- pop hl
- and a
- jr z, .getRandomMove
- ld d, a
- ld a, [H_WHOSETURN]
- and a
- ld hl, wBattleMonMoves
- ld a, [wPlayerMoveListIndex]
- jr z, .playerTurn
- ld hl, wEnemyMonMoves
- ld a, [wEnemyMoveListIndex]
- jr .playerTurn
- .letPlayerChooseMove
- ld a, [wEnemyBattleStatus1]
- bit INVULNERABLE, a
- jr nz, .mimicMissed
- ld a, [wCurrentMenuItem]
- push af
- ld a, $1
- ld [wMoveMenuType], a
- call MoveSelectionMenu
- call LoadScreenTilesFromBuffer1
- ld hl, wEnemyMonMoves
- ld a, [wCurrentMenuItem]
- ld c, a
- ld b, $0
- add hl, bc
- ld d, [hl]
- pop af
- ld hl, wBattleMonMoves
- .playerTurn
- ld c, a
- ld b, $0
- add hl, bc
- ld a, d
- ld [hl], a
- ld [wd11e], a
- call GetMoveName
- call PlayCurrentMoveAnimation
- ld hl, MimicLearnedMoveText
- jp PrintText
- .mimicMissed
- jp PrintButItFailedText_
- MimicLearnedMoveText:
- TX_FAR _MimicLearnedMoveText
- db "@"
- LeechSeedEffect:
- jpab LeechSeedEffect_
- SplashEffect:
- call PlayCurrentMoveAnimation
- jp PrintNoEffectText
- DisableEffect:
- call MoveHitTest
- ld a, [wMoveMissed]
- and a
- jr nz, .moveMissed
- ld de, wEnemyDisabledMove
- ld hl, wEnemyMonMoves
- ld a, [H_WHOSETURN]
- and a
- jr z, .disableEffect
- ld de, wPlayerDisabledMove
- ld hl, wBattleMonMoves
- .disableEffect
- ; no effect if target already has a move disabled
- ld a, [de]
- and a
- jr nz, .moveMissed
- .pickMoveToDisable
- push hl
- call BattleRandom
- and $3
- ld c, a
- ld b, $0
- add hl, bc
- ld a, [hl]
- pop hl
- and a
- jr z, .pickMoveToDisable ; loop until a non-00 move slot is found
- ld [wd11e], a ; store move number
- push hl
- ld a, [H_WHOSETURN]
- and a
- ld hl, wBattleMonPP
- jr nz, .enemyTurn
- ld a, [wLinkState]
- cp LINK_STATE_BATTLING
- pop hl ; wEnemyMonMoves
- jr nz, .playerTurnNotLinkBattle
- ; .playerTurnLinkBattle
- push hl
- ld hl, wEnemyMonPP
- .enemyTurn
- push hl
- ld a, [hli]
- or [hl]
- inc hl
- or [hl]
- inc hl
- or [hl]
- and $3f
- pop hl ; wBattleMonPP or wEnemyMonPP
- jr z, .moveMissedPopHL ; nothing to do if all moves have no PP left
- add hl, bc
- ld a, [hl]
- pop hl
- and a
- jr z, .pickMoveToDisable ; pick another move if this one had 0 PP
- .playerTurnNotLinkBattle
- ; non-link battle enemies have unlimited PP so the previous checks aren't needed
- call BattleRandom
- and $7
- inc a ; 1-8 turns disabled
- inc c ; move 1-4 will be disabled
- swap c
- add c ; map disabled move to high nibble of wEnemyDisabledMove / wPlayerDisabledMove
- ld [de], a
- call PlayCurrentMoveAnimation2
- ld hl, wPlayerDisabledMoveNumber
- ld a, [H_WHOSETURN]
- and a
- jr nz, .printDisableText
- inc hl ; wEnemyDisabledMoveNumber
- .printDisableText
- ld a, [wd11e] ; move number
- ld [hl], a
- call GetMoveName
- ld hl, MoveWasDisabledText
- jp PrintText
- .moveMissedPopHL
- pop hl
- .moveMissed
- jp PrintButItFailedText_
- MoveWasDisabledText:
- TX_FAR _MoveWasDisabledText
- db "@"
- PayDayEffect:
- jpab PayDayEffect_
- ConversionEffect:
- jpab ConversionEffect_
- HazeEffect:
- jpab HazeEffect_
- HealEffect:
- jpab HealEffect_
- TransformEffect:
- jpab TransformEffect_
- ReflectLightScreenEffect:
- jpab ReflectLightScreenEffect_
- NothingHappenedText:
- TX_FAR _NothingHappenedText
- db "@"
- PrintNoEffectText:
- ld hl, NoEffectText
- jp PrintText
- NoEffectText:
- TX_FAR _NoEffectText
- db "@"
- ConditionalPrintButItFailed:
- ld a, [wMoveDidntMiss]
- and a
- ret nz ; return if the side effect failed, yet the attack was successful
- PrintButItFailedText_:
- ld hl, ButItFailedText
- jp PrintText
- ButItFailedText:
- TX_FAR _ButItFailedText
- db "@"
- PrintDidntAffectText:
- ld hl, DidntAffectText
- jp PrintText
- DidntAffectText:
- TX_FAR _DidntAffectText
- db "@"
- IsUnaffectedText:
- TX_FAR _IsUnaffectedText
- db "@"
- PrintMayNotAttackText:
- ld hl, ParalyzedMayNotAttackText
- jp PrintText
- ParalyzedMayNotAttackText:
- TX_FAR _ParalyzedMayNotAttackText
- db "@"
- CheckTargetSubstitute:
- push hl
- ld hl, wEnemyBattleStatus2
- ld a, [H_WHOSETURN]
- and a
- jr z, .next1
- ld hl, wPlayerBattleStatus2
- .next1
- bit HAS_SUBSTITUTE_UP, [hl]
- pop hl
- ret
- PlayCurrentMoveAnimation2:
- ; animation at MOVENUM will be played unless MOVENUM is 0
- ; plays wAnimationType 3 or 6
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveNum]
- jr z, .notEnemyTurn
- ld a, [wEnemyMoveNum]
- .notEnemyTurn
- and a
- ret z
- PlayBattleAnimation2:
- ; play animation ID at a and animation type 6 or 3
- ld [wAnimationID], a
- ld a, [H_WHOSETURN]
- and a
- ld a, $6
- jr z, .storeAnimationType
- ld a, $3
- .storeAnimationType
- ld [wAnimationType], a
- jp PlayBattleAnimationGotID
- PlayCurrentMoveAnimation:
- ; animation at MOVENUM will be played unless MOVENUM is 0
- ; resets wAnimationType
- xor a
- ld [wAnimationType], a
- ld a, [H_WHOSETURN]
- and a
- ld a, [wPlayerMoveNum]
- jr z, .notEnemyTurn
- ld a, [wEnemyMoveNum]
- .notEnemyTurn
- and a
- ret z
- PlayBattleAnimation:
- ; play animation ID at a and predefined animation type
- ld [wAnimationID], a
- PlayBattleAnimationGotID:
- ; play animation at wAnimationID
- push hl
- push de
- push bc
- predef MoveAnimation
- pop bc
- pop de
- pop hl
- ret
|