| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870 |
- using System;
- using System.Drawing;
- using Server.MirDatabase;
- using Server.MirEnvir;
- using Server.MirObjects.Monsters;
- using System.Diagnostics.Eventing.Reader;
- using Shared;
- using S = ServerPackets;
- namespace Server.MirObjects
- {
- public class MonsterObject : MapObject
- {
- public static MonsterObject GetMonster(MonsterInfo info)
- {
- if (info == null) return null;
- switch (info.AI)
- {
- case 1:
- case 2:
- return new Deer(info);
- case 3:
- return new Tree(info);
- case 4:
- //Common AI: 1 Line Attack with Poison
- return new SpittingSpider(info);
- case 5:
- return new CannibalPlant(info);
- case 6:
- return new Guard(info);
- case 7:
- return new CaveMaggot(info);
- case 8:
- //Common AI: 1 Range Projectile Attack with Fear
- return new AxeSkeleton(info);
- case 9:
- return new HarvestMonster(info);
- case 10:
- //Common AI: 1 Magic Attack
- return new FlamingWooma(info);
- case 11:
- return new WoomaTaurus(info);
- case 12:
- return new BugBagMaggot(info);
- case 13:
- return new RedMoonEvil(info);
- case 14:
- return new EvilCentipede(info);
- case 15:
- return new ZumaMonster(info);
- case 16:
- return new RedThunderZuma(info);
- case 17:
- return new ZumaTaurus(info);
- case 18:
- return new Shinsu(info);
- case 19:
- return new KingScorpion(info);
- case 20:
- return new DarkDevil(info);
- case 21:
- return new IncarnatedGhoul(info);
- case 22:
- return new IncarnatedZT(info);
- case 23:
- return new BoneFamiliar(info);
- case 24:
- return new DigOutZombie(info);
- case 25:
- return new RevivingZombie(info);
- case 26:
- return new ShamanZombie(info);
- case 27:
- return new Khazard(info);
- case 28:
- return new ToxicGhoul(info);
- case 29:
- //Common AI: 1 Line Attack
- return new BoneSpearman(info);
- case 30:
- return new BoneLord(info);
- case 31:
- //Common AI: 2 Magic Attacks, 1 Close, 1 Range
- return new RightGuard(info);
- case 32:
- //Common AI: 2 Magic Attacks, 1 Close, 1 Range Projectile
- return new LeftGuard(info);
- case 33:
- return new MinotaurKing(info);
- case 34:
- return new FrostTiger(info); //Effect 0/1
- case 35:
- //Common AI: 1 Line Attack
- return new SandWorm(info);
- case 36:
- return new Yimoogi(info);
- case 37:
- return new CrystalSpider(info);
- case 38:
- return new HolyDeva(info);
- case 39:
- return new RootSpider(info);
- case 40:
- return new BombSpider(info);
- case 41:
- case 42:
- return new YinDevilNode(info);
- case 43:
- return new OmaKing(info);
- case 44:
- //Common AI: 2 Attacks, 1 Close, 1 Line Attack
- return new BlackFoxman(info);
- case 45:
- return new RedFoxman(info);
- case 46:
- return new WhiteFoxman(info);
- case 47:
- return new TrapRock(info);
- case 48:
- return new GuardianRock(info);
- case 49:
- return new ThunderElement(info);
- case 50:
- return new GreatFoxSpirit(info);
- case 51:
- //Common AI: 2 Physical Attacks, 1 Close, 1 Range
- return new HedgeKekTal(info);
- case 52:
- return new EvilMir(info);
- case 53:
- return new EvilMirBody(info);
- case 54:
- return new DragonStatue(info);
- case 55:
- return new HumanWizard(info);
- case 56:
- return new Trainer(info);
- case 57:
- return new TownArcher(info);
- case 58:
- return new Guard(info);
- case 59:
- return new HumanAssassin(info);
- case 60:
- return new VampireSpider(info); //TODO - Clean up
- case 61:
- return new SpittingToad(info);
- case 62:
- return new SnakeTotem(info);
- case 63:
- return new CharmedSnake(info);
- case 64:
- return new IntelligentCreatureObject(info);
- case 65:
- //Common AI: 2 Close attacks with WeakerTeleport
- return new MutatedManworm(info);
- case 66:
- //Common AI: 2 Close Attacks
- return new CrazyManworm(info);
- case 67:
- return new DarkDevourer(info);
- case 68:
- return new Football(info);
- case 69:
- return new PoisonHugger(info);
- case 70:
- return new Hugger(info);
- case 71:
- return new Behemoth(info);
- case 72:
- return new FinialTurtle(info);
- case 73:
- return new TurtleKing(info);
- case 74:
- return new LightTurtle(info);
- case 75:
- return new WitchDoctor(info);
- case 76:
- //Common AI: 2 Close Attacks, 1 Normal, 1 Halfmoon
- return new HellSlasher(info);
- case 77:
- //Common AI: 2 Close Attacks, 1 Normal, 1 Fullmoon
- return new HellPirate(info);
- case 78:
- return new HellCannibal(info);
- case 79:
- return new HellKeeper(info);
- case 80:
- return new ConquestArcher(info);
- case 81:
- return new Gate(info);
- case 82:
- return new Wall(info);
- case 83:
- return new Tornado(info);
- case 84:
- return new WingedTigerLord(info);
- case 85:
- return new FlamingMutant(info);
- case 86:
- return new ManectricClaw(info);
- case 87:
- return new ManectricBlest(info);
- case 88:
- return new ManectricKing(info);
- case 89:
- return new IcePillar(info);
- case 90:
- return new TrollBomber(info);
- case 91:
- return new TrollKing(info);
- case 92:
- //Common AI: 2 Attacks with Fear, 1 Normal, 1 Long Line
- return new FlameSpear(info);
- case 93:
- //Common AI: 2 Magic Attacks with Fear, 1 Close, 1 Range AOE
- return new FlameMage(info);
- case 94:
- //Common AI: 2 Magic Attacks with Fear, 1 Close, 1 Close AOE
- return new FlameScythe(info);
- case 95:
- return new FlameAssassin(info);
- case 96:
- return new FlameQueen(info);
- case 97:
- return new HellKnight(info);
- case 98:
- return new HellLord(info);
- case 99:
- return new HellBomb(info);
- case 100:
- //Common AI: 1 Magic Line Attack with Poison
- return new VenomSpider(info);
- case 101:
- return new AncientBringer(info);
- case 102:
- return new IceGuard(info);
- case 103:
- return new ElementGuard(info);
- case 104:
- return new DemonGuard(info);
- case 105:
- return new KingGuard(info);
- case 106:
- return new DeathCrawler(info);
- case 107:
- //Common AI: 2 Magic Attacks with Rush, 1 Close, 1 Range
- return new BurningZombie(info);
- case 108:
- return new MudZombie(info);
- case 109:
- return new HardenRhino(info);
- case 110:
- return new DemonWolf(info); //Effect 0/1
- case 111:
- return new WhiteMammoth(info);
- case 112:
- //Common AI: 2 Close attacks
- return new DarkBeast(info); //Effect 0/1
- case 113:
- return new ArcherGuard(info);
- case 114:
- //Common AI: 1 Close attack with WeakerTeleport
- return new Mandrill(info);
- case 115:
- return new SandSnail(info);
- case 116:
- return new BlackHammerCat(info);
- case 117:
- return new StrayCat(info);
- case 118:
- return new CatShaman(info);
- case 119:
- return new Jar1(info);
- case 120:
- return new Jar2(info);
- case 121:
- return new SeedingsGeneral(info);
- case 122:
- return new RestlessJar(info);
- case 123:
- return new GeneralMeowMeow(info);
- case 124:
- return new Armadillo(info);
- case 125:
- return new ArmadilloElder(info);
- case 126:
- return new TucsonMage(info);
- case 127:
- return new TucsonWarrior(info);
- case 128:
- return new TucsonEgg(info); //Effect 0/1
- case 129:
- return new SwampWarrior(info);
- case 130:
- return new CannibalTentacles(info);
- case 131:
- return new TucsonGeneral(info);
- case 132:
- return new GasToad(info);
- case 133:
- return new Mantis(info);
- case 134:
- return new AssassinBird(info);
- case 135:
- return new StoningStatue(info);
- case 136:
- return new FlyingStatue(info);
- case 137:
- return new RhinoPriest(info);
- case 138:
- return new ElephantMan(info);
- case 139:
- return new StoneGolem(info);
- case 140:
- return new EarthGolem(info);
- case 141:
- return new TreeGuardian(info);
- case 142:
- return new TreeQueen(info);
- case 143:
- return new PeacockSpider(info);
- case 144:
- return new OmaCannibal(info);
- case 145:
- //Common AI: 2 Attacks, 1 Close, 1 Close AOE
- return new OmaBlest(info);
- case 146:
- //Common AI: 1 Halfmoon Attack
- return new OmaSlasher(info);
- case 147:
- return new OmaMage(info);
- case 148:
- return new OmaWitchDoctor(info);
- case 149:
- return new PowerBead(info); //Effect 0/1/2
- case 150:
- return new DarkOmaKing(info);
- case 151:
- return new CaveStatue(info);
- case 152:
- return new PlagueCrab(info);
- case 153:
- return new CreeperPlant(info);
- case 154:
- return new Nadz(info);
- case 155:
- return new AvengingSpirit(info);
- case 156:
- return new AvengingWarrior(info);
- case 157:
- return new AxePlant(info);
- case 158:
- //Common AI: None With Attack On Death
- return new WoodBox(info);
- case 159:
- return new DarkCaptain(info);
- case 160:
- //Common AI: 1 Range Attack with Fear
- return new BlueSoul(info);
- case 161:
- return new SackWarrior(info);
- case 162:
- return new KingHydrax(info);
- case 163:
- return new HornedMage(info);
- case 164:
- return new HornedArcher(info); //Effect 0/1
- case 165:
- return new HornedWarrior(info);
- case 166:
- return new FloatingRock(info);
- case 167:
- return new ScalyBeast(info);
- case 168:
- return new WereTiger(info);
- case 169:
- return new HornedSorceror(info);
- case 170:
- return new BoulderSpirit(info);
- case 171:
- return new HornedCommander(info);
- //case 172: MoonSunLightningStone
- case 173:
- return new TurtleGrass(info);
- case 174:
- return new ManTree(info);
- case 175:
- return new ChieftainArcher(info);
- //case 176: ChieftainSword
- case 177:
- return new FrozenKnight(info);
- case 178:
- return new IcePhantom(info); //TODO
- case 179:
- return new SnowWolf(info);
- case 180:
- return new SnowWolfKing(info);
- case 181:
- return new WaterDragon(info);
- case 182:
- return new BlackTortoise(info);
- //case 183: Manticore
- case 184:
- return new DragonWarrior(info); //TODO
- //case 185: DragonArcher
- case 186:
- return new Kirin(info);
- case 187:
- return new FrozenMiner(info);
- case 188:
- return new FrozenAxeman(info);
- case 189:
- return new FrozenMagician(info);
- case 190:
- return new SnowYeti(info);
- case 191:
- return new IceCrystalSoldier(info);
- case 192:
- return new DarkWraith(info);
- //case 193: CrystalBeast
- //case 194: RedOrb
- //case 195: FatalLotus
- case 196:
- return new AntCommander(info);
- // Sanjian
- case 197:
- return new GlacierSnail(info);
- case 198:
- return new FurbolgWarrior(info);
- case 199:
- return new FurbolgArcher(info);
- case 200:
- return new FurbolgCommander(info);
- case 201:
- return new FurbolgGuard(info);
- case 202:
- return new GlacierBeast(info);
- case 203:
- return new GlacierWarrior(info);
- case 210:
- return new HoodedSummonerScrolls(info);
- case 211:
- return new HoodedSummoner(info);
- case 212:
- return new PurpleFaeFlower(info);
- case 213:
- return new Siege(info); //TODO
- case 214:
- return new SepWarrior(info); //TODO
- case 215:
- return new SepWizard(info); //TODO
- case 216:
- return new SepTaoist(info); //TODO
- case 217:
- return new SepAssassin(info); //TODO
- case 218:
- return new SepArcher(info); //TODO
- case 219:
- return new SepHighWarrior(info); //TODO
- case 220:
- return new SepHighWizard(info); //TODO
- case 221:
- return new SepHighTaoist(info); //TODO
- case 222:
- return new SepHighAssassin(info); //TODO
- case 223:
- return new SepHighArcher(info); //TODO
- case 255://Skill
- return new StoneTrap(info);
- default:
- return new MonsterObject(info);
- }
- }
- public override ObjectType Race
- {
- get { return ObjectType.Monster; }
- }
-
- public virtual bool IgnoresNoPetRestriction => false;
-
- public MonsterInfo Info;
- public MapRespawn Respawn;
- public MonsterType MonsterType { get; private set; } = MonsterType.Normal;
- private long NextRecallTime;
- public override string Name
- {
- get
- {
- return Master == null ? Info.GameName : string.Format("{0}({1})", Info.GameName, Master.Name);
- }
- set { throw new NotSupportedException(); }
- }
- public override int CurrentMapIndex { get; set; }
- public override Point CurrentLocation { get; set; }
- public override sealed MirDirection Direction { get; set; }
- public override ushort Level
- {
- get { return Info.Level; }
- set { throw new NotSupportedException(); }
- }
- public override sealed AttackMode AMode
- {
- get
- {
- return base.AMode;
- }
- set
- {
- base.AMode = value;
- }
- }
- public override sealed PetMode PMode
- {
- get
- {
- return base.PMode;
- }
- set
- {
- base.PMode = value;
- }
- }
- public override int Health
- {
- get { return HP; }
- }
- public override int MaxHealth
- {
- get { return Stats[Stat.HP]; }
- }
- public int HealthPercent
- {
- get
- {
- return (Health * 100) / MaxHealth;
- }
- }
- public int HP;
- public ushort MoveSpeed;
- public virtual uint Experience
- {
- get
- {
- MonsterRarityProfile profile = GetRarityProfile();
- double scaled = Info.Experience * profile.ExpMultiplier;
- if (scaled <= 0) return 0;
- if (scaled > uint.MaxValue) return uint.MaxValue;
- return (uint)Math.Round(scaled);
- }
- }
- public int DeadDelay
- {
- get
- {
- switch (Info.AI)
- {
- case 64:
- return 0;
- case 81:
- case 82:
- return int.MaxValue;
- case 252:
- return 5000;
- default:
- return 180000;
- }
- }
- }
- public const int RegenDelay = 10000, EXPOwnerDelay = 5000, AloneDelay = 3000, SearchDelay = 3000, RoamDelay = 1000, HealDelay = 600, RevivalDelay = 2000;
- public long ActionTime, MoveTime, AttackTime, RegenTime, DeadTime, AloneTime, SearchTime, RoamTime, HealTime;
- public long ShockTime, RageTime, HallucinationTime;
- public bool BindingShotCenter, PoisonStopRegen = true;
- protected bool Alone = false, Stacking = false;
- public byte PetLevel;
- public uint PetExperience;
- public byte MaxPetLevel;
- public long TameTime;
- public bool DieNextTurn;
- public int RoutePoint;
- public bool Waiting;
- public bool GMMade;
- public bool Frozen;
- public List<MonsterObject> SlaveList = new List<MonsterObject>();
- public List<RouteInfo> Route = new List<RouteInfo>();
- public override bool Blocking
- {
- get
- {
- return !Dead;
- }
- }
- protected virtual bool CanRegen
- {
- get { return Envir.Time >= RegenTime; }
- }
- protected virtual bool CanMove
- {
- get
- {
- return
- !Dead &&
- Envir.Time > MoveTime &&
- Envir.Time > ActionTime &&
- Envir.Time > ShockTime &&
- (Master == null || Master.PMode == PetMode.MoveOnly || Master.PMode == PetMode.Both || Master.PMode == PetMode.FocusMasterTarget) &&
- !CurrentPoison.HasFlag(PoisonType.Paralysis) &&
- !CurrentPoison.HasFlag(PoisonType.LRParalysis) &&
- !CurrentPoison.HasFlag(PoisonType.Frozen) &&
- (!CurrentPoison.HasFlag(PoisonType.Stun) || (Info.Light == 10 || Info.Light == 5));
- }
- }
- protected virtual bool CanAttack
- {
- get
- {
- return
- !Dead &&
- Envir.Time > AttackTime &&
- Envir.Time > ActionTime &&
- (Master == null || Master.PMode == PetMode.AttackOnly || Master.PMode == PetMode.Both || Master.PMode == PetMode.FocusMasterTarget) &&
- !CurrentPoison.HasFlag(PoisonType.Paralysis) &&
- !CurrentPoison.HasFlag(PoisonType.LRParalysis) &&
- !CurrentPoison.HasFlag(PoisonType.Dazed) &&
- !CurrentPoison.HasFlag(PoisonType.Frozen) &&
- (!CurrentPoison.HasFlag(PoisonType.Stun) || (Info.Light == 10 || Info.Light == 5));
- }
- }
- protected internal MonsterObject(MonsterInfo info)
- {
- Info = info;
- Stats = new Stats();
- Undead = Info.Undead;
- AutoRev = info.AutoRev;
- CoolEye = info.CoolEye > Envir.Random.Next(100);
- Direction = (MirDirection)Envir.Random.Next(8);
- AMode = AttackMode.All;
- PMode = PetMode.Both;
- RegenTime = Envir.Random.Next(RegenDelay) + Envir.Time;
- SearchTime = Envir.Random.Next(SearchDelay) + Envir.Time;
- RoamTime = Envir.Random.Next(RoamDelay) + Envir.Time;
- }
- public void SetMonsterType(MonsterType type)
- {
- MonsterType = type;
- }
- public bool Spawn(Map temp, Point location)
- {
- if (!temp.ValidPoint(location)) return false;
- CurrentMap = temp;
- CurrentLocation = location;
- CurrentMap.AddObject(this);
- RefreshAll();
- SetHP(Stats[Stat.HP]);
- Spawned();
- Envir.MonsterCount++;
- CurrentMap.MonsterCount++;
- return true;
- }
- public bool Spawn(MapRespawn respawn)
- {
- Respawn = respawn;
- if (Respawn.Map == null) return false;
- if (Respawn.WalkableCells == null || Respawn.WalkableCells.Count == 0) return false;
- var spawnPoint = Respawn.WalkableCells[Envir.Random.Next(Respawn.WalkableCells.Count)];
- CurrentLocation = spawnPoint;
- respawn.Map.AddObject(this);
- CurrentMap = respawn.Map;
- if (Respawn.Route.Count > 0)
- Route.AddRange(Respawn.Route);
- RefreshAll();
- SetHP(Stats[Stat.HP]);
- Spawned();
- Respawn.Count++;
- respawn.Map.MonsterCount++;
- Envir.MonsterCount++;
- return true;
- }
- public override void Spawned()
- {
- ActionTime = Envir.Time + 2000;
- if (Info.HasSpawnScript && (Envir.MonsterNPC != null))
- {
- Envir.MonsterNPC.Call(this,string.Format("[@_SPAWN({0})]",Info.Index));
- }
- base.Spawned();
- }
- protected virtual void RefreshBase()
- {
- Stats.Clear();
- Stats.Add(Info.Stats);
- ApplyMonsterTypeBonuses();
- MoveSpeed = Info.MoveSpeed;
- AttackSpeed = Info.AttackSpeed;
- }
- protected virtual void ApplyMonsterTypeBonuses()
- {
- if (MonsterType == MonsterType.Normal) return;
- MonsterRarityProfile profile = GetRarityProfile();
- ScaleStat(Stat.HP, profile.HpMultiplier);
- ScaleStat(Stat.MinAC, profile.DefenseMultiplier);
- ScaleStat(Stat.MaxAC, profile.DefenseMultiplier);
- ScaleStat(Stat.MinMAC, profile.DefenseMultiplier);
- ScaleStat(Stat.MaxMAC, profile.DefenseMultiplier);
- ScaleStat(Stat.MinDC, profile.DamageMultiplier);
- ScaleStat(Stat.MaxDC, profile.DamageMultiplier);
- ScaleStat(Stat.MinMC, profile.DamageMultiplier);
- ScaleStat(Stat.MaxMC, profile.DamageMultiplier);
- ScaleStat(Stat.MinSC, profile.DamageMultiplier);
- ScaleStat(Stat.MaxSC, profile.DamageMultiplier);
- }
- protected void ScaleStat(Stat stat, double multiplier)
- {
- if (Math.Abs(multiplier - 1D) < double.Epsilon) return;
- int value = Stats[stat];
- if (value == 0) return;
- double scaled = value * multiplier;
- if (stat == Stat.HP)
- Stats[stat] = Math.Max(1, (int)Math.Round(scaled));
- else
- Stats[stat] = (int)Math.Round(scaled);
- }
- protected MonsterRarityProfile GetRarityProfile()
- {
- return MonsterRarityData.GetProfile(MonsterType);
- }
- protected uint ApplyGoldModifier(uint amount)
- {
- if (amount == 0) return 0;
- MonsterRarityProfile profile = GetRarityProfile();
- double scaled = amount * profile.GoldMultiplier;
- if (scaled <= 0) return 0;
- if (scaled > uint.MaxValue) return uint.MaxValue;
- return (uint)Math.Round(scaled);
- }
- public virtual void RefreshAll()
- {
- RefreshBase();
- Stats[Stat.HP] += PetLevel * 20;
- Stats[Stat.MinAC] += PetLevel * 2;
- Stats[Stat.MaxAC] += PetLevel * 2;
- Stats[Stat.MinMAC] += PetLevel * 2;
- Stats[Stat.MaxMAC] += PetLevel * 2;
- Stats[Stat.MinDC] += PetLevel;
- Stats[Stat.MaxDC] += PetLevel;
- if (Info.Name == Settings.SkeletonName || Info.Name == Settings.ShinsuName || Info.Name == Settings.AngelName)
- {
- MoveSpeed = (ushort)Math.Min(ushort.MaxValue, (Math.Max(ushort.MinValue, MoveSpeed - MaxPetLevel * 130)));
- AttackSpeed = (ushort)Math.Min(ushort.MaxValue, (Math.Max(ushort.MinValue, AttackSpeed - MaxPetLevel * 70)));
- }
- if (MoveSpeed < 400) MoveSpeed = 400;
- if (AttackSpeed < 400) AttackSpeed = 400;
- RefreshBuffs();
- }
- protected virtual void RefreshBuffs()
- {
- for (int i = 0; i < Buffs.Count; i++)
- {
- Buff buff = Buffs[i];
- Stats.Add(buff.Stats);
- switch (buff.Type)
- {
- case BuffType.SwiftFeet:
- MoveSpeed = (ushort)Math.Max(ushort.MinValue, MoveSpeed + 100);
- break;
- }
- }
- }
- public virtual void RefreshNameColour(bool send = true)
- {
- if (ShockTime < Envir.Time) BindingShotCenter = false;
- Color colour = Color.White;
- if (Master != null)
- {
- switch (PetLevel)
- {
- case 1:
- colour = Color.Aqua;
- break;
- case 2:
- colour = Color.Aquamarine;
- break;
- case 3:
- colour = Color.LightSeaGreen;
- break;
- case 4:
- colour = Color.SlateBlue;
- break;
- case 5:
- colour = Color.SteelBlue;
- break;
- case 6:
- colour = Color.Blue;
- break;
- case 7:
- colour = Color.Navy;
- break;
- }
- }
- else if (MonsterType != MonsterType.Normal)
- {
- colour = GetRarityProfile().NameColour;
- }
- if (Envir.Time < ShockTime)
- colour = Color.Peru;
- else if (Envir.Time < RageTime)
- colour = Color.Red;
- else if (Envir.Time < HallucinationTime)
- colour = Color.MediumOrchid;
- if (colour == NameColour || !send) return;
- NameColour = colour;
- Broadcast(new S.ObjectColourChanged { ObjectID = ObjectID, NameColour = NameColour });
- }
- public void SetHP(int amount)
- {
- if (HP == amount) return;
- HP = amount <= Stats[Stat.HP] ? amount : Stats[Stat.HP];
- if (!Dead && HP == 0) Die();
- // HealthChanged = true;
- BroadcastHealthChange();
- }
- public virtual void ChangeHP(int amount)
- {
- if (HP + amount > Stats[Stat.HP])
- amount = Stats[Stat.HP] - HP;
- if (amount == 0) return;
- HP += amount;
- if (HP < 0) HP = 0;
- if (!Dead && HP == 0) Die();
- // HealthChanged = true;
- BroadcastHealthChange();
- }
- //use this so you can have mobs take no/reduced poison damage
- public virtual void PoisonDamage(int amount, MapObject Attacker)
- {
- ChangeHP(amount);
- }
- public override bool Teleport(Map temp, Point location, bool effects = true, byte effectnumber = 0)
- {
- if (temp == null || !temp.ValidPoint(location)) return false;
- CurrentMap.RemoveObject(this);
- if (effects) Broadcast(new S.ObjectTeleportOut { ObjectID = ObjectID, Type = effectnumber });
- Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
- CurrentMap.MonsterCount--;
- CurrentMap = temp;
- CurrentLocation = location;
- CurrentMap.MonsterCount++;
- InTrapRock = false;
- CurrentMap.AddObject(this);
- BroadcastInfo();
- if (effects) Broadcast(new S.ObjectTeleportIn { ObjectID = ObjectID, Type = effectnumber });
- BroadcastHealthChange();
- return true;
- }
- public override void Die()
- {
- if (Dead) return;
- HP = 0;
- Dead = true;
- DeadTime = Envir.Time + DeadDelay;
- Broadcast(new S.ObjectDied { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- if (Info.HasDieScript && (Envir.MonsterNPC != null))
- {
- Envir.MonsterNPC.Call(this, string.Format("[@_DIE({0})]", Info.Index));
- }
- if (EXPOwner != null && EXPOwner.Node != null && Master == null && (EXPOwner.Race == ObjectType.Player || EXPOwner.Race == ObjectType.Hero))
- {
- EXPOwner.WinExp(Experience, Level);
- if (EXPOwner.Race != ObjectType.Hero)
- {
- PlayerObject playerObj = (PlayerObject)EXPOwner;
- playerObj.CheckGroupQuestKill(Info);
- }
- }
- if (Respawn != null)
- Respawn.Count--;
- if (Master == null && EXPOwner != null)
- Drop();
- Master = null;
- PoisonList.Clear();
- Envir.MonsterCount--;
- if (CurrentMap != null)
- CurrentMap.MonsterCount--;
- }
- public MapObject GetAttacker(MapObject attacker)
- {
- return attacker switch
- {
- HeroObject hero => hero.Owner,
- _ => attacker
- };
- }
- public void Revive(int hp, bool effect)
- {
- if (!Dead) return;
- SetHP(hp);
- Dead = false;
- ActionTime = Envir.Time + RevivalDelay;
- Broadcast(new S.ObjectRevived { ObjectID = ObjectID, Effect = effect });
- if (Respawn != null)
- Respawn.Count++;
- Envir.MonsterCount++;
- CurrentMap.MonsterCount++;
- }
- public override int Pushed(MapObject pusher, MirDirection dir, int distance)
- {
- if (!Info.CanPush) return 0;
- //if (!CanMove) return 0; //stops mobs that can't move (like cannibalplants) from being pushed
- int result = 0;
- MirDirection reverse = Functions.ReverseDirection(dir);
- for (int i = 0; i < distance; i++)
- {
- Point location = Functions.PointMove(CurrentLocation, dir, 1);
- if (!CurrentMap.ValidPoint(location)) return result;
- Cell 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);
- Broadcast(new S.ObjectPushed { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- result++;
- }
- ActionTime = Envir.Time + 300 * result;
- MoveTime = Envir.Time + 500 * result;
- if (result > 0)
- {
- Cell 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;
- }
- }
- return result;
- }
- protected virtual void Drop()
- {
- if (CurrentMap.Info.NoDropMonster)
- return;
- MonsterRarityProfile profile = GetRarityProfile();
- int ownerItemBonus = EXPOwner?.Stats[Stat.ItemDropRatePercent] ?? 0;
- int ownerGoldBonus = EXPOwner?.Stats[Stat.GoldDropRatePercent] ?? 0;
- int totalItemBonus = ownerItemBonus + profile.ItemDropBonusPercent;
- int totalGoldBonus = ownerGoldBonus + profile.GoldDropBonusPercent;
- for (int i = 0; i < Info.Drops.Count; i++)
- {
- DropInfo drop = Info.Drops[i];
- var reward = drop.AttemptDrop(totalItemBonus, totalGoldBonus);
- if (reward != null)
- {
- uint scaledGold = ApplyGoldModifier(reward.Gold);
- if (scaledGold > 0)
- {
- DropGold(scaledGold);
- }
- foreach (var dropItem in reward.Items)
- {
- UserItem item = Envir.CreateDropItem(dropItem);
- if (item == null) continue;
- if (GMMade)
- {
- item.GMMade = true;
- }
- if (EXPOwner != null && EXPOwner.Race == ObjectType.Player)
- {
- PlayerObject ob = (PlayerObject)EXPOwner;
- if (ob.CheckGroupQuestItem(item))
- {
- continue;
- }
- }
- if (drop.QuestRequired) continue;
- if (!DropItem(item)) return;
- }
- }
- }
- }
- protected virtual bool DropItem(UserItem item)
- {
- if (CurrentMap.Info.NoDropMonster)
- return false;
- ItemObject ob = new ItemObject(this, item)
- {
- Owner = EXPOwner,
- OwnerTime = Envir.Time + Settings.Minute,
- };
- if (!item.Info.GlobalDropNotify)
- return ob.Drop(Settings.DropRange);
- foreach (var player in Envir.Players)
- {
- player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerHasDroppedItem), Name, item.FriendlyName), ChatType.System2);
- }
- return ob.Drop(Settings.DropRange);
- }
- protected virtual bool DropGold(uint gold)
- {
- if (EXPOwner != null && EXPOwner.CanGainGold(gold) && !Settings.DropGold)
- {
- EXPOwner.WinGold(gold);
- return true;
- }
- uint count = gold / Settings.MaxDropGold == 0 ? 1 : gold / Settings.MaxDropGold + 1;
- for (int i = 0; i < count; i++)
- {
- ItemObject ob = new ItemObject(this, i != count - 1 ? Settings.MaxDropGold : gold % Settings.MaxDropGold)
- {
- Owner = EXPOwner,
- OwnerTime = Envir.Time + Settings.Minute,
- };
- ob.Drop(Settings.DropRange);
- }
- return true;
- }
- public override void Process()
- {
- base.Process();
- RefreshNameColour();
- if (Target != null && (Target.CurrentMap != CurrentMap || !Target.IsAttackTarget(this) || !Functions.InRange(CurrentLocation, Target.CurrentLocation, Globals.DataRange)))
- Target = null;
- for (int i = SlaveList.Count - 1; i >= 0; i--)
- if (SlaveList[i].Dead || SlaveList[i].Node == null)
- SlaveList.RemoveAt(i);
- if (Dead && Envir.Time >= DeadTime)
- {
- CurrentMap.RemoveObject(this);
- if (Master != null)
- {
- Master.Pets.Remove(this);
- Master = null;
- }
- Despawn();
- return;
- }
- if (Master != null && TameTime > 0 && Envir.Time >= TameTime)
- {
- Master.Pets.Remove(this);
- Master = null;
- Broadcast(new S.ObjectName { ObjectID = ObjectID, Name = Name });
- }
- ProcessAI();
- ProcessBuffs();
- ProcessRegen();
- ProcessPoison();
- }
- public override void SetOperateTime()
- {
- long time = Envir.Time + 2000;
- if (AloneTime < time && AloneTime > Envir.Time)
- time = AloneTime;
- if (DeadTime < time && DeadTime > Envir.Time)
- time = DeadTime;
- if (OwnerTime < time && OwnerTime > Envir.Time)
- time = OwnerTime;
- if (ExpireTime < time && ExpireTime > Envir.Time)
- time = ExpireTime;
- if (PKPointTime < time && PKPointTime > Envir.Time)
- time = PKPointTime;
- if (LastHitTime < time && LastHitTime > Envir.Time)
- time = LastHitTime;
- if (EXPOwnerTime < time && EXPOwnerTime > Envir.Time)
- time = EXPOwnerTime;
- if (SearchTime < time && SearchTime > Envir.Time)
- time = SearchTime;
- if (RoamTime < time && RoamTime > Envir.Time)
- time = RoamTime;
- if (ShockTime < time && ShockTime > Envir.Time)
- time = ShockTime;
- if (RegenTime < time && RegenTime > Envir.Time && Health < MaxHealth)
- time = RegenTime;
- if (RageTime < time && RageTime > Envir.Time)
- time = RageTime;
- if (HallucinationTime < time && HallucinationTime > Envir.Time)
- time = HallucinationTime;
- if (ActionTime < time && ActionTime > Envir.Time)
- time = ActionTime;
- if (MoveTime < time && MoveTime > Envir.Time)
- time = MoveTime;
- if (AttackTime < time && AttackTime > Envir.Time)
- time = AttackTime;
- if (HealTime < time && HealTime > Envir.Time && HealAmount > 0)
- time = HealTime;
- if (BrownTime < time && BrownTime > Envir.Time)
- time = BrownTime;
- for (int i = 0; i < ActionList.Count; i++)
- {
- if (ActionList[i].Time >= time && ActionList[i].Time > Envir.Time) continue;
- time = ActionList[i].Time;
- }
- for (int i = 0; i < PoisonList.Count; i++)
- {
- if (PoisonList[i].TickTime >= time && PoisonList[i].TickTime > Envir.Time) continue;
- time = PoisonList[i].TickTime;
- }
- for (int i = 0; i < Buffs.Count; i++)
- {
- if (Buffs[i].NextTime >= time && Buffs[i].NextTime > Envir.Time) continue;
- time = Buffs[i].NextTime;
- }
- if (OperateTime <= Envir.Time || time < OperateTime)
- OperateTime = time;
- }
- public override void Process(DelayedAction action)
- {
- switch (action.Type)
- {
- case DelayedType.Damage:
- CompleteAttack(action.Params);
- break;
- case DelayedType.RangeDamage:
- CompleteRangeAttack(action.Params);
- break;
- case DelayedType.Die:
- CompleteDeath(action.Params);
- break;
- case DelayedType.Recall:
- PetRecall();
- break;
- case DelayedType.SpellEffect:
- CompleteSpellEffect(action.Params);
- break;
- }
- }
- public void PetRecall()
- {
- if (Master == null || Master.CurrentMap == null) return;
- // Prevent pet from warping into NoPets maps (unless exempt e.g. pickup pets)
- if (Master.CurrentMap.Info.NoPets && !IgnoresNoPetRestriction)
- {
- Master.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CannotFollowIntoMapWaitHere, Name), ChatType.System);
- Frozen = true;
- Target = null;
- PMode = PetMode.None;
- Broadcast(new S.ObjectTurn
- {
- Direction = Direction,
- Location = CurrentLocation
- });
- return;
- }
- bool wasFrozen = Frozen;
- // Restore pet state
- Frozen = false;
- Target = null;
- PMode = PetMode.Both;
- // Only teleport if needed
- if (CurrentMap != Master.CurrentMap)
- {
- if (!Teleport(Master.CurrentMap, Master.Back))
- Teleport(Master.CurrentMap, Master.CurrentLocation);
- // Only show message if returning from frozen/waiting state
- if (wasFrozen)
- {
- Master.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HasReturnedToYourSide,Name), ChatType.System);
- }
- }
- }
- protected virtual void CompleteAttack(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- int damage = (int)data[1];
- DefenceType defence = (DefenceType)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- target.Attacked(this, damage, defence);
- }
- protected virtual void CompleteRangeAttack(IList<object> data)
- {
- MapObject target = (MapObject)data[0];
- int damage = (int)data[1];
- DefenceType defence = (DefenceType)data[2];
- if (target == null || !target.IsAttackTarget(this) || target.CurrentMap != CurrentMap || target.Node == null) return;
- target.Attacked(this, damage, defence);
- }
- protected virtual void CompleteDeath(IList<object> data)
- {
- throw new NotImplementedException();
- }
- protected virtual 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 virtual void ProcessRegen()
- {
- if (Dead) return;
- int healthRegen = 0;
- if (CanRegen)
- {
- RegenTime = Envir.Time + RegenDelay;
- if (HP < Stats[Stat.HP])
- healthRegen += (int)(Stats[Stat.HP] * 0.022F) + 1;
- }
- if (Envir.Time > HealTime)
- {
- HealTime = Envir.Time + HealDelay;
- if (HealAmount > 5)
- {
- healthRegen += 5;
- HealAmount -= 5;
- }
- else
- {
- healthRegen += HealAmount;
- HealAmount = 0;
- }
- }
- if (healthRegen > 0)
- {
- ChangeHP(healthRegen);
- BroadcastDamageIndicator(DamageType.Hit, healthRegen);
- }
- if (HP == Stats[Stat.HP]) HealAmount = 0;
- }
- protected virtual 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)
- {
- if (poison.PType == PoisonType.Slow)
- {
- MoveSpeed = Info.MoveSpeed;
- AttackSpeed = Info.AttackSpeed;
- AttackTime = Envir.Time + AttackSpeed;
- }
- PoisonList.RemoveAt(i);
- continue;
- }
- if (Envir.Time > poison.TickTime)
- {
- poison.Time++;
- poison.TickTime = Envir.Time + poison.TickSpeed;
- if (poison.Time >= poison.Duration)
- {
- if (poison.PType == PoisonType.Slow)
- {
- MoveSpeed = Info.MoveSpeed;
- AttackSpeed = Info.AttackSpeed;
- AttackTime = Envir.Time + AttackSpeed;
- }
- PoisonList.RemoveAt(i);
- continue;
- }
- if (poison.PType == PoisonType.Green || poison.PType == PoisonType.Bleeding)
- {
- if (EXPOwner == null || EXPOwner.Dead)
- {
- EXPOwner = poison.Owner;
- EXPOwnerTime = Envir.Time + EXPOwnerDelay;
- }
- else if (EXPOwner == poison.Owner)
- EXPOwnerTime = Envir.Time + EXPOwnerDelay;
- if (poison.PType == PoisonType.Bleeding)
- {
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Bleeding, EffectType = 0 });
- }
- //ChangeHP(-poison.Value);
- PoisonDamage(-poison.Value, poison.Owner);
- BroadcastDamageIndicator(DamageType.Hit, -poison.Value);
- if (PoisonStopRegen)
- RegenTime = Envir.Time + RegenDelay;
- if (poison.Owner != null && Target == null)
- Target = poison.Owner;
- }
- if (poison.PType == PoisonType.DelayedExplosion)
- {
- if (Envir.Time > ExplosionInflictedTime) ExplosionInflictedStage++;
- if (!ProcessDelayedExplosion(poison))
- {
- ExplosionInflictedStage = 0;
- ExplosionInflictedTime = 0;
- if (Dead) break; //temp to stop crashing
- PoisonList.RemoveAt(i);
- continue;
- }
- }
- }
- switch (poison.PType)
- {
- case PoisonType.Red:
- ArmourRate -= 0.5F;
- break;
- case PoisonType.Stun:
- DamageRate += 0.5F;
- break;
- case PoisonType.Blindness:
- break;
- case PoisonType.Slow:
- MoveSpeed = (ushort)Math.Min(3500, MoveSpeed + 100);
- AttackSpeed = (ushort)Math.Min(3500, AttackSpeed + 100);
- if (poison.Time >= poison.Duration)
- {
- MoveSpeed = Info.MoveSpeed;
- AttackSpeed = Info.AttackSpeed;
- //Reset the Attack time
- AttackTime = Envir.Time + AttackSpeed;
- }
- break;
- }
- type |= poison.PType;
- /*
- if ((int)type < (int)poison.PType)
- type = poison.PType;
- */
- }
- if (type == CurrentPoison) return;
- CurrentPoison = type;
- Broadcast(new S.ObjectPoisoned { ObjectID = ObjectID, Poison = type });
- }
- private bool ProcessDelayedExplosion(Poison poison)
- {
- if (Dead) return false;
- if (ExplosionInflictedStage == 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;
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion, EffectType = 1 });
- return true;
- }
- if (ExplosionInflictedStage == 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);
- //Attacked((PlayerObject)poison.Owner, poison.Value, DefenceType.MAC, false);
- 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 ProcessBuffs()
- {
- bool refresh = false;
- for (int i = Buffs.Count - 1; i >= 0; i--)
- {
- Buff buff = Buffs[i];
- 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);
- 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:
- 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;
- }
- ProcessBuffEnd(buff);
- refresh = true;
- }
- if (refresh) RefreshAll();
- }
- protected virtual void ProcessBuffEnd(Buff buff)
- {
- }
- protected virtual void ProcessAI()
- {
- if (Dead) return;
- if (DieNextTurn)
- {
- Die();
- return;
- }
- if (Master != null && Master.CurrentMap != null)
- {
- bool masterAllowsPets = !Master.CurrentMap.Info.NoPets || IgnoresNoPetRestriction;
- bool needsRecall = CurrentMap != Master.CurrentMap;
- // If frozen AND on different map AND master allows pets — force recall
- if (Frozen && needsRecall && masterAllowsPets)
- {
- PetRecall();
- return;
- }
- // If frozen but already on correct map — unfreeze
- if (Frozen && !needsRecall && masterAllowsPets)
- {
- Frozen = false;
- PMode = PetMode.Both;
- }
- }
- // Still frozen = do nothing
- if (Frozen) return;
- if (Master != null)
- {
- PetMode mode = Master.PMode;
- Map masterMap = Master.CurrentMap;
- Point masterLocation = Master.CurrentLocation;
- if ((mode == PetMode.Both || mode == PetMode.MoveOnly || mode == PetMode.FocusMasterTarget)
- && masterMap != null)
- {
- if (!Functions.InRange(CurrentLocation, masterLocation, Globals.DataRange) || CurrentMap != masterMap)
- PetRecall();
- }
- if (mode == PetMode.MoveOnly || mode == PetMode.None)
- {
- Target = null;
- }
- }
- CheckAlone();
- if (!Alone || Settings.MonsterProcessWhenAlone)
- {
- ProcessStacking();
- ProcessSearch();
- ProcessRoam();
- ProcessTarget();
- }
- }
- protected virtual void CheckAlone()
- {
- if (Envir.Time < AloneTime) return;
- AloneTime = Envir.Time + AloneDelay;
- if (Route.Count > 0)
- {
- Alone = false;
- return;
- }
- if (CurrentMap.Players.Count == 0)
- {
- Alone = true;
- return;
- }
- for (int i = 0; i < CurrentMap.Players.Count; i++)
- {
- if (Functions.InRange(CurrentLocation, CurrentMap.Players[i].CurrentLocation, Globals.DataRange * 2))
- {
- Alone = false;
- return;
- }
- }
- Alone = true;
- }
- protected virtual void ProcessStacking()
- {
- //Stacking or Infront of master - Move
- Stacking = CheckStacked();
- if (CanMove && ((Master != null && Master.Front == CurrentLocation) || Stacking))
- {
- //Walk Randomly
- if (!Walk(Direction))
- {
- MirDirection dir = Direction;
- switch (Envir.Random.Next(3)) // favour Clockwise
- {
- case 0:
- for (int i = 0; i < 7; i++)
- {
- dir = Functions.NextDir(dir);
- if (Walk(dir))
- break;
- }
- break;
- default:
- for (int i = 0; i < 7; i++)
- {
- dir = Functions.PreviousDir(dir);
- if (Walk(dir))
- break;
- }
- break;
- }
- }
- return;
- }
- }
- protected virtual void ProcessSearch()
- {
- if (Envir.Time < SearchTime) return;
- if (Master != null && (Master.PMode == PetMode.MoveOnly || Master.PMode == PetMode.None || Master.PMode == PetMode.FocusMasterTarget)) return;
- SearchTime = Envir.Time + SearchDelay;
- if (Target == null || Envir.Random.Next(3) == 0)
- FindTarget();
- }
- protected virtual void ProcessRoam()
- {
- if (Target != null || Envir.Time < RoamTime) return;
- if (ProcessRoute()) return;
- if (Master != null)
- {
- MoveTo(Master.Back);
- return;
- }
- RoamTime = Envir.Time + RoamDelay;
- if (Envir.Random.Next(10) != 0) return;
- switch (Envir.Random.Next(3)) //Face Walk
- {
- case 0:
- Turn((MirDirection)Envir.Random.Next(8));
- break;
- default:
- Walk(Direction);
- break;
- }
- }
- protected virtual void ProcessTarget()
- {
- if (Target == null || !CanAttack) return;
- if (InAttackRange())
- {
- Attack();
- if (Target != null && Target.Dead)
- {
- FindTarget();
- }
- return;
- }
- if (Envir.Time < ShockTime)
- {
- Target = null;
- return;
- }
- if (Settings.MonsterRecallEnabled && Info.CanRecall && TryRecallToTarget())
- {
- return;
- }
- MoveTo(Target.CurrentLocation);
- }
- protected virtual bool TryRecallToTarget()
- {
- if (Target == null || Target.CurrentMap == null) return false;
- int recallRange = Math.Max(1, Settings.MonsterRecallRange);
- int recallCooldown = Math.Max(0, Settings.MonsterRecallCooldown);
- if (Envir.Time < NextRecallTime) return false;
- bool needsRecall = Target.CurrentMap != CurrentMap ||
- !Functions.InRange(CurrentLocation, Target.CurrentLocation, recallRange);
- if (!needsRecall) return false;
- Point destination = FindRecallPoint(Target.CurrentMap, Target.CurrentLocation);
- if (!Target.CurrentMap.ValidPoint(destination)) return false;
- if (!Teleport(Target.CurrentMap, destination, true))
- return false;
- NextRecallTime = Envir.Time + recallCooldown;
- ActionTime = Envir.Time + 1000;
- return true;
- }
- protected virtual Point FindRecallPoint(Map map, Point targetLocation)
- {
- if (map.ValidPoint(targetLocation)) return targetLocation;
- for (int i = 0; i < 8; i++)
- {
- Point point = Functions.PointMove(targetLocation, (MirDirection)i, 1);
- if (map.ValidPoint(point))
- return point;
- }
- return targetLocation;
- }
- protected virtual bool InAttackRange()
- {
- if (Target.CurrentMap != CurrentMap) return false;
- return Target.CurrentLocation != CurrentLocation && Functions.InRange(CurrentLocation, Target.CurrentLocation, 1);
- }
- protected virtual void FindTarget()
- {
- Map Current = CurrentMap;
- for (int d = 0; d <= Info.ViewRange; d++)
- {
- for (int y = CurrentLocation.Y - d; y <= CurrentLocation.Y + d; y++)
- {
- if (y < 0) continue;
- if (y >= Current.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 >= Current.Width) break;
- Cell cell = Current.Cells[x, y];
- if (cell.Objects == null || !cell.Valid) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Hero:
- if (!ob.IsAttackTarget(this)) continue;
- if (ob.Hidden && (!CoolEye || Level < ob.Level)) continue;
- if (this is TrapRock && ob.InTrapRock) continue;
- if (ob.Race == ObjectType.Monster &&
- ob is StoneTrap)
- {
- if (Target is null ||
- (Target is not null &&
- Target is not StoneTrap))
- {
- Target = ob;
- }
-
- return;
- }
- else
- {
- Target ??= ob;
- }
- continue;
-
- case ObjectType.Player:
- if (Target != null)
- {
- continue;
- }
- PlayerObject playerob = (PlayerObject)ob;
- if (!ob.IsAttackTarget(this)) continue;
- if (playerob.GMGameMaster || ob.Hidden && (!CoolEye || Level < ob.Level) || Envir.Time < HallucinationTime) continue;
- Target = ob;
- if (Master != null)
- {
- for (int j = 0; j < playerob.Pets.Count; j++)
- {
- MonsterObject pet = playerob.Pets[j];
- if (!pet.IsAttackTarget(this)) continue;
- Target = pet;
- break;
- }
- }
- continue;
- default:
- continue;
- }
- }
- }
- }
- }
- }
- protected virtual bool ProcessRoute()
- {
- if (Route.Count < 1) return false;
- RoamTime = Envir.Time + 500;
- if (CurrentLocation == Route[RoutePoint].Location)
- {
- if (Route[RoutePoint].Delay > 0 && !Waiting)
- {
- Waiting = true;
- RoamTime = Envir.Time + RoamDelay + Route[RoutePoint].Delay;
- return true;
- }
- Waiting = false;
- RoutePoint++;
- }
- if (RoutePoint > Route.Count - 1) RoutePoint = 0;
- if (!CurrentMap.ValidPoint(Route[RoutePoint].Location)) return true;
- MoveTo(Route[RoutePoint].Location);
- return true;
- }
- protected virtual void MoveTo(Point location)
- {
- if (CurrentLocation == location) return;
- bool inRange = Functions.InRange(location, CurrentLocation, 1);
- if (inRange)
- {
- if (!CurrentMap.ValidPoint(location)) return;
- 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.Blocking) continue;
- return;
- }
- }
- MirDirection dir = Functions.DirectionFromPoint(CurrentLocation, location);
- if (Walk(dir)) return;
- switch (Envir.Random.Next(2)) //No favour
- {
- case 0:
- for (int i = 0; i < 7; i++)
- {
- dir = Functions.NextDir(dir);
- if (Walk(dir))
- return;
- }
- break;
- default:
- for (int i = 0; i < 7; i++)
- {
- dir = Functions.PreviousDir(dir);
- if (Walk(dir))
- return;
- }
- break;
- }
- }
- public virtual void Turn(MirDirection dir)
- {
- if (!CanMove) return;
- Direction = dir;
- InSafeZone = CurrentMap.GetSafeZone(CurrentLocation) != null;
- Cell 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;
- }
- Broadcast(new S.ObjectTurn { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- }
- public virtual bool Walk(MirDirection dir)
- {
- if (!CanMove) return false;
- Point location = Functions.PointMove(CurrentLocation, dir, 1);
- if (!CurrentMap.ValidPoint(location)) 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.Blocking || Race == ObjectType.Creature) continue;
- return false;
- }
- CurrentMap.GetCell(CurrentLocation).Remove(this);
- Direction = dir;
- RemoveObjects(dir, 1);
- CurrentLocation = location;
- CurrentMap.GetCell(CurrentLocation).Add(this);
- AddObjects(dir, 1);
- if (Hidden)
- {
- RemoveBuff(BuffType.Hiding);
- }
- CellTime = Envir.Time + 500;
- ActionTime = Envir.Time + 300;
- MoveTime = Envir.Time + MoveSpeed;
- if (MoveTime > AttackTime)
- AttackTime = MoveTime;
- InSafeZone = CurrentMap.GetSafeZone(CurrentLocation) != null;
- Broadcast(new S.ObjectWalk { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- 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;
- }
- return true;
- }
- protected virtual void Attack()
- {
- if (BindingShotCenter) ReleaseBindingShot();
- ShockTime = 0;
- if (!Target.IsAttackTarget(this))
- {
- Target = null;
- return;
- }
- Direction = Functions.DirectionFromPoint(CurrentLocation, Target.CurrentLocation);
- Broadcast(new S.ObjectAttack { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
- ActionTime = Envir.Time + 300;
- AttackTime = Envir.Time + AttackSpeed;
- int damage = GetAttackPower(Stats[Stat.MinDC], Stats[Stat.MaxDC]);
- if (damage == 0) return;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + 300, Target, damage, DefenceType.ACAgility);
- ActionList.Add(action);
- }
- public void ReleaseBindingShot()
- {
- if (!BindingShotCenter) return;
- ShockTime = 0;
- Broadcast(GetInfo());//update clients in range (remove effect)
- BindingShotCenter = false;
- //the centertarget is escaped so make all shocked mobs awake (3x3 from center)
- Point place = 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 == null || targetob.Node == null || targetob.Race != ObjectType.Monster) continue;
- if (((MonsterObject)targetob).ShockTime == 0) continue;
- //each centerTarget has its own effect which needs to be cleared when no longer shocked
- if (((MonsterObject)targetob).BindingShotCenter) ((MonsterObject)targetob).ReleaseBindingShot();
- else ((MonsterObject)targetob).ShockTime = 0;
- break;
- }
- }
- }
- }
- public bool FindNearby(int distance)
- {
- for (int d = 0; d <= distance; 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;
- if (!CurrentMap.ValidPoint(x, y)) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- if (!ob.IsAttackTarget(this)) continue;
- if (ob.Hidden && (!CoolEye || Level < ob.Level)) continue;
- if (ob.Race == ObjectType.Player)
- {
- PlayerObject player = ((PlayerObject)ob);
- if (player.GMGameMaster) continue;
- }
- return true;
- default:
- continue;
- }
- }
- }
- }
- }
- return false;
- }
- public bool FindFriendsNearby(int distance)
- {
- for (int d = 0; d <= distance; 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;
- if (!CurrentMap.ValidPoint(x, y)) continue;
- Cell cell = CurrentMap.GetCell(x, y);
- if (cell.Objects == null) continue;
- for (int i = 0; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- if (ob == this || ob.Dead) continue;
- if (ob.IsAttackTarget(this)) continue;
- if (ob.Race == ObjectType.Player)
- {
- PlayerObject player = ((PlayerObject)ob);
- if (player.GMGameMaster) continue;
- }
- return true;
- default:
- continue;
- }
- }
- }
- }
- }
- return false;
- }
- protected List<MapObject> FindAllFriends(int dist, Point location, bool needSight = true, bool ownAI = true)
- {
- List<MapObject> targets = new List<MapObject>();
- for (int d = 0; d <= dist; d++)
- {
- for (int y = location.Y - d; y <= location.Y + d; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = location.X - d; x <= location.X + d; x += Math.Abs(y - location.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; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- if (ob == this) continue;
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- if (ob.Dead) continue;
- if (!ownAI && ob.Race == ObjectType.Monster && ((MonsterObject)ob).Info.AI == Info.AI) continue;
- if (!ob.IsFriendlyTarget(this)) continue;
- if (ob.Master != Master) continue;
- if (ob.Hidden && (!CoolEye || Level < ob.Level) && needSight) continue;
- targets.Add(ob);
- continue;
- default:
- continue;
- }
- }
- }
- }
- }
- return targets;
- }
- public List<MapObject> FindAllNearby(int dist, Point location, bool needSight = true)
- {
- List<MapObject> targets = new List<MapObject>();
- for (int d = 0; d <= dist; d++)
- {
- for (int y = location.Y - d; y <= location.Y + d; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = location.X - d; x <= location.X + d; x += Math.Abs(y - location.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; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- targets.Add(ob);
- continue;
- default:
- continue;
- }
- }
- }
- }
- }
- return targets;
- }
- protected List<MapObject> FindAllTargets(int dist, Point location, bool needSight = true)
- {
- List<MapObject> targets = new List<MapObject>();
- for (int d = 0; d <= dist; d++)
- {
- for (int y = location.Y - d; y <= location.Y + d; y++)
- {
- if (y < 0) continue;
- if (y >= CurrentMap.Height) break;
- for (int x = location.X - d; x <= location.X + d; x += Math.Abs(y - location.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; i < cell.Objects.Count; i++)
- {
- MapObject ob = cell.Objects[i];
- switch (ob.Race)
- {
- case ObjectType.Monster:
- case ObjectType.Player:
- case ObjectType.Hero:
- if (!ob.IsAttackTarget(this)) continue;
- if (ob.Hidden && (!CoolEye || Level < ob.Level) && needSight) continue;
- if (ob.Race == ObjectType.Player)
- {
- PlayerObject player = ((PlayerObject)ob);
- if (player.GMGameMaster) continue;
- }
- targets.Add(ob);
- continue;
- default:
- continue;
- }
- }
- }
- }
- }
- return targets;
- }
- public override bool IsAttackTarget(HumanObject attacker)
- {
- if (attacker == null || attacker.Node == null) return false;
- if (Dead) return false;
- if (Master == null) return true;
- if (attacker.Race == ObjectType.Hero)
- attacker = ((HeroObject)attacker).Owner;
- if (attacker.AMode == AttackMode.Peace) return false;
- if (Master == attacker) return attacker.AMode == AttackMode.All;
- if (Master.Race == ObjectType.Player && (attacker.InSafeZone || InSafeZone)) return false;
- switch (attacker.AMode)
- {
- case AttackMode.Group:
- return Master.GroupMembers == null || !Master.GroupMembers.Contains(attacker);
- case AttackMode.Guild:
- {
- if (!(Master is PlayerObject)) return false;
- PlayerObject master = (PlayerObject)Master;
- return master.MyGuild == null || master.MyGuild != attacker.MyGuild;
- }
- case AttackMode.EnemyGuild:
- {
- if (!(Master is PlayerObject)) return false;
- PlayerObject master = (PlayerObject)Master;
- return (master.MyGuild != null && attacker.MyGuild != null) && master.MyGuild.IsEnemy(attacker.MyGuild);
- }
- case AttackMode.RedBrown:
- return Master.PKPoints >= 200 || Envir.Time < Master.BrownTime;
- default:
- return true;
- }
- }
- public override bool IsAttackTarget(MonsterObject attacker)
- {
- if (attacker == null || attacker.Node == null) return false;
- if (Dead || attacker == this) return false;
- if (attacker.Race == ObjectType.Creature) return false;
- if (attacker.Info.AI == 6 || attacker.Info.AI == 113) // Guard
- {
- if (Info.AI != 1 && Info.AI != 2 && Info.AI != 3 && (Master == null || Master.PKPoints >= 200)) //Not Dear/Hen/Tree/Pets or Red Master
- return true;
- }
- else if (attacker.Info.AI == 58) // Tao Guard - attacks Pets
- {
- if (Info.AI != 1 && Info.AI != 2 && Info.AI != 3 && (Master == null || Master.AMode != AttackMode.Peace)) //Not Dear/Hen/Tree or Peaceful Master
- return true;
- }
- else if (Master != null) //Pet Attacked
- {
- if (attacker.Master == null) //Wild Monster
- return true;
- //Pet Vs Pet
- if (Master == attacker.Master)
- return false;
- if (Envir.Time < ShockTime) //Shocked
- return false;
- if (Master.Race == ObjectType.Player && attacker.Master.Race == ObjectType.Player && (Master.InSafeZone || attacker.Master.InSafeZone)) return false;
- switch (attacker.Master.AMode)
- {
- case AttackMode.Group:
- if (Master.GroupMembers != null && Master.GroupMembers.Contains((PlayerObject)attacker.Master)) return false;
- break;
- case AttackMode.Guild:
- break;
- case AttackMode.EnemyGuild:
- break;
- case AttackMode.RedBrown:
- if (attacker.Master.PKPoints < 200 || Envir.Time > attacker.Master.BrownTime) return false;
- break;
- case AttackMode.Peace:
- return false;
- }
- for (int i = 0; i < Master.Pets.Count; i++)
- if (Master.Pets[i].EXPOwner == attacker.Master) return true;
- for (int i = 0; i < attacker.Master.Pets.Count; i++)
- {
- MonsterObject ob = attacker.Master.Pets[i];
- if (ob == Target || ob.Target == this) return true;
- }
- return Master.LastHitter == attacker.Master;
- }
- else if (attacker.Master != null) //Pet Attacking Wild Monster
- {
- if (Envir.Time < ShockTime) //Shocked
- return false;
- for (int i = 0; i < attacker.Master.Pets.Count; i++)
- {
- MonsterObject ob = attacker.Master.Pets[i];
- if (ob == Target || ob.Target == this) return true;
- }
- if (Target == attacker.Master)
- return true;
- }
- if (Envir.Time < attacker.HallucinationTime) return true;
- return Envir.Time < attacker.RageTime;
- }
- public override bool IsFriendlyTarget(HumanObject ally)
- {
- if (Master == null) return false;
- if (Master == ally) return true;
- switch (ally.AMode)
- {
- case AttackMode.Group:
- return Master.GroupMembers != null && Master.GroupMembers.Contains(ally);
- case AttackMode.Guild:
- return false;
- case AttackMode.EnemyGuild:
- return true;
- case AttackMode.RedBrown:
- return Master.PKPoints < 200 & Envir.Time > Master.BrownTime;
- }
- return true;
- }
- public override bool IsFriendlyTarget(MonsterObject ally)
- {
- if (Master != null) return false;
- if (ally.Race != ObjectType.Monster) return false;
- if (ally.Master != null) return false;
- return true;
- }
- public override int Attacked(HumanObject attacker, int damage, DefenceType type = DefenceType.ACAgility, bool damageWeapon = true)
- {
- if (Target == null && attacker.IsAttackTarget(this))
- {
- Target = attacker;
- }
- 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 (armour >= damage)
- {
- BroadcastDamageIndicator(DamageType.Miss);
- return 0;
- }
- if (Envir.Random.Next(100) < (attacker.Stats[Stat.CriticalRate] * Settings.CriticalRateWeight))
- {
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Critical });
- damage = Math.Min(int.MaxValue, damage + (int)Math.Floor(damage * (((double)attacker.Stats[Stat.CriticalDamage] / (double)Settings.CriticalDamageWeight) * 10)));
- BroadcastDamageIndicator(DamageType.Critical);
- }
- if (Target != this && attacker.IsAttackTarget(this))
- {
- if (attacker.Info.MentalState == 2)
- {
- if (Functions.MaxDistance(CurrentLocation, attacker.CurrentLocation) < (8 - attacker.Info.MentalStateLvl))
- Target = attacker;
- }
- else
- Target = attacker;
- }
- if (BindingShotCenter) ReleaseBindingShot();
- ShockTime = 0;
- for (int i = PoisonList.Count - 1; i >= 0; i--)
- {
- if (PoisonList[i].PType != PoisonType.LRParalysis) continue;
- PoisonList.RemoveAt(i);
- OperateTime = 0;
- }
- if (Master != null && Master != attacker && (Master.Race != ObjectType.Hero || Master.Race == ObjectType.Hero && attacker != ((HeroObject)Master).Owner))
- if (Envir.Time > Master.BrownTime && Master.PKPoints < 200)
- attacker.BrownTime = Envir.Time + Settings.Minute;
- if (EXPOwner == null || EXPOwner.Dead)
- {
- EXPOwner = GetAttacker(attacker);
- }
- if (EXPOwner == attacker)
- EXPOwnerTime = Envir.Time + EXPOwnerDelay;
- ushort levelOffset = (ushort)(Level > attacker.Level ? 0 : Math.Min(10, attacker.Level - Level));
- ApplyNegativeEffects(attacker, type, levelOffset);
- Broadcast(new S.ObjectStruck { ObjectID = ObjectID, AttackerID = attacker.ObjectID, Direction = Direction, Location = CurrentLocation });
- 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;
- }
- }
- attacker.GatherElement();
- if (attacker.Info.Mentor != 0 && attacker.Info.IsMentor)
- {
- if (attacker.HasBuff(BuffType.Mentor, out _))
- {
- CharacterInfo mentee = Envir.GetCharacterInfo(attacker.Info.Mentor);
- PlayerObject player = Envir.GetPlayer(mentee.Name);
- if (player != null && player.CurrentMap == attacker.CurrentMap && Functions.InRange(player.CurrentLocation, attacker.CurrentLocation, Globals.DataRange) && !player.Dead)
- {
- if (GroupMembers != null && GroupMembers.Contains(player))
- damage += (int)Math.Round((double)(damage * attacker.Stats[Stat.MentorDamageRatePercent]) / 100);
- }
- }
- }
- if (Master != null && Master != attacker && Master.Race == ObjectType.Player && Envir.Time > Master.BrownTime && Master.PKPoints < 200 && !((PlayerObject)Master).AtWar(attacker))
- {
- attacker.BrownTime = Envir.Time + Settings.Minute;
- }
- for (int i = 0; i < attacker.Pets.Count; i++)
- {
- MonsterObject ob = attacker.Pets[i];
- if (IsAttackTarget(ob) && (ob.Target == null)) ob.Target = this;
- }
- BroadcastDamageIndicator(DamageType.Hit, armour - damage);
- ChangeHP(armour - damage);
- return damage - armour;
- }
- public override int Attacked(MonsterObject attacker, int damage, DefenceType type = DefenceType.ACAgility)
- {
- if (Target == null && attacker.IsAttackTarget(this))
- Target = attacker;
- 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 (armour >= damage)
- {
- BroadcastDamageIndicator(DamageType.Miss);
- return 0;
- }
- if (Target != this && attacker.IsAttackTarget(this))
- Target = attacker;
- if (BindingShotCenter) ReleaseBindingShot();
- ShockTime = 0;
- for (int i = PoisonList.Count - 1; i >= 0; i--)
- {
- if (PoisonList[i].PType != PoisonType.LRParalysis) continue;
- PoisonList.RemoveAt(i);
- OperateTime = 0;
- }
- if (attacker.Info.AI == 6 || attacker.Info.AI == 58 || attacker.Info.AI == 113)
- EXPOwner = null;
- else if (attacker.Master != null)
- {
- if (attacker.CurrentMap != attacker.Master.CurrentMap || !Functions.InRange(attacker.CurrentLocation, attacker.Master.CurrentLocation, Globals.DataRange))
- EXPOwner = null;
- else
- {
- if (EXPOwner == null || EXPOwner.Dead)
- EXPOwner = attacker.Master switch
- {
- HeroObject hero => hero.Owner,
- _ => attacker.Master
- };
- if (EXPOwner == attacker.Master)
- EXPOwnerTime = Envir.Time + EXPOwnerDelay;
- }
- }
- 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 Struck(int damage, DefenceType type = DefenceType.ACAgility)
- {
- int armour = 0;
- 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))));
- if (armour >= damage) return 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 (p.Owner != null && p.Owner.IsAttackTarget(this) && Target == null)
- Target = p.Owner;
- if (Master != null && p.Owner != null && p.Owner.Race == ObjectType.Player && p.Owner != Master)
- {
- if (Envir.Time > Master.BrownTime && Master.PKPoints < 200)
- p.Owner.BrownTime = Envir.Time + Settings.Minute;
- }
- 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.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 (p.PType == PoisonType.DelayedExplosion) return;
- 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
- PoisonList[i] = p;
- return;
- }
- if (p.PType == PoisonType.DelayedExplosion)
- {
- ExplosionInflictedTime = Envir.Time + 4000;
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.DelayedExplosion });
- }
- else if (p.PType == PoisonType.Dazed)
- {
- Broadcast(new S.ObjectEffect { ObjectID = ObjectID, Effect = SpellEffect.Stunned, Time = (uint)(p.Duration * p.TickSpeed) });
- }
- else if (p.PType == PoisonType.Blindness)
- {
- var stats = new Stats
- {
- [Stat.Accuracy] = p.Value * -1
- };
- AddBuff(BuffType.Blindness, Caster, (int)(p.Duration * p.TickSpeed), stats);
- }
- 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);
- var packet = new S.AddBuff
- {
- Buff = b.ToClientBuff(),
- };
- if (b.Info.Visible) Broadcast(packet);
- if (refreshStats)
- {
- RefreshAll();
- }
- return b;
- }
- public override Packet GetInfo()
- {
- return new S.ObjectMonster
- {
- ObjectID = ObjectID,
- Name = Name,
- NameColour = NameColour,
- Location = CurrentLocation,
- Image = Info.Image,
- Direction = Direction,
- Effect = Info.Effect,
- AI = Info.AI,
- Light = Info.Light,
- Dead = Dead,
- Skeleton = Harvested,
- Poison = CurrentPoison,
- Hidden = Hidden,
- ShockTime = (ShockTime > 0 ? ShockTime - Envir.Time : 0),
- BindingShotCenter = BindingShotCenter,
- Buffs = Buffs.Where(d => d.Info.Visible).Select(e => e.Type).ToList(),
- MasterObjectId = Master?.ObjectID ?? 0,
- Rarity= MonsterType
- };
- }
- public override void ReceiveChat(string text, ChatType type)
- {
- throw new NotSupportedException();
- }
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- 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];
- if (ob.Race != ObjectType.Player) continue;
- ob.Add(this);
- }
- }
- }
- break;
- }
- }
- public override void Add(HumanObject player)
- {
- player.Enqueue(GetInfo());
- SendHealth(player);
- }
- public override void SendHealth(HumanObject player)
- {
- if (!player.IsMember(Master) && !(player.IsMember(EXPOwner) && AutoRev) && 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 });
- }
- public void PetExp(uint amount)
- {
- if (PetLevel >= MaxPetLevel) return;
- if (Info.Name == Settings.SkeletonName || Info.Name == Settings.ShinsuName || Info.Name == Settings.AngelName)
- amount *= 3;
- PetExperience += amount;
- if (PetExperience < (PetLevel + 1) * 20000) return;
- PetExperience = (uint)(PetExperience - ((PetLevel + 1) * 20000));
- PetLevel++;
- RefreshAll();
- OperateTime = 0;
- BroadcastHealthChange();
- }
- public override void Despawn()
- {
- SlaveList.Clear();
- base.Despawn();
- }
- // MONSTER AI ATTACKS \\\
- protected virtual void PoisonTarget(MapObject target, int chanceToPoison, long poisonDuration, PoisonType poison, long poisonTickSpeed = 1000, bool noResist = false, bool ignoreDefence = true)
- {
- int value = GetAttackPower(Stats[Stat.MinSC], Stats[Stat.MaxSC]);
- if (Envir.Random.Next(Settings.PoisonResistWeight) >= target.Stats[Stat.PoisonResist])
- {
- if (Envir.Random.Next(chanceToPoison) == 0)
- {
- target.ApplyPoison(new Poison { Owner = this, Duration = poisonDuration, PType = poison, Value = value, TickSpeed = poisonTickSpeed }, this, noResist, ignoreDefence);
- }
- }
- }
- protected virtual void TriangleAttack(int damage, int distance, int limitWidth = -1, int additionalDelay = 500, DefenceType defenceType = DefenceType.ACAgility, bool push = false)
- {
- List<Point> points = new List<Point>();
- for (int i = 1; i <= distance; i++)
- {
- Point target = Functions.PointMove(CurrentLocation, Direction, i);
- if (!CurrentMap.ValidPoint(target)) continue;
- points.Add(target);
- if (distance > 1)
- {
- Point left = target;
- Point right = target;
- var offset = i - 1;
- for (int l = 1; l <= offset; l++)
- {
- if (limitWidth > -1 && l > limitWidth) break;
- left = Functions.Left(left, Direction);
- if (!CurrentMap.ValidPoint(left)) continue;
- points.Add(left);
- }
- for (int r = 1; r <= offset; r++)
- {
- if (limitWidth > -1 && r > limitWidth) break;
- right = Functions.Right(right, Direction);
- if (!CurrentMap.ValidPoint(right)) continue;
- points.Add(right);
- }
- }
- }
- foreach (var point in points)
- {
- Cell cell = CurrentMap.GetCell(point);
- if (cell.Objects == null) continue;
- for (int o = 0; o < cell.Objects.Count; o++)
- {
- MapObject ob = cell.Objects[o];
- if (ob.Race == ObjectType.Monster || ob.Race == ObjectType.Player || ob.Race == ObjectType.Hero)
- {
- if (!ob.IsAttackTarget(this)) continue;
- if (push)
- {
- var dir = Functions.DirectionFromPoint(CurrentLocation, ob.CurrentLocation);
- ob.Pushed(this, dir, distance - 1);
- }
- int delay = Functions.MaxDistance(CurrentLocation, ob.CurrentLocation) * 50 + additionalDelay; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- }
- else continue;
- break;
- }
- }
- }
- protected virtual void LineAttack(int damage, int distance, int additionalDelay = 500, DefenceType defenceType = DefenceType.ACAgility, bool push = false)
- {
- for (int i = 1; i <= distance; i++)
- {
- Point target = Functions.PointMove(CurrentLocation, Direction, i);
- if (!CurrentMap.ValidPoint(target)) continue;
- Cell 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.Monster || ob.Race == ObjectType.Player || ob.Race == ObjectType.Hero)
- {
- if (!ob.IsAttackTarget(this)) continue;
- if (push)
- {
- ob.Pushed(this, Direction, distance - 1);
- }
- int delay = Functions.MaxDistance(CurrentLocation, ob.CurrentLocation) * 50 + additionalDelay; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- }
- else continue;
- break;
- }
- }
- }
- protected virtual void WideLineAttack(int damage, int distance, int additionalDelay = 500, DefenceType defenceType = DefenceType.ACAgility, bool push = false, int width = 3)
- {
- if (width <= 2)
- {
- width = 3;
- }
- var even = width % 2 == 0;
- if (even)
- {
- width--;
- }
- var startPoints = new List<Point>
- {
- CurrentLocation
- };
- var half = (width - 1) / 2;
- var leftLoc = CurrentLocation;
- var rightLoc = CurrentLocation;
- for (int j = 0; j < half; j++)
- {
- leftLoc = Functions.Left(leftLoc, Direction);
- rightLoc = Functions.Right(rightLoc, Direction);
- startPoints.Add(leftLoc);
- startPoints.Add(rightLoc);
- }
- for (int j = 0; j < startPoints.Count; j++)
- {
- var point = startPoints[j];
- for (int i = 1; i <= distance; i++)
- {
- Point target = Functions.PointMove(point, Direction, i);
- if (!CurrentMap.ValidPoint(target)) continue;
- Cell 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.Monster || ob.Race == ObjectType.Player || ob.Race == ObjectType.Hero)
- {
- if (!ob.IsAttackTarget(this)) continue;
- if (push)
- {
- ob.Pushed(this, Direction, distance - 1);
- }
- int delay = Functions.MaxDistance(CurrentLocation, ob.CurrentLocation) * 50 + additionalDelay; //50 MS per Step
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- }
- else continue;
- break;
- }
- }
- }
- }
- protected virtual void HalfmoonAttack(int damage, int delay = 500, DefenceType defenceType = DefenceType.ACAgility)
- {
- MirDirection dir = Functions.PreviousDir(Direction);
- for (int i = 0; i < 4; i++)
- {
- Point target = Functions.PointMove(CurrentLocation, dir, 1);
- dir = Functions.NextDir(dir);
- if (!CurrentMap.ValidPoint(target)) continue;
- Cell 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 && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this)) continue;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- break;
- }
- }
- }
- // Sanjian
- protected virtual void ThreeQuarterMoonAttack(int damage, int delay = 500, DefenceType defenceType = DefenceType.ACAgility)
- {
- MirDirection dir = Functions.PreviousDir(Direction);
- for (int i = 0; i < 6; i++)
- {
- Point target = Functions.PointMove(CurrentLocation, dir, 1);
- dir = Functions.NextDir(dir);
- if (!CurrentMap.ValidPoint(target)) continue;
- Cell 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 && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this)) continue;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- break;
- }
- }
- }
- protected virtual void JumpBack(int distance)
- {
- MirDirection jumpDir = Functions.ReverseDirection(Direction);
- Point location = new Point();
- for (int i = 0; i < distance; i++)
- {
- location = Functions.PointMove(CurrentLocation, jumpDir, 1);
- if (!CurrentMap.ValidPoint(location)) return;
- }
- for (int i = 0; i < distance; 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);
- }
- Broadcast(new S.ObjectBackStep { ObjectID = ObjectID, Direction = Direction, Location = location, Distance = distance });
- }
- protected virtual void FullmoonAttack(int damage, int delay = 500, DefenceType defenceType = DefenceType.ACAgility, int pushDistance = -1, int distance = 1)
- {
- MirDirection dir = Direction;
- bool pushed = false;
- for (int j = 1; j <= distance; j++)
- {
- for (int i = 0; i < 8; i++)
- {
- dir = Functions.NextDir(dir);
- Point point = Functions.PointMove(CurrentLocation, dir, j);
- if (!CurrentMap.ValidPoint(point)) continue;
- Cell cell = CurrentMap.GetCell(point);
- 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 && ob.Race != ObjectType.Hero) continue;
- if (!ob.IsAttackTarget(this)) continue;
- if (pushDistance > 0 && !pushed)
- {
- ob.Pushed(this, Direction, pushDistance);
- pushed = true;
- }
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, ob, damage, defenceType);
- ActionList.Add(action);
- break;
- }
- }
- }
- }
-
- protected virtual void ProjectileAttack(int damage, DefenceType type = DefenceType.ACAgility, int additionalDelay = 500)
- {
- int delay = Functions.MaxDistance(CurrentLocation, Target.CurrentLocation) * 50 + additionalDelay;
- DelayedAction action = new DelayedAction(DelayedType.RangeDamage, Envir.Time + delay, Target, damage, type);
- ActionList.Add(action);
- }
- protected virtual void SinglePushAttack(int damage, DefenceType type = DefenceType.AC, int delay = 500, int pushDistance = 3)
- {
- //Repulsion - (utilises DelayedAction so player is hit at end of push)
- //need to put Damage Stats (DC/MC/SC) on mob for it to push
- int levelGap = 5;
- int mobLevel = this.Level;
- int targetLevel = Target.Level;
- if ((targetLevel <= mobLevel + levelGap))
- {
- if (Target.Pushed(this, Functions.DirectionFromPoint(CurrentLocation, Target.CurrentLocation), pushDistance) > 0)
- {
- AttackTime = Envir.Time + AttackSpeed + 300;
- if (damage == 0) return;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, Target, damage, type);
- ActionList.Add(action);
- }
- else
- {
- if (damage == 0) return;
- DelayedAction action = new DelayedAction(DelayedType.Damage, Envir.Time + delay, Target, damage, type);
- ActionList.Add(action);
- }
- }
- }
- }
- }
|