| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675 |
- using System.Drawing;
- using Server.MirDatabase;
- using Server.MirEnvir;
- using Server.MirNetwork;
- using Server.MirObjects.Monsters;
- using System.Numerics;
- using S = ServerPackets;
- namespace Server.MirObjects
- {
- public class HumanObject : MapObject
- {
- public override ObjectType Race
- {
- get { return ObjectType.Player; }
- }
- public CharacterInfo Info;
- protected MirConnection connection;
- public virtual MirConnection Connection
- {
- get { return connection; }
- set { connection = value; }
- }
- public override string Name
- {
- get { return Info.Name; }
- set { /*Check if Name exists.*/ }
- }
- public override int CurrentMapIndex
- {
- get { return Info.CurrentMapIndex; }
- set { Info.CurrentMapIndex = value; }
- }
- public override Point CurrentLocation
- {
- get { return Info.CurrentLocation; }
- set { Info.CurrentLocation = value; }
- }
- public override MirDirection Direction
- {
- get { return Info.Direction; }
- set { Info.Direction = value; }
- }
- public override ushort Level
- {
- get { return Info.Level; }
- set { Info.Level = value; }
- }
- public override int Health
- {
- get { return HP; }
- }
- public override int MaxHealth
- {
- get { return Stats[Stat.HP]; }
- }
- public int HP
- {
- get { return Info.HP; }
- set { Info.HP = value; }
- }
- public int MP
- {
- get { return Info.MP; }
- set { Info.MP = value; }
- }
- public override AttackMode AMode
- {
- get { return Info.AMode; }
- set { Info.AMode = value; }
- }
- public override PetMode PMode
- {
- get { return Info.PMode; }
- set { Info.PMode = value; }
- }
- public long Experience
- {
- set { Info.Experience = value; }
- get { return Info.Experience; }
- }
- public long MaxExperience;
- public byte Hair
- {
- get { return Info.Hair; }
- set { Info.Hair = value; }
- }
- public MirClass Class
- {
- get { return Info.Class; }
- }
- public MirGender Gender
- {
- get { return Info.Gender; }
- }
- public override List<Buff> Buffs
- {
- get { return Info.Buffs; }
- set { Info.Buffs = value; }
- }
- public override List<Poison> PoisonList
- {
- get { return Info.Poisons; }
- set { Info.Poisons = value; }
- }
- public bool RidingMount;
- public MountInfo Mount
- {
- get { return Info.Mount; }
- }
- public Reporting Report;
- public virtual bool CanMove
- {
- get
- {
- return !Dead && Envir.Time >= ActionTime && !CurrentPoison.HasFlag(PoisonType.Paralysis) && !CurrentPoison.HasFlag(PoisonType.LRParalysis) && !CurrentPoison.HasFlag(PoisonType.Frozen);
- }
- }
- public virtual bool CanWalk
- {
- get
- {
- return !Dead && Envir.Time >= ActionTime && !InTrapRock && !CurrentPoison.HasFlag(PoisonType.Paralysis) && !CurrentPoison.HasFlag(PoisonType.LRParalysis) && !CurrentPoison.HasFlag(PoisonType.Frozen);
- }
- }
- public virtual bool CanRun
- {
- get
- {
- return !Dead && Envir.Time >= ActionTime && (_stepCounter > 0 || FastRun) && (!Sneaking || ActiveSwiftFeet) && CurrentBagWeight <= Stats[Stat.BagWeight] && !CurrentPoison.HasFlag(PoisonType.Paralysis) && !CurrentPoison.HasFlag(PoisonType.LRParalysis) && !CurrentPoison.HasFlag(PoisonType.Frozen);
- }
- }
- public virtual bool CanAttack
- {
- get
- {
- return !Dead && Envir.Time >= ActionTime && Envir.Time >= AttackTime && !CurrentPoison.HasFlag(PoisonType.Paralysis) && !CurrentPoison.HasFlag(PoisonType.LRParalysis) && !CurrentPoison.HasFlag(PoisonType.Frozen) && !CurrentPoison.HasFlag(PoisonType.Dazed) && Mount.CanAttack;
- }
- }
- public bool CanRegen
- {
- get
- {
- return Envir.Time >= RegenTime;
- }
- }
- protected virtual bool CanCast
- {
- get
- {
- return !Dead && Envir.Time >= ActionTime && Envir.Time >= SpellTime && !CurrentPoison.HasFlag(PoisonType.Stun) && !CurrentPoison.HasFlag(PoisonType.Dazed) &&
- !CurrentPoison.HasFlag(PoisonType.Paralysis) && !CurrentPoison.HasFlag(PoisonType.Frozen) && Mount.CanAttack;
- }
- }
- protected bool CheckCellTime = true;
- public short TransformType;
- public short Looks_Armour = 0, Looks_Weapon = -1, Looks_WeaponEffect = 0;
- public byte Looks_Wings = 0;
- public int CurrentHandWeight,
- CurrentWearWeight,
- CurrentBagWeight;
- public bool HasElemental;
- public int ElementsLevel;
- public bool Stacking;
- public bool IsGM, GMNeverDie, GMGameMaster;
- public bool HasUpdatedBaseStats = true;
- public virtual int PotionBeltMinimum => 0;
- public virtual int PotionBeltMaximum => 4;
- public virtual int AmuletBeltMinimum => 4;
- public virtual int AmuletBeltMaximum => 6;
- public virtual int BeltSize => 6;
- public LevelEffects LevelEffects = LevelEffects.None;
- public const long LoyaltyDelay = 1000, ItemExpireDelay = 60000, DuraDelay = 10000, RegenDelay = 10000, PotDelay = 200, HealDelay = 600, VampDelay = 500, MoveDelay = 600;
- public long StruckTime, RunTime, ActionTime, AttackTime, RegenTime, SpellTime, StackingTime, IncreaseLoyaltyTime, ItemExpireTime, TorchTime, DuraTime, PotTime, HealTime, VampTime, LogTime, DecreaseLoyaltyTime, SearchTime;
- protected int _stepCounter, _runCounter;
- private GuildObject myGuild = null;
- public virtual GuildObject MyGuild
- {
- get { return myGuild; }
- set { myGuild = value; }
- }
- public GuildRank MyGuildRank = null;
- public IntelligentCreatureType SummonedCreatureType = IntelligentCreatureType.None;
- public bool CreatureSummoned;
- public SpecialItemMode SpecialMode;
- public List<ItemSets> ItemSets = new List<ItemSets>();
- public List<EquipmentSlot> MirSet = new List<EquipmentSlot>();
- public bool FatalSword, Slaying, TwinDrakeBlade, FlamingSword, MPEater, Hemorrhage, CounterAttack;
- public int MPEaterCount, HemorrhageAttackCount;
- public long FlamingSwordTime, CounterAttackTime;
- public bool ActiveBlizzard, ActiveReincarnation, ActiveSwiftFeet, ReincarnationReady;
- public HumanObject ReincarnationTarget, ReincarnationHost;
- public long ReincarnationExpireTime;
- public long LastRevivalTime;
- public float HpDrain = 0;
- public bool UnlockCurse = false;
- public bool FastRun = false;
- public bool CanGainExp = true;
- private int _fireWallCastSeq = 0;
- public override bool Blocking
- {
- get
- {
- return !Dead && !Observer;
- }
- }
- public HumanObject() { }
- public HumanObject(CharacterInfo info, MirConnection connection)
- {
- Load(info, connection);
- }
- protected virtual void Load(CharacterInfo info, MirConnection connection) { }
- protected virtual void NewCharacter()
- {
- Level = 1;
- Hair = (byte)Envir.Random.Next(0, 9);
- for (int i = 0; i < Envir.StartItems.Count; i++)
- {
- ItemInfo info = Envir.StartItems[i];
- if (!CorrectStartItem(info)) continue;
- AddItem(Envir.CreateFreshItem(info));
- }
- }
- public long GetDelayTime(long original)
- {
- if (CurrentPoison.HasFlag(PoisonType.Slow))
- {
- return original * 2;
- }
- return original;
- }
- public override void Process()
- {
- if ((Race == ObjectType.Player && Connection == null) || Node == null || Info == null) return;
- if (CellTime + 700 < Envir.Time) _stepCounter = 0;
- if (Sneaking) CheckSneakRadius();
- if (FlamingSword && Envir.Time >= FlamingSwordTime)
- {
- FlamingSword = false;
- Enqueue(new S.SpellToggle { ObjectID = ObjectID, Spell = Spell.FlamingSword, CanUse = false });
- }
- if (CounterAttack && Envir.Time >= CounterAttackTime)
- {
- CounterAttack = false;
- }
- if (ReincarnationReady && Envir.Time >= ReincarnationExpireTime)
- {
- ReincarnationReady = false;
- ActiveReincarnation = false;
- ReincarnationTarget = null;
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.ReincarnationFailed), ChatType.System);
- }
- if ((ReincarnationReady || ActiveReincarnation) && (ReincarnationTarget == null || !ReincarnationTarget.Dead))
- {
- ReincarnationReady = false;
- ActiveReincarnation = false;
- ReincarnationTarget = null;
- }
- if (Envir.Time > RunTime && _runCounter > 0)
- {
- RunTime = Envir.Time + 1500;
- _runCounter--;
- }
- if (Stacking && Envir.Time > StackingTime)
- {
- Stacking = false;
- for (int i = 0; i < 8; i++)
- {
- if (Pushed(this, (MirDirection)i, 1) == 1) break;
- }
- }
- if (Mount.HasMount && Envir.Time > IncreaseLoyaltyTime)
- {
- IncreaseLoyaltyTime = Envir.Time + (LoyaltyDelay * 60);
- IncreaseMountLoyalty(1);
- }
- if (Envir.Time > ItemExpireTime)
- {
- ItemExpireTime = Envir.Time + ItemExpireDelay;
- ProcessItems();
- }
- for (int i = Pets.Count() - 1; i >= 0; i--)
- {
- MonsterObject pet = Pets[i];
- if (pet.Dead) Pets.Remove(pet);
- }
- ProcessBuffs();
- ProcessRegen();
- ProcessPoison();
- UserItem item;
- if (Envir.Time > TorchTime)
- {
- TorchTime = Envir.Time + 10000;
- item = Info.Equipment[(int)EquipmentSlot.Torch];
- if (item != null)
- {
- DamageItem(item, 5);
- if (item.CurrentDura == 0)
- {
- Info.Equipment[(int)EquipmentSlot.Torch] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- RefreshStats();
- }
- }
- }
- if (Envir.Time > DuraTime)
- {
- DuraTime = Envir.Time + DuraDelay;
- for (int i = 0; i < Info.Equipment.Length; i++)
- {
- item = Info.Equipment[i];
- if (item == null || !item.DuraChanged) continue; // || item.Info.Type == ItemType.Mount
- item.DuraChanged = false;
- Enqueue(new S.DuraChanged { UniqueID = item.UniqueID, CurrentDura = item.CurrentDura });
- }
- }
- base.Process();
- RefreshNameColour();
- }
- public override void OnSafeZoneChanged()
- {
- base.OnSafeZoneChanged();
- bool needsUpdate = false;
- for (int i = 0; i < Buffs.Count; i++)
- {
- if (Buffs[i].ObjectID == 0) continue;
- if (!Buffs[i].Properties.HasFlag(BuffProperty.PauseInSafeZone)) continue;
- needsUpdate = true;
- if (InSafeZone)
- {
- PauseBuff(Buffs[i]);
- }
- else
- {
- UnpauseBuff(Buffs[i]);
- }
- }
- if (needsUpdate)
- {
- RefreshStats();
- }
- }
- public override void SetOperateTime()
- {
- OperateTime = Envir.Time;
- }
- public override void Die() { }
- protected virtual void ProcessBuffs()
- {
- bool refresh = false;
- bool clearRing = false, skill = false, gm = false, mentor = false, lover = false;
- for (int i = Buffs.Count - 1; i >= 0; i--)
- {
- Buff buff = Buffs[i];
- switch (buff.Type)
- {
- case BuffType.Concentration:
- if (buff.Get<bool>("Interrupted") && buff.Get<long>("InterruptTime") <= Envir.Time)
- {
- buff.Set("Interrupted", false);
- buff.Set("InterruptTime", (long)0);
- UpdateConcentration(true, false);
- }
- break;
- case BuffType.ClearRing:
- clearRing = true;
- if (!SpecialMode.HasFlag(SpecialItemMode.ClearRing)) buff.FlagForRemoval = true;
- break;
- case BuffType.Skill:
- skill = true;
- if (!SpecialMode.HasFlag(SpecialItemMode.Skill)) buff.FlagForRemoval = true;
- break;
- case BuffType.GameMaster:
- gm = true;
- if (!IsGM) buff.FlagForRemoval = true;
- break;
- case BuffType.Mentor:
- case BuffType.Mentee:
- mentor = true;
- if (Info.Mentor == 0) buff.FlagForRemoval = true;
- break;
- case BuffType.Lover:
- lover = true;
- if (Info.Married == 0) buff.FlagForRemoval = true;
- break;
- }
- if (buff.NextTime > Envir.Time) continue;
- if (!buff.Paused && buff.StackType != BuffStackType.Infinite)
- {
- var change = Envir.Time - buff.LastTime;
- buff.ExpireTime -= change;
- }
- buff.LastTime = Envir.Time;
- buff.NextTime = Envir.Time + 1000;
- if ((buff.ExpireTime > 0 || buff.StackType == BuffStackType.Infinite) && !buff.FlagForRemoval) continue;
- Buffs.RemoveAt(i);
- Enqueue(new S.RemoveBuff { Type = buff.Type, ObjectID = ObjectID });
- if (buff.Info.Visible)
- {
- Broadcast(new S.RemoveBuff { Type = buff.Type, ObjectID = ObjectID });
- }
- switch (buff.Type)
- {
- case BuffType.Hiding:
- case BuffType.MoonLight:
- case BuffType.DarkBody:
- case BuffType.ClearRing:
- if (!HasAnyBuffs(buff.Type, BuffType.ClearRing, BuffType.Hiding, BuffType.MoonLight, BuffType.DarkBody))
- {
- Hidden = false;
- }
- if (buff.Type == BuffType.MoonLight || buff.Type == BuffType.DarkBody)
- {
- if (!HasAnyBuffs(buff.Type, BuffType.MoonLight, BuffType.DarkBody))
- {
- Sneaking = false;
- }
- break;
- }
- break;
- case BuffType.Concentration:
- UpdateConcentration(false, false);
- break;
- case BuffType.SwiftFeet:
- ActiveSwiftFeet = false;
- break;
- case BuffType.MagicShield:
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.MagicShieldDown }, CurrentLocation);
- break;
- case BuffType.ElementalBarrier:
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.ElementalBarrierDown }, CurrentLocation);
- break;
- }
- refresh = true;
- }
- if (IsGM && !gm)
- {
- UpdateGMBuff();
- }
- if (SpecialMode.HasFlag(SpecialItemMode.ClearRing) && !clearRing)
- {
- AddBuff(BuffType.ClearRing, this, 0, new Stats());
- }
- if (SpecialMode.HasFlag(SpecialItemMode.Skill) && !skill)
- {
- AddBuff(BuffType.Skill, this, 0, new Stats { [Stat.SkillGainMultiplier] = 3 }, false);
- }
- if (Info.Mentor != 0 && !mentor)
- {
- CharacterInfo partnerC = Envir.GetCharacterInfo(Info.Mentor);
- PlayerObject partnerP = partnerC != null ? Envir.GetPlayer(partnerC.Name) : null;
- if (partnerP != null)
- {
- if (Info.IsMentor)
- {
- AddBuff(BuffType.Mentor, partnerP, 0, new Stats { [Stat.MentorDamageRatePercent] = Settings.MentorDamageBoost });
- }
- else
- {
- AddBuff(BuffType.Mentee, partnerP, 0, new Stats { [Stat.MentorExpRatePercent] = Settings.MentorExpBoost });
- }
- }
- }
- if (Info.Married != 0 && !lover)
- {
- CharacterInfo loverC = Envir.GetCharacterInfo(Info.Married);
- PlayerObject loverP = loverC != null ? Envir.GetPlayer(loverC.Name) : null;
- if (loverP != null)
- {
- AddBuff(BuffType.Lover, loverP, 0, new Stats { [Stat.LoverExpRatePercent] = Settings.LoverEXPBonus });
- }
- }
- if (MyGuild != null && MyGuild.Name == Settings.NewbieGuild && Settings.NewbieGuildBuffEnabled == true)
- {
- AddBuff(BuffType.Newbie, this, 0, new Stats { [Stat.ExpRatePercent] = Settings.NewbieGuildExpBuff });
- }
- if (refresh)
- {
- RefreshStats();
- }
- }
- private void ProcessRegen()
- {
- if (Dead) return;
- int healthRegen = 0, manaRegen = 0;
- if (CanRegen)
- {
- RegenTime = Envir.Time + RegenDelay;
- if (HP < Stats[Stat.HP])
- {
- healthRegen += (int)(Stats[Stat.HP] * 0.03F) + 1;
- healthRegen += (int)(healthRegen * ((double)Stats[Stat.HealthRecovery] / Settings.HealthRegenWeight));
- }
- if (MP < Stats[Stat.MP])
- {
- manaRegen += (int)(Stats[Stat.MP] * 0.03F) + 1;
- manaRegen += (int)(manaRegen * ((double)Stats[Stat.SpellRecovery] / Settings.ManaRegenWeight));
- }
- }
- if (Envir.Time > PotTime)
- {
- //PotTime = Envir.Time + Math.Max(50,Math.Min(PotDelay, 600 - (Level * 10)));
- PotTime = Envir.Time + PotDelay;
- int PerTickRegen = 5 + (Level / 10);
- if (PotHealthAmount > PerTickRegen)
- {
- healthRegen += PerTickRegen;
- PotHealthAmount -= (ushort)PerTickRegen;
- }
- else
- {
- healthRegen += PotHealthAmount;
- PotHealthAmount = 0;
- }
- if (PotManaAmount > PerTickRegen)
- {
- manaRegen += PerTickRegen;
- PotManaAmount -= (ushort)PerTickRegen;
- }
- else
- {
- manaRegen += PotManaAmount;
- PotManaAmount = 0;
- }
- }
- if (Envir.Time > HealTime)
- {
- HealTime = Envir.Time + HealDelay;
- int incHeal = (Level / 10) + (HealAmount / 10);
- if (HealAmount > (5 + incHeal))
- {
- healthRegen += (5 + incHeal);
- HealAmount -= (ushort)Math.Min(HealAmount, 5 + incHeal);
- }
- else
- {
- healthRegen += HealAmount;
- HealAmount = 0;
- }
- }
- if (Envir.Time > VampTime)
- {
- VampTime = Envir.Time + VampDelay;
- if (VampAmount > 10)
- {
- healthRegen += 10;
- VampAmount -= 10;
- }
- else
- {
- healthRegen += VampAmount;
- VampAmount = 0;
- }
- }
- if (healthRegen > 0)
- {
- ChangeHP(healthRegen);
- BroadcastDamageIndicator(DamageType.Hit, healthRegen);
- }
- if (HP == Stats[Stat.HP])
- {
- PotHealthAmount = 0;
- HealAmount = 0;
- }
- if (manaRegen > 0) ChangeMP(manaRegen);
- if (MP == Stats[Stat.MP]) PotManaAmount = 0;
- }
- private void ProcessPoison()
- {
- PoisonType type = PoisonType.None;
- ArmourRate = 1F;
- DamageRate = 1F;
- for (int i = PoisonList.Count - 1; i >= 0; i--)
- {
- if (Dead) return;
- Poison poison = PoisonList[i];
- if (poison.Owner != null && poison.Owner.Node == null)
- {
- PoisonList.RemoveAt(i);
- continue;
- }
- if (Envir.Time > poison.TickTime)
- {
- poison.Time++;
- poison.TickTime = Envir.Time + poison.TickSpeed;
- if (poison.Time >= poison.Duration)
- {
- PoisonList.RemoveAt(i);
- }
- if (poison.PType == PoisonType.Green || poison.PType == PoisonType.Bleeding)
- {
- LastHitter = poison.Owner;
- LastHitTime = Envir.Time + 10000;
- if (poison.PType == PoisonType.Bleeding)
- {
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Bleeding, EffectType = 0 });
- }
- PoisonDamage(-poison.Value, poison.Owner);
- BroadcastDamageIndicator(DamageType.Hit, -poison.Value);
- if (Dead) break;
- RegenTime = Envir.Time + RegenDelay;
- }
- if (poison.PType == PoisonType.DelayedExplosion)
- {
- if (Envir.Time > ExplosionInflictedTime) ExplosionInflictedStage++;
- if (!ProcessDelayedExplosion(poison))
- {
- if (Dead) break;
- ExplosionInflictedStage = 0;
- ExplosionInflictedTime = 0;
- PoisonList.RemoveAt(i);
- continue;
- }
- }
- }
- switch (poison.PType)
- {
- case PoisonType.Red:
- ArmourRate -= 0.10F;
- break;
- case PoisonType.Stun:
- DamageRate += 0.20F;
- break;
- case PoisonType.Blindness:
- break;
- }
- type |= poison.PType;
- }
- if (type == CurrentPoison) return;
- Enqueue(new S.Poisoned { Poison = type });
- Broadcast(new S.ObjectPoisoned { ObjectID = ObjectID, Poison = type });
- CurrentPoison = type;
- }
- private bool ProcessDelayedExplosion(Poison poison)
- {
- if (Dead) return false;
- if (ExplosionInflictedStage == 0)
- {
- Enqueue(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 0 });
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 0 });
- return true;
- }
- if (ExplosionInflictedStage == 1)
- {
- if (Envir.Time > ExplosionInflictedTime)
- ExplosionInflictedTime = poison.TickTime + 3000;
- Enqueue(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 1 });
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 1 });
- return true;
- }
- if (ExplosionInflictedStage == 2)
- {
- Enqueue(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 2 });
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 2 });
- if (poison.Owner != null)
- {
- switch (poison.Owner.Race)
- {
- case ObjectType.Player:
- PlayerObject caster = (PlayerObject)poison.Owner;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time, poison.Owner, caster.GetMagic(Spell.DelayedExplosion), poison.Value, this.CurrentLocation);
- CurrentMap.ActionList.Add(action);
- break;
- case ObjectType.Monster://this is in place so it could be used by mobs if one day someone chooses to
- Attacked((MonsterObject)poison.Owner, poison.Value, DefenceType.MAC);
- break;
- }
- LastHitter = poison.Owner;
- }
- return false;
- }
- return false;
- }
- private void ProcessItems()
- {
- for (var i = 0; i < Info.Inventory.Length; i++)
- {
- var item = Info.Inventory[i];
- if (item?.ExpireInfo?.ExpiryDate <= Envir.Now)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.ItemHasExpiredFromInventory), item.Info.FriendlyName), ChatType.Hint);
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Info.Inventory[i] = null;
- continue;
- }
- if (item?.RentalInformation?.RentalLocked == true && item?.RentalInformation?.ExpiryDate <= Envir.Now)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.RentalLockRemovedFrom), item.Info.FriendlyName), ChatType.Hint);
- item.RentalInformation = null;
- }
- }
- for (var i = 0; i < Info.Equipment.Length; i++)
- {
- var item = Info.Equipment[i];
- if (item?.ExpireInfo?.ExpiryDate <= Envir.Now)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.EquipmentExpired), item.Info.FriendlyName), ChatType.Hint);
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Info.Equipment[i] = null;
- continue;
- }
- if (item?.RentalInformation?.RentalLocked == true && item?.RentalInformation?.ExpiryDate <= Envir.Now)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.RentalLockRemovedFrom), item.Info.FriendlyName), ChatType.Hint);
- item.RentalInformation = null;
- }
- }
- if (Info.AccountInfo == null) return;
- for (int i = 0; i < Info.AccountInfo.Storage.Length; i++)
- {
- var item = Info.AccountInfo.Storage[i];
- if (item?.ExpireInfo?.ExpiryDate <= Envir.Now)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.ItemExpiredFromStorage), item.Info.FriendlyName), ChatType.Hint);
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Info.AccountInfo.Storage[i] = null;
- continue;
- }
- }
- }
- public override void Process(DelayedAction action)
- {
- }
- protected void UpdateGMBuff()
- {
- if (!IsGM) return;
- GMOptions options = GMOptions.None;
- if (GMGameMaster) options |= GMOptions.GameMaster;
- if (GMNeverDie) options |= GMOptions.Superman;
- if (Observer) options |= GMOptions.Observer;
- AddBuff(BuffType.GameMaster, this, 0, null, false, values: (byte)options);
- }
- public virtual void LevelUp()
- {
- RefreshStats();
- SetHP(Stats[Stat.HP]);
- SetMP(Stats[Stat.MP]);
-
- Broadcast(new S.ObjectLeveled { ObjectID = ObjectID });
- }
- public virtual Color GetNameColour(HumanObject human)
- {
- return NameColour;
- }
- public virtual void RefreshNameColour() { }
- protected void SetHP(int amount)
- {
- if (HP == amount) return;
- HP = amount <= Stats[Stat.HP] ? amount : Stats[Stat.HP];
- HP = GMNeverDie ? Stats[Stat.HP] : HP;
- if (!Dead && HP == 0) Die();
- //HealthChanged = true;
- SendHealthChanged();
- }
- protected virtual void SendHealthChanged()
- {
- Enqueue(new S.HealthChanged { HP = HP, MP = MP });
- BroadcastHealthChange();
- }
- protected void SetMP(int amount)
- {
- if (MP == amount) return;
- //was info.MP
- MP = amount <= Stats[Stat.MP] ? amount : Stats[Stat.MP];
- MP = GMNeverDie ? Stats[Stat.MP] : MP;
- // HealthChanged = true;
- SendHealthChanged();
- }
- public void ChangeHP(int amount)
- {
- if (SpecialMode.HasFlag(SpecialItemMode.Protection) && MP > 0 && amount < 0)
- {
- ChangeMP(amount);
- return;
- }
- if (HP + amount > Stats[Stat.HP])
- amount = Stats[Stat.HP] - HP;
- if (amount == 0) return;
- HP += amount;
- HP = GMNeverDie ? Stats[Stat.HP] : HP;
- if (HP < 0) HP = 0;
- if (!Dead && HP == 0) Die();
- // HealthChanged = true;
- SendHealthChanged();
- }
- public void PoisonDamage(int amount, MapObject Attacker)
- {
- ChangeHP(amount);
- }
- public void ChangeMP(int amount)
- {
- if (MP + amount > Stats[Stat.MP])
- amount = Stats[Stat.MP] - MP;
- if (amount == 0) return;
- MP += amount;
- MP = GMNeverDie ? Stats[Stat.MP] : MP;
- if (MP < 0) MP = 0;
- // HealthChanged = true;
- SendHealthChanged();
- }
- public void GetMinePayout(MineSet Mine)
- {
- if ((Mine.Drops == null) || (Mine.Drops.Count == 0)) return;
- if (FreeSpace(Info.Inventory) == 0) return;
- byte Slot = (byte)Envir.Random.Next(Mine.TotalSlots);
- for (int i = 0; i < Mine.Drops.Count; i++)
- {
- MineDrop Drop = Mine.Drops[i];
- if ((Drop.MinSlot <= Slot) && (Drop.MaxSlot >= Slot) && (Drop.Item != null))
- {
- var info = Envir.GetItemInfo(Drop.Item.Index);
- UserItem item = Envir.CreateDropItem(info);
- if (item.Info.Type == ItemType.Ore)
- {
- item.CurrentDura = (ushort)Math.Min(ushort.MaxValue, (Drop.MinDura + Envir.Random.Next(Math.Max(0, Drop.MaxDura - Drop.MinDura))) * 1000);
- if ((Drop.BonusChance > 0) && (Envir.Random.Next(100) <= Drop.BonusChance))
- item.CurrentDura = (ushort)Math.Min(ushort.MaxValue, item.CurrentDura + (Envir.Random.Next(Drop.MaxBonusDura) * 1000));
- }
- if (CheckGroupQuestItem(item)) continue;
- if (CanGainItem(item))
- {
- GainItem(item);
- Report.ItemChanged(item, item.Count, 2);
- }
- return;
- }
- }
- }
- public virtual bool CheckGroupQuestItem(UserItem item, bool gainItem = true)
- {
- return false;
- }
- protected bool TryLuckWeapon()
- {
- var item = Info.Equipment[(int)EquipmentSlot.Weapon];
- if (item == null || item.AddedStats[Stat.Luck] >= 7)
- return false;
- if (item.Info.Bind.HasFlag(BindMode.DontUpgrade))
- return false;
- if (item.RentalInformation != null && item.RentalInformation.BindingFlags.HasFlag(BindMode.DontUpgrade))
- return false;
- string message = String.Empty;
- ChatType chatType;
- if (item.AddedStats[Stat.Luck] > (Settings.MaxLuck * -1) && Envir.Random.Next(20) == 0)
- {
- Stats[Stat.Luck]--;
- item.AddedStats[Stat.Luck]--;
- Enqueue(new S.RefreshItem { Item = item });
- message = GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WeaponCurse);
- chatType = ChatType.System;
-
- }
- else if (item.AddedStats[Stat.Luck] <= 0 || Envir.Random.Next(10 * item.GetTotal(Stat.Luck)) == 0)
- {
- Stats[Stat.Luck]++;
- item.AddedStats[Stat.Luck]++;
- Enqueue(new S.RefreshItem { Item = item });
- message = GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WeaponLuck);
- chatType = ChatType.Hint;
- }
- else
- {
- message = GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WeaponNoEffect);
- chatType = ChatType.Hint;
- }
- if (this is HeroObject hero)
- {
- if (message == GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WeaponCurse) ||
- message == GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WeaponLuck))
- {
- hero.Owner.Enqueue(new S.RefreshItem { Item = item });
- }
- hero.Owner.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.HeroOwnerReceiveChat), hero.Name, message), chatType);
- }
- else
- {
- ReceiveChat(message, chatType);
- }
- return true;
- }
- protected bool CanUseItem(UserItem item)
- {
- if (item == null) return false;
- switch (Gender)
- {
- case MirGender.Male:
- if (!item.Info.RequiredGender.HasFlag(RequiredGender.Male))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NotFemale), ChatType.System);
- return false;
- }
- break;
- case MirGender.Female:
- if (!item.Info.RequiredGender.HasFlag(RequiredGender.Female))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NotMale), ChatType.System);
- return false;
- }
- break;
- }
- switch (Class)
- {
- case MirClass.Warrior:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Warrior))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WarriorsCannotUseItem), ChatType.System);
- return false;
- }
- break;
- case MirClass.Wizard:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Wizard))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.WizardsCannotUseItem), ChatType.System);
- return false;
- }
- break;
- case MirClass.Taoist:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Taoist))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.TaoistsCannotUseItem), ChatType.System);
- return false;
- }
- break;
- case MirClass.Assassin:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Assassin))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.AssassinsCannotUseItem), ChatType.System);
- return false;
- }
- break;
- case MirClass.Archer:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Archer))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.ArchersCannotUseItem), ChatType.System);
- return false;
- }
- break;
- }
- switch (item.Info.RequiredType)
- {
- case RequiredType.Level:
- if (Level < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.LowLevel), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxAC:
- if (Stats[Stat.MaxAC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNotEnoughAC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxMAC:
- if (Stats[Stat.MaxMAC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNotEnoughMAC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxDC:
- if (Stats[Stat.MaxDC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.LowDC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxMC:
- if (Stats[Stat.MaxMC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.LowMC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxSC:
- if (Stats[Stat.MaxSC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.LowSC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MaxLevel:
- if (Level > item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouExceededMaxLevel), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MinAC:
- if (Stats[Stat.MinAC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNoBaseAC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MinMAC:
- if (Stats[Stat.MinMAC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNoBaseMAC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MinDC:
- if (Stats[Stat.MinDC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNoBaseDC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MinMC:
- if (Stats[Stat.MinMC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNoBaseMC), ChatType.System);
- return false;
- }
- break;
- case RequiredType.MinSC:
- if (Stats[Stat.MinSC] < item.Info.RequiredAmount)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouNoBaseSC), ChatType.System);
- return false;
- }
- break;
- }
- switch (item.Info.Type)
- {
- case ItemType.Scroll:
- switch (item.Info.Shape)
- {
- case 0:
- if (CurrentMap.Info.NoEscape)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanNotDungeon), ChatType.System);
- return false;
- }
- break;
- case 1:
- if (CurrentMap.Info.NoTownTeleport)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NoTownTeleport), ChatType.System);
- return false;
- }
- break;
- case 2:
- if (CurrentMap.Info.NoRandom)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanNotRandom), ChatType.System);
- return false;
- }
- break;
- case 6:
- if (!Dead)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CannotResurrection), ChatType.Hint);
- return false;
- }
- break;
- case 10:
- {
- int skillId = item.Info.Effect;
- if (MyGuild == null)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouMustBeInGuildToUseSkill), ChatType.Hint);
- return false;
- }
- if (MyGuildRank != MyGuild.Ranks[0])
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouMustBeGuildLeaderToUseSkill), ChatType.Hint);
- return false;
- }
- GuildBuffInfo buffInfo = Envir.FindGuildBuffInfo(skillId);
- if (buffInfo == null) return false;
- if (MyGuild.BuffList.Any(e => e.Info.Id == skillId))
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YourGuildAlreadyHasSkill), ChatType.Hint);
- return false;
- }
- }
- break;
- }
- break;
- case ItemType.Potion:
- if (CurrentMap.Info.NoDrug)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotUsePotionsHere), ChatType.System);
- return false;
- }
- break;
- case ItemType.Book:
- if (Info.Magics.Any(t => t.Spell == (Spell)item.Info.Shape))
- {
- return false;
- }
- break;
- case ItemType.Saddle:
- case ItemType.Ribbon:
- case ItemType.Bells:
- case ItemType.Mask:
- case ItemType.Reins:
- if (Info.Equipment[(int)EquipmentSlot.Mount] == null)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanOnlyUseWithMount), ChatType.System);
- return false;
- }
- break;
- case ItemType.Hook:
- case ItemType.Float:
- case ItemType.Bait:
- case ItemType.Finder:
- case ItemType.Reel:
- if (Info.Equipment[(int)EquipmentSlot.Weapon] == null || !Info.Equipment[(int)EquipmentSlot.Weapon].Info.IsFishingRod)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanOnlyUseWithFishingRod), ChatType.System);
- return false;
- }
- break;
- case ItemType.Socket:
- break;
- case ItemType.Pets:
- switch (item.Info.Shape)
- {
- case 20://mirror rename creature
- if (Info.IntelligentCreatures.Count == 0) return false;
- break;
- case 21://creature stone
- break;
- case 22://nuts maintain food levels
- if (!CreatureSummoned)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanUseWithSummonedCreature), ChatType.System);
- return false;
- }
- break;
- case 23://basic creature food
- if (!CreatureSummoned)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanUseWithSummonedCreature), ChatType.System);
- return false;
- }
- else
- {
- for (int i = 0; i < Pets.Count; i++)
- {
- if (Pets[i].Race != ObjectType.Creature) continue;
- var pet = (IntelligentCreatureObject)Pets[i];
- if (pet.PetType != SummonedCreatureType) continue;
- if (pet.Fullness > 9900)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PetIsNotHungry), pet.Name), ChatType.System);
- return false;
- }
- return true;
- }
- return false;
- }
- case 24://wonderpill vitalize creature
- if (!CreatureSummoned)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CanUseWithSummonedCreature), ChatType.System);
- return false;
- }
- else
- {
- for (int i = 0; i < Pets.Count; i++)
- {
- if (Pets[i].Race != ObjectType.Creature) continue;
- var pet = (IntelligentCreatureObject)Pets[i];
- if (pet.PetType != SummonedCreatureType) continue;
- if (pet.Fullness > 0)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PetDoesNotNeedVitalize), pet.Name), ChatType.System);
- return false;
- }
- return true;
- }
- return false;
- }
- case 25://Strongbox
- break;
- case 26://Wonderdrugs
- break;
- case 27://Fortunecookies
- break;
- }
- break;
- }
- if (RidingMount && item.Info.Type != ItemType.Scroll && item.Info.Type != ItemType.Potion)
- {
- return false;
- }
- return true;
- }
- public virtual void UseItem(ulong id) { }
- protected void ConsumeItem(UserItem item, byte cost)
- {
- item.Count -= cost;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = cost });
- if (item.Count != 0) return;
- for (int i = 0; i < Info.Equipment.Length; i++)
- {
- if (Info.Equipment[i] != null && Info.Equipment[i].Slots.Length > 0)
- {
- for (int j = 0; j < Info.Equipment[i].Slots.Length; j++)
- {
- if (Info.Equipment[i].Slots[j] != item) continue;
- Info.Equipment[i].Slots[j] = null;
- return;
- }
- }
- if (Info.Equipment[i] != item) continue;
- Info.Equipment[i] = null;
- return;
- }
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- if (Info.Inventory[i] != item) continue;
- Info.Inventory[i] = null;
- return;
- }
- //Item not found
- }
- protected bool DropItem(UserItem item, int range = 1, bool DeathDrop = false)
- {
- ItemObject ob = new ItemObject(this, item, DeathDrop);
- if (!ob.Drop(range)) return false;
- if (item.Info.Type == ItemType.Meat)
- item.CurrentDura = (ushort)Math.Max(0, item.CurrentDura - 2000);
- return true;
- }
- protected void DeathDrop(MapObject killer)
- {
- var pkbodydrop = true;
- if (CurrentMap.Info.NoDropPlayer && (Race == ObjectType.Player || Race == ObjectType.Hero))
- return;
- if ((killer == null) || ((pkbodydrop) || (killer.Race != ObjectType.Player) || killer.Race != ObjectType.Hero))
- {
- for (var i = 0; i < Info.Equipment.Length; i++)
- {
- var item = Info.Equipment[i];
- if (item == null)
- continue;
- if (item.Info.Bind.HasFlag(BindMode.DontDeathdrop))
- continue;
- // TODO: Check this.
- if (item.WeddingRing != -1 && Info.Equipment[(int)EquipmentSlot.RingL].UniqueID == item.UniqueID)
- continue;
- if (item.SealedInfo != null && item.SealedInfo.ExpiryDate > Envir.Now)
- continue;
- if (((killer == null) || ((killer != null) && ((killer.Race != ObjectType.Player) || (killer.Race != ObjectType.Hero)))))
- {
- if (item.Info.Bind.HasFlag(BindMode.BreakOnDeath))
- {
- Info.Equipment[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.YourItemShatteredUponDeath), item.FriendlyName), ChatType.System2);
- Report?.ItemChanged(item, item.Count, 1);
- }
- }
- if (ItemSets.Any(set => set.Set == ItemSet.Spirit && !set.SetComplete))
- {
- if (item.Info.Set == ItemSet.Spirit)
- {
- Info.Equipment[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Report?.ItemChanged(item, item.Count, 1);
- }
- }
- if (item.Count > 1)
- {
- var percent = Envir.RandomomRange(10, 8);
- var count = (ushort)Math.Ceiling(item.Count / 10F * percent);
- if (count > item.Count)
- throw new ArgumentOutOfRangeException();
- var temp2 = Envir.CreateFreshItem(item.Info);
- temp2.Count = count;
- if (!DropItem(temp2, Settings.DropRange, true))
- continue;
- if (count == item.Count)
- Info.Equipment[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = count });
- item.Count -= count;
- Report?.ItemChanged(item, count, 1);
- }
- else if (Envir.Random.Next(30) == 0)
- {
- if (Envir.ReturnRentalItem(item, item.RentalInformation?.OwnerName, Info))
- {
- Info.Equipment[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.YouDiedItemReturnedOwner), item.Info.FriendlyName), ChatType.Hint);
- Report?.ItemMailed(item, 1, 1);
- continue;
- }
- if (!DropItem(item, Settings.DropRange, true))
- {
- continue;
- }
- if (item.Info.GlobalDropNotify)
- {
- foreach (var player in Envir.Players)
- {
- player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerHasDroppedItem), Name, item.FriendlyName), ChatType.System2);
- }
- }
- Info.Equipment[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Report?.ItemChanged(item, item.Count, 1);
- }
- }
- }
- for (var i = 0; i < Info.Inventory.Length; i++)
- {
- var item = Info.Inventory[i];
- if (item == null)
- continue;
- if (item.Info.Bind.HasFlag(BindMode.DontDeathdrop))
- continue;
- if (item.WeddingRing != -1)
- continue;
- if (item.SealedInfo != null && item.SealedInfo.ExpiryDate > Envir.Now)
- continue;
- if (item.Count > 1)
- {
- var percent = Envir.RandomomRange(10, 8);
- if (percent == 0)
- continue;
- var count = (ushort)Math.Ceiling(item.Count / 10F * percent);
- if (count > item.Count)
- throw new ArgumentOutOfRangeException();
- var temp2 = Envir.CreateFreshItem(item.Info);
- temp2.Count = count;
- if (!DropItem(temp2, Settings.DropRange, true))
- continue;
- if (count == item.Count)
- Info.Inventory[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = count });
- item.Count -= count;
- Report?.ItemChanged(item, count, 1);
- }
- else if (Envir.Random.Next(10) == 0)
- {
- if (Envir.ReturnRentalItem(item, item.RentalInformation?.OwnerName, Info))
- {
- Info.Inventory[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerDiedItemReturnedOwner), item.Info), ChatType.Hint);
- Report?.ItemMailed(item, 1, 1);
- continue;
- }
- if (!DropItem(item, Settings.DropRange, true))
- continue;
- if (item.Info.GlobalDropNotify)
- foreach (var player in Envir.Players)
- {
- player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerHasDroppedItem), Name, item.FriendlyName), ChatType.System2);;
- }
- Info.Inventory[i] = null;
- Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
- Report?.ItemChanged(item, item.Count, 1);
- }
- }
- RefreshStats();
- }
- protected static int FreeSpace(IList<UserItem> array)
- {
- int count = 0;
- for (int i = 0; i < array.Count; i++)
- if (array[i] == null) count++;
- return count;
- }
- protected void AddItem(UserItem item)
- {
- if (item.Info.StackSize > 1) //Stackable
- {
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- UserItem temp = Info.Inventory[i];
- if (temp == null || item.Info != temp.Info || temp.Count >= temp.Info.StackSize) continue;
- if (item.Count + temp.Count <= temp.Info.StackSize)
- {
- temp.Count += item.Count;
- return;
- }
- item.Count -= (ushort)(temp.Info.StackSize - temp.Count);
- temp.Count = temp.Info.StackSize;
- }
- }
- if (item.Info.Type == ItemType.Potion || item.Info.Type == ItemType.Scroll || (item.Info.Type == ItemType.Script && item.Info.Effect == 1))
- {
- for (int i = PotionBeltMinimum; i < PotionBeltMaximum; i++)
- {
- if (Info.Inventory[i] != null) continue;
- Info.Inventory[i] = item;
- return;
- }
- }
- else if (item.Info.Type == ItemType.Amulet)
- {
- for (int i = AmuletBeltMinimum; i < AmuletBeltMaximum; i++)
- {
- if (Info.Inventory[i] != null) continue;
- Info.Inventory[i] = item;
- return;
- }
- }
- else
- {
- for (int i = BeltSize; i < Info.Inventory.Length; i++)
- {
- if (Info.Inventory[i] != null) continue;
- Info.Inventory[i] = item;
- return;
- }
- }
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- if (Info.Inventory[i] != null) continue;
- Info.Inventory[i] = item;
- return;
- }
- }
- protected bool CorrectStartItem(ItemInfo info)
- {
- switch (Class)
- {
- case MirClass.Warrior:
- if (!info.RequiredClass.HasFlag(RequiredClass.Warrior)) return false;
- break;
- case MirClass.Wizard:
- if (!info.RequiredClass.HasFlag(RequiredClass.Wizard)) return false;
- break;
- case MirClass.Taoist:
- if (!info.RequiredClass.HasFlag(RequiredClass.Taoist)) return false;
- break;
- case MirClass.Assassin:
- if (!info.RequiredClass.HasFlag(RequiredClass.Assassin)) return false;
- break;
- case MirClass.Archer:
- if (!info.RequiredClass.HasFlag(RequiredClass.Archer)) return false;
- break;
- default:
- return false;
- }
- switch (Gender)
- {
- case MirGender.Male:
- if (!info.RequiredGender.HasFlag(RequiredGender.Male)) return false;
- break;
- case MirGender.Female:
- if (!info.RequiredGender.HasFlag(RequiredGender.Female)) return false;
- break;
- default:
- return false;
- }
- return true;
- }
- public void CheckItemInfo(ItemInfo info, bool dontLoop = false)
- {
- Connection.CheckItemInfo(info, dontLoop);
- }
- public void CheckItem(UserItem item)
- {
- Connection.CheckItem(item);
- }
- public void SetLevelEffects()
- {
- LevelEffects = LevelEffects.None;
- if (Info.Flags[990]) LevelEffects |= LevelEffects.Mist;
- if (Info.Flags[991]) LevelEffects |= LevelEffects.RedDragon;
- if (Info.Flags[992]) LevelEffects |= LevelEffects.BlueDragon;
- if (Info.Flags[993]) LevelEffects |= LevelEffects.Rebirth1;
- if (Info.Flags[994]) LevelEffects |= LevelEffects.Rebirth2;
- if (Info.Flags[995]) LevelEffects |= LevelEffects.Rebirth3;
- if (Info.Flags[996]) LevelEffects |= LevelEffects.NewBlue;
- if (Info.Flags[997]) LevelEffects |= LevelEffects.YellowDragon;
- if (Info.Flags[998]) LevelEffects |= LevelEffects.Phoenix;
- }
- public virtual void Revive(int hp, bool effect)
- {
- if (!Dead) return;
- Dead = false;
- SetHP(hp);
- CurrentMap.RemoveObject(this);
- Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
- CurrentMap = CurrentMap;
- CurrentLocation = CurrentLocation;
- CurrentMap.AddObject(this);
- Enqueue(new S.MapChanged
- {
- MapIndex = CurrentMap.Info.Index,
- FileName = CurrentMap.Info.FileName,
- Title = CurrentMap.Info.Title,
- Weather = CurrentMap.Info.WeatherParticles,
- MiniMap = CurrentMap.Info.MiniMap,
- BigMap = CurrentMap.Info.BigMap,
- Lights = CurrentMap.Info.Light,
- Location = CurrentLocation,
- Direction = Direction,
- MapDarkLight = CurrentMap.Info.MapDarkLight,
- Music = CurrentMap.Info.Music
- });
- Enqueue(new S.Revived());
- Broadcast(new S.ObjectRevived { ObjectID = ObjectID, Effect = effect });
- }
- protected virtual void SendBaseStats()
- {
- Enqueue(new S.BaseStatsInfo { Stats = Settings.ClassBaseStats[(byte)Class] });
- }
- #region Refresh Stats
- public void RefreshStats()
- {
- if (HasUpdatedBaseStats == false)
- {
- SendBaseStats();
- HasUpdatedBaseStats = true;
- }
- Stats.Clear();
- RefreshLevelStats();
- RefreshBagWeight();
- RefreshEquipmentStats();
- RefreshItemSetStats();
- RefreshMirSetStats();
- RefreshSkills();
- RefreshBuffs();
- RefreshGuildBuffs();
- //Add any rate percent changes
- Stats[Stat.HP] += (Stats[Stat.HP] * Stats[Stat.HPRatePercent]) / 100;
- Stats[Stat.MP] += (Stats[Stat.MP] * Stats[Stat.MPRatePercent]) / 100;
- Stats[Stat.MaxAC] += (Stats[Stat.MaxAC] * Stats[Stat.MaxACRatePercent]) / 100;
- Stats[Stat.MaxMAC] += (Stats[Stat.MaxMAC] * Stats[Stat.MaxMACRatePercent]) / 100;
- Stats[Stat.MaxDC] += (Stats[Stat.MaxDC] * Stats[Stat.MaxDCRatePercent]) / 100;
- Stats[Stat.MaxMC] += (Stats[Stat.MaxMC] * Stats[Stat.MaxMCRatePercent]) / 100;
- Stats[Stat.MaxSC] += (Stats[Stat.MaxSC] * Stats[Stat.MaxSCRatePercent]) / 100;
- Stats[Stat.AttackSpeed] += (Stats[Stat.AttackSpeed] * Stats[Stat.AttackSpeedRatePercent]) / 100;
- RefreshStatCaps();
- if (HP > Stats[Stat.HP]) SetHP(Stats[Stat.HP]);
- if (MP > Stats[Stat.MP]) SetMP(Stats[Stat.MP]);
- AttackSpeed = 1400 - ((Stats[Stat.AttackSpeed] * 60) + Math.Min(370, (Level * 14)));
- if (AttackSpeed < 550) AttackSpeed = 550;
- }
- public virtual void RefreshGuildBuffs() { }
- public virtual void RefreshMaxExperience() { }
- protected virtual void RefreshLevelStats()
- {
- RefreshMaxExperience();
- foreach (var stat in Settings.ClassBaseStats[(byte)Class].Stats)
- {
- Stats[stat.Type] = stat.Calculate(Class, Level);
- }
- }
- public void RefreshBagWeight()
- {
- CurrentBagWeight = 0;
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- UserItem item = Info.Inventory[i];
- if (item != null)
- {
- CurrentBagWeight += item.Weight;
- }
- }
- }
- private void RefreshEquipmentStats()
- {
- short OldLooks_Weapon = Looks_Weapon;
- short OldLooks_WeaponEffect = Looks_WeaponEffect;
- short OldLooks_Armour = Looks_Armour;
- short Old_MountType = Mount.MountType;
- byte OldLooks_Wings = Looks_Wings;
- byte OldLight = Light;
- Looks_Armour = 0;
- Looks_Weapon = -1;
- Looks_WeaponEffect = 0;
- Looks_Wings = 0;
- Light = 0;
- CurrentWearWeight = 0;
- CurrentHandWeight = 0;
- Mount.MountType = -1;
- SpecialMode = SpecialItemMode.None;
- FastRun = false;
- Stats[Stat.SkillGainMultiplier] = 1;
- var skillsToAdd = new List<string>();
- var skillsToRemove = new List<string> { Settings.HealRing, Settings.FireRing, Settings.BlinkSkill };
- ItemSets.Clear();
- MirSet.Clear();
- for (int i = 0; i < Info.Equipment.Length; i++)
- {
- UserItem temp = Info.Equipment[i];
- if (temp == null) continue;
- ItemInfo realItem = Functions.GetRealItem(temp.Info, Info.Level, Info.Class, Envir.ItemInfoList);
- if (realItem.Type == ItemType.Weapon || realItem.Type == ItemType.Torch)
- CurrentHandWeight = (int)Math.Min(int.MaxValue, CurrentHandWeight + temp.Weight);
- else
- CurrentWearWeight = (int)Math.Min(int.MaxValue, CurrentWearWeight + temp.Weight);
- if (temp.CurrentDura == 0 && temp.Info.Durability > 0) continue;
- if (realItem.Type == ItemType.Armour)
- {
- Looks_Armour = realItem.Shape;
- Looks_Wings = realItem.Effect;
- }
- if (realItem.Type == ItemType.Weapon)
- {
- Looks_Weapon = realItem.Shape;
- Looks_WeaponEffect = realItem.Effect;
- }
- if (realItem.Type == ItemType.Mount)
- {
- Mount.MountType = realItem.Shape;
- //RealItem.Effect;
- }
- if (temp.Info.IsFishingRod) continue;
- Stats.Add(realItem.Stats);
- Stats.Add(temp.AddedStats);
- Stats[Stat.MinAC] += temp.Awake.GetAC();
- Stats[Stat.MaxAC] += temp.Awake.GetAC();
- Stats[Stat.MinMAC] += temp.Awake.GetMAC();
- Stats[Stat.MaxMAC] += temp.Awake.GetMAC();
- Stats[Stat.MinDC] += temp.Awake.GetDC();
- Stats[Stat.MaxDC] += temp.Awake.GetDC();
- Stats[Stat.MinMC] += temp.Awake.GetMC();
- Stats[Stat.MaxMC] += temp.Awake.GetMC();
- Stats[Stat.MinSC] += temp.Awake.GetSC();
- Stats[Stat.MaxSC] += temp.Awake.GetSC();
- Stats[Stat.HP] += temp.Awake.GetHPMP();
- Stats[Stat.MP] += temp.Awake.GetHPMP();
- if (realItem.Light > Light) Light = realItem.Light;
- if (realItem.Unique != SpecialItemMode.None)
- {
- SpecialMode |= realItem.Unique;
- if (realItem.Unique.HasFlag(SpecialItemMode.Flame)) skillsToAdd.Add(Settings.FireRing);
- if (realItem.Unique.HasFlag(SpecialItemMode.Healing)) skillsToAdd.Add(Settings.HealRing);
- if (realItem.Unique.HasFlag(SpecialItemMode.Blink)) skillsToAdd.Add(Settings.BlinkSkill);
- }
- if (realItem.CanFastRun)
- {
- FastRun = true;
- }
- RefreshSocketStats(temp, skillsToAdd);
- if (realItem.Set == ItemSet.None) continue;
- ItemSets itemSet = ItemSets.Where(set => set.Set == realItem.Set && !set.Type.Contains(realItem.Type) && !set.SetComplete).FirstOrDefault();
- if (itemSet != null)
- {
- itemSet.Type.Add(realItem.Type);
- itemSet.Count++;
- }
- else
- {
- ItemSets.Add(new ItemSets { Count = 1, Set = realItem.Set, Type = new List<ItemType> { realItem.Type } });
- }
- //Mir Set
- if (realItem.Set == ItemSet.Mir)
- {
- if (!MirSet.Contains((EquipmentSlot)i))
- {
- MirSet.Add((EquipmentSlot)i);
- }
- }
- }
- AddTempSkills(skillsToAdd);
- RemoveTempSkills(skillsToRemove.Except(skillsToAdd));
- if (SpecialMode.HasFlag(SpecialItemMode.Muscle))
- {
- Stats[Stat.BagWeight] = Stats[Stat.BagWeight] * 2;
- Stats[Stat.WearWeight] = Stats[Stat.WearWeight] * 2;
- Stats[Stat.HandWeight] = Stats[Stat.HandWeight] * 2;
- }
- if ((OldLooks_Armour != Looks_Armour) || (OldLooks_Weapon != Looks_Weapon) || (OldLooks_WeaponEffect != Looks_WeaponEffect) || (OldLooks_Wings != Looks_Wings) || (OldLight != Light))
- {
- UpdateLooks(OldLooks_Weapon);
- }
- if (Old_MountType != Mount.MountType)
- {
- RefreshMount(false);
- }
- }
- private void RefreshSocketStats(UserItem equipItem, List<string> skillsToAdd)
- {
- if (equipItem == null) return;
- if (equipItem.Info.Type == ItemType.Weapon && equipItem.Info.IsFishingRod)
- {
- return;
- }
- if (equipItem.Info.Type == ItemType.Mount && !RidingMount)
- {
- return;
- }
- for (int j = 0; j < equipItem.Slots.Length; j++)
- {
- UserItem temp = equipItem.Slots[j];
- if (temp == null) continue;
- ItemInfo RealItem = Functions.GetRealItem(temp.Info, Info.Level, Info.Class, Envir.ItemInfoList);
- if (RealItem.Type == ItemType.Weapon || RealItem.Type == ItemType.Torch)
- CurrentHandWeight = (int)Math.Min(int.MaxValue, CurrentHandWeight + temp.Weight);
- else
- CurrentWearWeight = (int)Math.Min(int.MaxValue, CurrentWearWeight + temp.Weight);
- if (temp.CurrentDura == 0 && temp.Info.Durability > 0) continue;
- Stats.Add(RealItem.Stats);
- Stats.Add(temp.AddedStats);
- if (RealItem.Light > Light) Light = RealItem.Light;
- if (RealItem.Unique != SpecialItemMode.None)
- {
- SpecialMode |= RealItem.Unique;
- if (RealItem.Unique.HasFlag(SpecialItemMode.Skill)) Stats[Stat.SkillGainMultiplier] = 3;
- if (RealItem.Unique.HasFlag(SpecialItemMode.Flame)) skillsToAdd.Add(Settings.FireRing);
- if (RealItem.Unique.HasFlag(SpecialItemMode.Healing)) skillsToAdd.Add(Settings.HealRing);
- if (RealItem.Unique.HasFlag(SpecialItemMode.Blink)) skillsToAdd.Add(Settings.BlinkSkill);
- }
- }
- //TODO - Add Socket bonuses
- }
- private void RefreshItemSetStats()
- {
- bool hasSmashSetBonus = false; // Flag for Smash set AttackSpeed bonus
- bool hasPuritySetBonus = false; // Flag for Purity set Holy bonus
- bool hasHwanDevilSetBonus = false; // Flag for HwanDevil set Weight bonuses
- foreach (var s in ItemSets)
- {
- if ((s.Set == ItemSet.Smash) && (s.Type.Contains(ItemType.Ring)) && (s.Type.Contains(ItemType.Bracelet)))
- {
- if (!hasSmashSetBonus)
- {
- Stats[Stat.AttackSpeed] += 2;
- hasSmashSetBonus = true;
- }
- }
- if ((s.Set == ItemSet.Purity) && (s.Type.Contains(ItemType.Ring)) && (s.Type.Contains(ItemType.Bracelet)))
- {
- if (!hasPuritySetBonus)
- {
- Stats[Stat.Holy] += 3;
- hasPuritySetBonus = true;
- }
- }
- if ((s.Set == ItemSet.HwanDevil) && (s.Type.Contains(ItemType.Ring)) && (s.Type.Contains(ItemType.Bracelet)))
- {
- if (!hasHwanDevilSetBonus)
- {
- Stats[Stat.WearWeight] += 5;
- Stats[Stat.BagWeight] += 20;
- hasHwanDevilSetBonus = true;
- }
- }
- if ((s.Set == ItemSet.DarkGhost) && (s.Type.Contains(ItemType.Necklace)) && (s.Type.Contains(ItemType.Bracelet)))
- {
- Stats[Stat.HP] += 25;
- }
- if (!s.SetComplete) continue;
- switch (s.Set)
- {
- case ItemSet.Mundane:
- Stats[Stat.HP] += 50;
- break;
- case ItemSet.NokChi:
- Stats[Stat.MP] += 50;
- break;
- case ItemSet.TaoProtect:
- Stats[Stat.HP] += 30;
- Stats[Stat.MP] += 30;
- break;
- case ItemSet.RedOrchid:
- Stats[Stat.Accuracy] += 2;
- break;
- case ItemSet.RedFlower:
- Stats[Stat.HP] += 50;
- Stats[Stat.MP] -= 50;
- break;
- case ItemSet.Smash:
- Stats[Stat.MinDC] += 1;
- Stats[Stat.MaxDC] += 3;
- break;
- case ItemSet.HwanDevil:
- Stats[Stat.MinMC] += 1;
- Stats[Stat.MaxMC] += 2;
- break;
- case ItemSet.Purity:
- Stats[Stat.MinSC] += 1;
- Stats[Stat.MaxSC] += 2;
- break;
- case ItemSet.FiveString:
- Stats[Stat.HP] += (int)(((double)Stats[Stat.HP] / 100) * 30);
- Stats[Stat.MinAC] += 2;
- Stats[Stat.MaxAC] += 2;
- break;
- case ItemSet.Spirit:
- Stats[Stat.MinDC] += 2;
- Stats[Stat.MaxDC] += 5;
- Stats[Stat.AttackSpeed] += 2;
- break;
- case ItemSet.Bone:
- Stats[Stat.MaxAC] += 2;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.MaxSC] += 1;
- break;
- case ItemSet.Bug:
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.MaxSC] += 1;
- Stats[Stat.MaxMAC] += 1;
- Stats[Stat.PoisonResist] += 1;
- break;
- case ItemSet.WhiteGold:
- Stats[Stat.MaxDC] += 2;
- Stats[Stat.MaxAC] += 2;
- break;
- case ItemSet.WhiteGoldH:
- Stats[Stat.MaxDC] += 3;
- Stats[Stat.HP] += 30;
- Stats[Stat.AttackSpeed] += 2;
- break;
- case ItemSet.RedJade:
- Stats[Stat.MaxMC] += 2;
- Stats[Stat.MaxMAC] += 2;
- break;
- case ItemSet.RedJadeH:
- Stats[Stat.MaxMC] += 2;
- Stats[Stat.MP] += 40;
- Stats[Stat.Agility] += 2;
- break;
- case ItemSet.Nephrite:
- Stats[Stat.MaxSC] += 2;
- Stats[Stat.MaxAC] += 1;
- Stats[Stat.MaxMAC] += 1;
- break;
- case ItemSet.NephriteH:
- Stats[Stat.MaxSC] += 2;
- Stats[Stat.HP] += 15;
- Stats[Stat.MP] += 20;
- Stats[Stat.Holy] += 1;
- Stats[Stat.Accuracy] += 1;
- break;
- case ItemSet.Whisker1:
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.BagWeight] += 25;
- break;
- case ItemSet.Whisker2:
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.BagWeight] += 17;
- break;
- case ItemSet.Whisker3:
- Stats[Stat.MaxSC] += 1;
- Stats[Stat.BagWeight] += 17;
- break;
- case ItemSet.Whisker4:
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.BagWeight] += 20;
- break;
- case ItemSet.Whisker5:
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.BagWeight] += 17;
- break;
- case ItemSet.Hyeolryong:
- Stats[Stat.MaxSC] += 2;
- Stats[Stat.HP] += 15;
- Stats[Stat.MP] += 20;
- Stats[Stat.Holy] += 1;
- Stats[Stat.Accuracy] += 1;
- break;
- case ItemSet.Monitor:
- Stats[Stat.MagicResist] += 1;
- Stats[Stat.PoisonResist] += 1;
- break;
- case ItemSet.Oppressive:
- Stats[Stat.MaxAC] += 1;
- Stats[Stat.Agility] += 1;
- break;
- case ItemSet.BlueFrost:
- Stats[Stat.MinDC] += 1;
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.MinMC] += 1;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.HandWeight] += 1;
- Stats[Stat.WearWeight] += 2;
- break;
- case ItemSet.BlueFrostH:
- Stats[Stat.MinDC] += 1;
- Stats[Stat.MaxDC] += 2;
- Stats[Stat.MaxMC] += 2;
- Stats[Stat.Accuracy] += 1;
- Stats[Stat.HP] += 50;
- break;
- case ItemSet.DarkGhost:
- Stats[Stat.MP] += 25;
- Stats[Stat.AttackSpeed] += 2;
- break;
- }
- }
- }
- private void RefreshMirSetStats()
- {
- if (MirSet.Count() == 10)
- {
- Stats[Stat.MaxAC] += 1;
- Stats[Stat.MaxMAC] += 1;
- Stats[Stat.BagWeight] += 70;
- Stats[Stat.Luck] += 2;
- Stats[Stat.AttackSpeed] += 2;
- Stats[Stat.HP] += 70;
- Stats[Stat.MP] += 80;
- Stats[Stat.MagicResist] += 6;
- Stats[Stat.PoisonResist] += 6;
- }
- if (MirSet.Contains(EquipmentSlot.RingL) && MirSet.Contains(EquipmentSlot.RingR))
- {
- Stats[Stat.MaxMAC] += 1;
- Stats[Stat.MaxAC] += 1;
- }
- if (MirSet.Contains(EquipmentSlot.BraceletL) && MirSet.Contains(EquipmentSlot.BraceletR))
- {
- Stats[Stat.MinAC] += 1;
- Stats[Stat.MinMAC] += 1;
- }
- if ((MirSet.Contains(EquipmentSlot.RingL) | MirSet.Contains(EquipmentSlot.RingR)) && (MirSet.Contains(EquipmentSlot.BraceletL) | MirSet.Contains(EquipmentSlot.BraceletR)) && MirSet.Contains(EquipmentSlot.Necklace))
- {
- Stats[Stat.MaxMAC] += 1;
- Stats[Stat.MaxAC] += 1;
- Stats[Stat.BagWeight] += 30;
- Stats[Stat.WearWeight] += 17;
- }
- if (MirSet.Contains(EquipmentSlot.RingL) && MirSet.Contains(EquipmentSlot.RingR) && MirSet.Contains(EquipmentSlot.BraceletL) && MirSet.Contains(EquipmentSlot.BraceletR) && MirSet.Contains(EquipmentSlot.Necklace))
- {
- Stats[Stat.MaxMAC] += 1;
- Stats[Stat.MaxAC] += 1;
- Stats[Stat.BagWeight] += 20;
- Stats[Stat.WearWeight] += 10;
- }
- if (MirSet.Contains(EquipmentSlot.Armour) && MirSet.Contains(EquipmentSlot.Helmet) && MirSet.Contains(EquipmentSlot.Weapon))
- {
- Stats[Stat.MaxDC] += 2;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.MaxSC] += 1;
- Stats[Stat.Agility] += 1;
- }
- if (MirSet.Contains(EquipmentSlot.Armour) && MirSet.Contains(EquipmentSlot.Boots) && MirSet.Contains(EquipmentSlot.Belt))
- {
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.MaxSC] += 1;
- Stats[Stat.HandWeight] += 17;
- }
- if (MirSet.Contains(EquipmentSlot.Armour) && MirSet.Contains(EquipmentSlot.Boots) && MirSet.Contains(EquipmentSlot.Belt) && MirSet.Contains(EquipmentSlot.Helmet) && MirSet.Contains(EquipmentSlot.Weapon))
- {
- Stats[Stat.MinDC] += 1;
- Stats[Stat.MaxDC] += 1;
- Stats[Stat.MinMC] += 1;
- Stats[Stat.MaxMC] += 1;
- Stats[Stat.MinSC] += 1;
- Stats[Stat.MaxSC] += 1;
- Stats[Stat.HandWeight] += 17;
- }
- }
- public void RefreshStatCaps()
- {
- foreach (var cap in Settings.ClassBaseStats[(byte)Class].Caps.Values)
- {
- Stats[cap.Key] = Math.Min(cap.Value, Stats[cap.Key]);
- }
- Stats[Stat.HP] = Math.Max(0, Stats[Stat.HP]);
- Stats[Stat.MP] = Math.Max(0, Stats[Stat.MP]);
- Stats[Stat.MinAC] = Math.Max(0, Stats[Stat.MinAC]);
- Stats[Stat.MaxAC] = Math.Max(0, Stats[Stat.MaxAC]);
- Stats[Stat.MinMAC] = Math.Max(0, Stats[Stat.MinMAC]);
- Stats[Stat.MaxMAC] = Math.Max(0, Stats[Stat.MaxMAC]);
- Stats[Stat.MinDC] = Math.Max(0, Stats[Stat.MinDC]);
- Stats[Stat.MaxDC] = Math.Max(0, Stats[Stat.MaxDC]);
- Stats[Stat.MinMC] = Math.Max(0, Stats[Stat.MinMC]);
- Stats[Stat.MaxMC] = Math.Max(0, Stats[Stat.MaxMC]);
- Stats[Stat.MinSC] = Math.Max(0, Stats[Stat.MinSC]);
- Stats[Stat.MaxSC] = Math.Max(0, Stats[Stat.MaxSC]);
- Stats[Stat.MinDC] = Math.Min(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- Stats[Stat.MinMC] = Math.Min(Stats[Stat.MinMC], Stats[Stat.MaxMC]);
- Stats[Stat.MinSC] = Math.Min(Stats[Stat.MinSC], Stats[Stat.MaxSC]);
- }
- #endregion
- private void AddTempSkills(IEnumerable<string> skillsToAdd)
- {
- foreach (var skill in skillsToAdd)
- {
- Spell spelltype;
- bool hasSkill = false;
- if (!Enum.TryParse(skill, out spelltype)) return;
- for (var i = Info.Magics.Count - 1; i >= 0; i--)
- if (Info.Magics[i].Spell == spelltype) hasSkill = true;
- if (hasSkill) continue;
- var magic = new UserMagic(spelltype) { IsTempSpell = true };
- Info.Magics.Add(magic);
- SendMagicInfo(magic);
- }
- }
- public virtual void SendMagicInfo(UserMagic magic)
- {
- Enqueue(magic.GetInfo(false));
- }
- private void RemoveTempSkills(IEnumerable<string> skillsToRemove)
- {
- foreach (var skill in skillsToRemove)
- {
- if (!Enum.TryParse(skill, out Spell spelltype)) return;
- for (var i = Info.Magics.Count - 1; i >= 0; i--)
- {
- if (!Info.Magics[i].IsTempSpell || Info.Magics[i].Spell != spelltype) continue;
- Info.Magics.RemoveAt(i);
- Enqueue(new S.RemoveMagic { PlaceId = i });
- }
- }
- }
- private void RefreshSkills()
- {
- int[] spiritSwordLvPlus = { 0, 3, 5, 8 };
- int[] slayingLvPlus = { 5, 6, 7, 8 };
- for (int i = 0; i < Info.Magics.Count; i++)
- {
- UserMagic magic = Info.Magics[i];
- switch (magic.Spell)
- {
- case Spell.Fencing:
- Stats[Stat.Accuracy] += magic.Level * 3;
- // Stats[Stat.MaxAC] += (magic.Level + 1) * 3;
- break;
- // case Spell.FatalSword:
- case Spell.Slaying:
- Stats[Stat.Accuracy] += magic.Level;
- Stats[Stat.MaxDC] += slayingLvPlus[magic.Level];
- break;
- case Spell.SpiritSword:
- Stats[Stat.Accuracy] += spiritSwordLvPlus[magic.Level];
- // Stats[Stat.Accuracy] += magic.Level;
- // Stats[Stat.MaxDC] += (int)(Stats[Stat.MaxSC] * (magic.Level + 1) * 0.1F);
- break;
- }
- }
- }
- private void RefreshBuffs()
- {
- short Old_TransformType = TransformType;
- TransformType = -1;
- for (int i = 0; i < Buffs.Count; i++)
- {
- Buff buff = Buffs[i];
- if (buff.Paused) continue;
- Stats.Add(buff.Stats);
- if (buff.Values != null && buff.Values.Length > 0)
- {
- switch (buff.Type)
- {
- case BuffType.Transform:
- TransformType = (short)buff.Values[0];
- FastRun = true;
- break;
- }
- }
- }
- if (Old_TransformType != TransformType)
- {
- Broadcast(new S.TransformUpdate { ObjectID = ObjectID, TransformType = TransformType });
- }
- }
- public void BroadcastColourChange()
- {
- if (CurrentMap == null) return;
- for (int i = CurrentMap.Players.Count - 1; i >= 0; i--)
- {
- PlayerObject player = CurrentMap.Players[i];
- if (player == this) continue;
- if (Functions.InRange(CurrentLocation, player.CurrentLocation, Globals.DataRange))
- player.Enqueue(new S.ObjectColourChanged { ObjectID = ObjectID, NameColour = player.GetNameColour(this) });
- }
- }
- public virtual void GainExp(uint amount) { }
- public int ReduceExp(uint amount, uint targetLevel)
- {
- int expPoint;
- if (Level < targetLevel + 10 || !Settings.ExpMobLevelDifference)
- {
- expPoint = (int)amount;
- }
- else
- {
- expPoint = (int)amount - (int)Math.Round(Math.Max(amount / 15, 1) * ((double)Level - (targetLevel + 10)));
- }
- if (expPoint <= 0) expPoint = 1;
- return expPoint;
- }
- public override void BroadcastInfo()
- {
- Packet p;
- if (CurrentMap == null) return;
- for (int i = CurrentMap.Players.Count - 1; i >= 0; i--)
- {
- PlayerObject player = CurrentMap.Players[i];
- if (player == this) continue;
- if (Functions.InRange(CurrentLocation, player.CurrentLocation, Globals.DataRange))
- {
- p = GetInfoEx(player);
- if (p != null)
- player.Enqueue(p);
- }
- }
- }
- public virtual bool CheckMovement(Point location)
- {
- return false;
- }
- public bool Walk(MirDirection dir)
- {
- if (!CanMove || !CanWalk)
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- Point location = Functions.PointMove(CurrentLocation, dir, 1);
- if (!CurrentMap.ValidPoint(location))
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- if (!CurrentMap.CheckDoorOpen(location))
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- Cell cell = CurrentMap.GetCell(location);
- if (cell.Objects != null)
- {
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob.Race == ObjectType.Merchant && Race == ObjectType.Player)
- {
- NPCObject NPC = (NPCObject)ob;
- if (!NPC.Visible || !NPC.VisibleLog[Info.Index]) continue;
- }
- else
- {
- if (!ob.Blocking || (CheckCellTime && ob.CellTime >= Envir.Time)) continue;
- }
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- }
- if (HasBuff(BuffType.Concentration, out Buff concentration))
- {
- concentration.Set("InterruptTime", Envir.Time + (Settings.Second * 3));
- if (!concentration.Get<bool>("Interrupted"))
- {
- concentration.Set("Interrupted", true);
- UpdateConcentration(true, true);
- }
- }
- if (Hidden)
- {
- RemoveBuff(BuffType.Hiding);
- }
- Direction = dir;
- if (CheckMovement(location)) return false;
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- RemoveObjects(dir, 1);
- CurrentLocation = location;
- var dstCell = CurrentMap.GetCell(CurrentLocation);
- dstCell.Add(this);
- AddObjects(dir, 1);
- _stepCounter++;
- SafeZoneInfo szi = CurrentMap.GetSafeZone(CurrentLocation);
- if (szi != null)
- {
- SetBindSafeZone(szi);
- InSafeZone = true;
- }
- else
- InSafeZone = false;
- if (RidingMount) DecreaseMountLoyalty(1);
- Moved();
- CellTime = Envir.Time + 500;
- ActionTime = Envir.Time + GetDelayTime(MoveDelay);
-
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- Broadcast(new S.ObjectWalk { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- GetPlayerLocation();
- cell = CurrentMap.GetCell(CurrentLocation);
- if (cell.Objects != null)
- {
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- if (cell.Objects[i].Race != ObjectType.Spell) continue;
- SpellObject ob = (SpellObject)cell.Objects[i];
- ob.ProcessSpell(this);
- }
- }
- return true;
- }
- public bool Run(MirDirection dir)
- {
- if (CurrentBagWeight > Stats[Stat.BagWeight])
- {
- Walk(dir);
- }
- var steps = RidingMount || ActiveSwiftFeet && !Sneaking ? 3 : 2;
- if (!CanMove || !CanWalk || !CanRun)
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- if (HasBuff(BuffType.Concentration, out Buff concentration))
- {
- concentration.Set("InterruptTime", Envir.Time + (Settings.Second * 3));
- if (!concentration.Get<bool>("Interrupted"))
- {
- concentration.Set("Interrupted", true);
- UpdateConcentration(true, true);
- }
- }
- if (Hidden && !Sneaking)
- {
- RemoveBuff(BuffType.Hiding);
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- Moved();
- Direction = dir;
- Point location = Functions.PointMove(CurrentLocation, dir, 1);
- for (int j = 1; j <= steps; j++)
- {
- location = Functions.PointMove(CurrentLocation, dir, j);
- if (!CurrentMap.ValidPoint(location))
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- if (!CurrentMap.CheckDoorOpen(location))
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- Cell cell = CurrentMap.GetCell(location);
- if (cell.Objects != null)
- {
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob.Race == ObjectType.Merchant && Race == ObjectType.Player)
- {
- NPCObject NPC = (NPCObject)ob;
- if (!NPC.Visible || !NPC.VisibleLog[Info.Index]) continue;
- }
- else
- if (!ob.Blocking || (CheckCellTime && ob.CellTime >= Envir.Time)) continue;
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return false;
- }
- }
- if (CheckMovement(location)) return false;
- }
- Enqueue(new S.UserLocation { Direction = dir, Location = location });
- Broadcast(new S.ObjectRun { ObjectID = ObjectID, Direction = dir, Location = location });
- if (RidingMount && !Sneaking)
- {
- DecreaseMountLoyalty(2);
- }
- Direction = dir;
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- RemoveObjects(dir, steps);
- Point OldLocation = CurrentLocation;
- CurrentLocation = location;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- AddObjects(dir, steps);
- SafeZoneInfo szi = CurrentMap.GetSafeZone(CurrentLocation);
- if (szi != null)
- {
- SetBindSafeZone(szi);
- InSafeZone = true;
- }
- else
- InSafeZone = false;
- CellTime = Envir.Time + 500;
- ActionTime = Envir.Time + GetDelayTime(MoveDelay);
- if (!RidingMount)
- _runCounter++;
- if (_runCounter > 10)
- {
- _runCounter -= 8;
- ChangeHP(-1);
- }
- GetPlayerLocation();
- for (int j = 1; j <= steps; j++)
- {
- location = Functions.PointMove(OldLocation, dir, j);
- Cell cell = CurrentMap.GetCell(location);
- if (cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- if (cell.Objects[i].Race != ObjectType.Spell) continue;
- SpellObject ob = (SpellObject)cell.Objects[i];
- ob.ProcessSpell(this);
- //break;
- }
- }
- return true;
- }
- protected virtual void Moved()
- {
- }
- public override int Pushed(MapObject pusher, MirDirection dir, int distance)
- {
- int result = 0;
- MirDirection reverse = Functions.ReverseDirection(dir);
- Cell cell;
- for (int i = 0; i < distance; i++)
- {
- Point location = Functions.PointMove(CurrentLocation, dir, 1);
- if (!CurrentMap.ValidPoint(location)) return result;
- cell = CurrentMap.GetCell(location);
- bool stop = false;
- if (cell.Objects != null)
- for (int c = 0; c < cell.Objects.Count; c++)
- {
- MapObject ob = cell.Objects[c];
- if (!ob.Blocking) continue;
- stop = true;
- }
- if (stop) break;
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- Direction = reverse;
- RemoveObjects(dir, 1);
- CurrentLocation = location;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- AddObjects(dir, 1);
- Moved();
- Enqueue(new S.Pushed { Direction = Direction, Location = CurrentLocation });
- Broadcast(new S.ObjectPushed { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- GetPlayerLocation();
- result++;
- }
- if (result > 0)
- {
- if (HasBuff(BuffType.Concentration, out Buff concentration))
- {
- concentration.Set("InterruptTime", Envir.Time + (Settings.Second * 3));
- if (!concentration.Get<bool>("Interrupted"))
- {
- concentration.Set("Interrupted", true);
- UpdateConcentration(true, true);
- }
- }
- cell = CurrentMap.GetCell(CurrentLocation);
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- if (cell.Objects[i].Race != ObjectType.Spell) continue;
- SpellObject ob = (SpellObject)cell.Objects[i];
- ob.ProcessSpell(this);
- //break;
- }
- SafeZoneInfo szi = CurrentMap.GetSafeZone(CurrentLocation);
- if (szi != null)
- {
- SetBindSafeZone(szi);
- InSafeZone = true;
- }
- else
- InSafeZone = false;
- }
- ActionTime = Envir.Time + 500;
- return result;
- }
- public void GetPlayerLocation()
- {
- if (GroupMembers == null) return;
- for (int i = 0; i < GroupMembers.Count; i++)
- {
- PlayerObject member = GroupMembers[i];
-
- if (member.CurrentMap.Info.BigMap <= 0) continue;
-
- member.Enqueue(new S.SendMemberLocation { MemberName = Name, MemberLocation = CurrentLocation });
- Enqueue(new S.SendMemberLocation { MemberName = member.Name, MemberLocation = member.CurrentLocation });
- }
- Enqueue(new S.SendMemberLocation { MemberName = Name, MemberLocation = CurrentLocation });
- }
- public void RangeAttack(MirDirection dir, Point location, uint targetID)
- {
- LogTime = Envir.Time + Globals.LogDelay;
- if (Info.Equipment[(int)EquipmentSlot.Weapon] == null) return;
- ItemInfo RealItem = Functions.GetRealItem(Info.Equipment[(int)EquipmentSlot.Weapon].Info, Info.Level, Info.Class, Envir.ItemInfoList);
- if ((RealItem.Shape / Globals.ClassWeaponCount) != 2) return;
- if (Functions.InRange(CurrentLocation, location, Globals.MaxAttackRange) == false) return;
- MapObject target = null;
- if (targetID == ObjectID)
- target = this;
- else if (targetID > 0)
- target = FindObject(targetID, 10);
- if (target != null && target.Dead) return;
- if (target != null && target.Race != ObjectType.Monster && target.Race != ObjectType.Player && target.Race != ObjectType.Hero) return;
- if (target != null && !target.Dead && target.IsAttackTarget(this) && !target.IsFriendlyTarget(this))
- {
- if (this is PlayerObject player &&
- player.PMode == PetMode.FocusMasterTarget)
- {
- foreach (MonsterObject pet in player.Pets)
- {
- if (pet.Race != ObjectType.Creature)
- {
- pet.Target = target;
- }
- }
- if (player.HeroSpawned &&
- !player.Hero.Dead)
- {
- player.Hero.Target = target;
- }
- }
- }
- Direction = dir;
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- UserMagic magic;
- Spell spell = Spell.None;
- bool focus = false;
- if (target != null && !CanFly(target.CurrentLocation) && (Info.MentalState != 1))
- {
- target = null;
- targetID = 0;
- }
- if (target != null)
- {
- magic = GetMagic(Spell.Focus);
- if (magic != null && Envir.Random.Next(5) <= magic.Level)
- {
- focus = true;
- LevelMagic(magic);
- spell = Spell.Focus;
- }
- int distance = Functions.MaxDistance(CurrentLocation, target.CurrentLocation);
- int damage = GetRangeAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC], distance);
- damage = ApplyArcherState(damage);
- int chanceToHit = (100 + Settings.RangeAccuracyBonus - ((100 / Globals.MaxAttackRange) * distance)) * (focus ? 2 : 1);
- if (chanceToHit < 0) chanceToHit = 0;
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500 + 50; //50 MS per Step
- if (Envir.Random.Next(100) < chanceToHit)
- {
- if (target.CurrentLocation != location)
- location = target.CurrentLocation;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, target, damage, DefenceType.ACAgility, true);
- ActionList.Add(action);
- }
- else
- {
- DelayedAction action = new DelayedAction(DelayedType.DamageIndicator, Envir.Time + delay, target, DamageType.Miss);
- ActionList.Add(action);
- }
- }
- else
- targetID = 0;
- Enqueue(new S.RangeAttack { TargetID = targetID, Target = location, Spell = spell });
- Broadcast(new S.ObjectRangeAttack { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, TargetID = targetID, Target = location, Spell = spell });
- AttackTime = Envir.Time + AttackSpeed;
- ActionTime = Envir.Time + 550;
- RegenTime = Envir.Time + RegenDelay;
- }
- public void Attack(MirDirection dir, Spell spell)
- {
- LogTime = Envir.Time + Globals.LogDelay;
- bool Mined = false;
- bool MoonLightAttack = false;
- bool DarkBodyAttack = false;
- if (!CanAttack)
- {
- switch (spell)
- {
- case Spell.Slaying:
- Slaying = false;
- break;
- }
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return;
- }
- if (Hidden)
- {
- for (int i = 0; i < Buffs.Count; i++)
- {
- switch (Buffs[i].Type)
- {
- case BuffType.MoonLight:
- MoonLightAttack = true;
- break;
- case BuffType.DarkBody:
- DarkBodyAttack = true;
- break;
- }
- }
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- byte level = 0;
- UserMagic magic;
- if (RidingMount)
- {
- spell = Spell.None;
- }
- switch (spell)
- {
- case Spell.Slaying:
- if (!Slaying)
- spell = Spell.None;
- else
- {
- magic = GetMagic(Spell.Slaying);
- level = magic.Level;
- }
- Slaying = false;
- break;
- case Spell.DoubleSlash:
- magic = GetMagic(spell);
- if (magic == null || magic.Info.BaseCost + (magic.Level * magic.Info.LevelCost) > MP)
- {
- spell = Spell.None;
- break;
- }
- level = magic.Level;
- ChangeMP(-(magic.Info.BaseCost + magic.Level * magic.Info.LevelCost));
- break;
- case Spell.Thrusting:
- case Spell.FlamingSword:
- magic = GetMagic(spell);
- if ((magic == null) || (!FlamingSword && (spell == Spell.FlamingSword)))
- {
- spell = Spell.None;
- break;
- }
- level = magic.Level;
- break;
- case Spell.HalfMoon:
- case Spell.CrossHalfMoon:
- magic = GetMagic(spell);
- if (magic == null || magic.Info.BaseCost + (magic.Level * magic.Info.LevelCost) > MP)
- {
- spell = Spell.None;
- break;
- }
- level = magic.Level;
- ChangeMP(-(magic.Info.BaseCost + magic.Level * magic.Info.LevelCost));
- break;
- case Spell.TwinDrakeBlade:
- magic = GetMagic(spell);
- if (!TwinDrakeBlade || magic == null || magic.Info.BaseCost + magic.Level * magic.Info.LevelCost > MP)
- {
- spell = Spell.None;
- break;
- }
- level = magic.Level;
- ChangeMP(-(magic.Info.BaseCost + magic.Level * magic.Info.LevelCost));
- break;
- default:
- spell = Spell.None;
- break;
- }
- if (!Slaying)
- {
- magic = GetMagic(Spell.Slaying);
- if (magic != null && Envir.Random.Next(12) <= magic.Level)
- {
- Slaying = true;
- Enqueue(new S.SpellToggle { ObjectID = ObjectID, Spell = Spell.Slaying, CanUse = Slaying });
- }
- }
- Direction = dir;
- if (RidingMount) DecreaseMountLoyalty(3);
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- Broadcast(new S.ObjectAttack { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Spell = spell, Level = level });
- AttackTime = Envir.Time + AttackSpeed;
- ActionTime = Envir.Time + 550;
- RegenTime = Envir.Time + RegenDelay;
- Point target = Functions.PointMove(CurrentLocation, dir, 1);
- //damabeBase = the original damage from your gear (+ bonus from moonlight and darkbody)
- int damageBase = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- //damageFinal = the damage you're gonna do with skills added
- int damageFinal;
- if (MoonLightAttack || DarkBodyAttack)
- {
- magic = MoonLightAttack ? GetMagic(Spell.MoonLight) : GetMagic(Spell.DarkBody);
- if (magic != null)
- {
- damageBase += magic.GetPower();
- }
- }
- if (!CurrentMap.ValidPoint(target))
- {
- switch (spell)
- {
- case Spell.Thrusting:
- goto Thrusting;
- case Spell.HalfMoon:
- goto HalfMoon;
- case Spell.CrossHalfMoon:
- goto CrossHalfMoon;
- case Spell.None:
- Mined = true;
- goto Mining;
- }
- return;
- }
- Cell cell = CurrentMap.GetCell(target);
- if (cell.Objects == null)
- {
- switch (spell)
- {
- case Spell.Thrusting:
- goto Thrusting;
- case Spell.HalfMoon:
- goto HalfMoon;
- case Spell.CrossHalfMoon:
- goto CrossHalfMoon;
- }
- return;
- }
- damageFinal = damageBase;//incase we're not using skills
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this)) continue;
- if (ob != null && !ob.Dead && ob.IsAttackTarget(this) && !ob.IsFriendlyTarget(this))
- {
- if (this is PlayerObject player &&
- player.PMode == PetMode.FocusMasterTarget)
- {
- foreach (MonsterObject pet in player.Pets)
- {
- if (pet.Race != ObjectType.Creature)
- {
- pet.Target = ob;
- }
- }
- if (player.HeroSpawned &&
- !player.Hero.Dead)
- {
- player.Hero.Target = ob;
- }
- }
- }
- //Only undead targets
- if (ob.Undead)
- {
- damageBase = Math.Min(int.MaxValue, damageBase + Stats[Stat.Holy]);
- damageFinal = damageBase;//incase we're not using skills
- }
- #region FatalSword
- magic = GetMagic(Spell.FatalSword);
- DefenceType defence = DefenceType.ACAgility;
- if (magic != null)
- {
- if (!FatalSword && Envir.Random.Next(10) == 0)
- FatalSword = true;
- if (FatalSword)
- damageBase = magic.GetDamage(damageBase);
- }
- #endregion
- #region MPEater
- magic = GetMagic(Spell.MPEater);
- if (magic != null)
- {
- int baseCount = 1 + Stats[Stat.Accuracy] / 2;
- int maxCount = baseCount + magic.Level * 5;
- MPEaterCount += Envir.Random.Next(baseCount, maxCount);
- if (MPEater)
- {
- LevelMagic(magic);
- damageFinal = magic.GetDamage(damageBase);
- defence = DefenceType.ACAgility;
- S.ObjectEffect p = new S.ObjectEffect { ObjectID = ob.ObjectID, Effect = SpellEffect.MPEater, EffectType = ObjectID };
- CurrentMap.Broadcast(p, ob.CurrentLocation);
- int addMp = 5 * (magic.Level + Stats[Stat.Accuracy] / 4);
- if (ob.Race == ObjectType.Player)
- {
- ((PlayerObject)ob).ChangeMP(-addMp);
- }
- ChangeMP(addMp);
- MPEaterCount = 0;
- MPEater = false;
- }
- else if (!MPEater && 100 <= MPEaterCount) MPEater = true;
- }
- #endregion
- #region Hemorrhage
- magic = GetMagic(Spell.Hemorrhage);
- if (magic != null)
- {
- HemorrhageAttackCount += Envir.Random.Next(1, 1 + magic.Level * 2);
- if (Hemorrhage)
- {
- damageFinal = magic.GetDamage(damageBase);
- LevelMagic(magic);
- S.ObjectEffect ef = new S.ObjectEffect { ObjectID = ob.ObjectID, Effect = SpellEffect.Hemorrhage };
- CurrentMap.Broadcast(ef, ob.CurrentLocation);
- if (ob == null || ob.Node == null) continue;
- long calcDuration = magic.Level * 2 + Stats[Stat.Luck] / 6;
- ob.ApplyPoison(new Poison
- {
- Duration = (calcDuration <= 0) ? 1 : calcDuration,
- Owner = this,
- PType = PoisonType.Bleeding,
- TickSpeed = 1000,
- Value = Stats[Stat.MaxDC] + 1
- }, this);
- ob.OperateTime = 0;
- HemorrhageAttackCount = 0;
- Hemorrhage = false;
- }
- else if (!Hemorrhage && 55 <= HemorrhageAttackCount) Hemorrhage = true;
- }
- #endregion
- DelayedAction action;
- switch (spell)
- {
- case Spell.Slaying:
- magic = GetMagic(Spell.Slaying);
- damageFinal = magic.GetDamage(damageBase);
- LevelMagic(magic);
- break;
- case Spell.DoubleSlash:
- magic = GetMagic(Spell.DoubleSlash);
- damageFinal = magic.GetDamage(damageBase);
- if (defence == DefenceType.ACAgility) defence = DefenceType.MACAgility;
- action = new DelayedAction(DelayedType.Damage, Envir.Time + 400, ob, damageFinal, DefenceType.Agility, false);
- ActionList.Add(action);
- LevelMagic(magic);
- break;
- case Spell.Thrusting:
- magic = GetMagic(Spell.Thrusting);
- LevelMagic(magic);
- break;
- case Spell.HalfMoon:
- magic = GetMagic(Spell.HalfMoon);
- LevelMagic(magic);
- break;
- case Spell.CrossHalfMoon:
- magic = GetMagic(Spell.CrossHalfMoon);
- LevelMagic(magic);
- break;
- case Spell.TwinDrakeBlade:
- magic = GetMagic(Spell.TwinDrakeBlade);
- damageFinal = magic.GetDamage(damageBase);
- TwinDrakeBlade = false;
- action = new DelayedAction(DelayedType.Damage, Envir.Time + 400,
- ob, //Object (Target)
- damageFinal, //Damage
- DefenceType.Agility, //Defence to target
- false, //Damage Weapon
- magic, //Magic
- true); //Final hit
- ActionList.Add(action);
- LevelMagic(magic);
- break;
- case Spell.FlamingSword:
- magic = GetMagic(Spell.FlamingSword);
- damageFinal = magic.GetDamage(damageBase);
- FlamingSword = false;
- defence = DefenceType.AC;
- //action = new DelayedAction(DelayedType.Damage, Envir.Time + 400, ob, damage, DefenceType.Agility, true);
- //ActionList.Add(action);
- LevelMagic(magic);
- break;
- }
- //if (ob.Attacked(this, damage, defence) <= 0) break;
- action = new DelayedAction(DelayedType.Damage, Envir.Time + 300, ob, damageFinal, defence, true);
- ActionList.Add(action);
- break;
- }
- Thrusting:
- if (spell == Spell.Thrusting)
- {
- target = Functions.PointMove(target, dir, 1);
- if (!CurrentMap.ValidPoint(target)) return;
- cell = CurrentMap.GetCell(target);
- if (cell.Objects == null) return;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this)) continue;
- magic = GetMagic(spell);
- damageFinal = magic.GetDamage(damageBase);
- ob.Attacked(this, damageFinal,
- ob is MonsterObject monster && (monster.Info.AI == 49) ? DefenceType.Repulsion : DefenceType.Agility,
- false);
- break;
- }
- }
- HalfMoon:
- if (spell == Spell.HalfMoon)
- {
- dir = Functions.PreviousDir(dir);
- magic = GetMagic(spell);
- damageFinal = magic.GetDamage(damageBase);
- for (int i = 0; i < 4; i++)
- {
- target = Functions.PointMove(CurrentLocation, dir, 1);
- dir = Functions.NextDir(dir);
- if (target == Front) continue;
- if (!CurrentMap.ValidPoint(target)) continue;
- cell = CurrentMap.GetCell(target);
- if (cell.Objects == null) continue;
- for (int o = 0; o < cell.Objects.Count; o++)
- {
- MapObject ob = cell.Objects[o];
- if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) continue;
- if (!ob.IsAttackTarget(this)) continue;
- ob.Attacked(this, damageFinal, DefenceType.Agility, false);
- break;
- }
- }
- }
- CrossHalfMoon:
- if (spell == Spell.CrossHalfMoon)
- {
- magic = GetMagic(spell);
- damageFinal = magic.GetDamage(damageBase);
- for (int i = 0; i < 8; i++)
- {
- target = Functions.PointMove(CurrentLocation, dir, 1);
- dir = Functions.NextDir(dir);
- if (target == Front) continue;
- if (!CurrentMap.ValidPoint(target)) continue;
- cell = CurrentMap.GetCell(target);
- if (cell.Objects == null) continue;
- for (int o = 0; o < cell.Objects.Count; o++)
- {
- MapObject ob = cell.Objects[o];
- if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) continue;
- if (!ob.IsAttackTarget(this)) continue;
- ob.Attacked(this, damageFinal, DefenceType.Agility, false);
- break;
- }
- }
- }
- Mining:
- if (Mined)
- {
- if (Info.Equipment[(int)EquipmentSlot.Weapon] == null) return;
- if (!Info.Equipment[(int)EquipmentSlot.Weapon].Info.CanMine) return;
- if (Info.Equipment[(int)EquipmentSlot.Weapon].CurrentDura <= 0)//Stop dura 0 working. use below if you wish to break the item.
- /*{
- Enqueue(new S.DeleteItem { UniqueID = Info.Equipment[(int)EquipmentSlot.Weapon].UniqueID, Count = Info.Equipment[(int)EquipmentSlot.Weapon].Count });
- Info.Equipment[(int)EquipmentSlot.Weapon] = null;
- RefreshStats();*/
- return;
- /*}*/
- if (CurrentMap.Mine == null) return;
- MineSpot Mine = CurrentMap.Mine[target.X, target.Y];
- if ((Mine == null) || (Mine.Mine == null)) return;
- if (Mine.StonesLeft > 0)
- {
- Mine.StonesLeft--;
- if (Envir.Random.Next(100) < (Mine.Mine.HitRate + (Info.Equipment[(int)EquipmentSlot.Weapon].GetTotal(Stat.Accuracy)) * 10))
- {
- //create some rubble on the floor (or increase whats there)
- SpellObject Rubble = null;
- Cell minecell = CurrentMap.GetCell(CurrentLocation);
- for (int i = 0; i < minecell.Objects.Count; i++)
- {
- if (minecell.Objects[i].Race != ObjectType.Spell) continue;
- SpellObject ob = (SpellObject)minecell.Objects[i];
- if (ob.Spell != Spell.Rubble) continue;
- Rubble = ob;
- Rubble.ExpireTime = Envir.Time + (5 * 60 * 1000);
- break;
- }
- if (Rubble == null)
- {
- Rubble = new SpellObject
- {
- Spell = Spell.Rubble,
- Value = 1,
- ExpireTime = Envir.Time + (5 * 60 * 1000),
- TickSpeed = 2000,
- Caster = null,
- CurrentLocation = CurrentLocation,
- CurrentMap = this.CurrentMap,
- Direction = MirDirection.Up
- };
- CurrentMap.AddObject(Rubble);
- Rubble.Spawned();
- }
- if (Rubble != null)
- {
- ActionList.Add(new DelayedAction(DelayedType.Mine, Envir.Time + 400, Rubble));
- }
- //check if we get a payout
- if (Envir.Random.Next(100) < (Mine.Mine.DropRate + Stats[Stat.MineRatePercent]))
- {
- GetMinePayout(Mine.Mine);
- }
- DamageItem(Info.Equipment[(int)EquipmentSlot.Weapon], 5 + Envir.Random.Next(15));
- }
- }
- else
- {
- if (Envir.Time > Mine.LastRegenTick)
- {
- Mine.LastRegenTick = Envir.Time + Mine.Mine.SpotRegenRate * 60 * 1000;
- Mine.StonesLeft = (byte)Envir.Random.Next(Mine.Mine.MaxStones);
- }
- }
- }
- }
- public virtual bool TryMagic()
- {
- return !Dead && Envir.Time >= ActionTime || Envir.Time >= SpellTime;
- }
- public virtual void BeginMagic(Spell spell, MirDirection dir, uint targetID, Point location, Boolean spellTargetLock = false)
- {
- Magic(spell, dir, targetID, location, spellTargetLock);
- }
- public int MagicCost(UserMagic magic)
- {
- int cost = magic.Info.BaseCost + magic.Info.LevelCost * magic.Level;
- Spell spell = magic.Spell;
- if (spell == Spell.Teleport || spell == Spell.Blink || spell == Spell.StormEscape)
- {
- if (Stats[Stat.TeleportManaPenaltyPercent] > 0)
- {
- cost += (cost * Stats[Stat.TeleportManaPenaltyPercent]) / 100;
- }
- }
- if (Stats[Stat.ManaPenaltyPercent] > 0)
- {
- cost += (cost * Stats[Stat.ManaPenaltyPercent]) / 100;
- }
- if (spell == Spell.Plague)
- {
- cost = Stats[Stat.MaxSC] + Stats[Stat.MinSC];
- }
- return cost;
- }
- public virtual MapObject DefaultMagicTarget => this;
- public void Magic(Spell spell, MirDirection dir, uint targetID, Point location, bool spellTargetLock = false)
- {
- if (!CanCast)
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return;
- }
- UserMagic magic = GetMagic(spell);
- if (magic == null)
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return;
- }
- if ((location.X != 0) && (location.Y != 0) && magic.Info.Range != 0 && Functions.InRange(CurrentLocation, location, magic.Info.Range) == false) return;
- if (Hidden)
- {
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- AttackTime = Envir.Time + MoveDelay;
- SpellTime = Envir.Time + 1800; //Spell Delay
- if (spell != Spell.ShoulderDash)
- {
- ActionTime = Envir.Time + MoveDelay;
- }
- LogTime = Envir.Time + Globals.LogDelay;
- long delay = magic.GetDelay();
- if (magic != null && Envir.Time < (magic.CastTime + delay))
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return;
- }
- int cost = MagicCost(magic);
- if (cost > MP)
- {
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- return;
- }
- RegenTime = Envir.Time + RegenDelay;
- ChangeMP(-cost);
- if (spell == Spell.ShoulderDash && Race == ObjectType.Hero)
- dir = Direction;
- Direction = dir;
- if (spell != Spell.ShoulderDash && spell != Spell.BackStep && spell != Spell.FlashDash)
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- MapObject target = null;
- if (targetID == ObjectID)
- {
- target = this;
- }
- else if (targetID > 0)
- {
- target = FindObject(targetID, 10);
- }
- if (target != null && target.Race != ObjectType.Monster && target.Race != ObjectType.Player && target.Race != ObjectType.Hero)
- {
- target = null;
- }
- if (target != null && !target.Dead && target.IsAttackTarget(this) && !target.IsFriendlyTarget(this))
- {
- if (this is PlayerObject player &&
- player.PMode == PetMode.FocusMasterTarget)
- {
- foreach (MonsterObject pet in player.Pets)
- {
- if (pet.Race != ObjectType.Creature)
- {
- pet.Target = target;
- }
- }
- if (player.HeroSpawned &&
- !player.Hero.Dead)
- {
- player.Hero.Target = target;
- }
- }
- }
- bool cast = true;
- byte level = magic.Level;
- switch (spell)
- {
- case Spell.FireBall:
- case Spell.GreatFireBall:
- case Spell.FrostCrunch:
- if (!Fireball(target, magic)) targetID = 0;
- break;
- case Spell.Healing:
- if (target == null)
- {
- target = DefaultMagicTarget;
- targetID = ObjectID;
- }
- Healing(target, magic);
- break;
- case Spell.Repulsion:
- case Spell.EnergyRepulsor:
- case Spell.FireBurst:
- Repulsion(magic);
- break;
- case Spell.ElectricShock:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, target as MonsterObject));
- break;
- case Spell.Poisoning:
- if (!Poisoning(target, magic)) cast = false;
- break;
- case Spell.HellFire:
- HellFire(magic);
- break;
- case Spell.ThunderBolt:
- ThunderBolt(target, magic);
- break;
- case Spell.SoulFireBall:
- if (!SoulFireball(target, magic, out cast)) targetID = 0;
- break;
- case Spell.SummonSkeleton:
- SummonSkeleton(magic);
- break;
- case Spell.Teleport:
- case Spell.Blink:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 200, magic, location));
- break;
- case Spell.Hiding:
- Hiding(magic);
- break;
- case Spell.Haste:
- case Spell.LightBody:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic));
- break;
- case Spell.Fury:
- FurySpell(magic, out cast);
- break;
- case Spell.ImmortalSkin:
- ImmortalSkin(magic, out cast);
- break;
- case Spell.FireBang:
- case Spell.IceStorm:
- FireBang(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location);
- break;
- case Spell.MassHiding:
- MassHiding(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.SoulShield:
- case Spell.BlessedArmour:
- SoulShield(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.FireWall:
- FireWall(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location);
- break;
- case Spell.Lightning:
- Lightning(magic);
- break;
- case Spell.HeavenlySword:
- HeavenlySword(magic);
- break;
- case Spell.MassHealing:
- MassHealing(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location);
- break;
- case Spell.ShoulderDash:
- ShoulderDash(magic);
- return;
- case Spell.ThunderStorm:
- case Spell.FlameField:
- case Spell.StormEscape:
- ThunderStorm(magic);
- if (spell == Spell.FlameField)
- SpellTime = Envir.Time + 2500; //Spell Delay
- if (spell == Spell.StormEscape)
- //Start teleport.
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 750, magic, location));
- break;
- case Spell.MagicShield:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, magic.GetPower(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]) + 15)));
- break;
- case Spell.FlameDisruptor:
- FlameDisruptor(target, magic);
- break;
- case Spell.TurnUndead:
- TurnUndead(target, magic);
- break;
- case Spell.MagicBooster:
- MagicBooster(magic);
- break;
- case Spell.Vampirism:
- Vampirism(target, magic);
- break;
- case Spell.SummonShinsu:
- SummonShinsu(magic);
- break;
- case Spell.Purification:
- if (target == null)
- {
- target = DefaultMagicTarget;
- targetID = ObjectID;
- }
- Purification(target, magic);
- break;
- case Spell.LionRoar:
- case Spell.BattleCry:
- CurrentMap.ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, CurrentLocation));
- break;
- case Spell.Revelation:
- Revelation(target, magic);
- break;
- case Spell.PoisonCloud:
- PoisonCloud(magic, location, out cast);
- break;
- case Spell.Entrapment:
- Entrapment(target, magic);
- break;
- case Spell.BladeAvalanche:
- BladeAvalanche(magic);
- break;
- case Spell.SlashingBurst:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 300, magic));
- break;
- case Spell.Rage:
- Rage(magic);
- break;
- case Spell.Mirroring:
- Mirroring(magic);
- break;
- case Spell.Blizzard:
- Blizzard(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.MeteorStrike:
- MeteorStrike(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.IceThrust:
- IceThrust(magic);
- break;
- case Spell.ProtectionField:
- ProtectionField(magic);
- break;
- case Spell.PetEnhancer:
- PetEnhancer(target, magic, out cast);
- break;
- case Spell.TrapHexagon:
- TrapHexagon(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.Reincarnation:
- if (!CurrentMap.Info.NoReincarnation)
- Reincarnation(magic, target == null ? null : target as PlayerObject, out cast);
- break;
- case Spell.Curse:
- Curse(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.SummonHolyDeva:
- SummonHolyDeva(magic);
- break;
- case Spell.Hallucination:
- Hallucination(target, magic);
- break;
- case Spell.EnergyShield:
- EnergyShield(target, magic, out cast);
- break;
- case Spell.UltimateEnhancer:
- UltimateEnhancer(target, magic, out cast);
- break;
- case Spell.Plague:
- Plague(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.SwiftFeet:
- SwiftFeet(magic, out cast);
- break;
- case Spell.MoonLight:
- MoonLight(magic);
- break;
- case Spell.Trap:
- Trap(magic, target, out cast);
- break;
- case Spell.CatTongue:
- CatTongue(target, magic);
- break;
- case Spell.PoisonSword:
- PoisonSword(magic);
- break;
- case Spell.DarkBody:
- DarkBody(target, magic);
- break;
- case Spell.FlashDash:
- FlashDash(magic);
- return;
- case Spell.CrescentSlash:
- CrescentSlash(magic);
- break;
- case Spell.StraightShot:
- if (!StraightShot(target, magic)) targetID = 0;
- break;
- case Spell.DoubleShot:
- if (!DoubleShot(target, magic)) targetID = 0;
- break;
- case Spell.BackStep:
- BackStep(magic);
- return;
- case Spell.ExplosiveTrap:
- ExplosiveTrap(magic, Front);
- break;
- case Spell.DelayedExplosion:
- if (!DelayedExplosion(target, magic)) targetID = 0;
- break;
- case Spell.Concentration:
- Concentration(magic);
- break;
- case Spell.ElementalShot:
- if (!ElementalShot(target, magic)) targetID = 0;
- break;
- case Spell.ElementalBarrier:
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, magic.GetPower(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]))));
- break;
- case Spell.BindingShot:
- BindingShot(magic, target, out cast);
- break;
- case Spell.SummonVampire:
- case Spell.SummonToad:
- case Spell.SummonSnakes:
- ArcherSummon(magic, target, location);
- break;
- case Spell.Stonetrap:
- ArcherSummonStone(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location, out cast);
- break;
- case Spell.VampireShot:
- case Spell.PoisonShot:
- case Spell.CrippleShot:
- SpecialArrowShot(target, magic);
- break;
- case Spell.NapalmShot:
- NapalmShot(target, magic);
- break;
- case Spell.OneWithNature:
- OneWithNature(target, magic);
- break;
- case Spell.MoonMist:
- MoonMist(magic);
- break;
- case Spell.HealingCircle:
- HealingCircle(magic, spellTargetLock ? (target != null ? target.CurrentLocation : location) : location);
- break;
- //Custom Spells
- case Spell.Portal:
- Portal(magic, location, out cast);
- break;
- case Spell.MeteorShower:
- if (!MeteorShower(target, magic)) targetID = 0;
- return;
- case Spell.FireBounce:
- if (!FireBounce(target, magic, this)) targetID = 0;
- break;
- default:
- cast = false;
- break;
- }
- if (cast)
- {
- magic.CastTime = Envir.Time;
- }
- Enqueue(new S.Magic { Spell = spell, TargetID = targetID, Target = location, Cast = cast, Level = level });
- Broadcast(new S.ObjectMagic { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Spell = spell, TargetID = targetID, Target = location, Cast = cast, Level = level });
- }
- #region Elemental System
- private void Concentration(UserMagic magic)
- {
- int duration = 45 + (15 * magic.Level);
- var buff = AddBuff(BuffType.Concentration, this, Settings.Second * duration, new Stats());
- buff.Set("InterruptTime", (long)0);
- buff.Set("Interrupted", false);
- LevelMagic(magic);
- OperateTime = 0;
- UpdateConcentration(true, false);
- }
- public void UpdateConcentration(bool concentrating, bool interrupted)
- {
- Enqueue(new S.SetConcentration { ObjectID = ObjectID, Enabled = concentrating, Interrupted = interrupted });
- Broadcast(new S.SetConcentration { ObjectID = ObjectID, Enabled = concentrating, Interrupted = interrupted });
- }
- private bool ElementalShot(MapObject target, UserMagic magic)
- {
- if (HasElemental)
- {
- if (target == null || !target.IsAttackTarget(this)) return false;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return false;
- int orbPower = GetElementalOrbPower(false);//base power + orbpower
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]) + orbPower);
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- }
- else
- {
- ObtainElement(true);//gather orb through casting
- LevelMagic(magic);
- return false;
- }
- return true;
- }
- public void GatherElement()
- {
- UserMagic magic = GetMagic(Spell.Meditation);
- if (magic == null) return;
- int meditationLvl = magic.Level;
- int concentrateChance = 0;
- if (HasBuff(BuffType.Concentration, out Buff concentration) && !concentration.Get<bool>("Interrupted"))
- {
- magic = GetMagic(Spell.Concentration);
- if (magic != null)
- {
- concentrateChance = magic.Level + 1;
- }
- }
- if (meditationLvl >= 0)
- {
- int rnd = Envir.Random.Next(10);
- if (rnd >= (8 - meditationLvl - concentrateChance))
- {
- ObtainElement(false);
- LevelMagic(GetMagic(Spell.Meditation));
- }
- }
- }
- public void ObtainElement(bool cast)
- {
- int orbType = 0;
- int meditateLevel = 0;
- UserMagic spell = GetMagic(Spell.Meditation);
- if (spell == null)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.SkillRequiresMeditation), ChatType.System);
- return;
- }
- meditateLevel = spell.Level;
- int maxOrbs = (int)Settings.OrbsExpList[Settings.OrbsExpList.Count - 1];
- if (cast)
- {
- ElementsLevel = (int)Settings.OrbsExpList[0];
- orbType = 1;
- if (Settings.GatherOrbsPerLevel)//Meditation Orbs per level
- if (meditateLevel == 3)
- {
- Enqueue(new S.SetElemental { ObjectID = ObjectID, Enabled = true, Value = (uint)Settings.OrbsExpList[0], ElementType = 1, ExpLast = (uint)maxOrbs });
- Broadcast(new S.SetElemental { ObjectID = ObjectID, Enabled = true, Casted = true, Value = (uint)Settings.OrbsExpList[0], ElementType = 1, ExpLast = (uint)maxOrbs });
- ElementsLevel = (int)Settings.OrbsExpList[1];
- orbType = 2;
- }
- HasElemental = true;
- }
- else
- {
- HasElemental = false;
- ElementsLevel++;
- if (Settings.GatherOrbsPerLevel)//Meditation Orbs per level
- if (ElementsLevel > Settings.OrbsExpList[GetMagic(Spell.Meditation).Level])
- {
- HasElemental = true;
- ElementsLevel = (int)Settings.OrbsExpList[GetMagic(Spell.Meditation).Level];
- return;
- }
- if (ElementsLevel >= Settings.OrbsExpList[0]) HasElemental = true;
- for (int i = 0; i <= Settings.OrbsExpList.Count - 1; i++)
- {
- if (Settings.OrbsExpList[i] != ElementsLevel) continue;
- orbType = i + 1;
- break;
- }
- }
- Enqueue(new S.SetElemental { ObjectID = ObjectID, Enabled = HasElemental, Value = (uint)ElementsLevel, ElementType = (uint)orbType, ExpLast = (uint)maxOrbs });
- Broadcast(new S.SetElemental { ObjectID = ObjectID, Enabled = HasElemental, Casted = cast, Value = (uint)ElementsLevel, ElementType = (uint)orbType, ExpLast = (uint)maxOrbs });
- }
- public int GetElementalOrbCount()
- {
- int OrbCount = 0;
- for (int i = Settings.OrbsExpList.Count - 1; i >= 0; i--)
- {
- if (ElementsLevel >= Settings.OrbsExpList[i])
- {
- OrbCount = i + 1;
- break;
- }
- }
- return OrbCount;
- }
- public int GetElementalOrbPower(bool defensive)
- {
- if (!HasElemental) return 0;
- if (defensive)
- return (int)Settings.OrbsDefList[GetElementalOrbCount() - 1];
- if (!defensive)
- return (int)Settings.OrbsDmgList[GetElementalOrbCount() - 1];
- return 0;
- }
- #endregion
- #region Wizard Skills
- private bool Fireball(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this) || !CanFly(target.CurrentLocation)) return false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- return true;
- }
- private void Repulsion(UserMagic magic)
- {
- bool result = false;
- for (int d = 0; d <= 1; d++)
- {
- for (int y = CurrentLocation.Y - d; y <= CurrentLocation.Y + d; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = CurrentLocation.X - d; x <= CurrentLocation.X + d; x += Math.Abs(y - CurrentLocation.Y) == d ? 1 : d * 2)
- {
- if (x < 0) continue;
- if (x >= CurrentMap.Width) break;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; cell.Objects != null && i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob.Race != ObjectType.Monster && ob.Race != ObjectType.Player && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this) || ob.Level >= Level) continue;
- if (Envir.Random.Next(20) >= 6 + magic.Level * 3 + Level - ob.Level) continue;
- int distance = 1 + Math.Max(0, magic.Level - 1) + Envir.Random.Next(2);
- MirDirection dir = Functions.DirectionFromPoint(CurrentLocation, ob.CurrentLocation);
- if (ob.Pushed(this, dir, distance) == 0) continue;
- if (ob.Race == ObjectType.Player)
- {
- SafeZoneInfo szi = CurrentMap.GetSafeZone(ob.CurrentLocation);
- if (szi != null)
- {
- ((PlayerObject)ob).BindLocation = szi.Location;
- ((PlayerObject)ob).BindMapIndex = CurrentMapIndex;
- ob.InSafeZone = true;
- }
- else
- ob.InSafeZone = false;
- ob.Attacked(this, magic.GetDamage(0), DefenceType.None, false);
- }
- result = true;
- }
- }
- }
- }
- if (result) LevelMagic(magic);
- }
- private void ElectricShock(MonsterObject target, UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- if (target == null || !target.IsAttackTarget(this)) return;
- if (Envir.Random.Next(4 - magic.Level) > 0)
- {
- if (Envir.Random.Next(2) == 0) LevelMagic(magic);
- return;
- }
- LevelMagic(magic);
- if (target.Master == this)
- {
- target.ShockTime = Envir.Time + (magic.Level * 5 + 10) * 1000;
- target.Target = null;
- return;
- }
- if (Envir.Random.Next(2) > 0)
- {
- target.ShockTime = Envir.Time + (magic.Level * 5 + 10) * 1000;
- target.Target = null;
- return;
- }
- if (target.Level > Level + 2 || !target.Info.CanTame) return;
- if (target.Info.IsBoss)
- {
- int limit = Settings.MaxBossTames;
- if (limit <= 0) return;
- int currentBossTames = Pets.Count(p => !p.Dead && p.Race != ObjectType.Creature && p.Info != null && p.Info.IsBoss);
- if (currentBossTames >= limit) return;
- }
- if (Envir.Random.Next(Level + 20 + magic.Level * 5) <= target.Level + 10)
- {
- if (Envir.Random.Next(5) > 0 && target.Master == null)
- {
- target.RageTime = Envir.Time + (Envir.Random.Next(20) + 10) * 1000;
- target.Target = null;
- }
- return;
- }
- var petBonus = Globals.MaxPets - 3;
- if (Pets.Count(t => !t.Dead && t.Race != ObjectType.Creature) >= magic.Level + petBonus) return;
- int rate = (int)(target.Stats[Stat.HP] / 100);
- if (rate <= 2) rate = 2;
- else rate *= 2;
- if (Envir.Random.Next(rate) != 0) return;
- //else if (Envir.Random.Next(20) == 0) target.Die();
- if (target.Master != null)
- {
- target.SetHP(target.Stats[Stat.HP] / 10);
- target.Master.Pets.Remove(target);
- }
- else if (target.Respawn != null)
- {
- target.Respawn.Count--;
- Envir.MonsterCount--;
- CurrentMap.MonsterCount--;
- target.Respawn = null;
- }
- target.Master = this;
- //target.HealthChanged = true;
- target.BroadcastHealthChange();
- Pets.Add(target);
- target.Target = null;
- target.RageTime = 0;
- target.ShockTime = 0;
- target.OperateTime = 0;
- target.MaxPetLevel = (byte)(1 + magic.Level * 2);
- if (!Settings.PetSave)
- {
- target.TameTime = Envir.Time + (Settings.Minute * 60);
- }
- target.Broadcast(new S.ObjectName { ObjectID = target.ObjectID, Name = target.Name });
- }
- private void HellFire(UserMagic magic)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, Direction, 4);
- CurrentMap.ActionList.Add(action);
- if (magic.Level != 3) return;
- MirDirection dir = (MirDirection)(((int)Direction + 1) % 8);
- action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, dir, 4);
- CurrentMap.ActionList.Add(action);
- dir = (MirDirection)(((int)Direction - 1 + 8) % 8);
- action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, dir, 4);
- CurrentMap.ActionList.Add(action);
- }
- private void ThunderBolt(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- if (target.Undead) damage = (int)(damage * 1.5F);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- }
- private void Vampirism(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, damage, target);
- ActionList.Add(action);
- }
- private void FireBang(UserMagic magic, Point location)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, location);
- CurrentMap.ActionList.Add(action);
- }
- private void FireWall(UserMagic magic, Point location)
- {
- List<SpellObject> activeFireWalls = null;
- if (CurrentMap.Info.FireWallLimit && CurrentMap.Info.FireWallCount > 0)
- {
- activeFireWalls = Envir
- .GetObjects(CurrentMapIndex, ObjectType.Spell)
- .OfType<SpellObject>()
- .Where(so => so.Spell == Spell.FireWall && so.Caster == this)
- .ToList();
- var activeCasts = activeFireWalls
- .Select(so => so.CastInstanceId)
- .DefaultIfEmpty(0)
- .Distinct()
- .Count();
- if (activeCasts >= CurrentMap.Info.FireWallCount)
- {
- if (!TryReplaceOldestFireWallCast(activeFireWalls))
- {
- ReceiveChat("Unable to recycle an existing FireWall. Please wait a moment.", ChatType.System);
- return;
- }
- }
- }
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- int castId = ++_fireWallCastSeq;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, location, castId);
- CurrentMap.ActionList.Add(action);
- }
- private bool TryReplaceOldestFireWallCast(List<SpellObject> activeFireWalls)
- {
- if (activeFireWalls == null || activeFireWalls.Count == 0) return false;
- var targetGroup = activeFireWalls
- .GroupBy(so => so.CastInstanceId)
- .OrderBy(g => g.Key == 0 ? int.MinValue : g.Key)
- .FirstOrDefault();
- if (targetGroup == null) return false;
- foreach (var spell in targetGroup)
- {
- if (spell.CurrentMap != null)
- {
- spell.CurrentMap.RemoveObject(spell);
- }
- if (spell.Node != null)
- {
- spell.Despawn();
- }
- }
- return true;
- }
- private void Lightning(UserMagic magic)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, Direction);
- CurrentMap.ActionList.Add(action);
- }
- private void TurnUndead(MapObject target, UserMagic magic)
- {
- if(target != null &&
- target.Race == ObjectType.Monster &&
- target.Undead &&
- target.IsAttackTarget(this))
- {
- // undead pet logic
- if (target.Master is PlayerObject master)
- {
- if (master.PKPoints < 200 &&
- (master.BrownTime == 0 &&
- !master.AtWar(this)))
- {
- BrownTime = Envir.Time + Settings.Minute;
- }
- }
- if (Envir.Random.Next(2) + Level - 1 <= target.Level)
- {
- target.Target = this;
- return;
- }
- int dif = Level - target.Level + 15;
- if (Envir.Random.Next(100) >= (magic.Level + 1 << 3) + dif)
- {
- target.Target = this;
- return;
- }
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, target);
- ActionList.Add(action);
- }
- }
- private void FlameDisruptor(MapObject target, UserMagic magic)
- {
- if (target == null || (target.Race != ObjectType.Player && target.Race != ObjectType.Monster) || !target.IsAttackTarget(this)) return;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- if (!target.Undead) damage = (int)(damage * 1.5F);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- }
- private void ThunderStorm(UserMagic magic)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation);
- CurrentMap.ActionList.Add(action);
- }
- private void Mirroring(UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- MonsterObject monster;
- DelayedAction action;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != Settings.CloneName) || monster.Dead) continue;
- if (monster.Node == null) continue;
- action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, Front, true);
- CurrentMap.ActionList.Add(action);
- return;
- }
- MonsterInfo info = Envir.GetMonsterInfo(Settings.CloneName);
- if (info == null) return;
- LevelMagic(magic);
- monster = MonsterObject.GetMonster(info);
- monster.Master = this;
- monster.ActionTime = Envir.Time + 1000;
- monster.RefreshNameColour(false);
- Pets.Add(monster);
- action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, Front, false);
- CurrentMap.ActionList.Add(action);
- }
- private void Blizzard(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, location);
- ActiveBlizzard = true;
- CurrentMap.ActionList.Add(action);
- cast = true;
- }
- private void MeteorStrike(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, location);
- ActiveBlizzard = true;
- CurrentMap.ActionList.Add(action);
- cast = true;
- }
- private void IceThrust(UserMagic magic)
- {
- int damageBase = GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]);
- if (Envir.Random.Next(100) < (1 + Stats[Stat.Luck]))
- damageBase += damageBase;
- int damageFinish = magic.GetDamage(damageBase);
- Point location = Functions.PointMove(CurrentLocation, Direction, 1);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 1500, this, magic, location, Direction, damageFinish, (int)(damageFinish * 0.6));
- CurrentMap.ActionList.Add(action);
- }
- private void MagicBooster(UserMagic magic)
- {
- int bonus = 6 + magic.Level * 6;
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, bonus));
- }
- #endregion
- #region Taoist Skills
- private void Healing(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsFriendlyTarget(this)) return;
- int health = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) * 2) + Level;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, health, target);
- ActionList.Add(action);
- }
- private bool Poisoning(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return false;
- UserItem item = GetPoison(1);
- if (item == null) return false;
- int power = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, power, target, item);
- ActionList.Add(action);
- ConsumeItem(item, 1);
- return true;
- }
- private bool SoulFireball(MapObject target, UserMagic magic, out bool cast)
- {
- cast = false;
- UserItem item = GetAmulet(1);
- if (item == null) return false;
- cast = true;
- if (target == null || !target.IsAttackTarget(this) || !CanFly(target.CurrentLocation)) return false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- ConsumeItem(item, 1);
- return true;
- }
- private void SummonSkeleton(UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- MonsterObject monster;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != Settings.SkeletonName) || monster.Dead) continue;
- if (monster.Node == null) continue;
- monster.ActionList.Add(new DelayedAction(DelayedType.Recall, Envir.Time + 500));
- return;
- }
- if (Pets.Count(x => x.Race == ObjectType.Monster) >= 2) return;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- MonsterInfo info = Envir.GetMonsterInfo(Settings.SkeletonName);
- if (info == null) return;
- LevelMagic(magic);
- ConsumeItem(item, 1);
- monster = MonsterObject.GetMonster(info);
- monster.PetLevel = magic.Level;
- monster.Master = this;
- monster.MaxPetLevel = (byte)(4 + magic.Level);
- monster.ActionTime = Envir.Time + 1000;
- monster.RefreshNameColour(false);
- //Pets.Add(monster);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, Front);
- CurrentMap.ActionList.Add(action);
- }
- private void Purification(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsFriendlyTarget(this)) return;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, target);
- ActionList.Add(action);
- }
- private void SummonShinsu(UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- MonsterObject monster;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != Settings.ShinsuName) || monster.Dead) continue;
- if (monster.Node == null) continue;
- monster.ActionList.Add(new DelayedAction(DelayedType.Recall, Envir.Time + 500));
- return;
- }
- if (Pets.Count(x => x.Race == ObjectType.Monster) >= 2) return;
- UserItem item = GetAmulet(5);
- if (item == null) return;
- MonsterInfo info = Envir.GetMonsterInfo(Settings.ShinsuName);
- if (info == null) return;
- LevelMagic(magic);
- ConsumeItem(item, 5);
- monster = MonsterObject.GetMonster(info);
- monster.PetLevel = magic.Level;
- monster.Master = this;
- monster.MaxPetLevel = (byte)(1 + magic.Level * 2);
- monster.Direction = Direction;
- monster.ActionTime = Envir.Time + 1000;
- //Pets.Add(monster);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, Front);
- CurrentMap.ActionList.Add(action);
- }
- private void Hiding(UserMagic magic)
- {
- UserItem item = GetAmulet(1);
- if (item == null) return;
- ConsumeItem(item, 1);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) + (magic.Level + 1) * 5);
- ActionList.Add(action);
- }
- private void MassHiding(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- cast = true;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) / 2 + (magic.Level + 1) * 2, location);
- CurrentMap.ActionList.Add(action);
- }
- private void SoulShield(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- cast = true;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) * 4 + (magic.Level + 1) * 50, location);
- CurrentMap.ActionList.Add(action);
- ConsumeItem(item, 1);
- }
- private void MassHealing(UserMagic magic, Point location)
- {
- int value = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, value, location);
- CurrentMap.ActionList.Add(action);
- }
- private void Revelation(MapObject target, UserMagic magic)
- {
- if (target == null) return;
- int value = GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) + magic.GetPower();
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, value, target);
- ActionList.Add(action);
- }
- private void PoisonCloud(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- UserItem amulet = GetAmulet(5);
- if (amulet == null) return;
- UserItem poison = GetPoison(5, 1);
- if (poison == null) return;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, damage, location, (byte)Envir.Random.Next(Stats[Stat.PoisonAttack]));
- ConsumeItem(amulet, 5);
- ConsumeItem(poison, 5);
- CurrentMap.ActionList.Add(action);
- cast = true;
- }
- private void MoonMist(UserMagic magic)
- {
- for (int i = 0; i < Buffs.Count; i++)
- if (Buffs[i].Type == BuffType.MoonLight) return;
- var time = GetAttackPower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
- AddBuff(BuffType.MoonLight, this, (time + (magic.Level + 1) * 5) * 500, new Stats());
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.MoonMist }, CurrentLocation);
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, Direction);
- CurrentMap.ActionList.Add(action);
- LevelMagic(magic);
- }
- private bool CatTongue(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this) || !CanFly(target.CurrentLocation)) return false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]));
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target);
- ActionList.Add(action);
- return true;
- }
- private void TrapHexagon(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- bool anyTargetsFound = false;
- for (int x = location.X - 1; x <= location.X + 1; x++)
- {
- if (x < 0 || x >= CurrentMap.Width) continue;
- for (int y = location.Y - 1; y < location.Y + 1; y++)
- {
- if (y < 0 || y >= CurrentMap.Height) continue;
- if (!CurrentMap.ValidPoint(x, y)) continue;
- var cell = CurrentMap.GetCell(x, y);
- if (cell == null ||
- cell.Objects == null ||
- cell.Objects.Count <= 0) continue;
- foreach (var target in cell.Objects)
- {
- switch (target.Race)
- {
- case ObjectType.Monster:
- if (!target.IsAttackTarget(this)) continue;
- if (target.Level > Level + 2) continue;
- anyTargetsFound = true;
- break;
- }
- }
- }
- }
- if (!anyTargetsFound)
- return;
- UserItem item = GetAmulet(1);
- //Point location = target.CurrentLocation;
- if (item == null) return;
- LevelMagic(magic);
- uint duration = (uint)((magic.Level * 5 + 10) * 1000);
- int value = (int)duration;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, value, location);
- CurrentMap.ActionList.Add(action);
- ConsumeItem(item, 1);
- cast = true;
- }
- private void Reincarnation(UserMagic magic, PlayerObject target, out bool cast)
- {
- cast = true;
- if (target == null || !target.Dead) return;
- // checks for amulet of revival
- UserItem item = GetAmulet(1, 3);
- if (item == null) return;
- if (!ActiveReincarnation && !ReincarnationReady)
- {
- cast = false;
- int CastTime = Math.Abs(((magic.Level + 1) * 1000) - 9000);
- ExpireTime = Envir.Time + CastTime;
- ReincarnationReady = true;
- ActiveReincarnation = true;
- ReincarnationTarget = target;
- ReincarnationExpireTime = ExpireTime + 5000;
- target.ReincarnationHost = this;
- SpellObject ob = new SpellObject
- {
- Spell = Spell.Reincarnation,
- ExpireTime = ExpireTime,
- TickSpeed = 1000,
- Caster = this,
- CurrentLocation = CurrentLocation,
- CastLocation = CurrentLocation,
- Show = true,
- CurrentMap = CurrentMap,
- };
- Packet p = new S.Chat { Message = GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerReviveTarget), Name, target.Name), Type = ChatType.Shout };
- for (int i = 0; i < CurrentMap.Players.Count; i++)
- {
- if (!Functions.InRange(CurrentLocation, CurrentMap.Players[i].CurrentLocation, Globals.DataRange * 2)) continue;
- CurrentMap.Players[i].Enqueue(p);
- }
- CurrentMap.AddObject(ob);
- ob.Spawned();
- ConsumeItem(item, 1);
- // chance of failing Reincarnation when casting
- if (Envir.Random.Next(30) > (1 + magic.Level) * 10)
- {
- return;
- }
- DelayedAction action = new DelayedAction(DelayedType.Magic, ExpireTime, magic);
- ActionList.Add(action);
- return;
- }
- return;
- }
- private void SummonHolyDeva(UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- MonsterObject monster;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != Settings.AngelName) || monster.Dead) continue;
- if (monster.Node == null) continue;
- monster.ActionList.Add(new DelayedAction(DelayedType.Recall, Envir.Time + 500));
- return;
- }
- if (Pets.Count(x => x.Race == ObjectType.Monster) >= 2) return;
- UserItem item = GetAmulet(2);
- if (item == null) return;
- MonsterInfo info = Envir.GetMonsterInfo(Settings.AngelName);
- if (info == null) return;
- LevelMagic(magic);
- ConsumeItem(item, 2);
- monster = MonsterObject.GetMonster(info);
- monster.PetLevel = magic.Level;
- monster.Master = this;
- monster.MaxPetLevel = (byte)(1 + magic.Level * 2);
- monster.Direction = Direction;
- monster.ActionTime = Envir.Time + 1000;
- //Pets.Add(monster);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 1500, this, magic, monster, Front);
- CurrentMap.ActionList.Add(action);
- }
- private void Hallucination(MapObject target, UserMagic magic)
- {
- if (target == null || target.Race != ObjectType.Monster || !target.IsAttackTarget(this)) return;
- int damage = 0;
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, delay, magic, damage, target);
- ActionList.Add(action);
- }
- private void EnergyShield(MapObject target, UserMagic magic, out bool cast)
- {
- cast = false;
- if (target == null || target.Node == null || !target.IsFriendlyTarget(this)) target = this; //offical is only party target
- int duration = 30 + 50 * magic.Level;
- int power = magic.GetPower(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- int chance = (10 - (Stats[Stat.Luck] / 3 + magic.Level + 1));
- if (chance < 2) chance = 2;
- var stats = new Stats
- {
- [Stat.EnergyShieldPercent] = (int)Math.Round((1 / (decimal)chance) * 100),
- [Stat.EnergyShieldHPGain] = power
- };
- switch (target.Race)
- {
- case ObjectType.Player:
- //Only targets
- if (target.IsFriendlyTarget(this))
- {
- target.AddBuff(BuffType.EnergyShield, this, (Settings.Second * duration), stats);
- target.OperateTime = 0;
- LevelMagic(magic);
- cast = true;
- }
- break;
- }
- }
- private void UltimateEnhancer(MapObject target, UserMagic magic, out bool cast)
- {
- cast = false;
- if (target == null || target.Node == null || !target.IsFriendlyTarget(this)) return;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- int expiretime = GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) * 4 + (magic.Level + 1) * 50;
- int value = Stats[Stat.MaxSC] >= 5 ? Math.Min(8, Stats[Stat.MaxSC] / 5) : 1;
- switch (target.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- //Only targets
- if (target.IsFriendlyTarget(this))
- {
- var stats = new Stats();
- if (target.Race == ObjectType.Monster || ((HumanObject)target).Class == MirClass.Warrior || ((HumanObject)target).Class == MirClass.Assassin)
- {
- stats[Stat.MaxDC] = value;
- }
- else if (((HumanObject)target).Class == MirClass.Wizard || ((HumanObject)target).Class == MirClass.Archer)
- {
- stats[Stat.MaxMC] = value;
- }
- else if (((HumanObject)target).Class == MirClass.Taoist)
- {
- stats[Stat.MaxSC] = value;
- }
- target.AddBuff(BuffType.UltimateEnhancer, this, Settings.Second * expiretime, stats);
- target.OperateTime = 0;
- LevelMagic(magic);
- ConsumeItem(item, 1);
- cast = true;
- }
- break;
- }
- }
- private void Plague(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- cast = true;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- PoisonType pType = PoisonType.None;
- UserItem itemp = GetPoison(1, 1);
- if (itemp != null)
- pType = PoisonType.Green;
- else
- {
- itemp = GetPoison(1, 2);
- if (itemp != null)
- pType = PoisonType.Red;
- }
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC])), location, pType);
- CurrentMap.ActionList.Add(action);
- ConsumeItem(item, 1);
- if (itemp != null) ConsumeItem(itemp, 1);
- }
- private void Curse(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- UserItem item = GetAmulet(1);
- if (item == null) return;
- cast = true;
- ConsumeItem(item, 1);
- if (Envir.Random.Next(10 - ((magic.Level + 1) * 2)) > 2) return;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC])), location, 1 + ((magic.Level + 1) * 2));
- CurrentMap.ActionList.Add(action);
- }
- private void PetEnhancer(MapObject target, UserMagic magic, out bool cast)
- {
- cast = false;
- if (target == null || target.Race != ObjectType.Monster || !target.IsFriendlyTarget(this)) return;
- int duration = GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]) + magic.GetPower();
- cast = true;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, duration, target);
- ActionList.Add(action);
- }
- #endregion
- #region Warrior Skills
- private void Entrapment(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return;
- int damage = 0;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic, damage, target);
- ActionList.Add(action);
- }
- private void BladeAvalanche(UserMagic magic)
- {
- int damageBase = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- if (Envir.Random.Next(0, 100) <= (1 + Stats[Stat.Luck]))
- damageBase += damageBase;//crit should do something like double dmg, not double max dc dmg!
- int damageFinal = magic.GetDamage(damageBase);
- int col = 3;
- int row = 3;
- Point[] loc = new Point[col]; //0 = left 1 = center 2 = right
- loc[0] = Functions.PointMove(CurrentLocation, Functions.PreviousDir(Direction), 1);
- loc[1] = Functions.PointMove(CurrentLocation, Direction, 1);
- loc[2] = Functions.PointMove(CurrentLocation, Functions.NextDir(Direction), 1);
- bool train = false;
- for (int i = 0; i < col; i++)
- {
- Point startPoint = loc[i];
- for (int j = 0; j < row; j++)
- {
- Point hitPoint = Functions.PointMove(startPoint, Direction, j);
- if (!CurrentMap.ValidPoint(hitPoint)) continue;
- Cell cell = CurrentMap.GetCell(hitPoint);
- if (cell.Objects == null) continue;
- for (int k = 0; k < cell.Objects.Count; k++)
- {
- MapObject target = cell.Objects[k];
- switch (target.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- //Only targets
- if (target.IsAttackTarget(this))
- {
- if (target.Attacked(this, j <= 1 ? damageFinal : (int)(damageFinal * 0.6),
- target is MonsterObject monster && (monster.Info.AI == 49) ? DefenceType.Repulsion : DefenceType.MAC,
- false) > 0)
- train = true;
- }
- break;
- }
- }
- }
- }
- if (train)
- LevelMagic(magic);
- }
- private void ProtectionField(UserMagic magic)
- {
- int duration = 45 + (15 * magic.Level);
- int addValue = (int)Math.Round(Stats[Stat.MaxAC] * (0.2 + (0.03 * magic.Level)));
- AddBuff(BuffType.ProtectionField, this, Settings.Second * duration, new Stats { [Stat.MaxAC] = addValue, [Stat.MinAC] = addValue });
- OperateTime = 0;
- LevelMagic(magic);
- }
- private void Rage(UserMagic magic)
- {
- int duration = 18 + (6 * magic.Level);
- int addValue = (int)Math.Round(Stats[Stat.MaxDC] * (0.12 + (0.03 * magic.Level)));
- AddBuff(BuffType.Rage, this, Settings.Second * duration, new Stats { [Stat.MaxDC] = addValue, [Stat.MinDC] = addValue });
- OperateTime = 0;
- LevelMagic(magic);
- }
- private void ShoulderDash(UserMagic magic)
- {
- if (InTrapRock || !CanWalk)
- {
- return;
- }
- Point _nextLocation;
- MapObject _target = null;
- bool _blocking = false;
- bool _canDash = false;
- int _cellsTravelled = 0;
- int dist = Envir.Random.Next(2) + magic.Level + 2;
- ActionTime = Envir.Time + MoveDelay;
- for (int i = 0; i < dist; i++)
- {
- if (_blocking)
- {
- break;
- }
- _nextLocation = Functions.PointMove(CurrentLocation, Direction, 1);
- if (!CurrentMap.ValidPoint(_nextLocation) || CurrentMap.GetSafeZone(_nextLocation) != null)
- {
- break;
- }
- // acquire target
- if (i == 0)
- {
- Cell targetCell = CurrentMap.GetCell(_nextLocation);
- if (targetCell.Objects != null)
- {
- int cellCnt = targetCell.Objects.Count;
- for (int j = 0; j < cellCnt; j++)
- {
- MapObject ob = targetCell.Objects[j];
- if ((ob.Race == ObjectType.Player ||
- ob.Race == ObjectType.Monster ||
- ob.Race == ObjectType.Hero) &&
- ob.IsAttackTarget(this) &&
- ob.Level < Level)
- {
- _target = ob;
- break;
- }
- if(ob.Blocking)
- {
- _blocking = true;
- break;
- }
- }
- }
-
- if (_blocking)
- {
- break;
- }
- }
- // try to dash
- Cell dashCell = CurrentMap.GetCell(_nextLocation);
- _canDash = false;
- if (_target == null)
- {
- if (dashCell.Objects != null)
- {
- int cellCnt = dashCell.Objects.Count;
- for (int k = 0; k < cellCnt; k++)
- {
- MapObject ob = dashCell.Objects[k];
- if (ob.Blocking)
- {
- _blocking = true;
- break;
- }
- }
- if(!_blocking)
- {
- _canDash = true;
- }
- }
- else
- {
- _canDash = true;
- }
- }
- else
- {
- // try to push
- if (_target.Pushed(this, Direction, 1) == 0)
- {
- _blocking = true;
- }
- else
- {
- _canDash = true;
- }
- }
- if (_canDash)
- {
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- RemoveObjects(Direction, 1);
- Enqueue(new S.UserDash { Direction = Direction, Location = _nextLocation });
- Broadcast(new S.ObjectDash { ObjectID = ObjectID, Direction = Direction, Location = _nextLocation });
- CurrentMap.GetCell(_nextLocation).Add(this);
- AddObjects(Direction, 1);
- // dash interrupt
- Cell cell = CurrentMap.GetCell(_nextLocation);
- for (int l = 0; l < cell.Objects.Count; l++)
- {
- if (cell.Objects[l].Race == ObjectType.Spell)
- {
- SpellObject ob = (SpellObject)cell.Objects[l];
- if (IsAttackTarget(ob.Caster))
- {
- switch (ob.Spell)
- {
- case Spell.FireWall:
- if (Attacked((PlayerObject)ob.Caster, ob.Value, DefenceType.MAC, false) > 0)
- {
- _blocking = true;
- }
- break;
- }
- }
- }
- }
- CurrentLocation = _nextLocation;
- _cellsTravelled++;
- }
- }
- if (_cellsTravelled == 0)
- {
- Enqueue(new S.UserDashFail { Direction = Direction, Location = CurrentLocation });
- Broadcast(new S.ObjectDashFail { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- if (InSafeZone)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NoPushingInSafezone), ChatType.System);
- }
- else
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NotEnoughPushingPower), ChatType.System);
- }
- }
- else
- {
- _target?.Attacked(this, magic.GetDamage(0), DefenceType.None, false);
- LevelMagic(magic);
- // Broadcast(new S.ObjectDash { ObjectID = ObjectID, Direction = Direction, Location = Front });
- }
- long now = Envir.Time;
- magic.CastTime = now;
- Enqueue(new S.MagicCast { Spell = magic.Spell });
- CellTime = now + 500;
- _stepCounter = 0;
- }
- private void SlashingBurst(UserMagic magic)
- {
- int damageBase = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- int damageFinal = magic.GetDamage(damageBase);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damageFinal, CurrentLocation, Direction, 1);
- CurrentMap.ActionList.Add(action);
- Point location = Functions.PointMove(CurrentLocation, Direction, 2);
- if (!CurrentMap.ValidPoint(location)) return;
- Cell cInfo = CurrentMap.GetCell(location);
- bool blocked = false;
- if (cInfo.Objects != null)
- {
- for (int c = 0; c < cInfo.Objects.Count; c++)
- {
- MapObject ob = cInfo.Objects[c];
- if (!ob.Blocking) continue;
- blocked = true;
- if ((cInfo.Objects == null) || blocked) break;
- }
- }
- if (blocked) return;
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
- RemoveObjects(Direction, 2);
- CurrentLocation = location;
- InTrapRock = false;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- BroadcastInfo();
- AddObjects(Direction, 2);
- Enqueue(new S.UserLocation { Direction = Direction, Location = CurrentLocation });
- }
- private void FurySpell(UserMagic magic, out bool cast)
- {
- cast = true;
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic));
- }
- private void ImmortalSkin(UserMagic magic, out bool cast)
- {
- cast = true;
- ActionList.Add(new DelayedAction(DelayedType.Magic, Envir.Time + 500, magic));
- }
- private void CounterAttackCast(UserMagic magic, MapObject target)
- {
- if (target == null || magic == null) return;
- if (CounterAttack == false) return;
- int damageBase = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- if (Envir.Random.Next(0, 100) <= Stats[Stat.Accuracy])
- damageBase += damageBase;//crit should do something like double dmg, not double max dc dmg!
- int damageFinal = magic.GetDamage(damageBase);
- MirDirection dir = Functions.ReverseDirection(target.Direction);
- Direction = dir;
- if (Functions.InRange(CurrentLocation, target.CurrentLocation, 1) == false) return;
- if (Envir.Random.Next(10) > magic.Level + 6) return;
- Enqueue(new S.ObjectMagic { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Spell = Spell.CounterAttack, TargetID = target.ObjectID, Target = target.CurrentLocation, Cast = true, Level = GetMagic(Spell.CounterAttack).Level, SelfBroadcast = true });
- DelayedAction action = new DelayedAction(DelayedType.Damage, AttackTime, target, damageFinal, DefenceType.AC, true);
- ActionList.Add(action);
- LevelMagic(magic);
- CounterAttack = false;
- }
- #endregion
- #region Assassin Skills
- private void HeavenlySword(UserMagic magic)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation, Direction);
- CurrentMap.ActionList.Add(action);
- }
- private void SwiftFeet(UserMagic magic, out bool cast)
- {
- cast = true;
- ActiveSwiftFeet = true;
- AddBuff(BuffType.SwiftFeet, this, (Settings.Second * 25) + (magic.Level * 5000), new Stats(), true);
- LevelMagic(magic);
- }
- private void MoonLight(UserMagic magic)
- {
- var time = GetAttackPower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
- AddBuff(BuffType.MoonLight, this, (time + (magic.Level + 1) * 5) * 500, new Stats());
- LevelMagic(magic);
- }
- private void Trap(UserMagic magic, MapObject target, out bool cast)
- {
- cast = false;
- if (target == null || !target.IsAttackTarget(this) || !(target is MonsterObject)) return;
- if (target.Level >= Level + 2) return;
- LevelMagic(magic);
- uint duration = 60000;
- int value = (int)duration;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, value, target);
- CurrentMap.ActionList.Add(action);
- cast = true;
- }
- private bool PoisonSword(UserMagic magic)
- {
- UserItem item = GetPoison(1);
- if (item == null) return false;
- Point hitPoint;
- Cell cell;
- MirDirection dir = Functions.PreviousDir(Direction);
- int power = magic.GetDamage(GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]));
- for (int i = 0; i < 5; i++)
- {
- hitPoint = Functions.PointMove(CurrentLocation, dir, 1);
- dir = Functions.NextDir(dir);
- if (!CurrentMap.ValidPoint(hitPoint)) continue;
- cell = CurrentMap.GetCell(hitPoint);
- if (cell.Objects == null) continue;
- for (int o = 0; o < cell.Objects.Count; o++)
- {
- MapObject target = cell.Objects[o];
- if (target.Race != ObjectType.Player && target.Race != ObjectType.Monster) continue;
- if (target == null || !target.IsAttackTarget(this) || target.Node == null) continue;
- target.ApplyPoison(new Poison
- {
- Duration = 3 + power / 10 + magic.Level * 3,
- Owner = this,
- PType = PoisonType.Green,
- TickSpeed = 1000,
- Value = power / 10 + magic.Level + 1 + Envir.Random.Next(Stats[Stat.PoisonAttack])
- }, this);
- target.OperateTime = 0;
- break;
- }
- }
- LevelMagic(magic);
- ConsumeItem(item, 1);
- return true;
- }
- private void DarkBody(MapObject target, UserMagic magic)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- if (target == null) return;
- MonsterObject monster;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != Settings.AssassinCloneName) || monster.Dead) continue;
- if (monster.Node == null) continue;
- monster.Die();
- return;
- }
- MonsterInfo info = Envir.GetMonsterInfo(Settings.AssassinCloneName);
- if (info == null) return;
- monster = MonsterObject.GetMonster(info);
- monster.Master = this;
- monster.Direction = Direction;
- monster.ActionTime = Envir.Time + 500;
- monster.RefreshNameColour(false);
- monster.Target = target;
- Pets.Add(monster);
- monster.Spawn(CurrentMap, CurrentLocation);
- if (!HasBuff(BuffType.DarkBody, out _))
- {
- LevelMagic(magic);
- }
- var duration = (GetAttackPower(Stats[Stat.MinAC], Stats[Stat.MaxAC]) + (magic.Level + 1) * 5) * 500;
- AddBuff(BuffType.DarkBody, this, duration, new Stats());
- }
- private void CrescentSlash(UserMagic magic)
- {
- int damageBase = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- if (Envir.Random.Next(0, 100) <= Stats[Stat.Accuracy])
- damageBase += damageBase;//crit should do something like double dmg, not double max dc dmg!
- int damageFinal = magic.GetDamage(damageBase);
- MirDirection backDir = Functions.ReverseDirection(Direction);
- MirDirection preBackDir = Functions.PreviousDir(backDir);
- MirDirection nextBackDir = Functions.NextDir(backDir);
- for (int i = 0; i < 8; i++)
- {
- MirDirection dir = (MirDirection)i;
- Point hitPoint = Functions.PointMove(CurrentLocation, dir, 1);
- if (dir != backDir && dir != preBackDir && dir != nextBackDir)
- {
- if (!CurrentMap.ValidPoint(hitPoint)) continue;
- Cell cell = CurrentMap.GetCell(hitPoint);
- if (cell.Objects == null) continue;
- for (int j = 0; j < cell.Objects.Count; j++)
- {
- MapObject target = cell.Objects[j];
- switch (target.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- //Only targets
- if (target.IsAttackTarget(this))
- {
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + AttackSpeed, target, damageFinal, DefenceType.AC, true);
- ActionList.Add(action);
- }
- break;
- }
- }
- LevelMagic(magic);
- }
- }
- }
- private void FlashDash(UserMagic magic)
- {
- bool success = false;
- ActionTime = Envir.Time;
- int travel = 0;
- bool blocked = false;
- int jumpDistance = (magic.Level <= 1) ? 0 : 1;//3 max
- Point location = CurrentLocation;
- for (int i = 0; i < jumpDistance; i++)
- {
- location = Functions.PointMove(location, Direction, 1);
- if (!CurrentMap.ValidPoint(location)) break;
- Cell cInfo = CurrentMap.GetCell(location);
- if (cInfo.Objects != null)
- {
- for (int c = 0; c < cInfo.Objects.Count; c++)
- {
- MapObject ob = cInfo.Objects[c];
- if (!ob.Blocking) continue;
- blocked = true;
- if ((cInfo.Objects == null) || blocked) break;
- }
- }
- if (blocked) break;
- travel++;
- }
- jumpDistance = travel;
- if (jumpDistance > 0)
- {
- location = Functions.PointMove(CurrentLocation, Direction, jumpDistance);
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- RemoveObjects(Direction, 1);
- CurrentLocation = location;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- AddObjects(Direction, 1);
- Enqueue(new S.UserDashAttack { Direction = Direction, Location = location });
- Broadcast(new S.ObjectDashAttack { ObjectID = ObjectID, Direction = Direction, Location = location, Distance = jumpDistance });
- }
- else
- {
- Broadcast(new S.ObjectAttack { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- }
- if (travel == 0) location = CurrentLocation;
- int attackDelay = (AttackSpeed - 120) <= 300 ? 300 : (AttackSpeed - 120);
- AttackTime = Envir.Time + attackDelay;
- SpellTime = Envir.Time + 300;
- location = Functions.PointMove(location, Direction, 1);
- if (CurrentMap.ValidPoint(location))
- {
- Cell cInfo = CurrentMap.GetCell(location);
- if (cInfo.Objects != null)
- {
- for (int c = 0; c < cInfo.Objects.Count; c++)
- {
- MapObject ob = cInfo.Objects[c];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- //Only targets
- if (ob.IsAttackTarget(this))
- {
- DelayedAction action = new DelayedAction(DelayedType.Damage, AttackTime, ob, magic.GetDamage(GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC])), DefenceType.AC, true);
- ActionList.Add(action);
- success = true;
- if ((((ob.Race != ObjectType.Player) || Settings.PvpCanResistPoison) && (Envir.Random.Next(Settings.PoisonAttackWeight) >= ob.Stats[Stat.PoisonResist])) && (Envir.Random.Next(15) <= magic.Level + 1))
- {
- DelayedAction pa = new DelayedAction(DelayedType.Poison, AttackTime, ob, PoisonType.Stun, SpellEffect.TwinDrakeBlade, magic.Level + 1, 1000);
- ActionList.Add(pa);
- }
- }
- break;
- }
- }
- }
- }
- if (success) //technicaly this makes flashdash lvl when it casts rather then when it hits (it wont lvl if it's not hitting!)
- LevelMagic(magic);
- magic.CastTime = Envir.Time;
- Enqueue(new S.MagicCast { Spell = magic.Spell });
- }
- #endregion
- #region Archer Skills
- private int ApplyArcherState(int damage)
- {
- UserMagic magic = GetMagic(Spell.MentalState);
- if (magic != null)
- {
- LevelMagic(magic);
- }
- int dmgpenalty = 100;
- switch (Info.MentalState)
- {
- case 1: //trickshot
- dmgpenalty = 55 + (Info.MentalStateLvl * 5);
- break;
- case 2: //group attack
- dmgpenalty = 80;
- break;
- }
- return (damage * dmgpenalty) / 100;
- }
- private bool StraightShot(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return false;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return false;
- int distance = Functions.MaxDistance(CurrentLocation, target.CurrentLocation);
- int damage = magic.GetDamage(GetRangeAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC], distance));
- damage = ApplyArcherState(damage);
- int delay = distance * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- return true;
- }
- private bool DoubleShot(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return false;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return false;
- int distance = Functions.MaxDistance(CurrentLocation, target.CurrentLocation);
- int damage = magic.GetDamage(GetRangeAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC], distance));
- damage = ApplyArcherState(damage);
- int delay = distance * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- action = new DelayedAction(DelayedType.Magic, Envir.Time + delay + 50, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- return true;
- }
- private void BackStep(UserMagic magic)
- {
- ActionTime = Envir.Time;
- if (!CanWalk) return;
- int travel = 0;
- bool blocked = false;
- int jumpDistance = (magic.Level == 0) ? 1 : magic.Level;//3 max
- MirDirection jumpDir = Functions.ReverseDirection(Direction);
- Point location = CurrentLocation;
- for (int i = 0; i < jumpDistance; i++)
- {
- location = Functions.PointMove(location, jumpDir, 1);
- if (!CurrentMap.ValidPoint(location)) break;
- Cell cInfo = CurrentMap.GetCell(location);
- if (cInfo.Objects != null)
- for (int c = 0; c < cInfo.Objects.Count; c++)
- {
- MapObject ob = cInfo.Objects[c];
- if (!ob.Blocking) continue;
- blocked = true;
- if ((cInfo.Objects == null) || blocked) break;
- }
- if (blocked) break;
- travel++;
- }
- jumpDistance = travel;
- if (jumpDistance > 0)
- {
- for (int i = 0; i < jumpDistance; i++)
- {
- location = Functions.PointMove(CurrentLocation, jumpDir, 1);
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- RemoveObjects(jumpDir, 1);
- CurrentLocation = location;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- AddObjects(jumpDir, 1);
- }
- Enqueue(new S.UserBackStep { Direction = Direction, Location = location });
- Broadcast(new S.ObjectBackStep { ObjectID = ObjectID, Direction = Direction, Location = location, Distance = jumpDistance });
- LevelMagic(magic);
- }
- else
- {
- Broadcast(new S.ObjectBackStep { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Distance = jumpDistance });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NotEnoughJumpingPower), ChatType.System);
- }
- magic.CastTime = Envir.Time;
- Enqueue(new S.MagicCast { Spell = magic.Spell });
- CellTime = Envir.Time + 500;
- }
- private bool DelayedExplosion(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this) || !CanFly(target.CurrentLocation)) return false;
- int power = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, power, target, target.CurrentLocation);
- ActionList.Add(action);
- return true;
- }
- private void ExplosiveTrap(UserMagic magic, Point location)
- {
- int freeTrapSpot = -1;
- var trapIDs = CurrentMap.GetSpellObjects(Spell.ExplosiveTrap, this).Select(x => x.ExplosiveTrapID).Distinct();
- var max = magic.Level + 1;
- if (trapIDs.Count() >= max) return;
- for (int i = 0; i < max; i++)
- {
- if (!trapIDs.Contains(i))
- {
- freeTrapSpot = i;
- break;
- }
- }
- if (freeTrapSpot == -1) return;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, location, freeTrapSpot);
- CurrentMap.ActionList.Add(action);
- }
- public void DoKnockback(MapObject target, UserMagic magic)//ElementalShot - knockback
- {
- Cell cell = CurrentMap.GetCell(target.CurrentLocation);
- if (!cell.Valid || cell.Objects == null) return;
- if (target.CurrentLocation.Y < 0 || target.CurrentLocation.Y >= CurrentMap.Height || target.CurrentLocation.X < 0 || target.CurrentLocation.X >= CurrentMap.Height) return;
- if (target.Race != ObjectType.Monster && target.Race != ObjectType.Player && target.Race != ObjectType.Hero) return;
- if (!target.IsAttackTarget(this) || target.Level >= Level) return;
- if (Envir.Random.Next(20) >= 6 + magic.Level * 3 + ElementsLevel + Level - target.Level) return;
- int distance = 1 + Math.Max(0, magic.Level - 1) + Envir.Random.Next(2);
- MirDirection dir = Functions.DirectionFromPoint(CurrentLocation, target.CurrentLocation);
- target.Pushed(this, dir, distance);
- }
- public void BindingShot(UserMagic magic, MapObject target, out bool cast)
- {
- cast = false;
- if (target == null || !target.IsAttackTarget(this) || !(target is MonsterObject)) return;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return;
- if (target.Level > Level + 2) return;
- if (((MonsterObject)target).ShockTime >= Envir.Time) return;//Already shocked
- uint duration = (uint)((magic.Level * 5 + 10) * 1000);
- int value = (int)duration;
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, value, target, target.CurrentLocation);
- ActionList.Add(action);
- cast = true;
- }
- public void SpecialArrowShot(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return;
- int distance = Functions.MaxDistance(CurrentLocation, target.CurrentLocation);
- int damage = magic.GetDamage(GetRangeAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC], distance));
- damage = ApplyArcherState(damage);
- int delay = distance * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- }
- public void NapalmShot(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this)) return;
- if ((Info.MentalState != 1) && !CanFly(target.CurrentLocation)) return;
- int distance = Functions.MaxDistance(CurrentLocation, target.CurrentLocation);
- int damage = magic.GetDamage(GetRangeAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC], distance));
- damage = ApplyArcherState(damage);
- int delay = distance * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, this, magic, damage, target.CurrentLocation);
- CurrentMap.ActionList.Add(action);
- }
- public void ArcherSummon(UserMagic magic, MapObject target, Point location)
- {
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- if (target != null && target.IsAttackTarget(this))
- location = target.CurrentLocation;
- if (!CanFly(location)) return;
- uint duration = (uint)((magic.Level * 5 + 10) * 1000);
- int value = (int)duration;
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, value, location, target);
- ActionList.Add(action);
- }
- public void ArcherSummonStone(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- if (CurrentMap.Info.NoPets)
- {
- ReceiveChat("You cannot summon pets on this map.", ChatType.System);
- return;
- }
-
- if (!CurrentMap.ValidPoint(location) ||
- !CanFly(location))
- {
- return;
- }
- if (Pets.Exists(x => x.Info.GameName == Settings.StoneName))
- {
- MonsterObject st = Pets.First(x => x.Info.GameName == Settings.StoneName);
- if (!st.Dead)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.OnlyOneActiveStoneAlive), Settings.StoneName), ChatType.Hint);
- return;
- }
- }
- int duration = (((magic.Level * 5) + 10) * 1000);
- int delay = Functions.MaxDistance(CurrentLocation, location) * 50 + 500; //50 MS per Step
- //
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, duration, location);
- ActionList.Add(action);
- cast = true;
- }
- public void OneWithNature(MapObject target, UserMagic magic)
- {
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, damage, CurrentLocation);
- CurrentMap.ActionList.Add(action);
- }
- public void HealingCircle(UserMagic magic, Point location)
- {
- var damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]));
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500 + 1200, this, magic, damage, location);
- CurrentMap.ActionList.Add(action);
- }
- #endregion
- #region Custom
- private void Portal(UserMagic magic, Point location, out bool cast)
- {
- cast = false;
- if (!CurrentMap.ValidPoint(location)) return;
- var portalCount = Envir.Spells.Count(x => x.Spell == Spell.Portal && x.Caster == this);
- if (portalCount == 2) return;
- if (!CanFly(location)) return;
- int duration = 30 + (magic.Level * 30);
- int value = duration;
- int passthroughCount = (magic.Level * 2) - 1;
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, value, location, passthroughCount);
- CurrentMap.ActionList.Add(action);
- cast = true;
- }
- private bool FireBounce(MapObject target, UserMagic magic, MapObject source, int bounce = -1)
- {
- if (target == null || !target.IsAttackTarget(this) || !source.CanFly(target.CurrentLocation) || bounce == 0) return false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- int delay = Functions.MaxDistance(source.CurrentLocation, target.CurrentLocation) * 50; //50 MS per Step
- if (bounce == -1)
- {
- bounce = magic.Level + 2;
- delay += 500;
- }
- else
- {
- CurrentMap.Broadcast(new S.ObjectProjectile { Spell = magic.Info.Spell, Source = source.ObjectID, Destination = target.ObjectID }, source.CurrentLocation);
- }
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation, bounce);
- ActionList.Add(action);
- return true;
- }
- private bool MeteorShower(MapObject target, UserMagic magic)
- {
- if (target == null || !target.IsAttackTarget(this) || !CanFly(target.CurrentLocation)) return false;
- int damage = magic.GetDamage(GetAttackPower(Stats[Stat.MinMC], Stats[Stat.MaxMC]));
- int delay = Functions.MaxDistance(CurrentLocation, target.CurrentLocation) * 50 + 500; //50 MS per Step
- var targetIDs = new List<uint>();
- if (target.Race == ObjectType.Monster)
- {
- List<MapObject> targets = ((MonsterObject)target).FindAllNearby(4, target.CurrentLocation);
- int secondaryTargetCount = targets.Count > 3 ? 3 : targets.Count;
- for (int i = 0; i < secondaryTargetCount; i++)
- {
- if (targets[i] == target || !targets[i].IsAttackTarget(this)) continue;
- DelayedAction action2 = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage / 2, targets[i], targets[i].CurrentLocation);
- ActionList.Add(action2);
- targetIDs.Add(targets[i].ObjectID);
- }
- }
- Broadcast(new S.ObjectMagic { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Spell = magic.Info.Spell, TargetID = target.ObjectID, Target = target.CurrentLocation, Cast = true, Level = magic.Level, SecondaryTargetIDs = targetIDs });
- Enqueue(new S.Magic { Spell = Spell.MeteorShower, TargetID = target.ObjectID, Target = target.CurrentLocation, Cast = true, Level = magic.Level, SecondaryTargetIDs = targetIDs });
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + delay, magic, damage, target, target.CurrentLocation);
- ActionList.Add(action);
- return true;
- }
- #endregion
- private void CheckSneakRadius()
- {
- if (!Sneaking) return;
- for (int y = CurrentLocation.Y - 3; y <= CurrentLocation.Y + 3; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = CurrentLocation.X - 3; x <= CurrentLocation.X + 3; x++)
- {
- if (x < 0) continue;
- if (x >= CurrentMap.Width) break;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; cell.Objects != null && i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if ((ob.Race != ObjectType.Player) || ob == this) continue;
- SneakingActive = false;
- return;
- }
- }
- }
- SneakingActive = true;
- }
- protected void CompleteMagic(IList<object> data)
- {
- UserMagic magic = (UserMagic)data[0];
- int value;
- MapObject target;
- Point targetLocation;
- Point location;
- MonsterObject monster;
- switch (magic.Spell)
- {
- #region FireBall, GreatFireBall, ThunderBolt, SoulFireBall, FlameDisruptor
- case Spell.FireBall:
- case Spell.GreatFireBall:
- case Spell.ThunderBolt:
- case Spell.SoulFireBall:
- case Spell.FlameDisruptor:
- case Spell.StraightShot:
- case Spell.DoubleShot:
- case Spell.MeteorShower:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (target.Attacked(this, value, DefenceType.MAC, false) > 0) LevelMagic(magic);
- break;
- #endregion
- #region FireBounce
- case Spell.FireBounce:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- int bounce = (int)data[4];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (target.Attacked(this, value, DefenceType.MAC, false) > 0) LevelMagic(magic);
- if (target.Race == ObjectType.Monster)
- {
- var targets = ((MonsterObject)target).FindAllNearby(3, target.CurrentLocation).Where(x => x != target && x.IsAttackTarget(this) && target.CanFly(x.CurrentLocation)).ToList();
- if (targets.Count > 0)
- {
- var nextTarget = targets[Envir.Random.Next(targets.Count)];
- this.FireBounce(nextTarget, magic, target, --bounce);
- }
- }
- break;
- #endregion
- #region FrostCrunch
- case Spell.FrostCrunch:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (target.Attacked(this, value, DefenceType.MAC, false) > 0)
- {
- if (Level + (target.Race == ObjectType.Player ? 2 : 10) >= target.Level && Envir.Random.Next(target.Race == ObjectType.Player ? 100 : 20) <= magic.Level)
- {
- target.ApplyPoison(new Poison
- {
- Owner = this,
- Duration = target.Race == ObjectType.Player ? 4 : 5 + Envir.Random.Next(5),
- PType = PoisonType.Slow,
- TickSpeed = 1000,
- }, this);
- target.OperateTime = 0;
- }
- if (Level + (target.Race == ObjectType.Player ? 2 : 10) >= target.Level && Envir.Random.Next(target.Race == ObjectType.Player ? 100 : 40) <= magic.Level)
- {
- target.ApplyPoison(new Poison
- {
- Owner = this,
- Duration = target.Race == ObjectType.Player ? 2 : 5 + Envir.Random.Next(Stats[Stat.Freezing]),
- PType = PoisonType.Frozen,
- TickSpeed = 1000,
- }, this);
- target.OperateTime = 0;
- }
- LevelMagic(magic);
- }
- break;
- #endregion
- #region Vampirism
- case Spell.Vampirism:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- value = target.Attacked(this, value, DefenceType.MAC, false);
- if (value == 0) return;
- LevelMagic(magic);
- if (VampAmount == 0) VampTime = Envir.Time + 1000;
- VampAmount += (ushort)(value * (magic.Level + 1) * 0.25F);
- break;
- #endregion
- #region Healing
- case Spell.Healing:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || !target.IsFriendlyTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- if (target.Health >= target.MaxHealth) return;
- target.HealAmount = (ushort)Math.Min(ushort.MaxValue, target.HealAmount + value);
- target.OperateTime = 0;
- LevelMagic(magic);
- break;
- #endregion
- #region ElectricShock
- case Spell.ElectricShock:
- monster = (MonsterObject)data[1];
- if (monster == null || !monster.IsAttackTarget(this) || monster.CurrentMap != CurrentMap || monster.Node == null) return;
- ElectricShock(monster, magic);
- break;
- #endregion
- #region Poisoning
- case Spell.Poisoning:
- value = (int)data[1];
- target = (MapObject)data[2];
- UserItem item = (UserItem)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- switch (item.Info.Shape)
- {
- case 1:
- target.ApplyPoison(new Poison
- {
- Duration = (value * 2) + ((magic.Level + 1) * 7),
- Owner = this,
- PType = PoisonType.Green,
- TickSpeed = 2000,
- Value = value / 15 + magic.Level + 1 + Envir.Random.Next(Stats[Stat.PoisonAttack])
- }, this);
- break;
- case 2:
- target.ApplyPoison(new Poison
- {
- Duration = (value * 2) + (magic.Level + 1) * 7,
- Owner = this,
- PType = PoisonType.Red,
- TickSpeed = 2000,
- }, this);
- break;
- }
- target.OperateTime = 0;
- LevelMagic(magic);
- break;
- #endregion
- #region StormEscape
- case Spell.StormEscape:
- location = (Point)data[1];
- if (CurrentMap.Info.NoTeleport)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotTeleportOnMap), ChatType.System);
- return;
- }
- if (!CurrentMap.ValidPoint(location) || Envir.Random.Next(4) >= magic.Level + 1 || !Teleport(CurrentMap, location, false)) return;
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.StormEscape }, CurrentLocation);
- AddBuff(BuffType.TemporalFlux, this, Settings.Second * 30, new Stats { [Stat.TeleportManaPenaltyPercent] = 30 });
- LevelMagic(magic);
- break;
- #endregion
- #region Teleport
- case Spell.Teleport:
- if (CurrentMap.Info.NoTeleport)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotTeleportOnMap), ChatType.System);
- return;
- }
- if (!MagicTeleport(magic))
- return;
- AddBuff(BuffType.TemporalFlux, this, Settings.Second * 30, new Stats { [Stat.TeleportManaPenaltyPercent] = 30 });
- LevelMagic(magic);
- break;
- #endregion
- #region Blink
- case Spell.Blink:
- {
- location = (Point)data[1];
- if (CurrentMap.Info.NoTeleport)
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotTeleportOnMap), ChatType.System);
- return;
- }
- if (Functions.InRange(CurrentLocation, location, magic.Info.Range) == false) return;
- if (!CurrentMap.ValidPoint(location) || Envir.Random.Next(4) >= magic.Level + 1 || !Teleport(CurrentMap, location, false)) return;
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Teleport }, CurrentLocation);
- LevelMagic(magic);
- AddBuff(BuffType.TemporalFlux, this, Settings.Second * 30, new Stats { [Stat.TeleportManaPenaltyPercent] = 30 });
- }
- break;
- #endregion
- #region Hiding
- case Spell.Hiding:
- {
- value = (int)data[1];
- AddBuff(BuffType.Hiding, this, Settings.Second * value, new Stats());
- LevelMagic(magic);
- }
- break;
- #endregion
- #region Haste
- case Spell.Haste:
- {
- AddBuff(BuffType.Haste, this, (Settings.Second * 25) + (Settings.Second * magic.Level * 15), new Stats { [Stat.AttackSpeed] = (magic.Level * 2) + 2 });
- LevelMagic(magic);
- }
- break;
- #endregion
- #region Fury
- case Spell.Fury:
- {
- AddBuff(BuffType.Fury, this, (Settings.Second * 60) + (magic.Level * 10000), new Stats { [Stat.AttackSpeed] = 4 });
- LevelMagic(magic);
- }
- break;
- #endregion
- #region ImmortalSkin
- case Spell.ImmortalSkin:
- {
- var stats = new Stats
- {
- [Stat.MaxDC] = (int)Math.Round(Stats[Stat.MaxDC] * (0.05 + (0.01 * magic.Level))) * -1,
- [Stat.MaxAC] = (int)Math.Round(Stats[Stat.MaxAC] * (0.10 + (0.07 * magic.Level)))
- };
- AddBuff(BuffType.ImmortalSkin, this, (Settings.Second * 60) + (magic.Level * 1000), stats);
- LevelMagic(magic);
- }
- break;
- #endregion
- #region LightBody
- case Spell.LightBody:
- {
- AddBuff(BuffType.LightBody, this, (magic.Level + 1) * (Settings.Second * 30), new Stats { [Stat.Agility] = (magic.Level + 1) * 2 });
- LevelMagic(magic);
- }
- break;
- #endregion
- #region MagicShield
- case Spell.MagicShield:
- {
- if (HasBuff(BuffType.MagicShield, out _)) return;
- LevelMagic(magic);
- AddBuff(BuffType.MagicShield, this, Settings.Second * (int)data[1], new Stats { [Stat.DamageReductionPercent] = (magic.Level + 2) * 10 });
- }
- break;
- #endregion
- #region TurnUndead
- case Spell.TurnUndead:
- {
- monster = (MonsterObject)data[1];
- if (monster == null || !monster.IsAttackTarget(this) || monster.CurrentMap != CurrentMap || monster.Node == null) return;
- monster.LastHitter = this;
- monster.LastHitTime = Envir.Time + 5000;
- monster.EXPOwner = this;
- monster.EXPOwnerTime = Envir.Time + 5000;
- monster.Die();
- LevelMagic(magic);
- }
- break;
- #endregion
- #region MagicBooster
- case Spell.MagicBooster:
- {
- var stats = new Stats
- {
- [Stat.MinMC] = (int)data[1],
- [Stat.MaxMC] = (int)data[1],
- [Stat.ManaPenaltyPercent] = 6 + magic.Level
- };
- AddBuff(BuffType.MagicBooster, this, Settings.Second * 60, stats, true);
- LevelMagic(magic);
- }
- break;
- #endregion
- #region Purification
- case Spell.Purification:
- target = (MapObject)data[1];
- if (target == null || !target.IsFriendlyTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- if (Envir.Random.Next(4) > magic.Level) return;
- for (int i = 0; i < target.Buffs.Count; i++)
- {
- var buff = target.Buffs[i];
- if (!buff.Properties.HasFlag(BuffProperty.Debuff)) continue;
- target.RemoveBuff(buff.Type);
- }
- if (target.PoisonList.Any(x => x.PType == PoisonType.DelayedExplosion))
- {
- target.ExplosionInflictedTime = 0;
- target.ExplosionInflictedStage = 0;
- if (target.ObjectID == ObjectID)
- {
- Enqueue(new S.RemoveDelayedExplosion { ObjectID = target.ObjectID });
- }
- target.Broadcast(new S.RemoveDelayedExplosion { ObjectID = target.ObjectID });
- }
- target.PoisonList.Clear();
- target.OperateTime = 0;
- LevelMagic(magic);
- break;
- #endregion
- #region Revelation
- case Spell.Revelation:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || target.CurrentMap != CurrentMap || target.Node == null) return;
- if (target.Race != ObjectType.Player && target.Race != ObjectType.Monster) return;
- if (Envir.Random.Next(4) > magic.Level || Envir.Time < target.RevTime) return;
- target.RevTime = Envir.Time + value * 1000;
- target.OperateTime = 0;
- target.BroadcastHealthChange();
- LevelMagic(magic);
- break;
- #endregion
- #region Reincarnation
- case Spell.Reincarnation:
- if (ReincarnationReady)
- {
- ReincarnationTarget.Enqueue(new S.RequestReincarnation { });
- LevelMagic(magic);
- }
- break;
- #endregion
- #region Entrapment
- case Spell.Entrapment:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || target.Race != ObjectType.Monster ||
- Functions.MaxDistance(CurrentLocation, target.CurrentLocation) > 7 || target.Level >= Level + 5 + Envir.Random.Next(8)) return;
- MirDirection pulldirection = (MirDirection)((byte)(Direction - 4) % 8);
- int pulldistance = 0;
- if ((byte)pulldirection % 2 > 0)
- pulldistance = Math.Max(0, Math.Min(Math.Abs(CurrentLocation.X - target.CurrentLocation.X), Math.Abs(CurrentLocation.Y - target.CurrentLocation.Y)));
- else
- pulldistance = pulldirection == MirDirection.Up || pulldirection == MirDirection.Down ? Math.Abs(CurrentLocation.Y - target.CurrentLocation.Y) - 2 : Math.Abs(CurrentLocation.X - target.CurrentLocation.X) - 2;
- int levelgap = target.Race == ObjectType.Player ? Level - target.Level + 4 : Level - target.Level + 9;
- if (Envir.Random.Next(30) >= ((magic.Level + 1) * 3) + levelgap) return;
- int duration = target.Race == ObjectType.Player ? (int)Math.Round((magic.Level + 1) * 1.6) : (int)Math.Round((magic.Level + 1) * 0.8);
- if (duration > 0) target.ApplyPoison(new Poison { PType = PoisonType.Paralysis, Duration = duration, TickSpeed = 1000 }, this);
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = target.ObjectID, Effect = SpellEffect.Entrapment }, target.CurrentLocation);
- if (target.Pushed(this, pulldirection, pulldistance) > 0) LevelMagic(magic);
- break;
- #endregion
- #region Hallucination
- case Spell.Hallucination:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null ||
- Functions.MaxDistance(CurrentLocation, target.CurrentLocation) > 7 || Envir.Random.Next(Level + 20 + magic.Level * 5) <= target.Level + 10) return;
- item = GetAmulet(1);
- if (item == null) return;
- ((MonsterObject)target).HallucinationTime = Envir.Time + (Envir.Random.Next(20) + 10) * 1000;
- target.Target = null;
- ConsumeItem(item, 1);
- LevelMagic(magic);
- break;
- #endregion
- #region PetEnhancer
- case Spell.PetEnhancer:
- {
- value = (int)data[1];
- target = (MonsterObject)data[2];
- if (target.Node == null) return;
- int dcInc = 2 + target.Level * 2;
- int acInc = 4 + target.Level;
- var stats = new Stats
- {
- [Stat.MinDC] = dcInc,
- [Stat.MaxDC] = dcInc,
- [Stat.MinAC] = acInc,
- [Stat.MaxAC] = acInc
- };
- target.AddBuff(BuffType.PetEnhancer, this, (Settings.Second * value), stats);
- LevelMagic(magic);
- }
- break;
- #endregion
- #region CatTongue
- case Spell.CatTongue:
- value = (int)data[1];
- target = (MapObject)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- if (target.Attacked(this, value, DefenceType.AC, false) > 0)
- {
- int rnd = Envir.Random.Next(10);
- if (rnd >= 8)
- {
- if (target.Race == ObjectType.Player && Settings.PvpCanFreeze)
- target.ApplyPoison(new Poison { PType = PoisonType.Frozen, Duration = (magic.Level + 1) * 3, TickSpeed = 1000 }, this);
- else
- rnd -= 4;
- }
- if (rnd <= 4)
- target.ApplyPoison(new Poison { PType = PoisonType.Stun, Duration = (magic.Level + 1) * 3, TickSpeed = 1000 }, this);
- else if (rnd <= 7)
- target.ApplyPoison(new Poison { PType = PoisonType.Slow, Duration = (magic.Level + 1) * 3, TickSpeed = 1000 }, this);
- LevelMagic(magic);
- }
- break;
- #endregion
- #region ElementalBarrier, ElementalShot
- case Spell.ElementalBarrier:
- {
- if (HasBuff(BuffType.ElementalBarrier, out _)) return;
- if (!HasElemental)
- {
- ObtainElement(true);
- LevelMagic(magic);
- return;
- }
- int barrierPower = GetElementalOrbPower(true);//defensive orbpower
- //destroy orbs
- ElementsLevel = 0;
- ObtainElement(false);
- LevelMagic(magic);
- AddBuff(BuffType.ElementalBarrier, this, Settings.Second * ((int)data[1] + barrierPower), new Stats { [Stat.DamageReductionPercent] = (magic.Level + 1) * 10 });
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.ElementalBarrierUp }, CurrentLocation);
- }
- break;
- case Spell.ElementalShot:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2))
- {
- //destroy orbs
- ElementsLevel = 0;
- ObtainElement(false);//update and send to client
- return;
- }
- if (target.Attacked(this, value, DefenceType.MAC, false) > 0)
- LevelMagic(magic);
- DoKnockback(target, magic);//ElementalShot - Knockback
- //destroy orbs
- ElementsLevel = 0;
- ObtainElement(false);//update and send to client
- break;
- #endregion
- #region DelayedExplosion
- case Spell.DelayedExplosion:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (target.Attacked(this, value, DefenceType.MAC, false) > 0) LevelMagic(magic);
- target.ApplyPoison(new Poison
- {
- Duration = (value * 2) + (magic.Level + 1) * 7,
- Owner = this,
- PType = PoisonType.DelayedExplosion,
- TickSpeed = 2000,
- Value = value
- }, this);
- target.OperateTime = 0;
- LevelMagic(magic);
- break;
- #endregion
- #region BindingShot
- case Spell.BindingShot:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (((MonsterObject)target).ShockTime >= Envir.Time) return;//Already shocked
- Point place = target.CurrentLocation;
- MonsterObject centerTarget = null;
- for (int y = place.Y - 1; y <= place.Y + 1; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = place.X - 1; x <= place.X + 1; x++)
- {
- if (x < 0) continue;
- if (x >= CurrentMap.Width) break;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject targetob = cell.Objects[i];
- if (y == place.Y && x == place.X && targetob.Race == ObjectType.Monster)
- {
- centerTarget = (MonsterObject)targetob;
- }
- switch (targetob.Race)
- {
- case ObjectType.Monster:
- if (targetob == null || !targetob.IsAttackTarget(this) || targetob.Node == null || targetob.Level > this.Level + 2) continue;
- MonsterObject mobTarget = (MonsterObject)targetob;
- if (centerTarget == null) centerTarget = mobTarget;
- mobTarget.ShockTime = Envir.Time + value;
- mobTarget.Target = null;
- break;
- }
- }
- }
- }
- if (centerTarget == null) return;
- //only the centertarget holds the effect
- centerTarget.BindingShotCenter = true;
- centerTarget.Broadcast(new S.SetBindingShot { ObjectID = centerTarget.ObjectID, Enabled = true, Value = value });
- LevelMagic(magic);
- break;
- #endregion
- #region VampireShot, PoisonShot, CrippleShot
- case Spell.VampireShot:
- case Spell.PoisonShot:
- case Spell.CrippleShot:
- value = (int)data[1];
- target = (MapObject)data[2];
- targetLocation = (Point)data[3];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null || !Functions.InRange(target.CurrentLocation, targetLocation, 2)) return;
- if (target.Attacked(this, value, DefenceType.MAC, false) == 0) return;
- int buffTime = 5 + (5 * magic.Level);
- bool hasVampBuff = HasBuff(BuffType.VampireShot, out _);
- bool hasPoisonBuff = HasBuff(BuffType.PoisonShot, out _);
- bool doVamp = false, doPoison = false;
- if (magic.Spell == Spell.VampireShot)
- {
- doVamp = true;
- if (!hasVampBuff && !hasPoisonBuff && (Envir.Random.Next(20) >= 8))//40% chance
- {
- AddBuff(BuffType.VampireShot, this, Settings.Second * buffTime, new Stats());
- BroadcastInfo();
- }
- }
- if (magic.Spell == Spell.PoisonShot)
- {
- doPoison = true;
- if (!hasPoisonBuff && !hasVampBuff && (Envir.Random.Next(20) >= 8))//40% chance
- {
- AddBuff(BuffType.PoisonShot, this, Settings.Second * buffTime, new Stats());
- BroadcastInfo();
- }
- }
- if (magic.Spell == Spell.CrippleShot)
- {
- if (hasVampBuff || hasPoisonBuff)
- {
- place = target.CurrentLocation;
- for (int y = place.Y - 1; y <= place.Y + 1; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = place.X - 1; x <= place.X + 1; x++)
- {
- if (x < 0) continue;
- if (x >= CurrentMap.Width) break;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject targetob = cell.Objects[i];
- if (targetob.Race != ObjectType.Monster && targetob.Race != ObjectType.Player) continue;
- if (targetob == null || !targetob.IsAttackTarget(this) || targetob.Node == null) continue;
- if (targetob.Dead) continue;
- if (hasVampBuff)//Vampire Effect
- {
- //cancel out buff
- AddBuff(BuffType.VampireShot, this, 0, new Stats());
- target.Attacked(this, value, DefenceType.MAC, false);
- if (VampAmount == 0) VampTime = Envir.Time + Settings.Second;
- VampAmount += (ushort)(value * (magic.Level + 1) * 0.25F);
- }
- if (hasPoisonBuff)//Poison Effect
- {
- //cancel out buff
- AddBuff(BuffType.PoisonShot, this, 0, new Stats());
- targetob.ApplyPoison(new Poison
- {
- Duration = (value * 2) + (magic.Level + 1) * 7,
- Owner = this,
- PType = PoisonType.Green,
- TickSpeed = 2000,
- Value = value / 25 + magic.Level + 1 + Envir.Random.Next(Stats[Stat.PoisonAttack])
- }, this);
- targetob.OperateTime = 0;
- }
- }
- }
- }
- }
- }
- else
- {
- if (doVamp)//Vampire Effect
- {
- if (VampAmount == 0) VampTime = Envir.Time + 1000;
- VampAmount += (ushort)(value * (magic.Level + 1) * 0.25F);
- }
- if (doPoison)//Poison Effect
- {
- target.ApplyPoison(new Poison
- {
- Duration = (value * 2) + (magic.Level + 1) * 7,
- Owner = this,
- PType = PoisonType.Green,
- TickSpeed = 2000,
- Value = value / 25 + magic.Level + 1 + Envir.Random.Next(Stats[Stat.PoisonAttack])
- }, this);
- target.OperateTime = 0;
- }
- }
- LevelMagic(magic);
- break;
- #endregion
- #region ArcherSummons
- case Spell.SummonVampire:
- case Spell.SummonToad:
- case Spell.SummonSnakes:
- value = (int)data[1];
- location = (Point)data[2];
- target = (MapObject)data[3];
- int SummonType = 0;
- switch (magic.Spell)
- {
- case Spell.SummonVampire:
- SummonType = 1;
- break;
- case Spell.SummonToad:
- SummonType = 2;
- break;
- case Spell.SummonSnakes:
- SummonType = 3;
- break;
- }
- if (SummonType == 0) return;
- for (int i = 0; i < Pets.Count; i++)
- {
- monster = Pets[i];
- if ((monster.Info.Name != (SummonType == 1 ? Settings.VampireName : (SummonType == 2 ? Settings.ToadName : Settings.SnakeTotemName))) || monster.Dead) continue;
- if (monster.Node == null) continue;
- monster.ActionList.Add(new DelayedAction(DelayedType.Recall, Envir.Time + 500, target));
- monster.Target = target;
- return;
- }
- if (Pets.Count(x => x.Race == ObjectType.Monster) >= 2) return;
- //left it in for future summon amulets
- //UserItem item = GetAmulet(5);
- //if (item == null) return;
- MonsterInfo info = Envir.GetMonsterInfo((SummonType == 1 ? Settings.VampireName : (SummonType == 2 ? Settings.ToadName : Settings.SnakeTotemName)));
- if (info == null) return;
- LevelMagic(magic);
- //ConsumeItem(item, 5);
- monster = MonsterObject.GetMonster(info);
- monster.PetLevel = magic.Level;
- monster.Master = this;
- monster.MaxPetLevel = (byte)(1 + magic.Level * 2);
- monster.Direction = Direction;
- monster.ActionTime = Envir.Time + 1000;
- monster.Target = target;
- if (SummonType == 1)
- ((Monsters.VampireSpider)monster).AliveTime = Envir.Time + ((magic.Level * 1500) + 15000);
- if (SummonType == 2)
- ((Monsters.SpittingToad)monster).AliveTime = Envir.Time + ((magic.Level * 2000) + 25000);
- if (SummonType == 3)
- ((Monsters.SnakeTotem)monster).AliveTime = Envir.Time + ((magic.Level * 1500) + 20000);
- //Pets.Add(monster);
- DelayedAction action = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, location);
- CurrentMap.ActionList.Add(action);
- break;
- case Spell.Stonetrap:
- {
- duration = (int)data[1];
- location = (Point)data[2];
- if (Pets.Where(x => x.Race == ObjectType.Monster).Count() >= magic.Level + 1) return;
- MonsterInfo mInfo = Envir.GetMonsterInfo(Settings.StoneName);
- if (mInfo == null) return;
- LevelMagic(magic);
- monster = MonsterObject.GetMonster(mInfo);
- monster.Master = this;
- monster.MaxPetLevel = (byte)(1 + magic.Level * 2);
- monster.Direction = Direction;
- monster.ActionTime = Envir.Time + 1000;
- StoneTrap st = monster as StoneTrap;
- st.DieTime = Envir.Time + duration;
- DelayedAction act = new DelayedAction(DelayedType.Magic, Envir.Time + 500, this, magic, monster, location);
- CurrentMap.ActionList.Add(act);
- break;
- }
- #endregion
- #region SlashingBurst
- case Spell.SlashingBurst:
- SlashingBurst(magic);
- break;
- #endregion
- }
- }
- protected void CompleteMine(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- if (target == null) return;
- target.Broadcast(new S.MapEffect { Effect = SpellEffect.Mine, Location = target.CurrentLocation, Value = (byte)Direction });
- //target.Broadcast(new S.ObjectEffect { ObjectID = target.ObjectID, Effect = SpellEffect.Mine });
- if ((byte)target.Direction < 6)
- target.Direction++;
- target.Broadcast(target.GetInfo());
- }
- protected void CompleteAttack(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- int damage = (int)data[1];
- DefenceType defence = (DefenceType)data[2];
- bool damageWeapon = (bool)data[3];
- UserMagic userMagic = null;
- bool finalHit = false;
- if (data.Count >= 5)
- userMagic = (UserMagic)data[4];
- if (data.Count >= 6)
- finalHit = (bool)data[5];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- if (FatalSword)
- defence = DefenceType.Agility;
- if (target.Attacked(this, damage, defence, damageWeapon) <= 0) return;
- if (FatalSword)
- {
- S.ObjectEffect p = new S.ObjectEffect { ObjectID = target.ObjectID, Effect = SpellEffect.FatalSword };
- CurrentMap.Broadcast(p, target.CurrentLocation);
- FatalSword = false;
- var magic = GetMagic(Spell.FatalSword);
- if (magic != null) LevelMagic(magic);
- }
- if (userMagic != null && finalHit)
- {
- if (userMagic.Spell == Spell.TwinDrakeBlade)
- {
- if ((((target.Race != ObjectType.Player) || Settings.PvpCanResistPoison) &&
- (Envir.Random.Next(Settings.PoisonAttackWeight) >= target.Stats[Stat.PoisonResist])) &&
- (target.Level < Level + 10 && Envir.Random.Next(target.Race == ObjectType.Player ? 40 : 20) <= userMagic.Level + 1))
- {
- target.ApplyPoison(new Poison { PType = PoisonType.Stun, Duration = target.Race == ObjectType.Player ? 2 : 2 + userMagic.Level, TickSpeed = 1000 }, this);
- target.Broadcast(new S.ObjectEffect { ObjectID = target.ObjectID, Effect = SpellEffect.TwinDrakeBlade });
- }
- }
- }
- //Level Fencing / SpiritSword
- foreach (UserMagic magic in Info.Magics)
- {
- switch (magic.Spell)
- {
- case Spell.Fencing:
- case Spell.SpiritSword:
- LevelMagic(magic);
- break;
- }
- }
- }
- protected void CompleteDamageIndicator(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- DamageType type = (DamageType)data[1];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- target.BroadcastDamageIndicator(type);
- }
- protected void CompleteSpellEffect(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- SpellEffect effect = (SpellEffect)data[1];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- S.ObjectEffect p = new S.ObjectEffect { ObjectID = target.ObjectID, Effect = effect };
- CurrentMap.Broadcast(p, target.CurrentLocation);
- }
- protected void CompletePoison(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- PoisonType pt = (PoisonType)data[1];
- SpellEffect sp = (SpellEffect)data[2];
- int duration = (int)data[3];
- int tickSpeed = (int)data[4];
- if (target == null) return;
- target.ApplyPoison(new Poison { PType = pt, Duration = duration, TickSpeed = tickSpeed }, this);
- target.Broadcast(new S.ObjectEffect { ObjectID = target.ObjectID, Effect = sp });
- }
- protected UserItem GetAmulet(int count, int shape = 0)
- {
- for (int i = 0; i < Info.Equipment.Length; i++)
- {
- UserItem item = Info.Equipment[i];
- if (item != null && item.Info.Type == ItemType.Amulet && item.Info.Shape == shape && item.Count >= count)
- return item;
- }
- return null;
- }
- protected UserItem GetPoison(int count, byte shape = 0)
- {
- for (int i = 0; i < Info.Equipment.Length; i++)
- {
- UserItem item = Info.Equipment[i];
- if (item != null && item.Info.Type == ItemType.Amulet && item.Count >= count)
- {
- if (shape == 0)
- {
- if (item.Info.Shape == 1 || item.Info.Shape == 2)
- return item;
- }
- else
- {
- if (item.Info.Shape == shape)
- return item;
- }
- }
- }
- return null;
- }
- public UserMagic GetMagic(Spell spell)
- {
- for (int i = 0; i < Info.Magics.Count; i++)
- {
- UserMagic magic = Info.Magics[i];
- if (magic.Spell != spell) continue;
- return magic;
- }
- return null;
- }
- public void LevelMagic(UserMagic magic)
- {
- byte exp = (byte)(Envir.Random.Next(3) + 1);
- if (Settings.MentorSkillBoost && Info.Mentor != 0 && Info.IsMentor)
- {
- if (HasBuff(BuffType.Mentee, out _))
- {
- CharacterInfo mentor = Envir.GetCharacterInfo(Info.Mentor);
- PlayerObject player = Envir.GetPlayer(mentor.Name);
- if (player.CurrentMap == CurrentMap && Functions.InRange(player.CurrentLocation, CurrentLocation, Globals.DataRange) && !player.Dead)
- {
- if (Stats[Stat.SkillGainMultiplier] == 1)
- {
- if (GroupMembers != null && GroupMembers.Contains(player))
- exp *= 2;
- }
- }
- }
- }
- exp *= (byte)Math.Min(byte.MaxValue, Stats[Stat.SkillGainMultiplier]);
- if (Level == ushort.MaxValue) exp = byte.MaxValue;
- int oldLevel = magic.Level;
- switch (magic.Level)
- {
- case 0:
- if (Level < magic.Info.Level1)
- return;
- magic.Experience += exp;
- if (magic.Experience >= magic.Info.Need1)
- {
- magic.Level++;
- magic.Experience = (ushort)(magic.Experience - magic.Info.Need1);
- RefreshStats();
- }
- break;
- case 1:
- if (Level < magic.Info.Level2)
- return;
- magic.Experience += exp;
- if (magic.Experience >= magic.Info.Need2)
- {
- magic.Level++;
- magic.Experience = (ushort)(magic.Experience - magic.Info.Need2);
- RefreshStats();
- }
- break;
- case 2:
- if (Level < magic.Info.Level3)
- return;
- magic.Experience += exp;
- if (magic.Experience >= magic.Info.Need3)
- {
- magic.Level++;
- magic.Experience = 0;
- RefreshStats();
- }
- break;
- default:
- return;
- }
- if (oldLevel != magic.Level)
- {
- long delay = magic.GetDelay();
- Enqueue(new S.MagicDelay { ObjectID = ObjectID, Spell = magic.Spell, Delay = delay });
- }
- Enqueue(new S.MagicLeveled { ObjectID = ObjectID, Spell = magic.Spell, Level = magic.Level, Experience = magic.Experience });
- }
- public virtual bool MagicTeleport(UserMagic magic)
- {
- return false;
- }
- public override bool Teleport(Map temp, Point location, bool effects = true, byte effectnumber = 0)
- {
- Map oldMap = CurrentMap;
- Point oldLocation = CurrentLocation;
- bool mapChanged = temp != oldMap;
- if (!base.Teleport(temp, location, effects)) return false;
- Enqueue(new S.MapChanged
- {
- MapIndex = CurrentMap.Info.Index,
- FileName = CurrentMap.Info.FileName,
- Title = CurrentMap.Info.Title,
- Weather = CurrentMap.Info.WeatherParticles,
- MiniMap = CurrentMap.Info.MiniMap,
- BigMap = CurrentMap.Info.BigMap,
- Lights = CurrentMap.Info.Light,
- Location = CurrentLocation,
- Direction = Direction,
- MapDarkLight = CurrentMap.Info.MapDarkLight,
- Music = CurrentMap.Info.Music
- });
- if (effects) Enqueue(new S.ObjectTeleportIn { ObjectID = ObjectID, Type = effectnumber });
- if (RidingMount) RefreshMount();
- if (ActiveBlizzard) ActiveBlizzard = false;
- if (CheckStacked())
- {
- StackingTime = Envir.Time + 1000;
- Stacking = true;
- }
- SafeZoneInfo szi = CurrentMap.GetSafeZone(CurrentLocation);
- if (szi != null)
- {
- SetBindSafeZone(szi);
- InSafeZone = true;
- }
- else
- InSafeZone = false;
- return true;
- }
- protected virtual void SetBindSafeZone(SafeZoneInfo szi) { }
- public virtual bool AtWar(HumanObject attacker)
- {
- return false;
- }
- private Packet GetMountInfo()
- {
- return new S.MountUpdate
- {
- ObjectID = ObjectID,
- RidingMount = RidingMount,
- MountType = Mount.MountType
- };
- }
- protected Packet GetUpdateInfo()
- {
- return new S.PlayerUpdate
- {
- ObjectID = ObjectID,
- Weapon = Looks_Weapon,
- WeaponEffect = Looks_WeaponEffect,
- Armour = Looks_Armour,
- Light = Light,
- WingEffect = Looks_Wings
- };
- }
- protected virtual void UpdateLooks(short OldLooks_Weapon)
- {
- Broadcast(GetUpdateInfo());
- }
- public Packet GetInfoEx(HumanObject player)
- {
- var p = (S.ObjectPlayer)GetInfo();
- if (p != null)
- {
- p.NameColour = player.GetNameColour(this);
- }
- return p;
- }
- public override bool IsAttackTarget(HumanObject attacker)
- {
- return true;
- }
- public override bool IsAttackTarget(MonsterObject attacker)
- {
- return true;
- }
- public override bool IsFriendlyTarget(HumanObject ally)
- {
- return true;
- }
- public override bool IsFriendlyTarget(MonsterObject ally)
- {
- return true;
- }
- public override void ReceiveChat(string text, ChatType type) { }
- public override Packet GetInfo() { return null; }
- public override int Attacked(HumanObject attacker, int damage, DefenceType type = DefenceType.ACAgility, bool damageWeapon = true)
- {
- if (attacker.Race == ObjectType.Hero)
- {
- HeroObject heroAttacker = (HeroObject)attacker;
- attacker = heroAttacker.Owner;
- heroAttacker.Target = this;
- }
- var armour = GetArmour(type, attacker, out bool hit);
- if (!hit)
- {
- return 0;
- }
- armour = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(armour * ArmourRate))));
- damage = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(damage * DamageRate))));
- if (damageWeapon)
- attacker.DamageWeapon();
- damage += attacker.Stats[Stat.AttackBonus];
- if (Envir.Random.Next(100) < Stats[Stat.Reflect])
- {
- if (attacker.IsAttackTarget(this))
- {
- attacker.Attacked(this, damage, type, false);
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Reflect }, CurrentLocation);
- }
- return 0;
- }
- //MagicShield, ElementalBarrier
- if (Stats[Stat.DamageReductionPercent] > 0)
- {
- damage -= (damage * Stats[Stat.DamageReductionPercent]) / 100;
- }
- if (armour >= damage)
- {
- BroadcastDamageIndicator(DamageType.Miss);
- return 0;
- }
- if (Hidden)
- {
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- //EnergyShield
- if (Stats[Stat.EnergyShieldPercent] > 0)
- {
- if (Envir.Random.Next(100) < Stats[Stat.EnergyShieldPercent])
- {
- if (HP + (Stats[Stat.EnergyShieldHPGain]) >= Stats[Stat.HP])
- SetHP(Stats[Stat.HP]);
- else
- ChangeHP(Stats[Stat.EnergyShieldHPGain]);
- }
- }
- if (Envir.Random.Next(100) < (attacker.Stats[Stat.CriticalRate] * Settings.CriticalRateWeight))
- {
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Critical }, CurrentLocation);
- damage = Math.Min(int.MaxValue, damage + (int)Math.Floor(damage * (((double)attacker.Stats[Stat.CriticalDamage] / (double)Settings.CriticalDamageWeight) * 10)));
- BroadcastDamageIndicator(DamageType.Critical);
- }
- if (HasBuff(BuffType.MagicShield, out Buff magicShield))
- {
- var duration = (int)Math.Min(int.MaxValue, magicShield.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.MagicShield, this, duration, null);
- }
- if (HasBuff(BuffType.ElementalBarrier, out Buff elementalBarrier))
- {
- var duration = (int)Math.Min(int.MaxValue, elementalBarrier.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.ElementalBarrier, this, duration, null);
- }
- if (attacker.Stats[Stat.HPDrainRatePercent] > 0 && damageWeapon)
- {
- attacker.HpDrain += Math.Max(0, ((float)(damage - armour) / 100) * attacker.Stats[Stat.HPDrainRatePercent]);
- if (attacker.HpDrain > 2)
- {
- int HpGain = (int)Math.Floor(attacker.HpDrain);
- attacker.ChangeHP(HpGain);
- attacker.HpDrain -= HpGain;
- }
- }
- for (int i = PoisonList.Count - 1; i >= 0; i--)
- {
- if (PoisonList[i].PType != PoisonType.LRParalysis) continue;
- PoisonList.RemoveAt(i);
- OperateTime = 0;
- }
- LastHitter = attacker;
- LastHitTime = Envir.Time + 10000;
- RegenTime = Envir.Time + RegenDelay;
- LogTime = Envir.Time + Globals.LogDelay;
- if (Envir.Time > BrownTime && PKPoints < 200 && !AtWar(attacker))
- attacker.BrownTime = Envir.Time + Settings.Minute;
- ushort LevelOffset = (byte)(Level > attacker.Level ? 0 : Math.Min(10, attacker.Level - Level));
- ApplyNegativeEffects(attacker, type, LevelOffset);
- attacker.GatherElement();
- DamageDura();
- ActiveBlizzard = false;
- ActiveReincarnation = false;
- CounterAttackCast(GetMagic(Spell.CounterAttack), LastHitter);
- Enqueue(new S.Struck { AttackerID = attacker.ObjectID });
- Broadcast(new S.ObjectStruck { ObjectID = ObjectID, AttackerID = attacker.ObjectID, Direction = Direction, Location = CurrentLocation });
- BroadcastDamageIndicator(DamageType.Hit, armour - damage);
- ChangeHP(armour - damage);
- return damage - armour;
- }
- public override int Attacked(MonsterObject attacker, int damage, DefenceType type = DefenceType.ACAgility)
- {
- var armour = GetArmour(type, attacker, out bool hit);
- if (!hit)
- {
- return 0;
- }
- if (Envir.Random.Next(100) < Stats[Stat.Reflect])
- {
- if (attacker.IsAttackTarget(this))
- {
- attacker.Attacked(this, damage, type, false);
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Reflect }, CurrentLocation);
- }
- return 0;
- }
- armour = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(armour * ArmourRate))));
- damage = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(damage * DamageRate))));
- //MagicShield, ElementalBarrier
- if (Stats[Stat.DamageReductionPercent] != 0)
- {
- damage -= (damage * Stats[Stat.DamageReductionPercent]) / 100;
- }
- if (armour >= damage)
- {
- BroadcastDamageIndicator(DamageType.Miss);
- return 0;
- }
- if (Hidden)
- {
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- if (Stats[Stat.EnergyShieldPercent] > 0)
- {
- if (Envir.Random.Next(100) < Stats[Stat.EnergyShieldPercent])
- {
- if (HP + (Stats[Stat.EnergyShieldHPGain]) >= Stats[Stat.HP])
- SetHP(Stats[Stat.HP]);
- else
- ChangeHP(Stats[Stat.EnergyShieldHPGain]);
- }
- }
- if (HasBuff(BuffType.MagicShield, out Buff magicShield))
- {
- var duration = (int)Math.Min(int.MaxValue, magicShield.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.MagicShield, this, duration, null);
- }
- if (HasBuff(BuffType.ElementalBarrier, out Buff elementalBarrier))
- {
- var duration = (int)Math.Min(int.MaxValue, elementalBarrier.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.ElementalBarrier, this, duration, null);
- }
- for (int i = PoisonList.Count - 1; i >= 0; i--)
- {
- if (PoisonList[i].PType != PoisonType.LRParalysis) continue;
- PoisonList.RemoveAt(i);
- OperateTime = 0;
- }
- LastHitter = attacker.Master ?? attacker;
- LastHitTime = Envir.Time + 10000;
- RegenTime = Envir.Time + RegenDelay;
- LogTime = Envir.Time + Globals.LogDelay;
- DamageDura();
- ActiveBlizzard = false;
- ActiveReincarnation = false;
- CounterAttackCast(GetMagic(Spell.CounterAttack), LastHitter);
- if (StruckTime < Envir.Time)
- {
- Enqueue(new S.Struck { AttackerID = attacker.ObjectID });
- Broadcast(new S.ObjectStruck { ObjectID = ObjectID, AttackerID = attacker.ObjectID, Direction = Direction, Location = CurrentLocation });
- StruckTime = Envir.Time + 500;
- }
- BroadcastDamageIndicator(DamageType.Hit, armour - damage);
- ChangeHP(armour - damage);
- return damage - armour;
- }
- public override int Struck(int damage, DefenceType type = DefenceType.ACAgility)
- {
- int armour = 0;
- if (Hidden)
- {
- RemoveBuff(BuffType.MoonLight);
- RemoveBuff(BuffType.DarkBody);
- }
- switch (type)
- {
- case DefenceType.ACAgility:
- armour = GetAttackPower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
- break;
- case DefenceType.AC:
- armour = GetAttackPower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
- break;
- case DefenceType.MACAgility:
- armour = GetAttackPower(Stats[Stat.MinMAC], Stats[Stat.MaxMAC]);
- break;
- case DefenceType.MAC:
- armour = GetAttackPower(Stats[Stat.MinMAC], Stats[Stat.MaxMAC]);
- break;
- case DefenceType.Agility:
- break;
- }
- armour = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(armour * ArmourRate))));
- damage = (int)Math.Max(int.MinValue, (Math.Min(int.MaxValue, (decimal)(damage * DamageRate))));
- //MagicShield, ElementalBarrier
- if (Stats[Stat.DamageReductionPercent] != 0)
- {
- damage -= (damage * Stats[Stat.DamageReductionPercent]) / 100;
- }
- if (armour >= damage) return 0;
- if (HasBuff(BuffType.MagicShield, out Buff magicShield))
- {
- var duration = (int)Math.Min(int.MaxValue, magicShield.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.MagicShield, this, duration, null);
- }
- if (HasBuff(BuffType.ElementalBarrier, out Buff elementalBarrier))
- {
- var duration = (int)Math.Min(int.MaxValue, elementalBarrier.ExpireTime - ((damage - armour) * 60));
- AddBuff(BuffType.ElementalBarrier, this, duration, null);
- }
- RegenTime = Envir.Time + RegenDelay;
- LogTime = Envir.Time + Globals.LogDelay;
- DamageDura();
- ActiveBlizzard = false;
- ActiveReincarnation = false;
- Enqueue(new S.Struck { AttackerID = 0 });
- Broadcast(new S.ObjectStruck { ObjectID = ObjectID, AttackerID = 0, Direction = Direction, Location = CurrentLocation });
- ChangeHP(armour - damage);
- return damage - armour;
- }
- public override void ApplyPoison(Poison p, MapObject Caster = null, bool NoResist = false, bool ignoreDefence = true)
- {
- if (Caster != null && !NoResist)
- {
- if (((Caster.Race != ObjectType.Player) || Settings.PvpCanResistPoison) && (Envir.Random.Next(Settings.PoisonResistWeight) < Stats[Stat.PoisonResist]))
- {
- return;
- }
- }
- if (!ignoreDefence && (p.PType == PoisonType.Green))
- {
- int armour = GetAttackPower(Stats[Stat.MinMAC], Stats[Stat.MaxMAC]);
- if (p.Value < armour)
- p.PType = PoisonType.None;
- else
- p.Value -= armour;
- }
- if (p.Owner != null && p.Owner is PlayerObject player && Envir.Time > BrownTime && PKPoints < 200)
- {
- bool ownerBrowns = true;
- if (player.MyGuild != null && MyGuild != null && MyGuild.IsAtWar() && MyGuild.IsEnemy(player.MyGuild))
- ownerBrowns = false;
- if (ownerBrowns && !player.WarZone)
- p.Owner.BrownTime = Envir.Time + Settings.Minute;
- }
- if ((p.PType == PoisonType.Green) || (p.PType == PoisonType.Red)) p.Duration = Math.Max(0, p.Duration - Stats[Stat.PoisonRecovery]);
- if (p.Duration == 0) return;
- if (p.PType == PoisonType.None) return;
- for (int i = 0; i < PoisonList.Count; i++)
- {
- if (PoisonList[i].PType != p.PType) continue;
- if ((PoisonList[i].PType == PoisonType.Green) && (PoisonList[i].Value > p.Value)) return;//cant cast weak poison to cancel out strong poison
- if ((PoisonList[i].PType != PoisonType.Green) && ((PoisonList[i].Duration - PoisonList[i].Time) > p.Duration)) return;//cant cast 1 second poison to make a 1minute poison go away!
- if ((PoisonList[i].PType == PoisonType.Frozen) || (PoisonList[i].PType == PoisonType.Slow) || (PoisonList[i].PType == PoisonType.Paralysis) || (PoisonList[i].PType == PoisonType.LRParalysis)) return;//prevents mobs from being perma frozen/slowed
- if (p.PType == PoisonType.DelayedExplosion) return;
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.BeenPoisoned), ChatType.System2);
- PoisonList[i] = p;
- return;
- }
- switch (p.PType)
- {
- case PoisonType.DelayedExplosion:
- {
- ExplosionInflictedTime = Envir.Time + 4000;
- Enqueue(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion });
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouAreWalkingExplosive), ChatType.System);
- }
- break;
- case PoisonType.Dazed:
- {
- Enqueue(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Stunned, Time = (uint)(p.Duration * p.TickSpeed) });
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Stunned, Time = (uint)(p.Duration * p.TickSpeed) });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.BeenPoisoned), ChatType.System2);
- }
- break;
- case PoisonType.Blindness:
- {
- AddBuff(BuffType.Blindness, Caster, (int)(p.Duration * p.TickSpeed), new Stats { [Stat.Accuracy] = p.Value * -1 });
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.BeenPoisoned), ChatType.System2);
- }
- break;
- default:
- {
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.BeenPoisoned), ChatType.System2);
- }
- break;
- }
- PoisonList.Add(p);
- }
- public override Buff AddBuff(BuffType type, MapObject owner, int duration, Stats stats, bool refreshStats = true, bool updateOnly = false, params int[] values)
- {
- Buff b = base.AddBuff(type, owner, duration, stats, refreshStats, updateOnly, values);
- switch (b.Type)
- {
- case BuffType.MagicShield:
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.MagicShieldUp }, CurrentLocation);
- break;
- case BuffType.ElementalBarrier:
- CurrentMap.Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.ElementalBarrierUp }, CurrentLocation);
- break;
- }
- var packet = new S.AddBuff { Buff = b.ToClientBuff() };
- Enqueue(packet);
- if (b.Info.Visible)
- {
- Broadcast(packet);
- }
- if (refreshStats)
- {
- RefreshStats();
- }
- return b;
- }
- public override void PauseBuff(Buff b)
- {
- if (b.Paused) return;
- base.PauseBuff(b);
- Enqueue(new S.PauseBuff { Type = b.Type, ObjectID = ObjectID, Paused = true });
- }
- public override void UnpauseBuff(Buff b)
- {
- if (!b.Paused) return;
- base.UnpauseBuff(b);
- Enqueue(new S.PauseBuff { Type = b.Type, ObjectID = ObjectID, Paused = false });
- }
- protected int GetCurrentStatCount(UserItem gem, UserItem item)
- {
- if (gem.GetTotal(Stat.MaxDC) > 0)
- return item.AddedStats[Stat.MaxDC];
- else if (gem.GetTotal(Stat.MaxMC) > 0)
- return item.AddedStats[Stat.MaxMC];
- else if (gem.GetTotal(Stat.MaxSC) > 0)
- return item.AddedStats[Stat.MaxSC];
- else if (gem.GetTotal(Stat.MaxAC) > 0)
- return item.AddedStats[Stat.MaxAC];
- else if (gem.GetTotal(Stat.MaxMAC) > 0)
- return item.AddedStats[Stat.MaxMAC];
- else if ((gem.Info.Durability) > 0)
- return item.Info.Durability > item.MaxDura ? 0 : ((item.MaxDura - item.Info.Durability) / 1000);
- else if (gem.GetTotal(Stat.AttackSpeed) > 0)
- return item.AddedStats[Stat.AttackSpeed];
- else if (gem.GetTotal(Stat.Agility) > 0)
- return item.AddedStats[Stat.Agility];
- else if (gem.GetTotal(Stat.Accuracy) > 0)
- return item.AddedStats[Stat.Accuracy];
- else if (gem.GetTotal(Stat.PoisonAttack) > 0)
- return item.AddedStats[Stat.PoisonAttack];
- else if (gem.GetTotal(Stat.Freezing) > 0)
- return item.AddedStats[Stat.Freezing];
- else if (gem.GetTotal(Stat.MagicResist) > 0)
- return item.AddedStats[Stat.MagicResist];
- else if (gem.GetTotal(Stat.PoisonResist) > 0)
- return item.AddedStats[Stat.PoisonResist];
- else if (gem.GetTotal(Stat.Luck) > 0)
- return item.AddedStats[Stat.Luck];
- else if (gem.GetTotal(Stat.PoisonRecovery) > 0)
- return item.AddedStats[Stat.PoisonRecovery];
- else if (gem.GetTotal(Stat.HP) > 0)
- return item.AddedStats[Stat.HP];
- else if (gem.GetTotal(Stat.MP) > 0)
- return item.AddedStats[Stat.MP];
- else if (gem.GetTotal(Stat.HealthRecovery) > 0)
- return item.AddedStats[Stat.HealthRecovery];
- // Definitions are missing for these.
- /*
- else if ((gem.Info.HPrate) > 0)
- return item.h
- else if ((gem.Info.MPrate) > 0)
- return
- else if ((gem.Info.SpellRecovery) > 0)
- return
- else if ((gem.Info.Holy) > 0)
- return
- else if ((gem.Info.Strong + gem.Strong) > 0)
- return
- else if (gem.Info.HPrate > 0)
- return
- */
- return 0;
- }
- public bool CanGainItem(UserItem item)
- {
- if (FreeSpace(Info.Inventory) > 0)
- {
- return true;
- }
- if (item.Info.Type == ItemType.Amulet)
- {
- ushort count = item.Count;
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- UserItem bagItem = Info.Inventory[i];
- if (bagItem == null || bagItem.Info != item.Info) continue;
- if (bagItem.Count + count <= bagItem.Info.StackSize) return true;
- count -= (ushort)(bagItem.Info.StackSize - bagItem.Count);
- }
- return false;
- }
- if (item.Info.StackSize > 1)
- {
- ushort count = item.Count;
- for (int i = 0; i < Info.Inventory.Length; i++)
- {
- UserItem bagItem = Info.Inventory[i];
- if (bagItem.Info != item.Info) continue;
- if (bagItem.Count + count <= bagItem.Info.StackSize) return true;
- count -= (ushort)(bagItem.Info.StackSize - bagItem.Count);
- }
- }
- return false;
- }
- public bool CanGainItems(UserItem[] items)
- {
- int itemCount = items.Count(e => e != null);
- ushort stackOffset = 0;
- if (itemCount < 1) return true;
- for (int i = 0; i < items.Length; i++)
- {
- if (items[i] == null) continue;
- if (items[i].Info.StackSize > 1)
- {
- ushort count = items[i].Count;
- for (int u = 0; u < Info.Inventory.Length; u++)
- {
- UserItem bagItem = Info.Inventory[u];
- if (bagItem == null || bagItem.Info != items[i].Info) continue;
- if (bagItem.Count + count > bagItem.Info.StackSize) stackOffset++;
- break;
- }
- }
- }
- if (FreeSpace(Info.Inventory) < itemCount + stackOffset) return false;
- return true;
- }
- public bool CanEquipItem(UserItem item, int slot)
- {
- switch ((EquipmentSlot)slot)
- {
- case EquipmentSlot.Weapon:
- if (item.Info.Type != ItemType.Weapon)
- return false;
- break;
- case EquipmentSlot.Armour:
- if (item.Info.Type != ItemType.Armour)
- return false;
- break;
- case EquipmentSlot.Helmet:
- if (item.Info.Type != ItemType.Helmet)
- return false;
- break;
- case EquipmentSlot.Torch:
- if (item.Info.Type != ItemType.Torch)
- return false;
- break;
- case EquipmentSlot.Necklace:
- if (item.Info.Type != ItemType.Necklace)
- return false;
- break;
- case EquipmentSlot.BraceletL:
- if (item.Info.Type != ItemType.Bracelet)
- return false;
- break;
- case EquipmentSlot.BraceletR:
- if (item.Info.Type != ItemType.Bracelet && item.Info.Type != ItemType.Amulet)
- return false;
- break;
- case EquipmentSlot.RingL:
- case EquipmentSlot.RingR:
- if (item.Info.Type != ItemType.Ring)
- return false;
- break;
- case EquipmentSlot.Amulet:
- if (item.Info.Type != ItemType.Amulet)// || item.Info.Shape == 0
- return false;
- break;
- case EquipmentSlot.Boots:
- if (item.Info.Type != ItemType.Boots)
- return false;
- break;
- case EquipmentSlot.Belt:
- if (item.Info.Type != ItemType.Belt)
- return false;
- break;
- case EquipmentSlot.Stone:
- if (item.Info.Type != ItemType.Stone)
- return false;
- break;
- case EquipmentSlot.Mount:
- if (item.Info.Type != ItemType.Mount)
- return false;
- break;
- default:
- return false;
- }
- switch (Gender)
- {
- case MirGender.Male:
- if (!item.Info.RequiredGender.HasFlag(RequiredGender.Male))
- return false;
- break;
- case MirGender.Female:
- if (!item.Info.RequiredGender.HasFlag(RequiredGender.Female))
- return false;
- break;
- }
- switch (Class)
- {
- case MirClass.Warrior:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Warrior))
- return false;
- break;
- case MirClass.Wizard:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Wizard))
- return false;
- break;
- case MirClass.Taoist:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Taoist))
- return false;
- break;
- case MirClass.Assassin:
- if (!item.Info.RequiredClass.HasFlag(RequiredClass.Assassin))
- return false;
- break;
- }
- switch (item.Info.RequiredType)
- {
- case RequiredType.Level:
- if (Level < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxAC:
- if (Stats[Stat.MaxAC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxMAC:
- if (Stats[Stat.MaxMAC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxDC:
- if (Stats[Stat.MaxDC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxMC:
- if (Stats[Stat.MaxMC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxSC:
- if (Stats[Stat.MaxSC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MaxLevel:
- if (Level > item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MinAC:
- if (Stats[Stat.MinAC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MinMAC:
- if (Stats[Stat.MinMAC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MinDC:
- if (Stats[Stat.MinDC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MinMC:
- if (Stats[Stat.MinMC] < item.Info.RequiredAmount)
- return false;
- break;
- case RequiredType.MinSC:
- if (Stats[Stat.MinSC] < item.Info.RequiredAmount)
- return false;
- break;
- }
- if (item.Info.Type == ItemType.Weapon || item.Info.Type == ItemType.Torch)
- {
- if (item.Weight - (Info.Equipment[slot] != null ? Info.Equipment[slot].Weight : 0) + CurrentHandWeight > Stats[Stat.HandWeight])
- return false;
- }
- else
- if (item.Weight - (Info.Equipment[slot] != null ? Info.Equipment[slot].Weight : 0) + CurrentWearWeight > Stats[Stat.WearWeight])
- return false;
- if (RidingMount && item.Info.Type != ItemType.Torch)
- {
- return false;
- }
- return true;
- }
- public void GainItem(UserItem item)
- {
- //CheckItemInfo(item.Info);
- CheckItem(item);
- UserItem clonedItem = item.Clone();
- Enqueue(new S.GainedItem { Item = clonedItem }); //Cloned because we are probably going to change the amount.
- AddItem(item);
- RefreshBagWeight();
- }
- private void DamageDura()
- {
- if (!SpecialMode.HasFlag(SpecialItemMode.NoDuraLoss))
- for (int i = 0; i < Info.Equipment.Length; i++)
- if (i != (int)EquipmentSlot.Weapon)
- DamageItem(Info.Equipment[i], Envir.Random.Next(1) + 1);
- }
- public void DamageWeapon()
- {
- if (!SpecialMode.HasFlag(SpecialItemMode.NoDuraLoss))
- DamageItem(Info.Equipment[(int)EquipmentSlot.Weapon], Envir.Random.Next(4) + 1);
- }
- public void DamageItem(UserItem item, int amount, bool isChanged = false)
- {
- if (item == null || item.CurrentDura == 0 || item.Info.Type == ItemType.Amulet) return;
- if ((item.WeddingRing == Info.Married) && (Info.Equipment[(int)EquipmentSlot.RingL].UniqueID == item.UniqueID)) return;
- if (item.GetTotal(Stat.Strong) > 0) amount = Math.Max(1, amount - item.GetTotal(Stat.Strong));
- item.CurrentDura = (ushort)Math.Max(ushort.MinValue, item.CurrentDura - amount);
- item.DuraChanged = true;
- if (item.CurrentDura > 0 && isChanged != true) return;
- Enqueue(new S.DuraChanged { UniqueID = item.UniqueID, CurrentDura = item.CurrentDura });
- item.DuraChanged = false;
- RefreshStats();
- }
- public void RemoveObjects(MirDirection dir, int count)
- {
- switch (dir)
- {
- case MirDirection.Up:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.UpRight:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- //Left Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange - count; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.Right:
- //Left Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.DownRight:
- //Top Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- //Left Block
- for (int a = -Globals.DataRange + count; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.Down:
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.DownLeft:
- //Top Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- //Right Block
- for (int a = -Globals.DataRange + count; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.Left:
- for (int a = -Globals.DataRange; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- case MirDirection.UpLeft:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- //Right Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange - count; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Remove(this);
- }
- }
- }
- break;
- }
- }
- public void AddObjects(MirDirection dir, int count)
- {
- switch (dir)
- {
- case MirDirection.Up:
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.UpRight:
- //Top Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- //Right Block
- for (int a = -Globals.DataRange + count; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.Right:
- for (int a = -Globals.DataRange; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.DownRight:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- //Right Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange - count; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X + Globals.DataRange - b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.Down:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.DownLeft:
- //Bottom Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y + Globals.DataRange - a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- //Left Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange - count; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.Left:
- //Left Block
- for (int a = -Globals.DataRange; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- case MirDirection.UpLeft:
- //Top Block
- for (int a = 0; a < count; a++)
- {
- int y = CurrentLocation.Y - Globals.DataRange + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = -Globals.DataRange; b <= Globals.DataRange; b++)
- {
- int x = CurrentLocation.X + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- //Left Block
- for (int a = -Globals.DataRange + count; a <= Globals.DataRange; a++)
- {
- int y = CurrentLocation.Y + a;
- if (y < 0 || y >= CurrentMap.Height) continue;
- for (int b = 0; b < count; b++)
- {
- int x = CurrentLocation.X - Globals.DataRange + b;
- if (x < 0 || x >= CurrentMap.Width) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (!cell.Valid || cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- ob.Add(this);
- }
- }
- }
- break;
- }
- }
- public override void Remove(HumanObject player)
- {
- if (player == this) return;
- base.Remove(player);
- Enqueue(new S.ObjectRemove { ObjectID = player.ObjectID });
- }
- public override void Add(HumanObject player)
- {
- if (player == this) return;
- //base.Add(player);
- Enqueue(player.GetInfoEx(this));
- player.Enqueue(GetInfoEx(player));
- player.SendHealth(this);
- SendHealth(player);
- }
- public override void Remove(MonsterObject monster)
- {
- Enqueue(new S.ObjectRemove { ObjectID = monster.ObjectID });
- }
- public override void Add(MonsterObject monster)
- {
- Enqueue(monster.GetInfo());
- monster.SendHealth(this);
- }
- public override void SendHealth(HumanObject player)
- {
- if (!player.IsMember(this) && Envir.Time > RevTime) return;
- byte time = Math.Min(byte.MaxValue, (byte)Math.Max(5, (RevTime - Envir.Time) / 1000));
- player.Enqueue(new S.ObjectHealth { ObjectID = ObjectID, Percent = PercentHealth, Expire = time });
- }
- protected virtual void CleanUp()
- {
- Connection.Player = null;
- Info.Player = null;
- Info.Mount = null;
- Connection.CleanObservers();
- Connection = null;
- Info = null;
- }
- public virtual void Enqueue(Packet p)
- {
- if (Connection == null) return;
- Connection.Enqueue(p);
- //if (p != null) MessageQueue.EnqueueDebugging(((ServerPacketIds)p.Index).ToString());
- }
- public virtual void Enqueue(Packet p, MirConnection c)
- {
- if (c == null)
- {
- Enqueue(p);
- return;
- }
- c.Enqueue(p);
- }
- public void SpellToggle(Spell spell, SpellToggleState state)
- {
- if (Dead) return;
- UserMagic magic;
- bool use = Convert.ToBoolean(state);
- magic = GetMagic(spell);
- if (magic == null) return;
- int cost;
- switch (spell)
- {
- case Spell.Thrusting:
- Info.Thrusting = state == SpellToggleState.None ? !Info.Thrusting : use;
- break;
- case Spell.HalfMoon:
- Info.HalfMoon = state == SpellToggleState.None ? !Info.HalfMoon : use;
- break;
- case Spell.CrossHalfMoon:
- Info.CrossHalfMoon = state == SpellToggleState.None ? !Info.CrossHalfMoon : use;
- break;
- case Spell.DoubleSlash:
- Info.DoubleSlash = state == SpellToggleState.None ? !Info.DoubleSlash : use;
- break;
- case Spell.TwinDrakeBlade:
- if (TwinDrakeBlade) return;
- magic = GetMagic(spell);
- if (magic == null) return;
- cost = magic.Info.BaseCost + magic.Level * magic.Info.LevelCost;
- if (cost >= MP) return;
- TwinDrakeBlade = true;
- ChangeMP(-cost);
- Enqueue(new S.ObjectMagic { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation, Spell = spell });
- break;
- case Spell.FlamingSword:
- if (FlamingSword || Envir.Time < FlamingSwordTime) return;
- magic = GetMagic(spell);
- if (magic == null) return;
- cost = magic.Info.BaseCost + magic.Level * magic.Info.LevelCost;
- if (cost >= MP) return;
- FlamingSword = true;
- FlamingSwordTime = Envir.Time + 10000;
- Enqueue(new S.SpellToggle { ObjectID = ObjectID, Spell = Spell.FlamingSword, CanUse = true });
- ChangeMP(-cost);
- break;
- case Spell.CounterAttack:
- if (CounterAttack || Envir.Time < CounterAttackTime) return;
- magic = GetMagic(spell);
- if (magic == null) return;
- cost = magic.Info.BaseCost + magic.Level * magic.Info.LevelCost;
- if (cost >= MP) return;
- CounterAttack = true;
- CounterAttackTime = Envir.Time + (Settings.Second * 7);
- var stats = new Stats
- {
- [Stat.MinAC] = 11 + magic.Level * 3,
- [Stat.MinMAC] = 11 + magic.Level * 3,
- [Stat.MaxAC] = 11 + magic.Level * 3,
- [Stat.MaxMAC] = 11 + magic.Level * 3,
- };
- AddBuff(BuffType.CounterAttack, this, Settings.Second * 7, stats);
- ChangeMP(-cost);
- break;
- case Spell.MentalState:
- Info.MentalState = (byte)((Info.MentalState + 1) % 3);
- ShowMentalState();
- break;
- }
- }
- private void ShowMentalState()
- {
- switch (Info.MentalState)
- {
- case 0:
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.MentalstateAgressive), ChatType.Hint);
- break;
- case 1:
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.MentalstateTrickShot), ChatType.Hint);
- break;
- case 2:
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.MentalstateGroupMode), ChatType.Hint);
- break;
- }
- AddBuff(BuffType.MentalState, this, 0, new Stats(), false, values: Info.MentalState);
- }
- #region Mounts
- public void RefreshMount(bool refreshStats = true)
- {
- if (RidingMount)
- {
- if (Mount.MountType < 0)
- {
- RidingMount = false;
- }
- else if (!Mount.CanRide)
- {
- RidingMount = false;
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouMustHaveSaddle), ChatType.System);
- }
- else if (!Mount.CanMapRide)
- {
- RidingMount = false;
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotRideOnMap), ChatType.System);
- }
- else if (!Mount.CanDungeonRide)
- {
- RidingMount = false;
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCannotRideWithoutBridle), ChatType.System);
- }
- }
- else
- {
- RidingMount = false;
- }
- if (refreshStats)
- RefreshStats();
- Broadcast(GetMountInfo());
- Enqueue(GetMountInfo());
- }
- public void IncreaseMountLoyalty(int amount)
- {
- UserItem item = Info.Equipment[(int)EquipmentSlot.Mount];
- if (item != null && item.CurrentDura < item.MaxDura)
- {
- item.CurrentDura = (ushort)Math.Min(item.MaxDura, item.CurrentDura + amount);
- item.DuraChanged = false;
- Enqueue(new S.ItemRepaired { UniqueID = item.UniqueID, MaxDura = item.MaxDura, CurrentDura = item.CurrentDura });
- }
- }
- public void DecreaseMountLoyalty(int amount)
- {
- if (Envir.Time > DecreaseLoyaltyTime)
- {
- DecreaseLoyaltyTime = Envir.Time + (Mount.SlowLoyalty ? (LoyaltyDelay * 2) : LoyaltyDelay);
- UserItem item = Info.Equipment[(int)EquipmentSlot.Mount];
- if (item != null && item.CurrentDura > 0)
- {
- DamageItem(item, amount);
- if (item.CurrentDura == 0)
- {
- RefreshMount();
- }
- }
- }
- }
- public void ToggleRide()
- {
- if (Mount.MountType > -1)
- {
- RidingMount = !RidingMount;
- RefreshMount();
- }
- else
- ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouDoNotHaveMountEquiped), ChatType.System);
- }
- #endregion
- }
- }
|