WorldDatabase.cpp 323 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. EQ2Emulator is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emulator is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <math.h>
  17. #include <iostream>
  18. #include <sstream>
  19. #include <iomanip>
  20. #include <ios>
  21. #include <assert.h>
  22. #include "WorldDatabase.h"
  23. #include "../common/debug.h"
  24. #include "../common/packet_dump.h"
  25. #include "../common/GlobalHeaders.h"
  26. #include "Items/Items.h"
  27. #include "Factions.h"
  28. #include "World.h"
  29. #include "Variables.h"
  30. #include "VisualStates.h"
  31. #include "Appearances.h"
  32. #include "Skills.h"
  33. #include "Quests.h"
  34. #include "LuaInterface.h"
  35. #include "classes.h"
  36. #include "../common/Log.h"
  37. #include "Rules/Rules.h"
  38. #include "Titles.h"
  39. #include "Languages.h"
  40. #include "Traits/Traits.h"
  41. #include "ClientPacketFunctions.h"
  42. #include "Zone/ChestTrap.h"
  43. #include "../common/version.h"
  44. #include "SpellProcess.h"
  45. extern Classes classes;
  46. extern Commands commands;
  47. extern MasterTitlesList master_titles_list;
  48. extern MasterItemList master_item_list;
  49. extern MasterSpellList master_spell_list;
  50. extern MasterTraitList master_trait_list;
  51. extern MasterFactionList master_faction_list;
  52. extern World world;
  53. extern Variables variables;
  54. extern VisualStates visual_states;
  55. extern Appearances master_appearance_list;
  56. extern MasterSkillList master_skill_list;
  57. extern MasterQuestList master_quest_list;
  58. extern LuaInterface* lua_interface;
  59. extern ZoneList zone_list;
  60. extern GuildList guild_list;
  61. extern MasterCollectionList master_collection_list;
  62. extern RuleManager rule_manager;
  63. extern MasterLanguagesList master_languages_list;
  64. extern ChestTrapList chest_trap_list;
  65. //devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
  66. #if defined(__GNUC__)
  67. #define stricmp strcasecmp
  68. #define strnicmp strncasecmp
  69. #include <sys/stat.h>
  70. #endif
  71. WorldDatabase::WorldDatabase(){
  72. }
  73. WorldDatabase::~WorldDatabase(){
  74. }
  75. bool WorldDatabase::ConnectNewDatabase() {
  76. /*
  77. TESTS
  78. database_new.Connect();
  79. DatabaseResult result;
  80. database_new.Select(&result, "select name from characters where id=1");
  81. if (result.Next()) {
  82. printf("'%s'\n", result.GetStringStr("name"));
  83. printf("'%s'\n", result.GetStringStr("nameBAD"));
  84. printf("'%s'\n", result.GetString(3));
  85. }
  86. return true;
  87. */
  88. return database_new.Connect();
  89. }
  90. void WorldDatabase::PingNewDB()
  91. {
  92. database_new.PingNewDB();
  93. }
  94. void WorldDatabase::DeleteBuyBack(int32 char_id, int32 item_id, int16 quantity, int32 price) {
  95. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Deleting Buyback - Player: %u, Item ID: %u, Qty: %i, Price: %u", char_id, item_id, quantity, price);
  96. Query query;
  97. query.RunQuery2(Q_DELETE, "DELETE FROM character_buyback WHERE char_id = %u AND item_id = %u AND quantity = %i AND price = %u", char_id, item_id, quantity, price);
  98. }
  99. void WorldDatabase::LoadBuyBacks(Client* client) {
  100. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Loading Buyback - Player: %u", client->GetCharacterID());
  101. Query query;
  102. MYSQL_ROW row;
  103. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, item_id, quantity, price FROM character_buyback where char_id = %u ORDER BY id desc limit 10", client->GetCharacterID());
  104. int8 count = 0;
  105. int32 last_id = 0;
  106. if(result)
  107. {
  108. while(result && (row = mysql_fetch_row(result)))
  109. {
  110. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "AddBuyBack: item: %u, qty: %i, price: %u", atoul(row[1]), atoi(row[2]), atoul(row[3]));
  111. last_id = atoul(row[0]);
  112. client->AddBuyBack(last_id, atoul(row[1]), atoi(row[2]), atoul(row[3]), false);
  113. count++;
  114. }
  115. if(count >= 10)
  116. {
  117. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Deleting excess Buyback from Player: %u", client->GetCharacterID());
  118. Query query2;
  119. query2.RunQuery2(Q_DELETE, "DELETE FROM character_buyback WHERE char_id = %u AND id < %u", client->GetCharacterID(), last_id);
  120. }
  121. }
  122. }
  123. void WorldDatabase::SaveBuyBacks(Client* client)
  124. {
  125. LogWrite(MERCHANT__DEBUG, 3, "Merchant", "Saving Buybacks - Player: %u", client->GetCharacterID());
  126. deque<BuyBackItem*>* buybacks = client->GetBuyBacks();
  127. if(buybacks && buybacks->size() > 0)
  128. {
  129. BuyBackItem* item = 0;
  130. deque<BuyBackItem*>::iterator itr;
  131. for(itr = buybacks->begin(); itr != buybacks->end(); itr++)
  132. {
  133. item = *itr;
  134. if(item && item->save_needed)
  135. {
  136. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "SaveBuyBack: char: %u, item: %u, qty: %i, price: %u", client->GetCharacterID(), item->item_id, item->quantity, item->price);
  137. SaveBuyBack(client->GetCharacterID(), item->item_id, item->quantity, item->price);
  138. item->save_needed = false;
  139. }
  140. }
  141. }
  142. }
  143. void WorldDatabase::SaveBuyBack(int32 char_id, int32 item_id, int16 quantity, int32 price)
  144. {
  145. LogWrite(MERCHANT__DEBUG, 3, "Merchant", "Saving Buyback - Player: %u, Item ID: %u, Qty: %i, Price: %u", char_id, item_id, quantity, price);
  146. Query query;
  147. string insert = string("INSERT INTO character_buyback (char_id, item_id, quantity, price) VALUES (%u, %u, %i, %u) ");
  148. query.AddQueryAsync(char_id, this, Q_INSERT, insert.c_str(), char_id, item_id, quantity, price);
  149. }
  150. int32 WorldDatabase::LoadCharacterSpells(int32 char_id, Player* player)
  151. {
  152. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Character Spells for player %s...", player->GetName());
  153. Query query;
  154. MYSQL_ROW row;
  155. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_id, tier, knowledge_slot, spell_book_type, linked_timer_id FROM character_spells, spells where character_spells.spell_id = spells.id and character_spells.char_id = %u ORDER BY spell_id, tier desc", char_id);
  156. int32 old_spell_id = 0;
  157. int32 new_spell_id = 0;
  158. int32 count = 0;
  159. if(result && mysql_num_rows(result) >0)
  160. {
  161. while(result && (row = mysql_fetch_row(result)))
  162. {
  163. count++;
  164. new_spell_id = atoul(row[0]);
  165. if(new_spell_id == old_spell_id)
  166. continue;
  167. old_spell_id = new_spell_id;
  168. LogWrite(SPELL__DEBUG, 5, "Spells", "\tLoading SpellID: %u, tier: %i, slot: %i, type: %u linked_timer_id: %u", new_spell_id, atoi(row[1]), atoi(row[2]), atoul(row[3]), atoul(row[4]));
  169. int8 tier = atoi(row[1]);
  170. if (player->HasSpell(new_spell_id, tier, true))
  171. continue;
  172. player->AddSpellBookEntry(new_spell_id, tier, atoi(row[2]), atoul(row[3]), atoul(row[4]));
  173. }
  174. }
  175. return count;
  176. }
  177. void WorldDatabase::SavePlayerSpells(Client* client)
  178. {
  179. if(!client || client->GetCharacterID() < 1)
  180. return;
  181. LogWrite(SPELL__DEBUG, 3, "Spells", "Saving Spell(s) for Player: '%s'", client->GetPlayer()->GetName());
  182. vector<SpellBookEntry*>* spells = client->GetPlayer()->GetSpellsSaveNeeded();
  183. if(spells)
  184. {
  185. vector<SpellBookEntry*>::iterator itr;
  186. SpellBookEntry* spell = 0;
  187. for(itr = spells->begin(); itr != spells->end(); itr++)
  188. {
  189. spell = *itr;
  190. Query query;
  191. LogWrite(SPELL__DEBUG, 5, "Spells", "\tSaving SpellID: %u, tier: %i, slot: %i", spell->spell_id, spell->tier, spell->slot);
  192. query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "INSERT INTO character_spells (char_id, spell_id, tier) SELECT %u, %u, %i ON DUPLICATE KEY UPDATE tier = %i",
  193. client->GetPlayer()->GetCharacterID(), spell->spell_id, spell->tier, spell->tier);
  194. spell->save_needed = false;
  195. }
  196. safe_delete(spells);
  197. }
  198. }
  199. int32 WorldDatabase::LoadCharacterSkills(int32 char_id, Player* player)
  200. {
  201. Query query;
  202. MYSQL_ROW row;
  203. int32 count = 0;
  204. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT skill_id, current_val, max_val FROM character_skills, skills where character_skills.skill_id = skills.id and character_skills.char_id = %u", char_id);
  205. if(result && mysql_num_rows(result) >0)
  206. {
  207. while(result && (row = mysql_fetch_row(result)))
  208. {
  209. count++;
  210. LogWrite(SKILL__DEBUG, 5, "Skills", "Loading SkillID: %u, cur_val: %i, max_val: %l", strtoul(row[0], NULL, 0), atoi(row[1]), atoi(row[2]));
  211. player->AddSkill(strtoul(row[0], NULL, 0), atoi(row[1]), atoi(row[2]));
  212. }
  213. }
  214. return count;
  215. }
  216. void WorldDatabase::DeleteCharacterSkill(int32 char_id, Skill* skill)
  217. {
  218. if (char_id > 0 && skill)
  219. {
  220. LogWrite(SKILL__DEBUG, 0, "Skills", "Deleting Skill '%s' (%u) from char_id: %u", skill->name.data.c_str(), skill->skill_id, char_id);
  221. Query query;
  222. query.RunQuery2(Q_DELETE, "DELETE FROM `character_skills` WHERE `char_id`=%u AND `skill_id`=%u", char_id, skill->skill_id);
  223. }
  224. }
  225. int32 WorldDatabase::LoadSkills()
  226. {
  227. int32 total = 0;
  228. Query query;
  229. MYSQL_ROW row;
  230. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, short_name, name, description, skill_type, display FROM skills");
  231. if(result)
  232. {
  233. if (mysql_num_rows(result) >0)
  234. {
  235. Skill* skill = 0;
  236. while(result && (row = mysql_fetch_row(result)))
  237. {
  238. skill = new Skill();
  239. skill->skill_id = strtoul(row[0], NULL, 0);
  240. skill->short_name.data = string(row[1]);
  241. skill->short_name.size = skill->short_name.data.length();
  242. skill->name.data = string(row[2]);
  243. skill->name.size = skill->name.data.length();
  244. skill->description.data = string(row[3]);
  245. skill->description.size = skill->description.data.length();
  246. skill->skill_type = strtoul(row[4], NULL, 0);
  247. //these two need to be converted to the correct numbers
  248. if(skill->skill_type == 13)
  249. skill->skill_type = SKILL_TYPE_LANGUAGE;
  250. else if(skill->skill_type == 12)
  251. skill->skill_type = SKILL_TYPE_GENERAL;
  252. skill->display = atoi(row[5]);
  253. master_skill_list.AddSkill(skill);
  254. total++;
  255. LogWrite(SKILL__DEBUG, 5, "Skill", "---Loading Skill: %s (%u)", skill->name.data.c_str(), skill->skill_id);
  256. LogWrite(SKILL__DEBUG, 7, "Skill", "---short_name: %s, type: %i, display: %i", skill->short_name.data.c_str(), skill->skill_type, skill->display);
  257. }
  258. }
  259. }
  260. LogWrite(SKILL__DEBUG, 3, "Skill", "--Loaded %u Skill(s)", total);
  261. return total;
  262. }
  263. map<int8, vector<MacroData*> >* WorldDatabase::LoadCharacterMacros(int32 char_id)
  264. {
  265. Query query;
  266. MYSQL_ROW row;
  267. int32 total = 0;
  268. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT macro_number, macro_name, macro_icon, macro_text FROM character_macros where char_id = %u ORDER BY macro_number, id", char_id);
  269. if(result && mysql_num_rows(result) >0)
  270. {
  271. map<int8, vector<MacroData*> >* macros = new map<int8, vector<MacroData*> >;
  272. while(result && (row = mysql_fetch_row(result)))
  273. {
  274. MacroData* data = new MacroData;
  275. data->name = row[1];
  276. data->icon = atoi(row[2]);
  277. data->text = row[3];
  278. (*macros)[atoi(row[0])].push_back(data);
  279. total++;
  280. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLoading macro: %i. %s for player: %u", atoi(row[0]), row[1], char_id);
  281. }
  282. LogWrite(PLAYER__DEBUG, 0, "Player", "\tLoaded %u macro%s", total, total == 1 ? "" : "s");
  283. return macros;
  284. }
  285. return 0;
  286. }
  287. void WorldDatabase::UpdateCharacterMacro(int32 char_id, int8 number, const char* name, int16 icon, vector<string>* updates)
  288. {
  289. LogWrite(PLAYER__DEBUG, 0, "Player", "Update player id %u macro: %i", char_id, number);
  290. Query query;
  291. Query query2;
  292. query.RunQuery2(Q_DELETE, "delete FROM character_macros where char_id = %u and macro_number = %i", char_id, number);
  293. if(name && updates && updates->size() > 0)
  294. {
  295. for(int8 i=0;i<updates->size();i++)
  296. {
  297. query2.RunQuery2(Q_INSERT, "insert into character_macros (char_id, macro_number, macro_name, macro_icon, macro_text) values(%u, %i, '%s', %i, '%s')", char_id, number, getSafeEscapeString(name).c_str(), icon, getSafeEscapeString(updates->at(i).c_str()).c_str());
  298. LogWrite(PLAYER__DEBUG, 5, "Player", "\tAdding macro: %s, %s (Player: %u)", name, updates->at(i).c_str(), char_id);
  299. }
  300. }
  301. }
  302. //we use our timestamp just in case db is on another server, otherwise times might be off
  303. void WorldDatabase::UpdateVitality(int32 timestamp, float amount){
  304. Query query;
  305. LogWrite(PLAYER__DEBUG, 3, "Player", "Reset Vitality > 100: %f", amount);
  306. query.RunQuery2(Q_UPDATE, "update character_details set xp_vitality=100 where (xp_vitality + %f) > 100", amount);
  307. LogWrite(PLAYER__DEBUG, 3, "Player", "Update Vitality <= 100: %f", amount);
  308. query.RunQuery2(Q_UPDATE, "update character_details set xp_vitality=(xp_vitality+%f) where (xp_vitality + %f) <= 100", amount, amount);
  309. LogWrite(PLAYER__DEBUG, 3, "Player", "Update Vitality Timer: %u", timestamp);
  310. query.RunQuery2(Q_UPDATE, "update variables set variable_value=%u where variable_name='vitalitytimer'", timestamp);
  311. }
  312. void WorldDatabase::SaveVariable(const char* name, const char* value, const char* comment){
  313. LogWrite(WORLD__DEBUG, 0, "Variables", "Saving Variable: %s = %s", name, value);
  314. Query query;
  315. if(comment){
  316. query.RunQuery2(Q_REPLACE, "replace into variables (variable_name, variable_value, comment) values('%s', '%s', '%s')",
  317. getSafeEscapeString(name).c_str(), getSafeEscapeString(value).c_str(), getSafeEscapeString(comment).c_str());
  318. }
  319. else{
  320. query.RunQuery2(Q_REPLACE, "replace into variables (variable_name, variable_value) values('%s', '%s')",
  321. getSafeEscapeString(name).c_str(), getSafeEscapeString(value).c_str());
  322. }
  323. }
  324. void WorldDatabase::LoadGlobalVariables(){
  325. variables.ClearVariables ( );
  326. int32 total = 0;
  327. Query query;
  328. MYSQL_ROW row;
  329. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT variable_name, variable_value, comment FROM variables");
  330. while(result && (row = mysql_fetch_row(result)))
  331. {
  332. Variable* newVar = new Variable(row[0], row[1], row[2]);
  333. variables.AddVariable(newVar);
  334. total++;
  335. LogWrite(WORLD__DEBUG, 5, "World", "---Loading variable: '%s' = '%s'", row[0], row[1]);
  336. }
  337. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u variables", total);
  338. }
  339. void WorldDatabase::LoadAppearanceMasterList()
  340. {
  341. DatabaseResult result;
  342. int32 total = 0;
  343. int32 appearance_id;
  344. int16 appearance_version;
  345. master_appearance_list.ClearAppearances();
  346. database_new.Select(&result, "SELECT appearance_id, `name`, min_client_version FROM appearances ORDER BY appearance_id");
  347. while( result.Next() )
  348. {
  349. appearance_id = result.GetInt32Str("appearance_id");
  350. const char *appearance_name = result.GetStringStr("name");
  351. appearance_version = result.GetInt16Str("min_client_version");
  352. Appearance* a = new Appearance(appearance_id, appearance_name, appearance_version);
  353. master_appearance_list.InsertAppearance(a);
  354. total++;
  355. LogWrite(WORLD__DEBUG, 5, "World", "---Loading appearances: '%s' (%i)", appearance_name, appearance_id);
  356. }
  357. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u appearances", total);
  358. }
  359. void WorldDatabase::LoadVisualStates()
  360. {
  361. visual_states.Reset();
  362. int32 total = 0;
  363. Query query;
  364. Query query2;
  365. MYSQL_ROW row;
  366. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT visual_state_id, name FROM visual_states");
  367. while(result && (row = mysql_fetch_row(result)))
  368. {
  369. VisualState* vs = new VisualState(atoi(row[0]), row[1]);
  370. visual_states.InsertVisualState(vs);
  371. total++;
  372. LogWrite(WORLD__DEBUG, 5, "World", "---Loading visual state: '%s' (%i)", row[1], atoi(row[0]));
  373. }
  374. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u visual states", total);
  375. total = 0;
  376. result = query2.RunQuery2(Q_SELECT, "SELECT name, visual_state_id, message, targeted_message, min_version_range, max_version_range FROM emotes");
  377. while(result && (row = mysql_fetch_row(result)))
  378. {
  379. EmoteVersionRange* range = 0;
  380. if ((range = visual_states.FindEmoteRange(string(row[0]))) == NULL)
  381. {
  382. range = new EmoteVersionRange(row[0]);
  383. visual_states.InsertEmoteRange(range);
  384. }
  385. range->AddVersionRange(atoul(row[4]),atoul(row[5]), row[0], atoi(row[1]), row[2], row[3]);
  386. total++;
  387. LogWrite(WORLD__DEBUG, 5, "World", "---Loading emote state: '%s' (%i)", row[1], atoi(row[0]));
  388. }
  389. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u emote state(s)", total);
  390. }
  391. void WorldDatabase::LoadSubCommandList()
  392. {
  393. int32 total = 0;
  394. Query query;
  395. MYSQL_ROW row;
  396. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT command, subcommand, handler, required_status FROM commands where length(subcommand) > 0 ORDER BY handler asc");
  397. while(result && (row = mysql_fetch_row(result)))
  398. {
  399. commands.GetRemoteCommands()->CheckAddSubCommand(string(row[0]), EQ2_RemoteCommandString(row[1], (int32)strtoul(row[2], NULL, 0), atoi(row[3])));
  400. total++;
  401. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Command: '%s', sub '%s', handler, %u status %i", row[0], row[1], atoul(row[2]), atoi(row[3]));
  402. }
  403. LogWrite(COMMAND__DEBUG, 3, "Command", "--Loaded %i Subcommand(s)", total);
  404. }
  405. void WorldDatabase::LoadCommandList()
  406. {
  407. Query query;
  408. MYSQL_ROW row;
  409. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT command, handler, required_status FROM commands where length(subcommand) = 0 ORDER BY handler asc");
  410. int16 index = 0;
  411. while(result && (row = mysql_fetch_row(result)))
  412. {
  413. int32 handler = strtoul(row[1], NULL, 0);
  414. while(handler>index && handler != 999)
  415. {
  416. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Remote Commands: handler %u, index %u", handler, index);
  417. commands.GetRemoteCommands()->addZero();
  418. index++;
  419. }
  420. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Commands: handler %u, index %u", handler, index);
  421. commands.GetRemoteCommands()->addCommand(EQ2_RemoteCommandString(row[0], handler, atoi(row[2])));
  422. index++;
  423. }
  424. LogWrite(COMMAND__DEBUG, 3, "Command", "--Loaded %i Command%s", index, index > 0 ? "s" : "");
  425. LoadSubCommandList();
  426. }
  427. int32 WorldDatabase::LoadNPCSpells(ZoneServer* zone){
  428. Query query;
  429. MYSQL_ROW row;
  430. int32 count = 0;
  431. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_list_id, spell_id, spell_tier FROM spawn_npc_spells where spell_list_id > 0");
  432. while(result && (row = mysql_fetch_row(result))){
  433. zone->AddNPCSpell(atoul(row[0]), atoul(row[1]), atoi(row[2]));
  434. count++;
  435. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Spell List: %u, spell id: %u, tier: %i", atoul(row[0]), atoul(row[1]), atoi(row[2]));
  436. }
  437. return count;
  438. }
  439. int32 WorldDatabase::LoadNPCSkills(ZoneServer* zone){
  440. Query query;
  441. MYSQL_ROW row;
  442. int32 count = 0;
  443. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT skill_list_id, skill_id, starting_value FROM spawn_npc_skills");
  444. while(result && (row = mysql_fetch_row(result))){
  445. zone->AddNPCSkill(atoul(row[0]), atoul(row[1]), atoi(row[2]));
  446. count++;
  447. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Skill List: %u, skill id: %u, value: %i", atoul(row[0]), atoul(row[1]), atoi(row[2]));
  448. }
  449. return count;
  450. }
  451. int32 WorldDatabase::LoadNPCEquipment(ZoneServer* zone){
  452. Query query;
  453. MYSQL_ROW row;
  454. int32 count = 0;
  455. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT equipment_list_id, item_id FROM spawn_npc_equipment");
  456. while(result && (row = mysql_fetch_row(result))){
  457. zone->AddNPCEquipment(atoul(row[0]), atoul(row[1]));
  458. count++;
  459. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Equipment List: %u, item: %u", atoul(row[0]), atoul(row[1]));
  460. }
  461. return count;
  462. }
  463. int8 WorldDatabase::GetAppearanceType(string type){
  464. int8 ret = 255;
  465. if (type == "soga_hair_face_highlight_color")
  466. ret = APPEARANCE_SOGA_HFHC;
  467. else if (type == "soga_hair_type_highlight_color")
  468. ret = APPEARANCE_SOGA_HTHC;
  469. else if (type == "soga_hair_face_color")
  470. ret = APPEARANCE_SOGA_HFC;
  471. else if (type == "soga_hair_type_color")
  472. ret = APPEARANCE_SOGA_HTC;
  473. else if (type == "soga_hair_highlight")
  474. ret = APPEARANCE_SOGA_HH;
  475. else if (type == "soga_hair_color1")
  476. ret = APPEARANCE_SOGA_HC1;
  477. else if (type == "soga_hair_color2")
  478. ret = APPEARANCE_SOGA_HC2;
  479. else if (type == "hair_type_color")
  480. ret = APPEARANCE_HTC;
  481. else if (type == "soga_skin_color")
  482. ret = APPEARANCE_SOGA_SC;
  483. else if (type == "soga_eye_color")
  484. ret = APPEARANCE_SOGA_EC;
  485. else if (type == "hair_type_highlight_color")
  486. ret = APPEARANCE_HTHC;
  487. else if (type == "hair_face_highlight_color")
  488. ret = APPEARANCE_HFHC;
  489. else if (type == "hair_face_color")
  490. ret = APPEARANCE_HFC;
  491. else if (type == "hair_highlight")
  492. ret = APPEARANCE_HH;
  493. else if (type == "hair_color1")
  494. ret = APPEARANCE_HC1;
  495. else if (type == "wing_color1")
  496. ret = APPEARANCE_WC1;
  497. else if (type == "hair_color2")
  498. ret = APPEARANCE_HC2;
  499. else if (type == "wing_color2")
  500. ret = APPEARANCE_WC2;
  501. else if (type == "skin_color")
  502. ret = APPEARANCE_SC;
  503. else if (type == "eye_color")
  504. ret = APPEARANCE_EC;
  505. else if (type == "soga_eye_brow_type")
  506. ret = APPEARANCE_SOGA_EBT;
  507. else if (type == "soga_cheek_type")
  508. ret = APPEARANCE_SOGA_CHEEKT;
  509. else if (type == "soga_nose_type")
  510. ret = APPEARANCE_SOGA_NT;
  511. else if (type == "soga_chin_type")
  512. ret = APPEARANCE_SOGA_CHINT;
  513. else if (type == "soga_lip_type")
  514. ret = APPEARANCE_SOGA_LT;
  515. else if (type == "eye_brow_type")
  516. ret = APPEARANCE_EBT;
  517. else if (type == "soga_ear_type")
  518. ret = APPEARANCE_SOGA_EART;
  519. else if (type == "soga_eye_type")
  520. ret = APPEARANCE_SOGA_EYET;
  521. else if (type == "cheek_type")
  522. ret = APPEARANCE_CHEEKT;
  523. else if (type == "nose_type")
  524. ret = APPEARANCE_NT;
  525. else if (type == "chin_type")
  526. ret = APPEARANCE_CHINT;
  527. else if (type == "ear_type")
  528. ret = APPEARANCE_EART;
  529. else if (type == "eye_type")
  530. ret = APPEARANCE_EYET;
  531. else if (type == "lip_type")
  532. ret = APPEARANCE_LT;
  533. else if (type == "shirt_color")
  534. ret = APPEARANCE_SHIRT;
  535. else if (type == "unknown_chest_color")
  536. ret = APPEARANCE_UCC;
  537. else if (type == "pants_color")
  538. ret = APPEARANCE_PANTS;
  539. else if (type == "unknown_legs_color")
  540. ret = APPEARANCE_ULC;
  541. else if (type == "unknown9")
  542. ret = APPEARANCE_U9;
  543. else if (type == "body_size")
  544. ret = APPEARANCE_BODY_SIZE;
  545. else if (type == "soga_wing_color1")
  546. ret = APPEARANCE_SOGA_WC1;
  547. else if (type == "soga_wing_color2")
  548. ret = APPEARANCE_SOGA_WC2;
  549. else if (type == "soga_shirt_color")
  550. ret = APPEARANCE_SOGA_SHIRT;
  551. else if (type == "soga_unknown_chest_color")
  552. ret = APPEARANCE_SOGA_UCC;
  553. else if (type == "soga_pants_color")
  554. ret = APPEARANCE_SOGA_PANTS;
  555. else if (type == "soga_unknown_legs_color")
  556. ret = APPEARANCE_SOGA_ULC;
  557. else if (type == "soga_unknown13")
  558. ret = APPEARANCE_SOGA_U13;
  559. else if (type == "body_age")
  560. ret = APPEARANCE_BODY_AGE;
  561. else if (type == "model_color")
  562. ret = APPEARANCE_MC;
  563. else if (type == "soga_model_color")
  564. ret = APPEARANCE_SMC;
  565. return ret;
  566. }
  567. int32 WorldDatabase::LoadAppearances(ZoneServer* zone, Client* client){
  568. Query query, query2;
  569. MYSQL_ROW row;
  570. int32 count = 0, spawn_id = 0, new_spawn_id = 0;
  571. Entity* entity = 0;
  572. if(client)
  573. entity = client->GetPlayer();
  574. map<string, int8> appearance_types;
  575. map<int32, map<int8, EQ2_Color> > appearance_colors;
  576. EQ2_Color color;
  577. color.red = 0;
  578. color.green = 0;
  579. color.blue = 0;
  580. string type;
  581. MYSQL_RES* result = 0;
  582. if(!client)
  583. result = query.RunQuery2(Q_SELECT, "SELECT distinct `type` FROM npc_appearance where length(type) > 0");
  584. else
  585. result = query.RunQuery2(Q_SELECT, "SELECT distinct `type` FROM char_colors where length(type) > 0 and char_id=%u", client->GetCharacterID());
  586. while(result && (row = mysql_fetch_row(result))){
  587. type = string(row[0]);
  588. appearance_types[type] = GetAppearanceType(type);
  589. if(appearance_types[type] == 255)
  590. LogWrite(WORLD__ERROR, 0, "Appearance", "Unknown appearance type '%s' in LoadAppearances.", type.c_str());
  591. }
  592. MYSQL_RES* result2 = 0;
  593. if(!client)
  594. result2 = query2.RunQuery2(Q_SELECT, "SELECT `type`, spawn_id, signed_value, red, green, blue FROM npc_appearance where length(type) > 0 ORDER BY spawn_id");
  595. else
  596. result2 = query2.RunQuery2(Q_SELECT, "SELECT `type`, char_id, signed_value, red, green, blue FROM char_colors where length(type) > 0 and char_id=%u", client->GetCharacterID());
  597. while(result2 && (row = mysql_fetch_row(result2))){
  598. if(!client){
  599. new_spawn_id = atoul(row[1]);
  600. if(spawn_id != new_spawn_id){
  601. entity = zone->GetNPC(new_spawn_id, true);
  602. if(!entity)
  603. continue;
  604. if(spawn_id > 0)
  605. count++;
  606. spawn_id = new_spawn_id;
  607. }
  608. }
  609. if(appearance_types[row[0]] < APPEARANCE_SOGA_EBT){
  610. color.red = atoi(row[3]);
  611. color.green = atoi(row[4]);
  612. color.blue = atoi(row[5]);
  613. }
  614. switch(appearance_types[row[0]]){
  615. case APPEARANCE_SOGA_HFHC:{
  616. entity->features.soga_hair_face_highlight_color = color;
  617. break;
  618. }
  619. case APPEARANCE_SOGA_HTHC:{
  620. entity->features.soga_hair_type_highlight_color = color;
  621. break;
  622. }
  623. case APPEARANCE_SOGA_HFC:{
  624. entity->features.soga_hair_face_color = color;
  625. break;
  626. }
  627. case APPEARANCE_SOGA_HTC:{
  628. entity->features.soga_hair_type_color = color;
  629. break;
  630. }
  631. case APPEARANCE_SOGA_HH:{
  632. entity->features.soga_hair_highlight_color = color;
  633. break;
  634. }
  635. case APPEARANCE_SOGA_HC1:{
  636. entity->features.soga_hair_color1 = color;
  637. break;
  638. }
  639. case APPEARANCE_SOGA_HC2:{
  640. entity->features.soga_hair_color2 = color;
  641. break;
  642. }
  643. case APPEARANCE_SOGA_SC:{
  644. entity->features.soga_skin_color = color;
  645. break;
  646. }
  647. case APPEARANCE_SOGA_EC:{
  648. entity->features.soga_eye_color = color;
  649. break;
  650. }
  651. case APPEARANCE_HTHC:{
  652. entity->features.hair_type_highlight_color = color;
  653. break;
  654. }
  655. case APPEARANCE_HFHC:{
  656. entity->features.hair_face_highlight_color = color;
  657. break;
  658. }
  659. case APPEARANCE_HTC:{
  660. entity->features.hair_type_color = color;
  661. break;
  662. }
  663. case APPEARANCE_HFC:{
  664. entity->features.hair_face_color = color;
  665. break;
  666. }
  667. case APPEARANCE_HH:{
  668. entity->features.hair_highlight_color = color;
  669. break;
  670. }
  671. case APPEARANCE_HC1:{
  672. entity->features.hair_color1 = color;
  673. break;
  674. }
  675. case APPEARANCE_HC2:{
  676. entity->features.hair_color2 = color;
  677. break;
  678. }
  679. case APPEARANCE_WC1:{
  680. entity->features.wing_color1 = color;
  681. break;
  682. }
  683. case APPEARANCE_WC2:{
  684. entity->features.wing_color2 = color;
  685. break;
  686. }
  687. case APPEARANCE_SC:{
  688. entity->features.skin_color = color;
  689. break;
  690. }
  691. case APPEARANCE_EC:{
  692. entity->features.eye_color = color;
  693. break;
  694. }
  695. case APPEARANCE_SOGA_EBT:{
  696. for(int i=0;i<3;i++)
  697. entity->features.soga_eye_brow_type[i] = atoi(row[3+i]);
  698. break;
  699. }
  700. case APPEARANCE_SOGA_CHEEKT:{
  701. for(int i=0;i<3;i++)
  702. entity->features.soga_cheek_type[i] = atoi(row[3+i]);
  703. break;
  704. }
  705. case APPEARANCE_SOGA_NT:{
  706. for(int i=0;i<3;i++)
  707. entity->features.soga_nose_type[i] = atoi(row[3+i]);
  708. break;
  709. }
  710. case APPEARANCE_SOGA_CHINT:{
  711. for(int i=0;i<3;i++)
  712. entity->features.soga_chin_type[i] = atoi(row[3+i]);
  713. break;
  714. }
  715. case APPEARANCE_SOGA_LT:{
  716. for(int i=0;i<3;i++)
  717. entity->features.soga_lip_type[i] = atoi(row[3+i]);
  718. break;
  719. }
  720. case APPEARANCE_SOGA_EART:{
  721. for(int i=0;i<3;i++)
  722. entity->features.soga_ear_type[i] = atoi(row[3+i]);
  723. break;
  724. }
  725. case APPEARANCE_SOGA_EYET:{
  726. for(int i=0;i<3;i++)
  727. entity->features.soga_eye_type[i] = atoi(row[3+i]);
  728. break;
  729. }
  730. case APPEARANCE_EBT:{
  731. for(int i=0;i<3;i++)
  732. entity->features.eye_brow_type[i] = atoi(row[3+i]);
  733. break;
  734. }
  735. case APPEARANCE_CHEEKT:{
  736. for(int i=0;i<3;i++)
  737. entity->features.cheek_type[i] = atoi(row[3+i]);
  738. break;
  739. }
  740. case APPEARANCE_NT:{
  741. for(int i=0;i<3;i++)
  742. entity->features.nose_type[i] = atoi(row[3+i]);
  743. break;
  744. }
  745. case APPEARANCE_CHINT:{
  746. for(int i=0;i<3;i++)
  747. entity->features.chin_type[i] = atoi(row[3+i]);
  748. break;
  749. }
  750. case APPEARANCE_EART:{
  751. for(int i=0;i<3;i++)
  752. entity->features.ear_type[i] = atoi(row[3+i]);
  753. break;
  754. }
  755. case APPEARANCE_EYET:{
  756. for(int i=0;i<3;i++)
  757. entity->features.eye_type[i] = atoi(row[3+i]);
  758. break;
  759. }
  760. case APPEARANCE_LT:{
  761. for(int i=0;i<3;i++)
  762. entity->features.lip_type[i] = atoi(row[3+i]);
  763. break;
  764. }
  765. case APPEARANCE_SHIRT:{
  766. entity->features.shirt_color = color;
  767. break;
  768. }
  769. case APPEARANCE_UCC:{
  770. break;
  771. }
  772. case APPEARANCE_PANTS:{
  773. entity->features.pants_color = color;
  774. break;
  775. }
  776. case APPEARANCE_ULC:{
  777. break;
  778. }
  779. case APPEARANCE_U9:{
  780. break;
  781. }
  782. case APPEARANCE_BODY_SIZE:{
  783. entity->features.body_size = color.red;
  784. break;
  785. }
  786. case APPEARANCE_SOGA_WC1:{
  787. break;
  788. }
  789. case APPEARANCE_SOGA_WC2:{
  790. break;
  791. }
  792. case APPEARANCE_SOGA_SHIRT:{
  793. break;
  794. }
  795. case APPEARANCE_SOGA_UCC:{
  796. break;
  797. }
  798. case APPEARANCE_SOGA_PANTS:{
  799. break;
  800. }
  801. case APPEARANCE_SOGA_ULC:{
  802. break;
  803. }
  804. case APPEARANCE_SOGA_U13:{
  805. break;
  806. }
  807. case APPEARANCE_BODY_AGE: {
  808. entity->features.body_age = color.red;
  809. break;
  810. }
  811. case APPEARANCE_MC:{
  812. entity->features.model_color = color;
  813. break;
  814. }
  815. case APPEARANCE_SMC:{
  816. entity->features.soga_model_color = color;
  817. break;
  818. }
  819. }
  820. entity->info_changed = true;
  821. }
  822. return count;
  823. }
  824. void WorldDatabase::LoadNPCs(ZoneServer* zone){
  825. Query query;
  826. MYSQL_ROW row;
  827. NPC* npc = 0;
  828. int32 id = 0;
  829. int32 total = 0;
  830. MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type\n"
  831. "FROM spawn s\n"
  832. "INNER JOIN spawn_npcs npc\n"
  833. "ON s.id = npc.spawn_id\n"
  834. "INNER JOIN spawn_location_entry le\n"
  835. "ON npc.spawn_id = le.spawn_id\n"
  836. "INNER JOIN spawn_location_placement lp\n"
  837. "ON le.spawn_location_id = lp.spawn_location_id\n"
  838. "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
  839. "GROUP BY s.id",
  840. zone->GetZoneID(), zone->GetInstanceID());
  841. while(result && (row = mysql_fetch_row(result))){
  842. /*npc->SetAppearanceID(atoi(row[12]));
  843. AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
  844. if(appearance)
  845. memcpy(&npc->appearance, appearance, sizeof(AppearanceData));
  846. */
  847. int32 npcXpackFlag = atoul(row[75]);
  848. int32 npcHolidayFlag = atoul(row[76]);
  849. if (!CheckExpansionFlags(zone, npcXpackFlag) || !CheckHolidayFlags(zone, npcHolidayFlag))
  850. continue;
  851. id = atoul(row[0]);
  852. if(zone->GetNPC(id, true))
  853. continue;
  854. npc = new NPC();
  855. npc->SetDatabaseID(id);
  856. strcpy(npc->appearance.name, row[1]);
  857. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  858. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  859. if(primary_command_list){
  860. npc->SetPrimaryCommands(primary_command_list);
  861. npc->primary_command_list_id = atoul(row[9]);
  862. }
  863. if(secondary_command_list){
  864. npc->SetSecondaryCommands(secondary_command_list);
  865. npc->secondary_command_list_id = atoul(row[10]);
  866. }
  867. npc->appearance.min_level = atoi(row[2]);
  868. npc->appearance.max_level = atoi(row[3]);
  869. npc->appearance.level = atoi(row[2]);
  870. npc->appearance.encounter_level = atoi(row[4]);
  871. npc->appearance.race = atoi(row[5]);
  872. //npc->appearance.lua_race_id = atoi(row[75]);
  873. if (atoi(row[74]) > 0) {
  874. int16 xxx = atoi(row[75]);
  875. int8 yyy = 0;
  876. }
  877. npc->appearance.model_type = atoi(row[6]);
  878. npc->appearance.soga_model_type = atoi(row[62]);
  879. npc->appearance.adventure_class = atoi(row[7]);
  880. npc->appearance.gender = atoi(row[8]);
  881. npc->appearance.display_name = atoi(row[11]);
  882. npc->features.hair_type = atoi(row[14]);
  883. npc->features.hair_face_type = atoi(row[15]);
  884. npc->features.wing_type = atoi(row[16]);
  885. npc->features.chest_type = atoi(row[17]);
  886. npc->features.legs_type = atoi(row[18]);
  887. npc->features.soga_hair_type = atoi(row[19]);
  888. npc->features.soga_hair_face_type = atoi(row[20]);
  889. npc->appearance.attackable = atoi(row[21]);
  890. npc->appearance.show_level = atoi(row[22]);
  891. npc->appearance.targetable = atoi(row[23]);
  892. npc->appearance.show_command_icon = atoi(row[24]);
  893. npc->appearance.display_hand_icon = atoi(row[25]);
  894. npc->appearance.hide_hood = atoi(row[70]);
  895. npc->appearance.randomize = atoi(row[61]);
  896. npc->SetTotalHP(atoul(row[26]));
  897. npc->SetTotalPower(atoul(row[27]));
  898. npc->SetHP(npc->GetTotalHP());
  899. npc->SetPower(npc->GetTotalPower());
  900. if(npc->GetTotalHP() == 0){
  901. npc->SetTotalHP(15*npc->GetLevel() + 1);
  902. npc->SetHP(15*npc->GetLevel() + 1);
  903. }
  904. if(npc->GetTotalPower() == 0){
  905. npc->SetTotalPower(15*npc->GetLevel() + 1);
  906. npc->SetPower(15*npc->GetLevel() + 1);
  907. }
  908. npc->size = atoi(row[28]);
  909. npc->appearance.pos.collision_radius = atoi(row[29]);
  910. npc->appearance.action_state = atoi(row[30]);
  911. npc->appearance.visual_state = atoi(row[31]);
  912. npc->appearance.mood_state = atoi(row[32]);
  913. npc->appearance.emote_state = atoi(row[71]);
  914. npc->appearance.pos.state = atoi(row[33]);
  915. npc->appearance.activity_status = atoi(row[34]);
  916. npc->faction_id = atoul(row[35]);
  917. if(row[36]){
  918. if(strlen(row[36]) < sizeof(npc->appearance.sub_title))
  919. strcpy(npc->appearance.sub_title, row[36]);
  920. else
  921. strncpy(npc->appearance.sub_title, row[36], sizeof(npc->appearance.sub_title));
  922. }
  923. npc->SetMerchantID(atoul(row[37]));
  924. npc->SetMerchantType(atoi(row[38]));
  925. npc->SetSizeOffset(atoi(row[39]));
  926. npc->SetAttackType(atoi(row[40]));
  927. npc->SetAIStrategy(atoi(row[41]));
  928. npc->SetPrimarySpellList(atoul(row[42]));
  929. npc->SetSecondarySpellList(atoul(row[43]));
  930. npc->SetPrimarySkillList(atoul(row[44]));
  931. npc->SetSecondarySkillList(atoul(row[45]));
  932. npc->SetEquipmentListID(atoul(row[46]));
  933. InfoStruct* info = npc->GetInfoStruct();
  934. info->set_str_base(atoi(row[47]));
  935. info->set_sta_base(atoi(row[48]));
  936. info->set_wis_base(atoi(row[49]));
  937. info->set_intel_base(atoi(row[50]));
  938. info->set_agi_base(atoi(row[51]));
  939. info->set_heat_base(atoi(row[52]));
  940. info->set_cold_base(atoi(row[53]));
  941. info->set_magic_base(atoi(row[54]));
  942. info->set_mental_base(atoi(row[55]));
  943. info->set_divine_base(atoi(row[56]));
  944. info->set_disease_base(atoi(row[57]));
  945. info->set_poison_base(atoi(row[58]));
  946. info->set_alignment(atoi(row[64]));
  947. npc->SetAggroRadius(atof(row[59]));
  948. npc->SetCastPercentage(atoi(row[60]));
  949. npc->appearance.heroic_flag = atoi(row[63]);
  950. info->set_elemental_base(atoi(row[65]));
  951. info->set_arcane_base(atoi(row[66]));
  952. info->set_noxious_base(atoi(row[67]));
  953. npc->SetTotalSavagery(atoul(row[68]));
  954. npc->SetTotalDissonance(atoul(row[69]));
  955. npc->SetSavagery(npc->GetTotalSavagery());
  956. npc->SetDissonance(npc->GetTotalDissonance());
  957. if(npc->GetTotalSavagery() == 0){
  958. npc->SetTotalSavagery(15*npc->GetLevel() + 1);
  959. npc->SetSavagery(15*npc->GetLevel() + 1);
  960. }
  961. if(npc->GetTotalDissonance() == 0){
  962. npc->SetTotalDissonance(15*npc->GetLevel() + 1);
  963. npc->SetDissonance(15*npc->GetLevel() + 1);
  964. }
  965. npc->SetPrefixTitle(row[72]);
  966. npc->SetSuffixTitle(row[73]);
  967. npc->SetLastName(row[74]);
  968. // xpack+holiday value handled at top at position 75+76
  969. int8 disableSounds = atoul(row[77]);
  970. npc->SetSoundsDisabled(disableSounds);
  971. npc->SetMerchantLevelRange(atoul(row[78]), atoul(row[79]));
  972. npc->SetAAXPRewards(atoul(row[80]));
  973. info->set_water_type(atoul(row[81]));
  974. info->set_flying_type(atoul(row[82]));
  975. zone->AddNPC(id, npc);
  976. total++;
  977. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC: '%s' (%u)", npc->appearance.name, id);
  978. }
  979. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC(s).", total);
  980. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Spell(s).", LoadNPCSpells(zone));
  981. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Skill(s).", LoadNPCSkills(zone));
  982. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Equipment Piece(s).", LoadNPCEquipment(zone));
  983. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Appearance(s).", LoadAppearances(zone));
  984. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Equipment Appearance(s).", LoadNPCAppearanceEquipmentData(zone));
  985. }
  986. void WorldDatabase::LoadSpiritShards(ZoneServer* zone){
  987. Query query;
  988. MYSQL_ROW row;
  989. int32 id = 0;
  990. int32 total = 0;
  991. MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT timestamp, name, level, race, gender, adventure_class, model_type, soga_model_type, hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type, soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state, mood_state, emote_state, pos_state, activity_status, sub_title, prefix_title, suffix_title, lastname, x, y, z, heading, gridid, id, charid\n"
  992. "FROM character_spirit_shards\n"
  993. "WHERE zoneid = %u and (instanceid = 0 or instanceid = %u)",
  994. zone->GetZoneID(), zone->GetInstanceID());
  995. while(result && (row = mysql_fetch_row(result))){
  996. /*npc->SetAppearanceID(atoi(row[12]));
  997. AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
  998. if(appearance)
  999. memcpy(&npc->appearance, appearance, sizeof(AppearanceData));
  1000. */
  1001. sint64 timestamp = 0;
  1002. #ifdef WIN32
  1003. timestamp = _strtoui64(row[0], NULL, 10);
  1004. #else
  1005. timestamp = strtoull(row[0], 0, 10);
  1006. #endif
  1007. if(!row[1])
  1008. continue;
  1009. NPC* shard = new NPC();
  1010. shard->SetShardCreatedTimestamp(timestamp);
  1011. strcpy(shard->appearance.name, row[1]);
  1012. shard->appearance.level = atoul(row[2]);
  1013. shard->appearance.race = atoul(row[3]);
  1014. shard->appearance.gender = atoul(row[4]);
  1015. shard->appearance.adventure_class = atoul(row[5]);
  1016. //shard->appearance.lua_race_id = result.GetInt16(74);
  1017. shard->appearance.model_type = atoul(row[6]);
  1018. shard->appearance.soga_model_type = atoul(row[7]);
  1019. shard->appearance.display_name = 1;
  1020. shard->features.hair_type = atoul(row[8]);
  1021. shard->features.hair_face_type = atoul(row[9]);
  1022. shard->features.wing_type = atoul(row[10]);
  1023. shard->features.chest_type = atoul(row[11]);
  1024. shard->features.legs_type = atoul(row[12]);
  1025. shard->features.soga_hair_type = atoul(row[13]);
  1026. shard->features.soga_hair_face_type = atoul(row[14]);
  1027. shard->appearance.attackable = 0;
  1028. shard->appearance.show_level = 1;
  1029. shard->appearance.targetable = 1;
  1030. shard->appearance.show_command_icon = 1;
  1031. shard->appearance.display_hand_icon = 0;
  1032. shard->appearance.hide_hood = atoul(row[15]);
  1033. shard->size = atoul(row[16]);
  1034. shard->appearance.pos.collision_radius = atoul(row[17]);
  1035. shard->appearance.action_state = atoul(row[18]);
  1036. shard->appearance.visual_state = atoul(row[19]); // ghostly look
  1037. shard->appearance.mood_state = atoul(row[20]);
  1038. shard->appearance.emote_state = atoul(row[21]);
  1039. shard->appearance.pos.state = atoul(row[22]);
  1040. shard->appearance.activity_status = atoul(row[23]);
  1041. if(row[24])
  1042. strncpy(shard->appearance.sub_title, row[24], sizeof(shard->appearance.sub_title));
  1043. if(row[25])
  1044. shard->SetPrefixTitle(row[25]);
  1045. if(row[26])
  1046. shard->SetSuffixTitle(row[26]);
  1047. if(row[27])
  1048. shard->SetLastName(row[27]);
  1049. shard->SetX(atof(row[28]));
  1050. shard->SetY(atof(row[29]));
  1051. shard->SetZ(atof(row[30]));
  1052. shard->SetHeading(atof(row[31]));
  1053. shard->SetSpawnOrigX(shard->GetX());
  1054. shard->SetSpawnOrigY(shard->GetY());
  1055. shard->SetSpawnOrigZ(shard->GetZ());
  1056. shard->SetSpawnOrigHeading(shard->GetHeading());
  1057. shard->appearance.pos.grid_id = atoul(row[32]);
  1058. shard->SetShardID(atoul(row[33]));
  1059. shard->SetShardCharID(atoul(row[34]));
  1060. const char* script = rule_manager.GetGlobalRule(R_Combat, SpiritShardSpawnScript)->GetString();
  1061. if(script)
  1062. {
  1063. shard->SetSpawnScript(script);
  1064. zone->CallSpawnScript(shard, SPAWN_SCRIPT_PRESPAWN);
  1065. }
  1066. zone->AddSpawn(shard);
  1067. if(script)
  1068. zone->CallSpawnScript(shard, SPAWN_SCRIPT_SPAWN);
  1069. total++;
  1070. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading Player Spirit Shard: '%s' (%u)", shard->appearance.name, id);
  1071. }
  1072. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i Spirit Shard(s).", total);
  1073. }
  1074. void WorldDatabase::LoadSigns(ZoneServer* zone){
  1075. Query query;
  1076. MYSQL_ROW row;
  1077. Sign* sign = 0;
  1078. int32 id = 0;
  1079. int32 total = 0;
  1080. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  1081. "FROM spawn s\n"
  1082. "INNER JOIN spawn_signs ss\n"
  1083. "ON s.id = ss.spawn_id\n"
  1084. "INNER JOIN spawn_location_entry le\n"
  1085. "ON ss.spawn_id = le.spawn_id\n"
  1086. "INNER JOIN spawn_location_placement lp\n"
  1087. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1088. "WHERE lp.zone_id = %u\n"
  1089. "GROUP BY s.id",
  1090. zone->GetZoneID());
  1091. while(result && (row = mysql_fetch_row(result))){
  1092. int32 signXpackFlag = atoul(row[28]);
  1093. int32 signHolidayFlag = atoul(row[29]);
  1094. if (!CheckExpansionFlags(zone, signXpackFlag) || !CheckHolidayFlags(zone, signHolidayFlag))
  1095. continue;
  1096. id = atoul(row[0]);
  1097. if(zone->GetSign(id, true))
  1098. continue;
  1099. sign = new Sign();
  1100. sign->SetDatabaseID(id);
  1101. strcpy(sign->appearance.name, row[1]);
  1102. sign->appearance.model_type = atoi(row[2]);
  1103. sign->SetSize(atoi(row[3]));
  1104. sign->appearance.show_command_icon = atoi(row[4]);
  1105. sign->SetWidgetID(atoul(row[5]));
  1106. sign->SetWidgetX(atof(row[6]));
  1107. sign->SetWidgetY(atof(row[7]));
  1108. sign->SetWidgetZ(atof(row[8]));
  1109. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  1110. if(primary_command_list){
  1111. sign->SetPrimaryCommands(primary_command_list);
  1112. sign->primary_command_list_id = atoul(row[9]);
  1113. }
  1114. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  1115. if (secondary_command_list){
  1116. sign->SetSecondaryCommands(secondary_command_list);
  1117. sign->secondary_command_list_id = atoul(row[10]);
  1118. }
  1119. sign->appearance.pos.collision_radius = atoi(row[11]);
  1120. sign->SetSignIcon(atoi(row[12]));
  1121. if(strncasecmp(row[13],"Generic", 7) == 0)
  1122. sign->SetSignType(SIGN_TYPE_GENERIC);
  1123. else if(strncasecmp(row[13],"Zone", 4) == 0)
  1124. sign->SetSignType(SIGN_TYPE_ZONE);
  1125. sign->SetSignTitle(row[14]);
  1126. sign->SetSignDescription(row[15]);
  1127. sign->SetSignDistance(atof(row[16]));
  1128. sign->SetSignZoneID(atoul(row[17]));
  1129. sign->SetSignZoneX(atof(row[18]));
  1130. sign->SetSignZoneY(atof(row[19]));
  1131. sign->SetSignZoneZ(atof(row[20]));
  1132. sign->SetSignZoneHeading(atof(row[21]));
  1133. sign->SetIncludeHeading(atoi(row[22]) == 1);
  1134. sign->SetIncludeLocation(atoi(row[23]) == 1);
  1135. sign->SetTransporterID(atoul(row[24]));
  1136. sign->SetSizeOffset(atoi(row[25]));
  1137. sign->appearance.display_hand_icon = atoi(row[26]);
  1138. sign->SetVisualState(atoi(row[27]));
  1139. // xpack+holiday value handled at top at position 28+29
  1140. int8 disableSounds = atoul(row[30]);
  1141. sign->SetSoundsDisabled(disableSounds);
  1142. sign->SetMerchantLevelRange(atoul(row[31]), atoul(row[32]));
  1143. sign->SetAAXPRewards(atoul(row[33]));
  1144. zone->AddSign(id, sign);
  1145. total++;
  1146. LogWrite(SIGN__DEBUG, 5, "Sign", "---Loading Sign: '%s' (%u).", sign->appearance.name, id);
  1147. }
  1148. LogWrite(SIGN__DEBUG, 0, "Sign", "--Loaded %i Sign(s)", total);
  1149. }
  1150. void WorldDatabase::LoadWidgets(ZoneServer* zone){
  1151. Query query;
  1152. MYSQL_ROW row;
  1153. Widget* widget = 0;
  1154. int32 id = 0;
  1155. int32 total = 0;
  1156. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  1157. "FROM spawn s\n"
  1158. "INNER JOIN spawn_widgets sw\n"
  1159. "ON s.id = sw.spawn_id\n"
  1160. "INNER JOIN spawn_location_entry le\n"
  1161. "ON sw.spawn_id = le.spawn_id\n"
  1162. "INNER JOIN spawn_location_placement lp\n"
  1163. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1164. "WHERE lp.zone_id = %u\n"
  1165. "GROUP BY s.id",
  1166. zone->GetZoneID());
  1167. while(result && (row = mysql_fetch_row(result))){
  1168. int32 widgetXpackFlag = atoul(row[33]);
  1169. int32 widgetHolidayFlag = atoul(row[34]);
  1170. if (!CheckExpansionFlags(zone, widgetXpackFlag) || !CheckHolidayFlags(zone, widgetHolidayFlag))
  1171. continue;
  1172. id = atoul(row[0]);
  1173. if(zone->GetWidget(id, true))
  1174. continue;
  1175. widget = new Widget();
  1176. widget->SetDatabaseID(id);
  1177. strcpy(widget->appearance.name, row[1]);
  1178. widget->appearance.model_type = atoi(row[2]);
  1179. widget->SetSize(atoi(row[3]));
  1180. widget->appearance.show_command_icon = atoi(row[4]);
  1181. if (row[5] == NULL)
  1182. widget->SetWidgetID(0xFFFFFFFF);
  1183. else
  1184. widget->SetWidgetID(atoul(row[5]));
  1185. widget->SetWidgetX(atof(row[6]));
  1186. widget->SetWidgetY(atof(row[7]));
  1187. widget->SetWidgetZ(atof(row[8]));
  1188. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  1189. if(primary_command_list){
  1190. widget->SetPrimaryCommands(primary_command_list);
  1191. widget->primary_command_list_id = atoul(row[9]);
  1192. }
  1193. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  1194. if (secondary_command_list) {
  1195. widget->SetSecondaryCommands(secondary_command_list);
  1196. widget->secondary_command_list_id = atoul(row[10]);
  1197. }
  1198. widget->appearance.pos.collision_radius = atoi(row[11]);
  1199. widget->SetIncludeHeading(atoi(row[12]) == 1);
  1200. widget->SetIncludeLocation(atoi(row[13]) == 1);
  1201. widget->SetWidgetIcon(atoi(row[14]));
  1202. if (strncasecmp(row[15], "Generic", 7) == 0)
  1203. widget->SetWidgetType(WIDGET_TYPE_GENERIC);
  1204. else if (strncasecmp(row[15], "Door", 4) == 0)
  1205. widget->SetWidgetType(WIDGET_TYPE_DOOR);
  1206. else if (strncasecmp(row[15], "Lift", 4) == 0)
  1207. widget->SetWidgetType(WIDGET_TYPE_LIFT);
  1208. widget->SetOpenHeading(atof(row[16]));
  1209. widget->SetOpenY(atof(row[17]));
  1210. widget->SetActionSpawnID(atoul(row[18]));
  1211. if(row[19] && strlen(row[19]) > 5)
  1212. widget->SetOpenSound(row[19]);
  1213. if(row[20] && strlen(row[20]) > 5)
  1214. widget->SetCloseSound(row[20]);
  1215. widget->SetOpenDuration(atoi(row[21]));
  1216. widget->SetClosedHeading(atof(row[22]));
  1217. widget->SetLinkedSpawnID(atoul(row[23]));
  1218. widget->SetCloseY(atof(row[24]));
  1219. widget->SetTransporterID(atoul(row[25]));
  1220. widget->SetSizeOffset(atoi(row[26]));
  1221. widget->SetHouseID(atoul(row[27]));
  1222. widget->SetOpenX(atof(row[28]));
  1223. widget->SetOpenZ(atof(row[29]));
  1224. widget->SetCloseX(atof(row[30]));
  1225. widget->SetCloseZ(atof(row[31]));
  1226. widget->appearance.display_hand_icon = atoi(row[32]);
  1227. // xpack+holiday value handled at top at position 33+34
  1228. int8 disableSounds = atoul(row[35]);
  1229. widget->SetSoundsDisabled(disableSounds);
  1230. widget->SetMerchantLevelRange(atoul(row[36]), atoul(row[37]));
  1231. widget->SetAAXPRewards(atoul(row[38]));
  1232. zone->AddWidget(id, widget);
  1233. total++;
  1234. LogWrite(WIDGET__DEBUG, 5, "Widget", "---Loading Widget: '%s' (%u).", widget->appearance.name, id);
  1235. }
  1236. LogWrite(WIDGET__DEBUG, 0, "Widget", "--Loaded %i Widget(s)", total);
  1237. }
  1238. void WorldDatabase::LoadObjects(ZoneServer* zone){
  1239. Query query;
  1240. MYSQL_ROW row;
  1241. Object* object = 0;
  1242. int32 id = 0;
  1243. int32 total = 0;
  1244. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  1245. "FROM spawn s\n"
  1246. "INNER JOIN spawn_objects so\n"
  1247. "ON s.id = so.spawn_id\n"
  1248. "INNER JOIN spawn_location_entry le\n"
  1249. "ON so.spawn_id = le.spawn_id\n"
  1250. "INNER JOIN spawn_location_placement lp\n"
  1251. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1252. "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
  1253. "GROUP BY s.id",
  1254. zone->GetZoneID(), zone->GetInstanceID());
  1255. while(result && (row = mysql_fetch_row(result))){
  1256. int32 objXpackFlag = atoul(row[19]);
  1257. int32 objHolidayFlag = atoul(row[20]);
  1258. if (!CheckExpansionFlags(zone, objXpackFlag) || !CheckHolidayFlags(zone, objHolidayFlag))
  1259. continue;
  1260. id = atoul(row[0]);
  1261. if(zone->GetObject(id, true))
  1262. continue;
  1263. object = new Object();
  1264. object->SetDatabaseID(id);
  1265. strcpy(object->appearance.name, row[1]);
  1266. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[4]));
  1267. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[5]));
  1268. if(primary_command_list){
  1269. object->SetPrimaryCommands(primary_command_list);
  1270. object->primary_command_list_id = atoul(row[4]);
  1271. }
  1272. if(secondary_command_list){
  1273. object->SetSecondaryCommands(secondary_command_list);
  1274. object->secondary_command_list_id = atoul(row[5]);
  1275. }
  1276. object->appearance.race = atoi(row[2]);
  1277. object->appearance.model_type = atoi(row[3]);
  1278. object->appearance.targetable = atoi(row[6]);
  1279. object->size = atoi(row[7]);
  1280. object->appearance.display_name = atoi(row[8]);
  1281. object->appearance.visual_state = atoi(row[9]);
  1282. object->appearance.attackable = atoi(row[10]);
  1283. object->appearance.show_level = atoi(row[11]);
  1284. object->appearance.show_command_icon = atoi(row[12]);
  1285. object->appearance.display_hand_icon = atoi(row[13]);
  1286. object->faction_id = atoul(row[14]);
  1287. object->appearance.pos.collision_radius = atoi(row[15]);
  1288. object->SetTransporterID(atoul(row[16]));
  1289. object->SetSizeOffset(atoi(row[17]));
  1290. object->SetDeviceID(atoi(row[18]));
  1291. // xpack value handled at top at position 19
  1292. int8 disableSounds = atoul(row[21]);
  1293. object->SetSoundsDisabled(disableSounds);
  1294. object->SetMerchantLevelRange(atoul(row[22]), atoul(row[23]));
  1295. object->SetAAXPRewards(atoul(row[24]));
  1296. zone->AddObject(id, object);
  1297. total++;
  1298. LogWrite(OBJECT__DEBUG, 5, "Object", "---Loading Object: '%s' (%u)", object->appearance.name, id);
  1299. }
  1300. LogWrite(OBJECT__DEBUG, 0, "Object", "--Loaded %i Object(s)", total);
  1301. }
  1302. void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){
  1303. Query query;
  1304. MYSQL_ROW row;
  1305. GroundSpawn* spawn = 0;
  1306. int32 id = 0;
  1307. int32 total = 0;
  1308. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.aaxp_rewards\n"
  1309. "FROM spawn s\n"
  1310. "INNER JOIN spawn_ground sg\n"
  1311. "ON s.id = sg.spawn_id\n"
  1312. "INNER JOIN spawn_location_entry le\n"
  1313. "ON sg.spawn_id = le.spawn_id\n"
  1314. "INNER JOIN spawn_location_placement lp\n"
  1315. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1316. "WHERE lp.zone_id = %u\n"
  1317. "GROUP BY s.id",
  1318. zone->GetZoneID());
  1319. while(result && (row = mysql_fetch_row(result))){
  1320. int32 gsXpackFlag = atoul(row[21]);
  1321. int32 gsHolidayFlag = atoul(row[22]);
  1322. if (!CheckExpansionFlags(zone, gsXpackFlag) || !CheckHolidayFlags(zone, gsHolidayFlag))
  1323. continue;
  1324. id = atoul(row[0]);
  1325. if(zone->GetGroundSpawn(id, true))
  1326. continue;
  1327. spawn = new GroundSpawn();
  1328. spawn->SetDatabaseID(id);
  1329. spawn->forceMapCheck = true;
  1330. strcpy(spawn->appearance.name, row[1]);
  1331. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[4]));
  1332. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[5]));
  1333. if(primary_command_list){
  1334. spawn->SetPrimaryCommands(primary_command_list);
  1335. spawn->primary_command_list_id = atoul(row[4]);
  1336. }
  1337. if(secondary_command_list){
  1338. spawn->SetSecondaryCommands(secondary_command_list);
  1339. spawn->secondary_command_list_id = atoul(row[5]);
  1340. }
  1341. spawn->appearance.race = atoi(row[2]);
  1342. spawn->appearance.model_type = atoi(row[3]);
  1343. spawn->appearance.targetable = atoi(row[6]);
  1344. spawn->size = atoi(row[7]);
  1345. spawn->appearance.display_name = atoi(row[8]);
  1346. spawn->appearance.visual_state = atoi(row[9]);
  1347. spawn->appearance.attackable = atoi(row[10]);
  1348. spawn->appearance.show_level = atoi(row[11]);
  1349. spawn->appearance.show_command_icon = atoi(row[12]);
  1350. spawn->appearance.display_hand_icon = atoi(row[13]);
  1351. spawn->faction_id = atoul(row[14]);
  1352. spawn->appearance.pos.collision_radius = atoi(row[15]);
  1353. spawn->SetNumberHarvests(atoi(row[16]));
  1354. spawn->SetAttemptsPerHarvest(atoi(row[17]));
  1355. spawn->SetGroundSpawnEntryID(atoul(row[18]));
  1356. spawn->SetCollectionSkill(row[19]);
  1357. spawn->SetSizeOffset(atoi(row[20]));
  1358. // xpack+holiday value handled at top at position 21+22
  1359. int8 disableSounds = atoul(row[23]);
  1360. spawn->SetSoundsDisabled(disableSounds);
  1361. spawn->SetAAXPRewards(atoul(row[24]));
  1362. zone->AddGroundSpawn(id, spawn);
  1363. total++;
  1364. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn: '%s' (%u)", spawn->appearance.name, id);
  1365. }
  1366. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn(s)", total);
  1367. }
  1368. void WorldDatabase::LoadGroundSpawnItems(ZoneServer* zone) {
  1369. Query query;
  1370. MYSQL_ROW row;
  1371. int32 total = 0;
  1372. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT groundspawn_id, item_id, is_rare, grid_id FROM groundspawn_items;");
  1373. while(result && (row = mysql_fetch_row(result)))
  1374. {
  1375. zone->AddGroundSpawnItem(atoul(row[0]), atoul(row[1]), atoi(row[2]), atoul(row[3]));
  1376. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn Items: ID: %u\n", atoul(row[0]));
  1377. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---item: %ul, rare: %i, grid: %ul", atoul(row[1]), atoi(row[2]), atoul(row[3]));
  1378. total++;
  1379. }
  1380. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn Item%s.", total, total == 1 ? "" : "s");
  1381. }
  1382. void WorldDatabase::LoadGroundSpawnEntries(ZoneServer* zone) {
  1383. Query query;
  1384. MYSQL_ROW row;
  1385. int32 total = 0;
  1386. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT groundspawn_id, min_skill_level, min_adventure_level, bonus_table, harvest1, harvest3, harvest5, harvest_imbue, harvest_rare, harvest10, harvest_coin FROM groundspawns WHERE enabled = 1;");
  1387. while(result && (row = mysql_fetch_row(result)))
  1388. {
  1389. // this is getting ridonkulous...
  1390. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn ID: %u\n" \
  1391. "---min_skill_level: %i, min_adventure_level: %i, bonus_table: %i\n" \
  1392. "---harvest1: %.2f, harvest3: %.2f, harvest5: %.2f\n" \
  1393. "---harvest_imbue: %.2f, harvest_rare: %.2f, harvest10: %.2f\n" \
  1394. "---harvest_coin: %u", atoul(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atof(row[9]), atoul(row[10]));
  1395. zone->AddGroundSpawnEntry(atoul(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atof(row[9]), atoul(row[10]));
  1396. total++;
  1397. }
  1398. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn Entr%s.", total, total == 1 ? "y" : "ies");
  1399. LoadGroundSpawnItems(zone);
  1400. }
  1401. bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* client)
  1402. {
  1403. DatabaseResult result;
  1404. if( database_new.Select(&result, "SELECT * FROM character_details WHERE char_id = %i LIMIT 0, 1", id) )
  1405. {
  1406. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading character_details for '%s' (char_id: %u)", client->GetPlayer()->GetName(), id);
  1407. while( result.Next() )
  1408. {
  1409. InfoStruct* info = client->GetPlayer()->GetInfoStruct();
  1410. client->GetPlayer()->SetHP(result.GetSInt32Str("hp"));
  1411. client->GetPlayer()->SetPower(result.GetSInt32Str("power"));
  1412. info->set_max_concentration(result.GetInt8Str("max_concentration"));
  1413. if (info->get_max_concentration() == 0)
  1414. info->set_max_concentration(5);
  1415. info->set_attack_base(result.GetInt16Str("attack"));
  1416. info->set_mitigation_base(result.GetInt16Str("mitigation"));
  1417. info->set_avoidance_base(result.GetInt16Str("avoidance"));
  1418. info->set_parry_base(result.GetInt16Str("parry"));
  1419. info->set_deflection_base(result.GetInt16Str("deflection"));
  1420. info->set_block_base(result.GetInt16Str("block"));
  1421. info->set_str_base(result.GetInt16Str("str"));
  1422. info->set_sta_base(result.GetInt16Str("sta"));
  1423. info->set_agi_base(result.GetInt16Str("agi"));
  1424. info->set_wis_base(result.GetInt16Str("wis"));
  1425. info->set_intel_base(result.GetInt16Str("intel"));
  1426. // old resist types
  1427. info->set_heat_base(result.GetInt16Str("heat"));
  1428. info->set_cold_base(result.GetInt16Str("cold"));
  1429. info->set_magic_base(result.GetInt16Str("magic"));
  1430. info->set_mental_base(result.GetInt16Str("mental"));
  1431. info->set_divine_base(result.GetInt16Str("divine"));
  1432. info->set_disease_base(result.GetInt16Str("disease"));
  1433. info->set_poison_base(result.GetInt16Str("poison"));
  1434. //
  1435. info->set_coin_copper(result.GetInt32Str("coin_copper"));
  1436. info->set_coin_silver(result.GetInt32Str("coin_silver"));
  1437. info->set_coin_gold(result.GetInt32Str("coin_gold"));
  1438. info->set_coin_plat(result.GetInt32Str("coin_plat"));
  1439. info->set_pet_name(result.GetStringStr("pet_name") ? std::string(result.GetStringStr("pet_name")) : std::string(""));
  1440. const char* bio = result.GetStringStr("biography");
  1441. if(bio && strlen(bio) > 0)
  1442. info->set_biography(std::string(bio));
  1443. info->set_status_points(result.GetInt32Str("status_points"));
  1444. client->GetPlayer()->GetPlayerInfo()->SetBindZone(result.GetInt32Str("bind_zone_id"));
  1445. client->GetPlayer()->GetPlayerInfo()->SetBindX(result.GetFloatStr("bind_x"));
  1446. client->GetPlayer()->GetPlayerInfo()->SetBindY(result.GetFloatStr("bind_y"));
  1447. client->GetPlayer()->GetPlayerInfo()->SetBindZ(result.GetFloatStr("bind_z"));
  1448. client->GetPlayer()->GetPlayerInfo()->SetBindHeading(result.GetFloatStr("bind_heading"));
  1449. client->GetPlayer()->GetPlayerInfo()->SetHouseZone(result.GetInt32Str("house_zone_id"));
  1450. client->GetPlayer()->SetTotalHP(result.GetSInt32Str("max_hp"));
  1451. client->GetPlayer()->SetTotalPower(result.GetSInt32Str("max_power"));
  1452. client->GetPlayer()->SetAssignedAA(result.GetInt16Str("assigned_aa"));
  1453. client->GetPlayer()->SetUnassignedAA(result.GetInt16Str("unassigned_aa"));
  1454. client->GetPlayer()->SetTradeskillAA(result.GetInt16Str("tradeskill_aa"));
  1455. client->GetPlayer()->SetUnassignedTradeskillAA(result.GetInt16Str("unassigned_tradeskill_aa"));
  1456. client->GetPlayer()->SetPrestigeAA(result.GetInt16Str("prestige_aa"));
  1457. client->GetPlayer()->SetUnassignedPrestigeAA(result.GetInt16Str("unassigned_prestige_aa"));
  1458. client->GetPlayer()->SetTradeskillPrestigeAA(result.GetInt16Str("tradeskill_prestige_aa"));
  1459. client->GetPlayer()->SetUnassignedTradeskillPrestigeAA(result.GetInt16Str("unassigned_tradeskill_prestige_aa"));
  1460. info->set_xp(result.GetInt32Str("xp"));
  1461. info->set_xp_needed(result.GetInt32Str("xp_needed"));
  1462. if(info->get_xp_needed()== 0)
  1463. client->GetPlayer()->SetNeededXP();
  1464. info->set_xp_debt(result.GetFloatStr("xp_debt"));
  1465. info->set_xp_vitality(result.GetFloatStr("xp_vitality"));
  1466. info->set_ts_xp(result.GetInt32Str("tradeskill_xp"));
  1467. info->set_ts_xp_needed(result.GetInt32Str("tradeskill_xp_needed"));
  1468. if (info->get_ts_xp_needed() == 0)
  1469. client->GetPlayer()->SetNeededTSXP();
  1470. info->set_tradeskill_xp_vitality(result.GetFloatStr("tradeskill_xp_vitality"));
  1471. client->GetPlayer()->SetTotalHPBase(client->GetPlayer()->GetTotalHP());
  1472. client->GetPlayer()->SetTotalPowerBase(client->GetPlayer()->GetTotalPower());
  1473. info->set_bank_coin_copper(result.GetInt32Str("bank_copper"));
  1474. info->set_bank_coin_silver(result.GetInt32Str("bank_silver"));
  1475. info->set_bank_coin_gold(result.GetInt32Str("bank_gold"));
  1476. info->set_bank_coin_plat(result.GetInt32Str("bank_plat"));
  1477. client->GetPlayer()->SetCombatVoice(result.GetInt16Str("combat_voice"));
  1478. client->GetPlayer()->SetEmoteVoice(result.GetInt16Str("emote_voice"));
  1479. client->GetPlayer()->SetBiography(result.GetStringStr("biography"));
  1480. client->GetPlayer()->GetInfoStruct()->set_flags(result.GetInt32Str("flags"));
  1481. client->GetPlayer()->GetInfoStruct()->set_flags2(result.GetInt32Str("flags2"));
  1482. client->GetPlayer()->SetLastName(result.GetStringStr("last_name"));
  1483. // new resist types
  1484. info->set_elemental_base(result.GetInt16Str("elemental"));
  1485. info->set_arcane_base(result.GetInt16Str("arcane"));
  1486. info->set_noxious_base(result.GetInt16Str("noxious"));
  1487. // new savagery and dissonance
  1488. client->GetPlayer()->SetSavagery(result.GetSInt16Str("savagery"));
  1489. client->GetPlayer()->SetDissonance(result.GetSInt16Str("dissonance"));
  1490. client->GetPlayer()->SetTotalSavageryBase(client->GetPlayer()->GetTotalSavagery());
  1491. client->GetPlayer()->SetTotalDissonanceBase(client->GetPlayer()->GetTotalDissonance());
  1492. }
  1493. return true;
  1494. }
  1495. else
  1496. {
  1497. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character_details for '%s' (char_id: %u)", client->GetPlayer()->GetName(), id);
  1498. return false;
  1499. }
  1500. }
  1501. bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client* client){
  1502. Query query, query4;
  1503. MYSQL_ROW row, row4;
  1504. int32 id = 0;
  1505. query.escaped_name = getEscapeString(ch_name);
  1506. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id);
  1507. // no character found
  1508. if ( result == NULL ) {
  1509. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
  1510. return false;
  1511. }
  1512. if (mysql_num_rows(result) == 1){
  1513. row = mysql_fetch_row(result);
  1514. id = strtoul(row[0], NULL, 0);
  1515. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading character for '%s' (char_id: %u)", ch_name, id);
  1516. client->SetCharacterID(id);
  1517. client->GetPlayer()->SetCharacterID(id);
  1518. client->SetAccountID(account_id);
  1519. client->GetPlayer()->SetName(ch_name);
  1520. client->GetPlayer()->SetX(atof(row[2]));
  1521. client->GetPlayer()->SetY(atof(row[3]));
  1522. client->GetPlayer()->SetZ(atof(row[4]));
  1523. client->GetPlayer()->SetHeading(atof(row[5]));
  1524. client->SetAdminStatus(atoi(row[6]));
  1525. client->GetPlayer()->SetRace(atoi(row[7]));
  1526. client->GetPlayer()->SetModelType(atoi(row[8]));
  1527. client->GetPlayer()->SetAdventureClass(atoi(row[9]));
  1528. client->GetPlayer()->SetDeity(atoi(row[10]));
  1529. client->GetPlayer()->SetLevel(atoi(row[11]));
  1530. client->GetPlayer()->SetGender(atoi(row[12]));
  1531. client->GetPlayer()->SetTradeskillClass(atoi(row[13]));
  1532. client->GetPlayer()->SetTSLevel(atoi(row[14]));
  1533. LogWrite(MISC__TODO, 1, "TODO", "Fix client->GetPlayer()->SetArtLevel(atoi(row[14]));\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
  1534. client->GetPlayer()->features.wing_type = atoi(row[15]);
  1535. client->GetPlayer()->features.hair_type = atoi(row[16]);
  1536. client->GetPlayer()->features.chest_type = atoi(row[17]);
  1537. client->GetPlayer()->features.legs_type = atoi(row[18]);
  1538. LogWrite(MISC__TODO, 1, "TODO", "Fix SOGA appearances here\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
  1539. client->GetPlayer()->features.wing_type = atoi(row[19]);
  1540. client->GetPlayer()->features.soga_hair_type = atoi(row[20]);
  1541. client->GetPlayer()->features.soga_chest_type = atoi(row[21]);
  1542. client->GetPlayer()->features.soga_legs_type = atoi(row[22]);
  1543. client->SetNameCRC(atoul(row[23]));
  1544. client->GetPlayer()->features.hair_face_type = atoi(row[24]);
  1545. client->GetPlayer()->features.soga_hair_face_type = atoi(row[25]);
  1546. int32 instanceid = atoi(row[26]);
  1547. int32 groupid = atoi(row[27]);
  1548. client->SetRejoinGroupID(groupid);
  1549. int32 zoneid = atoul(row[1]);
  1550. /*
  1551. JA Notes on SOGA: I think there are many more settings to add than were commented out here,
  1552. because I can load a SOGA model player, but some features are missing (Barbarian WOAD, Skin tons, etc)
  1553. SOGA chars looked ok in LoginServer screen tho... odd.
  1554. */
  1555. // load character instances here
  1556. if ( LoadCharacterInstances(client) )
  1557. client->UpdateCharacterInstances();
  1558. if ( instanceid > 0 )
  1559. client->SetCurrentZoneByInstanceID(instanceid, zoneid);
  1560. else
  1561. client->SetCurrentZone(zoneid);
  1562. int32 lastsavedtime = atoi(row[28]);
  1563. client->SetLastSavedTimeStamp(lastsavedtime);
  1564. if (row[29])
  1565. client->GetPlayer()->GetPlayerInfo()->SetAccountAge(atoi(row[29]));
  1566. LoadCharacterFriendsIgnoreList(client->GetPlayer());
  1567. MYSQL_RES* result4 = query4.RunQuery2(Q_SELECT, "SELECT `guild_id` FROM `guild_members` WHERE `char_id`=%u", id);
  1568. if (result4 && (row4 = mysql_fetch_row(result4))) {
  1569. Guild* guild = guild_list.GetGuild(atoul(row4[0]));
  1570. if (guild) {
  1571. client->GetPlayer()->SetGuild(guild);
  1572. string subtitle;
  1573. subtitle.append("<").append(guild->GetName()).append(">");
  1574. client->GetPlayer()->SetSubTitle(subtitle.c_str());
  1575. }
  1576. }
  1577. LoadCharacterHistory(id, client->GetPlayer());
  1578. LoadCharacterLUAHistory(id, client->GetPlayer());
  1579. LoadPlayerStatistics(client->GetPlayer(), id);
  1580. LoadPlayerCollections(client->GetPlayer());
  1581. LoadPlayerRecipes(client->GetPlayer());
  1582. //LoadPlayerAchievements(client->GetPlayer());
  1583. LoadPlayerAchievementsUpdates(client->GetPlayer());
  1584. LoadAppearances(client->GetCurrentZone(), client);
  1585. return LoadCharacterStats(id, account_id, client);
  1586. }
  1587. // should not be here...
  1588. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
  1589. return false;
  1590. }
  1591. bool WorldDatabase::InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id){
  1592. Query query1;
  1593. Query query2;
  1594. Query query3;
  1595. Query query4;
  1596. Query query5;
  1597. /* Blank record */
  1598. query1.RunQuery2(Q_INSERT, "INSERT INTO `character_details` (`char_id`) VALUES (%u)", character_id);
  1599. /* Using the class id and race id */
  1600. query2.RunQuery2(Q_UPDATE, "UPDATE character_details c, starting_details s SET c.max_hp = s.max_hp, c.hp = s.max_hp, c.max_power = s.max_power, c.power = s.max_power, c.str = s.str, c.sta = s.sta, c.agi = s.agi, c.wis = s.wis, c.intel = s.intel,c.heat = s.heat, c.cold = s.cold, c.magic = s.magic, c.mental = s.mental, c.divine = s.divine, c.disease = s.disease, c.poison = s.poison, c.coin_copper = s.coin_copper, c.coin_silver = s.coin_silver, c.coin_gold = s.coin_gold, c.coin_plat = s.coin_plat, c.status_points = s.status_points WHERE s.race_id = %d AND class_id = %d AND char_id = %u", race_id, class_id, character_id);
  1601. if (query2.GetAffectedRows() > 0)
  1602. return true;
  1603. /* Using the class id and race id = 255 */
  1604. query3.RunQuery2(Q_UPDATE, "UPDATE character_details c, starting_details s SET c.max_hp = s.max_hp, c.hp = s.max_hp, c.max_power = s.max_power, c.power = s.max_power, c.str = s.str, c.sta = s.sta, c.agi = s.agi, c.wis = s.wis, c.intel = s.intel,c.heat = s.heat, c.cold = s.cold, c.magic = s.magic, c.mental = s.mental, c.divine = s.divine, c.disease = s.disease, c.poison = s.poison, c.coin_copper = s.coin_copper, c.coin_silver = s.coin_silver, c.coin_gold = s.coin_gold, c.coin_plat = s.coin_plat, c.status_points = s.status_points WHERE s.race_id = 255 AND class_id = %d AND char_id = %u", class_id, character_id);
  1605. if (query3.GetAffectedRows() > 0)
  1606. return true;
  1607. /* Using class id = 255 and the race id */
  1608. query4.RunQuery2(Q_UPDATE, "UPDATE character_details c, starting_details s SET c.max_hp = s.max_hp, c.hp = s.max_hp, c.max_power = s.max_power, c.power = s.max_power, c.str = s.str, c.sta = s.sta, c.agi = s.agi, c.wis = s.wis, c.intel = s.intel,c.heat = s.heat, c.cold = s.cold, c.magic = s.magic, c.mental = s.mental, c.divine = s.divine, c.disease = s.disease, c.poison = s.poison, c.coin_copper = s.coin_copper, c.coin_silver = s.coin_silver, c.coin_gold = s.coin_gold, c.coin_plat = s.coin_plat, c.status_points = s.status_points WHERE s.race_id = %d AND class_id = 255 AND char_id = %u", race_id, character_id);
  1609. if (query4.GetAffectedRows() > 0)
  1610. return true;
  1611. /* Using class id = 255 and race id = 255 */
  1612. query5.RunQuery2(Q_UPDATE, "UPDATE character_details c, starting_details s SET c.max_hp = s.max_hp, c.hp = s.max_hp, c.max_power = s.max_power, c.power = s.max_power, c.str = s.str, c.sta = s.sta, c.agi = s.agi, c.wis = s.wis, c.intel = s.intel,c.heat = s.heat, c.cold = s.cold, c.magic = s.magic, c.mental = s.mental, c.divine = s.divine, c.disease = s.disease, c.poison = s.poison, c.coin_copper = s.coin_copper, c.coin_silver = s.coin_silver, c.coin_gold = s.coin_gold, c.coin_plat = s.coin_plat, c.status_points = s.status_points WHERE s.race_id = 255 AND class_id = 255 AND char_id = %u", character_id);
  1613. if (query5.GetAffectedRows() > 0)
  1614. return true;
  1615. return false;
  1616. }
  1617. int32 WorldDatabase::GetCharacterTimeStamp(int32 character_id, int32 account_id,bool* char_exists){
  1618. Query query;
  1619. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT unix_timestamp FROM characters where id=%i and account_id=%i",character_id,account_id);
  1620. if(result && mysql_num_rows(result) > 0) {
  1621. MYSQL_ROW row;
  1622. row = mysql_fetch_row(result);
  1623. *char_exists = true;
  1624. return atoi(row[0]); // Return timestamp
  1625. }
  1626. else
  1627. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError());
  1628. *char_exists = false;
  1629. return 0;
  1630. }
  1631. int32 WorldDatabase::GetCharacterTimeStamp(int32 character_id) {
  1632. Query query;
  1633. MYSQL_ROW row;
  1634. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT unix_timestamp FROM characters WHERE id=%u", character_id);
  1635. int32 ret = 0;
  1636. if (result && (row = mysql_fetch_row(result)))
  1637. ret = atoul(row[0]);
  1638. return ret;
  1639. }
  1640. bool WorldDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update){
  1641. Query query;
  1642. string update_charts = string("update characters set unix_timestamp=%i where id=%i and account_id=%i");
  1643. query.RunQuery2(Q_UPDATE, update_charts.c_str(),timestamp_update,character_id,account_id);
  1644. if(!query.GetAffectedRows())
  1645. {
  1646. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError());
  1647. return false;
  1648. }
  1649. return true;
  1650. }
  1651. bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char* propValue) {
  1652. Query query;
  1653. string update_status = string("update character_properties set propvalue='%s' where charid=%i and propname='%s'");
  1654. query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, client->GetCharacterID(), propName);
  1655. if (!query.GetAffectedRows())
  1656. {
  1657. query.RunQuery2(Q_UPDATE, "insert into character_properties (charid, propname, propvalue) values(%i, '%s', '%s')", client->GetCharacterID(), propName, propValue);
  1658. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  1659. LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError());
  1660. return false;
  1661. }
  1662. }
  1663. return true;
  1664. }
  1665. bool WorldDatabase::loadCharacterProperties(Client* client) {
  1666. Query query;
  1667. MYSQL_ROW row;
  1668. int32 id = 0;
  1669. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM character_properties where charid = %i", client->GetCharacterID());
  1670. // no character found
  1671. if (result == NULL) {
  1672. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName());
  1673. return false;
  1674. }
  1675. while (result && (row = mysql_fetch_row(result))) {
  1676. char* prop_name = row[0];
  1677. char* prop_value = row[1];
  1678. if (!prop_name || !prop_value)
  1679. continue;
  1680. if (!stricmp(prop_name, CHAR_PROPERTY_SPEED))
  1681. {
  1682. float new_speed = atof(prop_value);
  1683. client->GetPlayer()->SetSpeed(new_speed,true);
  1684. client->GetPlayer()->SetCharSheetChanged(true);
  1685. }
  1686. else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE))
  1687. {
  1688. int8 flymode = atoi(prop_value);
  1689. if (flymode) // avoid fly mode notification unless enabled
  1690. ClientPacketFunctions::SendFlyMode(client, flymode, false);
  1691. }
  1692. else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL))
  1693. {
  1694. int8 invul = atoi(prop_value);
  1695. client->GetPlayer()->SetInvulnerable(invul == 1);
  1696. if (client->GetPlayer()->GetInvulnerable())
  1697. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!");
  1698. }
  1699. else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION))
  1700. {
  1701. int8 val = atoi(prop_value);
  1702. client->GetPlayer()->SetGMVision(val == 1);
  1703. client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false);
  1704. if (val)
  1705. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!");
  1706. }
  1707. else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG))
  1708. {
  1709. int8 val = atoi(prop_value);
  1710. client->SetRegionDebug(val == 1);
  1711. if (val)
  1712. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!");
  1713. }
  1714. else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG))
  1715. {
  1716. int8 val = atoi(prop_value);
  1717. if (val)
  1718. {
  1719. client->SetLuaDebugClient(true);
  1720. if (lua_interface)
  1721. lua_interface->UpdateDebugClients(client);
  1722. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages.");
  1723. }
  1724. }
  1725. }
  1726. return true;
  1727. }
  1728. //gets the name FROM the db with the right letters in caps
  1729. string WorldDatabase::GetPlayerName(char* name){
  1730. Query query;
  1731. string ret = "";
  1732. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM characters where name='%s'", getSafeEscapeString(name).c_str());
  1733. if(result && mysql_num_rows(result) > 0) {
  1734. MYSQL_ROW row;
  1735. row = mysql_fetch_row(result);
  1736. if(row[0])
  1737. ret = string(row[0]);
  1738. }
  1739. return ret;
  1740. }
  1741. int32 WorldDatabase::GetCharacterID(const char* name) {
  1742. int32 id = 0;
  1743. Query query;
  1744. if (name) {
  1745. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM `characters` WHERE `name`='%s'", name);
  1746. if (result && mysql_num_rows(result) > 0) {
  1747. MYSQL_ROW row;
  1748. row = mysql_fetch_row(result);
  1749. id = atoul(row[0]);
  1750. }
  1751. }
  1752. return id;
  1753. }
  1754. int32 WorldDatabase::GetCharacterCurrentZoneID(int32 character_id) {
  1755. int32 id = 0;
  1756. Query query;
  1757. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `current_zone_id` FROM `characters` WHERE `id`=%u", character_id);
  1758. if (result && mysql_num_rows(result) > 0) {
  1759. MYSQL_ROW row;
  1760. row = mysql_fetch_row(result);
  1761. id = atoul(row[0]);
  1762. }
  1763. return id;
  1764. }
  1765. int32 WorldDatabase::GetCharacterAccountID(int32 character_id) {
  1766. int32 id = 0;
  1767. Query query;
  1768. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `account_id` FROM `characters` WHERE `id`=%u", character_id);
  1769. if (result && mysql_num_rows(result) > 0) {
  1770. MYSQL_ROW row;
  1771. row = mysql_fetch_row(result);
  1772. id = atoul(row[0]);
  1773. }
  1774. return id;
  1775. }
  1776. sint16 WorldDatabase::GetHighestCharacterAdminStatus(int32 account_id){
  1777. Query query;
  1778. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(admin_status) FROM characters where account_id=%i ",account_id);
  1779. if(result && mysql_num_rows(result) > 0) {
  1780. MYSQL_ROW row;
  1781. row = mysql_fetch_row(result);
  1782. if ( row[0] != NULL )
  1783. return atoi(row[0]); // Return characters status
  1784. else
  1785. return 0;
  1786. }
  1787. return 0;
  1788. }
  1789. sint16 WorldDatabase::GetLowestCharacterAdminStatus(int32 account_id){
  1790. Query query;
  1791. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT min(admin_status) FROM characters where account_id=%i ",account_id);
  1792. if(result && mysql_num_rows(result) > 0) {
  1793. MYSQL_ROW row;
  1794. row = mysql_fetch_row(result);
  1795. if ( row[0] != NULL )
  1796. return atoi(row[0]); // Return characters status
  1797. else
  1798. return 0;
  1799. }
  1800. return 0;
  1801. }
  1802. sint16 WorldDatabase::GetCharacterAdminStatus(char* character_name){
  1803. Query query;
  1804. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT admin_status FROM characters where name='%s'", getSafeEscapeString(character_name).c_str());
  1805. if(result && mysql_num_rows(result) > 0) {
  1806. MYSQL_ROW row;
  1807. row = mysql_fetch_row(result);
  1808. return atoi(row[0]); // Return characters level
  1809. }
  1810. else
  1811. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1812. return -10;
  1813. }
  1814. sint16 WorldDatabase::GetCharacterAdminStatus(int32 account_id , int32 char_id){
  1815. Query query;
  1816. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT admin_status FROM characters where account_id=%i and id=%i",account_id,char_id);
  1817. if(result && mysql_num_rows(result) > 0) {
  1818. MYSQL_ROW row;
  1819. row = mysql_fetch_row(result);
  1820. return atoi(row[0]); // Return characters status
  1821. }
  1822. else{
  1823. Query query2;
  1824. result = query2.RunQuery2(Q_SELECT, "SELECT count(id) FROM characters where account_id=%i and id=%i",account_id,char_id);
  1825. if(result && mysql_num_rows(result) > 0) {
  1826. MYSQL_ROW row;
  1827. row = mysql_fetch_row(result);
  1828. if(atoi(row[0]) == 0) //old character, needs to be deleted FROM login server
  1829. return -10;
  1830. return -8;
  1831. }
  1832. else
  1833. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1834. }
  1835. return PLAY_ERROR_PROBLEM;
  1836. }
  1837. bool WorldDatabase::UpdateAdminStatus(char* character_name, sint16 flag){
  1838. Query query;
  1839. string update_status = string("update characters set admin_status=%i where name='%s'");
  1840. query.RunQuery2(Q_UPDATE, update_status.c_str(),flag,character_name);
  1841. if(!query.GetAffectedRows())
  1842. {
  1843. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1844. return false;
  1845. }
  1846. return true;
  1847. }
  1848. void WorldDatabase::SaveCharacterFloats(int32 char_id, const char* type, float float1, float float2, float float3){
  1849. Query query;
  1850. string create_char = string("insert into char_colors (char_id, type, red, green, blue, signed_value) values(%i,'%s',%i,%i,%i, 1)");
  1851. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, (sint8)(float1*100), (sint8)(float2*100), (sint8)(float3*100));
  1852. if(query.GetError() && strlen(query.GetError()) > 0){
  1853. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterFloats query '%s': %s", query.GetQuery(), query.GetError());
  1854. }
  1855. }
  1856. void WorldDatabase::SaveCharacterColors(int32 char_id, const char* type, EQ2_Color color){
  1857. Query query;
  1858. string create_char = string("insert into char_colors (char_id, type, red, green, blue) values(%i,'%s',%i,%i,%i)");
  1859. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, color.red, color.green, color.blue);
  1860. if(query.GetError() && strlen(query.GetError()) > 0){
  1861. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterColors query '%s': %s", query.GetQuery(), query.GetError());
  1862. }
  1863. }
  1864. void WorldDatabase::SaveNPCAppearanceEquipment(int32 spawn_id, int8 slot_id, int16 type, int8 red, int8 green, int8 blue, int8 hred, int8 hgreen, int8 hblue){
  1865. Query query;
  1866. string appearance = string("INSERT INTO npc_appearance_equip (spawn_id, slot_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue) values (%i, %i, %i, %i, %i, %i, %i, %i, %i) ON DUPLICATE KEY UPDATE equip_type=%i, red=%i, green=%i, blue=%i, highlight_red=%i, highlight_green=%i, highlight_blue=%i");
  1867. query.RunQuery2(Q_INSERT, appearance.c_str(), spawn_id, slot_id, type, red, green, blue, hred, hgreen, hblue, type, red, green, blue, hred, hgreen, hblue);
  1868. if(query.GetError() && strlen(query.GetError()) > 0){
  1869. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveNPCAppearanceEquipment query '%s': %s", query.GetQuery(), query.GetError());
  1870. }
  1871. }
  1872. int32 WorldDatabase::LoadNPCAppearanceEquipmentData(ZoneServer* zone){
  1873. Query query;
  1874. MYSQL_ROW row;
  1875. int32 spawn_id = 0, new_spawn_id = 0, count = 0;
  1876. NPC* npc = 0;
  1877. int8 slot = 0;
  1878. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spawn_id, slot_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue FROM npc_appearance_equip ORDER BY spawn_id");
  1879. while(result && (row = mysql_fetch_row(result))){
  1880. new_spawn_id = atoul(row[0]);
  1881. if(new_spawn_id != spawn_id){
  1882. npc = zone->GetNPC(new_spawn_id, true);
  1883. if(!npc)
  1884. continue;
  1885. if(spawn_id > 0)
  1886. count++;
  1887. spawn_id = new_spawn_id;
  1888. }
  1889. slot = atoul(row[1]);
  1890. if(slot < NUM_SLOTS){
  1891. npc->SetEquipment(slot, atoul(row[2]), atoul(row[3]), atoul(row[4]), atoul(row[5]), atoul(row[6]), atoul(row[7]), atoul(row[8]));
  1892. }
  1893. }
  1894. if(query.GetError() && strlen(query.GetError()) > 0)
  1895. LogWrite(WORLD__ERROR, 0, "World", "Error in LoadNPCAppearanceEquipmentData query '%s': %s", query.GetQuery(), query.GetError());
  1896. return count;
  1897. }
  1898. int16 WorldDatabase::GetAppearanceID(string name){
  1899. int32 id = 0;
  1900. Query query;
  1901. MYSQL_ROW row;
  1902. query.escaped_name = getEscapeString(name.c_str());
  1903. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT appearance_id FROM appearances where name='%s'", query.escaped_name);
  1904. if(result && mysql_num_rows(result) == 1){
  1905. row = mysql_fetch_row(result);
  1906. id = atoi(row[0]);
  1907. }
  1908. return id;
  1909. }
  1910. vector<int16>* WorldDatabase::GetAppearanceIDsLikeName(string name, bool filtered) {
  1911. vector<int16>* ids = 0;
  1912. Query query;
  1913. MYSQL_ROW row;
  1914. query.escaped_name = getEscapeString(name.c_str());
  1915. MYSQL_RES* result;
  1916. if (filtered)
  1917. result = query.RunQuery2(Q_SELECT, "SELECT `appearance_id` FROM `appearances` WHERE `name` RLIKE '%s' AND `name` NOT RLIKE 'ghost' AND `name` NOT RLIKE 'headless' AND `name` NOT RLIKE 'elemental' AND `name` NOT RLIKE 'test' AND `name` NOT RLIKE 'zombie' AND `name` NOT RLIKE 'vampire'", query.escaped_name);
  1918. else
  1919. result = query.RunQuery2(Q_SELECT, "SELECT `appearance_id` FROM `appearances` WHERE `name` RLIKE '%s' AND `name` NOT RLIKE 'ghost' AND `name`", query.escaped_name);
  1920. while (result && (row = mysql_fetch_row(result))) {
  1921. if (!ids)
  1922. ids = new vector<int16>;
  1923. ids->push_back(atoi(row[0]));
  1924. }
  1925. return ids;
  1926. }
  1927. string WorldDatabase::GetAppearanceName(int16 appearance_id) {
  1928. Query query;
  1929. MYSQL_ROW row;
  1930. string name;
  1931. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name` FROM `appearances` WHERE `appearance_id`=%u", appearance_id);
  1932. if (result && (row = mysql_fetch_row(result)))
  1933. name = string(row[0]);
  1934. return name;
  1935. }
  1936. void WorldDatabase::UpdateRandomize(int32 spawn_id, sint32 value) {
  1937. Query query;
  1938. query.RunQuery2(Q_UPDATE, "UPDATE `spawn_npcs` SET `randomize`=`randomize` + %i WHERE `spawn_id`=%u", value, spawn_id);
  1939. }
  1940. int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){
  1941. Query query;
  1942. int8 race_id = create->getType_int8_ByName("race");
  1943. int8 orig_class_id = create->getType_int8_ByName("class");//Normal server
  1944. int8 class_id = orig_class_id;
  1945. if ( create->GetVersion() <= 546 ) {
  1946. class_id = 0; //Classic Server Only
  1947. }
  1948. int8 gender_id = create->getType_int8_ByName("gender");
  1949. sint16 auto_admin_status = 0;
  1950. // fetch rules related to setting auto-admin status for server
  1951. bool auto_admin_players = rule_manager.GetGlobalRule(R_World, AutoAdminPlayers)->GetBool();
  1952. bool auto_admin_gm = rule_manager.GetGlobalRule(R_World, AutoAdminGMs)->GetBool();
  1953. /*
  1954. The way I think this is supposed to work :) is if any of your chars are already GM, and AutoAdminGMs rule is true,
  1955. set the new character's admin_status to your highest status for any character active on your loginID.
  1956. - If status > 0, new character > 0
  1957. - If status = 0, new character = 0
  1958. - If status < 0, new character < 0, too... even if auto_admin_gm is true
  1959. If we're not a GM (status = 0) but AutoAdminPlayers rule is true,
  1960. set the new character's admin_status to the default set in AutoAdminStatusValue rule.
  1961. Else, if both rules are False, set everyone to 0 like normal.
  1962. */
  1963. auto_admin_status = GetHighestCharacterAdminStatus(loginID);
  1964. if( auto_admin_status > 0 && auto_admin_gm ) {
  1965. LogWrite(WORLD__WARNING, 0, "World", "New character '%s' granted GM status (%i) from accountID: %i", create->getType_EQ2_16BitString_ByName("name").data.c_str(), auto_admin_status, loginID);
  1966. }
  1967. else if( auto_admin_players )
  1968. {
  1969. auto_admin_status = rule_manager.GetGlobalRule(R_World, AutoAdminStatusValue)->GetSInt16();
  1970. LogWrite(WORLD__DEBUG, 0, "World", "New character '%s' granted AutoAdminPlayer status: %i", create->getType_EQ2_16BitString_ByName("name").data.c_str(), auto_admin_status);
  1971. }
  1972. else {
  1973. auto_admin_status = 0;
  1974. }
  1975. string create_char = string("Insert into characters (account_id, server_id, name, race, class, gender, deity, body_size, body_age, soga_wing_type, soga_chest_type, soga_legs_type, soga_hair_type, soga_model_type, legs_type, chest_type, wing_type, hair_type, model_type, facial_hair_type, soga_facial_hair_type, created_date, last_saved, admin_status) values(%i, %i, '%s', %i, %i, %i, %i, %f, %f, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, now(), unix_timestamp(), %i)");
  1976. query.RunQuery2(Q_INSERT, create_char.c_str(),
  1977. loginID,
  1978. create->getType_int32_ByName("server_id"),
  1979. create->getType_EQ2_16BitString_ByName("name").data.c_str(),
  1980. race_id,
  1981. class_id,
  1982. gender_id,
  1983. create->getType_int8_ByName("deity"),
  1984. create->getType_float_ByName("body_size"),
  1985. create->getType_float_ByName("body_age"),
  1986. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_wing_file").data),
  1987. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_chest_file").data),
  1988. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_legs_file").data),
  1989. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_hair_file").data),
  1990. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_race_file").data),
  1991. GetAppearanceID(create->getType_EQ2_16BitString_ByName("legs_file").data),
  1992. GetAppearanceID(create->getType_EQ2_16BitString_ByName("chest_file").data),
  1993. GetAppearanceID(create->getType_EQ2_16BitString_ByName("wing_file").data),
  1994. GetAppearanceID(create->getType_EQ2_16BitString_ByName("hair_file").data),
  1995. GetAppearanceID(create->getType_EQ2_16BitString_ByName("race_file").data),
  1996. GetAppearanceID(create->getType_EQ2_16BitString_ByName("face_file").data),
  1997. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_face_file").data),
  1998. auto_admin_status);
  1999. if(query.GetError() && strlen(query.GetError()) > 0)
  2000. {
  2001. LogWrite(PLAYER__ERROR, 0, "Player", "Error in SaveCharacter query '%s': %s", query.GetQuery(), query.GetError());
  2002. return 0;
  2003. }
  2004. int32 last_insert_id = query.GetLastInsertedID();
  2005. int32 char_id = last_insert_id;
  2006. UpdateStartingFactions(char_id, create->getType_int8_ByName("starting_zone"));
  2007. UpdateStartingZone(char_id, class_id, race_id, create);
  2008. UpdateStartingItems(char_id, class_id, race_id);
  2009. UpdateStartingSkills(char_id, class_id, race_id);
  2010. UpdateStartingSpells(char_id, class_id, race_id);
  2011. UpdateStartingSkillbar(char_id, class_id, race_id);
  2012. UpdateStartingTitles(char_id, class_id, race_id, gender_id);
  2013. InsertCharacterStats(char_id, class_id, race_id);
  2014. AddNewPlayerToServerGuild(loginID, char_id);
  2015. SaveCharacterColors(char_id,"skin_color", create->getType_EQ2_Color_ByName("skin_color"));
  2016. SaveCharacterColors(char_id,"model_color", create->getType_EQ2_Color_ByName("model_color"));
  2017. SaveCharacterColors(char_id,"eye_color", create->getType_EQ2_Color_ByName("eye_color"));
  2018. SaveCharacterColors(char_id,"hair_color1", create->getType_EQ2_Color_ByName("hair_color1"));
  2019. SaveCharacterColors(char_id,"hair_color2", create->getType_EQ2_Color_ByName("hair_color2"));
  2020. SaveCharacterColors(char_id,"hair_highlight", create->getType_EQ2_Color_ByName("hair_highlight"));
  2021. SaveCharacterColors(char_id,"hair_type_color", create->getType_EQ2_Color_ByName("hair_type_color"));
  2022. SaveCharacterColors(char_id,"hair_type_highlight_color", create->getType_EQ2_Color_ByName("hair_type_highlight_color"));
  2023. SaveCharacterColors(char_id,"hair_face_color", create->getType_EQ2_Color_ByName("hair_face_color"));
  2024. SaveCharacterColors(char_id,"hair_face_highlight_color", create->getType_EQ2_Color_ByName("hair_face_highlight_color"));
  2025. SaveCharacterColors(char_id,"wing_color1", create->getType_EQ2_Color_ByName("wing_color1"));
  2026. SaveCharacterColors(char_id,"wing_color2", create->getType_EQ2_Color_ByName("wing_color2"));
  2027. SaveCharacterColors(char_id,"shirt_color", create->getType_EQ2_Color_ByName("shirt_color"));
  2028. SaveCharacterColors(char_id,"unknown_chest_color", create->getType_EQ2_Color_ByName("unknown_chest_color"));
  2029. SaveCharacterColors(char_id,"pants_color", create->getType_EQ2_Color_ByName("pants_color"));
  2030. SaveCharacterColors(char_id,"unknown_legs_color", create->getType_EQ2_Color_ByName("unknown_legs_color"));
  2031. SaveCharacterColors(char_id,"unknown9", create->getType_EQ2_Color_ByName("unknown9"));
  2032. SaveCharacterFloats(char_id,"eye_type", create->getType_float_ByName("eyes2",0), create->getType_float_ByName("eyes2",1), create->getType_float_ByName("eyes2",2));
  2033. SaveCharacterFloats(char_id,"ear_type", create->getType_float_ByName("ears",0), create->getType_float_ByName("ears",1), create->getType_float_ByName("ears",2));
  2034. SaveCharacterFloats(char_id,"eye_brow_type", create->getType_float_ByName("eye_brows",0), create->getType_float_ByName("eye_brows",1), create->getType_float_ByName("eye_brows",2));
  2035. SaveCharacterFloats(char_id,"cheek_type", create->getType_float_ByName("cheeks",0), create->getType_float_ByName("cheeks",1), create->getType_float_ByName("cheeks",2));
  2036. SaveCharacterFloats(char_id,"lip_type", create->getType_float_ByName("lips",0), create->getType_float_ByName("lips",1), create->getType_float_ByName("lips",2));
  2037. SaveCharacterFloats(char_id,"chin_type", create->getType_float_ByName("chin",0), create->getType_float_ByName("chin",1), create->getType_float_ByName("chin",2));
  2038. SaveCharacterFloats(char_id,"nose_type", create->getType_float_ByName("nose",0), create->getType_float_ByName("nose",1), create->getType_float_ByName("nose",2));
  2039. SaveCharacterFloats(char_id,"body_size", create->getType_float_ByName("body_size",0), 0, 0);
  2040. SaveCharacterColors(char_id,"soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color"));
  2041. SaveCharacterColors(char_id,"soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color"));
  2042. SaveCharacterColors(char_id,"soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color"));
  2043. SaveCharacterColors(char_id,"soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1"));
  2044. SaveCharacterColors(char_id,"soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2"));
  2045. SaveCharacterColors(char_id,"soga_hair_highlight", create->getType_EQ2_Color_ByName("soga_hair_highlight"));
  2046. SaveCharacterColors(char_id,"soga_hair_type_color", create->getType_EQ2_Color_ByName("soga_hair_type_color"));
  2047. SaveCharacterColors(char_id,"soga_hair_type_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_type_highlight_color"));
  2048. SaveCharacterColors(char_id,"soga_hair_face_color", create->getType_EQ2_Color_ByName("soga_hair_face_color"));
  2049. SaveCharacterColors(char_id,"soga_hair_face_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_face_highlight_color"));
  2050. SaveCharacterColors(char_id,"soga_wing_color1", create->getType_EQ2_Color_ByName("soga_wing_color1"));
  2051. SaveCharacterColors(char_id,"soga_wing_color2", create->getType_EQ2_Color_ByName("soga_wing_color2"));
  2052. SaveCharacterColors(char_id,"soga_shirt_color", create->getType_EQ2_Color_ByName("soga_shirt_color"));
  2053. SaveCharacterColors(char_id,"soga_unknown_chest_color", create->getType_EQ2_Color_ByName("soga_unknown_chest_color"));
  2054. SaveCharacterColors(char_id,"soga_pants_color", create->getType_EQ2_Color_ByName("soga_pants_color"));
  2055. SaveCharacterColors(char_id,"soga_unknown_legs_color", create->getType_EQ2_Color_ByName("soga_unknown_legs_color"));
  2056. SaveCharacterColors(char_id,"soga_unknown13", create->getType_EQ2_Color_ByName("soga_unknown13"));
  2057. SaveCharacterFloats(char_id,"soga_eye_type", create->getType_float_ByName("soga_eyes2",0), create->getType_float_ByName("soga_eyes2",1), create->getType_float_ByName("soga_eyes2",2));
  2058. SaveCharacterFloats(char_id,"soga_ear_type", create->getType_float_ByName("soga_ears",0), create->getType_float_ByName("soga_ears",1), create->getType_float_ByName("soga_ears",2));
  2059. SaveCharacterFloats(char_id,"soga_eye_brow_type", create->getType_float_ByName("soga_eye_brows",0), create->getType_float_ByName("soga_eye_brows",1), create->getType_float_ByName("soga_eye_brows",2));
  2060. SaveCharacterFloats(char_id,"soga_cheek_type", create->getType_float_ByName("soga_cheeks",0), create->getType_float_ByName("soga_cheeks",1), create->getType_float_ByName("soga_cheeks",2));
  2061. SaveCharacterFloats(char_id,"soga_lip_type", create->getType_float_ByName("soga_lips",0), create->getType_float_ByName("soga_lips",1), create->getType_float_ByName("soga_lips",2));
  2062. SaveCharacterFloats(char_id,"soga_chin_type", create->getType_float_ByName("soga_chin",0), create->getType_float_ByName("soga_chin",1), create->getType_float_ByName("soga_chin",2));
  2063. SaveCharacterFloats(char_id,"soga_nose_type", create->getType_float_ByName("soga_nose",0), create->getType_float_ByName("soga_nose",1), create->getType_float_ByName("soga_nose",2));
  2064. return char_id;
  2065. }
  2066. int8 WorldDatabase::CheckNameFilter(const char* name) {
  2067. // the minimum 4 is enforced by the client too
  2068. if(!name || strlen(name) < 4 || strlen(name) > 15) // Even 20 char length is long...
  2069. return BADNAMELENGTH_REPLY;
  2070. uchar* checkname = (uchar*)name;
  2071. for (int32 i = 0; i < strlen(name); i++)
  2072. {
  2073. if(!alpha_check(checkname[i]))
  2074. return NAMEINVALID_REPLY;
  2075. }
  2076. Query query;
  2077. LogWrite(WORLD__DEBUG, 0, "World", "Name check on: %s", name);
  2078. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT count(*) FROM characters WHERE name='%s'",name);
  2079. if(result && mysql_num_rows(result) > 0) {
  2080. MYSQL_ROW row;
  2081. row = mysql_fetch_row(result);
  2082. if(row[0] != 0 && atoi(row[0]) > 0)
  2083. return NAMETAKEN_REPLY;
  2084. }
  2085. else
  2086. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name exist check) (Name query '%s': %s", query.GetQuery(), query.GetError());
  2087. Query query3;
  2088. LogWrite(WORLD__DEBUG, 0, "World", "Name check on: %s (Bots table)", name);
  2089. MYSQL_RES* result3 = query3.RunQuery2(Q_SELECT, "SELECT count(*) FROM bots WHERE name='%s'", name);
  2090. if (result3 && mysql_num_rows(result3) > 0) {
  2091. MYSQL_ROW row;
  2092. row = mysql_fetch_row(result3);
  2093. if (row[0] != 0 && atoi(row[0]) > 0)
  2094. return NAMETAKEN_REPLY;
  2095. }
  2096. else
  2097. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name exist check, bot table) (Name query '%s': %s", query3.GetQuery(), query3.GetError());
  2098. Query query2;
  2099. MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT count(*) FROM name_filter WHERE '%s' like name",name);
  2100. if(result2 && mysql_num_rows(result2) > 0) {
  2101. MYSQL_ROW row;
  2102. row = mysql_fetch_row(result2);
  2103. if(row[0] != 0 && atoi(row[0]) > 0)
  2104. return NAMEFILTER_REPLY;
  2105. else if(row[0] != 0 && atoi(row[0]) == 0)
  2106. return CREATESUCCESS_REPLY;
  2107. }
  2108. else
  2109. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name_filter check) query '%s': %s", query.GetQuery(), query.GetError());
  2110. return UNKNOWNERROR_REPLY;
  2111. }
  2112. char* WorldDatabase::GetCharacterName(int32 character_id){
  2113. LogWrite(WORLD__TRACE, 9, "World", "Enter: %s", __FUNCTION__);
  2114. Query query;
  2115. char* name = 0;
  2116. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM characters where id=%u",character_id);
  2117. if(result && mysql_num_rows(result) > 0) {
  2118. MYSQL_ROW row;
  2119. row = mysql_fetch_row(result);
  2120. if(row[0] && strlen(row[0]) > 0)
  2121. {
  2122. name = new char[strlen(row[0])+1];
  2123. memset(name,0, strlen(row[0])+1);
  2124. strcpy(name, row[0]);
  2125. }
  2126. }
  2127. else
  2128. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterName query '%s': %s", query.GetQuery(), query.GetError());
  2129. LogWrite(WORLD__TRACE, 9, "World", "Exit: %s", __FUNCTION__);
  2130. return name;
  2131. }
  2132. int8 WorldDatabase::GetCharacterLevel(int32 character_id){
  2133. Query query;
  2134. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT level FROM characters where id=%u",character_id);
  2135. if(result && mysql_num_rows(result) > 0) {
  2136. MYSQL_ROW row;
  2137. row = mysql_fetch_row(result);
  2138. return atoi(row[0]); // Return characters level
  2139. }
  2140. else
  2141. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterLevel query '%s': %s", query.GetQuery(), query.GetError());
  2142. return 0;
  2143. }
  2144. int16 WorldDatabase::GetCharacterModelType(int32 character_id){
  2145. Query query;
  2146. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT model_type FROM characters where id=%u",character_id);
  2147. if(result && mysql_num_rows(result) > 0) {
  2148. MYSQL_ROW row;
  2149. row = mysql_fetch_row(result);
  2150. return atoi(row[0]); // Return characters race
  2151. }
  2152. else
  2153. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterModelType query '%s': %s", query.GetQuery(), query.GetError());
  2154. return 0;
  2155. }
  2156. int8 WorldDatabase::GetCharacterClass(int32 character_id){
  2157. Query query;
  2158. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT class FROM characters where id=%u",character_id);
  2159. if(result && mysql_num_rows(result) > 0) {
  2160. MYSQL_ROW row;
  2161. row = mysql_fetch_row(result);
  2162. return atoi(row[0]); // Return characters class
  2163. }
  2164. else
  2165. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterClass query '%s': %s", query.GetQuery(), query.GetError());
  2166. return 0;
  2167. }
  2168. int8 WorldDatabase::GetCharacterGender(int32 character_id){
  2169. Query query;
  2170. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT gender FROM characters where id=%u",character_id);
  2171. if(result && mysql_num_rows(result) > 0) {
  2172. MYSQL_ROW row;
  2173. row = mysql_fetch_row(result);
  2174. return atoi(row[0]); // Return characters gender
  2175. }
  2176. else
  2177. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterGender query '%s': %s", query.GetQuery(), query.GetError());
  2178. return 0;
  2179. }
  2180. void WorldDatabase::DeleteCharacterQuest(int32 quest_id, int32 char_id, bool repeated_quest) {
  2181. if (repeated_quest) {
  2182. if (!database_new.Query("UPDATE `character_quests` SET `given_date` = `completed_date` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2183. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2184. }
  2185. else {
  2186. if (!database_new.Query("DELETE FROM `character_quests` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2187. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2188. }
  2189. if (!database_new.Query("DELETE FROM `character_quest_progress` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2190. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2191. }
  2192. void WorldDatabase::SaveCharacterSkills(Client* client){
  2193. vector<Skill*>* skills = client->GetPlayer()->GetSkills()->GetSaveNeededSkills();
  2194. if(skills){
  2195. Query query;
  2196. if(skills->size() > 0){
  2197. Skill* skill = 0;
  2198. for(int32 i=0;i<skills->size();i++){
  2199. skill = skills->at(i);
  2200. query.AddQueryAsync(client->GetCharacterID(),this,Q_REPLACE, "replace into character_skills (char_id, skill_id, current_val, max_val) values(%u, %u, %i, %i)", client->GetCharacterID(), skill->skill_id, skill->current_val, skill->max_val);
  2201. }
  2202. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2203. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterSkills query '%s': %s", query.GetQuery(), query.GetError());
  2204. }
  2205. safe_delete(skills);
  2206. }
  2207. }
  2208. void WorldDatabase::SaveCharacterQuests(Client* client){
  2209. Query query;
  2210. map<int32, Quest*>::iterator itr;
  2211. master_quest_list.LockQuests(); //prevent reloading until we are done
  2212. client->GetPlayer()->LockQuests(); //prevent all quest modifications until we are done
  2213. map<int32, Quest*>* quests = client->GetPlayer()->GetPlayerQuests();
  2214. for(itr = quests->begin(); itr != quests->end(); itr++){
  2215. if(client->GetCurrentQuestID() == itr->first){
  2216. query.AddQueryAsync(client->GetCharacterID(),this,Q_UPDATE, "update character_quests set current_quest = 0 where char_id = %u", client->GetCharacterID());
  2217. query.AddQueryAsync(client->GetCharacterID(), this,Q_UPDATE, "update character_quests set current_quest = 1 where char_id = %u and quest_id = %u", client->GetCharacterID(), itr->first);
  2218. }
  2219. if(itr->second->GetSaveNeeded()){
  2220. query.AddQueryAsync(client->GetCharacterID(), this,Q_INSERT, "insert ignore into character_quests (char_id, quest_id, given_date, quest_giver) values(%u, %u, now(), %u)", client->GetCharacterID(), itr->first, itr->second->GetQuestGiver());
  2221. query.AddQueryAsync(client->GetCharacterID(), this,Q_UPDATE, "update character_quests set tracked = %i, quest_flags = %u, hidden = %i, complete_count = %u where char_id = %u and quest_id = %u", itr->second->IsTracked() ? 1 : 0, itr->second->GetQuestFlags(), itr->second->IsHidden() ? 1 : 0, itr->second->GetCompleteCount(), client->GetCharacterID(), itr->first);
  2222. SaveCharacterQuestProgress(client, itr->second);
  2223. itr->second->SetSaveNeeded(false);
  2224. }
  2225. }
  2226. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2227. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterQuests query '%s': %s", query.GetQuery(), query.GetError());
  2228. quests = client->GetPlayer()->GetCompletedPlayerQuests();
  2229. for(itr = quests->begin(); itr != quests->end(); itr++){
  2230. if(itr->second->GetSaveNeeded()){
  2231. query.AddQueryAsync(client->GetCharacterID(), this,Q_DELETE, "delete FROM character_quest_progress where char_id = %u and quest_id = %u", client->GetCharacterID(), itr->first);
  2232. /* incase the quest is completed before the quest could be inserted in the PlayerQuests loop, we first try to insert it. If it already exists then we can just update
  2233. * the completed_date */
  2234. query.AddQueryAsync(client->GetCharacterID(), this,Q_INSERT, "INSERT INTO character_quests (char_id, quest_id, quest_giver, current_quest, given_date, completed_date, complete_count) values (%u,%u,%u,0, now(),now(), %u) ON DUPLICATE KEY UPDATE completed_date = now(), complete_count = %u, current_quest = 0", client->GetCharacterID(), itr->first, itr->second->GetQuestGiver(), itr->second->GetCompleteCount(), itr->second->GetCompleteCount());
  2235. itr->second->SetSaveNeeded(false);
  2236. }
  2237. }
  2238. client->GetPlayer()->UnlockQuests();
  2239. master_quest_list.UnlockQuests();
  2240. }
  2241. void WorldDatabase::SaveCharRepeatableQuest(Client* client, int32 quest_id, int16 quest_complete_count) {
  2242. if (!database_new.Query("UPDATE `character_quests` SET `given_date` = now(), complete_count = %u WHERE `char_id` = %u AND `quest_id` = %u", quest_complete_count, client->GetCharacterID(), quest_id))
  2243. LogWrite(DATABASE__ERROR, 0, "DBNew", "DB Error %u\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2244. }
  2245. void WorldDatabase::SaveCharacterQuestProgress(Client* client, Quest* quest){
  2246. Query query;
  2247. vector<QuestStep*>* steps = quest->GetQuestSteps();
  2248. vector<QuestStep*>::iterator itr;
  2249. QuestStep* step = 0;
  2250. if(steps){
  2251. for(itr = steps->begin(); itr != steps->end(); itr++){
  2252. step = *itr;
  2253. if(step && step->GetQuestCurrentQuantity() > 0)
  2254. query.RunQuery2(Q_REPLACE, "replace into character_quest_progress (char_id, quest_id, step_id, progress) values(%u, %u, %u, %i)", client->GetCharacterID(), quest->GetQuestID(), step->GetStepID(), step->GetQuestCurrentQuantity());
  2255. }
  2256. }
  2257. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2258. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterQuestProgress query '%s': %s", query.GetQuery(), query.GetError());
  2259. }
  2260. void WorldDatabase::LoadCharacterQuestProgress(Client* client){
  2261. Query query;
  2262. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT character_quest_progress.quest_id, step_id, progress FROM character_quest_progress, character_quests where character_quest_progress.char_id=%u and character_quest_progress.quest_id = character_quests.quest_id and character_quest_progress.char_id = character_quests.char_id ORDER BY character_quest_progress.quest_id",client->GetCharacterID());
  2263. if(result && mysql_num_rows(result) > 0) {
  2264. MYSQL_ROW row;
  2265. Quest* quest = 0;
  2266. int32 quest_id = 0;
  2267. map<int32, int32>* progress_map = new map<int32, int32>();
  2268. while(result && (row = mysql_fetch_row(result))){
  2269. if(quest_id != atoul(row[0])){
  2270. if(quest_id > 0){
  2271. quest = client->GetPlayer()->GetQuest(quest_id);
  2272. if(quest)
  2273. client->SetPlayerQuest(quest, progress_map);
  2274. }
  2275. quest_id = atoul(row[0]);
  2276. progress_map->clear();
  2277. }
  2278. (*progress_map)[atoul(row[1])] = atoul(row[2]);
  2279. }
  2280. if(progress_map->size() > 0){
  2281. quest = client->GetPlayer()->GetQuest(quest_id);
  2282. if(quest)
  2283. client->SetPlayerQuest(quest, progress_map);
  2284. }
  2285. safe_delete(progress_map);
  2286. }
  2287. else if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2288. LogWrite(WORLD__ERROR, 0, "World", "Error in LoadCharacterQuestProgress query '%s': %s", query.GetQuery(), query.GetError());
  2289. }
  2290. void WorldDatabase::LoadCharacterQuests(Client* client){
  2291. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Character Quests...");
  2292. Query query;
  2293. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT quest_id, DAY(given_date), MONTH(given_date), YEAR(given_date), DAY(completed_date), MONTH(completed_date), YEAR(completed_date), quest_giver, tracked, quest_flags, hidden, UNIX_TIMESTAMP(given_date), UNIX_TIMESTAMP(completed_date), complete_count FROM character_quests WHERE char_id=%u ORDER BY current_quest", client->GetCharacterID());
  2294. if(result && mysql_num_rows(result) > 0) {
  2295. MYSQL_ROW row;
  2296. Quest* quest = 0;
  2297. while(result && (row = mysql_fetch_row(result))){
  2298. quest = master_quest_list.GetQuest(atoul(row[0]));
  2299. if(quest) {
  2300. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLoading quest_id: %u", atoul(row[0]));
  2301. bool addQuest = true;
  2302. if(row[4] && atoi(row[4]) > 0){
  2303. quest->SetTurnedIn(true);
  2304. if(row[4])
  2305. quest->SetDay(atoi(row[4]));
  2306. if(row[5])
  2307. quest->SetMonth(atoi(row[5]));
  2308. if(row[6] && atoi(row[6]) > 2000)
  2309. quest->SetYear(atoi(row[6]) - 2000);
  2310. client->GetPlayer()->AddCompletedQuest(quest);
  2311. // Added timestamps to quickly compare given and completed dates
  2312. int32 given_timestamp = atoul(row[11]);
  2313. int32 completed_timestamp = atoul(row[12]);
  2314. // If given timestamp is greater then completed then this is a repeatable quest we are working on
  2315. // so get a fresh quest object to add as an active quest
  2316. if (given_timestamp > completed_timestamp)
  2317. {
  2318. safe_delete(quest);
  2319. quest = master_quest_list.GetQuest(atoul(row[0]));
  2320. }
  2321. else
  2322. addQuest = false;
  2323. quest->SetCompleteCount(atoi(row[13]));
  2324. }
  2325. if (addQuest) {
  2326. if(row[1])
  2327. quest->SetDay(atoi(row[1]));
  2328. if(row[2])
  2329. quest->SetMonth(atoi(row[2]));
  2330. if(row[3] && atoi(row[3]) > 2000)
  2331. quest->SetYear(atoi(row[3]) - 2000);
  2332. quest->SetQuestGiver(atoul(row[7]));
  2333. quest->SetTracked(atoi(row[8]) == 1 ? true : false);
  2334. quest->SetQuestFlags(atoul(row[9]));
  2335. quest->SetHidden(atoi(row[10]) == 1 ? true : false);
  2336. client->AddPlayerQuest(quest, false, false);
  2337. }
  2338. quest->SetSaveNeeded(false);
  2339. // Changed this to call reload with step = 0 for all quests and not
  2340. // just quests with quest flags to allow customized set up if needed
  2341. if (lua_interface)
  2342. lua_interface->CallQuestFunction(quest, "Reload", client->GetPlayer(), 0);
  2343. }
  2344. }
  2345. LoadCharacterQuestProgress(client);
  2346. }
  2347. }
  2348. void WorldDatabase::LoadCharacterFriendsIgnoreList(Player* player) {
  2349. if (player) {
  2350. Query query;
  2351. MYSQL_ROW row;
  2352. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name`, `type` FROM `character_social` WHERE `char_id`=%u", player->GetCharacterID());
  2353. while (result && (row = mysql_fetch_row(result))) {
  2354. if (strncmp(row[1], "FRIEND", 6) == 0)
  2355. player->AddFriend(row[0], false);
  2356. else
  2357. player->AddIgnore(row[0], false);
  2358. }
  2359. }
  2360. }
  2361. void WorldDatabase::LoadZoneInfo(ZoneServer* zone){
  2362. Query query;
  2363. int32 ruleset_id;
  2364. char* escaped = getEscapeString(zone->GetZoneName());
  2365. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, file, description, underworld, safe_x, safe_y, safe_z, min_status, min_level, max_level, instance_type+0, shutdown_timer, zone_motd, default_reenter_time, default_reset_time, default_lockout_time, force_group_to_zone, safe_heading, xp_modifier, ruleset_id, expansion_id, weather_allowed, sky_file FROM zones where name='%s'",escaped);
  2366. if(result && mysql_num_rows(result) > 0) {
  2367. MYSQL_ROW row;
  2368. row = mysql_fetch_row(result);
  2369. zone->SetZoneName(escaped);
  2370. zone->SetZoneID(strtoul(row[0], NULL, 0));
  2371. zone->SetZoneFile(row[1]);
  2372. zone->SetZoneDescription(row[2]);
  2373. zone->SetUnderWorld(atof(row[3]));
  2374. zone->SetSafeX(atof(row[4]));
  2375. zone->SetSafeY(atof(row[5]));
  2376. zone->SetSafeZ(atof(row[6]));
  2377. zone->SetMinimumStatus(atoi(row[7]));
  2378. zone->SetMinimumLevel(atoi(row[8]));
  2379. zone->SetMaximumLevel(atoi(row[9]));
  2380. int8 type = (atoi(row[10]) == 0) ? 0 : atoi(row[10]) - 1;
  2381. zone->SetInstanceType(type);
  2382. zone->SetShutdownTimer(atoul(row[11]));
  2383. char* zone_motd = row[12];
  2384. if (zone_motd && strlen(zone_motd) > 0)
  2385. zone->SetZoneMOTD(string(zone_motd));
  2386. zone->SetDefaultReenterTime(atoi(row[13]));
  2387. zone->SetDefaultResetTime(atoi(row[14]));
  2388. zone->SetDefaultLockoutTime(atoi(row[15]));
  2389. zone->SetForceGroupZoneOption(atoi(row[16]));
  2390. zone->SetSafeHeading(atof(row[17]));
  2391. zone->SetXPModifier(atof(row[18]));
  2392. if ((ruleset_id = atoul(row[19])) > 0 && !rule_manager.SetZoneRuleSet(zone->GetZoneID(), ruleset_id))
  2393. LogWrite(ZONE__ERROR, 0, "Zones", "Error setting rule set for zone '%s' (%u). A rule set with ID %u does not exist.", zone->GetZoneName(), zone->GetZoneID(), ruleset_id);
  2394. // check data_version to see if client has proper expansion to enter a zone
  2395. zone->SetMinimumVersion(GetMinimumClientVersion(atoi(row[20])));
  2396. zone->SetWeatherAllowed(atoi(row[21]) == 0 ? false : true);
  2397. zone->SetZoneSkyFile(row[22]);
  2398. if (zone->IsInstanceZone())
  2399. {
  2400. if ( zone->GetInstanceID() < 1 )
  2401. zone->SetupInstance(CreateNewInstance(zone->GetZoneID()));
  2402. else
  2403. zone->SetupInstance(zone->GetInstanceID());
  2404. }
  2405. }
  2406. safe_delete_array(escaped);
  2407. }
  2408. void WorldDatabase::LoadZoneInfo(ZoneInfo* zone_info) {
  2409. Query query;
  2410. int32 ruleset_id;
  2411. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name, file, description, underworld, safe_x, safe_y, safe_z, min_status, min_level, max_level, instance_type, shutdown_timer, zone_motd, default_reenter_time, default_reset_time, default_lockout_time, force_group_to_zone, lua_script, xp_modifier, ruleset_id, expansion_id, always_loaded, city_zone, start_zone, zone_type, weather_allowed, sky_file FROM zones WHERE id = %u", zone_info->id);
  2412. if (result && mysql_num_rows(result) > 0) {
  2413. MYSQL_ROW row;
  2414. row = mysql_fetch_row(result);
  2415. strncpy(zone_info->name, row[0], sizeof(zone_info->name));
  2416. strncpy(zone_info->file, row[1], sizeof(zone_info->file));
  2417. strncpy(zone_info->description, row[2], sizeof(zone_info->description));
  2418. zone_info->underworld = atof(row[3]);
  2419. zone_info->safe_x = atof(row[4]);
  2420. zone_info->safe_y = atof(row[5]);
  2421. zone_info->safe_z = atof(row[6]);
  2422. zone_info->min_status = atoi(row[7]);
  2423. zone_info->min_level = atoi(row[8]);
  2424. zone_info->max_level = atoi(row[9]);
  2425. zone_info->instance_type = (atoi(row[10]) == 0) ? 0 : atoi(row[10]) - 1;
  2426. zone_info->shutdown_timer = atoul(row[11]);
  2427. row[12] == NULL ? strncpy(zone_info->zone_motd, "", sizeof(zone_info->zone_motd)) : strncpy(zone_info->zone_motd, row[12], sizeof(zone_info->zone_motd));
  2428. zone_info->default_reenter_time = atoi(row[13]);
  2429. zone_info->default_reset_time = atoi(row[14]);
  2430. zone_info->default_lockout_time = atoi(row[15]);
  2431. zone_info->force_group_to_zone = atoi(row[16]);
  2432. row[17] == NULL ? strncpy(zone_info->lua_script, "", sizeof(zone_info->lua_script)) : strncpy(zone_info->lua_script, row[17], sizeof(zone_info->lua_script));
  2433. zone_info->xp_modifier = atof(row[18]);
  2434. zone_info->ruleset_id = atoul(row[19]);
  2435. if ((ruleset_id = atoul(row[19])) > 0 && !rule_manager.SetZoneRuleSet(zone_info->id, ruleset_id))
  2436. LogWrite(ZONE__ERROR, 0, "Zones", "Error setting rule set for zone '%s' (%u). A rule set with ID %u does not exist.", zone_info->name, zone_info->id, ruleset_id);
  2437. zone_info->expansion_id = atoi(row[20]);
  2438. zone_info->min_version = GetMinimumClientVersion(zone_info->expansion_id);
  2439. zone_info->always_loaded = atoi(row[21]);
  2440. zone_info->city_zone = atoi(row[22]);
  2441. zone_info->start_zone = atoi(row[23]);
  2442. row[24] == NULL ? strncpy(zone_info->zone_type, "", sizeof(zone_info->zone_type)) : strncpy(zone_info->zone_type, row[24], sizeof(zone_info->zone_type));
  2443. zone_info->weather_allowed = atoi(row[25]);
  2444. strncpy(zone_info->sky_file, row[26], sizeof(zone_info->sky_file));
  2445. }
  2446. }
  2447. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, sint32 value) {
  2448. Query query;
  2449. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`=%i WHERE `id`=%u", field, value, zone_id);
  2450. }
  2451. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, float value) {
  2452. Query query;
  2453. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`=%f WHERE `id`=%u", field, value, zone_id);
  2454. }
  2455. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, const char* value) {
  2456. Query query;
  2457. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`='%s' WHERE `id`=%u", field, const_cast<char*>(getEscapeString(value)), zone_id);
  2458. }
  2459. int32 WorldDatabase::GetZoneID(const char* name) {
  2460. int32 zone_id = 0;
  2461. Query query;
  2462. char* escaped = getEscapeString(name);
  2463. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM zones WHERE `name`=\"%s\"", escaped);
  2464. if (result && mysql_num_rows(result) > 0) {
  2465. MYSQL_ROW row;
  2466. row = mysql_fetch_row(result);
  2467. zone_id = atoi(row[0]);
  2468. }
  2469. safe_delete_array(escaped);
  2470. return zone_id;
  2471. }
  2472. bool WorldDatabase::GetZoneRequirements(const char* zoneName, sint16* minStatus, int16* minLevel, int16* maxLevel, int16* minVersion) {
  2473. Query query;
  2474. char* escaped = getEscapeString(zoneName);
  2475. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT min_status, min_level, max_level, expansion_id FROM zones where name='%s'",escaped);
  2476. if(result && mysql_num_rows(result) > 0) {
  2477. MYSQL_ROW row;
  2478. row = mysql_fetch_row(result);
  2479. sint16 status = (sint16)atoi(row[0]);
  2480. int16 levelMin = (int16)atoi(row[1]);
  2481. int16 levelMax = (int16)atoi(row[2]);
  2482. int8 expansion_id = (int8)atoi(row[3]);
  2483. *minStatus = status;
  2484. *minLevel = levelMin;
  2485. *maxLevel = levelMax;
  2486. if( expansion_id >= 40 ) // lowest client we support is RoK - exp04
  2487. *minVersion = GetMinimumClientVersion(expansion_id);
  2488. else
  2489. *minVersion = 0;
  2490. safe_delete_array(escaped);
  2491. return true;
  2492. }
  2493. safe_delete_array(escaped);
  2494. return false;
  2495. }
  2496. int16 WorldDatabase::GetMinimumClientVersion(int8 expansion_id)
  2497. {
  2498. /*
  2499. 1 n/a Classic Expansion
  2500. 2 adv01 Bloodline Chronicles
  2501. 3 adv02 Splitpaw Saga
  2502. 10 exp01 Desert of Flames
  2503. 20 exp02 Kingdom of Sky
  2504. 21 adv04 Fallen Dynasty
  2505. 30 exp03 Echoes of Faydwer
  2506. 40 exp04 Rise of Kunark
  2507. 50 exp05 The Shadow Odyssey
  2508. 60 exp06 Sentinel's Fate
  2509. 61 halas Halas Reborn
  2510. 70 exp07 Destiny of Velious
  2511. 80 exp08 Age of Discovery
  2512. */
  2513. int16 minVer = 0;
  2514. // TODO: eventually replace this with reading values from eq2expansions table
  2515. switch(expansion_id)
  2516. {
  2517. case 40: // ROK
  2518. {
  2519. minVer = 843;
  2520. break;
  2521. }
  2522. case 50: // TSO
  2523. {
  2524. minVer = 908;
  2525. break;
  2526. }
  2527. case 60: // SF
  2528. {
  2529. minVer = 1008;
  2530. break;
  2531. }
  2532. case 61: // Halas
  2533. {
  2534. minVer = 1045;
  2535. break;
  2536. }
  2537. case 70: // DoV
  2538. {
  2539. minVer = 1096;
  2540. break;
  2541. }
  2542. case 80: // AoD
  2543. {
  2544. minVer = 1144;
  2545. break;
  2546. }
  2547. case 90: // CoE
  2548. {
  2549. minVer = 1188;
  2550. break;
  2551. }
  2552. }
  2553. return minVer;
  2554. }
  2555. // returns Expansion Name depending on the connected client's data version
  2556. string WorldDatabase::GetExpansionIDByVersion(int16 version)
  2557. {
  2558. /*
  2559. 0 n/a Classic Expansion
  2560. 0 adv01 Bloodline Chronicles
  2561. 0 adv02 Splitpaw Saga
  2562. 0 exp01 Desert of Flames
  2563. 0 exp02 Kingdom of Sky
  2564. 0 adv04 Fallen Dynasty
  2565. 0 exp03 Echoes of Faydwer
  2566. 843 exp04 Rise of Kunark
  2567. 908 exp05 The Shadow Odyssey
  2568. 1008 exp06 Sentinel's Fate
  2569. 1045 halas Halas Reborn
  2570. 1096 exp07 Destiny of Velious
  2571. 1142 exp08 Age of Discovery
  2572. 1188 exp09 Chains of Eternity
  2573. 9999 (and beyond)
  2574. */
  2575. string ret = "";
  2576. if( version >= 9999 )
  2577. ret = "Unknown";
  2578. else if( version >= 1188 )
  2579. ret = "Chains of Eternity";
  2580. else if( version >= 1142 )
  2581. ret = "Age of Discovery";
  2582. else if( version >= 1096 )
  2583. ret = "Destiny of Velious";
  2584. else if( version >= 1045 )
  2585. ret = "Halas Reborn";
  2586. else if( version >= 1008 )
  2587. ret = "Sentinel's Fate";
  2588. else if( version >= 908 )
  2589. ret = "The Shadow Odyssey";
  2590. else if( version >= 843 )
  2591. ret = "Rise of Kunark";
  2592. else
  2593. ret = "Any";
  2594. return ret;
  2595. }
  2596. void WorldDatabase::LoadSpecialZones(){
  2597. Query query;
  2598. ZoneServer* zone = 0;
  2599. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded, city_zone FROM zones where always_loaded = 1 or city_zone = 1");
  2600. if(result && mysql_num_rows(result) > 0) {
  2601. MYSQL_ROW row;
  2602. while(result && (row = mysql_fetch_row(result))){
  2603. zone = new ZoneServer(row[1]);
  2604. LoadZoneInfo(zone);
  2605. zone->Init();
  2606. zone->SetAlwaysLoaded(atoi(row[2]) == 1);
  2607. zone->SetCityZone(atoi(row[3]) == 1);
  2608. }
  2609. }
  2610. }
  2611. bool WorldDatabase::SpawnGroupRemoveAssociation(int32 group1, int32 group2){
  2612. Query query;
  2613. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_group_associations where (group_id1 = %u and group_id2 = %u) or (group_id1 = %u and group_id2 = %u)", group1, group2, group2, group1);
  2614. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2615. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2616. return false;
  2617. }
  2618. return true;
  2619. }
  2620. bool WorldDatabase::SpawnGroupAddAssociation(int32 group1, int32 group2){
  2621. Query query;
  2622. query.RunQuery2(Q_INSERT, "insert ignore into spawn_location_group_associations (group_id1, group_id2) values(%u, %u)", group1, group2);
  2623. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2624. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupAddAssociation query '%s': %s", query.GetQuery(), query.GetError());
  2625. return false;
  2626. }
  2627. return true;
  2628. }
  2629. bool WorldDatabase::SpawnGroupAddSpawn(Spawn* spawn, int32 group_id){
  2630. Query query;
  2631. query.RunQuery2(Q_INSERT, "insert ignore into spawn_location_group (group_id, placement_id) values(%u, %u)", group_id, spawn->GetSpawnLocationPlacementID());
  2632. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2633. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupAddSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2634. return false;
  2635. }
  2636. return true;
  2637. }
  2638. bool WorldDatabase::SpawnGroupRemoveSpawn(Spawn* spawn, int32 group_id){
  2639. Query query;
  2640. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_group where group_id = %u and placement_id = %u", group_id, spawn->GetSpawnLocationPlacementID());
  2641. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2642. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2643. return false;
  2644. }
  2645. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT count(group_id) FROM spawn_location_group where group_id = %u", group_id);
  2646. if(result && mysql_num_rows(result) > 0) {
  2647. MYSQL_ROW row;
  2648. if((row = mysql_fetch_row(result))){
  2649. if(atoul(row[0]) == 0)
  2650. DeleteSpawnGroup(group_id);
  2651. }
  2652. }
  2653. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2654. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2655. return false;
  2656. }
  2657. return true;
  2658. }
  2659. int32 WorldDatabase::CreateSpawnGroup(Spawn* spawn, string name)
  2660. {
  2661. int32 group_id = 0;
  2662. Query query;
  2663. // JA: As of 0.7.1, DB Milestone 2, Content Team needs to use group_id's from Raw Data, so start any manual group_id's > 100,000
  2664. query.RunQuery2(Q_INSERT, "INSERT INTO spawn_location_group (group_id, placement_id, name) SELECT IF(ISNULL(MAX(group_id))=1, 100000, MAX(group_id)+1), %u, '%s' FROM spawn_location_group", spawn->GetSpawnLocationPlacementID(), getSafeEscapeString(name.c_str()).c_str());
  2665. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2666. return 0;
  2667. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(group_id) FROM spawn_location_group");
  2668. if(result && mysql_num_rows(result) > 0)
  2669. {
  2670. MYSQL_ROW row;
  2671. if((row = mysql_fetch_row(result)))
  2672. {
  2673. if(row[0])
  2674. group_id = atoul(row[0]);
  2675. }
  2676. }
  2677. return group_id;
  2678. }
  2679. void WorldDatabase::DeleteSpawnGroup(int32 id){
  2680. Query query;
  2681. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_group where group_id = %u", id);
  2682. }
  2683. bool WorldDatabase::SetGroupSpawnChance(int32 id, float chance){
  2684. Query query;
  2685. query.RunQuery2(Q_UPDATE, "replace into spawn_location_group_chances (group_id, percentage) values(%u, %f)", id, chance);
  2686. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2687. LogWrite(WORLD__ERROR, 0, "World", "Error in SetGroupSpawnChance query '%s': %s", query.GetQuery(), query.GetError());
  2688. return false;
  2689. }
  2690. return true;
  2691. }
  2692. int32 WorldDatabase::LoadSpawnGroupChances(ZoneServer* zone){
  2693. Query query;
  2694. int32 count = 0;
  2695. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT slgc.group_id, slgc.percentage FROM spawn_location_group_chances slgc, spawn_location_group slg, spawn_location_placement slp where slgc.group_id = slg.group_id and slg.placement_id = slp.id and slp.zone_id = %u", zone->GetZoneID());
  2696. if(result && mysql_num_rows(result) > 0) {
  2697. MYSQL_ROW row;
  2698. int32 group_id = 0;
  2699. float percent = 0;
  2700. while(result && (row = mysql_fetch_row(result))){
  2701. group_id = atoul(row[0]);
  2702. percent = atof(row[1]);
  2703. zone->AddSpawnGroupChance(group_id, percent);
  2704. count++;
  2705. }
  2706. }
  2707. return count;
  2708. }
  2709. int32 WorldDatabase::LoadSpawnLocationGroupAssociations(ZoneServer* zone){
  2710. Query query;
  2711. int32 count = 0;
  2712. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT distinct slga.group_id1, slga.group_id2 FROM spawn_location_group_associations slga, spawn_location_group slg, spawn_location_placement slp where (slg.group_id = slga.group_id1 or slg.group_id = slga.group_id2) and slg.placement_id = slp.id and slp.zone_id = %u", zone->GetZoneID());
  2713. if(result && mysql_num_rows(result) > 0) {
  2714. MYSQL_ROW row;
  2715. while(result && (row = mysql_fetch_row(result))){
  2716. zone->AddSpawnGroupAssociation(atoul(row[0]), atoul(row[1]));
  2717. count++;
  2718. }
  2719. }
  2720. return count;
  2721. }
  2722. int32 WorldDatabase::LoadSpawnLocationGroups(ZoneServer* zone){
  2723. Query query;
  2724. int32 count = 0;
  2725. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT slg.group_id, slg.placement_id, slp.spawn_location_id FROM spawn_location_group slg, spawn_location_placement slp WHERE slg.placement_id = slp.id and slp.zone_id = %u", zone->GetZoneID());
  2726. if(result && mysql_num_rows(result) > 0) {
  2727. MYSQL_ROW row;
  2728. int32 placement_id = 0;
  2729. int32 group_id = 0;
  2730. int32 spawn_location_id = 0;
  2731. while(result && (row = mysql_fetch_row(result))){
  2732. group_id = atoul(row[0]);
  2733. placement_id = atoul(row[1]);
  2734. spawn_location_id = atoul(row[2]);
  2735. zone->AddSpawnGroupLocation(group_id, placement_id, spawn_location_id);
  2736. count++;
  2737. }
  2738. }
  2739. return count;
  2740. }
  2741. int32 WorldDatabase::ProcessSpawnLocations(ZoneServer* zone, const char* sql_query, int8 type){
  2742. int32 number = 0;
  2743. Query query;
  2744. MYSQL_RES* result = query.RunQuery2(Q_SELECT, sql_query, zone->GetZoneID());
  2745. if(result && mysql_num_rows(result) > 0) {
  2746. MYSQL_ROW row;
  2747. int32 spawn_location_id = 0xFFFFFFFF;
  2748. SpawnLocation* spawn_location = 0;
  2749. while(result && (row = mysql_fetch_row(result))){
  2750. if((spawn_location_id == 0xFFFFFFFF) || atoul(row[0]) != spawn_location_id){
  2751. if(spawn_location){
  2752. zone->AddSpawnLocation(spawn_location_id, spawn_location);
  2753. number++;
  2754. }
  2755. spawn_location = new SpawnLocation();
  2756. }
  2757. SpawnEntry* entry = new SpawnEntry;
  2758. spawn_location_id = atoul(row[0]);
  2759. entry->spawn_location_id = spawn_location_id;
  2760. entry->spawn_entry_id = atoul(row[1]);
  2761. entry->spawn_type = type;
  2762. entry->spawn_id = atoul(row[9]);
  2763. entry->spawn_percentage = atof(row[10]);
  2764. entry->respawn = atoul(row[11]);
  2765. entry->expire_time = atoul(row[14]);
  2766. entry->expire_offset = atoul(row[15]);
  2767. spawn_location->x = atof(row[2]);
  2768. spawn_location->y = atof(row[3]);
  2769. spawn_location->z = atof(row[4]);
  2770. spawn_location->x_offset = atof(row[5]);
  2771. spawn_location->y_offset = atof(row[6]);
  2772. spawn_location->z_offset = atof(row[7]);
  2773. spawn_location->heading = atof(row[8]);
  2774. spawn_location->pitch = atof(row[16]);
  2775. spawn_location->roll = atof(row[17]);
  2776. spawn_location->conditional = atoi(row[18]);
  2777. spawn_location->total_percentage += entry->spawn_percentage;
  2778. spawn_location->grid_id = strtoul(row[12], NULL, 0);
  2779. spawn_location->placement_id = strtoul(row[13], NULL, 0);
  2780. spawn_location->AddSpawn(entry);
  2781. }
  2782. if(spawn_location){
  2783. zone->AddSpawnLocation(spawn_location_id, spawn_location);
  2784. number++;
  2785. }
  2786. }
  2787. return number;
  2788. }
  2789. void WorldDatabase::ResetDatabase(){
  2790. Query query;
  2791. query.RunQuery2("delete FROM table_versions where name != 'table_versions'", Q_DELETE);
  2792. }
  2793. void WorldDatabase::EnableConstraints(){
  2794. Query query;
  2795. query.RunQuery2("/*!40101 SET SQL_MODE=@OLD_SQL_MODE */", Q_DBMS);
  2796. query.RunQuery2("/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */", Q_DBMS);
  2797. }
  2798. void WorldDatabase::DisableConstraints(){
  2799. Query query;
  2800. query.RunQuery2("/*!40101 SET NAMES utf8 */", Q_DBMS);
  2801. query.RunQuery2("/*!40101 SET SQL_MODE=''*/", Q_DBMS);
  2802. query.RunQuery2("/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */", Q_DBMS);
  2803. query.RunQuery2("/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */", Q_DBMS);
  2804. }
  2805. void WorldDatabase::LoadSpawns(ZoneServer* zone)
  2806. {
  2807. Query query;
  2808. int32 npcs = 0, objects = 0, widgets = 0, signs = 0, ground_spawns = 0, spawn_groups = 0, spawn_group_associations = 0, spawn_group_chances = 0;
  2809. LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns");
  2810. npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
  2811. objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
  2812. widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
  2813. signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
  2814. ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
  2815. spawn_groups = LoadSpawnLocationGroups(zone);
  2816. spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
  2817. spawn_group_chances = LoadSpawnGroupChances(zone);
  2818. LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s)", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances);
  2819. LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit LoadSpawns");
  2820. }
  2821. bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) {
  2822. Query query;
  2823. query.RunQuery2(Q_UPDATE, "update spawn_location_placement set x=%f, y=%f, z=%f, heading=%f, x_offset=%f, y_offset=%f, z_offset=%f, respawn=%u, expire_timer=%u, expire_offset=%u, grid_id=%u, pitch=%f, roll=%f where id = %u",
  2824. spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading(), spawn->GetXOffset(), spawn->GetYOffset(), spawn->GetZOffset(), spawn->GetRespawnTime(), spawn->GetExpireTime(), spawn->GetExpireOffsetTime(), spawn->appearance.pos.grid_id, spawn->GetPitch(), spawn->GetRoll(), spawn->GetSpawnLocationPlacementID());
  2825. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  2826. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnLocationSpawns query '%s': %s", query.GetQuery(), query.GetError());
  2827. return false;
  2828. }
  2829. return true;
  2830. }
  2831. bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString) {
  2832. Query query;
  2833. query.RunQuery2(Q_UPDATE, "update spawn_widgets set %s where widget_id = %u",
  2834. queryString, widget_id);
  2835. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  2836. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnWidget query '%s': %s", query.GetQuery(), query.GetError());
  2837. return false;
  2838. }
  2839. return true;
  2840. }
  2841. vector<string>* WorldDatabase::GetSpawnNameList(const char* in_name){
  2842. Query query;
  2843. string names = "";
  2844. vector<string>* ret = 0;
  2845. string name = getSafeEscapeString(in_name);
  2846. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT concat(spawn.id, ', ', name) FROM spawn where name like '%%%s%%'", name.c_str());
  2847. if(result && mysql_num_rows(result) > 0){
  2848. ret = new vector<string>;
  2849. MYSQL_ROW row;
  2850. int8 num = 0;
  2851. while(result && (row = mysql_fetch_row(result))){
  2852. if(num >= 10)
  2853. break;
  2854. ret->push_back(string(row[0]));
  2855. num++;
  2856. }
  2857. char total[60] = {0};
  2858. if(mysql_num_rows(result) > 10)
  2859. sprintf(total, "Total number of results: %u (Limited to 10)", (int32)mysql_num_rows(result));
  2860. else
  2861. sprintf(total, "Total number of results: %u", (int32)mysql_num_rows(result));
  2862. ret->push_back(string(total));
  2863. }
  2864. return ret;
  2865. }
  2866. string WorldDatabase::GetZoneName(char* zone_description){
  2867. string ret;
  2868. Query query;
  2869. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where description = '%s'", getSafeEscapeString(zone_description).c_str());
  2870. if(result && mysql_num_rows(result) > 0){
  2871. MYSQL_ROW row = mysql_fetch_row(result);
  2872. ret = string(row[0]);
  2873. }
  2874. return ret;
  2875. }
  2876. void WorldDatabase::LoadRevivePoints(vector<RevivePoint*>* revive_points, int32 zone_id){
  2877. if(revive_points && revive_points->size() > 0){
  2878. LogWrite(WORLD__ERROR, 0, "World", "Revive points have already been loaded for this zone!");
  2879. return;
  2880. }
  2881. else if(!revive_points || zone_id == 0){
  2882. LogWrite(WORLD__ERROR, 0, "World", "LoadRevivePoints called with null variables!");
  2883. return;
  2884. }
  2885. Query query;
  2886. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT respawn_zone_id, location_name, safe_x, safe_y, safe_z, heading FROM revive_points where zone_id=%u ORDER BY id asc", zone_id);
  2887. if(revive_points && result && mysql_num_rows(result) > 0){
  2888. MYSQL_ROW row;
  2889. int32 id = 0;
  2890. RevivePoint* point = 0;
  2891. while(result && (row=mysql_fetch_row(result))){
  2892. point = new RevivePoint;
  2893. point->id = id;
  2894. point->zone_id = atoul(row[0]);
  2895. point->location_name = string(row[1]);
  2896. point->x = atof(row[2]);
  2897. point->y = atof(row[3]);
  2898. point->z = atof(row[4]);
  2899. point->heading = atof(row[5]);
  2900. revive_points->push_back(point);
  2901. id++;
  2902. }
  2903. }
  2904. }
  2905. int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id)
  2906. {
  2907. Query query;
  2908. int32 ret = 0;
  2909. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn where id LIKE '%i____'", zone_id);
  2910. if(result && mysql_num_rows(result) > 0) {
  2911. MYSQL_ROW row;
  2912. row = mysql_fetch_row(result);
  2913. if(row[0])
  2914. ret = atoi(row[0]) + 1;
  2915. }
  2916. if( ret == 0 )
  2917. ret = zone_id * 10000; // there are no spawns for that zone yet, to start with the first ID
  2918. LogWrite(WORLD__DEBUG, 0, "World", "Next Spawn ID for Zone %i: %u", zone_id, ret);
  2919. return ret;
  2920. }
  2921. bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
  2922. Query query;
  2923. string name = getSafeEscapeString(spawn->GetName());
  2924. string suffix = getSafeEscapeString(spawn->GetSuffixTitle());
  2925. string prefix = getSafeEscapeString(spawn->GetPrefixTitle());
  2926. string last_name = getSafeEscapeString(spawn->GetLastName());
  2927. if(spawn->GetDatabaseID() == 0){
  2928. int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
  2929. int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID());
  2930. query.RunQuery2(Q_INSERT, "insert into spawn (id, name, race, model_type, size, targetable, show_name, command_primary, command_secondary, visual_state, attackable, show_level, show_command_icon, display_hand_icon, faction_id, collision_radius, hp, power, prefix, suffix, last_name, is_instanced_spawn, merchant_min_level, merchant_max_level) values(%u, '%s', %i, %i, %i, %i, %i, %u, %u, %i, %i, %i, %i, %i, %u, %i, %u, %u, '%s', '%s', '%s', %u, %u, %u)",
  2931. new_spawn_id, name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->GetSize(), spawn->appearance.targetable, spawn->appearance.display_name, spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, 0, spawn->appearance.pos.collision_radius, spawn->GetTotalHP(), spawn->GetTotalPower(), prefix.c_str(), suffix.c_str(), last_name.c_str(), isInstanceType, spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel());
  2932. if( new_spawn_id > 0 )
  2933. spawn->SetDatabaseID(new_spawn_id); // use the new zone_id range
  2934. else if( query.GetLastInsertedID() > 0 )
  2935. spawn->SetDatabaseID(query.GetLastInsertedID()); // else fall back to last_inserted_id
  2936. else
  2937. return false; // else, hang your head in shame as you are an utter failure
  2938. if(spawn->IsNPC()){
  2939. query.RunQuery2(Q_INSERT, "insert into spawn_npcs (spawn_id, min_level, max_level, enc_level, class_, gender, min_group_size, max_group_size, hair_type_id, facial_hair_type_id, wing_type_id, chest_type_id, legs_type_id, soga_hair_type_id, soga_facial_hair_type_id, soga_model_type, heroic_flag, action_state, mood_state, initial_state, activity_status, hide_hood, emote_state) values(%u, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)",
  2940. spawn->GetDatabaseID(), spawn->GetLevel(), spawn->GetLevel(), spawn->appearance.encounter_level, spawn->GetAdventureClass(), spawn->GetGender(), 0, 0, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type,
  2941. ((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->appearance.heroic_flag, spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(), spawn->GetActivityStatus(), spawn->appearance.hide_hood, spawn->appearance.emote_state);
  2942. }
  2943. else if(spawn->IsObject()){
  2944. query.RunQuery2(Q_INSERT, "insert into spawn_objects (spawn_id) values(%u)", spawn->GetDatabaseID());
  2945. }
  2946. else if(spawn->IsWidget()){
  2947. Widget* widget = (Widget*)spawn;
  2948. query.RunQuery2(Q_INSERT, "insert into spawn_widgets (spawn_id, widget_id) values(%u, %u)", spawn->GetDatabaseID(), widget->GetWidgetID());
  2949. }
  2950. else if(spawn->IsSign()){
  2951. query.RunQuery2(Q_INSERT, "insert into spawn_signs (spawn_id, description) values(%u, 'change me')", spawn->GetDatabaseID());
  2952. }
  2953. else if (spawn->IsGroundSpawn()) {
  2954. query.RunQuery2(Q_INSERT, "insert into spawn_ground (spawn_id) values(%u)", spawn->GetDatabaseID());
  2955. }
  2956. }
  2957. else{
  2958. if(spawn->IsNPC()){
  2959. query.RunQuery2(Q_UPDATE, "update spawn_npcs, spawn set name='%s', min_level=%i, max_level=%i, enc_level=%i, race=%i, model_type=%i, class_=%i, gender=%i, show_name=%i, attackable=%i, show_level=%i, targetable=%i, show_command_icon=%i, display_hand_icon=%i, hair_type_id=%i, facial_hair_type_id=%i, wing_type_id=%i, chest_type_id=%i, legs_type_id=%i, soga_hair_type_id=%i, soga_facial_hair_type_id=%i, soga_model_type=%i, size=%i, hp=%u, heroic_flag=%i, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, action_state=%i, mood_state=%i, initial_state=%i, activity_status=%i, alignment=%i, faction_id=%u, hide_hood=%i, emote_state=%i, suffix ='%s', prefix='%s', last_name='%s', merchant_min_level = %u, merchant_max_level = %u where spawn_npcs.spawn_id = spawn.id and spawn.id = %u",
  2960. name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->appearance.encounter_level, spawn->GetRace(), spawn->GetModelType(),
  2961. spawn->GetAdventureClass(), spawn->GetGender(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.targetable, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, ((NPC*)spawn)->features.hair_type,
  2962. ((NPC*)spawn)->features.hair_face_type, ((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->GetSize(),
  2963. spawn->GetTotalHP(), spawn->appearance.heroic_flag, spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(),
  2964. spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(),
  2965. spawn->GetActivityStatus(), ((NPC*)spawn)->GetAlignment(), spawn->GetFactionID(), spawn->appearance.hide_hood, spawn->appearance.emote_state,
  2966. suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  2967. }
  2968. else if(spawn->IsObject()){
  2969. query.RunQuery2(Q_UPDATE, "update spawn_objects, spawn set name='%s', model_type=%i, show_name=%i, targetable=%i, size=%i, command_primary=%u, command_secondary=%u, visual_state=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, collision_radius=%i, hp = %u, power = %u, device_id = %i, merchant_min_level = %u, merchant_max_level = %u where spawn_objects.spawn_id = spawn.id and spawn.id = %u",
  2970. name.c_str(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.targetable, spawn->GetSize(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon,
  2971. spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  2972. }
  2973. else if(spawn->IsWidget()){
  2974. Widget* widget = (Widget*)spawn;
  2975. char* openSound = 0;
  2976. char* closeSound = 0;
  2977. if (widget->GetOpenSound() != NULL) openSound = (char*)widget->GetOpenSound(); else openSound = (char*)string("0").c_str();
  2978. if (widget->GetCloseSound() != NULL) closeSound = (char*)widget->GetCloseSound(); else closeSound = (char*)string("0").c_str();
  2979. query.RunQuery2(Q_UPDATE, "update spawn_widgets, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s',widget_id = %u,widget_x = %f,widget_y = %f,widget_z = %f,include_heading = %u,include_location = %u,icon = %u,type='%s',open_heading = %f,closed_heading = %f,open_x = %f,open_y = %f,open_z = %f,action_spawn_id = %u,open_sound_file='%s',close_sound_file='%s',open_duration = %u,close_x = %f,close_y=%f,close_z=%f,linked_spawn_id = %u,house_id = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_widgets.spawn_id = spawn.id and spawn.id = %u",
  2980. name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
  2981. spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
  2982. suffix.c_str(), prefix.c_str(), last_name.c_str(), widget->GetWidgetID(), widget->GetX(), widget->GetY(), widget->GetZ(), widget->GetIncludeHeading(), widget->GetIncludeLocation(), widget->GetIconValue(), Widget::GetWidgetTypeNameByTypeID(widget->GetWidgetType()).c_str(),
  2983. widget->GetOpenHeading(), widget->GetClosedHeading(), widget->GetOpenX(), widget->GetOpenY(), widget->GetOpenZ(),
  2984. widget->GetActionSpawnID(), openSound, closeSound,widget->GetOpenDuration(),
  2985. widget->GetCloseX(),widget->GetCloseY(),widget->GetCloseZ(),widget->GetLinkedSpawnID(),widget->GetHouseID(),
  2986. spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  2987. }
  2988. else if(spawn->IsSign()){
  2989. Sign* sign = (Sign*)spawn;
  2990. query.RunQuery2(Q_UPDATE, "update spawn_signs, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', type='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u",
  2991. name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
  2992. spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
  2993. suffix.c_str(), prefix.c_str(), last_name.c_str(), sign->GetSignType(), sign->GetSignZoneID(),
  2994. sign->GetWidgetID(), sign->GetSignTitle(), sign->GetWidgetX(), sign->GetWidgetY(), sign->GetWidgetZ(),
  2995. sign->GetIconValue(), sign->GetSignDescription(), sign->GetSignDistance(), sign->GetSignZoneX(),
  2996. sign->GetSignZoneY(), sign->GetSignZoneZ(), sign->GetSignZoneHeading(), sign->GetIncludeHeading(),
  2997. sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  2998. }
  2999. }
  3000. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3001. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnInfo query '%s': %s", query.GetQuery(), query.GetError());
  3002. return false;
  3003. }
  3004. return true;
  3005. }
  3006. int32 WorldDatabase::SaveCombinedSpawnLocation(ZoneServer* zone, Spawn* in_spawn, const char* name){
  3007. vector<Spawn*>* spawns = in_spawn->GetSpawnGroup();
  3008. uint32 spawnLocationID = 0;
  3009. if(spawns && spawns->size() > 0){
  3010. vector<Spawn*>::iterator itr;
  3011. map<Spawn*, int32>::iterator freq_itr;
  3012. Spawn* spawn = 0;
  3013. map<Spawn*, int32> database_spawns;
  3014. int32 total = 0;
  3015. float x_offset = GetSpawnLocationPlacementOffsetX(in_spawn->GetSpawnLocationID());
  3016. float y_offset = GetSpawnLocationPlacementOffsetY(in_spawn->GetSpawnLocationID());
  3017. float z_offset = GetSpawnLocationPlacementOffsetZ(in_spawn->GetSpawnLocationID());
  3018. int32 spawn_location_id = GetNextSpawnLocation();
  3019. spawnLocationID = spawn_location_id;
  3020. if(!name)
  3021. name = "Combine SpawnGroup Generated";
  3022. if(!CreateNewSpawnLocation(spawn_location_id, name)){
  3023. safe_delete(spawns);
  3024. return 0;
  3025. }
  3026. for(itr = spawns->begin();itr!=spawns->end();itr++){
  3027. spawn = *itr;
  3028. if (spawn) {
  3029. RemoveSpawnFromSpawnLocation(spawn);
  3030. spawn->SetSpawnLocationID(spawn_location_id);
  3031. bool add = true;
  3032. for (freq_itr = database_spawns.begin(); freq_itr != database_spawns.end(); freq_itr++) {
  3033. if (spawn->GetDatabaseID() == freq_itr->first->GetDatabaseID()) {
  3034. freq_itr->second++;
  3035. total++;
  3036. add = false;
  3037. }
  3038. }
  3039. if (add) {
  3040. database_spawns[spawn] = 1;
  3041. total++;
  3042. }
  3043. }
  3044. }
  3045. for(freq_itr = database_spawns.begin(); freq_itr != database_spawns.end(); freq_itr++){
  3046. int8 percent = (freq_itr->second*100)/total;
  3047. if(!SaveSpawnEntry(freq_itr->first, name, percent, x_offset, y_offset, z_offset, freq_itr->first == in_spawn, false)){
  3048. safe_delete(spawns);
  3049. return 0;
  3050. }
  3051. }
  3052. for(itr=spawns->begin();itr!=spawns->end();itr++){
  3053. spawn = *itr;
  3054. zone->RemoveSpawn(spawn);
  3055. }
  3056. safe_delete(spawns);
  3057. }
  3058. else{
  3059. safe_delete(spawns);
  3060. return 0;
  3061. }
  3062. return spawnLocationID;
  3063. }
  3064. bool WorldDatabase::SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name, int8 percent, float x_offset, float y_offset, float z_offset, bool save_zonespawn, bool create_spawnlocation){
  3065. Query query;
  3066. Query query2;
  3067. int32 count = 0;
  3068. if(create_spawnlocation){
  3069. count = GetSpawnLocationCount(spawn->GetSpawnLocationID());
  3070. if(count == 0){
  3071. if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name))
  3072. return false;
  3073. }
  3074. }
  3075. query.RunQuery2(Q_INSERT, "insert into spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent);
  3076. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3077. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query.GetQuery(), query.GetError());
  3078. return false;
  3079. }
  3080. if(save_zonespawn){
  3081. query2.RunQuery2(Q_INSERT, "insert into spawn_location_placement (zone_id, instance_id, spawn_location_id, x, y, z, x_offset, y_offset, z_offset, heading, grid_id) values(%u, %u, %u, %f, %f, %f, %f, %f, %f, %f, %u)", spawn->GetZone()->GetZoneID(), spawn->GetZone()->GetInstanceID(), spawn->GetSpawnLocationID(), spawn->GetX(), spawn->GetY(), spawn->GetZ(),x_offset, y_offset, z_offset, spawn->GetHeading(), spawn->appearance.pos.grid_id);
  3082. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3083. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query2.GetQuery(), query2.GetError());
  3084. return false;
  3085. }
  3086. spawn->SetSpawnLocationPlacementID(query2.GetLastInsertedID());
  3087. }
  3088. return true;
  3089. }
  3090. float WorldDatabase::GetSpawnLocationPlacementOffsetX(int32 location_id) {
  3091. Query query;
  3092. MYSQL_ROW row;
  3093. float ret = 0;
  3094. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `x_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3095. if (result && (row = mysql_fetch_row(result))) {
  3096. if (row[0])
  3097. ret = atof(row[0]);
  3098. }
  3099. return ret;
  3100. }
  3101. float WorldDatabase::GetSpawnLocationPlacementOffsetY(int32 location_id) {
  3102. Query query;
  3103. MYSQL_ROW row;
  3104. float ret = 0;
  3105. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `y_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3106. if (result && (row = mysql_fetch_row(result))) {
  3107. if (row[0])
  3108. ret = atof(row[0]);
  3109. }
  3110. return ret;
  3111. }
  3112. float WorldDatabase::GetSpawnLocationPlacementOffsetZ(int32 location_id) {
  3113. Query query;
  3114. MYSQL_ROW row;
  3115. float ret = 0;
  3116. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `z_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3117. if (result && (row = mysql_fetch_row(result))) {
  3118. if (row[0])
  3119. ret = atof(row[0]);
  3120. }
  3121. return ret;
  3122. }
  3123. bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name){
  3124. Query query;
  3125. if(!name)
  3126. name = "Unknown Spawn Location Name";
  3127. string str_name = getSafeEscapeString(name);
  3128. query.RunQuery2(Q_INSERT, "insert into spawn_location_name (id, name) values(%u, '%s')", id, str_name.c_str());
  3129. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3130. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in CreateNewSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
  3131. return false;
  3132. }
  3133. return true;
  3134. }
  3135. int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){
  3136. Query query;
  3137. int32 ret = 0;
  3138. MYSQL_RES* result = 0;
  3139. if(spawn)
  3140. result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u and spawn_id=%u", location, spawn->GetDatabaseID());
  3141. else
  3142. result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u", location);
  3143. if(result && mysql_num_rows(result) > 0){
  3144. MYSQL_ROW row;
  3145. while(result && (row = mysql_fetch_row(result)) && row[0]){
  3146. ret = strtoul(row[0], NULL, 0);
  3147. }
  3148. }
  3149. return ret;
  3150. }
  3151. int32 WorldDatabase::GetNextSpawnLocation(){
  3152. Query query;
  3153. int32 ret = 0;
  3154. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name");
  3155. if(result && mysql_num_rows(result) > 0){
  3156. MYSQL_ROW row;
  3157. while(result && (row = mysql_fetch_row(result)) && row[0]){
  3158. ret = strtoul(row[0], NULL, 0);
  3159. }
  3160. }
  3161. ret++;
  3162. return ret;
  3163. }
  3164. bool WorldDatabase::RemoveSpawnFromSpawnLocation(Spawn* spawn){
  3165. Query query;
  3166. Query query2;
  3167. int32 count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn);
  3168. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement where spawn_location_id=%u", spawn->GetSpawnLocationID());
  3169. if(count == 1)
  3170. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name where id=%u", spawn->GetSpawnLocationID());
  3171. query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry where spawn_id=%u and spawn_location_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID());
  3172. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3173. LogWrite(WORLD__ERROR, 0, "World", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
  3174. return false;
  3175. }
  3176. else if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3177. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query2.GetQuery(), query.GetError());
  3178. return false;
  3179. }
  3180. return true;
  3181. }
  3182. map<int32, string>* WorldDatabase::GetZoneList(const char* name, bool is_admin)
  3183. {
  3184. Query query;
  3185. map<int32, string>* ret = 0;
  3186. string zone_name = getSafeEscapeString(name);
  3187. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `name` FROM zones WHERE `name` RLIKE '%s' %s", zone_name.c_str(), (is_admin)?"":" LIMIT 0,10");
  3188. if(result && mysql_num_rows(result) > 0)
  3189. {
  3190. ret = new map<int32, string>;
  3191. MYSQL_ROW row;
  3192. while(result && (row = mysql_fetch_row(result)))
  3193. {
  3194. zone_name = row[1];
  3195. (*ret)[atoi(row[0])] = zone_name;
  3196. }
  3197. }
  3198. return ret;
  3199. }
  3200. void WorldDatabase::UpdateStartingFactions(int32 char_id, int8 choice){
  3201. Query query;
  3202. query.RunQuery2(Q_INSERT, "insert into character_factions (char_id, faction_id, faction_level) select %u, faction_id, value FROM starting_factions where starting_city=%i", char_id, choice);
  3203. }
  3204. string WorldDatabase::GetStartingZoneName(int8 choice){
  3205. Query query;
  3206. string zone_name = "";
  3207. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where start_zone = %u", choice);
  3208. if(result && mysql_num_rows(result) > 0){
  3209. MYSQL_ROW row;
  3210. while(result && (row = mysql_fetch_row(result))){
  3211. zone_name = string(row[0]);
  3212. }
  3213. }
  3214. return zone_name;
  3215. }
  3216. void WorldDatabase::UpdateStartingZone(int32 char_id, int8 class_id, int8 race_id, PacketStruct* create)
  3217. {
  3218. Query query,query2;
  3219. int32 packetVersion = create->GetVersion();
  3220. int8 choice = create->getType_int8_ByName("starting_zone"); // 0 = far journey, 1 = isle of refuge
  3221. int8 deity = create->getType_int8_ByName("deity"); // aka 'alignment' for early DOF, 0 = evil, 1 = good
  3222. int32 startingZoneRuleFlag = rule_manager.GetGlobalRule(R_World, StartingZoneRuleFlag)->GetInt32();
  3223. if((startingZoneRuleFlag == 1 || startingZoneRuleFlag == 2) && packetVersion > 546)
  3224. {
  3225. LogWrite(PLAYER__INFO, 0, "Player", "Starting zone rule flag %u override choice %u to deity value of 0", startingZoneRuleFlag, choice);
  3226. choice = 0;
  3227. }
  3228. LogWrite(PLAYER__INFO, 0, "Player", "Adding default zone for race: %i, class: %i for char_id: %u (choice: %i), deity(alignment): %u, version: %u.", race_id, class_id, char_id, choice, deity, packetVersion);
  3229. // first, check to see if there is a starting_zones record for this race/class/choice combo (now using extended Archetype/BaseClass/Class combos
  3230. MYSQL_RES* result = 0;
  3231. string whereRuleFlag("");
  3232. if(startingZoneRuleFlag > 0)
  3233. whereRuleFlag = string(" AND ruleflag & " + std::to_string(startingZoneRuleFlag));
  3234. string syntaxSelect("SELECT z.name, sz.zone_id, z.safe_x, z.safe_y, z.safe_z, sz.x, sz.y, sz.z, sz.heading, sz.is_instance, z.city_zone FROM");
  3235. if ( class_id == 0 )
  3236. result = query.RunQuery2(Q_SELECT, "%s starting_zones sz, zones z WHERE sz.zone_id = z.id AND class_id = 255 AND race_id IN (%i, 255) AND deity IN (%i, 255) AND choice = %u AND (min_version = 0 or min_version <= %u) AND (max_version = 0 or max_version >= %u)%s",
  3237. syntaxSelect.c_str(), race_id, deity, choice, packetVersion, packetVersion, whereRuleFlag.c_str());
  3238. else
  3239. result = query.RunQuery2(Q_SELECT, "%s starting_zones sz, zones z WHERE sz.zone_id = z.id AND class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255) AND deity IN (%i, 255) AND choice IN (%i, 255) AND (min_version = 0 or min_version <= %u) AND (max_version = 0 or max_version >= %u)%s",
  3240. syntaxSelect.c_str(), classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id, deity, choice, packetVersion, packetVersion, whereRuleFlag.c_str());
  3241. // TODO: verify client version so clients do not crash trying to enter zones they do not own (paks)
  3242. if(result && mysql_num_rows(result) > 0)
  3243. {
  3244. string zone_name = "ERROR";
  3245. MYSQL_ROW row;
  3246. bool zoneSet = false;
  3247. float safeX = 0.0f, safeY = 0.0f, safeZ = 0.0f, x = 0.0f, y = 0.0f, z = 0.0f, heading = 0.0f;
  3248. int8 is_instance = 0;
  3249. int32 zone_id = 0;
  3250. int32 instance_id = 0;
  3251. int8 starting_city = 0;
  3252. if( result && (row = mysql_fetch_row(result)) )
  3253. {
  3254. int8 i=0;
  3255. zoneSet = true;
  3256. zone_name = string(row[i++]);
  3257. zone_id = atoul(row[i++]);
  3258. safeX = atof(row[i++]);
  3259. safeY = atof(row[i++]);
  3260. safeZ = atof(row[i++]);
  3261. x = atof(row[i++]);
  3262. y = atof(row[i++]);
  3263. z = atof(row[i++]);
  3264. if ( x == -999999.0f && y == -999999.0f && z == -999999.0f)
  3265. {
  3266. x = safeX;
  3267. y = safeY;
  3268. z = safeZ;
  3269. }
  3270. heading = atof(row[i++]);
  3271. if(heading == -999999.0f )
  3272. heading = 0.0f;
  3273. is_instance = atoul(row[i++]);
  3274. starting_city = atoul(row[i++]);
  3275. }
  3276. if(is_instance) // should only be true if we get a result
  3277. {
  3278. // this will force a pre-load
  3279. ZoneServer* instance_zone = zone_list.GetByInstanceID(0, zone_id);
  3280. if (instance_zone) {
  3281. instance_id = CreateNewInstance(zone_id);
  3282. AddCharacterInstance(char_id, instance_id, string(instance_zone->GetZoneName()), instance_zone->GetInstanceType(), Timer::GetUnixTimeStamp(), 0, instance_zone->GetDefaultLockoutTime(), instance_zone->GetDefaultReenterTime());
  3283. // make sure we inherit the instance id setup in the AddCharacterInstance
  3284. instance_zone->SetupInstance(instance_id);
  3285. }
  3286. }
  3287. query2.RunQuery2(Q_UPDATE, "UPDATE characters SET current_zone_id = %u, x = %f, y = %f, z = %f, heading = %f, starting_city = %i, instance_id = %u WHERE id = %u",
  3288. zone_id, x, y, z, heading, starting_city, instance_id, char_id);
  3289. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3290. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone custom starting_zones, query: '%s': %s", query2.GetQuery(), query2.GetError());
  3291. return;
  3292. }
  3293. if(query2.GetAffectedRows() > 0)
  3294. {
  3295. LogWrite(PLAYER__INFO, 0, "Player", "Setting New Character Starting Zone to '%s' with location %f, %f, %f and heading %f FROM starting_zones table.", zone_name.c_str(), x, y, z, heading);
  3296. return;
  3297. }
  3298. }
  3299. else
  3300. {
  3301. // there was no matching starting_zone value, so use default 'choice' starting city
  3302. query2.RunQuery2(Q_UPDATE, "UPDATE characters c, zones z SET c.current_zone_id = z.id, c.x = z.safe_x, c.y = z.safe_y, c.z = z.safe_z, c.starting_city = %i WHERE z.start_zone = %i and c.id = %u",
  3303. choice, choice, char_id);
  3304. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF)
  3305. {
  3306. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone player choice, query: '%s': %s", query2.GetQuery(), query2.GetError());
  3307. return;
  3308. }
  3309. if(query2.GetAffectedRows() > 0)
  3310. {
  3311. LogWrite(PLAYER__DEBUG, 0, "Player", "Setting New Character Starting Zone to '%s' FROM player choice.", GetStartingZoneName(choice).c_str());
  3312. return;
  3313. }
  3314. }
  3315. // if we are here, it's a bad thing. zone tables have no start_city values to match client 'choice', so throw the player into zone according to R_World::DefaultStartingZoneID rule.
  3316. // shout a few warnings so the admin fixes this asap!
  3317. int16 default_zone_id = rule_manager.GetGlobalRule(R_World, DefaultStartingZoneID)->GetInt16();
  3318. LogWrite(WORLD__WARNING, 0, "World", "No Starting City defined for player choice: %i! BAD! BAD! BAD! Defaulting player to zone %i.", choice, default_zone_id);
  3319. query.RunQuery2(Q_UPDATE, "UPDATE characters c, zones z SET c.current_zone_id = z.id, c.x = z.safe_x, c.y = z.safe_y, c.z = z.safe_z, c.heading = z.safe_heading, c.starting_city = 1 WHERE z.id = %i and c.id = %u", default_zone_id, char_id);
  3320. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3321. {
  3322. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone default zone %i, query: '%s': %s", default_zone_id, query.GetQuery(), query.GetError());
  3323. return;
  3324. }
  3325. if (query.GetAffectedRows() > 0) {
  3326. string zone_name = GetZoneName(1);
  3327. if(zone_name.length() > 0)
  3328. LogWrite(PLAYER__DEBUG, 0, "Player", "Setting New Character Starting Zone to '%s' due to no valid options!", zone_name.c_str());
  3329. else
  3330. LogWrite(PLAYER__DEBUG, 0, "Player", "Unable to set New Character Starting Zone due to no valid options!");
  3331. }
  3332. return;
  3333. }
  3334. void WorldDatabase::UpdateStartingItems(int32 char_id, int8 class_id, int8 race_id, bool base_class){
  3335. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default items for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3336. Query query;
  3337. Query query2;
  3338. vector<Item*> items;
  3339. vector<Item*> bags;
  3340. map<int32, int8> total_slots;
  3341. map<int32, int8> slots_left;
  3342. map<int8, bool> equip_slots;
  3343. map<Item*, StartingItem> item_list;
  3344. int32 item_id = 0;
  3345. Item* item = 0;
  3346. StartingItem* starting_item = 0;
  3347. //first get a list of the starting items for the character
  3348. MYSQL_RES* result = 0;
  3349. /*if(!base_class)
  3350. result = query2.RunQuery2(Q_SELECT, "SELECT `type`, item_id, creator, condition_, attuned, count FROM starting_items where (class_id=%i and race_id=%i) or (class_id=%i and race_id=255) or (class_id=255 and race_id=%i) or (class_id=255 and race_id=255) ORDER BY id", class_id, race_id, class_id, race_id);
  3351. else
  3352. result = query2.RunQuery2(Q_SELECT, "SELECT `type`, item_id, creator, condition_, attuned, count FROM starting_items where (class_id=%i and race_id=%i) or (class_id=%i and race_id=255) ORDER BY id", class_id, race_id, class_id);*/
  3353. result = query2.RunQuery2(Q_SELECT, "SELECT `type`, item_id, creator, condition_, attuned, count FROM starting_items WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255) ORDER BY id", classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3354. if(result && mysql_num_rows(result) > 0){
  3355. MYSQL_ROW row;
  3356. while(result && (row = mysql_fetch_row(result))){
  3357. item_id = atoul(row[1]);
  3358. item = master_item_list.GetItem(item_id);
  3359. if(item){
  3360. starting_item = &(item_list[item]);
  3361. starting_item->type = (row[0]) ? string(row[0]) : "";
  3362. starting_item->item_id = atoul(row[1]);
  3363. starting_item->creator = (row[2]) ? string(row[2]) : "";
  3364. starting_item->condition = atoi(row[3]);
  3365. starting_item->attuned = atoi(row[4]);
  3366. starting_item->count = atoi(row[5]);
  3367. item = master_item_list.GetItem(starting_item->item_id);
  3368. if(item){
  3369. if(bags.size() < NUM_INV_SLOTS && item->IsBag() && item->details.num_slots > 0)
  3370. bags.push_back(item);
  3371. else
  3372. items.push_back(item);
  3373. }
  3374. }
  3375. }
  3376. }
  3377. slots_left[0] = NUM_INV_SLOTS;
  3378. //next create the bags in the inventory
  3379. for(int8 i=0;i<bags.size();i++){
  3380. item = bags[i];
  3381. query.RunQuery2(Q_INSERT, "insert into character_items (char_id, type, slot, item_id, creator, condition_, attuned, bag_id, count) values(%u, '%s', %i, %u, '%s', %i, %i, %u, %i)",
  3382. char_id, item_list[item].type.c_str(), i, item_list[item].item_id, getSafeEscapeString(item_list[item].creator.c_str()).c_str(), item_list[item].condition, item_list[item].attuned, 0, item_list[item].count);
  3383. slots_left[query.GetLastInsertedID()] = item->details.num_slots;
  3384. total_slots[query.GetLastInsertedID()] = item->details.num_slots;
  3385. slots_left[0]--;
  3386. }
  3387. map<int32, int8>::iterator itr;
  3388. int32 inv_slot = 0;
  3389. int8 slot = 0;
  3390. //finally process the rest of the items, placing them in the first available slot
  3391. for(int32 x=0;x<items.size();x++){
  3392. item = items[x];
  3393. if(item_list[item].type.find("NOT") < 0xFFFFFFFF){ // NOT-EQUIPPED Items
  3394. for(itr = slots_left.begin(); itr != slots_left.end(); itr++){
  3395. if(itr->second > 0){
  3396. if(itr->first == 0 && slots_left.size() > 1) //we want items to go into bags first, then inventory after bags are full
  3397. continue;
  3398. inv_slot = itr->first;
  3399. slot = total_slots[itr->first] - itr->second;
  3400. itr->second--;
  3401. if(itr->second == 0)
  3402. slots_left.erase(itr);
  3403. break;
  3404. }
  3405. }
  3406. query.RunQuery2(Q_INSERT, "insert into character_items (char_id, type, slot, item_id, creator, condition_, attuned, bag_id, count) values(%u, '%s', %i, %u, '%s', %i, %i, %u, %i)",
  3407. char_id, item_list[item].type.c_str(), slot, item_list[item].item_id, getSafeEscapeString(item_list[item].creator.c_str()).c_str(), item_list[item].condition, item_list[item].attuned, inv_slot, item_list[item].count);
  3408. }
  3409. else{ //EQUIPPED Items
  3410. for(int8 i=0;i<item->slot_data.size();i++){
  3411. if(equip_slots.count(item->slot_data[i]) == 0){
  3412. equip_slots[item->slot_data[i]] = true;
  3413. query.RunQuery2(Q_INSERT, "insert into character_items (char_id, type, slot, item_id, creator, condition_, attuned, bag_id, count) values(%u, '%s', %i, %u, '%s', %i, %i, %u, %i)",
  3414. char_id, item_list[item].type.c_str(), item->slot_data[i], item_list[item].item_id, getSafeEscapeString(item_list[item].creator.c_str()).c_str(), item_list[item].condition, item_list[item].attuned, 0, item_list[item].count);
  3415. break;
  3416. }
  3417. }
  3418. }
  3419. }
  3420. }
  3421. void WorldDatabase::UpdateStartingSkills(int32 char_id, int8 class_id, int8 race_id)
  3422. {
  3423. Query query;
  3424. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default skills for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3425. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_skills (char_id, skill_id, current_val, max_val) SELECT %u, skill_id, current_val, max_val FROM starting_skills WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255)",
  3426. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3427. }
  3428. void WorldDatabase::UpdateStartingSpells(int32 char_id, int8 class_id, int8 race_id){
  3429. Query query;
  3430. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default spells for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3431. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_spells (char_id, spell_id, tier, knowledge_slot) SELECT %u, spell_id, tier, knowledge_slot FROM starting_spells WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255)",
  3432. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3433. }
  3434. void WorldDatabase::UpdateStartingSkillbar(int32 char_id, int8 class_id, int8 race_id){
  3435. Query query;
  3436. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default skillbar for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3437. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_skillbar (char_id, type, hotbar, spell_id, slot, text_val) SELECT %u, type, hotbar, spell_id, slot, text_val FROM starting_skillbar WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255)",
  3438. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3439. }
  3440. void WorldDatabase::UpdateStartingTitles(int32 char_id, int8 class_id, int8 race_id, int8 gender_id) {
  3441. Query query;
  3442. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default titles for race: %i, class: %i, gender: %i for char_id: %u", race_id, class_id, gender_id, char_id);
  3443. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_titles (char_id, title_id) SELECT %u, title_id FROM starting_titles WHERE class_id IN (%i, %i, %i, 255) AND race_id IN (%i, 255) and gender_id IN (%i, 255)",
  3444. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id, gender_id);
  3445. }
  3446. string WorldDatabase::GetZoneDescription(int32 id){
  3447. Query query;
  3448. string ret = "";
  3449. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT description FROM zones where id = %u", id);
  3450. if(result && mysql_num_rows(result) > 0){
  3451. MYSQL_ROW row;
  3452. row = mysql_fetch_row(result);
  3453. ret = string(row[0]);
  3454. }
  3455. return ret;
  3456. }
  3457. string WorldDatabase::GetZoneName(int32 id){
  3458. if (zone_names.count(id) > 0){
  3459. return zone_names[id];
  3460. }
  3461. Query query;
  3462. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name` FROM zones where `id` = %u", id);
  3463. if(result && mysql_num_rows(result) > 0){
  3464. MYSQL_ROW row;
  3465. row = mysql_fetch_row(result);
  3466. zone_names[id] = row[0];
  3467. return zone_names[id];
  3468. }
  3469. return string("");
  3470. }
  3471. bool WorldDatabase::VerifyZone(const char* name){
  3472. Query query;
  3473. char* escaped = getEscapeString(name);
  3474. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where name='%s'",escaped);
  3475. safe_delete_array(escaped);
  3476. if(result && mysql_num_rows(result) > 0)
  3477. return true;
  3478. else
  3479. return false;
  3480. }
  3481. int8 WorldDatabase::GetInstanceTypeByZoneID(int32 zoneID)
  3482. {
  3483. DatabaseResult result;
  3484. int8 ret = 0;
  3485. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Getting instances for zone_id %u", zoneID);
  3486. if( !database_new.Select(&result, "SELECT instance_type+0 FROM zones WHERE id = %u", zoneID) )
  3487. {
  3488. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceTypeByZoneID() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  3489. return ret;
  3490. }
  3491. if( result.GetNumRows() > 0 )
  3492. {
  3493. result.Next();
  3494. ret = (result.GetInt8Str("instance_type+0") == 0) ? 0 : result.GetInt8Str("instance_type+0") - 1;
  3495. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Found instance type %i for zone_id %u", ret, zoneID);
  3496. }
  3497. else
  3498. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No instances found for zone_id %u", zoneID);
  3499. return ret;
  3500. }
  3501. void WorldDatabase::Save(Client* client){
  3502. Query query;
  3503. Player* player = client->GetPlayer();
  3504. if(!player->CheckPlayerInfo())
  3505. return;
  3506. int32 instance_id = 0;
  3507. if ( client->GetCurrentZone ( ) != NULL )
  3508. instance_id = client->GetCurrentZone()->GetInstanceID();
  3509. int32 zone_id = 0;
  3510. if(client->GetCurrentZone())
  3511. zone_id = client->GetCurrentZone()->GetZoneID();
  3512. query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0, client->GetCharacterID());
  3513. query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
  3514. player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),
  3515. player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(),
  3516. player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), player->GetStatusPoints(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(),
  3517. client->GetPlayer()->GetCombatVoice(), client->GetPlayer()->GetEmoteVoice(), getSafeEscapeString(client->GetPlayer()->GetBiography().c_str()).c_str(), player->GetFlags(), player->GetFlags2(), client->GetPlayer()->GetLastName(),
  3518. client->GetPlayer()->GetAssignedAA(), client->GetPlayer()->GetUnassignedAA(), client->GetPlayer()->GetTradeskillAA(), client->GetPlayer()->GetUnassignedTradeskillAA(), client->GetPlayer()->GetPrestigeAA(),
  3519. client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetCharacterID());
  3520. map<string, int8>::iterator itr;
  3521. map<string, int8>* friends = player->GetFriends();
  3522. if(friends && friends->size() > 0){
  3523. for(itr = friends->begin(); itr != friends->end(); itr++){
  3524. if(itr->second == 1){
  3525. query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'FRIEND')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str());
  3526. itr->second = 0;
  3527. }
  3528. else if(itr->second == 2){
  3529. query.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str());
  3530. itr->second = 3;
  3531. }
  3532. }
  3533. }
  3534. map<string, int8>* ignored = player->GetIgnoredPlayers();
  3535. if(ignored && ignored->size() > 0){
  3536. for(itr = ignored->begin(); itr != ignored->end(); itr++){
  3537. if(itr->second == 1){
  3538. query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'IGNORE')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str());
  3539. itr->second = 0;
  3540. }
  3541. else if(itr->second == 2){
  3542. query.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str());
  3543. itr->second = 3;
  3544. }
  3545. }
  3546. }
  3547. SavePlayerFactions(client);
  3548. SaveCharacterQuests(client);
  3549. SaveCharacterSkills(client);
  3550. SavePlayerSpells(client);
  3551. SavePlayerMail(client);
  3552. SavePlayerCollections(client);
  3553. LogWrite(PLAYER__INFO, 3, "Player", "Player '%s' (%u) data saved.", player->GetName(), player->GetCharacterID());
  3554. }
  3555. void WorldDatabase::LoadEntityCommands(ZoneServer* zone) {
  3556. int32 total = 0;
  3557. int32 id = 0;
  3558. EntityCommand* command = 0;
  3559. DatabaseResult result;
  3560. if (!database_new.Select(&result, "SELECT `command_list_id`, `command_text`, `distance`, `command`, `error_text`, `cast_time`, `spell_visual` FROM `entity_commands` ORDER BY `id`")) {
  3561. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  3562. return;
  3563. }
  3564. while (result.Next()) {
  3565. command = new EntityCommand;
  3566. id = result.GetInt32(0);
  3567. command->name = result.GetString(1);
  3568. command->distance = result.GetFloat(2);
  3569. command->command = result.GetString(3);
  3570. command->error_text = result.GetString(4);
  3571. command->cast_time = result.GetInt16(5);
  3572. command->spell_visual = result.GetInt32(6);
  3573. command->default_allow_list = true;
  3574. zone->SetEntityCommandList(id, command);
  3575. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Command: '%s' (%s)", command->name.c_str(), command->command.c_str());
  3576. total++;
  3577. }
  3578. LogWrite(COMMAND__DEBUG, 0, "Command", "--Loaded %i entity command(s)", total);
  3579. }
  3580. void WorldDatabase::LoadFactionAlliances()
  3581. {
  3582. LogWrite(FACTION__DEBUG, 1, "Faction", "-Loading faction alliances...");
  3583. int32 total = 0;
  3584. int32 fTotal = 0;
  3585. int32 hTotal = 0;
  3586. Query query;
  3587. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT faction_id, friend_faction, hostile_faction FROM faction_alliances");
  3588. if(result && mysql_num_rows(result) > 0)
  3589. {
  3590. MYSQL_ROW row;
  3591. int32 faction_id = 0;
  3592. int32 friendly_id = 0;
  3593. int32 hostile_id = 0;
  3594. while(result && (row = mysql_fetch_row(result)))
  3595. {
  3596. faction_id = atoul(row[0]);
  3597. friendly_id = atoul(row[1]);
  3598. hostile_id = atoul(row[2]);
  3599. if(friendly_id > 0)
  3600. {
  3601. master_faction_list.AddFriendlyFaction(faction_id, friendly_id);
  3602. fTotal++;
  3603. LogWrite(FACTION__DEBUG, 5, "Faction", "---Faction %i is friendly towards %i", faction_id, friendly_id);
  3604. }
  3605. if(hostile_id > 0)
  3606. {
  3607. master_faction_list.AddHostileFaction(faction_id, hostile_id);
  3608. hTotal++;
  3609. LogWrite(FACTION__DEBUG, 5, "Faction", "---Faction %i is hostile towards %i", faction_id, hostile_id);
  3610. }
  3611. total++;
  3612. }
  3613. }
  3614. LogWrite(FACTION__DEBUG, 3, "Faction", "--Loaded %u Alliances: %i friendly, %i hostile", total, fTotal, hTotal);
  3615. }
  3616. bool WorldDatabase::UpdateSpawnScriptData(int32 spawn_id, int32 spawn_location_id, int32 spawnentry_id, const char* name){
  3617. bool ret = false;
  3618. if((spawn_id > 0 || spawn_location_id > 0 || spawnentry_id > 0) && name){
  3619. Query query;
  3620. int32 row_id = 0;
  3621. if(spawn_id > 0){
  3622. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawn_id=%u", spawn_id);
  3623. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawn_id, lua_script) values(%u, '%s')", spawn_id, getSafeEscapeString(name).c_str());
  3624. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3625. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3626. else{
  3627. row_id = query.GetLastInsertedID();
  3628. if(row_id > 0)
  3629. world.AddSpawnScript(row_id, name);
  3630. ret = true;
  3631. }
  3632. }
  3633. else if(spawn_location_id > 0){
  3634. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawn_location_id=%u", spawn_location_id);
  3635. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawn_location_id, lua_script) values(%u, '%s')", spawn_location_id, getSafeEscapeString(name).c_str());
  3636. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3637. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3638. else{
  3639. row_id = query.GetLastInsertedID();
  3640. if(row_id > 0)
  3641. world.AddSpawnLocationScript(row_id, name);
  3642. ret = true;
  3643. }
  3644. }
  3645. else if(spawnentry_id > 0){
  3646. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawnentry_id=%u", spawnentry_id);
  3647. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawnentry_id, lua_script) values(%u, '%s')", spawnentry_id, getSafeEscapeString(name).c_str());
  3648. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3649. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3650. else{
  3651. row_id = query.GetLastInsertedID();
  3652. if(row_id > 0)
  3653. world.AddSpawnEntryScript(row_id, name);
  3654. ret = true;
  3655. }
  3656. }
  3657. }
  3658. return ret;
  3659. }
  3660. void WorldDatabase::LoadSpawnScriptData() {
  3661. int32 total = 0;
  3662. Query query;
  3663. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spawn_id, spawnentry_id, spawn_location_id, lua_script FROM spawn_scripts");
  3664. if(result && mysql_num_rows(result) > 0)
  3665. {
  3666. MYSQL_ROW row;
  3667. int32 spawn_id = 0;
  3668. int32 spawnentry_id = 0;
  3669. int32 spawn_location_id = 0;
  3670. while(result && (row = mysql_fetch_row(result)))
  3671. {
  3672. spawn_id = atoul(row[0]);
  3673. spawnentry_id = atoul(row[1]);
  3674. spawn_location_id = atoul(row[2]);
  3675. string spawn_script = string(row[3]);
  3676. if(spawnentry_id > 0)
  3677. {
  3678. world.AddSpawnEntryScript(spawnentry_id, row[3]);
  3679. total++;
  3680. }
  3681. else if(spawn_location_id > 0)
  3682. {
  3683. world.AddSpawnLocationScript(spawn_location_id, row[3]);
  3684. total++;
  3685. }
  3686. else if(spawn_id > 0)
  3687. {
  3688. world.AddSpawnScript(spawn_id, row[3]);
  3689. total++;
  3690. }
  3691. else {
  3692. if(row[3])
  3693. LogWrite(LUA__ERROR, 0, "LUA", "Invalid Entry in spawn_scripts table for lua_script '%s' (spawn_id, spawnentry_id and spawn_location_id are all 0)", row[3]);
  3694. else
  3695. LogWrite(LUA__ERROR, 0, "LUA", "Invalid Entry in spawn_scripts table.");
  3696. }
  3697. spawn_id = 0;
  3698. spawnentry_id = 0;
  3699. spawn_location_id = 0;
  3700. }
  3701. }
  3702. LogWrite(LUA__DEBUG, 0, "LUA", "\tLoaded %u SpawnScript%s", total, total == 1 ? "" : "s");
  3703. }
  3704. void WorldDatabase::LoadZoneScriptData() {
  3705. Query query;
  3706. int32 total = 0;
  3707. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `lua_script` FROM `zones`");
  3708. if (result && mysql_num_rows(result) > 0) {
  3709. MYSQL_ROW row;
  3710. while(result && (row = mysql_fetch_row(result))) {
  3711. if (row[1]) {
  3712. int32 zone_id = atoul(row[0]);
  3713. string zone_script = string(row[1]);
  3714. if (zone_id > 0 && zone_script.length() > 0) {
  3715. LogWrite(LUA__DEBUG, 5, "LUA", "ZoneScript: %s loaded.", zone_script.c_str());
  3716. world.AddZoneScript(zone_id, zone_script.c_str());
  3717. total++;
  3718. }
  3719. else if (zone_id > 0) {
  3720. string tmpScript;
  3721. tmpScript.append("ZoneScripts/");
  3722. string zonename = GetZoneName(zone_id);
  3723. tmpScript.append(zonename);
  3724. tmpScript.append(".lua");
  3725. struct stat buffer;
  3726. bool fileExists = (stat(tmpScript.c_str(), &buffer) == 0);
  3727. if (fileExists)
  3728. {
  3729. LogWrite(LUA__INFO, 0, "LUA", "No zonescript file described in the database, overriding with ZoneScript %s for Zone ID %u", (char*)tmpScript.c_str(), zone_id);
  3730. world.AddZoneScript(zone_id, tmpScript.c_str());
  3731. }
  3732. }
  3733. }
  3734. }
  3735. }
  3736. LogWrite(LUA__DEBUG, 0, "LUA", "\tLoaded %u ZoneScript%s", total, total == 1 ? "" : "s");
  3737. }
  3738. int32 WorldDatabase::LoadSpellScriptData() {
  3739. Query query;
  3740. MYSQL_ROW row;
  3741. int32 count;
  3742. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `lua_script` FROM `spells`"); // WHERE is_active = 1
  3743. while (result && (row = mysql_fetch_row(result))) {
  3744. if (row[0] && strlen(row[0]) > 0) {
  3745. if (lua_interface->LoadLuaSpell(row[0]))
  3746. LogWrite(SPELL__DEBUG, 5, "Spells", "SpellScript: %s loaded.", row[0]);
  3747. }
  3748. }
  3749. count = mysql_num_rows(result);
  3750. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %i SpellScript%s", count, count == 1 ? "" : "s");
  3751. return count;
  3752. }
  3753. void WorldDatabase::LoadFactionList() {
  3754. int32 total = 0;
  3755. Query query;
  3756. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, type, description, default_level, negative_change, positive_change FROM factions");
  3757. if(result && mysql_num_rows(result) > 0) {
  3758. MYSQL_ROW row;
  3759. Faction* faction = 0;
  3760. while(result && (row = mysql_fetch_row(result))){
  3761. faction = new Faction;
  3762. faction->id = atoul(row[0]);
  3763. faction->name = string(row[1]);
  3764. faction->type = string(row[2]);
  3765. faction->description = string(row[3]);
  3766. faction->default_value = atoi(row[4]);
  3767. faction->negative_change = atoi(row[5]);
  3768. faction->positive_change = atoi(row[6]);
  3769. master_faction_list.AddFaction(faction);
  3770. total++;
  3771. LogWrite(FACTION__DEBUG, 5, "Faction", "---Loading Faction '%s' (%u)", faction->name.c_str(), faction->id);
  3772. }
  3773. }
  3774. LogWrite(FACTION__DEBUG, 3, "Faction", "--Loaded %u Faction%s", total, total == 1 ? "" : "s");
  3775. LoadFactionAlliances();
  3776. }
  3777. void WorldDatabase::SavePlayerFactions(Client* client){
  3778. LogWrite(PLAYER__DEBUG, 3, "Player", "Saving Player Factions...");
  3779. Query query;
  3780. map<int32, sint32>* factions = client->GetPlayer()->GetFactions()->GetFactionValues();
  3781. map<int32, sint32>::iterator itr;
  3782. for(itr = factions->begin(); itr != factions->end(); itr++)
  3783. query.AddQueryAsync(client->GetCharacterID(), this,Q_INSERT, "insert into character_factions (char_id, faction_id, faction_level) values(%u, %u, %i) ON DUPLICATE KEY UPDATE faction_level=%i", client->GetCharacterID(), itr->first, itr->second, itr->second);
  3784. }
  3785. bool WorldDatabase::LoadPlayerFactions(Client* client) {
  3786. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Player Factions...");
  3787. Query query;
  3788. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT faction_id, faction_level FROM character_factions where char_id=%i", client->GetCharacterID());
  3789. if(result && mysql_num_rows(result) > 0) {
  3790. MYSQL_ROW row;
  3791. while(result && (row = mysql_fetch_row(result))){
  3792. client->GetPlayer()->GetFactions()->SetFactionValue(atoul(row[0]), atol(row[1]));
  3793. }
  3794. }
  3795. if(query.GetErrorNumber())
  3796. return false;
  3797. return true;
  3798. }
  3799. void WorldDatabase::SavePlayerMail(Mail* mail) {
  3800. Query query_update;
  3801. Query query_insert;
  3802. if (mail) {
  3803. if(mail->mail_id > 0)
  3804. query_update.RunQuery2(Q_UPDATE, "UPDATE `character_mail` SET `already_read`=%u, `coin_copper`=%u, `coin_silver`=%u, `coin_gold`=%u, `coin_plat`=%u, `char_item_id`=%u, `stack`=%u WHERE `id`=%u", mail->already_read, mail->coin_copper, mail->coin_silver, mail->coin_gold, mail->coin_plat, mail->char_item_id, mail->stack, mail->mail_id);
  3805. else if (mail->mail_id == 0 || query_update.GetAffectedRows() == 0)
  3806. {
  3807. query_insert.RunQuery2(Q_INSERT, "INSERT INTO `character_mail` (`player_to_id`, `player_from`, `subject`, `mail_body`, `already_read`, `mail_type`, `coin_copper`, `coin_silver`, `coin_gold`, `coin_plat`, `stack`, `postage_cost`, `attachment_cost`, `char_item_id`, `time_sent`, `expire_time`) VALUES (%u, '%s', '%s', '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", mail->player_to_id, mail->player_from.c_str(), getSafeEscapeString(mail->subject.c_str()).c_str(), getSafeEscapeString(mail->mail_body.c_str()).c_str(), mail->already_read, mail->mail_type, mail->coin_copper, mail->coin_silver, mail->coin_gold, mail->coin_plat, mail->stack, mail->postage_cost, mail->attachment_cost, mail->char_item_id, mail->time_sent, mail->expire_time);
  3808. if(!mail->mail_id)
  3809. mail->mail_id = query_insert.GetLastInsertedID();
  3810. }
  3811. mail->save_needed = false;
  3812. }
  3813. }
  3814. void WorldDatabase::SavePlayerMail(Client* client) {
  3815. if (client) {
  3816. MutexMap<int32, Mail*>* mail_list = client->GetPlayer()->GetMail();
  3817. MutexMap<int32, Mail*>::iterator itr = mail_list->begin();
  3818. while (itr.Next()) {
  3819. Mail* mail = itr->second;
  3820. if (mail->save_needed)
  3821. SavePlayerMail(mail);
  3822. }
  3823. }
  3824. }
  3825. void WorldDatabase::LoadPlayerMail(Client* client, bool new_only) {
  3826. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Player Mail...");
  3827. if (client) {
  3828. Query query;
  3829. MYSQL_RES* result;
  3830. if (new_only)
  3831. result = query.RunQuery2(Q_SELECT, "SELECT `id`, `player_to_id`, `player_from`, `subject`, `mail_body`, `already_read`, `mail_type`, `coin_copper`, `coin_silver`, `coin_gold`, `coin_plat`, `stack`, `postage_cost`, `attachment_cost`, `char_item_id`, `time_sent`, `expire_time` FROM `character_mail` WHERE `player_to_id`=%u AND `unread`=1", client->GetCharacterID());
  3832. else
  3833. result = query.RunQuery2(Q_SELECT, "SELECT `id`, `player_to_id`, `player_from`, `subject`, `mail_body`, `already_read`, `mail_type`, `coin_copper`, `coin_silver`, `coin_gold`, `coin_plat`, `stack`, `postage_cost`, `attachment_cost`, `char_item_id`, `time_sent`, `expire_time` FROM `character_mail` WHERE `player_to_id`=%u", client->GetCharacterID());
  3834. if (result && mysql_num_rows(result) > 0) {
  3835. MYSQL_ROW row;
  3836. bool hasMail = false;
  3837. while (result && (row = mysql_fetch_row(result))) {
  3838. int32 time_sent = atoul(row[15]);
  3839. if ( time_sent > Timer::GetUnixTimeStamp() )
  3840. continue; // should have not been received yet
  3841. hasMail = true;
  3842. Mail* mail = new Mail;
  3843. mail->mail_id = atoul(row[0]);
  3844. mail->player_to_id = atoul(row[1]);
  3845. mail->player_from = string(row[2]);
  3846. mail->subject = string(row[3]);
  3847. if (row[4])
  3848. mail->mail_body = string(row[4]);
  3849. mail->already_read = atoi(row[5]);
  3850. mail->mail_type = atoi(row[6]);
  3851. mail->coin_copper = atoul(row[7]);
  3852. mail->coin_silver = atoul(row[8]);
  3853. mail->coin_gold = atoul(row[9]);
  3854. mail->coin_plat = atoul(row[10]);
  3855. mail->stack = atoi(row[11]);
  3856. mail->postage_cost = atoul(row[12]);
  3857. mail->attachment_cost = atoul(row[13]);
  3858. mail->char_item_id = atoul(row[14]);
  3859. mail->time_sent = time_sent;
  3860. mail->expire_time = atoul(row[16]);
  3861. mail->save_needed = false;
  3862. client->GetPlayer()->AddMail(mail);
  3863. LogWrite(PLAYER__DEBUG, 5, "Player", "Loaded Mail ID %i, to: %i, from: %s", atoul(row[0]), atoul(row[1]), string(row[2]).c_str());
  3864. }
  3865. if(hasMail)
  3866. client->SimpleMessage(CHANNEL_NARRATIVE, "You've got mail! :)");
  3867. }
  3868. }
  3869. }
  3870. void WorldDatabase::DeletePlayerMail(Mail* mail) {
  3871. Query query;
  3872. if (mail)
  3873. query.RunQuery2(Q_DELETE, "DELETE FROM `character_mail` WHERE `id`=%u", mail->mail_id);
  3874. LogWrite(PLAYER__DEBUG, 0, "Player", "Delete Player Mail...");
  3875. }
  3876. vector<int32>* WorldDatabase::GetAllPlayerIDs() {
  3877. Query query;
  3878. vector<int32>* ids = 0;
  3879. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM `characters`");
  3880. MYSQL_ROW row;
  3881. while (result && (row = mysql_fetch_row(result))) {
  3882. if (ids == 0)
  3883. ids = new vector<int32>;
  3884. ids->push_back(atoul(row[0]));
  3885. }
  3886. return ids;
  3887. }
  3888. void WorldDatabase::GetPetNames(ZoneServer* zone)
  3889. {
  3890. DatabaseResult result;
  3891. int32 total = 0;
  3892. if( database_new.Select(&result, "SELECT pet_name FROM spawn_pet_names") )
  3893. {
  3894. while(result.Next())
  3895. {
  3896. zone->pet_names.push_back(result.GetStringStr("pet_name"));
  3897. total++;
  3898. LogWrite(PET__DEBUG, 5, "Pet", "---Loading Pet Name: '%s'", result.GetStringStr("pet_name"));
  3899. }
  3900. LogWrite(PET__DEBUG, 0, "Pet", "--Loaded %u Pet Names", total);
  3901. }
  3902. }
  3903. int32 WorldDatabase::CheckTableVersions(char* tablename) {
  3904. Query query;
  3905. char* escaped_name = getEscapeString(tablename);
  3906. LogWrite(INIT__PATCHER_DEBUG, 1, "Patcher", "\tChecking current version for table: %s", tablename);
  3907. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT version FROM table_versions WHERE name='%s';", escaped_name);
  3908. int32 ret_version = 0;
  3909. if(result && mysql_num_rows(result) > 0)
  3910. {
  3911. MYSQL_ROW row;
  3912. row = mysql_fetch_row(result);
  3913. ret_version = atoul(row[0]);
  3914. }
  3915. safe_delete_array(escaped_name);
  3916. return ret_version;
  3917. }
  3918. bool WorldDatabase::RunDatabaseQueries(TableQuery* queries, bool output_result, bool data){
  3919. for(int16 i=0;i<queries->num_queries;i++){
  3920. Query query;
  3921. if(string(queries->GetQuery(i)).length() > 5)
  3922. query.RunQuery2(string(queries->GetQuery(i)), Q_UPDATE);
  3923. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3924. {
  3925. if(output_result)
  3926. LogWrite(INIT__PATCHER_ERROR, 0, "Patcher", "FAILED!");
  3927. LogWrite(INIT__PATCHER_ERROR, 0, "Patcher", "Error in updating tables query '%s': %s", query.GetQuery(), query.GetError());
  3928. return false;
  3929. }
  3930. }
  3931. if(output_result)
  3932. LogWrite(INIT__PATCHER_DEBUG, 0, "Patcher", "SUCCESS!");
  3933. if(data)
  3934. UpdateDataTableVersion(queries->tablename, queries->latest_version);
  3935. else
  3936. UpdateTableVersion(queries->tablename, queries->latest_version);
  3937. return true;
  3938. }
  3939. void WorldDatabase::UpdateDataTableVersion(char* name, int32 version){
  3940. Query query;
  3941. char* escaped_name = getEscapeString(name);
  3942. query.RunQuery2(Q_UPDATE, "update table_versions set download_version=%u where name='%s'", version, escaped_name);
  3943. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3944. {
  3945. LogWrite(INIT__PATCHER_ERROR, 0, "Patcher", "Error in updating version table query '%s': %s", query.GetQuery(), query.GetError());
  3946. }
  3947. safe_delete_array(escaped_name);
  3948. }
  3949. void WorldDatabase::UpdateTableVersion(char* name, int32 version){
  3950. Query query;
  3951. char* escaped_name = getEscapeString(name);
  3952. query.RunQuery2(Q_UPDATE, "INSERT into table_versions (name, version) values('%s', %u) ON DUPLICATE KEY UPDATE version=%u", escaped_name, version, version);
  3953. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3954. {
  3955. LogWrite(WORLD__ERROR, 0, "Patcher", "Error in updating version table query '%s': %s", query.GetQuery(), query.GetError());
  3956. }
  3957. safe_delete_array(escaped_name);
  3958. }
  3959. bool WorldDatabase::CheckVersionTable() {
  3960. Query query;
  3961. // todo: suppress SQL errors while this command is running, because ERROR is normal on a new DB creation (Zcoretri)
  3962. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SHOW COLUMNS FROM table_versions");
  3963. if(result && mysql_num_rows(result) > 0) {
  3964. LogWrite(INIT__PATCHER_DEBUG, 0, "Patcher", "--DB Schema exists! Checking for updates...");
  3965. return true;
  3966. }
  3967. else {
  3968. LogWrite(INIT__PATCHER_ERROR, 0, "Patcher", "Version Table NOT Found! Creating...");
  3969. Query query2;
  3970. query2.RunQuery2(Q_UPDATE, "CREATE TABLE `table_versions` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) NOT NULL DEFAULT '',`version` INT(10) UNSIGNED NOT NULL DEFAULT '0',`download_version` INT(10) UNSIGNED NOT NULL DEFAULT '0',PRIMARY KEY (`id`),UNIQUE KEY `UniqueName` (`name`)) ENGINE=INNODB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;");
  3971. LogWrite(INIT__PATCHER_DEBUG, 0, "Patcher", "--Setting table_version = 1...");
  3972. Query query3;
  3973. query3.RunQuery2(Q_INSERT, "INSERT INTO table_versions (name, version) VALUES ('table_versions', 1)");
  3974. }
  3975. return false;
  3976. }
  3977. sint32 WorldDatabase::GetLatestDataTableVersion(char* name){
  3978. Query query;
  3979. char* escaped_name = getEscapeString(name);
  3980. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT download_version FROM table_versions where name='%s'", escaped_name);
  3981. sint32 ret_version = 0;
  3982. if(result && mysql_num_rows(result) > 0) {
  3983. MYSQL_ROW row;
  3984. row = mysql_fetch_row(result);
  3985. ret_version = atol(row[0]);
  3986. }
  3987. safe_delete_array(escaped_name);
  3988. return ret_version;
  3989. }
  3990. int32 WorldDatabase::GetMaxHotBarID(){
  3991. Query query;
  3992. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM character_skillbar");
  3993. int32 ret = 0;
  3994. if(result && mysql_num_rows(result) > 0) {
  3995. MYSQL_ROW row;
  3996. row = mysql_fetch_row(result);
  3997. if(row && row[0])
  3998. ret = strtoul(row[0], NULL, 0);
  3999. }
  4000. return ret;
  4001. }
  4002. void WorldDatabase::SaveQuickBar(int32 char_id, vector<QuickBarItem*>* quickbar_items){
  4003. vector<QuickBarItem*>::iterator itr;
  4004. QuickBarItem* qbi = 0;
  4005. for(itr = quickbar_items->begin(); itr != quickbar_items->end(); itr++){
  4006. qbi = *itr;
  4007. if(!qbi)
  4008. continue;
  4009. if(qbi->deleted == false){
  4010. Query query;
  4011. if(qbi->text.size > 0){
  4012. query.AddQueryAsync(char_id, this, Q_REPLACE, "replace into character_skillbar (id, hotbar, slot, char_id, spell_id, type, text_val, tier) values(%u, %u, %u, %u, %u, %i, '%s', %i)",
  4013. qbi->unique_id, qbi->hotbar, qbi->slot, char_id, qbi->id, qbi->type, getSafeEscapeString(qbi->text.data.c_str()).c_str(), qbi->tier);
  4014. }
  4015. else{
  4016. query.AddQueryAsync(char_id, this, Q_REPLACE, "replace into character_skillbar (id, hotbar, slot, char_id, spell_id, type, text_val, tier) values(%u, %u, %u, %u, %u, %i, 'Unused', %i)",
  4017. qbi->unique_id, qbi->hotbar, qbi->slot, char_id, qbi->id, qbi->type, qbi->tier);
  4018. }
  4019. }
  4020. else{
  4021. Query query;
  4022. query.AddQueryAsync(char_id, this, Q_DELETE, "delete FROM character_skillbar where hotbar=%u and slot=%u and char_id=%u", qbi->hotbar, qbi->slot, char_id);
  4023. }
  4024. }
  4025. }
  4026. map<int32, vector<LevelArray*> >* WorldDatabase::LoadSpellClasses(){
  4027. map<int32, vector<LevelArray*> >* ret = new map<int32, vector<LevelArray*> >();
  4028. Query query;
  4029. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_id, adventure_class_id, tradeskill_class_id, level FROM spell_classes");
  4030. MYSQL_ROW row;
  4031. LevelArray* level = 0;
  4032. while(result && (row = mysql_fetch_row(result))){
  4033. level = new LevelArray();
  4034. level->adventure_class = atoi(row[1]);
  4035. level->tradeskill_class = atoi(row[2]);
  4036. level->spell_level = atoi(row[3]);
  4037. (*ret)[atoul(row[0])].push_back(level);
  4038. }
  4039. return ret;
  4040. }
  4041. void WorldDatabase::LoadTraits(){
  4042. Query query;
  4043. MYSQL_ROW row;
  4044. TraitData* trait;
  4045. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`, `level`, `class_req`, `race_req`, `isTrait`,`isInate`, `isFocusEffect`, `isTraining`,`tier`, `group` FROM spell_traits");
  4046. while (result && (row = mysql_fetch_row(result))){
  4047. trait = new TraitData;
  4048. int8 i = 0;
  4049. trait->spellID = strtoul(row[0], NULL, 0);
  4050. trait->level = atoi(row[(++i)]);
  4051. trait->classReq = atoi(row[(++i)]);
  4052. trait->raceReq = atoi(row[(++i)]);
  4053. trait->isTrait = (atoi(row[(++i)]) == 0) ? false : true;
  4054. trait->isInate = (atoi(row[(++i)]) == 0) ? false : true;
  4055. trait->isFocusEffect = (atoi(row[(++i)]) == 0) ? false : true;
  4056. trait->isTraining = (atoi(row[(++i)]) == 0) ? false : true;
  4057. trait->tier = atoi(row[(++i)]);
  4058. trait->group = atoi(row[(++i)]);
  4059. master_trait_list.AddTrait(trait);
  4060. }
  4061. LogWrite(SPELL__INFO, 0, "Traits", "Loaded %u Trait(s)", master_trait_list.Size());
  4062. }
  4063. void WorldDatabase::LoadSpells()
  4064. {
  4065. DatabaseResult result;
  4066. Spell *spell;
  4067. SpellData *data;
  4068. int32 t_now = Timer::GetUnixTimeStamp();
  4069. int32 total = 0;
  4070. map<int32, vector<LevelArray*> >* level_data = LoadSpellClasses();
  4071. if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `min_class_skill_req`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc', type_group_spell_id, can_fizzle "
  4072. "FROM (spells s, spell_tiers st) "
  4073. "LEFT JOIN spell_ts_ability_index ts "
  4074. "ON s.`id` = ts.spell_id "
  4075. "WHERE s.id = st.spell_id AND s.is_active = 1 "
  4076. "ORDER BY s.`id`, `tier`") )
  4077. {
  4078. // error
  4079. }
  4080. else
  4081. {
  4082. while( result.Next() )
  4083. {
  4084. data = new SpellData;
  4085. int32 spell_id = result.GetInt32Str("id");
  4086. string spell_name = result.GetStringStr("name");
  4087. /* General Spell info */
  4088. data->id = spell_id;
  4089. data->soe_spell_crc = result.GetInt32Str("soe_spell_crc");
  4090. data->tier = result.GetInt8Str("tier");
  4091. data->ts_loc_index = result.GetInt8Str("index");
  4092. data->name.data = spell_name.c_str();
  4093. data->name.size = data->name.data.length();
  4094. data->description.data = result.GetStringStr("description");
  4095. data->description.size = data->description.data.length();
  4096. data->icon = result.GetSInt16Str("icon");
  4097. data->icon_heroic_op = result.GetInt16Str("icon_heroic_op");
  4098. data->icon_backdrop = result.GetInt16Str("icon_backdrop");
  4099. data->spell_visual = result.GetInt32Str("spell_visual");
  4100. data->type = result.GetInt16Str("type");
  4101. data->target_type = result.GetInt8Str("target_type");
  4102. data->cast_type = result.GetInt8Str("cast_type");
  4103. data->spell_book_type = result.GetInt32Str("spell_book_type");
  4104. data->det_type = result.GetInt8Str("det_type");
  4105. data->incurable = (result.GetInt8Str("incurable") == 1);
  4106. data->control_effect_type = result.GetInt8Str("control_effect_type");
  4107. data->casting_flags = result.GetInt32Str("casting_flags");
  4108. data->savage_bar = result.GetInt8Str("savage_bar");
  4109. data->savage_bar_slot = result.GetInt8Str("savage_bar_slot");
  4110. data->spell_type = result.IsNullStr("spell_type+0") ? 0 : result.GetInt8Str("spell_type+0");
  4111. /* Toggles */
  4112. data->interruptable = ( result.GetInt8Str("interruptable") == 1);
  4113. data->duration_until_cancel = ( result.GetInt8Str("duration_until_cancel") == 1);
  4114. data->can_effect_raid = result.GetInt8Str("can_effect_raid");
  4115. data->affect_only_group_members = result.GetInt8Str("affect_only_group_members");
  4116. data->display_spell_tier = result.GetInt8Str("display_spell_tier");
  4117. data->friendly_spell = result.GetInt8Str("friendly_spell");
  4118. data->group_spell = result.GetInt8Str("group_spell");
  4119. data->is_active = result.GetInt8Str("is_active");
  4120. data->persist_though_death = ( result.GetInt8Str("persist_through_death") == 1);
  4121. data->cast_while_moving = ( result.GetInt8Str("cast_while_moving") == 1);
  4122. data->not_maintained = ( result.GetInt8Str("not_maintained") == 1);
  4123. data->is_aa = (result.GetInt8Str("is_aa") == 1);
  4124. /* Skill Requirements */
  4125. data->class_skill = result.GetInt32Str("class_skill");
  4126. data->min_class_skill_req = result.GetInt16Str("min_class_skill_req");
  4127. data->mastery_skill = result.GetInt32Str("mastery_skill");
  4128. /* Cost */
  4129. data->req_concentration = result.GetInt16Str("req_concentration");
  4130. data->hp_req = result.GetInt16Str("hp_req");
  4131. data->hp_upkeep = result.GetInt16Str("hp_upkeep");
  4132. data->hp_req_percent = result.GetInt8Str("hp_req_percent");
  4133. data->power_req = result.GetFloatStr("power_req");
  4134. data->power_by_level = ( result.GetInt8Str("power_by_level") == 0)? false : true;
  4135. data->power_upkeep = result.GetInt16Str("power_upkeep");
  4136. data->power_req_percent = result.GetInt8Str("power_req_percent");
  4137. data->savagery_req = result.GetInt16Str("savagery_req");
  4138. data->savagery_upkeep = result.GetInt16Str("savagery_upkeep");
  4139. data->savagery_req_percent = result.GetInt8Str("savagery_req_percent");
  4140. data->dissonance_req = result.GetInt16Str("dissonance_req");
  4141. data->dissonance_upkeep = result.GetInt16Str("dissonance_upkeep");
  4142. data->dissonance_req_percent = result.GetInt8Str("dissonance_req_percent");
  4143. /* Spell Parameters */
  4144. data->call_frequency = result.GetInt32Str("call_frequency");
  4145. data->cast_time = result.GetInt16Str("cast_time");
  4146. data->duration1 = result.GetInt32Str("duration1");
  4147. data->duration2 = result.GetInt32Str("duration2");
  4148. data->hit_bonus = result.GetFloatStr("hit_bonus");
  4149. data->max_aoe_targets = result.GetInt16Str("max_aoe_targets");
  4150. data->min_range = result.GetFloatStr("min_range");
  4151. data->radius = result.GetFloatStr("radius");
  4152. data->range = result.GetFloatStr("range");
  4153. data->recast = result.GetFloatStr("recast");
  4154. data->recovery = result.GetFloatStr("recovery");
  4155. data->resistibility = result.GetFloatStr("resistibility");
  4156. data->linked_timer = result.GetInt32Str("linked_timer_id");
  4157. data->spell_name_crc = result.GetInt32Str("spell_name_crc");
  4158. data->type_group_spell_id = result.GetSInt32Str("type_group_spell_id");
  4159. data->can_fizzle = ( result.GetInt8Str("can_fizzle") == 1);
  4160. /* Cast Messaging */
  4161. string message = result.GetStringStr("success_message");
  4162. if( message.length() > 0 )
  4163. data->success_message = message;
  4164. message = result.GetStringStr("fade_message");
  4165. if( message.length() > 0 )
  4166. data->fade_message = string(message);
  4167. message = result.GetStringStr("fade_message_others");
  4168. if (message.length() > 0)
  4169. data->fade_message_others = string(message);
  4170. message = result.GetStringStr("effect_message");
  4171. if( message.length() > 0 )
  4172. data->effect_message = string(message);
  4173. string lua_script = result.GetStringStr("lua_script");
  4174. if( lua_script.length() > 0 )
  4175. data->lua_script = string(lua_script);
  4176. /* Load spell level data */
  4177. spell = new Spell(data);
  4178. if(level_data && level_data->count(data->id) > 0)
  4179. {
  4180. vector<LevelArray*>* level_array = &((*level_data)[data->id]);
  4181. for(int8 i=0; i<level_array->size(); i++)
  4182. {
  4183. spell->AddSpellLevel(level_array->at(i)->adventure_class, level_array->at(i)->tradeskill_class, level_array->at(i)->spell_level*10);
  4184. }
  4185. }
  4186. /* Add spell to master list */
  4187. master_spell_list.AddSpell(data->id, data->tier, spell);
  4188. total++;
  4189. if( lua_script.length() > 0 )
  4190. LogWrite(SPELL__DEBUG, 5, "Spells", "\t%i. %s (Tier: %i) - '%s'", spell_id, spell_name.c_str(), data->tier, lua_script.c_str());
  4191. else if(data->is_active)
  4192. LogWrite(SPELL__WARNING, 1, "Spells", "\tSpell %s (%u, Tier: %i) set 'Active', but missing LUAScript", spell_name.c_str(), spell_id, data->tier);
  4193. } // end while
  4194. } // end else
  4195. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spell Effects...");
  4196. LoadSpellEffects();
  4197. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spell LUA Data...");
  4198. LoadSpellLuaData();
  4199. if(lua_interface)
  4200. {
  4201. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spells Scripts...");
  4202. LoadSpellScriptData();
  4203. }
  4204. if (level_data) {
  4205. map<int32, vector<LevelArray*> >::iterator map_itr;
  4206. vector<LevelArray*>::iterator level_itr;
  4207. for(map_itr = level_data->begin(); map_itr != level_data->end(); map_itr++)
  4208. {
  4209. for(level_itr = map_itr->second.begin(); level_itr != map_itr->second.end(); level_itr++)
  4210. {
  4211. safe_delete(*level_itr);
  4212. }
  4213. }
  4214. }
  4215. safe_delete(level_data);
  4216. LogWrite(SPELL__INFO, 0, "Spells", "Loaded %u Spell%s (took %u seconds)", total, total == 1 ? "" : "s", Timer::GetUnixTimeStamp() - t_now);
  4217. }
  4218. void WorldDatabase::LoadSpellLuaData(){
  4219. Spell *spell;
  4220. Query query;
  4221. MYSQL_ROW row;
  4222. int32 total = 0;
  4223. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`,`tier`,`value_type`,`value`,`value2`,`dynamic_helper` "
  4224. "FROM `spell_data` "
  4225. "ORDER BY `index_field`");
  4226. while (result && (row = mysql_fetch_row(result))) {
  4227. if ((spell = master_spell_list.GetSpell(atoul(row[0]), atoi(row[1]))) && row[2] && row[3] && row[4] && row[4]) {
  4228. LogWrite(SPELL__DEBUG, 5, "Spells", "\tLoading Spell LUA Data for spell_id: %u", atoul(row[0]));
  4229. if (!strcmp(row[2], "INT"))
  4230. spell->AddSpellLuaDataInt(atoi(row[3]), atoi(row[4]), string(row[5]));
  4231. else if (!strcmp(row[2], "FLOAT"))
  4232. spell->AddSpellLuaDataFloat(atof(row[3]), atof(row[4]),string(row[5]));
  4233. else if (!strcmp(row[2], "BOOL"))
  4234. spell->AddSpellLuaDataBool(!(strncasecmp(row[3], "true", 4)), string(row[5]));
  4235. else if (!strcmp(row[2], "STRING"))
  4236. spell->AddSpellLuaDataString(string(row[3]), string(row[4]), string(row[5]));
  4237. else
  4238. LogWrite(SPELL__ERROR, 0, "Spells", "Invalid Lua Spell data '%s' for Spell ID: %u", row[2], spell->GetSpellID());
  4239. total++;
  4240. }
  4241. }
  4242. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %i Spell LUA Data entr%s.", total, total == 1 ? "y" : "ies");
  4243. }
  4244. void WorldDatabase::LoadSpellEffects() {
  4245. Spell *spell;
  4246. Query query;
  4247. MYSQL_ROW row;
  4248. int32 total = 0;
  4249. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`,`tier`,`percentage`,`bullet`,`description` "
  4250. "FROM `spell_display_effects` "
  4251. "ORDER BY `spell_id`,`id` ASC");
  4252. while (result && (row = mysql_fetch_row(result))) {
  4253. if ((spell = master_spell_list.GetSpell(atoul(row[0]), atoi(row[1]))) && row[4]) {
  4254. LogWrite(SPELL__DEBUG, 5, "Spells", "\tLoading Spell Effects for spell_id: %u", atoul(row[0]));
  4255. spell->AddSpellEffect(atoi(row[2]), atoi(row[3]), row[4]);
  4256. total++;
  4257. }
  4258. }
  4259. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %u Spell Effect%s.", total, total == 1 ? "" : "s");
  4260. }
  4261. int32 WorldDatabase::LoadPlayerSkillbar(Client* client){
  4262. client->GetPlayer()->ClearQuickbarItems();
  4263. Query query;
  4264. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, type, spell_id, slot, text_val, hotbar, tier FROM character_skillbar where char_id = %u", client->GetCharacterID());
  4265. MYSQL_ROW row;
  4266. int32 count = 0;
  4267. while(result && (row = mysql_fetch_row(result))){
  4268. count++;
  4269. int8 tier = atoi(row[6]);
  4270. Spell* spell = master_spell_list.GetSpell(atoul(row[2]), tier);
  4271. if(spell)
  4272. client->GetPlayer()->AddQuickbarItem(atoul(row[5]), atoul(row[3]), atoul(row[1]), spell->GetSpellIcon(), spell->GetSpellIconBackdrop(), spell->GetSpellID(), spell->GetSpellTier(), atoul(row[0]), row[4], false);
  4273. else if(atoul(row[1]) == QUICKBAR_MACRO)
  4274. client->GetPlayer()->AddQuickbarItem(atoul(row[5]), atoul(row[3]), atoul(row[1]), client->GetPlayer()->macro_icons[atoul(row[2])], 0xFFFF, atoul(row[2]), 0, atoul(row[0]), row[4], false);
  4275. else
  4276. client->GetPlayer()->AddQuickbarItem(atoul(row[5]), atoul(row[3]), atoul(row[1]), 0, 0, atoul(row[2]), 0, atoul(row[0]), row[4], false);
  4277. }
  4278. return count;
  4279. }
  4280. bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
  4281. Guild *guild;
  4282. if((guild = guild_list.GetGuild(GetGuildIDByCharacterID(character_id))))
  4283. guild->RemoveGuildMember(character_id);
  4284. Query query;
  4285. query.RunQuery2(Q_DELETE, "DELETE FROM characters WHERE id=%u AND account_id=%u", character_id, account_id);
  4286. if(!query.GetAffectedRows())
  4287. {
  4288. //No error just in case ppl try doing stupid stuff
  4289. return false;
  4290. }
  4291. else{ //successfull, so the character did exist with that character_id and account_id
  4292. // new DB constraints should handle all these deletes, and more... commenting out for now
  4293. /*query.RunQuery2(Q_DELETE, "delete FROM character_details where char_id=%u", character_id);
  4294. query.RunQuery2(Q_DELETE, "delete FROM character_factions where char_id=%u", character_id);
  4295. query.RunQuery2(Q_DELETE, "delete FROM character_items where char_id=%u", character_id);
  4296. query.RunQuery2(Q_DELETE, "delete FROM character_skillbar where char_id=%u", character_id);
  4297. query.RunQuery2(Q_DELETE, "delete FROM character_skills where char_id=%u", character_id);
  4298. query.RunQuery2(Q_DELETE, "delete FROM character_spells where char_id=%u", character_id);
  4299. query.RunQuery2(Q_DELETE, "delete FROM char_colors where char_id=%u", character_id);*/
  4300. }
  4301. return true;
  4302. }
  4303. void WorldDatabase::DeleteCharacterSpell(int32 character_id, int32 spell_id) {
  4304. if (character_id > 0 && spell_id > 0) {
  4305. Query query;
  4306. query.RunQuery2(Q_DELETE, "DELETE FROM character_spells WHERE char_id=%u AND spell_id=%u", character_id, spell_id);
  4307. }
  4308. }
  4309. bool WorldDatabase::GetItemResultsToClient (Client* client, const char* varSearch, int maxResults) {
  4310. Query query;
  4311. MYSQL_ROW row;
  4312. int results = 0;
  4313. if( maxResults > 10 && client->GetAdminStatus ( ) < 100 )
  4314. maxResults = 10;
  4315. else if( maxResults > 20 )
  4316. maxResults = 20;
  4317. client->Message(CHANNEL_COLOR_YELLOW, "Item Search Results");
  4318. client->Message(CHANNEL_COLOR_YELLOW, "ResultNum) [ItemID] ItemName");
  4319. string itemsearch_query = string("SELECT id, name FROM items where name like '%%%s%%' limit %i");
  4320. MYSQL_RES* result = query.RunQuery2(Q_SELECT, itemsearch_query.c_str(),getSafeEscapeString(varSearch).c_str(),maxResults);
  4321. while(result && (row = mysql_fetch_row(result))){
  4322. results++;
  4323. client->Message(CHANNEL_COLOR_YELLOW, "%i) [%s] %s",results,row[0],row[1]);
  4324. }
  4325. if(results == 0)
  4326. {
  4327. client->Message(CHANNEL_COLOR_YELLOW, "No Items Found.");
  4328. return false;
  4329. }
  4330. client->Message(CHANNEL_COLOR_YELLOW, "%i Items Found.",results);
  4331. return true;
  4332. }
  4333. void WorldDatabase::SaveWorldTime(WorldTime* time){
  4334. Query query;
  4335. query.RunQuery2(Q_REPLACE, "replace into variables (variable_name, variable_value) values('gametime', '%i/%i/%i %i:%i')", time->month, time->day, time->year, time->hour, time->minute);
  4336. }
  4337. void WorldDatabase::SaveBugReport(const char* category, const char* subcategory, const char* causes_crash, const char* reproducible, const char* summary, const char* description, const char* version, const char* player, int32 account_id, const char* spawn_name, int32 spawn_id, int32 zone_id){
  4338. Query query;
  4339. int32 dbVersion = rule_manager.GetGlobalRule(R_World, DatabaseVersion)->GetInt32();
  4340. string bug_report = string("insert into bugs (category, subcategory, causes_crash, reproducible, summary, description, version, player, account_id, spawn_name, spawn_id, zone_id, dbversion, worldversion) values('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %u, '%s', %u, %u, %u, '%s')");
  4341. query.RunQuery2(Q_INSERT, bug_report.c_str(), getSafeEscapeString(category).c_str(), getSafeEscapeString(subcategory).c_str(),
  4342. getSafeEscapeString(causes_crash).c_str(), getSafeEscapeString(reproducible).c_str(), getSafeEscapeString(summary).c_str(),
  4343. getSafeEscapeString(description).c_str(), getSafeEscapeString(version).c_str(), getSafeEscapeString(player).c_str(), account_id,
  4344. getSafeEscapeString(spawn_name).c_str(), spawn_id, zone_id, dbVersion, CURRENT_VERSION);
  4345. FixBugReport();
  4346. FixBugReport();
  4347. FixBugReport();
  4348. }
  4349. void WorldDatabase::FixBugReport(){
  4350. Query query;
  4351. string bug_report = string("update bugs set description = REPLACE(description,SUBSTRING(description,INSTR(description,'%'), 3),char(CONV(SUBSTRING(description,INSTR(description,'%')+1, 2), 16, 10))), summary = REPLACE(summary,SUBSTRING(summary,INSTR(summary,'%'), 3),char(CONV(SUBSTRING(summary,INSTR(summary,'%')+1, 2), 16, 10)))");
  4352. query.RunQuery2(bug_report.c_str(), Q_UPDATE);
  4353. }
  4354. int32 WorldDatabase::LoadQuests(){
  4355. Query query;
  4356. MYSQL_ROW row;
  4357. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `quest_id`, `name`, `type`, `zone`, `level`, `enc_level`, `description`, `lua_script`, `completed_text`, `spawn_id` FROM `quests`");
  4358. Quest* quest = 0;
  4359. char* name = 0;
  4360. char* type = 0;
  4361. char* zone = 0;
  4362. int8 level = 0;
  4363. int8 enc_level = 0;
  4364. char* description = 0;
  4365. char* script = 0;
  4366. int32 total = 0;
  4367. int32 id = 0;
  4368. char* completed_description = 0;
  4369. int32 return_npc_id = 0;
  4370. if(result){
  4371. while(result && (row = mysql_fetch_row(result))){
  4372. id = atoul(row[0]);
  4373. name = row[1];
  4374. type = row[2];
  4375. zone = row[3];
  4376. level = atoul(row[4]);
  4377. enc_level = atoul(row[5]);
  4378. description = row[6];
  4379. script = row[7];
  4380. completed_description = row[8];
  4381. return_npc_id = atoi(row[9]);
  4382. if(lua_interface) {
  4383. quest = lua_interface->LoadQuest(id, name, type, zone, level, description, script);
  4384. }
  4385. if(quest){
  4386. LogWrite(QUEST__DEBUG, 5, "Quests", "\tLoading Quest: '%s' (%u)", name, id);
  4387. LoadQuestDetails(quest);
  4388. string compDescription;
  4389. if (completed_description == NULL)
  4390. {
  4391. compDescription = string("Missing! Notify Developer");
  4392. LogWrite(QUEST__WARNING, 5, "Quests", "\tLoading Quest MISSING completed_text in quests table for: '%s' (%u)", name, id);
  4393. }
  4394. else
  4395. compDescription = string(completed_description);
  4396. quest->SetCompletedDescription(string(compDescription));
  4397. quest->SetQuestReturnNPC(return_npc_id);
  4398. quest->SetEncounterLevel(enc_level);
  4399. total++;
  4400. master_quest_list.AddQuest(id, quest);
  4401. }
  4402. else
  4403. {
  4404. LogWrite(QUEST__ERROR, 5, "Quests", "\tFAILED LOADING QUEST: '%s' (%u), check that script file exists/permissions correct: %s", name, id, (script && strlen(script)) ? script : "Not Set, Missing!");
  4405. }
  4406. }
  4407. }
  4408. LogWrite(QUEST__DEBUG, 0, "Quest", "\tLoaded %i Quest(s)", total);
  4409. return total;
  4410. }
  4411. void WorldDatabase::LoadQuestDetails(Quest* quest) {
  4412. Query query;
  4413. MYSQL_ROW row;
  4414. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `type`, `subtype`, `value`, `faction_id`, `quantity` FROM `quest_details` WHERE `quest_id`=%u", quest->GetQuestID());
  4415. string type;
  4416. string subtype;
  4417. sint64 value;
  4418. int32 faction_id;
  4419. int32 quantity;
  4420. while (result && (row = mysql_fetch_row(result))) {
  4421. type = string(row[0]);
  4422. subtype = string(row[1]);
  4423. value = atoi(row[2]);
  4424. faction_id = atoi(row[3]);
  4425. quantity = atoi(row[4]);
  4426. LogWrite(QUEST__DEBUG, 5, "Quests", "\t- Type: %s, SubType: %s, Val: %u, Faction: %u, Qty: %u", type.c_str(), subtype.c_str(), value, faction_id, quantity);
  4427. if (type == "Prereq") {
  4428. if (subtype == "Class")
  4429. quest->AddPrereqClass(value);
  4430. else if (subtype == "Faction")
  4431. quest->AddPrereqFaction(faction_id, value);
  4432. else if (subtype == "Item") {
  4433. Item* master_item = master_item_list.GetItem(value);
  4434. if (master_item) {
  4435. Item* item = new Item(master_item);
  4436. quest->AddPrereqItem(item);
  4437. }
  4438. }
  4439. else if (subtype == "AdvLevel")
  4440. quest->SetPrereqLevel(value);
  4441. else if (subtype == "Quest")
  4442. quest->AddPrereqQuest(value);
  4443. else if (subtype == "Race")
  4444. quest->AddPrereqRace(value);
  4445. else if (subtype == "TSClass")
  4446. quest->AddPrereqTradeskillClass(value);
  4447. else if (subtype == "TSLevel")
  4448. quest->SetPrereqTSLevel(value);
  4449. else if (subtype == "MaxTSLevel")
  4450. quest->SetPrereqMaxTSLevel(value);
  4451. else if (subtype == "MaxAdvLevel")
  4452. quest->SetPrereqMaxLevel(value);
  4453. }
  4454. else if (type == "Reward") {
  4455. if (subtype == "Item") {
  4456. Item* master_item = master_item_list.GetItem(value);
  4457. if (master_item) {
  4458. Item* item = new Item(master_item);
  4459. item->details.count = quantity;
  4460. quest->AddRewardItem(item);
  4461. }
  4462. }
  4463. else if (subtype == "Selectable") {
  4464. Item* master_item = master_item_list.GetItem(value);
  4465. if (master_item) {
  4466. Item* item = new Item(master_item);
  4467. item->details.count = quantity;
  4468. quest->AddSelectableRewardItem(item);
  4469. }
  4470. }
  4471. else if (subtype == "Coin") {
  4472. int32 copper = 0;
  4473. int32 silver = 0;
  4474. int32 gold = 0;
  4475. int32 plat = 0;
  4476. if (value >= 1000000) {
  4477. plat = value / 1000000;
  4478. value -= 1000000 * plat;
  4479. }
  4480. if (value >= 10000) {
  4481. gold = value / 10000;
  4482. value -= 10000 * gold;
  4483. }
  4484. if (value >= 100) {
  4485. silver = value / 100;
  4486. value -= 100 * silver;
  4487. }
  4488. if (value > 0)
  4489. copper = value;
  4490. quest->AddRewardCoins(copper, silver, gold, plat);
  4491. }
  4492. else if (subtype == "MaxCoin") {
  4493. quest->AddRewardCoinsMax(value);
  4494. }
  4495. else if (subtype == "Faction")
  4496. quest->AddRewardFaction(faction_id, value);
  4497. else if (subtype == "Experience")
  4498. quest->SetRewardXP(value);
  4499. else if (subtype == "TSExperience")
  4500. quest->SetRewardTSXP(value);
  4501. }
  4502. }
  4503. }
  4504. void WorldDatabase::LoadMerchantInformation() {
  4505. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tClearing Merchant Inventory...");
  4506. world.DeleteMerchantItems();
  4507. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoading Merchant Inventory...");
  4508. LoadMerchantInventory();
  4509. Query query;
  4510. MYSQL_ROW row;
  4511. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT merchant_id, inventory_id FROM merchants ORDER BY merchant_id");
  4512. int32 total = 0;
  4513. int32 last_merchant_id = 0;
  4514. int32 id = 0;
  4515. MerchantInfo* merchant = 0;
  4516. if(result) {
  4517. while(result && (row = mysql_fetch_row(result))) {
  4518. id = atoul(row[0]);
  4519. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "\tMerchantID: %u, InventoryID: %u", id, atoul(row[1]));
  4520. if(id != last_merchant_id) {
  4521. if(merchant)
  4522. world.AddMerchantInfo(last_merchant_id, merchant);
  4523. merchant = new MerchantInfo;
  4524. last_merchant_id = id;
  4525. total++;
  4526. }
  4527. merchant->inventory_ids.push_back(atoul(row[1]));
  4528. }
  4529. if(merchant)
  4530. world.AddMerchantInfo(last_merchant_id, merchant);
  4531. }
  4532. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoaded %i Merchant List(s)", total);
  4533. }
  4534. void WorldDatabase::LoadMerchantInventory(){
  4535. Query query;
  4536. MYSQL_ROW row;
  4537. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT inventory_id, item_id, quantity, price_item_id, price_item_qty, price_item2_id, price_item2_qty, price_status, price_coins, price_stationcash FROM merchant_inventory ORDER BY inventory_id");
  4538. int32 total = 0;
  4539. int32 id;
  4540. if(result) {
  4541. while(result && (row = mysql_fetch_row(result))) {
  4542. MerchantItemInfo ItemInfo;
  4543. id = atoul(row[0]);
  4544. ItemInfo.item_id = atoul(row[1]);
  4545. ItemInfo.quantity = atoi(row[2]);
  4546. ItemInfo.price_item_id = atoul(row[3]);
  4547. ItemInfo.price_item_qty = atoi(row[4]);
  4548. ItemInfo.price_item2_id = atoul(row[5]);
  4549. ItemInfo.price_item2_qty = atoi(row[6]);
  4550. ItemInfo.price_status = atoul(row[7]);
  4551. ItemInfo.price_coins = atoul(row[8]);
  4552. ItemInfo.price_stationcash = atoul(row[9]);
  4553. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "\tInventoryID: %u, ItemID: %u, Qty: %u", id, ItemInfo.item_id, ItemInfo.quantity);
  4554. world.AddMerchantItem(id, ItemInfo);
  4555. total++;
  4556. }
  4557. }
  4558. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoaded %i Merchant Inventory Item(s)", total);
  4559. }
  4560. string WorldDatabase::GetMerchantDescription(int32 merchant_id) {
  4561. Query query;
  4562. MYSQL_ROW row;
  4563. string description;
  4564. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `description` FROM `merchants` WHERE `merchant_id`=%u", merchant_id);
  4565. if (result && (row = mysql_fetch_row(result)))
  4566. description = string(row[0]);
  4567. return description;
  4568. }
  4569. void WorldDatabase::LoadTransporters(ZoneServer* zone){
  4570. int32 total = 0;
  4571. zone->DeleteGlobalTransporters();
  4572. Query query;
  4573. MYSQL_ROW row;
  4574. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT transport_id, transport_type, display_name, message, destination_zone_id, destination_x, destination_y, destination_z, destination_heading, trigger_location_zone_id, trigger_location_x, trigger_location_y, trigger_location_z, trigger_radius, cost, id, min_level, max_level, quest_req, quest_step_req, quest_completed, map_x, map_y, expansion_flag, holiday_flag, min_client_version, max_client_version, flight_path_id, mount_id, mount_red_color, mount_green_color, mount_blue_color FROM transporters ORDER BY transport_id");
  4575. if(result){
  4576. while(result && (row = mysql_fetch_row(result))){
  4577. LogWrite(TRANSPORT__DEBUG, 5, "Transport", "---Loading Transporter ID: %u, transport_type: %s", row[0], row[1]);
  4578. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---display_name: %s, message: %s", row[2], row[3]);
  4579. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---destination_zone_id: %s", row[4]);
  4580. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---destination_x: %s, destination_y: %s, destination_z: %s, destination_heading: %s", row[5], row[6], row[7], row[8]);
  4581. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---trigger_location_zone_id: %s", row[9]);
  4582. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---trigger_location_x: %s, trigger_location_y: %s, trigger_location_z: %s", row[10], row[11], row[12], row[13]);
  4583. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---trigger_radius: %s, cost: %s, id: %s", row[14], row[15]);
  4584. string name = "";
  4585. if(row[2])
  4586. name = string(row[2]);
  4587. string message = "";
  4588. if(row[3])
  4589. message = string(row[3]);
  4590. if(row[1] && strcmp(row[1], "Zone") == 0)
  4591. zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_ZONE, name, message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31]));
  4592. else if (row[1] && strcmp(row[1], "Flight") == 0)
  4593. zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_FLIGHT, name, message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31]));
  4594. else if(row[1] && strcmp(row[1], "Location") == 0)
  4595. zone->AddLocationTransporter(atoul(row[9]), message, atof(row[10]), atof(row[11]), atof(row[12]), atof(row[13]), atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]));
  4596. else
  4597. zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_GENERIC, "", message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31]));
  4598. total++;
  4599. }
  4600. }
  4601. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "--Loaded %i Transporter(s)", total);
  4602. LoadTransportMaps(zone);
  4603. }
  4604. void WorldDatabase::LoadFogInit(string zone, PacketStruct* packet)
  4605. {
  4606. LogWrite(WORLD__TRACE, 9, "World", "Enter: %s", __FUNCTION__);
  4607. if(!packet || zone.length() == 0)
  4608. return;
  4609. Query query;
  4610. MYSQL_ROW row;
  4611. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT highest, lowest, zone_name, explored_map_name, unexplored_map_name, bounds1_x, bounds1_z, bounds2_x, bounds2_z, bounds3_x, bounds3_z, bounds4_x, bounds4_z, explored_key, unexplored_key, map_id FROM map_data where zone_name like '%s%%'", getSafeEscapeString(&zone).c_str());
  4612. if(result){
  4613. int count = mysql_num_rows(result);
  4614. int i=0;
  4615. int64 explored_key;
  4616. int64 unexplored_key;
  4617. packet->setArrayLengthByName("num_maps", count);
  4618. while(result && (row = mysql_fetch_row(result))){
  4619. packet->setDataByName("highest_z", atof(row[0]));
  4620. packet->setDataByName("lowest_z", atof(row[1]));
  4621. packet->setDataByName("map_id", atoul(row[15]));
  4622. packet->setArrayDataByName("unknown7", 1600, i);
  4623. packet->setArrayDataByName("unknown8", 1200, i);
  4624. packet->setArrayDataByName("zone_name", row[2], i);
  4625. packet->setArrayDataByName("explored_map_name", row[3], i);
  4626. packet->setArrayDataByName("unexplored_map_name", row[4], i);
  4627. packet->setArrayDataByName("map_bounds1_x", atof(row[5]), i);
  4628. packet->setArrayDataByName("map_bounds1_z", atof(row[6]), i);
  4629. packet->setArrayDataByName("map_bounds2_x", atof(row[7]), i);
  4630. packet->setArrayDataByName("map_bounds2_z", atof(row[8]), i);
  4631. packet->setArrayDataByName("map_bounds3_x", atof(row[9]), i);
  4632. packet->setArrayDataByName("map_bounds3_z", atof(row[10]), i);
  4633. packet->setArrayDataByName("map_bounds4_x", atof(row[11]), i);
  4634. packet->setArrayDataByName("map_bounds4_z", atof(row[12]), i);
  4635. #ifdef WIN32
  4636. explored_key = _strtoui64(row[13], NULL, 10);
  4637. unexplored_key = _strtoui64(row[14], NULL, 10);
  4638. #else
  4639. explored_key = strtoull(row[13], 0, 10);
  4640. unexplored_key = strtoull(row[14], 0, 10);
  4641. #endif
  4642. packet->setArrayDataByName("explored_key", explored_key, i);
  4643. packet->setArrayDataByName("unexplored_key", unexplored_key, i);
  4644. i++;
  4645. }
  4646. }
  4647. LogWrite(WORLD__TRACE, 9, "World", "Exit: %s", __FUNCTION__);
  4648. }
  4649. string WorldDatabase::GetColumnNames(char* name){
  4650. Query query;
  4651. MYSQL_ROW row;
  4652. string columns = "";
  4653. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "show columns FROM %s", name);
  4654. if(result && mysql_num_rows(result) > 0){
  4655. int16 i = 0;
  4656. while((row = mysql_fetch_row(result))){
  4657. if(strcmp(row[0], "table_data_version") != 0){
  4658. if(i>0)
  4659. columns.append(",");
  4660. columns.append(row[0]);
  4661. i++;
  4662. }
  4663. }
  4664. }
  4665. columns.append("");
  4666. return columns;
  4667. }
  4668. void WorldDatabase::ToggleCharacterOnline() {
  4669. Query query;
  4670. query.RunQuery2(Q_UPDATE, "UPDATE characters SET is_online = 0;");
  4671. }
  4672. void WorldDatabase::ToggleCharacterOnline(Client* client, int8 toggle) {
  4673. if (client) {
  4674. Query query;
  4675. Player* player = client->GetPlayer();
  4676. //if(!player->CheckPlayerInfo())
  4677. // return;
  4678. if (player)
  4679. {
  4680. LogWrite(PLAYER__DEBUG, 0, "Player", "Toggling Character %s", toggle ? "ONLINE!" : "OFFLINE!");
  4681. query.RunQuery2(Q_UPDATE, "UPDATE characters SET is_online=%i WHERE id = %u;", toggle, client->GetCharacterID());
  4682. }
  4683. }
  4684. }
  4685. void WorldDatabase::LoadPlayerStatistics(Player* player, int32 char_id) {
  4686. Query query;
  4687. MYSQL_ROW row;
  4688. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT stat_id, stat_value, stat_date FROM statistics WHERE char_id=%i", char_id);
  4689. while (result && (row = mysql_fetch_row(result))) {
  4690. int32 stat_id = atoi(row[0]);
  4691. sint32 stat_value = atoi(row[1]);
  4692. int32 stat_date = atoi(row[2]);
  4693. player->AddPlayerStatistic(stat_id, stat_value, stat_date);
  4694. }
  4695. }
  4696. void WorldDatabase::WritePlayerStatistic(Player *player, Statistic* stat) {
  4697. if (player && player->GetCharacterID() > 0 && stat) {
  4698. Query query;
  4699. query.RunQuery2(Q_INSERT, "INSERT INTO statistics (char_id, guild_id, stat_id, stat_value, stat_date) VALUES(%i, %i, %i, %i, %i) ON DUPLICATE KEY UPDATE stat_value = %i, stat_date = %i;",
  4700. player->GetCharacterID(), 0, stat->stat_id, stat->stat_value, stat->stat_date,
  4701. stat->stat_value, stat->stat_date);
  4702. }
  4703. }
  4704. void WorldDatabase::LoadServerStatistics()
  4705. {
  4706. Query query;
  4707. MYSQL_ROW row;
  4708. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT stat_id, stat_value, stat_date FROM statistics WHERE char_id=0 AND guild_id=0");
  4709. while (result && (row = mysql_fetch_row(result))) {
  4710. int32 stat_id = atoi(row[0]);
  4711. sint32 stat_value = atoi(row[1]);
  4712. int32 stat_date = atoi(row[2]);
  4713. world.AddServerStatistic(stat_id, stat_value, stat_date);
  4714. LogWrite(INIT__DEBUG, 5, "Stats", "Loading Stat ID %i, value: %i", stat_id, stat_value);
  4715. }
  4716. }
  4717. void WorldDatabase::WriteServerStatistic(Statistic* stat) {
  4718. if (stat) {
  4719. Query query;
  4720. query.RunQuery2(Q_INSERT, "INSERT INTO statistics (char_id, guild_id, stat_id, stat_value, stat_date) VALUES(0, 0, %i, %i, %i) ON DUPLICATE KEY UPDATE stat_value = %i, stat_date = %i;",
  4721. stat->stat_id, stat->stat_value, stat->stat_date,
  4722. stat->stat_value, stat->stat_date);
  4723. }
  4724. }
  4725. void WorldDatabase::WriteServerStatistic(int32 stat_id, sint32 stat_value) {
  4726. Query query;
  4727. query.RunQuery2(Q_INSERT, "INSERT INTO statistics (char_id, guild_id, stat_id, stat_value, stat_date) VALUES(0, 0, %i, %i, %i) ON DUPLICATE KEY UPDATE stat_value = %i, stat_date = %i;",
  4728. stat_id, stat_value, Timer::GetUnixTimeStamp(),
  4729. stat_value, Timer::GetUnixTimeStamp());
  4730. }
  4731. void WorldDatabase::WriteServerStatisticsNeededQueries() {
  4732. Query query1, query2, query3;
  4733. MYSQL_ROW row1, row2, row3;
  4734. // Number of unique accounts
  4735. MYSQL_RES* result1 = query1.RunQuery2(Q_SELECT, "SELECT COUNT(DISTINCT account_id) FROM characters");
  4736. if (result1 && (row1 = mysql_fetch_row(result1)) && row1[0] != NULL)
  4737. WriteServerStatistic(STAT_SERVER_NUM_ACCOUNTS, atoi(row1[0]));
  4738. else
  4739. WriteServerStatistic(STAT_SERVER_NUM_ACCOUNTS, 0);
  4740. // Number of characters
  4741. MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM characters");
  4742. if (result2 && (row2 = mysql_fetch_row(result2)) && row2[0] != NULL)
  4743. WriteServerStatistic(STAT_SERVER_NUM_CHARACTERS, atoi(row2[0]));
  4744. else
  4745. WriteServerStatistic(STAT_SERVER_NUM_CHARACTERS, 0);
  4746. // Average character level
  4747. MYSQL_RES* result3 = query3.RunQuery2(Q_SELECT, "SELECT ROUND(AVG(level)) FROM characters");
  4748. if (result3 && (row3 = mysql_fetch_row(result3)) && row3[0] != NULL)
  4749. WriteServerStatistic(STAT_SERVER_AVG_CHAR_LEVEL, atoi(row3[0]));
  4750. else
  4751. WriteServerStatistic(STAT_SERVER_AVG_CHAR_LEVEL, 0);
  4752. }
  4753. map<int32,int32>* WorldDatabase::GetInstanceRemovedSpawns(int32 instance_id, int8 type)
  4754. {
  4755. DatabaseResult result;
  4756. map<int32,int32>* ret = NULL;
  4757. LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
  4758. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Loading removed spawns for instance_id: %u, spawn_type: %i", instance_id, type);
  4759. if( !database_new.Select(&result, "SELECT spawn_location_entry_id, respawn_time FROM instance_spawns_removed WHERE instance_id = %i AND spawn_type = %i", instance_id, type) )
  4760. {
  4761. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceRemovedSpawns() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4762. return ret;
  4763. }
  4764. else
  4765. {
  4766. if( result.GetNumRows() > 0 )
  4767. {
  4768. ret = new map<int32,int32>;
  4769. while( result.Next() )
  4770. {
  4771. int32 spawn_location_entry_id = result.GetInt32Str("spawn_location_entry_id");
  4772. /*
  4773. respawnTime == 0 - never respawn
  4774. respawnTime = 1 - spawn now
  4775. respawnTime > 1 (continue timer)
  4776. */
  4777. int32 respawntime = result.GetInt32Str("respawn_time");
  4778. LogWrite(INSTANCE__DEBUG, 5, "Instance", "Found spawn point: %u, respawn time: %i", spawn_location_entry_id, respawntime);
  4779. ret->insert(make_pair(spawn_location_entry_id, respawntime));
  4780. }
  4781. }
  4782. else
  4783. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No removed spawns found for instance_id: %u, spawn_type: %i", instance_id, type);
  4784. }
  4785. LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
  4786. return ret;
  4787. }
  4788. bool WorldDatabase::CheckVectorForValue(vector<int32>* vector, int32 value) {
  4789. if ( vector != NULL )
  4790. {
  4791. for(int32 i=0;i<vector->size();i++)
  4792. {
  4793. int32 compare = vector->at(i);
  4794. if ( compare == value )
  4795. return true;
  4796. }
  4797. }
  4798. return false;
  4799. }
  4800. int32 WorldDatabase::CheckSpawnRemoveInfo(map<int32,int32>* inmap, int32 spawn_location_entry_id)
  4801. {
  4802. LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
  4803. map<int32, int32>::iterator iter;
  4804. if ( inmap != NULL )
  4805. {
  4806. for(iter=inmap->begin();iter!=inmap->end();iter++)
  4807. {
  4808. if ( iter->first == spawn_location_entry_id )
  4809. return (int32)iter->second;
  4810. }
  4811. }
  4812. return 1;
  4813. }
  4814. int32 WorldDatabase::AddCharacterInstance(int32 char_id, int32 instance_id, string zone_name, int8 instance_type, int32 last_success, int32 last_failure, int32 success_lockout, int32 failure_lockout)
  4815. {
  4816. int32 ret = 0;
  4817. if( !database_new.Query("INSERT INTO character_instances (char_id, instance_id, instance_zone_name, instance_type, last_success_timestamp, last_failure_timestamp, success_lockout_time, failure_lockout_time) VALUES (%u, %u, '%s', %i, %u, %u, %u, %u) ", char_id, instance_id, database_new.EscapeStr(zone_name).c_str(), instance_type, last_success, last_failure, success_lockout, failure_lockout) )
  4818. {
  4819. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in AddCharacterInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4820. return 0;
  4821. }
  4822. ret = database_new.LastInsertID();
  4823. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Adding character %u to instance: %u", char_id, instance_id);
  4824. //LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Reenter: %u, Reset: %u, Lockout: %u", grant_reenter_time_left, grant_reset_time_left, lockout_time);
  4825. return ret;
  4826. }
  4827. bool WorldDatabase::UpdateCharacterInstanceTimers(int32 char_id, int32 instance_id, int32 lockout_time, int32 reset_time, int32 reenter_time )
  4828. {
  4829. if ( lockout_time > 0 && reset_time > 0 && reenter_time > 0 )
  4830. database_new.Query("UPDATE character_instances SET lockout_time = %i, grant_reset_time_left = %i, grant_reenter_time_left = %i WHERE char_id = %i AND instance_id = %i", lockout_time, reset_time, reenter_time, char_id, instance_id);
  4831. else if ( lockout_time > 0 && reset_time > 0 )
  4832. database_new.Query("UPDATE character_instances SET lockout_time = %i, grant_reset_time_left = %i WHERE char_id = %i AND instance_id = %i", lockout_time, reset_time, char_id, instance_id);
  4833. else if ( reset_time > 0 && reenter_time > 0 )
  4834. database_new.Query("UPDATE character_instances SET grant_reset_time_left = %i, grant_reenter_time_left = %i WHERE char_id = %i AND instance_id = %i",reset_time, reenter_time, char_id, instance_id);
  4835. else if ( lockout_time > 0 && reenter_time > 0 )
  4836. database_new.Query("UPDATE character_instances SET lockout_time = %i, grant_reenter_time_left = %i WHERE char_id = %i AND instance_id = %i", lockout_time, reenter_time, char_id, instance_id);
  4837. else if ( lockout_time > 0 )
  4838. database_new.Query("UPDATE character_instances SET lockout_time = %i WHERE char_id = %i AND instance_id = %i", lockout_time, char_id, instance_id);
  4839. else if ( reset_time > 0 )
  4840. database_new.Query("UPDATE character_instances SET grant_reset_time_left = %i WHERE char_id = %i AND instance_id = %i", reset_time, char_id, instance_id);
  4841. else if ( reenter_time > 0 )
  4842. database_new.Query("UPDATE character_instances SET grant_reenter_time_left = %i WHERE char_id = %i AND instance_id = %i", reenter_time, char_id, instance_id);
  4843. if( database_new.GetError() )
  4844. {
  4845. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateCharacterInstanceTimers() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4846. return false;
  4847. }
  4848. else
  4849. {
  4850. if ( database_new.AffectedRows() > 0 )
  4851. {
  4852. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updating instance timers for character %u to instance: %u", char_id, instance_id);
  4853. LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Reenter: %u, Reset: %u, Lockout: %u", reenter_time, reset_time, lockout_time);
  4854. return true;
  4855. }
  4856. else
  4857. return false;
  4858. }
  4859. }
  4860. bool WorldDatabase::UpdateCharacterInstance(int32 char_id, string zone_name, int32 instance_id, int8 type, int32 timestamp) {
  4861. // type = 1 = success timestamp
  4862. // type = 2 = failure timestamp
  4863. if (instance_id > 0) {
  4864. if (type == 1) {
  4865. database_new.Query("UPDATE character_instances SET instance_id = %u, last_success_timestamp = %u WHERE char_id = %u AND instance_zone_name = '%s'", instance_id, timestamp, char_id, database_new.EscapeStr(zone_name).c_str());
  4866. }
  4867. else if (type == 2) {
  4868. database_new.Query("UPDATE character_instances SET instance_id = %u, last_failure_timestamp = %u WHERE char_id = %u AND instance_zone_name = '%s'", instance_id, timestamp, char_id, database_new.EscapeStr(zone_name).c_str());
  4869. }
  4870. else {
  4871. database_new.Query("UPDATE character_instances SET instance_id = %u WHERE char_id = %u AND instance_zone_name = '%s'", instance_id, char_id, database_new.EscapeStr(zone_name).c_str());
  4872. }
  4873. }
  4874. else {
  4875. if (type == 1) {
  4876. database_new.Query("UPDATE character_instances SET last_success_timestamp = %u WHERE char_id = %u AND instance_zone_name = '%s'", timestamp, char_id, database_new.EscapeStr(zone_name).c_str());
  4877. }
  4878. else if (type == 2) {
  4879. database_new.Query("UPDATE character_instances SET last_failure_timestamp = %u WHERE char_id = %u AND instance_zone_name = '%s'", timestamp, char_id, database_new.EscapeStr(zone_name).c_str());
  4880. }
  4881. }
  4882. if (database_new.GetError()) {
  4883. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateCharacterInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4884. return false;
  4885. }
  4886. return true;
  4887. }
  4888. bool WorldDatabase::VerifyInstanceID(int32 char_id, int32 instance_id) {
  4889. DatabaseResult result;
  4890. database_new.Select(&result, "SELECT COUNT(id) as num_instances FROM instances WHERE id = %u", instance_id);
  4891. if (result.Next() && result.GetInt32Str("num_instances") == 0) {
  4892. DeleteCharacterFromInstance(char_id, instance_id);
  4893. return false;
  4894. }
  4895. return true;
  4896. }
  4897. bool WorldDatabase::UpdateInstancedSpawnRemoved(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 instance_id )
  4898. {
  4899. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updating removed spawns for instance_id: %u", instance_id);
  4900. LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Spawn Location Entry ID: %u, Type: %u, Respawn: %u", spawn_location_entry_id, spawn_type, respawn_time);
  4901. if( !database_new.Query("UPDATE instance_spawns_removed SET respawn_time = %i WHERE spawn_location_entry_id = %i AND spawn_type = %i AND instance_id = %i", respawn_time, spawn_location_entry_id, spawn_type, instance_id) )
  4902. {
  4903. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateInstancedSpawnRemoved() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4904. return false;
  4905. }
  4906. if ( database_new.AffectedRows() > 0 )
  4907. {
  4908. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updated removed spawns for instance_id: %u", instance_id);
  4909. return true;
  4910. }
  4911. else
  4912. return false;
  4913. }
  4914. int32 WorldDatabase::CreateNewInstance(int32 zone_id)
  4915. {
  4916. int32 ret = 0;
  4917. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Creating new instance for zone: %u ", zone_id);
  4918. if( !database_new.Query("INSERT INTO instances (zone_id) VALUES (%u)", zone_id) )
  4919. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in CreateNewInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4920. else
  4921. ret = database_new.LastInsertID();
  4922. if( ret > 0 )
  4923. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Created new instance_id %u for zone: %u ", ret, zone_id);
  4924. return ret;
  4925. }
  4926. int32 WorldDatabase::CreateInstanceSpawnRemoved(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 instance_id )
  4927. {
  4928. int32 ret = 0;
  4929. LogWrite(INSTANCE__DEBUG, 3, "Instance", "Creating new instance spawn removed entries for instance_id: %u ", instance_id);
  4930. LogWrite(INSTANCE__DEBUG, 5, "Instance", "-- Spawn Location Entry ID: %u, Type: %u, Respawn: %u", spawn_location_entry_id, spawn_type, respawn_time);
  4931. if( !database_new.Query("INSERT INTO instance_spawns_removed (spawn_location_entry_id, spawn_type, instance_id, respawn_time) values(%u, %u, %u, %u)", spawn_location_entry_id, spawn_type, instance_id, respawn_time) )
  4932. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in CreateInstanceSpawnRemoved() query '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4933. else
  4934. ret = database_new.LastInsertID();
  4935. // potentially spammy, if it calls for every spawn added. Set to level 3 or 5?
  4936. if( ret > 0 )
  4937. LogWrite(INSTANCE__DEBUG, 5, "Instance", "Created new spawn removed entry: %u for instance_id %u", ret, instance_id);
  4938. return ret;
  4939. }
  4940. bool WorldDatabase::DeleteInstance(int32 instance_id)
  4941. {
  4942. if( !database_new.Query("DELETE FROM instances WHERE id = %u", instance_id) )
  4943. {
  4944. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4945. return false;
  4946. }
  4947. /* JA: should not need this delete with FK/Constraints
  4948. if( !database_new.Query("DELETE FROM instance_spawns_removed WHERE instance_id = %u", instance_id) )
  4949. {
  4950. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4951. return false;
  4952. }
  4953. */
  4954. // Remove the instance from the character_instances table
  4955. database_new.Query("UPDATE `character_instances` SET `instance_id` = 0 WHERE `instance_id` = %u", instance_id);
  4956. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Deleted instance_id %u", instance_id);
  4957. return true;
  4958. }
  4959. bool WorldDatabase::DeleteInstanceSpawnRemoved(int32 instance_id, int32 spawn_location_entry_id)
  4960. {
  4961. if( !database_new.Query("DELETE FROM instance_spawns_removed WHERE instance_id = %u AND spawn_location_entry_id = %u", instance_id, spawn_location_entry_id) )
  4962. {
  4963. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstanceSpawnRemoved() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4964. return false;
  4965. }
  4966. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Deleted removed spawn: %u for instance_id %u", spawn_location_entry_id, instance_id);
  4967. return true;
  4968. }
  4969. bool WorldDatabase::DeleteCharacterFromInstance(int32 char_id, int32 instance_id)
  4970. {
  4971. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Delete character %u from instance_id %u.", char_id, instance_id);
  4972. if( !database_new.Query("UPDATE `character_instances` SET `instance_id` = 0 WHERE `instance_id` = %u AND `char_id` = %u", instance_id, char_id) )
  4973. {
  4974. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteCharacterFromInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4975. return false;
  4976. }
  4977. if ( database_new.AffectedRows() == 0 ) // didn't find an instance to delete
  4978. {
  4979. LogWrite(INSTANCE__DEBUG, 1, "Instance", "No instance_id %u for character %u to delete.", instance_id, char_id);
  4980. return false;
  4981. }
  4982. else
  4983. {
  4984. // delete entire instance if the last player has left
  4985. DatabaseResult result;
  4986. database_new.Select(&result, "SELECT count(id) as num_instances FROM character_instances where instance_id = %u",instance_id);
  4987. if(result.Next() && result.GetInt32Str("num_instances") == 0)
  4988. {
  4989. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No characters in instance: Delete instance_id %u.", instance_id);
  4990. DeleteInstance(instance_id);
  4991. }
  4992. }
  4993. return true;
  4994. }
  4995. bool WorldDatabase::LoadCharacterInstances(Client* client)
  4996. {
  4997. DatabaseResult result;
  4998. DatabaseResult result2;
  4999. bool addedInstance = false;
  5000. database_new.Select(&result, "SELECT `id`, `instance_id`, `instance_zone_name`, `instance_type`, `last_success_timestamp`, `last_failure_timestamp`, `success_lockout_time`, `failure_lockout_time` FROM `character_instances` WHERE `char_id` = %u", client->GetCharacterID());
  5001. if( result.GetNumRows() > 0 )
  5002. {
  5003. while( result.Next() )
  5004. {
  5005. int32 zone_id = 0;
  5006. int32 instance_id = result.GetInt32Str("instance_id");
  5007. // If `instance_id` is greater then 0 then get the zone id with it, else get the zone id from the zone name
  5008. if (instance_id != 0) {
  5009. if (database_new.Select(&result2, "SELECT `zone_id` FROM `instances` WHERE `id` = %u", instance_id)) {
  5010. if (result2.Next())
  5011. zone_id = result2.GetInt32Str("zone_id");
  5012. }
  5013. }
  5014. if (zone_id == 0)
  5015. zone_id = GetZoneID(result.GetStringStr("instance_zone_name"));
  5016. client->GetPlayer()->GetCharacterInstances()->AddInstance(
  5017. result.GetInt32Str("id"),
  5018. instance_id,
  5019. result.GetInt32Str("last_success_timestamp"),
  5020. result.GetInt32Str("last_failure_timestamp"),
  5021. result.GetInt32Str("success_lockout_time"),
  5022. result.GetInt32Str("failure_lockout_time"),
  5023. zone_id,
  5024. result.GetInt8Str("instance_type"),
  5025. string(result.GetStringStr("instance_zone_name"))
  5026. );
  5027. addedInstance = true;
  5028. }
  5029. }
  5030. return addedInstance;
  5031. }
  5032. void WorldDatabase::UpdateLoginEquipment()
  5033. {
  5034. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Updating `character_items` CRC in %s", __FUNCTION__);
  5035. database_new.Query("UPDATE character_items SET login_checksum = CRC32(CRC32(type) + CRC32(slot) + CRC32(item_id)) WHERE `type` = 'EQUIPPED' AND ( slot <= 8 OR slot = 19 )");
  5036. }
  5037. MutexMap<int32, LoginEquipmentUpdate>* WorldDatabase::GetEquipmentUpdates()
  5038. {
  5039. DatabaseResult result;
  5040. MutexMap<int32, LoginEquipmentUpdate>* ret = 0;
  5041. LoginEquipmentUpdate update;
  5042. int32 count = 0;
  5043. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Appearance Updates...");
  5044. // TODO: Someday store the equipment colors in character_items, for custom colorization of gear (?)
  5045. if( database_new.Select(&result, "SELECT ci.id, ci.char_id, ia.equip_type, ia.red, green, ia.blue, ia.highlight_red, ia.highlight_green, ia.highlight_blue, ci.slot FROM characters c JOIN character_items ci ON c.id = ci.char_id JOIN item_appearances ia ON ci.item_id = ia.item_id WHERE c.deleted = 0 AND ci.type = 'EQUIPPED' AND ( ci.slot <= 8 OR ci.slot = 19 ) AND ci.login_checksum <> CRC32(CRC32(`ci`.`type`) + CRC32(ci.slot) + CRC32(ci.item_id)) ORDER BY ci.char_id, ci.slot") )
  5046. {
  5047. while( result.Next() )
  5048. {
  5049. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for char_id %i!", result.GetInt32Str("char_id"));
  5050. if(!ret)
  5051. ret = new MutexMap<int32, LoginEquipmentUpdate>();
  5052. update.world_char_id = result.GetInt32Str("char_id");
  5053. update.equip_type = result.GetInt16Str("equip_type");
  5054. update.red = result.GetInt8Str("red");
  5055. update.green = result.GetInt8Str("green");
  5056. update.blue = result.GetInt8Str("blue");
  5057. update.highlight_red = result.GetInt8Str("highlight_red");
  5058. update.highlight_green = result.GetInt8Str("highlight_green");
  5059. update.highlight_blue = result.GetInt8Str("highlight_blue");
  5060. update.slot = result.GetInt8Str("slot");
  5061. ret->Put(result.GetInt32Str("id"), update);
  5062. count++;
  5063. }
  5064. }
  5065. if(count)
  5066. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Appearance Update%s...", count, count == 1 ? "" : "s");
  5067. return ret;
  5068. }
  5069. MutexMap<int32, LoginEquipmentUpdate>* WorldDatabase::GetEquipmentUpdates(int32 char_id)
  5070. {
  5071. DatabaseResult result;
  5072. MutexMap<int32, LoginEquipmentUpdate>* ret = 0;
  5073. LoginEquipmentUpdate update;
  5074. int32 count = 0;
  5075. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Appearance Updates for char_id: %u", char_id);
  5076. // TODO: Someday store the equipment colors in character_items, for custom colorization of gear (?)
  5077. if( database_new.Select(&result, "SELECT ci.id, ci.char_id, ia.equip_type, ia.red, green, ia.blue, ia.highlight_red, ia.highlight_green, ia.highlight_blue, ci.slot FROM characters c JOIN character_items ci ON c.id = ci.char_id JOIN item_appearances ia ON ci.item_id = ia.item_id WHERE c.deleted = 0 AND ci.type = 'EQUIPPED' AND ( ci.slot <= 8 OR ci.slot = 19 ) AND ci.login_checksum <> CRC32(CRC32(ci.type) + CRC32(ci.slot) + CRC32(ci.item_id)) AND ci.char_id = %u ORDER BY ci.slot", char_id) )
  5078. {
  5079. while( result.Next() )
  5080. {
  5081. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for char_id %i!", result.GetInt32Str("char_id"));
  5082. if(!ret)
  5083. ret = new MutexMap<int32, LoginEquipmentUpdate>();
  5084. update.world_char_id = char_id;
  5085. update.equip_type = result.GetInt16Str("equip_type");
  5086. update.red = result.GetInt8Str("red");
  5087. update.green = result.GetInt8Str("green");
  5088. update.blue = result.GetInt8Str("blue");
  5089. update.highlight_red = result.GetInt8Str("highlight_red");
  5090. update.highlight_green = result.GetInt8Str("highlight_green");
  5091. update.highlight_blue = result.GetInt8Str("highlight_blue");
  5092. update.slot = result.GetInt8Str("slot");
  5093. ret->Put(result.GetInt32Str("id"), update);
  5094. count++;
  5095. }
  5096. }
  5097. if(count)
  5098. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Appearance Update%s...", count, count == 1 ? "" : "s");
  5099. return ret;
  5100. }
  5101. void WorldDatabase::UpdateLoginZones() {
  5102. Query query;
  5103. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Updating `zones` CRC in %s", __FUNCTION__);
  5104. query.RunQuery2("UPDATE zones SET login_checksum = CRC32(CRC32(id) + CRC32(`name`) + CRC32(`file`) + CRC32(description))", Q_UPDATE);
  5105. }
  5106. MutexMap<int32, LoginZoneUpdate>* WorldDatabase::GetZoneUpdates() {
  5107. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Zone Updates...");
  5108. MutexMap<int32, LoginZoneUpdate>* ret = 0;
  5109. LoginZoneUpdate update;
  5110. Query query;
  5111. MYSQL_ROW row;
  5112. int32 count = 0;
  5113. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, description FROM zones where login_checksum != crc32(crc32(id) + crc32(name) + crc32(file) + crc32(description))");
  5114. while(result && (row = mysql_fetch_row(result))) {
  5115. if(row[0] && row[1]) {
  5116. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for zone_id %i!", atoi(row[0]));
  5117. if(!ret)
  5118. ret = new MutexMap<int32, LoginZoneUpdate>();
  5119. update.name = string(row[1]);
  5120. if(row[2])
  5121. update.description = string(row[2]);
  5122. else
  5123. update.description = "";
  5124. ret->Put(atoi(row[0]), update);
  5125. count++;
  5126. }
  5127. }
  5128. if(count)
  5129. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Zone Update%s...", count, count == 1 ? "" : "s");
  5130. return ret;
  5131. }
  5132. void WorldDatabase::LoadLocationGrids(ZoneServer* zone) {
  5133. if (zone) {
  5134. Query query;
  5135. MYSQL_ROW row;
  5136. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `grid_id`, `name`, `include_y`, `discovery` FROM `locations` WHERE `zone_id`=%u", zone->GetZoneID());
  5137. while (result && (row = mysql_fetch_row(result))) {
  5138. LocationGrid* grid = new LocationGrid;
  5139. grid->id = atoul(row[0]);
  5140. grid->grid_id = atoul(row[1]);
  5141. grid->name = string(row[2]);
  5142. grid->include_y = (atoi(row[3]) == 1);
  5143. grid->discovery = (atoi(row[4]) == 1);
  5144. if (LoadLocationGridLocations(grid))
  5145. zone->AddLocationGrid(grid);
  5146. else
  5147. safe_delete(grid);
  5148. }
  5149. }
  5150. }
  5151. bool WorldDatabase::LoadLocationGridLocations(LocationGrid* grid) {
  5152. bool ret = false;
  5153. if (grid) {
  5154. Query query;
  5155. MYSQL_ROW row;
  5156. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `x`, `y`, `z` FROM `location_details` WHERE `location_id`=%u", grid->id);
  5157. if (result->row_count >= 3) {
  5158. while (result && (row = mysql_fetch_row(result))) {
  5159. Location* location = new Location;
  5160. location->id = atoul(row[0]);
  5161. location->x = atof(row[1]);
  5162. location->y = atof(row[2]);
  5163. location->z = atof(row[3]);
  5164. grid->locations.Add(location);
  5165. }
  5166. ret = true;
  5167. }
  5168. else
  5169. LogWrite(WORLD__ERROR, 0, "World", "Grid '%s' only has %u location(s). A minimum of 3 is needed.", grid->name.c_str(), result->row_count);
  5170. }
  5171. return ret;
  5172. }
  5173. int32 WorldDatabase::CreateLocation(int32 zone_id, int32 grid_id, const char* name, bool include_y) {
  5174. int32 ret = 0;
  5175. if (name && strlen(name) > 0) {
  5176. Query query;
  5177. query.RunQuery2(Q_INSERT, "INSERT INTO `locations` (`zone_id`, `grid_id`, `name`, `include_y`) VALUES (%u, %u, '%s', %u)", zone_id, grid_id, name, include_y == true ? 1 : 0);
  5178. ret = query.GetLastInsertedID();
  5179. }
  5180. return ret;
  5181. }
  5182. bool WorldDatabase::AddLocationPoint(int32 location_id, float x, float y, float z) {
  5183. bool ret = false;
  5184. if (LocationExists(location_id)) {
  5185. Query query;
  5186. query.RunQuery2(Q_INSERT, "INSERT INTO `location_details` (`location_id`, `x`, `y`, `z`) VALUES (%u, %f, %f, %f)", location_id, x, y, z);
  5187. ret = true;
  5188. }
  5189. return ret;
  5190. }
  5191. bool WorldDatabase::DeleteLocation(int32 location_id) {
  5192. bool ret = false;
  5193. if (LocationExists(location_id)) {
  5194. Query query;
  5195. query.RunQuery2(Q_DELETE, "DELETE FROM `locations` WHERE `id`=%u", location_id);
  5196. ret = true;
  5197. }
  5198. return ret;
  5199. }
  5200. bool WorldDatabase::DeleteLocationPoint(int32 location_point_id) {
  5201. Query query;
  5202. query.RunQuery2(Q_DELETE, "DELETE FROM `location_details` WHERE `id`=%u", location_point_id);
  5203. return true;
  5204. }
  5205. void WorldDatabase::ListLocations(Client* client) {
  5206. if (client) {
  5207. Query query;
  5208. MYSQL_ROW row;
  5209. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Listing all locations:");
  5210. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `zone_id`, `grid_id`, `name` FROM `locations`");
  5211. while (result && (row = mysql_fetch_row(result))) {
  5212. int32 id = atoul(row[0]);
  5213. int32 zone_id = atoul(row[1]);
  5214. int32 grid_id = atoul(row[2]);
  5215. const char* name = row[3];
  5216. client->Message(CHANNEL_COLOR_YELLOW, "%u) Zone ID: %u Grid ID:%u Name: '%s'", id, zone_id, grid_id, name);
  5217. }
  5218. }
  5219. }
  5220. void WorldDatabase::ListLocationPoints(Client* client, int32 location_id) {
  5221. if (client) {
  5222. if (LocationExists(location_id)) {
  5223. Query query;
  5224. client->Message(CHANNEL_COLOR_YELLOW, "Listing all points for location ID %u:", location_id);
  5225. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `x`, `y`, `z` FROM `location_details` WHERE `location_id`=%u", location_id);
  5226. MYSQL_ROW row;
  5227. while (result && (row = mysql_fetch_row(result))) {
  5228. int32 id = atoul(row[0]);
  5229. float x = atof(row[1]);
  5230. float y = atof(row[2]);
  5231. float z = atof(row[3]);
  5232. client->Message(CHANNEL_COLOR_YELLOW, "%u) (%f, %f, %f)", id, x, y, z);
  5233. }
  5234. }
  5235. else
  5236. client->Message(CHANNEL_COLOR_YELLOW, "A location with ID %u does not exist", location_id);
  5237. }
  5238. }
  5239. bool WorldDatabase::LocationExists(int32 location_id) {
  5240. bool ret = false;
  5241. Query query;
  5242. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM `locations` WHERE `id`=%u", location_id);
  5243. MYSQL_ROW row;
  5244. if (result && (row = mysql_fetch_row(result))) {
  5245. if (atoul(row[0]) > 0)
  5246. ret = true;
  5247. }
  5248. return ret;
  5249. }
  5250. bool WorldDatabase::GetTableVersions(vector<TableVersion*>* table_versions) {
  5251. DatabaseResult result;
  5252. TableVersion *table_version;
  5253. bool success;
  5254. //don't treat 1146 (table not found) as an error since the patch server will create it
  5255. database_new.SetIgnoredErrno(1146);
  5256. success = database_new.Select(&result, "SELECT `name`,`version`,`download_version`\n"
  5257. "FROM `table_versions`\n");
  5258. database_new.RemoveIgnoredErrno(1146);
  5259. if (!success)
  5260. return false;
  5261. while (result.Next()) {
  5262. table_version = (TableVersion *)malloc(sizeof(*table_version));
  5263. table_version->name_len = (unsigned int)strlcpy(table_version->name, result.GetString(0), sizeof(table_version->name));
  5264. table_version->version = result.GetInt32(1);
  5265. table_version->data_version = result.GetInt32(2);
  5266. table_versions->push_back(table_version);
  5267. }
  5268. return true;
  5269. }
  5270. bool WorldDatabase::QueriesFromFile(const char * file) {
  5271. return database_new.QueriesFromFile(file);
  5272. }
  5273. bool WorldDatabase::CheckBannedIPs(const char* loginIP)
  5274. {
  5275. // til you build the table, all IPs are allowed
  5276. return false;
  5277. }
  5278. sint32 WorldDatabase::AddMasterTitle(const char* titleName, int8 isPrefix)
  5279. {
  5280. if(titleName == nullptr || strlen(titleName) < 1)
  5281. {
  5282. LogWrite(DATABASE__ERROR, 0, "DBNew", "AddMasterTitle called with missing titleName");
  5283. return -1;
  5284. }
  5285. Query query;
  5286. Title* title = master_titles_list.GetTitleByName(titleName);
  5287. if(title)
  5288. return title->GetID();
  5289. query.RunQuery2(Q_INSERT, "INSERT INTO titles set title='%s', prefix=%u", titleName, isPrefix);
  5290. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  5291. LogWrite(DATABASE__ERROR, 0, "Database", "Error in AddMasterTitle query '%s': %s", query.GetQuery(), query.GetError());
  5292. return false;
  5293. }
  5294. int32 last_insert_id = query.GetLastInsertedID();
  5295. if(last_insert_id > 0)
  5296. {
  5297. title = new Title;
  5298. title->SetID(last_insert_id);
  5299. title->SetName(titleName);
  5300. title->SetPrefix(isPrefix);
  5301. master_titles_list.AddTitle(title);
  5302. return (sint32)last_insert_id;
  5303. }
  5304. return -1;
  5305. }
  5306. void WorldDatabase::LoadTitles(){
  5307. Query query;
  5308. MYSQL_ROW row;
  5309. int32 count = 0;
  5310. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, title, prefix FROM titles");
  5311. if(result && mysql_num_rows(result) > 0){
  5312. Title* title = 0;
  5313. while(result && (row = mysql_fetch_row(result))){
  5314. sint32 idx = atoi(row[0]);
  5315. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Title '%s' (%u), Prefix: %i, Index: %u", row[1], idx, atoi(row[2]), count);
  5316. title = new Title;
  5317. title->SetID(idx);
  5318. title->SetName(row[1]);
  5319. title->SetPrefix(atoi(row[2]));
  5320. master_titles_list.AddTitle(title);
  5321. count++;
  5322. }
  5323. }
  5324. LogWrite(WORLD__DEBUG, 0, "World", "\tLoaded %u Title%s", count, count == 1 ? "" : "s");
  5325. }
  5326. sint32 WorldDatabase::LoadCharacterTitles(int32 char_id, Player *player){
  5327. LogWrite(WORLD__DEBUG, 0, "World", "Loading Titles for player '%s'...", player->GetName());
  5328. Query query;
  5329. MYSQL_ROW row;
  5330. sint32 index = 0;
  5331. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT title_id, title, prefix FROM character_titles, titles WHERE character_titles.title_id = titles.id AND character_titles.char_id = %u", char_id);
  5332. if(result && mysql_num_rows(result) > 0){
  5333. while(result && (row = mysql_fetch_row(result))){
  5334. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Title ID: %u, Title: '%s' Index: %u", atoul(row[0]), row[1], index);
  5335. player->AddTitle(index, row[1], atoi(row[2]));
  5336. index++;
  5337. }
  5338. }
  5339. return index;
  5340. }
  5341. sint32 WorldDatabase::GetCharPrefixIndex(int32 char_id, Player *player){
  5342. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current title index for player '%s'...", player->GetName());
  5343. Query query;
  5344. MYSQL_ROW row;
  5345. sint32 ret = 0;
  5346. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT prefix_title FROM character_details WHERE char_id = %u", char_id);
  5347. if(result && mysql_num_rows(result) > 0)
  5348. while(result && (row = mysql_fetch_row(result))){
  5349. ret = atoi(row[0]);
  5350. LogWrite(PLAYER__DEBUG, 5, "Player", "\tPrefix Index: %i", ret);
  5351. }
  5352. return ret;
  5353. }
  5354. sint32 WorldDatabase::GetCharSuffixIndex(int32 char_id, Player *player){
  5355. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current title index for player '%s'...", player->GetName());
  5356. Query query;
  5357. MYSQL_ROW row;
  5358. sint32 ret = 0;
  5359. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT suffix_title FROM character_details WHERE char_id = %u", char_id);
  5360. if(result && mysql_num_rows(result) > 0)
  5361. while(result && (row = mysql_fetch_row(result))){
  5362. ret = atoi(row[0]);
  5363. LogWrite(PLAYER__DEBUG, 5, "Player", "\tSuffix Index: %i", ret);
  5364. }
  5365. return ret;
  5366. }
  5367. void WorldDatabase::SaveCharPrefixIndex(sint32 index, int32 char_id){
  5368. Query query;
  5369. query.RunQuery2(Q_UPDATE, "UPDATE character_details SET prefix_title = %i WHERE char_id = %u", index, char_id);
  5370. LogWrite(PLAYER__DEBUG, 0, "Player", "Saving Prefix Index %i for character id '%u'...", index, char_id);
  5371. }
  5372. void WorldDatabase::SaveCharSuffixIndex(sint32 index, int32 char_id){
  5373. Query query;
  5374. query.RunQuery2(Q_SELECT, "UPDATE character_details SET suffix_title = %i WHERE char_id = %u", index, char_id);
  5375. LogWrite(PLAYER__DEBUG, 0, "Player", "Saving Suffix Index %i for character id %u...", index, char_id);
  5376. }
  5377. sint32 WorldDatabase::AddCharacterTitle(sint32 index, int32 char_id, Spawn* player) {
  5378. if(!player || !player->IsPlayer())
  5379. {
  5380. LogWrite(DATABASE__ERROR, 0, "DBNew", "AddCharacterTitle spawn is not a player: %s", player ? player->GetName() : "Unset");
  5381. return -1;
  5382. }
  5383. Title* title = master_titles_list.GetTitle(index);
  5384. if(!title)
  5385. {
  5386. LogWrite(DATABASE__ERROR, 0, "DBNew", "AddCharacterTitle title index %u missing from master_titles_list for player: %s (%u)", index, player ? player->GetName() : "Unset", char_id);
  5387. return -1;
  5388. }
  5389. Query query;
  5390. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding titles for char_id: %u, index: %i", char_id, index);
  5391. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_titles (char_id, title_id) VALUES (%u, %i)", char_id, index);
  5392. sint32 curIndex = (sint32)((Player*)player)->GetPlayerTitles()->Size();
  5393. ((Player*)player)->AddTitle(curIndex++, title->GetName(), title->GetPrefix(), title->GetSaveNeeded());
  5394. return curIndex;
  5395. }
  5396. void WorldDatabase::LoadLanguages()
  5397. {
  5398. int32 count = 0;
  5399. Query query;
  5400. MYSQL_ROW row;
  5401. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, language FROM languages");
  5402. if(result && mysql_num_rows(result) > 0)
  5403. {
  5404. Language* language = 0;
  5405. while(result && (row = mysql_fetch_row(result)))
  5406. {
  5407. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading language '%s' , ID: %u", row[1], atoul(row[0]));
  5408. language = new Language;
  5409. language->SetID(atoul(row[0]));
  5410. language->SetName(row[1]);
  5411. master_languages_list.AddLanguage(language);
  5412. count++;
  5413. }
  5414. }
  5415. LogWrite(WORLD__DEBUG, 0, "World", "\tLoaded %u Language%s", count, count == 1 ? "" : "s");
  5416. }
  5417. int32 WorldDatabase::LoadCharacterLanguages(int32 char_id, Player *player)
  5418. {
  5419. LogWrite(WORLD__DEBUG, 0, "World", "Loading Languages for player '%s'...", player->GetName());
  5420. Query query;
  5421. MYSQL_ROW row;
  5422. int32 count = 0;
  5423. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT language_id, language FROM character_languages, languages WHERE character_languages.language_id = languages.id AND character_languages.char_id = %u", char_id);
  5424. if(result && mysql_num_rows(result) > 0)
  5425. {
  5426. while(result && (row = mysql_fetch_row(result)))
  5427. {
  5428. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Language ID: %u, Language: '%s'", atoul(row[0]), row[1]);
  5429. player->AddLanguage(atoul(row[0]), row[1]);
  5430. count++;
  5431. }
  5432. }
  5433. return count;
  5434. }
  5435. int16 WorldDatabase::GetCharacterCurrentLang(int32 char_id, Player *player)
  5436. {
  5437. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current language for player '%s'...", player->GetName());
  5438. Query query;
  5439. MYSQL_ROW row;
  5440. int16 ret = 0;
  5441. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT current_language FROM character_details WHERE char_id = %u", char_id);
  5442. if(result && mysql_num_rows(result) > 0)
  5443. while(result && (row = mysql_fetch_row(result)))
  5444. {
  5445. ret = atoi(row[0]);
  5446. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLanguage ID: %i", ret);
  5447. }
  5448. return ret;
  5449. }
  5450. void WorldDatabase::SaveCharacterCurrentLang(int32 id, int32 char_id, Client *client)
  5451. {
  5452. Query query;
  5453. query.RunQuery2(Q_UPDATE, "UPDATE character_details SET current_language = %i WHERE char_id = %u", id, char_id);
  5454. LogWrite(PLAYER__DEBUG, 3, "Player", "Saving current language ID %i for player '%s'...", id, client->GetPlayer()->GetName());
  5455. }
  5456. void WorldDatabase::SaveCharacterLang(int32 char_id, int32 lang_id) {
  5457. if (!database_new.Query("INSERT INTO character_languages (char_id, language_id) VALUES (%u, %u)", char_id, lang_id))
  5458. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  5459. }
  5460. // JA - this is not used yet, lots more to consider for storing player history
  5461. void WorldDatabase::LoadCharacterHistory(int32 char_id, Player *player)
  5462. {
  5463. DatabaseResult result;
  5464. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  5465. if (!database_new.Select(&result, "SELECT type-1, subtype-1, value, value2, location, event_id, event_date FROM character_history WHERE char_id = %u", char_id)) {
  5466. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  5467. return;
  5468. }
  5469. while (result.Next()) {
  5470. int8 type = result.GetInt8(0);
  5471. int8 subtype = result.GetInt8(1);
  5472. HistoryData* hd = new HistoryData;
  5473. hd->Value = result.GetInt32(2);
  5474. hd->Value2 = result.GetInt32(3);
  5475. strcpy(hd->Location, result.GetString(4));
  5476. // skipped event id as use for it has not been determined yet
  5477. hd->EventDate = result.GetInt32(6);
  5478. player->LoadPlayerHistory(type, subtype, hd);
  5479. }
  5480. }
  5481. void WorldDatabase::LoadSpellErrors() {
  5482. Query query;
  5483. MYSQL_ROW row;
  5484. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `version`, `error_index`, `value` FROM `spell_error_versions`");
  5485. if (result && mysql_num_rows(result) > 0) {
  5486. while ((row = mysql_fetch_row(result))) {
  5487. master_spell_list.AddSpellError(atoi(row[0]), atoi(row[1]), atoi(row[2]));
  5488. }
  5489. }
  5490. }
  5491. void WorldDatabase::SaveCharacterHistory(Player* player, int8 type, int8 subtype, int32 value, int32 value2, char* location, int32 event_date) {
  5492. string str_type;
  5493. string str_subtype;
  5494. switch (type) {
  5495. case HISTORY_TYPE_NONE:
  5496. str_type = "None";
  5497. break;
  5498. case HISTORY_TYPE_DEATH:
  5499. str_type = "Death";
  5500. break;
  5501. case HISTORY_TYPE_DISCOVERY:
  5502. str_type = "Discovery";
  5503. break;
  5504. case HISTORY_TYPE_XP:
  5505. str_type = "XP";
  5506. break;
  5507. default:
  5508. LogWrite(PLAYER__ERROR, 0, "Player", "WorldDatabase::SaveCharacterHistory() - Invalid history type given (%i) with subtype (%i), character history NOT saved.", type, subtype);
  5509. return;
  5510. }
  5511. switch (subtype) {
  5512. case HISTORY_SUBTYPE_NONE:
  5513. str_subtype = "None";
  5514. break;
  5515. case HISTORY_SUBTYPE_ADVENTURE:
  5516. str_subtype = "Adventure";
  5517. break;
  5518. case HISTORY_SUBTYPE_TRADESKILL:
  5519. str_subtype = "Tradeskill";
  5520. break;
  5521. case HISTORY_SUBTYPE_QUEST:
  5522. str_subtype = "Quest";
  5523. break;
  5524. case HISTORY_SUBTYPE_AA:
  5525. str_subtype = "AA";
  5526. break;
  5527. case HISTORY_SUBTYPE_ITEM:
  5528. str_subtype = "Item";
  5529. break;
  5530. case HISTORY_SUBTYPE_LOCATION:
  5531. str_subtype = "Location";
  5532. break;
  5533. default:
  5534. LogWrite(PLAYER__ERROR, 0, "Player", "WorldDatabase::SaveCharacterHistory() - Invalid history sub type given (%i) with type (%i), character history NOT saved.", subtype, type);
  5535. return;
  5536. }
  5537. LogWrite(PLAYER__INFO, 1, "Player", "Saving character history, type = %s (%i) subtype = %s (%i)", (char*)str_type.c_str(), type, (char*)str_subtype.c_str(), subtype);
  5538. Query query;
  5539. query.AddQueryAsync(player->GetCharacterID(), this, Q_REPLACE, "replace into character_history (char_id, type, subtype, value, value2, location, event_date) values (%u, '%s', '%s', %i, %i, '%s', %u)",
  5540. player->GetCharacterID(), str_type.c_str(), str_subtype.c_str(), value, value2, location, event_date);
  5541. }
  5542. void WorldDatabase::LoadTransportMaps(ZoneServer* zone) {
  5543. int32 total = 0;
  5544. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "-Loading Transporter Maps...");
  5545. zone->DeleteTransporterMaps();
  5546. Query query;
  5547. MYSQL_ROW row;
  5548. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `transport_id`, `map_name` FROM `transport_maps`");
  5549. if(result) {
  5550. while(result && (row = mysql_fetch_row(result))){
  5551. zone->AddTransportMap(atoul(row[0]), string(row[1]));
  5552. total++;
  5553. }
  5554. }
  5555. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "--Loaded %i Transporter Maps", total);
  5556. }
  5557. bool WorldDatabase::LoadSign(ZoneServer* zone, int32 spawn_id) {
  5558. Sign* sign = 0;
  5559. int32 id = 0;
  5560. DatabaseResult result;
  5561. database_new.Select(&result, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  5562. "FROM spawn s\n"
  5563. "INNER JOIN spawn_signs ss\n"
  5564. "ON ss.spawn_id = s.id\n"
  5565. "WHERE s.id = %u\n",
  5566. spawn_id);
  5567. if (result.GetNumRows() > 0 && result.Next()) {
  5568. id = result.GetInt32(0);
  5569. sign = new Sign();
  5570. sign->SetDatabaseID(id);
  5571. strcpy(sign->appearance.name, result.GetString(1));
  5572. sign->appearance.model_type = result.GetInt16(2);
  5573. sign->SetSize(result.GetInt16(3));
  5574. sign->appearance.show_command_icon = result.GetInt8(4);
  5575. sign->SetWidgetID(result.GetInt32(5));
  5576. sign->SetWidgetX(result.GetFloat(6));
  5577. sign->SetWidgetY(result.GetFloat(7));
  5578. sign->SetWidgetZ(result.GetFloat(8));
  5579. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5580. if(primary_command_list){
  5581. sign->SetPrimaryCommands(primary_command_list);
  5582. sign->primary_command_list_id = result.GetInt32(9);
  5583. }
  5584. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5585. if (secondary_command_list) {
  5586. sign->SetSecondaryCommands(secondary_command_list);
  5587. sign->secondary_command_list_id = result.GetInt32(10);
  5588. }
  5589. sign->appearance.pos.collision_radius = result.GetInt16(11);
  5590. sign->SetSignIcon(result.GetInt8(12));
  5591. if(strncasecmp(result.GetString(13), "Generic", 7) == 0)
  5592. sign->SetSignType(SIGN_TYPE_GENERIC);
  5593. else if(strncasecmp(result.GetString(13), "Zone", 4) == 0)
  5594. sign->SetSignType(SIGN_TYPE_ZONE);
  5595. sign->SetSignTitle(result.GetString(14));
  5596. sign->SetSignDescription(result.GetString(15));
  5597. sign->SetSignDistance(result.GetFloat(16));
  5598. sign->SetSignZoneID(result.GetInt32(17));
  5599. sign->SetSignZoneX(result.GetFloat(18));
  5600. sign->SetSignZoneY(result.GetFloat(19));
  5601. sign->SetSignZoneZ(result.GetFloat(20));
  5602. sign->SetSignZoneHeading(result.GetFloat(21));
  5603. sign->SetIncludeHeading(result.GetInt8(22) == 1);
  5604. sign->SetIncludeLocation(result.GetInt8(23) == 1);
  5605. sign->SetTransporterID(result.GetInt32(24));
  5606. sign->SetSizeOffset(result.GetInt8(25));
  5607. sign->appearance.display_hand_icon = result.GetInt8(26);
  5608. sign->SetVisualState(result.GetInt16(27));
  5609. sign->SetSoundsDisabled(result.GetInt8(28));
  5610. sign->SetMerchantLevelRange(result.GetInt32(29), result.GetInt32(30));
  5611. sign->SetAAXPRewards(result.GetInt32(31));
  5612. zone->AddSign(id, sign);
  5613. LogWrite(SIGN__DEBUG, 0, "Sign", "Loaded Sign: '%s' (%u).", sign->appearance.name, spawn_id);
  5614. return true;
  5615. }
  5616. LogWrite(SIGN__DEBUG, 0, "Sign", "Unable to find a sign for spawn id of %u", spawn_id);
  5617. return false;
  5618. }
  5619. bool WorldDatabase::LoadWidget(ZoneServer* zone, int32 spawn_id) {
  5620. Widget* widget = 0;
  5621. int32 id = 0;
  5622. DatabaseResult result;
  5623. database_new.Select(&result, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  5624. "FROM spawn s\n"
  5625. "INNER JOIN spawn_widgets sw\n"
  5626. "ON sw.spawn_id = s.id\n"
  5627. "WHERE s.id = %u",
  5628. spawn_id);
  5629. if (result.GetNumRows() > 0 && result.Next()) {
  5630. id = result.GetInt32(0);
  5631. widget = new Widget();
  5632. widget->SetDatabaseID(id);
  5633. strcpy(widget->appearance.name, result.GetString(1));
  5634. widget->appearance.model_type = result.GetInt16(2);
  5635. widget->SetSize(result.GetInt16(3));
  5636. widget->appearance.show_command_icon = result.GetInt8(4);
  5637. widget->SetWidgetID(result.GetInt32(5));
  5638. widget->SetWidgetX(result.GetFloat(6));
  5639. widget->SetWidgetY(result.GetFloat(7));
  5640. widget->SetWidgetZ(result.GetFloat(8));
  5641. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5642. if(primary_command_list){
  5643. widget->SetPrimaryCommands(primary_command_list);
  5644. widget->primary_command_list_id = result.GetInt32(9);
  5645. }
  5646. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5647. if (secondary_command_list) {
  5648. widget->SetSecondaryCommands(secondary_command_list);
  5649. widget->secondary_command_list_id = result.GetInt32(10);
  5650. }
  5651. widget->appearance.pos.collision_radius = result.GetInt16(11);
  5652. widget->SetIncludeHeading(result.GetInt8(12) == 1);
  5653. widget->SetIncludeLocation(result.GetInt8(13) == 1);
  5654. widget->SetWidgetIcon(result.GetInt8(14));
  5655. if(strncasecmp(result.GetString(15),"Generic", 7) == 0)
  5656. widget->SetWidgetType(WIDGET_TYPE_GENERIC);
  5657. else if(strncasecmp(result.GetString(15),"Door", 4) == 0)
  5658. widget->SetWidgetType(WIDGET_TYPE_DOOR);
  5659. widget->SetOpenHeading(result.GetFloat(16));
  5660. widget->SetOpenY(result.GetFloat(17));
  5661. widget->SetActionSpawnID(result.GetInt32(18));
  5662. if(!result.IsNull(19) && strlen(result.GetString(19)) > 5)
  5663. widget->SetOpenSound(result.GetString(19));
  5664. if(!result.IsNull(20) && strlen(result.GetString(20)) > 5)
  5665. widget->SetCloseSound(result.GetString(20));
  5666. widget->SetOpenDuration(result.GetInt16(21));
  5667. widget->SetClosedHeading(result.GetFloat(22));
  5668. widget->SetLinkedSpawnID(result.GetInt32(23));
  5669. widget->SetCloseY(result.GetFloat(24));
  5670. widget->SetTransporterID(result.GetInt32(25));
  5671. widget->SetSizeOffset(result.GetInt8(26));
  5672. widget->SetHouseID(result.GetInt32(27));
  5673. widget->SetOpenX(result.GetFloat(28));
  5674. widget->SetOpenZ(result.GetFloat(29));
  5675. widget->SetCloseX(result.GetFloat(30));
  5676. widget->SetCloseZ(result.GetFloat(31));
  5677. widget->appearance.display_hand_icon = result.GetInt8(32);
  5678. widget->SetSoundsDisabled(result.GetInt8(33));
  5679. widget->SetMerchantLevelRange(result.GetInt32(34), result.GetInt32(35));
  5680. widget->SetAAXPRewards(result.GetInt32(36));
  5681. zone->AddWidget(id, widget);
  5682. LogWrite(WIDGET__DEBUG, 0, "Widget", "Loaded Widget: '%s' (%u).", widget->appearance.name, spawn_id);
  5683. return true;
  5684. }
  5685. LogWrite(WIDGET__DEBUG, 0, "Widget", "Unable to find a widget for spawn id of %u", spawn_id);
  5686. return false;
  5687. }
  5688. bool WorldDatabase::LoadObject(ZoneServer* zone, int32 spawn_id) {
  5689. Object* object = 0;
  5690. int32 id = 0;
  5691. DatabaseResult result;
  5692. database_new.Select(&result, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  5693. "FROM spawn s\n"
  5694. "INNER JOIN spawn_objects so\n"
  5695. "ON so.spawn_id = s.id\n"
  5696. "WHERE s.id = %u",
  5697. spawn_id);
  5698. if (result.GetNumRows() > 0 && result.Next()) {
  5699. id = result.GetInt32(0);
  5700. object = new Object();
  5701. object->SetDatabaseID(id);
  5702. strcpy(object->appearance.name, result.GetString(1));
  5703. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(4));
  5704. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(5));
  5705. if(primary_command_list){
  5706. object->SetPrimaryCommands(primary_command_list);
  5707. object->primary_command_list_id = result.GetInt32(4);
  5708. }
  5709. if(secondary_command_list){
  5710. object->SetSecondaryCommands(secondary_command_list);
  5711. object->secondary_command_list_id = result.GetInt32(5);
  5712. }
  5713. object->appearance.race = result.GetInt8(2);
  5714. object->appearance.model_type = result.GetInt16(3);
  5715. object->appearance.targetable = result.GetInt8(6);
  5716. object->size = result.GetInt16(7);
  5717. object->appearance.display_name = result.GetInt8(8);
  5718. object->appearance.visual_state = result.GetInt16(9);
  5719. object->appearance.attackable = result.GetInt8(10);
  5720. object->appearance.show_level = result.GetInt8(11);
  5721. object->appearance.show_command_icon = result.GetInt8(12);
  5722. object->appearance.display_hand_icon = result.GetInt8(13);
  5723. object->faction_id = result.GetInt32(14);
  5724. object->appearance.pos.collision_radius = result.GetInt16(15);
  5725. object->SetTransporterID(result.GetInt32(16));
  5726. object->SetSizeOffset(result.GetInt8(17));
  5727. object->SetDeviceID(result.GetInt8(18));
  5728. object->SetSoundsDisabled(result.GetInt8(19));
  5729. object->SetMerchantLevelRange(result.GetInt32(20), result.GetInt32(21));
  5730. object->SetAAXPRewards(result.GetInt32(22));
  5731. zone->AddObject(id, object);
  5732. LogWrite(OBJECT__DEBUG, 0, "Object", "Loaded Object: '%s' (%u).", object->appearance.name, spawn_id);
  5733. return true;
  5734. }
  5735. LogWrite(OBJECT__DEBUG, 0, "Object", "Unable to find an object for spawn id of %u", spawn_id);
  5736. return false;
  5737. }
  5738. bool WorldDatabase::LoadGroundSpawn(ZoneServer* zone, int32 spawn_id) {
  5739. GroundSpawn* spawn = 0;
  5740. int32 id = 0;
  5741. DatabaseResult result;
  5742. database_new.Select(&result, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset\n"
  5743. "FROM spawn s\n"
  5744. "INNER JOIN spawn_ground sg\n"
  5745. "ON sg.spawn_id = s.id\n"
  5746. "WHERE s.id = %u",
  5747. spawn_id);
  5748. if (result.GetNumRows() > 0 && result.Next()) {
  5749. id = result.GetInt32(0);
  5750. spawn = new GroundSpawn();
  5751. spawn->SetDatabaseID(id);
  5752. strcpy(spawn->appearance.name, result.GetString(1));
  5753. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(4));
  5754. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(5));
  5755. if(primary_command_list){
  5756. spawn->SetPrimaryCommands(primary_command_list);
  5757. spawn->primary_command_list_id = result.GetInt32(4);
  5758. }
  5759. if(secondary_command_list){
  5760. spawn->SetSecondaryCommands(secondary_command_list);
  5761. spawn->secondary_command_list_id = result.GetInt32(5);
  5762. }
  5763. spawn->appearance.race = result.GetInt8(2);
  5764. spawn->appearance.model_type = result.GetInt16(3);
  5765. spawn->appearance.targetable = result.GetInt8(6);
  5766. spawn->size = result.GetInt16(7);
  5767. spawn->appearance.display_name = result.GetInt8(8);
  5768. spawn->appearance.visual_state = result.GetInt16(9);
  5769. spawn->appearance.attackable = result.GetInt8(10);
  5770. spawn->appearance.show_level = result.GetInt8(11);
  5771. spawn->appearance.show_command_icon = result.GetInt8(12);
  5772. spawn->appearance.display_hand_icon = result.GetInt8(13);
  5773. spawn->faction_id = result.GetInt32(14);
  5774. spawn->appearance.pos.collision_radius = result.GetInt16(15);
  5775. spawn->SetNumberHarvests(result.GetInt8(16));
  5776. spawn->SetAttemptsPerHarvest(result.GetInt8(17));
  5777. spawn->SetGroundSpawnEntryID(result.GetInt32(18));
  5778. spawn->SetCollectionSkill(result.GetString(19));
  5779. spawn->SetSizeOffset(result.GetInt8(20));
  5780. zone->AddGroundSpawn(id, spawn);
  5781. if (!zone->GetGroundSpawnEntries(spawn->GetGroundSpawnEntryID()))
  5782. LoadGroundSpawnEntry(zone, spawn->GetGroundSpawnEntryID());
  5783. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "Loaded Ground Spawn: '%s' (%u).", spawn->appearance.name, spawn_id);
  5784. return true;
  5785. }
  5786. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "Unable to find a ground spawn for spawn id of %u", spawn_id);
  5787. return false;
  5788. }
  5789. void WorldDatabase::LoadGroundSpawnItems(ZoneServer* zone, int32 entry_id) {
  5790. DatabaseResult result;
  5791. database_new.Select(&result, "SELECT item_id, is_rare, grid_id\n"
  5792. "FROM groundspawn_items\n"
  5793. "WHERE groundspawn_id = %u",
  5794. entry_id);
  5795. if (result.GetNumRows() > 0 && result.Next()) {
  5796. zone->AddGroundSpawnItem(entry_id, result.GetInt32(0), result.GetInt8(1), result.GetInt32(2));
  5797. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn Items: ID: %u\n", entry_id);
  5798. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---item: %ul, rare: %i, grid: %ul", result.GetInt32(0), result.GetInt8(1), result.GetInt32(2));
  5799. }
  5800. }
  5801. void WorldDatabase::LoadGroundSpawnEntry(ZoneServer* zone, int32 entry_id) {
  5802. DatabaseResult result;
  5803. database_new.Select(&result, "SELECT min_skill_level, min_adventure_level, bonus_table, harvest1, harvest3, harvest5, harvest_imbue, harvest_rare, harvest10, harvest_coin\n"
  5804. "FROM groundspawns\n"
  5805. "WHERE enabled = 1 AND groundspawn_id = %u",
  5806. entry_id);
  5807. if (result.GetNumRows() > 0 && result.Next()) {
  5808. // this is getting ridonkulous...
  5809. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn ID: %u\n" \
  5810. "---min_skill_level: %i, min_adventure_level: %i, bonus_table: %i\n" \
  5811. "---harvest1: %.2f, harvest3: %.2f, harvest5: %.2f\n" \
  5812. "---harvest_imbue: %.2f, harvest_rare: %.2f, harvest10: %.2f\n" \
  5813. "---harvest_coin: %u", entry_id, result.GetInt16(0), result.GetInt16(1), result.GetInt8(2), result.GetFloat(3), result.GetFloat(4), result.GetFloat(5), result.GetFloat(6), result.GetFloat(7), result.GetFloat(8), result.GetInt32(9));
  5814. zone->AddGroundSpawnEntry(entry_id, result.GetInt16(0), result.GetInt16(1), result.GetInt8(2), result.GetFloat(3), result.GetFloat(4), result.GetFloat(5), result.GetFloat(6), result.GetFloat(7), result.GetFloat(8), result.GetInt32(9));
  5815. LoadGroundSpawnItems(zone, entry_id);
  5816. }
  5817. }
  5818. bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
  5819. NPC* npc = nullptr;
  5820. int32 id = 0;
  5821. DatabaseResult result;
  5822. database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards\n"
  5823. "FROM spawn s\n"
  5824. "INNER JOIN spawn_npcs npc\n"
  5825. "ON npc.spawn_id = s.id\n"
  5826. "WHERE s.id = %u",
  5827. spawn_id);
  5828. if (result.GetNumRows() > 0 && result.Next()) {
  5829. id = result.GetInt32(0);
  5830. npc = new NPC();
  5831. npc->SetDatabaseID(id);
  5832. strcpy(npc->appearance.name, result.GetString(1));
  5833. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5834. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5835. if(primary_command_list){
  5836. npc->SetPrimaryCommands(primary_command_list);
  5837. npc->primary_command_list_id = result.GetInt32(9);
  5838. }
  5839. if(secondary_command_list){
  5840. npc->SetSecondaryCommands(secondary_command_list);
  5841. npc->secondary_command_list_id = result.GetInt32(10);
  5842. }
  5843. npc->appearance.min_level = result.GetInt8(2);
  5844. npc->appearance.max_level = result.GetInt8(3);
  5845. npc->appearance.level = result.GetInt8(2);
  5846. npc->appearance.encounter_level = result.GetInt8(4);
  5847. npc->appearance.race = result.GetInt8(5);
  5848. //npc->appearance.lua_race_id = result.GetInt16(74);
  5849. npc->appearance.model_type = result.GetInt16(6);
  5850. npc->appearance.soga_model_type = result.GetInt16(62);
  5851. npc->appearance.adventure_class = result.GetInt8(7);
  5852. npc->appearance.gender = result.GetInt8(8);
  5853. npc->appearance.display_name = result.GetInt8(11);
  5854. npc->features.hair_type = result.GetInt16(14);
  5855. npc->features.hair_face_type = result.GetInt16(15);
  5856. npc->features.wing_type = result.GetInt16(16);
  5857. npc->features.chest_type = result.GetInt16(17);
  5858. npc->features.legs_type = result.GetInt16(18);
  5859. npc->features.soga_hair_type = result.GetInt16(19);
  5860. npc->features.soga_hair_face_type = result.GetInt16(20);
  5861. npc->appearance.attackable = result.GetInt8(21);
  5862. npc->appearance.show_level = result.GetInt8(22);
  5863. npc->appearance.targetable = result.GetInt8(23);
  5864. npc->appearance.show_command_icon = result.GetInt8(24);
  5865. npc->appearance.display_hand_icon = result.GetInt8(25);
  5866. npc->appearance.hide_hood = result.GetInt8(70);
  5867. npc->appearance.randomize = result.GetInt32(61);
  5868. npc->SetTotalHP(result.GetInt32(26));
  5869. npc->SetTotalPower(result.GetInt32(27));
  5870. npc->SetHP(npc->GetTotalHP());
  5871. npc->SetPower(npc->GetTotalPower());
  5872. if(npc->GetTotalHP() == 0){
  5873. npc->SetTotalHP(15*npc->GetLevel() + 1);
  5874. npc->SetHP(15*npc->GetLevel() + 1);
  5875. }
  5876. if(npc->GetTotalPower() == 0){
  5877. npc->SetTotalPower(15*npc->GetLevel() + 1);
  5878. npc->SetPower(15*npc->GetLevel() + 1);
  5879. }
  5880. npc->size = result.GetInt16(28);
  5881. npc->appearance.pos.collision_radius = result.GetInt16(29);
  5882. npc->appearance.action_state = result.GetInt16(30);
  5883. npc->appearance.visual_state = result.GetInt16(31);
  5884. npc->appearance.mood_state = result.GetInt16(32);
  5885. npc->appearance.emote_state = result.GetInt16(71);
  5886. npc->appearance.pos.state = result.GetInt16(33);
  5887. npc->appearance.activity_status = result.GetInt16(34);
  5888. npc->faction_id = result.GetInt32(35);
  5889. if(!result.IsNull(36)){
  5890. if(strlen(result.GetString(36)) < sizeof(npc->appearance.sub_title))
  5891. strcpy(npc->appearance.sub_title, result.GetString(36));
  5892. else
  5893. strncpy(npc->appearance.sub_title, result.GetString(36), sizeof(npc->appearance.sub_title));
  5894. }
  5895. npc->SetMerchantID(result.GetInt32(37));
  5896. npc->SetMerchantType(result.GetInt8(38));
  5897. npc->SetSizeOffset(result.GetInt8(39));
  5898. npc->SetAttackType(result.GetInt8(40));
  5899. npc->SetAIStrategy(result.GetInt8(41));
  5900. npc->SetPrimarySpellList(result.GetInt32(42));
  5901. npc->SetSecondarySpellList(result.GetInt32(43));
  5902. npc->SetPrimarySkillList(result.GetInt32(44));
  5903. npc->SetSecondarySkillList(result.GetInt32(45));
  5904. npc->SetEquipmentListID(result.GetInt32(46));
  5905. InfoStruct* info = npc->GetInfoStruct();
  5906. info->set_str_base(result.GetInt16(47));
  5907. info->set_sta_base(result.GetInt16(48));
  5908. info->set_wis_base(result.GetInt16(49));
  5909. info->set_intel_base(result.GetInt16(50));
  5910. info->set_agi_base(result.GetInt16(51));
  5911. info->set_heat_base(result.GetInt16(52));
  5912. info->set_cold_base(result.GetInt16(53));
  5913. info->set_magic_base(result.GetInt16(54));
  5914. info->set_mental_base(result.GetInt16(55));
  5915. info->set_divine_base(result.GetInt16(56));
  5916. info->set_disease_base(result.GetInt16(57));
  5917. info->set_poison_base(result.GetInt16(58));
  5918. info->set_alignment(result.GetInt8(64));
  5919. npc->SetAggroRadius(result.GetFloat(59));
  5920. npc->SetCastPercentage(result.GetInt8(60));
  5921. npc->appearance.heroic_flag = result.GetInt8(63);
  5922. info->set_elemental_base(result.GetInt16(65));
  5923. info->set_arcane_base(result.GetInt16(66));
  5924. info->set_noxious_base(result.GetInt16(67));
  5925. npc->SetTotalSavagery(result.GetInt32(68));
  5926. npc->SetTotalDissonance(result.GetInt32(69));
  5927. npc->SetSavagery(npc->GetTotalSavagery());
  5928. npc->SetDissonance(npc->GetTotalDissonance());
  5929. if(npc->GetTotalSavagery() == 0){
  5930. npc->SetTotalSavagery(15*npc->GetLevel() + 1);
  5931. npc->SetSavagery(15*npc->GetLevel() + 1);
  5932. }
  5933. if(npc->GetTotalDissonance() == 0){
  5934. npc->SetTotalDissonance(15*npc->GetLevel() + 1);
  5935. npc->SetDissonance(15*npc->GetLevel() + 1);
  5936. }
  5937. npc->SetPrefixTitle(result.GetString(72));
  5938. npc->SetSuffixTitle(result.GetString(73));
  5939. npc->SetLastName(result.GetString(74));
  5940. npc->SetSoundsDisabled(result.GetInt8(75));
  5941. npc->SetMerchantLevelRange(result.GetInt32(76), result.GetInt32(77));
  5942. npc->SetAAXPRewards(result.GetInt32(78));
  5943. zone->AddNPC(id, npc);
  5944. //skipped spells/skills/equipment as it is all loaded, the following rely on a spawn to load
  5945. LoadAppearance(zone, spawn_id);
  5946. LoadNPCAppearanceEquipmentData(zone, spawn_id);
  5947. LogWrite(NPC__DEBUG, 0, "NPC", "Loaded NPC: '%s' (%u).", npc->appearance.name, spawn_id);
  5948. return true;
  5949. }
  5950. LogWrite(NPC__DEBUG, 0, "NPC", "Unable to find a npc for spawn id of %u", spawn_id);
  5951. return false;
  5952. }
  5953. void WorldDatabase::LoadAppearance(ZoneServer* zone, int32 spawn_id) {
  5954. Entity* entity = zone->GetNPC(spawn_id);
  5955. if (!entity)
  5956. return;
  5957. DatabaseResult result, result2;
  5958. map<string, int8> appearance_types;
  5959. map<int32, map<int8, EQ2_Color> > appearance_colors;
  5960. EQ2_Color color;
  5961. color.red = 0;
  5962. color.green = 0;
  5963. color.blue = 0;
  5964. string type;
  5965. database_new.Select(&result2, "SELECT distinct `type`\n"
  5966. "FROM npc_appearance\n"
  5967. "WHERE length(`type`) > 0 AND `spawn_id` = %u",
  5968. spawn_id);
  5969. while(result2.Next()) {
  5970. type = string(result2.GetString(0));
  5971. appearance_types[type] = GetAppearanceType(type);
  5972. if(appearance_types[type] == 255)
  5973. LogWrite(WORLD__ERROR, 0, "Appearance", "Unknown appearance type '%s' in LoadAppearances.", type.c_str());
  5974. }
  5975. database_new.Select(&result, "SELECT `type`, `signed_value`, `red`, `green`, `blue`\n"
  5976. "FROM npc_appearance\n"
  5977. "WHERE length(`type`) > 0 AND `spawn_id` = %u",
  5978. spawn_id);
  5979. while(result.Next()) {
  5980. if(appearance_types[result.GetString(0)] < APPEARANCE_SOGA_EBT){
  5981. color.red = result.GetInt8(2);
  5982. color.green = result.GetInt8(3);
  5983. color.blue = result.GetInt8(4);
  5984. }
  5985. switch(appearance_types[result.GetString(0)]){
  5986. case APPEARANCE_SOGA_HFHC:{
  5987. entity->features.soga_hair_face_highlight_color = color;
  5988. break;
  5989. }
  5990. case APPEARANCE_SOGA_HTHC:{
  5991. entity->features.soga_hair_type_highlight_color = color;
  5992. break;
  5993. }
  5994. case APPEARANCE_SOGA_HFC:{
  5995. entity->features.soga_hair_face_color = color;
  5996. break;
  5997. }
  5998. case APPEARANCE_SOGA_HTC:{
  5999. entity->features.soga_hair_type_color = color;
  6000. break;
  6001. }
  6002. case APPEARANCE_SOGA_HH:{
  6003. entity->features.soga_hair_highlight_color = color;
  6004. break;
  6005. }
  6006. case APPEARANCE_SOGA_HC1:{
  6007. entity->features.soga_hair_color1 = color;
  6008. break;
  6009. }
  6010. case APPEARANCE_SOGA_HC2:{
  6011. entity->features.soga_hair_color2 = color;
  6012. break;
  6013. }
  6014. case APPEARANCE_SOGA_SC:{
  6015. entity->features.soga_skin_color = color;
  6016. break;
  6017. }
  6018. case APPEARANCE_SOGA_EC:{
  6019. entity->features.soga_eye_color = color;
  6020. break;
  6021. }
  6022. case APPEARANCE_HTHC:{
  6023. entity->features.hair_type_highlight_color = color;
  6024. break;
  6025. }
  6026. case APPEARANCE_HFHC:{
  6027. entity->features.hair_face_highlight_color = color;
  6028. break;
  6029. }
  6030. case APPEARANCE_HTC:{
  6031. entity->features.hair_type_color = color;
  6032. break;
  6033. }
  6034. case APPEARANCE_HFC:{
  6035. entity->features.hair_face_color = color;
  6036. break;
  6037. }
  6038. case APPEARANCE_HH:{
  6039. entity->features.hair_highlight_color = color;
  6040. break;
  6041. }
  6042. case APPEARANCE_HC1:{
  6043. entity->features.hair_color1 = color;
  6044. break;
  6045. }
  6046. case APPEARANCE_HC2:{
  6047. entity->features.hair_color2 = color;
  6048. break;
  6049. }
  6050. case APPEARANCE_WC1:{
  6051. entity->features.wing_color1 = color;
  6052. break;
  6053. }
  6054. case APPEARANCE_WC2:{
  6055. entity->features.wing_color2 = color;
  6056. break;
  6057. }
  6058. case APPEARANCE_SC:{
  6059. entity->features.skin_color = color;
  6060. break;
  6061. }
  6062. case APPEARANCE_EC:{
  6063. entity->features.eye_color = color;
  6064. break;
  6065. }
  6066. case APPEARANCE_SOGA_EBT:{
  6067. for(int i=0;i<3;i++)
  6068. entity->features.soga_eye_brow_type[i] = result.GetInt8(2+i);
  6069. break;
  6070. }
  6071. case APPEARANCE_SOGA_CHEEKT:{
  6072. for(int i=0;i<3;i++)
  6073. entity->features.soga_cheek_type[i] = result.GetInt8(2+i);
  6074. break;
  6075. }
  6076. case APPEARANCE_SOGA_NT:{
  6077. for(int i=0;i<3;i++)
  6078. entity->features.soga_nose_type[i] = result.GetInt8(2+i);
  6079. break;
  6080. }
  6081. case APPEARANCE_SOGA_CHINT:{
  6082. for(int i=0;i<3;i++)
  6083. entity->features.soga_chin_type[i] = result.GetInt8(2+i);
  6084. break;
  6085. }
  6086. case APPEARANCE_SOGA_LT:{
  6087. for(int i=0;i<3;i++)
  6088. entity->features.soga_lip_type[i] = result.GetInt8(2+i);
  6089. break;
  6090. }
  6091. case APPEARANCE_SOGA_EART:{
  6092. for(int i=0;i<3;i++)
  6093. entity->features.soga_ear_type[i] = result.GetInt8(2+i);
  6094. break;
  6095. }
  6096. case APPEARANCE_SOGA_EYET:{
  6097. for(int i=0;i<3;i++)
  6098. entity->features.soga_eye_type[i] = result.GetInt8(2+i);
  6099. break;
  6100. }
  6101. case APPEARANCE_EBT:{
  6102. for(int i=0;i<3;i++)
  6103. entity->features.eye_brow_type[i] = result.GetInt8(2+i);
  6104. break;
  6105. }
  6106. case APPEARANCE_CHEEKT:{
  6107. for(int i=0;i<3;i++)
  6108. entity->features.cheek_type[i] = result.GetInt8(2+i);
  6109. break;
  6110. }
  6111. case APPEARANCE_NT:{
  6112. for(int i=0;i<3;i++)
  6113. entity->features.nose_type[i] = result.GetInt8(2+i);
  6114. break;
  6115. }
  6116. case APPEARANCE_CHINT:{
  6117. for(int i=0;i<3;i++)
  6118. entity->features.chin_type[i] = result.GetInt8(2+i);
  6119. break;
  6120. }
  6121. case APPEARANCE_EART:{
  6122. for(int i=0;i<3;i++)
  6123. entity->features.ear_type[i] = result.GetInt8(2+i);
  6124. break;
  6125. }
  6126. case APPEARANCE_EYET:{
  6127. for(int i=0;i<3;i++)
  6128. entity->features.eye_type[i] = result.GetInt8(2+i);
  6129. break;
  6130. }
  6131. case APPEARANCE_LT:{
  6132. for(int i=0;i<3;i++)
  6133. entity->features.lip_type[i] = result.GetInt8(2+i);
  6134. break;
  6135. }
  6136. case APPEARANCE_SHIRT:{
  6137. entity->features.shirt_color = color;
  6138. break;
  6139. }
  6140. case APPEARANCE_UCC:{
  6141. break;
  6142. }
  6143. case APPEARANCE_PANTS:{
  6144. entity->features.pants_color = color;
  6145. break;
  6146. }
  6147. case APPEARANCE_ULC:{
  6148. break;
  6149. }
  6150. case APPEARANCE_U9:{
  6151. break;
  6152. }
  6153. case APPEARANCE_BODY_SIZE:{
  6154. entity->features.body_size = color.red;
  6155. break;
  6156. }
  6157. case APPEARANCE_SOGA_WC1:{
  6158. break;
  6159. }
  6160. case APPEARANCE_SOGA_WC2:{
  6161. break;
  6162. }
  6163. case APPEARANCE_SOGA_SHIRT:{
  6164. break;
  6165. }
  6166. case APPEARANCE_SOGA_UCC:{
  6167. break;
  6168. }
  6169. case APPEARANCE_SOGA_PANTS:{
  6170. break;
  6171. }
  6172. case APPEARANCE_SOGA_ULC:{
  6173. break;
  6174. }
  6175. case APPEARANCE_SOGA_U13:{
  6176. break;
  6177. }
  6178. case APPEARANCE_MC:{
  6179. entity->features.model_color = color;
  6180. break;
  6181. }
  6182. case APPEARANCE_SMC:{
  6183. entity->features.soga_model_color = color;
  6184. break;
  6185. }
  6186. }
  6187. }
  6188. entity->info_changed = true;
  6189. }
  6190. void WorldDatabase::LoadNPCAppearanceEquipmentData(ZoneServer* zone, int32 spawn_id) {
  6191. NPC* npc = zone->GetNPC(spawn_id);
  6192. if(!npc) {
  6193. LogWrite(NPC__ERROR, 0, "NPC", "Unable to get a valid npc (%u) in %s", spawn_id, __FUNCTION__);
  6194. return;
  6195. }
  6196. DatabaseResult result;
  6197. int8 slot = 0;
  6198. if (!database_new.Select(&result, "SELECT slot_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue\n"
  6199. "FROM npc_appearance_equip\n"
  6200. "WHERE spawn_id = %u\n",
  6201. spawn_id))
  6202. {
  6203. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6204. return;
  6205. }
  6206. while (result.Next()) {
  6207. slot = result.GetInt8(0);
  6208. if(slot < NUM_SLOTS) {
  6209. npc->SetEquipment(slot, result.GetInt16(1), result.GetInt8(2), result.GetInt8(3), result.GetInt8(4), result.GetInt8(5), result.GetInt8(6), result.GetInt8(7));
  6210. }
  6211. }
  6212. }
  6213. void WorldDatabase::SaveCharacterPicture(int32 characterID, int8 type, uchar* picture, int32 picture_size) {
  6214. stringstream ss_hex;
  6215. stringstream ss_query;
  6216. ss_hex.flags(ios::hex);
  6217. for (int32 i = 0; i < picture_size; i++)
  6218. ss_hex << setfill('0') << setw(2) << (int32)picture[i];
  6219. ss_query << "INSERT INTO `character_pictures` (`char_id`, `pic_type`, `picture`) VALUES (" << characterID << ", " << (int32)type << ", '" << ss_hex.str() << "') ON DUPLICATE KEY UPDATE `picture` = '" << ss_hex.str() << "'";
  6220. if (!database_new.Query(ss_query.str().c_str()))
  6221. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6222. }
  6223. void WorldDatabase::LoadZoneFlightPaths(ZoneServer* zone) {
  6224. DatabaseResult result;
  6225. int32 total = 0;
  6226. if (!database_new.Select(&result, "SELECT id, speed, flying, early_dismount FROM flight_paths WHERE zone_id = %u", zone->GetZoneID())) {
  6227. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6228. return;
  6229. }
  6230. while (result.Next()) {
  6231. FlightPathInfo* info = new FlightPathInfo;
  6232. int32 id = result.GetInt32(0);
  6233. info->speed = result.GetFloat(1);
  6234. info->flying = result.GetInt8(2) == 1 ? true : false;
  6235. info->dismount = result.GetInt8(3) == 1 ? true : false;
  6236. zone->AddFlightPath(id, info);
  6237. total++;
  6238. }
  6239. LogWrite(ZONE__DEBUG, 0, "Zone", "Loaded %u flight paths for %s", total, zone->GetZoneDescription());
  6240. LoadZoneFlightPathLocations(zone);
  6241. }
  6242. void WorldDatabase::LoadZoneFlightPathLocations(ZoneServer* zone) {
  6243. DatabaseResult result;
  6244. int32 total = 0;
  6245. if (!database_new.Select(&result, "SELECT loc.flight_path, loc.x, loc.y, loc.z FROM flight_paths_locations loc\n"
  6246. "INNER JOIN flight_paths path\n"
  6247. "ON loc.flight_path = path.id\n"
  6248. "WHERE path.zone_id = %u\n"
  6249. "ORDER BY loc.id",
  6250. zone->GetZoneID()))
  6251. {
  6252. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6253. return;
  6254. }
  6255. while (result.Next()) {
  6256. FlightPathLocation* loc = new FlightPathLocation;
  6257. int32 id = result.GetInt32(0);
  6258. loc->X = result.GetFloat(1);
  6259. loc->Y = result.GetFloat(2);
  6260. loc->Z = result.GetFloat(3);
  6261. zone->AddFlightPathLocation(id, loc);
  6262. total++;
  6263. }
  6264. LogWrite(ZONE__DEBUG, 0, "Zone", "Loaded %u flight path locations for %s", total, zone->GetZoneDescription());
  6265. }
  6266. void WorldDatabase::SaveCharacterLUAHistory(Player* player, int32 event_id, int32 value, int32 value2) {
  6267. Query query;
  6268. query.AddQueryAsync(player->GetCharacterID(), this, Q_REPLACE, "REPLACE INTO character_lua_history(char_id, event_id, value, value2) VALUES(% u, % u, % u, % u)", player->GetCharacterID(), event_id, value, value2);
  6269. // if (!database_new.Query("REPLACE INTO character_lua_history (char_id, event_id, value, value2) VALUES (%u, %u, %u, %u)", player->GetCharacterID(), event_id, value, value2))
  6270. // LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6271. }
  6272. void WorldDatabase::LoadCharacterLUAHistory(int32 char_id, Player* player) {
  6273. DatabaseResult result;
  6274. int32 total = 0;
  6275. if (!database_new.Select(&result, "SELECT event_id, value, value2 FROM character_lua_history WHERE char_id = %u", char_id)) {
  6276. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6277. return;
  6278. }
  6279. while (result.Next()) {
  6280. int32 id = result.GetInt32(0);
  6281. LUAHistory* hd = new LUAHistory;
  6282. hd->Value = result.GetInt32(1);
  6283. hd->Value2 = result.GetInt32(2);
  6284. hd->SaveNeeded = false;
  6285. player->LoadLUAHistory(id, hd);
  6286. total++;
  6287. }
  6288. LogWrite(PLAYER__DEBUG, 0, "Player", "Loaded %u LUA history for %s", total, player->GetName());
  6289. }
  6290. void WorldDatabase::FindSpell(Client* client, char* findString)
  6291. {
  6292. DatabaseResult result;
  6293. if (!database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `tier` "
  6294. "FROM (spells s, spell_tiers st) "
  6295. "LEFT JOIN spell_ts_ability_index ts "
  6296. "ON s.`id` = ts.spell_id "
  6297. "WHERE s.id = st.spell_id and s.name like '%%%s%%' AND s.is_active = 1 "
  6298. "ORDER BY s.`id`, `tier` limit 50", findString))
  6299. {
  6300. // error
  6301. }
  6302. else
  6303. {
  6304. client->Message(CHANNEL_COLOR_YELLOW, "SpellID (SpellTier): SpellName for %s", findString);
  6305. while (result.Next())
  6306. {
  6307. int32 spell_id = result.GetInt32Str("id");
  6308. string spell_name = result.GetStringStr("name");
  6309. int8 tier = result.GetInt8Str("tier");
  6310. client->Message(CHANNEL_COLOR_YELLOW, "%i (%i): %s", spell_id, tier, spell_name.c_str());
  6311. }
  6312. client->Message(CHANNEL_COLOR_YELLOW, "End Spell Results for %s", findString);
  6313. }
  6314. }
  6315. void WorldDatabase::LoadChestTraps() {
  6316. chest_trap_list.Clear();
  6317. int32 index = 0;
  6318. Query query;
  6319. MYSQL_ROW row;
  6320. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, applicable_zone_id, chest_min_difficulty, chest_max_difficulty, spell_id, spell_tier FROM chest_traps");
  6321. if (result && mysql_num_rows(result) > 0) {
  6322. Title* title = 0;
  6323. while (result && (row = mysql_fetch_row(result))) {
  6324. int32 dbid = atoul(row[0]);
  6325. sint32 applicable_zone_id = atoi(row[1]);
  6326. int32 mindifficulty = atoul(row[2]);
  6327. int32 maxdifficulty = atoul(row[3]);
  6328. int32 spellid = atoul(row[4]);
  6329. int32 tier = atoul(row[5]);
  6330. ChestTrap* trap = new ChestTrap(dbid,applicable_zone_id,mindifficulty,maxdifficulty,spellid,tier);
  6331. chest_trap_list.AddChestTrap(trap);
  6332. }
  6333. }
  6334. }
  6335. bool WorldDatabase::CheckExpansionFlags(ZoneServer* zone, int32 spawnXpackFlag)
  6336. {
  6337. if (spawnXpackFlag == 0)
  6338. return true;
  6339. int32 globalXpackFlag = rule_manager.GetGlobalRule(R_Expansion, GlobalExpansionFlag)->GetInt32();
  6340. int32 zoneXpackFlag = zone->GetExpansionFlag();
  6341. // zone expansion flag takes priority
  6342. if (zoneXpackFlag > 0 && (spawnXpackFlag & zoneXpackFlag) == 0)
  6343. return false;
  6344. // zone expansion flag fails, then if global expansion flag set, we see if that bit operand doesn't match, skip mob then
  6345. else if (zoneXpackFlag == 0 && globalXpackFlag > 0 && (spawnXpackFlag & globalXpackFlag) == 0)
  6346. return false;
  6347. return true;
  6348. }
  6349. bool WorldDatabase::CheckHolidayFlags(ZoneServer* zone, int32 spawnHolidayFlag)
  6350. {
  6351. if (spawnHolidayFlag == 0)
  6352. return true;
  6353. int32 globalHolidayFlag = rule_manager.GetGlobalRule(R_Expansion, GlobalHolidayFlag)->GetInt32();
  6354. int32 zoneHolidayFlag = zone->GetHolidayFlag();
  6355. // zone holiday flag takes priority
  6356. if (zoneHolidayFlag > 0 && (spawnHolidayFlag & zoneHolidayFlag) == 0)
  6357. return false;
  6358. // zone holiday flag fails, then if global expansion flag set, we see if that bit operand doesn't match, skip mob then
  6359. else if (zoneHolidayFlag == 0 && globalHolidayFlag > 0 && (spawnHolidayFlag & globalHolidayFlag) == 0)
  6360. return false;
  6361. return true;
  6362. }
  6363. void WorldDatabase::GetHouseSpawnInstanceData(ZoneServer* zone, Spawn* spawn)
  6364. {
  6365. if (!spawn)
  6366. return;
  6367. if (zone->house_object_database_lookup.count(spawn->GetModelType()) < 1)
  6368. zone->house_object_database_lookup.Put(spawn->GetModelType(), spawn->GetDatabaseID());
  6369. DatabaseResult result;
  6370. database_new.Select(&result, "SELECT pickup_item_id, pickup_unique_item_id\n"
  6371. " FROM spawn_instance_data\n"
  6372. " WHERE spawn_id = %u and spawn_location_id = %u",
  6373. spawn->GetDatabaseID(),spawn->GetSpawnLocationID());
  6374. if (result.GetNumRows() > 0 && result.Next()) {
  6375. spawn->SetPickupItemID(result.GetInt32(0));
  6376. spawn->SetPickupUniqueItemID(result.GetInt32(1));
  6377. if (spawn->GetZone() != nullptr && spawn->GetMap() != nullptr && spawn->GetMap()->IsMapLoaded())
  6378. {
  6379. int32 newGrid = spawn->GetMap()->GetGrid()->GetGridID(spawn);
  6380. spawn->SetPos(&(spawn->appearance.pos.grid_id), newGrid);
  6381. }
  6382. }
  6383. }
  6384. int32 WorldDatabase::FindHouseInstanceSpawn(Spawn* spawn)
  6385. {
  6386. DatabaseResult result;
  6387. database_new.Select(&result, "SELECT id\n"
  6388. " FROM spawn\n"
  6389. " WHERE model_type = %u and is_instanced_spawn=1 limit 1",
  6390. spawn->GetModelType());
  6391. if (result.GetNumRows() > 0 && result.Next()) {
  6392. return result.GetInt32(0);
  6393. }
  6394. return 0;
  6395. }
  6396. void WorldDatabase::LoadStartingSkills(World* world)
  6397. {
  6398. world->MStartingLists.writelock();
  6399. int32 total = 0;
  6400. Query query;
  6401. MYSQL_ROW row;
  6402. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_id, class_id, skill_id, current_val, max_val, progress FROM starting_skills");
  6403. if (result)
  6404. {
  6405. if (mysql_num_rows(result) > 0)
  6406. {
  6407. Skill* skill = 0;
  6408. while (result && (row = mysql_fetch_row(result)))
  6409. {
  6410. StartingSkill skill;
  6411. skill.header.race_id = atoul(row[0]);
  6412. skill.header.class_id = atoul(row[1]);
  6413. skill.skill_id = atoul(row[2]);
  6414. skill.current_val = atoul(row[3]);
  6415. skill.max_val = atoul(row[4]);
  6416. if (!world->starting_skills.count(skill.header.race_id))
  6417. {
  6418. multimap<int8, StartingSkill>* skills = new multimap<int8, StartingSkill>();
  6419. skills->insert(make_pair(skill.header.class_id, skill));
  6420. world->starting_skills.insert(make_pair(skill.header.race_id, skills));
  6421. }
  6422. else
  6423. {
  6424. multimap<int8, multimap<int8, StartingSkill>*>::const_iterator skills = world->starting_skills.find(skill.header.race_id);
  6425. skills->second->insert(make_pair(skill.header.class_id, skill));
  6426. }
  6427. total++;
  6428. }
  6429. }
  6430. }
  6431. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u Starting Skill(s)", total);
  6432. world->MStartingLists.releasewritelock();
  6433. }
  6434. void WorldDatabase::LoadStartingSpells(World* world)
  6435. {
  6436. world->MStartingLists.writelock();
  6437. int32 total = 0;
  6438. Query query;
  6439. MYSQL_ROW row;
  6440. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_id, class_id, spell_id, tier, knowledge_slot FROM starting_spells");
  6441. if (result)
  6442. {
  6443. if (mysql_num_rows(result) > 0)
  6444. {
  6445. Skill* skill = 0;
  6446. while (result && (row = mysql_fetch_row(result)))
  6447. {
  6448. StartingSpell spell;
  6449. spell.header.race_id = atoul(row[0]);
  6450. spell.header.class_id = atoul(row[1]);
  6451. spell.spell_id = atoul(row[2]);
  6452. spell.tier = atoul(row[3]);
  6453. spell.knowledge_slot = atoul(row[4]);
  6454. if (!world->starting_spells.count(spell.header.race_id))
  6455. {
  6456. multimap<int8, StartingSpell>* spells = new multimap<int8, StartingSpell>();
  6457. spells->insert(make_pair(spell.header.class_id, spell));
  6458. world->starting_spells.insert(make_pair(spell.header.race_id, spells));
  6459. }
  6460. else
  6461. {
  6462. multimap<int8, multimap<int8, StartingSpell>*>::iterator spells = world->starting_spells.find(spell.header.race_id);
  6463. spells->second->insert(make_pair(spell.header.class_id, spell));
  6464. }
  6465. total++;
  6466. }
  6467. }
  6468. }
  6469. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u Starting Spell(s)", total);
  6470. world->MStartingLists.releasewritelock();
  6471. }
  6472. bool WorldDatabase::DeleteSpiritShard(int32 id){
  6473. Query query;
  6474. query.RunQuery2(Q_DELETE, "delete FROM character_spirit_shards where id=%u",id);
  6475. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  6476. LogWrite(WORLD__ERROR, 0, "World", "Error in DeleteSpiritShard query '%s': %s", query.GetQuery(), query.GetError());
  6477. return false;
  6478. }
  6479. return true;
  6480. }
  6481. int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race, int8 gender, int8 adventure_class,
  6482. int16 model_type, int16 soga_model_type, int16 hair_type, int16 hair_face_type, int16 wing_type,
  6483. int16 chest_type, int16 legs_type, int16 soga_hair_type, int16 soga_hair_face_type, int8 hide_hood,
  6484. int16 size, int16 collision_radius, int16 action_state, int16 visual_state, int16 mood_state, int16 emote_state,
  6485. int16 pos_state, int16 activity_status, char* sub_title, char* prefix_title, char* suffix_title, char* lastname,
  6486. float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid)
  6487. {
  6488. LogWrite(WORLD__INFO, 3, "World", "Saving Spirit Shard %s %u", name, charid);
  6489. Query query;
  6490. char* name_escaped = getEscapeString(name);
  6491. if(!sub_title)
  6492. sub_title = "";
  6493. char* subtitle_escaped = getEscapeString(sub_title);
  6494. char* prefix_escaped = getEscapeString(prefix_title);
  6495. char* suffix_escaped = getEscapeString(suffix_title);
  6496. char* lastname_escaped = getEscapeString(lastname);
  6497. string insert = string("INSERT INTO character_spirit_shards (name, level, race, gender, adventure_class, model_type, soga_model_type, hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type, soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state, mood_state, emote_state, pos_state, activity_status, sub_title, prefix_title, suffix_title, lastname, x, y, z, heading, gridid, charid, zoneid, instanceid) VALUES ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s', '%s', '%s', '%s', %f, %f, %f, %f, %u, %u, %u, %u)");
  6498. query.RunQuery2(Q_INSERT, insert.c_str(), name_escaped, level, race, gender, adventure_class, model_type, soga_model_type,
  6499. hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type,
  6500. soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state,
  6501. mood_state, emote_state, pos_state, activity_status, subtitle_escaped, prefix_escaped, suffix_escaped,
  6502. lastname_escaped, x, y, z, heading, gridid, charid, zoneid, instanceid);
  6503. safe_delete_array(name_escaped);
  6504. safe_delete_array(subtitle_escaped);
  6505. safe_delete_array(prefix_escaped);
  6506. safe_delete_array(suffix_escaped);
  6507. safe_delete_array(lastname_escaped);
  6508. return query.GetLastInsertedID();
  6509. }
  6510. void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type)
  6511. {
  6512. SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
  6513. Player* player = client->GetPlayer();
  6514. if(!spellProcess)
  6515. return;
  6516. DatabaseResult result;
  6517. multimap<LuaSpell*, Entity*> restoreSpells;
  6518. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  6519. if (!database_new.Select(&result, "SELECT name, caster_char_id, target_char_id, target_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function FROM character_spell_effects WHERE charid = %u and db_effect_type = %u", char_id, db_spell_type)) {
  6520. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6521. return;
  6522. }
  6523. InfoStruct* info = player->GetInfoStruct();
  6524. while (result.Next()) {
  6525. //result.GetInt8Str
  6526. char spell_name[60];
  6527. strncpy(spell_name, result.GetStringStr("name"), 60);
  6528. int32 caster_char_id = result.GetInt32Str("caster_char_id");
  6529. int32 target_char_id = result.GetInt32Str("target_char_id");
  6530. int8 target_type = result.GetInt8Str("target_type");
  6531. int32 spell_id = result.GetInt32Str("spell_id");
  6532. int32 effect_slot = result.GetInt32Str("effect_slot");
  6533. int32 slot_pos = result.GetInt32Str("slot_pos");
  6534. int16 icon = result.GetInt32Str("icon");
  6535. int16 icon_backdrop = result.GetInt32Str("icon_backdrop");
  6536. int8 conc_used = result.GetInt32Str("conc_used");
  6537. int8 tier = result.GetInt32Str("tier");
  6538. float total_time = result.GetFloatStr("total_time");
  6539. int32 expire_timestamp = result.GetInt32Str("expire_timestamp");
  6540. string lua_file (result.GetStringStr("lua_file"));
  6541. int8 custom_spell = result.GetInt32Str("custom_spell");
  6542. int32 damage_remaining = result.GetInt32Str("damage_remaining");
  6543. int32 effect_bitmask = result.GetInt32Str("effect_bitmask");
  6544. int16 num_triggers = result.GetInt32Str("num_triggers");
  6545. int8 had_triggers = result.GetInt32Str("had_triggers");
  6546. int8 cancel_after_triggers = result.GetInt32Str("cancel_after_triggers");
  6547. int8 crit = result.GetInt32Str("crit");
  6548. int8 last_spellattack_hit = result.GetInt32Str("last_spellattack_hit");
  6549. int8 interrupted = result.GetInt32Str("interrupted");
  6550. int8 resisted = result.GetInt32Str("resisted");
  6551. std::string custom_function = std::string(result.GetStringStr("custom_function"));
  6552. LuaSpell* lua_spell = 0;
  6553. if(custom_spell)
  6554. {
  6555. if((lua_spell = lua_interface->GetSpell(lua_file.c_str())) == nullptr)
  6556. {
  6557. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), custom lua script not loaded, when attempting to load.", spell_id, tier, lua_file.c_str());
  6558. lua_interface->LoadLuaSpell(lua_file);
  6559. }
  6560. }
  6561. Spell* spell = master_spell_list.GetSpell(spell_id, tier);
  6562. if(!spell)
  6563. {
  6564. LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u), spell could not be found!", spell_id, tier);
  6565. spell = master_spell_list.GetSpell(spell_id, 0);
  6566. if(spell)
  6567. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u), identified tier 0 as replacement since the GetSpell failed!", spell_id, tier);
  6568. else
  6569. continue;
  6570. }
  6571. bool isMaintained = false;
  6572. bool isExistingLuaSpell = false;
  6573. MaintainedEffects* effect = nullptr;
  6574. Client* tmpCaster = nullptr;
  6575. if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id)) != nullptr)
  6576. {
  6577. safe_delete(lua_spell);
  6578. lua_spell = effect->spell;
  6579. if(lua_spell)
  6580. spell = lua_spell->spell;
  6581. isMaintained = true;
  6582. isExistingLuaSpell = true;
  6583. }
  6584. else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
  6585. && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr)
  6586. {
  6587. if(effect->spell && effect->spell_id == spell_id)
  6588. {
  6589. safe_delete(lua_spell);
  6590. effect->spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6591. if(tmpCaster->GetCurrentZone() == player->GetZone())
  6592. effect->spell->targets.push_back(client->GetPlayer()->GetID());
  6593. effect->spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6594. lua_spell = effect->spell;
  6595. spell = effect->spell->spell;
  6596. isExistingLuaSpell = true;
  6597. isMaintained = true;
  6598. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), effect spell id %u maintained spell recovered from %s", spell_id, tier, spell_name, effect ? effect->spell_id : 0, (tmpCaster && tmpCaster->GetPlayer()) ? tmpCaster->GetPlayer()->GetName() : "?");
  6599. }
  6600. else
  6601. {
  6602. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), something went wrong loading another characters maintained spell. Effect has spell id %u", spell_id, tier, lua_file.c_str(), effect ? effect->spell_id : 0);
  6603. safe_delete(lua_spell);
  6604. continue;
  6605. }
  6606. }
  6607. else if(custom_spell && lua_spell)
  6608. {
  6609. lua_spell->spell = new Spell(spell);
  6610. lua_interface->AddCustomSpell(lua_spell);
  6611. }
  6612. else if(db_spell_type == DB_TYPE_MAINTAINEDEFFECTS)
  6613. {
  6614. safe_delete(lua_spell);
  6615. if(!target_char_id)
  6616. continue;
  6617. lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
  6618. if(lua_spell)
  6619. lua_spell->spell = spell;
  6620. }
  6621. if(!lua_spell)
  6622. {
  6623. LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), lua_spell FAILED, when attempting to load.", spell_id, tier, lua_file.c_str());
  6624. continue;
  6625. }
  6626. SpellScriptTimer* timer = nullptr;
  6627. if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && custom_function.size() > 0)
  6628. {
  6629. timer = new SpellScriptTimer;
  6630. timer->caster = 0;
  6631. timer->deleteWhenDone = false;
  6632. timer->target = 0;
  6633. timer->time = expire_timestamp;
  6634. timer->customFunction = string(custom_function); // TODO
  6635. timer->spell = lua_spell;
  6636. timer->caster = (caster_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6637. if(target_char_id == 0xFFFFFFFF && player->HasPet())
  6638. timer->target = player->GetPet()->GetID();
  6639. else
  6640. timer->target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6641. if(!timer->target && target_char_id)
  6642. {
  6643. Client* tmpClient = zone_list.GetClientByCharID(target_char_id);
  6644. if(tmpClient && tmpClient->GetPlayer() && tmpClient->GetPlayer()->GetZone() == player->GetZone())
  6645. timer->target = tmpClient->GetPlayer()->GetID();
  6646. }
  6647. }
  6648. if(!isExistingLuaSpell)
  6649. {
  6650. lua_spell->crit = crit;
  6651. lua_spell->damage_remaining = damage_remaining;
  6652. lua_spell->effect_bitmask = effect_bitmask;
  6653. lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
  6654. lua_spell->had_triggers = had_triggers;
  6655. lua_spell->initial_caster_char_id = caster_char_id;
  6656. lua_spell->initial_target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6657. lua_spell->initial_target_char_id = target_char_id;
  6658. lua_spell->interrupted = interrupted;
  6659. lua_spell->last_spellattack_hit = last_spellattack_hit;
  6660. lua_spell->num_triggers = num_triggers;
  6661. }
  6662. if(lua_spell->initial_target == 0 && target_char_id == 0xFFFFFFFF && player->HasPet())
  6663. {
  6664. lua_spell->initial_target = player->GetPet()->GetID();
  6665. lua_spell->initial_target_char_id = target_char_id;
  6666. }
  6667. //lua_spell->num_calls ??
  6668. //if(target_char_id == player->GetCharacterID())
  6669. // lua_spell->targets.push_back(player->GetID());
  6670. if(db_spell_type == DB_TYPE_SPELLEFFECTS)
  6671. {
  6672. if (caster_char_id != player->GetCharacterID() && lua_spell->spell->GetSpellData()->group_spell && lua_spell->spell->GetSpellData()->friendly_spell)
  6673. {
  6674. if(!isExistingLuaSpell)
  6675. safe_delete(lua_spell);
  6676. continue;
  6677. }
  6678. player->MSpellEffects.writelock();
  6679. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id);
  6680. if(lua_spell->caster && (rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
  6681. info->spell_effects[effect_slot].caster = lua_spell->caster;
  6682. else if(caster_char_id != player->GetCharacterID())
  6683. {
  6684. Client* tmpCaster = zone_list.GetClientByCharID(caster_char_id);
  6685. if(tmpCaster)
  6686. {
  6687. if((rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
  6688. {
  6689. info->spell_effects[effect_slot].caster = tmpCaster->GetPlayer();
  6690. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, found player %s.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, tmpCaster->GetPlayer()->GetName());
  6691. }
  6692. else
  6693. {
  6694. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, found player %s, SKIPPED due to R_Spells, EnableCrossZoneTargetBuffs.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, tmpCaster->GetPlayer()->GetName());
  6695. if(!isExistingLuaSpell)
  6696. {
  6697. safe_delete(lua_spell);
  6698. }
  6699. else
  6700. {
  6701. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6702. lua_spell->char_id_targets.insert(make_pair(player->GetCharacterID(),0));
  6703. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6704. }
  6705. player->MSpellEffects.releasewritelock();
  6706. continue;
  6707. }
  6708. }
  6709. }
  6710. else if(caster_char_id == player->GetCharacterID())
  6711. info->spell_effects[effect_slot].caster = player;
  6712. else
  6713. {
  6714. LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, failed to find caster will delete: %u", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, isExistingLuaSpell);
  6715. if(!isExistingLuaSpell)
  6716. safe_delete(lua_spell);
  6717. continue;
  6718. }
  6719. info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
  6720. info->spell_effects[effect_slot].icon = icon;
  6721. info->spell_effects[effect_slot].icon_backdrop = icon_backdrop;
  6722. info->spell_effects[effect_slot].spell_id = spell_id;
  6723. info->spell_effects[effect_slot].tier = tier;
  6724. info->spell_effects[effect_slot].total_time = total_time;
  6725. info->spell_effects[effect_slot].spell = lua_spell;
  6726. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6727. multimap<int32,int8>::iterator entries;
  6728. while((entries = lua_spell->char_id_targets.find(player->GetCharacterID())) != lua_spell->char_id_targets.end())
  6729. {
  6730. lua_spell->char_id_targets.erase(entries);
  6731. }
  6732. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6733. lua_spell->slot_pos = slot_pos;
  6734. if(!isExistingLuaSpell)
  6735. lua_spell->caster = player; // TODO: get actual player
  6736. player->MSpellEffects.releasewritelock();
  6737. if(!isMaintained)
  6738. spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
  6739. else
  6740. {
  6741. // track target id when caster isnt in zone somehow
  6742. }
  6743. }
  6744. else if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
  6745. {
  6746. player->MMaintainedSpells.writelock();
  6747. DatabaseResult targets;
  6748. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  6749. if (database_new.Select(&targets, "SELECT target_char_id, target_type, db_effect_type, spell_id from character_spell_effect_targets where caster_char_id = %u and effect_slot = %u and slot_pos = %u", char_id, effect_slot, slot_pos)) {
  6750. while (targets.Next()) {
  6751. int32 target_char = targets.GetInt32Str("target_char_id");
  6752. int8 maintained_target_type = targets.GetInt32Str("target_type");
  6753. int32 in_spell_id = targets.GetInt32Str("spell_id");
  6754. if(spell_id != in_spell_id)
  6755. continue;
  6756. int32 idToAdd = 0;
  6757. if(target_char == 0xFFFFFFFF)
  6758. {
  6759. if( player->HasPet() )
  6760. {
  6761. restoreSpells.insert(make_pair(lua_spell, player->GetPet()));
  6762. // target added via restoreSpells
  6763. }
  6764. }
  6765. else
  6766. {
  6767. Client* client2 = zone_list.GetClientByCharID(target_char);
  6768. if(client2 && client2->GetPlayer() && client2->GetCurrentZone() == client->GetCurrentZone())
  6769. {
  6770. if(maintained_target_type > 0)
  6771. {
  6772. if(client != client2)
  6773. {
  6774. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6775. if(client2->GetPlayer()->GetPet() && maintained_target_type == PET_TYPE_COMBAT)
  6776. {
  6777. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetPet()));
  6778. // target added via restoreSpells
  6779. }
  6780. if(client2->GetPlayer()->GetCharmedPet() && maintained_target_type == PET_TYPE_CHARMED)
  6781. {
  6782. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetCharmedPet()));
  6783. // target added via restoreSpells
  6784. }
  6785. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6786. }
  6787. } // end of pet clause
  6788. else if(client != client2) // maintained type must be 0, so client
  6789. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()));
  6790. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6791. multimap<int32,int8>::iterator entries;
  6792. for(entries = lua_spell->char_id_targets.begin(); entries != lua_spell->char_id_targets.end(); entries++)
  6793. {
  6794. int32 ent_char_id = entries->first;
  6795. int8 ent_target_type = entries->second;
  6796. if(ent_char_id == target_char && ent_target_type == maintained_target_type)
  6797. entries = lua_spell->char_id_targets.erase(entries);
  6798. }
  6799. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6800. }
  6801. else
  6802. {
  6803. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6804. lua_spell->char_id_targets.insert(make_pair(target_char,maintained_target_type));
  6805. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6806. }
  6807. }
  6808. }
  6809. }
  6810. Client* tmpClient = 0;
  6811. int32 targetID = 0;
  6812. if(target_char_id == 0xFFFFFFFF && player->HasPet())
  6813. targetID = player->GetPet()->GetID();
  6814. else if(target_char_id == player->GetCharacterID())
  6815. {
  6816. targetID = player->GetID();
  6817. tmpClient = player->GetClient();
  6818. }
  6819. else if((tmpClient = zone_list.GetClientByCharID(target_char_id)) != nullptr && tmpClient->GetPlayer())
  6820. targetID = tmpClient->GetPlayer()->GetID();
  6821. info->maintained_effects[effect_slot].conc_used = conc_used;
  6822. strncpy(info->maintained_effects[effect_slot].name, spell_name, 60);
  6823. info->maintained_effects[effect_slot].slot_pos = slot_pos;
  6824. info->maintained_effects[effect_slot].target = targetID;
  6825. info->maintained_effects[effect_slot].target_type = target_type;
  6826. info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
  6827. info->maintained_effects[effect_slot].icon = icon;
  6828. info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop;
  6829. info->maintained_effects[effect_slot].spell_id = spell_id;
  6830. info->maintained_effects[effect_slot].tier = tier;
  6831. info->maintained_effects[effect_slot].total_time = total_time;
  6832. info->maintained_effects[effect_slot].spell = lua_spell;
  6833. if(!isExistingLuaSpell)
  6834. lua_spell->caster = player;
  6835. player->MMaintainedSpells.releasewritelock();
  6836. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s process spell caster %s (%u), caster char id: %u, target id %u (%s).", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "",
  6837. lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, targetID, tmpClient ? tmpClient->GetPlayer()->GetName() : "");
  6838. if(tmpClient && lua_spell->initial_target_char_id == tmpClient->GetCharacterID())
  6839. {
  6840. lua_spell->initial_target = tmpClient->GetPlayer()->GetID();
  6841. lua_spell->targets.push_back(lua_spell->initial_target);
  6842. }
  6843. spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
  6844. }
  6845. if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && !isMaintained)
  6846. {
  6847. lua_spell->timer.SetTimer(expire_timestamp);
  6848. lua_spell->timer.SetAtTrigger(lua_spell->spell->GetSpellData()->duration1 * 100);
  6849. lua_spell->timer.Start();
  6850. }
  6851. if(target_char_id == player->GetCharacterID() && lua_spell->spell->GetSpellData()->det_type)
  6852. player->AddDetrimentalSpell(lua_spell, expire_timestamp);
  6853. if(!isExistingLuaSpell)
  6854. {
  6855. if(timer)
  6856. spellProcess->AddSpellScriptTimer(timer);
  6857. lua_spell->num_calls = 1;
  6858. lua_spell->restored = true;
  6859. }
  6860. if(!lua_spell->resisted && (lua_spell->spell->GetSpellDuration() > 0 || lua_spell->spell->GetSpellData()->duration_until_cancel))
  6861. spellProcess->AddActiveSpell(lua_spell);
  6862. if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
  6863. {
  6864. if (num_triggers > 0)
  6865. ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, num_triggers, 0);
  6866. if (damage_remaining > 0)
  6867. ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, damage_remaining, 1);
  6868. }
  6869. }
  6870. if(db_spell_type == DB_TYPE_SPELLEFFECTS)
  6871. {
  6872. // if the cross_zone_target_buff option is disabled then we check for all possible targets within the current zone
  6873. // if cross_zone_target_buff is enabled, we only check to restore pets, the players get restored by their own spell effects (we don't directly track the pets, indirectly we do through the player casting and their targets)
  6874. int8 cross_zone_target_buff = rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8();
  6875. DatabaseResult targets;
  6876. if (
  6877. (!cross_zone_target_buff && database_new.Select(&targets, "SELECT caster_char_id, target_type, spell_id from character_spell_effect_targets where target_char_id = %u", player->GetCharacterID())) ||
  6878. (database_new.Select(&targets, "SELECT caster_char_id, target_type, spell_id from character_spell_effect_targets where target_char_id = %u and target_type > 0", player->GetCharacterID())))
  6879. {
  6880. while (targets.Next()) {
  6881. int32 caster_char_id = targets.GetInt32Str("caster_char_id");
  6882. int8 prev_target_type = targets.GetInt32Str("target_type");
  6883. int32 in_spell_id = targets.GetInt32Str("spell_id");
  6884. Client* tmpCaster = nullptr;
  6885. MaintainedEffects* effect = nullptr;
  6886. if (caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr && (cross_zone_target_buff ||
  6887. tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id)) != nullptr)
  6888. {
  6889. if(prev_target_type > 0)
  6890. {
  6891. if(player->HasPet())
  6892. {
  6893. if(player->GetPet() && prev_target_type == PET_TYPE_COMBAT)
  6894. restoreSpells.insert(make_pair(effect->spell, player->GetPet()));
  6895. if(player->GetCharmedPet() && prev_target_type == PET_TYPE_CHARMED)
  6896. restoreSpells.insert(make_pair(effect->spell, player->GetCharmedPet()));
  6897. }
  6898. }
  6899. else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer()))
  6900. {
  6901. if(effect->spell->initial_target_char_id == player->GetCharacterID())
  6902. effect->spell->initial_target = player->GetID();
  6903. restoreSpells.insert(make_pair(effect->spell, player));
  6904. }
  6905. }
  6906. }
  6907. }
  6908. }
  6909. multimap<LuaSpell*, Entity*>::const_iterator itr;
  6910. for (itr = restoreSpells.begin(); itr != restoreSpells.end(); itr++)
  6911. {
  6912. LuaSpell* tmpSpell = itr->first;
  6913. Entity* target = itr->second;
  6914. if(!target)
  6915. {
  6916. target = client->GetPlayer()->GetPet();
  6917. if(!target)
  6918. continue;
  6919. }
  6920. Entity* caster = tmpSpell->caster;
  6921. if(!caster)
  6922. caster = client->GetPlayer();
  6923. int32 group_id_target = target->group_id;
  6924. if(target->IsPet() && target->GetOwner())
  6925. group_id_target = target->GetOwner()->group_id;
  6926. if(caster != target && caster->GetPet() != target &&
  6927. tmpSpell->spell->GetSpellData()->group_spell && tmpSpell->spell->GetSpellData()->friendly_spell && (caster->group_id == 0 || group_id_target == 0 || caster->group_id != group_id_target))
  6928. {
  6929. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s player no longer grouped with %s to reload bonuses for spell %s (target_groupid: %u, caster_groupid: %u).", target->GetName(), caster ? caster->GetName() : "?", tmpSpell->spell->GetName(), group_id_target, caster->group_id);
  6930. continue;
  6931. }
  6932. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s using caster %s to reload bonuses for spell %s.", player->GetName(), caster ? caster->GetName() : "?", tmpSpell->spell->GetName());
  6933. tmpSpell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6934. tmpSpell->targets.push_back(target->GetID());
  6935. tmpSpell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6936. target->AddSpellEffect(tmpSpell, tmpSpell->timer.GetRemainingTime() != 0 ? tmpSpell->timer.GetRemainingTime() : 0);
  6937. vector<BonusValues*>* sb_list = caster->GetAllSpellBonuses(tmpSpell);
  6938. for (int32 x = 0; x < sb_list->size(); x++) {
  6939. BonusValues* bv = sb_list->at(x);
  6940. target->AddSpellBonus(tmpSpell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req);
  6941. }
  6942. sb_list->clear();
  6943. safe_delete(sb_list);
  6944. // look for a skill bonus on the caster's spell
  6945. if(caster->IsPlayer())
  6946. {
  6947. SkillBonus* sb = ((Player*)caster)->GetSkillBonus(tmpSpell->spell->GetSpellID());
  6948. if (sb) {
  6949. map<int32, SkillBonusValue*>::iterator itr_skills;
  6950. for (itr_skills = sb->skills.begin(); itr_skills != sb->skills.end(); itr_skills++)
  6951. target->AddSkillBonus(sb->spell_id, (*itr_skills).second->skill_id, (*itr_skills).second->value);
  6952. }
  6953. }
  6954. }
  6955. }