9
3

client.cpp 484 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405
  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 "../common/debug.h"
  17. #include "../common/Log.h"
  18. #include <iostream>
  19. #include <iomanip>
  20. #include <string.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <math.h>
  24. #include <zlib.h>
  25. #include <assert.h>
  26. #include <algorithm>
  27. #include <boost/property_tree/json_parser.hpp>
  28. #include "Player.h"
  29. #include "PlayerGroups.h"
  30. #include "Commands/Commands.h"
  31. #include "ClientPacketFunctions.h"
  32. #include "../common/ConfigReader.h"
  33. #include "Guilds/Guild.h"
  34. #include "Variables.h"
  35. #include "Rules/Rules.h"
  36. #include "Titles.h"
  37. #include "Chat/Chat.h"
  38. #include "SpellProcess.h"
  39. #include "Zone/ChestTrap.h"
  40. #include "../common/GlobalHeaders.h"
  41. #include "./Web/PeerManager.h"
  42. #include "./Web/HTTPSClientPool.h"
  43. //#include "Quests.h"
  44. #ifdef WIN32
  45. #include <WinSock2.h>
  46. #include <windows.h>
  47. #define snprintf _snprintf
  48. #define vsnprintf _vsnprintf
  49. #define strncasecmp _strnicmp
  50. #define strcasecmp _stricmp
  51. #else
  52. #include <sys/socket.h>
  53. #ifdef FREEBSD
  54. #include <sys/types.h>
  55. #endif
  56. #include <netinet/in.h>
  57. #include <arpa/inet.h>
  58. #include <unistd.h>
  59. #endif
  60. #if defined(__GNUC__)
  61. #define _snprintf snprintf
  62. #endif
  63. #include "client.h"
  64. #include "../common/emu_opcodes.h"
  65. #include "../common/packet_dump.h"
  66. #include "WorldDatabase.h"
  67. #include "races.h"
  68. #include "classes.h"
  69. #include "LoginServer.h"
  70. #include "World.h"
  71. #include "../common/EQ2_Common_Structs.h"
  72. #include "net.h"
  73. #include "../common/MiscFunctions.h"
  74. #include "Skills.h"
  75. #include "LuaInterface.h"
  76. #include "Quests.h"
  77. #include "Collections/Collections.h"
  78. #include "Achievements/Achievements.h"
  79. #include "Traits/Traits.h"
  80. #include "Recipes/Recipe.h"
  81. #include "Tradeskills/Tradeskills.h"
  82. #include "AltAdvancement/AltAdvancement.h"
  83. #include "Bots/Bot.h"
  84. #include "VisualStates.h"
  85. extern WorldDatabase database;
  86. extern const char* ZONE_NAME;
  87. extern LoginServer loginserver;
  88. extern sint32 numclients;
  89. extern NetConnection net;
  90. extern Commands commands;
  91. extern ClientList client_list;
  92. extern ZoneList zone_list;
  93. extern ZoneAuth zone_auth;
  94. extern MasterItemList master_item_list;
  95. extern MasterSkillList master_skill_list;
  96. extern MasterSpellList master_spell_list;
  97. extern MasterTraitList master_trait_list;
  98. extern MasterQuestList master_quest_list;
  99. extern MasterFactionList master_faction_list;
  100. extern MasterRecipeList master_recipe_list;
  101. extern volatile bool RunLoops;
  102. extern ConfigReader configReader;
  103. extern LuaInterface* lua_interface;
  104. extern World world;
  105. extern Variables variables;
  106. extern Classes classes;
  107. extern Races races;
  108. extern GuildList guild_list;
  109. extern MasterCollectionList master_collection_list;
  110. extern MasterAchievementList master_achievement_list;
  111. extern RuleManager rule_manager;
  112. extern Chat chat;
  113. extern MasterAAList master_aa_list;
  114. extern MasterAAList master_tree_nodes;
  115. extern ChestTrapList chest_trap_list;
  116. extern MasterRecipeBookList master_recipebook_list;
  117. extern VisualStates visual_states;
  118. extern PeerManager peer_manager;
  119. extern HTTPSClientPool peer_https_pool;
  120. using namespace std;
  121. Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) {
  122. eqs = ieqs;
  123. ip = eqs->GetrIP();
  124. port = ntohs(eqs->GetrPort());
  125. merchant_transaction = nullptr;
  126. mail_window.item = nullptr; // don't want this to be set(loose ptr) when using ResetSendMail to provide rest of the defaults
  127. ResetSendMail(false);
  128. timestamp_flag = 0;
  129. current_quest_id = 0;
  130. last_update_time = 0;
  131. quest_updates = false;
  132. //autobootup_timeout = new Timer(10000);
  133. //autobootup_timeout->Disable();
  134. CLE_keepalive_timer = new Timer(15000);
  135. connect = new Timer(1000);
  136. connect->Disable();
  137. zoneID = 0;
  138. account_name[0] = 0;
  139. character_id = 0;
  140. account_id = 0;
  141. pwaitingforbootup = 0;
  142. current_zone = 0;
  143. connected_to_zone = false;
  144. connected = false;
  145. camp_timer = 0;
  146. linkdead_timer = 0;
  147. client_zoning = false;
  148. client_zoning_details_set = false;
  149. zoning_id = 0;
  150. zoning_x = 0;
  151. zoning_y = 0;
  152. zoning_z = 0;
  153. zoning_instance_id = 0;
  154. player_pos_changed = false;
  155. player_pos_timer = Timer::GetCurrentTime2() + 1000;
  156. enabled_player_pos_timer = true;
  157. ++numclients;
  158. if (world.GetServerStatisticValue(STAT_SERVER_MOST_CONNECTIONS) < numclients)
  159. world.UpdateServerStatistic(STAT_SERVER_MOST_CONNECTIONS, numclients, true);
  160. remove_from_list = false;
  161. new_client_login = NewLoginState::LOGIN_NONE;
  162. UpdateWindowTitle(0);
  163. num_active_failures = 0;
  164. player = new Player();
  165. player->SetClient(this);
  166. combine_spawn = 0;
  167. lua_debug = false;
  168. ready_for_spawns = false;
  169. ready_for_updates = false;
  170. lua_debug_timer.Disable();
  171. transport_spawn = 0;
  172. MBuyBack.SetName("Client::MBuyBack");
  173. MDeletePlayer.SetName("Client::MDeletePlayer");
  174. MQuestPendingUpdates.SetName("Client::MQuestPendingUpdates");
  175. search_items = 0;
  176. version = 0;
  177. next_conversation_id = 0;
  178. pending_guild_invite.guild = 0;
  179. pending_guild_invite.invited_by = 0;
  180. m_recipeListSent = false;
  181. m_resurrect.SetName("Client::m_resurrect");
  182. current_rez.expire_timer = 0;
  183. current_rez.should_delete = true;
  184. pending_last_name = 0;
  185. should_target = false;
  186. initial_spawns_sent = false;
  187. MQuestTimers.SetName("Client::quest_timers");
  188. memset(&incoming_paperdoll, 0, sizeof(incoming_paperdoll));
  189. on_auto_mount = false;
  190. should_load_spells = true;
  191. spawnPlacementMode = ServerSpawnPlacementMode::DEFAULT;
  192. delayedLogin = false;
  193. delayedAccountID = 0;
  194. delayedAccessKey = 0;
  195. delayTimer.Disable();
  196. tempPlacementSpawn = nullptr;
  197. placement_unique_item_id = 0;
  198. SetHasOwnerOrEditAccess(false);
  199. temporary_transport_id = 0;
  200. rejoin_group_id = 0;
  201. lastRegionRemapTime = 0;
  202. regionDebugMessaging = false;
  203. client_reloading_zone = false;
  204. last_saved_timestamp = 0;
  205. MQueueStateCmds.SetName("Client::MQueueStateCmds");
  206. save_spell_state_timer.Disable();
  207. save_spell_state_time_bucket = 0;
  208. player_loading_complete = false;
  209. MItemDetails.SetName("Client::MItemDetails");
  210. MSpellDetails.SetName("Client::MSpellDetails");
  211. hasSentTempPlacementSpawn = false;
  212. spawn_removal_timer.Start();
  213. disable_save = false;
  214. SetZoningDestination(nullptr);
  215. underworld_cooldown_timer.Disable();
  216. player_pos_change_count = 0;
  217. pov_ghost_spawn_id = 0;
  218. recipe_orig_packet = nullptr;
  219. recipe_xor_packet = nullptr;
  220. recipe_packet_count = 0;
  221. recipe_orig_packet_size = 0;
  222. }
  223. Client::~Client() {
  224. RemoveClientFromZone();
  225. //let the stream factory know were done with this stream
  226. if (eqs) {
  227. eqs->Close();
  228. try {
  229. eqs->ReleaseFromUse();
  230. }
  231. catch (...) {}
  232. }
  233. eqs = NULL;
  234. //safe_delete(autobootup_timeout);
  235. safe_delete(linkdead_timer);
  236. vector<QueuedQuest*>::iterator itr;
  237. QueuedQuest* queued_quest = 0;
  238. for (itr = quest_queue.begin(); itr != quest_queue.end(); itr++) {
  239. queued_quest = *itr;
  240. safe_delete(queued_quest);
  241. }
  242. quest_queue.clear();
  243. vector<QuestRewardData*>::iterator rwd_itr;
  244. QuestRewardData* quest_rwd_data = 0;
  245. for (rwd_itr = quest_pending_reward.begin(); rwd_itr != quest_pending_reward.end(); rwd_itr++) {
  246. quest_rwd_data = *rwd_itr;
  247. safe_delete(quest_rwd_data);
  248. }
  249. quest_pending_reward.clear();
  250. safe_delete(CLE_keepalive_timer);
  251. safe_delete(connect);
  252. --numclients;
  253. UpdateWindowTitle(0);
  254. }
  255. void Client::RemoveClientFromZone() {
  256. if (player && player->GetZone())
  257. player->GetZone()->GetSpellProcess()->RemoveSpellTimersFromSpawn(player, true, false, true, true);
  258. if (GetTempPlacementSpawn() && GetCurrentZone()) {
  259. Spawn* tmp = GetTempPlacementSpawn();
  260. SetTempPlacementSpawn(nullptr);
  261. GetCurrentZone()->RemoveSpawn(tmp, true, false, true, true, true);
  262. }
  263. if (current_zone && player) {
  264. if (player->GetGroupMemberInfo()) {
  265. TempRemoveGroup();
  266. }
  267. world.GetGroupManager()->ClearPendingInvite(player);
  268. }
  269. if (lua_interface)
  270. lua_interface->RemoveDebugClients(this);
  271. if (player)
  272. zone_list.RemoveClientFromMap(player->GetName(), this);
  273. safe_delete(camp_timer);
  274. safe_delete(search_items);
  275. safe_delete(current_rez.expire_timer);
  276. safe_delete(pending_last_name);
  277. safe_delete_array(incoming_paperdoll.image_bytes);
  278. MDeletePlayer.writelock(__FUNCTION__, __LINE__);
  279. player = nullptr;
  280. MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
  281. deque<BuyBackItem*>::iterator itr;
  282. MBuyBack.writelock(__FUNCTION__, __LINE__);
  283. for (itr = buy_back_items.begin(); itr != buy_back_items.end();) {
  284. safe_delete(*itr);
  285. itr = buy_back_items.erase(itr);
  286. }
  287. MBuyBack.releasewritelock(__FUNCTION__, __LINE__);
  288. }
  289. void Client::QueuePacket(EQ2Packet* app, bool attemptedCombine) {
  290. if (eqs) {
  291. if (!eqs->CheckActive()) {
  292. client_list.Remove(this);
  293. eqs = 0;
  294. }
  295. }
  296. if (app && eqs && version > 0)
  297. eqs->EQ2QueuePacket(app, attemptedCombine);
  298. else {
  299. safe_delete(app);
  300. }
  301. }
  302. void Client::PopulateSkillMap() {
  303. EQ2Packet* app = master_skill_list.GetPopulateSkillsPacket(GetVersion());
  304. if (app)
  305. QueuePacket(app);
  306. else {
  307. LogWrite(WORLD__ERROR, 0, "World", "Unable to send populate skills packet for version: %i!", GetVersion());
  308. Disconnect(); //the client cant proceed without the skill packet, might as well kick it now
  309. }
  310. }
  311. void Client::SendLoginInfo() {
  312. if (GetPlayer()->IsReturningFromLD())
  313. firstlogin = true;
  314. if (firstlogin) {
  315. LogWrite(WORLD__DEBUG, 0, "World", "Increment Server_Accepted_Connection + 1");
  316. world.UpdateServerStatistic(STAT_SERVER_ACCEPTED_CONNECTION, 1);
  317. LogWrite(CCLIENT__DEBUG, 0, "Client", "Populate Skill Map...");
  318. PopulateSkillMap();
  319. // JA: Check client version and move player to valid zone if current client does not support last saved zone (loading SF client on DoV saved zone) IT CAN HAPPEN!
  320. LogWrite(MISC__TODO, 1, "TODO", "Check client version at login, move char if invalid zone file");
  321. }
  322. LogWrite(CCLIENT__DEBUG, 0, "Client", "Toggle Character Online...");
  323. database.ToggleCharacterOnline(this, 1);
  324. int32 count = 0;
  325. if (!GetPlayer()->IsReturningFromLD())
  326. {
  327. count = database.LoadCharacterTitles(GetCharacterID(), player);
  328. if (count == 0) {
  329. LogWrite(CCLIENT__DEBUG, 0, "Client", "No character titles found!");
  330. LogWrite(CCLIENT__DEBUG, 0, "Client", "Initializing starting values - Titles");
  331. database.UpdateStartingTitles(GetCharacterID(), player->GetAdventureClass(), player->GetRace(), player->GetGender());
  332. }
  333. }
  334. if (!GetPlayer()->IsReturningFromLD())
  335. {
  336. count = database.LoadCharacterLanguages(GetCharacterID(), player);
  337. if (count == 0)
  338. LogWrite(CCLIENT__DEBUG, 0, "Client", "No character languages loaded!");
  339. count = database.LoadPlayerRecipeBooks(GetCharacterID(), player);
  340. if (count == 0)
  341. LogWrite(CCLIENT__DEBUG, 0, "Client", "No character recipe books found!");
  342. }
  343. ClientPacketFunctions::SendLoginAccepted(this);
  344. ClientPacketFunctions::SendAbilities(this);
  345. ClientPacketFunctions::SendCommandNamePacket(this);
  346. ClientPacketFunctions::SendQuickBarInit(this);
  347. // we only need to send the MOTD if it is the first time the person is logging in.
  348. if (firstlogin) {
  349. ClientPacketFunctions::SendMOTD(this);
  350. ClientPacketFunctions::SendCharacterMacros(this);
  351. zone_list.CheckFriendList(this);
  352. }
  353. if (!GetPlayer()->IsReturningFromLD())
  354. {
  355. database.LoadCharacterItemList(GetAccountID(), GetCharacterID(), player, GetVersion());
  356. if (firstlogin && player->item_list.GetNumberOfItems() == 0 && player->GetEquipmentList()->GetNumberOfItems() == 0) //re-add starting items if missing
  357. {
  358. LogWrite(CCLIENT__WARNING, 0, "Client", "Player has no items - reloading starting items: '%s' (%u)", player->GetName(), GetCharacterID());
  359. database.UpdateStartingItems(GetCharacterID(), player->GetAdventureClass(), player->GetRace());
  360. database.LoadCharacterItemList(GetAccountID(), GetCharacterID(), player, GetVersion());
  361. }
  362. GetPlayer()->item_list.SetMaxItemIndex();
  363. database.LoadPlayerFactions(this);
  364. database.LoadCharacterQuests(this);
  365. database.LoadCharacterQuestRewards(this);
  366. database.LoadPlayerMail(this);
  367. }
  368. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  369. SendQuestJournal(true, 0, false);
  370. if (version > 561) // right version? possibly not!
  371. master_aa_list.DisplayAA(this, 0, 3);
  372. if (version > 373)
  373. SendCollectionList();
  374. SendBiography();
  375. map<int32, Quest*>::iterator itr;
  376. Quest* quest = 0;
  377. GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
  378. for (itr = player->player_quests.begin(); itr != player->player_quests.end(); itr++) {
  379. quest = itr->second;
  380. if (quest->IsTracked()) {
  381. quest->SetTracked(false);
  382. QueuePacket(itr->second->QuestJournalReply(version, GetNameCRC(), player));
  383. quest->SetTracked(true);
  384. QueuePacket(itr->second->QuestJournalReply(version, GetNameCRC(), player));
  385. }
  386. }
  387. GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
  388. // SendAchievementsList();
  389. if (version > 373) {
  390. LogWrite(CCLIENT__DEBUG, 0, "Client", "Loading Faction Updates...");
  391. EQ2Packet* outapp = player->GetFactions()->FactionUpdate(GetVersion());
  392. if (outapp) {
  393. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  394. //DumpPacket(outapp);
  395. QueuePacket(outapp);
  396. }
  397. }
  398. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Command List...");
  399. ClientPacketFunctions::SendCommandList(this);
  400. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Language Updates...");
  401. // kos doesn't like one of these language or instance list
  402. SendLanguagesUpdate(database.GetCharacterCurrentLang(GetCharacterID(), player));
  403. //ClientPacketFunctions::SendInstanceList(this);
  404. SendZoneInfo();
  405. /*Spell* spell = 0;
  406. vector<QuickBarItem*>::iterator itr;
  407. for(itr = player->GetQuickbar()->begin(); itr != player->GetQuickbar()->end(); itr++){
  408. if((*itr)->type == 1){
  409. spell = master_spell_list.GetSpell((*itr)->id);
  410. if(spell)
  411. QueuePacket(spell->serialize(this, false, 0x20));
  412. }
  413. }*/
  414. }
  415. void Client::SendPlayerDeathWindow()
  416. {
  417. LogWrite(CCLIENT__DEBUG, 0, "Client", "SendPlayerDeathWindow");
  418. vector<RevivePoint*>* results = GetCurrentZone()->GetRevivePoints(this);
  419. vector<RevivePoint*>::iterator itr;
  420. if (results && results->size() > 0)
  421. {
  422. PacketStruct* packet = configReader.getStruct("WS_DeathWindow", GetVersion());
  423. if (packet)
  424. {
  425. packet->setArrayLengthByName("location_count", results->size());
  426. RevivePoint* point = 0;
  427. int32 i = 0;
  428. for (itr = results->begin(); itr != results->end(); itr++, i++)
  429. {
  430. point = *itr;
  431. if (point)
  432. {
  433. packet->setArrayDataByName("location_id", point->id, i);
  434. //zone_name = database.GetZoneName(point->zone_id);
  435. string zone_name = database.GetZoneDescription(point->zone_id);
  436. if (zone_name.length() > 0)
  437. packet->setArrayDataByName("zone_name", zone_name.c_str(), i);
  438. packet->setArrayDataByName("location_name", point->location_name.c_str(), i);
  439. packet->setArrayDataByName("distance", GetPlayer()->GetDistance(point->x, point->y, point->z), i);
  440. }
  441. if (point->id == 0xFFFFFFFF)//tmp location
  442. safe_delete(point);
  443. }
  444. #if EQDEBUG >= 3
  445. LogWrite(CCLIENT__DEBUG, 0, "Client", "WS_DeathWindow Packet:");
  446. packet->PrintPacket();
  447. #endif
  448. EQ2Packet* outapp = packet->serialize();
  449. QueuePacket(outapp);
  450. safe_delete(packet);
  451. }
  452. // done with the revive points so lets free up the pointer
  453. safe_delete(results);
  454. }
  455. }
  456. void Client::DisplayDeadWindow()
  457. {
  458. LogWrite(ZONE__DEBUG, 0, "Zone", "DisplayDeadWindow()");
  459. player->SetHP(0);
  460. player->SetPower(0);
  461. GetCurrentZone()->TriggerCharSheetTimer();
  462. if (GetVersion() <= 561) {
  463. ClientPacketFunctions::SendServerControlFlagsClassic(this, 8, 1);
  464. ClientPacketFunctions::SendServerControlFlagsClassic(this, 16, 1);
  465. }
  466. else {
  467. ClientPacketFunctions::SendServerControlFlags(this, 1, 8, 1);
  468. ClientPacketFunctions::SendServerControlFlags(this, 1, 16, 1);
  469. }
  470. PacketStruct* packet = configReader.getStruct("WS_ServerUpdateTarget", GetVersion());
  471. if (packet)
  472. {
  473. packet->setDataByName("spawn_id", 0xFFFFFFFF);
  474. QueuePacket(packet->serialize());
  475. safe_delete(packet);
  476. }
  477. SendPlayerDeathWindow();
  478. }
  479. void Client::HandlePlayerRevive(int32 point_id)
  480. {
  481. if (GetVersion() <= 561) {
  482. ClientPacketFunctions::SendServerControlFlagsClassic(this, 8, 0);
  483. ClientPacketFunctions::SendServerControlFlagsClassic(this, 16, 0);
  484. }
  485. else {
  486. ClientPacketFunctions::SendServerControlFlags(this, 1, 8, 0);
  487. ClientPacketFunctions::SendServerControlFlags(this, 1, 16, 0);
  488. }
  489. SimpleMessage(CHANNEL_NARRATIVE, "You regain consciousness!");
  490. PacketStruct* packet = configReader.getStruct("WS_Resurrected", GetVersion());
  491. if (packet)
  492. {
  493. QueuePacket(packet->serialize());
  494. safe_delete(packet);
  495. }
  496. float origX, origY, origZ, origHeading = 0.0f;
  497. origX = player->GetX();
  498. origY = player->GetY();
  499. origZ = player->GetZ();
  500. origHeading = player->GetHeading();
  501. ZoneServer* originalZone = GetCurrentZone();
  502. int32 origGridID = GetPlayer()->GetLocation();
  503. float x, y, z, heading;
  504. RevivePoint* revive_point = 0;
  505. if (point_id != 0xFFFFFFFF)
  506. revive_point = GetCurrentZone()->GetRevivePoint(point_id);
  507. string zone_desc;
  508. const char* location_name = "Unknown";
  509. player->SetAlive(true);
  510. player->SetResurrecting(true);
  511. player->SetHP(player->GetTotalHP());
  512. player->SetPower(player->GetTotalPower());
  513. //revive at zone safe coords
  514. if (!revive_point)
  515. {
  516. LogWrite(CCLIENT__WARNING, 0, "Client", "No Revive Point! Spawning player at safe coordinates!");
  517. x = GetCurrentZone()->GetSafeX();
  518. y = GetCurrentZone()->GetSafeY();
  519. z = GetCurrentZone()->GetSafeZ();
  520. heading = GetCurrentZone()->GetSafeHeading();
  521. zone_desc = GetCurrentZone()->GetZoneDescription();
  522. location_name = "Zone Safe Point";
  523. Zone(GetCurrentZone()->GetZoneName(), false);
  524. }
  525. else
  526. {
  527. LogWrite(CCLIENT__DEBUG, 0, "Client", "Sending player to chosen Revive Point.");
  528. x = revive_point->x;
  529. y = revive_point->y;
  530. z = revive_point->z;
  531. heading = revive_point->heading;
  532. zone_desc = database.GetZoneDescription(revive_point->zone_id);
  533. location_name = revive_point->location_name.c_str();
  534. Zone(GetCurrentZone()->GetZoneName(), false);
  535. }
  536. player->SetX(x);
  537. player->SetY(y);
  538. player->SetZ(z);
  539. player->SetHeading(heading);
  540. LogWrite(CCLIENT__DEBUG, 0, "Client", "Attempt Revive @ %s, %.2f, %.2f, %.2f, %.2f, HP: %i, Pow: %i, %s",
  541. zone_desc.c_str(),
  542. player->GetX(),
  543. player->GetY(),
  544. player->GetZ(),
  545. player->GetHeading(),
  546. player->GetHP(),
  547. player->GetPower(),
  548. location_name);
  549. //player->ClearEverything();
  550. Save();
  551. if (revive_point && revive_point->zone_id != GetCurrentZone()->GetZoneID() && revive_point->zone_id != 0)
  552. {
  553. string zone_name = database.GetZoneName(revive_point->zone_id);
  554. if (zone_name.length() == 0)
  555. {
  556. LogWrite(CCLIENT__WARNING, 0, "Client", "Unable to zone player to revive zone ID '%u', using current zone's safe coords.", revive_point->zone_id);
  557. x = GetCurrentZone()->GetSafeX();
  558. y = GetCurrentZone()->GetSafeY();
  559. z = GetCurrentZone()->GetSafeZ();
  560. heading = GetCurrentZone()->GetSafeHeading();
  561. location_name = "Zone Safe Point";
  562. }
  563. else
  564. {
  565. LogWrite(CCLIENT__DEBUG, 0, "Client", "Sending player to revive zone ID '%u', using current zone's safe coords.", revive_point->zone_id);
  566. location_name = revive_point->location_name.c_str();
  567. //player->ClearEverything();
  568. Save();
  569. Zone(zone_name.c_str(), false);
  570. }
  571. }
  572. zone_desc = GetCurrentZone()->GetZoneDescription();
  573. Message(CHANNEL_NARRATIVE, "Reviving in %s at %s.", zone_desc.c_str(), location_name);
  574. player->SetSpawnType(4);
  575. if (version > 373) {
  576. packet = configReader.getStruct("WS_CancelMoveObjectMode", GetVersion());
  577. if (packet)
  578. {
  579. QueuePacket(packet->serialize());
  580. safe_delete(packet);
  581. }
  582. }
  583. packet = configReader.getStruct("WS_TeleportWithinZone", GetVersion());
  584. if (packet)
  585. {
  586. packet->setDataByName("x", x);
  587. packet->setDataByName("y", y);
  588. packet->setDataByName("z", z);
  589. QueuePacket(packet->serialize());
  590. safe_delete(packet);
  591. }
  592. SendControlGhost();
  593. packet = configReader.getStruct("WS_SetPOVGhostCmd", GetVersion());
  594. if (packet)
  595. {
  596. packet->setDataByName("spawn_id", 0xFFFFFFFF);
  597. QueuePacket(packet->serialize());
  598. safe_delete(packet);
  599. }
  600. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_Combat, EnableSpiritShards)->GetBool())
  601. {
  602. NPC* shard = player->InstantiateSpiritShard(origX, origY, origZ, origHeading, origGridID, originalZone);
  603. if (shard->GetSpawnScript() && strlen(shard->GetSpawnScript()) > 0)
  604. originalZone->CallSpawnScript(shard, SPAWN_SCRIPT_PRESPAWN);
  605. originalZone->RemoveSpawn(player, false, true, true, true, true);
  606. originalZone->AddSpawn(shard);
  607. if (shard->GetSpawnScript() && strlen(shard->GetSpawnScript()) > 0)
  608. originalZone->CallSpawnScript(shard, SPAWN_SCRIPT_SPAWN);
  609. }
  610. m_resurrect.writelock(__FUNCTION__, __LINE__);
  611. if (current_rez.active)
  612. current_rez.should_delete = true;
  613. m_resurrect.releasewritelock(__FUNCTION__, __LINE__);
  614. }
  615. void Client::SendControlGhost(int32 send_id, int8 unknown2) {
  616. PacketStruct* packet = configReader.getStruct("WS_SetControlGhost", GetVersion());
  617. if (packet) {
  618. packet->setDataByName("spawn_id", send_id);
  619. packet->setDataByName("speed", GetPlayer()->GetSpeed());
  620. packet->setDataByName("size", 0.51);
  621. packet->setDataByName("unknown2", unknown2);
  622. packet->setDataByName("air_speed", player->GetAirSpeed());
  623. EQ2Packet* app = packet->serialize();
  624. QueuePacket(app);
  625. safe_delete(packet);
  626. }
  627. }
  628. void Client::SendCharInfo() {
  629. EQ2Packet* app;
  630. player->SetEquippedItemAppearances();
  631. ClientPacketFunctions::SendCharacterData(this);
  632. SendCharPOVGhost();
  633. SendControlGhost(player->GetIDWithPlayerSpawn(player), 255);
  634. //sending bad spawn packet?
  635. //SendAchievementsList();
  636. //if (version > 561)
  637. //ClientPacketFunctions::SendHousingList(this);
  638. ClientPacketFunctions::SendCharacterSheet(this);
  639. ClientPacketFunctions::SendTraitList(this);// moved from below
  640. ClientPacketFunctions::SendAbilities(this);
  641. ClientPacketFunctions::SendSkillBook(this);
  642. if (!IsReloadingZone() && !player->IsResurrecting() && GetVersion() >= 546) {
  643. ClientPacketFunctions::SendUpdateSpellBook(this);
  644. }
  645. else {
  646. player->SetResurrecting(false);
  647. }
  648. GetCurrentZone()->AddSpawn(player);
  649. if (IsReloadingZone() && (zoning_x || zoning_y || zoning_z)) {
  650. GetPlayer()->SetX(zoning_x);
  651. GetPlayer()->SetY(zoning_y);
  652. GetPlayer()->SetZ(zoning_z);
  653. GetPlayer()->SetHeading(zoning_h);
  654. EQ2Packet* packet = GetPlayer()->Move(zoning_x, zoning_y, zoning_z, GetVersion(), zoning_h);
  655. QueuePacket(packet);
  656. }
  657. //SendCollectionList();
  658. Guild* guild = player->GetGuild();
  659. if (guild)
  660. guild->GuildMemberLogin(this, firstlogin);
  661. app = player->GetPlayerItemList()->serialize(GetPlayer(), GetVersion());
  662. if (app) {
  663. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  664. //DumpPacket(app);
  665. QueuePacket(app);
  666. }
  667. app = player->GetEquipmentList()->serialize(GetVersion(), player);
  668. if (app) {
  669. QueuePacket(app);
  670. }
  671. app = player->GetAppearanceEquipmentList()->serialize(GetVersion(), player);
  672. if (app) {
  673. QueuePacket(app);
  674. }
  675. vector<Item*>* items = player->GetPlayerItemList()->GetItemsFromBagID(-3); // bank items
  676. if (items && items->size() > 0) {
  677. for (int32 i = 0; i < items->size(); i++) {
  678. EQ2Packet* outapp = items->at(i)->serialize(GetVersion(), false, GetPlayer());
  679. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  680. //DumpPacket(outapp);
  681. QueuePacket(outapp);
  682. }
  683. }
  684. if (firstlogin && (app = chat.GetWorldChannelList(this)) != NULL)
  685. QueuePacket(app);
  686. safe_delete(items);
  687. items = player->GetPlayerItemList()->GetItemsFromBagID(-4); //shared bank items
  688. if (items && items->size() > 0) {
  689. for (int32 i = 0; i < items->size(); i++)
  690. QueuePacket(items->at(i)->serialize(GetVersion(), false, GetPlayer()));
  691. }
  692. safe_delete(items);
  693. if (version >= 373) {
  694. SendTitleUpdate();
  695. }
  696. GetPlayer()->UpdateWeapons();
  697. if (!GetPlayer()->IsReturningFromLD()) {
  698. database.LoadBuyBacks(this);
  699. }
  700. if (version > 561)
  701. master_aa_list.DisplayAA(this, 0, 0);
  702. string zone_motd = GetCurrentZone()->GetZoneMOTD();
  703. if (zone_motd.length() > 0 && zone_motd[0] != ' ') {
  704. string zone_motd_send = "Zone MOTD: " + zone_motd;
  705. SimpleMessage(CHANNEL_NARRATIVE, zone_motd_send.c_str());
  706. }
  707. const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID());
  708. if (zone_script && lua_interface)
  709. lua_interface->RunZoneScript(zone_script, "player_entry", GetCurrentZone(), GetPlayer());
  710. this->client_zoning = false;
  711. this->client_zoning_details_set = false;
  712. this->zoning_id = 0;
  713. this->zoning_instance_id = 0;
  714. SetZoningDestination(nullptr);
  715. if (player->GetHP() < player->GetTotalHP() || player->GetPower() < player->GetTotalPower())
  716. GetCurrentZone()->AddDamagedSpawn(player);
  717. if (firstlogin)
  718. firstlogin = false;
  719. player->ClearProcs();
  720. items = player->GetEquippedItemList();
  721. if (items && items->size() > 0) {
  722. for (int32 i = 0; i < items->size(); i++) {
  723. Item* item = items->at(i);
  724. if (item && item->GetItemScript() && lua_interface)
  725. lua_interface->RunItemScript(item->GetItemScript(), "equipped", item, player);
  726. }
  727. }
  728. //Allow this player to change their last name if they meet the level requirement
  729. if (!player->get_character_flag(CF_ENABLE_CHANGE_LASTNAME) && player->GetLevel() >= rule_manager.GetZoneRule(GetCurrentZoneID(), R_Player, MinLastNameLevel)->GetInt8())
  730. player->set_character_flag(CF_ENABLE_CHANGE_LASTNAME);
  731. safe_delete(items);
  732. if (!player->Alive())
  733. DisplayDeadWindow();
  734. ClientPacketFunctions::SendLocalizedTextMessage(this);
  735. if (GetCurrentZone()->GetInstanceID())
  736. {
  737. PlayerHouse* ph = world.GetPlayerHouseByInstanceID(GetCurrentZone()->GetInstanceID());
  738. if (ph) {
  739. //HouseZone* hz = world.GetHouseZone(ph->house_id);
  740. string name = string(GetPlayer()->GetName());
  741. if (name.compare(ph->player_name) == 0)
  742. SetHasOwnerOrEditAccess(true);
  743. }
  744. }
  745. bool groupMentor = false;
  746. GetPlayer()->group_id = rejoin_group_id;
  747. if (!world.RejoinGroup(this, rejoin_group_id))
  748. GetPlayer()->group_id = 0;
  749. else
  750. {
  751. Entity* ent = world.GetGroupManager()->IsPlayerInGroup(rejoin_group_id, GetPlayer()->GetGroupMemberInfo()->mentor_target_char_id);
  752. if (ent && ent->IsPlayer())
  753. {
  754. GetPlayer()->SetMentorStats(ent->GetLevel(), ent->GetID(), false);
  755. groupMentor = true;
  756. }
  757. }
  758. if (!groupMentor)
  759. GetPlayer()->SetMentorStats(GetPlayer()->GetLevel(), 0, false);
  760. if (!GetPlayer()->IsReturningFromLD()) {
  761. database.LoadCharacterSpellEffects(GetCharacterID(), this, DB_TYPE_MAINTAINEDEFFECTS);
  762. database.LoadCharacterSpellEffects(GetCharacterID(), this, DB_TYPE_SPELLEFFECTS);
  763. }
  764. else {
  765. Spawn* pet_spawn = nullptr;
  766. if (GetPlayer()->GetPet())
  767. pet_spawn = GetPlayer()->GetPet();
  768. else if (GetPlayer()->GetCharmedPet())
  769. pet_spawn = GetPlayer()->GetCharmedPet();
  770. else if (GetPlayer()->GetCosmeticPet())
  771. pet_spawn = GetPlayer()->GetCosmeticPet();
  772. else if (GetPlayer()->GetDeityPet())
  773. pet_spawn = GetPlayer()->GetDeityPet();
  774. if (pet_spawn) {
  775. GetPlayer()->GetInfoStruct()->set_pet_id(GetPlayer()->GetIDWithPlayerSpawn(pet_spawn));
  776. }
  777. }
  778. GetPlayer()->SetSaveSpellEffects(false);
  779. GetPlayer()->SetCharSheetChanged(true);
  780. GetPlayer()->SetReturningFromLD(false);
  781. }
  782. void Client::SendZoneSpawns() {
  783. //Allows us to place spawns almost anywhere
  784. if (version > 373) {
  785. uchar blah[] = { 0x00,0x3C,0x1C,0x46,0x00,0x3C,0x1C,0x46,0x00,0x3C,0x1C,0x46 };
  786. EQ2Packet* app = new EQ2Packet(OP_MoveableObjectPlacementCriteri, blah, sizeof(blah));
  787. QueuePacket(app);
  788. }
  789. ClientPacketFunctions::SendSkillSlotMappings(this);
  790. ClientPacketFunctions::SendGameWorldTime(this);
  791. GetCurrentZone()->StartZoneInitialSpawnThread(this);
  792. }
  793. void Client::SendCharPOVGhost() {
  794. bool use_ghost_pov = false;
  795. PacketStruct* set_pov = configReader.getStruct("WS_SetPOVGhostCmd", GetVersion());
  796. int32 ghost_id = 0;
  797. if (set_pov) {
  798. if (pov_ghost_spawn_id) {
  799. Spawn* spawn = GetCurrentZone()->GetSpawnByID(pov_ghost_spawn_id);
  800. ghost_id = player->GetIDWithPlayerSpawn(spawn);
  801. if (spawn) {
  802. use_ghost_pov = true;
  803. }
  804. }
  805. if (use_ghost_pov) {
  806. set_pov->setDataByName("spawn_id", ghost_id);
  807. }
  808. else {
  809. set_pov->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(player));
  810. }
  811. EQ2Packet* app_pov = set_pov->serialize();
  812. QueuePacket(app_pov);
  813. safe_delete(set_pov);
  814. }
  815. }
  816. void Client::SendZoneInfo() {
  817. ZoneServer* zone = GetCurrentZone();
  818. if (zone) {
  819. EQ2Packet* packet = zone->GetZoneInfoPacket(this);
  820. QueuePacket(packet);
  821. if (version > 561) {
  822. PacketStruct* fog_packet = configReader.getStruct("WS_FogInit", GetVersion());
  823. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  824. #if EQDEBUG >= 9
  825. fog_packet->PrintPacket();
  826. #endif
  827. if (fog_packet) {
  828. database.LoadFogInit(zone->GetZoneFile(), fog_packet);
  829. QueuePacket(fog_packet->serialize());
  830. safe_delete(fog_packet);
  831. }
  832. zone->SendFlightPathsPackets(this);
  833. }
  834. }
  835. /*
  836. uchar blah[] ={0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x01,0x00,0x00,0x00,0x00
  837. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  838. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF
  839. ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
  840. ,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00
  841. ,0x00,0x00,0x00,0x00,0x10,0x49,0x2B,0x62,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  842. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  843. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  844. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  845. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  846. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  847. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  848. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  849. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  850. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  851. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  852. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  853. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  854. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  855. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  856. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  857. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  858. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  859. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  860. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  861. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  862. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  863. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  864. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  865. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  866. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  867. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  868. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  869. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  870. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  871. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00};
  872. EQ2Packet* appA = new EQ2Packet(OP_GuildUpdateMsg, blah, sizeof(blah));
  873. QueuePacket(appA);
  874. uchar blahA[] ={0x45,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00
  875. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  876. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
  877. ,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00
  878. ,0x00,0x10,0xE2,0x10,0x6C,0x00,0x00,0x00,0x00};
  879. EQ2Packet* appB = new EQ2Packet(OP_KeymapDataMsg, blahA, sizeof(blahA));
  880. QueuePacket(appB);
  881. */
  882. LogWrite(CCLIENT__DEBUG, 0, "Client", "SendFriendList");
  883. SendFriendList();
  884. LogWrite(CCLIENT__DEBUG, 0, "Client", "SendIgnoreList");
  885. SendIgnoreList();
  886. }
  887. void Client::SendDefaultGroupOptions() {
  888. /*
  889. 0 - loot method
  890. 1 - loot items rarity
  891. 2 - Auto split coin
  892. 4 - default yell method
  893. 6 - group autolock
  894. 7 - solo autolock
  895. */
  896. PacketStruct* default_options = configReader.getStruct("WS_DefaultGroupOptions", GetVersion());
  897. if (default_options) {
  898. default_options->setDataByName("loot_method", GetPlayer()->GetInfoStruct()->get_group_loot_method());
  899. default_options->setDataByName("loot_items_rarity", GetPlayer()->GetInfoStruct()->get_group_loot_items_rarity());
  900. default_options->setDataByName("auto_split_coin", GetPlayer()->GetInfoStruct()->get_group_auto_split());
  901. default_options->setDataByName("default_yell_method", GetPlayer()->GetInfoStruct()->get_group_default_yell());
  902. default_options->setDataByName("group_autolock", GetPlayer()->GetInfoStruct()->get_group_autolock());
  903. default_options->setDataByName("default_group_lock_method", GetPlayer()->GetInfoStruct()->get_group_lock_method());
  904. if (GetVersion() > 561) {
  905. default_options->setDataByName("solo_autolock", GetPlayer()->GetInfoStruct()->get_group_solo_autolock());
  906. default_options->setDataByName("auto_loot_method", GetPlayer()->GetInfoStruct()->get_group_auto_loot_method());
  907. }
  908. EQ2Packet* app7 = default_options->serialize();
  909. QueuePacket(app7);
  910. safe_delete(default_options);
  911. }
  912. }
  913. bool Client::HandlePacket(EQApplicationPacket* app) {
  914. bool ret = true;
  915. //cout << "INCOMING PACKET!!!!!!!: " << app->GetOpcodeName() << endl;
  916. //DumpPacket(app);
  917. #if EQDEBUG >= 9
  918. LogWrite(PACKET__DEBUG, 9, "Packet", "[EQDEBUG] Received Packet:");
  919. DumpPacket(app, true);
  920. #endif
  921. EmuOpcode opcode = app->GetOpcode();
  922. #if EQDEBUG >= 9
  923. const char* name = app->GetOpcodeName();
  924. if (name)
  925. cout << name;
  926. else
  927. cout << "Unknown";
  928. cout << " Packet: OPCode: 0x" << hex << setw(2) << setfill('0') << app->GetOpcode() << dec << ", size: " << setw(5) << setfill(' ') << app->Size() << endl;
  929. DumpPacket(app);
  930. #endif
  931. //if (opcode != OP_UpdatePositionMsg) {
  932. // LogWrite(PACKET__DEBUG, 0, "opcode %s received", app->GetOpcodeName());
  933. //}
  934. if (!connected_to_zone && opcode != OP_LoginByNumRequestMsg)
  935. {
  936. opcode = _maxEmuOpcode; // skip since this is not a valid packet, sent before we allowed the login
  937. }
  938. switch (opcode) {
  939. case _maxEmuOpcode:
  940. break;
  941. case OP_LoginByNumRequestMsg: {
  942. LogWrite(OPCODE__DEBUG, 0, "Opcode", "Opcode 0x%X (%i): OP_LoginByNumRequestMsg", opcode, opcode);
  943. PacketStruct* request;
  944. request = configReader.getStruct("LoginByNumRequest", 1);
  945. if (request) {
  946. if (request->LoadPacketData(app->pBuffer, app->size)) {
  947. // test the original location of Version for clients older than 1212
  948. version = request->getType_int16_ByName("version");
  949. if (version == 0 || version >= 1208 || EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) {
  950. // must be new client data version method, re-fetch the packet
  951. safe_delete(request);
  952. request = configReader.getStruct("LoginByNumRequest", 1208);
  953. if (request && request->LoadPacketData(app->pBuffer, app->size)) {
  954. // Xinux suggests using an INT16 here. Our first new version = 57000
  955. version = request->getType_int16_ByName("version");
  956. }
  957. else {
  958. LogWrite(LOGIN__ERROR, 0, "Login", "Nasty Horrible things happening. Tell a dev asap! Version: %i", version);
  959. break;
  960. }
  961. }
  962. if (EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) {
  963. LogWrite(WORLD__ERROR, 0, "World", "Incompatible version: %i", version);
  964. ClientPacketFunctions::SendLoginDenied(this);
  965. /* reset version and protect server from trying to send packets out to a bad client
  966. ** cause of Dec 6th/Dec 7th 2023 crash
  967. ** Client::MakeSpawnChangePacket
  968. ** int16 opcode_val = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(OP_EqUpdateGhostCmd); <-- crashes pulling opcode with bad version
  969. */
  970. version = 546;
  971. ready_for_updates = false;
  972. ready_for_spawns = false;
  973. return false;
  974. }
  975. int32 account_id = request->getType_int32_ByName("account_id");
  976. int32 access_code = request->getType_int32_ByName("access_code");
  977. if (!HandleNewLogin(account_id, access_code))
  978. return false;
  979. }
  980. }
  981. safe_delete(request);
  982. break;
  983. }
  984. case OP_DefaultGroupOptionsMsg: {
  985. PacketStruct* packet = configReader.getStruct("WS_DefaultGroupOptions", GetVersion());
  986. if (packet) {
  987. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  988. packet->PrintPacket();
  989. int8 loot_method = packet->getType_int8_ByName("loot_method");
  990. int8 loot_items_rarity = packet->getType_int8_ByName("loot_items_rarity");
  991. if (GetVersion() <= 561)
  992. loot_items_rarity = 0;
  993. int8 auto_split_coin = packet->getType_int8_ByName("auto_split_coin");
  994. int8 default_yell_method = packet->getType_int8_ByName("default_yell_method");
  995. int8 autolock = packet->getType_int8_ByName("group_autolock");
  996. int8 group_lock_method = packet->getType_int8_ByName("default_group_lock_method");
  997. int8 solo_autolock = packet->getType_int8_ByName("solo_autolock");
  998. int8 auto_loot_method = 0;
  999. if (GetVersion() > 561) {
  1000. auto_loot_method = packet->getType_int8_ByName("auto_loot_method");
  1001. if (auto_loot_method > AutoLootMode::METHOD_DECLINE)
  1002. auto_loot_method = AutoLootMode::METHOD_DECLINE;
  1003. }
  1004. GetPlayer()->GetInfoStruct()->set_group_loot_method(loot_method);
  1005. GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(loot_items_rarity);
  1006. GetPlayer()->GetInfoStruct()->set_group_auto_split(auto_split_coin);
  1007. GetPlayer()->GetInfoStruct()->set_group_default_yell(default_yell_method);
  1008. GetPlayer()->GetInfoStruct()->set_group_autolock(autolock);
  1009. GetPlayer()->GetInfoStruct()->set_group_lock_method(group_lock_method);
  1010. GetPlayer()->GetInfoStruct()->set_group_solo_autolock(solo_autolock);
  1011. GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(auto_loot_method);
  1012. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOOTMETHOD, (char*)std::to_string(loot_method).c_str());
  1013. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOOTITEMRARITY, (char*)std::to_string(loot_items_rarity).c_str());
  1014. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPAUTOSPLIT, (char*)std::to_string(auto_split_coin).c_str());
  1015. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPDEFAULTYELL, (char*)std::to_string(default_yell_method).c_str());
  1016. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPAUTOLOCK, (char*)std::to_string(autolock).c_str());
  1017. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPLOCKMETHOD, (char*)std::to_string(group_lock_method).c_str());
  1018. database.insertCharacterProperty(this, CHAR_PROPERTY_GROUPSOLOAUTOLOCK, (char*)std::to_string(solo_autolock).c_str());
  1019. database.insertCharacterProperty(this, CHAR_PROPERTY_AUTOLOOTMETHOD, (char*)std::to_string(auto_loot_method).c_str());
  1020. if (this->GetPlayer()->GetGroupMemberInfo() && this->GetPlayer()->GetGroupMemberInfo()->leader)
  1021. {
  1022. world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
  1023. PlayerGroup* group = world.GetGroupManager()->GetGroup(this->GetPlayer()->GetGroupMemberInfo()->group_id);
  1024. if (group)
  1025. {
  1026. bool isLeadGroup = group->IsInRaidGroup(group->GetID(), true);
  1027. bool isInRaid = group->IsInRaidGroup(group->GetID());
  1028. if (!isInRaid || isLeadGroup) {
  1029. GroupOptions goptions;
  1030. goptions.loot_method = loot_method;
  1031. goptions.loot_items_rarity = loot_items_rarity;
  1032. goptions.auto_split = auto_split_coin;
  1033. goptions.default_yell = default_yell_method;
  1034. goptions.group_autolock = autolock;
  1035. goptions.solo_autolock = solo_autolock;
  1036. goptions.auto_loot_method = auto_loot_method;
  1037. group->SetDefaultGroupOptions(&goptions);
  1038. // update group options with peers
  1039. std::vector<int32> raidGroups;
  1040. group->GetRaidGroups(&raidGroups);
  1041. std::vector<int32>::iterator group_itr;
  1042. for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
  1043. PlayerGroup* otherGroup = world.GetGroupManager()->GetGroup((*group_itr));
  1044. if (otherGroup)
  1045. otherGroup->SetDefaultGroupOptions(&goptions);
  1046. }
  1047. peer_manager.sendPeersNewGroupRequest("", 0, group->GetID(), "", "", &goptions, "", &raidGroups, true);
  1048. }
  1049. else {
  1050. SimpleMessage(CHANNEL_COLOR_YELLOW, "Group options may only be changed by raid leader.");
  1051. }
  1052. }
  1053. world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
  1054. }
  1055. }
  1056. safe_delete(packet);
  1057. }
  1058. break;
  1059. }
  1060. case OP_MapRequest: {
  1061. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_MapRequest", opcode, opcode);
  1062. PacketStruct* packet = configReader.getStruct("WS_MapRequest", GetVersion());
  1063. if (packet && app->size > 2 && GetCurrentZone()) {
  1064. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1065. PacketStruct* fog_packet = configReader.getStruct("WS_FogInit", GetVersion());
  1066. if (fog_packet) {
  1067. LogWrite(PACKET__DEBUG, 0, "Packet", "In OP_MapRequest: Fog Packet");
  1068. database.LoadFogInit(packet->getType_EQ2_16BitString_ByName("zone").data, fog_packet);
  1069. fog_packet->setDataByName("unknown1", 1);
  1070. fog_packet->setDataByName("unknown3", 1);
  1071. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  1072. //fog_packet->PrintPacket();
  1073. QueuePacket(fog_packet->serialize());
  1074. safe_delete(fog_packet);
  1075. }
  1076. }
  1077. safe_delete(packet);
  1078. }
  1079. break;
  1080. }
  1081. case OP_RequestCampMsg: {
  1082. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_RequestCampMsg", opcode, opcode);
  1083. PacketStruct* request = configReader.getStruct("WS_RequestCamp", GetVersion());
  1084. if (request && request->LoadPacketData(app->pBuffer, app->size)) {
  1085. LogWrite(CCLIENT__DEBUG, 0, "CClient", "Client '%s' (%u) is camping...", GetPlayer()->GetName(), GetPlayer()->GetCharacterID());
  1086. LogWrite(CCLIENT__DEBUG, 0, "CClient", "WS_RequestCamp - quit: %i, camp_desktop: %i, camp_char_select: %i, (to) char_name: %s",
  1087. request->getType_int8_ByName("quit"),
  1088. request->getType_int8_ByName("camp_desktop"),
  1089. request->getType_int16_ByName("camp_char_select"),
  1090. (request->getType_EQ2_16BitString_ByName("char_name").data.length() > 0) ? request->getType_EQ2_16BitString_ByName("char_name").data.c_str() : "");
  1091. //DumpPacket(app->pBuffer, app->size);
  1092. //request->PrintPacket();
  1093. if (!camp_timer) {
  1094. int16 camp_time = 20; // default if rule cannot be found
  1095. if (GetAdminStatus() >= 100)
  1096. camp_time = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, GMCampTimer)->GetInt16();
  1097. else
  1098. camp_time = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, PlayerCampTimer)->GetInt16();
  1099. PacketStruct* response = configReader.getStruct("WS_Camp", GetVersion());
  1100. if (response) {
  1101. bool disconnect = false;
  1102. if (request->getType_int8_ByName("camp_desktop") == 1 && request->getType_int8_ByName("quit") == 1) {
  1103. // Command: /camp desktop
  1104. // Command: /quit
  1105. response->setDataByName("camp_desktop", 1);
  1106. disconnect = true;
  1107. }
  1108. else {
  1109. // Command: /camp
  1110. response->setDataByName("camp_desktop", request->getType_int8_ByName("camp_desktop"));
  1111. response->setDataByName("camp_char_select", request->getType_int16_ByName("camp_char_select"));
  1112. response->setDataByName("seconds", camp_time);
  1113. }
  1114. camp_timer = new Timer(camp_time * 1000);
  1115. camp_timer->Enable();
  1116. if (request->getType_EQ2_16BitString_ByName("char_name").data.length() > 0) {
  1117. // /camp {char_name}
  1118. response->setDataByName("char_name", request->getType_EQ2_16BitString_ByName("char_name").data.c_str());
  1119. }
  1120. else if (request->getType_int8_ByName("camp_desktop") == 0 && request->getType_int16_ByName("camp_char_select") == 0) {
  1121. // /camp (go back to char selection screen)
  1122. response->setDataByName("char_name", " ");
  1123. response->setDataByName("camp_char_select", 1);
  1124. }
  1125. LogWrite(CCLIENT__DEBUG, 0, "CClient", "WS_Camp - seconds: %i, camp_desktop: %i, camp_char_select: %i, (to) char_name: %s",
  1126. response->getType_int8_ByName("seconds"),
  1127. response->getType_int8_ByName("camp_desktop"),
  1128. response->getType_int8_ByName("camp_char_select"),
  1129. (response->getType_EQ2_16BitString_ByName("char_name").data.length() > 0) ? response->getType_EQ2_16BitString_ByName("char_name").data.c_str() : "");
  1130. // JA: trying to recognize /camp vs LD (see ZoneServer::ClientProcess())
  1131. if ((player->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0)
  1132. player->SetActivityStatus(player->GetActivityStatus() + ACTIVITY_STATUS_CAMPING);
  1133. //response->PrintPacket();
  1134. QueuePacket(response->serialize());
  1135. safe_delete(response);
  1136. if (disconnect)
  1137. Disconnect();
  1138. }
  1139. }
  1140. }
  1141. safe_delete(request);
  1142. break;
  1143. }
  1144. case OP_StoodMsg: {
  1145. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StoodMsg", opcode, opcode);
  1146. if (camp_timer)
  1147. {
  1148. // JA: clear camping flag
  1149. if ((player->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) > 0)
  1150. player->SetActivityStatus(player->GetActivityStatus() - ACTIVITY_STATUS_CAMPING);
  1151. safe_delete(camp_timer);
  1152. EQ2Packet* outapp = new EQ2Packet(OP_CampAbortedMsg, 0, 0);
  1153. QueuePacket(outapp);
  1154. }
  1155. player->SetTempVisualState(0);
  1156. break;
  1157. }
  1158. case OP_StandMsg: {
  1159. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StandMsg", opcode, opcode);
  1160. if (camp_timer)
  1161. {
  1162. // JA: clear camping flag
  1163. if ((player->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) > 0)
  1164. player->SetActivityStatus(player->GetActivityStatus() - ACTIVITY_STATUS_CAMPING);
  1165. safe_delete(camp_timer);
  1166. EQ2Packet* outapp = new EQ2Packet(OP_CampAbortedMsg, 0, 0);
  1167. QueuePacket(outapp);
  1168. }
  1169. player->SetTempVisualState(539);
  1170. break;
  1171. }
  1172. case OP_SitMsg: {
  1173. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SitMsg", opcode, opcode);
  1174. player->SetTempVisualState(538);
  1175. break;
  1176. }
  1177. case OP_SatMsg: {
  1178. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SatMsg", opcode, opcode);
  1179. player->SetTempVisualState(540);
  1180. break;
  1181. }
  1182. case OP_QuestJournalOpenMsg:
  1183. case OP_QuestJournalInspectMsg: {
  1184. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_QuestJournalOpenMsg, OP_QuestJournalInspectMsg", opcode, opcode);
  1185. if (app->size < sizeof(int32))
  1186. break;
  1187. int32 quest_id = 0;
  1188. memcpy(&quest_id, app->pBuffer, sizeof(int32));
  1189. GetPlayer()->SendQuest(quest_id);
  1190. break;
  1191. }
  1192. case OP_QuestJournalSetVisibleMsg: {
  1193. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_QuestJournalSetVisibleMsg", opcode, opcode);
  1194. PacketStruct* packet = configReader.getStruct("WS_QuestJournalVisible", GetVersion());
  1195. if (packet) {
  1196. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1197. int32 quest_id = packet->getType_int32_ByName("quest_id");
  1198. bool hidden = packet->getType_int8_ByName("visible") == 1 ? false : true;
  1199. GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
  1200. map<int32, Quest*>* player_quests = player->GetPlayerQuests();
  1201. if (player_quests) {
  1202. if (player_quests->count(quest_id) > 0)
  1203. player_quests->at(quest_id)->SetHidden(hidden);
  1204. else
  1205. LogWrite(CCLIENT__ERROR, 0, "Client", "OP_QuestJournalSetVisibleMsg error: Player does not have quest with id of %u", quest_id);
  1206. }
  1207. else
  1208. LogWrite(CCLIENT__ERROR, 0, "Client", "OP_QuestJournalSetVisibleMsg error: Unable to get player(%s) quests", player->GetName());
  1209. GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
  1210. }
  1211. safe_delete(packet);
  1212. }
  1213. break;
  1214. }
  1215. case OP_MacroUpdateMsg: {
  1216. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_MacroUpdateMsg", opcode, opcode);
  1217. PacketStruct* macro_update = configReader.getStruct("WS_MacroUpdate", GetVersion());
  1218. if (macro_update) {
  1219. if (macro_update->LoadPacketData(app->pBuffer, app->size)) {
  1220. vector<string>* update = new vector<string>;
  1221. int8 number = macro_update->getType_int8_ByName("number");
  1222. int16 icon = macro_update->getType_int16_ByName("icon");
  1223. string name = macro_update->getType_EQ2_8BitString_ByName("name").data;
  1224. int8 count = macro_update->getType_int8_ByName("macro_count");
  1225. if (GetVersion() <= 373) {
  1226. update->push_back(macro_update->getType_EQ2_8BitString_ByName("command").data);
  1227. }
  1228. else {
  1229. for (int8 i = 0; i < count; i++) {
  1230. char tmp_command[15] = { 0 };
  1231. sprintf(tmp_command, "command_%i", i);
  1232. update->push_back(macro_update->getType_EQ2_16BitString_ByName(tmp_command).data);
  1233. }
  1234. }
  1235. if (name.length() == 0)
  1236. database.UpdateCharacterMacro(GetCharacterID(), number, 0, icon, update);
  1237. else
  1238. database.UpdateCharacterMacro(GetCharacterID(), number, name.c_str(), icon, update);
  1239. safe_delete(update);
  1240. }
  1241. safe_delete(macro_update);
  1242. }
  1243. break;
  1244. }
  1245. case OP_DialogSelectMsg: {
  1246. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_DialogSelectMsg", opcode, opcode);
  1247. PacketStruct* packet = configReader.getStruct("WS_DialogSelect", GetVersion());
  1248. if (packet) {
  1249. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1250. int32 conversation_id = packet->getType_int32_ByName("conversation_id");
  1251. int32 response_index = packet->getType_int32_ByName("response");
  1252. HandleDialogSelectMsg(conversation_id, response_index);
  1253. }
  1254. }
  1255. safe_delete(packet);
  1256. break;
  1257. }
  1258. case OP_CancelMoveObjectModeMsg: {
  1259. SetSpawnPlacementMode(Client::ServerSpawnPlacementMode::DEFAULT);
  1260. if (GetTempPlacementSpawn())
  1261. {
  1262. Spawn* tmp = GetTempPlacementSpawn();
  1263. SetTempPlacementSpawn(nullptr);
  1264. SetPlacementUniqueItemID(0);
  1265. GetCurrentZone()->RemoveSpawn(tmp, true, false, true, true, true);
  1266. break; // break out early if we are tied to a temp spawn
  1267. }
  1268. // if we are moving some other object? other use-cases not covered
  1269. break;
  1270. }
  1271. case OP_PositionMoveableObject: {
  1272. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_PositionMoveableObject", opcode, opcode);
  1273. PacketStruct* place_object = configReader.getStruct("WS_PlaceMoveableObject", GetVersion());
  1274. if (place_object && place_object->LoadPacketData(app->pBuffer, app->size)) {
  1275. Spawn* spawn = 0;
  1276. bool was_temp_placement = false;
  1277. if (GetTempPlacementSpawn()) {
  1278. spawn = GetTempPlacementSpawn();
  1279. was_temp_placement = true;
  1280. }
  1281. else
  1282. spawn = GetPlayer()->GetSpawnWithPlayerID(place_object->getType_int32_ByName("spawn_id"));
  1283. if (!spawn) {
  1284. SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find spawn.");
  1285. break;
  1286. }
  1287. else if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE && !HasOwnerOrEditAccess())
  1288. {
  1289. SimpleMessage(CHANNEL_COLOR_RED, "This is not your home!");
  1290. break;
  1291. }
  1292. int32 uniqueID = spawn->GetPickupUniqueItemID();
  1293. if (uniqueID) {
  1294. Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
  1295. if (uniqueItem && uniqueItem->CheckFlag2(HOUSE_LORE) && GetCurrentZone()->HouseItemSpawnExists(uniqueItem->details.item_id)) {
  1296. Message(CHANNEL_COLOR_RED, "Item %s is house lore and you cannot place another.", uniqueItem->name.c_str());
  1297. break;
  1298. }
  1299. }
  1300. // handles instantiation logic + adding to zone of a new house object
  1301. PopulateHouseSpawn(place_object);
  1302. float newHeading = place_object->getType_float_ByName("heading") + 180;
  1303. char query[256];
  1304. switch (GetSpawnPlacementMode())
  1305. {
  1306. case ServerSpawnPlacementMode::OPEN_HEADING:
  1307. {
  1308. if (spawn && spawn->IsWidget())
  1309. {
  1310. Widget* widget = (Widget*)spawn;
  1311. widget->SetOpenHeading(newHeading);
  1312. widget->SetIncludeHeading(true);
  1313. spawn->position_changed = true;
  1314. _snprintf(query, 256, "open_heading=%f,include_heading=1", newHeading);
  1315. if (database.UpdateSpawnWidget(widget->GetWidgetID(), query))
  1316. SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget open heading information.");
  1317. }
  1318. else
  1319. SimpleMessage(CHANNEL_COLOR_YELLOW, "Spawn is not widget, unable to set close heading information.");
  1320. break;
  1321. }
  1322. case ServerSpawnPlacementMode::CLOSE_HEADING:
  1323. {
  1324. if (spawn && spawn->IsWidget())
  1325. {
  1326. Widget* widget = (Widget*)spawn;
  1327. widget->SetClosedHeading(newHeading);
  1328. widget->SetIncludeHeading(true);
  1329. spawn->position_changed = true;
  1330. _snprintf(query, 256, "closed_heading=%f,include_heading=1", newHeading);
  1331. if (database.UpdateSpawnWidget(widget->GetWidgetID(), query))
  1332. SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget close heading information.");
  1333. if (spawn->GetSpawnLocationID())
  1334. {
  1335. Query query;
  1336. query.RunQuery2(Q_INSERT, "update spawn_location_placement set heading = %f where id = %u", newHeading, spawn->GetSpawnLocationID());
  1337. }
  1338. }
  1339. else
  1340. SimpleMessage(CHANNEL_COLOR_YELLOW, "Spawn is not widget, unable to set close heading information.");
  1341. break;
  1342. }
  1343. default:
  1344. {
  1345. spawn->SetX(place_object->getType_float_ByName("x"));
  1346. spawn->SetY(place_object->getType_float_ByName("y"));
  1347. spawn->SetZ(place_object->getType_float_ByName("z"));
  1348. spawn->SetHeading(newHeading);
  1349. spawn->SetSpawnOrigX(spawn->GetX());
  1350. spawn->SetSpawnOrigY(spawn->GetY());
  1351. spawn->SetSpawnOrigZ(spawn->GetZ());
  1352. spawn->SetSpawnOrigHeading(spawn->GetHeading());
  1353. if (spawn->GetSpawnLocationID() > 0 && database.UpdateSpawnLocationSpawns(spawn)) {
  1354. if (!was_temp_placement) {
  1355. SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved spawn information.");
  1356. }
  1357. }
  1358. else if (spawn->GetSpawnLocationID() > 0) {
  1359. SimpleMessage(CHANNEL_COLOR_YELLOW, "Error saving spawn information, see console window for details.");
  1360. }
  1361. }
  1362. }
  1363. PopulateHouseSpawnFinalize();
  1364. SetSpawnPlacementMode(Client::ServerSpawnPlacementMode::DEFAULT);
  1365. }
  1366. safe_delete(place_object);
  1367. break;
  1368. }
  1369. case OP_CampAbortedMsg: {
  1370. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_CampAbortedMsg", opcode, opcode);
  1371. if (camp_timer)
  1372. {
  1373. // JA: clear camping flag
  1374. if ((player->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) > 0)
  1375. player->SetActivityStatus(player->GetActivityStatus() - ACTIVITY_STATUS_CAMPING);
  1376. safe_delete(camp_timer);
  1377. EQ2Packet* outapp = new EQ2Packet(OP_CampAbortedMsg, 0, 0);
  1378. QueuePacket(outapp);
  1379. }
  1380. break;
  1381. }
  1382. case OP_DoneLoadingUIResourcesMsg: {
  1383. if (GetVersion() <= 561) {
  1384. ClientPacketFunctions::SendUpdateSpellBook(this);
  1385. }
  1386. // need to quickly flash the DoF client the rest of their inventory
  1387. if (GetVersion() <= 561) {
  1388. EQ2Packet* item_app = player->GetPlayerItemList()->serialize(GetPlayer(), GetVersion());
  1389. if (item_app) {
  1390. QueuePacket(item_app);
  1391. }
  1392. }
  1393. EQ2Packet* app = new EQ2Packet(OP_DoneLoadingUIResourcesMsg, 0, 0);
  1394. QueuePacket(app);
  1395. if (!player_loading_complete)
  1396. {
  1397. const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID());
  1398. if (zone_script && lua_interface)
  1399. lua_interface->RunZoneScript(zone_script, "player_loadcomplete", GetCurrentZone(), GetPlayer());
  1400. player_loading_complete = true;
  1401. }
  1402. break;
  1403. }
  1404. case OP_DoneLoadingZoneResourcesMsg: {
  1405. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_DoneLoadingZoneResourcesMsg", opcode, opcode);
  1406. SendZoneSpawns();
  1407. break;
  1408. }
  1409. case OP_DefaultGroupOptionsRequestMsg: {
  1410. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_DefaultGroupOptionsRequestMsg", opcode, opcode);
  1411. SendDefaultGroupOptions();
  1412. break;
  1413. }
  1414. case OP_DoneLoadingEntityResourcesMsg: {
  1415. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_DoneLoadingEntityResourcesMsg", opcode, opcode);
  1416. if (!IsReadyForSpawns()) {
  1417. if (GetPlayer()->GetMap()) {
  1418. auto loc = glm::vec3(GetPlayer()->GetX(), GetPlayer()->GetZ(), GetPlayer()->GetY());
  1419. uint32 GridID = 0;
  1420. float new_z = GetPlayer()->FindBestZ(loc, nullptr, &GridID);
  1421. GetPlayer()->SetLocation(GridID);
  1422. }
  1423. SetReadyForSpawns(true);
  1424. }
  1425. player->CalculateApplyWeight();
  1426. SendCharInfo();
  1427. GetPlayer()->GetZone()->GetSpellProcess()->SendSpellBookUpdate(this);
  1428. pos_update.Start();
  1429. quest_pos_timer.Start();
  1430. break;
  1431. }
  1432. case OP_LootItemsRequestMsg: {
  1433. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_LootItemsRequestMsg", opcode, opcode);
  1434. HandleLootItemRequestPacket(app);
  1435. break;
  1436. }
  1437. case OP_StoppedLootingMsg: {
  1438. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StoppedLootingMsg", opcode, opcode);
  1439. if (app->size < sizeof(int32))
  1440. break;
  1441. int32 loot_id = 0;
  1442. memcpy(&loot_id, app->pBuffer, sizeof(int32));
  1443. Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id);
  1444. if (spawn) {
  1445. spawn->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
  1446. spawn->SetLooterSpawnID(0);
  1447. }
  1448. break;
  1449. }
  1450. case OP_WaypointSelectMsg: {
  1451. PacketStruct* packet = configReader.getStruct("WS_WaypointSelect", GetVersion());
  1452. if (packet) {
  1453. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1454. int32 selection = packet->getType_int32_ByName("selection");
  1455. if (selection > 0) {
  1456. SelectWaypoint(selection);
  1457. }
  1458. }
  1459. }
  1460. safe_delete(packet);
  1461. break;
  1462. }
  1463. case OP_KnowledgeWindowSlotMappingMsg: {
  1464. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_KnowledgeWindowSlotMappingMsg", opcode, opcode);
  1465. PacketStruct* packet = configReader.getStruct("WS_SpellSlotMapping", GetVersion());
  1466. if (packet) {
  1467. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1468. int num_updates = packet->getType_int16_ByName("spell_count");
  1469. int32 spell_id = 0;
  1470. int16 slot_id = 0;
  1471. char tmp_spell_id[15];
  1472. char tmp_slot[15];
  1473. for (int i = 0; i < num_updates; i++) {
  1474. memset(tmp_spell_id, 0, 15);
  1475. memset(tmp_slot, 0, 15);
  1476. sprintf(tmp_spell_id, "spell_id_%i", i);
  1477. sprintf(tmp_slot, "slot_id_%i", i);
  1478. spell_id = packet->getType_int32_ByName(tmp_spell_id);
  1479. if (spell_id > 0) {
  1480. slot_id = packet->getType_int16_ByName(tmp_slot);
  1481. SpellBookEntry* spell = player->GetSpellBookSpell(spell_id);
  1482. if (spell && spell->slot != slot_id) {
  1483. spell->slot = slot_id;
  1484. spell->save_needed = true;
  1485. }
  1486. }
  1487. }
  1488. }
  1489. safe_delete(packet);
  1490. }
  1491. //SendKnowledgeWindowSlot();
  1492. break;
  1493. }
  1494. case OP_ReadyToZoneMsg: {
  1495. if (!IsReadyForSpawns())
  1496. {
  1497. LogWrite(WORLD__INFO, 0, "World", "OP_ReadyToZone: Player %s is logging into zone, skipping disconnect.");
  1498. }
  1499. else
  1500. {
  1501. if (zoning_destination) {
  1502. SetCurrentZone(zoning_destination);
  1503. }
  1504. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ReadyToZoneMsg", opcode, opcode);
  1505. if (client_zoning)
  1506. LogWrite(WORLD__INFO, 0, "World", "OP_ReadyToZone: Player %s zoning to %u, instance id %u", player->GetName(), zoning_id, zoning_instance_id);
  1507. else
  1508. LogWrite(WORLD__ERROR, 0, "World", "OP_ReadyToZone: Player %s attempting to zone without server authorization.", player->GetName());
  1509. Disconnect();
  1510. }
  1511. break;
  1512. }
  1513. case OP_ClientFellMsg: {
  1514. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ClientFellMsg (ouch!)", opcode, opcode);
  1515. PacketStruct* request = configReader.getStruct("WS_ClientFell", GetVersion());
  1516. if (request && request->LoadPacketData(app->pBuffer, app->size)) {
  1517. float height = request->getType_float_ByName("height");
  1518. /*int32 spawn_id = request->getType_int32_ByName("spawn_id");
  1519. if(GetPlayer()->GetSpawnWithPlayerID(spawn_id) != GetPlayer()){
  1520. cout << "Error: " << GetPlayer()->GetName() << " called ClientFell with an invalid ID of: " << spawn_id << endl;
  1521. break;
  1522. }*/
  1523. float safe_height = 13.0f;
  1524. float safe_skill_with_bonus = GetPlayer()->CalculateSkillWithBonus("Safe Fall", ITEM_STAT_SAFE_FALL, true);
  1525. if (safe_skill_with_bonus > 0.0f)
  1526. safe_height += (1 + safe_skill_with_bonus) / 5;
  1527. if (height > safe_height) {
  1528. int16 damage = (int16)ceil((height - safe_height) * 125);
  1529. if (height >= 80)
  1530. damage = 30000;
  1531. //cout << "Detected fall height:" << height << " damage:" << damage << endl;
  1532. if (damage > 0) {
  1533. GetPlayer()->TakeDamage(damage);
  1534. if (GetPlayer()->GetPlayerStatisticValue(STAT_PLAYER_HIGHEST_FALLING_HIT) < damage)
  1535. GetPlayer()->UpdatePlayerStatistic(STAT_PLAYER_HIGHEST_FALLING_HIT, damage, true);
  1536. if (!GetPlayer()->GetInvulnerable())
  1537. GetPlayer()->SetCharSheetChanged(true);
  1538. GetCurrentZone()->SendDamagePacket(0, GetPlayer(), DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, GetPlayer()->GetInvulnerable() ? DAMAGE_PACKET_RESULT_INVULNERABLE : DAMAGE_PACKET_RESULT_SUCCESSFUL, DAMAGE_PACKET_DAMAGE_TYPE_FALLING, damage, 0);
  1539. if (GetPlayer()->GetHP() == 0) {
  1540. GetCurrentZone()->KillSpawn(false, GetPlayer(), 0);
  1541. }
  1542. }
  1543. }
  1544. }
  1545. safe_delete(request);
  1546. break;
  1547. }
  1548. case OP_MapFogDataUpdateMsg: {
  1549. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_MapFogDataUpdateMsg", opcode, opcode);
  1550. LogWrite(MISC__TODO, 3, "TODO", "Handle (OP_MapFogDataUpdateMsg), ignoring it for now\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
  1551. break;
  1552. }
  1553. case OP_SelectZoneTeleporterDestinatio: {
  1554. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SelectZoneTeleporterDestinatio", opcode, opcode);
  1555. ProcessTeleportLocation(app);
  1556. break;
  1557. }
  1558. case OP_SendLatestRequestMsg: {
  1559. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SendLatestRequestMsg", opcode, opcode);
  1560. if (GetVersion() < 60085) {
  1561. // this does not exist in newer clients like AoM, confirmed to exist in DoF, other clients will need review at a later time
  1562. uchar blah25[] = { 0x01 };
  1563. EQ2Packet* app25 = new EQ2Packet(OP_ClearDataMsg, blah25, sizeof(blah25));
  1564. QueuePacket(app25);
  1565. }
  1566. break;
  1567. }
  1568. case OP_RequestRecipeDetailsMsg: {
  1569. PacketStruct* packet = configReader.getStruct("WS_RequestRecipeDetail", GetVersion());
  1570. if (packet) {
  1571. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1572. vector<int32> recipes;
  1573. int32 recipe_id = 0;
  1574. char recipe_prop_name[30];
  1575. int32 num_recipes = packet->getType_int32_ByName("num_recipes");
  1576. // WS_RecipeDetails
  1577. for (int32 i = 0; i < num_recipes; i++) {
  1578. memset(recipe_prop_name, 0, 30);
  1579. snprintf(recipe_prop_name, 30, "recipe_id_%i", i);
  1580. recipe_id = packet->getType_int32_ByName(recipe_prop_name);
  1581. if (recipe_id > 0) {
  1582. recipes.push_back(recipe_id);
  1583. }
  1584. }
  1585. SendRecipeDetails(&recipes);
  1586. }
  1587. safe_delete(packet);
  1588. }
  1589. break;
  1590. }
  1591. case OP_ShowCreateFromRecipeUIMsg: {
  1592. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ShowCreateFromRecipeUIMsg", opcode, opcode);
  1593. break;
  1594. /*uchar blah[] ={0x09,0x0e,0x00,0x51,0x75,0x65,0x65,0x6e,0x27,0x73,0x20,0x43,0x6f,0x6c,0x6f,0x6e
  1595. ,0x79,0x00,0x00,0x00,0x00,0x40,0x40,0xff,0xff,0xff};
  1596. EQ2Packet* app = new EQ2Packet(OP_EncounterBrokenMsg, blah, sizeof(blah));
  1597. QueuePacket(app);
  1598. uchar blah2[] = {0x00,0x00,0xff,0xff,0xff,0xff};
  1599. app = new EQ2Packet(OP_CreateCharFromCBBRequestMsg, blah2, sizeof(blah2));
  1600. QueuePacket(app);
  1601. uchar blah3[] ={0x09,0x17,0x00,0x5c,0x23,0x46,0x46,0x45,0x34,0x30,0x30,0x20,0x51,0x75,0x65,0x65
  1602. ,0x6e,0x27,0x73,0x20,0x43,0x6f,0x6c,0x6f,0x6e,0x79,0x00,0x00,0x00,0x00,0xa0,0x40
  1603. ,0xff,0xff,0xff};
  1604. app = new EQ2Packet(OP_CreateCharFromCBBRequestMsg, blah3, sizeof(blah3));
  1605. QueuePacket(app);
  1606. uchar blah4[] ={0x0b,0x00,0x21,0x00,0x00,0x00,0x1d,0x81,0x42,0x17,0x81,0x42,0x17,0x81,0x42,0x17
  1607. ,0x81,0x42,0x17,0x81,0x42,0x17,0x81,0x42,0x17,0x81,0x42,0x17,0x81,0x42,0x17,0x81
  1608. ,0x42,0x17,0x81,0x42,0x17,0x81,0x42};
  1609. app = new EQ2Packet(OP_UpdateSpellBookMsg, blah4, sizeof(blah4));
  1610. QueuePacket(app);
  1611. uchar blah5[] ={0x00,0x00};
  1612. app = new EQ2Packet(OP_RecipeDetailsMsg, blah5, sizeof(blah5));
  1613. QueuePacket(app);
  1614. break;*/
  1615. //player->GetPlayerInfo()->GetInfo()->cur_power = 100;
  1616. //EQ2Packet* app = player->GetPlayerInfo()->serialize(1);
  1617. //QueuePacket(app);
  1618. }
  1619. case OP_BeginItemCreationMsg: {
  1620. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_BeginItemCreationMsg", opcode, opcode);
  1621. //DumpPacket(app->pBuffer, app->size);
  1622. PacketStruct* packet = configReader.getStruct("WS_BeginItemCreation", GetVersion());
  1623. if (packet) {
  1624. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1625. Recipe* recipe = master_recipe_list.GetRecipe(GetPlayer()->GetCurrentRecipe());
  1626. if (recipe) {
  1627. int32 item = 0;
  1628. int8 qty = 0;
  1629. vector<pair<int32, int16>> items;
  1630. char tmp_item_id[30];
  1631. if (GetVersion() > 1193) {
  1632. int8 num_primary_selected_items = packet->getType_int8_ByName("num_primary_selected_items");
  1633. for (int8 i = 0; i < num_primary_selected_items; i++) {
  1634. memset(tmp_item_id, 0, 30);
  1635. sprintf(tmp_item_id, "primary_selected_item_id_%i", i);
  1636. item = packet->getType_int32_ByName(tmp_item_id);
  1637. sprintf(tmp_item_id, "primary_selected_item_qty_%i", i);
  1638. qty = packet->getType_int16_ByName(tmp_item_id);
  1639. if (item > 0)
  1640. items.push_back(make_pair(item, qty));
  1641. item = 0;
  1642. }
  1643. }
  1644. else {
  1645. item = packet->getType_int32_ByName("primary_component_id");
  1646. qty = 1;
  1647. if (item > 0)
  1648. items.push_back(make_pair(item, qty));
  1649. }
  1650. int8 build_components = packet->getType_int8_ByName("num_build_components");
  1651. if (GetVersion() > 1193) {
  1652. for (int8 i = 0; i < build_components; i++) {
  1653. memset(tmp_item_id, 0, 30);
  1654. sprintf(tmp_item_id, "num_selected_items_%i", i);
  1655. int8 num_selected_items = packet->getType_int8_ByName(tmp_item_id);
  1656. for (int8 j = 0; j < num_selected_items; j++) {
  1657. memset(tmp_item_id, 0, 30);
  1658. sprintf(tmp_item_id, "selected_id%i_%i", i, j);
  1659. item = packet->getType_int32_ByName(tmp_item_id);
  1660. sprintf(tmp_item_id, "selected_qty%i_%i", i, j);
  1661. qty = packet->getType_int16_ByName(tmp_item_id);
  1662. if (item > 0)
  1663. items.push_back(make_pair(item, qty));
  1664. item = 0;
  1665. }
  1666. }
  1667. }
  1668. else {
  1669. for (int8 i = 0; i < build_components; i++) {
  1670. memset(tmp_item_id, 0, 30);
  1671. sprintf(tmp_item_id, "component_id_%i", i);
  1672. int32 item = packet->getType_int32_ByName(tmp_item_id);
  1673. sprintf(tmp_item_id, "component_qty_%i", i);
  1674. qty = packet->getType_int32_ByName(tmp_item_id);
  1675. if (item > 0)
  1676. items.push_back(make_pair(item, qty));
  1677. }
  1678. }
  1679. if (GetVersion() > 1193) {
  1680. int8 num_fuel_items = packet->getType_int8_ByName("num_fuel_items");
  1681. for (int8 i = 0; i < num_fuel_items; i++) {
  1682. memset(tmp_item_id, 0, 30);
  1683. sprintf(tmp_item_id, "fuel_id_%i", i);
  1684. item = packet->getType_int32_ByName(tmp_item_id);
  1685. sprintf(tmp_item_id, "fuel_qty_%i", i);
  1686. qty = packet->getType_int16_ByName(tmp_item_id);
  1687. if (item > 0)
  1688. items.push_back(make_pair(item, qty));
  1689. item = 0;
  1690. }
  1691. }
  1692. else {
  1693. item = packet->getType_int32_ByName("fuel_id");
  1694. qty = packet->getType_int16_ByName("fuel_qty");
  1695. if (item > 0)
  1696. items.push_back(make_pair(item, qty));
  1697. }
  1698. GetCurrentZone()->GetTradeskillMgr()->BeginCrafting(this, items);
  1699. }
  1700. else {
  1701. LogWrite(CCLIENT__ERROR, 0, "Client", "Client '%s' (%u) attempted to call OP_BeginItemCreationMsg, but with no recipe selected.", GetPlayer()->GetName(), GetPlayer()->GetCharacterID());
  1702. }
  1703. }
  1704. safe_delete(packet);
  1705. }
  1706. break;
  1707. }
  1708. case OP_StopItemCreationMsg: {
  1709. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StopItemCreationMsg", opcode, opcode);
  1710. //DumpPacket(app->pBuffer, app->size);
  1711. GetCurrentZone()->GetTradeskillMgr()->StopCrafting(this);
  1712. break;
  1713. }
  1714. case OP_SysClient:
  1715. case OP_SignalMsg: {
  1716. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SysClient/OP_SignalMsg", opcode, opcode);
  1717. PacketStruct* packet = configReader.getStruct("WS_Signal", 1);
  1718. if (packet) {
  1719. if (packet->LoadPacketData(app->pBuffer, app->size) && player->GetZone()) {
  1720. EQ2_16BitString str = packet->getType_EQ2_16BitString_ByName("signal");
  1721. if (strcmp(str.data.c_str(), "sys_client_avatar_ready") == 0) {
  1722. LogWrite(CCLIENT__DEBUG, 0, "Client", "Client '%s' (%u) is ready for spawn updates.", GetPlayer()->GetName(), GetPlayer()->GetCharacterID());
  1723. SetReloadingZone(false);
  1724. if (GetPlayer()->IsDeletedSpawn()) {
  1725. GetPlayer()->SetDeletedSpawn(false);
  1726. }
  1727. ResetZoningCoords();
  1728. SetReadyForUpdates();
  1729. GetPlayer()->SendSpawnChanges(true);
  1730. ProcessStateCommands();
  1731. GetPlayer()->changed = true;
  1732. GetPlayer()->info_changed = true;
  1733. GetPlayer()->vis_changed = true;
  1734. player_pos_changed = true;
  1735. GetPlayer()->AddChangedZoneSpawn();
  1736. ProcessZoneIgnoreWidgets();
  1737. if (version <= 561) {
  1738. master_trait_list.ChooseNextTrait(this);
  1739. Guild* guild = GetPlayer()->GetGuild();
  1740. if(guild)
  1741. guild->SendGuildMemberList();
  1742. }
  1743. const char* zone_script = world.GetZoneScript(GetPlayer()->GetZone()->GetZoneID());
  1744. if (zone_script && lua_interface) {
  1745. lua_interface->RunZoneScript(zone_script, "enter_location", GetPlayer()->GetZone(), GetPlayer(), GetPlayer()->GetLocation());
  1746. }
  1747. if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower())
  1748. GetCurrentZone()->AddDamagedSpawn(GetPlayer());
  1749. }
  1750. else {
  1751. LogWrite(CCLIENT__WARNING, 0, "Client", "Player %s reported SysClient/SignalMsg state %s.", GetPlayer()->GetName(), str.data.c_str());
  1752. }
  1753. const char* zone_script = world.GetZoneScript(player->GetZone()->GetZoneID());
  1754. if (zone_script && lua_interface)
  1755. {
  1756. lua_interface->RunZoneScript(zone_script, "signal_changed", player->GetZone(), player, 0, str.data.c_str());
  1757. }
  1758. }
  1759. safe_delete(packet);
  1760. }
  1761. break;
  1762. }
  1763. case OP_EntityVerbsRequestMsg: {
  1764. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_EntityVerbsRequestMsg", opcode, opcode);
  1765. HandleVerbRequest(app);
  1766. break;
  1767. }
  1768. case OP_EntityVerbsVerbMsg: {
  1769. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_EntityVerbsVerbMsg", opcode, opcode);
  1770. PacketStruct* packet = configReader.getStruct("WS_EntityVerbsVerb", GetVersion());
  1771. if (packet) {
  1772. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1773. int32 spawn_id = packet->getType_int32_ByName("spawn_id");
  1774. Spawn* spawn = player->GetSpawnWithPlayerID(spawn_id); // fixed using GetTarget and the target was never set causing commands not to work
  1775. player->SetTarget(spawn);
  1776. if (spawn && !spawn->IsNPC() && !spawn->IsPlayer()) {
  1777. string command = packet->getType_EQ2_16BitString_ByName("command").data;
  1778. if (!HandleHouseEntityCommands(spawn, spawn_id, command))
  1779. {
  1780. if (EntityCommandPrecheck(spawn, command.c_str())) {
  1781. if (spawn->IsGroundSpawn())
  1782. ((GroundSpawn*)spawn)->HandleUse(this, command);
  1783. else if (spawn->IsObject())
  1784. ((Object*)spawn)->HandleUse(this, command);
  1785. else if (spawn->IsWidget())
  1786. ((Widget*)spawn)->HandleUse(this, command);
  1787. else if (spawn->IsSign())
  1788. ((Sign*)spawn)->HandleUse(this, command);
  1789. }
  1790. }
  1791. }
  1792. else {
  1793. EQ2_16BitString command = packet->getType_EQ2_16BitString_ByName("command");
  1794. if (command.size > 0) {
  1795. string command_name = command.data;
  1796. if (command_name.find(" ") < 0xFFFFFFFF) {
  1797. if (GetVersion() <= 561) { //this version uses commands in the form "Buy From Merchant" instead of buy_from_merchant
  1798. string::size_type pos = command_name.find(" ");
  1799. while (pos != string::npos) {
  1800. command_name.replace(pos, 1, "_");
  1801. pos = command_name.find(" ");
  1802. }
  1803. }
  1804. else
  1805. command_name = command_name.substr(0, command_name.find(" "));
  1806. }
  1807. int32 handler = commands.GetCommandHandler(command_name.c_str());
  1808. if (handler != 0xFFFFFFFF) {
  1809. if (command.data == command_name) {
  1810. command.data = "";
  1811. command.size = 0;
  1812. }
  1813. else {
  1814. command.data = command.data.substr(command.data.find(" ") + 1);
  1815. command.size = command.data.length();
  1816. }
  1817. commands.Process(handler, &command, this);
  1818. }
  1819. else {
  1820. if (spawn && spawn->IsNPC()) {
  1821. if (EntityCommandPrecheck(spawn, command.data.c_str())) {
  1822. if (!((NPC*)spawn)->HandleUse(this, command.data)) {
  1823. command_name = command.data;
  1824. string::size_type pos = command_name.find(" ");
  1825. while (pos != string::npos) {
  1826. command_name.replace(pos, 1, "_");
  1827. pos = command_name.find(" ");
  1828. }
  1829. if (!((NPC*)spawn)->HandleUse(this, command_name)) { //convert the spaces to underscores and see if that makes a difference
  1830. LogWrite(WORLD__ERROR, 0, "World", "Unhandled command in OP_EntityVerbsVerbMsg: %s", command.data.c_str());
  1831. }
  1832. }
  1833. }
  1834. }
  1835. else
  1836. LogWrite(WORLD__ERROR, 0, "World", "Unknown command in OP_EntityVerbsVerbMsg: %s", command.data.c_str());
  1837. }
  1838. }
  1839. }
  1840. }
  1841. safe_delete(packet);
  1842. }
  1843. break;
  1844. }
  1845. case OP_SkillInfoRequest: {
  1846. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SkillInfoRequest", opcode, opcode);
  1847. HandleSkillInfoRequest(app);
  1848. break;
  1849. }
  1850. case OP_UpdateTargetMsg: {
  1851. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_UpdateTargetMsg", opcode, opcode);
  1852. int16 index = 0;
  1853. memcpy(&index, app->pBuffer, sizeof(int16));
  1854. if (index == 0xFFFF)
  1855. GetPlayer()->SetTarget(0);
  1856. else {
  1857. Spawn* spawn = GetPlayer()->GetSpawnByIndex(index);
  1858. if (spawn)
  1859. GetPlayer()->SetTarget(spawn);
  1860. else {
  1861. LogWrite(PLAYER__ERROR, 1, "Player", "Player %s tried to target %u index, but that index was not valid.", GetPlayer()->GetName(), index);
  1862. }
  1863. }
  1864. if (GetPlayer()->GetTarget())
  1865. GetCurrentZone()->CallSpawnScript(GetPlayer()->GetTarget(), SPAWN_SCRIPT_TARGETED, GetPlayer());
  1866. //player->SetTarget((int16*)app->pBuffer);
  1867. break;
  1868. }
  1869. case OP_ExamineInfoRequestMsg: {
  1870. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ExamineInfoRequestMsg", opcode, opcode);
  1871. HandleExamineInfoRequest(app);
  1872. break;
  1873. }
  1874. case OP_QuickbarUpdateMsg:
  1875. //case OP_QuickbarAddMsg:
  1876. {
  1877. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_QuickbarUpdateMsg, OP_QuickbarAddMsg", opcode, opcode);
  1878. HandleQuickbarUpdateRequest(app);
  1879. break;
  1880. }
  1881. case OP_PredictionUpdateMsg: {
  1882. LogWrite(OPCODE__DEBUG, 7, "Opcode", "Opcode 0x%X (%i): OP_PredictionUpdateMsg from %s", opcode, opcode, GetPlayer()->GetName());
  1883. if (version <= 561 && GetPlayer() && GetPlayer()->GetZone()) {
  1884. int8 offset = 9;
  1885. if (app->pBuffer[0] == 0xFF)
  1886. offset += 2;
  1887. if (app->size > offset) {
  1888. if (player->IsCasting()) {
  1889. float distance = 0;
  1890. float x = player->GetX();
  1891. float y = player->GetY();
  1892. float z = player->GetZ();
  1893. player->PrepareIncomingMovementPacket(app->size - offset, app->pBuffer + offset, version);
  1894. distance = player->GetDistance(x, y, z, false);
  1895. if (distance > .5)
  1896. current_zone->Interrupted(player, 0, SPELL_ERROR_INTERRUPTED, false, true);
  1897. }
  1898. else
  1899. player->PrepareIncomingMovementPacket(app->size - offset, app->pBuffer + offset, version);
  1900. player_pos_changed = true;
  1901. GetPlayer()->changed = true;
  1902. GetPlayer()->info_changed = true;
  1903. GetPlayer()->vis_changed = true;
  1904. GetPlayer()->AddChangedZoneSpawn();
  1905. //DumpPacket(app);
  1906. }
  1907. }
  1908. else {
  1909. EQ2Packet* app = new EQ2Packet(OP_PredictionUpdateMsg, 0, 0);
  1910. QueuePacket(app);
  1911. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  1912. }
  1913. break;
  1914. }
  1915. case OP_RemoteCmdMsg: {
  1916. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_RemoteCmdMsg", opcode, opcode);
  1917. if (app->size > 0) {
  1918. EQ2_CommandString remote(app->pBuffer, app->size);
  1919. LogWrite(PACKET__DEBUG, 1, "Packet", "RemoteCmdMsg Packet dump:");
  1920. #if EQDEBUG >= 9
  1921. DumpPacket(app);
  1922. #endif
  1923. commands.Process(remote.handler, &remote.command, this);
  1924. }
  1925. else //bad client, disconnect
  1926. Disconnect();
  1927. break;
  1928. }
  1929. case OP_CancelSpellCast: {
  1930. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_CancelSpellCast", opcode, opcode);
  1931. current_zone->Interrupted(player, 0, 0, true);
  1932. SimpleMessage(CHANNEL_SPELLS_OTHER, "You stop casting.");
  1933. break;
  1934. }
  1935. case OP_UpdatePositionMsg: {
  1936. LogWrite(OPCODE__DEBUG, 7, "Opcode", "Opcode 0x%X (%i): OP_UpdatePositionMsg from %s", opcode, opcode, GetPlayer()->GetName());
  1937. int8 offset = 13;
  1938. if (app->pBuffer[0] == 0xFF)
  1939. offset += 2;
  1940. if (app->size > offset) {
  1941. if (player->IsCasting()) {
  1942. float distance = 0;
  1943. float x = player->GetX();
  1944. float y = player->GetY();
  1945. float z = player->GetZ();
  1946. player->PrepareIncomingMovementPacket(app->size - offset, app->pBuffer + offset, version);
  1947. distance = player->GetDistance(x, y, z, false);
  1948. if (distance > .5)
  1949. current_zone->Interrupted(player, 0, SPELL_ERROR_INTERRUPTED, false, true);
  1950. }
  1951. else
  1952. player->PrepareIncomingMovementPacket(app->size - offset, app->pBuffer + offset, version);
  1953. player_pos_changed = true;
  1954. GetPlayer()->changed = true;
  1955. GetPlayer()->info_changed = true;
  1956. GetPlayer()->vis_changed = true;
  1957. GetPlayer()->AddChangedZoneSpawn();
  1958. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  1959. //DumpPacket(app);
  1960. }
  1961. break;
  1962. }
  1963. case OP_MailSendMessageMsg: {
  1964. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_MailSendMessageMsg", opcode, opcode);
  1965. HandleSentMail(app);
  1966. break;
  1967. }
  1968. case OP_StopTrackingMsg: {
  1969. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_StopTrackingMsg", opcode, opcode);
  1970. player->GetZone()->RemovePlayerTracking(player, TRACKING_STOP);
  1971. break;
  1972. }
  1973. case OP_BeginTrackingMsg: {
  1974. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_BeginTrackingMsg", opcode, opcode);
  1975. PacketStruct* packet = configReader.getStruct("WS_BeginTracking", GetVersion());
  1976. if (packet) {
  1977. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1978. int32 spawn_id = packet->getType_int32_ByName("spawn_id");
  1979. Spawn* spawn = player->GetSpawnWithPlayerID(spawn_id);
  1980. if (spawn) {
  1981. AddWaypoint(spawn->GetName(), WAYPOINT_CATEGORY_TRACKING, spawn_id);
  1982. BeginWaypoint(spawn->GetName(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
  1983. player->GetZone()->RemovePlayerTracking(player, TRACKING_CLOSE_WINDOW);
  1984. }
  1985. }
  1986. safe_delete(packet);
  1987. }
  1988. break;
  1989. }
  1990. case OP_BioUpdateMsg: {
  1991. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_BioUpdateMsg", opcode, opcode);
  1992. PacketStruct* packet = configReader.getStruct("WS_BioUpdate", GetVersion());
  1993. if (packet) {
  1994. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  1995. player->SetBiography(packet->getType_EQ2_16BitString_ByName("biography").data);
  1996. }
  1997. safe_delete(packet);
  1998. }
  1999. break;
  2000. }
  2001. case OP_RewardPackMsg: {
  2002. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_RewardPackMsg", opcode, opcode);
  2003. /* This logging is still here because I remember another system using this packet and just want to make sure we can figure out that it's being sent
  2004. when we come across it (scatman) */
  2005. const char* name = app->GetOpcodeName();
  2006. if (name)
  2007. LogWrite(WORLD__DEBUG, 0, "World", "%s Received OP_RewardPackMsg %04i", name, app->GetRawOpcode());
  2008. else
  2009. LogWrite(WORLD__DEBUG, 0, "World", "Received OP_RewardPackMsg %04i", app->GetRawOpcode());
  2010. //DumpPacket(app);
  2011. PacketStruct* packet = configReader.getStruct("WS_RewardPackMsg", GetVersion());
  2012. if (packet) {
  2013. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2014. string recruiter_name = packet->getType_EQ2_16BitString_ByName("recruiter_name").data;
  2015. /* Player has contacted a guild recruiter */
  2016. if (recruiter_name.length() > 0) {
  2017. Guild* guild = guild_list.GetGuild(packet->getType_int32_ByName("guild_id"));
  2018. Client* recruiter = zone_list.GetClientByCharName(recruiter_name);
  2019. if (recruiter && guild) {
  2020. Message(CHANNEL_GUILD_EVENT, "Contact request sent to %s of %s.", recruiter->GetPlayer()->GetName(), guild->GetName());
  2021. recruiter->Message(CHANNEL_GUILD_EVENT, "%s [%u %s], [0 Unskilled] (%s) is requesting to speak to YOU about joining the guild.", player->GetName(), player->GetLevel(), classes.GetClassNameCase(player->GetAdventureClass()).c_str(), races.GetRaceNameCase(player->GetRace()));
  2022. recruiter->PlaySound("ui_guild_page");
  2023. }
  2024. }
  2025. /* New picture taken for guild recruiting */
  2026. else {
  2027. //DumpPacket(app->pBuffer, app->size);
  2028. int32 guild_id = 0;
  2029. int16 picture_data_size = 0;
  2030. unsigned char* recruiter_picture_data = 0;
  2031. memcpy(&guild_id, app->pBuffer + 4, sizeof(int32));
  2032. memcpy(&picture_data_size, app->pBuffer + 15, sizeof(int16));
  2033. Guild* guild = guild_list.GetGuild(guild_id);
  2034. if (guild) {
  2035. GuildMember* gm = guild->GetGuildMember(player);
  2036. if (gm) {
  2037. safe_delete_array(gm->recruiter_picture_data);
  2038. recruiter_picture_data = new unsigned char[picture_data_size];
  2039. for (int16 i = 0; i < picture_data_size; i++)
  2040. memcpy(recruiter_picture_data + i, app->pBuffer + 17 + i, 2);
  2041. gm->recruiter_picture_data = recruiter_picture_data;
  2042. gm->recruiter_picture_data_size = picture_data_size;
  2043. guild->SetMemberSaveNeeded(true);
  2044. }
  2045. }
  2046. }
  2047. }
  2048. safe_delete(packet);
  2049. }
  2050. break;
  2051. }
  2052. case OP_PetOptions: {
  2053. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_PetOptions", opcode, opcode);
  2054. Spawn* target = player->GetTarget();
  2055. PacketStruct* packet = configReader.getStruct("WS_PetOptions", GetVersion());
  2056. if (packet && target && (target == player->GetPet() || target == player->GetCharmedPet() || target == player->GetDeityPet() || target == player->GetCosmeticPet())) {
  2057. bool change = false;
  2058. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2059. string name = packet->getType_EQ2_16BitString_ByName("pet_name").data;
  2060. if (strlen(name.c_str()) != 0 && SetPetName(name.c_str())) {
  2061. target->SetName(name.c_str());
  2062. GetCurrentZone()->SendUpdateTitles(target);
  2063. change = true;
  2064. }
  2065. int8 pet_behavior = player->GetInfoStruct()->get_pet_behavior();
  2066. // Check protect self setting and update if needed
  2067. if (packet->getType_int8_ByName("protect_self") == 1) {
  2068. if ((pet_behavior & 2) == 0) {
  2069. player->GetInfoStruct()->set_pet_behavior(pet_behavior + 2);
  2070. change = true;
  2071. }
  2072. }
  2073. else {
  2074. if ((pet_behavior & 2) != 0) {
  2075. player->GetInfoStruct()->set_pet_behavior(pet_behavior - 2);
  2076. change = true;
  2077. }
  2078. }
  2079. // Check protect master setting and update if needed
  2080. if (packet->getType_int8_ByName("protect_master") == 1) {
  2081. if ((pet_behavior & 1) == 0) {
  2082. player->GetInfoStruct()->set_pet_behavior(pet_behavior + 1);
  2083. change = true;
  2084. }
  2085. }
  2086. else {
  2087. if ((pet_behavior & 1) != 0) {
  2088. player->GetInfoStruct()->set_pet_behavior(pet_behavior - 1);
  2089. change = true;
  2090. }
  2091. }
  2092. int8 pet_movement = player->GetInfoStruct()->get_pet_movement();
  2093. // Check stay/follow setting and update if needed
  2094. if (packet->getType_int8_ByName("stay_follow_toggle") == 1) {
  2095. if (pet_movement != 2) {
  2096. player->GetInfoStruct()->set_pet_movement(2);
  2097. change = true;
  2098. }
  2099. }
  2100. else {
  2101. if (pet_movement != 1) {
  2102. player->GetInfoStruct()->set_pet_movement(1);
  2103. change = true;
  2104. }
  2105. }
  2106. // Ranged/Melee settings are not implemented yet
  2107. if (change)
  2108. player->SetCharSheetChanged(true);
  2109. }
  2110. safe_delete(packet);
  2111. }
  2112. break;
  2113. }
  2114. case OP_RecipeBook: {
  2115. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_RecipeBook", opcode, opcode);
  2116. SendRecipeList();
  2117. break;
  2118. }
  2119. case OP_BuyPlayerHouseMsg: {
  2120. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_BuyPlayerHouseMsg", opcode, opcode);
  2121. //DumpPacket(app);
  2122. int64 bank_money = GetPlayer()->GetBankCoinsPlat();
  2123. PacketStruct* packet = configReader.getStruct("WS_BuyHouse", GetVersion());
  2124. if (packet) {
  2125. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2126. int64 house_id = 0;
  2127. if (GetVersion() <= 561) {
  2128. house_id = packet->getType_int32_ByName("house_id");
  2129. }
  2130. else {
  2131. house_id = packet->getType_int64_ByName("house_id");
  2132. }
  2133. HouseZone* hz = world.GetHouseZone(house_id);
  2134. if (hz) {
  2135. bool got_bank_money = BankHasCoin(hz->cost_coin);
  2136. int8 disable_alignment_req = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Player, DisableHouseAlignmentRequirement)->GetInt8();
  2137. std::vector<PlayerHouse*> houses = world.GetAllPlayerHouses(GetCharacterID());
  2138. if (houses.size() > 24)
  2139. {
  2140. SimpleMessage(CHANNEL_COLOR_YELLOW, "You already own 25 houses and may not own another.");
  2141. safe_delete(packet);
  2142. break;
  2143. }
  2144. if (disable_alignment_req && hz->alignment > 0 && hz->alignment != GetPlayer()->GetAlignment())
  2145. {
  2146. std::string req = "You must be of ";
  2147. if (hz->alignment == 1)
  2148. req.append("Good");
  2149. else
  2150. req.append("Evil");
  2151. req.append(" alignment to purchase this house");
  2152. SimpleMessage(CHANNEL_COLOR_YELLOW, req.c_str());
  2153. safe_delete(packet);
  2154. break;
  2155. }
  2156. ZoneChangeDetails zone_details;
  2157. int32 status_req = hz->cost_status;
  2158. int32 available_status = player->GetInfoStruct()->get_status_points();
  2159. if (status_req <= available_status && (!hz->cost_coin || (hz->cost_coin && player->RemoveCoins(hz->cost_coin))))
  2160. {
  2161. player->GetInfoStruct()->subtract_status_points(status_req);
  2162. if (zone_list.GetZoneByInstance(&zone_details, 0, hz->zone_id, true, true)) {
  2163. int32 upkeep_due = Timer::GetUnixTimeStamp() + 604800; // 604800 = 7 days
  2164. int64 unique_id = database.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, zone_details.instanceId, upkeep_due);
  2165. int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
  2166. if (db_id > 0)
  2167. GetPlayer()->GetCharacterInstances()->AddInstance(db_id, zone_details.instanceId, 0, 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime, zoneID, zone_details.instanceType, zone_details.zoneName);
  2168. world.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, unique_id, zone_details.instanceId, upkeep_due, 0, 0, GetPlayer()->GetName());
  2169. //ClientPacketFunctions::SendHousingList(this);
  2170. PlayerHouse* ph = world.GetPlayerHouseByUniqueID(unique_id);
  2171. ClientPacketFunctions::SendBaseHouseWindow(this, hz, ph, GetVersion() <= 561 ? house_id : this->GetPlayer()->GetID());
  2172. PlaySound("coin_cha_ching");
  2173. }
  2174. }
  2175. else if (status_req <= available_status && got_bank_money == 1) {
  2176. player->GetInfoStruct()->subtract_status_points(status_req);
  2177. bool bankwithdrawl = BankWithdrawalNoBanker(hz->cost_coin);
  2178. //this should NEVER happen since we check with got_bank_money, however adding it here should something go nutty.
  2179. if (bankwithdrawl == 0) {
  2180. PlaySound("buy_failed");
  2181. SimpleMessage(CHANNEL_COLOR_RED, "There was an error in bankwithdrawl function.");
  2182. safe_delete(packet);
  2183. break;
  2184. }
  2185. if (zone_list.GetZoneByInstance(&zone_details, 0, hz->zone_id, true, true)) {
  2186. int32 upkeep_due = Timer::GetUnixTimeStamp() + 604800; // 604800 = 7 days
  2187. int64 unique_id = database.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, zone_details.instanceId, upkeep_due);
  2188. int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
  2189. if (db_id > 0)
  2190. GetPlayer()->GetCharacterInstances()->AddInstance(db_id, zone_details.instanceId, 0, 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime, zoneID, zone_details.instanceType, zone_details.zoneName);
  2191. world.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, unique_id, zone_details.instanceId, upkeep_due, 0, 0, GetPlayer()->GetName());
  2192. PlayerHouse* ph = world.GetPlayerHouseByUniqueID(unique_id);
  2193. ClientPacketFunctions::SendBaseHouseWindow(this, hz, ph, GetVersion() <= 561 ? house_id : this->GetPlayer()->GetID());
  2194. PlaySound("coin_cha_ching");
  2195. }
  2196. }
  2197. else
  2198. {
  2199. SimpleMessage(CHANNEL_COLOR_YELLOW, "You do not have enough money to purchase the house.");
  2200. PlaySound("buy_failed");
  2201. }
  2202. }
  2203. }
  2204. }
  2205. safe_delete(packet);
  2206. break;
  2207. }
  2208. case OP_EnterHouseMsg: {
  2209. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_EnterHouseMsg", opcode, opcode);
  2210. //DumpPacket(app);
  2211. PacketStruct* packet = configReader.getStruct("WS_EnterHouse", GetVersion());
  2212. if (packet) {
  2213. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2214. PlayerHouse* ph = nullptr;
  2215. HouseZone* hz = nullptr;
  2216. int64 house_id = 0;
  2217. int32 spawn_index = 0;
  2218. if (GetVersion() <= 561) {
  2219. spawn_index = packet->getType_int32_ByName("house_id");
  2220. }
  2221. else {
  2222. house_id = packet->getType_int64_ByName("house_id");
  2223. }
  2224. ZoneChangeDetails zone_details;
  2225. if (GetHouseZoneServer(&zone_details, spawn_index, house_id)) {
  2226. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, true);
  2227. }
  2228. }
  2229. }
  2230. safe_delete(packet);
  2231. break;
  2232. }
  2233. case OP_PayHouseUpkeepMsg: {
  2234. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_PayHouseUpkeepMsg", opcode, opcode);
  2235. PacketStruct* packet = configReader.getStruct("WS_PayUpkeep", GetVersion());
  2236. if (packet) {
  2237. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2238. int64 house_id = 0;
  2239. if (GetVersion() <= 561) {
  2240. house_id = packet->getType_int32_ByName("house_id");
  2241. }
  2242. else {
  2243. house_id = packet->getType_int64_ByName("house_id");
  2244. }
  2245. HouseZone* hz = nullptr;
  2246. PlayerHouse* ph = world.GetPlayerHouse(this, GetVersion() <= 561 ? house_id : 0, GetVersion() > 561 ? house_id : 0, &hz);
  2247. if (ph)
  2248. {
  2249. if (!hz)
  2250. {
  2251. Message(CHANNEL_COLOR_YELLOW, "HouseZone ID %u does NOT exist!", ph->house_id);
  2252. safe_delete(packet);
  2253. break;
  2254. }
  2255. int32 upkeep_due = Timer::GetUnixTimeStamp() + 604800;
  2256. if (((sint64)ph->upkeep_due - (sint64)Timer::GetUnixTimeStamp()) > 0)
  2257. {
  2258. upkeep_due = ph->upkeep_due + 604800; // 604800 = 7 days
  2259. if (upkeep_due > (Timer::GetUnixTimeStamp() + 7257600)) // 84 days max upkeep to pay https://eq2.zam.com/wiki/Housing_%28EQ2%29#Upkeep
  2260. {
  2261. Message(CHANNEL_COLOR_YELLOW, "You cannot pay more than 3 months of upkeep.");
  2262. PlaySound("buy_failed");
  2263. safe_delete(packet);
  2264. break;
  2265. }
  2266. }
  2267. bool escrowChange = false;
  2268. int64 statusReq = hz->upkeep_status;
  2269. int64 tmpRecoverStatus = 0;
  2270. if (ph->escrow_status && statusReq >= ph->escrow_status)
  2271. {
  2272. escrowChange = true;
  2273. tmpRecoverStatus = ph->escrow_status;
  2274. statusReq -= ph->escrow_status;
  2275. ph->escrow_status = 0;
  2276. }
  2277. else if (ph->escrow_status && statusReq && statusReq <= ph->escrow_status)
  2278. {
  2279. escrowChange = true;
  2280. ph->escrow_status -= statusReq;
  2281. tmpRecoverStatus = statusReq;
  2282. statusReq = 0;
  2283. }
  2284. int64 coinReq = hz->upkeep_coin;
  2285. int64 tmpRecoverCoins = 0;
  2286. if (ph->escrow_coins && coinReq >= ph->escrow_coins) // more required to upkeep than in escrow, subtract what we have left
  2287. {
  2288. escrowChange = true;
  2289. tmpRecoverCoins = ph->escrow_coins;
  2290. coinReq -= ph->escrow_coins;
  2291. ph->escrow_coins = 0;
  2292. }
  2293. else if (ph->escrow_coins && coinReq && coinReq <= ph->escrow_coins)
  2294. {
  2295. escrowChange = true;
  2296. // more than enough in escrow, subtract and make our cost 0!
  2297. ph->escrow_coins -= coinReq;
  2298. tmpRecoverCoins = coinReq;
  2299. coinReq = 0;
  2300. }
  2301. int32 available_status_points = player->GetInfoStruct()->get_status_points();
  2302. if (!statusReq || (statusReq && statusReq <= available_status_points))
  2303. {
  2304. if (coinReq && player->RemoveCoins(coinReq))
  2305. coinReq = 0;
  2306. if (!coinReq && statusReq && player->GetInfoStruct()->subtract_status_points(statusReq))
  2307. statusReq = 0;
  2308. }
  2309. bool got_bank_money = BankHasCoin(hz->upkeep_coin);
  2310. if (!coinReq && !statusReq) // TODO: Need option to take from bank if player does not have enough coin on them
  2311. {
  2312. database.AddHistory(ph, GetPlayer()->GetName(), "Paid Upkeep", Timer::GetUnixTimeStamp(), hz->upkeep_coin, 0, 0);
  2313. if (escrowChange)
  2314. database.UpdateHouseEscrow(ph->house_id, ph->instance_id, ph->escrow_coins, ph->escrow_status);
  2315. ph->upkeep_due = upkeep_due;
  2316. database.SetHouseUpkeepDue(GetCharacterID(), ph->house_id, ph->instance_id, ph->upkeep_due);
  2317. //ClientPacketFunctions::SendHousingList(this);
  2318. ClientPacketFunctions::SendBaseHouseWindow(this, hz, ph, GetVersion() <= 561 ? house_id : this->GetPlayer()->GetID());
  2319. PlaySound("coin_cha_ching");
  2320. }
  2321. else if (!statusReq && got_bank_money == 1) {
  2322. bool bankwithdrawl = BankWithdrawalNoBanker(hz->upkeep_coin);
  2323. //this should NEVER happen since we check with got_bank_money, however adding it here should something go nutty.
  2324. if (bankwithdrawl == 0) {
  2325. PlaySound("buy_failed");
  2326. SimpleMessage(CHANNEL_COLOR_RED, "There was an error in bankwithdrawl function.");
  2327. safe_delete(packet);
  2328. break;
  2329. }
  2330. database.AddHistory(ph, GetPlayer()->GetName(), "Paid Upkeep", Timer::GetUnixTimeStamp(), hz->upkeep_coin, 0, 0);
  2331. if (escrowChange)
  2332. database.UpdateHouseEscrow(ph->house_id, ph->instance_id, ph->escrow_coins, ph->escrow_status);
  2333. ph->upkeep_due = upkeep_due;
  2334. database.SetHouseUpkeepDue(GetCharacterID(), ph->house_id, ph->instance_id, ph->upkeep_due);
  2335. ClientPacketFunctions::SendBaseHouseWindow(this, hz, ph, GetVersion() <= 561 ? house_id : this->GetPlayer()->GetID());
  2336. PlaySound("coin_cha_ching");
  2337. }
  2338. else
  2339. {
  2340. // recover the escrow we were going to use but could not spend due to lack of funds
  2341. if (tmpRecoverCoins)
  2342. ph->escrow_coins += tmpRecoverCoins;
  2343. if (tmpRecoverStatus)
  2344. ph->escrow_status += tmpRecoverStatus;
  2345. SimpleMessage(CHANNEL_COLOR_YELLOW, "You do not have enough money or status to pay for upkeep.");
  2346. PlaySound("buy_failed");
  2347. }
  2348. }
  2349. else
  2350. Message(CHANNEL_COLOR_YELLOW, "PlayerHouse ID %u does NOT exist!", house_id);
  2351. }
  2352. }
  2353. safe_delete(packet);
  2354. break;
  2355. }
  2356. case OP_ExitHouseMsg: {
  2357. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ExitHouseMsg", opcode, opcode);
  2358. int32 instance_id = GetCurrentZone()->GetInstanceID();
  2359. if (instance_id > 0) {
  2360. PlayerHouse* ph = world.GetPlayerHouseByInstanceID(instance_id);
  2361. if (ph) {
  2362. HouseZone* hz = world.GetHouseZone(ph->house_id);
  2363. if (hz) {
  2364. // determine if this is an instanced zone that already exists
  2365. ZoneChangeDetails zone_details;
  2366. bool foundZone = world.GetGroupManager()->IdentifyMemberInGroupOrRaid(&zone_details, this, hz->exit_zone_id);
  2367. if (foundZone) {
  2368. GetPlayer()->SetX(hz->exit_x);
  2369. GetPlayer()->SetY(hz->exit_y);
  2370. GetPlayer()->SetZ(hz->exit_z);
  2371. GetPlayer()->SetHeading(hz->exit_heading);
  2372. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false, true);
  2373. }
  2374. else {
  2375. if (zone_list.GetZone(&zone_details, hz->exit_zone_id)) {
  2376. GetPlayer()->SetX(hz->exit_x);
  2377. GetPlayer()->SetY(hz->exit_y);
  2378. GetPlayer()->SetZ(hz->exit_z);
  2379. GetPlayer()->SetHeading(hz->exit_heading);
  2380. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false);
  2381. }
  2382. else {
  2383. LogWrite(CCLIENT__ERROR, 0, "Client", "Failed to OP_ExitHouseMsg(exit_zone_id = %u) for Player %s.", hz->exit_zone_id, GetPlayer()->GetName());
  2384. }
  2385. }
  2386. }
  2387. }
  2388. }
  2389. break;
  2390. }
  2391. case OP_QuestJournalWaypointMsg: {
  2392. //DumpPacket(app);
  2393. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_QuestJournalWaypointMsg", opcode, opcode);
  2394. PacketStruct* packet = configReader.getStruct("WS_QuestJournalWaypoint", GetVersion());
  2395. if (packet) {
  2396. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2397. if (GetVersion() <= 561) {
  2398. int32 quest_id = packet->getType_int32_ByName("quest_id");
  2399. GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
  2400. if (player->player_quests.count(quest_id) > 0 && player->player_quests[quest_id]) {
  2401. if (player->player_quests[quest_id]->GetTracked())
  2402. player->player_quests[quest_id]->SetTracked(false);
  2403. else
  2404. player->player_quests[quest_id]->SetTracked(true);
  2405. player->player_quests[quest_id]->SetSaveNeeded(true);
  2406. }
  2407. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  2408. }
  2409. else {
  2410. int32 quests = packet->getType_int32_ByName("num_quests");
  2411. if (quests > 100) // just picking a number higher than max allowed
  2412. {
  2413. LogWrite(CCLIENT__ERROR, 0, "Client", "num_quests = %u - quantity too high, aborting load.", quests);
  2414. break;
  2415. }
  2416. LogWrite(CCLIENT__DEBUG, 0, "Client", "num_quests = %u", quests);
  2417. for (int32 i = 0; i < quests; i++) {
  2418. int32 id = packet->getType_int32_ByName("quest_id_0", i);
  2419. if (id == 0)
  2420. continue;
  2421. LogWrite(CCLIENT__DEBUG, 5, "Client", "quest_id = %u", id);
  2422. bool tracked = packet->getType_int8_ByName("quest_tracked_0", i) == 1 ? true : false;
  2423. GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
  2424. if (player->player_quests.count(id) > 0 && player->player_quests[id]) {
  2425. player->player_quests[id]->SetTracked(tracked);
  2426. player->player_quests[id]->SetSaveNeeded(true);
  2427. }
  2428. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  2429. }
  2430. }
  2431. }
  2432. safe_delete(packet);
  2433. }
  2434. break;
  2435. }
  2436. case OP_PaperdollImage: {
  2437. /* PacketStruct* packet = configReader.getStruct("WS_PaperdollImage", version);
  2438. if (packet && packet->LoadPacketData(app->pBuffer, app->size)) {
  2439. //First check if this is a new image... delete an existing partial image if one exists
  2440. int8 packet_index = packet->getType_int8_ByName("packetIndex");
  2441. if (packet_index == 0) {
  2442. safe_delete_array(incoming_paperdoll.image_bytes);
  2443. incoming_paperdoll.last_received_packet_index = 0;
  2444. incoming_paperdoll.current_size_bytes = 0;
  2445. }
  2446. //return if this packet is not the one we are expecting...
  2447. else if (packet_index != incoming_paperdoll.last_received_packet_index + 1) {
  2448. safe_delete(packet);
  2449. break;
  2450. }
  2451. //Check how many packets we're supposed to be receiving for this/these images
  2452. incoming_paperdoll.image_num_packets = packet->getType_int8_ByName("totalNumPackets");
  2453. //Check the image type, if this is a new type in the same series of packets we have a new image
  2454. int8 img_type = packet->getType_int8_ByName("image_type");
  2455. if (packet_index != 0 && img_type != incoming_paperdoll.image_type) {
  2456. //We have a new image. Save the old data and clear before continuing
  2457. SavePlayerImages();
  2458. }
  2459. incoming_paperdoll.image_type = img_type;
  2460. //Get the size of the image data in this packet
  2461. sint64 image_size = packet->getType_int32_ByName("imageSize");
  2462. if (image_size <= 0 || image_size > 1048576) {
  2463. //If this packet is saying that the array is size <= 0 or > 1 MiB return out... it shouldn't be those sizes ever
  2464. safe_delete(packet);
  2465. break;
  2466. }
  2467. //Create a new array
  2468. int32 new_image_size = image_size;
  2469. uchar* new_image = new uchar[incoming_paperdoll.current_size_bytes + new_image_size];
  2470. if (incoming_paperdoll.image_bytes) {
  2471. memcpy(new_image, incoming_paperdoll.image_bytes, incoming_paperdoll.current_size_bytes);
  2472. safe_delete_array(incoming_paperdoll.image_bytes);
  2473. }
  2474. //variable i should be the index in the packet of the first PNG file byte
  2475. vector<DataStruct*>* d_structs = packet->getStructs();
  2476. vector<DataStruct*>::iterator itr;
  2477. int32 i = 0;
  2478. for (itr = d_structs->begin(); itr != d_structs->end(); itr++) {
  2479. DataStruct* ds = (*itr);
  2480. if (strcmp(ds->GetName(), "pngData_0") != 0)
  2481. i += ds->GetDataSizeInBytes();
  2482. else
  2483. break;
  2484. }
  2485. //Return if this packet is bad and we would read out of bounds
  2486. if (app->size - i < new_image_size) {
  2487. safe_delete(packet);
  2488. safe_delete_array(new_image);
  2489. break;
  2490. }
  2491. uchar* tmp = new_image + incoming_paperdoll.current_size_bytes;
  2492. memcpy(tmp, app->pBuffer + i, new_image_size);
  2493. incoming_paperdoll.current_size_bytes += new_image_size;
  2494. incoming_paperdoll.image_bytes = new_image;
  2495. //Check if this is the last packet we're expecting for this image. Create a final image if so
  2496. if (incoming_paperdoll.image_num_packets == 1 ||
  2497. incoming_paperdoll.last_received_packet_index + 2 == incoming_paperdoll.image_num_packets) {
  2498. SavePlayerImages();
  2499. }
  2500. incoming_paperdoll.last_received_packet_index = packet_index;
  2501. }
  2502. safe_delete(packet);
  2503. */
  2504. break;
  2505. }
  2506. case OP_ReadyForTakeOffMsg:
  2507. {
  2508. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ReadyForTakeOffMsg", opcode, opcode);
  2509. int32 index = GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath());
  2510. if (GetPendingFlightPath() > 0) {
  2511. if (index != -1) {
  2512. PacketStruct* packet = configReader.getStruct("WS_ClearForTakeOff", GetVersion());
  2513. if (packet) {
  2514. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(GetPlayer()));
  2515. packet->setDataByName("path_id", index);
  2516. packet->setDataByName("speed", GetCurrentZone()->GetFlightPathSpeed(GetPendingFlightPath()));
  2517. QueuePacket(packet->serialize());
  2518. safe_delete(packet);
  2519. on_auto_mount = true;
  2520. }
  2521. }
  2522. else {
  2523. LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but unable to get an index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID());
  2524. Message(CHANNEL_ERROR, "Unable to get index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID());
  2525. EndAutoMount();
  2526. }
  2527. SetPendingFlightPath(0);
  2528. }
  2529. else
  2530. LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but there is no pending flight path...");
  2531. break;
  2532. }
  2533. case OP_EarlyLandingRequestMsg:
  2534. {
  2535. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_EarlyLandingRequestMsg", opcode, opcode);
  2536. EndAutoMount();
  2537. break;
  2538. }
  2539. case OP_SubmitCharCust:
  2540. {
  2541. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_SubmitCharCust", opcode, opcode);
  2542. PacketStruct* packet = configReader.getStruct("WS_SubmitCharCust", version);
  2543. if (packet && packet->LoadPacketData(app->pBuffer, app->size, GetVersion() <= 561 ? false : true)) {
  2544. int8 type = packet->getType_int8_ByName("type");
  2545. if (type == 0) {
  2546. /*if (player->custNPC) {
  2547. player->custNPCTarget->CustomizeAppearance(packet);
  2548. current_zone->SendSpawnChanges(player->custNPCTarget);
  2549. }
  2550. else {*/
  2551. player->CustomizeAppearance(packet);
  2552. current_zone->SendSpawnChanges(player);
  2553. //}
  2554. }
  2555. }
  2556. safe_delete(packet);
  2557. /*
  2558. if (player->custNPC) {
  2559. memcpy(&player->appearance, &player->SavedApp, sizeof(AppearanceData));
  2560. memcpy(&player->features, &player->SavedFeatures, sizeof(CharFeatures));
  2561. if (player->custNPCTarget->IsBot())
  2562. database.SaveBotAppearance((Bot*)player->custNPCTarget);
  2563. player->custNPC = false;
  2564. player->custNPCTarget = 0;
  2565. player->changed = true;
  2566. player->info_changed = true;
  2567. current_zone->SendSpawnChanges(player, this);
  2568. }*/
  2569. break;
  2570. }
  2571. default: {
  2572. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): Unknown in %s", opcode, opcode, __FILE__);
  2573. const char* name = app->GetOpcodeName();
  2574. if (name)
  2575. LogWrite(OPCODE__DEBUG, 1, "Opcode", "%s Received %04X (%i)", name, app->GetRawOpcode(), app->GetRawOpcode());
  2576. else
  2577. LogWrite(OPCODE__DEBUG, 1, "Opcode", "Received %04X (%i)", app->GetRawOpcode(), app->GetRawOpcode());
  2578. // keeping this around for debugging purposes
  2579. //DumpPacket(app);
  2580. }
  2581. }
  2582. if (!eqs || !eqs->CheckActive()) {
  2583. return false;
  2584. }
  2585. return ret;
  2586. }
  2587. bool Client::HandleLootItem(Spawn* entity, Item* item, Spawn* target, bool overrideLootRestrictions) {
  2588. if (!item) {
  2589. SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find item to loot!");
  2590. return false;
  2591. }
  2592. int32 conflictItemList = 0, conflictequipmentList = 0, conflictAppearanceEquipmentList = 0;
  2593. int16 lore_stack_count = 0;
  2594. Player* lootingPlayer = player;
  2595. Client* lootingClient = this;
  2596. if (target != nullptr && target != lootingPlayer && target->IsPlayer()) {
  2597. lootingPlayer = (Player*)target;
  2598. lootingClient = lootingPlayer->GetClient();
  2599. }
  2600. // needs to only be checked before expiration of loot restrictions
  2601. if (entity && !overrideLootRestrictions) {
  2602. std::vector<int32> raidGroups;
  2603. if (lootingPlayer && lootingPlayer->GetGroupMemberInfo())
  2604. world.GetGroupManager()->GetRaidGroups(lootingPlayer->GetGroupMemberInfo()->group_id, &raidGroups);
  2605. if (entity->GetLootGroupID() > 0 && std::find(raidGroups.begin(), raidGroups.end(), entity->GetLootGroupID()) == raidGroups.end() && (!lootingPlayer->GetGroupMemberInfo() || lootingPlayer->GetGroupMemberInfo()->group_id != entity->GetLootGroupID())) {
  2606. LogWrite(LOOT__ERROR, 0, "Loot", "%s: Loot Group ID from %s did not match Item: %s (%u), expected group id %u.", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id, entity->GetLootGroupID());
  2607. return false;
  2608. }
  2609. if (entity->GetLootMethod() != GroupLootMethod::METHOD_FFA) {
  2610. switch (entity->GetLootMethod()) {
  2611. case GroupLootMethod::METHOD_LEADER: {
  2612. bool inRaid = false;
  2613. bool isLeaderRaid = false;
  2614. if (GetPlayer()->GetGroupMemberInfo()) {
  2615. inRaid = world.GetGroupManager()->IsInRaidGroup(GetPlayer()->GetGroupMemberInfo()->group_id, GetPlayer()->GetGroupMemberInfo()->group_id, false);
  2616. isLeaderRaid = world.GetGroupManager()->IsInRaidGroup(GetPlayer()->GetGroupMemberInfo()->group_id, GetPlayer()->GetGroupMemberInfo()->group_id, true);
  2617. }
  2618. if (entity->GetLootGroupID() > 0 && (!GetPlayer()->GetGroupMemberInfo() || !lootingPlayer->GetGroupMemberInfo() || (inRaid && !isLeaderRaid) || ((std::find(raidGroups.begin(), raidGroups.end(), GetPlayer()->GetGroupMemberInfo()->group_id) == raidGroups.end()) && lootingPlayer->GetGroupMemberInfo()->group_id != entity->GetLootGroupID()) || !GetPlayer()->GetGroupMemberInfo()->leader)) {
  2619. LogWrite(LOOT__ERROR, 0, "Loot", "%s: Loot Attempt from %s was not allowed with Item: %s (%u), must be group leader.", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id);
  2620. return false;
  2621. }
  2622. break;
  2623. }
  2624. case GroupLootMethod::METHOD_LOTTO:
  2625. case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
  2626. if (entity->IsLootTimerRunning()) {
  2627. LogWrite(LOOT__INFO, 0, "Loot", "%s: Loot Timer is still running, flag player %s to lotto Item: %s (%u).", entity->GetName(), lootingPlayer->GetName(), item->name.c_str(), item->details.item_id);
  2628. return false;
  2629. }
  2630. break;
  2631. }
  2632. }
  2633. }
  2634. }
  2635. if (((conflictItemList = lootingPlayer->item_list.CheckSlotConflict(item, true, true, &lore_stack_count)) == LORE ||
  2636. (conflictequipmentList = lootingPlayer->equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE ||
  2637. (conflictAppearanceEquipmentList = lootingPlayer->appearance_equipment_list.CheckSlotConflict(item, true, &lore_stack_count)) == LORE) && !item->CheckFlag(STACK_LORE)) {
  2638. Message(CHANNEL_COLOR_RED, "You cannot loot %s due to lore conflict.", item->name.c_str());
  2639. return false;
  2640. }
  2641. else if (conflictItemList == STACK_LORE || conflictequipmentList == STACK_LORE || conflictAppearanceEquipmentList == STACK_LORE) {
  2642. Message(CHANNEL_COLOR_RED, "You cannot loot %s due to stack lore conflict.", item->name.c_str());
  2643. return false;
  2644. }
  2645. if (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item)) {
  2646. if (lootingPlayer->item_list.AssignItemToFreeSlot(item)) {
  2647. if (item->CheckFlag2(HEIRLOOM)) { // TODO: RAID Support
  2648. GroupMemberInfo* gmi = lootingClient->GetPlayer()->GetGroupMemberInfo();
  2649. if (gmi && gmi->group_id)
  2650. {
  2651. PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
  2652. if (group)
  2653. {
  2654. group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
  2655. deque<GroupMemberInfo*>* members = group->GetMembers();
  2656. if (members) {
  2657. for (int8 i = 0; i < members->size(); i++) {
  2658. Entity* member = members->at(i)->member;
  2659. if (!member)
  2660. continue;
  2661. if ((member->GetZone() != lootingClient->GetPlayer()->GetZone()))
  2662. continue;
  2663. if (member->IsPlayer()) {
  2664. item->grouped_char_ids.insert(std::make_pair(((Player*)member)->GetCharacterID(), true));
  2665. item->save_needed = true;
  2666. }
  2667. }
  2668. }
  2669. group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
  2670. }
  2671. }
  2672. }
  2673. int8 type = CHANNEL_LOOT;
  2674. if (entity) {
  2675. lootingClient->Message(type, "You loot %s from the corpse of %s", item->CreateItemLink(GetVersion()).c_str(), entity->GetName());
  2676. }
  2677. else {
  2678. lootingClient->Message(type, "You found a %s.", item->CreateItemLink(GetVersion()).c_str());
  2679. }
  2680. Guild* guild = lootingPlayer->GetGuild();
  2681. if (guild && item->details.tier >= ITEM_TAG_LEGENDARY) {
  2682. char adjective[32];
  2683. int8 type;
  2684. memset(adjective, 0, sizeof(adjective));
  2685. if (item->details.tier >= ITEM_TAG_MYTHICAL) {
  2686. strncpy(adjective, "Mythical", sizeof(adjective) - 1);
  2687. type = GUILD_EVENT_LOOTS_MYTHICAL_ITEM;
  2688. }
  2689. else if (item->details.tier >= ITEM_TAG_FABLED) {
  2690. strncpy(adjective, "Fabled", sizeof(adjective) - 1);
  2691. type = GUILD_EVENT_LOOTS_FABELED_ITEM;
  2692. }
  2693. else {
  2694. strncpy(adjective, "Legendary", sizeof(adjective) - 1);
  2695. type = GUILD_EVENT_LOOTS_LEGENDARY_ITEM;
  2696. }
  2697. guild->AddNewGuildEvent(type, "%s has looted the %s %s", Timer::GetUnixTimeStamp(), true, lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
  2698. guild->SendMessageToGuild(type, "%s has looted the %s %s", lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str());
  2699. }
  2700. if (item->GetItemScript() && lua_interface)
  2701. lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, lootingPlayer);
  2702. lootingClient->CheckPlayerQuestsItemUpdate(item);
  2703. if (GetVersion() <= 561) {
  2704. EQ2Packet* outapp = lootingPlayer->SendInventoryUpdate(GetVersion());
  2705. if (outapp)
  2706. lootingClient->QueuePacket(outapp);
  2707. }
  2708. return true;
  2709. }
  2710. else
  2711. lootingClient->SimpleMessage(CHANNEL_COLOR_RED, "Could not find free slot to place item.");
  2712. }
  2713. else
  2714. lootingClient->SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to loot item: Inventory is FULL.");
  2715. return false;
  2716. }
  2717. bool Client::HandleLootItemByID(Spawn* entity, int32 item_id, Spawn* target) {
  2718. if (!entity) {
  2719. return false;
  2720. }
  2721. Item* item = entity->LootItem(item_id);
  2722. bool success = false;
  2723. success = HandleLootItem(entity, item, target);
  2724. if (!success)
  2725. entity->AddLootItem(item);
  2726. return success;
  2727. }
  2728. void Client::HandleLootItemRequestPacket(EQApplicationPacket* app) {
  2729. PacketStruct* packet = configReader.getStruct("WS_LootItem", GetVersion());
  2730. if (packet) {
  2731. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  2732. int32 loot_id = packet->getType_int32_ByName("loot_id");
  2733. bool loot_all = (packet->getType_int8_ByName("loot_all") == 1);
  2734. int32 target_id = packet->getType_int32_ByName("target_id");
  2735. int8 button_clicked = packet->getType_int8_ByName("button_clicked");
  2736. Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id);
  2737. if (!spawn) {
  2738. safe_delete(packet);
  2739. return;
  2740. }
  2741. Item* item = nullptr;
  2742. vector<Item*>* items = player->GetPendingLootItems(loot_id);
  2743. if (items) {
  2744. int32 items_looted = 0;
  2745. int32 item_id = packet->getType_int32_ByName("item_id_0");
  2746. for (int32 i = 0; i < items->size(); i++) {
  2747. Item* master_item = items->at(i);
  2748. if (master_item && (loot_all || master_item->details.item_id == item_id)) {
  2749. item = new Item(master_item);
  2750. if (item) {
  2751. loot_all = HandleLootItem(0, item);
  2752. if (loot_all) {
  2753. player->RemovePendingLootItem(loot_id, item->details.item_id);
  2754. if (GetVersion() <= 561) {
  2755. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  2756. if (outapp)
  2757. QueuePacket(outapp);
  2758. }
  2759. if (master_item->details.item_id == item_id) {
  2760. break;
  2761. }
  2762. }
  2763. }
  2764. if (!loot_all)
  2765. break;
  2766. }
  2767. }
  2768. if (((loot_all && !item_id) || (loot_all && item_id && items->size() == 1)) || items->size() < 1) {
  2769. CloseLoot(loot_id);
  2770. }
  2771. else {
  2772. vector<Item*>* items2 = player->GetPendingLootItems(loot_id);
  2773. SendLootResponsePacket(spawn->GetLootCoins(), items2, spawn, true);
  2774. safe_delete(items2);
  2775. }
  2776. safe_delete(items);
  2777. safe_delete(packet);
  2778. return;
  2779. }
  2780. spawn->LockLoot();
  2781. bool unlockedLoot = false;
  2782. if (spawn && !spawn->Alive() && spawn->IsNPC() && ((NPC*)spawn)->Brain()->CheckLootAllowed(player)) {
  2783. if (loot_all) {
  2784. switch (spawn->GetLootMethod()) {
  2785. case GroupLootMethod::METHOD_LOTTO: {
  2786. spawn->AddLottoItemRequest(0xFFFFFFFF, GetPlayer()->GetID());
  2787. break;
  2788. }
  2789. case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
  2790. spawn->AddNeedGreedItemRequest(0xFFFFFFFF, GetPlayer()->GetID(), true);
  2791. }
  2792. default: {
  2793. if (!unlockedLoot) {
  2794. spawn->UnlockLoot();
  2795. unlockedLoot = true;
  2796. }
  2797. int32 item_id = 0;
  2798. while (loot_all && ((item_id = spawn->GetLootItemID()) > 0)) {
  2799. loot_all = HandleLootItemByID(spawn, item_id, GetPlayer());
  2800. }
  2801. break;
  2802. }
  2803. }
  2804. spawn->UnlockLoot();
  2805. if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
  2806. CloseLoot(loot_id);
  2807. }
  2808. }
  2809. else {
  2810. int8 item_count = packet->getType_int8_ByName("item_count");
  2811. for (int8 cur = 0; cur < item_count; cur++) {
  2812. char item_field_name[64];
  2813. snprintf(item_field_name, 64, "item_id_%u", cur);
  2814. int32 item_id = packet->getType_int32_ByName(item_field_name);
  2815. Spawn* target = this->GetPlayer();
  2816. if (target_id != 0xFFFFFFFF && GetPlayer()->GetGroupMemberInfo()) {
  2817. Spawn* destTarget = GetPlayer()->GetSpawnWithPlayerID(target_id);
  2818. std::vector<int32> raidGroups;
  2819. if (destTarget && destTarget->IsEntity() && ((Entity*)destTarget)->GetGroupMemberInfo())
  2820. world.GetGroupManager()->GetRaidGroups(((Entity*)destTarget)->GetGroupMemberInfo()->group_id, &raidGroups);
  2821. if (destTarget && (!destTarget->IsPlayer() || (std::find(raidGroups.begin(), raidGroups.end(), GetPlayer()->GetGroupMemberInfo()->group_id) == raidGroups.end() && !world.GetGroupManager()->IsInGroup(GetPlayer()->GetGroupMemberInfo()->group_id, ((Player*)destTarget))))) {
  2822. SimpleMessage(CHANNEL_COMMAND_TEXT, "HACKS!!");
  2823. safe_delete(packet);
  2824. spawn->UnlockLoot();
  2825. return;
  2826. }
  2827. target = destTarget;
  2828. }
  2829. bool breakLoopAllLooted = false;
  2830. switch (spawn->GetLootMethod()) {
  2831. case GroupLootMethod::METHOD_LOTTO: {
  2832. spawn->AddLottoItemRequest(item_id, GetPlayer()->GetID());
  2833. break;
  2834. }
  2835. case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
  2836. if (button_clicked == 3) { // decline
  2837. break;
  2838. }
  2839. if (GetVersion() <= 561) {
  2840. button_clicked = 1; // selecting is need
  2841. }
  2842. spawn->AddNeedGreedItemRequest(item_id, GetPlayer()->GetID(), (button_clicked == 1));
  2843. break;
  2844. }
  2845. default: {
  2846. if (!unlockedLoot) {
  2847. spawn->UnlockLoot();
  2848. unlockedLoot = true;
  2849. }
  2850. if (!loot_all) {
  2851. HandleLootItemByID(spawn, item_id, target);
  2852. }
  2853. else {
  2854. while (loot_all && ((item_id = spawn->GetLootItemID()) > 0)) {
  2855. loot_all = HandleLootItemByID(spawn, item_id, target);
  2856. }
  2857. breakLoopAllLooted = true;
  2858. }
  2859. break;
  2860. }
  2861. }
  2862. if (breakLoopAllLooted) {
  2863. break;
  2864. }
  2865. }
  2866. if (!unlockedLoot) {
  2867. spawn->UnlockLoot();
  2868. }
  2869. if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO ||
  2870. (spawn->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED && item_count >= spawn->GetLootCount())) {
  2871. CloseLoot(loot_id);
  2872. }
  2873. }
  2874. if (GetVersion() > 561) {
  2875. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  2876. if (outapp)
  2877. QueuePacket(outapp);
  2878. }
  2879. if (spawn->GetLootMethod() != GroupLootMethod::METHOD_LOTTO && spawn->GetLootMethod() != GroupLootMethod::METHOD_NEED_BEFORE_GREED) {
  2880. LootSpawnRequest(spawn);
  2881. }
  2882. else {
  2883. spawn->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
  2884. }
  2885. if (!spawn->HasLoot()) {
  2886. CloseLoot(loot_id);
  2887. if (spawn->IsNPC())
  2888. GetCurrentZone()->RemoveDeadSpawn(spawn);
  2889. }
  2890. }
  2891. else {
  2892. spawn->UnlockLoot();
  2893. if (!spawn) {
  2894. LogWrite(WORLD__ERROR, 0, "World", "Unknown id of %u when looting!", loot_id);
  2895. SimpleMessage(CHANNEL_COLOR_YELLOW, "Unable to find spawn to loot!");
  2896. }
  2897. else
  2898. SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not unable to loot that at this time.");
  2899. }
  2900. }
  2901. safe_delete(packet);
  2902. }
  2903. }
  2904. void Client::HandleSkillInfoRequest(EQApplicationPacket* app) {
  2905. PacketStruct* request = 0;
  2906. // cout << "Request: \n";
  2907. // DumpPacket(app);
  2908. switch (app->pBuffer[0]) {
  2909. case 0: { //items
  2910. request = configReader.getStruct("WS_SkillInfoItemRequest", GetVersion());
  2911. if (request) {
  2912. if (request->LoadPacketData(app->pBuffer, app->size)) {
  2913. Item* item = GetPlayer()->GetEquipmentList()->GetItemFromUniqueID(request->getType_int32_ByName("unique_id"));
  2914. if (!item)
  2915. item = GetPlayer()->item_list.GetItemFromUniqueID(request->getType_int32_ByName("unique_id"), true);
  2916. if (item) {
  2917. PacketStruct* response = configReader.getStruct("WS_SkillInfoItemResponse", GetVersion());
  2918. if (response) {
  2919. response->setDataByName("request_type", request->getType_int32_ByName("request_type"));
  2920. response->setDataByName("unique_id", request->getType_int32_ByName("unique_id"));
  2921. response->setSmallStringByName("name", item->name.c_str());
  2922. EQ2Packet* app2 = response->serialize();
  2923. //DumpPacket(app2);
  2924. QueuePacket(app2);
  2925. safe_delete(response);
  2926. }
  2927. }
  2928. }
  2929. safe_delete(request);
  2930. }
  2931. break;
  2932. }
  2933. case 2: {//spells
  2934. request = configReader.getStruct("WS_SkillInfoSpellRequest", GetVersion());
  2935. if (request) {
  2936. if (request->LoadPacketData(app->pBuffer, app->size)) {
  2937. int32 id = request->getType_int32_ByName("id");
  2938. int8 tier = request->getType_int32_ByName("unique_id"); //on live this is really unique_id, but I'm going to make it tier instead :)
  2939. Spell* spell = master_spell_list.GetSpell(id, tier);
  2940. PacketStruct* response = configReader.getStruct("WS_SkillInfoResponse", GetVersion());
  2941. if (response) {
  2942. response->setDataByName("request_type", 2);
  2943. response->setDataByName("unique_id", tier);
  2944. response->setDataByName("id", id);
  2945. if (spell)
  2946. response->setSmallStringByName("name", spell->GetName());
  2947. else
  2948. response->setSmallStringByName("name", "Unknown Spell");
  2949. EQ2Packet* app2 = response->serialize();
  2950. // DumpPacket(app2);
  2951. QueuePacket(app2);
  2952. safe_delete(response);
  2953. }
  2954. }
  2955. safe_delete(request);
  2956. }
  2957. break;
  2958. }
  2959. default: {
  2960. LogWrite(WORLD__ERROR, 0, "World", "Unknown SkillInfoRequest type of %i", (int)app->pBuffer[0]);
  2961. }
  2962. }
  2963. safe_delete(request);
  2964. }
  2965. void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
  2966. PacketStruct* request = 0;
  2967. if (!app || app->size == 0)
  2968. return;
  2969. //LogWrite(CCLIENT__DEBUG, 0, "Client", "Request2:");
  2970. int8 type = app->pBuffer[0];
  2971. //DumpPacket(app->pBuffer,app->size);
  2972. //283: item: 0, effect: 1, recipe: 2, spell: 3
  2973. if (version <= 373) {
  2974. if (type == 1)
  2975. type = 4;
  2976. else if (type == 2)
  2977. type = 5;
  2978. }
  2979. if (type == 3) {
  2980. Spell* spell = 0;
  2981. bool trait_display;
  2982. request = configReader.getStruct((GetVersion() <= 373) ? "WS_ExamineInfoRequest" : "WS_ExamineInfoRequestMsg", GetVersion());
  2983. if (!request) {
  2984. return;
  2985. }
  2986. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  2987. safe_delete(request);
  2988. return;
  2989. }
  2990. int32 id = request->getType_int32_ByName("id");
  2991. int32 tier = request->getType_int32_ByName("tier");
  2992. int32 trait_tier = request->getType_int32_ByName("unknown_id");
  2993. if (GetVersion() > 373 && GetVersion() <= 561) {
  2994. trait_tier = request->getType_int32_ByName("unique_id");
  2995. }
  2996. bool display = true;
  2997. if (version <= 373 && request->getType_int8_ByName("display") == 1) // this is really requesting a partial packet
  2998. display = false;
  2999. else if (version <= 561)
  3000. display = request->getType_int8_ByName("display");
  3001. else if (version > 561)
  3002. display = false; // clients default is false otherwise it pops up a window when hovering over the knowledge book abilities
  3003. LogWrite(CCLIENT__DEBUG, 5, "Client", "Client::HandleExamineInfoRequest from %s: Type: (%i) Tier: (%u) Unknown ID: (%u) Item ID: (%u)", GetPlayer()->GetName(), type, tier, trait_tier, id);
  3004. if (trait_tier != 0xFFFFFFFF) {
  3005. spell = master_spell_list.GetSpell(id, trait_tier);
  3006. if (!spell) {
  3007. spell = master_spell_list.GetSpell(id, trait_tier + 1);
  3008. }
  3009. trait_display = true;
  3010. }
  3011. else {
  3012. spell = master_spell_list.GetSpell(id, tier);
  3013. trait_display = false;
  3014. }
  3015. // if we can't find it on the master spell list, see if it is a custom spell
  3016. if (!spell)
  3017. {
  3018. lua_interface->FindCustomSpellLock();
  3019. LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
  3020. if (tmpSpell)
  3021. spell = tmpSpell->spell;
  3022. lua_interface->FindCustomSpellUnlock();
  3023. }
  3024. if (!spell) { // fix ui timeout for classic, isle of refuge, dof, kos clients
  3025. int8 playerTier = GetPlayer()->GetSpellTier(id);
  3026. spell = master_spell_list.GetSpell(id, playerTier);
  3027. LogWrite(CCLIENT__WARNING, 0, "Client", "Client::HandleExamineInfoRequest from %s: Failed to find tier 1 spell. Last resort try to get the spell from the player book, spell %u, tier %u", GetPlayer()->GetName(), id, playerTier);
  3028. }
  3029. if (spell && !CountSentSpell(spell->GetSpellID(), spell->GetSpellTier())) {
  3030. if (!spell->IsCopiedSpell())
  3031. SetSentSpell(spell->GetSpellID(), spell->GetSpellTier());
  3032. EQ2Packet* app = spell->SerializeSpell(this, display, trait_display);
  3033. //DumpPacket(app);
  3034. QueuePacket(app);
  3035. }
  3036. else if (spell && GetVersion() <= 561 && CountSentSpell(spell->GetSpellID(), spell->GetSpellTier())) {
  3037. EQ2Packet* app = spell->SerializeSpell(this, display, trait_display, GetVersion() <= 561 ? true : false);
  3038. //DumpPacket(app);
  3039. QueuePacket(app);
  3040. }
  3041. else {
  3042. LogWrite(CCLIENT__ERROR, 0, "Client", "Client::HandleExamineInfoRequest from %s: Failed to successfully send Type: (%i) Tier: (%u) Unknown ID: (%u) Item ID: (%u)", GetPlayer()->GetName(), type, tier, trait_tier, id);
  3043. }
  3044. }
  3045. else if (type == 0) {
  3046. request = configReader.getStruct("WS_ExamineInfoItemRequest", GetVersion());
  3047. if (!request) {
  3048. return;
  3049. }
  3050. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3051. safe_delete(request);
  3052. return;
  3053. }
  3054. int32 id = request->getType_int32_ByName("id");
  3055. Item* item = 0;
  3056. // translate from unique id to spawn id for houses
  3057. Spawn* spawn = this->GetCurrentZone()->GetSpawnFromUniqueItemID(id);
  3058. bool wasSpawn = false;
  3059. if (spawn)
  3060. {
  3061. item = master_item_list.GetItem(spawn->GetPickupItemID());
  3062. if (item)
  3063. {
  3064. wasSpawn = true;
  3065. item = new Item(item);
  3066. item->details.unique_id = spawn->GetPickupUniqueItemID();
  3067. }
  3068. }
  3069. if (!item)
  3070. item = GetPlayer()->item_list.GetItemFromUniqueID(id, true);
  3071. if (!item)
  3072. item = GetPlayer()->GetEquipmentList()->GetItemFromUniqueID(id);
  3073. if (!item)
  3074. item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(id);
  3075. if (!item)
  3076. item = master_item_list.GetItem(id);
  3077. if (item) {// && sent_item_details.count(id) == 0){
  3078. MItemDetails.writelock(__FUNCTION__, __LINE__);
  3079. sent_item_details[id] = true;
  3080. MItemDetails.releasewritelock(__FUNCTION__, __LINE__);
  3081. EQ2Packet* app = item->serialize(GetVersion(), false, GetPlayer());
  3082. QueuePacket(app);
  3083. if (wasSpawn)
  3084. delete item;
  3085. }
  3086. else {
  3087. LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
  3088. DumpPacket(app);
  3089. }
  3090. }
  3091. else if (type == 1) {
  3092. request = configReader.getStruct("WS_ExamineInfoItemRequest", GetVersion());
  3093. if (!request) {
  3094. return;
  3095. }
  3096. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3097. safe_delete(request);
  3098. return;
  3099. }
  3100. int32 id = request->getType_int32_ByName("id");
  3101. int32 unique_id = request->getType_int32_ByName("unique_id");
  3102. Item* item = GetPlayer()->item_list.GetItemFromUniqueID(unique_id, true);
  3103. if (!item)
  3104. item = GetPlayer()->GetEquipmentList()->GetItemFromUniqueID(unique_id);
  3105. if (!item)
  3106. item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(unique_id);
  3107. if (!item)
  3108. item = master_item_list.GetItem(id);
  3109. if (item) {
  3110. MItemDetails.writelock(__FUNCTION__, __LINE__);
  3111. sent_item_details[id] = true;
  3112. MItemDetails.releasewritelock(__FUNCTION__, __LINE__);
  3113. EQ2Packet* app = item->serialize(GetVersion(), false, GetPlayer());
  3114. QueuePacket(app);
  3115. }
  3116. else {
  3117. LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
  3118. DumpPacket(app);
  3119. }
  3120. }
  3121. else if (type == 2) {
  3122. request = configReader.getStruct("WS_ExamineInfoItemLinkRequest", GetVersion());
  3123. if (!request) {
  3124. return;
  3125. }
  3126. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3127. safe_delete(request);
  3128. return;
  3129. }
  3130. int32 id = request->getType_int32_ByName("item_id");
  3131. LogWrite(CCLIENT__DEBUG, 5, "Client", "Client::HandleExamineInfoRequest from %s: Found Type: (%i) Item ID: (%u)", GetPlayer()->GetName(), type, id);
  3132. //int32 unknown_0 = request->getType_int32_ByName("unknown",0);
  3133. //int32 unknown_1 = request->getType_int32_ByName("unknown",1);
  3134. //int8 unknown2 = request->getType_int8_ByName("unknown2");
  3135. //int32 unique_id = request->getType_int32_ByName("unique_id");
  3136. //int16 unknown5 = request->getType_sint16_ByName("unknown5");
  3137. //printf("Type: (%i) Unknown_0: (%u) Unknown_1: (%u) Unknown2: (%i) Unique ID: (%u) Unknown5: (%i) Item ID: (%u)\n",type,unknown_0,unknown_1,unknown2,unique_id,unknown5,id);
  3138. Item* item = master_item_list.GetItem(id);
  3139. if (item) {
  3140. //only display popup for non merchant links
  3141. EQ2Packet* app = item->serialize(GetVersion(), (request->getType_int8_ByName("show_popup") != 0), GetPlayer(), true, 0, 0, GetVersion() > 561 ? true : false);
  3142. QueuePacket(app);
  3143. }
  3144. else {
  3145. LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
  3146. DumpPacket(app);
  3147. }
  3148. }
  3149. else if (type == 4) { //spell effect
  3150. request = configReader.getStruct("WS_ExamineSpellEffectRequest", GetVersion());
  3151. if (!request) {
  3152. return;
  3153. }
  3154. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3155. safe_delete(request);
  3156. return;
  3157. }
  3158. int32 id = request->getType_int32_ByName("id");
  3159. int16 display = request->getType_int8_ByName("partial_info");
  3160. SpellEffects* effect = player->GetSpellEffect(id);
  3161. //printf("Type: (%i) Unknown5: (%i) Item ID: (%u)\n",type,unknown5,id);
  3162. if (effect) {
  3163. LogWrite(CCLIENT__DEBUG, 5, "Client", "Client::HandleExamineInfoRequest from %s: Found Type: (%i) Item ID: (%u)", GetPlayer()->GetName(), type, id);
  3164. int8 tier = effect->tier;
  3165. Spell* spell = master_spell_list.GetSpell(id, tier);
  3166. // if we can't find it on the master spell list, see if it is a custom spell
  3167. if (!spell)
  3168. {
  3169. lua_interface->FindCustomSpellLock();
  3170. LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
  3171. EQ2Packet* pack = 0;
  3172. if (tmpSpell)
  3173. spell = tmpSpell->spell;
  3174. lua_interface->FindCustomSpellUnlock();
  3175. }
  3176. if (spell && (version <= 561 || !CountSentSpell(id, tier))) { // fix DoF and KoS clients UI Timeout
  3177. if (!spell->IsCopiedSpell())
  3178. SetSentSpell(spell->GetSpellID(), spell->GetSpellTier());
  3179. int8 type = 0;
  3180. if (version <= 373)
  3181. type = 1;
  3182. EQ2Packet* app = spell->SerializeSpecialSpell(this, false, type, 0x81);
  3183. //DumpPacket(app);
  3184. QueuePacket(app);
  3185. }
  3186. }
  3187. else {
  3188. LogWrite(CCLIENT__ERROR, 0, "Client", "Client::HandleExamineInfoRequest from %s: Cannot Find Type: (%i) Item ID: (%u)", GetPlayer()->GetName(), type, id);
  3189. }
  3190. }
  3191. else if (type == 5) { // recipe info
  3192. request = configReader.getStruct((GetVersion() <= 373) ? "WS_ExamineInfoRequest" : "WS_ExamineInfoRequestMsg", GetVersion());
  3193. if (!request)
  3194. return;
  3195. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3196. safe_delete(request);
  3197. return;
  3198. }
  3199. int32 id = 0;
  3200. if (GetVersion() < 546) {
  3201. id = request->getType_int32_ByName("id");
  3202. }
  3203. else if (GetVersion() <= 561) {
  3204. id = request->getType_int32_ByName("unique_id");
  3205. }
  3206. else {
  3207. id = request->getType_int32_ByName("unknown_id");
  3208. }
  3209. Recipe* recipe = master_recipe_list.GetRecipe(id);
  3210. if (recipe) {
  3211. EQ2Packet* app = recipe->SerializeRecipe(this, recipe, false, GetItemPacketType(GetVersion()), 0x02);
  3212. //DumpPacket(app);
  3213. QueuePacket(app);
  3214. }
  3215. }
  3216. else if (type == 6) { // AA spell info
  3217. Spell* spell = 0;
  3218. //Spell* spell2 = 0;
  3219. //AltAdvanceData* data = 0;
  3220. request = configReader.getStruct((GetVersion() <= 373) ? "WS_ExamineInfoRequest" : "WS_ExamineInfoRequestMsg", GetVersion());
  3221. if (!request)
  3222. return;
  3223. if (!request->LoadPacketData(app->pBuffer, app->size)) {
  3224. safe_delete(request);
  3225. return;
  3226. }
  3227. int32 id = request->getType_int32_ByName("id");
  3228. int32 tier = GetPlayer()->GetSpellTier(id);
  3229. LogWrite(WORLD__INFO, 0, "World", "Examine Info Request->Unique ID: %u Tier: %u ", id, tier);
  3230. //data = master_aa_list.GetAltAdvancement(id);
  3231. //LogWrite(WORLD__INFO, 0, "World", "SOE Spell CRC: %u", data->spell_crc);
  3232. //spell = master_spell_list.GetSpellByCRC(data->spell_crc);
  3233. spell = master_spell_list.GetSpell(id, tier);
  3234. if (!spell)
  3235. spell = master_spell_list.GetSpell(id, 1);
  3236. if (!spell)
  3237. {
  3238. lua_interface->FindCustomSpellLock();
  3239. LuaSpell* tmpSpell = lua_interface->FindCustomSpell(id);
  3240. if (tmpSpell)
  3241. spell = tmpSpell->spell;
  3242. lua_interface->FindCustomSpellUnlock();
  3243. }
  3244. if (!spell)
  3245. {
  3246. LogWrite(CCLIENT__ERROR, 0, "Client", "WORLD", "Client::HandleExamineInfoRequest from %s: FAILED Examine Info Request-> Spell ID: %u, tier: %i", GetPlayer()->GetName(), id, tier);
  3247. return;
  3248. }
  3249. //if (spell && sent_spell_details.count(spell->GetSpellID()) == 0) {
  3250. if (!spell->IsCopiedSpell())
  3251. SetSentSpell(spell->GetSpellID(), spell->GetSpellTier());
  3252. // EQ2Packet* app = spell->SerializeAASpell(this,tier, data, false, GetItemPacketType(GetVersion()), 0x04);
  3253. LogWrite(WORLD__INFO, 0, "WORLD", "Examine Info Request-> Spell ID: %u", spell->GetSpellID());
  3254. if (GetVersion() > 561) {
  3255. EQ2Packet* app = master_spell_list.GetAASpellPacket(id, tier, this, false, 0x4F);//0x45 change version to match client
  3256. /////////////////////////////////////////GetAASpellPacket(int32 id, int8 tier, Client* client, bool display, int8 packet_type) {
  3257. //DumpPacket(app);
  3258. QueuePacket(app);
  3259. }
  3260. //}
  3261. }
  3262. else {
  3263. LogWrite(CCLIENT__ERROR, 0, "World", "Client::HandleExamineInfoRequest from %s: Unknown examine request: %i", GetPlayer()->GetName(), (int)type);
  3264. DumpPacket(app);
  3265. }
  3266. safe_delete(request);
  3267. }
  3268. void Client::HandleQuickbarUpdateRequest(EQApplicationPacket* app) {
  3269. PacketStruct* request = configReader.getStruct("WS_QuickBarUpdateRequest", GetVersion());
  3270. if (request) {
  3271. if (request->LoadPacketData(app->pBuffer, app->size)) {
  3272. int32 id = request->getType_int32_ByName("id");
  3273. int32 bar = request->getType_int32_ByName("hotbar_number");
  3274. int32 slot = request->getType_int32_ByName("hotkey_slot");
  3275. int32 type = request->getType_int32_ByName("type");
  3276. int8 tier = request->getType_int32_ByName("unique_id");
  3277. EQ2_16BitString text = request->getType_EQ2_16BitString_ByName("text");
  3278. Spell* spell = 0;
  3279. if (type == 0xFFFFFFFF)
  3280. GetPlayer()->RemoveQuickbarItem(bar, slot);
  3281. else {
  3282. if (type == QUICKBAR_NORMAL)
  3283. spell = master_spell_list.GetSpell(id, tier);
  3284. if (spell)
  3285. GetPlayer()->AddQuickbarItem(bar, slot, type, spell->GetSpellIcon(), spell->GetSpellIconBackdrop(), id, tier, 0, text.data.c_str());
  3286. else
  3287. GetPlayer()->AddQuickbarItem(bar, slot, type, 0, 0, id, 0, 0, text.data.c_str());
  3288. }
  3289. }
  3290. safe_delete(request);
  3291. }
  3292. }
  3293. bool Client::Process(bool zone_process) {
  3294. bool ret = true;
  3295. // EQS can become null if player is linkdead, we want to always be able to process the camp/linkdead timers when active
  3296. if ((camp_timer && camp_timer->Check()) || (linkdead_timer && linkdead_timer->Check())) {
  3297. ResetSendMail();
  3298. if (getConnection())
  3299. getConnection()->SendDisconnect(false);
  3300. safe_delete(camp_timer);
  3301. if (linkdead_timer) {
  3302. LogWrite(CCLIENT__DEBUG, 0, "Client", "Player %s triggered linkdead timer, disconnecting", GetPlayer()->GetName());
  3303. // we remove the linkdead status and force a camp out immediately
  3304. if ((GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) {
  3305. GetPlayer()->SetActivityStatus(GetPlayer()->GetActivityStatus() - ACTIVITY_STATUS_LINKDEAD);
  3306. }
  3307. if ((GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0) {
  3308. GetPlayer()->SetActivityStatus(GetPlayer()->GetActivityStatus() + ACTIVITY_STATUS_CAMPING);
  3309. }
  3310. }
  3311. safe_delete(linkdead_timer);
  3312. ret = false;
  3313. }
  3314. if (client_zoning_details_set) {
  3315. if (!zoning_details.zoningPastAuth && (zoning_details.authDispatchedTime + 5) <= Timer::GetUnixTimeStamp()) {
  3316. zoning_details.zoningPastAuth = true; // don't repeat
  3317. world.ClientAuthApproval(0, std::string(player->GetName()), GetAccountID(), zoning_details.zoneName, zoning_details.zoneId, zoning_details.instanceId, false);
  3318. }
  3319. }
  3320. if (!eqs) {
  3321. return false;
  3322. }
  3323. if ((connected_to_zone && !zone_process) || (!connected_to_zone && zone_process)) {
  3324. return true;
  3325. }
  3326. switch (new_client_login) {
  3327. case NewLoginState::LOGIN_SEND: {
  3328. LogWrite(CCLIENT__DEBUG, 0, "Client", "SendLoginInfo to new client...");
  3329. SendLoginInfo();
  3330. new_client_login = NewLoginState::LOGIN_NONE;
  3331. break;
  3332. }
  3333. case NewLoginState::LOGIN_INITIAL_LOAD: {
  3334. bool isDBActive = database.IsActiveQuery(GetCharacterID());
  3335. // wait for starting skills/spells to load and reload from DB.
  3336. if (!isDBActive) {
  3337. if (GetPlayer()->GetInfoStruct()->get_reload_player_spells()) {
  3338. database.LoadCharacterSpells(GetCharacterID(), GetPlayer());
  3339. GetPlayer()->GetInfoStruct()->set_reload_player_spells(0);
  3340. }
  3341. new_client_login = NewLoginState::LOGIN_SEND;
  3342. }
  3343. break;
  3344. }
  3345. case NewLoginState::LOGIN_ALLOWED: {
  3346. int32 count = 0;
  3347. if (!GetPlayer()->IsReturningFromLD())
  3348. {
  3349. LogWrite(CCLIENT__DEBUG, 0, "Client", "Loading Character Skills for player '%s'...", player->GetName());
  3350. count = database.LoadCharacterSkills(GetCharacterID(), player);
  3351. LogWrite(CCLIENT__DEBUG, 0, "Client", "Loading Character Spells for player '%s'...", player->GetName());
  3352. count = database.LoadCharacterSpells(GetCharacterID(), player);
  3353. }
  3354. else
  3355. {
  3356. LogWrite(CCLIENT__INFO, 0, "Client", "Player is returning from linkdead status (Player does not need reload) thus skipping database loading for '%s'...", player->GetName());
  3357. }
  3358. // get the latest character starting skills / spells, may have been updated after character creation
  3359. if (GetPlayer()->GetInfoStruct()->get_first_world_login()) {
  3360. world.SyncCharacterAbilities(this);
  3361. Query query;
  3362. query.AddQueryAsync(GetCharacterID(), &database, Q_UPDATE, "UPDATE characters set first_world_login = 0 where id=%u", GetCharacterID());
  3363. GetPlayer()->GetInfoStruct()->set_first_world_login(0);
  3364. }
  3365. new_client_login = NewLoginState::LOGIN_INITIAL_LOAD;
  3366. break;
  3367. }
  3368. case NewLoginState::LOGIN_DELAYED: {
  3369. if (!delay_msg_timer.Enabled() || delay_msg_timer.Check()) {
  3370. LogWrite(CCLIENT__INFO, 0, "Client", "Wait for zone %s to load for new client %s...", GetCurrentZone()->GetZoneName(), GetPlayer()->GetName());
  3371. delay_msg_timer.Start(1000, true);
  3372. }
  3373. if (!GetCurrentZone()->IsLoading()) {
  3374. new_client_login = NewLoginState::LOGIN_ALLOWED;
  3375. }
  3376. return true;
  3377. break;
  3378. }
  3379. }
  3380. delay_msg_timer.Disable();
  3381. sockaddr_in to;
  3382. memset((char*)&to, 0, sizeof(to));
  3383. to.sin_family = AF_INET;
  3384. to.sin_port = port;
  3385. to.sin_addr.s_addr = ip;
  3386. /************ Get all packets from packet manager out queue and process them ************/
  3387. EQApplicationPacket* app = 0;
  3388. if (eqs && !eqs->CheckActive()) {
  3389. num_active_failures++;
  3390. LogWrite(CCLIENT__DEBUG, 7, "Client", "%s, num_active_failures = %i", __FUNCTION__, num_active_failures);
  3391. if (num_active_failures > 100) {
  3392. return false;
  3393. }
  3394. return true;
  3395. }
  3396. while (ret && eqs && (app = eqs->PopPacket())) {
  3397. ret = HandlePacket(app);
  3398. LogWrite(CCLIENT__DEBUG, 5, "Client", "Func: %s, Line: %i, Opcode: '%s'", __FUNCTION__, __LINE__, app->GetOpcodeName());
  3399. delete app;
  3400. }
  3401. if (GetCurrentZone() && !GetCurrentZone()->IsLoading() && GetCurrentZone()->GetSpawnByID(GetPlayer()->GetID()) && should_load_spells) {
  3402. //database.LoadCharacterActiveSpells(player);
  3403. player->UnlockAllSpells(true);
  3404. should_load_spells = false;
  3405. }
  3406. if (spawn_removal_timer.Check() && GetPlayer()) {
  3407. GetPlayer()->ProcessSpawnRangeUpdates();
  3408. GetPlayer()->CheckSpawnStateQueue();
  3409. }
  3410. if (delayedLogin && delayTimer.Enabled() && delayTimer.Check())
  3411. {
  3412. if (!HandleNewLogin(delayedAccountID, delayedAccessKey))
  3413. return false;
  3414. }
  3415. if (quest_updates) {
  3416. LogWrite(CCLIENT__DEBUG, 1, "Client", "%s, ProcessQuestUpdates", __FUNCTION__, __LINE__);
  3417. ProcessQuestUpdates();
  3418. }
  3419. int32 queue_timer_delay = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Client, QuestQueueTimer)->GetInt32();
  3420. if (queue_timer_delay < 10) {
  3421. queue_timer_delay = 10;
  3422. }
  3423. if (last_update_time > 0 && last_update_time < (Timer::GetCurrentTime2() - queue_timer_delay)) {
  3424. LogWrite(CCLIENT__DEBUG, 1, "Client", "%s, CheckQuestQueue", __FUNCTION__, __LINE__);
  3425. CheckQuestQueue();
  3426. }
  3427. MSaveSpellStateMutex.lock();
  3428. if (save_spell_state_timer.Check())
  3429. {
  3430. save_spell_state_timer.Disable();
  3431. GetPlayer()->SaveSpellEffects();
  3432. }
  3433. MSaveSpellStateMutex.unlock();
  3434. if (temp_placement_timer.Check()) {
  3435. if (GetTempPlacementSpawn() && GetPlayer()->WasSentSpawn(GetTempPlacementSpawn()->GetID()) && !hasSentTempPlacementSpawn) {
  3436. int8 placement = 0;
  3437. int32 uniqueID = GetPlacementUniqueItemID();
  3438. Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
  3439. if (uniqueItem && uniqueItem->houseitem_info)
  3440. placement = uniqueItem->houseitem_info->house_location;
  3441. SendMoveObjectMode(GetTempPlacementSpawn(), placement);
  3442. hasSentTempPlacementSpawn = true;
  3443. temp_placement_timer.Disable();
  3444. }
  3445. }
  3446. if (GetCurrentZone() && pos_update.Check())
  3447. {
  3448. ProcessStateCommands();
  3449. GetPlayer()->ResetMentorship(); // check if we need to asynchronously reset mentorship
  3450. if (GetPlayer()->GetRegionMap())
  3451. GetPlayer()->GetRegionMap()->TicRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
  3452. if (!player_pos_changed && IsReadyForUpdates() && player_pos_timer < Timer::GetCurrentTime2() && enabled_player_pos_timer) {
  3453. if (version > 373) {
  3454. GetPlayer()->info_changed = true;
  3455. GetPlayer()->vis_changed = true;
  3456. GetPlayer()->position_changed = true;
  3457. GetPlayer()->changed = true;
  3458. GetPlayer()->AddChangedZoneSpawn();
  3459. }
  3460. player_pos_timer = Timer::GetCurrentTime2() + 5000;
  3461. enabled_player_pos_timer = false;
  3462. }
  3463. if (player_pos_changed && IsReadyForUpdates()) {
  3464. player_pos_timer = Timer::GetCurrentTime2() + 500;
  3465. enabled_player_pos_timer = true;
  3466. if (!underworld_cooldown_timer.Enabled() || (underworld_cooldown_timer.Enabled() && underworld_cooldown_timer.Check())) {
  3467. bool underworld = false;
  3468. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, UseMapUnderworldCoords)->GetBool()) {
  3469. if (GetCurrentZone() && GetCurrentZone()->GetUnderWorld() != -1000000.0f) {
  3470. if (GetPlayer()->GetY() < GetCurrentZone()->GetUnderWorld())
  3471. underworld = true;
  3472. }
  3473. else if (GetPlayer()->GetMap() && GetPlayer()->GetMap()->GetMinY() != 9999999.0f && GetPlayer()->GetY() < (GetPlayer()->GetMap()->GetMinY() + rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, MapUnderworldCoordOffset)->GetFloat())) {
  3474. underworld = true;
  3475. }
  3476. }
  3477. else if (GetPlayer()->GetMap() && GetPlayer()->GetY() < GetCurrentZone()->GetUnderWorld()) {
  3478. underworld = true;
  3479. }
  3480. if (underworld && GetCurrentZone()) {
  3481. player->SetX(GetCurrentZone()->GetSafeX());
  3482. player->SetY(GetCurrentZone()->GetSafeY());
  3483. player->SetZ(GetCurrentZone()->GetSafeZ());
  3484. player->SetHeading(GetCurrentZone()->GetSafeHeading());
  3485. EQ2Packet* app = GetPlayer()->Move(player->GetX(), player->GetY(), player->GetZ(), GetVersion(), player->GetHeading());
  3486. if (app) {
  3487. QueuePacket(app);
  3488. }
  3489. SimpleMessage(CHANNEL_COLOR_RED, "You have been teleported to a safe location in the zone, because you appeared to have fallen through the world.");
  3490. }
  3491. underworld_cooldown_timer.Start();
  3492. }
  3493. //GetPlayer()->CalculateLocation();
  3494. client_list.CheckPlayersInvisStatus(this);
  3495. player_pos_changed = false;
  3496. GetCurrentZone()->CheckTransporters(this);
  3497. if (GetPlayer()->GetRegionMap())
  3498. {
  3499. GetPlayer()->GetRegionMap()->MapRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
  3500. }
  3501. }
  3502. else if (IsReadyForUpdates() && GetPlayer()->GetRegionMap()) {
  3503. GetPlayer()->GetRegionMap()->UpdateRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
  3504. }
  3505. }
  3506. if (lua_interface && lua_debug && lua_debug_timer.Check())
  3507. lua_interface->UpdateDebugClients(this);
  3508. if (quest_pos_timer.Check())
  3509. CheckPlayerQuestsLocationUpdate();
  3510. if (player->GetSkills()->HasSkillUpdates()) {
  3511. vector<Skill*>* skills = player->GetSkills()->GetSkillUpdates();
  3512. if (skills) {
  3513. Skill* skill = 0;
  3514. vector<Skill*>::iterator itr;
  3515. for (itr = skills->begin(); itr != skills->end(); itr++) {
  3516. skill = *itr;
  3517. SkillChanged(skill, skill->previous_val, skill->current_val);
  3518. }
  3519. EQ2Packet* app = GetPlayer()->skill_list.GetSkillPacket(GetVersion());
  3520. if (app) QueuePacket(app);
  3521. safe_delete(skills);
  3522. }
  3523. }
  3524. m_resurrect.writelock(__FUNCTION__, __LINE__);
  3525. if (current_rez.should_delete || (current_rez.expire_timer && current_rez.expire_timer->Check(false))) {
  3526. safe_delete(current_rez.expire_timer);
  3527. current_rez.expire_timer = 0;
  3528. current_rez.active = false;
  3529. current_rez.caster = 0;
  3530. current_rez.crit = false;
  3531. current_rez.crit_mod = 0;
  3532. current_rez.expire_timer = 0;
  3533. current_rez.heal_name = "";
  3534. current_rez.hp_perc = 0;
  3535. current_rez.mp_perc = 0;
  3536. current_rez.no_calcs = false;
  3537. current_rez.range = 0;
  3538. current_rez.should_delete = false;
  3539. current_rez.spell_name = "";
  3540. current_rez.spell_visual = 0;
  3541. current_rez.subspell = 0;
  3542. }
  3543. m_resurrect.releasewritelock(__FUNCTION__, __LINE__);
  3544. // Quest timers
  3545. Quest* failed_step = 0;
  3546. MQuestTimers.writelock(__FUNCTION__, __LINE__);
  3547. if (quest_timers.size() > 0) {
  3548. vector<int32>::iterator itr;
  3549. GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
  3550. map<int32, Quest*>* player_quests = player->GetPlayerQuests();
  3551. for (itr = quest_timers.begin(); itr != quest_timers.end(); itr++) {
  3552. if (player_quests->count(*itr) > 0 && player_quests->at(*itr)->GetStepTimer() != 0) {
  3553. Quest* quest = player_quests->at(*itr);
  3554. if (Timer::GetUnixTimeStamp() >= quest->GetStepTimer()) {
  3555. failed_step = quest;
  3556. break;
  3557. }
  3558. }
  3559. else {
  3560. itr = quest_timers.erase(itr);
  3561. break;
  3562. }
  3563. }
  3564. GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
  3565. }
  3566. MQuestTimers.releasewritelock(__FUNCTION__, __LINE__);
  3567. if (failed_step)
  3568. failed_step->StepFailed(failed_step->GetTimerStep());
  3569. if (player->ControlFlagsChanged())
  3570. player->SendControlFlagUpdates(this);
  3571. if (!eqs || (eqs && !eqs->CheckActive()))
  3572. ret = false;
  3573. // redundant to client disconnect
  3574. // if(!ret)
  3575. // Save();
  3576. return ret;
  3577. }
  3578. ClientList::ClientList() {
  3579. MClients.SetName("ClientList::MClients");
  3580. }
  3581. ClientList::~ClientList() {
  3582. }
  3583. void ClientList::ReloadQuests() {
  3584. list<Client*>::iterator client_iter;
  3585. Client* client = 0;
  3586. MClients.readlock(__FUNCTION__, __LINE__);
  3587. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3588. client = *client_iter;
  3589. if (client)
  3590. client->ReloadQuests();
  3591. }
  3592. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3593. }
  3594. void ClientList::CheckPlayersInvisStatus(Client* owner) {
  3595. if (!owner->GetPlayer() || (!owner->GetPlayer()->IsInvis() && !owner->GetPlayer()->IsStealthed()))
  3596. return;
  3597. list<Client*>::iterator client_iter;
  3598. Client* client = 0;
  3599. MClients.readlock(__FUNCTION__, __LINE__);
  3600. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3601. client = *client_iter;
  3602. if (client == owner || client->GetPlayer() == NULL)
  3603. continue;
  3604. if (client->GetPlayer()->CheckChangeInvisHistory((Entity*)owner->GetPlayer()))
  3605. client->GetPlayer()->GetZone()->SendSpawnChanges(owner->GetPlayer(), client, true, true);
  3606. }
  3607. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3608. }
  3609. void ClientList::RemovePlayerFromInvisHistory(int32 spawnID) {
  3610. list<Client*>::iterator client_iter;
  3611. Client* client = 0;
  3612. MClients.readlock(__FUNCTION__, __LINE__);
  3613. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3614. client = *client_iter;
  3615. if (!client->GetPlayer())
  3616. continue;
  3617. client->GetPlayer()->RemoveTargetInvisHistory(spawnID);
  3618. }
  3619. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3620. }
  3621. int32 ClientList::Count() {
  3622. return client_list.size();
  3623. }
  3624. void ClientList::Add(Client* client) {
  3625. MClients.writelock(__FUNCTION__, __LINE__);
  3626. client_list.push_back(client);
  3627. MClients.releasewritelock(__FUNCTION__, __LINE__);
  3628. }
  3629. Client* ClientList::FindByAccountID(int32 account_id) {
  3630. list<Client*>::iterator client_iter;
  3631. Client* client = 0;
  3632. Client* ret = 0;
  3633. MClients.readlock(__FUNCTION__, __LINE__);
  3634. for (client_iter = client_list.begin(); client_list.size() > 0 && client_iter != client_list.end(); client_iter++) {
  3635. client = *client_iter;
  3636. if (client->GetAccountID() == account_id) {
  3637. ret = client;
  3638. break;
  3639. }
  3640. }
  3641. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3642. return ret;
  3643. }
  3644. Client* ClientList::FindByName(char* charName) {
  3645. list<Client*>::iterator client_iter;
  3646. Client* client = 0;
  3647. Client* ret = 0;
  3648. MClients.readlock(__FUNCTION__, __LINE__);
  3649. for (client_iter = client_list.begin(); client_list.size() > 0 && client_iter != client_list.end(); client_iter++) {
  3650. client = *client_iter;
  3651. if (!client || !client->GetPlayer())
  3652. continue;
  3653. if (!strncmp(client->GetPlayer()->GetName(), charName, strlen(charName))) {
  3654. ret = client;
  3655. break;
  3656. }
  3657. }
  3658. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3659. return ret;
  3660. }
  3661. Client* ClientList::Get(int32 ip, int16 port) {
  3662. list<Client*>::iterator client_iter;
  3663. Client* client = 0;
  3664. Client* ret = 0;
  3665. MClients.readlock(__FUNCTION__, __LINE__);
  3666. for (client_iter = client_list.begin(); client_list.size() > 0 && client_iter != client_list.end(); client_iter++) {
  3667. client = *client_iter;
  3668. if (client->GetIP() == ip && client->GetPort() == port) {
  3669. ret = client;
  3670. break;
  3671. }
  3672. }
  3673. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3674. return ret;
  3675. }
  3676. void ClientList::Process() {
  3677. list<Client*>::iterator client_iter;
  3678. list<Client*>::iterator erase_iter;
  3679. Client* client = 0;
  3680. MClients.readlock(__FUNCTION__, __LINE__);
  3681. erase_iter = client_list.end();
  3682. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3683. client = *client_iter;
  3684. // have a sanity check because the client list can sometimes obtain null client pointers
  3685. if (!client || client->remove_from_list || (!client->Process())) { // if we should be removing from list, don't process any further
  3686. erase_iter = client_iter;
  3687. break;
  3688. }
  3689. }
  3690. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3691. if (erase_iter != client_list.end()) {
  3692. client = *erase_iter;
  3693. MClients.writelock(__FUNCTION__, __LINE__);
  3694. client_list.erase(erase_iter);
  3695. MClients.releasewritelock(__FUNCTION__, __LINE__);
  3696. if (client && !client->remove_from_list) {
  3697. struct in_addr in;
  3698. in.s_addr = client->GetIP();
  3699. LogWrite(WORLD__INFO, 0, "World", "Removing client from ip: %s port: %i", inet_ntoa(in), client->GetPort());
  3700. safe_delete(client);
  3701. }
  3702. }
  3703. }
  3704. void ClientList::RemoveConnection(EQStream* eqs) {
  3705. Client* client;
  3706. if (eqs) {
  3707. list<Client*>::iterator client_iter;
  3708. MClients.readlock(__FUNCTION__, __LINE__);
  3709. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3710. client = *client_iter;
  3711. if (client->getConnection() == eqs)
  3712. client->Disconnect(true);
  3713. }
  3714. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3715. }
  3716. }
  3717. bool ClientList::ContainsStream(EQStream* eqs) {
  3718. if (!eqs) {
  3719. return false;
  3720. }
  3721. list<Client*>::iterator client_iter;
  3722. bool ret = false;
  3723. MClients.readlock(__FUNCTION__, __LINE__);
  3724. for (client_iter = client_list.begin(); client_iter != client_list.end(); client_iter++) {
  3725. if ((*client_iter)->getConnection() && (*client_iter)->getConnection()->GetRemotePort() == eqs->GetRemotePort() && (*client_iter)->getConnection()->GetRemoteIP() == eqs->GetRemoteIP()) {
  3726. ret = true;
  3727. break;
  3728. }
  3729. }
  3730. MClients.releasereadlock(__FUNCTION__, __LINE__);
  3731. return ret;
  3732. }
  3733. void ClientList::Remove(Client* client, bool remove_data) {
  3734. client->remove_from_list = true;
  3735. if (remove_data) {
  3736. safe_delete(client);
  3737. }
  3738. }
  3739. void Client::SetCurrentZone(ZoneServer* zone) {
  3740. current_zone = zone;
  3741. if (player) {
  3742. player->SetZone(zone, GetVersion());
  3743. }
  3744. }
  3745. void Client::SetCurrentZone(int32 id) {
  3746. if (current_zone) {
  3747. //current_zone->GetCombat()->RemoveHate(player);
  3748. current_zone->RemoveSpawn(player, false, true, true, true, true);
  3749. }
  3750. ZoneChangeDetails zone_details;
  3751. if (zone_list.GetZone(&zone_details, id, "", true, false, true, false)) {
  3752. SetCurrentZone((ZoneServer*)zone_details.zonePtr);
  3753. }
  3754. }
  3755. void Client::SetCurrentZoneByInstanceID(int32 id, int32 zoneid) {
  3756. if (current_zone) {
  3757. //current_zone->GetCombat()->RemoveHate(player);
  3758. current_zone->RemoveSpawn(player, false, true, true, true, true);
  3759. }
  3760. ZoneChangeDetails zone_details;
  3761. if (zone_list.GetZoneByInstance(&zone_details, id, zoneid, true, false, true, false)) {
  3762. SetCurrentZone((ZoneServer*)zone_details.zonePtr);
  3763. }
  3764. else {
  3765. LogWrite(CCLIENT__ERROR, 0, "Client", "Failed to Client::SetCurrentZoneByInstanceID(id = %u, zoneid = %u) for Player %s.", id, zoneid, GetPlayer()->GetName());
  3766. }
  3767. }
  3768. ZoneServer* Client::GetCurrentZone() {
  3769. return current_zone;
  3770. }
  3771. int32 Client::GetCurrentZoneID() {
  3772. return current_zone ? current_zone->GetZoneID() : 0;
  3773. }
  3774. int8 Client::GetMessageChannelColor(int8 channel_type) {
  3775. if (GetVersion() >= 973 && GetVersion() <= 1000) {
  3776. if (channel_type == CHANNEL_LOOT)
  3777. return CHANNEL_COLOR_NEW_LOOT;
  3778. }
  3779. else if (GetVersion() >= 973) {
  3780. if (channel_type == CHANNEL_LOOT)
  3781. return CHANNEL_COLOR_NEWEST_LOOT;
  3782. }
  3783. if (GetVersion() <= 283) {
  3784. if (channel_type <= 12)
  3785. return channel_type;
  3786. switch (channel_type) {
  3787. case CHANNEL_GROUP_CHAT:
  3788. case CHANNEL_GROUP_SAY:
  3789. case CHANNEL_RAID_SAY:
  3790. case CHANNEL_GUILD_CHAT:
  3791. case CHANNEL_GUILD_SAY:
  3792. case CHANNEL_OFFICER_SAY:
  3793. case CHANNEL_GUILD_MOTD:
  3794. return channel_type - 1;
  3795. case CHANNEL_PRIVATE_CHAT:
  3796. case CHANNEL_NONPLAYER_TELL:
  3797. return channel_type - 5;
  3798. case CHANNEL_PRIVATE_TELL:
  3799. case CHANNEL_TELL_FROM_CS:
  3800. return channel_type - 6;
  3801. case CHANNEL_CHAT_CHANNEL_TEXT:
  3802. case CHANNEL_OUT_OF_CHARACTER:
  3803. case CHANNEL_AUCTION:
  3804. case CHANNEL_CUSTOM_CHANNEL:
  3805. case CHANNEL_CHARACTER_TEXT:
  3806. case CHANNEL_REWARD:
  3807. case CHANNEL_DEATH:
  3808. case CHANNEL_PET_CHAT:
  3809. case CHANNEL_SKILL:
  3810. return channel_type - 7;
  3811. case CHANNEL_SPELLS:
  3812. case CHANNEL_YOU_CAST:
  3813. case CHANNEL_YOU_FAIL:
  3814. return channel_type - 8;
  3815. case CHANNEL_FRIENDLY_CAST:
  3816. case CHANNEL_FRIENDLY_FAIL:
  3817. case CHANNEL_OTHER_CAST:
  3818. case CHANNEL_OTHER_FAIL:
  3819. case CHANNEL_HOSTILE_CAST:
  3820. case CHANNEL_HOSTILE_FAIL:
  3821. case CHANNEL_WORN_OFF:
  3822. case CHANNEL_SPELLS_OTHER:
  3823. return channel_type - 9;
  3824. case CHANNEL_COMBAT:
  3825. return channel_type - 15;
  3826. case CHANNEL_HEROIC_OPPORTUNITY:
  3827. case CHANNEL_NON_MELEE_DAMAGE:
  3828. case CHANNEL_DAMAGE_SHIELD:
  3829. return channel_type - 16;
  3830. case CHANNEL_MELEE_COMBAT:
  3831. case CHANNEL_WARNINGS:
  3832. case CHANNEL_YOU_HIT:
  3833. case CHANNEL_YOU_MISS:
  3834. case CHANNEL_ATTACKER_HITS:
  3835. case CHANNEL_ATTACKER_MISSES:
  3836. return channel_type - 18;
  3837. case CHANNEL_OTHER_HIT:
  3838. case CHANNEL_OTHER_MISSES:
  3839. case CHANNEL_CRITICAL_HIT:
  3840. return channel_type - 22;
  3841. case CHANNEL_OTHER:
  3842. case CHANNEL_MONEY_SPLIT:
  3843. case CHANNEL_LOOT:
  3844. return channel_type - 30;
  3845. case CHANNEL_COMMAND_TEXT:
  3846. case CHANNEL_BROADCAST:
  3847. case CHANNEL_WHO:
  3848. case CHANNEL_COMMANDS:
  3849. case CHANNEL_MERCHANT:
  3850. case CHANNEL_MERCHANT_BUY_SELL:
  3851. case CHANNEL_CONSIDER_MESSAGE:
  3852. case CHANNEL_CON_MINUS_2:
  3853. case CHANNEL_CON_MINUS_1:
  3854. case CHANNEL_CON_0:
  3855. case CHANNEL_CON_1:
  3856. case CHANNEL_CON_2:
  3857. return channel_type - 31;
  3858. default: {
  3859. return CHANNEL_DEFAULT;
  3860. }
  3861. }
  3862. }
  3863. else if (GetVersion() <= 373) {
  3864. if (channel_type <= 18)
  3865. return channel_type;
  3866. switch (channel_type) {
  3867. case CHANNEL_PRIVATE_CHAT:
  3868. case CHANNEL_NONPLAYER_TELL:
  3869. return 22;
  3870. case CHANNEL_PRIVATE_TELL:
  3871. case CHANNEL_TELL_FROM_CS:
  3872. return 23;
  3873. case CHANNEL_CHAT_CHANNEL_TEXT:
  3874. case CHANNEL_OUT_OF_CHARACTER:
  3875. case CHANNEL_CUSTOM_CHANNEL:
  3876. case CHANNEL_CHARACTER_TEXT:
  3877. case CHANNEL_REWARD:
  3878. case CHANNEL_DEATH:
  3879. case CHANNEL_PET_CHAT:
  3880. case CHANNEL_SKILL:
  3881. return 26;
  3882. case CHANNEL_AUCTION:
  3883. return 27;
  3884. case CHANNEL_SPELLS:
  3885. case CHANNEL_YOU_CAST:
  3886. case CHANNEL_YOU_FAIL:
  3887. return channel_type - 8;
  3888. case CHANNEL_FRIENDLY_CAST:
  3889. case CHANNEL_FRIENDLY_FAIL:
  3890. case CHANNEL_OTHER_CAST:
  3891. case CHANNEL_OTHER_FAIL:
  3892. case CHANNEL_HOSTILE_CAST:
  3893. case CHANNEL_HOSTILE_FAIL:
  3894. case CHANNEL_WORN_OFF:
  3895. case CHANNEL_SPELLS_OTHER:
  3896. return channel_type - 9;
  3897. case CHANNEL_COMBAT:
  3898. return channel_type - 15;
  3899. case CHANNEL_HEROIC_OPPORTUNITY:
  3900. case CHANNEL_NON_MELEE_DAMAGE:
  3901. case CHANNEL_DAMAGE_SHIELD:
  3902. return channel_type - 16;
  3903. case CHANNEL_MELEE_COMBAT:
  3904. case CHANNEL_WARNINGS:
  3905. case CHANNEL_YOU_HIT:
  3906. case CHANNEL_YOU_MISS:
  3907. case CHANNEL_ATTACKER_HITS:
  3908. case CHANNEL_ATTACKER_MISSES:
  3909. return channel_type - 18;
  3910. case CHANNEL_OTHER_HIT:
  3911. case CHANNEL_OTHER_MISSES:
  3912. case CHANNEL_CRITICAL_HIT:
  3913. return channel_type - 22;
  3914. case CHANNEL_OTHER:
  3915. case CHANNEL_MONEY_SPLIT:
  3916. case CHANNEL_LOOT:
  3917. return channel_type - 30;
  3918. case CHANNEL_COMMAND_TEXT:
  3919. case CHANNEL_BROADCAST:
  3920. case CHANNEL_WHO:
  3921. case CHANNEL_COMMANDS:
  3922. case CHANNEL_MERCHANT:
  3923. case CHANNEL_MERCHANT_BUY_SELL:
  3924. case CHANNEL_CONSIDER_MESSAGE:
  3925. case CHANNEL_CON_MINUS_2:
  3926. case CHANNEL_CON_MINUS_1:
  3927. case CHANNEL_CON_0:
  3928. case CHANNEL_CON_1:
  3929. case CHANNEL_CON_2:
  3930. return 68;
  3931. default: {
  3932. return CHANNEL_DEFAULT;
  3933. }
  3934. }
  3935. }
  3936. else if (GetVersion() <= 561) {
  3937. if (channel_type < 20)
  3938. return channel_type;
  3939. switch (channel_type) {
  3940. case CHANNEL_GUILD_MOTD:
  3941. case CHANNEL_GUILD_MEMBER_ONLINE:
  3942. case CHANNEL_GUILD_EVENT:
  3943. return channel_type + 1;
  3944. case CHANNEL_PRIVATE_CHAT:
  3945. case CHANNEL_NONPLAYER_TELL:
  3946. return channel_type - 1;
  3947. case CHANNEL_PRIVATE_TELL:
  3948. case CHANNEL_TELL_FROM_CS:
  3949. case CHANNEL_ARENA:
  3950. case CHANNEL_CHAT_CHANNEL_TEXT:
  3951. case CHANNEL_OUT_OF_CHARACTER:
  3952. case CHANNEL_AUCTION:
  3953. case CHANNEL_CUSTOM_CHANNEL:
  3954. case CHANNEL_CHARACTER_TEXT:
  3955. case CHANNEL_REWARD:
  3956. case CHANNEL_DEATH:
  3957. case CHANNEL_PET_CHAT:
  3958. case CHANNEL_SKILL:
  3959. case CHANNEL_FACTION:
  3960. case CHANNEL_SPELLS:
  3961. case CHANNEL_YOU_CAST:
  3962. case CHANNEL_YOU_FAIL:
  3963. return channel_type - 2;
  3964. case CHANNEL_FRIENDLY_CAST:
  3965. case CHANNEL_FRIENDLY_FAIL:
  3966. case CHANNEL_OTHER_CAST:
  3967. case CHANNEL_OTHER_FAIL:
  3968. case CHANNEL_HOSTILE_CAST:
  3969. case CHANNEL_HOSTILE_FAIL:
  3970. case CHANNEL_WORN_OFF:
  3971. case CHANNEL_SPELLS_OTHER:
  3972. case CHANNEL_HEAL_SPELLS:
  3973. case CHANNEL_HEALS:
  3974. case CHANNEL_FRIENDLY_HEALS:
  3975. case CHANNEL_OTHER_HEALS:
  3976. case CHANNEL_HOSTILE_HEALS:
  3977. return channel_type - 3;
  3978. case CHANNEL_COMBAT:
  3979. case CHANNEL_GENERAL_COMBAT:
  3980. case CHANNEL_HEROIC_OPPORTUNITY:
  3981. case CHANNEL_NON_MELEE_DAMAGE:
  3982. case CHANNEL_DAMAGE_SHIELD:
  3983. case CHANNEL_WARD:
  3984. return channel_type - 4;
  3985. case CHANNEL_MELEE_COMBAT:
  3986. case CHANNEL_WARNINGS:
  3987. case CHANNEL_YOU_HIT:
  3988. case CHANNEL_YOU_MISS:
  3989. case CHANNEL_ATTACKER_HITS:
  3990. case CHANNEL_ATTACKER_MISSES:
  3991. case CHANNEL_YOUR_PET_HITS:
  3992. case CHANNEL_YOUR_PET_MISSES:
  3993. case CHANNEL_ATTACKER_HITS_PET:
  3994. case CHANNEL_ATTACKER_MISSES_PET:
  3995. case CHANNEL_OTHER_HIT:
  3996. case CHANNEL_OTHER_MISSES:
  3997. return channel_type - 5;
  3998. case CHANNEL_OTHER:
  3999. case CHANNEL_MONEY_SPLIT:
  4000. case CHANNEL_LOOT:
  4001. return channel_type - 14;
  4002. case CHANNEL_COMMAND_TEXT:
  4003. case CHANNEL_BROADCAST:
  4004. case CHANNEL_WHO:
  4005. case CHANNEL_COMMANDS:
  4006. case CHANNEL_MERCHANT:
  4007. case CHANNEL_MERCHANT_BUY_SELL:
  4008. case CHANNEL_CONSIDER_MESSAGE:
  4009. case CHANNEL_CON_MINUS_2:
  4010. case CHANNEL_CON_MINUS_1:
  4011. case CHANNEL_CON_0:
  4012. case CHANNEL_CON_1:
  4013. case CHANNEL_CON_2:
  4014. case CHANNEL_TRADESKILLS:
  4015. case CHANNEL_HARVESTING:
  4016. case CHANNEL_HARVESTING_WARNINGS:
  4017. return channel_type - 15;
  4018. default: {
  4019. return CHANNEL_DEFAULT;
  4020. }
  4021. }
  4022. }
  4023. else {
  4024. switch (channel_type) {
  4025. default: {
  4026. return channel_type;
  4027. }
  4028. }
  4029. }
  4030. return channel_type;
  4031. }
  4032. void Client::HandleTellMessage(const char* fromName, const char* message, const char* to, int32 current_language_id) {
  4033. if (!fromName || !to || GetPlayer()->IsIgnored(fromName))
  4034. return;
  4035. PacketStruct* packet = configReader.getStruct("WS_HearChat", GetVersion());
  4036. if (packet) {
  4037. packet->setDataByName("from", fromName);
  4038. packet->setDataByName("to", to);
  4039. packet->setDataByName("channel", GetMessageChannelColor(CHANNEL_PRIVATE_TELL));
  4040. packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
  4041. packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
  4042. packet->setDataByName("unknown2", 1, 1);
  4043. packet->setDataByName("show_bubble", 1);
  4044. if (current_language_id == 0 || GetPlayer()->HasLanguage(current_language_id)) {
  4045. packet->setDataByName("understood", 1);
  4046. }
  4047. packet->setDataByName("time", 2);
  4048. packet->setDataByName("language", current_language_id);
  4049. packet->setMediumStringByName("message", message);
  4050. EQ2Packet* outpacket = packet->serialize();
  4051. QueuePacket(outpacket);
  4052. safe_delete(packet);
  4053. }
  4054. }
  4055. void Client::SimpleMessage(int8 color, const char* message) {
  4056. PacketStruct* command_packet = configReader.getStruct("WS_DisplayText", GetVersion());
  4057. if (command_packet) {
  4058. command_packet->setDataByName("color", GetMessageChannelColor(color));//convert this to the correct client type (different clients have different chat numbers)
  4059. command_packet->setMediumStringByName("text", message);
  4060. command_packet->setDataByName("unknown02", 0x00ff);
  4061. EQ2Packet* outapp = command_packet->serialize();
  4062. QueuePacket(outapp);
  4063. safe_delete(command_packet);
  4064. }
  4065. }
  4066. void Client::SendSpellUpdate(Spell* spell, bool add_silently, bool add_to_hotbar) {
  4067. PacketStruct* packet = configReader.getStruct("WS_SpellGainedMsg", GetVersion());
  4068. if (packet) {
  4069. int8 xxx = spell->GetSpellData()->is_aa;
  4070. packet->setDataByName("spell_type", spell->GetSpellData()->type);
  4071. packet->setDataByName("spell_id", spell->GetSpellID());
  4072. packet->setDataByName("unique_id", spell->GetSpellData()->spell_name_crc);
  4073. packet->setDataByName("spell_name", spell->GetName());
  4074. if (add_silently)
  4075. packet->setDataByName("add_silently", 1);
  4076. if (add_to_hotbar)
  4077. packet->setDataByName("add_to_hotbar", 1);
  4078. packet->setDataByName("unknown", xxx);
  4079. packet->setDataByName("display_spell_tier", 1);
  4080. packet->setDataByName("unknown3", 1);
  4081. packet->setDataByName("tier", spell->GetSpellTier());
  4082. packet->setDataByName("icon", spell->GetSpellIcon());
  4083. packet->setDataByName("icon_type", spell->GetSpellIconBackdrop());
  4084. packet->setDataByName("unknown5", 0xFFFFFFFF);
  4085. //packet->PrintPacket();
  4086. EQ2Packet* outapp = packet->serialize();
  4087. //DumpPacket(outapp);
  4088. QueuePacket(outapp);
  4089. safe_delete(packet);
  4090. }
  4091. }
  4092. void Client::Message(int8 type, const char* message, ...) {
  4093. va_list argptr;
  4094. char buffer[4096];
  4095. va_start(argptr, message);
  4096. vsnprintf(buffer, sizeof(buffer), message, argptr);
  4097. va_end(argptr);
  4098. SimpleMessage(type, buffer);
  4099. }
  4100. void Client::Disconnect(bool send_disconnect)
  4101. {
  4102. LogWrite(CCLIENT__DEBUG, 0, "CClient", "Client Disconnect...");
  4103. this->Save();
  4104. this->GetPlayer()->WritePlayerStatistics();
  4105. SetConnected(false);
  4106. if (send_disconnect && getConnection())
  4107. getConnection()->SendDisconnect(true);
  4108. eqs = 0;
  4109. }
  4110. bool Client::Summon(const char* search_name) {
  4111. Spawn* target = 0;
  4112. if (search_name || GetPlayer()->GetTarget()) {
  4113. Client* search_client = 0;
  4114. if (search_name) {
  4115. target = GetCurrentZone()->FindSpawn(GetPlayer(), search_name);
  4116. if (target && target->IsPlayer())
  4117. search_client = target->GetClient();
  4118. if (!target) {
  4119. search_client = zone_list.GetClientByCharName(string(search_name));
  4120. if (search_client)
  4121. target = search_client->GetPlayer();
  4122. }
  4123. }
  4124. else
  4125. target = GetPlayer()->GetTarget();
  4126. if (target && target != GetPlayer()) {
  4127. target->SetX(GetPlayer()->GetX());
  4128. target->SetY(GetPlayer()->GetY());
  4129. target->SetZ(GetPlayer()->GetZ());
  4130. target->SetHeading(GetPlayer()->GetHeading());
  4131. if (!target->IsPlayer()) {
  4132. target->SetSpawnOrigX(target->GetX());
  4133. target->SetSpawnOrigY(target->GetY());
  4134. target->SetSpawnOrigZ(target->GetZ());
  4135. target->SetSpawnOrigHeading(target->GetHeading());
  4136. }
  4137. target->SetLocation(GetPlayer()->GetLocation());
  4138. if (target->IsNPC()) {
  4139. ((NPC*)target)->HaltMovement();
  4140. }
  4141. }
  4142. else if (target)
  4143. Message(CHANNEL_COLOR_RED, "Error: You cannot summon yourself!");
  4144. if (search_client && search_client != this) {
  4145. search_client->Message(CHANNEL_COLOR_YELLOW, "You have been summoned by '%s'!", GetPlayer()->GetName());
  4146. Message(CHANNEL_COLOR_YELLOW, "Summoning '%s'...", search_client->GetPlayer()->GetName());
  4147. if (search_client->GetCurrentZone() != GetCurrentZone())
  4148. search_client->Zone(GetCurrentZone()->GetZoneName(), false);
  4149. else {
  4150. EQ2Packet* app = search_client->GetPlayer()->Move(GetPlayer()->GetX(), GetPlayer()->GetY(), GetPlayer()->GetZ(), search_client->GetVersion());
  4151. if (app)
  4152. search_client->QueuePacket(app);
  4153. }
  4154. }
  4155. }
  4156. if (!target)
  4157. return false;
  4158. else
  4159. return true;
  4160. }
  4161. std::string Client::IdentifyInstanceLockout(int32 zoneID, bool displayClient) {
  4162. int8 instanceType = database.GetInstanceTypeByZoneID(zoneID);
  4163. if (instanceType < 1)
  4164. return std::string("");
  4165. ZoneServer* instance_zone = nullptr;
  4166. InstanceData* data = GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zoneID);
  4167. if (data) {
  4168. // If lockout instances check to see if we are locked out
  4169. if (instanceType == SOLO_LOCKOUT_INSTANCE || instanceType == GROUP_LOCKOUT_INSTANCE || instanceType == RAID_LOCKOUT_INSTANCE) {
  4170. int32 time = 0;
  4171. // Check success timer
  4172. if (data->last_success_timestamp > 0) {
  4173. if (Timer::GetUnixTimeStamp() < data->last_success_timestamp + data->success_lockout_time) {
  4174. // Timer has not expired yet can't re enter
  4175. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Success lockout not expired for character %s in zone %u", GetPlayer()->GetName(), zoneID);
  4176. time = (data->last_success_timestamp + data->success_lockout_time) - Timer::GetUnixTimeStamp();
  4177. }
  4178. }
  4179. // Check failure timer
  4180. if (data->last_failure_timestamp > 0) {
  4181. if (Timer::GetUnixTimeStamp() < data->last_failure_timestamp + data->failure_lockout_time) {
  4182. // Timer has not expired yet
  4183. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Failure lockout not expired for character %s in zone %u", GetPlayer()->GetName(), zoneID);
  4184. time = (data->last_failure_timestamp + data->failure_lockout_time) - Timer::GetUnixTimeStamp();
  4185. }
  4186. }
  4187. // Time > 0 then we are locked out, make the message and send it and return true
  4188. if (time > 0) {
  4189. string time_msg = "";
  4190. int16 hour;
  4191. int8 min;
  4192. int8 sec;
  4193. hour = time / 3600;
  4194. time = time % 3600;
  4195. min = time / 60;
  4196. time = time % 60;
  4197. sec = time;
  4198. if (hour > 0) {
  4199. char temp[10];
  4200. snprintf(temp, 9, " %i", hour);
  4201. time_msg.append(temp);
  4202. time_msg.append(" hour");
  4203. time_msg.append((hour > 1) ? "s" : "");
  4204. }
  4205. if (min > 0) {
  4206. char temp[5];
  4207. snprintf(temp, 4, " %i", min);
  4208. time_msg.append(temp);
  4209. time_msg.append(" minute");
  4210. time_msg.append((min > 1) ? "s" : "");
  4211. }
  4212. // Only add seconds if minutes and hours are 0
  4213. if (hour == 0 && min == 0 && sec > 0) {
  4214. char temp[5];
  4215. snprintf(temp, 4, " %i", sec);
  4216. time_msg.append(temp);
  4217. time_msg.append(" second");
  4218. time_msg.append((sec > 1) ? "s" : "");
  4219. }
  4220. if (displayClient)
  4221. Message(CHANNEL_COLOR_YELLOW, "You may not enter again for%s.", time_msg.c_str());
  4222. return time_msg;
  4223. }
  4224. }
  4225. }
  4226. return std::string("");
  4227. }
  4228. bool Client::IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID) {
  4229. int8 instanceType = database.GetInstanceTypeByZoneID(zoneID);
  4230. if (instanceType < 1)
  4231. return false;
  4232. bool foundZone = false;
  4233. InstanceData* data = GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zoneID);
  4234. if (data) {
  4235. std::string lockoutTime = IdentifyInstanceLockout(zoneID);
  4236. // If lockout instances check to see if we are locked out
  4237. if (lockoutTime.length() > 0) {
  4238. return false;
  4239. }
  4240. // Need to update `character_instances` table with new timestamps (for persistent) and instance id's
  4241. foundZone = zone_list.GetZoneByInstance(zone_details, data->instance_id, zoneID, true, false, false);
  4242. // if we got an instance_zone and the instance_id from the data is 0 or data instance id is not the same as the zone instance id then update values
  4243. if (foundZone && (data->instance_id == 0 || data->instance_id != zone_details->instanceId)) {
  4244. if (instanceType == SOLO_PERSIST_INSTANCE || instanceType == GROUP_PERSIST_INSTANCE || instanceType == RAID_PERSIST_INSTANCE) {
  4245. database.UpdateCharacterInstance(GetCharacterID(), zone_details->zoneName, zone_details->instanceId, 1, Timer::GetUnixTimeStamp());
  4246. data->last_success_timestamp = Timer::GetUnixTimeStamp();
  4247. }
  4248. else
  4249. database.UpdateCharacterInstance(GetCharacterID(), zone_details->zoneName, zone_details->instanceId);
  4250. data->instance_id = zone_details->instanceId;
  4251. }
  4252. }
  4253. return foundZone;
  4254. }
  4255. bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
  4256. ZoneServer* instance_zone = NULL;
  4257. int8 instanceType = 0;
  4258. bool foundZone = false;
  4259. ZoneChangeDetails zone_details;
  4260. // determine if this is a group instanced zone that already exists
  4261. foundZone = world.GetGroupManager()->IdentifyMemberInGroupOrRaid(&zone_details, this, zoneID);
  4262. if (foundZone) {
  4263. InstanceData* data = nullptr;
  4264. if (zone_details.instanceId)
  4265. data = GetPlayer()->GetCharacterInstances()->FindInstanceByInstanceID(zone_details.instanceId);
  4266. switch (zone_details.instanceType) {
  4267. case SOLO_LOCKOUT_INSTANCE:
  4268. case GROUP_LOCKOUT_INSTANCE:
  4269. case RAID_LOCKOUT_INSTANCE:
  4270. case SOLO_PERSIST_INSTANCE:
  4271. case GROUP_PERSIST_INSTANCE:
  4272. case RAID_PERSIST_INSTANCE:
  4273. {
  4274. if (!data && zone_details.instanceId) {
  4275. int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
  4276. if (db_id > 0)
  4277. GetPlayer()->GetCharacterInstances()->AddInstance(db_id, zone_details.instanceId, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime, zoneID, zone_details.instanceType, zone_details.zoneName);
  4278. }
  4279. break;
  4280. }
  4281. }
  4282. Zone(&zone_details, instance_zone, zone_coords_valid);
  4283. }
  4284. else if ((instanceType = database.GetInstanceTypeByZoneID(zoneID)) > 0)
  4285. {
  4286. // best to check if we already have our own instance!
  4287. if (!(foundZone = IdentifyInstance(&zone_details, zoneID)))
  4288. {
  4289. switch (instanceType)
  4290. {
  4291. case SOLO_LOCKOUT_INSTANCE:
  4292. case GROUP_LOCKOUT_INSTANCE:
  4293. case RAID_LOCKOUT_INSTANCE:
  4294. {
  4295. if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID))) {
  4296. // once lockout instance zone shuts down you can't renenter if you have a lockout or if you don't you get a new zone
  4297. // so delete `instances` entry for the zone when it shuts down.
  4298. int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, 0, 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
  4299. if (db_id > 0)
  4300. GetPlayer()->GetCharacterInstances()->AddInstance(db_id, zone_details.instanceId, 0, 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime, zoneID, zone_details.instanceType, zone_details.zoneName);
  4301. }
  4302. break;
  4303. }
  4304. case SOLO_PERSIST_INSTANCE:
  4305. case GROUP_PERSIST_INSTANCE:
  4306. case RAID_PERSIST_INSTANCE:
  4307. {
  4308. if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID))) {
  4309. int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
  4310. if (db_id > 0)
  4311. GetPlayer()->GetCharacterInstances()->AddInstance(db_id, zone_details.instanceId, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime, zoneID, zone_details.instanceType, zone_details.zoneName);
  4312. }
  4313. break;
  4314. }
  4315. case PUBLIC_INSTANCE:
  4316. case TRADESKILL_INSTANCE:
  4317. {
  4318. // if its public/tradeskill, look for a public already setup
  4319. instance_zone = zone_list.GetByLowestPopulation(zoneID);
  4320. if (instance_zone) {
  4321. // Check the current population against the max population, if greater or equal start a new version
  4322. if (instance_zone->GetClientCount() >= rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, MaxPlayers)->GetInt32()) {
  4323. foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
  4324. }
  4325. else {
  4326. peer_manager.setZonePeerDataSelf(&zone_details, std::string(instance_zone->GetZoneFile()), std::string(instance_zone->GetZoneName()),
  4327. instance_zone->GetZoneID(), instance_zone->GetInstanceID(), instance_zone->GetSafeX(), instance_zone->GetSafeY(),
  4328. instance_zone->GetSafeZ(), instance_zone->GetSafeHeading(), instance_zone->GetZoneLockState(),
  4329. instance_zone->GetMinimumStatus(), instance_zone->GetMinimumLevel(), instance_zone->GetMaximumLevel(),
  4330. instance_zone->GetMinimumVersion(), instance_zone->GetDefaultLockoutTime(), instance_zone->GetDefaultReenterTime(),
  4331. instance_zone->GetInstanceType(), instance_zone->NumPlayers(), instance_zone);
  4332. }
  4333. }
  4334. else {
  4335. foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
  4336. }
  4337. break;
  4338. }
  4339. case PERSONAL_HOUSE_INSTANCE:
  4340. case GUILD_HOUSE_INSTANCE:
  4341. {
  4342. // Because of the way housing works (need to load a specific instance id supplied in a packet) we can't
  4343. // use this function without some rework, so it will all be handled in Client::HandlePacket()
  4344. // with the OP_EnterHouseMsg opcode
  4345. break;
  4346. }
  4347. case QUEST_INSTANCE:
  4348. {
  4349. foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
  4350. break;
  4351. /*
  4352. ALTER TABLE `zones` CHANGE COLUMN `instance_type` `instance_type` ENUM('NONE','GROUP_LOCKOUT_INSTANCE','GROUP_PERSIST_INSTANCE','RAID_LOCKOUT_INSTANCE','RAID_PERSIST_INSTANCE','SOLO_LOCKOUT_INSTANCE','SOLO_PERSIST_INSTANCE','TRADESKILL_INSTANCE','PUBLIC_INSTANCE','PERSONAL_HOUSE_INSTANCE','GUILD_HOUSE_INSTANCE','QUEST_INSTANCE') NOT NULL DEFAULT 'NONE' COLLATE 'latin1_general_ci' AFTER `start_zone`;
  4353. */
  4354. }
  4355. default:
  4356. {
  4357. // NONE
  4358. }
  4359. }
  4360. }
  4361. if (foundZone)
  4362. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, zone_coords_valid);
  4363. }
  4364. return foundZone;
  4365. }
  4366. bool Client::GotoSpawn(const char* search_name, bool forceTarget) {
  4367. Spawn* target = 0;
  4368. if (search_name || GetPlayer()->GetTarget()) {
  4369. Client* search_client = 0;
  4370. if (search_name) {
  4371. target = GetCurrentZone()->FindSpawn(GetPlayer(), search_name);
  4372. if (!target) {
  4373. search_client = zone_list.GetClientByCharName(search_name);
  4374. if (search_client)
  4375. target = search_client->GetPlayer();
  4376. }
  4377. if (target)
  4378. GetPlayer()->SetTarget(target);
  4379. }
  4380. else
  4381. target = GetPlayer()->GetTarget();
  4382. float y = (target != nullptr) ? target->GetY() : 0.0f;
  4383. if (target && target->GetMap() != GetPlayer()->GetMap()) {
  4384. auto loc = glm::vec3(target->GetX(), target->GetZ(), target->GetY());
  4385. y = GetPlayer()->FindBestZ(loc, nullptr);
  4386. }
  4387. if (target && target != GetPlayer()) {
  4388. GetPlayer()->SetX(target->GetX());
  4389. GetPlayer()->SetY(y);
  4390. GetPlayer()->SetZ(target->GetZ());
  4391. GetPlayer()->SetHeading(target->GetHeading());
  4392. GetPlayer()->SetLocation(target->GetLocation());
  4393. Message(CHANNEL_COLOR_YELLOW, "Warping to '%s'", target->GetName());
  4394. }
  4395. else if (target)
  4396. Message(CHANNEL_COLOR_RED, "Error: You cannot goto yourself!");
  4397. if (search_client && search_client->GetCurrentZone() != GetCurrentZone())
  4398. Zone(search_client->GetCurrentZone()->GetZoneName(), false);
  4399. else if (target) {
  4400. EQ2Packet* app = GetPlayer()->Move(target->GetX(), y, target->GetZ(), GetVersion(), (target->GetHeading() + 180.0f));
  4401. if (app)
  4402. QueuePacket(app);
  4403. }
  4404. }
  4405. if (!target)
  4406. return false;
  4407. else
  4408. return true;
  4409. }
  4410. bool Client::CheckZoneAccess(const char* zoneName) {
  4411. LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone access check for %s (%u), client: %u", zoneName, database.GetZoneID(zoneName), GetVersion());
  4412. bool zoneFound = false;
  4413. ZoneChangeDetails zone_details;
  4414. if (zoneFound = zone_list.GetZone(&zone_details, 0, std::string(zoneName), false, false, false)) {
  4415. // JA: implemented /zone lock|unlock commands (2012.07.28)
  4416. if (zone_details.lockState)
  4417. {
  4418. LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone currently LOCKED: '%s' (%ul)", zoneName, zone_details.zoneId);
  4419. Message(CHANNEL_COLOR_RED, "This zone is locked, and you don't have the key! (%s).", zoneName);
  4420. return false;
  4421. }
  4422. }
  4423. sint16 zoneMinStatus = 0;
  4424. int16 zoneMinLevel = 0;
  4425. int16 zoneMaxLevel = 0;
  4426. int16 zoneMinVersion = 0;
  4427. if (!zoneFound)
  4428. {
  4429. LogWrite(CCLIENT__DEBUG, 0, "Client", "Grabbing zone requirements for %s", zoneName);
  4430. bool success = database.GetZoneRequirements(zoneName, &zoneMinStatus, &zoneMinLevel, &zoneMaxLevel, &zoneMinVersion);
  4431. if (!success) { // couldn't even find the zone, this shouldn't happen though..
  4432. return true;
  4433. }
  4434. }
  4435. else
  4436. {
  4437. zoneMinStatus = zone_details.minStatus;
  4438. zoneMinLevel = zone_details.minLevel;
  4439. zoneMaxLevel = zone_details.maxLevel;
  4440. zoneMinVersion = zone_details.minVersion;
  4441. }
  4442. LogWrite(CCLIENT__DEBUG, 0, "Client", "Access Requirements: status %i, level %i - %i, req >= %i version", zoneMinStatus, zoneMinLevel, zoneMaxLevel, zoneMinVersion);
  4443. // use ZoneLevelOverrideStatus in both min_level and max_level checks
  4444. sint16 ZoneLevelOverrideStatus = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, MinZoneLevelOverrideStatus)->GetSInt16();
  4445. if ((zoneMinVersion > 0) && (GetVersion() < zoneMinVersion))
  4446. {
  4447. LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone MinVersion of %i challenge...", zoneMinVersion);
  4448. Message(CHANNEL_COLOR_RED, "You do not have the required expansion pack to enter here (%s).", database.GetExpansionIDByVersion(zoneMinVersion).c_str());
  4449. LogWrite(CCLIENT__DEBUG, 0, "Client", "Client denied access to zone '%s' (version req: %i)", zoneName, zoneMinVersion);
  4450. return false;
  4451. }
  4452. if ((zoneMinLevel > 1) && (player->GetLevel() < zoneMinLevel))
  4453. {
  4454. if (ZoneLevelOverrideStatus && ZoneLevelOverrideStatus > GetAdminStatus())
  4455. {
  4456. LogWrite(CCLIENT__DEBUG, 0, "Client", "Player denied access to zone '%s' (level req: %i)", zoneName, player->GetLevel());
  4457. Message(CHANNEL_COLOR_RED, "Your level is too low to enter here (%s)", zoneMinLevel);
  4458. return false;
  4459. }
  4460. }
  4461. if ((zoneMaxLevel > 1) && (player->GetLevel() > zoneMaxLevel))
  4462. {
  4463. if (ZoneLevelOverrideStatus && ZoneLevelOverrideStatus > GetAdminStatus())
  4464. {
  4465. LogWrite(CCLIENT__DEBUG, 0, "Client", "Player denied access to zone '%s' (level req: %i)", zoneName, player->GetLevel());
  4466. Message(CHANNEL_COLOR_RED, "Your level is too high to enter here (%s)", zoneMaxLevel);
  4467. return false;
  4468. }
  4469. }
  4470. if ((zoneMinStatus > 0) && (GetAdminStatus() < zoneMinStatus))
  4471. {
  4472. LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone MinStatus of %i challenge...", zoneMinStatus);
  4473. sint16 ZoneAccessOverrideStatus = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, MinZoneAccessOverrideStatus)->GetSInt16();
  4474. if (ZoneAccessOverrideStatus && ZoneAccessOverrideStatus > GetAdminStatus())
  4475. {
  4476. LogWrite(CCLIENT__DEBUG, 0, "Client", "Player denied access to zone '%s' (status req: %i)", zoneName, GetAdminStatus());
  4477. Message(CHANNEL_COLOR_RED, "You do not have permission to enter here (%i).", zoneMinStatus);
  4478. return false;
  4479. }
  4480. }
  4481. return true;
  4482. }
  4483. void Client::Zone(int32 instanceid, bool set_coords, bool byInstanceID, bool is_spell) {
  4484. ZoneChangeDetails zone_details;
  4485. if (zone_list.GetZoneByInstance(&zone_details, instanceid, 0)) {
  4486. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, set_coords, is_spell);
  4487. }
  4488. }
  4489. void Client::Zone(ZoneChangeDetails* new_zone, ZoneServer* opt_zone, bool set_coords, bool is_spell) {
  4490. if (GetVersion() == 373 && GetAdminStatus() == 0) {
  4491. if (new_zone->zoneFileName.find("boat_06p_tutorial02") == std::string::npos && new_zone->zoneFileName.find("tutorial_island02") == std::string::npos
  4492. && new_zone->zoneFileName.find("tutorial_island02_epic") == std::string::npos) { // accounts for 01 and 02 zones
  4493. SimpleMessage(CHANNEL_COLOR_RED, "This client does not currently support beyond the boat tutorial (farjourneyfreeport) or island of refuge (islerefuge1)");
  4494. return;
  4495. }
  4496. }
  4497. else if (GetVersion() < 546 && GetVersion() > 561) {
  4498. if (new_zone->zoneFileName.find("design_path_script") != std::string::npos) {
  4499. SimpleMessage(CHANNEL_COLOR_RED, "This zone is only available for KoS and earlier clients.");
  4500. return;
  4501. }
  4502. }
  4503. if (client_zoning) {
  4504. return;
  4505. }
  4506. client_zoning = true;
  4507. zoning_id = new_zone->zoneId;
  4508. zoning_instance_id = new_zone->instanceId;
  4509. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Setting player Resurrecting to 'true'", __FUNCTION__);
  4510. player->SetResurrecting(true);
  4511. if (set_coords)
  4512. {
  4513. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Zoning player to coordinates x: %2f, y: %2f, z: %2f, heading: %2f in zone '%s'...",
  4514. __FUNCTION__,
  4515. new_zone->safeX,
  4516. new_zone->safeY,
  4517. new_zone->safeZ,
  4518. new_zone->safeHeading,
  4519. new_zone->zoneName.c_str()
  4520. );
  4521. player->SetX(new_zone->safeX);
  4522. player->SetY(new_zone->safeY);
  4523. player->SetZ(new_zone->safeZ);
  4524. player->SetHeading(new_zone->safeHeading);
  4525. SetZoningCoords(new_zone->safeX, new_zone->safeY,
  4526. new_zone->safeZ, new_zone->safeHeading);
  4527. }
  4528. else {
  4529. ResetZoningCoords();
  4530. }
  4531. Save();
  4532. GetPlayer()->SetSaveSpellEffects(true);
  4533. ResetSendMail();
  4534. // Remove players pet from zone if there is one
  4535. ((Entity*)player)->DismissAllPets();
  4536. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Removing player from current zone...", __FUNCTION__);
  4537. GetCurrentZone()->RemoveSpawn(player, false, true, true, true, !is_spell);
  4538. GetPlayer()->DeleteSpellEffects(true);
  4539. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Setting zone to '%s'...", __FUNCTION__, new_zone->zoneName.c_str());
  4540. SetZoningDestination(opt_zone);
  4541. SetCurrentZone(opt_zone);
  4542. if (player->GetGroupMemberInfo())
  4543. {
  4544. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Player in group, updating group info...", __FUNCTION__);
  4545. player->UpdateGroupMemberInfo();
  4546. world.GetGroupManager()->SendGroupUpdate(player->GetGroupMemberInfo()->group_id, this);
  4547. }
  4548. // block out the member info for the group
  4549. TempRemoveGroup();
  4550. UpdateTimeStampFlag(ZONE_UPDATE_FLAG);
  4551. const char* new_zone_ip = 0;
  4552. if (IsPrivateAddress(this->GetIP()) && new_zone->peerInternalWorldAddress.length() > 0)
  4553. new_zone_ip = new_zone->peerInternalWorldAddress.c_str();
  4554. else
  4555. new_zone_ip = new_zone->peerWorldAddress.c_str();
  4556. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: New Zone IP '%s'...", __FUNCTION__, new_zone_ip);
  4557. int32 key = static_cast<unsigned int>(MakeRandomFloat(0.01, 1.0) * UINT32_MAX);
  4558. new_zone->zoneKey = key;
  4559. new_zone->authDispatchedTime = Timer::GetUnixTimeStamp();
  4560. zoning_details = ZoneChangeDetails(new_zone);
  4561. client_zoning_details_set = true;
  4562. if (new_zone->peerId.length() > 0 && new_zone->peerId != "self") {
  4563. if (new_zone->peerAuthorized) {
  4564. LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s authorized us to zone...", __FUNCTION__, new_zone->peerId.c_str());
  4565. }
  4566. else {
  4567. boost::property_tree::ptree root;
  4568. struct in_addr in;
  4569. in.s_addr = GetIP();
  4570. root.put("account_id", std::to_string(GetAccountID()));
  4571. root.put("character_name", std::string(player->GetName()));
  4572. root.put("zone_name", new_zone->zoneName);
  4573. root.put("zone_id", new_zone->zoneId);
  4574. root.put("instance_id", new_zone->instanceId);
  4575. root.put("login_key", std::to_string(key));
  4576. root.put("client_ip", std::string(inet_ntoa(in)));
  4577. root.put("first_login", false);
  4578. std::ostringstream jsonStream;
  4579. boost::property_tree::write_json(jsonStream, root);
  4580. std::string jsonPayload = jsonStream.str();
  4581. LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for zone %s", __FUNCTION__, player->GetName(), new_zone->peerWebAddress.c_str(), new_zone->peerWebPort, new_zone->zoneName.c_str());
  4582. peer_https_pool.sendPostRequestToPeerAsync(new_zone->peerId, new_zone->peerWebAddress, std::to_string(new_zone->peerWebPort), "/addcharauth", jsonPayload);
  4583. }
  4584. }
  4585. else {
  4586. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Sending to zone_auth.AddAuth...", __FUNCTION__);
  4587. zone_auth.AddAuth(new ZoneAuthRequest(GetAccountID(), player->GetName(), key));
  4588. new_zone->peerAuthorized = true; // local, we can bypass (should technically already be true)
  4589. zoning_details = ZoneChangeDetails(new_zone);
  4590. }
  4591. if (new_zone->peerAuthorized) {
  4592. ApproveZone();
  4593. }
  4594. }
  4595. void Client::ApproveZone() {
  4596. if (!client_zoning_details_set) {
  4597. LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: client zoning details not set to zone player %s...", __FUNCTION__, player->GetName());
  4598. return;
  4599. }
  4600. zoning_details.zoningPastAuth = true;
  4601. zoning_details.peerAuthorized = true;
  4602. const char* new_zone_ip = 0;
  4603. if (IsPrivateAddress(this->GetIP()) && zoning_details.peerInternalWorldAddress.length() > 0)
  4604. new_zone_ip = zoning_details.peerInternalWorldAddress.c_str();
  4605. else
  4606. new_zone_ip = zoning_details.peerWorldAddress.c_str();
  4607. LogWrite(CCLIENT__INFO, 0, "Client", "%s: Sending ZoneChangeMsg %s:%u with key %u...", player->GetName(), (char*)new_zone_ip, zoning_details.peerWorldPort, zoning_details.zoneKey);
  4608. ClientPacketFunctions::SendZoneChange(this, (char*)new_zone_ip, zoning_details.peerWorldPort, zoning_details.zoneKey);
  4609. if (version > 373) {
  4610. PacketStruct* packet = configReader.getStruct("WS_CancelMoveObjectMode", version);
  4611. if (packet)
  4612. {
  4613. QueuePacket(packet->serialize());
  4614. safe_delete(packet);
  4615. }
  4616. }
  4617. }
  4618. void Client::Zone(const char* new_zone, bool set_coords, bool is_spell)
  4619. {
  4620. LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone Request to '%s'", new_zone);
  4621. ZoneChangeDetails zone_details;
  4622. int32 zone_id = database.GetZoneID(new_zone);
  4623. std::string camelCaseName = database.GetZoneName(zone_id);
  4624. InstanceData* data = GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zone_id);
  4625. if ((data && zone_list.GetZoneByInstance(&zone_details, data->instance_id, zone_id)) || zone_list.GetZone(&zone_details, 0, camelCaseName)) {
  4626. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, set_coords, is_spell);
  4627. }
  4628. }
  4629. float Client::DistanceFrom(Client* client) {
  4630. float ret = 0;
  4631. if (client && client != this) {
  4632. ret = pow(player->GetX() - client->player->GetX(), 2) + pow(player->GetY() - client->player->GetY(), 2) + pow(player->GetZ() - client->player->GetZ(), 2);
  4633. ret = sqrt(ret);
  4634. }
  4635. return ret;
  4636. }
  4637. void Client::DetermineCharacterUpdates() {
  4638. ServerPacket* outpack = new ServerPacket(ServerOP_BasicCharUpdate, sizeof(CharDataUpdate_Struct));
  4639. CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*)outpack->pBuffer;
  4640. cdu->account_id = GetAccountID();
  4641. cdu->char_id = GetCharacterID();
  4642. int32 timestamp = Timer::GetUnixTimeStamp();
  4643. int8 flag = GetTimeStampFlag();
  4644. if (flag & LEVEL_UPDATE_FLAG)
  4645. {
  4646. cdu->update_field = LEVEL_UPDATE_FLAG;
  4647. cdu->update_data = player->GetLevel();
  4648. loginserver.SendPacket(outpack);
  4649. }
  4650. //if(flag&CLASS_UPDATE_FLAG && player->GetLevel() >= 20)// Perseverance only
  4651. if (flag & CLASS_UPDATE_FLAG)
  4652. {
  4653. cdu->update_field = CLASS_UPDATE_FLAG;
  4654. cdu->update_data = player->GetAdventureClass();
  4655. loginserver.SendPacket(outpack);
  4656. }
  4657. if (flag & GENDER_UPDATE_FLAG)
  4658. {
  4659. cdu->update_field = GENDER_UPDATE_FLAG;
  4660. cdu->update_data = player->GetGender();
  4661. loginserver.SendPacket(outpack);
  4662. }
  4663. if (flag & DELETE_UPDATE_FLAG) {
  4664. LogWrite(MISC__TODO, 1, "TODO", "Delete update req in func: %s, line: %i", __FUNCTION__, __LINE__);
  4665. }
  4666. safe_delete(outpack); // Zone, armor and name use a different structure
  4667. if (flag & RACE_UPDATE_FLAG)
  4668. {
  4669. outpack = new ServerPacket(ServerOP_RaceUpdate, sizeof(RaceUpdate_Struct));
  4670. RaceUpdate_Struct* ru = (RaceUpdate_Struct*)outpack->pBuffer;
  4671. ru->account_id = GetAccountID();
  4672. ru->char_id = GetCharacterID();
  4673. ru->race = player->GetRace();
  4674. ru->model_type = player->GetModelType();
  4675. loginserver.SendPacket(outpack);
  4676. safe_delete(outpack);
  4677. }
  4678. if (flag & ZONE_UPDATE_FLAG) {
  4679. ServerPacket* outpack = new ServerPacket(ServerOP_ZoneUpdate, CHARZONESTRUCT_MAXSIZE);
  4680. memset(outpack->pBuffer, 0, CHARZONESTRUCT_MAXSIZE);
  4681. CharZoneUpdate_Struct* czu = (CharZoneUpdate_Struct*)outpack->pBuffer;
  4682. czu->account_id = GetAccountID();
  4683. czu->char_id = GetCharacterID();
  4684. czu->zone_id = GetCurrentZone()->GetZoneID();
  4685. const char* zone_file = GetCurrentZone()->GetZoneFile();
  4686. czu->zone_length = strlen(zone_file);
  4687. if (czu->zone_length > 64)
  4688. czu->zone_length = 64;
  4689. strncpy(czu->new_zone, zone_file, czu->zone_length);
  4690. loginserver.SendPacket(outpack);
  4691. safe_delete(outpack);
  4692. }
  4693. if (flag & ARMOR_UPDATE_FLAG) {
  4694. LogWrite(MISC__TODO, 1, "TODO", "Armor update req in func: %s, line: %i", __FUNCTION__, __LINE__);
  4695. }
  4696. if (flag & NAME_UPDATE_FLAG) {
  4697. LogWrite(MISC__TODO, 1, "TODO", "Name update req in func: %s, line: %i", __FUNCTION__, __LINE__);
  4698. }
  4699. database.UpdateCharacterTimeStamp(GetAccountID(), GetCharacterID(), timestamp);
  4700. }
  4701. void Client::Save() {
  4702. if (GetCharacterID() == 0 || IsSaveDisabled())
  4703. return;
  4704. if (current_zone) {
  4705. DetermineCharacterUpdates();
  4706. UpdateCharacterInstances();
  4707. this->SetLastSavedTimeStamp(Timer::GetCurrentTime2());
  4708. database.Save(this);
  4709. if (GetPlayer()->UpdateQuickbarNeeded()) {
  4710. database.SaveQuickBar(GetCharacterID(), GetPlayer()->GetQuickbar());
  4711. GetPlayer()->ResetQuickbarNeeded();
  4712. }
  4713. database.SaveItems(this);
  4714. database.SaveBuyBacks(this);
  4715. GetPlayer()->SaveHistory();
  4716. GetPlayer()->SaveLUAHistory();
  4717. MSaveSpellStateMutex.lock();
  4718. GetPlayer()->SaveSpellEffects();
  4719. MSaveSpellStateMutex.unlock();
  4720. }
  4721. }
  4722. void Client::UpdateCharacterInstances() {
  4723. if (GetPlayer() != NULL)
  4724. GetPlayer()->GetCharacterInstances()->ProcessInstanceTimers(GetPlayer());
  4725. /*if ( GetPlayer() != NULL )
  4726. {
  4727. // determine the last timestamp then get a new one, determine the difference in the timestamp
  4728. // to use for applying the update to each instances timer
  4729. int32 lastSaveTS = GetLastSavedTimeStamp();
  4730. int32 newSaveTS = Timer::GetUnixTimeStamp();
  4731. int32 diffTS = newSaveTS - lastSaveTS;
  4732. // update instance timers
  4733. GetPlayer()->GetCharacterInstances().ProcessInstanceTimers(GetPlayer(),diffTS);
  4734. // update with the new timestamp and save the db
  4735. this->SetLastSavedTimeStamp(newSaveTS);
  4736. }*/
  4737. }
  4738. void Client::HandleVerbRequest(EQApplicationPacket* app) {
  4739. PacketStruct* packet = configReader.getStruct("WS_EntityVerbsRequest", GetVersion());
  4740. if (packet) {
  4741. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  4742. int32 spawn_id = packet->getType_int32_ByName("spawn_id");
  4743. PacketStruct* out = configReader.getStruct("WS_EntityVerbsResponse", GetVersion());
  4744. Spawn* spawn = GetPlayer()->GetSpawnWithPlayerID(spawn_id);
  4745. vector<EntityCommand*> commands;
  4746. vector<EntityCommand*> delete_commands;
  4747. if (out && spawn) {
  4748. for (int32 i = 0; i < spawn->primary_command_list.size(); i++)
  4749. {
  4750. // default is a deny list not allow, only allow if on the iterator list and itr second is not false (deny)
  4751. if (!spawn->primary_command_list[i]->default_allow_list)
  4752. {
  4753. map<int32, bool>::iterator itr = spawn->primary_command_list[i]->allow_or_deny.find(GetPlayer()->GetCharacterID());
  4754. if (itr == spawn->primary_command_list[i]->allow_or_deny.end() || !itr->second)
  4755. continue;
  4756. }
  4757. else
  4758. {
  4759. // default is allow list, only deny if added to the list as deny (false itr second)
  4760. map<int32, bool>::iterator itr = spawn->primary_command_list[i]->allow_or_deny.find(GetPlayer()->GetCharacterID());
  4761. if (itr != spawn->primary_command_list[i]->allow_or_deny.end() && !itr->second)
  4762. continue;
  4763. }
  4764. commands.push_back(spawn->primary_command_list[i]);
  4765. }
  4766. for (int32 i = 0; i < spawn->secondary_command_list.size(); i++)
  4767. commands.push_back(spawn->secondary_command_list[i]);
  4768. if (spawn->IsPlayer()) {
  4769. if (player->IsFriend(spawn->GetName()))
  4770. delete_commands.push_back(player->CreateEntityCommand("remove from friends list", 10000, "friend_remove", "", 0, 0));
  4771. else
  4772. delete_commands.push_back(player->CreateEntityCommand("add to friends list", 10000, "friend_add", "", 0, 0));
  4773. if (player->IsIgnored(spawn->GetName()))
  4774. delete_commands.push_back(player->CreateEntityCommand("remove from ignore list", 10000, "ignore_remove", "", 0, 0));
  4775. else
  4776. {
  4777. delete_commands.push_back(player->CreateEntityCommand("add to ignore list", 10000, "ignore_add", "", 0, 0));
  4778. delete_commands.push_back(player->CreateEntityCommand("Trade", 10, "start_trade", "", 0, 0));
  4779. }
  4780. if (((Player*)spawn)->GetGroupMemberInfo()) {
  4781. if (player->IsGroupMember((Player*)spawn) && player->GetGroupMemberInfo()->leader) { //group leader
  4782. delete_commands.push_back(player->CreateEntityCommand("kick from group", 10000, "kickfromgroup", "", 0, 0));
  4783. delete_commands.push_back(player->CreateEntityCommand("make group leader", 10000, "makeleader", "", 0, 0));
  4784. }
  4785. world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
  4786. int32 spawn_group_id = ((Player*)spawn)->GetGroupMemberInfo()->group_id;
  4787. PlayerGroup* spawn_group = world.GetGroupManager()->GetGroup(spawn_group_id);
  4788. int32 player_group_id = player->GetGroupMemberInfo() ? player->GetGroupMemberInfo()->group_id : 0;
  4789. PlayerGroup* player_group = nullptr;
  4790. if (player_group_id)
  4791. player_group = world.GetGroupManager()->GetGroup(player_group_id);
  4792. if (spawn_group && !player->IsGroupMember((Player*)spawn) && !spawn_group->IsGroupRaid() && player_group && player->GetGroupMemberInfo()->leader
  4793. && (!player_group->IsInRaidGroup(spawn_group_id) || player_group->IsInRaidGroup(player_group_id, true))) {
  4794. delete_commands.push_back(player->CreateEntityCommand("invite to raid", 10000, "raidinvite", "", 0, 0));
  4795. }
  4796. else if (spawn_group && player_group && player_group->IsInRaidGroup(player_group_id, true) && player->GetGroupMemberInfo()->leader && player_group->IsInRaidGroup(spawn_group_id)) {
  4797. if (((Player*)spawn)->GetGroupMemberInfo()->is_raid_looter) {
  4798. delete_commands.push_back(player->CreateEntityCommand("remove looter", 10000, "raid_looter", "", 0, 0));
  4799. }
  4800. else {
  4801. delete_commands.push_back(player->CreateEntityCommand("add looter", 10000, "raid_looter", "", 0, 0));
  4802. }
  4803. delete_commands.push_back(player->CreateEntityCommand("disband from raid", 10000, "raiddisband", "", 0, 0));
  4804. }
  4805. world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
  4806. if (spawn->IsPlayer() && player->GetGroupMemberInfo() && !player->GetGroupMemberInfo()->mentor_target_char_id && player_group_id == spawn_group_id)
  4807. delete_commands.push_back(player->CreateEntityCommand("Mentor", 10000, "mentor", "", 0, 0));
  4808. else if (spawn->IsPlayer() && player->GetGroupMemberInfo() && player->GetGroupMemberInfo()->mentor_target_char_id == ((Player*)spawn)->GetCharacterID())
  4809. delete_commands.push_back(player->CreateEntityCommand("Stop Mentoring", 10000, "unmentor", "", 0, 0));
  4810. }
  4811. else if (!player->GetGroupMemberInfo() || (player->GetGroupMemberInfo()->leader && world.GetGroupManager()->GetGroupSize(player->GetGroupMemberInfo()->group_id) < 6))
  4812. delete_commands.push_back(player->CreateEntityCommand("invite to group", 10000, "invite", "", 0, 0));
  4813. commands.insert(commands.end(), delete_commands.begin(), delete_commands.end());
  4814. }
  4815. out->setDataByName("spawn_id", spawn_id);
  4816. out->setArrayLengthByName("num_verbs", commands.size());
  4817. for (int32 i = 0; i < commands.size(); i++) {
  4818. out->setArrayDataByName("command", commands[i]->command.c_str(), i);
  4819. out->setArrayDataByName("distance", commands[i]->distance, i);
  4820. if (commands[i]->error_text.length() == 0)
  4821. out->setArrayAddToPacketByName("error", false, i);
  4822. else {
  4823. out->setArrayDataByName("display_error", 1, i);
  4824. out->setArrayDataByName("error", commands[i]->error_text.c_str(), i);
  4825. }
  4826. out->setArrayDataByName("display_text", commands[i]->name.c_str(), i);
  4827. }
  4828. EQ2Packet* outapp = out->serialize();
  4829. //DumpPacket(outapp);
  4830. QueuePacket(outapp);
  4831. safe_delete(out);
  4832. for (int32 i = 0; i < delete_commands.size(); i++) {
  4833. safe_delete(delete_commands[i]);
  4834. }
  4835. }
  4836. }
  4837. safe_delete(packet);
  4838. }
  4839. }
  4840. void Client::SkillChanged(Skill* skill, int16 previous_value, int16 new_value) {
  4841. if (previous_value != new_value) {
  4842. Message(CHANNEL_SKILL, "You get %s at %s (%i/%i).", new_value > previous_value ? "better" : "worse", skill->name.data.c_str(), new_value, skill->max_val);
  4843. char tmp[200] = { 0 };
  4844. sprintf(tmp, "\\#6EFF6EYou get %s at \12\\#C8FFC8%s\\#6EFF6E! (%i/%i)", new_value > previous_value ? "better" : "worse", skill->name.data.c_str(), new_value, skill->max_val);
  4845. SendPopupMessage(6, tmp, new_value > previous_value ? "skill_up" : "skill_down", 2.75f, 0xFF, 0xFF, 0xFF);
  4846. }
  4847. }
  4848. void Client::SendPopupMessage(int8 unknown, const char* text, const char* type, float size, int8 red, int8 green, int8 blue)
  4849. {
  4850. /* JA notes on the unknown:
  4851. 2 = ding glimmer
  4852. 16 = Achievement Unlocked
  4853. 6 no longer does anything
  4854. */
  4855. PacketStruct* packet = configReader.getStruct("WS_OnScreenMsg", GetVersion());
  4856. if (packet) {
  4857. packet->setDataByName("unknown", unknown);
  4858. packet->setMediumStringByName("text", text);
  4859. if (type && strlen(type) > 0)
  4860. packet->setMediumStringByName("message_type", type);
  4861. packet->setDataByName("size", size);
  4862. packet->setDataByName("red", red);
  4863. packet->setDataByName("green", green);
  4864. packet->setDataByName("blue", blue);
  4865. QueuePacket(packet->serialize());
  4866. safe_delete(packet);
  4867. }
  4868. }
  4869. void Client::ChangeLevel(int16 old_level, int16 new_level) {
  4870. if (new_level < 1) {
  4871. SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!");
  4872. return;
  4873. }
  4874. if (player->GetLevel() != new_level) {
  4875. player->SetLevel(new_level);
  4876. if (player->GetGroupMemberInfo()) {
  4877. player->UpdateGroupMemberInfo();
  4878. world.GetGroupManager()->SendGroupUpdate(player->GetGroupMemberInfo()->group_id);
  4879. }
  4880. }
  4881. if (new_level > old_level) {
  4882. player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_ADVENTURE, new_level, player->GetAdventureClass());
  4883. }
  4884. if (player->GetPet()) {
  4885. NPC* pet = (NPC*)player->GetPet();
  4886. if (pet->GetMaxPetLevel() == 0 || new_level <= pet->GetMaxPetLevel()) {
  4887. pet->SetLevel(new_level);
  4888. pet->UpdateWeapons();
  4889. PacketStruct* command_packet = configReader.getStruct("WS_CannedEmote", GetVersion());
  4890. if (command_packet) {
  4891. command_packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(pet));
  4892. command_packet->setDataByName("anim_type", 1753);
  4893. QueuePacket(command_packet->serialize());
  4894. safe_delete(command_packet);
  4895. }
  4896. }
  4897. }
  4898. PacketStruct* level_update = configReader.getStruct("WS_LevelChanged", GetVersion());
  4899. if (level_update) {
  4900. level_update->setDataByName("old_level", old_level);
  4901. level_update->setDataByName("new_level", new_level);
  4902. QueuePacket(level_update->serialize());
  4903. safe_delete(level_update);
  4904. GetCurrentZone()->StartZoneSpawnsForLevelThread(this);
  4905. GetCurrentZone()->SendCastSpellPacket(322, player, player); //send level up spell effect
  4906. //GetCurrentZone()->SendAllSpawnsForLevelChange(this);
  4907. }
  4908. PacketStruct* command_packet = configReader.getStruct("WS_CannedEmote", GetVersion());
  4909. if (command_packet) {
  4910. command_packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(player));
  4911. command_packet->setDataByName("anim_type", 1753);
  4912. QueuePacket(command_packet->serialize());
  4913. safe_delete(command_packet);
  4914. }
  4915. if (!player->get_character_flag(CF_ENABLE_CHANGE_LASTNAME) && new_level >= rule_manager.GetZoneRule(GetCurrentZoneID(), R_Player, MinLastNameLevel)->GetInt8())
  4916. player->set_character_flag(CF_ENABLE_CHANGE_LASTNAME);
  4917. SendNewAdventureSpells();
  4918. GetPlayer()->GetInfoStruct()->set_level(new_level);
  4919. GetPlayer()->UpdateWeapons();
  4920. // GetPlayer()->SetLevel(new_level);
  4921. LogWrite(MISC__TODO, 1, "TODO", "Get new HP/POWER/stat based on default values from DB\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
  4922. GetPlayer()->CalculatePlayerHPPower();
  4923. GetPlayer()->CalculateBonuses();
  4924. GetPlayer()->SetHP(GetPlayer()->GetTotalHP());
  4925. GetPlayer()->SetPower(GetPlayer()->GetTotalPower());
  4926. /*InfoStruct* info = player->GetInfoStruct();
  4927. info->set_agi_base(new_level * 2 + 15);
  4928. info->set_intel_base(new_level * 2 + 15);
  4929. info->set_wis_base(new_level * 2 + 15);
  4930. info->set_str_base(new_level * 2 + 15);
  4931. info->set_sta_base(new_level * 2 + 15);
  4932. info->set_cold_base((int16)(new_level * 1.5 + 10));
  4933. info->set_heat_base((int16)(new_level * 1.5 + 10));
  4934. info->set_disease_base((int16)(new_level * 1.5 + 10));
  4935. info->set_mental_base((int16)(new_level * 1.5 + 10));
  4936. info->set_magic_base((int16)(new_level * 1.5 + 10));
  4937. info->set_divine_base((int16)(new_level * 1.5 + 10));
  4938. info->set_poison_base((int16)(new_level * 1.5 + 10));
  4939. GetPlayer()->GetInfoStruct()->set_poison_base((int16)(new_level * 1.5 + 10));*/
  4940. UpdateTimeStampFlag(LEVEL_UPDATE_FLAG);
  4941. GetPlayer()->SetCharSheetChanged(true);
  4942. Message(CHANNEL_REWARD, "You are now level %i!", new_level);
  4943. LogWrite(WORLD__DEBUG, 0, "World", "Player: %s leveled from %u to %u", GetPlayer()->GetName(), old_level, new_level);
  4944. int16 new_skill_cap = 5 * new_level;
  4945. PlayerSkillList* player_skills = player->GetSkills();
  4946. player_skills->SetSkillCapsByType(SKILL_TYPE_ARMOR, new_skill_cap);
  4947. player_skills->SetSkillCapsByType(SKILL_TYPE_SHIELD, new_skill_cap);
  4948. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_Player, AutoSkillUpBaseSkills)->GetBool()) {
  4949. //SKILL_TYPE_ARMOR/SKILL_TYPE_SHIELD always has the same current / max values
  4950. player_skills->SetSkillValuesByType(SKILL_TYPE_ARMOR, new_skill_cap, false);
  4951. player_skills->SetSkillValuesByType(SKILL_TYPE_SHIELD, new_skill_cap, false);
  4952. }
  4953. player_skills->SetSkillCapsByType(SKILL_TYPE_CLASS, new_skill_cap);
  4954. player_skills->SetSkillCapsByType(SKILL_TYPE_WEAPON, new_skill_cap);
  4955. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_Player, AutoSkillUpBaseSkills)->GetBool()) {
  4956. //SKILL_TYPE_CLASS/SKILL_TYPE_WEAPON always has the same current/max values
  4957. player_skills->SetSkillValuesByType(SKILL_TYPE_CLASS, new_skill_cap, false);
  4958. player_skills->SetSkillValuesByType(SKILL_TYPE_WEAPON, new_skill_cap, false);
  4959. }
  4960. player_skills->SetSkillCapsByType(SKILL_TYPE_COMBAT, new_skill_cap);
  4961. player_skills->SetSkillCapsByType(SKILL_TYPE_GENERAL, new_skill_cap);
  4962. player_skills->SetSkillCapsByType(SKILL_TYPE_SPELLCASTING, new_skill_cap);
  4963. player_skills->SetSkillCapsByType(SKILL_TYPE_AVOIDANCE, new_skill_cap);
  4964. player_skills->SetSkillCapsByType(SKILL_TYPE_WEAPONRY, new_skill_cap);
  4965. if (new_level > player->GetTSLevel())
  4966. player_skills->SetSkillCapsByType(SKILL_TYPE_HARVESTING, new_skill_cap);
  4967. //SKILL_ID_DUALWIELD, SKILL_ID_FISTS, SKILL_ID_DESTROYING, and SKILL_ID_MAGIC_AFFINITY always have the current_val equal to max_val
  4968. if (player_skills->HasSkill(SKILL_ID_DUALWIELD))
  4969. player_skills->SetSkill(SKILL_ID_DUALWIELD, new_skill_cap);
  4970. if (player_skills->HasSkill(SKILL_ID_FISTS))
  4971. player_skills->SetSkill(SKILL_ID_FISTS, new_skill_cap);
  4972. if (player_skills->HasSkill(SKILL_ID_DESTROYING))
  4973. player_skills->SetSkill(SKILL_ID_DESTROYING, new_skill_cap);
  4974. if (player_skills->HasSkill(SKILL_ID_MAGIC_AFFINITY))
  4975. player_skills->SetSkill(SKILL_ID_MAGIC_AFFINITY, new_skill_cap);
  4976. Guild* guild = GetPlayer()->GetGuild();
  4977. if (guild) {
  4978. int8 event_type = 0;
  4979. if (new_level < 10)
  4980. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_1_10;
  4981. else if (new_level == 10)
  4982. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_10;
  4983. else if (new_level >= 11 && new_level < 20)
  4984. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_11_20;
  4985. else if (new_level == 20)
  4986. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_20;
  4987. else if (new_level >= 21 && new_level < 30)
  4988. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_21_30;
  4989. else if (new_level == 30)
  4990. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_30;
  4991. else if (new_level >= 31 && new_level < 40)
  4992. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_31_40;
  4993. else if (new_level == 40)
  4994. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_40;
  4995. else if (new_level >= 41 && new_level < 50)
  4996. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_41_50;
  4997. else if (new_level == 50)
  4998. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_50;
  4999. else if (new_level >= 51 && new_level < 60)
  5000. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_51_60;
  5001. else if (new_level == 60)
  5002. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_60;
  5003. else if (new_level >= 61 && new_level < 70)
  5004. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_61_70;
  5005. else if (new_level == 70)
  5006. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_70;
  5007. else if (new_level >= 71 && new_level < 80)
  5008. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_71_80;
  5009. else if (new_level == 80)
  5010. event_type = GUILD_EVENT_GAINS_ADV_LEVEL_80;
  5011. guild->AddNewGuildEvent(event_type, "%s has gained an adventure level and is now a level %u %s.", Timer::GetUnixTimeStamp(), true, GetPlayer()->GetName(), new_level, classes.GetClassNameCase(GetPlayer()->GetAdventureClass()).c_str());
  5012. guild->SendMessageToGuild(event_type, "%s has gained an adventure level and is now a level %u %s.", GetPlayer()->GetName(), new_level, classes.GetClassNameCase(GetPlayer()->GetAdventureClass()).c_str());
  5013. guild->UpdateGuildMemberInfo(GetPlayer());
  5014. guild->SendGuildMember(GetPlayer());
  5015. guild->SendGuildMemberList();
  5016. }
  5017. // Need to send the trait list every time the players level changes
  5018. // Also need to force the char sheet update or else there can be a large delay from when you level
  5019. // to when you are actually able to select traits.
  5020. QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion()));
  5021. GetPlayer()->need_trait_update = true;
  5022. if (version > 561) {
  5023. QueuePacket(master_trait_list.GetTraitListPacket(this));
  5024. master_aa_list.DisplayAA(this, 0, 0);
  5025. }
  5026. else
  5027. master_trait_list.ChooseNextTrait(this);
  5028. if (GetPlayer()->SpawnedBots.size() > 0) {
  5029. map<int32, int32>::iterator itr;
  5030. for (itr = GetPlayer()->SpawnedBots.begin(); itr != GetPlayer()->SpawnedBots.end(); itr++) {
  5031. Spawn* bot = GetCurrentZone()->GetSpawnByID(itr->second);
  5032. if (bot && bot->IsBot())
  5033. ((Bot*)bot)->ChangeLevel(old_level, new_level);
  5034. }
  5035. }
  5036. if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower())
  5037. GetPlayer()->GetZone()->AddDamagedSpawn(GetPlayer());
  5038. }
  5039. void Client::ChangeTSLevel(int16 old_level, int16 new_level) {
  5040. if (new_level < 1) {
  5041. SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!");
  5042. return;
  5043. }
  5044. if ((player->GetTSLevel() >= 9 && player->GetTradeskillClass() == 1) || (player->GetTSLevel() >= 19 && (player->GetTradeskillClass() == 1 || player->GetTradeskillClass() == 2 || player->GetTradeskillClass() == 6 || player->GetTradeskillClass() == 10))) {
  5045. SimpleMessage(CHANNEL_COLOR_YELLOW, "You can not gain levels until you select your next class!");
  5046. return;
  5047. }
  5048. if (new_level > old_level)
  5049. player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_TRADESKILL, new_level, player->GetTradeskillClass());
  5050. if (player->GetTSLevel() != new_level) {
  5051. player->SetTSLevel(new_level);
  5052. if (player->GetGroupMemberInfo()) {
  5053. player->UpdateGroupMemberInfo();
  5054. world.GetGroupManager()->SendGroupUpdate(player->GetGroupMemberInfo()->group_id);
  5055. }
  5056. }
  5057. // Only tradeskill skills should increace, and then only those related to your class
  5058. PacketStruct* level_update = configReader.getStruct("WS_LevelChanged", GetVersion());
  5059. if (level_update) {
  5060. level_update->setDataByName("old_level", old_level);
  5061. level_update->setDataByName("new_level", new_level);
  5062. level_update->setDataByName("type", 1);
  5063. QueuePacket(level_update->serialize());
  5064. safe_delete(level_update);
  5065. }
  5066. // provide new spells upon levelling
  5067. SendNewTradeskillSpells();
  5068. PacketStruct* command_packet = configReader.getStruct("WS_CannedEmote", GetVersion());
  5069. if (command_packet) {
  5070. command_packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(GetPlayer()));
  5071. command_packet->setDataByName("anim_type", 1753);
  5072. QueuePacket(command_packet->serialize());
  5073. safe_delete(command_packet);
  5074. }
  5075. GetPlayer()->GetInfoStruct()->set_tradeskill_level(new_level);
  5076. GetPlayer()->SetTSLevel(new_level);
  5077. UpdateTimeStampFlag(LEVEL_UPDATE_FLAG);
  5078. GetPlayer()->SetCharSheetChanged(true);
  5079. Message(CHANNEL_NARRATIVE, "Your tradeskill level is now %i!", new_level);
  5080. LogWrite(WORLD__DEBUG, 0, "World", "Player: %s leveled from %u to %u", GetPlayer()->GetName(), old_level, new_level);
  5081. PlayerSkillList* player_skills = player->GetSkills();
  5082. int16 specialize_skill_cap = new_level * 5;
  5083. int16 artisan_skill_cap = std::max<int16>(specialize_skill_cap, 49);
  5084. int16 specialize_10_skill_cap = std::max<int16>(specialize_skill_cap, 99);
  5085. int8 ts_class = player->GetTradeskillClass();
  5086. int8 base_ts_class = classes.GetSecondaryTSBaseClass(ts_class);
  5087. int32 skill_id_1, skill_id_2, skill_id_3;
  5088. switch (base_ts_class) {
  5089. case ARTISAN:
  5090. player_skills->SetSkillCapsByType(SKILL_TYPE_OUTFITTER, artisan_skill_cap);
  5091. player_skills->SetSkillCapsByType(SKILL_TYPE_SCHOLAR, artisan_skill_cap);
  5092. player_skills->SetSkillCapsByType(SKILL_TYPE_CRAFTSMAN, artisan_skill_cap);
  5093. break;
  5094. case OUTFITTER:
  5095. player_skills->SetSkillCapsByType(SKILL_TYPE_SCHOLAR, artisan_skill_cap);
  5096. player_skills->SetSkillCapsByType(SKILL_TYPE_CRAFTSMAN, artisan_skill_cap);
  5097. skill_id_1 = SKILL_ID_TAILORING;
  5098. skill_id_2 = SKILL_ID_METALSHAPING;
  5099. skill_id_3 = SKILL_ID_METALWORKING;
  5100. if (ts_class == TAILOR) {
  5101. player_skills->SetSkillCap(skill_id_1, specialize_skill_cap);
  5102. skill_id_1 = 0;
  5103. }
  5104. else if (ts_class == ARMORER) {
  5105. player_skills->SetSkillCap(skill_id_2, specialize_skill_cap);
  5106. skill_id_2 = 0;
  5107. }
  5108. else if (ts_class == WEAPONSMITH) {
  5109. player_skills->SetSkillCap(skill_id_3, specialize_skill_cap);
  5110. skill_id_3 = 0;
  5111. }
  5112. if (skill_id_1) player_skills->SetSkillCap(skill_id_1, specialize_10_skill_cap);
  5113. if (skill_id_2) player_skills->SetSkillCap(skill_id_2, specialize_10_skill_cap);
  5114. if (skill_id_3) player_skills->SetSkillCap(skill_id_3, specialize_10_skill_cap);
  5115. break;
  5116. case SCHOLAR:
  5117. player_skills->SetSkillCapsByType(SKILL_TYPE_OUTFITTER, artisan_skill_cap);
  5118. player_skills->SetSkillCapsByType(SKILL_TYPE_CRAFTSMAN, artisan_skill_cap);
  5119. skill_id_1 = SKILL_ID_SCRIBING;
  5120. skill_id_2 = SKILL_ID_CHEMISTRY;
  5121. skill_id_3 = SKILL_ID_ARTIFICING;
  5122. if (ts_class == SAGE) {
  5123. player_skills->SetSkillCap(skill_id_1, specialize_skill_cap);
  5124. skill_id_1 = 0;
  5125. }
  5126. else if (ts_class == ALCHEMIST) {
  5127. player_skills->SetSkillCap(skill_id_2, specialize_skill_cap);
  5128. skill_id_2 = 0;
  5129. }
  5130. else if (ts_class == JEWELER) {
  5131. player_skills->SetSkillCap(skill_id_3, specialize_skill_cap);
  5132. skill_id_3 = 0;
  5133. }
  5134. if (skill_id_1) player_skills->SetSkillCap(skill_id_1, specialize_10_skill_cap);
  5135. if (skill_id_2) player_skills->SetSkillCap(skill_id_2, specialize_10_skill_cap);
  5136. if (skill_id_3) player_skills->SetSkillCap(skill_id_3, specialize_10_skill_cap);
  5137. break;
  5138. case CRAFTSMAN:
  5139. player_skills->SetSkillCapsByType(SKILL_TYPE_OUTFITTER, artisan_skill_cap);
  5140. player_skills->SetSkillCapsByType(SKILL_TYPE_SCHOLAR, artisan_skill_cap);
  5141. skill_id_1 = SKILL_ID_ARTISTRY;
  5142. skill_id_2 = SKILL_ID_FLETCHING;
  5143. skill_id_3 = SKILL_ID_SCULPTING;
  5144. if (ts_class == PROVISIONER) {
  5145. player_skills->SetSkillCap(skill_id_1, specialize_skill_cap);
  5146. skill_id_1 = 0;
  5147. }
  5148. else if (ts_class == WOODWORKER) {
  5149. player_skills->SetSkillCap(skill_id_2, specialize_skill_cap);
  5150. skill_id_2 = 0;
  5151. }
  5152. else if (ts_class == CARPENTER) {
  5153. player_skills->SetSkillCap(skill_id_3, specialize_skill_cap);
  5154. skill_id_3 = 0;
  5155. }
  5156. if (skill_id_1) player_skills->SetSkillCap(skill_id_1, specialize_10_skill_cap);
  5157. if (skill_id_2) player_skills->SetSkillCap(skill_id_2, specialize_10_skill_cap);
  5158. if (skill_id_3) player_skills->SetSkillCap(skill_id_3, specialize_10_skill_cap);
  5159. break;
  5160. default:
  5161. break;
  5162. }
  5163. if (new_level > player->GetAdventureClass())
  5164. player_skills->SetSkillCapsByType(SKILL_TYPE_HARVESTING, specialize_skill_cap);
  5165. Guild* guild = GetPlayer()->GetGuild();
  5166. if (guild) {
  5167. int8 event_type = 0;
  5168. if (new_level < 10)
  5169. event_type = GUILD_EVENT_GAINS_TS_LEVEL_1_10;
  5170. else if (new_level == 10)
  5171. event_type = GUILD_EVENT_GAINS_TS_LEVEL_10;
  5172. else if (new_level >= 11 && new_level < 20)
  5173. event_type = GUILD_EVENT_GAINS_TS_LEVEL_11_20;
  5174. else if (new_level == 20)
  5175. event_type = GUILD_EVENT_GAINS_TS_LEVEL_20;
  5176. else if (new_level >= 21 && new_level < 30)
  5177. event_type = GUILD_EVENT_GAINS_TS_LEVEL_21_30;
  5178. else if (new_level == 30)
  5179. event_type = GUILD_EVENT_GAINS_TS_LEVEL_30;
  5180. else if (new_level >= 31 && new_level < 40)
  5181. event_type = GUILD_EVENT_GAINS_TS_LEVEL_31_40;
  5182. else if (new_level == 40)
  5183. event_type = GUILD_EVENT_GAINS_TS_LEVEL_40;
  5184. else if (new_level >= 41 && new_level < 50)
  5185. event_type = GUILD_EVENT_GAINS_TS_LEVEL_41_50;
  5186. else if (new_level == 50)
  5187. event_type = GUILD_EVENT_GAINS_TS_LEVEL_50;
  5188. else if (new_level >= 51 && new_level < 60)
  5189. event_type = GUILD_EVENT_GAINS_TS_LEVEL_51_60;
  5190. else if (new_level == 60)
  5191. event_type = GUILD_EVENT_GAINS_TS_LEVEL_60;
  5192. else if (new_level >= 61 && new_level < 70)
  5193. event_type = GUILD_EVENT_GAINS_TS_LEVEL_61_70;
  5194. else if (new_level == 70)
  5195. event_type = GUILD_EVENT_GAINS_TS_LEVEL_70;
  5196. else if (new_level >= 71 && new_level < 80)
  5197. event_type = GUILD_EVENT_GAINS_TS_LEVEL_71_80;
  5198. else if (new_level == 80)
  5199. event_type = GUILD_EVENT_GAINS_TS_LEVEL_80;
  5200. guild->AddNewGuildEvent(event_type, "%s has gained a tradeskill level and is now a level %u %s.", Timer::GetUnixTimeStamp(), true, GetPlayer()->GetName(), new_level, classes.GetClassNameCase(GetPlayer()->GetTradeskillClass() + 42).c_str());
  5201. guild->SendMessageToGuild(event_type, "%s has gained a tradeskill level and is now a level %u %s.", GetPlayer()->GetName(), new_level, classes.GetClassNameCase(GetPlayer()->GetTradeskillClass() + 42).c_str());
  5202. guild->UpdateGuildMemberInfo(GetPlayer());
  5203. guild->SendGuildMember(GetPlayer());
  5204. guild->SendGuildMemberList();
  5205. }
  5206. // Need to send the trait list every time the players level changes
  5207. // Also need to force the char sheet update or else there can be a large delay from when you level
  5208. // to when you are actually able to select traits.
  5209. QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion()));
  5210. QueuePacket(master_trait_list.GetTraitListPacket(this));
  5211. }
  5212. void Client::CloseLoot(int32 spawn_id) {
  5213. if (GetVersion() > 561) {
  5214. PacketStruct* packet = configReader.getStruct("WS_CloseWindow", GetVersion());
  5215. if (packet) {
  5216. packet->setDataByName("window_id", 4);
  5217. EQ2Packet* outapp = packet->serialize();
  5218. if (outapp) {
  5219. //DumpPacket(outapp);
  5220. QueuePacket(outapp);
  5221. }
  5222. safe_delete(packet);
  5223. }
  5224. }
  5225. if (spawn_id > 0) {
  5226. PacketStruct* packet = configReader.getStruct("WS_StoppedLooting", GetVersion());
  5227. if (packet) {
  5228. packet->setDataByName("spawn_id", spawn_id);
  5229. EQ2Packet* outapp = packet->serialize();
  5230. if (outapp)
  5231. QueuePacket(outapp);
  5232. safe_delete(packet);
  5233. }
  5234. Spawn* spawn = GetPlayer()->GetSpawnWithPlayerID(spawn_id);
  5235. if (spawn) {
  5236. spawn->CloseLoot(GetPlayer());
  5237. }
  5238. }
  5239. }
  5240. string Client::GetCoinMessage(int32 total_coins) {
  5241. if (total_coins == 0) {
  5242. return " 0 Copper";
  5243. }
  5244. char tmp[64] = { 0 };
  5245. string message = "";
  5246. int32 val = 0;
  5247. if (total_coins >= 1000000) {
  5248. val = total_coins / 1000000;
  5249. total_coins -= 1000000 * val;
  5250. sprintf(tmp, " %u Platinum", val);
  5251. message.append(tmp);
  5252. memset(tmp, 0, 64);
  5253. }
  5254. if (total_coins >= 10000) {
  5255. val = total_coins / 10000;
  5256. total_coins -= 10000 * val;
  5257. sprintf(tmp, " %u Gold", val);
  5258. message.append(tmp);
  5259. memset(tmp, 0, 64);
  5260. }
  5261. if (total_coins >= 100) {
  5262. val = total_coins / 100;
  5263. total_coins -= 100 * val;
  5264. sprintf(tmp, " %u Silver", val);
  5265. message.append(tmp);
  5266. memset(tmp, 0, 64);
  5267. }
  5268. if (total_coins > 0) {
  5269. sprintf(tmp, " %u Copper", (int32)total_coins);
  5270. message.append(tmp);
  5271. }
  5272. return message;
  5273. }
  5274. void Client::SendLootResponsePacket(int32 total_coins, vector<Item*>* items, Spawn* entity, bool ignore_loot_tier) {
  5275. if (!entity) {
  5276. CloseLoot(0);
  5277. return;
  5278. }
  5279. if (total_coins > 0) {
  5280. player->AddCoins(total_coins);
  5281. //PlaySound("coin_cha_ching");
  5282. string message = "";
  5283. if (entity->GetHP() == 0) {
  5284. message = "You loot ";
  5285. entity->SetLootCoins(0, false);
  5286. }
  5287. else
  5288. message = "You receive ";
  5289. message.append(GetCoinMessage(total_coins));
  5290. if (entity->GetHP() == 0)
  5291. message.append(" from the corpse of ").append(entity->GetName());
  5292. int8 type = CHANNEL_LOOT;
  5293. SimpleMessage(type, message.c_str());
  5294. }
  5295. entity->StartLootTimer(GetPlayer());
  5296. PacketStruct* packet = configReader.getStruct("WS_UpdateLoot", GetVersion());
  5297. if (packet) {
  5298. entity->AddSpawnLootWindowCompleted(GetPlayer()->GetID(), false);
  5299. vector<Item*>::iterator itr;
  5300. int32 packet_size = 0;
  5301. EQ2Packet* outapp = 0;
  5302. uchar* data = 0;
  5303. vector<Item*> send_items;
  5304. if (items && items->size() > 0) {
  5305. for (int i = 0; i < items->size(); i++) {
  5306. Item* item = (*items)[i];
  5307. if (entity->GetLootMethod() > GroupLootMethod::METHOD_FFA && !ignore_loot_tier) {
  5308. bool skipItem = entity->IsItemInLootTier(item);
  5309. if (!skipItem) {
  5310. send_items.push_back(item);
  5311. }
  5312. }
  5313. else {
  5314. send_items.push_back(item);
  5315. }
  5316. }
  5317. }
  5318. if (GetVersion() >= 374) {
  5319. if (GetVersion() > 561) {
  5320. if (send_items.size() > 0) {
  5321. packet->setDataByName("loot_count", send_items.size());
  5322. packet->setDataByName("display", 1);
  5323. }
  5324. packet->setDataByName("loot_type", entity->GetLootMethod());
  5325. packet->setDataByName("lotto_timeout", entity->GetLootTimeRemaining() / 1000);
  5326. packet->setDataByName("loot_id", entity->GetID());
  5327. EQ2Packet* tmpPacket = packet->serialize();
  5328. packet_size += tmpPacket->size;
  5329. if (send_items.size() > 0) {
  5330. data = new uchar[send_items.size() * 1000 + packet_size];
  5331. memset(data, 0, send_items.size() * 1000 + packet_size);
  5332. }
  5333. else {
  5334. data = new uchar[packet_size];
  5335. memset(data, 0, packet_size);
  5336. }
  5337. uchar* ptr = data;
  5338. memcpy(ptr, tmpPacket->pBuffer, tmpPacket->size);
  5339. ptr += tmpPacket->size;
  5340. safe_delete(tmpPacket);
  5341. Item* item = 0;
  5342. if (send_items.size() > 0) {
  5343. for (itr = send_items.begin(); itr != send_items.end(); itr++) {
  5344. item = *itr;
  5345. memcpy(ptr, &item->details.item_id, sizeof(int32));
  5346. ptr += sizeof(int32);
  5347. packet_size += sizeof(int32);
  5348. tmpPacket = item->serialize(GetVersion(), true, GetPlayer(), false, 1, 0, false, true);
  5349. int8 offset = 0;
  5350. if (GetVersion() >= 1188) {
  5351. offset = 13;
  5352. }
  5353. else if (GetVersion() >= 860) {
  5354. offset = 11;
  5355. }
  5356. else if (GetVersion() <= 561) {
  5357. offset = 19;
  5358. }
  5359. else {
  5360. offset = 10;
  5361. }
  5362. memcpy(ptr, tmpPacket->pBuffer + offset, tmpPacket->size - offset);
  5363. ptr += tmpPacket->size - offset;
  5364. packet_size += tmpPacket->size - offset;
  5365. safe_delete(tmpPacket);
  5366. }
  5367. }
  5368. packet_size -= sizeof(int32);
  5369. memcpy(data, &packet_size, sizeof(int32));
  5370. packet_size += sizeof(int32);
  5371. outapp = new EQ2Packet(OP_ClientCmdMsg, data, packet_size);
  5372. }
  5373. else {
  5374. if (send_items.size() > 0) {
  5375. packet->setArrayLengthByName("loot_count", send_items.size());
  5376. Item* item = 0;
  5377. if (send_items.size() > 0) {
  5378. int i = 0;
  5379. for (itr = send_items.begin(); itr != send_items.end(); itr++) {
  5380. item = *itr;
  5381. packet->setArrayDataByName("loot_id", item->details.item_id, i);
  5382. packet->setItemArrayDataByName("item", item, GetPlayer(), i, 0, 2, true);
  5383. i++;
  5384. }
  5385. }
  5386. packet->setDataByName("display", 1);
  5387. }
  5388. packet->setDataByName("loot_type", entity->GetLootMethod()); // normal
  5389. packet->setDataByName("lotto_timeout", entity->GetLootTimeRemaining() / 1000); // 60 seconds
  5390. packet->setDataByName("spawn_id", entity->GetID());
  5391. outapp = packet->serialize();
  5392. }
  5393. }
  5394. else {
  5395. if (send_items.size() > 0) {
  5396. packet->setArrayLengthByName("loot_count", send_items.size());
  5397. for (int i = 0; i < send_items.size(); i++) {
  5398. Item* item = (send_items)[i];
  5399. packet->setArrayDataByName("name", item->name.c_str(), i);
  5400. packet->setArrayDataByName("item_id", item->details.item_id, i);
  5401. packet->setArrayDataByName("count", item->details.count, i);
  5402. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  5403. if (item->generic_info.skill_req1 > 0 && item->generic_info.skill_req1 < 0xFFFFFFFF)
  5404. packet->setArrayDataByName("ability_id", item->generic_info.skill_req1, i);
  5405. else if (item->generic_info.skill_req2 > 0 && item->generic_info.skill_req2 < 0xFFFFFFFF)
  5406. packet->setArrayDataByName("ability_id", item->generic_info.skill_req2, i);
  5407. else
  5408. packet->setArrayDataByName("ability_id", 0xFFFFFFFF, i);
  5409. }
  5410. }
  5411. packet->setDataByName("display", 1);
  5412. packet->setDataByName("loot_type", entity->GetLootMethod()); // normal
  5413. packet->setDataByName("lotto_timeout", entity->GetLootTimeRemaining() / 1000); // 60 seconds
  5414. packet->setDataByName("object_id", entity->GetID());
  5415. outapp = packet->serialize();
  5416. }
  5417. if (outapp) {
  5418. QueuePacket(outapp);
  5419. }
  5420. safe_delete_array(data);
  5421. safe_delete(packet);
  5422. if (!items || items->size() == 0)
  5423. CloseLoot(entity->GetID());
  5424. }
  5425. }
  5426. bool Client::LootSpawnByMethod(Spawn* entity) {
  5427. bool sentLoot = false;
  5428. bool startWithLooter = true;
  5429. world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
  5430. GroupMemberInfo* gmi = GetPlayer()->GetGroupMemberInfo();
  5431. if (gmi && gmi->group_id)
  5432. {
  5433. PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
  5434. if (group)
  5435. {
  5436. int8 auto_split_coin = group->GetGroupOptions()->auto_split;
  5437. bool isLeadGroup = group->IsInRaidGroup(group->GetID(), true);
  5438. bool isInRaid = group->IsInRaidGroup(group->GetID());
  5439. std::vector<int32> raidGroups;
  5440. group->GetRaidGroups(&raidGroups);
  5441. if (!isInRaid && raidGroups.size() < 1) {
  5442. raidGroups.push_back(group->GetID());
  5443. }
  5444. std::vector<int32>::iterator group_itr;
  5445. int32 split_coin_per_player = 0;
  5446. int32 coins_remain_after_split = entity->GetLootCoins();
  5447. int32 total_coins = entity->GetLootCoins();
  5448. bool foundLooterResetRaidRun = false;
  5449. int8 members_in_zone = 0;
  5450. for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
  5451. group = world.GetGroupManager()->GetGroup((*group_itr));
  5452. if (!group)
  5453. continue;
  5454. group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
  5455. deque<GroupMemberInfo*>* members = group->GetMembers();
  5456. for (int8 i = 0; i < members->size(); i++) {
  5457. Entity* member = members->at(i)->member;
  5458. if (!member || !member->IsPlayer())
  5459. continue;
  5460. if (member->GetZone() != GetPlayer()->GetZone())
  5461. continue;
  5462. members_in_zone++;
  5463. }
  5464. group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
  5465. }
  5466. if (auto_split_coin) {
  5467. if (members_in_zone < 1) // this should not happen, but divide by zero checked
  5468. members_in_zone = 1;
  5469. split_coin_per_player = entity->GetLootCoins() / members_in_zone;
  5470. coins_remain_after_split = entity->GetLootCoins() - (split_coin_per_player * members_in_zone);
  5471. entity->SetLootCoins(0, false);
  5472. }
  5473. int32 lootGroup = 0;
  5474. for (group_itr = raidGroups.begin(); group_itr != raidGroups.end();) {
  5475. group = world.GetGroupManager()->GetGroup((*group_itr));
  5476. if (!group)
  5477. continue;
  5478. isLeadGroup = group->IsInRaidGroup((*group_itr), true);
  5479. group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
  5480. deque<GroupMemberInfo*>* members = group->GetMembers();
  5481. LogWrite(LOOT__INFO, 0, "Loot", "%s: Group LootSpawnByMethod %u, auto coin split %u, split coin per player %u, remaining coin after split %u", entity->GetName(), entity->GetLootMethod(), auto_split_coin, split_coin_per_player, coins_remain_after_split);
  5482. for (int8 i = 0; i < members->size(); i++) {
  5483. Entity* member = members->at(i)->member;
  5484. if (!member || !member->IsPlayer())
  5485. continue;
  5486. if (member->GetZone() != GetPlayer()->GetZone())
  5487. continue;
  5488. // this will make sure we properly send the loot window to the initial requester if there is no item rarity matches
  5489. if (startWithLooter && member != GetPlayer())
  5490. continue;
  5491. else if (!startWithLooter && member == GetPlayer())
  5492. continue;
  5493. if (auto_split_coin) {
  5494. int32 coin_recv = 0;
  5495. if (member == GetPlayer() && auto_split_coin && (split_coin_per_player + coins_remain_after_split) > 0) {
  5496. coin_recv = split_coin_per_player + coins_remain_after_split;
  5497. player->AddCoins(split_coin_per_player + coins_remain_after_split);
  5498. if (coins_remain_after_split > 0) // overflow of coin division went to the first player
  5499. coins_remain_after_split = 0;
  5500. }
  5501. else if (split_coin_per_player > 0) {
  5502. coin_recv = split_coin_per_player;
  5503. player->AddCoins(split_coin_per_player);
  5504. }
  5505. if (coin_recv && ((Player*)member)->GetClient()) {
  5506. ((Player*)member)->GetClient()->Message(CHANNEL_MONEY_SPLIT, "Your share of %s from the corpse of %s is %s.", GetCoinMessage(total_coins).c_str(), entity->GetLootName(), GetCoinMessage(coin_recv).c_str());
  5507. }
  5508. }
  5509. switch (entity->GetLootMethod()) {
  5510. case GroupLootMethod::METHOD_LOTTO:
  5511. case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
  5512. if (((Player*)member)->GetClient()) {
  5513. switch (member->GetInfoStruct()->get_group_auto_loot_method()) {
  5514. case 1: { // lotto, need
  5515. if (entity->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
  5516. entity->AddLottoItemRequest(0xFFFFFFFF, GetPlayer()->GetID());
  5517. }
  5518. else { // *need* before greed
  5519. entity->AddNeedGreedItemRequest(0xFFFFFFFF, GetPlayer()->GetID(), true);
  5520. }
  5521. entity->AddSpawnLootWindowCompleted(member->GetID(), true);
  5522. // if it already exists we have to override the setting
  5523. entity->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
  5524. break;
  5525. }
  5526. case 2: { // decline
  5527. entity->AddSpawnLootWindowCompleted(member->GetID(), true);
  5528. // if it already exists we have to override the setting
  5529. entity->SetSpawnLootWindowCompleted(GetPlayer()->GetID());
  5530. break;
  5531. }
  5532. default: { // presume 0 or higher than 2
  5533. ((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity, true);
  5534. break;
  5535. }
  5536. }
  5537. sentLoot = true;
  5538. }
  5539. break;
  5540. }
  5541. case GroupLootMethod::METHOD_ROUND_ROBIN: {
  5542. entity->AddSpawnLootWindowCompleted(member->GetID(), true);
  5543. sentLoot = true;
  5544. break;
  5545. }
  5546. case GroupLootMethod::METHOD_LEADER: {
  5547. if ((!isInRaid || (isInRaid && isLeadGroup)) && member->GetGroupMemberInfo()->leader)
  5548. ((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity);
  5549. break;
  5550. }
  5551. case GroupLootMethod::METHOD_FFA: {
  5552. if (member == GetPlayer()) {
  5553. ((Player*)member)->GetClient()->SendLootResponsePacket((!auto_split_coin && member == GetPlayer()) ? entity->GetLootCoins() : 0, entity->GetLootItems(), entity);
  5554. }
  5555. break;
  5556. }
  5557. }
  5558. if (startWithLooter) {
  5559. startWithLooter = false;
  5560. foundLooterResetRaidRun = true; // we got it, shouldn't hit this again
  5561. break;
  5562. }
  5563. }
  5564. group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
  5565. if (foundLooterResetRaidRun) {
  5566. group_itr = raidGroups.begin();
  5567. foundLooterResetRaidRun = false; // disable running it again
  5568. if (entity->GetLootMethod() == GroupLootMethod::METHOD_LEADER)
  5569. break;
  5570. }
  5571. else
  5572. group_itr++;
  5573. }
  5574. }
  5575. }
  5576. world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
  5577. return sentLoot;
  5578. }
  5579. void Client::LootSpawnRequest(Spawn* entity, bool attemptDisarm) {
  5580. bool lootAllowed = false;
  5581. bool sentLoot = false;
  5582. std::vector<int32> item_list;
  5583. if (entity->IsNPC()) {
  5584. entity->LockLoot();
  5585. lootAllowed = ((NPC*)entity)->Brain()->CheckLootAllowed(GetPlayer());
  5586. entity->UnlockLoot();
  5587. if (lootAllowed) {
  5588. OpenChest(entity, attemptDisarm);
  5589. }
  5590. else {
  5591. SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not allowed to loot at this time.");
  5592. return;
  5593. }
  5594. entity->LockLoot();
  5595. if (((NPC*)entity)->Brain()->CheckLootAllowed(GetPlayer())) {
  5596. lootAllowed = true;
  5597. if ((sentLoot = LootSpawnByMethod(entity))) {
  5598. entity->GetLootItemsList(&item_list);
  5599. }
  5600. else {
  5601. SendLootResponsePacket(entity->GetLootCoins(), entity->GetLootItems(), entity);
  5602. }
  5603. }
  5604. entity->UnlockLoot();
  5605. if (lootAllowed) {
  5606. entity->DistributeGroupLoot_RoundRobin(&item_list, true);
  5607. }
  5608. }
  5609. }
  5610. void Client::OpenChest(Spawn* entity, bool attemptDisarm)
  5611. {
  5612. if (!entity)
  5613. return;
  5614. int8 chest_difficulty = 0;
  5615. int32 state = 0;
  5616. // Check for the chest and set the action state
  5617. /*4034 = small chest | 5864 = treasure chest | 5865 = ornate treasure chest | 4015 = exquisite chest*/
  5618. string modelName;
  5619. if (entity->GetModelType() == 4034) {
  5620. // small chest, open with copper coins
  5621. // does not include traps, however can be disarmed
  5622. chest_difficulty = 1;
  5623. state = 11899;
  5624. modelName.append("Small Chest");
  5625. }
  5626. else if (entity->GetModelType() == 5864) {
  5627. // treasure chest, open with silver coins
  5628. chest_difficulty = 2;
  5629. state = 11901;
  5630. modelName.append("Treasure Chest");
  5631. }
  5632. else if (entity->GetModelType() == 5865) {
  5633. // ornate chest, open with gold coins
  5634. chest_difficulty = 3;
  5635. state = 11900;
  5636. modelName.append("Ornate Chest");
  5637. }
  5638. else if (entity->GetModelType() == 4015) {
  5639. // exquisite chest, open with gold coins and jewels as well as a glow effect
  5640. chest_difficulty = 5;
  5641. state = 11898;
  5642. modelName.append("Exquisite Chest");
  5643. }
  5644. bool firstChestOpen = false;
  5645. if (chest_difficulty > 0 && !entity->HasTrapTriggered())
  5646. {
  5647. ChestTrap::ChestTrapInfo nextTrap;
  5648. bool ret = chest_trap_list.GetNextTrap(GetCurrentZone()->GetZoneID(), chest_difficulty, &nextTrap);
  5649. Skill* disarmSkill = GetPlayer()->GetSkillByName("Disarm Trap", false);
  5650. firstChestOpen = true;
  5651. entity->SetTrapTriggered(true, state);
  5652. if (ret)
  5653. {
  5654. if (disarmSkill && attemptDisarm)
  5655. {
  5656. if (disarmSkill->CheckDisarmSkill(entity->GetLevel(), chest_difficulty) < 1)
  5657. {
  5658. CastGroupOrSelf(entity && entity->IsEntity() ? (Entity*)entity : 0, nextTrap.spell_id, nextTrap.spell_tier,
  5659. rule_manager.GetZoneRule(GetCurrentZoneID(), R_Loot, ChestTriggerRadiusGroup)->GetFloat());
  5660. Message(CHANNEL_NARRATIVE, "You trigger the trap on %s!", modelName.c_str());
  5661. }
  5662. else
  5663. {
  5664. Message(CHANNEL_NARRATIVE, "You disarm the trap on %s", modelName.c_str());
  5665. }
  5666. // despite fail/succeed we should always try to increase skill if disarm is available
  5667. GetPlayer()->GetSkillByName("Disarm Trap", true);
  5668. }
  5669. else // no disarm skill, always fail
  5670. {
  5671. CastGroupOrSelf(entity && entity->IsEntity() ? (Entity*)entity : 0, nextTrap.spell_id, nextTrap.spell_tier,
  5672. rule_manager.GetZoneRule(GetCurrentZoneID(), R_Loot, ChestTriggerRadiusGroup)->GetFloat());
  5673. Message(CHANNEL_NARRATIVE, "You trigger the trap on %s!", modelName.c_str());
  5674. }
  5675. }
  5676. }
  5677. else if (!entity->HasTrapTriggered())
  5678. {
  5679. firstChestOpen = true;
  5680. entity->SetTrapTriggered(true, state);
  5681. }
  5682. // We set the visual state with out updating so those not in range will see it opened when it is finally sent to them,
  5683. // for those in range the SendStateCommand will cause it to animate open.
  5684. // players not currently in radius will have it queued with client->QueueStateCommand when SendSpawn takes place
  5685. if (firstChestOpen)
  5686. GetCurrentZone()->SendStateCommand(entity, state);
  5687. }
  5688. void Client::CastGroupOrSelf(Entity* source, uint32 spellID, uint32 spellTier, float restrictiveRadius)
  5689. {
  5690. Spell* spell = master_spell_list.GetSpell(spellID, spellTier);
  5691. SpellProcess* spellProcess = GetCurrentZone()->GetSpellProcess();
  5692. if (source == NULL)
  5693. source = (Entity*)GetPlayer();
  5694. if (spell)
  5695. {
  5696. GroupMemberInfo* gmi = GetPlayer()->GetGroupMemberInfo();
  5697. if (gmi && gmi->group_id)
  5698. {
  5699. PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
  5700. if (group)
  5701. {
  5702. group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
  5703. deque<GroupMemberInfo*>* members = group->GetMembers();
  5704. for (int8 i = 0; i < members->size(); i++) {
  5705. Entity* member = members->at(i)->member;
  5706. if (!member)
  5707. continue;
  5708. if (!member->Alive() || (member->GetZone() != source->GetZone()))
  5709. continue;
  5710. // if we have a radius provided then check if the group member is outside the radius or not
  5711. if (restrictiveRadius > 0.0f && member->GetDistance(source) > restrictiveRadius)
  5712. continue;
  5713. spellProcess->CastInstant(spell, source, (Entity*)GetPlayer());
  5714. }
  5715. group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
  5716. }
  5717. }
  5718. else
  5719. spellProcess->CastInstant(spell, source, (Entity*)GetPlayer());
  5720. }
  5721. }
  5722. Spawn* Client::GetBanker() {
  5723. return banker;
  5724. }
  5725. void Client::SetBanker(Spawn* in_banker) {
  5726. banker = in_banker;
  5727. }
  5728. void Client::Bank(Spawn* banker, bool cancel) {
  5729. if (banker && banker->primary_command_list.size() > 0 && banker->primary_command_list[0]->command == "bank") {
  5730. if (!cancel)
  5731. SetBanker(banker);
  5732. else
  5733. SetBanker(0);
  5734. PacketStruct* packet = configReader.getStruct("WS_UpdateBank", GetVersion());
  5735. if (packet) {
  5736. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(banker));
  5737. int64 coins = GetPlayer()->GetInfoStruct()->get_bank_coin_copper() + GetPlayer()->GetInfoStruct()->get_bank_coin_silver() * 100 +
  5738. GetPlayer()->GetInfoStruct()->get_bank_coin_gold() * 10000 + (int64)GetPlayer()->GetInfoStruct()->get_bank_coin_plat() * 1000000;
  5739. int32 coins1, coins2;
  5740. coins1 = ((int32*)&coins)[0];
  5741. coins2 = ((int32*)&coins)[1];
  5742. packet->setDataByName("bank_coins", coins1);
  5743. packet->setDataByName("bank_coins2", coins2);
  5744. packet->setDataByName("copper", GetPlayer()->GetInfoStruct()->get_coin_copper());
  5745. packet->setDataByName("silver", GetPlayer()->GetInfoStruct()->get_coin_silver());
  5746. packet->setDataByName("gold", GetPlayer()->GetInfoStruct()->get_coin_gold());
  5747. packet->setDataByName("plat", GetPlayer()->GetInfoStruct()->get_coin_plat());
  5748. if (!cancel)
  5749. packet->setDataByName("display", 1);
  5750. QueuePacket(packet->serialize());
  5751. safe_delete(packet);
  5752. }
  5753. }
  5754. }
  5755. bool Client::BankHasCoin(int64 amount) {
  5756. int32 tmp = 0;
  5757. if (amount <= 0)
  5758. return 0;
  5759. //plat
  5760. if (amount >= 1000000) {
  5761. tmp = amount / 1000000;
  5762. int32 bank_coins_plat = GetPlayer()->GetBankCoinsPlat();
  5763. if (bank_coins_plat >= tmp)
  5764. return 1;
  5765. }
  5766. //gold
  5767. if (amount >= 10000) {
  5768. tmp = amount / 10000;
  5769. int32 bank_coins_gold = GetPlayer()->GetBankCoinsGold();
  5770. if (bank_coins_gold >= tmp)
  5771. return 1;
  5772. }
  5773. //silver
  5774. if (amount >= 100) {
  5775. tmp = amount / 100;
  5776. int32 bank_coins_silver = GetPlayer()->GetBankCoinsSilver();
  5777. if (bank_coins_silver >= tmp)
  5778. return 1;
  5779. }
  5780. //copper
  5781. if (amount > 0) {
  5782. int32 bank_coins_copper = GetPlayer()->GetBankCoinsCopper();
  5783. if (bank_coins_copper >= amount)
  5784. return 1;
  5785. }
  5786. return 0;
  5787. }
  5788. bool Client::BankWithdrawalNoBanker(int64 amount) {
  5789. bool cheater = false;
  5790. if (amount > 0) {
  5791. string withdrawal = "";
  5792. char withdrawal_data[512] = { 0 };
  5793. int32 tmp = 0;
  5794. if (amount >= 1000000) {
  5795. tmp = amount / 1000000;
  5796. int32 bank_coins_plat = GetPlayer()->GetBankCoinsPlat();
  5797. if (tmp > bank_coins_plat)
  5798. cheater = true;
  5799. else {
  5800. GetPlayer()->GetInfoStruct()->set_bank_coin_plat(bank_coins_plat - tmp);
  5801. GetPlayer()->GetInfoStruct()->add_coin_plat(tmp);
  5802. amount -= (int64)tmp * 1000000;
  5803. sprintf(withdrawal_data, "%u Platinum ", tmp);
  5804. withdrawal.append(withdrawal_data);
  5805. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5806. }
  5807. }
  5808. if (!cheater && amount >= 10000) {
  5809. tmp = amount / 10000;
  5810. if (tmp > GetPlayer()->GetBankCoinsGold())
  5811. cheater = true;
  5812. else {
  5813. int32 bank_coins_gold = GetPlayer()->GetInfoStruct()->get_bank_coin_gold();
  5814. bank_coins_gold -= tmp;
  5815. if ((GetPlayer()->GetInfoStruct()->get_coin_gold() + tmp) > 100) {
  5816. GetPlayer()->GetInfoStruct()->set_coin_gold((GetPlayer()->GetInfoStruct()->get_coin_gold() + tmp) - 100);
  5817. GetPlayer()->GetInfoStruct()->add_coin_plat(1);
  5818. }
  5819. else
  5820. GetPlayer()->GetInfoStruct()->add_coin_gold(tmp);
  5821. amount -= tmp * 10000;
  5822. sprintf(withdrawal_data, "%u Gold ", tmp);
  5823. withdrawal.append(withdrawal_data);
  5824. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5825. }
  5826. }
  5827. if (!cheater && amount >= 100) {
  5828. tmp = amount / 100;
  5829. int32 bank_coins_silver = GetPlayer()->GetBankCoinsSilver();
  5830. if (tmp > bank_coins_silver)
  5831. cheater = true;
  5832. else {
  5833. GetPlayer()->GetInfoStruct()->set_bank_coin_silver(bank_coins_silver - tmp);
  5834. if ((GetPlayer()->GetInfoStruct()->get_coin_silver() + tmp) > 100) {
  5835. GetPlayer()->GetInfoStruct()->set_coin_silver((GetPlayer()->GetInfoStruct()->get_coin_silver() + tmp) - 100);
  5836. GetPlayer()->GetInfoStruct()->add_coin_gold(1);
  5837. }
  5838. else
  5839. GetPlayer()->GetInfoStruct()->add_coin_silver(tmp);
  5840. amount -= tmp * 100;
  5841. sprintf(withdrawal_data, "%u Silver ", tmp);
  5842. withdrawal.append(withdrawal_data);
  5843. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5844. }
  5845. }
  5846. if (!cheater) {
  5847. if (amount > 0) {
  5848. sprintf(withdrawal_data, "%u Copper ", (int32)amount);
  5849. withdrawal.append(withdrawal_data);
  5850. int32 bank_coin_copper = GetPlayer()->GetInfoStruct()->get_bank_coin_copper();
  5851. GetPlayer()->GetInfoStruct()->set_bank_coin_copper(bank_coin_copper - amount);
  5852. if ((GetPlayer()->GetInfoStruct()->get_coin_copper() + amount) > 100) {
  5853. GetPlayer()->GetInfoStruct()->set_coin_copper((GetPlayer()->GetInfoStruct()->get_coin_copper() + amount) - 100);
  5854. GetPlayer()->GetInfoStruct()->add_coin_silver(1);
  5855. }
  5856. else
  5857. GetPlayer()->GetInfoStruct()->add_coin_copper(amount);
  5858. }
  5859. if (withdrawal.length() > 0) {
  5860. withdrawal.append("withdrawn ");
  5861. sprintf(withdrawal_data, "(%u Plat %u Gold %u Silver %u Copper in the bank now.)", GetPlayer()->GetInfoStruct()->get_bank_coin_plat(),
  5862. GetPlayer()->GetInfoStruct()->get_bank_coin_gold(), GetPlayer()->GetInfoStruct()->get_bank_coin_silver(), GetPlayer()->GetInfoStruct()->get_bank_coin_copper());
  5863. withdrawal.append(withdrawal_data);
  5864. SimpleMessage(CHANNEL_NARRATIVE, withdrawal.c_str());
  5865. return 1;
  5866. }
  5867. }
  5868. else
  5869. Message(CHANNEL_COLOR_RED, "Stop trying to cheat!");
  5870. return 0;
  5871. }
  5872. return 0;
  5873. }
  5874. void Client::BankWithdrawal(int64 amount) {
  5875. bool cheater = false;
  5876. if (GetBanker() && amount > 0) {
  5877. string withdrawal = "";
  5878. char withdrawal_data[512] = { 0 };
  5879. int32 tmp = 0;
  5880. if (amount >= 1000000) {
  5881. tmp = amount / 1000000;
  5882. int32 bank_coins_plat = GetPlayer()->GetBankCoinsPlat();
  5883. if (tmp > bank_coins_plat)
  5884. cheater = true;
  5885. else {
  5886. GetPlayer()->GetInfoStruct()->set_bank_coin_plat(bank_coins_plat - tmp);
  5887. GetPlayer()->GetInfoStruct()->add_coin_plat(tmp);
  5888. amount -= (int64)tmp * 1000000;
  5889. sprintf(withdrawal_data, "%u Platinum ", tmp);
  5890. withdrawal.append(withdrawal_data);
  5891. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5892. }
  5893. }
  5894. if (!cheater && amount >= 10000) {
  5895. tmp = amount / 10000;
  5896. if (tmp > GetPlayer()->GetBankCoinsGold())
  5897. cheater = true;
  5898. else {
  5899. int32 bank_coins_gold = GetPlayer()->GetInfoStruct()->get_bank_coin_gold();
  5900. bank_coins_gold -= tmp;
  5901. if ((GetPlayer()->GetInfoStruct()->get_coin_gold() + tmp) > 100) {
  5902. GetPlayer()->GetInfoStruct()->set_coin_gold((GetPlayer()->GetInfoStruct()->get_coin_gold() + tmp) - 100);
  5903. GetPlayer()->GetInfoStruct()->add_coin_plat(1);
  5904. }
  5905. else
  5906. GetPlayer()->GetInfoStruct()->add_coin_gold(tmp);
  5907. amount -= tmp * 10000;
  5908. sprintf(withdrawal_data, "%u Gold ", tmp);
  5909. withdrawal.append(withdrawal_data);
  5910. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5911. }
  5912. }
  5913. if (!cheater && amount >= 100) {
  5914. tmp = amount / 100;
  5915. int32 bank_coins_silver = GetPlayer()->GetBankCoinsSilver();
  5916. if (tmp > bank_coins_silver)
  5917. cheater = true;
  5918. else {
  5919. GetPlayer()->GetInfoStruct()->set_bank_coin_silver(bank_coins_silver - tmp);
  5920. if ((GetPlayer()->GetInfoStruct()->get_coin_silver() + tmp) > 100) {
  5921. GetPlayer()->GetInfoStruct()->set_coin_silver((GetPlayer()->GetInfoStruct()->get_coin_silver() + tmp) - 100);
  5922. GetPlayer()->GetInfoStruct()->add_coin_gold(1);
  5923. }
  5924. else
  5925. GetPlayer()->GetInfoStruct()->add_coin_silver(tmp);
  5926. amount -= tmp * 100;
  5927. sprintf(withdrawal_data, "%u Silver ", tmp);
  5928. withdrawal.append(withdrawal_data);
  5929. memset(withdrawal_data, 0, sizeof(withdrawal_data));
  5930. }
  5931. }
  5932. if (!cheater) {
  5933. if (amount > 0) {
  5934. sprintf(withdrawal_data, "%u Copper ", (int32)amount);
  5935. withdrawal.append(withdrawal_data);
  5936. int32 bank_coin_copper = GetPlayer()->GetInfoStruct()->get_bank_coin_copper();
  5937. GetPlayer()->GetInfoStruct()->set_bank_coin_copper(bank_coin_copper - amount);
  5938. if ((GetPlayer()->GetInfoStruct()->get_coin_copper() + amount) > 100) {
  5939. GetPlayer()->GetInfoStruct()->set_coin_copper((GetPlayer()->GetInfoStruct()->get_coin_copper() + amount) - 100);
  5940. GetPlayer()->GetInfoStruct()->add_coin_silver(1);
  5941. }
  5942. else
  5943. GetPlayer()->GetInfoStruct()->add_coin_copper(amount);
  5944. }
  5945. if (withdrawal.length() > 0) {
  5946. withdrawal.append("withdrawn ");
  5947. sprintf(withdrawal_data, "(%u Plat %u Gold %u Silver %u Copper in the bank now.)", GetPlayer()->GetInfoStruct()->get_bank_coin_plat(),
  5948. GetPlayer()->GetInfoStruct()->get_bank_coin_gold(), GetPlayer()->GetInfoStruct()->get_bank_coin_silver(), GetPlayer()->GetInfoStruct()->get_bank_coin_copper());
  5949. withdrawal.append(withdrawal_data);
  5950. SimpleMessage(CHANNEL_NARRATIVE, withdrawal.c_str());
  5951. }
  5952. }
  5953. else
  5954. Message(CHANNEL_COLOR_RED, "Stop trying to cheat!");
  5955. player->SetCharSheetChanged(true);
  5956. Bank(banker);
  5957. }
  5958. }
  5959. void Client::BankDeposit(int64 amount) {
  5960. bool cheater = false;
  5961. if (GetBanker() && amount > 0) {
  5962. int32 tmp = 0;
  5963. char deposit_data[512] = { 0 };
  5964. string deposit = "";
  5965. if (amount >= 1000000) {
  5966. tmp = amount / 1000000;
  5967. if (tmp > GetPlayer()->GetCoinsPlat())
  5968. cheater = true;
  5969. else {
  5970. GetPlayer()->GetInfoStruct()->add_bank_coin_plat(tmp);
  5971. GetPlayer()->GetInfoStruct()->set_coin_plat(GetPlayer()->GetInfoStruct()->get_coin_plat() - tmp);
  5972. amount -= (int64)tmp * 1000000;
  5973. sprintf(deposit_data, "%u Platinum ", tmp);
  5974. deposit.append(deposit_data);
  5975. memset(deposit_data, 0, sizeof(deposit_data));
  5976. }
  5977. }
  5978. if (!cheater && amount >= 10000) {
  5979. tmp = amount / 10000;
  5980. if (tmp > GetPlayer()->GetCoinsGold())
  5981. cheater = true;
  5982. else {
  5983. if ((GetPlayer()->GetInfoStruct()->get_bank_coin_gold() + tmp) > 100) {
  5984. GetPlayer()->GetInfoStruct()->set_bank_coin_gold((GetPlayer()->GetInfoStruct()->get_bank_coin_gold() + tmp) - 100);
  5985. GetPlayer()->GetInfoStruct()->add_bank_coin_plat(1);
  5986. }
  5987. else
  5988. GetPlayer()->GetInfoStruct()->add_bank_coin_gold(tmp);
  5989. GetPlayer()->GetInfoStruct()->set_coin_gold(GetPlayer()->GetInfoStruct()->get_coin_gold() - tmp);
  5990. amount -= tmp * 10000;
  5991. sprintf(deposit_data, "%u Gold ", tmp);
  5992. deposit.append(deposit_data);
  5993. memset(deposit_data, 0, sizeof(deposit_data));
  5994. }
  5995. }
  5996. if (!cheater && amount >= 100) {
  5997. tmp = amount / 100;
  5998. if (tmp > GetPlayer()->GetCoinsSilver())
  5999. cheater = true;
  6000. else {
  6001. if ((GetPlayer()->GetInfoStruct()->get_bank_coin_silver() + tmp) > 100) {
  6002. GetPlayer()->GetInfoStruct()->set_bank_coin_silver((GetPlayer()->GetInfoStruct()->get_bank_coin_silver() + tmp) - 100);
  6003. GetPlayer()->GetInfoStruct()->add_bank_coin_gold(1);
  6004. }
  6005. else
  6006. GetPlayer()->GetInfoStruct()->add_bank_coin_silver(tmp);
  6007. GetPlayer()->GetInfoStruct()->set_coin_silver(GetPlayer()->GetInfoStruct()->get_coin_silver() - tmp);
  6008. amount -= tmp * 100;
  6009. sprintf(deposit_data, "%u Silver ", tmp);
  6010. deposit.append(deposit_data);
  6011. memset(deposit_data, 0, sizeof(deposit_data));
  6012. }
  6013. }
  6014. if (!cheater) {
  6015. if (amount > 0) {
  6016. sprintf(deposit_data, "%u Copper ", (int32)amount);
  6017. deposit.append(deposit_data);
  6018. if ((GetPlayer()->GetInfoStruct()->get_bank_coin_copper() + amount) > 100) {
  6019. GetPlayer()->GetInfoStruct()->set_bank_coin_copper((GetPlayer()->GetInfoStruct()->get_bank_coin_copper() + amount) - 100);
  6020. GetPlayer()->GetInfoStruct()->add_bank_coin_silver(1);
  6021. }
  6022. else
  6023. GetPlayer()->GetInfoStruct()->add_bank_coin_copper(amount);
  6024. GetPlayer()->GetInfoStruct()->set_coin_copper(GetPlayer()->GetInfoStruct()->get_coin_copper() - amount);
  6025. }
  6026. if (deposit.length() > 0) {
  6027. deposit.append("deposited ");
  6028. sprintf(deposit_data, "(%u Plat %u Gold %u Silver %u Copper in the bank now.)", GetPlayer()->GetInfoStruct()->get_bank_coin_plat(),
  6029. GetPlayer()->GetInfoStruct()->get_bank_coin_gold(), GetPlayer()->GetInfoStruct()->get_bank_coin_silver(), GetPlayer()->GetInfoStruct()->get_bank_coin_copper());
  6030. deposit.append(deposit_data);
  6031. SimpleMessage(CHANNEL_NARRATIVE, deposit.c_str());
  6032. }
  6033. }
  6034. else
  6035. Message(CHANNEL_COLOR_RED, "Stop trying to cheat!");
  6036. player->SetCharSheetChanged(true);
  6037. Bank(banker);
  6038. }
  6039. }
  6040. void Client::AddPendingQuestAcceptReward(Quest* quest)
  6041. {
  6042. std::unique_lock lock(MPendingQuestAccept);
  6043. pending_quest_accept.push_back(quest->GetQuestID());
  6044. }
  6045. void Client::AddPendingQuestReward(Quest* quest, bool update, bool is_temporary, std::string description) {
  6046. QueueQuestReward(quest->GetQuestID(), is_temporary, false, false, (is_temporary ? quest->GetCoinTmpReward() : 0),
  6047. (is_temporary ? quest->GetStatusTmpReward() : 0), description, false, 0);
  6048. quest_updates = update;
  6049. if (quest_updates) {
  6050. SaveQuestRewardData(true);
  6051. }
  6052. }
  6053. void Client::QueueQuestReward(int32 quest_id, bool is_temporary, bool is_collection, bool has_displayed, int64 tmp_coin, int32 tmp_status, std::string description, bool db_saved, int32 index) {
  6054. if (HasQuestRewardQueued(quest_id, is_temporary, is_collection))
  6055. return;
  6056. QuestRewardData* data = new QuestRewardData;
  6057. data->quest_id = quest_id;
  6058. data->is_temporary = is_temporary;
  6059. data->is_collection = is_collection;
  6060. data->has_displayed = has_displayed;
  6061. data->tmp_coin = tmp_coin;
  6062. data->tmp_status = tmp_status;
  6063. data->description = std::string(description);
  6064. data->db_saved = db_saved;
  6065. data->db_index = index;
  6066. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  6067. quest_pending_reward.push_back(data);
  6068. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  6069. }
  6070. bool Client::HasQuestRewardQueued(int32 quest_id, bool is_temporary, bool is_collection) {
  6071. bool success = false;
  6072. MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
  6073. if (quest_pending_reward.size() > 0) {
  6074. vector<QuestRewardData*>::iterator itr;
  6075. for (itr = quest_pending_reward.begin(); itr != quest_pending_reward.end(); itr++) {
  6076. int32 questID = (*itr)->quest_id;
  6077. bool temporary = (*itr)->is_temporary;
  6078. bool collection = (*itr)->is_collection;
  6079. if (questID == quest_id && is_temporary == temporary && is_collection == collection) {
  6080. success = true;
  6081. break;
  6082. }
  6083. }
  6084. }
  6085. MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
  6086. return success;
  6087. }
  6088. void Client::RemoveQueuedQuestReward() {
  6089. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  6090. if (quest_pending_reward.size() > 0) {
  6091. QuestRewardData* data = quest_pending_reward.at(0);
  6092. if (data->db_saved) {
  6093. Query query;
  6094. query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete FROM character_quest_rewards where char_id = %u and indexed = %u", GetCharacterID(), data->db_index);
  6095. if (data->is_temporary && data->quest_id) {
  6096. query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete FROM character_quest_temporary_rewards where char_id = %u and quest_id = %u", GetCharacterID(), data->quest_id);
  6097. }
  6098. }
  6099. quest_pending_reward.erase(quest_pending_reward.begin());
  6100. }
  6101. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  6102. SaveQuestRewardData(true);
  6103. }
  6104. void Client::AddPendingQuestUpdate(int32 quest_id, int32 step_id, int32 progress) {
  6105. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  6106. quest_pending_updates[quest_id][step_id] = progress;
  6107. quest_updates = true;
  6108. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  6109. }
  6110. void Client::ProcessQuestUpdates() {
  6111. if (!IsReadyForUpdates())
  6112. return;
  6113. if (quest_pending_updates.size() > 0) {
  6114. map<int32, map<int32, int32> > tmp_quest_updates;
  6115. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  6116. tmp_quest_updates.insert(quest_pending_updates.begin(), quest_pending_updates.end());
  6117. quest_pending_updates.clear();
  6118. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  6119. map<int32, map<int32, int32> >::iterator quest_itr;
  6120. map<int32, int32>::iterator step_itr;
  6121. for (quest_itr = tmp_quest_updates.begin(); quest_itr != tmp_quest_updates.end(); quest_itr++) {
  6122. for (step_itr = quest_itr->second.begin(); step_itr != quest_itr->second.end(); step_itr++) {
  6123. if (step_itr->second == 0xFFFFFFFF) {
  6124. SetStepComplete(quest_itr->first, step_itr->first);
  6125. player->SendQuestRequiredSpawns(quest_itr->first);
  6126. }
  6127. else
  6128. AddStepProgress(quest_itr->first, step_itr->first, step_itr->second);
  6129. }
  6130. }
  6131. }
  6132. MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
  6133. if (quest_pending_reward.size() > 0) {
  6134. MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
  6135. // only able to display one reward at a time
  6136. if (GetPlayer()->IsActiveReward())
  6137. return;
  6138. Query query;
  6139. vector<QuestRewardData*>::iterator itr;
  6140. vector<QuestRewardData*> tmp_quest_rewards;
  6141. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  6142. tmp_quest_rewards.insert(tmp_quest_rewards.begin(), quest_pending_reward.begin(), quest_pending_reward.begin() + 1);
  6143. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  6144. bool delete_first = false;
  6145. for (itr = tmp_quest_rewards.begin(); itr != tmp_quest_rewards.end();) {
  6146. int32 questID = (*itr)->quest_id;
  6147. if ((*itr)->is_collection && GetPlayer()->GetPendingCollectionReward()) {
  6148. DisplayCollectionComplete(GetPlayer()->GetPendingCollectionReward());
  6149. GetPlayer()->SetActiveReward(true);
  6150. (*itr)->has_displayed = true;
  6151. UpdateCharacterRewardData((*itr));
  6152. break;
  6153. }
  6154. else if (questID > 0 && GetPlayer()->UpdateQuestReward(questID, (*itr))) {
  6155. (*itr)->has_displayed = true;
  6156. UpdateCharacterRewardData((*itr));
  6157. // only able to display one reward at a time
  6158. break;
  6159. }
  6160. else {
  6161. delete_first = true;
  6162. LogWrite(CCLIENT__ERROR, 0, "Client", "Quest ID %u missing for Player %s, deleting quest id from tmp_quest_rewards.", questID, GetPlayer()->GetName());
  6163. break;
  6164. }
  6165. }
  6166. if (delete_first) {
  6167. RemoveQueuedQuestReward();
  6168. }
  6169. }
  6170. else {
  6171. MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
  6172. }
  6173. MQuestPendingUpdates.readlock(__FUNCTION__, __LINE__);
  6174. if (quest_pending_reward.size() > 0) {
  6175. quest_updates = true;
  6176. }
  6177. else {
  6178. quest_updates = false;
  6179. }
  6180. MQuestPendingUpdates.releasereadlock(__FUNCTION__, __LINE__);
  6181. }
  6182. void Client::CheckQuestQueue() {
  6183. MQuestQueue.writelock();
  6184. last_update_time = 0;
  6185. vector<QueuedQuest*>::iterator itr;
  6186. for (itr = quest_queue.begin(); itr != quest_queue.end(); itr++) {
  6187. if (!GetPlayer()->SendQuestStepUpdate((*itr)->quest_id, (*itr)->step, (*itr)->display_quest_helper)) {
  6188. LogWrite(CCLIENT__ERROR, 0, "Client", "Queued Quest ID %u missing for Player %s, cannot send quest step update.", (*itr)->quest_id, GetPlayer()->GetName());
  6189. }
  6190. safe_delete((*itr));
  6191. }
  6192. quest_queue.clear();
  6193. MQuestQueue.releasewritelock();
  6194. }
  6195. void Client::SetStepComplete(int32 quest_id, int32 step) {
  6196. Quest* quest = player->SetStepComplete(quest_id, step);
  6197. if (quest) {
  6198. SendQuestUpdate(quest);
  6199. GetCurrentZone()->SendQuestUpdates(this);
  6200. }
  6201. }
  6202. void Client::AddStepProgress(int32 quest_id, int32 step, int32 progress) {
  6203. Quest* quest = player->AddStepProgress(quest_id, step, progress);
  6204. if (quest) {
  6205. SendQuestUpdate(quest);
  6206. GetCurrentZone()->SendQuestUpdates(this);
  6207. }
  6208. }
  6209. void Client::CheckPlayerQuestsKillUpdate(Spawn* spawn) {
  6210. bool hadUpdates = false;
  6211. vector<Quest*>* quest_updates = player->CheckQuestsKillUpdate(spawn);
  6212. if (quest_updates) {
  6213. for (int32 i = 0; i < quest_updates->size(); i++)
  6214. {
  6215. SendQuestUpdate(quest_updates->at(i));
  6216. hadUpdates = true;
  6217. }
  6218. }
  6219. safe_delete(quest_updates);
  6220. vector<Quest*>* quest_failures = player->CheckQuestsFailures();
  6221. if (quest_failures) {
  6222. for (int32 i = 0; i < quest_failures->size(); i++)
  6223. {
  6224. SendQuestFailure(quest_failures->at(i));
  6225. hadUpdates = true;
  6226. }
  6227. }
  6228. safe_delete(quest_failures);
  6229. if (hadUpdates)
  6230. GetCurrentZone()->SendAllSpawnsForVisChange(this);
  6231. }
  6232. void Client::CheckPlayerQuestsChatUpdate(Spawn* spawn) {
  6233. vector<Quest*>* quest_updates = player->CheckQuestsChatUpdate(spawn);
  6234. if (quest_updates) {
  6235. for (int32 i = 0; i < quest_updates->size(); i++)
  6236. SendQuestUpdate(quest_updates->at(i));
  6237. GetCurrentZone()->SendQuestUpdates(this);
  6238. }
  6239. safe_delete(quest_updates);
  6240. }
  6241. void Client::CheckPlayerQuestsItemUpdate(Item* item) {
  6242. vector<Quest*>* quest_updates = player->CheckQuestsItemUpdate(item);
  6243. if (quest_updates) {
  6244. for (int32 i = 0; i < quest_updates->size(); i++)
  6245. SendQuestUpdate(quest_updates->at(i));
  6246. }
  6247. safe_delete(quest_updates);
  6248. vector<Quest*>* quest_failures = player->CheckQuestsFailures();
  6249. if (quest_failures) {
  6250. for (int32 i = 0; i < quest_failures->size(); i++)
  6251. SendQuestFailure(quest_failures->at(i));
  6252. }
  6253. safe_delete(quest_failures);
  6254. }
  6255. void Client::CheckPlayerQuestsLocationUpdate() {
  6256. vector<Quest*>* quest_updates = player->CheckQuestsLocationUpdate();
  6257. if (quest_updates) {
  6258. for (int32 i = 0; i < quest_updates->size(); i++)
  6259. SendQuestUpdate(quest_updates->at(i));
  6260. }
  6261. safe_delete(quest_updates);
  6262. }
  6263. void Client::CheckPlayerQuestsSpellUpdate(Spell* spell) {
  6264. vector<Quest*>* quest_updates = player->CheckQuestsSpellUpdate(spell);
  6265. if (quest_updates) {
  6266. for (int32 i = 0; i < quest_updates->size(); i++)
  6267. SendQuestUpdate(quest_updates->at(i));
  6268. }
  6269. safe_delete(quest_updates);
  6270. vector<Quest*>* quest_failures = player->CheckQuestsFailures();
  6271. if (quest_failures) {
  6272. for (int32 i = 0; i < quest_failures->size(); i++)
  6273. SendQuestFailure(quest_failures->at(i));
  6274. }
  6275. safe_delete(quest_failures);
  6276. }
  6277. void Client::AddPendingQuest(Quest* quest, bool forced) {
  6278. if (version <= 372 || forced) { //this client doesn't ask if you want the quest, so auto accept
  6279. MPendingQuestAccept.lock();
  6280. player->pending_quests[quest->GetQuestID()] = quest;
  6281. MPendingQuestAccept.unlock();
  6282. AcceptQuest(quest->GetQuestID());
  6283. }
  6284. else {
  6285. MPendingQuestAccept.lock();
  6286. player->pending_quests[quest->GetQuestID()] = quest;
  6287. MPendingQuestAccept.unlock();
  6288. EQ2Packet* outapp = quest->OfferQuest(GetVersion(), player);
  6289. //DumpPacket(outapp);
  6290. QueuePacket(outapp);
  6291. }
  6292. }
  6293. void Client::AcceptQuest(int32 quest_id) {
  6294. MPendingQuestAccept.lock_shared();
  6295. if (player->pending_quests.count(quest_id) > 0) {
  6296. Quest* quest = player->pending_quests[quest_id];
  6297. if (quest) {
  6298. MPendingQuestAccept.unlock_shared();
  6299. MPendingQuestAccept.lock();
  6300. player->pending_quests.erase(quest->GetQuestID());
  6301. MPendingQuestAccept.unlock();
  6302. AddPlayerQuest(quest);
  6303. GetCurrentZone()->SendQuestUpdates(this);
  6304. GetPlayer()->UpdateQuestCompleteCount(quest_id);
  6305. return; // already unlocked mutex
  6306. }
  6307. }
  6308. MPendingQuestAccept.unlock_shared();
  6309. }
  6310. void Client::RemovePendingQuest(int32 quest_id) {
  6311. bool send_updates = false;
  6312. MPendingQuestAccept.lock_shared();
  6313. if (player->pending_quests.count(quest_id) > 0) {
  6314. Quest* quest = player->pending_quests[quest_id];
  6315. MPendingQuestAccept.unlock_shared();
  6316. MPendingQuestAccept.lock();
  6317. player->pending_quests.erase(quest_id);
  6318. MPendingQuestAccept.unlock();
  6319. if (lua_interface) {
  6320. lua_interface->CallQuestFunction(quest, "Declined", GetPlayer());
  6321. lua_interface->SetLuaUserDataStale(quest);
  6322. }
  6323. safe_delete(quest);
  6324. send_updates = true;
  6325. }
  6326. else {
  6327. MPendingQuestAccept.unlock_shared();
  6328. }
  6329. if (send_updates) {
  6330. GetCurrentZone()->SendQuestUpdates(this);
  6331. }
  6332. }
  6333. void Client::SetPlayerQuest(Quest* quest, map<int32, int32>* progress) {
  6334. if (!quest || !progress) {
  6335. return;
  6336. }
  6337. map<int32, int32>::iterator itr;
  6338. QuestStep* step = 0;
  6339. for (itr = progress->begin(); itr != progress->end(); itr++) {
  6340. step = quest->GetQuestStep(itr->first);
  6341. if (step && itr->second > 0) {
  6342. step->SetStepProgress(itr->second);
  6343. if (lua_interface && step->GetQuestCurrentQuantity() >= step->GetQuestNeededQuantity())
  6344. lua_interface->CallQuestFunction(quest, "Reload", player, step->GetStepID());
  6345. }
  6346. }
  6347. if (lua_interface && step)
  6348. lua_interface->CallQuestFunction(quest, "CurrentStep", player, step->GetStepID());
  6349. else if (!step) {
  6350. LogWrite(QUEST__ERROR, 0, "Client", "Missing step for quest %s (ID %u), cannot CallQuestFunction for CurrentStep", quest->GetName(), quest->GetQuestID());
  6351. }
  6352. }
  6353. void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets) {
  6354. bool lockCleared = false;
  6355. GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
  6356. if (player->player_quests.count(quest->GetQuestID()) > 0 && player->player_quests[quest->GetQuestID()]) {
  6357. if (player->player_quests[quest->GetQuestID()]->GetQuestFlags() > 0)
  6358. quest->SetQuestFlags(player->player_quests[quest->GetQuestID()]->GetQuestFlags());
  6359. int32 questID = quest->GetQuestID();
  6360. lockCleared = true;
  6361. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  6362. RemovePlayerQuest(questID, false, false);
  6363. }
  6364. player->player_quests[quest->GetQuestID()] = quest;
  6365. if (!lockCleared)
  6366. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  6367. quest->SetPlayer(player);
  6368. quest->SetSaveNeeded(true);
  6369. current_quest_id = quest->GetQuestID();
  6370. if (send_packets && quest->GetQuestGiver() > 0)
  6371. GetCurrentZone()->SendSpawnChangesByDBID(quest->GetQuestGiver(), this, false, true);
  6372. if (lua_interface && call_accepted)
  6373. lua_interface->CallQuestFunction(quest, "Accepted", player);
  6374. if (send_packets) {
  6375. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6376. //SendQuestJournal();
  6377. SendQuestJournalUpdate(quest);
  6378. // sent twice to match live
  6379. quest->SetTracked(false);
  6380. QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player));
  6381. quest->SetTracked(true);
  6382. QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player));
  6383. GetCurrentZone()->SendAllSpawnsForVisChange(this);
  6384. }
  6385. //This isn't during a load screen, so update spawns with required quests
  6386. if (call_accepted)
  6387. player->SendQuestRequiredSpawns(quest->GetQuestID());
  6388. }
  6389. void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) {
  6390. if (current_quest_id == id)
  6391. current_quest_id = 0;
  6392. GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
  6393. if (player->player_quests.count(id) > 0 && player->player_quests[id]) {
  6394. if (delete_quest) {
  6395. player->player_quests[id]->SetDeleted(true);
  6396. database.DeleteCharacterQuest(id, GetCharacterID(), player->GetCompletedPlayerQuests()->count(id) > 0);
  6397. }
  6398. int32 quest_giver = player->player_quests[id]->GetQuestGiver();
  6399. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  6400. if (send_update && quest_giver > 0)
  6401. GetCurrentZone()->SendSpawnChangesByDBID(quest_giver, this, false, true);
  6402. if (send_update) {
  6403. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6404. SendQuestJournal(false, 0, true);
  6405. }
  6406. player->RemoveQuest(id, delete_quest);
  6407. if (send_update) {
  6408. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6409. SendQuestJournal(false, 0, true);
  6410. GetCurrentZone()->SendAllSpawnsForVisChange(this);
  6411. }
  6412. }
  6413. else {
  6414. // if we don't have any quests to count then release the write lock
  6415. GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
  6416. }
  6417. }
  6418. void Client::SendQuestUpdateStepImmediately(Quest* quest, int32 step, bool display_quest_helper) {
  6419. if (quest) {
  6420. QuestStep* quest_step = quest->GetQuestStep(step);
  6421. if (quest_step) {
  6422. QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, quest_step, 1, false, false, display_quest_helper));
  6423. quest_step->WasUpdated(false);
  6424. }
  6425. }
  6426. }
  6427. void Client::SendQuestUpdateStep(Quest* quest, int32 step, bool display_quest_helper) {
  6428. QueuedQuest* item = new QueuedQuest;
  6429. item->quest_id = quest->GetQuestID();
  6430. item->step = step;
  6431. item->display_quest_helper = display_quest_helper;
  6432. MQuestQueue.writelock();
  6433. quest_queue.push_back(item);
  6434. last_update_time = Timer::GetCurrentTime2();
  6435. MQuestQueue.releasewritelock();
  6436. }
  6437. void Client::SendQuestFailure(Quest* quest) {
  6438. vector<QuestStep*>* failures = quest->GetQuestFailures();
  6439. if (failures) {
  6440. QuestStep* step = 0;
  6441. for (int32 i = 0; i < failures->size(); i++) {
  6442. step = failures->at(i);
  6443. QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, step, 1, false, true));
  6444. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6445. SendQuestJournal(false, 0, true);
  6446. }
  6447. failures->clear();
  6448. }
  6449. }
  6450. void Client::SendQuestUpdate(Quest* quest) {
  6451. vector<QuestStep*>* updates = quest->GetQuestUpdates();
  6452. if (updates) {
  6453. QuestStep* step = 0;
  6454. bool updated = false;
  6455. for (int32 i = 0; i < updates->size(); i++) {
  6456. step = updates->at(i);
  6457. if (lua_interface && step->Complete() && quest->GetCompleteAction(step->GetStepID()))
  6458. {
  6459. lua_interface->CallQuestFunction(quest, quest->GetCompleteAction(step->GetStepID()), player);
  6460. SendQuestUpdateStep(quest, step->GetStepID());
  6461. updated = true;
  6462. }
  6463. if (step->WasUpdated()) {
  6464. // reversing the order of SendQuestJournal and QueuePacket QuestJournalReply causes AoM client to crash!
  6465. SendQuestJournal(false, 0, true);
  6466. if (!updated)
  6467. QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, step));
  6468. updated = true;
  6469. }
  6470. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6471. }
  6472. if (lua_interface && quest->GetCompleted() && quest->GetCompleteAction()) {
  6473. lua_interface->CallQuestFunction(quest, quest->GetCompleteAction(), player);
  6474. SendQuestJournalUpdate(quest, true);
  6475. }
  6476. if (quest->GetCompleted()) {
  6477. if (quest->GetQuestReturnNPC() > 0)
  6478. GetCurrentZone()->SendSpawnChangesByDBID(quest->GetQuestReturnNPC(), this, false, true);
  6479. if (quest->GetCompletedFlag())
  6480. quest->SetCompletedFlag(false);
  6481. }
  6482. updates->clear();
  6483. }
  6484. }
  6485. void Client::SendQuestJournal(bool all_quests, Client* client, bool updated) {
  6486. if (!client)
  6487. client = this;
  6488. PacketStruct* packet = player->GetQuestJournalPacket(all_quests, GetVersion(), GetNameCRC(), current_quest_id, updated);
  6489. if (packet) {
  6490. EQ2Packet* outapp = packet->serialize();
  6491. //DumpPacket(outapp);
  6492. client->QueuePacket(outapp);
  6493. safe_delete(packet);
  6494. }
  6495. }
  6496. void Client::SendQuestJournalUpdate(Quest* quest, bool updated) {
  6497. PacketStruct* packet = player->GetQuestJournalPacket(quest, GetVersion(), GetNameCRC(), updated);
  6498. if (packet) {
  6499. QueuePacket(packet->serialize());
  6500. safe_delete(packet);
  6501. }
  6502. }
  6503. void Client::ReloadQuests() {
  6504. vector<int32> ids = player->GetQuestIDs();
  6505. Quest* quest = 0;
  6506. for (int32 i = 0; i < ids.size(); i++) {
  6507. quest = master_quest_list.GetQuest(ids[i]);
  6508. if (quest)
  6509. AddPlayerQuest(quest, false);
  6510. else
  6511. RemovePlayerQuest(ids[i]);
  6512. }
  6513. }
  6514. Quest* Client::GetPendingQuestAcceptance(int32 item_id) {
  6515. std::unique_lock lock(MPendingQuestAccept);
  6516. bool found_quest = false;
  6517. vector<int32>::iterator itr;
  6518. int32 questID = 0;
  6519. Quest* quest = nullptr;
  6520. for (itr = pending_quest_accept.begin(); itr != pending_quest_accept.end();) {
  6521. questID = *itr;
  6522. bool quest_exists = false;
  6523. quest = GetPlayer()->PendingQuestAcceptance(questID, item_id, &quest_exists);
  6524. if (!quest_exists) {
  6525. LogWrite(CCLIENT__ERROR, 0, "Client", "Quest ID %u missing for Player %s, removing quest id from pending_quest_accept.", questID, GetPlayer()->GetName());
  6526. itr = pending_quest_accept.erase(itr);
  6527. quest = nullptr;
  6528. continue;
  6529. }
  6530. else if (quest) {
  6531. pending_quest_accept.erase(itr);
  6532. break;
  6533. }
  6534. itr++;
  6535. }
  6536. return quest;
  6537. }
  6538. void Client::AcceptQuestReward(Quest* quest, int32 item_id) {
  6539. int8 num_slots_needed = 0;
  6540. int16 free_slots = player->item_list.GetNumberOfFreeSlots();
  6541. Item* master_item = 0;
  6542. if (item_id > 0) {
  6543. num_slots_needed++;
  6544. master_item = master_item_list.GetItem(item_id);
  6545. }
  6546. int32 totalItems = 0;
  6547. vector<Item*>* items = 0;
  6548. vector<Item*>* tmpItems = 0;
  6549. bool isTempState = quest->GetQuestTemporaryState();
  6550. if (isTempState)
  6551. {
  6552. tmpItems = quest->GetTmpRewardItems();
  6553. if (tmpItems && tmpItems->size() > 0)
  6554. {
  6555. num_slots_needed += tmpItems->size();
  6556. totalItems += tmpItems->size();
  6557. }
  6558. }
  6559. else
  6560. {
  6561. items = quest->GetRewardItems();
  6562. if (items && items->size() > 0)
  6563. {
  6564. num_slots_needed += items->size();
  6565. totalItems += items->size();
  6566. }
  6567. }
  6568. RemoveQueuedQuestReward();
  6569. GetPlayer()->SetActiveReward(false);
  6570. if (free_slots >= num_slots_needed || (player->item_list.HasFreeBagSlot() && master_item && master_item->IsBag() && master_item->bag_info->num_slots >= totalItems)) {
  6571. if (master_item)
  6572. AddItem(item_id);
  6573. if (tmpItems && tmpItems->size() > 0) {
  6574. for (int32 i = 0; i < tmpItems->size(); i++)
  6575. AddItem(new Item(tmpItems->at(i)));
  6576. }
  6577. if (items && items->size() > 0) {
  6578. for (int32 i = 0; i < items->size(); i++)
  6579. AddItem(new Item(items->at(i)));
  6580. }
  6581. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  6582. if (outapp)
  6583. QueuePacket(outapp);
  6584. map<int32, sint32>* reward_factions = quest->GetRewardFactions();
  6585. map<int32, sint32>::iterator itr;
  6586. for (itr = reward_factions->begin(); itr != reward_factions->end(); itr++) {
  6587. int32 faction_id = itr->first;
  6588. sint32 amount = itr->second;
  6589. if (amount > 0)
  6590. player->GetFactions()->IncreaseFaction(faction_id, amount);
  6591. else
  6592. player->GetFactions()->DecreaseFaction(faction_id, (amount * -1));
  6593. }
  6594. if (quest->GetQuestTemporaryState())
  6595. {
  6596. int64 total_coins = quest->GetCoinTmpReward();
  6597. if (total_coins > 0)
  6598. AwardCoins(total_coins, std::string("for completing ").append(quest->GetName()));
  6599. player->GetInfoStruct()->add_status_points(quest->GetStatusTmpReward());
  6600. }
  6601. else {
  6602. player->GetInfoStruct()->add_status_points(quest->GetStatusPoints());
  6603. }
  6604. quest->SetQuestTemporaryState(false);
  6605. player->SetCharSheetChanged(true);
  6606. }
  6607. else {
  6608. GetPlayer()->SetActiveReward(true);
  6609. AddPendingQuestAcceptReward(quest);
  6610. SimpleMessage(CHANNEL_COLOR_RED, "You do not have enough free slots! Free some slots and try again.");
  6611. DisplayQuestComplete(quest, quest->GetQuestTemporaryState(), quest->GetQuestTemporaryDescription());
  6612. }
  6613. }
  6614. void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector<Item*>* rewards, vector<Item*>* selectable_rewards, map<int32, sint32>* factions, const char* header, int32 status_points, const char* text, bool was_displayed) {
  6615. if (coin == 0 && (!rewards || rewards->size() == 0) && (!selectable_rewards || selectable_rewards->size() == 0) && (!factions || factions->size() == 0) && status_points == 0 && text == 0 && (!quest || (quest->GetCoinsReward() == 0 && quest->GetCoinsRewardMax() == 0))) {
  6616. /*if (quest)
  6617. text = quest->GetName();
  6618. else*/
  6619. return;//nothing to give
  6620. }
  6621. GetPlayer()->ClearPendingSelectableItemRewards(0, true);
  6622. GetPlayer()->ClearPendingItemRewards();
  6623. PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", GetVersion());
  6624. if (packet2) {
  6625. int32 source_id = 0;
  6626. if (quest)
  6627. source_id = quest->GetQuestID();
  6628. int64 rewarded_coin = 0;
  6629. if (quest) {
  6630. if (quest->GetCoinsReward() > 0) {
  6631. if (quest->GetCoinsRewardMax() > 0)
  6632. rewarded_coin = MakeRandomInt(quest->GetCoinsReward(), quest->GetCoinsRewardMax());
  6633. else
  6634. rewarded_coin = quest->GetCoinsReward();
  6635. }
  6636. quest->SetGeneratedCoin(rewarded_coin);
  6637. }
  6638. if (rewarded_coin > coin)
  6639. coin = rewarded_coin;
  6640. if (!quest && !was_displayed) { //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6641. if (coin > 0) {
  6642. player->AddCoins(coin);
  6643. PlaySound("coin_cha_ching");
  6644. }
  6645. }
  6646. packet2->setSubstructDataByName("reward_data", "unknown1", 255);
  6647. packet2->setSubstructDataByName("reward_data", "reward", header);
  6648. packet2->setSubstructDataByName("reward_data", "max_coin", coin);
  6649. if (player->GetGuild() && !was_displayed) {
  6650. if (!quest) { //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6651. player->GetInfoStruct()->add_status_points(status_points);
  6652. player->SetCharSheetChanged(true);
  6653. }
  6654. packet2->setSubstructDataByName("reward_data", "status_points", status_points);
  6655. }
  6656. if (text)
  6657. packet2->setSubstructDataByName("reward_data", "text", text);
  6658. std::vector<Item*> items;
  6659. quest->GetTmpRewardItemsByID(&items);
  6660. if (rewards || items.size() > 0) {
  6661. int32 item_count = items.size();
  6662. item_count += rewards ? rewards->size() : 0;
  6663. packet2->setSubstructArrayLengthByName("reward_data", "num_rewards", item_count);
  6664. int i = 0;
  6665. if (rewards) {
  6666. for (i = 0; i < rewards->size(); i++) {
  6667. Item* item = rewards->at(i);
  6668. if (item) {
  6669. packet2->setArrayDataByName("reward_id", item->details.item_id, i);
  6670. packet2->setItemArrayDataByName("item", item, player, i, 0, GetClientItemPacketOffset());
  6671. if (!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6672. player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it
  6673. }
  6674. }
  6675. }
  6676. for (int j = 0; j < items.size(); j++) {
  6677. Item* item = items.at(j);
  6678. if (item) {
  6679. packet2->setArrayDataByName("reward_id", item->details.item_id, i);
  6680. packet2->setItemArrayDataByName("item", item, player, i, 0, GetClientItemPacketOffset());
  6681. if (!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6682. player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it
  6683. }
  6684. i++;
  6685. }
  6686. }
  6687. if (selectable_rewards) {
  6688. packet2->setSubstructArrayLengthByName("reward_data", "num_select_rewards", selectable_rewards->size());
  6689. for (int i = 0; i < selectable_rewards->size(); i++) {
  6690. Item* item = selectable_rewards->at(i);
  6691. if (item) {
  6692. packet2->setArrayDataByName("select_reward_id", item->details.item_id, i);
  6693. packet2->setItemArrayDataByName("select_item", item, player, i, 0, GetClientItemPacketOffset());
  6694. if (!quest) //this entire function is either for version <=561 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6695. player->AddPendingSelectableItemReward(source_id, item); //item reference will be deleted after the player selects one
  6696. }
  6697. }
  6698. }
  6699. if (factions) {
  6700. map<int32, sint32>::iterator itr;
  6701. map<Faction*, signed int> factions_map;
  6702. for (itr = factions->begin(); itr != factions->end(); itr++) {
  6703. Faction* faction = master_faction_list.GetFaction(itr->first);
  6704. if (faction)
  6705. factions_map[faction] = itr->second;
  6706. }
  6707. packet2->setSubstructArrayLengthByName("reward_data", "num_factions", factions_map.size());
  6708. map<Faction*, signed int>::iterator faction_itr;
  6709. int8 i = 0;
  6710. for (faction_itr = factions_map.begin(); faction_itr != factions_map.end(); faction_itr++) {
  6711. packet2->setArrayDataByName("faction_name", faction_itr->first->name.c_str(), i);
  6712. sint32 amount = faction_itr->second;
  6713. packet2->setArrayDataByName("amount", amount, i);
  6714. if (!quest) { //this entire function is for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards
  6715. if (amount > 0)
  6716. player->GetFactions()->IncreaseFaction(faction_itr->first->id, amount);
  6717. else
  6718. player->GetFactions()->DecreaseFaction(faction_itr->first->id, (amount * -1));
  6719. }
  6720. i++;
  6721. }
  6722. }
  6723. QueuePacket(packet2->serialize());
  6724. safe_delete(packet2);
  6725. }
  6726. }
  6727. void Client::PopulateQuestRewardItems(vector <Item*>* items, PacketStruct* packet,
  6728. std::string num_rewards_str, std::string reward_id_str, std::string item_str) {
  6729. if (!items || !packet)
  6730. return;
  6731. if (items) {
  6732. int32 total_item_count = 0;
  6733. for (int s = 0; s < items->size(); s++) {
  6734. Item* tmpItem = items->at(s);
  6735. if (tmpItem) {
  6736. if (tmpItem->details.count > 1) {
  6737. total_item_count += tmpItem->details.count;
  6738. }
  6739. else {
  6740. total_item_count += 1;
  6741. }
  6742. }
  6743. }
  6744. packet->setArrayLengthByName(num_rewards_str.c_str(), total_item_count);
  6745. int16 count = 0;
  6746. int16 pos = 0;
  6747. for (int32 i = 0; i < items->size();) {
  6748. packet->setArrayDataByName(reward_id_str.c_str(), items->at(i)->details.item_id, pos);
  6749. if (version < 860)
  6750. packet->setItemArrayDataByName(item_str.c_str(), items->at(i), player, pos, 0, GetClientItemPacketOffset());
  6751. else if (version < 1193)
  6752. packet->setItemArrayDataByName(item_str.c_str(), items->at(i), player, pos);
  6753. else
  6754. packet->setItemArrayDataByName(item_str.c_str(), items->at(i), player, pos, 0, 2);
  6755. pos++;
  6756. if (count >= items->at(i)->details.count - 1) {
  6757. count = 0;
  6758. }
  6759. else if (items->at(i)->details.count > 1) {
  6760. count++;
  6761. continue;
  6762. }
  6763. i++;
  6764. }
  6765. }
  6766. }
  6767. void Client::DisplayQuestComplete(Quest* quest, bool tempReward, std::string customDescription, bool was_displayed) {
  6768. if (!quest)
  6769. return;
  6770. if (GetVersion() <= 561) {
  6771. DisplayQuestRewards(quest, 0, quest->GetRewardItems(), quest->GetSelectableRewardItems(), quest->GetRewardFactions(), "Quest Complete!", quest->GetStatusPoints(), tempReward ? customDescription.c_str() : quest->GetCompletedDescription(), was_displayed);
  6772. return;
  6773. }
  6774. PacketStruct* packet = configReader.getStruct("WS_QuestComplete", GetVersion());
  6775. if (packet) {
  6776. packet->setDataByName("title", "Quest Reward!");
  6777. packet->setDataByName("name", quest->GetName());
  6778. if (tempReward)
  6779. {
  6780. packet->setDataByName("description", customDescription.c_str());
  6781. }
  6782. else
  6783. packet->setDataByName("description", quest->GetCompletedDescription());
  6784. packet->setDataByName("level", quest->GetLevel());
  6785. packet->setDataByName("encounter_level", quest->GetEncounterLevel());
  6786. int8 difficulty = 0;
  6787. if ((string)quest->GetType() == "Tradeskill")
  6788. difficulty = player->GetTSArrowColor(quest->GetLevel());
  6789. else
  6790. difficulty = player->GetArrowColor(quest->GetLevel());
  6791. packet->setDataByName("difficulty", difficulty);
  6792. if (tempReward)
  6793. {
  6794. packet->setDataByName("max_coin", quest->GetCoinTmpReward());
  6795. packet->setDataByName("min_coin", quest->GetCoinTmpReward());
  6796. packet->setDataByName("status_points", quest->GetStatusPoints());
  6797. }
  6798. else
  6799. {
  6800. int64 rewarded_coin = 0;
  6801. if (quest->GetCoinsReward() > 0) {
  6802. if (quest->GetCoinsRewardMax() > 0)
  6803. rewarded_coin = MakeRandomInt(quest->GetCoinsReward(), quest->GetCoinsRewardMax());
  6804. else
  6805. rewarded_coin = quest->GetCoinsReward();
  6806. }
  6807. quest->SetGeneratedCoin(rewarded_coin);
  6808. packet->setDataByName("max_coin", rewarded_coin);
  6809. packet->setDataByName("min_coin", rewarded_coin);
  6810. packet->setDataByName("status_points", quest->GetStatusPoints());
  6811. }
  6812. if (tempReward) {
  6813. PopulateQuestRewardItems(quest->GetTmpRewardItems(), packet);
  6814. }
  6815. else
  6816. {
  6817. vector<Item*>* items2 = quest->GetSelectableRewardItems();
  6818. PopulateQuestRewardItems(quest->GetRewardItems(), packet);
  6819. PopulateQuestRewardItems(quest->GetSelectableRewardItems(), packet, std::string("num_select_rewards"),
  6820. std::string("select_reward_id"), std::string("select_item"));
  6821. map<int32, sint32>* reward_factions = quest->GetRewardFactions();
  6822. if (reward_factions && reward_factions->size() > 0) {
  6823. packet->setArrayLengthByName("num_factions", reward_factions->size());
  6824. map<int32, sint32>::iterator itr;
  6825. int16 index = 0;
  6826. for (itr = reward_factions->begin(); itr != reward_factions->end(); itr++) {
  6827. int32 faction_id = itr->first;
  6828. sint32 amount = itr->second;
  6829. const char* faction_name = master_faction_list.GetFactionNameByID(faction_id);
  6830. if (faction_name) {
  6831. packet->setArrayDataByName("faction_name", const_cast<char*>(faction_name), index);
  6832. packet->setArrayDataByName("amount", amount, index);
  6833. }
  6834. index++;
  6835. }
  6836. }
  6837. }
  6838. EQ2Packet* outapp = packet->serialize();
  6839. // DumpPacket(outapp);
  6840. QueuePacket(outapp);
  6841. safe_delete(packet);
  6842. }
  6843. }
  6844. void Client::DisplayRandomizeFeatures(int32 flags) {
  6845. SimpleMessage(CHANNEL_NARRATIVE, "Showing Active Randomize Features:");
  6846. if (flags > 0) {
  6847. if (flags & RANDOMIZE_GENDER)
  6848. SimpleMessage(CHANNEL_NARRATIVE, "- Gender");
  6849. if (flags & RANDOMIZE_RACE)
  6850. SimpleMessage(CHANNEL_NARRATIVE, "- Race");
  6851. if (flags & RANDOMIZE_MODEL_TYPE)
  6852. SimpleMessage(CHANNEL_NARRATIVE, "- Model");
  6853. if (flags & RANDOMIZE_FACIAL_HAIR_TYPE)
  6854. SimpleMessage(CHANNEL_NARRATIVE, "- Facial Hair");
  6855. if (flags & RANDOMIZE_HAIR_TYPE)
  6856. SimpleMessage(CHANNEL_NARRATIVE, "- Hair");
  6857. if (flags & RANDOMIZE_WING_TYPE)
  6858. SimpleMessage(CHANNEL_NARRATIVE, "- Wing");
  6859. if (flags & RANDOMIZE_CHEEK_TYPE)
  6860. SimpleMessage(CHANNEL_NARRATIVE, "- Cheek");
  6861. if (flags & RANDOMIZE_CHIN_TYPE)
  6862. SimpleMessage(CHANNEL_NARRATIVE, "- Chin");
  6863. if (flags & RANDOMIZE_EAR_TYPE)
  6864. SimpleMessage(CHANNEL_NARRATIVE, "- Ear");
  6865. if (flags & RANDOMIZE_EYE_BROW_TYPE)
  6866. SimpleMessage(CHANNEL_NARRATIVE, "- Eyebrow");
  6867. if (flags & RANDOMIZE_EYE_TYPE)
  6868. SimpleMessage(CHANNEL_NARRATIVE, "- Eye");
  6869. if (flags & RANDOMIZE_LIP_TYPE)
  6870. SimpleMessage(CHANNEL_NARRATIVE, "- Lip");
  6871. if (flags & RANDOMIZE_NOSE_TYPE)
  6872. SimpleMessage(CHANNEL_NARRATIVE, "- Nose");
  6873. if (flags & RANDOMIZE_EYE_COLOR)
  6874. SimpleMessage(CHANNEL_NARRATIVE, "- Eye Color");
  6875. if (flags & RANDOMIZE_HAIR_COLOR1)
  6876. SimpleMessage(CHANNEL_NARRATIVE, "- Hair Color1");
  6877. if (flags & RANDOMIZE_HAIR_COLOR2)
  6878. SimpleMessage(CHANNEL_NARRATIVE, "- Hair Color2");
  6879. if (flags & RANDOMIZE_HAIR_HIGHLIGHT)
  6880. SimpleMessage(CHANNEL_NARRATIVE, "- Hair Color Highlights");
  6881. if (flags & RANDOMIZE_HAIR_FACE_COLOR)
  6882. SimpleMessage(CHANNEL_NARRATIVE, "- Facial Hair Color");
  6883. if (flags & RANDOMIZE_HAIR_FACE_HIGHLIGHT_COLOR)
  6884. SimpleMessage(CHANNEL_NARRATIVE, "- Facial Hair Color Highlights");
  6885. if (flags & RANDOMIZE_HAIR_TYPE_COLOR)
  6886. SimpleMessage(CHANNEL_NARRATIVE, "- Hair Type Color");
  6887. if (flags & RANDOMIZE_HAIR_TYPE_HIGHLIGHT_COLOR)
  6888. SimpleMessage(CHANNEL_NARRATIVE, "- Hair Type Highlights");
  6889. if (flags & RANDOMIZE_SKIN_COLOR)
  6890. SimpleMessage(CHANNEL_NARRATIVE, "- Skin Color");
  6891. if (flags & RANDOMIZE_WING_COLOR1)
  6892. SimpleMessage(CHANNEL_NARRATIVE, "- Wing Color1");
  6893. if (flags & RANDOMIZE_WING_COLOR2)
  6894. SimpleMessage(CHANNEL_NARRATIVE, "- Wing Color2");
  6895. }
  6896. else
  6897. {
  6898. SimpleMessage(CHANNEL_NARRATIVE, "- No Randomization Set.");
  6899. }
  6900. }
  6901. void Client::GiveQuestReward(Quest* quest, bool has_displayed) {
  6902. current_quest_id = 0;
  6903. if (!quest->GetQuestTemporaryState() && !has_displayed)
  6904. {
  6905. quest->IncrementCompleteCount();
  6906. player->AddCompletedQuest(quest);
  6907. }
  6908. AddPendingQuestAcceptReward(quest);
  6909. DisplayQuestComplete(quest, quest->GetQuestTemporaryState(), quest->GetQuestTemporaryDescription());
  6910. LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
  6911. SendQuestJournal();
  6912. if (quest->GetQuestTemporaryState()) {
  6913. return;
  6914. }
  6915. if (!has_displayed) {
  6916. if (quest->GetExpReward() > 0) {
  6917. int32 xp = quest->GetExpReward();
  6918. player->AddXP(xp);
  6919. }
  6920. if (quest->GetTSExpReward() > 0) {
  6921. int8 ts_level = player->GetTSLevel();
  6922. int32 xp = quest->GetTSExpReward();
  6923. if (player->AddTSXP(xp)) {
  6924. Message(CHANNEL_REWARD, "You gain %u tradeskill experience!", (int32)xp);
  6925. if (player->GetTSLevel() != ts_level)
  6926. ChangeTSLevel(ts_level, player->GetTSLevel());
  6927. player->SetCharSheetChanged(true);
  6928. }
  6929. }
  6930. int64 total_coins = quest->GetGeneratedCoin();
  6931. if (total_coins > 0)
  6932. AwardCoins(total_coins, std::string("for completing ").append(quest->GetName()));
  6933. player->RemoveQuest(quest->GetQuestID(), false);
  6934. }
  6935. if (quest->GetQuestGiver() > 0)
  6936. GetCurrentZone()->SendSpawnChangesByDBID(quest->GetQuestGiver(), this, false, true);
  6937. if (!has_displayed) {
  6938. RemovePlayerQuest(quest->GetQuestID(), true, false);
  6939. }
  6940. }
  6941. void Client::DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
  6942. std::unique_lock lock(MConversation);
  6943. PacketStruct* packet = configReader.getStruct("WS_DialogOpen", GetVersion());
  6944. if (packet) {
  6945. packet->setDataByName("conversation_id", conversation_id);
  6946. packet->setDataByName("text", text);
  6947. packet->setDataByName("language", language); // default 0
  6948. packet->setDataByName("enable_blue_ui", 0); // default 0
  6949. packet->setDataByName("can_close", can_close); // default 1
  6950. conversation_map[conversation_id].clear();
  6951. if (conversations) {
  6952. packet->setArrayLengthByName("num_responses", conversations->size());
  6953. for (int32 i = 0; i < conversations->size(); i++) {
  6954. packet->setArrayDataByName("response", conversations->at(i).option.c_str(), i);
  6955. if (conversations->at(i).function.length() > 0)
  6956. conversation_map[conversation_id][i] = conversations->at(i).function;
  6957. }
  6958. }
  6959. packet->setDataByName("spawn_id", spawn_id);
  6960. if (mp3) {
  6961. packet->setDataByName("voice", mp3);
  6962. packet->setDataByName("key1", key1);
  6963. packet->setDataByName("key2", key2);
  6964. }
  6965. QueuePacket(packet->serialize());
  6966. safe_delete(packet);
  6967. }
  6968. }
  6969. void Client::DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
  6970. if (!item || !text || !conversations || conversations->size() == 0) {
  6971. return;
  6972. }
  6973. int32 conversation_id = GetConversationID(0, item);
  6974. if (conversation_id == 0) {
  6975. next_conversation_id++;
  6976. conversation_id = next_conversation_id;
  6977. }
  6978. MConversation.lock();
  6979. conversation_items[conversation_id] = item;
  6980. MConversation.unlock();
  6981. if (type == 4)
  6982. DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2, language, can_close);
  6983. else
  6984. DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2, language, can_close);
  6985. }
  6986. void Client::DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language, int8 can_close) {
  6987. if (!src || !(type == 1 || type == 2 || type == 3) || !text /*|| !conversations || conversations->size() == 0*/) {
  6988. return;
  6989. }
  6990. int32 conversation_id = GetConversationID(src, 0);
  6991. if (conversation_id == 0) {
  6992. next_conversation_id++;
  6993. conversation_id = next_conversation_id;
  6994. }
  6995. MConversation.lock();
  6996. conversation_spawns[conversation_id] = src->GetID();
  6997. MConversation.unlock();
  6998. /* Spawns can start two different types of conversations.
  6999. * Type 1: The chat type with bubbles.
  7000. * Type 2: The dialog type with the blue box. */
  7001. if (type == 1)
  7002. DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(src), conversations, text, mp3, key1, key2, language, can_close);
  7003. else if (type == 2)
  7004. DisplayConversation(conversation_id, 0xFFFFFFFF, conversations, text, mp3, key1, key2, language, can_close);
  7005. else //if (type == 3)
  7006. DisplayConversation(conversation_id, player->GetIDWithPlayerSpawn(player), conversations, text, mp3, key1, key2, language, can_close);
  7007. }
  7008. void Client::CloseDialog(int32 conversation_id) {
  7009. std::unique_lock lock(MConversation);
  7010. PacketStruct* packet = configReader.getStruct("WS_ServerDialogClose", GetVersion());
  7011. if (packet) {
  7012. packet->setDataByName("conversation_id", conversation_id);
  7013. QueuePacket(packet->serialize());
  7014. safe_delete(packet);
  7015. }
  7016. std::map<int32, Item*>::iterator itr;
  7017. while ((itr = conversation_items.find(conversation_id)) != conversation_items.end())
  7018. {
  7019. conversation_items.erase(itr);
  7020. }
  7021. std::map<int32, int32>::iterator itr2 = conversation_spawns.find(conversation_id);
  7022. while ((itr2 = conversation_spawns.find(conversation_id)) != conversation_spawns.end())
  7023. {
  7024. conversation_spawns.erase(itr2);
  7025. }
  7026. }
  7027. int32 Client::GetConversationID(Spawn* spawn, Item* item) {
  7028. std::shared_lock lock(MConversation);
  7029. int32 conversation_id = 0;
  7030. if (spawn) {
  7031. map<int32, int32>::iterator itr;
  7032. for (itr = conversation_spawns.begin(); itr != conversation_spawns.end(); itr++) {
  7033. if (itr->second == spawn->GetID()) {
  7034. conversation_id = itr->first;
  7035. break;
  7036. }
  7037. }
  7038. }
  7039. else if (item) {
  7040. map<int32, Item*>::iterator itr;
  7041. for (itr = conversation_items.begin(); itr != conversation_items.end(); itr++) {
  7042. if (itr->second == item) {
  7043. conversation_id = itr->first;
  7044. break;
  7045. }
  7046. }
  7047. }
  7048. return conversation_id;
  7049. }
  7050. Spawn* Client::GetCombineSpawn() {
  7051. return combine_spawn;
  7052. }
  7053. bool Client::ShouldTarget() {
  7054. return should_target;
  7055. }
  7056. void Client::TargetSpawn(Spawn* spawn) {
  7057. should_target = false;
  7058. PacketStruct* packet = configReader.getStruct("WS_ServerUpdateTarget", GetVersion());
  7059. if (packet) {
  7060. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
  7061. QueuePacket(packet->serialize());
  7062. safe_delete(packet);
  7063. }
  7064. GetPlayer()->SetTarget(spawn);
  7065. GetPlayer()->info_changed = true;
  7066. GetPlayer()->changed = true;
  7067. GetPlayer()->AddChangedZoneSpawn();
  7068. }
  7069. void Client::CombineSpawns(float radius, Spawn* spawn) {
  7070. combine_spawn = spawn;
  7071. spawn->RemoveSpawnFromGroup(true);
  7072. if (!GetCurrentZone()->AddCloseSpawnsToSpawnGroup(combine_spawn, radius))
  7073. SimpleMessage(CHANNEL_COLOR_YELLOW, "One or more spawns are in a spawn group and cannot be combined until they are removed from their group.");
  7074. GetCurrentZone()->RepopSpawns(this, combine_spawn);
  7075. should_target = true;
  7076. }
  7077. void Client::AddCombineSpawn(Spawn* spawn) {
  7078. if (combine_spawn && combine_spawn != spawn && spawn) {
  7079. combine_spawn->AddSpawnToGroup(spawn);
  7080. spawn->AddSpawnToGroup(combine_spawn);
  7081. GetCurrentZone()->RepopSpawns(this, combine_spawn);
  7082. }
  7083. else if (spawn)
  7084. combine_spawn = spawn;
  7085. should_target = true;
  7086. }
  7087. void Client::RemoveCombineSpawn(Spawn* spawn) {
  7088. if (combine_spawn && spawn)
  7089. spawn->RemoveSpawnFromGroup();
  7090. if (combine_spawn == spawn)
  7091. combine_spawn->RemoveSpawnFromGroup(true);
  7092. GetCurrentZone()->RepopSpawns(this, combine_spawn);
  7093. if (combine_spawn == spawn)
  7094. combine_spawn = 0;
  7095. }
  7096. void Client::SaveCombineSpawns(const char* name) {
  7097. if (!combine_spawn) {
  7098. return;
  7099. }
  7100. vector<Spawn*>* spawns = combine_spawn->GetSpawnGroup();
  7101. if (!spawns) {
  7102. return;
  7103. }
  7104. int32 count = spawns->size();
  7105. int32 spawnLocationID = 0;
  7106. if (count == 1)
  7107. SimpleMessage(CHANNEL_COLOR_YELLOW, "Error: You only have a single Spawn in the group!");
  7108. else if ((spawnLocationID = database.SaveCombinedSpawnLocation(GetCurrentZone(), combine_spawn, name)) > 0) {
  7109. Message(CHANNEL_COLOR_YELLOW, "Successfully combined %u spawns into spawn location: %u", count, spawnLocationID);
  7110. // we remove the spawn inside SaveCombinedSpawnLocation
  7111. //GetCurrentZone()->RemoveSpawn(combine_spawn);
  7112. }
  7113. else
  7114. SimpleMessage(CHANNEL_COLOR_YELLOW, "Error saving spawn group, check console for details.");
  7115. safe_delete(spawns);
  7116. combine_spawn = 0;
  7117. }
  7118. bool Client::AddItem(int32 item_id, int16 quantity, AddItemType type) {
  7119. Item* master_item = master_item_list.GetItem(item_id);
  7120. Item* item = 0;
  7121. if (master_item)
  7122. item = new Item(master_item);
  7123. if (item) {
  7124. if (quantity > 0)
  7125. item->details.count = quantity;
  7126. return AddItem(item, nullptr, type);
  7127. }
  7128. else
  7129. Message(CHANNEL_COLOR_RED, "Could not find item with id of: %i", item_id);
  7130. return false;
  7131. }
  7132. bool Client::AddItem(Item* item, bool* item_deleted, AddItemType type) {
  7133. if (!item) {
  7134. return false;
  7135. }
  7136. if (player->AddItem(item, type)) {
  7137. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  7138. if (outapp) {
  7139. //DumpPacket(outapp);
  7140. QueuePacket(outapp);
  7141. //resend bag desc with new item name added
  7142. outapp = player->SendBagUpdate(item->details.unique_id, GetVersion());
  7143. if (outapp) {
  7144. //DumpPacket(outapp);
  7145. QueuePacket(outapp);
  7146. }
  7147. /*EQ2Packet* app = item->serialize(client->GetVersion(), false);
  7148. DumpPacket(app);
  7149. client->QueuePacket(app);
  7150. */
  7151. }
  7152. CheckPlayerQuestsItemUpdate(item);
  7153. if (item->GetItemScript() && lua_interface)
  7154. lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, player);
  7155. }
  7156. else {
  7157. lua_interface->SetLuaUserDataStale(item);
  7158. // likely lore conflict
  7159. if (item_deleted)
  7160. *item_deleted = true;
  7161. return false;
  7162. }
  7163. return true;
  7164. }
  7165. bool Client::AddItemToBank(int32 item_id, int16 quantity) {
  7166. Item* master_item = master_item_list.GetItem(item_id);
  7167. Item* item = 0;
  7168. if (master_item)
  7169. item = new Item(master_item);
  7170. if (item) {
  7171. if (quantity > 0)
  7172. item->details.count = quantity;
  7173. return AddItemToBank(item);
  7174. }
  7175. else
  7176. Message(CHANNEL_COLOR_RED, "Could not find item with id of: %i", item_id);
  7177. return false;
  7178. }
  7179. bool Client::AddItemToBank(Item* item) {
  7180. if (!item) {
  7181. return false;
  7182. }
  7183. if (player->AddItemToBank(item)) {
  7184. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  7185. if (outapp) {
  7186. QueuePacket(outapp);
  7187. //resend bag desc with new item name added
  7188. outapp = player->SendBagUpdate(item->details.inv_slot_id, GetVersion());
  7189. if (outapp)
  7190. QueuePacket(outapp);
  7191. /*EQ2Packet* app = item->serialize(client->GetVersion(), false);
  7192. DumpPacket(app);
  7193. client->QueuePacket(app);
  7194. */
  7195. }
  7196. CheckPlayerQuestsItemUpdate(item);
  7197. if (item->GetItemScript() && lua_interface)
  7198. lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, player);
  7199. }
  7200. else {
  7201. lua_interface->SetLuaUserDataStale(item);
  7202. // likely lore conflict
  7203. safe_delete(item);
  7204. return false;
  7205. }
  7206. return true;
  7207. }
  7208. void Client::UnequipItem(int16 index, sint32 bag_id, int8 to_slot, int8 appearance_equip) {
  7209. vector<EQ2Packet*> packets = GetPlayer()->UnequipItem(index, bag_id, to_slot, GetVersion(), appearance_equip);
  7210. EQ2Packet* outapp = 0;
  7211. for (int32 i = 0; i < packets.size(); i++)
  7212. {
  7213. outapp = packets[i];
  7214. if (outapp)
  7215. QueuePacket(outapp);
  7216. }
  7217. GetPlayer()->UpdateWeapons();
  7218. EQ2Packet* characterSheetPackets = GetPlayer()->GetPlayerInfo()->serialize(GetVersion());
  7219. QueuePacket(characterSheetPackets);
  7220. }
  7221. bool Client::RemoveItem(Item* item, int16 quantity, bool force_override_no_delete) {
  7222. EQ2Packet* outapp;
  7223. bool delete_item = false;
  7224. assert(item);
  7225. if (quantity > 0 && !item->IsBag() && item->details.count > quantity) {
  7226. item->details.count -= quantity;
  7227. item->save_needed = true;
  7228. }
  7229. else {
  7230. database.DeleteItem(character_id, item, 0);
  7231. player->GetPlayerItemList()->RemoveItem(item, false);
  7232. delete_item = true;
  7233. }
  7234. if (force_override_no_delete)
  7235. delete_item = false;
  7236. if ((outapp = player->SendInventoryUpdate(version))) {
  7237. QueuePacket(outapp);
  7238. if (item->GetItemScript() && lua_interface)
  7239. lua_interface->RunItemScript(item->GetItemScript(), "removed", item, player);
  7240. if (delete_item)
  7241. {
  7242. PurgeItem(item);
  7243. lua_interface->SetLuaUserDataStale(item);
  7244. safe_delete(item);
  7245. }
  7246. GetPlayer()->CalculateApplyWeight();
  7247. return true;
  7248. }
  7249. return false;
  7250. }
  7251. void Client::SetLuaDebugClient(bool val) {
  7252. if (val)
  7253. lua_debug_timer.Start();
  7254. lua_debug = val;
  7255. if (lua_interface && !val) {
  7256. lua_interface->RemoveDebugClients(this);
  7257. lua_debug_timer.Disable();
  7258. }
  7259. }
  7260. void Client::SetMerchantTransaction(Spawn* spawn) {
  7261. merchant_transaction = spawn;
  7262. }
  7263. Spawn* Client::GetMerchantTransaction() {
  7264. return merchant_transaction;
  7265. }
  7266. void Client::SetMailTransaction(Spawn* spawn) {
  7267. ResetSendMail(spawn ? false : true);
  7268. MMailWindowMutex.lock();
  7269. mail_transaction = spawn;
  7270. MMailWindowMutex.unlock();
  7271. }
  7272. Spawn* Client::GetMailTransaction() {
  7273. return mail_transaction;
  7274. }
  7275. void Client::PlaySound(const char* name) {
  7276. if (name) {
  7277. PacketStruct* packet = configReader.getStruct("WS_PlaySound", GetVersion());
  7278. if (packet) {
  7279. packet->setMediumStringByName("name", name);
  7280. QueuePacket(packet->serialize());
  7281. safe_delete(packet);
  7282. }
  7283. }
  7284. }
  7285. float Client::CalculateBuyMultiplier(int32 merchant_id) {
  7286. /*MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(merchant_id);
  7287. if(multiplier){
  7288. sint32 faction_val = player->GetFactions()->GetFactionValue(multiplier->faction_id);
  7289. float diff_low = faction_val - multiplier->faction_min;
  7290. if(diff_low < 0)
  7291. diff_low*=-1;
  7292. float total_diff = multiplier->faction_max - multiplier->faction_min;
  7293. if(total_diff < 0)
  7294. total_diff*=-1;
  7295. float buy_multiplier = multiplier->high_buy_multiplier - multiplier->low_buy_multiplier;
  7296. float total1 = (diff_low/total_diff);
  7297. float final_buy_multiplier = total1*buy_multiplier + total1*multiplier->low_buy_multiplier;
  7298. return final_buy_multiplier;
  7299. }*/
  7300. return 1;
  7301. }
  7302. float Client::CalculateSellMultiplier(int32 merchant_id) {
  7303. /*MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(merchant_id);
  7304. if(multiplier){
  7305. sint32 faction_val = player->GetFactions()->GetFactionValue(multiplier->faction_id);
  7306. float diff_low = faction_val - multiplier->faction_min;
  7307. if(diff_low < 0)
  7308. diff_low*=-1;
  7309. float total_diff = multiplier->faction_max - multiplier->faction_min;
  7310. if(total_diff < 0)
  7311. total_diff*=-1;
  7312. float sell_multiplier = multiplier->high_sell_multiplier - multiplier->low_sell_multiplier;
  7313. float total1 = (diff_low/total_diff);
  7314. float final_sell_multiplier = total1*sell_multiplier + total1*multiplier->low_sell_multiplier;
  7315. return final_sell_multiplier;
  7316. }*/
  7317. return 1;
  7318. }
  7319. void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
  7320. Spawn* spawn = GetMerchantTransaction();
  7321. Guild* guild = GetPlayer()->GetGuild();
  7322. if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) &&
  7323. spawn->IsClientInMerchantLevelRange(this)) {
  7324. int32 total_sell_price = 0;
  7325. int32 total_status_sell_price = 0; //for status
  7326. float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
  7327. int32 sell_price = 0;
  7328. int32 status_sell_price = 0; //for status
  7329. Item* master_item = master_item_list.GetItem(item_id);
  7330. Item* item = 0;
  7331. if (unique_id == 0)
  7332. item = player->item_list.GetItemFromID(item_id, quantity);
  7333. else
  7334. item = player->item_list.GetItemFromUniqueID(unique_id);
  7335. if (!item)
  7336. item = player->item_list.GetItemFromID(item_id);
  7337. if (item && master_item) {
  7338. if (item->details.item_locked || item->details.equip_slot_id)
  7339. {
  7340. SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell the item in use.");
  7341. return;
  7342. }
  7343. else if (item->CheckFlag(NO_VALUE))
  7344. {
  7345. SimpleMessage(CHANNEL_COLOR_RED, "This item has no value.");
  7346. return;
  7347. }
  7348. else if (item->IsBag())
  7349. {
  7350. int32 bagitemcount = player->GetPlayerItemList()->GetItemCountInBag(item);
  7351. if (bagitemcount > 0) {
  7352. SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell a bag with items inside it.");
  7353. return;
  7354. }
  7355. }
  7356. int32 sell_price = (int32)(master_item->sell_price * multiplier);
  7357. if (sell_price > item->sell_price)
  7358. sell_price = item->sell_price;
  7359. if (quantity > item->details.count)
  7360. quantity = item->details.count;
  7361. total_sell_price = sell_price * quantity;
  7362. //------------------------------For Selling Status Items
  7363. status_sell_price = (int32)(master_item->sell_status * multiplier);
  7364. if (status_sell_price > item->sell_status)
  7365. status_sell_price = item->sell_status;
  7366. if (quantity > item->details.count)
  7367. quantity = item->details.count;
  7368. total_status_sell_price = status_sell_price * quantity;
  7369. if (total_status_sell_price > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))
  7370. total_status_sell_price = 0;
  7371. player->GetInfoStruct()->add_status_points(total_status_sell_price);
  7372. int32 guildMaxLevel = 5 + item->details.recommended_level; // client hard codes +5 to the level
  7373. if (player->GetGuild() && guild->GetLevel() < guildMaxLevel) {
  7374. guild->UpdateGuildStatus(GetPlayer(), total_status_sell_price / 10);
  7375. guild->SendGuildMemberList();
  7376. guild->AddEXPCurrent((total_status_sell_price / 10), true);
  7377. }
  7378. if (quantity > 1)
  7379. {
  7380. if (total_status_sell_price)
  7381. Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %i %s to %s for %s and %u Status Points.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str(), status_sell_price);
  7382. else
  7383. Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %i %s to %s for %s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
  7384. }
  7385. else
  7386. {
  7387. if (total_status_sell_price)
  7388. Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %s to %s for %s and %u Status Points.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str(), status_sell_price);
  7389. else
  7390. Message(CHANNEL_MERCHANT_BUY_SELL, "You sell %s to %s for %s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_sell_price).c_str());
  7391. }
  7392. player->AddCoins(total_sell_price);
  7393. if (!item->no_buy_back && (total_status_sell_price == 0 || (total_status_sell_price > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))))
  7394. AddBuyBack(unique_id, item_id, quantity, sell_price);
  7395. if (quantity >= item->details.count) {
  7396. database.DeleteItem(GetCharacterID(), item, 0);
  7397. player->item_list.RemoveItem(item, true);
  7398. }
  7399. else {
  7400. item->details.count -= quantity;
  7401. item->save_needed = true;
  7402. }
  7403. EQ2Packet* outapp = player->SendInventoryUpdate(GetVersion());
  7404. if (outapp)
  7405. QueuePacket(outapp);
  7406. if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
  7407. SendBuyBackList();
  7408. }
  7409. }
  7410. }
  7411. void Client::BuyBack(int32 item_id, int16 quantity) {
  7412. Spawn* spawn = GetMerchantTransaction();
  7413. if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) &&
  7414. spawn->IsClientInMerchantLevelRange(this)) {
  7415. deque<BuyBackItem*>::iterator itr;
  7416. BuyBackItem* buyback = 0;
  7417. BuyBackItem* closest = 0;
  7418. MBuyBack.readlock(__FUNCTION__, __LINE__);
  7419. for (itr = buy_back_items.begin(); itr != buy_back_items.end(); itr++) {
  7420. buyback = *itr;
  7421. if (buyback->unique_id == item_id) {
  7422. closest = buyback;
  7423. quantity = buyback->quantity;
  7424. break;
  7425. }
  7426. }
  7427. MBuyBack.releasereadlock(__FUNCTION__, __LINE__);
  7428. if (closest) {
  7429. Item* item = 0;
  7430. Item* master_item = master_item_list.GetItem(closest->item_id);
  7431. if (master_item) {
  7432. item = new Item(master_item);
  7433. if (closest->quantity >= quantity)
  7434. item->details.count = quantity;
  7435. else
  7436. item->details.count = closest->quantity;
  7437. }
  7438. bool itemDeleted = false;
  7439. bool itemAdded = false;
  7440. sint64 dispFlags = 0;
  7441. if (item && item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buyback_display_flags", item, player, nullptr, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY))
  7442. SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item.");
  7443. else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item))
  7444. SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item.");
  7445. else if (player->RemoveCoins(closest->quantity * closest->price)) {
  7446. bool removed = false;
  7447. if (closest->quantity == quantity) {
  7448. MBuyBack.writelock(__FUNCTION__, __LINE__);
  7449. for (itr = buy_back_items.begin(); itr != buy_back_items.end(); itr++) {
  7450. if (*itr == closest) {
  7451. buy_back_items.erase(itr);
  7452. removed = true;
  7453. break;
  7454. }
  7455. }
  7456. MBuyBack.releasewritelock(__FUNCTION__, __LINE__);
  7457. }
  7458. else {
  7459. closest->quantity -= quantity;
  7460. closest->save_needed = true;
  7461. }
  7462. itemAdded = AddItem(item, &itemDeleted);
  7463. if (removed) {
  7464. database.DeleteBuyBack(GetCharacterID(), closest->item_id, closest->quantity, closest->price);
  7465. safe_delete(closest);
  7466. }
  7467. if (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
  7468. SendBuyBackList();
  7469. }
  7470. else
  7471. SimpleMessage(CHANNEL_COLOR_RED, "You cannot afford this item.");
  7472. if (!itemAdded && !itemDeleted) {
  7473. lua_interface->SetLuaUserDataStale(item);
  7474. safe_delete(item);
  7475. }
  7476. }
  7477. }
  7478. }
  7479. void Client::BuyItem(int32 item_id, int16 quantity) {
  7480. // Get the merchant we are buying from
  7481. Spawn* spawn = GetMerchantTransaction();
  7482. // Make sure the spawn has a merchant list
  7483. if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
  7484. int32 total_buy_price = 0;
  7485. float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
  7486. int32 sell_price = 0;
  7487. Item* master_item = master_item_list.GetItem(item_id);
  7488. Item* item = 0;
  7489. int16 total_available = 0;
  7490. vector<MerchantItemInfo>* temp;
  7491. vector<MerchantItemInfo>::iterator itr;
  7492. MerchantItemInfo* ItemInfo = 0;
  7493. temp = world.GetMerchantList(spawn->GetMerchantID());
  7494. for (itr = temp->begin(); itr != temp->end(); itr++) {
  7495. if ((*itr).item_id == item_id) {
  7496. ItemInfo = &(*itr);
  7497. break;
  7498. }
  7499. }
  7500. if (master_item && ItemInfo) {
  7501. if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO) {
  7502. quantity = 1;
  7503. total_available = 0xFFFF;
  7504. sell_price = master_item->sell_price;
  7505. }
  7506. else {
  7507. total_available = world.GetMerchantItemQuantity(spawn->GetMerchantID(), item_id);
  7508. sell_price = (int32)(master_item->sell_price * multiplier);
  7509. if (quantity > total_available)
  7510. quantity = total_available;
  7511. }
  7512. sint64 dispFlags = 0;
  7513. if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "buy_display_flags", master_item, player, nullptr, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY))
  7514. {
  7515. SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item.");
  7516. return;
  7517. }
  7518. if (quantity < 1)
  7519. {
  7520. SimpleMessage(CHANNEL_COLOR_RED, "Merchant does not have item for purchase (quantity < 1).");
  7521. return;
  7522. }
  7523. total_buy_price = sell_price * quantity;
  7524. item = new Item(master_item);
  7525. item->details.count = quantity;
  7526. if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) {
  7527. SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item.");
  7528. lua_interface->SetLuaUserDataStale(item);
  7529. safe_delete(item);
  7530. }
  7531. else {
  7532. // Price not set in the merchant_inventory table, use the old method
  7533. if (ItemInfo->price_item_id == 0 && ItemInfo->price_item2_id == 0 && ItemInfo->price_status == 0 && ItemInfo->price_stationcash == 0 && ItemInfo->price_coins == 0) {
  7534. if (player->RemoveCoins(total_buy_price)) {
  7535. item->SetMaxSellValue(sell_price);
  7536. if (quantity > 1)
  7537. Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %i %s from %s for%s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_buy_price).c_str());
  7538. else
  7539. Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %s from %s for%s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(total_buy_price).c_str());
  7540. bool itemDeleted = false;
  7541. AddItem(item, &itemDeleted);
  7542. if (!itemDeleted) {
  7543. CheckPlayerQuestsItemUpdate(item);
  7544. if (item && total_available < 0xFF) {
  7545. world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
  7546. SendBuyMerchantList();
  7547. }
  7548. if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
  7549. PlayLotto(total_buy_price, item->details.item_id);
  7550. }
  7551. }
  7552. else {
  7553. Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase %s.", master_item->CreateItemLink(GetVersion()).c_str());
  7554. GetCurrentZone()->SendSpellFailedPacket(this, SPELL_ERROR_NOT_ENOUGH_COIN);
  7555. PlaySound("buy_failed");
  7556. }
  7557. }
  7558. else {
  7559. // Price set in merchant_inventory table
  7560. // Check if the player has enough status, coins and staion cash to buy the item before checking the items
  7561. // TODO: need to add support for station cash
  7562. if (player->GetInfoStruct()->get_status_points() >= (ItemInfo->price_status * quantity) && player->HasCoins(ItemInfo->price_coins * quantity)) {
  7563. // Check items
  7564. int16 item_quantity = 0;
  7565. // Default these to true in case price_item_id or price_item2_id was never set
  7566. bool hasItem1 = true;
  7567. bool hasItem2 = true;
  7568. Item* tempItem1 = 0;
  7569. Item* tempItem2 = 0;
  7570. if (ItemInfo->price_item_id != 0) {
  7571. // Same item for whatever reason lets add the quantities together
  7572. if (ItemInfo->price_item_id == ItemInfo->price_item2_id)
  7573. item_quantity = ItemInfo->price_item_qty + ItemInfo->price_item2_qty;
  7574. else
  7575. item_quantity = ItemInfo->price_item_qty;
  7576. tempItem1 = player->item_list.GetItemFromID(ItemInfo->price_item_id);
  7577. if (tempItem1) {
  7578. if (tempItem1->details.count < item_quantity)
  7579. hasItem1 = false;
  7580. }
  7581. else {
  7582. hasItem1 = false;
  7583. }
  7584. }
  7585. // Check item2, if item_quantity is greater then item1 quantity then item2 is the same item
  7586. // as item1 and we already checked for it so we can skip this check
  7587. if (ItemInfo->price_item2_id != 0 && item_quantity <= ItemInfo->price_item_qty) {
  7588. tempItem2 = player->item_list.GetItemFromID(ItemInfo->price_item2_id);
  7589. if (tempItem2) {
  7590. if (tempItem2->details.count < ItemInfo->price_item2_qty)
  7591. hasItem2 = false;
  7592. }
  7593. else {
  7594. hasItem2 = false;
  7595. }
  7596. }
  7597. // if we have every thing then remove the price and give the item
  7598. if (hasItem1 && hasItem2) {
  7599. player->GetInfoStruct()->set_status_points(player->GetInfoStruct()->get_status_points() - (ItemInfo->price_status * quantity));
  7600. // TODO: station cash
  7601. // The update that would normally be sent after modifing the players inventory is automatically sent in AddItem wich is called later
  7602. // so there is no need to send it more then that one time
  7603. if (tempItem1) {
  7604. if (tempItem1->details.count > item_quantity) {
  7605. tempItem1->details.count -= item_quantity;
  7606. tempItem1->save_needed = true;
  7607. }
  7608. else {
  7609. database.DeleteItem(GetCharacterID(), tempItem1, 0);
  7610. player->item_list.DestroyItem(tempItem1->details.index);
  7611. }
  7612. }
  7613. if (tempItem2) {
  7614. if (tempItem2->details.count > ItemInfo->price_item2_qty) {
  7615. tempItem2->details.count -= ItemInfo->price_item2_qty;
  7616. tempItem2->save_needed = true;
  7617. }
  7618. else {
  7619. database.DeleteItem(GetCharacterID(), tempItem2, 0);
  7620. player->item_list.DestroyItem(tempItem2->details.index);
  7621. }
  7622. }
  7623. // Checked to see if we had enough coins already so don't need to check the return type on RemoveCoins as it will always be true
  7624. player->RemoveCoins(ItemInfo->price_coins * quantity);
  7625. item->SetMaxSellValue(sell_price);
  7626. if (quantity > 1)
  7627. Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %i %s from %s for%s.", quantity, master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(ItemInfo->price_coins * quantity).c_str());
  7628. else
  7629. Message(CHANNEL_MERCHANT_BUY_SELL, "You buy %s from %s for%s.", master_item->CreateItemLink(GetVersion()).c_str(), spawn->GetName(), GetCoinMessage(ItemInfo->price_coins * quantity).c_str());
  7630. bool itemDeleted = false;
  7631. AddItem(item, &itemDeleted);
  7632. if (!itemDeleted) {
  7633. CheckPlayerQuestsItemUpdate(item);
  7634. if (item && total_available < 0xFF) {
  7635. world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity);
  7636. SendBuyMerchantList();
  7637. }
  7638. SendSellMerchantList();
  7639. if (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)
  7640. PlayLotto(total_buy_price, item->details.item_id);
  7641. }
  7642. }
  7643. else {
  7644. Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase %s.", master_item->CreateItemLink(GetVersion()).c_str());
  7645. GetCurrentZone()->SendSpellFailedPacket(this, SPELL_ERROR_NOT_ENOUGH_COIN);
  7646. PlaySound("buy_failed");
  7647. }
  7648. }
  7649. else {
  7650. Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase %s.", master_item->CreateItemLink(GetVersion()).c_str());
  7651. GetCurrentZone()->SendSpellFailedPacket(this, SPELL_ERROR_NOT_ENOUGH_COIN);
  7652. PlaySound("buy_failed");
  7653. }
  7654. }
  7655. }
  7656. }
  7657. }
  7658. }
  7659. void Client::RepairItem(int32 item_id) {
  7660. Spawn* spawn = GetMerchantTransaction();
  7661. if (spawn) {
  7662. Item* item = player->item_list.GetItemFromID(item_id);
  7663. if (!item)
  7664. item = player->GetEquipmentList()->GetItemFromItemID(item_id);
  7665. if (item) {
  7666. if (item->CheckFlag2(NO_REPAIR)) {
  7667. Message(CHANNEL_MERCHANT, "The mender was unable to repair your items.");
  7668. PlaySound("buy_failed");
  7669. }
  7670. else {
  7671. int32 repair_cost = item->CalculateRepairCost();
  7672. if (player->RemoveCoins((int32)repair_cost)) {
  7673. item->generic_info.condition = 100;
  7674. item->save_needed = true;
  7675. QueuePacket(player->GetEquipmentList()->serialize(GetVersion(), player));
  7676. QueuePacket(player->SendInventoryUpdate(GetVersion()));
  7677. QueuePacket(item->serialize(version, false, player));
  7678. Message(CHANNEL_MERCHANT, "You give %s %s to repair your %s.", spawn->GetName(), GetCoinMessage(repair_cost).c_str(), item->CreateItemLink(GetVersion()).c_str());
  7679. PlaySound("coin_cha_ching");
  7680. if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
  7681. SendRepairList();
  7682. }
  7683. else {
  7684. string popup_text = "You do not have enough coin to repair ";
  7685. string popup_item = item->CreateItemLink(GetVersion()).c_str();
  7686. popup_text.append(popup_item);
  7687. SendPopupMessage(10, popup_text.c_str(), "", 3, 0xFF, 0xFF, 0xFF);
  7688. Message(CHANNEL_MERCHANT, "You do not have enough coin to repair %s.", item->CreateItemLink(GetVersion()).c_str());
  7689. PlaySound("buy_failed");
  7690. }
  7691. }
  7692. }
  7693. }
  7694. }
  7695. void Client::RepairAllItems() {
  7696. Spawn* spawn = GetMerchantTransaction();
  7697. if (spawn) {
  7698. vector<Item*>* repairable_items = GetRepairableItems();
  7699. if (repairable_items && repairable_items->size() > 0) {
  7700. vector<Item*>::iterator itr;
  7701. int64 total_cost = 0;
  7702. for (itr = repairable_items->begin(); itr != repairable_items->end(); itr++)
  7703. total_cost += (*itr)->CalculateRepairCost();
  7704. if (player->RemoveCoins((int32)total_cost)) {
  7705. Message(CHANNEL_MERCHANT, "You give %s to repair all of your items.", GetCoinMessage((int32)total_cost).c_str());
  7706. for (itr = repairable_items->begin(); itr != repairable_items->end(); itr++) {
  7707. Item* item = *itr;
  7708. if (item) {
  7709. item->generic_info.condition = 100;
  7710. item->save_needed = true;
  7711. QueuePacket(item->serialize(version, false, player));
  7712. Message(CHANNEL_COLOR_YELLOW, "Repaired: %s.", item->CreateItemLink(GetVersion()).c_str());
  7713. }
  7714. }
  7715. QueuePacket(player->GetEquipmentList()->serialize(GetVersion(), player));
  7716. QueuePacket(player->SendInventoryUpdate(GetVersion()));
  7717. PlaySound("coin_cha_ching");
  7718. if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
  7719. SendRepairList();
  7720. }
  7721. else {
  7722. string popup_text = "You do not have enough coin to repair all of your items. ";
  7723. SendPopupMessage(10, popup_text.c_str(), "", 3, 0xFF, 0xFF, 0xFF);
  7724. SimpleMessage(CHANNEL_MERCHANT, "You do not have enough coin to repair all of your items.");
  7725. PlaySound("buy_failed");
  7726. }
  7727. }
  7728. safe_delete(repairable_items);
  7729. }
  7730. }
  7731. void Client::SendAchievementsList()
  7732. {
  7733. /*map<int32, Achievement *> *achievements = player->GetAchievementList()->GetAchievements();
  7734. map<int32, Achievement *>::iterator itr;
  7735. Achievement *achievement;
  7736. vector<AchievementRequirements *> *requirements = 0;
  7737. vector<AchievementRequirements *>::iterator itr2;
  7738. AchievementRequirements *requirement;
  7739. vector<AchievementRewards *> *rewards = 0;
  7740. vector<AchievementRewards *>::iterator itr3;
  7741. AchievementRewards *reward;
  7742. PacketStruct *packet;
  7743. int16 i = 0;
  7744. int16 j = 0;
  7745. int16 k = 0;
  7746. if (!(packet = configReader.getStruct("WS_CharacterAchievements", version))) {
  7747. return;
  7748. }
  7749. packet->setArrayLengthByName("num_achievements" , achievements->size());
  7750. for (itr = achievements->begin(); itr != achievements->end(); itr++) {
  7751. achievement = itr->second;
  7752. packet->setArrayDataByName("achievement_id", achievement->GetID(), i);
  7753. packet->setArrayDataByName("title", achievement->GetTitle(), i);
  7754. packet->setArrayDataByName("uncompleted_text", achievement->GetUncompletedText(), i);
  7755. packet->setArrayDataByName("completed_text", achievement->GetCompletedText(), i);
  7756. packet->setArrayDataByName("category", achievement->GetCategory(), i);
  7757. packet->setArrayDataByName("expansion", achievement->GetExpansion(), i);
  7758. packet->setArrayDataByName("icon", achievement->GetIcon(), i);
  7759. packet->setArrayDataByName("point_value", achievement->GetPointValue(), i);
  7760. packet->setArrayDataByName("qty_req", achievement->GetQtyReq(), i);
  7761. packet->setArrayDataByName("hide_achievement", achievement->GetHide(), i);
  7762. packet->setArrayDataByName("unknown3", achievement->GetUnknown3a(), i);
  7763. packet->setArrayDataByName("unknown3", achievement->GetUnknown3b(), i);
  7764. requirements = achievement->GetRequirements();
  7765. rewards = achievement->GetRewards();
  7766. j = 0;
  7767. k = 0;
  7768. packet->setSubArrayLengthByName("num_items", requirements->size(), i, j);
  7769. for (itr2 = requirements->begin(); itr2 != requirements->end(); itr2++) {
  7770. requirement = *itr2;
  7771. packet->setSubArrayDataByName("item_name", requirement->name.c_str(), i, j);
  7772. packet->setSubArrayDataByName("item_qty_req", requirement->qty_req, i, j);
  7773. j++;
  7774. }
  7775. packet->setSubArrayLengthByName("num_rewards", achievement->GetRewards()->size(), i, k);
  7776. for (itr3 = rewards->begin(); itr3 != rewards->end(); itr3++) {
  7777. reward = *itr3;
  7778. packet->setSubArrayDataByName("reward_item", reward->reward.c_str(), i, k);
  7779. k++;
  7780. }
  7781. i++;
  7782. }
  7783. //packet->PrintPacket();
  7784. EQ2Packet* data = packet->serialize();
  7785. EQ2Packet* app = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
  7786. safe_delete(packet);
  7787. safe_delete(data);
  7788. //DumpPacket(app);
  7789. QueuePacket(app);*/
  7790. QueuePacket(master_achievement_list.GetAchievementPacket()->Copy());
  7791. SendAchievementUpdate(true);
  7792. }
  7793. void Client::SendAchievementUpdate(bool first_login) {
  7794. map<int32, AchievementUpdate*>* updates = player->GetAchievementUpdateList()->GetAchievementUpdates();
  7795. map<int32, AchievementUpdate*>::iterator itr;
  7796. AchievementUpdate* update;
  7797. vector<AchievementUpdateItems*>* update_items = 0;
  7798. vector<AchievementUpdateItems*>::iterator itr2;
  7799. AchievementUpdateItems* update_item;
  7800. int16 i = 0;
  7801. int16 j = 0;
  7802. PacketStruct* packet;
  7803. if (!(packet = configReader.getStruct("WS_AchievementUpdate", version))) {
  7804. return;
  7805. }
  7806. packet->setDataByName("unknown1", first_login ? 1 : 0);
  7807. packet->setArrayLengthByName("num_achievements", updates->size());
  7808. for (itr = updates->begin(); itr != updates->end(); itr++) {
  7809. update = itr->second;
  7810. packet->setArrayDataByName("achievement_id", update->GetID(), i);
  7811. packet->setArrayDataByName("completed_date", update->GetCompletedDate(), i);
  7812. update_items = update->GetUpdateItems();
  7813. j = 0;
  7814. packet->setSubArrayLengthByName("num_items", update_items->size(), i);
  7815. for (itr2 = update_items->begin(); itr2 != update_items->end(); itr2++) {
  7816. update_item = *itr2;
  7817. packet->setSubArrayDataByName("item_update", update_item->item_update, i, j);
  7818. j++;
  7819. }
  7820. i++;
  7821. }
  7822. //packet->PrintPacket();
  7823. EQ2Packet* data = packet->serialize();
  7824. EQ2Packet* app = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
  7825. safe_delete(packet);
  7826. safe_delete(data);
  7827. //DumpPacket(app);
  7828. QueuePacket(app);
  7829. }
  7830. void Client::SendBuyMerchantList(bool sell) {
  7831. Spawn* spawn = GetMerchantTransaction();
  7832. if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
  7833. vector<MerchantItemInfo>* items = world.GetMerchantItemList(spawn->GetMerchantID(), spawn->GetMerchantType(), player);
  7834. if (items) {
  7835. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  7836. if (packet) {
  7837. float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
  7838. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  7839. packet->setArrayLengthByName("num_items", items->size());
  7840. vector<MerchantItemInfo>::iterator itr;
  7841. sint8 item_difficulty = 0;
  7842. int32 sell_price = 0;
  7843. int i = 0;
  7844. int tmp_level = 0;
  7845. for (itr = items->begin(); itr != items->end(); itr++, i++) {
  7846. MerchantItemInfo ItemInfo = *itr;
  7847. Item* item = master_item_list.GetItem(ItemInfo.item_id);
  7848. if (!item)
  7849. continue;
  7850. packet->setArrayDataByName("item_name", item->name.c_str(), i);
  7851. packet->setArrayDataByName("item_id", item->details.item_id, i);
  7852. packet->setArrayDataByName("stack_size", item->stack_count, i);
  7853. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  7854. if (item->generic_info.adventure_default_level > 0)
  7855. tmp_level = item->generic_info.adventure_default_level;
  7856. else
  7857. tmp_level = item->generic_info.tradeskill_default_level;
  7858. packet->setArrayDataByName("level", tmp_level, i);
  7859. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
  7860. packet->setArrayDataByName("tier", item->details.tier, i);
  7861. }
  7862. packet->setArrayDataByName("item_id2", item->details.item_id, i);
  7863. item_difficulty = player->GetArrowColor(tmp_level);
  7864. if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
  7865. item_difficulty = ARROW_COLOR_WHITE;
  7866. sint64 overrideValue = 0;
  7867. if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, nullptr, &overrideValue))
  7868. item_difficulty = (sint8)overrideValue;
  7869. item_difficulty -= 6;
  7870. if (item_difficulty < 0)
  7871. item_difficulty *= -1;
  7872. packet->setArrayDataByName("item_difficulty", item_difficulty, i);
  7873. packet->setArrayDataByName("quantity", ItemInfo.quantity, i);
  7874. packet->setArrayDataByName("unknown5", 255, i);
  7875. packet->setArrayDataByName("stack_size2", item->stack_count, i);
  7876. sint64 dispFlags = 0;
  7877. if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buy_display_flags", item, player, nullptr, &dispFlags))
  7878. packet->setArrayDataByName("display_flags", (int8)dispFlags, i);
  7879. std::string overrideValueStr;
  7880. // classic client isn't properly tracking this field, DoF we don't have it identified yet, but no field to cause any issues (can add later if identified)
  7881. if (GetVersion() >= 546 && item->GetItemScript() && lua_interface && lua_interface->RunItemScriptWithReturnString(item->GetItemScript(), "item_description", item, player, &overrideValueStr))
  7882. packet->setArrayDataByName("description", overrideValueStr.c_str(), i);
  7883. // If no price set in the merchant_inventory table then use the old method
  7884. if (ItemInfo.price_item_id == 0 && ItemInfo.price_item2_id == 0 && ItemInfo.price_coins == 0 && ItemInfo.price_status == 0 && ItemInfo.price_stationcash == 0) {
  7885. sell_price = (int32)(item->sell_price * multiplier);
  7886. packet->setArrayDataByName("price", sell_price, i);
  7887. }
  7888. else {
  7889. int8 count = 0;
  7890. if (ItemInfo.price_item_id != 0 && ItemInfo.price_item_qty != 0)
  7891. count++;
  7892. if (ItemInfo.price_item2_id != 0 && ItemInfo.price_item2_qty != 0)
  7893. count++;
  7894. if (count != 0) {
  7895. packet->setSubArrayLengthByName("num_tokens", count, i);
  7896. int8 index = 0;
  7897. Item* token = 0;
  7898. if (ItemInfo.price_item_id != 0) {
  7899. token = master_item_list.GetItem(ItemInfo.price_item_id);
  7900. if (item) {
  7901. packet->setSubArrayDataByName("token_icon", token->GetIcon(GetVersion()), i, index);
  7902. packet->setSubArrayDataByName("token_qty", ItemInfo.price_item_qty, i, index);
  7903. packet->setSubArrayDataByName("token_id", ItemInfo.price_item_id, i, index);
  7904. packet->setSubArrayDataByName("token_name", token->name.c_str(), i, index);
  7905. }
  7906. token = 0;
  7907. index++;
  7908. }
  7909. if (ItemInfo.price_item2_id != 0) {
  7910. token = master_item_list.GetItem(ItemInfo.price_item2_id);
  7911. if (item) {
  7912. packet->setSubArrayDataByName("token_icon", token->GetIcon(GetVersion()), i, index);
  7913. packet->setSubArrayDataByName("token_qty", ItemInfo.price_item2_qty, i, index);
  7914. packet->setSubArrayDataByName("token_id", ItemInfo.price_item2_id, i, index);
  7915. packet->setSubArrayDataByName("token_name", token->name.c_str(), i, index);
  7916. }
  7917. }
  7918. }
  7919. packet->setArrayDataByName("price", ItemInfo.price_coins, i);
  7920. packet->setArrayDataByName("status2", ItemInfo.price_status, i);
  7921. packet->setArrayDataByName("station_cash", ItemInfo.price_stationcash, i);
  7922. }
  7923. }
  7924. if (GetVersion() < 561) {
  7925. //buy is 0 so dont need to set it
  7926. if (sell)
  7927. packet->setDataByName("type", 1);
  7928. }
  7929. else if (GetVersion() == 561) {
  7930. packet->setDataByName("type", 2);
  7931. }
  7932. else {
  7933. if (sell)
  7934. packet->setDataByName("type", 130);
  7935. else
  7936. packet->setDataByName("type", 2);
  7937. }
  7938. EQ2Packet* outapp = packet->serialize();
  7939. QueuePacket(outapp);
  7940. safe_delete(packet);
  7941. }
  7942. safe_delete(items);
  7943. }
  7944. else {
  7945. // Need to send an empty packet in the event there is no item list, otherwise the
  7946. // last item list sent to the player will show for this merchant
  7947. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  7948. if (packet) {
  7949. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  7950. if (GetVersion() <= 561) {
  7951. //buy is 0 so dont need to set it
  7952. if (sell)
  7953. packet->setDataByName("type", 1);
  7954. }
  7955. else {
  7956. if (sell)
  7957. packet->setDataByName("type", 130);
  7958. else
  7959. packet->setDataByName("type", 2);
  7960. }
  7961. EQ2Packet* outapp = packet->serialize();
  7962. QueuePacket(outapp);
  7963. safe_delete(packet);
  7964. }
  7965. }
  7966. }
  7967. }
  7968. void Client::SendSellMerchantList(bool sell) {
  7969. Spawn* spawn = GetMerchantTransaction();
  7970. if (!spawn || (spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) || (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
  7971. return;
  7972. if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
  7973. map<int32, Item*>* items = player->GetItemList();
  7974. if (items) {
  7975. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  7976. if (packet) {
  7977. vector<Item*> sellable_items;
  7978. map<int32, Item*>::iterator test_itr;
  7979. for (test_itr = items->begin(); test_itr != items->end(); test_itr++) {
  7980. bool isbagwithitems = false;
  7981. if(test_itr->second && (test_itr->second->details.inv_slot_id == -3 || test_itr->second->details.inv_slot_id == -4))
  7982. continue; // omit bank/shared-bank
  7983. if (test_itr->second && test_itr->second->IsBag() && (test_itr->second->details.num_slots - test_itr->second->details.num_free_slots != test_itr->second->details.num_slots))
  7984. isbagwithitems = true;
  7985. if (test_itr->second && !test_itr->second->CheckFlag(NO_VALUE) && (isbagwithitems == false) && (test_itr->second->details.inv_slot_id != -3) && (test_itr->second->details.inv_slot_id != -4))
  7986. sellable_items.push_back(test_itr->second);
  7987. }
  7988. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  7989. packet->setArrayLengthByName("num_items", sellable_items.size());
  7990. vector<Item*>::iterator itr;
  7991. Item* item = 0;
  7992. sint8 item_difficulty = 0;
  7993. float multiplier = CalculateSellMultiplier(spawn->GetMerchantID());
  7994. int32 sell_price = 0;
  7995. Item* master_item = 0;
  7996. int i = 0;
  7997. int tmp_level = 0;
  7998. for (itr = sellable_items.begin(); itr != sellable_items.end(); itr++, i++) {
  7999. item = *itr;
  8000. master_item = master_item_list.GetItem(item->details.item_id);
  8001. if (master_item)
  8002. sell_price = (int32)(master_item->sell_price * multiplier);
  8003. else
  8004. sell_price = 0;
  8005. if (sell_price > item->sell_price)
  8006. sell_price = item->sell_price;
  8007. packet->setArrayDataByName("item_name", item->name.c_str(), i);
  8008. string thename = item->name;
  8009. packet->setArrayDataByName("price", sell_price, i);
  8010. packet->setArrayDataByName("status", 0, i);//additive to status 2 maybe for server bonus etc
  8011. int8 dispFlags = 0;
  8012. // only city merchants allow selling for status
  8013. if (item->sell_status > 0 && (spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT))
  8014. {
  8015. packet->setArrayDataByName("status2", item->sell_status, i); //this one is the main status
  8016. int32 guildMaxLevel = 5 + item->details.recommended_level; // client hard codes +5 to the level
  8017. if (GetPlayer()->GetGuild() && GetPlayer()->GetGuild()->GetLevel() >= guildMaxLevel) {
  8018. dispFlags += DISPLAY_FLAG_NO_GUILD_STATUS;
  8019. }
  8020. }
  8021. if (item->no_buy_back || (item->sell_status > 0 && (spawn->GetMerchantType() & MERCHANT_TYPE_CITYMERCHANT)))
  8022. {
  8023. if (GetVersion() < 1188)
  8024. dispFlags += DISPLAY_FLAG_RED_TEXT; // for older clients it isn't "no buy back", you can either have 1 for red text or 255 for 'not for sale' to be checked
  8025. else
  8026. dispFlags += DISPLAY_FLAG_NO_BUYBACK;
  8027. }
  8028. if (item->no_sale)
  8029. dispFlags += DISPLAY_FLAG_NOT_FOR_SALE;
  8030. packet->setArrayDataByName("display_flags", dispFlags, i);
  8031. packet->setArrayDataByName("item_id", item->details.item_id, i);
  8032. packet->setArrayDataByName("unique_item_id", item->details.unique_id, i);
  8033. packet->setArrayDataByName("stack_size", item->details.count, i);
  8034. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  8035. if (item->generic_info.adventure_default_level > 0)
  8036. tmp_level = item->generic_info.adventure_default_level;
  8037. else
  8038. tmp_level = item->generic_info.tradeskill_default_level;
  8039. packet->setArrayDataByName("level", item->details.recommended_level, i);
  8040. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
  8041. packet->setArrayDataByName("tier", item->details.tier, i);
  8042. }
  8043. packet->setArrayDataByName("item_id2", item->details.item_id, i);
  8044. item_difficulty = player->GetArrowColor(tmp_level);
  8045. if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
  8046. item_difficulty = ARROW_COLOR_WHITE;
  8047. sint64 overrideValue = 0;
  8048. if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, nullptr, &overrideValue))
  8049. item_difficulty = (sint8)overrideValue;
  8050. item_difficulty -= 6;
  8051. if (item_difficulty < 0)
  8052. item_difficulty *= -1;
  8053. packet->setArrayDataByName("item_difficulty", item_difficulty, i);
  8054. if (item->details.count == 1)
  8055. packet->setArrayDataByName("quantity", 0xFFFF, i);
  8056. else
  8057. packet->setArrayDataByName("quantity", item->details.count, i);
  8058. packet->setArrayDataByName("stack_size2", item->details.count, i);
  8059. if (GetVersion() <= 1096)
  8060. packet->setArrayDataByName("description", item->description.c_str(), i);
  8061. }
  8062. if (GetVersion() < 561) {
  8063. packet->setDataByName("type", 1);
  8064. }
  8065. else if (GetVersion() == 561) {
  8066. packet->setDataByName("type", 1);
  8067. }
  8068. else {
  8069. if (sell)
  8070. packet->setDataByName("type", 129);
  8071. else
  8072. packet->setDataByName("type", 1);
  8073. }
  8074. packet->setDataByName("unknown8a", 16256, 6);
  8075. packet->setDataByName("unknown8a", 16256, 10);
  8076. //packet->PrintPacket();
  8077. EQ2Packet* outapp = packet->serialize();
  8078. //DumpPacket(outapp);
  8079. QueuePacket(outapp);
  8080. safe_delete(packet);
  8081. }
  8082. safe_delete(items);
  8083. }
  8084. }
  8085. }
  8086. void Client::SendBuyBackList(bool sell) {
  8087. if (GetVersion() <= 561) //this wasn't added until LU37 on July 31st 2007, well after the DoF client
  8088. return;
  8089. Spawn* spawn = GetMerchantTransaction();
  8090. if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
  8091. deque<BuyBackItem*>::iterator itr;
  8092. int i = 0;
  8093. Item* master_item = 0;
  8094. BuyBackItem* buyback = 0;
  8095. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  8096. if (packet) {
  8097. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  8098. packet->setArrayLengthByName("num_items", buy_back_items.size());
  8099. sint8 item_difficulty = 0;
  8100. MBuyBack.readlock(__FUNCTION__, __LINE__);
  8101. int tmp_level = 0;
  8102. for (itr = buy_back_items.begin(); itr != buy_back_items.end(); itr++, i++) {
  8103. buyback = *itr;
  8104. master_item = master_item_list.GetItem(buyback->item_id);
  8105. if (master_item) {
  8106. packet->setArrayDataByName("item_name", master_item->name.c_str(), i);
  8107. packet->setArrayDataByName("price", buyback->price, i);
  8108. packet->setArrayDataByName("item_id", master_item->details.item_id, i);
  8109. packet->setArrayDataByName("unique_item_id", buyback->unique_id, i);
  8110. packet->setArrayDataByName("stack_size", buyback->quantity, i);
  8111. packet->setArrayDataByName("icon", master_item->GetIcon(GetVersion()), i);
  8112. if (master_item->generic_info.adventure_default_level > 0)
  8113. tmp_level = master_item->generic_info.adventure_default_level;
  8114. else
  8115. tmp_level = master_item->generic_info.tradeskill_default_level;
  8116. packet->setArrayDataByName("level", tmp_level, i);
  8117. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
  8118. packet->setArrayDataByName("tier", master_item->details.tier, i);
  8119. }
  8120. packet->setArrayDataByName("item_id2", master_item->details.item_id, i);
  8121. item_difficulty = player->GetArrowColor(tmp_level);
  8122. if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
  8123. item_difficulty = ARROW_COLOR_WHITE;
  8124. sint64 overrideValue = 0;
  8125. if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "item_difficulty", master_item, player, nullptr, &overrideValue))
  8126. item_difficulty = (sint8)overrideValue;
  8127. item_difficulty -= 6;
  8128. if (item_difficulty < 0)
  8129. item_difficulty *= -1;
  8130. packet->setArrayDataByName("item_difficulty", item_difficulty, i);
  8131. sint64 dispFlags = 0;
  8132. if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "buyback_display_flags", master_item, player, nullptr, &dispFlags))
  8133. packet->setArrayDataByName("display_flags", (int8)dispFlags, i);
  8134. if (buyback->quantity == 1)
  8135. packet->setArrayDataByName("quantity", 0xFFFF, i);
  8136. else
  8137. packet->setArrayDataByName("quantity", buyback->quantity, i);
  8138. packet->setArrayDataByName("stack_size2", buyback->quantity, i);
  8139. if (GetVersion() <= 1096)
  8140. packet->setArrayDataByName("description", master_item->description.c_str(), i);
  8141. }
  8142. }
  8143. MBuyBack.releasereadlock(__FUNCTION__, __LINE__);
  8144. if (sell)
  8145. packet->setDataByName("type", 640);
  8146. else
  8147. packet->setDataByName("type", 512);
  8148. EQ2Packet* outapp = packet->serialize();
  8149. // DumpPacket(outapp);
  8150. QueuePacket(outapp);
  8151. safe_delete(packet);
  8152. }
  8153. }
  8154. }
  8155. void Client::SendRepairList() {
  8156. Spawn* spawn = GetMerchantTransaction();
  8157. if (spawn) {
  8158. vector<Item*>* repairable_items = GetRepairableItems();
  8159. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  8160. if (packet) {
  8161. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  8162. packet->setArrayLengthByName("num_items", repairable_items->size());
  8163. Item* item = 0;
  8164. sint8 item_difficulty = 0;
  8165. int32 i = 0;
  8166. vector<Item*>::iterator itr;
  8167. for (itr = repairable_items->begin(); itr != repairable_items->end(); itr++, i++) {
  8168. item = *itr;
  8169. packet->setArrayDataByName("item_name", item->name.c_str(), i);
  8170. packet->setArrayDataByName("price", item->CalculateRepairCost(), i);
  8171. packet->setArrayDataByName("item_id", item->details.item_id, i);
  8172. packet->setArrayDataByName("stack_size", item->details.count, i);
  8173. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  8174. /*if (item->generic_info.adventure_default_level > 0)
  8175. tmp_level = item->generic_info.adventure_default_level;
  8176. else
  8177. tmp_level = item->generic_info.tradeskill_default_level;
  8178. packet->setArrayDataByName("level", tmp_level, i);*/
  8179. packet->setArrayDataByName("level", item->generic_info.adventure_default_level, i);
  8180. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
  8181. packet->setArrayDataByName("tier", item->details.tier, i);
  8182. }
  8183. packet->setArrayDataByName("item_id2", item->details.item_id, i);
  8184. item_difficulty = player->GetArrowColor(item->generic_info.adventure_default_level);
  8185. if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
  8186. item_difficulty = ARROW_COLOR_WHITE;
  8187. item_difficulty -= 6;
  8188. if (item_difficulty < 0)
  8189. item_difficulty *= -1;
  8190. packet->setArrayDataByName("item_difficulty", item_difficulty, i);
  8191. if (item->details.count == 1)
  8192. packet->setArrayDataByName("quantity", 0xFFFF, i);
  8193. else
  8194. packet->setArrayDataByName("quantity", item->details.count, i);
  8195. packet->setArrayDataByName("stack_size2", item->details.count, i);
  8196. if (GetVersion() <= 1096)
  8197. packet->setArrayDataByName("description", item->description.c_str(), i);
  8198. }
  8199. if (GetVersion() <= 561) {
  8200. packet->setDataByName("type", 112);
  8201. }
  8202. else {
  8203. packet->setDataByName("type", 96);
  8204. }
  8205. EQ2Packet* outapp = packet->serialize();
  8206. //DumpPacket(outapp);
  8207. QueuePacket(outapp);
  8208. /*if (GetVersion() <= 561) {
  8209. packet->setDataByName("type", 16);
  8210. EQ2Packet* outapp2 = packet->serialize();
  8211. QueuePacket(outapp2);
  8212. }*/
  8213. safe_delete(packet);
  8214. }
  8215. safe_delete(repairable_items);
  8216. }
  8217. }
  8218. void Client::ShowLottoWindow() {
  8219. if (GetVersion() <= 373) {
  8220. SimpleMessage(CHANNEL_COLOR_RED, "This client does not support the gambler UI, only Desert of Flames or later client.");
  8221. return;
  8222. }
  8223. Spawn* spawn = GetMerchantTransaction();
  8224. if (spawn) {
  8225. int32 item_id = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, GamblingTokenItemID)->GetInt32();
  8226. if (!item_id)
  8227. {
  8228. LogWrite(WORLD__ERROR, 0, "World", "No GamblingTokenItemID rule set!");
  8229. SimpleMessage(CHANNEL_COLOR_RED, "The server admin has not setup a lotto item ticket.");
  8230. return;
  8231. }
  8232. else if (item_id == 0)
  8233. {
  8234. LogWrite(WORLD__ERROR, 0, "World", "Error! Invalid GamblingTokenItemID value!");
  8235. return;
  8236. }
  8237. Item* item = master_item_list.GetItem(item_id);
  8238. if (!item) {
  8239. LogWrite(WORLD__ERROR, 0, "World", "The 'GamblingTokenItemID' rule value %u is not a valid item id.", item_id);
  8240. return;
  8241. }
  8242. LogWrite(WORLD__DEBUG, 0, "World", "GamblingTokenItemID = '%s' (%u)", item->name.c_str(), item_id);
  8243. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  8244. if (packet) {
  8245. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  8246. packet->setArrayLengthByName("num_items", 1);
  8247. packet->setArrayDataByName("item_name", item->name.c_str());
  8248. packet->setArrayDataByName("price", item->sell_price);
  8249. packet->setArrayDataByName("item_id", item->details.item_id);
  8250. packet->setArrayDataByName("stack_size", item->details.count);
  8251. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()));
  8252. packet->setArrayDataByName("level", item->generic_info.adventure_default_level);
  8253. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
  8254. packet->setArrayDataByName("tier", item->details.tier);
  8255. }
  8256. packet->setArrayDataByName("item_id2", item->details.item_id);
  8257. int8 item_difficulty = player->GetArrowColor(item->generic_info.adventure_default_level);
  8258. if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
  8259. item_difficulty = ARROW_COLOR_WHITE;
  8260. item_difficulty -= 6;
  8261. if (item_difficulty < 0)
  8262. item_difficulty *= -1;
  8263. packet->setArrayDataByName("item_difficulty", item_difficulty);
  8264. //if(item->details.count == 1)
  8265. packet->setArrayDataByName("quantity", 0xFFFF);
  8266. //else
  8267. // packet->setArrayDataByName("quantity", item->details.count);
  8268. packet->setArrayDataByName("stack_size2", item->details.count);
  8269. packet->setArrayDataByName("description", item->description.c_str());
  8270. if (GetVersion() <= 546) {
  8271. packet->setDataByName("type", 128);
  8272. }
  8273. else {
  8274. packet->setDataByName("type", 0x00000102);
  8275. }
  8276. QueuePacket(packet->serialize());
  8277. safe_delete(packet);
  8278. }
  8279. }
  8280. }
  8281. void Client::PlayLotto(int32 price, int32 ticket_item_id) {
  8282. PacketStruct* packet = configReader.getStruct("WS_Lottery", GetVersion());
  8283. if (packet) {
  8284. world.AddLottoPlayer(GetCharacterID(), Timer::GetCurrentTime2() + 4500);
  8285. int32 rolls[6] = { 0 };
  8286. int32 lottery_digits[6] = { 0 };
  8287. int8 num_matches = 0;
  8288. int64 jackpot = 0;
  8289. Item* item = GetPlayer()->item_list.GetItemFromID(ticket_item_id);
  8290. if (!item) {
  8291. return;
  8292. }
  8293. database.DeleteItem(GetCharacterID(), item, 0);
  8294. GetPlayer()->item_list.RemoveItem(item, true);
  8295. QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
  8296. Variable* winning_numbers = variables.FindVariable("gambling_winning_numbers");
  8297. if (!winning_numbers) {
  8298. winning_numbers = new Variable("gambling_winning_numbers", "231205182236", "Current Gigglegibber Gambling Game winning numbers");
  8299. variables.AddVariable(winning_numbers);
  8300. database.SaveVariable(winning_numbers->GetName(), winning_numbers->GetValue(), winning_numbers->GetComment());
  8301. }
  8302. if (strlen(winning_numbers->GetValue()) != 12) {
  8303. winning_numbers->SetValue("231205182236");
  8304. database.SaveVariable(winning_numbers->GetName(), winning_numbers->GetValue(), winning_numbers->GetComment());
  8305. }
  8306. try {
  8307. for (int32 i = 0; i < 12; i += 2) {
  8308. char num[4];
  8309. strncpy(num, winning_numbers->GetValue() + i, 2);
  8310. lottery_digits[i / 2] = atoi(num);
  8311. }
  8312. }
  8313. catch (...) {
  8314. LogWrite(WORLD__ERROR, 0, "World", "Error parsing 'gambling_winning_numbers' variable");
  8315. return;
  8316. }
  8317. Variable* jackpot_var = variables.FindVariable("gambling_current_jackpot");
  8318. if (!jackpot_var) {
  8319. jackpot_var = new Variable("gambling_current_jackpot", "10000", "Current Gigglegibber Gambling Game Jackpot");
  8320. variables.AddVariable(jackpot_var);
  8321. database.SaveVariable(jackpot_var->GetName(), jackpot_var->GetValue(), jackpot_var->GetComment());
  8322. }
  8323. try {
  8324. jackpot = atoul(jackpot_var->GetValue());
  8325. if (jackpot < 10000)
  8326. jackpot = 10000;
  8327. }
  8328. catch (...) {
  8329. jackpot = 10000;
  8330. }
  8331. char new_jackpot[128] = { 0 };
  8332. sprintf(new_jackpot, "%llu", jackpot + price);
  8333. jackpot_var->SetValue(new_jackpot);
  8334. database.SaveVariable(jackpot_var->GetName(), jackpot_var->GetValue(), jackpot_var->GetComment());
  8335. world.PickRandomLottoDigits(rolls);
  8336. packet->setDataByName("roll_digit1", rolls[0]);
  8337. packet->setDataByName("roll_digit2", rolls[1]);
  8338. packet->setDataByName("roll_digit3", rolls[2]);
  8339. packet->setDataByName("roll_digit4", rolls[3]);
  8340. packet->setDataByName("roll_digit5", rolls[4]);
  8341. packet->setDataByName("roll_digit6", rolls[5]);
  8342. packet->setDataByName("lottery_digit1", lottery_digits[0]);
  8343. packet->setDataByName("lottery_digit2", lottery_digits[1]);
  8344. packet->setDataByName("lottery_digit3", lottery_digits[2]);
  8345. packet->setDataByName("lottery_digit4", lottery_digits[3]);
  8346. packet->setDataByName("lottery_digit5", lottery_digits[4]);
  8347. packet->setDataByName("lottery_digit6", lottery_digits[5]);
  8348. QueuePacket(packet->serialize());
  8349. safe_delete(packet);
  8350. for (int32 i = 0; i < 6; i++) {
  8351. for (int32 j = 0; j < 6; j++) {
  8352. if (rolls[i] == lottery_digits[j]) {
  8353. num_matches++;
  8354. break;
  8355. }
  8356. }
  8357. }
  8358. char new_jackpot_str[16];
  8359. memset(new_jackpot_str, 0, sizeof(new_jackpot_str));
  8360. world.SetLottoPlayerNumMatches(GetCharacterID(), num_matches);
  8361. if (num_matches == 6) {
  8362. world.PickRandomLottoDigits(lottery_digits);
  8363. for (int32 i = 0; i < 12; i += 2)
  8364. sprintf(new_jackpot_str + i, "%02d", lottery_digits[i / 2]);
  8365. winning_numbers->SetValue(new_jackpot_str);
  8366. jackpot_var->SetValue("10000");
  8367. database.SaveVariable(winning_numbers->GetName(), winning_numbers->GetValue(), winning_numbers->GetComment());
  8368. database.SaveVariable(jackpot_var->GetName(), jackpot_var->GetValue(), jackpot_var->GetComment());
  8369. }
  8370. }
  8371. }
  8372. void Client::SendGuildCreateWindow() {
  8373. if (GetVersion() <= 561) {
  8374. SimpleMessage(0, "Not implemented on this client...yet?");
  8375. }
  8376. else {
  8377. Spawn* spawn = GetPlayer()->GetTarget();
  8378. if (spawn) {
  8379. PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
  8380. if (packet) {
  8381. packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
  8382. packet->setArrayLengthByName("num_items", 0);
  8383. packet->setDataByName("type", 0x00008000);
  8384. QueuePacket(packet->serialize());
  8385. safe_delete(packet);
  8386. }
  8387. }
  8388. }
  8389. }
  8390. void Client::AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) {
  8391. BuyBackItem* item = new BuyBackItem;
  8392. item->item_id = item_id;
  8393. item->unique_id = unique_id;
  8394. item->price = price;
  8395. item->quantity = quantity;
  8396. item->save_needed = save_needed;
  8397. MBuyBack.writelock(__FUNCTION__, __LINE__);
  8398. buy_back_items.push_back(item);
  8399. if (buy_back_items.size() > 10) {
  8400. safe_delete(buy_back_items.front());
  8401. buy_back_items.pop_front();
  8402. }
  8403. MBuyBack.releasewritelock(__FUNCTION__, __LINE__);
  8404. }
  8405. deque<BuyBackItem*>* Client::GetBuyBacks() {
  8406. return &buy_back_items;
  8407. }
  8408. vector<Item*>* Client::GetRepairableItems() {
  8409. vector<Item*>* repairable_items = new vector<Item*>;
  8410. vector<Item*>* equipped_items = player->GetEquipmentList()->GetAllEquippedItems();
  8411. map<int32, Item*>* items = player->GetItemList();
  8412. if (equipped_items && equipped_items->size() > 0) {
  8413. for (int32 i = 0; i < equipped_items->size(); i++) {
  8414. Item* item = equipped_items->at(i);
  8415. if (item && !item->CheckFlag2(NO_REPAIR) && item->generic_info.condition < 100)
  8416. repairable_items->push_back(item);
  8417. }
  8418. }
  8419. if (items && items->size() > 0) {
  8420. map<int32, Item*>::iterator itr;
  8421. for (itr = items->begin(); itr != items->end(); itr++) {
  8422. Item* item = itr->second;
  8423. if (item && !item->CheckFlag2(NO_REPAIR) && item->generic_info.condition < 100)
  8424. repairable_items->push_back(item);
  8425. }
  8426. }
  8427. safe_delete(equipped_items);
  8428. safe_delete(items);
  8429. return repairable_items;
  8430. }
  8431. vector<Item*>* Client::GetItemsByEffectType(ItemEffectType type, ItemEffectType type2) {
  8432. if (type == NO_EFFECT_TYPE)
  8433. return nullptr;
  8434. vector<Item*>* return_items = new vector<Item*>;
  8435. vector<Item*>* equipped_items = player->GetEquipmentList()->GetAllEquippedItems();
  8436. map<int32, Item*>* items = player->GetItemList();
  8437. if (equipped_items && equipped_items->size() > 0) {
  8438. for (int32 i = 0; i < equipped_items->size(); i++) {
  8439. Item* item = equipped_items->at(i);
  8440. if (item && (item->effect_type == type || (type2 != NO_EFFECT_TYPE && item->effect_type == type2)))
  8441. return_items->push_back(item);
  8442. }
  8443. }
  8444. if (items && items->size() > 0) {
  8445. map<int32, Item*>::iterator itr;
  8446. for (itr = items->begin(); itr != items->end(); itr++) {
  8447. Item* item = itr->second;
  8448. if (item && (item->effect_type == type || (type2 != NO_EFFECT_TYPE && item->effect_type == type2)))
  8449. return_items->push_back(item);
  8450. }
  8451. }
  8452. safe_delete(equipped_items);
  8453. safe_delete(items);
  8454. return return_items;
  8455. }
  8456. void Client::SendMailList() {
  8457. int32 kiosk_id = player->GetIDWithPlayerSpawn(GetMailTransaction());
  8458. if (kiosk_id > 0) {
  8459. PacketStruct* p = configReader.getStruct("WS_GetMailHeader", GetVersion());
  8460. if (p) {
  8461. MutexMap<int32, Mail*>* mail_list = player->GetMail();
  8462. MutexMap<int32, Mail*>::iterator itr = mail_list->begin();
  8463. int32 i = 0;
  8464. p->setDataByName("kiosk_id", kiosk_id);
  8465. p->setArrayLengthByName("num_messages", (int16)mail_list->size());
  8466. while (itr.Next()) {
  8467. Mail* mail = itr->second;
  8468. p->setArrayDataByName("mail_id", mail->mail_id, i);
  8469. p->setArrayDataByName("player_to_id", mail->player_to_id, i);
  8470. p->setArrayDataByName("player_from", mail->player_from.c_str(), i);
  8471. p->setArrayDataByName("subject", mail->subject.c_str(), i);
  8472. p->setArrayDataByName("already_read", mail->already_read, i);
  8473. if (mail->expire_time)
  8474. p->setArrayDataByName("mail_deletion", mail->expire_time - mail->time_sent, i);
  8475. else
  8476. p->setArrayDataByName("mail_deletion", 0, i);
  8477. p->setArrayDataByName("mail_type", mail->mail_type, i);
  8478. p->setArrayDataByName("mail_expire", 0xFFFFFFFF, i);
  8479. p->setArrayDataByName("unknown1a", 0xFFFFFFFF, i);
  8480. p->setArrayDataByName("coin_copper", mail->coin_copper, i);
  8481. p->setArrayDataByName("coin_silver", mail->coin_silver, i);
  8482. p->setArrayDataByName("coin_gold", mail->coin_gold, i);
  8483. p->setArrayDataByName("coin_plat", mail->coin_plat, i);
  8484. //p->setArrayDataByName("unknown2", 0, i);
  8485. bool successItemAdd = false;
  8486. if (mail->stack || mail->char_item_id)
  8487. {
  8488. Item* item = master_item_list.GetItem(mail->char_item_id);
  8489. if (item)
  8490. {
  8491. item->stack_count = mail->stack > 1 ? mail->stack : 0;
  8492. if (version < 860)
  8493. p->setItemArrayDataByName("item", item, player, i, 0, GetClientItemPacketOffset());
  8494. else if (version < 1193)
  8495. p->setItemArrayDataByName("item", item, player, i);
  8496. else
  8497. p->setItemArrayDataByName("item", item, player, i, 0, 2);
  8498. successItemAdd = true;
  8499. }
  8500. }
  8501. if (!successItemAdd)
  8502. {
  8503. p->setArrayDataByName("end_tag2", GetItemPacketType(GetVersion()), i);
  8504. p->setArrayDataByName("end_tag3", 0xFF, i);
  8505. }
  8506. i++;
  8507. }
  8508. // GMs send mail for free!
  8509. if (GetAdminStatus() > 0)
  8510. {
  8511. p->setDataByName("postage_cost", 0);
  8512. p->setDataByName("attachment_cost", 0);
  8513. }
  8514. else
  8515. {
  8516. p->setDataByName("postage_cost", 10);
  8517. p->setDataByName("attachment_cost", 50);
  8518. }
  8519. p->setDataByName("unknown3", 0x01F4);
  8520. p->setDataByName("unknown4", 0x01000000);
  8521. EQ2Packet* pack = p->serialize();
  8522. //DumpPacket(pack);
  8523. QueuePacket(pack);
  8524. safe_delete(p);
  8525. }
  8526. }
  8527. else
  8528. SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
  8529. }
  8530. void Client::DisplayMailMessage(int32 mail_id) {
  8531. Mail* mail = player->GetMail(mail_id);
  8532. if (mail) {
  8533. int32 kiosk_id = player->GetIDWithPlayerSpawn(GetMailTransaction());
  8534. if (kiosk_id > 0) {
  8535. PacketStruct* update = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
  8536. if (update) {
  8537. update->setDataByName("action", 0x03);
  8538. update->setDataByName("packettype", GetItemPacketType(GetVersion()));
  8539. update->setDataByName("packetsubtype", 0xFF);
  8540. QueuePacket(update->serialize());
  8541. safe_delete(update);
  8542. }
  8543. if (!mail->already_read) {
  8544. mail->already_read = true;
  8545. SendMailList();
  8546. }
  8547. PacketStruct* packet = configReader.getStruct("WS_MailGetMessage", GetVersion());
  8548. if (packet) {
  8549. packet->setDataByName("kiosk_id", kiosk_id);
  8550. packet->setDataByName("mail_id", mail->mail_id);
  8551. packet->setDataByName("player_to_id", mail->player_to_id);
  8552. packet->setDataByName("player_from", mail->player_from.c_str());
  8553. packet->setDataByName("subject", mail->subject.c_str());
  8554. packet->setDataByName("mail_body", mail->mail_body.c_str());
  8555. packet->setDataByName("unknown1", 1);
  8556. packet->setDataByName("unknown2", 0);
  8557. packet->setDataByName("lock_report_button", 1);
  8558. packet->setDataByName("unknown3", 0xFFFFFFFF);
  8559. packet->setDataByName("unknown3a", 0xFFFFFFFF);
  8560. packet->setDataByName("coin_copper", mail->coin_copper);
  8561. packet->setDataByName("coin_silver", mail->coin_silver);
  8562. packet->setDataByName("coin_gold", mail->coin_gold);
  8563. packet->setDataByName("coin_plat", mail->coin_plat);
  8564. if (mail->stack || mail->char_item_id)
  8565. {
  8566. Item* item = master_item_list.GetItem(mail->char_item_id);
  8567. item->stack_count = mail->stack > 1 ? mail->stack : 0;
  8568. if (version < 860)
  8569. packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1);
  8570. else if (version < 1193)
  8571. packet->setItemByName("item", item, player, 0, 0);
  8572. else
  8573. packet->setItemByName("item", item, player, 0, 2);
  8574. }
  8575. else
  8576. {
  8577. packet->setDataByName("end_tag2", GetItemPacketType(GetVersion()));
  8578. packet->setDataByName("end_tag3", 0xFF);
  8579. }
  8580. mail->save_needed = true;
  8581. EQ2Packet* pack = packet->serialize();
  8582. QueuePacket(pack);
  8583. safe_delete(packet);
  8584. // trying to update this causes the window not to open
  8585. //SendMailList();
  8586. }
  8587. }
  8588. else
  8589. SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
  8590. }
  8591. }
  8592. /* This is called when the client sends a mail message. This determines whether or not the mail can be sent and must send the reply
  8593. packet back to the client before the mail actually sent. */
  8594. void Client::HandleSentMail(EQApplicationPacket* app) {
  8595. PacketStruct* packet = configReader.getStruct("WS_MailSendMessage", GetVersion());
  8596. if (packet) {
  8597. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  8598. string player_to = packet->getType_EQ2_16BitString_ByName("player_to").data;
  8599. PacketStruct* reply_packet = configReader.getStruct("WS_MailSendMessageReply", GetVersion());
  8600. vector<int32>* ids = 0;
  8601. MMailWindowMutex.lock();
  8602. if (reply_packet) {
  8603. int8 reply_type = MAIL_SEND_RESULT_UNKNOWN_ERROR;
  8604. if (player_to.length() == 0)
  8605. reply_type = MAIL_SEND_RESULT_EMPTY_TO_LIST;
  8606. else if (player_to.compare(string(GetPlayer()->GetName())) == 0)
  8607. reply_type = MAIL_SEND_RESULT_CANNOT_SEND_TO_SELF;
  8608. else if (GetAdminStatus() == 0 && !player->RemoveCoins(10))
  8609. reply_type = MAIL_SEND_RESULT_NOT_ENOUGH_COIN;
  8610. else {
  8611. if (GetAdminStatus() > 200 && player_to.compare("<all>") == 0) {
  8612. if (mail_window.char_item_id == 0 && (mail_window.coin_copper + mail_window.coin_silver + mail_window.coin_gold + mail_window.coin_plat) == 0)
  8613. ids = database.GetAllPlayerIDs();
  8614. else
  8615. SimpleMessage(CHANNEL_NARRATIVE, "You may not mail gifts to multiple players.");
  8616. }
  8617. else {
  8618. ids = new vector<int32>;
  8619. ids->push_back(database.GetCharacterID(player_to.c_str()));
  8620. }
  8621. if (ids) {
  8622. for (int32 i = 0; i < ids->size(); i++) {
  8623. int32 player_to_id = ids->at(i);
  8624. if (player_to_id > 0) {
  8625. reply_type = MAIL_SEND_RESULT_SUCCESS;
  8626. Mail* mail = new Mail;
  8627. mail->mail_id = 0;
  8628. mail->player_to_id = player_to_id;
  8629. mail->player_from = string(GetPlayer()->GetName());
  8630. mail->subject = packet->getType_EQ2_16BitString_ByName("subject").data;
  8631. mail->mail_body = packet->getType_EQ2_16BitString_ByName("mail_body").data;
  8632. mail->already_read = 0;
  8633. mail->mail_type = MAIL_TYPE_REGULAR;
  8634. mail->coin_copper = mail_window.coin_copper;
  8635. mail->coin_silver = mail_window.coin_silver;
  8636. mail->coin_gold = mail_window.coin_gold;
  8637. mail->coin_plat = mail_window.coin_plat;
  8638. mail->char_item_id = mail_window.char_item_id;
  8639. mail->stack = mail_window.stack;
  8640. // GM's send mail for free!
  8641. if (GetAdminStatus() > 0)
  8642. {
  8643. mail->postage_cost = 0;
  8644. mail->attachment_cost = 0;
  8645. }
  8646. else
  8647. {
  8648. mail->postage_cost = 10;
  8649. mail->attachment_cost = 50;
  8650. }
  8651. mail->time_sent = Timer::GetUnixTimeStamp();
  8652. mail->expire_time = mail->time_sent + 2592000; //30 days in seconds
  8653. mail->save_needed = false;
  8654. database.SavePlayerMail(mail);
  8655. Client* to_client = zone_list.GetClientByCharID(player_to_id);
  8656. if (to_client) {
  8657. to_client->GetPlayer()->AddMail(mail);
  8658. to_client->SimpleMessage(CHANNEL_NARRATIVE, "You have unread mail in your mailbox.");
  8659. string popup_text = "You have unread mail!";
  8660. to_client->SendPopupMessage(10, popup_text.c_str(), "", 3, 0xFF, 0xFF, 0xFF);
  8661. }
  8662. else {
  8663. // don't need the pointer the client doesn't exist currently
  8664. safe_delete(mail);
  8665. }
  8666. ResetSendMail(false, false);
  8667. }
  8668. else
  8669. reply_type = MAIL_SEND_RESULT_UNKNOWN_PLAYER;
  8670. }
  8671. }
  8672. }
  8673. string players_to = "";
  8674. if (ids) {
  8675. for (int32 i = 0; i < ids->size(); i++) {
  8676. if (ids->at(i) != 0)
  8677. players_to.append(database.GetCharacterName(ids->at(i)));
  8678. if (i < (ids->size() - 1))
  8679. players_to.append(", ");
  8680. }
  8681. }
  8682. reply_packet->setDataByName("player_to", players_to.c_str());
  8683. reply_packet->setDataByName("reply_type", reply_type);
  8684. QueuePacket(reply_packet->serialize());
  8685. safe_delete(reply_packet);
  8686. safe_delete(ids);
  8687. }
  8688. MMailWindowMutex.unlock();
  8689. }
  8690. safe_delete(packet);
  8691. }
  8692. }
  8693. void Client::DeleteMail(int32 mail_id, bool from_database) {
  8694. player->DeleteMail(mail_id, from_database);
  8695. }
  8696. bool Client::AddMailItem(Item* item)
  8697. {
  8698. if (item && (item->CheckFlag(LORE) || item->CheckFlag(STACK_LORE))) {
  8699. Message(CHANNEL_COLOR_CHAT_RELATIONSHIP, "Lore items cannot be mailed.");
  8700. return false;
  8701. }
  8702. bool ret = false;
  8703. if (GetMailTransaction()) {
  8704. MMailWindowMutex.lock();
  8705. if (mail_window.char_item_id == 0)
  8706. {
  8707. mail_window.item = item;
  8708. mail_window.char_item_id = item->details.item_id;
  8709. mail_window.stack = item->details.count;
  8710. ret = true;
  8711. PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
  8712. packet->setDataByName("coin_copper", mail_window.coin_copper);
  8713. packet->setDataByName("coin_silver", mail_window.coin_silver);
  8714. packet->setDataByName("coin_gold", mail_window.coin_gold);
  8715. packet->setDataByName("coin_plat", mail_window.coin_plat);
  8716. if (item)
  8717. {
  8718. packet->setDataByName("stack", mail_window.stack);
  8719. item->stack_count = mail_window.stack;
  8720. if (version < 860)
  8721. packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1);
  8722. else if (version < 1193)
  8723. packet->setItemByName("item", item, player, 0, 0);
  8724. else
  8725. packet->setItemByName("item", item, player, 0, 2);
  8726. }
  8727. else
  8728. {
  8729. packet->setDataByName("end_tag2", GetItemPacketType(GetVersion()));
  8730. packet->setDataByName("end_tag3", 0xFF);
  8731. }
  8732. QueuePacket(packet->serialize());
  8733. }
  8734. MMailWindowMutex.unlock();
  8735. }
  8736. return ret;
  8737. }
  8738. bool Client::AddMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
  8739. bool ret = false;
  8740. if (GetMailTransaction()) {
  8741. MMailWindowMutex.lock();
  8742. PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
  8743. if (packet) {
  8744. if (copper > 0) {
  8745. if (player->RemoveCoins(copper)) {
  8746. mail_window.coin_copper += copper;
  8747. Message(CHANNEL_NARRATIVE, "You add %u copper to the mail window.", copper);
  8748. ret = true;
  8749. }
  8750. }
  8751. else if (silver > 0) {
  8752. if (player->RemoveCoins(silver * 100)) {
  8753. mail_window.coin_silver += silver;
  8754. Message(CHANNEL_NARRATIVE, "You add %u silver to the mail window.", silver);
  8755. ret = true;
  8756. }
  8757. }
  8758. else if (gold > 0) {
  8759. if (player->RemoveCoins(gold * 10000)) {
  8760. mail_window.coin_gold += gold;
  8761. Message(CHANNEL_NARRATIVE, "You add %u gold to the mail window.", gold);
  8762. ret = true;
  8763. }
  8764. }
  8765. else if (plat > 0) {
  8766. if (player->RemoveCoins(plat * 1000000)) {
  8767. mail_window.coin_plat += plat;
  8768. Message(CHANNEL_NARRATIVE, "You add %u platinum to the mail window.", plat);
  8769. ret = true;
  8770. }
  8771. }
  8772. if (ret) {
  8773. packet->setDataByName("coin_copper", mail_window.coin_copper);
  8774. packet->setDataByName("coin_silver", mail_window.coin_silver);
  8775. packet->setDataByName("coin_gold", mail_window.coin_gold);
  8776. packet->setDataByName("coin_plat", mail_window.coin_plat);
  8777. Item* item = master_item_list.GetItem(mail_window.char_item_id);
  8778. if (item)
  8779. {
  8780. packet->setDataByName("stack", mail_window.stack);
  8781. item->stack_count = mail_window.stack;
  8782. if (version < 860)
  8783. packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1);
  8784. else if (version < 1193)
  8785. packet->setItemByName("item", item, player, 0, 0);
  8786. else
  8787. packet->setItemByName("item", item, player, 0, 2);
  8788. }
  8789. else
  8790. {
  8791. packet->setDataByName("end_tag2", GetItemPacketType(GetVersion()));
  8792. packet->setDataByName("end_tag3", 0xFF);
  8793. }
  8794. //packet->PrintPacket();
  8795. QueuePacket(packet->serialize());
  8796. }
  8797. else
  8798. SimpleMessage(CHANNEL_NARRATIVE, "You don't have that much money.");
  8799. safe_delete(packet);
  8800. }
  8801. MMailWindowMutex.unlock();
  8802. }
  8803. else
  8804. SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
  8805. return ret;
  8806. }
  8807. bool Client::RemoveMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
  8808. bool ret = false;
  8809. if (GetMailTransaction()) {
  8810. MMailWindowMutex.lock();
  8811. PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
  8812. if (packet) {
  8813. if (copper > 0) {
  8814. player->AddCoins(copper);
  8815. mail_window.coin_copper -= copper;
  8816. Message(CHANNEL_NARRATIVE, "You remove %u copper from the mail window.", copper);
  8817. ret = true;
  8818. }
  8819. else if (silver > 0) {
  8820. player->AddCoins(silver * 100);
  8821. mail_window.coin_silver -= silver;
  8822. Message(CHANNEL_NARRATIVE, "You remove %u silver from the mail window.", silver);
  8823. ret = true;
  8824. }
  8825. else if (gold > 0) {
  8826. player->AddCoins(gold * 10000);
  8827. mail_window.coin_gold -= gold;
  8828. Message(CHANNEL_NARRATIVE, "You remove %u gold from the mail window.", gold);
  8829. ret = true;
  8830. }
  8831. else if (plat > 0) {
  8832. player->AddCoins(plat * 1000000);
  8833. mail_window.coin_plat -= plat;
  8834. Message(CHANNEL_NARRATIVE, "You remove %u platinum from the mail window.", plat);
  8835. ret = true;
  8836. }
  8837. if (ret) {
  8838. packet->setDataByName("coin_copper", mail_window.coin_copper);
  8839. packet->setDataByName("coin_silver", mail_window.coin_silver);
  8840. packet->setDataByName("coin_gold", mail_window.coin_gold);
  8841. packet->setDataByName("coin_plat", mail_window.coin_plat);
  8842. packet->setDataByName("stack", 0);
  8843. packet->setDataByName("packettype", 0x2BFE);
  8844. packet->setDataByName("packetsubtype", 0xFF);
  8845. packet->setDataByName("unknown2", 0);
  8846. QueuePacket(packet->serialize());
  8847. }
  8848. safe_delete(packet);
  8849. }
  8850. MMailWindowMutex.unlock();
  8851. }
  8852. else
  8853. SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
  8854. return ret;
  8855. }
  8856. void Client::TakeMailAttachments(int32 mail_id) {
  8857. if (GetMailTransaction()) {
  8858. Mail* mail = player->GetMail(mail_id);
  8859. if (mail) {
  8860. int64 amount = 0;
  8861. if (mail->coin_copper > 0) {
  8862. amount += mail->coin_copper;
  8863. mail->coin_copper = 0;
  8864. }
  8865. if (mail->coin_silver > 0) {
  8866. amount += mail->coin_silver * 100;
  8867. mail->coin_silver = 0;
  8868. }
  8869. if (mail->coin_gold > 0) {
  8870. amount += mail->coin_gold * 10000;
  8871. mail->coin_gold = 0;
  8872. }
  8873. if (mail->coin_plat > 0) {
  8874. amount += mail->coin_plat * 1000000;
  8875. mail->coin_plat = 0;
  8876. }
  8877. if (mail->char_item_id > 0) {
  8878. AddItem(mail->char_item_id, mail->stack);
  8879. mail->char_item_id = 0;
  8880. mail->stack = 0;
  8881. }
  8882. /* Can't find the right packet to send to update the player's mail. This packet below updates the mail the player is sending, not
  8883. the mail the player is getting attachments from. There is an opcode OP_MailRemoveAttachFromMailMsg with opcode 328 but i can't
  8884. find it in any packet logs.*/
  8885. /*PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
  8886. if (packet) {
  8887. packet->setDataByName("unknown", 0x03);
  8888. packet->setDataByName("coin_copper", mail->coin_copper);
  8889. packet->setDataByName("coin_silver", mail->coin_silver);
  8890. packet->setDataByName("coin_gold", mail->coin_gold);
  8891. packet->setDataByName("coin_plat", mail->coin_plat);
  8892. packet->setDataByName("stack", 0);
  8893. packet->setDataByName("packettype", 0x2BFE);
  8894. packet->setDataByName("packetsubtype", 0xFF);
  8895. packet->setDataByName("unknown2", 0);
  8896. packet->setDataByName("unknown3", 0x00000001);//0x00000016
  8897. DumpPacket(packet->serialize());
  8898. QueuePacket(packet->serialize());
  8899. safe_delete(packet);
  8900. }*/
  8901. database.SavePlayerMail(mail);
  8902. if (amount > 0)
  8903. player->AddCoins(amount);
  8904. SendMailList();
  8905. }
  8906. }
  8907. else
  8908. SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
  8909. }
  8910. void Client::ResetSendMail(bool cancel, bool needslock) {
  8911. if (cancel && mail_transaction)
  8912. SimpleMessage(CHANNEL_NARRATIVE, "You cancel sending a letter.");
  8913. if (needslock)
  8914. MMailWindowMutex.lock();
  8915. if (cancel)
  8916. player->AddCoins(mail_window.coin_copper + (mail_window.coin_silver * 100) + (mail_window.coin_gold * 10000) + (mail_window.coin_plat * 1000000));
  8917. if (!cancel)
  8918. mail_transaction = 0;
  8919. mail_window.coin_copper = 0;
  8920. mail_window.coin_silver = 0;
  8921. mail_window.coin_gold = 0;
  8922. mail_window.coin_plat = 0;
  8923. mail_window.char_item_id = 0;
  8924. mail_window.stack = 0;
  8925. if (mail_window.item) {
  8926. if (cancel)
  8927. AddItem(mail_window.item);
  8928. else
  8929. safe_delete(mail_window.item);
  8930. }
  8931. mail_window.item = nullptr;
  8932. if (needslock)
  8933. MMailWindowMutex.unlock();
  8934. }
  8935. bool Client::GateAllowed() {
  8936. ZoneServer* zone = GetCurrentZone();
  8937. if (zone) {
  8938. bool cangate = zone->GetCanGate();
  8939. return cangate;
  8940. }
  8941. return false;
  8942. }
  8943. bool Client::BindAllowed() {
  8944. ZoneServer* zone = GetCurrentZone();
  8945. if (zone) {
  8946. bool canbind = zone->GetCanBind();
  8947. return canbind;
  8948. }
  8949. return false;
  8950. }
  8951. bool Client::Bind() {
  8952. int canbind = BindAllowed();
  8953. if (canbind == 0) {
  8954. Message(CHANNEL_MERCHANT, "You cannot bind at this location.");
  8955. return false;
  8956. }
  8957. player->GetPlayerInfo()->SetBindZone(GetCurrentZone()->GetZoneID());
  8958. player->GetPlayerInfo()->SetBindX(player->GetX());
  8959. player->GetPlayerInfo()->SetBindY(player->GetY());
  8960. player->GetPlayerInfo()->SetBindZ(player->GetZ());
  8961. player->GetPlayerInfo()->SetBindHeading(player->GetHeading());
  8962. Message(CHANNEL_MERCHANT, "Your spirit has been bound to this location.");
  8963. return true;
  8964. }
  8965. bool Client::Gate(bool is_spell) {
  8966. if (player->GetPlayerInfo()->GetBindZoneID() == 0) {
  8967. SimpleMessage(CHANNEL_MERCHANT, "You can not cast recall spells. You have no bind location set.");
  8968. return false;
  8969. }
  8970. ZoneChangeDetails zone_details;
  8971. if (zone_list.GetZone(&zone_details, player->GetPlayerInfo()->GetBindZoneID())) {
  8972. int cangate = GateAllowed();
  8973. if (cangate == 0) {
  8974. SimpleMessage(CHANNEL_MERCHANT, "You can not cast recall spells in this zone.");
  8975. return false;
  8976. }
  8977. player->SetX(player->GetPlayerInfo()->GetBindZoneX());
  8978. player->SetY(player->GetPlayerInfo()->GetBindZoneY());
  8979. player->SetZ(player->GetPlayerInfo()->GetBindZoneZ());
  8980. player->SetHeading(player->GetPlayerInfo()->GetBindZoneHeading());
  8981. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false, is_spell);
  8982. return true;
  8983. }
  8984. return false;
  8985. }
  8986. void Client::ProcessTeleport(Spawn* spawn, vector<TransportDestination*>* destinations, int32 transport_id, bool is_spell) {
  8987. if (!destinations || !spawn) {
  8988. return;
  8989. }
  8990. bool has_map = false;
  8991. if (transport_id > 0)
  8992. has_map = GetCurrentZone()->TransportHasMap(transport_id);
  8993. transport_spawn = spawn;
  8994. vector<TransportDestination*> transport_list;
  8995. vector<TransportDestination*>::iterator itr;
  8996. TransportDestination* destination = 0;
  8997. for (itr = destinations->begin(); itr != destinations->end(); itr++) {
  8998. destination = *itr;
  8999. if (has_map || (destination->type == TRANSPORT_TYPE_ZONE && ((destination->destination_zone_id != GetCurrentZone()->GetZoneID()) || GetPlayer()->GetDistance(destination->destination_x, destination->destination_y, destination->destination_z) > 100)))
  9000. transport_list.push_back(destination);
  9001. }
  9002. if (transport_list.size() == 0 && destination) {
  9003. if (destination->destination_zone_id == 0 || destination->destination_zone_id == GetCurrentZone()->GetZoneID()) {
  9004. if (destination->type == TRANSPORT_TYPE_FLIGHT)
  9005. SendFlightAutoMount(destination->flight_path_id, destination->mount_id, destination->mount_red_color, destination->mount_green_color, destination->mount_blue_color);
  9006. else
  9007. {
  9008. EQ2Packet* app = GetPlayer()->Move(destination->destination_x, destination->destination_y, destination->destination_z, GetVersion());
  9009. if (app)
  9010. QueuePacket(app);
  9011. }
  9012. }
  9013. else {
  9014. // determine if this is an instanced zone that already exists
  9015. ZoneChangeDetails zone_details;
  9016. bool foundZone = world.GetGroupManager()->IdentifyMemberInGroupOrRaid(&zone_details, this, destination->destination_zone_id);
  9017. if (foundZone) {
  9018. GetPlayer()->SetX(destination->destination_x);
  9019. GetPlayer()->SetY(destination->destination_y);
  9020. GetPlayer()->SetZ(destination->destination_z);
  9021. GetPlayer()->SetHeading(destination->destination_heading);
  9022. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false, is_spell);
  9023. }
  9024. else {
  9025. bool isZone = zone_list.GetZone(&zone_details, destination->destination_zone_id);
  9026. if (isZone) {
  9027. GetPlayer()->SetX(destination->destination_x);
  9028. GetPlayer()->SetY(destination->destination_y);
  9029. GetPlayer()->SetZ(destination->destination_z);
  9030. GetPlayer()->SetHeading(destination->destination_heading);
  9031. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false, is_spell);
  9032. }
  9033. else {
  9034. SimpleMessage(CHANNEL_COLOR_RED, "Error establishing a zone destination");
  9035. }
  9036. }
  9037. }
  9038. if (destination->message.length() > 0)
  9039. SimpleMessage(CHANNEL_COLOR_YELLOW, destination->message.c_str());
  9040. }
  9041. else if (transport_list.size() > 0) {
  9042. if (!spawn->IsSoundsDisabled())
  9043. PlaySound("mariner_bell");
  9044. PacketStruct* packet = configReader.getStruct("WS_TeleportList", GetVersion());
  9045. if (packet) {
  9046. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
  9047. // Put all the destinations the player can go in a new vector
  9048. vector<TransportDestination*> destinations;
  9049. for (int32 i = 0; i < transport_list.size(); i++) {
  9050. destination = transport_list.at(i);
  9051. // Check min level
  9052. if (destination->min_level > 0 && GetPlayer()->GetLevel() < destination->min_level)
  9053. continue;
  9054. // Check max level
  9055. if (destination->max_level > 0 && GetPlayer()->GetLevel() > destination->max_level)
  9056. continue;
  9057. // Check quest complete
  9058. if (destination->req_quest_complete > 0 && GetPlayer()->HasQuestBeenCompleted(destination->req_quest_complete) == 0)
  9059. continue;
  9060. // Check req quest and step
  9061. if (destination->req_quest > 0 && destination->req_quest_step > 0 && GetPlayer()->GetQuestStep(destination->req_quest) != destination->req_quest_step)
  9062. continue;
  9063. // If we have a map and our current location is the same as the detination and player is within 20 units from the transport set the "current" elements but don't addt to the destination list
  9064. if (has_map && (destination->destination_zone_id == GetCurrentZone()->GetZoneID() && GetPlayer()->GetDistance(destination->destination_x, destination->destination_y, destination->destination_z) < 20)) {
  9065. packet->setDataByName("current_zone", destination->display_name.c_str());
  9066. packet->setDataByName("current_map_x", destination->map_x);
  9067. packet->setDataByName("current_map_y", destination->map_y);
  9068. }
  9069. else {
  9070. destinations.push_back(destination);
  9071. }
  9072. }
  9073. // Use the new vector to create the packet
  9074. destination = 0;
  9075. packet->setArrayLengthByName("num_destinations", destinations.size());
  9076. for (int32 i = 0; i < destinations.size(); i++) {
  9077. destination = destinations.at(i);
  9078. packet->setArrayDataByName("unique_id", destination->unique_id, i);
  9079. packet->setArrayDataByName("display_name", destination->display_name.c_str(), i);
  9080. packet->setArrayDataByName("zone_name", destination->display_name.c_str(), i);
  9081. packet->setArrayDataByName("zone_file_name", destination->display_name.c_str(), i);
  9082. packet->setArrayDataByName("cost", destination->cost, i);
  9083. if (has_map) {
  9084. packet->setArrayDataByName("map_x", destination->map_x, i);
  9085. packet->setArrayDataByName("map_y", destination->map_y, i);
  9086. }
  9087. }
  9088. if (has_map)
  9089. packet->setDataByName("map_name", GetCurrentZone()->GetTransportMap(transport_id).c_str());
  9090. EQ2Packet* app = packet->serialize();
  9091. //DumpPacket(app);
  9092. if (destinations.size() > 0)
  9093. QueuePacket(app);
  9094. safe_delete(packet);
  9095. }
  9096. }
  9097. }
  9098. void Client::ProcessTeleportLocation(EQApplicationPacket* app) {
  9099. PacketStruct* packet = configReader.getStruct("WS_TeleportDestination", GetVersion());
  9100. if (packet) {
  9101. if (packet->LoadPacketData(app->pBuffer, app->size)) {
  9102. Spawn* spawn = GetPlayer()->GetSpawnWithPlayerID(packet->getType_int32_ByName("spawn_id"));
  9103. int32 unique_id = packet->getType_int32_ByName("unique_id");
  9104. string zone_name = packet->getType_EQ2_16BitString_ByName("zone_name").data;
  9105. int32 cost = packet->getType_int32_ByName("cost");
  9106. vector<TransportDestination*> destinations;
  9107. TransportDestination* destination = 0;
  9108. if (this->GetTemporaryTransportID() || (spawn && spawn == transport_spawn && spawn->GetTransporterID()))
  9109. GetCurrentZone()->GetTransporters(&destinations, this, this->GetTemporaryTransportID() ? this->GetTemporaryTransportID() : spawn->GetTransporterID());
  9110. vector<TransportDestination*>::iterator itr;
  9111. for (itr = destinations.begin(); itr != destinations.end(); itr++) {
  9112. if ((*itr)->unique_id == unique_id && (*itr)->display_name == zone_name && (*itr)->cost == cost) {
  9113. destination = *itr;
  9114. break;
  9115. }
  9116. }
  9117. SetTemporaryTransportID(0);
  9118. if (!destination)
  9119. SimpleMessage(CHANNEL_COLOR_RED, "Error processing transport.");
  9120. else {
  9121. if (cost == 0 || player->RemoveCoins(cost)) {
  9122. if (destination->destination_zone_id == 0 || destination->destination_zone_id == GetCurrentZone()->GetZoneID()) {
  9123. if (destination->type == TRANSPORT_TYPE_FLIGHT)
  9124. SendFlightAutoMount(destination->flight_path_id, destination->mount_id, destination->mount_red_color, destination->mount_green_color, destination->mount_blue_color);
  9125. else
  9126. {
  9127. EQ2Packet* outapp = GetPlayer()->Move(destination->destination_x, destination->destination_y, destination->destination_z, GetVersion());
  9128. if (outapp)
  9129. QueuePacket(outapp);
  9130. }
  9131. }
  9132. else {
  9133. GetPlayer()->SetX(destination->destination_x);
  9134. GetPlayer()->SetY(destination->destination_y);
  9135. GetPlayer()->SetZ(destination->destination_z);
  9136. GetPlayer()->SetHeading(destination->destination_heading);
  9137. // Test if where we're going is an Instanced zone
  9138. if (!TryZoneInstance(destination->destination_zone_id, false)) {
  9139. LogWrite(INSTANCE__DEBUG, 0, "Instance", "Attempting to zone normally");
  9140. ZoneChangeDetails zone_details;
  9141. if (zone_list.GetZone(&zone_details, destination->destination_zone_id)) {
  9142. Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false);
  9143. }
  9144. }
  9145. }
  9146. if (destination->message.length() > 0)
  9147. SimpleMessage(CHANNEL_COLOR_YELLOW, destination->message.c_str());
  9148. }
  9149. else
  9150. SimpleMessage(CHANNEL_COLOR_RED, "You do not have enough money to use this transport.");
  9151. }
  9152. }
  9153. safe_delete(packet);
  9154. }
  9155. }
  9156. void Client::SendNewSpells(int8 class_id) {
  9157. if (class_id > 0) {
  9158. vector<Spell*>* spells = master_spell_list.GetSpellListByAdventureClass(class_id, player->GetLevel(), 1);
  9159. AddSendNewSpells(spells);
  9160. safe_delete(spells);
  9161. }
  9162. }
  9163. void Client::SendNewTSSpells(int8 class_id) {
  9164. if (class_id > 0) {
  9165. vector<Spell*>* spells = master_spell_list.GetSpellListByTradeskillClass(class_id, player->GetLevel(), 1);
  9166. AddSendNewSpells(spells);
  9167. safe_delete(spells);
  9168. }
  9169. }
  9170. void Client::AddSendNewSpells(vector<Spell*>* spells) {
  9171. Spell* spell = 0;
  9172. bool send_updates = false;
  9173. vector<Spell*>::iterator itr;
  9174. for (itr = spells->begin(); itr != spells->end(); itr++) {
  9175. spell = *itr;
  9176. if (spell && !player->HasSpell(spell->GetSpellID(), spell->GetSpellTier(), true) && spell->GetSpellData()->lua_script.length() > 0) {
  9177. send_updates = true;
  9178. SendSpellUpdate(spell);
  9179. player->AddSpellBookEntry(spell->GetSpellID(), spell->GetSpellTier(), player->GetFreeSpellBookSlot(spell->GetSpellData()->spell_book_type), spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
  9180. player->UnlockSpell(spell);
  9181. }
  9182. }
  9183. if (send_updates) {
  9184. EQ2Packet* outapp = player->GetSpellBookUpdatePacket(GetVersion());
  9185. if (outapp)
  9186. QueuePacket(outapp);
  9187. }
  9188. }
  9189. void Client::SetItemSearch(vector<Item*>* items) {
  9190. if (items) {
  9191. safe_delete(search_items);
  9192. search_items = items;
  9193. }
  9194. }
  9195. vector<Item*>* Client::GetSearchItems() {
  9196. return search_items;
  9197. }
  9198. void Client::SearchStore(int32 page) {
  9199. if (search_items) {
  9200. PacketStruct* packet = configReader.getStruct("WS_BrokerItems", GetVersion());
  9201. if (packet) {
  9202. int32 x = page * 8;
  9203. if (search_items->size() > 8) {
  9204. if ((search_items->size() - x) > 8)
  9205. packet->setArrayLengthByName("num_items", 8);
  9206. else
  9207. packet->setArrayLengthByName("num_items", search_items->size() - x);
  9208. }
  9209. else
  9210. packet->setArrayLengthByName("num_items", search_items->size());
  9211. if (search_items->size() > 0) {
  9212. packet->setArrayLengthByName("num_sellers", 1);
  9213. packet->setArrayDataByName("seller_seller_id", 1);
  9214. packet->setDataByName("per_page", 8);
  9215. packet->setDataByName("num_pages", search_items->size() / 8 + 1);
  9216. packet->setDataByName("page", page);
  9217. Item* item = 0;
  9218. int32 limit = search_items->size() > 8 ? 8 : search_items->size();
  9219. for (int32 i = 0; i < limit; i++, x++) {
  9220. if (x >= search_items->size())
  9221. break;
  9222. item = search_items->at(x);
  9223. std::string teststr("test ");
  9224. teststr.append(std::to_string(i));
  9225. packet->setArrayDataByName("string_one", teststr.c_str(), i);
  9226. packet->setArrayDataByName("string_two", "testtwo", i);
  9227. packet->setArrayDataByName("seller_name", "EQ2EMuDev", i);
  9228. packet->setArrayDataByName("item_id", item->details.item_id, i);
  9229. packet->setArrayDataByName("item_id2", item->details.item_id, i);
  9230. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  9231. //packet->setArrayDataByName("unknown2b", i, i);
  9232. packet->setArrayDataByName("item_seller_id", 1, i);
  9233. if (item->stack_count == 0)
  9234. packet->setArrayDataByName("quantity", 1, i);
  9235. else
  9236. packet->setArrayDataByName("quantity", item->stack_count, i);
  9237. packet->setArrayDataByName("stack_size", item->stack_count, i);
  9238. packet->setArrayDataByName("sell_price", item->sell_price, i);
  9239. std::string tmpStr("");
  9240. tmpStr.append(item->name.c_str());
  9241. tmpStr.append(" (");
  9242. tmpStr.append(std::to_string(item->details.item_id));
  9243. tmpStr.append(")");
  9244. packet->setArrayDataByName("item_name", tmpStr.c_str(), i);
  9245. packet->setArrayDataByName("req_level", item->generic_info.adventure_default_level, i);
  9246. //QueuePacket(item->serialize(GetVersion(), false, GetPlayer()));
  9247. }
  9248. }
  9249. EQ2Packet* outapp = packet->serialize();
  9250. //DumpPacket(outapp);
  9251. QueuePacket(outapp);
  9252. safe_delete(packet);
  9253. }
  9254. }
  9255. }
  9256. void Client::SetReadyForUpdates() {
  9257. if (!ready_for_updates) {
  9258. database.loadCharacterProperties(this);
  9259. SendDefaultGroupOptions();
  9260. }
  9261. ready_for_updates = true;
  9262. if (GetVersion() <= 561) {
  9263. SendRecipeList();
  9264. }
  9265. }
  9266. void Client::SetReadyForSpawns(bool val) {
  9267. ready_for_spawns = val;
  9268. if (GetPlayer()->GetActivityStatus() > 0) {
  9269. GetPlayer()->SetActivityStatus(0);
  9270. if (GetPlayer()->GetGroupMemberInfo()) {
  9271. world.GetGroupManager()->GroupMessage(GetPlayer()->GetGroupMemberInfo()->group_id, "%s has returned from Linkdead.", GetPlayer()->GetName());
  9272. }
  9273. }
  9274. GetPlayer()->SetActiveReward(false);
  9275. zone_list.CheckFriendZoned(this);
  9276. }
  9277. void Client::SendChatRelationship(int8 type, const char* name) {
  9278. if (!name) {
  9279. return;
  9280. }
  9281. PacketStruct* packet = configReader.getStruct("WS_ChatRelationship", GetVersion());
  9282. if (packet) {
  9283. packet->setDataByName("account_id", GetAccountID());
  9284. packet->setDataByName("type", type);
  9285. packet->setArrayLengthByName("num_names", 1);
  9286. packet->setArrayDataByName("name", name);
  9287. if (type == 0) {
  9288. Client* client = zone_list.GetClientByCharName(name);
  9289. if (client) {
  9290. packet->setArrayDataByName("location", client->GetCurrentZone()->GetZoneName());
  9291. packet->setArrayDataByName("class_name", classes.GetClassName(client->GetPlayer()->GetAdventureClass()));
  9292. }
  9293. }
  9294. QueuePacket(packet->serialize());
  9295. safe_delete(packet);
  9296. }
  9297. }
  9298. void Client::SendFriendList() {
  9299. PacketStruct* packet = configReader.getStruct("WS_ChatRelationship", GetVersion());
  9300. if (packet) {
  9301. packet->setDataByName("account_id", GetAccountID());
  9302. map<string, int8>::iterator itr;
  9303. map<string, int8>* friends = player->GetFriends();
  9304. if (friends && friends->size() > 0) {
  9305. Client* client = 0;
  9306. vector<string> names;
  9307. for (itr = friends->begin(); itr != friends->end(); itr++) {
  9308. if (itr->second == 2)
  9309. continue;
  9310. names.push_back(itr->first);
  9311. }
  9312. packet->setArrayLengthByName("num_names", names.size());
  9313. for (int32 i = 0; i < names.size(); i++) {
  9314. client = zone_list.GetClientByCharName(names[i]);
  9315. packet->setArrayDataByName("name", names[i].c_str(), i);
  9316. if (client) {
  9317. packet->setArrayDataByName("location", client->GetCurrentZone()->GetZoneName(), i);
  9318. packet->setArrayDataByName("class_name", classes.GetClassName(client->GetPlayer()->GetAdventureClass()), i);
  9319. }
  9320. }
  9321. }
  9322. QueuePacket(packet->serialize());
  9323. safe_delete(packet);
  9324. }
  9325. }
  9326. void Client::SendIgnoreList() {
  9327. PacketStruct* packet = configReader.getStruct("WS_ChatRelationship", GetVersion());
  9328. if (packet) {
  9329. packet->setDataByName("account_id", GetAccountID());
  9330. packet->setDataByName("type", 2);
  9331. map<string, int8>::iterator itr;
  9332. map<string, int8>* ignored = player->GetIgnoredPlayers();
  9333. if (ignored && ignored->size() > 0) {
  9334. vector<string> names;
  9335. for (itr = ignored->begin(); itr != ignored->end(); itr++) {
  9336. if (itr->second == 2)
  9337. continue;
  9338. names.push_back(itr->first);
  9339. }
  9340. packet->setArrayLengthByName("num_names", names.size());
  9341. for (int32 i = 0; i < names.size(); i++)
  9342. packet->setArrayDataByName("name", names[i].c_str(), i);
  9343. }
  9344. QueuePacket(packet->serialize());
  9345. safe_delete(packet);
  9346. }
  9347. }
  9348. void Client::AddWaypoint(string name, int8 type) {
  9349. waypoint_id++;
  9350. WaypointInfo info;
  9351. info.id = waypoint_id;
  9352. info.type = type;
  9353. waypoints[name] = info;
  9354. }
  9355. void Client::SendWaypoints() {
  9356. PacketStruct* packet = configReader.getStruct("WS_WaypointUpdate", GetVersion());
  9357. if (packet) {
  9358. packet->setArrayLengthByName("num_updates", waypoints.size());
  9359. map<string, WaypointInfo>::iterator itr;
  9360. int16 i = 0;
  9361. for (itr = waypoints.begin(); itr != waypoints.end(); itr++) {
  9362. packet->setArrayDataByName("waypoint_name", itr->first.c_str(), i);
  9363. packet->setArrayDataByName("waypoint_category", itr->second.type, i);
  9364. packet->setArrayDataByName("spawn_id", itr->second.id, i);
  9365. i++;
  9366. }
  9367. packet->setDataByName("unknown", 0xFFFFFFFF);
  9368. QueuePacket(packet->serialize());
  9369. safe_delete(packet);
  9370. }
  9371. }
  9372. void Client::SelectWaypoint(int32 id) {
  9373. string found_name = "";
  9374. map<string, WaypointInfo>::iterator itr;
  9375. for (itr = waypoints.begin(); itr != waypoints.end(); itr++) {
  9376. if (itr->second.id == id) {
  9377. found_name = itr->first;
  9378. break;
  9379. }
  9380. }
  9381. if (found_name.length() > 0) {
  9382. Spawn* spawn = current_zone->FindSpawn(player, found_name.c_str());
  9383. ShowPathToTarget(spawn);
  9384. }
  9385. }
  9386. void Client::AddWaypoint(const char* waypoint_name, int8 waypoint_category, int32 spawn_id) {
  9387. if (waypoint_name) {
  9388. PacketStruct* packet = configReader.getStruct("WS_WaypointUpdate", GetVersion());
  9389. if (packet) {
  9390. packet->setArrayLengthByName("num_updates", 1);
  9391. packet->setArrayDataByName("waypoint_name", waypoint_name, 0);
  9392. packet->setArrayDataByName("waypoint_category", waypoint_category, 0);
  9393. packet->setArrayDataByName("spawn_id", spawn_id, 0);
  9394. packet->setArrayDataByName("waypoint_category2", waypoint_category, 0);
  9395. packet->setArrayDataByName("spawn_id2", spawn_id, 0);
  9396. packet->setDataByName("unknown", 0xFFFFFFFF);
  9397. QueuePacket(packet->serialize());
  9398. safe_delete(packet);
  9399. }
  9400. }
  9401. }
  9402. void Client::ClearWaypoint() {
  9403. PacketStruct* packet = configReader.getStruct("WS_GlowPath", GetVersion());
  9404. if (packet) {
  9405. QueuePacket(packet->serialize());
  9406. safe_delete(packet);
  9407. }
  9408. }
  9409. bool Client::ShowPathToTarget(float x, float y, float z, float y_offset) {
  9410. if (current_zone->pathing) {
  9411. if (GetPlayer()->GetMap()) {
  9412. if (x < GetPlayer()->GetMap()->GetMinX() || x > GetPlayer()->GetMap()->GetMaxX())
  9413. return false;
  9414. if (z < GetPlayer()->GetMap()->GetMinZ() || z > GetPlayer()->GetMap()->GetMaxZ())
  9415. return false;
  9416. auto loc = glm::vec3(x, z, y);
  9417. float new_z = GetPlayer()->FindBestZ(loc, nullptr);
  9418. if (new_z != BEST_Z_INVALID) //this is actually y
  9419. y = new_z;
  9420. }
  9421. bool partial = false;
  9422. bool stuck = false;
  9423. PathfinderOptions opts;
  9424. opts.smooth_path = true;
  9425. opts.step_size = 100.0f;//RuleR(Pathing, NavmeshStepSize);
  9426. opts.offset = y_offset + 1.0f;
  9427. opts.flags = PathingNotDisabled ^ PathingZoneLine;
  9428. PacketStruct* packet = configReader.getStruct("WS_GlowPath", GetVersion());
  9429. if (packet) {
  9430. auto path = current_zone->pathing->FindPath(glm::vec3(player->GetX(), player->GetZ(), player->GetY()), glm::vec3(x, z, y), partial, stuck, opts);
  9431. packet->setArrayLengthByName("num_points", path.size());
  9432. int i = 0;
  9433. for (auto& node : path)
  9434. {
  9435. packet->setArrayDataByName("x", node.pos.x, i);
  9436. packet->setArrayDataByName("y", node.pos.z, i);
  9437. packet->setArrayDataByName("z", node.pos.y, i);
  9438. packet->setDataByName("waypoint_x", x);
  9439. packet->setDataByName("waypoint_y", y);
  9440. packet->setDataByName("waypoint_z", z);
  9441. i++;
  9442. }
  9443. if (i > 0)
  9444. QueuePacket(packet->serialize());
  9445. safe_delete(packet);
  9446. return (i > 0);
  9447. }
  9448. }
  9449. return false;
  9450. }
  9451. bool Client::ShowPathToTarget(Spawn* spawn) {
  9452. if (spawn) {
  9453. return ShowPathToTarget(spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetYOffset());
  9454. }
  9455. return false;
  9456. }
  9457. void Client::BeginWaypoint(const char* waypoint_name, float x, float y, float z) {
  9458. if (waypoint_name) {
  9459. PacketStruct* packet = configReader.getStruct("WS_GlowPath", GetVersion());
  9460. if (packet) {
  9461. packet->setArrayLengthByName("num_points", 1);
  9462. packet->setArrayDataByName("x", x, 0);
  9463. packet->setArrayDataByName("y", y, 0);
  9464. packet->setArrayDataByName("z", z, 0);
  9465. packet->setDataByName("waypoint_x", x);
  9466. packet->setDataByName("waypoint_y", y);
  9467. packet->setDataByName("waypoint_z", z);
  9468. packet->setDataByName("waypoint_name", waypoint_name);
  9469. packet->setDataByName("unknown", 0);
  9470. QueuePacket(packet->serialize());
  9471. safe_delete(packet);
  9472. }
  9473. }
  9474. }
  9475. void Client::InspectPlayer(Player* player_to_inspect) {
  9476. int source_pvp_alignment = GetPlayer()->GetPVPAlignment();
  9477. int target_pvp_alignment = player_to_inspect->GetPVPAlignment();
  9478. bool pvp_allowed = rule_manager.GetZoneRule(GetCurrentZoneID(), R_PVP, AllowPVP)->GetBool();
  9479. if (pvp_allowed == true) {
  9480. if (source_pvp_alignment != target_pvp_alignment) {
  9481. Message(CHANNEL_COLOR_RED, "You can not inspect players of different alignments.");
  9482. return;
  9483. }
  9484. }
  9485. if (player_to_inspect && player_to_inspect->GetClient()) {
  9486. PacketStruct* packet = configReader.getStruct("WS_InspectPlayer", GetVersion());
  9487. if (packet) {
  9488. packet->setDataByName("unknown", 0);
  9489. packet->setSmallStringByName("name", player_to_inspect->GetName());
  9490. packet->setDataByName("race", player_to_inspect->GetRace());
  9491. packet->setDataByName("gender", player_to_inspect->GetGender());
  9492. packet->setDataByName("adventure_level", player_to_inspect->GetLevel());
  9493. int16 effective_level = player_to_inspect->GetInfoStruct()->get_effective_level() != 0 ? player_to_inspect->GetInfoStruct()->get_effective_level() : player_to_inspect->GetLevel();
  9494. packet->setDataByName("adventure_level_effective", effective_level);
  9495. packet->setDataByName("adventure_class", player_to_inspect->GetAdventureClass());
  9496. packet->setDataByName("tradeskill_level", player_to_inspect->GetTSLevel());
  9497. packet->setDataByName("tradeskill_class", player_to_inspect->GetTradeskillClass());
  9498. packet->setDataByName("health", player_to_inspect->GetHP());
  9499. packet->setDataByName("health_max", player_to_inspect->GetTotalHP());
  9500. packet->setDataByName("health_base", player_to_inspect->GetTotalHPBase());
  9501. packet->setDataByName("power", player_to_inspect->GetPower());
  9502. packet->setDataByName("power_max", player_to_inspect->GetTotalPower());
  9503. packet->setDataByName("power_base", player_to_inspect->GetTotalPowerBase());
  9504. packet->setDataByName("mitigation", player_to_inspect->GetInfoStruct()->get_cur_mitigation());
  9505. packet->setDataByName("unknown1", 0);
  9506. packet->setDataByName("avoidance", player_to_inspect->GetInfoStruct()->get_avoidance_display() * 10.0f);
  9507. packet->setDataByName("unknown2", 0);
  9508. packet->setDataByName("mitigation_percentage", 0);
  9509. packet->setDataByName("strength", player_to_inspect->GetStr());
  9510. packet->setDataByName("strength_base", player_to_inspect->GetStrBase());
  9511. packet->setDataByName("stamina", player_to_inspect->GetSta());
  9512. packet->setDataByName("stamina_base", player_to_inspect->GetStaBase());
  9513. packet->setDataByName("agility", player_to_inspect->GetAgi());
  9514. packet->setDataByName("agility_base", player_to_inspect->GetAgiBase());
  9515. packet->setDataByName("wisdom", player_to_inspect->GetWis());
  9516. packet->setDataByName("wisdom_base", player_to_inspect->GetWisBase());
  9517. packet->setDataByName("intelligence", player_to_inspect->GetInt());
  9518. packet->setDataByName("intelligence_base", player_to_inspect->GetIntBase());
  9519. packet->setDataByName("unknown4", 0);
  9520. packet->setDataByName("unknown5", 0);
  9521. packet->setDataByName("unknown6", 0);
  9522. packet->setDataByName("unknown7", 0);
  9523. packet->setDataByName("unknown8", 0);
  9524. packet->setDataByName("unknown9", 0);
  9525. packet->setDataByName("unknown10", 0);
  9526. packet->setDataByName("unknown11", 0);
  9527. packet->setDataByName("unknown12", 0);
  9528. packet->setDataByName("heat_resist", player_to_inspect->GetHeatResistance());
  9529. packet->setDataByName("heat_resist_base", player_to_inspect->GetHeatResistanceBase());
  9530. packet->setDataByName("heat_resist_percentage", 0);
  9531. packet->setDataByName("cold_resist", player_to_inspect->GetColdResistance());
  9532. packet->setDataByName("cold_resist_base", player_to_inspect->GetColdResistanceBase());
  9533. packet->setDataByName("cold_resist_percentage", 0);
  9534. packet->setDataByName("magic_resist", player_to_inspect->GetMagicResistance());
  9535. packet->setDataByName("magic_resist_base", player_to_inspect->GetMagicResistanceBase());
  9536. packet->setDataByName("magic_resist_percentage", 0);
  9537. packet->setDataByName("mental_resist", player_to_inspect->GetMentalResistance());
  9538. packet->setDataByName("mental_resist_base", player_to_inspect->GetMentalResistanceBase());
  9539. packet->setDataByName("mental_resist_percentage", 0);
  9540. packet->setDataByName("divine_resist", player_to_inspect->GetDivineResistance());
  9541. packet->setDataByName("divine_resist_base", player_to_inspect->GetDivineResistanceBase());
  9542. packet->setDataByName("divine_resist_percentage", 0);
  9543. packet->setDataByName("disease_resist", player_to_inspect->GetDiseaseResistance());
  9544. packet->setDataByName("disease_resist_base", player_to_inspect->GetDiseaseResistanceBase());
  9545. packet->setDataByName("disease_resist_percentage", 0);
  9546. packet->setDataByName("poison_resist", player_to_inspect->GetPoisonResistance());
  9547. packet->setDataByName("poison_resist_base", player_to_inspect->GetPoisonResistanceBase());
  9548. packet->setDataByName("poison_resist_percentage", 0);
  9549. packet->setArrayLengthByName("num_chars", 0x01FF);
  9550. string biography = player_to_inspect->GetBiography();
  9551. for (size_t i = 0; i < biography.length(); i++)
  9552. packet->setArrayDataByName("biography_char", biography[i], i);
  9553. if (GetVersion() <= 373) {
  9554. for (int32 s = 0; s < 20; s++) {
  9555. int32 slot = s;
  9556. char item_slot_name[64], item_slot_name_appearance[64];
  9557. _snprintf(item_slot_name, 64, "slot_%u", slot);
  9558. Item* pw = player_to_inspect->GetEquipmentList()->GetItem(GetPlayer()->ConvertSlotFromClient(s, GetVersion()));
  9559. packet->setItemByName(item_slot_name, pw, this->GetPlayer(), 0, 7, true, true, true);
  9560. }
  9561. }
  9562. else if (GetVersion() <= 561) {
  9563. for (int32 s = 0; s < 22; s++) {
  9564. int32 slot = s;
  9565. char item_slot_name[64], item_slot_name_appearance[64];
  9566. _snprintf(item_slot_name, 64, "slot_%u", slot);
  9567. Item* pw = player_to_inspect->GetEquipmentList()->GetItem(GetPlayer()->ConvertSlotFromClient(s, GetVersion()));
  9568. packet->setItemByName(item_slot_name, pw, this->GetPlayer(), 0, 5, true, true);
  9569. }
  9570. }
  9571. else {
  9572. for (int32 s = 0; s < NUM_SLOTS; s++) {
  9573. int32 slot = s * 2;
  9574. char item_slot_name[64], item_slot_name_appearance[64];
  9575. _snprintf(item_slot_name, 64, "slot_%u", slot);
  9576. int32 slot_appearance = (s * 2) + 1;
  9577. _snprintf(item_slot_name_appearance, 64, "slot_%u", slot_appearance);
  9578. Item* pw = player_to_inspect->GetEquipmentList()->GetItem(GetPlayer()->ConvertSlotFromClient(s, player_to_inspect->GetClient()->GetVersion()));
  9579. packet->setItemByName(item_slot_name, pw, this->GetPlayer(), 0, 13, false, true);
  9580. if (s <= EQ2_FEET_SLOT || s == EQ2_RANGE_SLOT || s == EQ2_CLOAK_SLOT) {
  9581. pw = player_to_inspect->GetAppearanceEquipmentList()->GetItem(s);
  9582. packet->setItemByName(item_slot_name_appearance, pw, this->GetPlayer(), 0, 13, false, true);
  9583. }
  9584. else {
  9585. packet->setItemByName(item_slot_name_appearance, nullptr, this->GetPlayer(), 0, 13, false, true);
  9586. }
  9587. }
  9588. }
  9589. QueuePacket(packet->serialize());
  9590. safe_delete(packet);
  9591. }
  9592. }
  9593. }
  9594. void Client::SetPendingGuildInvite(Guild* guild, Player* invited_by) {
  9595. pending_guild_invite.guild = guild;
  9596. pending_guild_invite.invited_by = invited_by;
  9597. }
  9598. void Client::ShowClaimWindow() {
  9599. PacketStruct* packet = configReader.getStruct("WS_PromoFlagsDetails", GetVersion());
  9600. if (packet) {
  9601. int16 loaded = database.CountCharClaimItems(GetCharacterID());
  9602. vector<ClaimItems> claim = database.LoadCharacterClaimItems(GetCharacterID());
  9603. int32 account_age = database.GetAccountAge(GetAccountID());
  9604. //not sure if there is a message or not, but adding this and a return, so if we have nothing do nothing rather than display an empty window.
  9605. if (loaded == 0 || claim.empty()) {
  9606. Message(CHANNEL_COLOR_RED, "You have nothing to claim.");
  9607. return;
  9608. }
  9609. packet->setArrayLengthByName("num_claim_items", loaded);
  9610. int j = 0; //use this to track skipped vet items.
  9611. for (int i = 0; i < claim.size(); i++)
  9612. {
  9613. if (j == claim.size()) {
  9614. Message(CHANNEL_COLOR_RED, "You have nothing to claim.");
  9615. return;
  9616. }
  9617. Item* item = master_item_list.GetItem(claim[i].item_id);
  9618. int16 claimed = 0;
  9619. if (claim[i].curr_claim < claim[i].max_claim) {
  9620. claimed = claim[i].max_claim - claim[i].curr_claim;
  9621. }
  9622. else {
  9623. claimed = 0;
  9624. }
  9625. //dont display vet rewards until they reach the age required
  9626. if (account_age < claim[i].vet_reward_time) {
  9627. j++;
  9628. continue;
  9629. }
  9630. packet->setArrayDataByName("id", i, i);
  9631. packet->setArrayDataByName("not_yet_claimed", 1, i);
  9632. packet->setArrayDataByName("num_remaining", claim[i].curr_claim, i);
  9633. packet->setArrayDataByName("one_per_character", claim[i].one_per_char, i);
  9634. packet->setArrayDataByName("claimed_on_this_char", 0, i);
  9635. packet->setArrayDataByName("item_name", item->name.c_str(), i);
  9636. packet->setArrayDataByName("text", "If you ever see this text, let a developer know!", i); //I've not seen this!
  9637. //packet->setArrayDataByName("category", "Scott's Shit", i); //devn00b: not using so commenting out, leaving in case we ever do implement categories.
  9638. packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
  9639. packet->setArrayDataByName("item_id", item->details.item_id, i);
  9640. j++;
  9641. }
  9642. }
  9643. packet->setDataByName("unknown3", 1);
  9644. QueuePacket(packet->serialize());
  9645. safe_delete(packet);
  9646. }
  9647. void Client::ShowGuildSearchWindow() {
  9648. PacketStruct* packet = configReader.getStruct("WS_GuildRecruiting", GetVersion());
  9649. if (packet) {
  9650. MutexMap<int32, Guild*>* guilds = guild_list.GetGuilds();
  9651. MutexMap<int32, Guild*>::iterator itr = guilds->begin();
  9652. packet->setArrayLengthByName("num_guilds", guilds->size());
  9653. int32 i = 0;
  9654. while (itr.Next()) {
  9655. Guild* guild = itr.second;
  9656. packet->setArrayDataByName("guild_id", guild->GetID(), i);
  9657. packet->setArrayDataByName("guild_name", guild->GetName(), i);
  9658. packet->setArrayDataByName("recruiting_short_description", guild->GetRecruitingShortDesc().c_str(), i);
  9659. packet->setArrayDataByName("descriptive_tag1", guild->GetRecruitingDescTag(0), i);
  9660. packet->setArrayDataByName("descriptive_tag2", guild->GetRecruitingDescTag(1), i);
  9661. packet->setArrayDataByName("descriptive_tag3", guild->GetRecruitingDescTag(2), i);
  9662. packet->setArrayDataByName("descriptive_tag4", guild->GetRecruitingDescTag(3), i);
  9663. packet->setArrayDataByName("playstyle", guild->GetRecruitingPlayStyle(), i);
  9664. packet->setArrayDataByName("looking_for", guild->GetRecruitingLookingForPacketValue(), i); //tradeskillers, fighters, new
  9665. packet->setArrayDataByName("unknown7", 0x02, i);
  9666. packet->setArrayDataByName("min_level", guild->GetRecruitingMinLevel(), i);
  9667. i++;
  9668. }
  9669. QueuePacket(packet->serialize());
  9670. safe_delete(packet);
  9671. }
  9672. }
  9673. void Client::ShowDressingRoom(Item* item, sint32 crc) {
  9674. PacketStruct* packet;
  9675. vector<int8>* slot_data;
  9676. vector<int8>::iterator itr;
  9677. int32 slots = 0;
  9678. assert(item);
  9679. if (!(packet = configReader.getStruct("WS_DressingRoom", GetVersion()))) {
  9680. return;
  9681. }
  9682. slot_data = &item->slot_data;
  9683. for (itr = slot_data->begin(); itr != slot_data->end(); itr++) {
  9684. if (version >= 1188)
  9685. slots = *itr;
  9686. else
  9687. slots += (int8)pow(2.0, *itr);
  9688. }
  9689. packet->setDataByName("slot", slots);
  9690. packet->setDataByName("appearance_id", item->generic_info.appearance_id);
  9691. if (slots == 2) {
  9692. packet->setDataByName("rgb", item->generic_info.appearance_red, 2);
  9693. packet->setDataByName("rgb", item->generic_info.appearance_blue, 0);
  9694. }
  9695. else {
  9696. packet->setDataByName("rgb", item->generic_info.appearance_red, 0);
  9697. packet->setDataByName("rgb", item->generic_info.appearance_blue, 2);
  9698. }
  9699. packet->setDataByName("rgb", item->generic_info.appearance_green, 1);
  9700. if (slots == 4) {
  9701. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_red, 2);
  9702. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_green, 1);
  9703. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_blue, 0);
  9704. }
  9705. else if (slots == 7) {
  9706. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_red, 1);
  9707. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_green, 0);
  9708. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_blue, 2);
  9709. }
  9710. else {
  9711. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_red, 0);
  9712. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_green, 1);
  9713. packet->setDataByName("highlight_rgb", item->generic_info.appearance_highlight_blue, 2);
  9714. }
  9715. packet->setDataByName("icon", item->GetIcon(GetVersion()));
  9716. packet->setDataByName("item_id", item->details.item_id);
  9717. packet->setDataByName("item_crc", crc);
  9718. packet->setDataByName("unknown2", 0xFFFFFFFF);
  9719. packet->setDataByName("unknown4", 0xFFFFFFFF);
  9720. packet->setDataByName("unknown5", 0xFF, 9);
  9721. packet->setDataByName("unknown5", 0xFF, 10);
  9722. QueuePacket(packet->serialize());
  9723. safe_delete(packet);
  9724. }
  9725. void Client::SendCollectionList() {
  9726. map<int32, Collection*>* collections = player->GetCollectionList()->GetCollections();
  9727. map<int32, Collection*>::iterator itr;
  9728. vector<struct CollectionItem*>* collection_items;
  9729. Collection* collection;
  9730. struct CollectionItem* collection_item;
  9731. PacketStruct* packet = 0;
  9732. int16 i = 0, j;
  9733. if (!(packet = configReader.getStruct("WS_CollectionUpdate", version)))
  9734. return;
  9735. packet->setArrayLengthByName("num_collections", collections->size());
  9736. for (itr = collections->begin(); itr != collections->end(); itr++) {
  9737. collection = itr->second;
  9738. collection_items = collection->GetCollectionItems();
  9739. packet->setArrayDataByName("unknown", 1, i);
  9740. packet->setArrayDataByName("collection_name", collection->GetName(), i);
  9741. packet->setArrayDataByName("collection_category", collection->GetCategory(), i);
  9742. packet->setArrayDataByName("completed", collection->GetCompleted(), i);
  9743. packet->setArrayDataByName("collection_id", collection->GetID(), i);
  9744. packet->setArrayDataByName("level", collection->GetLevel(), i);
  9745. packet->setArrayDataByName("ready_to_turn_in", collection->GetIsReadyToTurnIn(), i);
  9746. packet->setSubArrayLengthByName("num_items", collection_items->size(), i);
  9747. for (j = 0; j < collection_items->size(); j++) {
  9748. collection_item = collection_items->at(j);
  9749. Item* item = master_item_list.GetItem(collection_item->item);
  9750. if (item) {
  9751. packet->setSubArrayDataByName("item_icon", item->GetIcon(GetVersion()), i, j);
  9752. if (version < 955)
  9753. packet->setSubArrayDataByName("item_name", item->name.c_str(), i, j);
  9754. else
  9755. packet->setSubArrayDataByName("item_id", item->details.item_id, i, j);
  9756. }
  9757. packet->setSubArrayDataByName("item_flag", collection_item->found, i, j);
  9758. }
  9759. i++;
  9760. }
  9761. packet->setDataByName("new_collection_flag", 1);
  9762. QueuePacket(packet->serialize());
  9763. safe_delete(packet);
  9764. }
  9765. bool Client::SendCollectionsForItem(Item* item) {
  9766. map<int32, Collection*> collections_to_send;
  9767. map<int32, Collection*>* collections;
  9768. map<int32, Collection*>::iterator itr;
  9769. vector<struct CollectionItem*>* collection_items;
  9770. Collection* collection;
  9771. struct CollectionItem* collection_item;
  9772. PacketStruct* packet;
  9773. int16 i;
  9774. assert(item);
  9775. /* get any collections required by this item that the player already has */
  9776. collections = player->GetCollectionList()->GetCollections();
  9777. for (itr = collections->begin(); itr != collections->end(); itr++) {
  9778. collection = itr->second;
  9779. if (!collection->GetCompleted() && !collection->GetIsReadyToTurnIn() && collection->NeedsItem(item)) {
  9780. LogWrite(COLLECTION__DEBUG, 0, "Collect", "Adding collection from player list %u\n", collection->GetID());
  9781. collections_to_send[collection->GetID()] = collection;
  9782. }
  9783. }
  9784. /* get any collections required by this item that the player does not have and send it to the client */
  9785. collections = master_collection_list.GetCollections();
  9786. for (itr = collections->begin(); itr != collections->end(); itr++) {
  9787. collection = itr->second;
  9788. if (collection->NeedsItem(item) && !player->GetCollectionList()->GetCollection(collection->GetID())) {
  9789. if (!(packet = configReader.getStruct("WS_CollectionUpdate", version))) {
  9790. return false;
  9791. }
  9792. packet->setArrayLengthByName("num_collections", 1);
  9793. packet->setArrayDataByName("unknown", 1, 0);
  9794. packet->setArrayDataByName("collection_name", collection->GetName(), 0);
  9795. packet->setArrayDataByName("collection_category", collection->GetCategory(), 0);
  9796. packet->setArrayDataByName("completed", 0, 0);
  9797. packet->setArrayDataByName("collection_id", collection->GetID(), 0);
  9798. packet->setArrayDataByName("level", collection->GetLevel(), 0);
  9799. packet->setArrayDataByName("ready_to_turn_in", 0, 0);
  9800. packet->setArrayDataByName("unknown3", 0x28, 0);
  9801. collection_items = collection->GetCollectionItems();
  9802. packet->setSubArrayLengthByName("num_items", collection_items->size(), 0);
  9803. for (i = 0; i < collection_items->size(); i++) {
  9804. collection_item = collection_items->at(i);
  9805. Item* item2 = master_item_list.GetItem(collection_item->item);
  9806. if (item2) {
  9807. packet->setSubArrayDataByName("item_icon", item2->GetIcon(GetVersion()), 0, i);
  9808. if (version < 955)
  9809. packet->setSubArrayDataByName("item_name", item2->name.c_str(), 0, i);
  9810. else
  9811. packet->setSubArrayDataByName("item_id", item2->details.item_id, 0, i);
  9812. packet->setSubArrayDataByName("item_flag", collection_item->found, 0, i);
  9813. }
  9814. }
  9815. packet->setDataByName("new_collection_flag", 0);
  9816. QueuePacket(packet->serialize());
  9817. safe_delete(packet);
  9818. LogWrite(COLLECTION__DEBUG, 0, "Collect", "Adding collection from master list %u\n", collection->GetID());
  9819. collections_to_send[collection->GetID()] = collection;
  9820. }
  9821. }
  9822. /* send the client a list of collections that should be filtered for this item */
  9823. if (collections_to_send.size() > 0) {
  9824. if (!(packet = configReader.getStruct("WS_CollectionFilter", version))) {
  9825. return false;
  9826. }
  9827. i = 0;
  9828. packet->setArrayLengthByName("num_filters", collections_to_send.size());
  9829. for (itr = collections_to_send.begin(); itr != collections_to_send.end(); itr++) {
  9830. collection = itr->second;
  9831. packet->setArrayDataByName("collection_id", collection->GetID(), i);
  9832. packet->setArrayDataByName("collection_item_num", collection->GetCollectionItemByItemID(item->details.item_id)->index, i);
  9833. i++;
  9834. }
  9835. packet->setDataByName("item_icon", item->GetIcon(GetVersion()));
  9836. packet->setDataByName("item_name", item->name.c_str());
  9837. packet->setDataByName("item_id", item->details.item_id);
  9838. packet->setDataByName("unknown3", player->GetCollectionList()->Size());
  9839. QueuePacket(packet->serialize());
  9840. safe_delete(packet);
  9841. }
  9842. return collections_to_send.size() > 0;
  9843. }
  9844. void Client::HandleCollectionAddItem(int32 collection_id, Item* item) {
  9845. Collection* collection;
  9846. struct CollectionItem* collection_item;
  9847. PacketStruct* packet;
  9848. char tmp[200] = { 0 };
  9849. assert(item);
  9850. /* first try to get the collection from the player's collection list. if it's not found, get it from the master list */
  9851. if ((collection = player->GetCollectionList()->GetCollection(collection_id))) {
  9852. /* get the item struct that represents the item for this collection */
  9853. if (!(collection_item = collection->GetCollectionItemByItemID(item->details.item_id))) {
  9854. SendCollectionList();
  9855. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Could not find item '%s' required by collection '%s'", item->name.c_str(), collection->GetName());
  9856. return;
  9857. }
  9858. /* sanity check */
  9859. if (collection_item->found) {
  9860. SendCollectionList();
  9861. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Player '%s' has already found item '%s' for collection '%s'", player->GetName(), item->name.c_str(), collection->GetName());
  9862. return;
  9863. }
  9864. }
  9865. else if ((collection = master_collection_list.GetCollection(collection_id))) {
  9866. collection = new Collection(collection);
  9867. /* get the item struct that represents the item for this collection */
  9868. if (!(collection_item = collection->GetCollectionItemByItemID(item->details.item_id))) {
  9869. SendCollectionList();
  9870. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Could not find item '%s' required by collection '%s'", item->name.c_str(), collection->GetName());
  9871. safe_delete(collection);
  9872. return;
  9873. }
  9874. /* add the new collection to the player's collection list */
  9875. if (!player->GetCollectionList()->AddCollection(collection)) {
  9876. SendCollectionList();
  9877. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Player '%s' already has collection '%s'", player->GetName(), collection->GetName());
  9878. safe_delete(collection);
  9879. return;
  9880. }
  9881. }
  9882. else {
  9883. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Could not find collection with id %u", collection_id);
  9884. return;
  9885. }
  9886. collection_item->found = 1;
  9887. collection->SetSaveNeeded(true);
  9888. if (!(packet = configReader.getStruct("WS_CollectionItem", version))) {
  9889. SendCollectionList();
  9890. LogWrite(COLLECTION__ERROR, 0, "Collect", "Error in Client::HandleCollectionAddItem. Could not find struct 'WS_CollectionItem'");
  9891. return;
  9892. }
  9893. packet->setDataByName("collection_id", collection_id);
  9894. packet->setDataByName("collection_item_num", collection_item->index);
  9895. packet->setDataByName("add", 1);
  9896. QueuePacket(packet->serialize());
  9897. Item* item2 = master_item_list.GetItem(collection_item->item);
  9898. if (item2) {
  9899. Message(CHANNEL_COLOR_YELLOW, "You added: %s to %s", item2->name.c_str(), collection->GetName());
  9900. sprintf(tmp, "You added: %s to %s", item2->name.c_str(), collection->GetName());
  9901. SendPopupMessage(5, tmp, "quest_item", 3.5, 0x64, 0xFF, 0xFF);
  9902. }
  9903. safe_delete(packet);
  9904. RemoveItem(item, 1);
  9905. SendCollectionList();
  9906. }
  9907. void Client::DisplayCollectionComplete(Collection* collection) {
  9908. vector<struct CollectionRewardItem*>* reward_items;
  9909. vector<struct CollectionRewardItem*>* selectable_reward_items;
  9910. struct CollectionRewardItem* reward_item;
  9911. PacketStruct* packet;
  9912. int32 i;
  9913. assert(collection);
  9914. reward_items = collection->GetRewardItems();
  9915. selectable_reward_items = collection->GetSelectableRewardItems();
  9916. if (GetVersion() <= 561) {
  9917. int32 source_id = collection->GetID();
  9918. PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", GetVersion());
  9919. if (packet2) {
  9920. packet2->setSubstructDataByName("reward_data", "unknown1", 255);
  9921. packet2->setSubstructDataByName("reward_data", "reward", "Quest Reward!");
  9922. packet2->setSubstructDataByName("reward_data", "max_coin", collection->GetRewardCoin());
  9923. packet2->setSubstructDataByName("reward_data", "exp_bonus", collection->GetRewardXP());
  9924. if (reward_items) {
  9925. int32 item_count = reward_items->size();
  9926. packet2->setSubstructArrayLengthByName("reward_data", "num_rewards", item_count);
  9927. i = 0;
  9928. if (reward_items) {
  9929. for (i = 0; i < reward_items->size(); i++) {
  9930. Item* item = reward_items->at(i)->item;
  9931. if (item) {
  9932. packet2->setArrayDataByName("reward_id", item->details.item_id, i);
  9933. packet2->setItemArrayDataByName("item", item, player, i, 0, GetClientItemPacketOffset());
  9934. }
  9935. }
  9936. }
  9937. }
  9938. if (selectable_reward_items) {
  9939. packet2->setSubstructArrayLengthByName("reward_data", "num_select_rewards", selectable_reward_items->size());
  9940. for (i = 0; i < selectable_reward_items->size(); i++) {
  9941. Item* item = selectable_reward_items->at(i)->item;
  9942. if (item) {
  9943. packet2->setArrayDataByName("select_reward_id", item->details.item_id, i);
  9944. packet2->setItemArrayDataByName("select_item", item, player, i, 0, GetClientItemPacketOffset());
  9945. }
  9946. }
  9947. }
  9948. }
  9949. EQ2Packet* app = packet2->serialize();
  9950. QueuePacket(app);
  9951. safe_delete(packet2);
  9952. return;
  9953. }
  9954. if (!(packet = configReader.getStruct("WS_QuestComplete", version))) {
  9955. return;
  9956. }
  9957. packet->setDataByName("title", "Quest Reward!");
  9958. packet->setDataByName("name", collection->GetName());
  9959. packet->setDataByName("description", collection->GetCategory());
  9960. packet->setDataByName("level", collection->GetLevel());
  9961. packet->setDataByName("max_coin", collection->GetRewardCoin());
  9962. packet->setDataByName("min_coin", collection->GetRewardCoin());
  9963. //packet->setDataByName("status_points", quest->GetStatusPoints());
  9964. packet->setArrayLengthByName("num_rewards", reward_items->size());
  9965. for (i = 0; i < reward_items->size(); i++) {
  9966. reward_item = reward_items->at(i);
  9967. if (!reward_item->item)
  9968. {
  9969. LogWrite(DATABASE__ERROR, 0, "Database", "DisplayCollectionComplete Collection %s (%u) num_rewards has missing item in slot %u", collection->GetName(), collection->GetID(), i);
  9970. Message(CHANNEL_COLOR_RED, "DisplayCollectionComplete Collection %s (%u) num_rewards has missing item in slot %u", collection->GetName(), collection->GetID(), i);
  9971. continue;
  9972. }
  9973. packet->setArrayDataByName("reward_id", reward_item->item->details.item_id, i);
  9974. if (version < 860)
  9975. packet->setItemArrayDataByName("item", reward_item->item, player, i, 0, GetClientItemPacketOffset());
  9976. else if (version < 1193)
  9977. packet->setItemArrayDataByName("item", reward_item->item, player, i);
  9978. else
  9979. packet->setItemArrayDataByName("item", reward_item->item, player, i, 0, 2);
  9980. }
  9981. packet->setArrayLengthByName("num_select_rewards", selectable_reward_items->size());
  9982. for (i = 0; i < selectable_reward_items->size(); i++) {
  9983. reward_item = selectable_reward_items->at(i);
  9984. if (!reward_item->item)
  9985. {
  9986. LogWrite(DATABASE__ERROR, 0, "Database", "DisplayCollectionComplete Collection %s (%u) num_select_rewards has missing item in slot %u", collection->GetName(), collection->GetID(), i);
  9987. Message(CHANNEL_COLOR_RED, "DisplayCollectionComplete Collection %s (%u) num_select_rewards has missing item in slot %u", collection->GetName(), collection->GetID(), i);
  9988. continue;
  9989. }
  9990. packet->setArrayDataByName("select_reward_id", reward_item->item->details.item_id, i);
  9991. if (version < 860)
  9992. packet->setItemArrayDataByName("select_item", reward_item->item, player, i, 0, GetClientItemPacketOffset());
  9993. else if (version < 1193)
  9994. packet->setItemArrayDataByName("select_item", reward_item->item, player, i);
  9995. else
  9996. packet->setItemArrayDataByName("select_item", reward_item->item, player, i, 0, 2);
  9997. }
  9998. QueuePacket(packet->serialize());
  9999. safe_delete(packet);
  10000. }
  10001. void Client::HandInCollections() {
  10002. map<int32, Collection*>* collections;
  10003. map<int32, Collection*>::iterator itr;
  10004. Collection* collection;
  10005. /* only show 1 collection reward dialog at a time */
  10006. if (player->GetPendingCollectionReward()) {
  10007. return;
  10008. }
  10009. collections = player->GetCollectionList()->GetCollections();
  10010. /* we only want to display 1 collection reward dialog at a time. so once we find one to display, send it and return. once the player accepts the reward
  10011. for this collection, this function gets called again and the process repeats until there are no more collections to hand in */
  10012. for (itr = collections->begin(); itr != collections->end(); itr++) {
  10013. collection = itr->second;
  10014. if (collection->GetIsReadyToTurnIn()) {
  10015. player->SetPendingCollectionReward(collection);
  10016. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  10017. QuestRewardData* data = new QuestRewardData;
  10018. data->quest_id = 0;
  10019. data->is_temporary = false;
  10020. data->description = std::string("");
  10021. data->is_collection = true;
  10022. data->has_displayed = false;
  10023. data->tmp_coin = 0;
  10024. data->tmp_status = 0;
  10025. data->db_saved = false;
  10026. data->db_index = 0;
  10027. quest_pending_reward.push_back(data);
  10028. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  10029. quest_updates = true;
  10030. break;
  10031. }
  10032. }
  10033. if (quest_updates) {
  10034. SaveQuestRewardData(true);
  10035. }
  10036. }
  10037. void Client::AcceptCollectionRewards(Collection* collection, int32 selectable_item_id) {
  10038. vector<struct CollectionRewardItem*>* reward_items;
  10039. vector<struct CollectionRewardItem*>::iterator itr;
  10040. struct CollectionRewardItem* reward_item;
  10041. int16 num_slots_needed;
  10042. int16 num_slots;
  10043. assert(collection);
  10044. reward_items = collection->GetRewardItems();
  10045. num_slots_needed = (int16)reward_items->size();
  10046. if (selectable_item_id > 0)
  10047. num_slots_needed++;
  10048. num_slots = player->GetPlayerItemList()->GetNumberOfFreeSlots();
  10049. if (num_slots < num_slots_needed) {
  10050. SimpleMessage(CHANNEL_COLOR_RED, "You do not have enough free slots. Free up some slots and try again");
  10051. DisplayCollectionComplete(collection);
  10052. return;
  10053. }
  10054. /* add manditory items */
  10055. for (itr = reward_items->begin(); itr != reward_items->end(); itr++) {
  10056. reward_item = *itr;
  10057. AddItem(reward_item->item->details.item_id, reward_item->quantity);
  10058. }
  10059. /* find and add the selectable item if there's one */
  10060. if (selectable_item_id > 0) {
  10061. reward_items = collection->GetSelectableRewardItems();
  10062. for (itr = reward_items->begin(); itr != reward_items->end(); itr++) {
  10063. reward_item = *itr;
  10064. if (reward_item->item->details.item_id == selectable_item_id) {
  10065. AddItem(reward_item->item->details.item_id, reward_item->quantity);
  10066. break;
  10067. }
  10068. }
  10069. }
  10070. if (collection->GetRewardXP() > 0) {
  10071. player->AddXP((int32)collection->GetRewardXP());
  10072. }
  10073. if (collection->GetRewardCoin() > 0) {
  10074. player->AddCoins(collection->GetRewardCoin());
  10075. Message(CHANNEL_COLOR_YELLOW, "You receive %s", GetCoinMessage(collection->GetRewardCoin()).c_str());
  10076. }
  10077. collection->SetCompleted(true);
  10078. //update achievements for completeing collection here
  10079. collection->SetSaveNeeded(true);
  10080. SendCollectionList();
  10081. /* reset the pending collection reward and check for my collections that the player needs to hand in */
  10082. player->SetPendingCollectionReward(0);
  10083. RemoveQueuedQuestReward();
  10084. GetPlayer()->SetActiveReward(false);
  10085. HandInCollections();
  10086. GetPlayer()->GetZone()->SendSubSpawnUpdates(SUBSPAWN_TYPES::COLLECTOR);
  10087. }
  10088. void Client::SendRecipeList() {
  10089. if (GetRecipeListSent()) {
  10090. return;
  10091. }
  10092. PacketStruct* packet = 0;
  10093. map<int32, Recipe*>* recipes = player->GetRecipeList()->GetRecipes();
  10094. map<int32, Recipe*>::iterator itr;
  10095. int16 i = 0;
  10096. Recipe* recipe;
  10097. if (version <= 561) {
  10098. PacketStruct* packet = 0;
  10099. if (!(packet = configReader.getStruct("WS_UpdateRecipeBook", GetVersion()))) {
  10100. return;
  10101. }
  10102. packet->setArrayLengthByName("recipe_count", recipes->size());
  10103. for (itr = recipes->begin(); itr != recipes->end(); itr++) {
  10104. recipe = itr->second;
  10105. int32 recipe_id = recipe->GetID();
  10106. packet->setArrayDataByName("recipe_id", recipe_id, i);
  10107. packet->setArrayDataByName("recipe_data_crc", GetRecipeCRC(recipe), i);
  10108. packet->setArrayDataByName("unknown", 0x7005BE3, i); //0x7005BE3
  10109. i++;
  10110. }
  10111. EQ2Packet* ret = packet->serializeCountPacket(GetVersion(), 0, nullptr, nullptr);
  10112. QueuePacket(ret);
  10113. safe_delete(packet);
  10114. SetRecipeListSent(true);
  10115. return;
  10116. }
  10117. else if (!(packet = configReader.getStruct("WS_RecipeList", version))) {
  10118. return;
  10119. }
  10120. int8 level = player->GetTSLevel();
  10121. int index = 0;
  10122. for (itr = recipes->begin(); itr != recipes->end(); itr++) {
  10123. recipe = itr->second;
  10124. auto res = std::find(devices.begin(), devices.end(), recipe->GetDevice());
  10125. if (res != devices.end())
  10126. index = res - devices.begin();
  10127. else
  10128. devices.push_back(recipe->GetDevice());
  10129. }
  10130. packet->setDataByName("command_type", 0);
  10131. packet->setArrayLengthByName("num_recipes", recipes->size());
  10132. int stringsize = 0;
  10133. for (itr = recipes->begin(); itr != recipes->end(); itr++) {
  10134. recipe = itr->second;
  10135. int32 myid = recipe->GetID();
  10136. int8 rlevel = recipe->GetLevel();
  10137. int8 even = level - level * .05 + .5;
  10138. int8 easymin = level - level * .25 + .5;
  10139. int8 veasymin = level - level * .35 + .5;
  10140. if (rlevel > level)
  10141. packet->setArrayDataByName("tier", 4, i);
  10142. else if ((rlevel <= level) & (rlevel >= even))
  10143. packet->setArrayDataByName("tier", 3, i);
  10144. else if ((rlevel <= even) & (rlevel >= easymin))
  10145. packet->setArrayDataByName("tier", 2, i);
  10146. else if ((rlevel <= easymin) & (rlevel >= veasymin))
  10147. packet->setArrayDataByName("tier", 1, i);
  10148. else if ((rlevel <= veasymin) & (rlevel >= 0))
  10149. packet->setArrayDataByName("tier", 0, i);
  10150. if (rlevel == 2)
  10151. int xxx = 1;
  10152. packet->setArrayDataByName("recipe_id", myid, i);
  10153. packet->setArrayDataByName("level", recipe->GetLevel(), i);
  10154. packet->setArrayDataByName("unknown1", recipe->GetLevel(), i);
  10155. packet->setArrayDataByName("icon", recipe->GetIcon(), i);
  10156. packet->setArrayDataByName("classes", recipe->GetClasses(), i);
  10157. packet->setArrayDataByName("technique", recipe->GetTechnique(), i);
  10158. packet->setArrayDataByName("knowledge", recipe->GetKnowledge(), i);
  10159. auto recipe_device = std::find(devices.begin(), devices.end(), recipe->GetDevice());
  10160. if (recipe_device != devices.end())
  10161. packet->setArrayDataByName("device_type", recipe_device - devices.begin(), i);
  10162. else
  10163. {//TODO error should never get here
  10164. }
  10165. packet->setArrayDataByName("device_sub_type", recipe->GetDevice_Sub_Type(), i);
  10166. packet->setArrayDataByName("recipe_name", recipe->GetName(), i);
  10167. packet->setArrayDataByName("recipe_book", recipe->GetBook(), i);
  10168. packet->setArrayDataByName("unknown3", recipe->GetUnknown3(), i);
  10169. i++;
  10170. }
  10171. //packet->PrintPacket();
  10172. EQ2Packet* pack = packet->serialize();
  10173. QueuePacket(pack);
  10174. safe_delete(packet);
  10175. SetRecipeListSent(true);
  10176. }
  10177. void Client::ShowRecipeBook() {
  10178. PacketStruct* packet = 0;
  10179. Spawn* target = 0;
  10180. int index = 0;
  10181. if (!(target = player->GetTarget())) {
  10182. SimpleMessage(CHANNEL_COLOR_YELLOW, "You do not have a tradeskill device targeted");
  10183. return;
  10184. }
  10185. if (!target->IsObject() || !((Object*)target)->GetDeviceID()) {
  10186. SimpleMessage(CHANNEL_COLOR_YELLOW, "You do not have a tradeskill device targeted");
  10187. return;
  10188. }
  10189. if (!(packet = configReader.getStruct("WS_ShowRecipeBook", version))) {
  10190. return;
  10191. }
  10192. packet->setDataByName("device", target->GetName());
  10193. packet->setDataByName("unknown1", 1);
  10194. auto res = std::find(devices.begin(), devices.end(), target->GetName());
  10195. if (res != devices.end()) {
  10196. index = res - devices.begin();
  10197. int32 deviceID = 0;
  10198. deviceID |= 1UL << index;
  10199. //LogWrite(TRADESKILL__ERROR, 0, "Tradeskills", "GetDeviceID() = %u, deviceID = %u", ((Object*)target)->GetDeviceID(), deviceID);
  10200. packet->setDataByName("unknown2", devices.size());
  10201. packet->setDataByName("unknown3", deviceID);
  10202. }
  10203. else
  10204. packet->setDataByName("unknown2", devices.size());
  10205. QueuePacket(packet->serialize());
  10206. safe_delete(packet);
  10207. }
  10208. void Client::SendTitleUpdate() {
  10209. // must call release read lock before leaving function on GetPlayerTitles
  10210. vector<Title*>* titles = player->GetPlayerTitles()->GetAllTitles();
  10211. vector<Title*>::iterator itr;
  10212. Title* title;
  10213. sint32 i = 0;
  10214. sint32 prefix_index = database.GetCharPrefixIndex(GetCharacterID(), player);
  10215. sint32 suffix_index = database.GetCharSuffixIndex(GetCharacterID(), player);
  10216. PacketStruct* packet = configReader.getStruct("WS_TitleUpdate", GetVersion());
  10217. if (packet) {
  10218. packet->setArrayLengthByName("num_titles", titles->size());
  10219. for (itr = titles->begin(); itr != titles->end(); itr++) {
  10220. title = *itr;
  10221. packet->setArrayDataByName("title", title->GetName(), i);
  10222. packet->setArrayDataByName("prefix", title->GetPrefix(), i);
  10223. i++;
  10224. }
  10225. packet->setDataByName("current_prefix", prefix_index);
  10226. packet->setDataByName("current_suffix", suffix_index);
  10227. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  10228. #if EQDEBUG >= 9
  10229. packet->PrintPacket();
  10230. #endif
  10231. QueuePacket(packet->serialize());
  10232. safe_delete(packet);
  10233. SendUpdateTitles(prefix_index, suffix_index);
  10234. }
  10235. player->GetPlayerTitles()->ReleaseReadLock();
  10236. }
  10237. void Client::SendUpdateTitles(sint32 prefix, sint32 suffix) {
  10238. Title* suffix_title = 0;
  10239. Title* prefix_title = 0;
  10240. if (suffix != -1) {
  10241. suffix_title = player->GetPlayerTitles()->GetTitle(suffix);
  10242. if (suffix_title)
  10243. strcpy(player->appearance.suffix_title, suffix_title->GetName());
  10244. }
  10245. else
  10246. memset(player->appearance.suffix_title, 0, strlen(player->appearance.suffix_title));
  10247. if (prefix != -1) {
  10248. prefix_title = player->GetPlayerTitles()->GetTitle(prefix);
  10249. if (prefix_title)
  10250. strcpy(player->appearance.prefix_title, prefix_title->GetName());
  10251. }
  10252. else
  10253. memset(player->appearance.prefix_title, 0, strlen(player->appearance.prefix_title));
  10254. current_zone->SendUpdateTitles(this, suffix_title, prefix_title);
  10255. }
  10256. void Client::SendLanguagesUpdate(int32 id, bool setlang) {
  10257. list<Language*>* languages = player->GetPlayerLanguages()->GetAllLanguages();
  10258. list<Language*>::iterator itr;
  10259. Language* language;
  10260. int32 i = 0;
  10261. if (setlang == 1) {
  10262. GetPlayer()->SetCurrentLanguage(id);
  10263. }
  10264. PacketStruct* packet = configReader.getStruct("WS_Languages", GetVersion());
  10265. if (packet) {
  10266. packet->setArrayLengthByName("num_languages", languages->size());
  10267. for (itr = languages->begin(); itr != languages->end(); itr++) {
  10268. language = *itr;
  10269. packet->setArrayDataByName("language_id", language->GetID(), i);
  10270. i++;
  10271. }
  10272. packet->setDataByName("current_language", id);
  10273. LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__);
  10274. #if EQDEBUG >= 9
  10275. packet->PrintPacket();
  10276. #endif
  10277. QueuePacket(packet->serialize());
  10278. safe_delete(packet);
  10279. }
  10280. }
  10281. void Client::SendPetOptionsWindow(const char* pet_name, int8 type) {
  10282. PacketStruct* packet = configReader.getStruct("WS_PetOptions", GetVersion());
  10283. if (packet) {
  10284. if (pet_name)
  10285. packet->setDataByName("pet_name", pet_name);
  10286. if (player->GetInfoStruct()->get_pet_behavior() & 1)
  10287. packet->setDataByName("protect_master", 1);
  10288. if (player->GetInfoStruct()->get_pet_behavior() & 2)
  10289. packet->setDataByName("protect_self", 1);
  10290. if (player->GetInfoStruct()->get_pet_movement() == 2)
  10291. packet->setDataByName("stay_follow_toggle", 1);
  10292. packet->setDataByName("pet_type", type);
  10293. QueuePacket(packet->serialize());
  10294. }
  10295. safe_delete(packet);
  10296. }
  10297. bool Client::IsCrafting() {
  10298. return current_zone->GetTradeskillMgr()->IsClientCrafting(this);
  10299. }
  10300. void Client::SendBiography() {
  10301. PacketStruct* packet = configReader.getStruct("WS_BioUpdate", GetVersion());
  10302. if (packet) {
  10303. char biography[512];
  10304. if (player->GetInfoStruct()->get_biography().size() < 1)
  10305. {
  10306. safe_delete(packet);
  10307. return;
  10308. }
  10309. else
  10310. {
  10311. int16 size = player->GetInfoStruct()->get_biography().size();
  10312. if (size > 511)
  10313. size = 511;
  10314. strncpy(biography, player->GetInfoStruct()->get_biography().c_str(), player->GetInfoStruct()->get_biography().size());
  10315. biography[player->GetInfoStruct()->get_biography().size()] = '\0';
  10316. }
  10317. packet->setDataByName("biography", biography);
  10318. QueuePacket(packet->serialize());
  10319. }
  10320. safe_delete(packet);
  10321. }
  10322. PendingResurrection* Client::GetCurrentRez() {
  10323. return &current_rez;
  10324. }
  10325. Mutex* Client::GetResurrectMutex() {
  10326. return &m_resurrect;
  10327. }
  10328. void Client::SendResurrectionWindow() {
  10329. Spawn* caster = current_rez.caster;
  10330. if (!caster || !player)
  10331. return;
  10332. PacketStruct* packet = configReader.getStruct("WS_ChoiceWindow", GetVersion());
  10333. if (!packet)
  10334. return;
  10335. char* tmp = new char[512];
  10336. sprintf(tmp, "%s would like to cast '%s' on you. Do you accept?", caster->GetName(), current_rez.spell_name.c_str());
  10337. packet->setMediumStringByName("text", tmp);
  10338. packet->setMediumStringByName("accept_text", "Yes");
  10339. packet->setMediumStringByName("cancel_text", "No");
  10340. sprintf(tmp, "accept_resurrection %u", player->GetID());
  10341. packet->setMediumStringByName("accept_command", tmp);
  10342. sprintf(tmp, "decline_resurrection %u", player->GetID());
  10343. packet->setMediumStringByName("cancel_command", tmp);
  10344. packet->setDataByName("time", current_rez.expire_timer->GetRemainingTime() / 1000);
  10345. QueuePacket(packet->serialize());
  10346. safe_delete(packet);
  10347. safe_delete_array(tmp);
  10348. }
  10349. void Client::AcceptResurrection() {
  10350. Spawn* caster = current_rez.caster;
  10351. if (!player || !caster)
  10352. return;
  10353. if (player->Alive())
  10354. return;
  10355. if ((caster->GetZone() != player->GetZone()) || (current_rez.range > 0 && player->GetDistance(caster) > current_rez.range)) {
  10356. SimpleMessage(CHANNEL_COLOR_YELLOW, "The caster must be nearby to complete the spell.");
  10357. SendResurrectionWindow();
  10358. return;
  10359. }
  10360. player->GetZone()->ResurrectSpawn(player, this);
  10361. current_rez.should_delete = true;
  10362. }
  10363. void Client::SetPendingLastName(string last_name) {
  10364. pending_last_name = new string;
  10365. pending_last_name->assign(last_name);
  10366. }
  10367. string* Client::GetPendingLastName() {
  10368. return pending_last_name;
  10369. }
  10370. void Client::RemovePendingLastName() {
  10371. safe_delete(pending_last_name);
  10372. }
  10373. void Client::SendLastNameConfirmation() {
  10374. if (!pending_last_name)
  10375. return;
  10376. PacketStruct* packet = configReader.getStruct("WS_ChoiceWindow", GetVersion());
  10377. if (packet) {
  10378. char* text = new char[128];
  10379. sprintf(text, "Are you sure you want your last name to be \"%s\"?", pending_last_name->c_str());
  10380. packet->setDataByName("text", text);
  10381. packet->setDataByName("accept_text", "Yes");
  10382. packet->setDataByName("accept_command", "confirmedlastname");
  10383. packet->setDataByName("cancel_text", "No");
  10384. packet->setDataByName("max_length", 50);
  10385. packet->setDataByName("unknown4", 1);
  10386. packet->setDataByName("unknown5", 1);
  10387. QueuePacket(packet->serialize());
  10388. safe_delete(packet);
  10389. safe_delete_array(text);
  10390. }
  10391. }
  10392. void Client::AddQuestTimer(int32 quest_id) {
  10393. MQuestTimers.writelock(__FUNCTION__, __LINE__);
  10394. quest_timers.push_back(quest_id);
  10395. MQuestTimers.releasewritelock(__FUNCTION__, __LINE__);
  10396. }
  10397. void Client::RemoveQuestTimer(int32 quest_id) {
  10398. MQuestTimers.writelock(__FUNCTION__, __LINE__);
  10399. vector<int32>::iterator itr;
  10400. for (itr = quest_timers.begin(); itr != quest_timers.end(); itr++) {
  10401. if ((*itr) == quest_id) {
  10402. quest_timers.erase(itr);
  10403. break;
  10404. }
  10405. }
  10406. MQuestTimers.releasewritelock(__FUNCTION__, __LINE__);
  10407. }
  10408. void Client::SavePlayerImages() {
  10409. LogWrite(CCLIENT__DEBUG, 0, "Client", "Saving %s image for player %s (%u)", (incoming_paperdoll.image_type == PAPERDOLL_TYPE_FULL ? "paperdoll" : "headshot"), GetPlayer()->GetName(), GetCharacterID());
  10410. // Save the paperdoll image if the server allows it
  10411. if (incoming_paperdoll.image_type == PAPERDOLL_TYPE_FULL && rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, SavePaperdollImage)->GetBool())
  10412. database.SaveCharacterPicture(GetCharacterID(), incoming_paperdoll.image_type, incoming_paperdoll.image_bytes, incoming_paperdoll.current_size_bytes);
  10413. if (incoming_paperdoll.image_type == PAPERDOLL_TYPE_HEAD) {
  10414. // Save the head shot if the server allows it
  10415. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, SaveHeadshotImage)->GetBool())
  10416. database.SaveCharacterPicture(GetCharacterID(), incoming_paperdoll.image_type, incoming_paperdoll.image_bytes, incoming_paperdoll.current_size_bytes);
  10417. // Send the head shot to the login server
  10418. if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, SendPaperdollImagesToLogin)->GetBool()) {
  10419. int32 size = incoming_paperdoll.current_size_bytes + CHARPICSTRUCT_MINSIZE;
  10420. ServerPacket* packet = new ServerPacket(ServerOP_CharacterPicture, size);
  10421. memset(packet->pBuffer, 0, size);
  10422. CharPictureUpdate_Struct* pic = (CharPictureUpdate_Struct*)packet->pBuffer;
  10423. pic->account_id = GetAccountID();
  10424. pic->char_id = GetCharacterID();
  10425. pic->pic_size = (int16)incoming_paperdoll.current_size_bytes;
  10426. memcpy(pic->pic, incoming_paperdoll.image_bytes, incoming_paperdoll.current_size_bytes);
  10427. loginserver.SendPacket(packet);
  10428. safe_delete(packet);
  10429. }
  10430. }
  10431. safe_delete_array(incoming_paperdoll.image_bytes);
  10432. incoming_paperdoll.current_size_bytes = 0;
  10433. }
  10434. void Client::EndAutoMount() {
  10435. PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", GetVersion());
  10436. if (packet) {
  10437. packet->setDataByName("parameter1", 128);
  10438. packet->setDataByName("parameter2", 64);
  10439. packet->setDataByName("value", 1);
  10440. QueuePacket(packet->serialize());
  10441. safe_delete(packet);
  10442. }
  10443. packet = configReader.getStruct("WS_ServerControlFlags", GetVersion());
  10444. if (packet) {
  10445. packet->setDataByName("parameter3", 4);
  10446. packet->setDataByName("parameter5", 2);
  10447. packet->setDataByName("value", 0);
  10448. QueuePacket(packet->serialize());
  10449. safe_delete(packet);
  10450. }
  10451. packet = configReader.getStruct("WS_ClearForLanding", GetVersion());
  10452. if (packet) {
  10453. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(GetPlayer()));
  10454. QueuePacket(packet->serialize());
  10455. safe_delete(packet);
  10456. }
  10457. on_auto_mount = false;
  10458. player->SetMount(((Player*)player)->GetTempMount());
  10459. EQ2_Color mount_color = player->GetTempMountColor();
  10460. EQ2_Color saddle_color = player->GetTempMountSaddleColor();
  10461. player->SetMountColor(&mount_color);
  10462. player->SetMountSaddleColor(&saddle_color);
  10463. player->SetTempMount(0);
  10464. }
  10465. bool Client::EntityCommandPrecheck(Spawn* spawn, const char* command) {
  10466. const char* spawn_script = spawn->GetSpawnScript();
  10467. bool should_use_spawn = true;
  10468. if (spawn_script) {
  10469. lua_State* state = lua_interface->GetSpawnScript(spawn_script);
  10470. if (state) {
  10471. Mutex* state_mutex = lua_interface->GetSpawnScriptMutex(spawn_script);
  10472. if (state_mutex)
  10473. state_mutex->writelock(__FUNCTION__, __LINE__);
  10474. lua_getglobal(state, "can_use_command");
  10475. if (lua_isfunction(state, lua_gettop(state))) {
  10476. lua_interface->SetSpawnValue(state, spawn);
  10477. lua_interface->SetSpawnValue(state, GetPlayer());
  10478. lua_interface->SetStringValue(state, command ? command : "");
  10479. if (lua_pcall(state, 3, 1, 0) == 0) {
  10480. should_use_spawn = lua_interface->GetBooleanValue(state, 1);
  10481. }
  10482. }
  10483. lua_interface->ResetFunctionStack(state);
  10484. if (state_mutex)
  10485. state_mutex->releasewritelock(__FUNCTION__, __LINE__);
  10486. }
  10487. }
  10488. return should_use_spawn;
  10489. }
  10490. bool Client::IsCurrentTransmuteID(int32 req_id) {
  10491. return req_id == transmuteID;
  10492. }
  10493. void Client::SetTransmuteID(int32 trans_id) {
  10494. transmuteID = trans_id;
  10495. }
  10496. int32 Client::GetTransmuteID() {
  10497. return transmuteID;
  10498. }
  10499. bool Client::HandleNewLogin(int32 account_id, int32 access_code)
  10500. {
  10501. ZoneAuthRequest* zar = zone_auth.GetAuth(account_id, access_code);
  10502. if (zar)
  10503. {
  10504. int32 charID = database.GetCharacterID(zar->GetCharacterName());
  10505. if (database.IsActiveQuery(charID))
  10506. {
  10507. delayedLogin = true;
  10508. delayedAccountID = account_id;
  10509. delayedAccessKey = access_code;
  10510. delayTimer.Start(500);
  10511. LogWrite(ZONE__INFO, 0, "ZoneAuth", "Attempt to Login must be delayed, async character save in progress! ... Access Key: %u, Character Name: %s, Account ID: %u, Client Data Version: %u", zar->GetAccessKey(), zar->GetCharacterName(), zar->GetAccountID(), version);
  10512. return true;
  10513. }
  10514. delayedLogin = false;
  10515. delayTimer.Disable();
  10516. firstlogin = zar->isFirstLogin();
  10517. SetReadyForSpawns(false);
  10518. ready_for_updates = false;
  10519. LogWrite(ZONE__INFO, 0, "ZoneAuth", "Access Key: %u, Character Name: %s, Account ID: %u, Client Data Version: %u", zar->GetAccessKey(), zar->GetCharacterName(), zar->GetAccountID(), version);
  10520. Client* client = zone_list.GetClientByCharName(zar->GetCharacterName());
  10521. if (client || database.loadCharacter(zar->GetCharacterName(), zar->GetAccountID(), this)) {
  10522. GetPlayer()->CalculateOfflineDebtRecovery(GetLastSavedTimeStamp());
  10523. GetPlayer()->vis_changed = false;
  10524. GetPlayer()->info_changed = false;
  10525. bool pvp_allowed = rule_manager.GetZoneRule(GetCurrentZoneID(), R_PVP, AllowPVP)->GetBool();
  10526. if (pvp_allowed)
  10527. this->GetPlayer()->SetAttackable(1);
  10528. MDeletePlayer.writelock(__FUNCTION__, __LINE__);
  10529. if (client) {
  10530. if (client->getConnection())
  10531. client->getConnection()->SendDisconnect(true);
  10532. bool restore_ld_success = false;
  10533. if (client->GetCurrentZone() && !client->IsZoning()) {
  10534. //swap players, allowing the client to resume his LD'd player (ONLY if same version of the client)
  10535. if (client->GetVersion() == version) {
  10536. client->DisableSave();
  10537. client->ReplaceGroupClient(this);
  10538. Player* current_player = GetPlayer();
  10539. SetPlayer(client->GetPlayer());
  10540. GetPlayer()->SetClient(this);
  10541. GetPlayer()->SetReturningFromLD(true);
  10542. SetCharacterID(client->GetCharacterID());
  10543. SetAccountID(client->GetAccountID());
  10544. SetAdminStatus(client->GetAdminStatus());
  10545. SetCurrentZone(GetPlayer()->GetZone());
  10546. client->SetPlayer(current_player);
  10547. GetPlayer()->ResetSavedSpawns();
  10548. restore_ld_success = true;
  10549. char tmpldname[128];
  10550. snprintf(tmpldname, 128, "%s Linkdead", GetPlayer()->GetName());
  10551. client->GetPlayer()->SetName(tmpldname, false);
  10552. }
  10553. ZoneServer* tmpZone = client->GetCurrentZone();
  10554. tmpZone->RemoveClientImmediately(client);
  10555. }
  10556. if (!restore_ld_success && !database.loadCharacter(zar->GetCharacterName(), zar->GetAccountID(), this)) {
  10557. LogWrite(ZONE__ERROR, 0, "Zone", "Error reloading LD character and loading DB character: %s", player->GetName());
  10558. ClientPacketFunctions::SendLoginDenied(this);
  10559. Disconnect();
  10560. return false;
  10561. }
  10562. }
  10563. MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
  10564. if (!GetCurrentZone()) {
  10565. LogWrite(ZONE__ERROR, 0, "Zone", "Error loading zone for character: %s", player->GetName());
  10566. ClientPacketFunctions::SendLoginDenied(this);
  10567. Disconnect();
  10568. return false;
  10569. }
  10570. else if (EQOpcodeManager.count(GetOpcodeVersion(version)) > 0 && getConnection()) {
  10571. getConnection()->SetClientVersion(version);
  10572. GetCurrentZone()->SetSpawnStructs(this);
  10573. connected_to_zone = true;
  10574. client_list.Remove(this); //remove from master client list
  10575. GetCurrentZone()->AddClient(this); //add to zones client list
  10576. zone_list.AddClientToMap(player->GetName(), this);
  10577. // this initiates additional DB loading and client offloading within the Zone the player resides, need the client already added in zone and to the map above.
  10578. if (GetCurrentZone()->IsLoading()) {
  10579. new_client_login = NewLoginState::LOGIN_DELAYED;
  10580. }
  10581. else {
  10582. new_client_login = NewLoginState::LOGIN_ALLOWED;
  10583. }
  10584. const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID());
  10585. if (zone_script && lua_interface)
  10586. lua_interface->RunZoneScript(zone_script, "new_client", GetCurrentZone(), GetPlayer());
  10587. }
  10588. else {
  10589. LogWrite(WORLD__ERROR, 0, "World", "Incompatible version: %i", version);
  10590. version = 0;
  10591. ClientPacketFunctions::SendLoginDenied(this);
  10592. Disconnect();
  10593. return false;
  10594. }
  10595. }
  10596. else {
  10597. LogWrite(WORLD__ERROR, 0, "World", "Could not load character '%s' with account id of: %u", zar->GetCharacterName(), zar->GetAccountID());
  10598. ClientPacketFunctions::SendLoginDenied(this);
  10599. Disconnect();
  10600. return false;
  10601. }
  10602. zone_auth.RemoveAuth(zar);
  10603. }
  10604. else
  10605. {
  10606. LogWrite(WORLD__ERROR, 0, "World", "Invalid ZoneAuthRequest, disconnecting client.");
  10607. Disconnect();
  10608. return false;
  10609. }
  10610. return true;
  10611. }
  10612. void Client::SendSpawnChanges(set<Spawn*>& spawns) {
  10613. if (!IsReadyForUpdates())
  10614. return;
  10615. map<int32, SpawnData> info_changes;
  10616. map<int32, SpawnData> pos_changes;
  10617. map<int32, SpawnData> vis_changes;
  10618. map<int32, SpawnData> empty_changes;
  10619. int32 info_size = 0;
  10620. int32 pos_size = 0;
  10621. int32 vis_size = 0;
  10622. int count = 0;
  10623. for (const auto& spawn : spawns) {
  10624. int16 index = GetPlayer()->GetIndexForSpawn(spawn);
  10625. if (index == 0 || !GetPlayer()->WasSentSpawn(spawn->GetID()))
  10626. continue;
  10627. if (GetPlayer() == spawn && GetVersion() <= 284) { // stopped self client/player warping in the EQ2 release disc (Beta), don't send yourself in bulk spawn updates
  10628. continue;
  10629. }
  10630. int16 tmp_info_size = 0;
  10631. int16 tmp_pos_size = 0;
  10632. int16 tmp_vis_size = 0;
  10633. if (spawn->vis_changed)
  10634. {
  10635. auto vis_change = spawn->spawn_vis_changes_ex(GetPlayer(), GetVersion(), &tmp_vis_size);
  10636. if (vis_change) {
  10637. SpawnData data;
  10638. data.spawn = spawn;
  10639. data.data = vis_change;
  10640. data.size = tmp_vis_size;
  10641. map<int32, SpawnData> tmp_vis_changes;
  10642. tmp_vis_changes[index] = data;
  10643. map<int32, SpawnData> tmp_info_changes;
  10644. map<int32, SpawnData> tmp_pos_changes;
  10645. if (spawn->info_changed) {
  10646. auto info_change = spawn->spawn_info_changes_ex(GetPlayer(), GetVersion(), &tmp_info_size);
  10647. if (info_change) {
  10648. SpawnData data;
  10649. data.spawn = spawn;
  10650. data.data = info_change;
  10651. data.size = tmp_info_size;
  10652. tmp_info_changes[index] = data;
  10653. }
  10654. }
  10655. if (spawn->position_changed) {
  10656. auto pos_change = spawn->spawn_pos_changes_ex(GetPlayer(), GetVersion(), &tmp_pos_size);
  10657. if (pos_change) {
  10658. SpawnData data;
  10659. data.spawn = spawn;
  10660. data.data = pos_change;
  10661. data.size = tmp_pos_size;
  10662. tmp_pos_changes[index] = data;
  10663. }
  10664. }
  10665. MakeSpawnChangePacket(tmp_info_changes, tmp_pos_changes, tmp_vis_changes, tmp_info_size, tmp_pos_size, data.size);
  10666. for (auto& kv : tmp_info_changes) {
  10667. safe_delete_array(kv.second.data);
  10668. }
  10669. for (auto& kv : tmp_pos_changes) {
  10670. safe_delete_array(kv.second.data);
  10671. }
  10672. for (auto& kv : tmp_vis_changes) {
  10673. safe_delete_array(kv.second.data);
  10674. }
  10675. continue;
  10676. }
  10677. }
  10678. if (spawn->info_changed) {
  10679. auto info_change = spawn->spawn_info_changes_ex(GetPlayer(), GetVersion(), &tmp_info_size);
  10680. if (info_change) {
  10681. SpawnData data;
  10682. data.spawn = spawn;
  10683. data.data = info_change;
  10684. data.size = tmp_info_size;
  10685. info_size += tmp_info_size;
  10686. info_changes[index] = data;
  10687. }
  10688. count++;
  10689. }
  10690. if (spawn->position_changed) {
  10691. auto pos_change = spawn->spawn_pos_changes_ex(GetPlayer(), GetVersion(), &tmp_pos_size);
  10692. if (pos_change) {
  10693. SpawnData data;
  10694. data.spawn = spawn;
  10695. data.data = pos_change;
  10696. data.size = tmp_pos_size;
  10697. pos_size += tmp_pos_size;
  10698. pos_changes[index] = data;
  10699. }
  10700. count++;
  10701. }
  10702. if (spawn->vis_changed) {
  10703. auto vis_change = spawn->spawn_vis_changes_ex(GetPlayer(), GetVersion(), &tmp_vis_size);
  10704. if (vis_change) {
  10705. SpawnData data;
  10706. data.spawn = spawn;
  10707. data.data = vis_change;
  10708. data.size = tmp_vis_size;
  10709. vis_size += tmp_vis_size;
  10710. vis_changes[index] = data;
  10711. }
  10712. count++;
  10713. }
  10714. }
  10715. if (info_size == 0 && pos_size == 0 && vis_size == 0) {
  10716. return;
  10717. }
  10718. MakeSpawnChangePacket(info_changes, pos_changes, vis_changes, info_size, pos_size, vis_size);
  10719. for (auto& kv : info_changes) {
  10720. safe_delete_array(kv.second.data);
  10721. }
  10722. for (auto& kv : pos_changes) {
  10723. safe_delete_array(kv.second.data);
  10724. }
  10725. for (auto& kv : vis_changes) {
  10726. safe_delete_array(kv.second.data);
  10727. }
  10728. }
  10729. void Client::MakeSpawnChangePacket(map<int32, SpawnData> info_changes, map<int32, SpawnData> pos_changes, map<int32, SpawnData> vis_changes, int32 info_size, int32 pos_size, int32 vis_size)
  10730. {
  10731. static const int8 oversized = 255;
  10732. int16 opcode_val = 0;
  10733. if (EQOpcodeManager.count(GetOpcodeVersion(version)) != 0) {
  10734. opcode_val = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(OP_EqUpdateGhostCmd);
  10735. }
  10736. int32 size = info_size + pos_size + vis_size + 8;
  10737. size += CheckOverLoadSize(info_size);
  10738. size += CheckOverLoadSize(pos_size);
  10739. size += CheckOverLoadSize(vis_size);
  10740. if (version <= 373 && size >= 255) {//1 byte to 3 for overloaded val
  10741. size += 2;
  10742. }
  10743. else if (version >= 374)
  10744. size += 3;
  10745. uchar* tmp = new uchar[size];
  10746. uchar* ptr = tmp;
  10747. memset(tmp, 0, size);
  10748. if (version <= 373) {
  10749. if (size >= 255) {
  10750. size -= 3;
  10751. memcpy(ptr, &oversized, sizeof(int8));
  10752. ptr += sizeof(int8);
  10753. memcpy(ptr, &size, sizeof(int16));
  10754. ptr += sizeof(int16);
  10755. size += 3;
  10756. }
  10757. else {
  10758. size -= 1;
  10759. memcpy(ptr, &size, sizeof(int8));
  10760. ptr += sizeof(int8);
  10761. size += 1;
  10762. }
  10763. }
  10764. else {
  10765. size -= 4;
  10766. memcpy(ptr, &size, sizeof(int32));
  10767. ptr += sizeof(int32);
  10768. size += 4;
  10769. }
  10770. memcpy(ptr, &oversized, sizeof(int8));
  10771. ptr += sizeof(int8);
  10772. memcpy(ptr, &opcode_val, sizeof(int16));
  10773. ptr += sizeof(int16);
  10774. int32 current_time = Timer::GetCurrentTime2();
  10775. memcpy(ptr, &current_time, sizeof(int32));
  10776. ptr += sizeof(int32);
  10777. ptr += DoOverLoad(info_size, ptr);
  10778. for (const auto& kv : info_changes) {
  10779. auto info = kv.second;
  10780. memcpy(ptr, info.data, info.size);
  10781. ptr += info.size;
  10782. }
  10783. ptr += DoOverLoad(pos_size, ptr);
  10784. for (const auto& kv : pos_changes) {
  10785. auto pos = kv.second;
  10786. memcpy(ptr, pos.data, pos.size);
  10787. ptr += pos.size;
  10788. }
  10789. ptr += DoOverLoad(vis_size, ptr);
  10790. for (const auto& kv : vis_changes) {
  10791. auto vis = kv.second;
  10792. memcpy(ptr, vis.data, vis.size);
  10793. ptr += vis.size;
  10794. }
  10795. EQ2Packet* packet = new EQ2Packet(OP_ClientCmdMsg, tmp, size);
  10796. if (packet) {
  10797. /*char blah[64];
  10798. snprintf(blah, 64, "Sending %i", current_time);
  10799. SimpleMessage(4, blah);*/
  10800. QueuePacket(packet);
  10801. }
  10802. delete[] tmp;
  10803. }
  10804. void Client::SendHailCommand(Spawn* spawn)
  10805. {
  10806. // hardcoded 'hail' entity commands
  10807. switch (spawn->secondary_command_list_id)
  10808. {
  10809. case 9:
  10810. case 1262:
  10811. case 1265:
  10812. case 1267:
  10813. {
  10814. EQ2_16BitString* command = new EQ2_16BitString();
  10815. command->data = "";
  10816. command->size = 0;
  10817. commands.Process(COMMAND_HAIL, command, this, spawn);
  10818. safe_delete(command);
  10819. break;
  10820. }
  10821. }
  10822. }
  10823. void Client::SendDefaultCommand(Spawn* spawn, const char* command, float distance)
  10824. {
  10825. if (GetPlayer()->WasSentSpawn(spawn->GetID())) {
  10826. PacketStruct* packet = configReader.getStruct("WS_SetDefaultCommand", GetVersion());
  10827. if (packet) {
  10828. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
  10829. packet->setMediumStringByName("command_name", command);
  10830. packet->setDataByName("distance", distance);
  10831. QueuePacket(packet->serialize());
  10832. safe_delete(packet);
  10833. }
  10834. }
  10835. }
  10836. bool Client::HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string command)
  10837. {
  10838. if (GetCurrentZone()->GetInstanceType() != PERSONAL_HOUSE_INSTANCE)
  10839. return false;
  10840. if (command == "house_spawn_examine")
  10841. {
  10842. uint32 itemID = spawn->GetPickupItemID();
  10843. if (itemID)
  10844. {
  10845. Item* item = master_item_list.GetItem(itemID);
  10846. if (item)
  10847. {
  10848. EQ2Packet* app = item->serialize(GetVersion(), true, GetPlayer());
  10849. //DumpPacket(app);
  10850. QueuePacket(app);
  10851. }
  10852. }
  10853. return true;
  10854. }
  10855. return false;
  10856. }
  10857. bool Client::PopulateHouseSpawn(PacketStruct* place_object)
  10858. {
  10859. if (GetTempPlacementSpawn())
  10860. {
  10861. Spawn* tmp = GetTempPlacementSpawn();
  10862. int32 spawn_group_id = database.GetNextSpawnLocation();
  10863. tmp->SetSpawnLocationID(spawn_group_id);
  10864. float newHeading = place_object->getType_float_ByName("heading") + 180;
  10865. int32 spawnDBID = 0;
  10866. if (GetCurrentZone()->house_object_database_lookup.count(tmp->GetModelType()) > 0)
  10867. {
  10868. spawnDBID = GetCurrentZone()->house_object_database_lookup.Get(tmp->GetModelType());
  10869. tmp->SetDatabaseID(spawnDBID);
  10870. }
  10871. else
  10872. {
  10873. spawnDBID = database.FindHouseInstanceSpawn(tmp);
  10874. if (spawnDBID)
  10875. {
  10876. GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), spawnDBID);
  10877. tmp->SetDatabaseID(spawnDBID);
  10878. }
  10879. }
  10880. tmp->SetX(place_object->getType_float_ByName("x"));
  10881. tmp->SetY(place_object->getType_float_ByName("y"));
  10882. tmp->SetZ(place_object->getType_float_ByName("z"));
  10883. tmp->SetHeading(newHeading);
  10884. tmp->SetSpawnOrigX(tmp->GetX());
  10885. tmp->SetSpawnOrigY(tmp->GetY());
  10886. tmp->SetSpawnOrigZ(tmp->GetZ());
  10887. tmp->SetSpawnOrigHeading(tmp->GetHeading());
  10888. database.SaveSpawnInfo(tmp);
  10889. database.SaveSpawnEntry(tmp, "houseplacement", 100, 0, 0, 0);
  10890. if (!spawnDBID)
  10891. {
  10892. GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), tmp->GetDatabaseID());
  10893. // we need to copy as to not delete the ZoneServer object_list entry this on house item pickup
  10894. GetCurrentZone()->AddObject(tmp->GetDatabaseID(), ((Object*)tmp)->Copy());
  10895. }
  10896. return true;
  10897. }
  10898. return false;
  10899. }
  10900. bool Client::PopulateHouseSpawnFinalize()
  10901. {
  10902. if (GetTempPlacementSpawn())
  10903. {
  10904. Spawn* tmp = GetTempPlacementSpawn();
  10905. GetCurrentZone()->AddSpawn(tmp);
  10906. GetCurrentZone()->SendSpawnChanges(tmp, this);
  10907. SetTempPlacementSpawn(nullptr);
  10908. int32 uniqueID = GetPlacementUniqueItemID();
  10909. Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
  10910. tmp->SetPickupItemID(uniqueItem->details.item_id);
  10911. tmp->SetPickupUniqueItemID(uniqueID);
  10912. if (uniqueItem)
  10913. {
  10914. if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
  10915. {
  10916. Query query;
  10917. query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID);
  10918. }
  10919. if (uniqueItem->GetItemScript() &&
  10920. lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp))
  10921. {
  10922. uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
  10923. }
  10924. if (uniqueItem) {
  10925. database.DeleteItem(GetCharacterID(), uniqueItem, 0);
  10926. GetPlayer()->item_list.RemoveItem(uniqueItem, true);
  10927. QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
  10928. }
  10929. SetPlacementUniqueItemID(0);
  10930. }
  10931. return true;
  10932. }
  10933. return false;
  10934. }
  10935. void Client::SendMoveObjectMode(Spawn* spawn, uint8 placementMode, float unknown2_3)
  10936. {
  10937. PacketStruct* packet = configReader.getStruct("WS_MoveObjectMode", GetVersion());
  10938. packet->setDataByName("placement_mode", placementMode);
  10939. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
  10940. packet->setDataByName("model_type", spawn->GetModelType());
  10941. packet->setDataByName("unknown", 1); //size
  10942. packet->setDataByName("unknown2", 1); //size 2
  10943. packet->setDataByName("unknown2", .5, 1); //size 3
  10944. packet->setDataByName("unknown2", 3, 2);
  10945. packet->setDataByName("unknown2", unknown2_3, 3);
  10946. packet->setDataByName("max_distance", 500);
  10947. packet->setDataByName("CoEunknown", 0xFFFFFFFF);
  10948. QueuePacket(packet->serialize());
  10949. safe_delete(packet);
  10950. }
  10951. void Client::SendFlightAutoMount(int32 path_id, int16 mount_id, int8 mount_red_color, int8 mount_green_color, int8 mount_blue_color)
  10952. {
  10953. SetPendingFlightPath(path_id);
  10954. ((Player*)player)->SetTempMount(((Entity*)player)->GetMount());
  10955. ((Player*)player)->SetTempMountColor(((Entity*)player)->GetMountColor());
  10956. ((Player*)player)->SetTempMountSaddleColor(((Entity*)player)->GetMountSaddleColor());
  10957. PacketStruct* packet = configReader.getStruct("WS_ReadyForTakeOff", GetVersion());
  10958. if (packet) {
  10959. QueuePacket(packet->serialize());
  10960. safe_delete(packet);
  10961. }
  10962. if (mount_id)
  10963. ((Entity*)GetPlayer())->SetMount(mount_id, mount_red_color, mount_green_color, mount_blue_color);
  10964. }
  10965. void Client::SendShowBook(Spawn* sender, string title, int8 language, int8 num_pages, ...)
  10966. {
  10967. if (!sender)
  10968. {
  10969. LogWrite(CCLIENT__ERROR, 0, "Client", "SendShowBook missing sender for Player %s, book title %s", GetPlayer()->GetName(), title);
  10970. return;
  10971. }
  10972. PacketStruct* packet = configReader.getStruct("WS_EqShowBook", GetVersion());
  10973. if (!packet) {
  10974. LogWrite(CCLIENT__ERROR, 0, "Client", "WS_EqShowBook missing for version %u", GetVersion());
  10975. return;
  10976. }
  10977. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(sender));
  10978. packet->setDataByName("book_title", title.c_str());
  10979. packet->setDataByName("book_type", "simple");
  10980. packet->setDataByName("unknown2", 1);
  10981. if (language > 0 && !GetPlayer()->HasLanguage(language))
  10982. packet->setDataByName("language", language);
  10983. if (GetVersion() > 561)
  10984. packet->setDataByName("unknown5", 1, 4);
  10985. packet->setArrayLengthByName("num_pages", num_pages);
  10986. va_list args;
  10987. va_start(args, num_pages);
  10988. std::string endString("");
  10989. for (int8 p = 0; p < num_pages; p++)
  10990. {
  10991. std::string page = va_arg(args, string);
  10992. switch (GetVersion())
  10993. {
  10994. // release client
  10995. case 283:
  10996. case 373: // trial isle client
  10997. {
  10998. endString.append(page);
  10999. break;
  11000. }
  11001. // DoF trial
  11002. case 546:
  11003. case 561:
  11004. {
  11005. if (p == 0)
  11006. packet->setDataByName("cover_page", page.c_str());
  11007. else
  11008. packet->setArrayDataByName("page_text", page.c_str(), p - 1);
  11009. break;
  11010. }
  11011. // all other clients
  11012. default:
  11013. {
  11014. packet->setArrayDataByName("page_text", page.c_str(), p);
  11015. break;
  11016. }
  11017. }
  11018. }
  11019. if (GetVersion() <= 373)
  11020. {
  11021. packet->setDataByName("page_text", endString.c_str());
  11022. }
  11023. va_end(args);
  11024. QueuePacket(packet->serialize());
  11025. safe_delete(packet);
  11026. }
  11027. void Client::SendShowBook(Spawn* sender, string title, int8 language, vector<Item::BookPage*> pages)
  11028. {
  11029. if (!sender)
  11030. {
  11031. LogWrite(CCLIENT__ERROR, 0, "Client", "SendShowBook missing sender for Player %s, book title %s", GetPlayer()->GetName(), title);
  11032. return;
  11033. }
  11034. PacketStruct* packet = configReader.getStruct("WS_EqShowBook", GetVersion());
  11035. if (!packet) {
  11036. LogWrite(CCLIENT__ERROR, 0, "Client", "WS_EqShowBook missing for version %u", GetVersion());
  11037. return;
  11038. }
  11039. packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(sender));
  11040. packet->setDataByName("book_title", title.c_str());
  11041. packet->setDataByName("book_type", "simple");
  11042. packet->setDataByName("unknown2", 1);
  11043. if (language > 0 && !GetPlayer()->HasLanguage(language))
  11044. packet->setDataByName("language", language);
  11045. if (GetVersion() > 561)
  11046. packet->setDataByName("unknown5", 1, 4);
  11047. packet->setArrayLengthByName("num_pages", pages.size());
  11048. std::string endString("");
  11049. for (int8 p = 0; p < pages.size(); p++)
  11050. {
  11051. Item::BookPage* page = pages[p];
  11052. std::string pageText = string(page->page_text.data);
  11053. switch (GetVersion())
  11054. {
  11055. // release client
  11056. case 283:
  11057. case 373: // trial isle client
  11058. {
  11059. endString.append(pageText);
  11060. break;
  11061. }
  11062. // DoF trial
  11063. case 546:
  11064. case 561:
  11065. {
  11066. if (p == 0)
  11067. packet->setDataByName("cover_page", pageText.c_str());
  11068. else
  11069. packet->setArrayDataByName("page_text", pageText.c_str(), p - 1);
  11070. break;
  11071. }
  11072. // all other clients
  11073. default:
  11074. {
  11075. int8 valign = int8(page->valign);
  11076. int8 halign = int8(page->halign);
  11077. packet->setArrayDataByName("page_text", pageText.c_str(), p);
  11078. packet->setArrayDataByName("page_text_valign", valign, p);
  11079. packet->setArrayDataByName("page_text_halign", halign, p);
  11080. break;
  11081. }
  11082. }
  11083. }
  11084. if (GetVersion() == 283)
  11085. {
  11086. packet->setDataByName("page_text", endString.c_str());
  11087. }
  11088. QueuePacket(packet->serialize());
  11089. safe_delete(packet);
  11090. }
  11091. void Client::ReplaceGroupClient(Client* new_client)
  11092. {
  11093. if (this->GetPlayer()->GetGroupMemberInfo())
  11094. {
  11095. world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
  11096. PlayerGroup* group = world.GetGroupManager()->GetGroup(this->GetPlayer()->GetGroupMemberInfo()->group_id);
  11097. if (group)
  11098. {
  11099. group->MGroupMembers.writelock();
  11100. rejoin_group_id = this->GetPlayer()->GetGroupMemberInfo()->group_id;
  11101. this->GetPlayer()->GetGroupMemberInfo()->client = new_client;
  11102. this->GetPlayer()->GetGroupMemberInfo()->member = GetPlayer();
  11103. group->MGroupMembers.releasewritelock();
  11104. }
  11105. else
  11106. {
  11107. this->GetPlayer()->GetGroupMemberInfo()->client = 0;
  11108. this->GetPlayer()->GetGroupMemberInfo()->member = 0;
  11109. this->GetPlayer()->SetGroupMemberInfo(0);
  11110. }
  11111. world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
  11112. }
  11113. }
  11114. void Client::TempRemoveGroup()
  11115. {
  11116. if (this->GetPlayer()->GetGroupMemberInfo())
  11117. {
  11118. world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
  11119. PlayerGroup* group = world.GetGroupManager()->GetGroup(this->GetPlayer()->GetGroupMemberInfo()->group_id);
  11120. if (group)
  11121. {
  11122. group->MGroupMembers.writelock();
  11123. rejoin_group_id = this->GetPlayer()->GetGroupMemberInfo()->group_id;
  11124. this->GetPlayer()->GetGroupMemberInfo()->client = 0;
  11125. this->GetPlayer()->GetGroupMemberInfo()->member = 0;
  11126. this->GetPlayer()->SetGroupMemberInfo(0);
  11127. group->MGroupMembers.releasewritelock();
  11128. group->RemoveClientReference(this);
  11129. }
  11130. else
  11131. {
  11132. this->GetPlayer()->GetGroupMemberInfo()->client = 0;
  11133. this->GetPlayer()->GetGroupMemberInfo()->member = 0;
  11134. this->GetPlayer()->SetGroupMemberInfo(0);
  11135. }
  11136. world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
  11137. }
  11138. }
  11139. void Client::CreateMail(int32 charID, std::string fromName, std::string subjectName, std::string mailBody,
  11140. int8 mailType, int32 copper, int32 silver, int32 gold, int32 platinum, int32 item_id, int16 stack_size, int32 time_sent, int32 expire_time)
  11141. {
  11142. Mail mail;
  11143. mail.mail_id = 0;
  11144. mail.already_read = 0;
  11145. mail.postage_cost = 0;
  11146. mail.attachment_cost = 0;
  11147. mail.save_needed = 1;
  11148. //uhh...mail has std::strings so
  11149. //memset(&mail,0,sizeof(Mail));
  11150. mail.player_to_id = charID;
  11151. mail.player_from = fromName;
  11152. mail.subject = subjectName;
  11153. mail.mail_body = mailBody;
  11154. mail.mail_type = mailType;
  11155. mail.coin_copper = copper;
  11156. mail.coin_silver = silver;
  11157. mail.coin_gold = gold;
  11158. mail.coin_plat = platinum;
  11159. mail.char_item_id = item_id;
  11160. mail.stack = stack_size;
  11161. mail.time_sent = time_sent;
  11162. mail.expire_time = expire_time;
  11163. database.SavePlayerMail(&mail);
  11164. }
  11165. void Client::CreateAndUpdateMail(std::string fromName, std::string subjectName, std::string mailBody,
  11166. int8 mailType, int32 copper, int32 silver, int32 gold, int32 platinum, int32 item_id, int16 stack_size, int32 time_sent, int32 expire_time)
  11167. {
  11168. Mail* mail = new Mail();
  11169. mail->player_to_id = GetCharacterID();
  11170. mail->player_from = fromName;
  11171. mail->subject = subjectName;
  11172. mail->mail_body = mailBody;
  11173. mail->mail_type = mailType;
  11174. mail->coin_copper = copper;
  11175. mail->coin_silver = silver;
  11176. mail->coin_gold = gold;
  11177. mail->coin_plat = platinum;
  11178. mail->char_item_id = item_id;
  11179. mail->stack = stack_size;
  11180. mail->time_sent = time_sent;
  11181. mail->expire_time = expire_time;
  11182. mail->postage_cost = 0;
  11183. mail->save_needed = 1;
  11184. mail->already_read = 0;
  11185. database.SavePlayerMail(mail);
  11186. GetPlayer()->AddMail(mail);
  11187. }
  11188. void Client::SendEquipOrInvUpdateBySlot(int8 slot)
  11189. {
  11190. if (slot < NUM_SLOTS)
  11191. {
  11192. EQ2Packet* app = GetPlayer()->GetEquipmentList()->serialize(GetVersion(), GetPlayer());
  11193. if (app)
  11194. QueuePacket(app);
  11195. }
  11196. else
  11197. {
  11198. EQ2Packet* outapp = GetPlayer()->SendInventoryUpdate(GetVersion());
  11199. if (outapp)
  11200. QueuePacket(outapp);
  11201. }
  11202. }
  11203. void Client::QueueStateCommand(int32 spawn_player_id, int32 state)
  11204. {
  11205. if (spawn_player_id < 1)
  11206. return;
  11207. MQueueStateCmds.writelock();
  11208. queued_state_commands.insert(make_pair(spawn_player_id, state));
  11209. MQueueStateCmds.releasewritelock();
  11210. }
  11211. void Client::ProcessStateCommands()
  11212. {
  11213. if (!IsReadyForUpdates())
  11214. return;
  11215. MQueueStateCmds.writelock();
  11216. map<int32, int32>::iterator itr = queued_state_commands.begin();
  11217. for (; itr != queued_state_commands.end(); itr++)
  11218. ClientPacketFunctions::SendStateCommand(this, itr->first, itr->second);
  11219. queued_state_commands.clear();
  11220. MQueueStateCmds.releasewritelock();
  11221. }
  11222. void Client::PurgeItem(Item* item)
  11223. {
  11224. std::unique_lock lock(MConversation);
  11225. map<int32, Item*>::iterator itr;
  11226. for (itr = conversation_items.begin(); itr != conversation_items.end(); itr++)
  11227. {
  11228. if (itr->second == item)
  11229. {
  11230. conversation_items.erase(itr);
  11231. break;
  11232. }
  11233. }
  11234. }
  11235. void Client::ConsumeFoodDrink(Item* item, int32 slot)
  11236. {
  11237. if (GetPlayer()->StopSaveSpellEffects())
  11238. return;
  11239. if (item) {
  11240. LogWrite(MISC__INFO, 1, "Command", "ItemID: %u, ItemName: %s ItemCount: %i ", item->details.item_id, item->name.c_str(), item->details.count);
  11241. if (item->GetItemScript() && lua_interface) {
  11242. lua_interface->RunItemScript(item->GetItemScript(), "cast", item, GetPlayer());
  11243. if (slot == 22) {
  11244. Message(CHANNEL_NARRATIVE, "You eat a %s.", item->name.c_str());
  11245. GetPlayer()->SetActiveFoodUniqueID(item->details.unique_id);
  11246. }
  11247. else {
  11248. Message(CHANNEL_NARRATIVE, "You drink a %s.", item->name.c_str());
  11249. GetPlayer()->SetActiveDrinkUniqueID(item->details.unique_id);
  11250. }
  11251. }
  11252. else {
  11253. Message(CHANNEL_NARRATIVE, "SERVER BUG! Item Script not assigned for consuming '%s'.", item->name.c_str());
  11254. return;
  11255. }
  11256. if (item->details.count > 1) {
  11257. item->details.count -= 1;
  11258. item->save_needed = true;
  11259. }
  11260. else {
  11261. database.DeleteItem(GetPlayer()->GetCharacterID(), item, "EQUIPPED");
  11262. GetPlayer()->GetEquipmentList()->RemoveItem(slot, true);
  11263. }
  11264. GetPlayer()->SetCharSheetChanged(true);
  11265. QueuePacket(player->GetEquipmentList()->serialize(GetVersion(), player));
  11266. if (GetVersion() <= 373) {
  11267. EQ2Packet* outapp = GetPlayer()->SendInventoryUpdate(GetVersion());
  11268. QueuePacket(outapp);
  11269. }
  11270. }
  11271. }
  11272. void Client::AwardCoins(int64 total_coins, std::string reason)
  11273. {
  11274. if (total_coins > 0) {
  11275. player->AddCoins(total_coins);
  11276. PlaySound("coin_cha_ching");
  11277. char tmp[64] = { 0 };
  11278. string message = "You receive ";
  11279. int32 val = 0;
  11280. if (total_coins >= 1000000) {
  11281. val = total_coins / 1000000;
  11282. total_coins -= 1000000 * val;
  11283. sprintf(tmp, "%u Platinum ", val);
  11284. message.append(tmp);
  11285. memset(tmp, 0, 64);
  11286. }
  11287. if (total_coins >= 10000) {
  11288. val = total_coins / 10000;
  11289. total_coins -= 10000 * val;
  11290. sprintf(tmp, "%u Gold ", val);
  11291. message.append(tmp);
  11292. memset(tmp, 0, 64);
  11293. }
  11294. if (total_coins >= 100) {
  11295. val = total_coins / 100;
  11296. total_coins -= 100 * val;
  11297. sprintf(tmp, "%u Silver ", val);
  11298. message.append(tmp);
  11299. memset(tmp, 0, 64);
  11300. }
  11301. if (total_coins > 0) {
  11302. sprintf(tmp, "%u Copper ", (int32)total_coins);
  11303. message.append(tmp);
  11304. }
  11305. message.append(reason);
  11306. int8 type = CHANNEL_LOOT;
  11307. SimpleMessage(type, message.c_str());
  11308. }
  11309. }
  11310. void Client::TriggerSpellSave()
  11311. {
  11312. int32 interval = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Spells, PlayerSpellSaveStateWaitInterval)->GetInt32();
  11313. // default to not have some bogus value in the rule
  11314. if (interval < 1)
  11315. interval = 100;
  11316. MSaveSpellStateMutex.lock();
  11317. if (!save_spell_state_timer.Enabled())
  11318. {
  11319. save_spell_state_time_bucket = 0;
  11320. save_spell_state_timer.Start(interval, true);
  11321. }
  11322. else
  11323. {
  11324. int32 elapsed_time = save_spell_state_timer.GetElapsedTime();
  11325. save_spell_state_time_bucket += elapsed_time;
  11326. int32 save_wait_cap = rule_manager.GetZoneRule(GetCurrentZoneID(), R_Spells, PlayerSpellSaveStateCap)->GetInt32();
  11327. // default to not have some bogus value in the rule
  11328. if (save_wait_cap < interval)
  11329. save_wait_cap = interval + 1;
  11330. if (save_spell_state_time_bucket >= save_wait_cap)
  11331. {
  11332. save_spell_state_timer.Trigger();
  11333. }
  11334. }
  11335. MSaveSpellStateMutex.unlock();
  11336. }
  11337. void Client::UpdateSentSpellList() {
  11338. MSpellDetails.readlock(__FUNCTION__, __LINE__);
  11339. std::map<int32, int32>::iterator itr;
  11340. for (itr = sent_spell_details.begin(); itr != sent_spell_details.end(); itr++) {
  11341. Spell* spell = master_spell_list.GetSpell(itr->first, itr->second);
  11342. EQ2Packet* app = spell->SerializeSpell(this, false, false);
  11343. QueuePacket(app);
  11344. }
  11345. MSpellDetails.releasereadlock(__FUNCTION__, __LINE__);
  11346. }
  11347. void Client::SetTempPlacementSpawn(Spawn* tmp) {
  11348. tempPlacementSpawn = tmp;
  11349. hasSentTempPlacementSpawn = false;
  11350. if (tempPlacementSpawn)
  11351. temp_placement_timer.Start();
  11352. else
  11353. temp_placement_timer.Disable();
  11354. }
  11355. void Client::SetPlayer(Player* new_player) {
  11356. if (player && player != new_player)
  11357. zone_list.RemoveClientFromMap(player->GetName(), this);
  11358. player = new_player;
  11359. player->SetClient(this);
  11360. }
  11361. bool Client::UseItem(Item* item, Spawn* target) {
  11362. if (item && item->GetItemScript()) {
  11363. int16 item_index = item->details.index;
  11364. if (!item->CheckFlag2(INDESTRUCTABLE) && item->generic_info.condition == 0) {
  11365. SimpleMessage(CHANNEL_COLOR_RED, "This item is broken and must be repaired at a mender before it can be used");
  11366. }
  11367. else if (item->CheckFlag(EVIL_ONLY) && GetPlayer()->GetAlignment() != ALIGNMENT_EVIL) {
  11368. Message(0, "%s requires an evil race.", item->name.c_str());
  11369. }
  11370. else if (item->CheckFlag(GOOD_ONLY) && GetPlayer()->GetAlignment() != ALIGNMENT_GOOD) {
  11371. Message(0, "%s requires a good race.", item->name.c_str());
  11372. }
  11373. else if (item->generic_info.max_charges == 0 || item->generic_info.max_charges == 0xFFFF) {
  11374. lua_interface->RunItemScript(item->GetItemScript(), "used", item, player, target);
  11375. return true;
  11376. }
  11377. else {
  11378. if (item->details.count > 0) {
  11379. std::string itemName = string(item->name);
  11380. int32 item_id = item->details.item_id;
  11381. sint64 flags = 0;
  11382. if (lua_interface->RunItemScript(item->GetItemScript(), "used", item, player, target, &flags) && flags >= 0)
  11383. {
  11384. //reobtain item make sure it wasn't removed
  11385. item = player->item_list.GetItemFromIndex(item_index);
  11386. if (!item) {
  11387. LogWrite(PLAYER__WARNING, 0, "Command", "%s: Item %s (%i) was used, however after the item looks to be removed.", GetPlayer()->GetName(), itemName.c_str(), item_id);
  11388. return true;
  11389. }
  11390. else if (!item->generic_info.display_charges && item->generic_info.max_charges == 1) {
  11391. Message(CHANNEL_NARRATIVE, "%s is out of charges. It has been removed.", item->name.c_str());
  11392. RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
  11393. return true;
  11394. }
  11395. else
  11396. {
  11397. item->details.count--; // charges
  11398. item->save_needed = true;
  11399. QueuePacket(item->serialize(GetVersion(), false, GetPlayer()));
  11400. if (!item->details.count) {
  11401. Message(CHANNEL_NARRATIVE, "%s is out of charges. It has been removed.", item->name.c_str());
  11402. RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
  11403. }
  11404. return true;
  11405. }
  11406. }
  11407. else {
  11408. LogWrite(PLAYER__WARNING, 0, "Command", "%s: Item %s (%i) was used, after it returned %i, bypassing any removal/update of items.", GetPlayer()->GetName(), itemName.c_str(), item_id, flags);
  11409. return true;
  11410. }
  11411. }
  11412. else
  11413. {
  11414. //reobtain item make sure it wasn't removed
  11415. item = player->item_list.GetItemFromIndex(item_index);
  11416. SimpleMessage(CHANNEL_COLOR_YELLOW, "Item is out of charges.");
  11417. if (item) {
  11418. LogWrite(PLAYER__ERROR, 0, "Command", "%s: Item %s (%i) attempted to be used, however details.count is 0.", GetPlayer()->GetName(), item->name.c_str(), item->details.item_id);
  11419. }
  11420. }
  11421. }
  11422. }
  11423. return false;
  11424. }
  11425. void Client::SendPlayFlavor(Spawn* spawn, int8 language, VoiceOverStruct* non_garble,
  11426. VoiceOverStruct* garble, bool success, bool garble_success) {
  11427. VoiceOverStruct* resStruct = nullptr;
  11428. if (language == 0 || GetPlayer()->HasLanguage(language)) {
  11429. if (success) {
  11430. resStruct = non_garble;
  11431. }
  11432. }
  11433. else if (garble_success) {
  11434. resStruct = garble;
  11435. }
  11436. if (resStruct) {
  11437. GetPlayer()->GetZone()->PlayFlavor(this, spawn, resStruct->mp3_string.c_str(), resStruct->text_string.c_str(), resStruct->emote_string.c_str(), resStruct->key1, resStruct->key2, language);
  11438. }
  11439. }
  11440. void Client::SaveQuestRewardData(bool force_refresh) {
  11441. Query query;
  11442. if (force_refresh) {
  11443. query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_quest_rewards where char_id = %u",
  11444. GetCharacterID());
  11445. query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_quest_temporary_rewards where char_id = %u",
  11446. GetCharacterID());
  11447. }
  11448. vector<QuestRewardData*>::iterator itr;
  11449. vector<QuestRewardData*> tmp_quest_rewards;
  11450. MQuestPendingUpdates.writelock(__FUNCTION__, __LINE__);
  11451. int index = 0;
  11452. for (itr = quest_pending_reward.begin(); itr != quest_pending_reward.end(); itr++) {
  11453. int32 questID = (*itr)->quest_id;
  11454. if (!(*itr)->db_saved || force_refresh) {
  11455. query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_rewards (char_id, indexed, quest_id, is_temporary, is_collection, has_displayed, tmp_coin, tmp_status, description) values(%u, %u, %u, %u, %u, %u, %llu, %u, '%s')",
  11456. GetCharacterID(), index, questID, (*itr)->is_temporary, (*itr)->is_collection, (*itr)->has_displayed, (*itr)->tmp_coin, (*itr)->tmp_status, database.getSafeEscapeString((*itr)->description.c_str()).c_str());
  11457. (*itr)->db_saved = true;
  11458. (*itr)->db_index = index;
  11459. if (questID && (*itr)->is_temporary) {
  11460. std::vector<Item*> items;
  11461. GetPlayer()->GetQuestTemporaryRewards(questID, &items);
  11462. if (!force_refresh && items.size() > 0) {
  11463. query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "delete from character_quest_temporary_rewards where char_id = %u and quest_id = %u",
  11464. GetCharacterID(), questID);
  11465. }
  11466. for (int i = 0; i < items.size(); i++) {
  11467. query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_temporary_rewards (char_id, quest_id, item_id, count) values(%u, %u, %u, %u)",
  11468. GetCharacterID(), questID, items[i]->details.item_id, items[i]->details.count);
  11469. }
  11470. }
  11471. }
  11472. index++;
  11473. }
  11474. MQuestPendingUpdates.releasewritelock(__FUNCTION__, __LINE__);
  11475. }
  11476. void Client::UpdateCharacterRewardData(QuestRewardData* data) {
  11477. if (!data)
  11478. return;
  11479. if (data->db_saved) {
  11480. Query query;
  11481. query.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "update character_quest_rewards set is_temporary = %u, is_collection = %u, has_displayed = %u, tmp_coin = %llu, tmp_status = %u, description = '%s' where char_id=%u and indexed=%u and quest_id=%u",
  11482. data->is_temporary, data->is_collection, data->has_displayed, data->tmp_coin, data->tmp_status, database.getSafeEscapeString(data->description.c_str()).c_str(), GetCharacterID(), data->db_index, data->quest_id);
  11483. }
  11484. }
  11485. void Client::AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i) {
  11486. int index = 0;
  11487. if (recipe == nullptr)
  11488. return;
  11489. PlayerRecipeList* prl = GetPlayer()->GetRecipeList();
  11490. if (prl->GetRecipe(recipe->GetID())) {
  11491. delete recipe;
  11492. return;
  11493. }
  11494. auto res = std::find(devices.begin(), devices.end(), recipe->GetDevice());
  11495. if (res != devices.end())
  11496. index = res - devices.begin();
  11497. else
  11498. devices.push_back(recipe->GetDevice());
  11499. prl->AddRecipe(recipe);
  11500. database.SavePlayerRecipe(GetPlayer(), recipe->GetID());
  11501. Message(CHANNEL_NARRATIVE, "Recipe: \"%s\" put in recipe book.", recipe->GetName());
  11502. if (packet && GetRecipeListSent()) {
  11503. packet->setArrayDataByName("id", recipe->GetID(), *i);
  11504. packet->setArrayDataByName("tier", recipe->GetTier(), *i);
  11505. packet->setArrayDataByName("level", recipe->GetLevel(), *i);
  11506. packet->setArrayDataByName("icon", recipe->GetIcon(), *i);
  11507. packet->setArrayDataByName("classes", recipe->GetClasses(), *i);
  11508. //packet->setArrayDataByName("skill", recipe->GetSkill(), *i);
  11509. packet->setArrayDataByName("technique", recipe->GetTechnique(), *i);
  11510. packet->setArrayDataByName("knowledge", recipe->GetKnowledge(), *i);
  11511. auto recipe_device = std::find(devices.begin(), devices.end(), recipe->GetDevice());
  11512. if (recipe_device != devices.end())
  11513. packet->setArrayDataByName("device_type", recipe_device - devices.begin(), *i);
  11514. else
  11515. {//TODO error should never get here
  11516. }
  11517. packet->setArrayDataByName("device_sub_type", recipe->GetDevice_Sub_Type(), *i);
  11518. packet->setArrayDataByName("recipe_name", recipe->GetName(), *i);
  11519. packet->setArrayDataByName("recipe_book", recipe->GetBook(), *i);
  11520. packet->setArrayDataByName("unknown3", recipe->GetUnknown3(), *i);
  11521. if (i) {
  11522. (*i)++;
  11523. }
  11524. }
  11525. }
  11526. bool Client::SetPlayerPOVGhost(Spawn* spawn) {
  11527. if (!spawn) {
  11528. pov_ghost_spawn_id = 0;
  11529. SendCharPOVGhost();
  11530. return true;
  11531. }
  11532. int32 ghost_id = player->GetIDWithPlayerSpawn(spawn);
  11533. if (ghost_id) {
  11534. pov_ghost_spawn_id = spawn->GetID();
  11535. SendCharPOVGhost();
  11536. return true;
  11537. }
  11538. return false;
  11539. }
  11540. void Client::HandleDialogSelectMsg(int32 conversation_id, int32 response_index) {
  11541. std::string conversation = "";
  11542. bool conv_established = false;
  11543. MConversation.lock_shared();
  11544. if (conversation_map.count(conversation_id) > 0 && conversation_map[conversation_id].count(response_index) > 0) {
  11545. conversation = std::string(conversation_map[conversation_id][response_index].c_str());
  11546. conv_established = true;
  11547. }
  11548. int32 spawn_id = conversation_spawns[conversation_id];
  11549. Item* item = conversation_items[conversation_id];
  11550. MConversation.unlock_shared();
  11551. if (GetCurrentZone()) {
  11552. Spawn* spawn = nullptr;
  11553. if (spawn_id) {
  11554. spawn = GetCurrentZone()->GetSpawnByID(spawn_id);
  11555. }
  11556. if (conv_established) {
  11557. if (spawn) {
  11558. if (conversation == "CloseItemConversation") {
  11559. LogWrite(LUA__ERROR, 0, "LUA", "CloseItemConversation is an invalid function call for this conversation with spawn id %u", spawn_id);
  11560. }
  11561. else {
  11562. GetCurrentZone()->CallSpawnScript(spawn, SPAWN_SCRIPT_CONVERSATION, player, conversation.c_str());
  11563. }
  11564. }
  11565. else if (item && lua_interface && item->GetItemScript())
  11566. lua_interface->RunItemScript(item->GetItemScript(), conversation.c_str(), item, player);
  11567. else
  11568. CloseDialog(conversation_id);
  11569. }
  11570. else
  11571. CloseDialog(conversation_id);
  11572. }
  11573. }
  11574. bool Client::SetPetName(const char* petName) {
  11575. int8 result = database.CheckNameFilter(petName, 4, 31);
  11576. if (result == BADNAMELENGTH_REPLY) {
  11577. SimpleMessage(CHANNEL_COLOR_YELLOW, "Name length is invalid, must be greater then 3 characters and less then 31.");
  11578. return false;
  11579. }
  11580. else if (result == NAMEINVALID_REPLY) {
  11581. SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is invalid, can only contain letters.");
  11582. return false;
  11583. }
  11584. else if (result == NAMETAKEN_REPLY) {
  11585. SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is already taken, please choose another.");
  11586. return false;
  11587. }
  11588. else if (result == NAMEFILTER_REPLY) {
  11589. SimpleMessage(CHANNEL_COLOR_YELLOW, "Name failed the filter check.");
  11590. return false;
  11591. }
  11592. else if (result == UNKNOWNERROR_REPLY) {
  11593. SimpleMessage(CHANNEL_COLOR_YELLOW, "Unknown error while checking the name.");
  11594. return false;
  11595. }
  11596. GetPlayer()->GetInfoStruct()->set_pet_name(petName);
  11597. return true;
  11598. }
  11599. bool Client::CheckConsumptionAllowed(int16 slot, bool send_message) {
  11600. switch (slot) {
  11601. case EQ2_FOOD_SLOT: {
  11602. if (GetPlayer()->GetSpellEffectBySpellType(SPELL_TYPE_FOOD)) {
  11603. if (send_message) {
  11604. Message(CHANNEL_NARRATIVE, "If you ate anymore you would explode!");
  11605. }
  11606. return false;
  11607. }
  11608. break;
  11609. }
  11610. case EQ2_DRINK_SLOT: {
  11611. if (GetPlayer()->GetSpellEffectBySpellType(SPELL_TYPE_DRINK))
  11612. {
  11613. if (send_message) {
  11614. Message(CHANNEL_NARRATIVE, "If you drank anymore you would explode!");
  11615. }
  11616. return false;
  11617. }
  11618. break;
  11619. }
  11620. default: {
  11621. if (GetVersion() <= 373) {
  11622. Item* item = GetPlayer()->item_list.GetItemFromIndex(slot);
  11623. if (item->IsFood()) {
  11624. if (item->IsFoodFood()) {
  11625. if (GetPlayer()->GetSpellEffectBySpellType(SPELL_TYPE_FOOD)) {
  11626. if (send_message) {
  11627. Message(CHANNEL_NARRATIVE, "If you ate anymore you would explode!");
  11628. }
  11629. return false;
  11630. }
  11631. return true;
  11632. }
  11633. else if (item->IsFoodDrink()) {
  11634. if (GetPlayer()->GetSpellEffectBySpellType(SPELL_TYPE_DRINK))
  11635. {
  11636. if (send_message) {
  11637. Message(CHANNEL_NARRATIVE, "If you drank anymore you would explode!");
  11638. }
  11639. return false;
  11640. }
  11641. return true;
  11642. }
  11643. }
  11644. }
  11645. return false;
  11646. break;
  11647. }
  11648. }
  11649. return true;
  11650. }
  11651. void Client::StartLinkdeadTimer() {
  11652. if (!linkdead_timer) {
  11653. int32 LD_Timer = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, LinkDeadTimer)->GetInt32();
  11654. LogWrite(CCLIENT__DEBUG, 0, "Client", "Starting linkdead timer for %s (timer %u seconds)", GetPlayer()->GetName(), (LD_Timer / 1000));
  11655. linkdead_timer = new Timer(LD_Timer);
  11656. linkdead_timer->Enable();
  11657. if (GetPlayer()->GetGroupMemberInfo()) {
  11658. LogWrite(CCLIENT__DEBUG, 0, "Client", "Telling player %s group they are disconnecting", GetPlayer()->GetName());
  11659. world.GetGroupManager()->GroupMessage(GetPlayer()->GetGroupMemberInfo()->group_id, "%s has gone Linkdead.", GetPlayer()->GetName());
  11660. }
  11661. }
  11662. }
  11663. bool Client::IsLinkdeadTimerEnabled() {
  11664. if (linkdead_timer) {
  11665. return linkdead_timer->Enabled();
  11666. }
  11667. return false;
  11668. }
  11669. void Client::SendNewAdventureSpells() {
  11670. SendNewSpells(player->GetAdventureClass());
  11671. int8 base_class = classes.GetBaseClass(player->GetAdventureClass());
  11672. int secondary_class = classes.GetSecondaryBaseClass(player->GetAdventureClass());
  11673. if (base_class != player->GetAdventureClass()) {
  11674. SendNewSpells(base_class);
  11675. }
  11676. if (secondary_class != player->GetAdventureClass() && secondary_class != base_class) {
  11677. SendNewSpells(secondary_class);
  11678. }
  11679. }
  11680. void Client::SendNewTradeskillSpells() {
  11681. SendNewTSSpells(player->GetTradeskillClass());
  11682. int8 secondary_class = classes.GetSecondaryTSBaseClass(player->GetTradeskillClass());
  11683. if (secondary_class != player->GetTradeskillClass()) {
  11684. SendNewTSSpells(secondary_class);
  11685. }
  11686. }
  11687. bool Client::AddRecipeBookToPlayer(int32 recipe_book_id, Item* item) {
  11688. Recipe* master_recipe = master_recipebook_list.GetRecipeBooks(recipe_book_id);
  11689. if (master_recipe) {
  11690. Recipe* recipe_book = new Recipe(master_recipe);
  11691. // if valid recipe book and the player doesn't have it
  11692. if (recipe_book && recipe_book->GetLevel() > GetPlayer()->GetTSLevel()) {
  11693. if (item) {
  11694. Message(CHANNEL_NARRATIVE, "Your tradeskill level is not high enough to scribe this book.");
  11695. }
  11696. safe_delete(recipe_book);
  11697. }
  11698. else if (recipe_book && item && !recipe_book->CanUseRecipeByClass(item, GetPlayer()->GetTradeskillClass())) {
  11699. if (item) {
  11700. Message(CHANNEL_NARRATIVE, "Your tradeskill class cannot use this recipe.");
  11701. }
  11702. safe_delete(recipe_book);
  11703. }
  11704. else if (recipe_book && (!item || !(GetPlayer()->GetRecipeBookList()->HasRecipeBook(recipe_book_id)))) {
  11705. LogWrite(PLAYER__DEBUG, 0, "Recipe", "Valid recipe book that the player doesn't have");
  11706. // Add recipe book to the players list
  11707. if (!GetPlayer()->GetRecipeBookList()->HasRecipeBook(recipe_book_id)) {
  11708. GetPlayer()->GetRecipeBookList()->AddRecipeBook(recipe_book);
  11709. }
  11710. std::vector<Recipe*> recipes;
  11711. // Get a list of all recipes this book contains
  11712. if (item && item->recipebook_info) {
  11713. //Backup I guess if the recipe book is empty for whatever reason?
  11714. for (auto& itr : item->recipebook_info->recipes) {
  11715. Recipe* r = master_recipe_list.GetRecipeByCRC(itr); //GetRecipeByName(itr.c_str());
  11716. if (r) {
  11717. recipes.push_back(r);
  11718. }
  11719. }
  11720. LogWrite(PLAYER__DEBUG, 0, "Recipe", "%i recipes found for %s book", recipes.size(), recipe_book->GetBookName());
  11721. }
  11722. else {
  11723. LogWrite(PLAYER__ERROR, 0, "Recipe", "no recipes found for %s book", recipe_book->GetBookName());
  11724. }
  11725. //Filter out duplicate recipes the player already has
  11726. for (auto itr = recipes.begin(); itr != recipes.end();) {
  11727. Recipe* recipe = *itr;
  11728. if (GetPlayer()->GetRecipeList()->GetRecipe(recipe->GetID())) {
  11729. itr = recipes.erase(itr);
  11730. }
  11731. else itr++;
  11732. }
  11733. int16 i = 0;
  11734. // Create the packet to send to update the players recipe list
  11735. PacketStruct* packet = 0;
  11736. if (!recipes.empty() && GetRecipeListSent()) {
  11737. packet = configReader.getStruct("WS_RecipeList", GetVersion());
  11738. if (packet) {
  11739. packet->setArrayLengthByName("num_recipes", recipes.size());
  11740. }
  11741. }
  11742. for (int32 r = 0; r < recipes.size(); r++) {
  11743. Recipe* recipe = recipes[r];
  11744. if (recipe) {
  11745. Recipe* player_recipe = new Recipe(recipe);
  11746. AddRecipeToPlayerPack(player_recipe, packet, &i);
  11747. }
  11748. }
  11749. LogWrite(TRADESKILL__DEBUG, 0, "Recipe", "Done adding recipes");
  11750. database.SavePlayerRecipeBook(GetPlayer(), recipe_book->GetBookID());
  11751. if (item) {
  11752. database.DeleteItem(GetCharacterID(), item, 0);
  11753. GetPlayer()->item_list.RemoveItem(item, true);
  11754. }
  11755. QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
  11756. SetRecipeListSent(false);
  11757. SendRecipeList();
  11758. safe_delete(packet);
  11759. return true;
  11760. }
  11761. else {
  11762. if (recipe_book && item) {
  11763. Message(CHANNEL_NARRATIVE, "You have already learned all you can from this item.");
  11764. }
  11765. safe_delete(recipe_book);
  11766. }
  11767. }
  11768. else {
  11769. LogWrite(PLAYER__ERROR, 0, "Player", "%u recipe book id does not exist. Cannot AddRecipeToPlayer.", recipe_book_id);
  11770. }
  11771. return false;
  11772. }
  11773. bool Client::RemoveRecipeFromPlayer(int32 recipe_id) {
  11774. PlayerRecipeList* prl = GetPlayer()->GetRecipeList();
  11775. PacketStruct* packet = configReader.getStruct("WS_RecipeList", version);
  11776. Recipe* recipe = prl->GetRecipe(recipe_id);
  11777. int8 level = player->GetTSLevel();
  11778. if (packet && recipe) {
  11779. packet->setDataByName("command_type", 1);
  11780. packet->setArrayLengthByName("num_recipes", 1);
  11781. int32 myid = recipe->GetID();
  11782. int8 rlevel = recipe->GetLevel();
  11783. int8 even = level - level * .05 + .5;
  11784. int8 easymin = level - level * .25 + .5;
  11785. int8 veasymin = level - level * .35 + .5;
  11786. if (rlevel > level)
  11787. packet->setArrayDataByName("tier", 4, 0);
  11788. else if ((rlevel <= level) & (rlevel >= even))
  11789. packet->setArrayDataByName("tier", 3, 0);
  11790. else if ((rlevel <= even) & (rlevel >= easymin))
  11791. packet->setArrayDataByName("tier", 2, 0);
  11792. else if ((rlevel <= easymin) & (rlevel >= veasymin))
  11793. packet->setArrayDataByName("tier", 1, 0);
  11794. else if ((rlevel <= veasymin) & (rlevel >= 0))
  11795. packet->setArrayDataByName("tier", 0, 0);
  11796. if (rlevel == 2)
  11797. int xxx = 1;
  11798. packet->setArrayDataByName("recipe_id", myid, 0);
  11799. packet->setArrayDataByName("level", recipe->GetLevel(), 0);
  11800. packet->setArrayDataByName("unknown1", recipe->GetLevel(), 0);
  11801. packet->setArrayDataByName("icon", recipe->GetIcon(), 0);
  11802. packet->setArrayDataByName("classes", recipe->GetClasses(), 0);
  11803. packet->setArrayDataByName("technique", recipe->GetTechnique(), 0);
  11804. packet->setArrayDataByName("knowledge", recipe->GetKnowledge(), 0);
  11805. auto recipe_device = std::find(devices.begin(), devices.end(), recipe->GetDevice());
  11806. if (recipe_device != devices.end())
  11807. packet->setArrayDataByName("device_type", recipe_device - devices.begin(), 0);
  11808. else
  11809. {//TODO error should never get here
  11810. }
  11811. packet->setArrayDataByName("device_sub_type", recipe->GetDevice_Sub_Type(), 0);
  11812. packet->setArrayDataByName("recipe_name", recipe->GetName(), 0);
  11813. packet->setArrayDataByName("recipe_book", recipe->GetBook(), 0);
  11814. packet->setArrayDataByName("unknown3", recipe->GetUnknown3(), 0);
  11815. QueuePacket(packet->serialize());
  11816. }
  11817. safe_delete(packet);
  11818. bool res = prl->RemoveRecipe(recipe_id);
  11819. if (res) {
  11820. Query query;
  11821. query.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "DELETE FROM character_recipes where char_id=%u and recipe_id=%u", GetCharacterID(), recipe_id);
  11822. }
  11823. return res;
  11824. }
  11825. void Client::SaveSpells() {
  11826. MSaveSpellStateMutex.lock();
  11827. player->SaveSpellEffects();
  11828. player->SetSaveSpellEffects(true);
  11829. MSaveSpellStateMutex.unlock();
  11830. }
  11831. void Client::SendReplaceWidget(int32 widget_id, bool delete_widget, float x, float y, float z, int32 grid_id) {
  11832. Widget* new_spawn = new Widget();
  11833. new_spawn->SetWidgetID(widget_id);
  11834. new_spawn->SetLocation(grid_id);
  11835. new_spawn->SetWidgetX(x);
  11836. new_spawn->SetWidgetY(y);
  11837. new_spawn->SetWidgetZ(z);
  11838. new_spawn->SetX(x);
  11839. new_spawn->SetY(y);
  11840. new_spawn->SetZ(z);
  11841. EQ2Packet* ret = new_spawn->serialize(GetPlayer(), GetVersion());
  11842. QueuePacket(ret);
  11843. // we have to delete spawn* references anyway, we don't keep this widget live in the spawn list
  11844. GetPlayer()->RemoveSpawn(new_spawn, delete_widget);
  11845. safe_delete(new_spawn);
  11846. }
  11847. void Client::ProcessZoneIgnoreWidgets() {
  11848. GetPlayer()->MIgnoredWidgets.lock_shared();
  11849. std::map<int32, bool>::iterator itr;
  11850. for (itr = GetPlayer()->ignored_widgets.begin(); itr != GetPlayer()->ignored_widgets.end(); itr++) {
  11851. SendReplaceWidget(itr->first, true);
  11852. }
  11853. GetPlayer()->MIgnoredWidgets.unlock_shared();
  11854. }
  11855. void Client::PopulateRecipeData(Recipe* recipe, PacketStruct* packet, int i) {
  11856. if (!recipe || !packet)
  11857. return;
  11858. int8 level = player->GetTSLevel();
  11859. int32 myid = recipe->GetID();
  11860. int8 rlevel = recipe->GetLevel();
  11861. int8 even = level - level * .05 + .5;
  11862. int8 easymin = level - level * .25 + .5;
  11863. int8 veasymin = level - level * .35 + .5;
  11864. if (rlevel > level)
  11865. packet->setArrayDataByName("tier", 4, i);
  11866. else if ((rlevel <= level) & (rlevel >= even))
  11867. packet->setArrayDataByName("tier", 3, i);
  11868. else if ((rlevel <= even) & (rlevel >= easymin))
  11869. packet->setArrayDataByName("tier", 2, i);
  11870. else if ((rlevel <= easymin) & (rlevel >= veasymin))
  11871. packet->setArrayDataByName("tier", 1, i);
  11872. else if ((rlevel <= veasymin) & (rlevel >= 0))
  11873. packet->setArrayDataByName("tier", 0, i);
  11874. if (rlevel == 2)
  11875. int xxx = 1;
  11876. packet->setArrayDataByName("recipe_id", myid, i);
  11877. packet->setArrayDataByName("level", recipe->GetLevel(), i);
  11878. packet->setArrayDataByName("icon", recipe->GetIcon(), i);
  11879. packet->setArrayDataByName("classes", recipe->GetClasses(), i);
  11880. packet->setArrayDataByName("technique", recipe->GetTechnique(), i);
  11881. packet->setArrayDataByName("knowledge", recipe->GetKnowledge(), i);
  11882. packet->setArrayDataByName("device", recipe->GetDevice(), i);
  11883. packet->setArrayDataByName("device_sub_type", recipe->GetDevice_Sub_Type(), i);
  11884. packet->setArrayDataByName("recipe_name", recipe->GetName(), i);
  11885. packet->setArrayDataByName("recipe_book", recipe->GetBook(), i);
  11886. packet->setArrayDataByName("unknown3", recipe->GetUnknown3(), i);
  11887. packet->setArrayDataByName("book_volume", 0x01, i);
  11888. packet->setArrayDataByName("device_id", 0x01, i);
  11889. }
  11890. int32 Client::GetRecipeCRC(Recipe* recipe) {
  11891. PacketStruct* packet = 0;
  11892. if (!(packet = configReader.getStruct("WS_RecipeDetailList", GetVersion()))) {
  11893. return 0;
  11894. }
  11895. packet->setArrayLengthByName("num_recipes", 1);
  11896. PopulateRecipeData(recipe, packet);
  11897. string* generic_string_data = packet->serializeString();
  11898. int32 size = generic_string_data->length();
  11899. if (size < 5)
  11900. return 0;
  11901. uchar* out_data = new uchar[size + 1];
  11902. uchar* out_ptr = out_data;
  11903. memcpy(out_ptr, (uchar*)generic_string_data->c_str() + 4, generic_string_data->length() - 4);
  11904. uint32 out_crc = GenerateCRCRecipe(0, (void*)out_ptr, size - 4);
  11905. safe_delete(packet);
  11906. safe_delete_array(out_data);
  11907. return out_crc;
  11908. }
  11909. void Client::SendRecipeDetails(vector<int32>* recipes) {
  11910. if (!recipes || recipes->size() == 0)
  11911. return;
  11912. PacketStruct* packet = 0;
  11913. if (!(packet = configReader.getStruct("WS_RecipeDetailList", GetVersion()))) {
  11914. return;
  11915. }
  11916. int32 recipe_size = player->GetRecipeList()->Size();
  11917. packet->setArrayLengthByName("num_recipes", recipe_size > 100 ? 100 : recipe_size);
  11918. int16 i = 0;
  11919. int32 count = 0;
  11920. vector<int32>::iterator recipe_itr;
  11921. for (recipe_itr = recipes->begin(); recipe_itr != recipes->end(); recipe_itr++) {
  11922. Recipe* recipe = player->GetRecipeList()->GetRecipe(*recipe_itr);
  11923. if (!recipe) {
  11924. continue;
  11925. }
  11926. else if (i > 99) {
  11927. QueuePacket(packet->serialize());
  11928. safe_delete(packet);
  11929. packet = configReader.getStruct("WS_RecipeDetailList", GetVersion());
  11930. recipe_size -= i;
  11931. packet->setArrayLengthByName("num_recipes", recipe_size > 100 ? 100 : recipe_size);
  11932. i = 0;
  11933. }
  11934. count++;
  11935. PopulateRecipeData(recipe, packet, i);
  11936. i++;
  11937. }
  11938. //packet->PrintPacket();
  11939. QueuePacket(packet->serialize());
  11940. safe_delete(packet);
  11941. }
  11942. bool Client::GetHouseZoneServer(ZoneChangeDetails* zone_details, int32 spawn_id, int64 house_id) {
  11943. PlayerHouse* ph = nullptr;
  11944. HouseZone* hz = nullptr;
  11945. if (spawn_id) {
  11946. Spawn* houseWidget = GetPlayer()->GetSpawnByIndex(spawn_id);
  11947. if (houseWidget && houseWidget->IsWidget() && ((Widget*)houseWidget)->GetHouseID()) {
  11948. hz = world.GetHouseZone(((Widget*)houseWidget)->GetHouseID());
  11949. if (hz) {
  11950. ph = world.GetPlayerHouseByHouseID(GetPlayer()->GetCharacterID(), hz->id);
  11951. }
  11952. else {
  11953. Message(CHANNEL_COLOR_YELLOW, "HouseWidget spawn index %u house zone could not be found.", spawn_id);
  11954. }
  11955. }
  11956. }
  11957. if (!ph && house_id) {
  11958. ph = world.GetPlayerHouseByUniqueID(house_id);
  11959. if (ph) {
  11960. hz = world.GetHouseZone(ph->house_id);
  11961. }
  11962. }
  11963. if (ph && hz) {
  11964. if (zone_list.GetZoneByInstance(zone_details, ph->instance_id, hz->zone_id)) {
  11965. return true;
  11966. }
  11967. return false;
  11968. }
  11969. return false;
  11970. }
  11971. void Client::SendHearCast(Spawn* caster, Spawn* target, int32 spell_visual, int16 cast_time) {
  11972. PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", GetVersion());
  11973. if (packet) {
  11974. int32 caster_id = GetPlayer()->GetIDWithPlayerSpawn(caster);
  11975. int32 target_id = GetPlayer()->GetIDWithPlayerSpawn(target);
  11976. packet->setDataByName("spawn_id", caster_id);
  11977. packet->setArrayLengthByName("num_targets", 1);
  11978. packet->setArrayDataByName("target", target_id);
  11979. packet->setDataByName("num_targets", 1);
  11980. int32 visual = GetSpellVisualOverride(spell_visual);
  11981. packet->setDataByName("spell_visual", visual); //result
  11982. packet->setDataByName("cast_time", cast_time * .01f); //delay
  11983. packet->setDataByName("spell_id", 1);
  11984. packet->setDataByName("spell_level", 1);
  11985. packet->setDataByName("spell_tier", 1);
  11986. EQ2Packet* outapp = packet->serialize();
  11987. //DumpPacket(outapp);
  11988. QueuePacket(outapp);
  11989. safe_delete(packet);
  11990. }
  11991. }
  11992. int32 Client::GetSpellVisualOverride(int32 spell_visual) {
  11993. int32 visual = spell_visual;
  11994. if (GetVersion() <= 561) { // spell's spell_visual field is based on newer clients, DoF has to convert
  11995. Emote* spellVisualEmote = visual_states.FindSpellVisualByID(visual, 60085);
  11996. if (spellVisualEmote != nullptr && spellVisualEmote->GetMessageString().size() > 0) {
  11997. spellVisualEmote = visual_states.FindSpellVisual(spellVisualEmote->GetMessageString(), GetVersion());
  11998. if (spellVisualEmote) {
  11999. visual = (int32)spellVisualEmote->GetVisualState();
  12000. }
  12001. }
  12002. }
  12003. return visual;
  12004. }
  12005. void Client::HandleGroupAcceptResponse(int8 result) {
  12006. if (result == 0)
  12007. SimpleMessage(CHANNEL_GROUP_CHAT, "You have joined the group.");
  12008. else if (result == 1)
  12009. SimpleMessage(CHANNEL_GROUP_CHAT, "You do not have a pending invite.");
  12010. else if (result == 2)
  12011. SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - could not find leader.");
  12012. else
  12013. SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - unknown error.");
  12014. }
  12015. void Client::SetGroupOptionsReference(GroupOptions* options) {
  12016. if (options) {
  12017. options->loot_method = GetPlayer()->GetInfoStruct()->get_group_loot_method();
  12018. options->loot_items_rarity = GetPlayer()->GetInfoStruct()->get_group_loot_items_rarity();
  12019. options->auto_split = GetPlayer()->GetInfoStruct()->get_group_auto_split();
  12020. options->default_yell = GetPlayer()->GetInfoStruct()->get_group_default_yell();
  12021. options->group_autolock = GetPlayer()->GetInfoStruct()->get_group_autolock();
  12022. options->group_lock_method = GetPlayer()->GetInfoStruct()->get_group_lock_method();
  12023. options->solo_autolock = GetPlayer()->GetInfoStruct()->get_group_solo_autolock();
  12024. options->auto_loot_method = GetPlayer()->GetInfoStruct()->get_group_auto_loot_method();
  12025. }
  12026. }
  12027. void Client::SendReceiveOffer(Client* target_client, int8 type, std::string name, int8 unknown2) {
  12028. PacketStruct* packet = configReader.getStruct("WS_ReceiveOffer", target_client->GetVersion());
  12029. if (packet) {
  12030. packet->setDataByName("type", type);
  12031. packet->setDataByName("name", name.c_str());
  12032. packet->setDataByName("unknown2", unknown2);
  12033. target_client->QueuePacket(packet->serialize());
  12034. }
  12035. safe_delete(packet);
  12036. }