WorldDatabase.cpp 331 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058
  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. #include "races.h"
  46. extern Classes classes;
  47. extern Commands commands;
  48. extern MasterTitlesList master_titles_list;
  49. extern MasterItemList master_item_list;
  50. extern MasterSpellList master_spell_list;
  51. extern MasterTraitList master_trait_list;
  52. extern MasterFactionList master_faction_list;
  53. extern World world;
  54. extern Variables variables;
  55. extern VisualStates visual_states;
  56. extern Appearances master_appearance_list;
  57. extern MasterSkillList master_skill_list;
  58. extern MasterQuestList master_quest_list;
  59. extern LuaInterface* lua_interface;
  60. extern ZoneList zone_list;
  61. extern GuildList guild_list;
  62. extern MasterCollectionList master_collection_list;
  63. extern RuleManager rule_manager;
  64. extern MasterLanguagesList master_languages_list;
  65. extern ChestTrapList chest_trap_list;
  66. //devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
  67. #if defined(__GNUC__)
  68. #define stricmp strcasecmp
  69. #define strnicmp strncasecmp
  70. #include <sys/stat.h>
  71. #endif
  72. WorldDatabase::WorldDatabase(){
  73. }
  74. WorldDatabase::~WorldDatabase(){
  75. }
  76. bool WorldDatabase::ConnectNewDatabase() {
  77. /*
  78. TESTS
  79. database_new.Connect();
  80. DatabaseResult result;
  81. database_new.Select(&result, "select name from characters where id=1");
  82. if (result.Next()) {
  83. printf("'%s'\n", result.GetStringStr("name"));
  84. printf("'%s'\n", result.GetStringStr("nameBAD"));
  85. printf("'%s'\n", result.GetString(3));
  86. }
  87. return true;
  88. */
  89. return database_new.Connect();
  90. }
  91. void WorldDatabase::PingNewDB()
  92. {
  93. database_new.PingNewDB();
  94. }
  95. void WorldDatabase::DeleteBuyBack(int32 char_id, int32 item_id, int16 quantity, int32 price) {
  96. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Deleting Buyback - Player: %u, Item ID: %u, Qty: %i, Price: %u", char_id, item_id, quantity, price);
  97. Query query;
  98. 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);
  99. }
  100. void WorldDatabase::LoadBuyBacks(Client* client) {
  101. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Loading Buyback - Player: %u", client->GetCharacterID());
  102. Query query;
  103. MYSQL_ROW row;
  104. 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());
  105. int8 count = 0;
  106. int32 last_id = 0;
  107. if(result)
  108. {
  109. while(result && (row = mysql_fetch_row(result)))
  110. {
  111. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "AddBuyBack: item: %u, qty: %i, price: %u", atoul(row[1]), atoi(row[2]), atoul(row[3]));
  112. last_id = atoul(row[0]);
  113. client->AddBuyBack(last_id, atoul(row[1]), atoi(row[2]), atoul(row[3]), false);
  114. count++;
  115. }
  116. if(count >= 10)
  117. {
  118. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "Deleting excess Buyback from Player: %u", client->GetCharacterID());
  119. Query query2;
  120. query2.RunQuery2(Q_DELETE, "DELETE FROM character_buyback WHERE char_id = %u AND id < %u", client->GetCharacterID(), last_id);
  121. }
  122. }
  123. }
  124. void WorldDatabase::SaveBuyBacks(Client* client)
  125. {
  126. LogWrite(MERCHANT__DEBUG, 3, "Merchant", "Saving Buybacks - Player: %u", client->GetCharacterID());
  127. deque<BuyBackItem*>* buybacks = client->GetBuyBacks();
  128. if(buybacks && buybacks->size() > 0)
  129. {
  130. BuyBackItem* item = 0;
  131. deque<BuyBackItem*>::iterator itr;
  132. for(itr = buybacks->begin(); itr != buybacks->end(); itr++)
  133. {
  134. item = *itr;
  135. if(item && item->save_needed)
  136. {
  137. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "SaveBuyBack: char: %u, item: %u, qty: %i, price: %u", client->GetCharacterID(), item->item_id, item->quantity, item->price);
  138. SaveBuyBack(client->GetCharacterID(), item->item_id, item->quantity, item->price);
  139. item->save_needed = false;
  140. }
  141. }
  142. }
  143. }
  144. void WorldDatabase::SaveBuyBack(int32 char_id, int32 item_id, int16 quantity, int32 price)
  145. {
  146. LogWrite(MERCHANT__DEBUG, 3, "Merchant", "Saving Buyback - Player: %u, Item ID: %u, Qty: %i, Price: %u", char_id, item_id, quantity, price);
  147. Query query;
  148. string insert = string("INSERT INTO character_buyback (char_id, item_id, quantity, price) VALUES (%u, %u, %i, %u) ");
  149. query.AddQueryAsync(char_id, this, Q_INSERT, insert.c_str(), char_id, item_id, quantity, price);
  150. }
  151. int32 WorldDatabase::LoadCharacterSpells(int32 char_id, Player* player)
  152. {
  153. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Character Spells for player %s...", player->GetName());
  154. Query query;
  155. MYSQL_ROW row;
  156. 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);
  157. int32 old_spell_id = 0;
  158. int32 new_spell_id = 0;
  159. int32 count = 0;
  160. if(result && mysql_num_rows(result) >0)
  161. {
  162. while(result && (row = mysql_fetch_row(result)))
  163. {
  164. count++;
  165. new_spell_id = atoul(row[0]);
  166. if(new_spell_id == old_spell_id)
  167. continue;
  168. old_spell_id = new_spell_id;
  169. 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]));
  170. int8 tier = atoi(row[1]);
  171. if (player->HasSpell(new_spell_id, tier, true))
  172. continue;
  173. player->AddSpellBookEntry(new_spell_id, tier, atoi(row[2]), atoul(row[3]), atoul(row[4]));
  174. }
  175. }
  176. return count;
  177. }
  178. void WorldDatabase::SavePlayerSpells(Client* client)
  179. {
  180. if(!client || client->GetCharacterID() < 1)
  181. return;
  182. LogWrite(SPELL__DEBUG, 3, "Spells", "Saving Spell(s) for Player: '%s'", client->GetPlayer()->GetName());
  183. vector<SpellBookEntry*>* spells = client->GetPlayer()->GetSpellsSaveNeeded();
  184. if(spells)
  185. {
  186. vector<SpellBookEntry*>::iterator itr;
  187. SpellBookEntry* spell = 0;
  188. for(itr = spells->begin(); itr != spells->end(); itr++)
  189. {
  190. spell = *itr;
  191. Query query;
  192. LogWrite(SPELL__DEBUG, 5, "Spells", "\tSaving SpellID: %u, tier: %i, slot: %i", spell->spell_id, spell->tier, spell->slot);
  193. 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",
  194. client->GetPlayer()->GetCharacterID(), spell->spell_id, spell->tier, spell->tier);
  195. spell->save_needed = false;
  196. }
  197. safe_delete(spells);
  198. }
  199. }
  200. int32 WorldDatabase::LoadCharacterSkills(int32 char_id, Player* player)
  201. {
  202. Query query;
  203. MYSQL_ROW row;
  204. int32 count = 0;
  205. 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);
  206. if(result && mysql_num_rows(result) >0)
  207. {
  208. while(result && (row = mysql_fetch_row(result)))
  209. {
  210. count++;
  211. 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]));
  212. player->AddSkill(strtoul(row[0], NULL, 0), atoi(row[1]), atoi(row[2]));
  213. }
  214. }
  215. return count;
  216. }
  217. void WorldDatabase::DeleteCharacterSkill(int32 char_id, Skill* skill)
  218. {
  219. if (char_id > 0 && skill)
  220. {
  221. LogWrite(SKILL__DEBUG, 0, "Skills", "Deleting Skill '%s' (%u) from char_id: %u", skill->name.data.c_str(), skill->skill_id, char_id);
  222. Query query;
  223. query.RunQuery2(Q_DELETE, "DELETE FROM `character_skills` WHERE `char_id`=%u AND `skill_id`=%u", char_id, skill->skill_id);
  224. }
  225. }
  226. int32 WorldDatabase::LoadSkills()
  227. {
  228. int32 total = 0;
  229. Query query;
  230. MYSQL_ROW row;
  231. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, short_name, name, description, skill_type, display FROM skills");
  232. if(result)
  233. {
  234. if (mysql_num_rows(result) >0)
  235. {
  236. Skill* skill = 0;
  237. while(result && (row = mysql_fetch_row(result)))
  238. {
  239. skill = new Skill();
  240. skill->skill_id = strtoul(row[0], NULL, 0);
  241. skill->short_name.data = string(row[1]);
  242. skill->short_name.size = skill->short_name.data.length();
  243. skill->name.data = string(row[2]);
  244. skill->name.size = skill->name.data.length();
  245. skill->description.data = string(row[3]);
  246. skill->description.size = skill->description.data.length();
  247. skill->skill_type = strtoul(row[4], NULL, 0);
  248. //these two need to be converted to the correct numbers
  249. if(skill->skill_type == 13)
  250. skill->skill_type = SKILL_TYPE_LANGUAGE;
  251. else if(skill->skill_type == 12)
  252. skill->skill_type = SKILL_TYPE_GENERAL;
  253. skill->display = atoi(row[5]);
  254. master_skill_list.AddSkill(skill);
  255. total++;
  256. LogWrite(SKILL__DEBUG, 5, "Skill", "---Loading Skill: %s (%u)", skill->name.data.c_str(), skill->skill_id);
  257. LogWrite(SKILL__DEBUG, 7, "Skill", "---short_name: %s, type: %i, display: %i", skill->short_name.data.c_str(), skill->skill_type, skill->display);
  258. }
  259. }
  260. }
  261. LogWrite(SKILL__DEBUG, 3, "Skill", "--Loaded %u Skill(s)", total);
  262. return total;
  263. }
  264. map<int8, vector<MacroData*> >* WorldDatabase::LoadCharacterMacros(int32 char_id)
  265. {
  266. Query query;
  267. MYSQL_ROW row;
  268. int32 total = 0;
  269. 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);
  270. if(result && mysql_num_rows(result) >0)
  271. {
  272. map<int8, vector<MacroData*> >* macros = new map<int8, vector<MacroData*> >;
  273. while(result && (row = mysql_fetch_row(result)))
  274. {
  275. MacroData* data = new MacroData;
  276. data->name = row[1];
  277. data->icon = atoi(row[2]);
  278. data->text = row[3];
  279. (*macros)[atoi(row[0])].push_back(data);
  280. total++;
  281. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLoading macro: %i. %s for player: %u", atoi(row[0]), row[1], char_id);
  282. }
  283. LogWrite(PLAYER__DEBUG, 0, "Player", "\tLoaded %u macro%s", total, total == 1 ? "" : "s");
  284. return macros;
  285. }
  286. return 0;
  287. }
  288. void WorldDatabase::UpdateCharacterMacro(int32 char_id, int8 number, const char* name, int16 icon, vector<string>* updates)
  289. {
  290. LogWrite(PLAYER__DEBUG, 0, "Player", "Update player id %u macro: %i", char_id, number);
  291. Query query;
  292. Query query2;
  293. query.RunQuery2(Q_DELETE, "delete FROM character_macros where char_id = %u and macro_number = %i", char_id, number);
  294. if(name && updates && updates->size() > 0)
  295. {
  296. for(int8 i=0;i<updates->size();i++)
  297. {
  298. 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());
  299. LogWrite(PLAYER__DEBUG, 5, "Player", "\tAdding macro: %s, %s (Player: %u)", name, updates->at(i).c_str(), char_id);
  300. }
  301. }
  302. }
  303. //we use our timestamp just in case db is on another server, otherwise times might be off
  304. void WorldDatabase::UpdateVitality(int32 timestamp, float amount){
  305. Query query;
  306. LogWrite(PLAYER__DEBUG, 3, "Player", "Reset Vitality > 100: %f", amount);
  307. query.RunQuery2(Q_UPDATE, "update character_details set xp_vitality=100 where (xp_vitality + %f) > 100", amount);
  308. LogWrite(PLAYER__DEBUG, 3, "Player", "Update Vitality <= 100: %f", amount);
  309. query.RunQuery2(Q_UPDATE, "update character_details set xp_vitality=(xp_vitality+%f) where (xp_vitality + %f) <= 100", amount, amount);
  310. LogWrite(PLAYER__DEBUG, 3, "Player", "Update Vitality Timer: %u", timestamp);
  311. query.RunQuery2(Q_UPDATE, "update variables set variable_value=%u where variable_name='vitalitytimer'", timestamp);
  312. }
  313. void WorldDatabase::SaveVariable(const char* name, const char* value, const char* comment){
  314. LogWrite(WORLD__DEBUG, 0, "Variables", "Saving Variable: %s = %s", name, value);
  315. Query query;
  316. if(comment){
  317. query.RunQuery2(Q_REPLACE, "replace into variables (variable_name, variable_value, comment) values('%s', '%s', '%s')",
  318. getSafeEscapeString(name).c_str(), getSafeEscapeString(value).c_str(), getSafeEscapeString(comment).c_str());
  319. }
  320. else{
  321. query.RunQuery2(Q_REPLACE, "replace into variables (variable_name, variable_value) values('%s', '%s')",
  322. getSafeEscapeString(name).c_str(), getSafeEscapeString(value).c_str());
  323. }
  324. }
  325. void WorldDatabase::LoadGlobalVariables(){
  326. variables.ClearVariables ( );
  327. int32 total = 0;
  328. Query query;
  329. MYSQL_ROW row;
  330. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT variable_name, variable_value, comment FROM variables");
  331. while(result && (row = mysql_fetch_row(result)))
  332. {
  333. Variable* newVar = new Variable(row[0], row[1], row[2]);
  334. variables.AddVariable(newVar);
  335. total++;
  336. LogWrite(WORLD__DEBUG, 5, "World", "---Loading variable: '%s' = '%s'", row[0], row[1]);
  337. }
  338. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u variables", total);
  339. }
  340. void WorldDatabase::LoadAppearanceMasterList()
  341. {
  342. DatabaseResult result;
  343. int32 total = 0;
  344. int32 appearance_id;
  345. int16 appearance_version;
  346. master_appearance_list.ClearAppearances();
  347. database_new.Select(&result, "SELECT appearance_id, `name`, min_client_version FROM appearances ORDER BY appearance_id");
  348. while( result.Next() )
  349. {
  350. appearance_id = result.GetInt32Str("appearance_id");
  351. const char *appearance_name = result.GetStringStr("name");
  352. appearance_version = result.GetInt16Str("min_client_version");
  353. Appearance* a = new Appearance(appearance_id, appearance_name, appearance_version);
  354. master_appearance_list.InsertAppearance(a);
  355. total++;
  356. LogWrite(WORLD__DEBUG, 5, "World", "---Loading appearances: '%s' (%i)", appearance_name, appearance_id);
  357. }
  358. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u appearances", total);
  359. }
  360. void WorldDatabase::LoadVisualStates()
  361. {
  362. visual_states.Reset();
  363. int32 total = 0;
  364. Query query;
  365. Query query2;
  366. MYSQL_ROW row;
  367. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT visual_state_id, name FROM visual_states");
  368. while(result && (row = mysql_fetch_row(result)))
  369. {
  370. VisualState* vs = new VisualState(atoi(row[0]), row[1]);
  371. visual_states.InsertVisualState(vs);
  372. total++;
  373. LogWrite(WORLD__DEBUG, 5, "World", "---Loading visual state: '%s' (%i)", row[1], atoi(row[0]));
  374. }
  375. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u visual states", total);
  376. total = 0;
  377. result = query2.RunQuery2(Q_SELECT, "SELECT name, visual_state_id, message, targeted_message, min_version_range, max_version_range FROM emotes");
  378. while(result && (row = mysql_fetch_row(result)))
  379. {
  380. EmoteVersionRange* range = 0;
  381. if ((range = visual_states.FindEmoteRange(string(row[0]))) == NULL)
  382. {
  383. range = new EmoteVersionRange(row[0]);
  384. visual_states.InsertEmoteRange(range);
  385. }
  386. range->AddVersionRange(atoul(row[4]),atoul(row[5]), row[0], atoi(row[1]), row[2], row[3]);
  387. total++;
  388. LogWrite(WORLD__DEBUG, 5, "World", "---Loading emote state: '%s' (%i)", row[1], atoi(row[0]));
  389. }
  390. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u emote state(s)", total);
  391. }
  392. void WorldDatabase::LoadSubCommandList()
  393. {
  394. int32 total = 0;
  395. Query query;
  396. MYSQL_ROW row;
  397. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT command, subcommand, handler, required_status FROM commands where length(subcommand) > 0 ORDER BY handler asc");
  398. while(result && (row = mysql_fetch_row(result)))
  399. {
  400. commands.GetRemoteCommands()->CheckAddSubCommand(string(row[0]), EQ2_RemoteCommandString(row[1], (int32)strtoul(row[2], NULL, 0), atoi(row[3])));
  401. total++;
  402. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Command: '%s', sub '%s', handler, %u status %i", row[0], row[1], atoul(row[2]), atoi(row[3]));
  403. }
  404. LogWrite(COMMAND__DEBUG, 3, "Command", "--Loaded %i Subcommand(s)", total);
  405. }
  406. void WorldDatabase::LoadCommandList()
  407. {
  408. Query query;
  409. MYSQL_ROW row;
  410. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT command, handler, required_status FROM commands where length(subcommand) = 0 ORDER BY handler asc");
  411. int16 index = 0;
  412. while(result && (row = mysql_fetch_row(result)))
  413. {
  414. int32 handler = strtoul(row[1], NULL, 0);
  415. while(handler>index && handler != 999)
  416. {
  417. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Remote Commands: handler %u, index %u", handler, index);
  418. commands.GetRemoteCommands()->addZero();
  419. index++;
  420. }
  421. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Commands: handler %u, index %u", handler, index);
  422. commands.GetRemoteCommands()->addCommand(EQ2_RemoteCommandString(row[0], handler, atoi(row[2])));
  423. index++;
  424. }
  425. LogWrite(COMMAND__DEBUG, 3, "Command", "--Loaded %i Command%s", index, index > 0 ? "s" : "");
  426. LoadSubCommandList();
  427. }
  428. int32 WorldDatabase::LoadNPCSpells(){
  429. Query query;
  430. MYSQL_ROW row;
  431. int32 count = 0;
  432. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_list_id, spell_id, spell_tier, on_spawn_cast, on_aggro_cast FROM spawn_npc_spells where spell_list_id > 0");
  433. while(result && (row = mysql_fetch_row(result))){
  434. world.AddNPCSpell(atoul(row[0]), atoul(row[1]), atoi(row[2]), atoul(row[3]), atoul(row[4]));
  435. count++;
  436. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Spell List: %u, spell id: %u, tier: %i", atoul(row[0]), atoul(row[1]), atoi(row[2]));
  437. }
  438. return count;
  439. }
  440. int32 WorldDatabase::LoadNPCSkills(ZoneServer* zone){
  441. Query query;
  442. MYSQL_ROW row;
  443. int32 count = 0;
  444. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT skill_list_id, skill_id, starting_value FROM spawn_npc_skills");
  445. while(result && (row = mysql_fetch_row(result))){
  446. zone->AddNPCSkill(atoul(row[0]), atoul(row[1]), atoi(row[2]));
  447. count++;
  448. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Skill List: %u, skill id: %u, value: %i", atoul(row[0]), atoul(row[1]), atoi(row[2]));
  449. }
  450. return count;
  451. }
  452. int32 WorldDatabase::LoadNPCEquipment(ZoneServer* zone){
  453. Query query;
  454. MYSQL_ROW row;
  455. int32 count = 0;
  456. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT equipment_list_id, item_id FROM spawn_npc_equipment");
  457. while(result && (row = mysql_fetch_row(result))){
  458. zone->AddNPCEquipment(atoul(row[0]), atoul(row[1]));
  459. count++;
  460. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC Equipment List: %u, item: %u", atoul(row[0]), atoul(row[1]));
  461. }
  462. return count;
  463. }
  464. int8 WorldDatabase::GetAppearanceType(string type){
  465. int8 ret = 255;
  466. if (type == "soga_hair_face_highlight_color")
  467. ret = APPEARANCE_SOGA_HFHC;
  468. else if (type == "soga_hair_type_highlight_color")
  469. ret = APPEARANCE_SOGA_HTHC;
  470. else if (type == "soga_hair_face_color")
  471. ret = APPEARANCE_SOGA_HFC;
  472. else if (type == "soga_hair_type_color")
  473. ret = APPEARANCE_SOGA_HTC;
  474. else if (type == "soga_hair_highlight")
  475. ret = APPEARANCE_SOGA_HH;
  476. else if (type == "soga_hair_color1")
  477. ret = APPEARANCE_SOGA_HC1;
  478. else if (type == "soga_hair_color2")
  479. ret = APPEARANCE_SOGA_HC2;
  480. else if (type == "hair_type_color")
  481. ret = APPEARANCE_HTC;
  482. else if (type == "soga_skin_color")
  483. ret = APPEARANCE_SOGA_SC;
  484. else if (type == "soga_eye_color")
  485. ret = APPEARANCE_SOGA_EC;
  486. else if (type == "hair_type_highlight_color")
  487. ret = APPEARANCE_HTHC;
  488. else if (type == "hair_face_highlight_color")
  489. ret = APPEARANCE_HFHC;
  490. else if (type == "hair_face_color")
  491. ret = APPEARANCE_HFC;
  492. else if (type == "hair_highlight")
  493. ret = APPEARANCE_HH;
  494. else if (type == "hair_color1")
  495. ret = APPEARANCE_HC1;
  496. else if (type == "wing_color1")
  497. ret = APPEARANCE_WC1;
  498. else if (type == "hair_color2")
  499. ret = APPEARANCE_HC2;
  500. else if (type == "wing_color2")
  501. ret = APPEARANCE_WC2;
  502. else if (type == "skin_color")
  503. ret = APPEARANCE_SC;
  504. else if (type == "eye_color")
  505. ret = APPEARANCE_EC;
  506. else if (type == "soga_eye_brow_type")
  507. ret = APPEARANCE_SOGA_EBT;
  508. else if (type == "soga_cheek_type")
  509. ret = APPEARANCE_SOGA_CHEEKT;
  510. else if (type == "soga_nose_type")
  511. ret = APPEARANCE_SOGA_NT;
  512. else if (type == "soga_chin_type")
  513. ret = APPEARANCE_SOGA_CHINT;
  514. else if (type == "soga_lip_type")
  515. ret = APPEARANCE_SOGA_LT;
  516. else if (type == "eye_brow_type")
  517. ret = APPEARANCE_EBT;
  518. else if (type == "soga_ear_type")
  519. ret = APPEARANCE_SOGA_EART;
  520. else if (type == "soga_eye_type")
  521. ret = APPEARANCE_SOGA_EYET;
  522. else if (type == "cheek_type")
  523. ret = APPEARANCE_CHEEKT;
  524. else if (type == "nose_type")
  525. ret = APPEARANCE_NT;
  526. else if (type == "chin_type")
  527. ret = APPEARANCE_CHINT;
  528. else if (type == "ear_type")
  529. ret = APPEARANCE_EART;
  530. else if (type == "eye_type")
  531. ret = APPEARANCE_EYET;
  532. else if (type == "lip_type")
  533. ret = APPEARANCE_LT;
  534. else if (type == "shirt_color")
  535. ret = APPEARANCE_SHIRT;
  536. else if (type == "unknown_chest_color")
  537. ret = APPEARANCE_UCC;
  538. else if (type == "pants_color")
  539. ret = APPEARANCE_PANTS;
  540. else if (type == "unknown_legs_color")
  541. ret = APPEARANCE_ULC;
  542. else if (type == "unknown9")
  543. ret = APPEARANCE_U9;
  544. else if (type == "body_size")
  545. ret = APPEARANCE_BODY_SIZE;
  546. else if (type == "soga_wing_color1")
  547. ret = APPEARANCE_SOGA_WC1;
  548. else if (type == "soga_wing_color2")
  549. ret = APPEARANCE_SOGA_WC2;
  550. else if (type == "soga_shirt_color")
  551. ret = APPEARANCE_SOGA_SHIRT;
  552. else if (type == "soga_unknown_chest_color")
  553. ret = APPEARANCE_SOGA_UCC;
  554. else if (type == "soga_pants_color")
  555. ret = APPEARANCE_SOGA_PANTS;
  556. else if (type == "soga_unknown_legs_color")
  557. ret = APPEARANCE_SOGA_ULC;
  558. else if (type == "soga_unknown13")
  559. ret = APPEARANCE_SOGA_U13;
  560. else if (type == "body_age")
  561. ret = APPEARANCE_BODY_AGE;
  562. else if (type == "model_color")
  563. ret = APPEARANCE_MC;
  564. else if (type == "soga_model_color")
  565. ret = APPEARANCE_SMC;
  566. else if (type == "soga_body_size")
  567. ret = APPEARANCE_SBS;
  568. else if (type == "soga_body_age")
  569. ret = APPEARANCE_SBA;
  570. return ret;
  571. }
  572. int32 WorldDatabase::LoadAppearances(ZoneServer* zone, Client* client){
  573. Query query, query2;
  574. MYSQL_ROW row;
  575. int32 count = 0, spawn_id = 0, new_spawn_id = 0;
  576. Entity* entity = 0;
  577. if(client)
  578. entity = client->GetPlayer();
  579. map<string, int8> appearance_types;
  580. map<int32, map<int8, EQ2_Color> > appearance_colors;
  581. EQ2_Color color;
  582. color.red = 0;
  583. color.green = 0;
  584. color.blue = 0;
  585. string type;
  586. MYSQL_RES* result = 0;
  587. if(!client)
  588. result = query.RunQuery2(Q_SELECT, "SELECT distinct `type` FROM npc_appearance where length(type) > 0");
  589. else
  590. result = query.RunQuery2(Q_SELECT, "SELECT distinct `type` FROM char_colors where length(type) > 0 and char_id=%u", client->GetCharacterID());
  591. while(result && (row = mysql_fetch_row(result))){
  592. type = string(row[0]);
  593. appearance_types[type] = GetAppearanceType(type);
  594. if(appearance_types[type] == 255)
  595. LogWrite(WORLD__ERROR, 0, "Appearance", "Unknown appearance type '%s' in LoadAppearances.", type.c_str());
  596. }
  597. MYSQL_RES* result2 = 0;
  598. if(!client)
  599. 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");
  600. else
  601. 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());
  602. while(result2 && (row = mysql_fetch_row(result2))){
  603. if(!client){
  604. new_spawn_id = atoul(row[1]);
  605. if(spawn_id != new_spawn_id){
  606. entity = zone->GetNPC(new_spawn_id, true);
  607. if(!entity)
  608. continue;
  609. if(spawn_id > 0)
  610. count++;
  611. spawn_id = new_spawn_id;
  612. }
  613. }
  614. if(appearance_types[row[0]] < APPEARANCE_SOGA_EBT){
  615. color.red = atoi(row[3]);
  616. color.green = atoi(row[4]);
  617. color.blue = atoi(row[5]);
  618. }
  619. switch(appearance_types[row[0]]){
  620. case APPEARANCE_SOGA_HFHC:{
  621. entity->features.soga_hair_face_highlight_color = color;
  622. break;
  623. }
  624. case APPEARANCE_SOGA_HTHC:{
  625. entity->features.soga_hair_type_highlight_color = color;
  626. break;
  627. }
  628. case APPEARANCE_SOGA_HFC:{
  629. entity->features.soga_hair_face_color = color;
  630. break;
  631. }
  632. case APPEARANCE_SOGA_HTC:{
  633. entity->features.soga_hair_type_color = color;
  634. break;
  635. }
  636. case APPEARANCE_SOGA_HH:{
  637. entity->features.soga_hair_highlight_color = color;
  638. break;
  639. }
  640. case APPEARANCE_SOGA_HC1:{
  641. entity->features.soga_hair_color1 = color;
  642. break;
  643. }
  644. case APPEARANCE_SOGA_HC2:{
  645. entity->features.soga_hair_color2 = color;
  646. break;
  647. }
  648. case APPEARANCE_SOGA_SC:{
  649. entity->features.soga_skin_color = color;
  650. break;
  651. }
  652. case APPEARANCE_SOGA_EC:{
  653. entity->features.soga_eye_color = color;
  654. break;
  655. }
  656. case APPEARANCE_HTHC:{
  657. entity->features.hair_type_highlight_color = color;
  658. break;
  659. }
  660. case APPEARANCE_HFHC:{
  661. entity->features.hair_face_highlight_color = color;
  662. break;
  663. }
  664. case APPEARANCE_HTC:{
  665. entity->features.hair_type_color = color;
  666. break;
  667. }
  668. case APPEARANCE_HFC:{
  669. entity->features.hair_face_color = color;
  670. break;
  671. }
  672. case APPEARANCE_HH:{
  673. entity->features.hair_highlight_color = color;
  674. break;
  675. }
  676. case APPEARANCE_HC1:{
  677. entity->features.hair_color1 = color;
  678. break;
  679. }
  680. case APPEARANCE_HC2:{
  681. entity->features.hair_color2 = color;
  682. break;
  683. }
  684. case APPEARANCE_WC1:{
  685. entity->features.wing_color1 = color;
  686. break;
  687. }
  688. case APPEARANCE_WC2:{
  689. entity->features.wing_color2 = color;
  690. break;
  691. }
  692. case APPEARANCE_SC:{
  693. entity->features.skin_color = color;
  694. break;
  695. }
  696. case APPEARANCE_EC:{
  697. entity->features.eye_color = color;
  698. break;
  699. }
  700. case APPEARANCE_SOGA_EBT:{
  701. for(int i=0;i<3;i++)
  702. entity->features.soga_eye_brow_type[i] = atoi(row[3+i]);
  703. break;
  704. }
  705. case APPEARANCE_SOGA_CHEEKT:{
  706. for(int i=0;i<3;i++)
  707. entity->features.soga_cheek_type[i] = atoi(row[3+i]);
  708. break;
  709. }
  710. case APPEARANCE_SOGA_NT:{
  711. for(int i=0;i<3;i++)
  712. entity->features.soga_nose_type[i] = atoi(row[3+i]);
  713. break;
  714. }
  715. case APPEARANCE_SOGA_CHINT:{
  716. for(int i=0;i<3;i++)
  717. entity->features.soga_chin_type[i] = atoi(row[3+i]);
  718. break;
  719. }
  720. case APPEARANCE_SOGA_LT:{
  721. for(int i=0;i<3;i++)
  722. entity->features.soga_lip_type[i] = atoi(row[3+i]);
  723. break;
  724. }
  725. case APPEARANCE_SOGA_EART:{
  726. for(int i=0;i<3;i++)
  727. entity->features.soga_ear_type[i] = atoi(row[3+i]);
  728. break;
  729. }
  730. case APPEARANCE_SOGA_EYET:{
  731. for(int i=0;i<3;i++)
  732. entity->features.soga_eye_type[i] = atoi(row[3+i]);
  733. break;
  734. }
  735. case APPEARANCE_EBT:{
  736. for(int i=0;i<3;i++)
  737. entity->features.eye_brow_type[i] = atoi(row[3+i]);
  738. break;
  739. }
  740. case APPEARANCE_CHEEKT:{
  741. for(int i=0;i<3;i++)
  742. entity->features.cheek_type[i] = atoi(row[3+i]);
  743. break;
  744. }
  745. case APPEARANCE_NT:{
  746. for(int i=0;i<3;i++)
  747. entity->features.nose_type[i] = atoi(row[3+i]);
  748. break;
  749. }
  750. case APPEARANCE_CHINT:{
  751. for(int i=0;i<3;i++)
  752. entity->features.chin_type[i] = atoi(row[3+i]);
  753. break;
  754. }
  755. case APPEARANCE_EART:{
  756. for(int i=0;i<3;i++)
  757. entity->features.ear_type[i] = atoi(row[3+i]);
  758. break;
  759. }
  760. case APPEARANCE_EYET:{
  761. for(int i=0;i<3;i++)
  762. entity->features.eye_type[i] = atoi(row[3+i]);
  763. break;
  764. }
  765. case APPEARANCE_LT:{
  766. for(int i=0;i<3;i++)
  767. entity->features.lip_type[i] = atoi(row[3+i]);
  768. break;
  769. }
  770. case APPEARANCE_SHIRT:{
  771. entity->features.shirt_color = color;
  772. break;
  773. }
  774. case APPEARANCE_UCC:{
  775. break;
  776. }
  777. case APPEARANCE_PANTS:{
  778. entity->features.pants_color = color;
  779. break;
  780. }
  781. case APPEARANCE_ULC:{
  782. break;
  783. }
  784. case APPEARANCE_U9:{
  785. break;
  786. }
  787. case APPEARANCE_BODY_SIZE:{
  788. entity->features.body_size = color.red;
  789. break;
  790. }
  791. case APPEARANCE_SOGA_WC1:{
  792. break;
  793. }
  794. case APPEARANCE_SOGA_WC2:{
  795. break;
  796. }
  797. case APPEARANCE_SOGA_SHIRT:{
  798. break;
  799. }
  800. case APPEARANCE_SOGA_UCC:{
  801. break;
  802. }
  803. case APPEARANCE_SOGA_PANTS:{
  804. break;
  805. }
  806. case APPEARANCE_SOGA_ULC:{
  807. break;
  808. }
  809. case APPEARANCE_SOGA_U13:{
  810. break;
  811. }
  812. case APPEARANCE_BODY_AGE: {
  813. entity->features.body_age = color.red;
  814. break;
  815. }
  816. case APPEARANCE_MC:{
  817. entity->features.model_color = color;
  818. break;
  819. }
  820. case APPEARANCE_SMC:{
  821. entity->features.soga_model_color = color;
  822. break;
  823. }
  824. case APPEARANCE_SBS: {
  825. entity->features.soga_body_size = color.red;
  826. break;
  827. }
  828. case APPEARANCE_SBA: {
  829. entity->features.soga_body_age = color.red;
  830. break;
  831. }
  832. }
  833. entity->info_changed = true;
  834. }
  835. return count;
  836. }
  837. void WorldDatabase::LoadNPCs(ZoneServer* zone){
  838. Query query;
  839. MYSQL_ROW row;
  840. NPC* npc = 0;
  841. int32 id = 0;
  842. int32 total = 0;
  843. 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, s.loot_tier, s.loot_drop_type\n"
  844. "FROM spawn s\n"
  845. "INNER JOIN spawn_npcs npc\n"
  846. "ON s.id = npc.spawn_id\n"
  847. "INNER JOIN spawn_location_entry le\n"
  848. "ON npc.spawn_id = le.spawn_id\n"
  849. "INNER JOIN spawn_location_placement lp\n"
  850. "ON le.spawn_location_id = lp.spawn_location_id\n"
  851. "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
  852. "GROUP BY s.id",
  853. zone->GetZoneID(), zone->GetInstanceID());
  854. while(result && (row = mysql_fetch_row(result))){
  855. /*npc->SetAppearanceID(atoi(row[12]));
  856. AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
  857. if(appearance)
  858. memcpy(&npc->appearance, appearance, sizeof(AppearanceData));
  859. */
  860. int32 npcXpackFlag = atoul(row[75]);
  861. int32 npcHolidayFlag = atoul(row[76]);
  862. id = atoul(row[0]);
  863. if(zone->GetNPC(id, true))
  864. continue;
  865. npc = new NPC();
  866. if (!CheckExpansionFlags(zone, npcXpackFlag) || !CheckHolidayFlags(zone, npcHolidayFlag))
  867. npc->SetOmittedByDBFlag(true);
  868. npc->SetDatabaseID(id);
  869. strcpy(npc->appearance.name, row[1]);
  870. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  871. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  872. if(primary_command_list){
  873. npc->SetPrimaryCommands(primary_command_list);
  874. npc->primary_command_list_id = atoul(row[9]);
  875. }
  876. if(secondary_command_list){
  877. npc->SetSecondaryCommands(secondary_command_list);
  878. npc->secondary_command_list_id = atoul(row[10]);
  879. }
  880. npc->appearance.min_level = atoi(row[2]);
  881. npc->appearance.max_level = atoi(row[3]);
  882. npc->appearance.level = atoi(row[2]);
  883. npc->appearance.encounter_level = atoi(row[4]);
  884. npc->appearance.race = atoi(row[5]);
  885. npc->appearance.model_type = atoi(row[6]);
  886. npc->appearance.soga_model_type = atoi(row[62]);
  887. npc->appearance.adventure_class = atoi(row[7]);
  888. npc->appearance.gender = atoi(row[8]);
  889. npc->appearance.display_name = atoi(row[11]);
  890. npc->features.hair_type = atoi(row[14]);
  891. npc->features.hair_face_type = atoi(row[15]);
  892. npc->features.wing_type = atoi(row[16]);
  893. npc->features.chest_type = atoi(row[17]);
  894. npc->features.legs_type = atoi(row[18]);
  895. npc->features.soga_hair_type = atoi(row[19]);
  896. npc->features.soga_hair_face_type = atoi(row[20]);
  897. npc->appearance.attackable = atoi(row[21]);
  898. npc->appearance.show_level = atoi(row[22]);
  899. npc->appearance.targetable = atoi(row[23]);
  900. npc->appearance.show_command_icon = atoi(row[24]);
  901. npc->appearance.display_hand_icon = atoi(row[25]);
  902. npc->appearance.hide_hood = atoi(row[70]);
  903. npc->appearance.randomize = atoi(row[61]);
  904. npc->SetTotalHP(atoul(row[26]));
  905. npc->SetTotalPower(atoul(row[27]));
  906. npc->SetHP(npc->GetTotalHP());
  907. npc->SetPower(npc->GetTotalPower());
  908. if(npc->GetTotalHP() == 0){
  909. npc->SetTotalHP(15*npc->GetLevel() + 1);
  910. npc->SetHP(15*npc->GetLevel() + 1);
  911. }
  912. if(npc->GetTotalPower() == 0){
  913. npc->SetTotalPower(15*npc->GetLevel() + 1);
  914. npc->SetPower(15*npc->GetLevel() + 1);
  915. }
  916. npc->size = atoi(row[28]);
  917. npc->appearance.pos.collision_radius = atoi(row[29]);
  918. npc->appearance.action_state = atoi(row[30]);
  919. npc->appearance.visual_state = atoi(row[31]);
  920. npc->appearance.mood_state = atoi(row[32]);
  921. npc->appearance.emote_state = atoi(row[71]);
  922. npc->appearance.pos.state = atoi(row[33]);
  923. npc->appearance.activity_status = atoi(row[34]);
  924. npc->faction_id = atoul(row[35]);
  925. if(row[36]){
  926. std::string sub_title = std::string(row[36]);
  927. if(strncmp(row[36],"<Collector>", 11) == 0) {
  928. npc->SetCollector(true);
  929. }
  930. if(strlen(row[36]) < sizeof(npc->appearance.sub_title))
  931. strcpy(npc->appearance.sub_title, row[36]);
  932. else
  933. strncpy(npc->appearance.sub_title, row[36], sizeof(npc->appearance.sub_title));
  934. }
  935. npc->SetMerchantID(atoul(row[37]));
  936. npc->SetMerchantType(atoi(row[38]));
  937. npc->SetSizeOffset(atoi(row[39]));
  938. npc->SetAIStrategy(atoi(row[41]));
  939. npc->SetPrimarySpellList(atoul(row[42]));
  940. npc->SetSecondarySpellList(atoul(row[43]));
  941. npc->SetPrimarySkillList(atoul(row[44]));
  942. npc->SetSecondarySkillList(atoul(row[45]));
  943. npc->SetEquipmentListID(atoul(row[46]));
  944. InfoStruct* info = npc->GetInfoStruct();
  945. info->set_attack_type(atoi(row[40]));
  946. info->set_str_base(atoi(row[47]));
  947. info->set_sta_base(atoi(row[48]));
  948. info->set_wis_base(atoi(row[49]));
  949. info->set_intel_base(atoi(row[50]));
  950. info->set_agi_base(atoi(row[51]));
  951. info->set_heat_base(atoi(row[52]));
  952. info->set_cold_base(atoi(row[53]));
  953. info->set_magic_base(atoi(row[54]));
  954. info->set_mental_base(atoi(row[55]));
  955. info->set_divine_base(atoi(row[56]));
  956. info->set_disease_base(atoi(row[57]));
  957. info->set_poison_base(atoi(row[58]));
  958. info->set_alignment(atoi(row[64]));
  959. npc->SetAggroRadius(atof(row[59]));
  960. npc->SetCastPercentage(atoi(row[60]));
  961. npc->appearance.heroic_flag = atoi(row[63]);
  962. info->set_elemental_base(atoi(row[65]));
  963. info->set_arcane_base(atoi(row[66]));
  964. info->set_noxious_base(atoi(row[67]));
  965. npc->SetTotalSavagery(atoul(row[68]));
  966. npc->SetTotalDissonance(atoul(row[69]));
  967. npc->SetSavagery(npc->GetTotalSavagery());
  968. npc->SetDissonance(npc->GetTotalDissonance());
  969. if(npc->GetTotalSavagery() == 0){
  970. npc->SetTotalSavagery(15*npc->GetLevel() + 1);
  971. npc->SetSavagery(15*npc->GetLevel() + 1);
  972. }
  973. if(npc->GetTotalDissonance() == 0){
  974. npc->SetTotalDissonance(15*npc->GetLevel() + 1);
  975. npc->SetDissonance(15*npc->GetLevel() + 1);
  976. }
  977. npc->SetPrefixTitle(row[72]);
  978. npc->SetSuffixTitle(row[73]);
  979. npc->SetLastName(row[74]);
  980. // xpack+holiday value handled at top at position 75+76
  981. int8 disableSounds = atoul(row[77]);
  982. npc->SetSoundsDisabled(disableSounds);
  983. npc->SetMerchantLevelRange(atoul(row[78]), atoul(row[79]));
  984. npc->SetAAXPRewards(atoul(row[80]));
  985. info->set_water_type(atoul(row[81]));
  986. info->set_flying_type(atoul(row[82]));
  987. npc->SetLootTier(atoul(row[83]));
  988. npc->SetLootDropType(atoul(row[84]));
  989. zone->AddNPC(id, npc);
  990. total++;
  991. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC: '%s' (%u)", npc->appearance.name, id);
  992. }
  993. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC(s).", total);
  994. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Skill(s).", LoadNPCSkills(zone));
  995. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Equipment Piece(s).", LoadNPCEquipment(zone));
  996. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Appearance(s).", LoadAppearances(zone));
  997. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Equipment Appearance(s).", LoadNPCAppearanceEquipmentData(zone));
  998. }
  999. void WorldDatabase::LoadSpiritShards(ZoneServer* zone){
  1000. Query query;
  1001. MYSQL_ROW row;
  1002. int32 id = 0;
  1003. int32 total = 0;
  1004. 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"
  1005. "FROM character_spirit_shards\n"
  1006. "WHERE zoneid = %u and (instanceid = 0 or instanceid = %u)",
  1007. zone->GetZoneID(), zone->GetInstanceID());
  1008. while(result && (row = mysql_fetch_row(result))){
  1009. /*npc->SetAppearanceID(atoi(row[12]));
  1010. AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
  1011. if(appearance)
  1012. memcpy(&npc->appearance, appearance, sizeof(AppearanceData));
  1013. */
  1014. sint64 timestamp = 0;
  1015. #ifdef WIN32
  1016. timestamp = _strtoui64(row[0], NULL, 10);
  1017. #else
  1018. timestamp = strtoull(row[0], 0, 10);
  1019. #endif
  1020. if(!row[1])
  1021. continue;
  1022. NPC* shard = new NPC();
  1023. shard->SetShardCreatedTimestamp(timestamp);
  1024. strcpy(shard->appearance.name, row[1]);
  1025. shard->appearance.level = atoul(row[2]);
  1026. shard->appearance.race = atoul(row[3]);
  1027. shard->appearance.gender = atoul(row[4]);
  1028. shard->appearance.adventure_class = atoul(row[5]);
  1029. shard->appearance.model_type = atoul(row[6]);
  1030. shard->appearance.soga_model_type = atoul(row[7]);
  1031. shard->appearance.display_name = 1;
  1032. shard->features.hair_type = atoul(row[8]);
  1033. shard->features.hair_face_type = atoul(row[9]);
  1034. shard->features.wing_type = atoul(row[10]);
  1035. shard->features.chest_type = atoul(row[11]);
  1036. shard->features.legs_type = atoul(row[12]);
  1037. shard->features.soga_hair_type = atoul(row[13]);
  1038. shard->features.soga_hair_face_type = atoul(row[14]);
  1039. shard->appearance.attackable = 0;
  1040. shard->appearance.show_level = 1;
  1041. shard->appearance.targetable = 1;
  1042. shard->appearance.show_command_icon = 1;
  1043. shard->appearance.display_hand_icon = 0;
  1044. shard->appearance.hide_hood = atoul(row[15]);
  1045. shard->size = atoul(row[16]);
  1046. shard->appearance.pos.collision_radius = atoul(row[17]);
  1047. shard->appearance.action_state = atoul(row[18]);
  1048. shard->appearance.visual_state = atoul(row[19]); // ghostly look
  1049. shard->appearance.mood_state = atoul(row[20]);
  1050. shard->appearance.emote_state = atoul(row[21]);
  1051. shard->appearance.pos.state = atoul(row[22]);
  1052. shard->appearance.activity_status = atoul(row[23]);
  1053. if(row[24])
  1054. strncpy(shard->appearance.sub_title, row[24], sizeof(shard->appearance.sub_title));
  1055. if(row[25])
  1056. shard->SetPrefixTitle(row[25]);
  1057. if(row[26])
  1058. shard->SetSuffixTitle(row[26]);
  1059. if(row[27])
  1060. shard->SetLastName(row[27]);
  1061. shard->SetX(atof(row[28]));
  1062. shard->SetY(atof(row[29]));
  1063. shard->SetZ(atof(row[30]));
  1064. shard->SetHeading(atof(row[31]));
  1065. shard->SetSpawnOrigX(shard->GetX());
  1066. shard->SetSpawnOrigY(shard->GetY());
  1067. shard->SetSpawnOrigZ(shard->GetZ());
  1068. shard->SetSpawnOrigHeading(shard->GetHeading());
  1069. shard->appearance.pos.grid_id = atoul(row[32]);
  1070. shard->SetShardID(atoul(row[33]));
  1071. shard->SetShardCharID(atoul(row[34]));
  1072. const char* script = rule_manager.GetGlobalRule(R_Combat, SpiritShardSpawnScript)->GetString();
  1073. if(script)
  1074. {
  1075. shard->SetSpawnScript(script);
  1076. zone->CallSpawnScript(shard, SPAWN_SCRIPT_PRESPAWN);
  1077. }
  1078. zone->AddSpawn(shard);
  1079. if(script)
  1080. zone->CallSpawnScript(shard, SPAWN_SCRIPT_SPAWN);
  1081. total++;
  1082. LogWrite(NPC__DEBUG, 5, "NPC", "---Loading Player Spirit Shard: '%s' (%u)", shard->appearance.name, id);
  1083. }
  1084. LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i Spirit Shard(s).", total);
  1085. }
  1086. void WorldDatabase::LoadSigns(ZoneServer* zone){
  1087. Query query;
  1088. MYSQL_ROW row;
  1089. Sign* sign = 0;
  1090. int32 id = 0;
  1091. int32 total = 0;
  1092. 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, s.loot_tier, s.loot_drop_type, ss.language\n"
  1093. "FROM spawn s\n"
  1094. "INNER JOIN spawn_signs ss\n"
  1095. "ON s.id = ss.spawn_id\n"
  1096. "INNER JOIN spawn_location_entry le\n"
  1097. "ON ss.spawn_id = le.spawn_id\n"
  1098. "INNER JOIN spawn_location_placement lp\n"
  1099. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1100. "WHERE lp.zone_id = %u\n"
  1101. "GROUP BY s.id",
  1102. zone->GetZoneID());
  1103. while(result && (row = mysql_fetch_row(result))){
  1104. int32 signXpackFlag = atoul(row[28]);
  1105. int32 signHolidayFlag = atoul(row[29]);
  1106. id = atoul(row[0]);
  1107. if(zone->GetSign(id, true))
  1108. continue;
  1109. sign = new Sign();
  1110. if (!CheckExpansionFlags(zone, signXpackFlag) || !CheckHolidayFlags(zone, signHolidayFlag))
  1111. sign->SetOmittedByDBFlag(true);
  1112. sign->SetDatabaseID(id);
  1113. strcpy(sign->appearance.name, row[1]);
  1114. sign->appearance.model_type = atoi(row[2]);
  1115. sign->SetSize(atoi(row[3]));
  1116. sign->appearance.show_command_icon = atoi(row[4]);
  1117. sign->SetWidgetID(atoul(row[5]));
  1118. sign->SetWidgetX(atof(row[6]));
  1119. sign->SetWidgetY(atof(row[7]));
  1120. sign->SetWidgetZ(atof(row[8]));
  1121. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  1122. if(primary_command_list){
  1123. sign->SetPrimaryCommands(primary_command_list);
  1124. sign->primary_command_list_id = atoul(row[9]);
  1125. }
  1126. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  1127. if (secondary_command_list){
  1128. sign->SetSecondaryCommands(secondary_command_list);
  1129. sign->secondary_command_list_id = atoul(row[10]);
  1130. }
  1131. sign->appearance.pos.collision_radius = atoi(row[11]);
  1132. sign->SetSignIcon(atoi(row[12]));
  1133. if(strncasecmp(row[13],"Generic", 7) == 0)
  1134. sign->SetSignType(SIGN_TYPE_GENERIC);
  1135. else if(strncasecmp(row[13],"Zone", 4) == 0)
  1136. sign->SetSignType(SIGN_TYPE_ZONE);
  1137. sign->SetSignTitle(row[14]);
  1138. sign->SetSignDescription(row[15]);
  1139. sign->SetSignDistance(atof(row[16]));
  1140. sign->SetSignZoneID(atoul(row[17]));
  1141. sign->SetSignZoneX(atof(row[18]));
  1142. sign->SetSignZoneY(atof(row[19]));
  1143. sign->SetSignZoneZ(atof(row[20]));
  1144. sign->SetSignZoneHeading(atof(row[21]));
  1145. sign->SetIncludeHeading(atoi(row[22]) == 1);
  1146. sign->SetIncludeLocation(atoi(row[23]) == 1);
  1147. sign->SetTransporterID(atoul(row[24]));
  1148. sign->SetSizeOffset(atoi(row[25]));
  1149. sign->appearance.display_hand_icon = atoi(row[26]);
  1150. sign->SetVisualState(atoi(row[27]));
  1151. // xpack+holiday value handled at top at position 28+29
  1152. int8 disableSounds = atoul(row[30]);
  1153. sign->SetSoundsDisabled(disableSounds);
  1154. sign->SetMerchantLevelRange(atoul(row[31]), atoul(row[32]));
  1155. sign->SetAAXPRewards(atoul(row[33]));
  1156. sign->SetLootTier(atoul(row[34]));
  1157. sign->SetLootDropType(atoul(row[35]));
  1158. sign->SetLanguage(atoul(row[36]));
  1159. zone->AddSign(id, sign);
  1160. total++;
  1161. LogWrite(SIGN__DEBUG, 5, "Sign", "---Loading Sign: '%s' (%u).", sign->appearance.name, id);
  1162. }
  1163. LogWrite(SIGN__DEBUG, 0, "Sign", "--Loaded %i Sign(s)", total);
  1164. }
  1165. void WorldDatabase::LoadWidgets(ZoneServer* zone){
  1166. Query query;
  1167. MYSQL_ROW row;
  1168. Widget* widget = 0;
  1169. int32 id = 0;
  1170. int32 total = 0;
  1171. 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, s.loot_tier, s.loot_drop_type\n"
  1172. "FROM spawn s\n"
  1173. "INNER JOIN spawn_widgets sw\n"
  1174. "ON s.id = sw.spawn_id\n"
  1175. "INNER JOIN spawn_location_entry le\n"
  1176. "ON sw.spawn_id = le.spawn_id\n"
  1177. "INNER JOIN spawn_location_placement lp\n"
  1178. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1179. "WHERE lp.zone_id = %u\n"
  1180. "GROUP BY s.id",
  1181. zone->GetZoneID());
  1182. while(result && (row = mysql_fetch_row(result))){
  1183. int32 widgetXpackFlag = atoul(row[33]);
  1184. int32 widgetHolidayFlag = atoul(row[34]);
  1185. id = atoul(row[0]);
  1186. if(zone->GetWidget(id, true))
  1187. continue;
  1188. widget = new Widget();
  1189. if (!CheckExpansionFlags(zone, widgetXpackFlag) || !CheckHolidayFlags(zone, widgetHolidayFlag))
  1190. widget->SetOmittedByDBFlag(true);
  1191. widget->SetDatabaseID(id);
  1192. strcpy(widget->appearance.name, row[1]);
  1193. widget->appearance.model_type = atoi(row[2]);
  1194. widget->SetSize(atoi(row[3]));
  1195. widget->appearance.show_command_icon = atoi(row[4]);
  1196. if (row[5] == NULL)
  1197. widget->SetWidgetID(0xFFFFFFFF);
  1198. else
  1199. widget->SetWidgetID(atoul(row[5]));
  1200. widget->SetWidgetX(atof(row[6]));
  1201. widget->SetWidgetY(atof(row[7]));
  1202. widget->SetWidgetZ(atof(row[8]));
  1203. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[9]));
  1204. if(primary_command_list){
  1205. widget->SetPrimaryCommands(primary_command_list);
  1206. widget->primary_command_list_id = atoul(row[9]);
  1207. }
  1208. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[10]));
  1209. if (secondary_command_list) {
  1210. widget->SetSecondaryCommands(secondary_command_list);
  1211. widget->secondary_command_list_id = atoul(row[10]);
  1212. }
  1213. widget->appearance.pos.collision_radius = atoi(row[11]);
  1214. widget->SetIncludeHeading(atoi(row[12]) == 1);
  1215. widget->SetIncludeLocation(atoi(row[13]) == 1);
  1216. widget->SetWidgetIcon(atoi(row[14]));
  1217. if (strncasecmp(row[15], "Generic", 7) == 0)
  1218. widget->SetWidgetType(WIDGET_TYPE_GENERIC);
  1219. else if (strncasecmp(row[15], "Door", 4) == 0)
  1220. widget->SetWidgetType(WIDGET_TYPE_DOOR);
  1221. else if (strncasecmp(row[15], "Lift", 4) == 0)
  1222. widget->SetWidgetType(WIDGET_TYPE_LIFT);
  1223. widget->SetOpenHeading(atof(row[16]));
  1224. widget->SetOpenY(atof(row[17]));
  1225. widget->SetActionSpawnID(atoul(row[18]));
  1226. if(row[19] && strlen(row[19]) > 5)
  1227. widget->SetOpenSound(row[19]);
  1228. if(row[20] && strlen(row[20]) > 5)
  1229. widget->SetCloseSound(row[20]);
  1230. widget->SetOpenDuration(atoi(row[21]));
  1231. widget->SetClosedHeading(atof(row[22]));
  1232. widget->SetLinkedSpawnID(atoul(row[23]));
  1233. widget->SetCloseY(atof(row[24]));
  1234. widget->SetTransporterID(atoul(row[25]));
  1235. widget->SetSizeOffset(atoi(row[26]));
  1236. widget->SetHouseID(atoul(row[27]));
  1237. widget->SetOpenX(atof(row[28]));
  1238. widget->SetOpenZ(atof(row[29]));
  1239. widget->SetCloseX(atof(row[30]));
  1240. widget->SetCloseZ(atof(row[31]));
  1241. widget->appearance.display_hand_icon = atoi(row[32]);
  1242. // xpack+holiday value handled at top at position 33+34
  1243. int8 disableSounds = atoul(row[35]);
  1244. widget->SetSoundsDisabled(disableSounds);
  1245. widget->SetMerchantLevelRange(atoul(row[36]), atoul(row[37]));
  1246. widget->SetAAXPRewards(atoul(row[38]));
  1247. widget->SetLootTier(atoul(row[39]));
  1248. widget->SetLootDropType(atoul(row[40]));
  1249. zone->AddWidget(id, widget);
  1250. total++;
  1251. LogWrite(WIDGET__DEBUG, 5, "Widget", "---Loading Widget: '%s' (%u).", widget->appearance.name, id);
  1252. }
  1253. LogWrite(WIDGET__DEBUG, 0, "Widget", "--Loaded %i Widget(s)", total);
  1254. }
  1255. void WorldDatabase::LoadObjects(ZoneServer* zone){
  1256. Query query;
  1257. MYSQL_ROW row;
  1258. Object* object = 0;
  1259. int32 id = 0;
  1260. int32 total = 0;
  1261. 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, s.loot_tier, s.loot_drop_type\n"
  1262. "FROM spawn s\n"
  1263. "INNER JOIN spawn_objects so\n"
  1264. "ON s.id = so.spawn_id\n"
  1265. "INNER JOIN spawn_location_entry le\n"
  1266. "ON so.spawn_id = le.spawn_id\n"
  1267. "INNER JOIN spawn_location_placement lp\n"
  1268. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1269. "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
  1270. "GROUP BY s.id",
  1271. zone->GetZoneID(), zone->GetInstanceID());
  1272. while(result && (row = mysql_fetch_row(result))){
  1273. int32 objXpackFlag = atoul(row[19]);
  1274. int32 objHolidayFlag = atoul(row[20]);
  1275. id = atoul(row[0]);
  1276. if(zone->GetObject(id, true))
  1277. continue;
  1278. object = new Object();
  1279. if (!CheckExpansionFlags(zone, objXpackFlag) || !CheckHolidayFlags(zone, objHolidayFlag))
  1280. object->SetOmittedByDBFlag(true);
  1281. object->SetDatabaseID(id);
  1282. strcpy(object->appearance.name, row[1]);
  1283. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[4]));
  1284. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[5]));
  1285. if(primary_command_list){
  1286. object->SetPrimaryCommands(primary_command_list);
  1287. object->primary_command_list_id = atoul(row[4]);
  1288. }
  1289. if(secondary_command_list){
  1290. object->SetSecondaryCommands(secondary_command_list);
  1291. object->secondary_command_list_id = atoul(row[5]);
  1292. }
  1293. object->appearance.race = atoi(row[2]);
  1294. object->appearance.model_type = atoi(row[3]);
  1295. object->appearance.targetable = atoi(row[6]);
  1296. object->size = atoi(row[7]);
  1297. object->appearance.display_name = atoi(row[8]);
  1298. object->appearance.visual_state = atoi(row[9]);
  1299. object->appearance.attackable = atoi(row[10]);
  1300. object->appearance.show_level = atoi(row[11]);
  1301. object->appearance.show_command_icon = atoi(row[12]);
  1302. object->appearance.display_hand_icon = atoi(row[13]);
  1303. object->faction_id = atoul(row[14]);
  1304. object->appearance.pos.collision_radius = atoi(row[15]);
  1305. object->SetTransporterID(atoul(row[16]));
  1306. object->SetSizeOffset(atoi(row[17]));
  1307. object->SetDeviceID(atoi(row[18]));
  1308. // xpack value handled at top at position 19
  1309. int8 disableSounds = atoul(row[21]);
  1310. object->SetSoundsDisabled(disableSounds);
  1311. object->SetMerchantLevelRange(atoul(row[22]), atoul(row[23]));
  1312. object->SetAAXPRewards(atoul(row[24]));
  1313. object->SetLootTier(atoul(row[25]));
  1314. object->SetLootDropType(atoul(row[26]));
  1315. zone->AddObject(id, object);
  1316. total++;
  1317. LogWrite(OBJECT__DEBUG, 5, "Object", "---Loading Object: '%s' (%u)", object->appearance.name, id);
  1318. }
  1319. LogWrite(OBJECT__DEBUG, 0, "Object", "--Loaded %i Object(s)", total);
  1320. }
  1321. void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){
  1322. Query query;
  1323. MYSQL_ROW row;
  1324. GroundSpawn* spawn = 0;
  1325. int32 id = 0;
  1326. int32 total = 0;
  1327. 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, s.loot_tier, s.loot_drop_type\n"
  1328. "FROM spawn s\n"
  1329. "INNER JOIN spawn_ground sg\n"
  1330. "ON s.id = sg.spawn_id\n"
  1331. "INNER JOIN spawn_location_entry le\n"
  1332. "ON sg.spawn_id = le.spawn_id\n"
  1333. "INNER JOIN spawn_location_placement lp\n"
  1334. "ON le.spawn_location_id = lp.spawn_location_id\n"
  1335. "WHERE lp.zone_id = %u\n"
  1336. "GROUP BY s.id",
  1337. zone->GetZoneID());
  1338. while(result && (row = mysql_fetch_row(result))){
  1339. int32 gsXpackFlag = atoul(row[21]);
  1340. int32 gsHolidayFlag = atoul(row[22]);
  1341. id = atoul(row[0]);
  1342. if(zone->GetGroundSpawn(id, true))
  1343. continue;
  1344. spawn = new GroundSpawn();
  1345. if (!CheckExpansionFlags(zone, gsXpackFlag) || !CheckHolidayFlags(zone, gsHolidayFlag))
  1346. spawn->SetOmittedByDBFlag(true);
  1347. spawn->SetDatabaseID(id);
  1348. spawn->forceMapCheck = true;
  1349. strcpy(spawn->appearance.name, row[1]);
  1350. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(atoul(row[4]));
  1351. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(atoul(row[5]));
  1352. if(primary_command_list){
  1353. spawn->SetPrimaryCommands(primary_command_list);
  1354. spawn->primary_command_list_id = atoul(row[4]);
  1355. }
  1356. if(secondary_command_list){
  1357. spawn->SetSecondaryCommands(secondary_command_list);
  1358. spawn->secondary_command_list_id = atoul(row[5]);
  1359. }
  1360. spawn->appearance.race = atoi(row[2]);
  1361. spawn->appearance.model_type = atoi(row[3]);
  1362. spawn->appearance.targetable = atoi(row[6]);
  1363. spawn->size = atoi(row[7]);
  1364. spawn->appearance.display_name = atoi(row[8]);
  1365. spawn->appearance.visual_state = atoi(row[9]);
  1366. spawn->appearance.attackable = atoi(row[10]);
  1367. spawn->appearance.show_level = atoi(row[11]);
  1368. spawn->appearance.show_command_icon = atoi(row[12]);
  1369. spawn->appearance.display_hand_icon = atoi(row[13]);
  1370. spawn->faction_id = atoul(row[14]);
  1371. spawn->appearance.pos.collision_radius = atoi(row[15]);
  1372. spawn->SetNumberHarvests(atoi(row[16]));
  1373. spawn->SetAttemptsPerHarvest(atoi(row[17]));
  1374. spawn->SetGroundSpawnEntryID(atoul(row[18]));
  1375. spawn->SetCollectionSkill(row[19]);
  1376. spawn->SetSizeOffset(atoi(row[20]));
  1377. // xpack+holiday value handled at top at position 21+22
  1378. int8 disableSounds = atoul(row[23]);
  1379. spawn->SetSoundsDisabled(disableSounds);
  1380. spawn->SetAAXPRewards(atoul(row[24]));
  1381. spawn->SetLootTier(atoul(row[25]));
  1382. spawn->SetLootDropType(atoul(row[26]));
  1383. zone->AddGroundSpawn(id, spawn);
  1384. total++;
  1385. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn: '%s' (%u)", spawn->appearance.name, id);
  1386. }
  1387. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn(s)", total);
  1388. }
  1389. void WorldDatabase::LoadGroundSpawnItems(ZoneServer* zone) {
  1390. Query query;
  1391. MYSQL_ROW row;
  1392. int32 total = 0;
  1393. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT groundspawn_id, item_id, is_rare, grid_id FROM groundspawn_items;");
  1394. while(result && (row = mysql_fetch_row(result)))
  1395. {
  1396. zone->AddGroundSpawnItem(atoul(row[0]), atoul(row[1]), atoi(row[2]), atoul(row[3]));
  1397. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn Items: ID: %u\n", atoul(row[0]));
  1398. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---item: %ul, rare: %i, grid: %ul", atoul(row[1]), atoi(row[2]), atoul(row[3]));
  1399. total++;
  1400. }
  1401. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn Item%s.", total, total == 1 ? "" : "s");
  1402. }
  1403. void WorldDatabase::LoadGroundSpawnEntries(ZoneServer* zone) {
  1404. Query query;
  1405. MYSQL_ROW row;
  1406. int32 total = 0;
  1407. 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;");
  1408. while(result && (row = mysql_fetch_row(result)))
  1409. {
  1410. // this is getting ridonkulous...
  1411. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn ID: %u\n" \
  1412. "---min_skill_level: %i, min_adventure_level: %i, bonus_table: %i\n" \
  1413. "---harvest1: %.2f, harvest3: %.2f, harvest5: %.2f\n" \
  1414. "---harvest_imbue: %.2f, harvest_rare: %.2f, harvest10: %.2f\n" \
  1415. "---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]));
  1416. 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]));
  1417. total++;
  1418. }
  1419. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "--Loaded %i GroundSpawn Entr%s.", total, total == 1 ? "y" : "ies");
  1420. LoadGroundSpawnItems(zone);
  1421. }
  1422. bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* client)
  1423. {
  1424. DatabaseResult result;
  1425. if( database_new.Select(&result, "SELECT * FROM character_details WHERE char_id = %i LIMIT 0, 1", id) )
  1426. {
  1427. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading character_details for '%s' (char_id: %u)", client->GetPlayer()->GetName(), id);
  1428. while( result.Next() )
  1429. {
  1430. InfoStruct* info = client->GetPlayer()->GetInfoStruct();
  1431. client->GetPlayer()->SetHP(result.GetSInt32Str("hp"));
  1432. client->GetPlayer()->SetPower(result.GetSInt32Str("power"));
  1433. info->set_max_concentration(result.GetInt8Str("max_concentration"));
  1434. if (info->get_max_concentration() == 0)
  1435. info->set_max_concentration(5);
  1436. info->set_attack_base(result.GetInt16Str("attack"));
  1437. info->set_mitigation_base(result.GetInt16Str("mitigation"));
  1438. info->set_avoidance_base(result.GetInt16Str("avoidance"));
  1439. info->set_parry_base(result.GetInt16Str("parry"));
  1440. info->set_deflection_base(result.GetInt16Str("deflection"));
  1441. info->set_block_base(result.GetInt16Str("block"));
  1442. info->set_str_base(result.GetInt16Str("str"));
  1443. info->set_sta_base(result.GetInt16Str("sta"));
  1444. info->set_agi_base(result.GetInt16Str("agi"));
  1445. info->set_wis_base(result.GetInt16Str("wis"));
  1446. info->set_intel_base(result.GetInt16Str("intel"));
  1447. // old resist types
  1448. info->set_heat_base(result.GetInt16Str("heat"));
  1449. info->set_cold_base(result.GetInt16Str("cold"));
  1450. info->set_magic_base(result.GetInt16Str("magic"));
  1451. info->set_mental_base(result.GetInt16Str("mental"));
  1452. info->set_divine_base(result.GetInt16Str("divine"));
  1453. info->set_disease_base(result.GetInt16Str("disease"));
  1454. info->set_poison_base(result.GetInt16Str("poison"));
  1455. //
  1456. info->set_coin_copper(result.GetInt32Str("coin_copper"));
  1457. info->set_coin_silver(result.GetInt32Str("coin_silver"));
  1458. info->set_coin_gold(result.GetInt32Str("coin_gold"));
  1459. info->set_coin_plat(result.GetInt32Str("coin_plat"));
  1460. info->set_pet_name(result.GetStringStr("pet_name") ? std::string(result.GetStringStr("pet_name")) : std::string(""));
  1461. const char* bio = result.GetStringStr("biography");
  1462. if(bio && strlen(bio) > 0)
  1463. info->set_biography(std::string(bio));
  1464. info->set_status_points(result.GetInt32Str("status_points"));
  1465. client->GetPlayer()->GetPlayerInfo()->SetBindZone(result.GetInt32Str("bind_zone_id"));
  1466. client->GetPlayer()->GetPlayerInfo()->SetBindX(result.GetFloatStr("bind_x"));
  1467. client->GetPlayer()->GetPlayerInfo()->SetBindY(result.GetFloatStr("bind_y"));
  1468. client->GetPlayer()->GetPlayerInfo()->SetBindZ(result.GetFloatStr("bind_z"));
  1469. client->GetPlayer()->GetPlayerInfo()->SetBindHeading(result.GetFloatStr("bind_heading"));
  1470. client->GetPlayer()->GetPlayerInfo()->SetHouseZone(result.GetInt32Str("house_zone_id"));
  1471. client->GetPlayer()->SetTotalHP(result.GetSInt32Str("max_hp"));
  1472. client->GetPlayer()->SetTotalPower(result.GetSInt32Str("max_power"));
  1473. client->GetPlayer()->SetAssignedAA(result.GetInt16Str("assigned_aa"));
  1474. client->GetPlayer()->SetUnassignedAA(result.GetInt16Str("unassigned_aa"));
  1475. client->GetPlayer()->SetTradeskillAA(result.GetInt16Str("tradeskill_aa"));
  1476. client->GetPlayer()->SetUnassignedTradeskillAA(result.GetInt16Str("unassigned_tradeskill_aa"));
  1477. client->GetPlayer()->SetPrestigeAA(result.GetInt16Str("prestige_aa"));
  1478. client->GetPlayer()->SetUnassignedPrestigeAA(result.GetInt16Str("unassigned_prestige_aa"));
  1479. client->GetPlayer()->SetTradeskillPrestigeAA(result.GetInt16Str("tradeskill_prestige_aa"));
  1480. client->GetPlayer()->SetUnassignedTradeskillPrestigeAA(result.GetInt16Str("unassigned_tradeskill_prestige_aa"));
  1481. info->set_xp(result.GetInt32Str("xp"));
  1482. info->set_xp_needed(result.GetInt32Str("xp_needed"));
  1483. if(info->get_xp_needed()== 0)
  1484. client->GetPlayer()->SetNeededXP();
  1485. info->set_xp_debt(result.GetFloatStr("xp_debt"));
  1486. info->set_xp_vitality(result.GetFloatStr("xp_vitality"));
  1487. info->set_ts_xp(result.GetInt32Str("tradeskill_xp"));
  1488. info->set_ts_xp_needed(result.GetInt32Str("tradeskill_xp_needed"));
  1489. if (info->get_ts_xp_needed() == 0)
  1490. client->GetPlayer()->SetNeededTSXP();
  1491. info->set_tradeskill_xp_vitality(result.GetFloatStr("tradeskill_xp_vitality"));
  1492. client->GetPlayer()->SetTotalHPBase(client->GetPlayer()->GetTotalHP());
  1493. client->GetPlayer()->SetTotalPowerBase(client->GetPlayer()->GetTotalPower());
  1494. info->set_bank_coin_copper(result.GetInt32Str("bank_copper"));
  1495. info->set_bank_coin_silver(result.GetInt32Str("bank_silver"));
  1496. info->set_bank_coin_gold(result.GetInt32Str("bank_gold"));
  1497. info->set_bank_coin_plat(result.GetInt32Str("bank_plat"));
  1498. client->GetPlayer()->SetCombatVoice(result.GetInt16Str("combat_voice"));
  1499. client->GetPlayer()->SetEmoteVoice(result.GetInt16Str("emote_voice"));
  1500. client->GetPlayer()->SetBiography(result.GetStringStr("biography"));
  1501. client->GetPlayer()->GetInfoStruct()->set_flags(result.GetInt32Str("flags"));
  1502. client->GetPlayer()->GetInfoStruct()->set_flags2(result.GetInt32Str("flags2"));
  1503. client->GetPlayer()->SetLastName(result.GetStringStr("last_name"));
  1504. // new resist types
  1505. info->set_elemental_base(result.GetInt16Str("elemental"));
  1506. info->set_arcane_base(result.GetInt16Str("arcane"));
  1507. info->set_noxious_base(result.GetInt16Str("noxious"));
  1508. // new savagery and dissonance
  1509. client->GetPlayer()->SetSavagery(result.GetSInt16Str("savagery"));
  1510. client->GetPlayer()->SetDissonance(result.GetSInt16Str("dissonance"));
  1511. client->GetPlayer()->SetTotalSavageryBase(client->GetPlayer()->GetTotalSavagery());
  1512. client->GetPlayer()->SetTotalDissonanceBase(client->GetPlayer()->GetTotalDissonance());
  1513. client->GetPlayer()->SetCurrentLanguage(result.GetInt32Str("current_language"));
  1514. }
  1515. return true;
  1516. }
  1517. else
  1518. {
  1519. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character_details for '%s' (char_id: %u)", client->GetPlayer()->GetName(), id);
  1520. return false;
  1521. }
  1522. }
  1523. bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client* client){
  1524. Query query, query4;
  1525. MYSQL_ROW row, row4;
  1526. int32 id = 0;
  1527. query.escaped_name = getEscapeString(ch_name);
  1528. 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, alignment FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id);
  1529. // no character found
  1530. if ( result == NULL ) {
  1531. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
  1532. return false;
  1533. }
  1534. if (mysql_num_rows(result) == 1){
  1535. row = mysql_fetch_row(result);
  1536. id = strtoul(row[0], NULL, 0);
  1537. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading character for '%s' (char_id: %u)", ch_name, id);
  1538. client->SetCharacterID(id);
  1539. client->GetPlayer()->SetCharacterID(id);
  1540. client->SetAccountID(account_id);
  1541. client->GetPlayer()->SetName(ch_name);
  1542. client->GetPlayer()->SetX(atof(row[2]));
  1543. client->GetPlayer()->SetY(atof(row[3]));
  1544. client->GetPlayer()->SetZ(atof(row[4]));
  1545. client->GetPlayer()->SetHeading(atof(row[5]));
  1546. client->SetAdminStatus(atoi(row[6]));
  1547. client->GetPlayer()->SetRace(atoi(row[7]));
  1548. client->GetPlayer()->SetModelType(atoi(row[8]));
  1549. client->GetPlayer()->SetAdventureClass(atoi(row[9]));
  1550. client->GetPlayer()->SetDeity(atoi(row[10]));
  1551. client->GetPlayer()->SetLevel(atoi(row[11]));
  1552. client->GetPlayer()->SetGender(atoi(row[12]));
  1553. client->GetPlayer()->SetTradeskillClass(atoi(row[13]));
  1554. client->GetPlayer()->SetTSLevel(atoi(row[14]));
  1555. client->GetPlayer()->features.wing_type = atoi(row[15]);
  1556. client->GetPlayer()->features.hair_type = atoi(row[16]);
  1557. client->GetPlayer()->features.chest_type = atoi(row[17]);
  1558. client->GetPlayer()->features.legs_type = atoi(row[18]);
  1559. client->GetPlayer()->features.wing_type = atoi(row[19]);
  1560. client->GetPlayer()->features.soga_hair_type = atoi(row[20]);
  1561. client->GetPlayer()->features.soga_chest_type = atoi(row[21]);
  1562. client->GetPlayer()->features.soga_legs_type = atoi(row[22]);
  1563. client->SetNameCRC(atoul(row[23]));
  1564. client->GetPlayer()->features.hair_face_type = atoi(row[24]);
  1565. client->GetPlayer()->features.soga_hair_face_type = atoi(row[25]);
  1566. int32 instanceid = atoi(row[26]);
  1567. int32 groupid = atoi(row[27]);
  1568. client->SetRejoinGroupID(groupid);
  1569. int32 zoneid = atoul(row[1]);
  1570. /*
  1571. JA Notes on SOGA: I think there are many more settings to add than were commented out here,
  1572. because I can load a SOGA model player, but some features are missing (Barbarian WOAD, Skin tons, etc)
  1573. SOGA chars looked ok in LoginServer screen tho... odd.
  1574. */
  1575. // load character instances here
  1576. if ( LoadCharacterInstances(client) )
  1577. client->UpdateCharacterInstances();
  1578. if ( instanceid > 0 )
  1579. client->SetCurrentZoneByInstanceID(instanceid, zoneid);
  1580. else
  1581. client->SetCurrentZone(zoneid);
  1582. int32 lastsavedtime = atoi(row[28]);
  1583. client->SetLastSavedTimeStamp(lastsavedtime);
  1584. if (row[29])
  1585. client->GetPlayer()->GetPlayerInfo()->SetAccountAge(atoi(row[29]));
  1586. client->GetPlayer()->GetInfoStruct()->set_alignment(atoi(row[30]));
  1587. LoadCharacterFriendsIgnoreList(client->GetPlayer());
  1588. MYSQL_RES* result4 = query4.RunQuery2(Q_SELECT, "SELECT `guild_id` FROM `guild_members` WHERE `char_id`=%u", id);
  1589. if (result4 && (row4 = mysql_fetch_row(result4))) {
  1590. Guild* guild = guild_list.GetGuild(atoul(row4[0]));
  1591. if (guild) {
  1592. client->GetPlayer()->SetGuild(guild);
  1593. string subtitle;
  1594. subtitle.append("<").append(guild->GetName()).append(">");
  1595. client->GetPlayer()->SetSubTitle(subtitle.c_str());
  1596. }
  1597. }
  1598. LoadCharacterHistory(id, client->GetPlayer());
  1599. LoadCharacterLUAHistory(id, client->GetPlayer());
  1600. LoadPlayerStatistics(client->GetPlayer(), id);
  1601. LoadPlayerCollections(client->GetPlayer());
  1602. LoadPlayerRecipes(client->GetPlayer());
  1603. //LoadPlayerAchievements(client->GetPlayer());
  1604. LoadPlayerAchievementsUpdates(client->GetPlayer());
  1605. LoadAppearances(client->GetCurrentZone(), client);
  1606. return LoadCharacterStats(id, account_id, client);
  1607. }
  1608. // should not be here...
  1609. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name);
  1610. return false;
  1611. }
  1612. void WorldDatabase::LoadCharacterQuestRewards(Client* client) {
  1613. Query query;
  1614. MYSQL_ROW row;
  1615. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT indexed, quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description FROM character_quest_rewards where char_id = %u ORDER BY indexed asc", client->GetCharacterID());
  1616. int8 count = 0;
  1617. if(result)
  1618. {
  1619. while(result && (row = mysql_fetch_row(result)))
  1620. {
  1621. int32 index = atoul(row[0]);
  1622. int32 quest_id = atoul(row[1]);
  1623. bool is_temporary = atoul(row[2]);
  1624. bool is_collection = atoul(row[3]);
  1625. bool has_displayed = atoul(row[4]);
  1626. int64 tmp_coin = 0;
  1627. #ifdef WIN32
  1628. tmp_coin = _strtoui64(row[5], NULL, 10);
  1629. #else
  1630. tmp_coin = strtoull(row[5], 0, 10);
  1631. #endif
  1632. int32 tmp_status = atoul(row[6]);
  1633. std::string description = std::string("");
  1634. if(row[7]) {
  1635. std::string description = std::string(row[7]);
  1636. }
  1637. if(is_collection) {
  1638. map<int32, Collection*>* collections = client->GetPlayer()->GetCollectionList()->GetCollections();
  1639. map<int32, Collection*>::iterator itr;
  1640. Collection* collection = 0;
  1641. for (itr = collections->begin(); itr != collections->end(); itr++) {
  1642. collection = itr->second;
  1643. if (collection->GetIsReadyToTurnIn()) {
  1644. client->GetPlayer()->SetPendingCollectionReward(collection);
  1645. break;
  1646. }
  1647. }
  1648. }
  1649. if(is_temporary) {
  1650. LoadCharacterQuestTemporaryRewards(client, quest_id);
  1651. }
  1652. client->QueueQuestReward(quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description, true, index);
  1653. count++;
  1654. }
  1655. }
  1656. if(count) {
  1657. client->SetQuestUpdateState(true);
  1658. }
  1659. }
  1660. void WorldDatabase::LoadCharacterQuestTemporaryRewards(Client* client, int32 quest_id) {
  1661. Query query;
  1662. MYSQL_ROW row;
  1663. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id, count FROM character_quest_temporary_rewards where char_id = %u and quest_id = %u", client->GetCharacterID(), quest_id);
  1664. int8 count = 0;
  1665. if(result)
  1666. {
  1667. while(result && (row = mysql_fetch_row(result)))
  1668. {
  1669. int32 item_id = atoul(row[0]);
  1670. Quest* quest = client->GetPlayer()->GetAnyQuest(quest_id);
  1671. if(quest) {
  1672. Item* item = master_item_list.GetItem(item_id);
  1673. if(item) {
  1674. Item* tmpItem = new Item(item);
  1675. tmpItem->details.count = atoul(row[1]);
  1676. quest->AddTmpRewardItem(tmpItem);
  1677. }
  1678. }
  1679. }
  1680. }
  1681. }
  1682. bool WorldDatabase::InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id){
  1683. Query query1;
  1684. Query query2;
  1685. Query query3;
  1686. Query query4;
  1687. Query query5;
  1688. /* Blank record */
  1689. query1.RunQuery2(Q_INSERT, "INSERT INTO `character_details` (`char_id`) VALUES (%u)", character_id);
  1690. /* Using the class id and race id */
  1691. 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);
  1692. if (query2.GetAffectedRows() > 0)
  1693. return true;
  1694. /* Using the class id and race id = 255 */
  1695. 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);
  1696. if (query3.GetAffectedRows() > 0)
  1697. return true;
  1698. /* Using class id = 255 and the race id */
  1699. 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);
  1700. if (query4.GetAffectedRows() > 0)
  1701. return true;
  1702. /* Using class id = 255 and race id = 255 */
  1703. 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);
  1704. if (query5.GetAffectedRows() > 0)
  1705. return true;
  1706. return false;
  1707. }
  1708. int32 WorldDatabase::GetCharacterTimeStamp(int32 character_id, int32 account_id,bool* char_exists){
  1709. Query query;
  1710. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT unix_timestamp FROM characters where id=%i and account_id=%i",character_id,account_id);
  1711. if(result && mysql_num_rows(result) > 0) {
  1712. MYSQL_ROW row;
  1713. row = mysql_fetch_row(result);
  1714. *char_exists = true;
  1715. return atoi(row[0]); // Return timestamp
  1716. }
  1717. else
  1718. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError());
  1719. *char_exists = false;
  1720. return 0;
  1721. }
  1722. int32 WorldDatabase::GetCharacterTimeStamp(int32 character_id) {
  1723. Query query;
  1724. MYSQL_ROW row;
  1725. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT unix_timestamp FROM characters WHERE id=%u", character_id);
  1726. int32 ret = 0;
  1727. if (result && (row = mysql_fetch_row(result)))
  1728. ret = atoul(row[0]);
  1729. return ret;
  1730. }
  1731. bool WorldDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update){
  1732. Query query;
  1733. string update_charts = string("update characters set unix_timestamp=%i where id=%i and account_id=%i");
  1734. query.RunQuery2(Q_UPDATE, update_charts.c_str(),timestamp_update,character_id,account_id);
  1735. if(!query.GetAffectedRows())
  1736. {
  1737. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError());
  1738. return false;
  1739. }
  1740. return true;
  1741. }
  1742. bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char* propValue) {
  1743. Query query;
  1744. string update_status = string("update character_properties set propvalue='%s' where charid=%i and propname='%s'");
  1745. query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, client->GetCharacterID(), propName);
  1746. if (!query.GetAffectedRows())
  1747. {
  1748. query.RunQuery2(Q_UPDATE, "insert into character_properties (charid, propname, propvalue) values(%i, '%s', '%s')", client->GetCharacterID(), propName, propValue);
  1749. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  1750. LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError());
  1751. return false;
  1752. }
  1753. }
  1754. return true;
  1755. }
  1756. bool WorldDatabase::loadCharacterProperties(Client* client) {
  1757. Query query;
  1758. MYSQL_ROW row;
  1759. int32 id = 0;
  1760. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM character_properties where charid = %i", client->GetCharacterID());
  1761. // no character found
  1762. if (result == NULL) {
  1763. LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName());
  1764. return false;
  1765. }
  1766. while (result && (row = mysql_fetch_row(result))) {
  1767. char* prop_name = row[0];
  1768. char* prop_value = row[1];
  1769. if (!prop_name || !prop_value)
  1770. continue;
  1771. if (!stricmp(prop_name, CHAR_PROPERTY_SPEED))
  1772. {
  1773. float new_speed = atof(prop_value);
  1774. client->GetPlayer()->SetSpeed(new_speed,true);
  1775. client->GetPlayer()->SetCharSheetChanged(true);
  1776. }
  1777. else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE))
  1778. {
  1779. int8 flymode = atoi(prop_value);
  1780. if (flymode) // avoid fly mode notification unless enabled
  1781. ClientPacketFunctions::SendFlyMode(client, flymode, false);
  1782. }
  1783. else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL))
  1784. {
  1785. int8 invul = atoi(prop_value);
  1786. client->GetPlayer()->SetInvulnerable(invul == 1);
  1787. if (client->GetPlayer()->GetInvulnerable())
  1788. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!");
  1789. }
  1790. else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION))
  1791. {
  1792. int8 val = atoi(prop_value);
  1793. client->GetPlayer()->SetGMVision(val == 1);
  1794. client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false);
  1795. if (val)
  1796. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!");
  1797. }
  1798. else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG))
  1799. {
  1800. int8 val = atoi(prop_value);
  1801. client->SetRegionDebug(val == 1);
  1802. if (val)
  1803. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!");
  1804. }
  1805. else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG))
  1806. {
  1807. int8 val = atoi(prop_value);
  1808. if (val)
  1809. {
  1810. client->SetLuaDebugClient(true);
  1811. if (lua_interface)
  1812. lua_interface->UpdateDebugClients(client);
  1813. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages.");
  1814. }
  1815. }
  1816. }
  1817. return true;
  1818. }
  1819. //gets the name FROM the db with the right letters in caps
  1820. string WorldDatabase::GetPlayerName(char* name){
  1821. Query query;
  1822. string ret = "";
  1823. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM characters where name='%s'", getSafeEscapeString(name).c_str());
  1824. if(result && mysql_num_rows(result) > 0) {
  1825. MYSQL_ROW row;
  1826. row = mysql_fetch_row(result);
  1827. if(row[0])
  1828. ret = string(row[0]);
  1829. }
  1830. return ret;
  1831. }
  1832. int32 WorldDatabase::GetCharacterID(const char* name) {
  1833. int32 id = 0;
  1834. Query query;
  1835. if (name) {
  1836. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM `characters` WHERE `name`='%s'", name);
  1837. if (result && mysql_num_rows(result) > 0) {
  1838. MYSQL_ROW row;
  1839. row = mysql_fetch_row(result);
  1840. id = atoul(row[0]);
  1841. }
  1842. }
  1843. return id;
  1844. }
  1845. int32 WorldDatabase::GetCharacterCurrentZoneID(int32 character_id) {
  1846. int32 id = 0;
  1847. Query query;
  1848. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `current_zone_id` FROM `characters` WHERE `id`=%u", character_id);
  1849. if (result && mysql_num_rows(result) > 0) {
  1850. MYSQL_ROW row;
  1851. row = mysql_fetch_row(result);
  1852. id = atoul(row[0]);
  1853. }
  1854. return id;
  1855. }
  1856. int32 WorldDatabase::GetCharacterAccountID(int32 character_id) {
  1857. int32 id = 0;
  1858. Query query;
  1859. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `account_id` FROM `characters` WHERE `id`=%u", character_id);
  1860. if (result && mysql_num_rows(result) > 0) {
  1861. MYSQL_ROW row;
  1862. row = mysql_fetch_row(result);
  1863. id = atoul(row[0]);
  1864. }
  1865. return id;
  1866. }
  1867. sint16 WorldDatabase::GetHighestCharacterAdminStatus(int32 account_id){
  1868. Query query;
  1869. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(admin_status) FROM characters where account_id=%i ",account_id);
  1870. if(result && mysql_num_rows(result) > 0) {
  1871. MYSQL_ROW row;
  1872. row = mysql_fetch_row(result);
  1873. if ( row[0] != NULL )
  1874. return atoi(row[0]); // Return characters status
  1875. else
  1876. return 0;
  1877. }
  1878. return 0;
  1879. }
  1880. sint16 WorldDatabase::GetLowestCharacterAdminStatus(int32 account_id){
  1881. Query query;
  1882. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT min(admin_status) FROM characters where account_id=%i ",account_id);
  1883. if(result && mysql_num_rows(result) > 0) {
  1884. MYSQL_ROW row;
  1885. row = mysql_fetch_row(result);
  1886. if ( row[0] != NULL )
  1887. return atoi(row[0]); // Return characters status
  1888. else
  1889. return 0;
  1890. }
  1891. return 0;
  1892. }
  1893. sint16 WorldDatabase::GetCharacterAdminStatus(char* character_name){
  1894. Query query;
  1895. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT admin_status FROM characters where name='%s'", getSafeEscapeString(character_name).c_str());
  1896. if(result && mysql_num_rows(result) > 0) {
  1897. MYSQL_ROW row;
  1898. row = mysql_fetch_row(result);
  1899. return atoi(row[0]); // Return characters level
  1900. }
  1901. else
  1902. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1903. return -10;
  1904. }
  1905. sint16 WorldDatabase::GetCharacterAdminStatus(int32 account_id , int32 char_id){
  1906. Query query;
  1907. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT admin_status FROM characters where account_id=%i and id=%i",account_id,char_id);
  1908. if(result && mysql_num_rows(result) > 0) {
  1909. MYSQL_ROW row;
  1910. row = mysql_fetch_row(result);
  1911. return atoi(row[0]); // Return characters status
  1912. }
  1913. else{
  1914. Query query2;
  1915. result = query2.RunQuery2(Q_SELECT, "SELECT count(id) FROM characters where account_id=%i and id=%i",account_id,char_id);
  1916. if(result && mysql_num_rows(result) > 0) {
  1917. MYSQL_ROW row;
  1918. row = mysql_fetch_row(result);
  1919. if(atoi(row[0]) == 0) //old character, needs to be deleted FROM login server
  1920. return -10;
  1921. return -8;
  1922. }
  1923. else
  1924. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1925. }
  1926. return PLAY_ERROR_PROBLEM;
  1927. }
  1928. bool WorldDatabase::UpdateAdminStatus(char* character_name, sint16 flag){
  1929. Query query;
  1930. string update_status = string("update characters set admin_status=%i where name='%s'");
  1931. query.RunQuery2(Q_UPDATE, update_status.c_str(),flag,character_name);
  1932. if(!query.GetAffectedRows())
  1933. {
  1934. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateAdminStatus query '%s': %s", query.GetQuery(), query.GetError());
  1935. return false;
  1936. }
  1937. return true;
  1938. }
  1939. void WorldDatabase::SaveCharacterFloats(int32 char_id, const char* type, float float1, float float2, float float3){
  1940. Query query;
  1941. string create_char = string("insert into char_colors (char_id, type, red, green, blue, signed_value) values(%i,'%s',%i,%i,%i, 1)");
  1942. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, (sint8)(float1*100), (sint8)(float2*100), (sint8)(float3*100));
  1943. if(query.GetError() && strlen(query.GetError()) > 0){
  1944. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterFloats query '%s': %s", query.GetQuery(), query.GetError());
  1945. }
  1946. }
  1947. void WorldDatabase::SaveCharacterColors(int32 char_id, const char* type, EQ2_Color color){
  1948. Query query;
  1949. string create_char = string("insert into char_colors (char_id, type, red, green, blue) values(%i,'%s',%i,%i,%i)");
  1950. query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, color.red, color.green, color.blue);
  1951. if(query.GetError() && strlen(query.GetError()) > 0){
  1952. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterColors query '%s': %s", query.GetQuery(), query.GetError());
  1953. }
  1954. }
  1955. void WorldDatabase::SaveNPCAppearanceEquipment(int32 spawn_id, int8 slot_id, int16 type, int8 red, int8 green, int8 blue, int8 hred, int8 hgreen, int8 hblue){
  1956. Query query;
  1957. 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");
  1958. 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);
  1959. if(query.GetError() && strlen(query.GetError()) > 0){
  1960. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveNPCAppearanceEquipment query '%s': %s", query.GetQuery(), query.GetError());
  1961. }
  1962. }
  1963. int32 WorldDatabase::LoadNPCAppearanceEquipmentData(ZoneServer* zone){
  1964. Query query;
  1965. MYSQL_ROW row;
  1966. int32 spawn_id = 0, new_spawn_id = 0, count = 0;
  1967. NPC* npc = 0;
  1968. int8 slot = 0;
  1969. 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");
  1970. while(result && (row = mysql_fetch_row(result))){
  1971. new_spawn_id = atoul(row[0]);
  1972. if(new_spawn_id != spawn_id){
  1973. npc = zone->GetNPC(new_spawn_id, true);
  1974. if(!npc)
  1975. continue;
  1976. if(spawn_id > 0)
  1977. count++;
  1978. spawn_id = new_spawn_id;
  1979. }
  1980. slot = atoul(row[1]);
  1981. if(slot < NUM_SLOTS){
  1982. npc->SetEquipment(slot, atoul(row[2]), atoul(row[3]), atoul(row[4]), atoul(row[5]), atoul(row[6]), atoul(row[7]), atoul(row[8]));
  1983. }
  1984. }
  1985. if(query.GetError() && strlen(query.GetError()) > 0)
  1986. LogWrite(WORLD__ERROR, 0, "World", "Error in LoadNPCAppearanceEquipmentData query '%s': %s", query.GetQuery(), query.GetError());
  1987. return count;
  1988. }
  1989. int16 WorldDatabase::GetAppearanceID(string name){
  1990. int32 id = 0;
  1991. Query query;
  1992. MYSQL_ROW row;
  1993. query.escaped_name = getEscapeString(name.c_str());
  1994. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT appearance_id FROM appearances where name='%s'", query.escaped_name);
  1995. if(result && mysql_num_rows(result) == 1){
  1996. row = mysql_fetch_row(result);
  1997. id = atoi(row[0]);
  1998. }
  1999. return id;
  2000. }
  2001. vector<int16>* WorldDatabase::GetAppearanceIDsLikeName(string name, bool filtered) {
  2002. vector<int16>* ids = 0;
  2003. Query query;
  2004. MYSQL_ROW row;
  2005. query.escaped_name = getEscapeString(name.c_str());
  2006. MYSQL_RES* result;
  2007. if (filtered)
  2008. 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);
  2009. else
  2010. result = query.RunQuery2(Q_SELECT, "SELECT `appearance_id` FROM `appearances` WHERE `name` RLIKE '%s' AND `name` NOT RLIKE 'ghost' AND `name`", query.escaped_name);
  2011. while (result && (row = mysql_fetch_row(result))) {
  2012. if (!ids)
  2013. ids = new vector<int16>;
  2014. ids->push_back(atoi(row[0]));
  2015. }
  2016. return ids;
  2017. }
  2018. string WorldDatabase::GetAppearanceName(int16 appearance_id) {
  2019. Query query;
  2020. MYSQL_ROW row;
  2021. string name;
  2022. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name` FROM `appearances` WHERE `appearance_id`=%u", appearance_id);
  2023. if (result && (row = mysql_fetch_row(result)))
  2024. name = string(row[0]);
  2025. return name;
  2026. }
  2027. void WorldDatabase::UpdateRandomize(int32 spawn_id, sint32 value) {
  2028. Query query;
  2029. query.RunQuery2(Q_UPDATE, "UPDATE `spawn_npcs` SET `randomize`=`randomize` + %i WHERE `spawn_id`=%u", value, spawn_id);
  2030. }
  2031. int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){
  2032. Query query;
  2033. int8 race_id = create->getType_int8_ByName("race");
  2034. int8 orig_class_id = create->getType_int8_ByName("class");//Normal server
  2035. int8 class_id = orig_class_id;
  2036. if ( create->GetVersion() <= 546 ) {
  2037. class_id = 0; //Classic Server Only
  2038. }
  2039. create->PrintPacket();
  2040. int8 gender_id = create->getType_int8_ByName("gender");
  2041. sint16 auto_admin_status = 0;
  2042. // fetch rules related to setting auto-admin status for server
  2043. bool auto_admin_players = rule_manager.GetGlobalRule(R_World, AutoAdminPlayers)->GetBool();
  2044. bool auto_admin_gm = rule_manager.GetGlobalRule(R_World, AutoAdminGMs)->GetBool();
  2045. /*
  2046. The way I think this is supposed to work :) is if any of your chars are already GM, and AutoAdminGMs rule is true,
  2047. set the new character's admin_status to your highest status for any character active on your loginID.
  2048. - If status > 0, new character > 0
  2049. - If status = 0, new character = 0
  2050. - If status < 0, new character < 0, too... even if auto_admin_gm is true
  2051. If we're not a GM (status = 0) but AutoAdminPlayers rule is true,
  2052. set the new character's admin_status to the default set in AutoAdminStatusValue rule.
  2053. Else, if both rules are False, set everyone to 0 like normal.
  2054. */
  2055. auto_admin_status = GetHighestCharacterAdminStatus(loginID);
  2056. if( auto_admin_status > 0 && auto_admin_gm ) {
  2057. 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);
  2058. }
  2059. else if( auto_admin_players )
  2060. {
  2061. auto_admin_status = rule_manager.GetGlobalRule(R_World, AutoAdminStatusValue)->GetSInt16();
  2062. LogWrite(WORLD__DEBUG, 0, "World", "New character '%s' granted AutoAdminPlayer status: %i", create->getType_EQ2_16BitString_ByName("name").data.c_str(), auto_admin_status);
  2063. }
  2064. else {
  2065. auto_admin_status = 0;
  2066. }
  2067. 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)");
  2068. query.RunQuery2(Q_INSERT, create_char.c_str(),
  2069. loginID,
  2070. create->getType_int32_ByName("server_id"),
  2071. create->getType_EQ2_16BitString_ByName("name").data.c_str(),
  2072. race_id,
  2073. class_id,
  2074. gender_id,
  2075. create->getType_int8_ByName("deity"),
  2076. create->getType_float_ByName("body_size"),
  2077. create->getType_float_ByName("body_age"),
  2078. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_wing_file").data),
  2079. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_chest_file").data),
  2080. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_legs_file").data),
  2081. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_hair_file").data),
  2082. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_race_file").data),
  2083. GetAppearanceID(create->getType_EQ2_16BitString_ByName("legs_file").data),
  2084. GetAppearanceID(create->getType_EQ2_16BitString_ByName("chest_file").data),
  2085. GetAppearanceID(create->getType_EQ2_16BitString_ByName("wing_file").data),
  2086. GetAppearanceID(create->getType_EQ2_16BitString_ByName("hair_file").data),
  2087. GetAppearanceID(create->getType_EQ2_16BitString_ByName("race_file").data),
  2088. GetAppearanceID(create->getType_EQ2_16BitString_ByName("face_file").data),
  2089. GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_face_file").data),
  2090. auto_admin_status);
  2091. if(query.GetError() && strlen(query.GetError()) > 0)
  2092. {
  2093. LogWrite(PLAYER__ERROR, 0, "Player", "Error in SaveCharacter query '%s': %s", query.GetQuery(), query.GetError());
  2094. return 0;
  2095. }
  2096. int32 last_insert_id = query.GetLastInsertedID();
  2097. int32 char_id = last_insert_id;
  2098. UpdateStartingFactions(char_id, create->getType_int8_ByName("starting_zone"));
  2099. UpdateStartingZone(char_id, class_id, race_id, create);
  2100. UpdateStartingItems(char_id, class_id, race_id);
  2101. UpdateStartingSkills(char_id, class_id, race_id);
  2102. UpdateStartingSpells(char_id, class_id, race_id);
  2103. UpdateStartingSkillbar(char_id, class_id, race_id);
  2104. UpdateStartingTitles(char_id, class_id, race_id, gender_id);
  2105. InsertCharacterStats(char_id, class_id, race_id);
  2106. UpdateStartingLanguage(char_id, race_id, create->getType_int8_ByName("starting_zone"));
  2107. AddNewPlayerToServerGuild(loginID, char_id);
  2108. SaveCharacterColors(char_id,"skin_color", create->getType_EQ2_Color_ByName("skin_color"));
  2109. SaveCharacterColors(char_id,"model_color", create->getType_EQ2_Color_ByName("model_color"));
  2110. SaveCharacterColors(char_id,"eye_color", create->getType_EQ2_Color_ByName("eye_color"));
  2111. SaveCharacterColors(char_id,"hair_color1", create->getType_EQ2_Color_ByName("hair_color1"));
  2112. SaveCharacterColors(char_id,"hair_color2", create->getType_EQ2_Color_ByName("hair_color2"));
  2113. SaveCharacterColors(char_id,"hair_highlight", create->getType_EQ2_Color_ByName("hair_highlight"));
  2114. SaveCharacterColors(char_id,"hair_type_color", create->getType_EQ2_Color_ByName("hair_type_color"));
  2115. SaveCharacterColors(char_id,"hair_type_highlight_color", create->getType_EQ2_Color_ByName("hair_type_highlight_color"));
  2116. SaveCharacterColors(char_id,"hair_face_color", create->getType_EQ2_Color_ByName("hair_face_color"));
  2117. SaveCharacterColors(char_id,"hair_face_highlight_color", create->getType_EQ2_Color_ByName("hair_face_highlight_color"));
  2118. SaveCharacterColors(char_id,"wing_color1", create->getType_EQ2_Color_ByName("wing_color1"));
  2119. SaveCharacterColors(char_id,"wing_color2", create->getType_EQ2_Color_ByName("wing_color2"));
  2120. SaveCharacterColors(char_id,"shirt_color", create->getType_EQ2_Color_ByName("shirt_color"));
  2121. SaveCharacterColors(char_id,"unknown_chest_color", create->getType_EQ2_Color_ByName("unknown_chest_color"));
  2122. SaveCharacterColors(char_id,"pants_color", create->getType_EQ2_Color_ByName("pants_color"));
  2123. SaveCharacterColors(char_id,"unknown_legs_color", create->getType_EQ2_Color_ByName("unknown_legs_color"));
  2124. SaveCharacterColors(char_id,"unknown9", create->getType_EQ2_Color_ByName("unknown9"));
  2125. SaveCharacterFloats(char_id,"eye_type", create->getType_float_ByName("eyes2",0), create->getType_float_ByName("eyes2",1), create->getType_float_ByName("eyes2",2));
  2126. SaveCharacterFloats(char_id,"ear_type", create->getType_float_ByName("ears",0), create->getType_float_ByName("ears",1), create->getType_float_ByName("ears",2));
  2127. 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));
  2128. SaveCharacterFloats(char_id,"cheek_type", create->getType_float_ByName("cheeks",0), create->getType_float_ByName("cheeks",1), create->getType_float_ByName("cheeks",2));
  2129. SaveCharacterFloats(char_id,"lip_type", create->getType_float_ByName("lips",0), create->getType_float_ByName("lips",1), create->getType_float_ByName("lips",2));
  2130. SaveCharacterFloats(char_id,"chin_type", create->getType_float_ByName("chin",0), create->getType_float_ByName("chin",1), create->getType_float_ByName("chin",2));
  2131. SaveCharacterFloats(char_id,"nose_type", create->getType_float_ByName("nose",0), create->getType_float_ByName("nose",1), create->getType_float_ByName("nose",2));
  2132. SaveCharacterFloats(char_id,"body_size", create->getType_float_ByName("body_size",0), 0, 0);
  2133. SaveCharacterColors(char_id,"soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color"));
  2134. SaveCharacterColors(char_id,"soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color"));
  2135. SaveCharacterColors(char_id,"soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color"));
  2136. SaveCharacterColors(char_id,"soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1"));
  2137. SaveCharacterColors(char_id,"soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2"));
  2138. SaveCharacterColors(char_id,"soga_hair_highlight", create->getType_EQ2_Color_ByName("soga_hair_highlight"));
  2139. SaveCharacterColors(char_id,"soga_hair_type_color", create->getType_EQ2_Color_ByName("soga_hair_type_color"));
  2140. SaveCharacterColors(char_id,"soga_hair_type_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_type_highlight_color"));
  2141. SaveCharacterColors(char_id,"soga_hair_face_color", create->getType_EQ2_Color_ByName("soga_hair_face_color"));
  2142. SaveCharacterColors(char_id,"soga_hair_face_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_face_highlight_color"));
  2143. SaveCharacterColors(char_id,"soga_wing_color1", create->getType_EQ2_Color_ByName("soga_wing_color1"));
  2144. SaveCharacterColors(char_id,"soga_wing_color2", create->getType_EQ2_Color_ByName("soga_wing_color2"));
  2145. SaveCharacterColors(char_id,"soga_shirt_color", create->getType_EQ2_Color_ByName("soga_shirt_color"));
  2146. SaveCharacterColors(char_id,"soga_unknown_chest_color", create->getType_EQ2_Color_ByName("soga_unknown_chest_color"));
  2147. SaveCharacterColors(char_id,"soga_pants_color", create->getType_EQ2_Color_ByName("soga_pants_color"));
  2148. SaveCharacterColors(char_id,"soga_unknown_legs_color", create->getType_EQ2_Color_ByName("soga_unknown_legs_color"));
  2149. SaveCharacterColors(char_id,"soga_unknown13", create->getType_EQ2_Color_ByName("soga_unknown13"));
  2150. 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));
  2151. 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));
  2152. 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));
  2153. 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));
  2154. 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));
  2155. 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));
  2156. 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));
  2157. return char_id;
  2158. }
  2159. int8 WorldDatabase::CheckNameFilter(const char* name) {
  2160. // the minimum 4 is enforced by the client too
  2161. if(!name || strlen(name) < 4 || strlen(name) > 15) // Even 20 char length is long...
  2162. return BADNAMELENGTH_REPLY;
  2163. uchar* checkname = (uchar*)name;
  2164. for (int32 i = 0; i < strlen(name); i++)
  2165. {
  2166. if(!alpha_check(checkname[i]))
  2167. return NAMEINVALID_REPLY;
  2168. }
  2169. Query query;
  2170. LogWrite(WORLD__DEBUG, 0, "World", "Name check on: %s", name);
  2171. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT count(*) FROM characters WHERE name='%s'",name);
  2172. if(result && mysql_num_rows(result) > 0) {
  2173. MYSQL_ROW row;
  2174. row = mysql_fetch_row(result);
  2175. if(row[0] != 0 && atoi(row[0]) > 0)
  2176. return NAMETAKEN_REPLY;
  2177. }
  2178. else
  2179. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name exist check) (Name query '%s': %s", query.GetQuery(), query.GetError());
  2180. Query query3;
  2181. LogWrite(WORLD__DEBUG, 0, "World", "Name check on: %s (Bots table)", name);
  2182. MYSQL_RES* result3 = query3.RunQuery2(Q_SELECT, "SELECT count(*) FROM bots WHERE name='%s'", name);
  2183. if (result3 && mysql_num_rows(result3) > 0) {
  2184. MYSQL_ROW row;
  2185. row = mysql_fetch_row(result3);
  2186. if (row[0] != 0 && atoi(row[0]) > 0)
  2187. return NAMETAKEN_REPLY;
  2188. }
  2189. else
  2190. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name exist check, bot table) (Name query '%s': %s", query3.GetQuery(), query3.GetError());
  2191. Query query2;
  2192. MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT count(*) FROM name_filter WHERE '%s' like name",name);
  2193. if(result2 && mysql_num_rows(result2) > 0) {
  2194. MYSQL_ROW row;
  2195. row = mysql_fetch_row(result2);
  2196. if(row[0] != 0 && atoi(row[0]) > 0)
  2197. return NAMEFILTER_REPLY;
  2198. else if(row[0] != 0 && atoi(row[0]) == 0)
  2199. return CREATESUCCESS_REPLY;
  2200. }
  2201. else
  2202. LogWrite(WORLD__ERROR, 0, "World", "Error in CheckNameFilter (name_filter check) query '%s': %s", query.GetQuery(), query.GetError());
  2203. return UNKNOWNERROR_REPLY;
  2204. }
  2205. char* WorldDatabase::GetCharacterName(int32 character_id){
  2206. LogWrite(WORLD__TRACE, 9, "World", "Enter: %s", __FUNCTION__);
  2207. Query query;
  2208. char* name = 0;
  2209. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM characters where id=%u",character_id);
  2210. if(result && mysql_num_rows(result) > 0) {
  2211. MYSQL_ROW row;
  2212. row = mysql_fetch_row(result);
  2213. if(row[0] && strlen(row[0]) > 0)
  2214. {
  2215. name = new char[strlen(row[0])+1];
  2216. memset(name,0, strlen(row[0])+1);
  2217. strcpy(name, row[0]);
  2218. }
  2219. }
  2220. else
  2221. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterName query '%s': %s", query.GetQuery(), query.GetError());
  2222. LogWrite(WORLD__TRACE, 9, "World", "Exit: %s", __FUNCTION__);
  2223. return name;
  2224. }
  2225. int8 WorldDatabase::GetCharacterLevel(int32 character_id){
  2226. Query query;
  2227. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT level FROM characters where id=%u",character_id);
  2228. if(result && mysql_num_rows(result) > 0) {
  2229. MYSQL_ROW row;
  2230. row = mysql_fetch_row(result);
  2231. return atoi(row[0]); // Return characters level
  2232. }
  2233. else
  2234. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterLevel query '%s': %s", query.GetQuery(), query.GetError());
  2235. return 0;
  2236. }
  2237. int16 WorldDatabase::GetCharacterModelType(int32 character_id){
  2238. Query query;
  2239. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT model_type FROM characters where id=%u",character_id);
  2240. if(result && mysql_num_rows(result) > 0) {
  2241. MYSQL_ROW row;
  2242. row = mysql_fetch_row(result);
  2243. return atoi(row[0]); // Return characters race
  2244. }
  2245. else
  2246. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterModelType query '%s': %s", query.GetQuery(), query.GetError());
  2247. return 0;
  2248. }
  2249. int8 WorldDatabase::GetCharacterClass(int32 character_id){
  2250. Query query;
  2251. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT class FROM characters where id=%u",character_id);
  2252. if(result && mysql_num_rows(result) > 0) {
  2253. MYSQL_ROW row;
  2254. row = mysql_fetch_row(result);
  2255. return atoi(row[0]); // Return characters class
  2256. }
  2257. else
  2258. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterClass query '%s': %s", query.GetQuery(), query.GetError());
  2259. return 0;
  2260. }
  2261. int8 WorldDatabase::GetCharacterGender(int32 character_id){
  2262. Query query;
  2263. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT gender FROM characters where id=%u",character_id);
  2264. if(result && mysql_num_rows(result) > 0) {
  2265. MYSQL_ROW row;
  2266. row = mysql_fetch_row(result);
  2267. return atoi(row[0]); // Return characters gender
  2268. }
  2269. else
  2270. LogWrite(WORLD__ERROR, 0, "World", "Error in GetCharacterGender query '%s': %s", query.GetQuery(), query.GetError());
  2271. return 0;
  2272. }
  2273. void WorldDatabase::DeleteCharacterQuest(int32 quest_id, int32 char_id, bool repeated_quest) {
  2274. if (repeated_quest) {
  2275. if (!database_new.Query("UPDATE `character_quests` SET `given_date` = `completed_date` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2276. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2277. }
  2278. else {
  2279. if (!database_new.Query("DELETE FROM `character_quests` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2280. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2281. }
  2282. if (!database_new.Query("DELETE FROM `character_quest_progress` WHERE `char_id` = %u AND `quest_id` = %u", char_id, quest_id))
  2283. LogWrite(DATABASE__ERROR, 0, "DBNew", "Error (%u) in DeleteCharacterQuest query:\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2284. }
  2285. void WorldDatabase::SaveCharacterSkills(Client* client){
  2286. vector<Skill*>* skills = client->GetPlayer()->GetSkills()->GetSaveNeededSkills();
  2287. if(skills){
  2288. Query query;
  2289. if(skills->size() > 0){
  2290. Skill* skill = 0;
  2291. for(int32 i=0;i<skills->size();i++){
  2292. skill = skills->at(i);
  2293. 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);
  2294. }
  2295. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2296. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterSkills query '%s': %s", query.GetQuery(), query.GetError());
  2297. }
  2298. safe_delete(skills);
  2299. }
  2300. }
  2301. void WorldDatabase::SaveCharacterQuests(Client* client){
  2302. Query query;
  2303. map<int32, Quest*>::iterator itr;
  2304. master_quest_list.LockQuests(); //prevent reloading until we are done
  2305. client->GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__); //prevent all quest modifications until we are done
  2306. map<int32, Quest*>* quests = client->GetPlayer()->GetPlayerQuests();
  2307. for(itr = quests->begin(); itr != quests->end(); itr++){
  2308. if(client->GetCurrentQuestID() == itr->first){
  2309. query.AddQueryAsync(client->GetCharacterID(),this,Q_UPDATE, "update character_quests set current_quest = 0 where char_id = %u", client->GetCharacterID());
  2310. 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);
  2311. }
  2312. if(itr->second->GetSaveNeeded()){
  2313. 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());
  2314. 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);
  2315. SaveCharacterQuestProgress(client, itr->second);
  2316. itr->second->SetSaveNeeded(false);
  2317. }
  2318. }
  2319. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2320. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterQuests query '%s': %s", query.GetQuery(), query.GetError());
  2321. quests = client->GetPlayer()->GetCompletedPlayerQuests();
  2322. for(itr = quests->begin(); itr != quests->end(); itr++){
  2323. if(itr->second->GetSaveNeeded()){
  2324. query.AddQueryAsync(client->GetCharacterID(), this,Q_DELETE, "delete FROM character_quest_progress where char_id = %u and quest_id = %u", client->GetCharacterID(), itr->first);
  2325. /* 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
  2326. * the completed_date */
  2327. 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());
  2328. itr->second->SetSaveNeeded(false);
  2329. }
  2330. }
  2331. client->GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  2332. master_quest_list.UnlockQuests();
  2333. }
  2334. void WorldDatabase::SaveCharRepeatableQuest(Client* client, int32 quest_id, int16 quest_complete_count) {
  2335. 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))
  2336. LogWrite(DATABASE__ERROR, 0, "DBNew", "DB Error %u\n%s", database_new.GetError(), database_new.GetErrorMsg());
  2337. }
  2338. void WorldDatabase::SaveCharacterQuestProgress(Client* client, Quest* quest){
  2339. Query query;
  2340. vector<QuestStep*>* steps = quest->GetQuestSteps();
  2341. vector<QuestStep*>::iterator itr;
  2342. QuestStep* step = 0;
  2343. if(steps){
  2344. for(itr = steps->begin(); itr != steps->end(); itr++){
  2345. step = *itr;
  2346. if(step && step->GetQuestCurrentQuantity() > 0)
  2347. 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());
  2348. }
  2349. }
  2350. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2351. LogWrite(WORLD__ERROR, 0, "World", "Error in SaveCharacterQuestProgress query '%s': %s", query.GetQuery(), query.GetError());
  2352. }
  2353. void WorldDatabase::LoadCharacterQuestProgress(Client* client){
  2354. Query query;
  2355. 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());
  2356. if(result && mysql_num_rows(result) > 0) {
  2357. MYSQL_ROW row;
  2358. Quest* quest = 0;
  2359. int32 quest_id = 0;
  2360. map<int32, int32>* progress_map = new map<int32, int32>();
  2361. while(result && (row = mysql_fetch_row(result))){
  2362. if(quest_id != atoul(row[0])){
  2363. if(quest_id > 0){
  2364. quest = client->GetPlayer()->GetQuest(quest_id);
  2365. if(quest)
  2366. client->SetPlayerQuest(quest, progress_map);
  2367. }
  2368. quest_id = atoul(row[0]);
  2369. progress_map->clear();
  2370. }
  2371. (*progress_map)[atoul(row[1])] = atoul(row[2]);
  2372. }
  2373. if(progress_map->size() > 0){
  2374. quest = client->GetPlayer()->GetQuest(quest_id);
  2375. if(quest)
  2376. client->SetPlayerQuest(quest, progress_map);
  2377. }
  2378. safe_delete(progress_map);
  2379. }
  2380. else if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2381. LogWrite(WORLD__ERROR, 0, "World", "Error in LoadCharacterQuestProgress query '%s': %s", query.GetQuery(), query.GetError());
  2382. }
  2383. void WorldDatabase::LoadCharacterQuests(Client* client){
  2384. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Character Quests...");
  2385. Query query;
  2386. 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());
  2387. if(result && mysql_num_rows(result) > 0) {
  2388. MYSQL_ROW row;
  2389. Quest* quest = 0;
  2390. while(result && (row = mysql_fetch_row(result))){
  2391. quest = master_quest_list.GetQuest(atoul(row[0]));
  2392. if(quest) {
  2393. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLoading quest_id: %u", atoul(row[0]));
  2394. bool addQuest = true;
  2395. if(row[4] && atoi(row[4]) > 0){
  2396. quest->SetTurnedIn(true);
  2397. if(row[4])
  2398. quest->SetDay(atoi(row[4]));
  2399. if(row[5])
  2400. quest->SetMonth(atoi(row[5]));
  2401. if(row[6] && atoi(row[6]) > 2000)
  2402. quest->SetYear(atoi(row[6]) - 2000);
  2403. client->GetPlayer()->AddCompletedQuest(quest);
  2404. // Added timestamps to quickly compare given and completed dates
  2405. int32 given_timestamp = atoul(row[11]);
  2406. int32 completed_timestamp = atoul(row[12]);
  2407. // If given timestamp is greater then completed then this is a repeatable quest we are working on
  2408. // so get a fresh quest object to add as an active quest
  2409. if (given_timestamp > completed_timestamp)
  2410. {
  2411. safe_delete(quest);
  2412. quest = master_quest_list.GetQuest(atoul(row[0]));
  2413. }
  2414. else
  2415. addQuest = false;
  2416. quest->SetCompleteCount(atoi(row[13]));
  2417. }
  2418. if (addQuest) {
  2419. if(row[1])
  2420. quest->SetDay(atoi(row[1]));
  2421. if(row[2])
  2422. quest->SetMonth(atoi(row[2]));
  2423. if(row[3] && atoi(row[3]) > 2000)
  2424. quest->SetYear(atoi(row[3]) - 2000);
  2425. quest->SetQuestGiver(atoul(row[7]));
  2426. quest->SetTracked(atoi(row[8]) == 1 ? true : false);
  2427. quest->SetQuestFlags(atoul(row[9]));
  2428. quest->SetHidden(atoi(row[10]) == 1 ? true : false);
  2429. client->AddPlayerQuest(quest, false, false);
  2430. }
  2431. quest->SetSaveNeeded(false);
  2432. // Changed this to call reload with step = 0 for all quests and not
  2433. // just quests with quest flags to allow customized set up if needed
  2434. if (lua_interface)
  2435. lua_interface->CallQuestFunction(quest, "Reload", client->GetPlayer(), 0);
  2436. }
  2437. }
  2438. LoadCharacterQuestProgress(client);
  2439. }
  2440. }
  2441. void WorldDatabase::LoadCharacterFriendsIgnoreList(Player* player) {
  2442. if (player) {
  2443. Query query;
  2444. MYSQL_ROW row;
  2445. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name`, `type` FROM `character_social` WHERE `char_id`=%u", player->GetCharacterID());
  2446. while (result && (row = mysql_fetch_row(result))) {
  2447. if (strncmp(row[1], "FRIEND", 6) == 0)
  2448. player->AddFriend(row[0], false);
  2449. else
  2450. player->AddIgnore(row[0], false);
  2451. }
  2452. }
  2453. }
  2454. void WorldDatabase::LoadZoneInfo(ZoneServer* zone){
  2455. Query query;
  2456. int32 ruleset_id;
  2457. char* escaped = getEscapeString(zone->GetZoneName());
  2458. 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, can_bind, can_gate, city_zone FROM zones where name='%s'",escaped);
  2459. if(result && mysql_num_rows(result) > 0) {
  2460. MYSQL_ROW row;
  2461. row = mysql_fetch_row(result);
  2462. zone->SetZoneName(escaped);
  2463. zone->SetZoneID(strtoul(row[0], NULL, 0));
  2464. zone->SetZoneFile(row[1]);
  2465. zone->SetZoneDescription(row[2]);
  2466. zone->SetUnderWorld(atof(row[3]));
  2467. zone->SetSafeX(atof(row[4]));
  2468. zone->SetSafeY(atof(row[5]));
  2469. zone->SetSafeZ(atof(row[6]));
  2470. zone->SetMinimumStatus(atoi(row[7]));
  2471. zone->SetMinimumLevel(atoi(row[8]));
  2472. zone->SetMaximumLevel(atoi(row[9]));
  2473. int8 type = (atoi(row[10]) == 0) ? 0 : atoi(row[10]) - 1;
  2474. zone->SetInstanceType(type);
  2475. zone->SetShutdownTimer(atoul(row[11]));
  2476. char* zone_motd = row[12];
  2477. if (zone_motd && strlen(zone_motd) > 0)
  2478. zone->SetZoneMOTD(string(zone_motd));
  2479. zone->SetDefaultReenterTime(atoi(row[13]));
  2480. zone->SetDefaultResetTime(atoi(row[14]));
  2481. zone->SetDefaultLockoutTime(atoi(row[15]));
  2482. zone->SetForceGroupZoneOption(atoi(row[16]));
  2483. zone->SetSafeHeading(atof(row[17]));
  2484. zone->SetXPModifier(atof(row[18]));
  2485. if ((ruleset_id = atoul(row[19])) > 0 && !rule_manager.SetZoneRuleSet(zone->GetZoneID(), ruleset_id))
  2486. 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);
  2487. // check data_version to see if client has proper expansion to enter a zone
  2488. zone->SetMinimumVersion(GetMinimumClientVersion(atoi(row[20])));
  2489. zone->SetWeatherAllowed(atoi(row[21]) == 0 ? false : true);
  2490. zone->SetZoneSkyFile(row[22]);
  2491. if (zone->IsInstanceZone())
  2492. {
  2493. if ( zone->GetInstanceID() < 1 )
  2494. zone->SetupInstance(CreateNewInstance(zone->GetZoneID()));
  2495. else
  2496. zone->SetupInstance(zone->GetInstanceID());
  2497. }
  2498. zone->SetCanBind(atoul(row[23]));
  2499. zone->SetCanGate(atoul(row[24]));
  2500. zone->SetCityZone(atoi(row[25]));
  2501. }
  2502. safe_delete_array(escaped);
  2503. }
  2504. void WorldDatabase::LoadZoneInfo(ZoneInfo* zone_info) {
  2505. Query query;
  2506. int32 ruleset_id;
  2507. 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);
  2508. if (result && mysql_num_rows(result) > 0) {
  2509. MYSQL_ROW row;
  2510. row = mysql_fetch_row(result);
  2511. strncpy(zone_info->name, row[0], sizeof(zone_info->name));
  2512. strncpy(zone_info->file, row[1], sizeof(zone_info->file));
  2513. strncpy(zone_info->description, row[2], sizeof(zone_info->description));
  2514. zone_info->underworld = atof(row[3]);
  2515. zone_info->safe_x = atof(row[4]);
  2516. zone_info->safe_y = atof(row[5]);
  2517. zone_info->safe_z = atof(row[6]);
  2518. zone_info->min_status = atoi(row[7]);
  2519. zone_info->min_level = atoi(row[8]);
  2520. zone_info->max_level = atoi(row[9]);
  2521. zone_info->instance_type = (atoi(row[10]) == 0) ? 0 : atoi(row[10]) - 1;
  2522. zone_info->shutdown_timer = atoul(row[11]);
  2523. 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));
  2524. zone_info->default_reenter_time = atoi(row[13]);
  2525. zone_info->default_reset_time = atoi(row[14]);
  2526. zone_info->default_lockout_time = atoi(row[15]);
  2527. zone_info->force_group_to_zone = atoi(row[16]);
  2528. 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));
  2529. zone_info->xp_modifier = atof(row[18]);
  2530. zone_info->ruleset_id = atoul(row[19]);
  2531. if ((ruleset_id = atoul(row[19])) > 0 && !rule_manager.SetZoneRuleSet(zone_info->id, ruleset_id))
  2532. 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);
  2533. zone_info->expansion_id = atoi(row[20]);
  2534. zone_info->min_version = GetMinimumClientVersion(zone_info->expansion_id);
  2535. zone_info->always_loaded = atoi(row[21]);
  2536. zone_info->city_zone = atoi(row[22]);
  2537. zone_info->start_zone = atoi(row[23]);
  2538. 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));
  2539. zone_info->weather_allowed = atoi(row[25]);
  2540. strncpy(zone_info->sky_file, row[26], sizeof(zone_info->sky_file));
  2541. }
  2542. }
  2543. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, sint32 value) {
  2544. Query query;
  2545. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`=%i WHERE `id`=%u", field, value, zone_id);
  2546. }
  2547. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, float value) {
  2548. Query query;
  2549. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`=%f WHERE `id`=%u", field, value, zone_id);
  2550. }
  2551. void WorldDatabase::SaveZoneInfo(int32 zone_id, const char* field, const char* value) {
  2552. Query query;
  2553. query.RunQuery2(Q_UPDATE, "UPDATE `zones` SET `%s`='%s' WHERE `id`=%u", field, const_cast<char*>(getEscapeString(value)), zone_id);
  2554. }
  2555. int32 WorldDatabase::GetZoneID(const char* name) {
  2556. int32 zone_id = 0;
  2557. Query query;
  2558. char* escaped = getEscapeString(name);
  2559. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM zones WHERE `name`=\"%s\"", escaped);
  2560. if (result && mysql_num_rows(result) > 0) {
  2561. MYSQL_ROW row;
  2562. row = mysql_fetch_row(result);
  2563. zone_id = atoi(row[0]);
  2564. }
  2565. safe_delete_array(escaped);
  2566. return zone_id;
  2567. }
  2568. bool WorldDatabase::GetZoneRequirements(const char* zoneName, sint16* minStatus, int16* minLevel, int16* maxLevel, int16* minVersion) {
  2569. Query query;
  2570. char* escaped = getEscapeString(zoneName);
  2571. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT min_status, min_level, max_level, expansion_id FROM zones where name='%s'",escaped);
  2572. if(result && mysql_num_rows(result) > 0) {
  2573. MYSQL_ROW row;
  2574. row = mysql_fetch_row(result);
  2575. sint16 status = (sint16)atoi(row[0]);
  2576. int16 levelMin = (int16)atoi(row[1]);
  2577. int16 levelMax = (int16)atoi(row[2]);
  2578. int8 expansion_id = (int8)atoi(row[3]);
  2579. *minStatus = status;
  2580. *minLevel = levelMin;
  2581. *maxLevel = levelMax;
  2582. if( expansion_id >= 40 ) // lowest client we support is RoK - exp04
  2583. *minVersion = GetMinimumClientVersion(expansion_id);
  2584. else
  2585. *minVersion = 0;
  2586. safe_delete_array(escaped);
  2587. return true;
  2588. }
  2589. safe_delete_array(escaped);
  2590. return false;
  2591. }
  2592. int16 WorldDatabase::GetMinimumClientVersion(int8 expansion_id)
  2593. {
  2594. /*
  2595. 1 n/a Classic Expansion
  2596. 2 adv01 Bloodline Chronicles
  2597. 3 adv02 Splitpaw Saga
  2598. 10 exp01 Desert of Flames
  2599. 20 exp02 Kingdom of Sky
  2600. 21 adv04 Fallen Dynasty
  2601. 30 exp03 Echoes of Faydwer
  2602. 40 exp04 Rise of Kunark
  2603. 50 exp05 The Shadow Odyssey
  2604. 60 exp06 Sentinel's Fate
  2605. 61 halas Halas Reborn
  2606. 70 exp07 Destiny of Velious
  2607. 80 exp08 Age of Discovery
  2608. */
  2609. int16 minVer = 0;
  2610. // TODO: eventually replace this with reading values from eq2expansions table
  2611. switch(expansion_id)
  2612. {
  2613. case 40: // ROK
  2614. {
  2615. minVer = 843;
  2616. break;
  2617. }
  2618. case 50: // TSO
  2619. {
  2620. minVer = 908;
  2621. break;
  2622. }
  2623. case 60: // SF
  2624. {
  2625. minVer = 1008;
  2626. break;
  2627. }
  2628. case 61: // Halas
  2629. {
  2630. minVer = 1045;
  2631. break;
  2632. }
  2633. case 70: // DoV
  2634. {
  2635. minVer = 1096;
  2636. break;
  2637. }
  2638. case 80: // AoD
  2639. {
  2640. minVer = 1144;
  2641. break;
  2642. }
  2643. case 90: // CoE
  2644. {
  2645. minVer = 1188;
  2646. break;
  2647. }
  2648. }
  2649. return minVer;
  2650. }
  2651. // returns Expansion Name depending on the connected client's data version
  2652. string WorldDatabase::GetExpansionIDByVersion(int16 version)
  2653. {
  2654. /*
  2655. 0 n/a Classic Expansion
  2656. 0 adv01 Bloodline Chronicles
  2657. 0 adv02 Splitpaw Saga
  2658. 0 exp01 Desert of Flames
  2659. 0 exp02 Kingdom of Sky
  2660. 0 adv04 Fallen Dynasty
  2661. 0 exp03 Echoes of Faydwer
  2662. 843 exp04 Rise of Kunark
  2663. 908 exp05 The Shadow Odyssey
  2664. 1008 exp06 Sentinel's Fate
  2665. 1045 halas Halas Reborn
  2666. 1096 exp07 Destiny of Velious
  2667. 1142 exp08 Age of Discovery
  2668. 1188 exp09 Chains of Eternity
  2669. 9999 (and beyond)
  2670. */
  2671. string ret = "";
  2672. if( version >= 9999 )
  2673. ret = "Unknown";
  2674. else if( version >= 1188 )
  2675. ret = "Chains of Eternity";
  2676. else if( version >= 1142 )
  2677. ret = "Age of Discovery";
  2678. else if( version >= 1096 )
  2679. ret = "Destiny of Velious";
  2680. else if( version >= 1045 )
  2681. ret = "Halas Reborn";
  2682. else if( version >= 1008 )
  2683. ret = "Sentinel's Fate";
  2684. else if( version >= 908 )
  2685. ret = "The Shadow Odyssey";
  2686. else if( version >= 843 )
  2687. ret = "Rise of Kunark";
  2688. else
  2689. ret = "Any";
  2690. return ret;
  2691. }
  2692. void WorldDatabase::LoadSpecialZones(){
  2693. Query query;
  2694. ZoneServer* zone = 0;
  2695. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded FROM zones where always_loaded = 1");
  2696. if(result && mysql_num_rows(result) > 0) {
  2697. MYSQL_ROW row;
  2698. while(result && (row = mysql_fetch_row(result))){
  2699. zone = new ZoneServer(row[1]);
  2700. LoadZoneInfo(zone);
  2701. zone->Init();
  2702. zone->SetAlwaysLoaded(atoi(row[2]) == 1);
  2703. // zone->SetCityZone(atoi(row[3]) == 1);
  2704. }
  2705. }
  2706. }
  2707. bool WorldDatabase::SpawnGroupRemoveAssociation(int32 group1, int32 group2){
  2708. Query query;
  2709. 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);
  2710. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2711. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2712. return false;
  2713. }
  2714. return true;
  2715. }
  2716. bool WorldDatabase::SpawnGroupAddAssociation(int32 group1, int32 group2){
  2717. Query query;
  2718. query.RunQuery2(Q_INSERT, "insert ignore into spawn_location_group_associations (group_id1, group_id2) values(%u, %u)", group1, group2);
  2719. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2720. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupAddAssociation query '%s': %s", query.GetQuery(), query.GetError());
  2721. return false;
  2722. }
  2723. return true;
  2724. }
  2725. bool WorldDatabase::SpawnGroupAddSpawn(Spawn* spawn, int32 group_id){
  2726. Query query;
  2727. query.RunQuery2(Q_INSERT, "insert ignore into spawn_location_group (group_id, placement_id) values(%u, %u)", group_id, spawn->GetSpawnLocationPlacementID());
  2728. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2729. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupAddSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2730. return false;
  2731. }
  2732. return true;
  2733. }
  2734. bool WorldDatabase::SpawnGroupRemoveSpawn(Spawn* spawn, int32 group_id){
  2735. Query query;
  2736. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_group where group_id = %u and placement_id = %u", group_id, spawn->GetSpawnLocationPlacementID());
  2737. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2738. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2739. return false;
  2740. }
  2741. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT count(group_id) FROM spawn_location_group where group_id = %u", group_id);
  2742. if(result && mysql_num_rows(result) > 0) {
  2743. MYSQL_ROW row;
  2744. if((row = mysql_fetch_row(result))){
  2745. if(atoul(row[0]) == 0)
  2746. DeleteSpawnGroup(group_id);
  2747. }
  2748. }
  2749. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2750. LogWrite(WORLD__ERROR, 0, "World", "Error in SpawnGroupRemoveSpawn query '%s': %s", query.GetQuery(), query.GetError());
  2751. return false;
  2752. }
  2753. return true;
  2754. }
  2755. int32 WorldDatabase::CreateSpawnGroup(Spawn* spawn, string name)
  2756. {
  2757. int32 group_id = 0;
  2758. Query query;
  2759. // 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
  2760. 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());
  2761. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  2762. return 0;
  2763. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(group_id) FROM spawn_location_group");
  2764. if(result && mysql_num_rows(result) > 0)
  2765. {
  2766. MYSQL_ROW row;
  2767. if((row = mysql_fetch_row(result)))
  2768. {
  2769. if(row[0])
  2770. group_id = atoul(row[0]);
  2771. }
  2772. }
  2773. return group_id;
  2774. }
  2775. void WorldDatabase::DeleteSpawnGroup(int32 id){
  2776. Query query;
  2777. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_group where group_id = %u", id);
  2778. }
  2779. bool WorldDatabase::SetGroupSpawnChance(int32 id, float chance){
  2780. Query query;
  2781. query.RunQuery2(Q_UPDATE, "replace into spawn_location_group_chances (group_id, percentage) values(%u, %f)", id, chance);
  2782. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  2783. LogWrite(WORLD__ERROR, 0, "World", "Error in SetGroupSpawnChance query '%s': %s", query.GetQuery(), query.GetError());
  2784. return false;
  2785. }
  2786. return true;
  2787. }
  2788. int32 WorldDatabase::LoadSpawnGroupChances(ZoneServer* zone){
  2789. Query query;
  2790. int32 count = 0;
  2791. 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());
  2792. if(result && mysql_num_rows(result) > 0) {
  2793. MYSQL_ROW row;
  2794. int32 group_id = 0;
  2795. float percent = 0;
  2796. while(result && (row = mysql_fetch_row(result))){
  2797. group_id = atoul(row[0]);
  2798. percent = atof(row[1]);
  2799. zone->AddSpawnGroupChance(group_id, percent);
  2800. count++;
  2801. }
  2802. }
  2803. return count;
  2804. }
  2805. int32 WorldDatabase::LoadSpawnLocationGroupAssociations(ZoneServer* zone){
  2806. Query query;
  2807. int32 count = 0;
  2808. 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());
  2809. if(result && mysql_num_rows(result) > 0) {
  2810. MYSQL_ROW row;
  2811. while(result && (row = mysql_fetch_row(result))){
  2812. zone->AddSpawnGroupAssociation(atoul(row[0]), atoul(row[1]));
  2813. count++;
  2814. }
  2815. }
  2816. return count;
  2817. }
  2818. int32 WorldDatabase::LoadSpawnLocationGroups(ZoneServer* zone){
  2819. Query query;
  2820. int32 count = 0;
  2821. 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());
  2822. if(result && mysql_num_rows(result) > 0) {
  2823. MYSQL_ROW row;
  2824. int32 placement_id = 0;
  2825. int32 group_id = 0;
  2826. int32 spawn_location_id = 0;
  2827. while(result && (row = mysql_fetch_row(result))){
  2828. group_id = atoul(row[0]);
  2829. placement_id = atoul(row[1]);
  2830. spawn_location_id = atoul(row[2]);
  2831. zone->AddSpawnGroupLocation(group_id, placement_id, spawn_location_id);
  2832. count++;
  2833. }
  2834. }
  2835. return count;
  2836. }
  2837. int32 WorldDatabase::ProcessSpawnLocations(ZoneServer* zone, const char* sql_query, int8 type){
  2838. int32 number = 0;
  2839. Query query;
  2840. MYSQL_RES* result = query.RunQuery2(Q_SELECT, sql_query, zone->GetZoneID());
  2841. if(result && mysql_num_rows(result) > 0) {
  2842. MYSQL_ROW row;
  2843. int32 spawn_location_id = 0xFFFFFFFF;
  2844. SpawnLocation* spawn_location = 0;
  2845. while(result && (row = mysql_fetch_row(result))){
  2846. if((spawn_location_id == 0xFFFFFFFF) || atoul(row[0]) != spawn_location_id){
  2847. if(spawn_location){
  2848. zone->AddSpawnLocation(spawn_location_id, spawn_location);
  2849. number++;
  2850. }
  2851. spawn_location = new SpawnLocation();
  2852. }
  2853. SpawnEntry* entry = new SpawnEntry;
  2854. spawn_location_id = atoul(row[0]);
  2855. entry->spawn_location_id = spawn_location_id;
  2856. entry->spawn_entry_id = atoul(row[1]);
  2857. entry->spawn_type = type;
  2858. entry->spawn_id = atoul(row[9]);
  2859. entry->spawn_percentage = atof(row[10]);
  2860. entry->respawn = atoul(row[11]);
  2861. entry->expire_time = atoul(row[14]);
  2862. entry->expire_offset = atoul(row[15]);
  2863. //devn00b add stat overrides. Just a slight increase in size. Used in ZoneServer::AddNPCSpawn.
  2864. entry->lvl_override = atoul(row[19]);
  2865. entry->hp_override = atoul(row[20]);
  2866. entry->mp_override = atoul(row[21]);
  2867. entry->str_override = atoul(row[22]);
  2868. entry->sta_override = atoul(row[23]);
  2869. entry->wis_override = atoul(row[24]);
  2870. entry->int_override = atoul(row[25]);
  2871. entry->agi_override = atoul(row[26]);
  2872. entry->heat_override = atoul(row[27]);
  2873. entry->cold_override = atoul(row[28]);
  2874. entry->magic_override = atoul(row[29]);
  2875. entry->mental_override = atoul(row[30]);
  2876. entry->divine_override = atoul(row[31]);
  2877. entry->disease_override = atoul(row[32]);
  2878. entry->poison_override = atoul(row[33]);
  2879. entry->difficulty_override = atoul(row[34]);
  2880. spawn_location->x = atof(row[2]);
  2881. spawn_location->y = atof(row[3]);
  2882. spawn_location->z = atof(row[4]);
  2883. spawn_location->x_offset = atof(row[5]);
  2884. spawn_location->y_offset = atof(row[6]);
  2885. spawn_location->z_offset = atof(row[7]);
  2886. spawn_location->heading = atof(row[8]);
  2887. spawn_location->pitch = atof(row[16]);
  2888. spawn_location->roll = atof(row[17]);
  2889. spawn_location->conditional = atoi(row[18]);
  2890. spawn_location->total_percentage += entry->spawn_percentage;
  2891. spawn_location->grid_id = strtoul(row[12], NULL, 0);
  2892. spawn_location->placement_id = strtoul(row[13], NULL, 0);
  2893. spawn_location->AddSpawn(entry);
  2894. }
  2895. if(spawn_location){
  2896. zone->AddSpawnLocation(spawn_location_id, spawn_location);
  2897. number++;
  2898. }
  2899. }
  2900. return number;
  2901. }
  2902. void WorldDatabase::ResetDatabase(){
  2903. Query query;
  2904. query.RunQuery2("delete FROM table_versions where name != 'table_versions'", Q_DELETE);
  2905. }
  2906. void WorldDatabase::EnableConstraints(){
  2907. Query query;
  2908. query.RunQuery2("/*!40101 SET SQL_MODE=@OLD_SQL_MODE */", Q_DBMS);
  2909. query.RunQuery2("/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */", Q_DBMS);
  2910. }
  2911. void WorldDatabase::DisableConstraints(){
  2912. Query query;
  2913. query.RunQuery2("/*!40101 SET NAMES utf8 */", Q_DBMS);
  2914. query.RunQuery2("/*!40101 SET SQL_MODE=''*/", Q_DBMS);
  2915. query.RunQuery2("/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */", Q_DBMS);
  2916. query.RunQuery2("/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */", Q_DBMS);
  2917. }
  2918. void WorldDatabase::LoadSpawns(ZoneServer* zone)
  2919. {
  2920. Query query;
  2921. int32 npcs = 0, objects = 0, widgets = 0, signs = 0, ground_spawns = 0, spawn_groups = 0, spawn_group_associations = 0, spawn_group_chances = 0;
  2922. LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns");
  2923. 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, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override 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);
  2924. 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, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override 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);
  2925. 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, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override 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);
  2926. 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, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override 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);
  2927. 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, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override 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);
  2928. spawn_groups = LoadSpawnLocationGroups(zone);
  2929. spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
  2930. spawn_group_chances = LoadSpawnGroupChances(zone);
  2931. 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);
  2932. LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit LoadSpawns");
  2933. }
  2934. bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) {
  2935. Query query;
  2936. 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",
  2937. 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());
  2938. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  2939. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnLocationSpawns query '%s': %s", query.GetQuery(), query.GetError());
  2940. return false;
  2941. }
  2942. return true;
  2943. }
  2944. bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString) {
  2945. Query query;
  2946. query.RunQuery2(Q_UPDATE, "update spawn_widgets set %s where widget_id = %u",
  2947. queryString, widget_id);
  2948. if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
  2949. LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnWidget query '%s': %s", query.GetQuery(), query.GetError());
  2950. return false;
  2951. }
  2952. return true;
  2953. }
  2954. vector<string>* WorldDatabase::GetSpawnNameList(const char* in_name){
  2955. Query query;
  2956. string names = "";
  2957. vector<string>* ret = 0;
  2958. string name = getSafeEscapeString(in_name);
  2959. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT concat(spawn.id, ', ', name) FROM spawn where name like '%%%s%%'", name.c_str());
  2960. if(result && mysql_num_rows(result) > 0){
  2961. ret = new vector<string>;
  2962. MYSQL_ROW row;
  2963. int8 num = 0;
  2964. while(result && (row = mysql_fetch_row(result))){
  2965. if(num >= 10)
  2966. break;
  2967. ret->push_back(string(row[0]));
  2968. num++;
  2969. }
  2970. char total[60] = {0};
  2971. if(mysql_num_rows(result) > 10)
  2972. sprintf(total, "Total number of results: %u (Limited to 10)", (int32)mysql_num_rows(result));
  2973. else
  2974. sprintf(total, "Total number of results: %u", (int32)mysql_num_rows(result));
  2975. ret->push_back(string(total));
  2976. }
  2977. return ret;
  2978. }
  2979. string WorldDatabase::GetZoneName(char* zone_description){
  2980. string ret;
  2981. Query query;
  2982. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where description = '%s'", getSafeEscapeString(zone_description).c_str());
  2983. if(result && mysql_num_rows(result) > 0){
  2984. MYSQL_ROW row = mysql_fetch_row(result);
  2985. ret = string(row[0]);
  2986. }
  2987. return ret;
  2988. }
  2989. void WorldDatabase::LoadRevivePoints(vector<RevivePoint*>* revive_points, int32 zone_id){
  2990. if(revive_points && revive_points->size() > 0){
  2991. LogWrite(WORLD__ERROR, 0, "World", "Revive points have already been loaded for this zone!");
  2992. return;
  2993. }
  2994. else if(!revive_points || zone_id == 0){
  2995. LogWrite(WORLD__ERROR, 0, "World", "LoadRevivePoints called with null variables!");
  2996. return;
  2997. }
  2998. Query query;
  2999. 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);
  3000. if(revive_points && result && mysql_num_rows(result) > 0){
  3001. MYSQL_ROW row;
  3002. int32 id = 0;
  3003. RevivePoint* point = 0;
  3004. while(result && (row=mysql_fetch_row(result))){
  3005. point = new RevivePoint;
  3006. point->id = id;
  3007. point->zone_id = atoul(row[0]);
  3008. point->location_name = string(row[1]);
  3009. point->x = atof(row[2]);
  3010. point->y = atof(row[3]);
  3011. point->z = atof(row[4]);
  3012. point->heading = atof(row[5]);
  3013. revive_points->push_back(point);
  3014. id++;
  3015. }
  3016. }
  3017. }
  3018. int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id)
  3019. {
  3020. Query query;
  3021. int32 ret = 0;
  3022. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn where id LIKE '%i____'", zone_id);
  3023. if(result && mysql_num_rows(result) > 0) {
  3024. MYSQL_ROW row;
  3025. row = mysql_fetch_row(result);
  3026. if(row[0])
  3027. ret = atoi(row[0]) + 1;
  3028. }
  3029. if( ret == 0 )
  3030. ret = zone_id * 10000; // there are no spawns for that zone yet, to start with the first ID
  3031. LogWrite(WORLD__DEBUG, 0, "World", "Next Spawn ID for Zone %i: %u", zone_id, ret);
  3032. return ret;
  3033. }
  3034. bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
  3035. Query query;
  3036. string name = getSafeEscapeString(spawn->GetName());
  3037. string suffix = getSafeEscapeString(spawn->GetSuffixTitle());
  3038. string prefix = getSafeEscapeString(spawn->GetPrefixTitle());
  3039. string last_name = getSafeEscapeString(spawn->GetLastName());
  3040. if(spawn->GetDatabaseID() == 0){
  3041. int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
  3042. int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID());
  3043. 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)",
  3044. 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());
  3045. if( new_spawn_id > 0 )
  3046. spawn->SetDatabaseID(new_spawn_id); // use the new zone_id range
  3047. else if( query.GetLastInsertedID() > 0 )
  3048. spawn->SetDatabaseID(query.GetLastInsertedID()); // else fall back to last_inserted_id
  3049. else
  3050. return false; // else, hang your head in shame as you are an utter failure
  3051. if(spawn->IsNPC()){
  3052. 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)",
  3053. 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,
  3054. ((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);
  3055. }
  3056. else if(spawn->IsObject()){
  3057. query.RunQuery2(Q_INSERT, "insert into spawn_objects (spawn_id) values(%u)", spawn->GetDatabaseID());
  3058. }
  3059. else if(spawn->IsWidget()){
  3060. Widget* widget = (Widget*)spawn;
  3061. query.RunQuery2(Q_INSERT, "insert into spawn_widgets (spawn_id, widget_id) values(%u, %u)", spawn->GetDatabaseID(), widget->GetWidgetID());
  3062. }
  3063. else if(spawn->IsSign()){
  3064. query.RunQuery2(Q_INSERT, "insert into spawn_signs (spawn_id, description) values(%u, 'change me')", spawn->GetDatabaseID());
  3065. }
  3066. else if (spawn->IsGroundSpawn()) {
  3067. query.RunQuery2(Q_INSERT, "insert into spawn_ground (spawn_id) values(%u)", spawn->GetDatabaseID());
  3068. }
  3069. }
  3070. else{
  3071. if(spawn->IsNPC()){
  3072. 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",
  3073. name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->appearance.encounter_level, spawn->GetRace(), spawn->GetModelType(),
  3074. 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,
  3075. ((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(),
  3076. spawn->GetTotalHP(), spawn->appearance.heroic_flag, spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(),
  3077. spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(),
  3078. spawn->GetActivityStatus(), ((NPC*)spawn)->GetAlignment(), spawn->GetFactionID(), spawn->appearance.hide_hood, spawn->appearance.emote_state,
  3079. suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  3080. }
  3081. else if(spawn->IsObject()){
  3082. 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",
  3083. 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,
  3084. spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  3085. }
  3086. else if(spawn->IsWidget()){
  3087. Widget* widget = (Widget*)spawn;
  3088. char* openSound = 0;
  3089. char* closeSound = 0;
  3090. if (widget->GetOpenSound() != NULL) openSound = (char*)widget->GetOpenSound(); else openSound = (char*)string("0").c_str();
  3091. if (widget->GetCloseSound() != NULL) closeSound = (char*)widget->GetCloseSound(); else closeSound = (char*)string("0").c_str();
  3092. 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",
  3093. 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(),
  3094. spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
  3095. 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(),
  3096. widget->GetOpenHeading(), widget->GetClosedHeading(), widget->GetOpenX(), widget->GetOpenY(), widget->GetOpenZ(),
  3097. widget->GetActionSpawnID(), openSound, closeSound,widget->GetOpenDuration(),
  3098. widget->GetCloseX(),widget->GetCloseY(),widget->GetCloseZ(),widget->GetLinkedSpawnID(),widget->GetHouseID(),
  3099. spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
  3100. }
  3101. else if(spawn->IsSign()){
  3102. Sign* sign = (Sign*)spawn;
  3103. 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, language = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u",
  3104. 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(),
  3105. spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
  3106. suffix.c_str(), prefix.c_str(), last_name.c_str(), sign->GetSignType(), sign->GetSignZoneID(),
  3107. sign->GetWidgetID(), sign->GetSignTitle(), sign->GetWidgetX(), sign->GetWidgetY(), sign->GetWidgetZ(),
  3108. sign->GetIconValue(), sign->GetSignDescription(), sign->GetSignDistance(), sign->GetSignZoneX(),
  3109. sign->GetSignZoneY(), sign->GetSignZoneZ(), sign->GetSignZoneHeading(), sign->GetIncludeHeading(),
  3110. sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), spawn->GetDatabaseID());
  3111. }
  3112. }
  3113. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3114. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnInfo query '%s': %s", query.GetQuery(), query.GetError());
  3115. return false;
  3116. }
  3117. return true;
  3118. }
  3119. int32 WorldDatabase::SaveCombinedSpawnLocation(ZoneServer* zone, Spawn* in_spawn, const char* name){
  3120. vector<Spawn*>* spawns = in_spawn->GetSpawnGroup();
  3121. uint32 spawnLocationID = 0;
  3122. if(spawns && spawns->size() > 0){
  3123. vector<Spawn*>::iterator itr;
  3124. map<Spawn*, int32>::iterator freq_itr;
  3125. Spawn* spawn = 0;
  3126. map<Spawn*, int32> database_spawns;
  3127. int32 total = 0;
  3128. float x_offset = GetSpawnLocationPlacementOffsetX(in_spawn->GetSpawnLocationID());
  3129. float y_offset = GetSpawnLocationPlacementOffsetY(in_spawn->GetSpawnLocationID());
  3130. float z_offset = GetSpawnLocationPlacementOffsetZ(in_spawn->GetSpawnLocationID());
  3131. int32 spawn_location_id = GetNextSpawnLocation();
  3132. spawnLocationID = spawn_location_id;
  3133. if(!name)
  3134. name = "Combine SpawnGroup Generated";
  3135. if(!CreateNewSpawnLocation(spawn_location_id, name)){
  3136. safe_delete(spawns);
  3137. return 0;
  3138. }
  3139. for(itr = spawns->begin();itr!=spawns->end();itr++){
  3140. spawn = *itr;
  3141. if (spawn) {
  3142. RemoveSpawnFromSpawnLocation(spawn);
  3143. spawn->SetSpawnLocationID(spawn_location_id);
  3144. bool add = true;
  3145. for (freq_itr = database_spawns.begin(); freq_itr != database_spawns.end(); freq_itr++) {
  3146. if (spawn->GetDatabaseID() == freq_itr->first->GetDatabaseID()) {
  3147. freq_itr->second++;
  3148. total++;
  3149. add = false;
  3150. }
  3151. }
  3152. if (add) {
  3153. database_spawns[spawn] = 1;
  3154. total++;
  3155. }
  3156. }
  3157. }
  3158. for(freq_itr = database_spawns.begin(); freq_itr != database_spawns.end(); freq_itr++){
  3159. int8 percent = (freq_itr->second*100)/total;
  3160. if(!SaveSpawnEntry(freq_itr->first, name, percent, x_offset, y_offset, z_offset, freq_itr->first == in_spawn, false)){
  3161. safe_delete(spawns);
  3162. return 0;
  3163. }
  3164. }
  3165. for(itr=spawns->begin();itr!=spawns->end();itr++){
  3166. spawn = *itr;
  3167. zone->RemoveSpawn(spawn, true, true, true, true, true);
  3168. }
  3169. safe_delete(spawns);
  3170. }
  3171. else{
  3172. safe_delete(spawns);
  3173. return 0;
  3174. }
  3175. return spawnLocationID;
  3176. }
  3177. 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){
  3178. Query query;
  3179. Query query2;
  3180. int32 count = 0;
  3181. if(create_spawnlocation){
  3182. count = GetSpawnLocationCount(spawn->GetSpawnLocationID());
  3183. if(count == 0){
  3184. if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name))
  3185. return false;
  3186. }
  3187. }
  3188. query.RunQuery2(Q_INSERT, "insert into spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent);
  3189. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3190. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query.GetQuery(), query.GetError());
  3191. return false;
  3192. }
  3193. if(save_zonespawn){
  3194. 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);
  3195. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3196. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query2.GetQuery(), query2.GetError());
  3197. return false;
  3198. }
  3199. spawn->SetSpawnLocationPlacementID(query2.GetLastInsertedID());
  3200. }
  3201. return true;
  3202. }
  3203. float WorldDatabase::GetSpawnLocationPlacementOffsetX(int32 location_id) {
  3204. Query query;
  3205. MYSQL_ROW row;
  3206. float ret = 0;
  3207. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `x_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3208. if (result && (row = mysql_fetch_row(result))) {
  3209. if (row[0])
  3210. ret = atof(row[0]);
  3211. }
  3212. return ret;
  3213. }
  3214. float WorldDatabase::GetSpawnLocationPlacementOffsetY(int32 location_id) {
  3215. Query query;
  3216. MYSQL_ROW row;
  3217. float ret = 0;
  3218. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `y_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3219. if (result && (row = mysql_fetch_row(result))) {
  3220. if (row[0])
  3221. ret = atof(row[0]);
  3222. }
  3223. return ret;
  3224. }
  3225. float WorldDatabase::GetSpawnLocationPlacementOffsetZ(int32 location_id) {
  3226. Query query;
  3227. MYSQL_ROW row;
  3228. float ret = 0;
  3229. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `z_offset` FROM `spawn_location_placement` WHERE `spawn_location_id`=%u", location_id);
  3230. if (result && (row = mysql_fetch_row(result))) {
  3231. if (row[0])
  3232. ret = atof(row[0]);
  3233. }
  3234. return ret;
  3235. }
  3236. bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name){
  3237. Query query;
  3238. if(!name)
  3239. name = "Unknown Spawn Location Name";
  3240. string str_name = getSafeEscapeString(name);
  3241. query.RunQuery2(Q_INSERT, "insert into spawn_location_name (id, name) values(%u, '%s')", id, str_name.c_str());
  3242. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3243. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in CreateNewSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
  3244. return false;
  3245. }
  3246. return true;
  3247. }
  3248. int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){
  3249. Query query;
  3250. int32 ret = 0;
  3251. MYSQL_RES* result = 0;
  3252. if(spawn)
  3253. result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u and spawn_id=%u", location, spawn->GetDatabaseID());
  3254. else
  3255. result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u", location);
  3256. if(result && mysql_num_rows(result) > 0){
  3257. MYSQL_ROW row;
  3258. while(result && (row = mysql_fetch_row(result)) && row[0]){
  3259. ret = strtoul(row[0], NULL, 0);
  3260. }
  3261. }
  3262. return ret;
  3263. }
  3264. int32 WorldDatabase::GetNextSpawnLocation(){
  3265. Query query;
  3266. int32 ret = 0;
  3267. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name");
  3268. if(result && mysql_num_rows(result) > 0){
  3269. MYSQL_ROW row;
  3270. while(result && (row = mysql_fetch_row(result)) && row[0]){
  3271. ret = strtoul(row[0], NULL, 0);
  3272. }
  3273. }
  3274. ret++;
  3275. return ret;
  3276. }
  3277. bool WorldDatabase::RemoveSpawnFromSpawnLocation(Spawn* spawn){
  3278. Query query;
  3279. Query query2;
  3280. int32 count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn);
  3281. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement where spawn_location_id=%u", spawn->GetSpawnLocationID());
  3282. if(count == 1)
  3283. query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name where id=%u", spawn->GetSpawnLocationID());
  3284. query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry where spawn_id=%u and spawn_location_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID());
  3285. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  3286. LogWrite(WORLD__ERROR, 0, "World", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
  3287. return false;
  3288. }
  3289. else if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3290. LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query2.GetQuery(), query.GetError());
  3291. return false;
  3292. }
  3293. return true;
  3294. }
  3295. map<int32, string>* WorldDatabase::GetZoneList(const char* name, bool is_admin)
  3296. {
  3297. Query query;
  3298. map<int32, string>* ret = 0;
  3299. string zone_name = getSafeEscapeString(name);
  3300. 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");
  3301. if(result && mysql_num_rows(result) > 0)
  3302. {
  3303. ret = new map<int32, string>;
  3304. MYSQL_ROW row;
  3305. while(result && (row = mysql_fetch_row(result)))
  3306. {
  3307. zone_name = row[1];
  3308. (*ret)[atoi(row[0])] = zone_name;
  3309. }
  3310. }
  3311. return ret;
  3312. }
  3313. void WorldDatabase::UpdateStartingFactions(int32 char_id, int8 choice){
  3314. Query query;
  3315. 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);
  3316. }
  3317. string WorldDatabase::GetStartingZoneName(int8 choice){
  3318. Query query;
  3319. string zone_name = "";
  3320. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where start_zone = %u", choice);
  3321. if(result && mysql_num_rows(result) > 0){
  3322. MYSQL_ROW row;
  3323. while(result && (row = mysql_fetch_row(result))){
  3324. zone_name = string(row[0]);
  3325. }
  3326. }
  3327. return zone_name;
  3328. }
  3329. void WorldDatabase::UpdateStartingZone(int32 char_id, int8 class_id, int8 race_id, PacketStruct* create)
  3330. {
  3331. Query query,query2;
  3332. int32 packetVersion = create->GetVersion();
  3333. int8 choice = create->getType_int8_ByName("starting_zone"); // 0 = far journey, 1 = isle of refuge
  3334. int8 deity = create->getType_int8_ByName("deity"); // aka 'alignment' for early DOF, 0 = evil, 1 = good
  3335. int32 startingZoneRuleFlag = rule_manager.GetGlobalRule(R_World, StartingZoneRuleFlag)->GetInt32();
  3336. bool enforceRacialAlignment = rule_manager.GetGlobalRule(R_World, EnforceRacialAlignment)->GetBool();
  3337. if((startingZoneRuleFlag == 1 || startingZoneRuleFlag == 2) && packetVersion > 546)
  3338. {
  3339. LogWrite(PLAYER__INFO, 0, "Player", "Starting zone rule flag %u override choice %u to deity value of 0", startingZoneRuleFlag, choice);
  3340. choice = 0;
  3341. }
  3342. 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);
  3343. // first, check to see if there is a starting_zones record for this race/class/choice combo (now using extended Archetype/BaseClass/Class combos
  3344. MYSQL_RES* result = 0;
  3345. string whereRuleFlag("");
  3346. if(startingZoneRuleFlag > 0)
  3347. whereRuleFlag = string(" AND ruleflag & " + std::to_string(startingZoneRuleFlag));
  3348. 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, sz.start_alignment FROM");
  3349. if ( class_id == 0 )
  3350. 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",
  3351. syntaxSelect.c_str(), race_id, deity, choice, packetVersion, packetVersion, whereRuleFlag.c_str());
  3352. else
  3353. 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",
  3354. syntaxSelect.c_str(), classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id, deity, choice, packetVersion, packetVersion, whereRuleFlag.c_str());
  3355. if(result && mysql_num_rows(result) > 0)
  3356. {
  3357. string zone_name = "ERROR";
  3358. MYSQL_ROW row;
  3359. bool zoneSet = false;
  3360. float safeX = 0.0f, safeY = 0.0f, safeZ = 0.0f, x = 0.0f, y = 0.0f, z = 0.0f, heading = 0.0f;
  3361. int8 is_instance = 0;
  3362. int32 zone_id = 0;
  3363. int32 instance_id = 0;
  3364. int8 starting_city = 0;
  3365. sint8 start_alignment = 0;
  3366. if( result && (row = mysql_fetch_row(result)) )
  3367. {
  3368. int8 i=0;
  3369. zoneSet = true;
  3370. zone_name = string(row[i++]);
  3371. zone_id = atoul(row[i++]);
  3372. safeX = atof(row[i++]);
  3373. safeY = atof(row[i++]);
  3374. safeZ = atof(row[i++]);
  3375. x = atof(row[i++]);
  3376. y = atof(row[i++]);
  3377. z = atof(row[i++]);
  3378. if ( x == -999999.0f && y == -999999.0f && z == -999999.0f)
  3379. {
  3380. x = safeX;
  3381. y = safeY;
  3382. z = safeZ;
  3383. }
  3384. heading = atof(row[i++]);
  3385. if(heading == -999999.0f )
  3386. heading = 0.0f;
  3387. is_instance = atoul(row[i++]);
  3388. starting_city = atoul(row[i++]);
  3389. start_alignment = atoi(row[i++]);
  3390. }
  3391. // all EQ2 clients hard-code these restrictions in some form (later clients added Neutral instead of good/evil only)
  3392. // start with good races only
  3393. if(enforceRacialAlignment && (race_id == DWARF || race_id == FROGLOK || race_id == HALFLING ||
  3394. race_id == HIGH_ELF || race_id == WOOD_ELF || race_id == FAE)) {
  3395. start_alignment = ALIGNMENT_GOOD; // always should good
  3396. }
  3397. // we drop into this case because it is a special check compared to the straigt good/evil checks on top and below
  3398. else if(start_alignment == ALIGNMENT_EVIL && deity == ALIGNMENT_GOOD) {
  3399. if (enforceRacialAlignment && (race_id == AERAKYN || race_id == RATONGA || race_id == BARBARIAN || race_id == ERUDITE ||
  3400. race_id == HUMAN || race_id == VAMPIRE || race_id == HALF_ELF || race_id == GNOME || race_id == KERRA)) {
  3401. if(zone_id == 21 || zone_id == 25 || zone_id == 26 || zone_id == 27) { // far journey zones
  3402. start_alignment = deity; // inheriting the clients alignment of 'good' due to the fact we start in far journey and it is a shared instance right now (farjourneyfreeport)
  3403. }
  3404. // otherwise we use the starting zone alignment since these particular races can be evil which will be set in start_alignment of 0 within starting_zones (neriak, freeport, etc.)
  3405. }
  3406. else if(enforceRacialAlignment) {
  3407. LogWrite(WORLD__WARNING, 0, "World", "Starting alignment seems unexpected, zone id %u, race id %u, deity(alignment) choice: %u, start alignment: %i", zone_id, race_id, deity, start_alignment);
  3408. }
  3409. }
  3410. // anyone else is simply evil with the enforcement check
  3411. else if(enforceRacialAlignment && (race_id != DWARF && race_id != FROGLOK && race_id != HALFLING &&
  3412. race_id != HIGH_ELF && race_id != WOOD_ELF && race_id != FAE)) {
  3413. start_alignment = ALIGNMENT_EVIL;
  3414. }
  3415. if(is_instance) // should only be true if we get a result
  3416. {
  3417. // this will force a pre-load
  3418. ZoneServer* instance_zone = zone_list.GetByInstanceID(0, zone_id);
  3419. if (instance_zone) {
  3420. instance_id = CreateNewInstance(zone_id);
  3421. AddCharacterInstance(char_id, instance_id, string(instance_zone->GetZoneName()), instance_zone->GetInstanceType(), Timer::GetUnixTimeStamp(), 0, instance_zone->GetDefaultLockoutTime(), instance_zone->GetDefaultReenterTime());
  3422. // make sure we inherit the instance id setup in the AddCharacterInstance
  3423. instance_zone->SetupInstance(instance_id);
  3424. }
  3425. }
  3426. 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, alignment = %u WHERE id = %u",
  3427. zone_id, x, y, z, heading, starting_city, instance_id, start_alignment, char_id);
  3428. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
  3429. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone custom starting_zones, query: '%s': %s", query2.GetQuery(), query2.GetError());
  3430. return;
  3431. }
  3432. if(query2.GetAffectedRows() > 0)
  3433. {
  3434. 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);
  3435. return;
  3436. }
  3437. }
  3438. else
  3439. {
  3440. // there was no matching starting_zone value, so use default 'choice' starting city
  3441. 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",
  3442. choice, choice, char_id);
  3443. if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF)
  3444. {
  3445. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone player choice, query: '%s': %s", query2.GetQuery(), query2.GetError());
  3446. return;
  3447. }
  3448. if(query2.GetAffectedRows() > 0)
  3449. {
  3450. LogWrite(PLAYER__DEBUG, 0, "Player", "Setting New Character Starting Zone to '%s' FROM player choice.", GetStartingZoneName(choice).c_str());
  3451. return;
  3452. }
  3453. }
  3454. // 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.
  3455. // shout a few warnings so the admin fixes this asap!
  3456. int16 default_zone_id = rule_manager.GetGlobalRule(R_World, DefaultStartingZoneID)->GetInt16();
  3457. 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);
  3458. 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);
  3459. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3460. {
  3461. LogWrite(PLAYER__ERROR, 0, "Player", "Error in UpdateStartingZone default zone %i, query: '%s': %s", default_zone_id, query.GetQuery(), query.GetError());
  3462. return;
  3463. }
  3464. if (query.GetAffectedRows() > 0) {
  3465. string zone_name = GetZoneName(1);
  3466. if(zone_name.length() > 0)
  3467. LogWrite(PLAYER__DEBUG, 0, "Player", "Setting New Character Starting Zone to '%s' due to no valid options!", zone_name.c_str());
  3468. else
  3469. LogWrite(PLAYER__DEBUG, 0, "Player", "Unable to set New Character Starting Zone due to no valid options!");
  3470. }
  3471. return;
  3472. }
  3473. void WorldDatabase::UpdateStartingItems(int32 char_id, int8 class_id, int8 race_id, bool base_class){
  3474. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default items for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3475. Query query;
  3476. Query query2;
  3477. vector<Item*> items;
  3478. vector<Item*> bags;
  3479. map<int32, int8> total_slots;
  3480. map<int32, int8> slots_left;
  3481. map<int8, bool> equip_slots;
  3482. map<Item*, StartingItem> item_list;
  3483. int32 item_id = 0;
  3484. Item* item = 0;
  3485. StartingItem* starting_item = 0;
  3486. //first get a list of the starting items for the character
  3487. MYSQL_RES* result = 0;
  3488. /*if(!base_class)
  3489. 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);
  3490. else
  3491. 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);*/
  3492. 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);
  3493. if(result && mysql_num_rows(result) > 0){
  3494. MYSQL_ROW row;
  3495. while(result && (row = mysql_fetch_row(result))){
  3496. item_id = atoul(row[1]);
  3497. item = master_item_list.GetItem(item_id);
  3498. if(item){
  3499. starting_item = &(item_list[item]);
  3500. starting_item->type = (row[0]) ? string(row[0]) : "";
  3501. starting_item->item_id = atoul(row[1]);
  3502. starting_item->creator = (row[2]) ? string(row[2]) : "";
  3503. starting_item->condition = atoi(row[3]);
  3504. starting_item->attuned = atoi(row[4]);
  3505. starting_item->count = atoi(row[5]);
  3506. item = master_item_list.GetItem(starting_item->item_id);
  3507. if(item){
  3508. if(bags.size() < NUM_INV_SLOTS && item->IsBag() && item->details.num_slots > 0)
  3509. bags.push_back(item);
  3510. else
  3511. items.push_back(item);
  3512. }
  3513. }
  3514. }
  3515. }
  3516. slots_left[0] = NUM_INV_SLOTS;
  3517. //next create the bags in the inventory
  3518. for(int8 i=0;i<bags.size();i++){
  3519. item = bags[i];
  3520. 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)",
  3521. 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);
  3522. slots_left[query.GetLastInsertedID()] = item->details.num_slots;
  3523. total_slots[query.GetLastInsertedID()] = item->details.num_slots;
  3524. slots_left[0]--;
  3525. }
  3526. map<int32, int8>::iterator itr;
  3527. int32 inv_slot = 0;
  3528. int8 slot = 0;
  3529. //finally process the rest of the items, placing them in the first available slot
  3530. for(int32 x=0;x<items.size();x++){
  3531. item = items[x];
  3532. if(item_list[item].type.find("NOT") < 0xFFFFFFFF){ // NOT-EQUIPPED Items
  3533. for(itr = slots_left.begin(); itr != slots_left.end(); itr++){
  3534. if(itr->second > 0){
  3535. if(itr->first == 0 && slots_left.size() > 1) //we want items to go into bags first, then inventory after bags are full
  3536. continue;
  3537. inv_slot = itr->first;
  3538. slot = total_slots[itr->first] - itr->second;
  3539. itr->second--;
  3540. if(itr->second == 0)
  3541. slots_left.erase(itr);
  3542. break;
  3543. }
  3544. }
  3545. 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)",
  3546. 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);
  3547. }
  3548. else{ //EQUIPPED Items
  3549. for(int8 i=0;i<item->slot_data.size();i++){
  3550. if(equip_slots.count(item->slot_data[i]) == 0){
  3551. equip_slots[item->slot_data[i]] = true;
  3552. 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)",
  3553. 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);
  3554. break;
  3555. }
  3556. }
  3557. }
  3558. }
  3559. }
  3560. void WorldDatabase::UpdateStartingSkills(int32 char_id, int8 class_id, int8 race_id)
  3561. {
  3562. Query query;
  3563. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default skills for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3564. 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)",
  3565. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3566. }
  3567. void WorldDatabase::UpdateStartingSpells(int32 char_id, int8 class_id, int8 race_id){
  3568. Query query;
  3569. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default spells for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3570. 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)",
  3571. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3572. }
  3573. void WorldDatabase::UpdateStartingSkillbar(int32 char_id, int8 class_id, int8 race_id){
  3574. Query query;
  3575. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding default skillbar for race: %i, class: %i for char_id: %u", race_id, class_id, char_id);
  3576. 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)",
  3577. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id);
  3578. }
  3579. void WorldDatabase::UpdateStartingTitles(int32 char_id, int8 class_id, int8 race_id, int8 gender_id) {
  3580. Query query;
  3581. 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);
  3582. 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)",
  3583. char_id, classes.GetBaseClass(class_id), classes.GetSecondaryBaseClass(class_id), class_id, race_id, gender_id);
  3584. }
  3585. string WorldDatabase::GetZoneDescription(int32 id){
  3586. Query query;
  3587. string ret = "";
  3588. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT description FROM zones where id = %u", id);
  3589. if(result && mysql_num_rows(result) > 0){
  3590. MYSQL_ROW row;
  3591. row = mysql_fetch_row(result);
  3592. ret = string(row[0]);
  3593. }
  3594. return ret;
  3595. }
  3596. string WorldDatabase::GetZoneName(int32 id){
  3597. if (zone_names.count(id) > 0){
  3598. return zone_names[id];
  3599. }
  3600. Query query;
  3601. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `name` FROM zones where `id` = %u", id);
  3602. if(result && mysql_num_rows(result) > 0){
  3603. MYSQL_ROW row;
  3604. row = mysql_fetch_row(result);
  3605. zone_names[id] = row[0];
  3606. return zone_names[id];
  3607. }
  3608. return string("");
  3609. }
  3610. bool WorldDatabase::VerifyZone(const char* name){
  3611. Query query;
  3612. char* escaped = getEscapeString(name);
  3613. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name FROM zones where name='%s'",escaped);
  3614. safe_delete_array(escaped);
  3615. if(result && mysql_num_rows(result) > 0)
  3616. return true;
  3617. else
  3618. return false;
  3619. }
  3620. int8 WorldDatabase::GetInstanceTypeByZoneID(int32 zoneID)
  3621. {
  3622. std::map<int32, int8>::iterator itr = zone_instance_types.find(zoneID);
  3623. if(itr != zone_instance_types.end())
  3624. return itr->second;
  3625. DatabaseResult result;
  3626. int8 ret = 0;
  3627. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Getting instances for zone_id %u", zoneID);
  3628. if( !database_new.Select(&result, "SELECT instance_type+0 FROM zones WHERE id = %u", zoneID) )
  3629. {
  3630. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceTypeByZoneID() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  3631. return ret;
  3632. }
  3633. if( result.GetNumRows() > 0 )
  3634. {
  3635. result.Next();
  3636. ret = (result.GetInt8Str("instance_type+0") == 0) ? 0 : result.GetInt8Str("instance_type+0") - 1;
  3637. zone_instance_types.insert(make_pair(zoneID, ret));
  3638. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Found instance type %i for zone_id %u", ret, zoneID);
  3639. }
  3640. else
  3641. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No instances found for zone_id %u", zoneID);
  3642. return ret;
  3643. }
  3644. void WorldDatabase::Save(Client* client){
  3645. Query query;
  3646. Player* player = client->GetPlayer();
  3647. if(!player->CheckPlayerInfo())
  3648. return;
  3649. int32 instance_id = 0;
  3650. if ( client->GetCurrentZone ( ) != NULL )
  3651. instance_id = client->GetCurrentZone()->GetInstanceID();
  3652. int32 zone_id = 0;
  3653. if(client->GetCurrentZone())
  3654. zone_id = client->GetCurrentZone()->GetZoneID();
  3655. 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, deity = %u, alignment = %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 : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetCharacterID());
  3656. 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",
  3657. player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),
  3658. 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(),
  3659. 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(),
  3660. client->GetPlayer()->GetCombatVoice(), client->GetPlayer()->GetEmoteVoice(), getSafeEscapeString(client->GetPlayer()->GetBiography().c_str()).c_str(), player->GetFlags(), player->GetFlags2(), client->GetPlayer()->GetLastName(),
  3661. client->GetPlayer()->GetAssignedAA(), client->GetPlayer()->GetUnassignedAA(), client->GetPlayer()->GetTradeskillAA(), client->GetPlayer()->GetUnassignedTradeskillAA(), client->GetPlayer()->GetPrestigeAA(),
  3662. client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetCharacterID());
  3663. map<string, int8>::iterator itr;
  3664. map<string, int8>* friends = player->GetFriends();
  3665. if(friends && friends->size() > 0){
  3666. for(itr = friends->begin(); itr != friends->end(); itr++){
  3667. if(itr->second == 1){
  3668. 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());
  3669. itr->second = 0;
  3670. }
  3671. else if(itr->second == 2){
  3672. 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());
  3673. itr->second = 3;
  3674. }
  3675. }
  3676. }
  3677. map<string, int8>* ignored = player->GetIgnoredPlayers();
  3678. if(ignored && ignored->size() > 0){
  3679. for(itr = ignored->begin(); itr != ignored->end(); itr++){
  3680. if(itr->second == 1){
  3681. 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());
  3682. itr->second = 0;
  3683. }
  3684. else if(itr->second == 2){
  3685. 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());
  3686. itr->second = 3;
  3687. }
  3688. }
  3689. }
  3690. SavePlayerFactions(client);
  3691. SaveCharacterQuests(client);
  3692. SaveCharacterSkills(client);
  3693. SavePlayerSpells(client);
  3694. SavePlayerMail(client);
  3695. SavePlayerCollections(client);
  3696. client->SaveQuestRewardData();
  3697. LogWrite(PLAYER__INFO, 3, "Player", "Player '%s' (%u) data saved.", player->GetName(), player->GetCharacterID());
  3698. }
  3699. void WorldDatabase::LoadEntityCommands(ZoneServer* zone) {
  3700. int32 total = 0;
  3701. int32 id = 0;
  3702. EntityCommand* command = 0;
  3703. DatabaseResult result;
  3704. 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`")) {
  3705. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  3706. return;
  3707. }
  3708. while (result.Next()) {
  3709. command = new EntityCommand;
  3710. id = result.GetInt32(0);
  3711. command->name = result.GetString(1);
  3712. command->distance = result.GetFloat(2);
  3713. command->command = result.GetString(3);
  3714. command->error_text = result.GetString(4);
  3715. command->cast_time = result.GetInt16(5);
  3716. command->spell_visual = result.GetInt32(6);
  3717. command->default_allow_list = true;
  3718. zone->SetEntityCommandList(id, command);
  3719. LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Command: '%s' (%s)", command->name.c_str(), command->command.c_str());
  3720. total++;
  3721. }
  3722. LogWrite(COMMAND__DEBUG, 0, "Command", "--Loaded %i entity command(s)", total);
  3723. }
  3724. void WorldDatabase::LoadFactionAlliances()
  3725. {
  3726. LogWrite(FACTION__DEBUG, 1, "Faction", "-Loading faction alliances...");
  3727. int32 total = 0;
  3728. int32 fTotal = 0;
  3729. int32 hTotal = 0;
  3730. Query query;
  3731. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT faction_id, friend_faction, hostile_faction FROM faction_alliances");
  3732. if(result && mysql_num_rows(result) > 0)
  3733. {
  3734. MYSQL_ROW row;
  3735. int32 faction_id = 0;
  3736. int32 friendly_id = 0;
  3737. int32 hostile_id = 0;
  3738. while(result && (row = mysql_fetch_row(result)))
  3739. {
  3740. faction_id = atoul(row[0]);
  3741. friendly_id = atoul(row[1]);
  3742. hostile_id = atoul(row[2]);
  3743. if(friendly_id > 0)
  3744. {
  3745. master_faction_list.AddFriendlyFaction(faction_id, friendly_id);
  3746. fTotal++;
  3747. LogWrite(FACTION__DEBUG, 5, "Faction", "---Faction %i is friendly towards %i", faction_id, friendly_id);
  3748. }
  3749. if(hostile_id > 0)
  3750. {
  3751. master_faction_list.AddHostileFaction(faction_id, hostile_id);
  3752. hTotal++;
  3753. LogWrite(FACTION__DEBUG, 5, "Faction", "---Faction %i is hostile towards %i", faction_id, hostile_id);
  3754. }
  3755. total++;
  3756. }
  3757. }
  3758. LogWrite(FACTION__DEBUG, 3, "Faction", "--Loaded %u Alliances: %i friendly, %i hostile", total, fTotal, hTotal);
  3759. }
  3760. bool WorldDatabase::UpdateSpawnScriptData(int32 spawn_id, int32 spawn_location_id, int32 spawnentry_id, const char* name){
  3761. bool ret = false;
  3762. if((spawn_id > 0 || spawn_location_id > 0 || spawnentry_id > 0) && name){
  3763. Query query;
  3764. int32 row_id = 0;
  3765. if(spawn_id > 0){
  3766. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawn_id=%u", spawn_id);
  3767. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawn_id, lua_script) values(%u, '%s')", spawn_id, getSafeEscapeString(name).c_str());
  3768. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3769. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3770. else{
  3771. row_id = query.GetLastInsertedID();
  3772. if(row_id > 0)
  3773. world.AddSpawnScript(row_id, name);
  3774. ret = true;
  3775. }
  3776. }
  3777. else if(spawn_location_id > 0){
  3778. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawn_location_id=%u", spawn_location_id);
  3779. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawn_location_id, lua_script) values(%u, '%s')", spawn_location_id, getSafeEscapeString(name).c_str());
  3780. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3781. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3782. else{
  3783. row_id = query.GetLastInsertedID();
  3784. if(row_id > 0)
  3785. world.AddSpawnLocationScript(row_id, name);
  3786. ret = true;
  3787. }
  3788. }
  3789. else if(spawnentry_id > 0){
  3790. query.RunQuery2(Q_DELETE, "DELETE FROM spawn_scripts where spawnentry_id=%u", spawnentry_id);
  3791. query.RunQuery2(Q_INSERT, "INSERT into spawn_scripts (spawnentry_id, lua_script) values(%u, '%s')", spawnentry_id, getSafeEscapeString(name).c_str());
  3792. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF)
  3793. LogWrite(LUA__ERROR, 0, "LUA", "Error in UpdateSpawnScriptData, Query: %s, Error: %s", query.GetQuery(), query.GetError());
  3794. else{
  3795. row_id = query.GetLastInsertedID();
  3796. if(row_id > 0)
  3797. world.AddSpawnEntryScript(row_id, name);
  3798. ret = true;
  3799. }
  3800. }
  3801. }
  3802. return ret;
  3803. }
  3804. void WorldDatabase::LoadSpawnScriptData() {
  3805. int32 total = 0;
  3806. Query query;
  3807. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spawn_id, spawnentry_id, spawn_location_id, lua_script FROM spawn_scripts");
  3808. if(result && mysql_num_rows(result) > 0)
  3809. {
  3810. MYSQL_ROW row;
  3811. int32 spawn_id = 0;
  3812. int32 spawnentry_id = 0;
  3813. int32 spawn_location_id = 0;
  3814. while(result && (row = mysql_fetch_row(result)))
  3815. {
  3816. spawn_id = atoul(row[0]);
  3817. spawnentry_id = atoul(row[1]);
  3818. spawn_location_id = atoul(row[2]);
  3819. string spawn_script = string(row[3]);
  3820. if(spawnentry_id > 0)
  3821. {
  3822. world.AddSpawnEntryScript(spawnentry_id, row[3]);
  3823. total++;
  3824. }
  3825. else if(spawn_location_id > 0)
  3826. {
  3827. world.AddSpawnLocationScript(spawn_location_id, row[3]);
  3828. total++;
  3829. }
  3830. else if(spawn_id > 0)
  3831. {
  3832. world.AddSpawnScript(spawn_id, row[3]);
  3833. total++;
  3834. }
  3835. else {
  3836. if(row[3])
  3837. 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]);
  3838. else
  3839. LogWrite(LUA__ERROR, 0, "LUA", "Invalid Entry in spawn_scripts table.");
  3840. }
  3841. spawn_id = 0;
  3842. spawnentry_id = 0;
  3843. spawn_location_id = 0;
  3844. }
  3845. }
  3846. LogWrite(LUA__DEBUG, 0, "LUA", "\tLoaded %u SpawnScript%s", total, total == 1 ? "" : "s");
  3847. }
  3848. void WorldDatabase::LoadZoneScriptData() {
  3849. Query query;
  3850. int32 total = 0;
  3851. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `lua_script` FROM `zones`");
  3852. if (result && mysql_num_rows(result) > 0) {
  3853. MYSQL_ROW row;
  3854. while(result && (row = mysql_fetch_row(result))) {
  3855. if (row[1]) {
  3856. int32 zone_id = atoul(row[0]);
  3857. string zone_script = string(row[1]);
  3858. if (zone_id > 0 && zone_script.length() > 0) {
  3859. LogWrite(LUA__DEBUG, 5, "LUA", "ZoneScript: %s loaded.", zone_script.c_str());
  3860. world.AddZoneScript(zone_id, zone_script.c_str());
  3861. total++;
  3862. }
  3863. else if (zone_id > 0) {
  3864. string tmpScript;
  3865. tmpScript.append("ZoneScripts/");
  3866. string zonename = GetZoneName(zone_id);
  3867. tmpScript.append(zonename);
  3868. tmpScript.append(".lua");
  3869. struct stat buffer;
  3870. bool fileExists = (stat(tmpScript.c_str(), &buffer) == 0);
  3871. if (fileExists)
  3872. {
  3873. 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);
  3874. world.AddZoneScript(zone_id, tmpScript.c_str());
  3875. }
  3876. }
  3877. }
  3878. }
  3879. }
  3880. LogWrite(LUA__DEBUG, 0, "LUA", "\tLoaded %u ZoneScript%s", total, total == 1 ? "" : "s");
  3881. }
  3882. int32 WorldDatabase::LoadSpellScriptData() {
  3883. Query query;
  3884. MYSQL_ROW row;
  3885. int32 count;
  3886. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `lua_script` FROM `spells`"); // WHERE is_active = 1
  3887. while (result && (row = mysql_fetch_row(result))) {
  3888. if (row[0] && strlen(row[0]) > 0) {
  3889. if (lua_interface->LoadLuaSpell(row[0]))
  3890. LogWrite(SPELL__DEBUG, 5, "Spells", "SpellScript: %s loaded.", row[0]);
  3891. }
  3892. }
  3893. count = mysql_num_rows(result);
  3894. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %i SpellScript%s", count, count == 1 ? "" : "s");
  3895. return count;
  3896. }
  3897. void WorldDatabase::LoadFactionList() {
  3898. int32 total = 0;
  3899. Query query;
  3900. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, type, description, default_level, negative_change, positive_change FROM factions");
  3901. if(result && mysql_num_rows(result) > 0) {
  3902. MYSQL_ROW row;
  3903. Faction* faction = 0;
  3904. while(result && (row = mysql_fetch_row(result))){
  3905. faction = new Faction;
  3906. faction->id = atoul(row[0]);
  3907. faction->name = string(row[1]);
  3908. faction->type = string(row[2]);
  3909. faction->description = string(row[3]);
  3910. faction->default_value = atoi(row[4]);
  3911. faction->negative_change = atoi(row[5]);
  3912. faction->positive_change = atoi(row[6]);
  3913. master_faction_list.AddFaction(faction);
  3914. total++;
  3915. LogWrite(FACTION__DEBUG, 5, "Faction", "---Loading Faction '%s' (%u)", faction->name.c_str(), faction->id);
  3916. }
  3917. }
  3918. LogWrite(FACTION__DEBUG, 3, "Faction", "--Loaded %u Faction%s", total, total == 1 ? "" : "s");
  3919. LoadFactionAlliances();
  3920. }
  3921. void WorldDatabase::SavePlayerFactions(Client* client){
  3922. LogWrite(PLAYER__DEBUG, 3, "Player", "Saving Player Factions...");
  3923. Query query;
  3924. map<int32, sint32>* factions = client->GetPlayer()->GetFactions()->GetFactionValues();
  3925. map<int32, sint32>::iterator itr;
  3926. for(itr = factions->begin(); itr != factions->end(); itr++)
  3927. 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);
  3928. }
  3929. bool WorldDatabase::LoadPlayerFactions(Client* client) {
  3930. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Player Factions...");
  3931. Query query;
  3932. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT faction_id, faction_level FROM character_factions where char_id=%i", client->GetCharacterID());
  3933. if(result && mysql_num_rows(result) > 0) {
  3934. MYSQL_ROW row;
  3935. while(result && (row = mysql_fetch_row(result))){
  3936. client->GetPlayer()->GetFactions()->SetFactionValue(atoul(row[0]), atol(row[1]));
  3937. }
  3938. }
  3939. if(query.GetErrorNumber())
  3940. return false;
  3941. return true;
  3942. }
  3943. void WorldDatabase::SavePlayerMail(Mail* mail) {
  3944. Query query_update;
  3945. Query query_insert;
  3946. if (mail) {
  3947. if(mail->mail_id > 0)
  3948. 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);
  3949. else if (mail->mail_id == 0 || query_update.GetAffectedRows() == 0)
  3950. {
  3951. 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);
  3952. if(!mail->mail_id)
  3953. mail->mail_id = query_insert.GetLastInsertedID();
  3954. }
  3955. mail->save_needed = false;
  3956. }
  3957. }
  3958. void WorldDatabase::SavePlayerMail(Client* client) {
  3959. if (client) {
  3960. MutexMap<int32, Mail*>* mail_list = client->GetPlayer()->GetMail();
  3961. MutexMap<int32, Mail*>::iterator itr = mail_list->begin();
  3962. while (itr.Next()) {
  3963. Mail* mail = itr->second;
  3964. if (mail->save_needed)
  3965. SavePlayerMail(mail);
  3966. }
  3967. }
  3968. }
  3969. void WorldDatabase::LoadPlayerMail(Client* client, bool new_only) {
  3970. LogWrite(PLAYER__DEBUG, 0, "Player", "Loading Player Mail...");
  3971. if (client) {
  3972. Query query;
  3973. MYSQL_RES* result;
  3974. if (new_only)
  3975. 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());
  3976. else
  3977. 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());
  3978. if (result && mysql_num_rows(result) > 0) {
  3979. MYSQL_ROW row;
  3980. bool hasMail = false;
  3981. while (result && (row = mysql_fetch_row(result))) {
  3982. int32 time_sent = atoul(row[15]);
  3983. if ( time_sent > Timer::GetUnixTimeStamp() )
  3984. continue; // should have not been received yet
  3985. hasMail = true;
  3986. Mail* mail = new Mail;
  3987. mail->mail_id = atoul(row[0]);
  3988. mail->player_to_id = atoul(row[1]);
  3989. mail->player_from = string(row[2]);
  3990. mail->subject = string(row[3]);
  3991. if (row[4])
  3992. mail->mail_body = string(row[4]);
  3993. mail->already_read = atoi(row[5]);
  3994. mail->mail_type = atoi(row[6]);
  3995. mail->coin_copper = atoul(row[7]);
  3996. mail->coin_silver = atoul(row[8]);
  3997. mail->coin_gold = atoul(row[9]);
  3998. mail->coin_plat = atoul(row[10]);
  3999. mail->stack = atoi(row[11]);
  4000. mail->postage_cost = atoul(row[12]);
  4001. mail->attachment_cost = atoul(row[13]);
  4002. mail->char_item_id = atoul(row[14]);
  4003. mail->time_sent = time_sent;
  4004. mail->expire_time = atoul(row[16]);
  4005. mail->save_needed = false;
  4006. client->GetPlayer()->AddMail(mail);
  4007. LogWrite(PLAYER__DEBUG, 5, "Player", "Loaded Mail ID %i, to: %i, from: %s", atoul(row[0]), atoul(row[1]), string(row[2]).c_str());
  4008. }
  4009. if(hasMail)
  4010. client->SimpleMessage(CHANNEL_NARRATIVE, "You've got mail! :)");
  4011. }
  4012. }
  4013. }
  4014. void WorldDatabase::DeletePlayerMail(Mail* mail) {
  4015. Query query;
  4016. if (mail)
  4017. query.RunQuery2(Q_DELETE, "DELETE FROM `character_mail` WHERE `id`=%u", mail->mail_id);
  4018. LogWrite(PLAYER__DEBUG, 0, "Player", "Delete Player Mail...");
  4019. }
  4020. vector<int32>* WorldDatabase::GetAllPlayerIDs() {
  4021. Query query;
  4022. vector<int32>* ids = 0;
  4023. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id` FROM `characters`");
  4024. MYSQL_ROW row;
  4025. while (result && (row = mysql_fetch_row(result))) {
  4026. if (ids == 0)
  4027. ids = new vector<int32>;
  4028. ids->push_back(atoul(row[0]));
  4029. }
  4030. return ids;
  4031. }
  4032. void WorldDatabase::GetPetNames(ZoneServer* zone)
  4033. {
  4034. DatabaseResult result;
  4035. int32 total = 0;
  4036. if( database_new.Select(&result, "SELECT pet_name FROM spawn_pet_names") )
  4037. {
  4038. while(result.Next())
  4039. {
  4040. zone->pet_names.push_back(result.GetStringStr("pet_name"));
  4041. total++;
  4042. LogWrite(PET__DEBUG, 5, "Pet", "---Loading Pet Name: '%s'", result.GetStringStr("pet_name"));
  4043. }
  4044. LogWrite(PET__DEBUG, 0, "Pet", "--Loaded %u Pet Names", total);
  4045. }
  4046. }
  4047. int32 WorldDatabase::GetMaxHotBarID(){
  4048. Query query;
  4049. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM character_skillbar");
  4050. int32 ret = 0;
  4051. if(result && mysql_num_rows(result) > 0) {
  4052. MYSQL_ROW row;
  4053. row = mysql_fetch_row(result);
  4054. if(row && row[0])
  4055. ret = strtoul(row[0], NULL, 0);
  4056. }
  4057. return ret;
  4058. }
  4059. void WorldDatabase::SaveQuickBar(int32 char_id, vector<QuickBarItem*>* quickbar_items){
  4060. vector<QuickBarItem*>::iterator itr;
  4061. QuickBarItem* qbi = 0;
  4062. for(itr = quickbar_items->begin(); itr != quickbar_items->end(); itr++){
  4063. qbi = *itr;
  4064. if(!qbi)
  4065. continue;
  4066. if(qbi->deleted == false){
  4067. Query query;
  4068. if(qbi->text.size > 0){
  4069. 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)",
  4070. qbi->unique_id, qbi->hotbar, qbi->slot, char_id, qbi->id, qbi->type, getSafeEscapeString(qbi->text.data.c_str()).c_str(), qbi->tier);
  4071. }
  4072. else{
  4073. 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)",
  4074. qbi->unique_id, qbi->hotbar, qbi->slot, char_id, qbi->id, qbi->type, qbi->tier);
  4075. }
  4076. }
  4077. else{
  4078. Query query;
  4079. 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);
  4080. }
  4081. }
  4082. }
  4083. map<int32, vector<LevelArray*> >* WorldDatabase::LoadSpellClasses(){
  4084. map<int32, vector<LevelArray*> >* ret = new map<int32, vector<LevelArray*> >();
  4085. Query query;
  4086. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_id, adventure_class_id, tradeskill_class_id, level FROM spell_classes");
  4087. MYSQL_ROW row;
  4088. LevelArray* level = 0;
  4089. while(result && (row = mysql_fetch_row(result))){
  4090. level = new LevelArray();
  4091. level->adventure_class = atoi(row[1]);
  4092. level->tradeskill_class = atoi(row[2]);
  4093. level->spell_level = atoi(row[3]);
  4094. (*ret)[atoul(row[0])].push_back(level);
  4095. }
  4096. return ret;
  4097. }
  4098. void WorldDatabase::LoadTraits(){
  4099. Query query;
  4100. MYSQL_ROW row;
  4101. TraitData* trait;
  4102. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`, `level`, `class_req`, `race_req`, `isTrait`,`isInate`, `isFocusEffect`, `isTraining`,`tier`, `group` FROM spell_traits");
  4103. while (result && (row = mysql_fetch_row(result))){
  4104. trait = new TraitData;
  4105. int8 i = 0;
  4106. trait->spellID = strtoul(row[0], NULL, 0);
  4107. trait->level = atoi(row[(++i)]);
  4108. trait->classReq = atoi(row[(++i)]);
  4109. trait->raceReq = atoi(row[(++i)]);
  4110. trait->isTrait = (atoi(row[(++i)]) == 0) ? false : true;
  4111. trait->isInate = (atoi(row[(++i)]) == 0) ? false : true;
  4112. trait->isFocusEffect = (atoi(row[(++i)]) == 0) ? false : true;
  4113. trait->isTraining = (atoi(row[(++i)]) == 0) ? false : true;
  4114. trait->tier = atoi(row[(++i)]);
  4115. trait->group = atoi(row[(++i)]);
  4116. master_trait_list.AddTrait(trait);
  4117. }
  4118. LogWrite(SPELL__INFO, 0, "Traits", "Loaded %u Trait(s)", master_trait_list.Size());
  4119. }
  4120. void WorldDatabase::LoadSpells()
  4121. {
  4122. DatabaseResult result;
  4123. Spell *spell;
  4124. SpellData *data;
  4125. int32 t_now = Timer::GetUnixTimeStamp();
  4126. int32 total = 0;
  4127. map<int32, vector<LevelArray*> >* level_data = LoadSpellClasses();
  4128. 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 "
  4129. "FROM (spells s, spell_tiers st) "
  4130. "LEFT JOIN spell_ts_ability_index ts "
  4131. "ON s.`id` = ts.spell_id "
  4132. "WHERE s.id = st.spell_id AND s.is_active = 1 "
  4133. "ORDER BY s.`id`, `tier`") )
  4134. {
  4135. // error
  4136. }
  4137. else
  4138. {
  4139. while( result.Next() )
  4140. {
  4141. data = new SpellData;
  4142. int32 spell_id = result.GetInt32Str("id");
  4143. string spell_name = result.GetStringStr("name");
  4144. /* General Spell info */
  4145. data->id = spell_id;
  4146. data->soe_spell_crc = result.GetInt32Str("soe_spell_crc");
  4147. data->tier = result.GetInt8Str("tier");
  4148. data->ts_loc_index = result.GetInt8Str("index");
  4149. data->name.data = spell_name.c_str();
  4150. data->name.size = data->name.data.length();
  4151. data->description.data = result.GetStringStr("description");
  4152. data->description.size = data->description.data.length();
  4153. data->icon = result.GetSInt16Str("icon");
  4154. data->icon_heroic_op = result.GetInt16Str("icon_heroic_op");
  4155. data->icon_backdrop = result.GetInt16Str("icon_backdrop");
  4156. data->spell_visual = result.GetInt32Str("spell_visual");
  4157. data->type = result.GetInt16Str("type");
  4158. data->target_type = result.GetInt8Str("target_type");
  4159. data->cast_type = result.GetInt8Str("cast_type");
  4160. data->spell_book_type = result.GetInt32Str("spell_book_type");
  4161. data->det_type = result.GetInt8Str("det_type");
  4162. data->incurable = (result.GetInt8Str("incurable") == 1);
  4163. data->control_effect_type = result.GetInt8Str("control_effect_type");
  4164. data->casting_flags = result.GetInt32Str("casting_flags");
  4165. data->savage_bar = result.GetInt8Str("savage_bar");
  4166. data->savage_bar_slot = result.GetInt8Str("savage_bar_slot");
  4167. data->spell_type = result.IsNullStr("spell_type+0") ? 0 : result.GetInt8Str("spell_type+0");
  4168. /* Toggles */
  4169. data->interruptable = ( result.GetInt8Str("interruptable") == 1);
  4170. data->duration_until_cancel = ( result.GetInt8Str("duration_until_cancel") == 1);
  4171. data->can_effect_raid = result.GetInt8Str("can_effect_raid");
  4172. data->affect_only_group_members = result.GetInt8Str("affect_only_group_members");
  4173. data->display_spell_tier = result.GetInt8Str("display_spell_tier");
  4174. data->friendly_spell = result.GetInt8Str("friendly_spell");
  4175. data->group_spell = result.GetInt8Str("group_spell");
  4176. data->is_active = result.GetInt8Str("is_active");
  4177. data->persist_through_death = ( result.GetInt8Str("persist_through_death") == 1);
  4178. data->cast_while_moving = ( result.GetInt8Str("cast_while_moving") == 1);
  4179. data->not_maintained = ( result.GetInt8Str("not_maintained") == 1);
  4180. data->is_aa = (result.GetInt8Str("is_aa") == 1);
  4181. /* Skill Requirements */
  4182. data->class_skill = result.GetInt32Str("class_skill");
  4183. data->min_class_skill_req = result.GetInt16Str("min_class_skill_req");
  4184. data->mastery_skill = result.GetInt32Str("mastery_skill");
  4185. /* Cost */
  4186. data->req_concentration = result.GetInt16Str("req_concentration");
  4187. data->hp_req = result.GetInt16Str("hp_req");
  4188. data->hp_upkeep = result.GetInt16Str("hp_upkeep");
  4189. data->hp_req_percent = result.GetInt8Str("hp_req_percent");
  4190. data->power_req = result.GetFloatStr("power_req");
  4191. data->power_by_level = ( result.GetInt8Str("power_by_level") == 0)? false : true;
  4192. data->power_upkeep = result.GetInt16Str("power_upkeep");
  4193. data->power_req_percent = result.GetInt8Str("power_req_percent");
  4194. data->savagery_req = result.GetInt16Str("savagery_req");
  4195. data->savagery_upkeep = result.GetInt16Str("savagery_upkeep");
  4196. data->savagery_req_percent = result.GetInt8Str("savagery_req_percent");
  4197. data->dissonance_req = result.GetInt16Str("dissonance_req");
  4198. data->dissonance_upkeep = result.GetInt16Str("dissonance_upkeep");
  4199. data->dissonance_req_percent = result.GetInt8Str("dissonance_req_percent");
  4200. /* Spell Parameters */
  4201. data->call_frequency = result.GetInt32Str("call_frequency");
  4202. data->cast_time = result.GetInt16Str("cast_time");
  4203. data->duration1 = result.GetInt32Str("duration1");
  4204. data->duration2 = result.GetInt32Str("duration2");
  4205. data->hit_bonus = result.GetFloatStr("hit_bonus");
  4206. data->max_aoe_targets = result.GetInt16Str("max_aoe_targets");
  4207. data->min_range = result.GetFloatStr("min_range");
  4208. data->radius = result.GetFloatStr("radius");
  4209. data->range = result.GetFloatStr("range");
  4210. data->recast = result.GetFloatStr("recast");
  4211. data->recovery = result.GetFloatStr("recovery");
  4212. data->resistibility = result.GetFloatStr("resistibility");
  4213. data->linked_timer = result.GetInt32Str("linked_timer_id");
  4214. data->spell_name_crc = result.GetInt32Str("spell_name_crc");
  4215. data->type_group_spell_id = result.GetSInt32Str("type_group_spell_id");
  4216. data->can_fizzle = ( result.GetInt8Str("can_fizzle") == 1);
  4217. /* Cast Messaging */
  4218. string message = result.GetStringStr("success_message");
  4219. if( message.length() > 0 )
  4220. data->success_message = message;
  4221. message = result.GetStringStr("fade_message");
  4222. if( message.length() > 0 )
  4223. data->fade_message = string(message);
  4224. message = result.GetStringStr("fade_message_others");
  4225. if (message.length() > 0)
  4226. data->fade_message_others = string(message);
  4227. message = result.GetStringStr("effect_message");
  4228. if( message.length() > 0 )
  4229. data->effect_message = string(message);
  4230. string lua_script = result.GetStringStr("lua_script");
  4231. if( lua_script.length() > 0 )
  4232. data->lua_script = string(lua_script);
  4233. /* Load spell level data */
  4234. spell = new Spell(data);
  4235. if(level_data && level_data->count(data->id) > 0)
  4236. {
  4237. vector<LevelArray*>* level_array = &((*level_data)[data->id]);
  4238. for(int8 i=0; i<level_array->size(); i++)
  4239. {
  4240. spell->AddSpellLevel(level_array->at(i)->adventure_class, level_array->at(i)->tradeskill_class, level_array->at(i)->spell_level*10);
  4241. }
  4242. }
  4243. /* Add spell to master list */
  4244. master_spell_list.AddSpell(data->id, data->tier, spell);
  4245. total++;
  4246. if( lua_script.length() > 0 )
  4247. LogWrite(SPELL__DEBUG, 5, "Spells", "\t%i. %s (Tier: %i) - '%s'", spell_id, spell_name.c_str(), data->tier, lua_script.c_str());
  4248. else if(data->is_active)
  4249. LogWrite(SPELL__WARNING, 1, "Spells", "\tSpell %s (%u, Tier: %i) set 'Active', but missing LUAScript", spell_name.c_str(), spell_id, data->tier);
  4250. } // end while
  4251. } // end else
  4252. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spell Effects...");
  4253. LoadSpellEffects();
  4254. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spell LUA Data...");
  4255. LoadSpellLuaData();
  4256. if(lua_interface)
  4257. {
  4258. LogWrite(SPELL__DEBUG, 0, "Spells", "Loading Spells Scripts...");
  4259. LoadSpellScriptData();
  4260. }
  4261. if (level_data) {
  4262. map<int32, vector<LevelArray*> >::iterator map_itr;
  4263. vector<LevelArray*>::iterator level_itr;
  4264. for(map_itr = level_data->begin(); map_itr != level_data->end(); map_itr++)
  4265. {
  4266. for(level_itr = map_itr->second.begin(); level_itr != map_itr->second.end(); level_itr++)
  4267. {
  4268. safe_delete(*level_itr);
  4269. }
  4270. }
  4271. }
  4272. safe_delete(level_data);
  4273. LogWrite(SPELL__INFO, 0, "Spells", "Loaded %u Spell%s (took %u seconds)", total, total == 1 ? "" : "s", Timer::GetUnixTimeStamp() - t_now);
  4274. }
  4275. void WorldDatabase::LoadSpellLuaData(){
  4276. Spell *spell;
  4277. Query query;
  4278. MYSQL_ROW row;
  4279. int32 total = 0;
  4280. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`,`tier`,`value_type`,`value`,`value2`,`dynamic_helper` "
  4281. "FROM `spell_data` "
  4282. "ORDER BY `index_field`");
  4283. while (result && (row = mysql_fetch_row(result))) {
  4284. if ((spell = master_spell_list.GetSpell(atoul(row[0]), atoi(row[1]))) && row[2] && row[3] && row[4] && row[4]) {
  4285. LogWrite(SPELL__DEBUG, 5, "Spells", "\tLoading Spell LUA Data for spell_id: %u", atoul(row[0]));
  4286. if (!strcmp(row[2], "INT"))
  4287. spell->AddSpellLuaDataInt(atoi(row[3]), atoi(row[4]), string(row[5]));
  4288. else if (!strcmp(row[2], "FLOAT"))
  4289. spell->AddSpellLuaDataFloat(atof(row[3]), atof(row[4]),string(row[5]));
  4290. else if (!strcmp(row[2], "BOOL"))
  4291. spell->AddSpellLuaDataBool(!(strncasecmp(row[3], "true", 4)), string(row[5]));
  4292. else if (!strcmp(row[2], "STRING"))
  4293. spell->AddSpellLuaDataString(string(row[3]), string(row[4]), string(row[5]));
  4294. else
  4295. LogWrite(SPELL__ERROR, 0, "Spells", "Invalid Lua Spell data '%s' for Spell ID: %u", row[2], spell->GetSpellID());
  4296. total++;
  4297. }
  4298. }
  4299. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %i Spell LUA Data entr%s.", total, total == 1 ? "y" : "ies");
  4300. }
  4301. void WorldDatabase::LoadSpellEffects() {
  4302. Spell *spell;
  4303. Query query;
  4304. MYSQL_ROW row;
  4305. int32 total = 0;
  4306. MYSQL_RES *result = query.RunQuery2(Q_SELECT, "SELECT `spell_id`,`tier`,`percentage`,`bullet`,`description` "
  4307. "FROM `spell_display_effects` "
  4308. "ORDER BY `spell_id`,`id` ASC");
  4309. while (result && (row = mysql_fetch_row(result))) {
  4310. if ((spell = master_spell_list.GetSpell(atoul(row[0]), atoi(row[1]))) && row[4]) {
  4311. LogWrite(SPELL__DEBUG, 5, "Spells", "\tLoading Spell Effects for spell_id: %u", atoul(row[0]));
  4312. spell->AddSpellEffect(atoi(row[2]), atoi(row[3]), row[4]);
  4313. total++;
  4314. }
  4315. }
  4316. LogWrite(SPELL__DEBUG, 0, "Spells", "\tLoaded %u Spell Effect%s.", total, total == 1 ? "" : "s");
  4317. }
  4318. int32 WorldDatabase::LoadPlayerSkillbar(Client* client){
  4319. client->GetPlayer()->ClearQuickbarItems();
  4320. Query query;
  4321. 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());
  4322. MYSQL_ROW row;
  4323. int32 count = 0;
  4324. while(result && (row = mysql_fetch_row(result))){
  4325. count++;
  4326. int8 tier = atoi(row[6]);
  4327. Spell* spell = master_spell_list.GetSpell(atoul(row[2]), tier);
  4328. if(spell)
  4329. 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);
  4330. else if(atoul(row[1]) == QUICKBAR_MACRO)
  4331. 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);
  4332. else
  4333. client->GetPlayer()->AddQuickbarItem(atoul(row[5]), atoul(row[3]), atoul(row[1]), 0, 0, atoul(row[2]), 0, atoul(row[0]), row[4], false);
  4334. }
  4335. return count;
  4336. }
  4337. bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
  4338. Guild *guild;
  4339. if((guild = guild_list.GetGuild(GetGuildIDByCharacterID(character_id))))
  4340. guild->RemoveGuildMember(character_id);
  4341. Query query;
  4342. Query query2;
  4343. query.RunQuery2(Q_DELETE, "DELETE FROM characters WHERE id=%u AND account_id=%u", character_id, account_id);
  4344. //delete languages
  4345. query2.RunQuery2(Q_DELETE, "DELETE FROM character_languages WHERE char_id=%u", character_id);
  4346. //delete quest rewards
  4347. query2.RunQuery2(Q_DELETE, "DELETE FROM character_quest_rewards WHERE char_id=%u", character_id);
  4348. query2.RunQuery2(Q_DELETE, "DELETE FROM character_quest_temporary_rewards WHERE char_id=%u", character_id);
  4349. if(!query.GetAffectedRows())
  4350. {
  4351. //No error just in case ppl try doing stupid stuff
  4352. return false;
  4353. }
  4354. else{ //successfull, so the character did exist with that character_id and account_id
  4355. // new DB constraints should handle all these deletes, and more... commenting out for now
  4356. /*query.RunQuery2(Q_DELETE, "delete FROM character_details where char_id=%u", character_id);
  4357. query.RunQuery2(Q_DELETE, "delete FROM character_factions where char_id=%u", character_id);
  4358. query.RunQuery2(Q_DELETE, "delete FROM character_items where char_id=%u", character_id);
  4359. query.RunQuery2(Q_DELETE, "delete FROM character_skillbar where char_id=%u", character_id);
  4360. query.RunQuery2(Q_DELETE, "delete FROM character_skills where char_id=%u", character_id);
  4361. query.RunQuery2(Q_DELETE, "delete FROM character_spells where char_id=%u", character_id);
  4362. query.RunQuery2(Q_DELETE, "delete FROM char_colors where char_id=%u", character_id);*/
  4363. }
  4364. return true;
  4365. }
  4366. void WorldDatabase::DeleteCharacterSpell(int32 character_id, int32 spell_id) {
  4367. if (character_id > 0 && spell_id > 0) {
  4368. Query query;
  4369. query.RunQuery2(Q_DELETE, "DELETE FROM character_spells WHERE char_id=%u AND spell_id=%u", character_id, spell_id);
  4370. }
  4371. }
  4372. bool WorldDatabase::GetItemResultsToClient (Client* client, const char* varSearch, int maxResults) {
  4373. Query query;
  4374. MYSQL_ROW row;
  4375. int results = 0;
  4376. if( maxResults > 10 && client->GetAdminStatus ( ) < 100 )
  4377. maxResults = 10;
  4378. else if( maxResults > 20 )
  4379. maxResults = 20;
  4380. client->Message(CHANNEL_COLOR_YELLOW, "Item Search Results");
  4381. client->Message(CHANNEL_COLOR_YELLOW, "ResultNum) [ItemID] ItemName");
  4382. string itemsearch_query = string("SELECT id, name FROM items where name like '%%%s%%' limit %i");
  4383. MYSQL_RES* result = query.RunQuery2(Q_SELECT, itemsearch_query.c_str(),getSafeEscapeString(varSearch).c_str(),maxResults);
  4384. while(result && (row = mysql_fetch_row(result))){
  4385. results++;
  4386. client->Message(CHANNEL_COLOR_YELLOW, "%i) [%s] %s",results,row[0],row[1]);
  4387. }
  4388. if(results == 0)
  4389. {
  4390. client->Message(CHANNEL_COLOR_YELLOW, "No Items Found.");
  4391. return false;
  4392. }
  4393. client->Message(CHANNEL_COLOR_YELLOW, "%i Items Found.",results);
  4394. return true;
  4395. }
  4396. void WorldDatabase::SaveWorldTime(WorldTime* time){
  4397. Query query;
  4398. 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);
  4399. }
  4400. 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){
  4401. Query query;
  4402. int32 dbVersion = rule_manager.GetGlobalRule(R_World, DatabaseVersion)->GetInt32();
  4403. 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')");
  4404. query.RunQuery2(Q_INSERT, bug_report.c_str(), getSafeEscapeString(category).c_str(), getSafeEscapeString(subcategory).c_str(),
  4405. getSafeEscapeString(causes_crash).c_str(), getSafeEscapeString(reproducible).c_str(), getSafeEscapeString(summary).c_str(),
  4406. getSafeEscapeString(description).c_str(), getSafeEscapeString(version).c_str(), getSafeEscapeString(player).c_str(), account_id,
  4407. getSafeEscapeString(spawn_name).c_str(), spawn_id, zone_id, dbVersion, CURRENT_VERSION);
  4408. FixBugReport();
  4409. FixBugReport();
  4410. FixBugReport();
  4411. }
  4412. void WorldDatabase::FixBugReport(){
  4413. Query query;
  4414. 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)))");
  4415. query.RunQuery2(bug_report.c_str(), Q_UPDATE);
  4416. }
  4417. int32 WorldDatabase::LoadQuests(){
  4418. Query query;
  4419. MYSQL_ROW row;
  4420. 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`");
  4421. Quest* quest = 0;
  4422. char* name = 0;
  4423. char* type = 0;
  4424. char* zone = 0;
  4425. int8 level = 0;
  4426. int8 enc_level = 0;
  4427. char* description = 0;
  4428. char* script = 0;
  4429. int32 total = 0;
  4430. int32 id = 0;
  4431. char* completed_description = 0;
  4432. int32 return_npc_id = 0;
  4433. if(result){
  4434. while(result && (row = mysql_fetch_row(result))){
  4435. id = atoul(row[0]);
  4436. name = row[1];
  4437. type = row[2];
  4438. zone = row[3];
  4439. level = atoul(row[4]);
  4440. enc_level = atoul(row[5]);
  4441. description = row[6];
  4442. script = row[7];
  4443. completed_description = row[8];
  4444. return_npc_id = atoi(row[9]);
  4445. if(lua_interface) {
  4446. quest = lua_interface->LoadQuest(id, name, type, zone, level, description, script);
  4447. }
  4448. if(quest){
  4449. LogWrite(QUEST__DEBUG, 5, "Quests", "\tLoading Quest: '%s' (%u)", name, id);
  4450. LoadQuestDetails(quest);
  4451. string compDescription;
  4452. if (completed_description == NULL)
  4453. {
  4454. compDescription = string("Missing! Notify Developer");
  4455. LogWrite(QUEST__WARNING, 5, "Quests", "\tLoading Quest MISSING completed_text in quests table for: '%s' (%u)", name, id);
  4456. }
  4457. else
  4458. compDescription = string(completed_description);
  4459. quest->SetCompletedDescription(string(compDescription));
  4460. quest->SetQuestReturnNPC(return_npc_id);
  4461. quest->SetEncounterLevel(enc_level);
  4462. total++;
  4463. master_quest_list.AddQuest(id, quest);
  4464. }
  4465. else
  4466. {
  4467. 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!");
  4468. }
  4469. }
  4470. }
  4471. LogWrite(QUEST__DEBUG, 0, "Quest", "\tLoaded %i Quest(s)", total);
  4472. return total;
  4473. }
  4474. void WorldDatabase::LoadQuestDetails(Quest* quest) {
  4475. Query query;
  4476. MYSQL_ROW row;
  4477. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `type`, `subtype`, `value`, `faction_id`, `quantity` FROM `quest_details` WHERE `quest_id`=%u", quest->GetQuestID());
  4478. string type;
  4479. string subtype;
  4480. sint64 value;
  4481. int32 faction_id;
  4482. int32 quantity;
  4483. while (result && (row = mysql_fetch_row(result))) {
  4484. type = string(row[0]);
  4485. subtype = string(row[1]);
  4486. value = atoi(row[2]);
  4487. faction_id = atoi(row[3]);
  4488. quantity = atoi(row[4]);
  4489. 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);
  4490. if (type == "Prereq") {
  4491. if (subtype == "Class")
  4492. quest->AddPrereqClass(value);
  4493. else if (subtype == "Faction")
  4494. quest->AddPrereqFaction(faction_id, value);
  4495. else if (subtype == "Item") {
  4496. Item* master_item = master_item_list.GetItem(value);
  4497. if (master_item) {
  4498. Item* item = new Item(master_item);
  4499. quest->AddPrereqItem(item);
  4500. }
  4501. }
  4502. else if (subtype == "AdvLevel")
  4503. quest->SetPrereqLevel(value);
  4504. else if (subtype == "Quest")
  4505. quest->AddPrereqQuest(value);
  4506. else if (subtype == "Race")
  4507. quest->AddPrereqRace(value);
  4508. else if (subtype == "TSClass")
  4509. quest->AddPrereqTradeskillClass(value);
  4510. else if (subtype == "TSLevel")
  4511. quest->SetPrereqTSLevel(value);
  4512. else if (subtype == "MaxTSLevel")
  4513. quest->SetPrereqMaxTSLevel(value);
  4514. else if (subtype == "MaxAdvLevel")
  4515. quest->SetPrereqMaxLevel(value);
  4516. }
  4517. else if (type == "Reward") {
  4518. if (subtype == "Item") {
  4519. Item* master_item = master_item_list.GetItem(value);
  4520. if (master_item) {
  4521. Item* item = new Item(master_item);
  4522. item->details.count = quantity;
  4523. quest->AddRewardItem(item);
  4524. }
  4525. }
  4526. else if (subtype == "Selectable") {
  4527. Item* master_item = master_item_list.GetItem(value);
  4528. if (master_item) {
  4529. Item* item = new Item(master_item);
  4530. item->details.count = quantity;
  4531. quest->AddSelectableRewardItem(item);
  4532. }
  4533. }
  4534. else if (subtype == "Coin") {
  4535. int32 copper = 0;
  4536. int32 silver = 0;
  4537. int32 gold = 0;
  4538. int32 plat = 0;
  4539. if (value >= 1000000) {
  4540. plat = value / 1000000;
  4541. value -= 1000000 * plat;
  4542. }
  4543. if (value >= 10000) {
  4544. gold = value / 10000;
  4545. value -= 10000 * gold;
  4546. }
  4547. if (value >= 100) {
  4548. silver = value / 100;
  4549. value -= 100 * silver;
  4550. }
  4551. if (value > 0)
  4552. copper = value;
  4553. quest->AddRewardCoins(copper, silver, gold, plat);
  4554. }
  4555. else if (subtype == "MaxCoin") {
  4556. quest->AddRewardCoinsMax(value);
  4557. }
  4558. else if (subtype == "Faction")
  4559. quest->AddRewardFaction(faction_id, value);
  4560. else if (subtype == "Experience")
  4561. quest->SetRewardXP(value);
  4562. else if (subtype == "TSExperience")
  4563. quest->SetRewardTSXP(value);
  4564. }
  4565. }
  4566. }
  4567. void WorldDatabase::LoadMerchantInformation() {
  4568. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tClearing Merchant Inventory...");
  4569. world.DeleteMerchantItems();
  4570. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoading Merchant Inventory...");
  4571. LoadMerchantInventory();
  4572. Query query;
  4573. MYSQL_ROW row;
  4574. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT merchant_id, inventory_id FROM merchants ORDER BY merchant_id");
  4575. int32 total = 0;
  4576. int32 last_merchant_id = 0;
  4577. int32 id = 0;
  4578. MerchantInfo* merchant = 0;
  4579. if(result) {
  4580. while(result && (row = mysql_fetch_row(result))) {
  4581. id = atoul(row[0]);
  4582. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "\tMerchantID: %u, InventoryID: %u", id, atoul(row[1]));
  4583. if(id != last_merchant_id) {
  4584. if(merchant)
  4585. world.AddMerchantInfo(last_merchant_id, merchant);
  4586. merchant = new MerchantInfo;
  4587. last_merchant_id = id;
  4588. total++;
  4589. }
  4590. merchant->inventory_ids.push_back(atoul(row[1]));
  4591. }
  4592. if(merchant)
  4593. world.AddMerchantInfo(last_merchant_id, merchant);
  4594. }
  4595. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoaded %i Merchant List(s)", total);
  4596. }
  4597. void WorldDatabase::LoadMerchantInventory(){
  4598. Query query;
  4599. MYSQL_ROW row;
  4600. 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");
  4601. int32 total = 0;
  4602. int32 id;
  4603. if(result) {
  4604. while(result && (row = mysql_fetch_row(result))) {
  4605. MerchantItemInfo ItemInfo;
  4606. id = atoul(row[0]);
  4607. ItemInfo.item_id = atoul(row[1]);
  4608. ItemInfo.quantity = atoi(row[2]);
  4609. ItemInfo.price_item_id = atoul(row[3]);
  4610. ItemInfo.price_item_qty = atoi(row[4]);
  4611. ItemInfo.price_item2_id = atoul(row[5]);
  4612. ItemInfo.price_item2_qty = atoi(row[6]);
  4613. ItemInfo.price_status = atoul(row[7]);
  4614. ItemInfo.price_coins = atoul(row[8]);
  4615. ItemInfo.price_stationcash = atoul(row[9]);
  4616. LogWrite(MERCHANT__DEBUG, 5, "Merchant", "\tInventoryID: %u, ItemID: %u, Qty: %u", id, ItemInfo.item_id, ItemInfo.quantity);
  4617. world.AddMerchantItem(id, ItemInfo);
  4618. total++;
  4619. }
  4620. }
  4621. LogWrite(MERCHANT__DEBUG, 0, "Merchant", "\tLoaded %i Merchant Inventory Item(s)", total);
  4622. }
  4623. string WorldDatabase::GetMerchantDescription(int32 merchant_id) {
  4624. Query query;
  4625. MYSQL_ROW row;
  4626. string description;
  4627. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `description` FROM `merchants` WHERE `merchant_id`=%u", merchant_id);
  4628. if (result && (row = mysql_fetch_row(result)))
  4629. description = string(row[0]);
  4630. return description;
  4631. }
  4632. void WorldDatabase::LoadTransporters(ZoneServer* zone){
  4633. int32 total = 0;
  4634. zone->DeleteGlobalTransporters();
  4635. Query query;
  4636. MYSQL_ROW row;
  4637. 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");
  4638. if(result){
  4639. while(result && (row = mysql_fetch_row(result))){
  4640. LogWrite(TRANSPORT__DEBUG, 5, "Transport", "---Loading Transporter ID: %u, transport_type: %s", row[0], row[1]);
  4641. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---display_name: %s, message: %s", row[2], row[3]);
  4642. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---destination_zone_id: %s", row[4]);
  4643. 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]);
  4644. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---trigger_location_zone_id: %s", row[9]);
  4645. 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]);
  4646. LogWrite(TRANSPORT__DEBUG, 7, "Transport", "---trigger_radius: %s, cost: %s, id: %s", row[14], row[15]);
  4647. string name = "";
  4648. if(row[2])
  4649. name = string(row[2]);
  4650. string message = "";
  4651. if(row[3])
  4652. message = string(row[3]);
  4653. if(row[1] && strcmp(row[1], "Zone") == 0)
  4654. 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]));
  4655. else if (row[1] && strcmp(row[1], "Flight") == 0)
  4656. 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]));
  4657. else if(row[1] && strcmp(row[1], "Location") == 0)
  4658. 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]));
  4659. else
  4660. 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]));
  4661. total++;
  4662. }
  4663. }
  4664. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "--Loaded %i Transporter(s)", total);
  4665. LoadTransportMaps(zone);
  4666. }
  4667. void WorldDatabase::LoadFogInit(string zone, PacketStruct* packet)
  4668. {
  4669. LogWrite(WORLD__TRACE, 9, "World", "Enter: %s", __FUNCTION__);
  4670. if(!packet || zone.length() == 0)
  4671. return;
  4672. Query query;
  4673. MYSQL_ROW row;
  4674. 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());
  4675. if(result){
  4676. int count = mysql_num_rows(result);
  4677. int i=0;
  4678. int64 explored_key;
  4679. int64 unexplored_key;
  4680. packet->setArrayLengthByName("num_maps", count);
  4681. while(result && (row = mysql_fetch_row(result))){
  4682. packet->setDataByName("highest_z", atof(row[0]));
  4683. packet->setDataByName("lowest_z", atof(row[1]));
  4684. packet->setDataByName("map_id", atoul(row[15]));
  4685. packet->setArrayDataByName("unknown7", 1600, i);
  4686. packet->setArrayDataByName("unknown8", 1200, i);
  4687. packet->setArrayDataByName("zone_name", row[2], i);
  4688. packet->setArrayDataByName("explored_map_name", row[3], i);
  4689. packet->setArrayDataByName("unexplored_map_name", row[4], i);
  4690. packet->setArrayDataByName("map_bounds1_x", atof(row[5]), i);
  4691. packet->setArrayDataByName("map_bounds1_z", atof(row[6]), i);
  4692. packet->setArrayDataByName("map_bounds2_x", atof(row[7]), i);
  4693. packet->setArrayDataByName("map_bounds2_z", atof(row[8]), i);
  4694. packet->setArrayDataByName("map_bounds3_x", atof(row[9]), i);
  4695. packet->setArrayDataByName("map_bounds3_z", atof(row[10]), i);
  4696. packet->setArrayDataByName("map_bounds4_x", atof(row[11]), i);
  4697. packet->setArrayDataByName("map_bounds4_z", atof(row[12]), i);
  4698. #ifdef WIN32
  4699. explored_key = _strtoui64(row[13], NULL, 10);
  4700. unexplored_key = _strtoui64(row[14], NULL, 10);
  4701. #else
  4702. explored_key = strtoull(row[13], 0, 10);
  4703. unexplored_key = strtoull(row[14], 0, 10);
  4704. #endif
  4705. packet->setArrayDataByName("explored_key", explored_key, i);
  4706. packet->setArrayDataByName("unexplored_key", unexplored_key, i);
  4707. i++;
  4708. }
  4709. }
  4710. LogWrite(WORLD__TRACE, 9, "World", "Exit: %s", __FUNCTION__);
  4711. }
  4712. string WorldDatabase::GetColumnNames(char* name){
  4713. Query query;
  4714. MYSQL_ROW row;
  4715. string columns = "";
  4716. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "show columns FROM %s", name);
  4717. if(result && mysql_num_rows(result) > 0){
  4718. int16 i = 0;
  4719. while((row = mysql_fetch_row(result))){
  4720. if(strcmp(row[0], "table_data_version") != 0){
  4721. if(i>0)
  4722. columns.append(",");
  4723. columns.append(row[0]);
  4724. i++;
  4725. }
  4726. }
  4727. }
  4728. columns.append("");
  4729. return columns;
  4730. }
  4731. void WorldDatabase::ToggleCharacterOnline() {
  4732. Query query;
  4733. query.RunQuery2(Q_UPDATE, "UPDATE characters SET is_online = 0;");
  4734. }
  4735. void WorldDatabase::ToggleCharacterOnline(Client* client, int8 toggle) {
  4736. if (client) {
  4737. Query query;
  4738. Player* player = client->GetPlayer();
  4739. //if(!player->CheckPlayerInfo())
  4740. // return;
  4741. if (player)
  4742. {
  4743. LogWrite(PLAYER__DEBUG, 0, "Player", "Toggling Character %s", toggle ? "ONLINE!" : "OFFLINE!");
  4744. query.RunQuery2(Q_UPDATE, "UPDATE characters SET is_online=%i WHERE id = %u;", toggle, client->GetCharacterID());
  4745. }
  4746. }
  4747. }
  4748. void WorldDatabase::LoadPlayerStatistics(Player* player, int32 char_id) {
  4749. Query query;
  4750. MYSQL_ROW row;
  4751. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT stat_id, stat_value, stat_date FROM statistics WHERE char_id=%i", char_id);
  4752. while (result && (row = mysql_fetch_row(result))) {
  4753. int32 stat_id = atoi(row[0]);
  4754. sint32 stat_value = atoi(row[1]);
  4755. int32 stat_date = atoi(row[2]);
  4756. player->AddPlayerStatistic(stat_id, stat_value, stat_date);
  4757. }
  4758. }
  4759. void WorldDatabase::WritePlayerStatistic(Player *player, Statistic* stat) {
  4760. if (player && player->GetCharacterID() > 0 && stat) {
  4761. Query query;
  4762. 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;",
  4763. player->GetCharacterID(), 0, stat->stat_id, stat->stat_value, stat->stat_date,
  4764. stat->stat_value, stat->stat_date);
  4765. }
  4766. }
  4767. void WorldDatabase::LoadServerStatistics()
  4768. {
  4769. Query query;
  4770. MYSQL_ROW row;
  4771. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT stat_id, stat_value, stat_date FROM statistics WHERE char_id=0 AND guild_id=0");
  4772. while (result && (row = mysql_fetch_row(result))) {
  4773. int32 stat_id = atoi(row[0]);
  4774. sint32 stat_value = atoi(row[1]);
  4775. int32 stat_date = atoi(row[2]);
  4776. world.AddServerStatistic(stat_id, stat_value, stat_date);
  4777. LogWrite(INIT__DEBUG, 5, "Stats", "Loading Stat ID %i, value: %i", stat_id, stat_value);
  4778. }
  4779. }
  4780. void WorldDatabase::WriteServerStatistic(Statistic* stat) {
  4781. if (stat) {
  4782. Query query;
  4783. 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;",
  4784. stat->stat_id, stat->stat_value, stat->stat_date,
  4785. stat->stat_value, stat->stat_date);
  4786. }
  4787. }
  4788. void WorldDatabase::WriteServerStatistic(int32 stat_id, sint32 stat_value) {
  4789. Query query;
  4790. 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;",
  4791. stat_id, stat_value, Timer::GetUnixTimeStamp(),
  4792. stat_value, Timer::GetUnixTimeStamp());
  4793. }
  4794. void WorldDatabase::WriteServerStatisticsNeededQueries() {
  4795. Query query1, query2, query3;
  4796. MYSQL_ROW row1, row2, row3;
  4797. // Number of unique accounts
  4798. MYSQL_RES* result1 = query1.RunQuery2(Q_SELECT, "SELECT COUNT(DISTINCT account_id) FROM characters");
  4799. if (result1 && (row1 = mysql_fetch_row(result1)) && row1[0] != NULL)
  4800. WriteServerStatistic(STAT_SERVER_NUM_ACCOUNTS, atoi(row1[0]));
  4801. else
  4802. WriteServerStatistic(STAT_SERVER_NUM_ACCOUNTS, 0);
  4803. // Number of characters
  4804. MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM characters");
  4805. if (result2 && (row2 = mysql_fetch_row(result2)) && row2[0] != NULL)
  4806. WriteServerStatistic(STAT_SERVER_NUM_CHARACTERS, atoi(row2[0]));
  4807. else
  4808. WriteServerStatistic(STAT_SERVER_NUM_CHARACTERS, 0);
  4809. // Average character level
  4810. MYSQL_RES* result3 = query3.RunQuery2(Q_SELECT, "SELECT ROUND(AVG(level)) FROM characters");
  4811. if (result3 && (row3 = mysql_fetch_row(result3)) && row3[0] != NULL)
  4812. WriteServerStatistic(STAT_SERVER_AVG_CHAR_LEVEL, atoi(row3[0]));
  4813. else
  4814. WriteServerStatistic(STAT_SERVER_AVG_CHAR_LEVEL, 0);
  4815. }
  4816. map<int32,int32>* WorldDatabase::GetInstanceRemovedSpawns(int32 instance_id, int8 type)
  4817. {
  4818. DatabaseResult result;
  4819. map<int32,int32>* ret = NULL;
  4820. LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
  4821. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Loading removed spawns for instance_id: %u, spawn_type: %i", instance_id, type);
  4822. 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) )
  4823. {
  4824. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceRemovedSpawns() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4825. return ret;
  4826. }
  4827. else
  4828. {
  4829. if( result.GetNumRows() > 0 )
  4830. {
  4831. ret = new map<int32,int32>;
  4832. while( result.Next() )
  4833. {
  4834. int32 spawn_location_entry_id = result.GetInt32Str("spawn_location_entry_id");
  4835. /*
  4836. respawnTime == 0 - never respawn
  4837. respawnTime = 1 - spawn now
  4838. respawnTime > 1 (continue timer)
  4839. */
  4840. int32 respawntime = result.GetInt32Str("respawn_time");
  4841. LogWrite(INSTANCE__DEBUG, 5, "Instance", "Found spawn point: %u, respawn time: %i", spawn_location_entry_id, respawntime);
  4842. ret->insert(make_pair(spawn_location_entry_id, respawntime));
  4843. }
  4844. }
  4845. else
  4846. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No removed spawns found for instance_id: %u, spawn_type: %i", instance_id, type);
  4847. }
  4848. LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
  4849. return ret;
  4850. }
  4851. bool WorldDatabase::CheckVectorForValue(vector<int32>* vector, int32 value) {
  4852. if ( vector != NULL )
  4853. {
  4854. for(int32 i=0;i<vector->size();i++)
  4855. {
  4856. int32 compare = vector->at(i);
  4857. if ( compare == value )
  4858. return true;
  4859. }
  4860. }
  4861. return false;
  4862. }
  4863. int32 WorldDatabase::CheckSpawnRemoveInfo(map<int32,int32>* inmap, int32 spawn_location_entry_id)
  4864. {
  4865. LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
  4866. map<int32, int32>::iterator iter;
  4867. if ( inmap != NULL )
  4868. {
  4869. for(iter=inmap->begin();iter!=inmap->end();iter++)
  4870. {
  4871. if ( iter->first == spawn_location_entry_id )
  4872. return (int32)iter->second;
  4873. }
  4874. }
  4875. return 1;
  4876. }
  4877. 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)
  4878. {
  4879. int32 ret = 0;
  4880. 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) )
  4881. {
  4882. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in AddCharacterInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4883. return 0;
  4884. }
  4885. ret = database_new.LastInsertID();
  4886. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Adding character %u to instance: %u", char_id, instance_id);
  4887. //LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Reenter: %u, Reset: %u, Lockout: %u", grant_reenter_time_left, grant_reset_time_left, lockout_time);
  4888. return ret;
  4889. }
  4890. bool WorldDatabase::UpdateCharacterInstanceTimers(int32 char_id, int32 instance_id, int32 lockout_time, int32 reset_time, int32 reenter_time )
  4891. {
  4892. if ( lockout_time > 0 && reset_time > 0 && reenter_time > 0 )
  4893. 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);
  4894. else if ( lockout_time > 0 && reset_time > 0 )
  4895. 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);
  4896. else if ( reset_time > 0 && reenter_time > 0 )
  4897. 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);
  4898. else if ( lockout_time > 0 && reenter_time > 0 )
  4899. 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);
  4900. else if ( lockout_time > 0 )
  4901. database_new.Query("UPDATE character_instances SET lockout_time = %i WHERE char_id = %i AND instance_id = %i", lockout_time, char_id, instance_id);
  4902. else if ( reset_time > 0 )
  4903. 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);
  4904. else if ( reenter_time > 0 )
  4905. 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);
  4906. if( database_new.GetError() )
  4907. {
  4908. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateCharacterInstanceTimers() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4909. return false;
  4910. }
  4911. else
  4912. {
  4913. if ( database_new.AffectedRows() > 0 )
  4914. {
  4915. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updating instance timers for character %u to instance: %u", char_id, instance_id);
  4916. LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Reenter: %u, Reset: %u, Lockout: %u", reenter_time, reset_time, lockout_time);
  4917. return true;
  4918. }
  4919. else
  4920. return false;
  4921. }
  4922. }
  4923. bool WorldDatabase::UpdateCharacterInstance(int32 char_id, string zone_name, int32 instance_id, int8 type, int32 timestamp) {
  4924. // type = 1 = success timestamp
  4925. // type = 2 = failure timestamp
  4926. if (instance_id > 0) {
  4927. if (type == 1) {
  4928. 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());
  4929. }
  4930. else if (type == 2) {
  4931. 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());
  4932. }
  4933. else {
  4934. 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());
  4935. }
  4936. }
  4937. else {
  4938. if (type == 1) {
  4939. 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());
  4940. }
  4941. else if (type == 2) {
  4942. 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());
  4943. }
  4944. }
  4945. if (database_new.GetError()) {
  4946. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateCharacterInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4947. return false;
  4948. }
  4949. return true;
  4950. }
  4951. bool WorldDatabase::VerifyInstanceID(int32 char_id, int32 instance_id) {
  4952. DatabaseResult result;
  4953. database_new.Select(&result, "SELECT COUNT(id) as num_instances FROM instances WHERE id = %u", instance_id);
  4954. if (result.Next() && result.GetInt32Str("num_instances") == 0) {
  4955. DeleteCharacterFromInstance(char_id, instance_id);
  4956. return false;
  4957. }
  4958. return true;
  4959. }
  4960. bool WorldDatabase::UpdateInstancedSpawnRemoved(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 instance_id )
  4961. {
  4962. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updating removed spawns for instance_id: %u", instance_id);
  4963. LogWrite(INSTANCE__DEBUG, 1, "Instance", "-- Spawn Location Entry ID: %u, Type: %u, Respawn: %u", spawn_location_entry_id, spawn_type, respawn_time);
  4964. 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) )
  4965. {
  4966. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in UpdateInstancedSpawnRemoved() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4967. return false;
  4968. }
  4969. if ( database_new.AffectedRows() > 0 )
  4970. {
  4971. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Updated removed spawns for instance_id: %u", instance_id);
  4972. return true;
  4973. }
  4974. else
  4975. return false;
  4976. }
  4977. int32 WorldDatabase::CreateNewInstance(int32 zone_id)
  4978. {
  4979. int32 ret = 0;
  4980. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Creating new instance for zone: %u ", zone_id);
  4981. if( !database_new.Query("INSERT INTO instances (zone_id) VALUES (%u)", zone_id) )
  4982. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in CreateNewInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4983. else
  4984. ret = database_new.LastInsertID();
  4985. if( ret > 0 )
  4986. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Created new instance_id %u for zone: %u ", ret, zone_id);
  4987. return ret;
  4988. }
  4989. int32 WorldDatabase::CreateInstanceSpawnRemoved(int32 spawn_location_entry_id, int32 spawn_type, int32 respawn_time, int32 instance_id )
  4990. {
  4991. int32 ret = 0;
  4992. LogWrite(INSTANCE__DEBUG, 3, "Instance", "Creating new instance spawn removed entries for instance_id: %u ", instance_id);
  4993. LogWrite(INSTANCE__DEBUG, 5, "Instance", "-- Spawn Location Entry ID: %u, Type: %u, Respawn: %u", spawn_location_entry_id, spawn_type, respawn_time);
  4994. 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) )
  4995. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in CreateInstanceSpawnRemoved() query '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  4996. else
  4997. ret = database_new.LastInsertID();
  4998. // potentially spammy, if it calls for every spawn added. Set to level 3 or 5?
  4999. if( ret > 0 )
  5000. LogWrite(INSTANCE__DEBUG, 5, "Instance", "Created new spawn removed entry: %u for instance_id %u", ret, instance_id);
  5001. return ret;
  5002. }
  5003. bool WorldDatabase::DeleteInstance(int32 instance_id)
  5004. {
  5005. if( !database_new.Query("DELETE FROM instances WHERE id = %u", instance_id) )
  5006. {
  5007. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  5008. return false;
  5009. }
  5010. /* JA: should not need this delete with FK/Constraints
  5011. if( !database_new.Query("DELETE FROM instance_spawns_removed WHERE instance_id = %u", instance_id) )
  5012. {
  5013. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  5014. return false;
  5015. }
  5016. */
  5017. // Remove the instance from the character_instances table
  5018. database_new.Query("UPDATE `character_instances` SET `instance_id` = 0 WHERE `instance_id` = %u", instance_id);
  5019. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Deleted instance_id %u", instance_id);
  5020. return true;
  5021. }
  5022. bool WorldDatabase::DeleteInstanceSpawnRemoved(int32 instance_id, int32 spawn_location_entry_id)
  5023. {
  5024. 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) )
  5025. {
  5026. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteInstanceSpawnRemoved() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  5027. return false;
  5028. }
  5029. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Deleted removed spawn: %u for instance_id %u", spawn_location_entry_id, instance_id);
  5030. return true;
  5031. }
  5032. bool WorldDatabase::DeleteCharacterFromInstance(int32 char_id, int32 instance_id)
  5033. {
  5034. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Delete character %u from instance_id %u.", char_id, instance_id);
  5035. if( !database_new.Query("UPDATE `character_instances` SET `instance_id` = 0 WHERE `instance_id` = %u AND `char_id` = %u", instance_id, char_id) )
  5036. {
  5037. LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in DeleteCharacterFromInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
  5038. return false;
  5039. }
  5040. if ( database_new.AffectedRows() == 0 ) // didn't find an instance to delete
  5041. {
  5042. LogWrite(INSTANCE__DEBUG, 1, "Instance", "No instance_id %u for character %u to delete.", instance_id, char_id);
  5043. return false;
  5044. }
  5045. else
  5046. {
  5047. // delete entire instance if the last player has left
  5048. DatabaseResult result;
  5049. database_new.Select(&result, "SELECT count(id) as num_instances FROM character_instances where instance_id = %u",instance_id);
  5050. if(result.Next() && result.GetInt32Str("num_instances") == 0)
  5051. {
  5052. LogWrite(INSTANCE__DEBUG, 0, "Instance", "No characters in instance: Delete instance_id %u.", instance_id);
  5053. DeleteInstance(instance_id);
  5054. }
  5055. }
  5056. return true;
  5057. }
  5058. bool WorldDatabase::LoadCharacterInstances(Client* client)
  5059. {
  5060. DatabaseResult result;
  5061. DatabaseResult result2;
  5062. bool addedInstance = false;
  5063. 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());
  5064. if( result.GetNumRows() > 0 )
  5065. {
  5066. while( result.Next() )
  5067. {
  5068. int32 zone_id = 0;
  5069. int32 instance_id = result.GetInt32Str("instance_id");
  5070. // If `instance_id` is greater then 0 then get the zone id with it, else get the zone id from the zone name
  5071. if (instance_id != 0) {
  5072. if (database_new.Select(&result2, "SELECT `zone_id` FROM `instances` WHERE `id` = %u", instance_id)) {
  5073. if (result2.Next())
  5074. zone_id = result2.GetInt32Str("zone_id");
  5075. }
  5076. }
  5077. if (zone_id == 0)
  5078. zone_id = GetZoneID(result.GetStringStr("instance_zone_name"));
  5079. client->GetPlayer()->GetCharacterInstances()->AddInstance(
  5080. result.GetInt32Str("id"),
  5081. instance_id,
  5082. result.GetInt32Str("last_success_timestamp"),
  5083. result.GetInt32Str("last_failure_timestamp"),
  5084. result.GetInt32Str("success_lockout_time"),
  5085. result.GetInt32Str("failure_lockout_time"),
  5086. zone_id,
  5087. result.GetInt8Str("instance_type"),
  5088. string(result.GetStringStr("instance_zone_name"))
  5089. );
  5090. addedInstance = true;
  5091. }
  5092. }
  5093. return addedInstance;
  5094. }
  5095. void WorldDatabase::UpdateLoginEquipment()
  5096. {
  5097. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Updating `character_items` CRC in %s", __FUNCTION__);
  5098. 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 )");
  5099. }
  5100. MutexMap<int32, LoginEquipmentUpdate>* WorldDatabase::GetEquipmentUpdates()
  5101. {
  5102. DatabaseResult result;
  5103. MutexMap<int32, LoginEquipmentUpdate>* ret = 0;
  5104. LoginEquipmentUpdate update;
  5105. int32 count = 0;
  5106. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Appearance Updates...");
  5107. // TODO: Someday store the equipment colors in character_items, for custom colorization of gear (?)
  5108. 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") )
  5109. {
  5110. while( result.Next() )
  5111. {
  5112. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for char_id %i!", result.GetInt32Str("char_id"));
  5113. if(!ret)
  5114. ret = new MutexMap<int32, LoginEquipmentUpdate>();
  5115. update.world_char_id = result.GetInt32Str("char_id");
  5116. update.equip_type = result.GetInt16Str("equip_type");
  5117. update.red = result.GetInt8Str("red");
  5118. update.green = result.GetInt8Str("green");
  5119. update.blue = result.GetInt8Str("blue");
  5120. update.highlight_red = result.GetInt8Str("highlight_red");
  5121. update.highlight_green = result.GetInt8Str("highlight_green");
  5122. update.highlight_blue = result.GetInt8Str("highlight_blue");
  5123. update.slot = result.GetInt8Str("slot");
  5124. ret->Put(result.GetInt32Str("id"), update);
  5125. count++;
  5126. }
  5127. }
  5128. if(count)
  5129. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Appearance Update%s...", count, count == 1 ? "" : "s");
  5130. return ret;
  5131. }
  5132. MutexMap<int32, LoginEquipmentUpdate>* WorldDatabase::GetEquipmentUpdates(int32 char_id)
  5133. {
  5134. DatabaseResult result;
  5135. MutexMap<int32, LoginEquipmentUpdate>* ret = 0;
  5136. LoginEquipmentUpdate update;
  5137. int32 count = 0;
  5138. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Appearance Updates for char_id: %u", char_id);
  5139. // TODO: Someday store the equipment colors in character_items, for custom colorization of gear (?)
  5140. 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) )
  5141. {
  5142. while( result.Next() )
  5143. {
  5144. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for char_id %i!", result.GetInt32Str("char_id"));
  5145. if(!ret)
  5146. ret = new MutexMap<int32, LoginEquipmentUpdate>();
  5147. update.world_char_id = char_id;
  5148. update.equip_type = result.GetInt16Str("equip_type");
  5149. update.red = result.GetInt8Str("red");
  5150. update.green = result.GetInt8Str("green");
  5151. update.blue = result.GetInt8Str("blue");
  5152. update.highlight_red = result.GetInt8Str("highlight_red");
  5153. update.highlight_green = result.GetInt8Str("highlight_green");
  5154. update.highlight_blue = result.GetInt8Str("highlight_blue");
  5155. update.slot = result.GetInt8Str("slot");
  5156. ret->Put(result.GetInt32Str("id"), update);
  5157. count++;
  5158. }
  5159. }
  5160. if(count)
  5161. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Appearance Update%s...", count, count == 1 ? "" : "s");
  5162. return ret;
  5163. }
  5164. void WorldDatabase::UpdateLoginZones() {
  5165. Query query;
  5166. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Updating `zones` CRC in %s", __FUNCTION__);
  5167. query.RunQuery2("UPDATE zones SET login_checksum = CRC32(CRC32(id) + CRC32(`name`) + CRC32(`file`) + CRC32(description))", Q_UPDATE);
  5168. }
  5169. MutexMap<int32, LoginZoneUpdate>* WorldDatabase::GetZoneUpdates() {
  5170. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Looking for Login Zone Updates...");
  5171. MutexMap<int32, LoginZoneUpdate>* ret = 0;
  5172. LoginZoneUpdate update;
  5173. Query query;
  5174. MYSQL_ROW row;
  5175. int32 count = 0;
  5176. 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))");
  5177. while(result && (row = mysql_fetch_row(result))) {
  5178. if(row[0] && row[1]) {
  5179. LogWrite(INIT__LOGIN_DEBUG, 5, "Login", "Found update for zone_id %i!", atoi(row[0]));
  5180. if(!ret)
  5181. ret = new MutexMap<int32, LoginZoneUpdate>();
  5182. update.name = string(row[1]);
  5183. if(row[2])
  5184. update.description = string(row[2]);
  5185. else
  5186. update.description = "";
  5187. ret->Put(atoi(row[0]), update);
  5188. count++;
  5189. }
  5190. }
  5191. if(count)
  5192. LogWrite(INIT__LOGIN_DEBUG, 0, "Login", "Found %i Login Zone Update%s...", count, count == 1 ? "" : "s");
  5193. return ret;
  5194. }
  5195. void WorldDatabase::LoadLocationGrids(ZoneServer* zone) {
  5196. if (zone) {
  5197. Query query;
  5198. MYSQL_ROW row;
  5199. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `grid_id`, `name`, `include_y`, `discovery` FROM `locations` WHERE `zone_id`=%u", zone->GetZoneID());
  5200. while (result && (row = mysql_fetch_row(result))) {
  5201. LocationGrid* grid = new LocationGrid;
  5202. grid->id = atoul(row[0]);
  5203. grid->grid_id = atoul(row[1]);
  5204. grid->name = string(row[2]);
  5205. grid->include_y = (atoi(row[3]) == 1);
  5206. grid->discovery = (atoi(row[4]) == 1);
  5207. if (LoadLocationGridLocations(grid))
  5208. zone->AddLocationGrid(grid);
  5209. else
  5210. safe_delete(grid);
  5211. }
  5212. }
  5213. }
  5214. bool WorldDatabase::LoadLocationGridLocations(LocationGrid* grid) {
  5215. bool ret = false;
  5216. if (grid) {
  5217. Query query;
  5218. MYSQL_ROW row;
  5219. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `x`, `y`, `z` FROM `location_details` WHERE `location_id`=%u", grid->id);
  5220. if (result->row_count >= 3) {
  5221. while (result && (row = mysql_fetch_row(result))) {
  5222. Location* location = new Location;
  5223. location->id = atoul(row[0]);
  5224. location->x = atof(row[1]);
  5225. location->y = atof(row[2]);
  5226. location->z = atof(row[3]);
  5227. grid->locations.Add(location);
  5228. }
  5229. ret = true;
  5230. }
  5231. else
  5232. 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);
  5233. }
  5234. return ret;
  5235. }
  5236. int32 WorldDatabase::CreateLocation(int32 zone_id, int32 grid_id, const char* name, bool include_y) {
  5237. int32 ret = 0;
  5238. if (name && strlen(name) > 0) {
  5239. Query query;
  5240. 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);
  5241. ret = query.GetLastInsertedID();
  5242. }
  5243. return ret;
  5244. }
  5245. bool WorldDatabase::AddLocationPoint(int32 location_id, float x, float y, float z) {
  5246. bool ret = false;
  5247. if (LocationExists(location_id)) {
  5248. Query query;
  5249. query.RunQuery2(Q_INSERT, "INSERT INTO `location_details` (`location_id`, `x`, `y`, `z`) VALUES (%u, %f, %f, %f)", location_id, x, y, z);
  5250. ret = true;
  5251. }
  5252. return ret;
  5253. }
  5254. bool WorldDatabase::DeleteLocation(int32 location_id) {
  5255. bool ret = false;
  5256. if (LocationExists(location_id)) {
  5257. Query query;
  5258. query.RunQuery2(Q_DELETE, "DELETE FROM `locations` WHERE `id`=%u", location_id);
  5259. ret = true;
  5260. }
  5261. return ret;
  5262. }
  5263. bool WorldDatabase::DeleteLocationPoint(int32 location_point_id) {
  5264. Query query;
  5265. query.RunQuery2(Q_DELETE, "DELETE FROM `location_details` WHERE `id`=%u", location_point_id);
  5266. return true;
  5267. }
  5268. void WorldDatabase::ListLocations(Client* client) {
  5269. if (client) {
  5270. Query query;
  5271. MYSQL_ROW row;
  5272. client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Listing all locations:");
  5273. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `zone_id`, `grid_id`, `name` FROM `locations`");
  5274. while (result && (row = mysql_fetch_row(result))) {
  5275. int32 id = atoul(row[0]);
  5276. int32 zone_id = atoul(row[1]);
  5277. int32 grid_id = atoul(row[2]);
  5278. const char* name = row[3];
  5279. client->Message(CHANNEL_COLOR_YELLOW, "%u) Zone ID: %u Grid ID:%u Name: '%s'", id, zone_id, grid_id, name);
  5280. }
  5281. }
  5282. }
  5283. void WorldDatabase::ListLocationPoints(Client* client, int32 location_id) {
  5284. if (client) {
  5285. if (LocationExists(location_id)) {
  5286. Query query;
  5287. client->Message(CHANNEL_COLOR_YELLOW, "Listing all points for location ID %u:", location_id);
  5288. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `x`, `y`, `z` FROM `location_details` WHERE `location_id`=%u", location_id);
  5289. MYSQL_ROW row;
  5290. while (result && (row = mysql_fetch_row(result))) {
  5291. int32 id = atoul(row[0]);
  5292. float x = atof(row[1]);
  5293. float y = atof(row[2]);
  5294. float z = atof(row[3]);
  5295. client->Message(CHANNEL_COLOR_YELLOW, "%u) (%f, %f, %f)", id, x, y, z);
  5296. }
  5297. }
  5298. else
  5299. client->Message(CHANNEL_COLOR_YELLOW, "A location with ID %u does not exist", location_id);
  5300. }
  5301. }
  5302. bool WorldDatabase::LocationExists(int32 location_id) {
  5303. bool ret = false;
  5304. Query query;
  5305. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM `locations` WHERE `id`=%u", location_id);
  5306. MYSQL_ROW row;
  5307. if (result && (row = mysql_fetch_row(result))) {
  5308. if (atoul(row[0]) > 0)
  5309. ret = true;
  5310. }
  5311. return ret;
  5312. }
  5313. bool WorldDatabase::QueriesFromFile(const char * file) {
  5314. return database_new.QueriesFromFile(file);
  5315. }
  5316. bool WorldDatabase::CheckBannedIPs(const char* loginIP)
  5317. {
  5318. // til you build the table, all IPs are allowed
  5319. return false;
  5320. }
  5321. sint32 WorldDatabase::AddMasterTitle(const char* titleName, int8 isPrefix)
  5322. {
  5323. if(titleName == nullptr || strlen(titleName) < 1)
  5324. {
  5325. LogWrite(DATABASE__ERROR, 0, "DBNew", "AddMasterTitle called with missing titleName");
  5326. return -1;
  5327. }
  5328. Query query;
  5329. Title* title = master_titles_list.GetTitleByName(titleName);
  5330. if(title)
  5331. return title->GetID();
  5332. query.RunQuery2(Q_INSERT, "INSERT INTO titles set title='%s', prefix=%u", titleName, isPrefix);
  5333. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  5334. LogWrite(DATABASE__ERROR, 0, "Database", "Error in AddMasterTitle query '%s': %s", query.GetQuery(), query.GetError());
  5335. return false;
  5336. }
  5337. int32 last_insert_id = query.GetLastInsertedID();
  5338. if(last_insert_id > 0)
  5339. {
  5340. title = new Title;
  5341. title->SetID(last_insert_id);
  5342. title->SetName(titleName);
  5343. title->SetPrefix(isPrefix);
  5344. master_titles_list.AddTitle(title);
  5345. return (sint32)last_insert_id;
  5346. }
  5347. return -1;
  5348. }
  5349. void WorldDatabase::LoadTitles(){
  5350. Query query;
  5351. MYSQL_ROW row;
  5352. int32 count = 0;
  5353. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, title, prefix FROM titles");
  5354. if(result && mysql_num_rows(result) > 0){
  5355. Title* title = 0;
  5356. while(result && (row = mysql_fetch_row(result))){
  5357. sint32 idx = atoi(row[0]);
  5358. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Title '%s' (%u), Prefix: %i, Index: %u", row[1], idx, atoi(row[2]), count);
  5359. title = new Title;
  5360. title->SetID(idx);
  5361. title->SetName(row[1]);
  5362. title->SetPrefix(atoi(row[2]));
  5363. master_titles_list.AddTitle(title);
  5364. count++;
  5365. }
  5366. }
  5367. LogWrite(WORLD__DEBUG, 0, "World", "\tLoaded %u Title%s", count, count == 1 ? "" : "s");
  5368. }
  5369. sint32 WorldDatabase::LoadCharacterTitles(int32 char_id, Player *player){
  5370. LogWrite(WORLD__DEBUG, 0, "World", "Loading Titles for player '%s'...", player->GetName());
  5371. Query query;
  5372. MYSQL_ROW row;
  5373. sint32 index = 0;
  5374. 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);
  5375. if(result && mysql_num_rows(result) > 0){
  5376. while(result && (row = mysql_fetch_row(result))){
  5377. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Title ID: %u, Title: '%s' Index: %u", atoul(row[0]), row[1], index);
  5378. player->AddTitle(index, row[1], atoi(row[2]));
  5379. index++;
  5380. }
  5381. }
  5382. return index;
  5383. }
  5384. sint32 WorldDatabase::GetCharPrefixIndex(int32 char_id, Player *player){
  5385. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current title index for player '%s'...", player->GetName());
  5386. Query query;
  5387. MYSQL_ROW row;
  5388. sint32 ret = 0;
  5389. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT prefix_title FROM character_details WHERE char_id = %u", char_id);
  5390. if(result && mysql_num_rows(result) > 0)
  5391. while(result && (row = mysql_fetch_row(result))){
  5392. ret = atoi(row[0]);
  5393. LogWrite(PLAYER__DEBUG, 5, "Player", "\tPrefix Index: %i", ret);
  5394. }
  5395. return ret;
  5396. }
  5397. sint32 WorldDatabase::GetCharSuffixIndex(int32 char_id, Player *player){
  5398. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current title index for player '%s'...", player->GetName());
  5399. Query query;
  5400. MYSQL_ROW row;
  5401. sint32 ret = 0;
  5402. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT suffix_title FROM character_details WHERE char_id = %u", char_id);
  5403. if(result && mysql_num_rows(result) > 0)
  5404. while(result && (row = mysql_fetch_row(result))){
  5405. ret = atoi(row[0]);
  5406. LogWrite(PLAYER__DEBUG, 5, "Player", "\tSuffix Index: %i", ret);
  5407. }
  5408. return ret;
  5409. }
  5410. void WorldDatabase::SaveCharPrefixIndex(sint32 index, int32 char_id){
  5411. Query query;
  5412. query.RunQuery2(Q_UPDATE, "UPDATE character_details SET prefix_title = %i WHERE char_id = %u", index, char_id);
  5413. LogWrite(PLAYER__DEBUG, 0, "Player", "Saving Prefix Index %i for character id '%u'...", index, char_id);
  5414. }
  5415. void WorldDatabase::SaveCharSuffixIndex(sint32 index, int32 char_id){
  5416. Query query;
  5417. query.RunQuery2(Q_SELECT, "UPDATE character_details SET suffix_title = %i WHERE char_id = %u", index, char_id);
  5418. LogWrite(PLAYER__DEBUG, 0, "Player", "Saving Suffix Index %i for character id %u...", index, char_id);
  5419. }
  5420. sint32 WorldDatabase::AddCharacterTitle(sint32 index, int32 char_id, Spawn* player) {
  5421. if(!player || !player->IsPlayer())
  5422. {
  5423. LogWrite(DATABASE__ERROR, 0, "DBNew", "AddCharacterTitle spawn is not a player: %s", player ? player->GetName() : "Unset");
  5424. return -1;
  5425. }
  5426. Title* title = master_titles_list.GetTitle(index);
  5427. if(!title)
  5428. {
  5429. 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);
  5430. return -1;
  5431. }
  5432. Query query;
  5433. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding titles for char_id: %u, index: %i", char_id, index);
  5434. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_titles (char_id, title_id) VALUES (%u, %i)", char_id, index);
  5435. sint32 curIndex = (sint32)((Player*)player)->GetPlayerTitles()->Size();
  5436. ((Player*)player)->AddTitle(curIndex++, title->GetName(), title->GetPrefix(), title->GetSaveNeeded());
  5437. return curIndex;
  5438. }
  5439. void WorldDatabase::LoadLanguages()
  5440. {
  5441. int32 count = 0;
  5442. Query query;
  5443. MYSQL_ROW row;
  5444. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, language FROM languages");
  5445. if(result && mysql_num_rows(result) > 0)
  5446. {
  5447. Language* language = 0;
  5448. while(result && (row = mysql_fetch_row(result)))
  5449. {
  5450. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading language '%s' , ID: %u", row[1], atoul(row[0]));
  5451. language = new Language;
  5452. language->SetID(atoul(row[0]));
  5453. language->SetName(row[1]);
  5454. master_languages_list.AddLanguage(language);
  5455. count++;
  5456. }
  5457. }
  5458. LogWrite(WORLD__DEBUG, 0, "World", "\tLoaded %u Language%s", count, count == 1 ? "" : "s");
  5459. }
  5460. int32 WorldDatabase::LoadCharacterLanguages(int32 char_id, Player *player)
  5461. {
  5462. LogWrite(WORLD__DEBUG, 0, "World", "Loading Languages for player '%s'...", player->GetName());
  5463. Query query;
  5464. MYSQL_ROW row;
  5465. int32 count = 0;
  5466. 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);
  5467. if(result && mysql_num_rows(result) > 0)
  5468. {
  5469. while(result && (row = mysql_fetch_row(result)))
  5470. {
  5471. LogWrite(WORLD__DEBUG, 5, "World", "\tLoading Language ID: %u, Language: '%s'", atoul(row[0]), row[1]);
  5472. player->AddLanguage(atoul(row[0]), row[1]);
  5473. count++;
  5474. }
  5475. }
  5476. return count;
  5477. }
  5478. int16 WorldDatabase::GetCharacterCurrentLang(int32 char_id, Player *player)
  5479. {
  5480. LogWrite(PLAYER__DEBUG, 0, "Player", "Getting current language for player '%s'...", player->GetName());
  5481. Query query;
  5482. MYSQL_ROW row;
  5483. int16 ret = 0;
  5484. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT current_language FROM character_details WHERE char_id = %u", char_id);
  5485. if(result && mysql_num_rows(result) > 0)
  5486. while(result && (row = mysql_fetch_row(result)))
  5487. {
  5488. ret = atoi(row[0]);
  5489. LogWrite(PLAYER__DEBUG, 5, "Player", "\tLanguage ID: %i", ret);
  5490. }
  5491. return ret;
  5492. }
  5493. void WorldDatabase::SaveCharacterCurrentLang(int32 id, int32 char_id, Client *client)
  5494. {
  5495. Query query;
  5496. query.RunQuery2(Q_UPDATE, "UPDATE character_details SET current_language = %i WHERE char_id = %u", id, char_id);
  5497. LogWrite(PLAYER__DEBUG, 3, "Player", "Saving current language ID %i for player '%s'...", id, client->GetPlayer()->GetName());
  5498. }
  5499. void WorldDatabase::SaveCharacterLang(int32 char_id, int32 lang_id) {
  5500. if (!database_new.Query("INSERT INTO character_languages (char_id, language_id) VALUES (%u, %u)", char_id, lang_id))
  5501. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  5502. }
  5503. // JA - this is not used yet, lots more to consider for storing player history
  5504. void WorldDatabase::LoadCharacterHistory(int32 char_id, Player *player)
  5505. {
  5506. DatabaseResult result;
  5507. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  5508. 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)) {
  5509. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  5510. return;
  5511. }
  5512. while (result.Next()) {
  5513. int8 type = result.GetInt8(0);
  5514. int8 subtype = result.GetInt8(1);
  5515. HistoryData* hd = new HistoryData;
  5516. hd->Value = result.GetInt32(2);
  5517. hd->Value2 = result.GetInt32(3);
  5518. strcpy(hd->Location, result.GetString(4));
  5519. // skipped event id as use for it has not been determined yet
  5520. hd->EventDate = result.GetInt32(6);
  5521. player->LoadPlayerHistory(type, subtype, hd);
  5522. }
  5523. }
  5524. void WorldDatabase::LoadSpellErrors() {
  5525. Query query;
  5526. MYSQL_ROW row;
  5527. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `version`, `error_index`, `value` FROM `spell_error_versions`");
  5528. if (result && mysql_num_rows(result) > 0) {
  5529. while ((row = mysql_fetch_row(result))) {
  5530. master_spell_list.AddSpellError(atoi(row[0]), atoi(row[1]), atoi(row[2]));
  5531. }
  5532. }
  5533. }
  5534. void WorldDatabase::SaveCharacterHistory(Player* player, int8 type, int8 subtype, int32 value, int32 value2, char* location, int32 event_date) {
  5535. string str_type;
  5536. string str_subtype;
  5537. switch (type) {
  5538. case HISTORY_TYPE_NONE:
  5539. str_type = "None";
  5540. break;
  5541. case HISTORY_TYPE_DEATH:
  5542. str_type = "Death";
  5543. break;
  5544. case HISTORY_TYPE_DISCOVERY:
  5545. str_type = "Discovery";
  5546. break;
  5547. case HISTORY_TYPE_XP:
  5548. str_type = "XP";
  5549. break;
  5550. default:
  5551. LogWrite(PLAYER__ERROR, 0, "Player", "WorldDatabase::SaveCharacterHistory() - Invalid history type given (%i) with subtype (%i), character history NOT saved.", type, subtype);
  5552. return;
  5553. }
  5554. switch (subtype) {
  5555. case HISTORY_SUBTYPE_NONE:
  5556. str_subtype = "None";
  5557. break;
  5558. case HISTORY_SUBTYPE_ADVENTURE:
  5559. str_subtype = "Adventure";
  5560. break;
  5561. case HISTORY_SUBTYPE_TRADESKILL:
  5562. str_subtype = "Tradeskill";
  5563. break;
  5564. case HISTORY_SUBTYPE_QUEST:
  5565. str_subtype = "Quest";
  5566. break;
  5567. case HISTORY_SUBTYPE_AA:
  5568. str_subtype = "AA";
  5569. break;
  5570. case HISTORY_SUBTYPE_ITEM:
  5571. str_subtype = "Item";
  5572. break;
  5573. case HISTORY_SUBTYPE_LOCATION:
  5574. str_subtype = "Location";
  5575. break;
  5576. default:
  5577. LogWrite(PLAYER__ERROR, 0, "Player", "WorldDatabase::SaveCharacterHistory() - Invalid history sub type given (%i) with type (%i), character history NOT saved.", subtype, type);
  5578. return;
  5579. }
  5580. 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);
  5581. Query query;
  5582. 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)",
  5583. player->GetCharacterID(), str_type.c_str(), str_subtype.c_str(), value, value2, location, event_date);
  5584. }
  5585. void WorldDatabase::LoadTransportMaps(ZoneServer* zone) {
  5586. int32 total = 0;
  5587. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "-Loading Transporter Maps...");
  5588. zone->DeleteTransporterMaps();
  5589. Query query;
  5590. MYSQL_ROW row;
  5591. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `transport_id`, `map_name` FROM `transport_maps`");
  5592. if(result) {
  5593. while(result && (row = mysql_fetch_row(result))){
  5594. zone->AddTransportMap(atoul(row[0]), string(row[1]));
  5595. total++;
  5596. }
  5597. }
  5598. LogWrite(TRANSPORT__DEBUG, 0, "Transport", "--Loaded %i Transporter Maps", total);
  5599. }
  5600. bool WorldDatabase::LoadSign(ZoneServer* zone, int32 spawn_id) {
  5601. Sign* sign = 0;
  5602. int32 id = 0;
  5603. DatabaseResult result;
  5604. 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, s.loot_tier, s.loot_drop_type, ss.language\n"
  5605. "FROM spawn s\n"
  5606. "INNER JOIN spawn_signs ss\n"
  5607. "ON ss.spawn_id = s.id\n"
  5608. "WHERE s.id = %u\n",
  5609. spawn_id);
  5610. if (result.GetNumRows() > 0 && result.Next()) {
  5611. id = result.GetInt32(0);
  5612. sign = new Sign();
  5613. sign->SetDatabaseID(id);
  5614. strcpy(sign->appearance.name, result.GetString(1));
  5615. sign->appearance.model_type = result.GetInt16(2);
  5616. sign->SetSize(result.GetInt16(3));
  5617. sign->appearance.show_command_icon = result.GetInt8(4);
  5618. sign->SetWidgetID(result.GetInt32(5));
  5619. sign->SetWidgetX(result.GetFloat(6));
  5620. sign->SetWidgetY(result.GetFloat(7));
  5621. sign->SetWidgetZ(result.GetFloat(8));
  5622. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5623. if(primary_command_list){
  5624. sign->SetPrimaryCommands(primary_command_list);
  5625. sign->primary_command_list_id = result.GetInt32(9);
  5626. }
  5627. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5628. if (secondary_command_list) {
  5629. sign->SetSecondaryCommands(secondary_command_list);
  5630. sign->secondary_command_list_id = result.GetInt32(10);
  5631. }
  5632. sign->appearance.pos.collision_radius = result.GetInt16(11);
  5633. sign->SetSignIcon(result.GetInt8(12));
  5634. if(strncasecmp(result.GetString(13), "Generic", 7) == 0)
  5635. sign->SetSignType(SIGN_TYPE_GENERIC);
  5636. else if(strncasecmp(result.GetString(13), "Zone", 4) == 0)
  5637. sign->SetSignType(SIGN_TYPE_ZONE);
  5638. sign->SetSignTitle(result.GetString(14));
  5639. sign->SetSignDescription(result.GetString(15));
  5640. sign->SetSignDistance(result.GetFloat(16));
  5641. sign->SetSignZoneID(result.GetInt32(17));
  5642. sign->SetSignZoneX(result.GetFloat(18));
  5643. sign->SetSignZoneY(result.GetFloat(19));
  5644. sign->SetSignZoneZ(result.GetFloat(20));
  5645. sign->SetSignZoneHeading(result.GetFloat(21));
  5646. sign->SetIncludeHeading(result.GetInt8(22) == 1);
  5647. sign->SetIncludeLocation(result.GetInt8(23) == 1);
  5648. sign->SetTransporterID(result.GetInt32(24));
  5649. sign->SetSizeOffset(result.GetInt8(25));
  5650. sign->appearance.display_hand_icon = result.GetInt8(26);
  5651. sign->SetVisualState(result.GetInt16(27));
  5652. sign->SetSoundsDisabled(result.GetInt8(28));
  5653. sign->SetMerchantLevelRange(result.GetInt32(29), result.GetInt32(30));
  5654. sign->SetAAXPRewards(result.GetInt32(31));
  5655. sign->SetLootTier(result.GetInt32(32));
  5656. sign->SetLootDropType(result.GetInt32(33));
  5657. sign->SetLanguage(result.GetInt8(34));
  5658. zone->AddSign(id, sign);
  5659. LogWrite(SIGN__DEBUG, 0, "Sign", "Loaded Sign: '%s' (%u).", sign->appearance.name, spawn_id);
  5660. return true;
  5661. }
  5662. LogWrite(SIGN__DEBUG, 0, "Sign", "Unable to find a sign for spawn id of %u", spawn_id);
  5663. return false;
  5664. }
  5665. bool WorldDatabase::LoadWidget(ZoneServer* zone, int32 spawn_id) {
  5666. Widget* widget = 0;
  5667. int32 id = 0;
  5668. DatabaseResult result;
  5669. 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, s.loot_tier, s.loot_drop_type\n"
  5670. "FROM spawn s\n"
  5671. "INNER JOIN spawn_widgets sw\n"
  5672. "ON sw.spawn_id = s.id\n"
  5673. "WHERE s.id = %u",
  5674. spawn_id);
  5675. if (result.GetNumRows() > 0 && result.Next()) {
  5676. id = result.GetInt32(0);
  5677. widget = new Widget();
  5678. widget->SetDatabaseID(id);
  5679. strcpy(widget->appearance.name, result.GetString(1));
  5680. widget->appearance.model_type = result.GetInt16(2);
  5681. widget->SetSize(result.GetInt16(3));
  5682. widget->appearance.show_command_icon = result.GetInt8(4);
  5683. widget->SetWidgetID(result.GetInt32(5));
  5684. widget->SetWidgetX(result.GetFloat(6));
  5685. widget->SetWidgetY(result.GetFloat(7));
  5686. widget->SetWidgetZ(result.GetFloat(8));
  5687. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5688. if(primary_command_list){
  5689. widget->SetPrimaryCommands(primary_command_list);
  5690. widget->primary_command_list_id = result.GetInt32(9);
  5691. }
  5692. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5693. if (secondary_command_list) {
  5694. widget->SetSecondaryCommands(secondary_command_list);
  5695. widget->secondary_command_list_id = result.GetInt32(10);
  5696. }
  5697. widget->appearance.pos.collision_radius = result.GetInt16(11);
  5698. widget->SetIncludeHeading(result.GetInt8(12) == 1);
  5699. widget->SetIncludeLocation(result.GetInt8(13) == 1);
  5700. widget->SetWidgetIcon(result.GetInt8(14));
  5701. if(strncasecmp(result.GetString(15),"Generic", 7) == 0)
  5702. widget->SetWidgetType(WIDGET_TYPE_GENERIC);
  5703. else if(strncasecmp(result.GetString(15),"Door", 4) == 0)
  5704. widget->SetWidgetType(WIDGET_TYPE_DOOR);
  5705. widget->SetOpenHeading(result.GetFloat(16));
  5706. widget->SetOpenY(result.GetFloat(17));
  5707. widget->SetActionSpawnID(result.GetInt32(18));
  5708. if(!result.IsNull(19) && strlen(result.GetString(19)) > 5)
  5709. widget->SetOpenSound(result.GetString(19));
  5710. if(!result.IsNull(20) && strlen(result.GetString(20)) > 5)
  5711. widget->SetCloseSound(result.GetString(20));
  5712. widget->SetOpenDuration(result.GetInt16(21));
  5713. widget->SetClosedHeading(result.GetFloat(22));
  5714. widget->SetLinkedSpawnID(result.GetInt32(23));
  5715. widget->SetCloseY(result.GetFloat(24));
  5716. widget->SetTransporterID(result.GetInt32(25));
  5717. widget->SetSizeOffset(result.GetInt8(26));
  5718. widget->SetHouseID(result.GetInt32(27));
  5719. widget->SetOpenX(result.GetFloat(28));
  5720. widget->SetOpenZ(result.GetFloat(29));
  5721. widget->SetCloseX(result.GetFloat(30));
  5722. widget->SetCloseZ(result.GetFloat(31));
  5723. widget->appearance.display_hand_icon = result.GetInt8(32);
  5724. widget->SetSoundsDisabled(result.GetInt8(33));
  5725. widget->SetMerchantLevelRange(result.GetInt32(34), result.GetInt32(35));
  5726. widget->SetAAXPRewards(result.GetInt32(36));
  5727. widget->SetLootTier(result.GetInt32(37));
  5728. widget->SetLootDropType(result.GetInt32(38));
  5729. zone->AddWidget(id, widget);
  5730. LogWrite(WIDGET__DEBUG, 0, "Widget", "Loaded Widget: '%s' (%u).", widget->appearance.name, spawn_id);
  5731. return true;
  5732. }
  5733. LogWrite(WIDGET__DEBUG, 0, "Widget", "Unable to find a widget for spawn id of %u", spawn_id);
  5734. return false;
  5735. }
  5736. bool WorldDatabase::LoadObject(ZoneServer* zone, int32 spawn_id) {
  5737. Object* object = 0;
  5738. int32 id = 0;
  5739. DatabaseResult result;
  5740. 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, s.loot_tier, s.loot_drop_type\n"
  5741. "FROM spawn s\n"
  5742. "INNER JOIN spawn_objects so\n"
  5743. "ON so.spawn_id = s.id\n"
  5744. "WHERE s.id = %u",
  5745. spawn_id);
  5746. if (result.GetNumRows() > 0 && result.Next()) {
  5747. id = result.GetInt32(0);
  5748. object = new Object();
  5749. object->SetDatabaseID(id);
  5750. strcpy(object->appearance.name, result.GetString(1));
  5751. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(4));
  5752. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(5));
  5753. if(primary_command_list){
  5754. object->SetPrimaryCommands(primary_command_list);
  5755. object->primary_command_list_id = result.GetInt32(4);
  5756. }
  5757. if(secondary_command_list){
  5758. object->SetSecondaryCommands(secondary_command_list);
  5759. object->secondary_command_list_id = result.GetInt32(5);
  5760. }
  5761. object->appearance.race = result.GetInt8(2);
  5762. object->appearance.model_type = result.GetInt16(3);
  5763. object->appearance.targetable = result.GetInt8(6);
  5764. object->size = result.GetInt16(7);
  5765. object->appearance.display_name = result.GetInt8(8);
  5766. object->appearance.visual_state = result.GetInt16(9);
  5767. object->appearance.attackable = result.GetInt8(10);
  5768. object->appearance.show_level = result.GetInt8(11);
  5769. object->appearance.show_command_icon = result.GetInt8(12);
  5770. object->appearance.display_hand_icon = result.GetInt8(13);
  5771. object->faction_id = result.GetInt32(14);
  5772. object->appearance.pos.collision_radius = result.GetInt16(15);
  5773. object->SetTransporterID(result.GetInt32(16));
  5774. object->SetSizeOffset(result.GetInt8(17));
  5775. object->SetDeviceID(result.GetInt8(18));
  5776. object->SetSoundsDisabled(result.GetInt8(19));
  5777. object->SetMerchantLevelRange(result.GetInt32(20), result.GetInt32(21));
  5778. object->SetAAXPRewards(result.GetInt32(22));
  5779. object->SetLootTier(result.GetInt32(23));
  5780. object->SetLootDropType(result.GetInt32(24));
  5781. zone->AddObject(id, object);
  5782. LogWrite(OBJECT__DEBUG, 0, "Object", "Loaded Object: '%s' (%u).", object->appearance.name, spawn_id);
  5783. return true;
  5784. }
  5785. LogWrite(OBJECT__DEBUG, 0, "Object", "Unable to find an object for spawn id of %u", spawn_id);
  5786. return false;
  5787. }
  5788. bool WorldDatabase::LoadGroundSpawn(ZoneServer* zone, int32 spawn_id) {
  5789. GroundSpawn* spawn = 0;
  5790. int32 id = 0;
  5791. DatabaseResult result;
  5792. 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, s.disable_sounds, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, sg.randomize_heading\n"
  5793. "FROM spawn s\n"
  5794. "INNER JOIN spawn_ground sg\n"
  5795. "ON sg.spawn_id = s.id\n"
  5796. "WHERE s.id = %u",
  5797. spawn_id);
  5798. if (result.GetNumRows() > 0 && result.Next()) {
  5799. id = result.GetInt32(0);
  5800. spawn = new GroundSpawn();
  5801. spawn->SetDatabaseID(id);
  5802. strcpy(spawn->appearance.name, result.GetString(1));
  5803. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(4));
  5804. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(5));
  5805. if(primary_command_list){
  5806. spawn->SetPrimaryCommands(primary_command_list);
  5807. spawn->primary_command_list_id = result.GetInt32(4);
  5808. }
  5809. if(secondary_command_list){
  5810. spawn->SetSecondaryCommands(secondary_command_list);
  5811. spawn->secondary_command_list_id = result.GetInt32(5);
  5812. }
  5813. spawn->appearance.race = result.GetInt8(2);
  5814. spawn->appearance.model_type = result.GetInt16(3);
  5815. spawn->appearance.targetable = result.GetInt8(6);
  5816. spawn->size = result.GetInt16(7);
  5817. spawn->appearance.display_name = result.GetInt8(8);
  5818. spawn->appearance.visual_state = result.GetInt16(9);
  5819. spawn->appearance.attackable = result.GetInt8(10);
  5820. spawn->appearance.show_level = result.GetInt8(11);
  5821. spawn->appearance.show_command_icon = result.GetInt8(12);
  5822. spawn->appearance.display_hand_icon = result.GetInt8(13);
  5823. spawn->faction_id = result.GetInt32(14);
  5824. spawn->appearance.pos.collision_radius = result.GetInt16(15);
  5825. spawn->SetNumberHarvests(result.GetInt8(16));
  5826. spawn->SetAttemptsPerHarvest(result.GetInt8(17));
  5827. spawn->SetGroundSpawnEntryID(result.GetInt32(18));
  5828. spawn->SetCollectionSkill(result.GetString(19));
  5829. spawn->SetSizeOffset(result.GetInt8(20));
  5830. spawn->SetSoundsDisabled(result.GetInt8(21));
  5831. spawn->SetAAXPRewards(result.GetInt32(22));
  5832. spawn->SetLootTier(result.GetInt32(23));
  5833. spawn->SetLootDropType(result.GetInt32(24));
  5834. spawn->SetRandomizeHeading(result.GetInt32(25));
  5835. zone->AddGroundSpawn(id, spawn);
  5836. if (!zone->GetGroundSpawnEntries(spawn->GetGroundSpawnEntryID()))
  5837. LoadGroundSpawnEntry(zone, spawn->GetGroundSpawnEntryID());
  5838. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "Loaded Ground Spawn: '%s' (%u).", spawn->appearance.name, spawn_id);
  5839. return true;
  5840. }
  5841. LogWrite(GROUNDSPAWN__DEBUG, 0, "GSpawn", "Unable to find a ground spawn for spawn id of %u", spawn_id);
  5842. return false;
  5843. }
  5844. void WorldDatabase::LoadGroundSpawnItems(ZoneServer* zone, int32 entry_id) {
  5845. DatabaseResult result;
  5846. database_new.Select(&result, "SELECT item_id, is_rare, grid_id\n"
  5847. "FROM groundspawn_items\n"
  5848. "WHERE groundspawn_id = %u",
  5849. entry_id);
  5850. if (result.GetNumRows() > 0 && result.Next()) {
  5851. zone->AddGroundSpawnItem(entry_id, result.GetInt32(0), result.GetInt8(1), result.GetInt32(2));
  5852. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn Items: ID: %u\n", entry_id);
  5853. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---item: %ul, rare: %i, grid: %ul", result.GetInt32(0), result.GetInt8(1), result.GetInt32(2));
  5854. }
  5855. }
  5856. void WorldDatabase::LoadGroundSpawnEntry(ZoneServer* zone, int32 entry_id) {
  5857. DatabaseResult result;
  5858. database_new.Select(&result, "SELECT min_skill_level, min_adventure_level, bonus_table, harvest1, harvest3, harvest5, harvest_imbue, harvest_rare, harvest10, harvest_coin\n"
  5859. "FROM groundspawns\n"
  5860. "WHERE enabled = 1 AND groundspawn_id = %u",
  5861. entry_id);
  5862. if (result.GetNumRows() > 0 && result.Next()) {
  5863. // this is getting ridonkulous...
  5864. LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn ID: %u\n" \
  5865. "---min_skill_level: %i, min_adventure_level: %i, bonus_table: %i\n" \
  5866. "---harvest1: %.2f, harvest3: %.2f, harvest5: %.2f\n" \
  5867. "---harvest_imbue: %.2f, harvest_rare: %.2f, harvest10: %.2f\n" \
  5868. "---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));
  5869. 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));
  5870. LoadGroundSpawnItems(zone, entry_id);
  5871. }
  5872. }
  5873. bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
  5874. NPC* npc = nullptr;
  5875. int32 id = 0;
  5876. DatabaseResult result;
  5877. 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, s.loot_tier, s.loot_drop_type\n"
  5878. "FROM spawn s\n"
  5879. "INNER JOIN spawn_npcs npc\n"
  5880. "ON npc.spawn_id = s.id\n"
  5881. "WHERE s.id = %u",
  5882. spawn_id);
  5883. if (result.GetNumRows() > 0 && result.Next()) {
  5884. id = result.GetInt32(0);
  5885. npc = new NPC();
  5886. npc->SetDatabaseID(id);
  5887. strcpy(npc->appearance.name, result.GetString(1));
  5888. vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
  5889. vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
  5890. if(primary_command_list){
  5891. npc->SetPrimaryCommands(primary_command_list);
  5892. npc->primary_command_list_id = result.GetInt32(9);
  5893. }
  5894. if(secondary_command_list){
  5895. npc->SetSecondaryCommands(secondary_command_list);
  5896. npc->secondary_command_list_id = result.GetInt32(10);
  5897. }
  5898. npc->appearance.min_level = result.GetInt8(2);
  5899. npc->appearance.max_level = result.GetInt8(3);
  5900. npc->appearance.level = result.GetInt8(2);
  5901. npc->appearance.encounter_level = result.GetInt8(4);
  5902. npc->appearance.race = result.GetInt8(5);
  5903. npc->appearance.model_type = result.GetInt16(6);
  5904. npc->appearance.soga_model_type = result.GetInt16(62);
  5905. npc->appearance.adventure_class = result.GetInt8(7);
  5906. npc->appearance.gender = result.GetInt8(8);
  5907. npc->appearance.display_name = result.GetInt8(11);
  5908. npc->features.hair_type = result.GetInt16(14);
  5909. npc->features.hair_face_type = result.GetInt16(15);
  5910. npc->features.wing_type = result.GetInt16(16);
  5911. npc->features.chest_type = result.GetInt16(17);
  5912. npc->features.legs_type = result.GetInt16(18);
  5913. npc->features.soga_hair_type = result.GetInt16(19);
  5914. npc->features.soga_hair_face_type = result.GetInt16(20);
  5915. npc->appearance.attackable = result.GetInt8(21);
  5916. npc->appearance.show_level = result.GetInt8(22);
  5917. npc->appearance.targetable = result.GetInt8(23);
  5918. npc->appearance.show_command_icon = result.GetInt8(24);
  5919. npc->appearance.display_hand_icon = result.GetInt8(25);
  5920. npc->appearance.hide_hood = result.GetInt8(70);
  5921. npc->appearance.randomize = result.GetInt32(61);
  5922. npc->SetTotalHP(result.GetInt32(26));
  5923. npc->SetTotalPower(result.GetInt32(27));
  5924. npc->SetHP(npc->GetTotalHP());
  5925. npc->SetPower(npc->GetTotalPower());
  5926. if(npc->GetTotalHP() == 0){
  5927. npc->SetTotalHP(15*npc->GetLevel() + 1);
  5928. npc->SetHP(15*npc->GetLevel() + 1);
  5929. }
  5930. if(npc->GetTotalPower() == 0){
  5931. npc->SetTotalPower(15*npc->GetLevel() + 1);
  5932. npc->SetPower(15*npc->GetLevel() + 1);
  5933. }
  5934. npc->size = result.GetInt16(28);
  5935. npc->appearance.pos.collision_radius = result.GetInt16(29);
  5936. npc->appearance.action_state = result.GetInt16(30);
  5937. npc->appearance.visual_state = result.GetInt16(31);
  5938. npc->appearance.mood_state = result.GetInt16(32);
  5939. npc->appearance.emote_state = result.GetInt16(71);
  5940. npc->appearance.pos.state = result.GetInt16(33);
  5941. npc->appearance.activity_status = result.GetInt16(34);
  5942. npc->faction_id = result.GetInt32(35);
  5943. if(!result.IsNull(36)){
  5944. std::string sub_title = std::string(result.GetString(36));
  5945. if(sub_title.find("Collector") != std::string::npos) {
  5946. npc->SetCollector(true);
  5947. }
  5948. if(strlen(result.GetString(36)) < sizeof(npc->appearance.sub_title))
  5949. strcpy(npc->appearance.sub_title, result.GetString(36));
  5950. else
  5951. strncpy(npc->appearance.sub_title, result.GetString(36), sizeof(npc->appearance.sub_title));
  5952. }
  5953. npc->SetMerchantID(result.GetInt32(37));
  5954. npc->SetMerchantType(result.GetInt8(38));
  5955. npc->SetSizeOffset(result.GetInt8(39));
  5956. npc->SetAIStrategy(result.GetInt8(41));
  5957. npc->SetPrimarySpellList(result.GetInt32(42));
  5958. npc->SetSecondarySpellList(result.GetInt32(43));
  5959. npc->SetPrimarySkillList(result.GetInt32(44));
  5960. npc->SetSecondarySkillList(result.GetInt32(45));
  5961. npc->SetEquipmentListID(result.GetInt32(46));
  5962. InfoStruct* info = npc->GetInfoStruct();
  5963. info->set_attack_type(result.GetInt8(40));
  5964. info->set_str_base(result.GetInt16(47));
  5965. info->set_sta_base(result.GetInt16(48));
  5966. info->set_wis_base(result.GetInt16(49));
  5967. info->set_intel_base(result.GetInt16(50));
  5968. info->set_agi_base(result.GetInt16(51));
  5969. info->set_heat_base(result.GetInt16(52));
  5970. info->set_cold_base(result.GetInt16(53));
  5971. info->set_magic_base(result.GetInt16(54));
  5972. info->set_mental_base(result.GetInt16(55));
  5973. info->set_divine_base(result.GetInt16(56));
  5974. info->set_disease_base(result.GetInt16(57));
  5975. info->set_poison_base(result.GetInt16(58));
  5976. info->set_alignment(result.GetSInt8(64));
  5977. npc->SetAggroRadius(result.GetFloat(59));
  5978. npc->SetCastPercentage(result.GetInt8(60));
  5979. npc->appearance.heroic_flag = result.GetInt8(63);
  5980. info->set_elemental_base(result.GetInt16(65));
  5981. info->set_arcane_base(result.GetInt16(66));
  5982. info->set_noxious_base(result.GetInt16(67));
  5983. npc->SetTotalSavagery(result.GetInt32(68));
  5984. npc->SetTotalDissonance(result.GetInt32(69));
  5985. npc->SetSavagery(npc->GetTotalSavagery());
  5986. npc->SetDissonance(npc->GetTotalDissonance());
  5987. if(npc->GetTotalSavagery() == 0){
  5988. npc->SetTotalSavagery(15*npc->GetLevel() + 1);
  5989. npc->SetSavagery(15*npc->GetLevel() + 1);
  5990. }
  5991. if(npc->GetTotalDissonance() == 0){
  5992. npc->SetTotalDissonance(15*npc->GetLevel() + 1);
  5993. npc->SetDissonance(15*npc->GetLevel() + 1);
  5994. }
  5995. npc->SetPrefixTitle(result.GetString(72));
  5996. npc->SetSuffixTitle(result.GetString(73));
  5997. npc->SetLastName(result.GetString(74));
  5998. npc->SetSoundsDisabled(result.GetInt8(75));
  5999. npc->SetMerchantLevelRange(result.GetInt32(76), result.GetInt32(77));
  6000. npc->SetAAXPRewards(result.GetInt32(78));
  6001. npc->SetLootTier(result.GetInt32(79));
  6002. npc->SetLootDropType(result.GetInt32(80));
  6003. zone->AddNPC(id, npc);
  6004. //skipped spells/skills/equipment as it is all loaded, the following rely on a spawn to load
  6005. LoadAppearance(zone, spawn_id);
  6006. LoadNPCAppearanceEquipmentData(zone, spawn_id);
  6007. LogWrite(NPC__DEBUG, 0, "NPC", "Loaded NPC: '%s' (%u).", npc->appearance.name, spawn_id);
  6008. return true;
  6009. }
  6010. LogWrite(NPC__DEBUG, 0, "NPC", "Unable to find a npc for spawn id of %u", spawn_id);
  6011. return false;
  6012. }
  6013. void WorldDatabase::LoadAppearance(ZoneServer* zone, int32 spawn_id) {
  6014. Entity* entity = zone->GetNPC(spawn_id);
  6015. if (!entity)
  6016. return;
  6017. DatabaseResult result, result2;
  6018. map<string, int8> appearance_types;
  6019. map<int32, map<int8, EQ2_Color> > appearance_colors;
  6020. EQ2_Color color;
  6021. color.red = 0;
  6022. color.green = 0;
  6023. color.blue = 0;
  6024. string type;
  6025. database_new.Select(&result2, "SELECT distinct `type`\n"
  6026. "FROM npc_appearance\n"
  6027. "WHERE length(`type`) > 0 AND `spawn_id` = %u",
  6028. spawn_id);
  6029. while(result2.Next()) {
  6030. type = string(result2.GetString(0));
  6031. appearance_types[type] = GetAppearanceType(type);
  6032. if(appearance_types[type] == 255)
  6033. LogWrite(WORLD__ERROR, 0, "Appearance", "Unknown appearance type '%s' in LoadAppearances.", type.c_str());
  6034. }
  6035. database_new.Select(&result, "SELECT `type`, `signed_value`, `red`, `green`, `blue`\n"
  6036. "FROM npc_appearance\n"
  6037. "WHERE length(`type`) > 0 AND `spawn_id` = %u",
  6038. spawn_id);
  6039. while(result.Next()) {
  6040. if(appearance_types[result.GetString(0)] < APPEARANCE_SOGA_EBT){
  6041. color.red = result.GetInt8(2);
  6042. color.green = result.GetInt8(3);
  6043. color.blue = result.GetInt8(4);
  6044. }
  6045. switch(appearance_types[result.GetString(0)]){
  6046. case APPEARANCE_SOGA_HFHC:{
  6047. entity->features.soga_hair_face_highlight_color = color;
  6048. break;
  6049. }
  6050. case APPEARANCE_SOGA_HTHC:{
  6051. entity->features.soga_hair_type_highlight_color = color;
  6052. break;
  6053. }
  6054. case APPEARANCE_SOGA_HFC:{
  6055. entity->features.soga_hair_face_color = color;
  6056. break;
  6057. }
  6058. case APPEARANCE_SOGA_HTC:{
  6059. entity->features.soga_hair_type_color = color;
  6060. break;
  6061. }
  6062. case APPEARANCE_SOGA_HH:{
  6063. entity->features.soga_hair_highlight_color = color;
  6064. break;
  6065. }
  6066. case APPEARANCE_SOGA_HC1:{
  6067. entity->features.soga_hair_color1 = color;
  6068. break;
  6069. }
  6070. case APPEARANCE_SOGA_HC2:{
  6071. entity->features.soga_hair_color2 = color;
  6072. break;
  6073. }
  6074. case APPEARANCE_SOGA_SC:{
  6075. entity->features.soga_skin_color = color;
  6076. break;
  6077. }
  6078. case APPEARANCE_SOGA_EC:{
  6079. entity->features.soga_eye_color = color;
  6080. break;
  6081. }
  6082. case APPEARANCE_HTHC:{
  6083. entity->features.hair_type_highlight_color = color;
  6084. break;
  6085. }
  6086. case APPEARANCE_HFHC:{
  6087. entity->features.hair_face_highlight_color = color;
  6088. break;
  6089. }
  6090. case APPEARANCE_HTC:{
  6091. entity->features.hair_type_color = color;
  6092. break;
  6093. }
  6094. case APPEARANCE_HFC:{
  6095. entity->features.hair_face_color = color;
  6096. break;
  6097. }
  6098. case APPEARANCE_HH:{
  6099. entity->features.hair_highlight_color = color;
  6100. break;
  6101. }
  6102. case APPEARANCE_HC1:{
  6103. entity->features.hair_color1 = color;
  6104. break;
  6105. }
  6106. case APPEARANCE_HC2:{
  6107. entity->features.hair_color2 = color;
  6108. break;
  6109. }
  6110. case APPEARANCE_WC1:{
  6111. entity->features.wing_color1 = color;
  6112. break;
  6113. }
  6114. case APPEARANCE_WC2:{
  6115. entity->features.wing_color2 = color;
  6116. break;
  6117. }
  6118. case APPEARANCE_SC:{
  6119. entity->features.skin_color = color;
  6120. break;
  6121. }
  6122. case APPEARANCE_EC:{
  6123. entity->features.eye_color = color;
  6124. break;
  6125. }
  6126. case APPEARANCE_SOGA_EBT:{
  6127. for(int i=0;i<3;i++)
  6128. entity->features.soga_eye_brow_type[i] = result.GetInt8(2+i);
  6129. break;
  6130. }
  6131. case APPEARANCE_SOGA_CHEEKT:{
  6132. for(int i=0;i<3;i++)
  6133. entity->features.soga_cheek_type[i] = result.GetInt8(2+i);
  6134. break;
  6135. }
  6136. case APPEARANCE_SOGA_NT:{
  6137. for(int i=0;i<3;i++)
  6138. entity->features.soga_nose_type[i] = result.GetInt8(2+i);
  6139. break;
  6140. }
  6141. case APPEARANCE_SOGA_CHINT:{
  6142. for(int i=0;i<3;i++)
  6143. entity->features.soga_chin_type[i] = result.GetInt8(2+i);
  6144. break;
  6145. }
  6146. case APPEARANCE_SOGA_LT:{
  6147. for(int i=0;i<3;i++)
  6148. entity->features.soga_lip_type[i] = result.GetInt8(2+i);
  6149. break;
  6150. }
  6151. case APPEARANCE_SOGA_EART:{
  6152. for(int i=0;i<3;i++)
  6153. entity->features.soga_ear_type[i] = result.GetInt8(2+i);
  6154. break;
  6155. }
  6156. case APPEARANCE_SOGA_EYET:{
  6157. for(int i=0;i<3;i++)
  6158. entity->features.soga_eye_type[i] = result.GetInt8(2+i);
  6159. break;
  6160. }
  6161. case APPEARANCE_EBT:{
  6162. for(int i=0;i<3;i++)
  6163. entity->features.eye_brow_type[i] = result.GetInt8(2+i);
  6164. break;
  6165. }
  6166. case APPEARANCE_CHEEKT:{
  6167. for(int i=0;i<3;i++)
  6168. entity->features.cheek_type[i] = result.GetInt8(2+i);
  6169. break;
  6170. }
  6171. case APPEARANCE_NT:{
  6172. for(int i=0;i<3;i++)
  6173. entity->features.nose_type[i] = result.GetInt8(2+i);
  6174. break;
  6175. }
  6176. case APPEARANCE_CHINT:{
  6177. for(int i=0;i<3;i++)
  6178. entity->features.chin_type[i] = result.GetInt8(2+i);
  6179. break;
  6180. }
  6181. case APPEARANCE_EART:{
  6182. for(int i=0;i<3;i++)
  6183. entity->features.ear_type[i] = result.GetInt8(2+i);
  6184. break;
  6185. }
  6186. case APPEARANCE_EYET:{
  6187. for(int i=0;i<3;i++)
  6188. entity->features.eye_type[i] = result.GetInt8(2+i);
  6189. break;
  6190. }
  6191. case APPEARANCE_LT:{
  6192. for(int i=0;i<3;i++)
  6193. entity->features.lip_type[i] = result.GetInt8(2+i);
  6194. break;
  6195. }
  6196. case APPEARANCE_SHIRT:{
  6197. entity->features.shirt_color = color;
  6198. break;
  6199. }
  6200. case APPEARANCE_UCC:{
  6201. break;
  6202. }
  6203. case APPEARANCE_PANTS:{
  6204. entity->features.pants_color = color;
  6205. break;
  6206. }
  6207. case APPEARANCE_ULC:{
  6208. break;
  6209. }
  6210. case APPEARANCE_U9:{
  6211. break;
  6212. }
  6213. case APPEARANCE_BODY_SIZE:{
  6214. entity->features.body_size = color.red;
  6215. break;
  6216. }
  6217. case APPEARANCE_SOGA_WC1:{
  6218. break;
  6219. }
  6220. case APPEARANCE_SOGA_WC2:{
  6221. break;
  6222. }
  6223. case APPEARANCE_SOGA_SHIRT:{
  6224. break;
  6225. }
  6226. case APPEARANCE_SOGA_UCC:{
  6227. break;
  6228. }
  6229. case APPEARANCE_SOGA_PANTS:{
  6230. break;
  6231. }
  6232. case APPEARANCE_SOGA_ULC:{
  6233. break;
  6234. }
  6235. case APPEARANCE_SOGA_U13:{
  6236. break;
  6237. }
  6238. case APPEARANCE_BODY_AGE: {
  6239. entity->features.body_age = color.red;
  6240. break;
  6241. }
  6242. case APPEARANCE_MC:{
  6243. entity->features.model_color = color;
  6244. break;
  6245. }
  6246. case APPEARANCE_SMC:{
  6247. entity->features.soga_model_color = color;
  6248. break;
  6249. }
  6250. case APPEARANCE_SBS: {
  6251. entity->features.soga_body_size = color.red;
  6252. break;
  6253. }
  6254. case APPEARANCE_SBA: {
  6255. entity->features.soga_body_age = color.red;
  6256. break;
  6257. }
  6258. }
  6259. }
  6260. entity->info_changed = true;
  6261. }
  6262. void WorldDatabase::LoadNPCAppearanceEquipmentData(ZoneServer* zone, int32 spawn_id) {
  6263. NPC* npc = zone->GetNPC(spawn_id);
  6264. if(!npc) {
  6265. LogWrite(NPC__ERROR, 0, "NPC", "Unable to get a valid npc (%u) in %s", spawn_id, __FUNCTION__);
  6266. return;
  6267. }
  6268. DatabaseResult result;
  6269. int8 slot = 0;
  6270. if (!database_new.Select(&result, "SELECT slot_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue\n"
  6271. "FROM npc_appearance_equip\n"
  6272. "WHERE spawn_id = %u\n",
  6273. spawn_id))
  6274. {
  6275. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6276. return;
  6277. }
  6278. while (result.Next()) {
  6279. slot = result.GetInt8(0);
  6280. if(slot < NUM_SLOTS) {
  6281. npc->SetEquipment(slot, result.GetInt16(1), result.GetInt8(2), result.GetInt8(3), result.GetInt8(4), result.GetInt8(5), result.GetInt8(6), result.GetInt8(7));
  6282. }
  6283. }
  6284. }
  6285. void WorldDatabase::SaveCharacterPicture(int32 characterID, int8 type, uchar* picture, int32 picture_size) {
  6286. stringstream ss_hex;
  6287. stringstream ss_query;
  6288. ss_hex.flags(ios::hex);
  6289. for (int32 i = 0; i < picture_size; i++)
  6290. ss_hex << setfill('0') << setw(2) << (int32)picture[i];
  6291. 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() << "'";
  6292. if (!database_new.Query(ss_query.str().c_str()))
  6293. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6294. }
  6295. void WorldDatabase::LoadZoneFlightPaths(ZoneServer* zone) {
  6296. DatabaseResult result;
  6297. int32 total = 0;
  6298. if (!database_new.Select(&result, "SELECT id, speed, flying, early_dismount FROM flight_paths WHERE zone_id = %u", zone->GetZoneID())) {
  6299. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6300. return;
  6301. }
  6302. while (result.Next()) {
  6303. FlightPathInfo* info = new FlightPathInfo;
  6304. int32 id = result.GetInt32(0);
  6305. info->speed = result.GetFloat(1);
  6306. info->flying = result.GetInt8(2) == 1 ? true : false;
  6307. info->dismount = result.GetInt8(3) == 1 ? true : false;
  6308. zone->AddFlightPath(id, info);
  6309. total++;
  6310. }
  6311. LogWrite(ZONE__DEBUG, 0, "Zone", "Loaded %u flight paths for %s", total, zone->GetZoneDescription());
  6312. LoadZoneFlightPathLocations(zone);
  6313. }
  6314. void WorldDatabase::LoadZoneFlightPathLocations(ZoneServer* zone) {
  6315. DatabaseResult result;
  6316. int32 total = 0;
  6317. if (!database_new.Select(&result, "SELECT loc.flight_path, loc.x, loc.y, loc.z FROM flight_paths_locations loc\n"
  6318. "INNER JOIN flight_paths path\n"
  6319. "ON loc.flight_path = path.id\n"
  6320. "WHERE path.zone_id = %u\n"
  6321. "ORDER BY loc.id",
  6322. zone->GetZoneID()))
  6323. {
  6324. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6325. return;
  6326. }
  6327. while (result.Next()) {
  6328. FlightPathLocation* loc = new FlightPathLocation;
  6329. int32 id = result.GetInt32(0);
  6330. loc->X = result.GetFloat(1);
  6331. loc->Y = result.GetFloat(2);
  6332. loc->Z = result.GetFloat(3);
  6333. zone->AddFlightPathLocation(id, loc);
  6334. total++;
  6335. }
  6336. LogWrite(ZONE__DEBUG, 0, "Zone", "Loaded %u flight path locations for %s", total, zone->GetZoneDescription());
  6337. }
  6338. void WorldDatabase::SaveCharacterLUAHistory(Player* player, int32 event_id, int32 value, int32 value2) {
  6339. Query query;
  6340. 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);
  6341. // 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))
  6342. // LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6343. }
  6344. void WorldDatabase::LoadCharacterLUAHistory(int32 char_id, Player* player) {
  6345. DatabaseResult result;
  6346. int32 total = 0;
  6347. if (!database_new.Select(&result, "SELECT event_id, value, value2 FROM character_lua_history WHERE char_id = %u", char_id)) {
  6348. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6349. return;
  6350. }
  6351. while (result.Next()) {
  6352. int32 id = result.GetInt32(0);
  6353. LUAHistory* hd = new LUAHistory;
  6354. hd->Value = result.GetInt32(1);
  6355. hd->Value2 = result.GetInt32(2);
  6356. hd->SaveNeeded = false;
  6357. player->LoadLUAHistory(id, hd);
  6358. total++;
  6359. }
  6360. LogWrite(PLAYER__DEBUG, 0, "Player", "Loaded %u LUA history for %s", total, player->GetName());
  6361. }
  6362. void WorldDatabase::FindSpell(Client* client, char* findString)
  6363. {
  6364. char* find_escaped = getEscapeString(findString);
  6365. DatabaseResult result;
  6366. if (!database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `tier` "
  6367. "FROM (spells s, spell_tiers st) "
  6368. "LEFT JOIN spell_ts_ability_index ts "
  6369. "ON s.`id` = ts.spell_id "
  6370. "WHERE s.id = st.spell_id and s.name like '%%%s%%' AND s.is_active = 1 "
  6371. "ORDER BY s.`id`, `tier` limit 50", find_escaped))
  6372. {
  6373. // error
  6374. }
  6375. else
  6376. {
  6377. client->Message(CHANNEL_COLOR_YELLOW, "SpellID (SpellTier): SpellName for %s", findString);
  6378. while (result.Next())
  6379. {
  6380. int32 spell_id = result.GetInt32Str("id");
  6381. string spell_name = result.GetStringStr("name");
  6382. int8 tier = result.GetInt8Str("tier");
  6383. client->Message(CHANNEL_COLOR_YELLOW, "%i (%i): %s", spell_id, tier, spell_name.c_str());
  6384. }
  6385. client->Message(CHANNEL_COLOR_YELLOW, "End Spell Results for %s", find_escaped);
  6386. }
  6387. safe_delete_array(find_escaped);
  6388. }
  6389. void WorldDatabase::LoadChestTraps() {
  6390. chest_trap_list.Clear();
  6391. int32 index = 0;
  6392. Query query;
  6393. MYSQL_ROW row;
  6394. 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");
  6395. if (result && mysql_num_rows(result) > 0) {
  6396. Title* title = 0;
  6397. while (result && (row = mysql_fetch_row(result))) {
  6398. int32 dbid = atoul(row[0]);
  6399. sint32 applicable_zone_id = atoi(row[1]);
  6400. int32 mindifficulty = atoul(row[2]);
  6401. int32 maxdifficulty = atoul(row[3]);
  6402. int32 spellid = atoul(row[4]);
  6403. int32 tier = atoul(row[5]);
  6404. ChestTrap* trap = new ChestTrap(dbid,applicable_zone_id,mindifficulty,maxdifficulty,spellid,tier);
  6405. chest_trap_list.AddChestTrap(trap);
  6406. }
  6407. }
  6408. }
  6409. bool WorldDatabase::CheckExpansionFlags(ZoneServer* zone, int32 spawnXpackFlag)
  6410. {
  6411. if (spawnXpackFlag == 0)
  6412. return true;
  6413. int32 globalXpackFlag = rule_manager.GetGlobalRule(R_Expansion, GlobalExpansionFlag)->GetInt32();
  6414. int32 zoneXpackFlag = zone->GetExpansionFlag();
  6415. // zone expansion flag takes priority
  6416. if (zoneXpackFlag > 0 && (spawnXpackFlag & zoneXpackFlag) == 0)
  6417. return false;
  6418. // zone expansion flag fails, then if global expansion flag set, we see if that bit operand doesn't match, skip mob then
  6419. else if (zoneXpackFlag == 0 && globalXpackFlag > 0 && (spawnXpackFlag & globalXpackFlag) == 0)
  6420. return false;
  6421. return true;
  6422. }
  6423. bool WorldDatabase::CheckHolidayFlags(ZoneServer* zone, int32 spawnHolidayFlag)
  6424. {
  6425. if (spawnHolidayFlag == 0)
  6426. return true;
  6427. int32 globalHolidayFlag = rule_manager.GetGlobalRule(R_Expansion, GlobalHolidayFlag)->GetInt32();
  6428. int32 zoneHolidayFlag = zone->GetHolidayFlag();
  6429. // zone holiday flag takes priority
  6430. if (zoneHolidayFlag > 0 && (spawnHolidayFlag & zoneHolidayFlag) == 0)
  6431. return false;
  6432. // zone holiday flag fails, then if global expansion flag set, we see if that bit operand doesn't match, skip mob then
  6433. else if (zoneHolidayFlag == 0 && globalHolidayFlag > 0 && (spawnHolidayFlag & globalHolidayFlag) == 0)
  6434. return false;
  6435. return true;
  6436. }
  6437. void WorldDatabase::GetHouseSpawnInstanceData(ZoneServer* zone, Spawn* spawn)
  6438. {
  6439. if (!spawn)
  6440. return;
  6441. if (zone->house_object_database_lookup.count(spawn->GetModelType()) < 1)
  6442. zone->house_object_database_lookup.Put(spawn->GetModelType(), spawn->GetDatabaseID());
  6443. DatabaseResult result;
  6444. database_new.Select(&result, "SELECT pickup_item_id, pickup_unique_item_id\n"
  6445. " FROM spawn_instance_data\n"
  6446. " WHERE spawn_id = %u and spawn_location_id = %u",
  6447. spawn->GetDatabaseID(),spawn->GetSpawnLocationID());
  6448. if (result.GetNumRows() > 0 && result.Next()) {
  6449. spawn->SetPickupItemID(result.GetInt32(0));
  6450. spawn->SetPickupUniqueItemID(result.GetInt32(1));
  6451. if (spawn->GetZone() != nullptr && spawn->GetMap() != nullptr && spawn->GetMap()->IsMapLoaded())
  6452. {
  6453. auto loc = glm::vec3(spawn->GetX(),spawn->GetZ(),spawn->GetY());
  6454. uint32 GridID = 0;
  6455. float new_z = spawn->GetMap()->FindBestZ(loc, nullptr, &GridID);
  6456. spawn->SetPos(&(spawn->appearance.pos.grid_id), GridID);
  6457. }
  6458. }
  6459. }
  6460. int32 WorldDatabase::FindHouseInstanceSpawn(Spawn* spawn)
  6461. {
  6462. DatabaseResult result;
  6463. database_new.Select(&result, "SELECT id\n"
  6464. " FROM spawn\n"
  6465. " WHERE model_type = %u and is_instanced_spawn=1 limit 1",
  6466. spawn->GetModelType());
  6467. if (result.GetNumRows() > 0 && result.Next()) {
  6468. return result.GetInt32(0);
  6469. }
  6470. return 0;
  6471. }
  6472. void WorldDatabase::LoadStartingSkills(World* world)
  6473. {
  6474. world->MStartingLists.writelock();
  6475. int32 total = 0;
  6476. Query query;
  6477. MYSQL_ROW row;
  6478. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_id, class_id, skill_id, current_val, max_val, progress FROM starting_skills");
  6479. if (result)
  6480. {
  6481. if (mysql_num_rows(result) > 0)
  6482. {
  6483. Skill* skill = 0;
  6484. while (result && (row = mysql_fetch_row(result)))
  6485. {
  6486. StartingSkill skill;
  6487. skill.header.race_id = atoul(row[0]);
  6488. skill.header.class_id = atoul(row[1]);
  6489. skill.skill_id = atoul(row[2]);
  6490. skill.current_val = atoul(row[3]);
  6491. skill.max_val = atoul(row[4]);
  6492. if (!world->starting_skills.count(skill.header.race_id))
  6493. {
  6494. multimap<int8, StartingSkill>* skills = new multimap<int8, StartingSkill>();
  6495. skills->insert(make_pair(skill.header.class_id, skill));
  6496. world->starting_skills.insert(make_pair(skill.header.race_id, skills));
  6497. }
  6498. else
  6499. {
  6500. multimap<int8, multimap<int8, StartingSkill>*>::const_iterator skills = world->starting_skills.find(skill.header.race_id);
  6501. skills->second->insert(make_pair(skill.header.class_id, skill));
  6502. }
  6503. total++;
  6504. }
  6505. }
  6506. }
  6507. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u Starting Skill(s)", total);
  6508. world->MStartingLists.releasewritelock();
  6509. }
  6510. void WorldDatabase::LoadVoiceOvers(World* world)
  6511. {
  6512. int32 total = 0;
  6513. Query query;
  6514. MYSQL_ROW row;
  6515. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type_id, id, indexed, mp3_string, text_string, emote_string, key1, key2, garbled, garble_link_id FROM voiceovers");
  6516. if (result)
  6517. {
  6518. if (mysql_num_rows(result) > 0)
  6519. {
  6520. Skill* skill = 0;
  6521. while (result && (row = mysql_fetch_row(result)))
  6522. {
  6523. VoiceOverStruct vos;
  6524. vos.mp3_string = std::string(row[3]);
  6525. vos.text_string = std::string(row[4]);
  6526. vos.emote_string = std::string(row[5]);
  6527. vos.key1 = atoul(row[6]);
  6528. vos.key2 = atoul(row[7]);
  6529. vos.is_garbled = atoul(row[8]);
  6530. vos.garble_link_id = atoul(row[9]);
  6531. int8 type = atoul(row[0]);
  6532. int32 id = atoul(row[1]);
  6533. int16 index = atoul(row[2]);
  6534. world->AddVoiceOver(type, id, index, &vos);
  6535. total++;
  6536. }
  6537. }
  6538. }
  6539. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u Voiceover(s)", total);
  6540. }
  6541. void WorldDatabase::LoadStartingSpells(World* world)
  6542. {
  6543. world->MStartingLists.writelock();
  6544. int32 total = 0;
  6545. Query query;
  6546. MYSQL_ROW row;
  6547. MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_id, class_id, spell_id, tier, knowledge_slot FROM starting_spells");
  6548. if (result)
  6549. {
  6550. if (mysql_num_rows(result) > 0)
  6551. {
  6552. Skill* skill = 0;
  6553. while (result && (row = mysql_fetch_row(result)))
  6554. {
  6555. StartingSpell spell;
  6556. spell.header.race_id = atoul(row[0]);
  6557. spell.header.class_id = atoul(row[1]);
  6558. spell.spell_id = atoul(row[2]);
  6559. spell.tier = atoul(row[3]);
  6560. spell.knowledge_slot = atoul(row[4]);
  6561. if (!world->starting_spells.count(spell.header.race_id))
  6562. {
  6563. multimap<int8, StartingSpell>* spells = new multimap<int8, StartingSpell>();
  6564. spells->insert(make_pair(spell.header.class_id, spell));
  6565. world->starting_spells.insert(make_pair(spell.header.race_id, spells));
  6566. }
  6567. else
  6568. {
  6569. multimap<int8, multimap<int8, StartingSpell>*>::iterator spells = world->starting_spells.find(spell.header.race_id);
  6570. spells->second->insert(make_pair(spell.header.class_id, spell));
  6571. }
  6572. total++;
  6573. }
  6574. }
  6575. }
  6576. LogWrite(WORLD__DEBUG, 3, "World", "--Loaded %u Starting Spell(s)", total);
  6577. world->MStartingLists.releasewritelock();
  6578. }
  6579. bool WorldDatabase::DeleteSpiritShard(int32 id){
  6580. Query query;
  6581. query.RunQuery2(Q_DELETE, "delete FROM character_spirit_shards where id=%u",id);
  6582. if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
  6583. LogWrite(WORLD__ERROR, 0, "World", "Error in DeleteSpiritShard query '%s': %s", query.GetQuery(), query.GetError());
  6584. return false;
  6585. }
  6586. return true;
  6587. }
  6588. int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race, int8 gender, int8 adventure_class,
  6589. int16 model_type, int16 soga_model_type, int16 hair_type, int16 hair_face_type, int16 wing_type,
  6590. int16 chest_type, int16 legs_type, int16 soga_hair_type, int16 soga_hair_face_type, int8 hide_hood,
  6591. int16 size, int16 collision_radius, int16 action_state, int16 visual_state, int16 mood_state, int16 emote_state,
  6592. int16 pos_state, int16 activity_status, char* sub_title, char* prefix_title, char* suffix_title, char* lastname,
  6593. float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid)
  6594. {
  6595. LogWrite(WORLD__INFO, 3, "World", "Saving Spirit Shard %s %u", name, charid);
  6596. Query query;
  6597. char* name_escaped = getEscapeString(name);
  6598. if(!sub_title)
  6599. sub_title = "";
  6600. char* subtitle_escaped = getEscapeString(sub_title);
  6601. char* prefix_escaped = getEscapeString(prefix_title);
  6602. char* suffix_escaped = getEscapeString(suffix_title);
  6603. char* lastname_escaped = getEscapeString(lastname);
  6604. 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)");
  6605. query.RunQuery2(Q_INSERT, insert.c_str(), name_escaped, level, race, gender, adventure_class, model_type, soga_model_type,
  6606. hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type,
  6607. soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state,
  6608. mood_state, emote_state, pos_state, activity_status, subtitle_escaped, prefix_escaped, suffix_escaped,
  6609. lastname_escaped, x, y, z, heading, gridid, charid, zoneid, instanceid);
  6610. safe_delete_array(name_escaped);
  6611. safe_delete_array(subtitle_escaped);
  6612. safe_delete_array(prefix_escaped);
  6613. safe_delete_array(suffix_escaped);
  6614. safe_delete_array(lastname_escaped);
  6615. return query.GetLastInsertedID();
  6616. }
  6617. void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type)
  6618. {
  6619. SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
  6620. Player* player = client->GetPlayer();
  6621. if(!spellProcess)
  6622. return;
  6623. DatabaseResult result;
  6624. multimap<LuaSpell*, Entity*> restoreSpells;
  6625. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  6626. 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)) {
  6627. LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
  6628. return;
  6629. }
  6630. InfoStruct* info = player->GetInfoStruct();
  6631. while (result.Next()) {
  6632. //result.GetInt8Str
  6633. char spell_name[60];
  6634. strncpy(spell_name, result.GetStringStr("name"), 60);
  6635. int32 caster_char_id = result.GetInt32Str("caster_char_id");
  6636. int32 target_char_id = result.GetInt32Str("target_char_id");
  6637. int8 target_type = result.GetInt8Str("target_type");
  6638. int32 spell_id = result.GetInt32Str("spell_id");
  6639. int32 effect_slot = result.GetInt32Str("effect_slot");
  6640. int32 slot_pos = result.GetInt32Str("slot_pos");
  6641. int16 icon = result.GetInt32Str("icon");
  6642. int16 icon_backdrop = result.GetInt32Str("icon_backdrop");
  6643. int8 conc_used = result.GetInt32Str("conc_used");
  6644. int8 tier = result.GetInt32Str("tier");
  6645. float total_time = result.GetFloatStr("total_time");
  6646. int32 expire_timestamp = result.GetInt32Str("expire_timestamp");
  6647. string lua_file (result.GetStringStr("lua_file"));
  6648. int8 custom_spell = result.GetInt32Str("custom_spell");
  6649. int32 damage_remaining = result.GetInt32Str("damage_remaining");
  6650. int32 effect_bitmask = result.GetInt32Str("effect_bitmask");
  6651. int16 num_triggers = result.GetInt32Str("num_triggers");
  6652. int8 had_triggers = result.GetInt32Str("had_triggers");
  6653. int8 cancel_after_triggers = result.GetInt32Str("cancel_after_triggers");
  6654. int8 crit = result.GetInt32Str("crit");
  6655. int8 last_spellattack_hit = result.GetInt32Str("last_spellattack_hit");
  6656. int8 interrupted = result.GetInt32Str("interrupted");
  6657. int8 resisted = result.GetInt32Str("resisted");
  6658. std::string custom_function = std::string(result.GetStringStr("custom_function"));
  6659. LuaSpell* lua_spell = 0;
  6660. if(custom_spell)
  6661. {
  6662. if((lua_spell = lua_interface->GetSpell(lua_file.c_str())) == nullptr)
  6663. {
  6664. 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());
  6665. lua_interface->LoadLuaSpell(lua_file);
  6666. }
  6667. }
  6668. Spell* spell = master_spell_list.GetSpell(spell_id, tier);
  6669. if(!spell)
  6670. {
  6671. LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u), spell could not be found!", spell_id, tier);
  6672. spell = master_spell_list.GetSpell(spell_id, 0);
  6673. if(spell)
  6674. LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u), identified tier 0 as replacement since the GetSpell failed!", spell_id, tier);
  6675. else
  6676. continue;
  6677. }
  6678. bool isMaintained = false;
  6679. bool isExistingLuaSpell = false;
  6680. MaintainedEffects* effect = nullptr;
  6681. Client* tmpCaster = nullptr;
  6682. if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id)) != nullptr)
  6683. {
  6684. safe_delete(lua_spell);
  6685. lua_spell = effect->spell;
  6686. if(lua_spell)
  6687. spell = lua_spell->spell;
  6688. isMaintained = true;
  6689. isExistingLuaSpell = true;
  6690. }
  6691. else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
  6692. && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr)
  6693. {
  6694. if(effect->spell && effect->spell_id == spell_id)
  6695. {
  6696. safe_delete(lua_spell);
  6697. if(tmpCaster->GetCurrentZone() == player->GetZone())
  6698. spellProcess->AddLuaSpellTarget(effect->spell, client->GetPlayer()->GetID());
  6699. lua_spell = effect->spell;
  6700. spell = effect->spell->spell;
  6701. isExistingLuaSpell = true;
  6702. isMaintained = true;
  6703. 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() : "?");
  6704. }
  6705. else
  6706. {
  6707. 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);
  6708. safe_delete(lua_spell);
  6709. continue;
  6710. }
  6711. }
  6712. else if(custom_spell && lua_spell)
  6713. {
  6714. lua_spell->spell = new Spell(spell);
  6715. lua_interface->AddCustomSpell(lua_spell);
  6716. }
  6717. else if(db_spell_type == DB_TYPE_MAINTAINEDEFFECTS)
  6718. {
  6719. safe_delete(lua_spell);
  6720. if(!target_char_id)
  6721. continue;
  6722. lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
  6723. if(lua_spell)
  6724. lua_spell->spell = spell;
  6725. }
  6726. if(!lua_spell)
  6727. {
  6728. 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());
  6729. continue;
  6730. }
  6731. SpellScriptTimer* timer = nullptr;
  6732. if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && custom_function.size() > 0)
  6733. {
  6734. timer = new SpellScriptTimer;
  6735. timer->caster = 0;
  6736. timer->deleteWhenDone = false;
  6737. timer->target = 0;
  6738. timer->time = expire_timestamp;
  6739. timer->customFunction = string(custom_function); // TODO
  6740. timer->spell = lua_spell;
  6741. timer->caster = (caster_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6742. if(target_char_id == 0xFFFFFFFF && player->HasPet())
  6743. timer->target = player->GetPet()->GetID();
  6744. else
  6745. timer->target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6746. if(!timer->target && target_char_id)
  6747. {
  6748. Client* tmpClient = zone_list.GetClientByCharID(target_char_id);
  6749. if(tmpClient && tmpClient->GetPlayer() && tmpClient->GetPlayer()->GetZone() == player->GetZone())
  6750. timer->target = tmpClient->GetPlayer()->GetID();
  6751. }
  6752. }
  6753. if(!isExistingLuaSpell)
  6754. {
  6755. lua_spell->crit = crit;
  6756. lua_spell->damage_remaining = damage_remaining;
  6757. lua_spell->effect_bitmask = effect_bitmask;
  6758. lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
  6759. lua_spell->had_triggers = had_triggers;
  6760. lua_spell->initial_caster_char_id = caster_char_id;
  6761. lua_spell->initial_target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
  6762. lua_spell->initial_target_char_id = target_char_id;
  6763. lua_spell->interrupted = interrupted;
  6764. lua_spell->last_spellattack_hit = last_spellattack_hit;
  6765. lua_spell->num_triggers = num_triggers;
  6766. }
  6767. if(lua_spell->initial_target == 0 && target_char_id == 0xFFFFFFFF && player->HasPet())
  6768. {
  6769. lua_spell->initial_target = player->GetPet()->GetID();
  6770. lua_spell->initial_target_char_id = target_char_id;
  6771. }
  6772. //lua_spell->num_calls ??
  6773. //if(target_char_id == player->GetCharacterID())
  6774. // lua_spell->targets.push_back(player->GetID());
  6775. if(db_spell_type == DB_TYPE_SPELLEFFECTS)
  6776. {
  6777. if (caster_char_id != player->GetCharacterID() && lua_spell->spell->GetSpellData()->group_spell && lua_spell->spell->GetSpellData()->friendly_spell)
  6778. {
  6779. if(!isExistingLuaSpell)
  6780. safe_delete(lua_spell);
  6781. continue;
  6782. }
  6783. player->MSpellEffects.writelock();
  6784. 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);
  6785. if(lua_spell->caster && (rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
  6786. info->spell_effects[effect_slot].caster = lua_spell->caster;
  6787. else if(caster_char_id != player->GetCharacterID())
  6788. {
  6789. Client* tmpCaster = zone_list.GetClientByCharID(caster_char_id);
  6790. if(tmpCaster)
  6791. {
  6792. if((rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
  6793. {
  6794. info->spell_effects[effect_slot].caster = tmpCaster->GetPlayer();
  6795. 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());
  6796. }
  6797. else
  6798. {
  6799. 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());
  6800. if(!isExistingLuaSpell)
  6801. {
  6802. safe_delete(lua_spell);
  6803. }
  6804. else
  6805. {
  6806. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6807. lua_spell->char_id_targets.insert(make_pair(player->GetCharacterID(),0));
  6808. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6809. }
  6810. player->MSpellEffects.releasewritelock();
  6811. continue;
  6812. }
  6813. }
  6814. }
  6815. else if(caster_char_id == player->GetCharacterID())
  6816. info->spell_effects[effect_slot].caster = player;
  6817. else
  6818. {
  6819. 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);
  6820. if(!isExistingLuaSpell)
  6821. safe_delete(lua_spell);
  6822. continue;
  6823. }
  6824. if(spell->GetSpellData()->duration_until_cancel)
  6825. info->spell_effects[effect_slot].expire_timestamp = 0xFFFFFFFF;
  6826. else
  6827. info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
  6828. info->spell_effects[effect_slot].icon = icon;
  6829. info->spell_effects[effect_slot].icon_backdrop = icon_backdrop;
  6830. info->spell_effects[effect_slot].spell_id = spell_id;
  6831. info->spell_effects[effect_slot].tier = tier;
  6832. info->spell_effects[effect_slot].total_time = total_time;
  6833. info->spell_effects[effect_slot].spell = lua_spell;
  6834. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6835. multimap<int32,int8>::iterator entries;
  6836. while((entries = lua_spell->char_id_targets.find(player->GetCharacterID())) != lua_spell->char_id_targets.end())
  6837. {
  6838. lua_spell->char_id_targets.erase(entries);
  6839. }
  6840. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6841. lua_spell->slot_pos = slot_pos;
  6842. if(!isExistingLuaSpell)
  6843. lua_spell->caster = player; // TODO: get actual player
  6844. player->MSpellEffects.releasewritelock();
  6845. if(!isMaintained)
  6846. spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
  6847. else
  6848. {
  6849. // track target id when caster isnt in zone somehow
  6850. }
  6851. }
  6852. else if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
  6853. {
  6854. player->MMaintainedSpells.writelock();
  6855. DatabaseResult targets;
  6856. // Use -1 on type and subtype to turn the enum into an int and make it a 0 index
  6857. 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)) {
  6858. while (targets.Next()) {
  6859. int32 target_char = targets.GetInt32Str("target_char_id");
  6860. int8 maintained_target_type = targets.GetInt32Str("target_type");
  6861. int32 in_spell_id = targets.GetInt32Str("spell_id");
  6862. if(spell_id != in_spell_id)
  6863. continue;
  6864. int32 idToAdd = 0;
  6865. if(target_char == 0xFFFFFFFF)
  6866. {
  6867. if( player->HasPet() )
  6868. {
  6869. restoreSpells.insert(make_pair(lua_spell, player->GetPet()));
  6870. // target added via restoreSpells
  6871. }
  6872. }
  6873. else
  6874. {
  6875. Client* client2 = zone_list.GetClientByCharID(target_char);
  6876. if(client2 && client2->GetPlayer() && client2->GetCurrentZone() == client->GetCurrentZone())
  6877. {
  6878. if(maintained_target_type > 0)
  6879. {
  6880. if(client != client2)
  6881. {
  6882. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6883. if(client2->GetPlayer()->GetPet() && maintained_target_type == PET_TYPE_COMBAT)
  6884. {
  6885. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetPet()));
  6886. // target added via restoreSpells
  6887. }
  6888. if(client2->GetPlayer()->GetCharmedPet() && maintained_target_type == PET_TYPE_CHARMED)
  6889. {
  6890. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetCharmedPet()));
  6891. // target added via restoreSpells
  6892. }
  6893. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6894. }
  6895. } // end of pet clause
  6896. else if(client != client2) // maintained type must be 0, so client
  6897. restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()));
  6898. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6899. multimap<int32,int8>::iterator entries;
  6900. for(entries = lua_spell->char_id_targets.begin(); entries != lua_spell->char_id_targets.end(); entries++)
  6901. {
  6902. int32 ent_char_id = entries->first;
  6903. int8 ent_target_type = entries->second;
  6904. if(ent_char_id == target_char && ent_target_type == maintained_target_type)
  6905. entries = lua_spell->char_id_targets.erase(entries);
  6906. }
  6907. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6908. }
  6909. else
  6910. {
  6911. lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
  6912. lua_spell->char_id_targets.insert(make_pair(target_char,maintained_target_type));
  6913. lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
  6914. }
  6915. }
  6916. }
  6917. }
  6918. Client* tmpClient = 0;
  6919. int32 targetID = 0;
  6920. if(target_char_id == 0xFFFFFFFF && player->HasPet())
  6921. targetID = player->GetPet()->GetID();
  6922. else if(target_char_id == player->GetCharacterID())
  6923. {
  6924. targetID = player->GetID();
  6925. tmpClient = player->GetClient();
  6926. }
  6927. else if((tmpClient = zone_list.GetClientByCharID(target_char_id)) != nullptr && tmpClient->GetPlayer())
  6928. targetID = tmpClient->GetPlayer()->GetID();
  6929. info->maintained_effects[effect_slot].conc_used = conc_used;
  6930. strncpy(info->maintained_effects[effect_slot].name, spell_name, 60);
  6931. info->maintained_effects[effect_slot].slot_pos = slot_pos;
  6932. info->maintained_effects[effect_slot].target = targetID;
  6933. info->maintained_effects[effect_slot].target_type = target_type;
  6934. if(spell->GetSpellData()->duration_until_cancel)
  6935. info->maintained_effects[effect_slot].expire_timestamp = 0xFFFFFFFF;
  6936. else
  6937. info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
  6938. info->maintained_effects[effect_slot].icon = icon;
  6939. info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop;
  6940. info->maintained_effects[effect_slot].spell_id = spell_id;
  6941. info->maintained_effects[effect_slot].tier = tier;
  6942. info->maintained_effects[effect_slot].total_time = total_time;
  6943. info->maintained_effects[effect_slot].spell = lua_spell;
  6944. if(!isExistingLuaSpell)
  6945. lua_spell->caster = player;
  6946. player->MMaintainedSpells.releasewritelock();
  6947. 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() : "",
  6948. lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, targetID, tmpClient ? tmpClient->GetPlayer()->GetName() : "");
  6949. if(tmpClient && lua_spell->initial_target_char_id == tmpClient->GetCharacterID())
  6950. {
  6951. lua_spell->initial_target = tmpClient->GetPlayer()->GetID();
  6952. spellProcess->AddLuaSpellTarget(lua_spell, lua_spell->initial_target, false);
  6953. }
  6954. spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
  6955. }
  6956. if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && !isMaintained)
  6957. {
  6958. lua_spell->timer.SetTimer(expire_timestamp);
  6959. lua_spell->timer.SetAtTrigger(lua_spell->spell->GetSpellData()->duration1 * 100);
  6960. lua_spell->timer.Start();
  6961. }
  6962. if(target_char_id == player->GetCharacterID() && lua_spell->spell->GetSpellData()->det_type)
  6963. player->AddDetrimentalSpell(lua_spell, expire_timestamp);
  6964. if(!isExistingLuaSpell)
  6965. {
  6966. if(timer)
  6967. spellProcess->AddSpellScriptTimer(timer);
  6968. lua_spell->num_calls = 1;
  6969. lua_spell->restored = true;
  6970. }
  6971. if(!lua_spell->resisted && (lua_spell->spell->GetSpellDuration() > 0 || lua_spell->spell->GetSpellData()->duration_until_cancel))
  6972. spellProcess->AddActiveSpell(lua_spell);
  6973. if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
  6974. {
  6975. if (num_triggers > 0)
  6976. ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, num_triggers, 0);
  6977. if (damage_remaining > 0)
  6978. ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, damage_remaining, 1);
  6979. }
  6980. }
  6981. if(db_spell_type == DB_TYPE_SPELLEFFECTS)
  6982. {
  6983. // if the cross_zone_target_buff option is disabled then we check for all possible targets within the current zone
  6984. // 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)
  6985. int8 cross_zone_target_buff = rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8();
  6986. DatabaseResult targets;
  6987. if (
  6988. (!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())) ||
  6989. (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())))
  6990. {
  6991. while (targets.Next()) {
  6992. int32 caster_char_id = targets.GetInt32Str("caster_char_id");
  6993. int8 prev_target_type = targets.GetInt32Str("target_type");
  6994. int32 in_spell_id = targets.GetInt32Str("spell_id");
  6995. Client* tmpCaster = nullptr;
  6996. MaintainedEffects* effect = nullptr;
  6997. if (caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr && (cross_zone_target_buff ||
  6998. tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id)) != nullptr)
  6999. {
  7000. if(prev_target_type > 0)
  7001. {
  7002. if(player->HasPet())
  7003. {
  7004. if(player->GetPet() && prev_target_type == PET_TYPE_COMBAT)
  7005. restoreSpells.insert(make_pair(effect->spell, player->GetPet()));
  7006. if(player->GetCharmedPet() && prev_target_type == PET_TYPE_CHARMED)
  7007. restoreSpells.insert(make_pair(effect->spell, player->GetCharmedPet()));
  7008. }
  7009. }
  7010. else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer()))
  7011. {
  7012. if(effect->spell->initial_target_char_id == player->GetCharacterID())
  7013. effect->spell->initial_target = player->GetID();
  7014. restoreSpells.insert(make_pair(effect->spell, player));
  7015. }
  7016. }
  7017. }
  7018. }
  7019. }
  7020. multimap<LuaSpell*, Entity*>::const_iterator itr;
  7021. for (itr = restoreSpells.begin(); itr != restoreSpells.end(); itr++)
  7022. {
  7023. LuaSpell* tmpSpell = itr->first;
  7024. Entity* target = itr->second;
  7025. if(!target)
  7026. {
  7027. target = client->GetPlayer()->GetPet();
  7028. if(!target)
  7029. continue;
  7030. }
  7031. Entity* caster = tmpSpell->caster;
  7032. if(!caster)
  7033. caster = client->GetPlayer();
  7034. int32 group_id_target = target->group_id;
  7035. if(target->IsPet() && target->GetOwner())
  7036. group_id_target = target->GetOwner()->group_id;
  7037. if(caster != target && caster->GetPet() != target &&
  7038. tmpSpell->spell->GetSpellData()->group_spell && tmpSpell->spell->GetSpellData()->friendly_spell && (caster->group_id == 0 || group_id_target == 0 || caster->group_id != group_id_target))
  7039. {
  7040. 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);
  7041. continue;
  7042. }
  7043. 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());
  7044. spellProcess->AddLuaSpellTarget(tmpSpell, target->GetID());
  7045. target->AddSpellEffect(tmpSpell, tmpSpell->timer.GetRemainingTime() != 0 ? tmpSpell->timer.GetRemainingTime() : 0);
  7046. vector<BonusValues*>* sb_list = caster->GetAllSpellBonuses(tmpSpell);
  7047. for (int32 x = 0; x < sb_list->size(); x++) {
  7048. BonusValues* bv = sb_list->at(x);
  7049. target->AddSpellBonus(tmpSpell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req);
  7050. }
  7051. sb_list->clear();
  7052. safe_delete(sb_list);
  7053. // look for a skill bonus on the caster's spell
  7054. if(caster->IsPlayer())
  7055. {
  7056. SkillBonus* sb = ((Player*)caster)->GetSkillBonus(tmpSpell->spell->GetSpellID());
  7057. if (sb) {
  7058. map<int32, SkillBonusValue*>::iterator itr_skills;
  7059. for (itr_skills = sb->skills.begin(); itr_skills != sb->skills.end(); itr_skills++)
  7060. target->AddSkillBonus(sb->spell_id, (*itr_skills).second->skill_id, (*itr_skills).second->value);
  7061. }
  7062. }
  7063. }
  7064. }
  7065. //devn00b: We need to handle non-found factions so the subtraction/addition works on 1st kill. Need to find a better way to handle this, but for now..
  7066. bool WorldDatabase::VerifyFactionID(int32 char_id, int32 faction_id) {
  7067. DatabaseResult result;
  7068. database_new.Select(&result, "SELECT COUNT(id) as faction_exists from character_factions where faction_id=%u and char_id=%u", faction_id, char_id);
  7069. if (result.Next() && result.GetInt32Str("faction_exists") == 0)
  7070. return false;
  7071. return true;
  7072. }
  7073. //using int/32 for starting city, should be large enough to support future zones (LOL). TODO: Will probably need more support bolted on, to support PVP and such.
  7074. void WorldDatabase::UpdateStartingLanguage(int32 char_id, uint8 race_id, int32 starting_city)
  7075. {
  7076. int8 rule = rule_manager.GetGlobalRule(R_World, StartingZoneLanguages)->GetInt8();
  7077. //this should never need to be done but lets make sure we got all the stuff we need. char_id at min since used for both.
  7078. if(!char_id || (rule == 0 && !race_id) || (rule == 1 && !starting_city))
  7079. return;
  7080. std::string query_string = "SELECT language_id FROM starting_languages ";
  7081. //check the rule, and that they gave us a race, if so use default entries to match live.
  7082. if(rule == 0 && race_id >= 0) {
  7083. query_string.append("WHERE race=" + std::to_string(race_id));
  7084. }
  7085. //if we have a starting city supplied, and the rule is set to use it, deal with it
  7086. else if(rule == 1) {
  7087. query_string.append("WHERE starting_city=" + std::to_string(starting_city) + " or (starting_city=0 and race_id=" + std::to_string(race_id) + ")");
  7088. }
  7089. Query query;
  7090. MYSQL_ROW row;
  7091. MYSQL_RES* result = query.RunQuery2(Q_SELECT,query_string.c_str());
  7092. LogWrite(PLAYER__DEBUG, 0, "Player", "Adding Languages for starting_city: %i based on rule R_World:StartingZoneLanguages value %u", starting_city, rule);
  7093. if (result) {
  7094. if (mysql_num_rows(result) > 0) {
  7095. while (result && (row = mysql_fetch_row(result))){
  7096. //add custom languages to the character_languages db.
  7097. query.RunQuery2(Q_INSERT, "INSERT IGNORE INTO character_languages (char_id, language_id) VALUES (%u,%u)",char_id, atoul(row[0]));
  7098. }
  7099. }
  7100. }
  7101. }