12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309 |
- //**************************************************************************
- //**
- //** p_enemy.c : Heretic 2 : Raven Software, Corp.
- //**
- //** $RCSfile: p_enemy.c,v $
- //** $Revision: 1.170 $
- //** $Date: 96/01/06 03:23:28 $
- //** $Author: bgokey $
- //**
- //**************************************************************************
- #include "h2def.h"
- #include "p_local.h"
- #include "soundst.h"
- // Macros
- // Types
- // Private Data
- // External Data
- extern fixed_t FloatBobOffsets[64];
- //----------------------------------------------------------------------------
- //
- // PROC P_RecursiveSound
- //
- //----------------------------------------------------------------------------
- mobj_t *soundtarget;
- void P_RecursiveSound(sector_t *sec, int soundblocks)
- {
- int i;
- line_t *check;
- sector_t *other;
- // Wake up all monsters in this sector
- if(sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
- { // Already flooded
- return;
- }
- sec->validcount = validcount;
- sec->soundtraversed = soundblocks+1;
- sec->soundtarget = soundtarget;
- for(i = 0; i < sec->linecount; i++)
- {
- check = sec->lines[i];
- if(!(check->flags&ML_TWOSIDED))
- {
- continue;
- }
- P_LineOpening(check);
- if(openrange <= 0)
- { // Closed door
- continue;
- }
- if(sides[check->sidenum[0]].sector == sec)
- {
- other = sides[check->sidenum[1]].sector;
- }
- else
- {
- other = sides[check->sidenum[0]].sector;
- }
- if(check->flags&ML_SOUNDBLOCK)
- {
- if(!soundblocks)
- {
- P_RecursiveSound(other, 1);
- }
- }
- else
- {
- P_RecursiveSound(other, soundblocks);
- }
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC P_NoiseAlert
- //
- // If a monster yells at a player, it will alert other monsters to the
- // player.
- //
- //----------------------------------------------------------------------------
- void P_NoiseAlert(mobj_t *target, mobj_t *emmiter)
- {
- soundtarget = target;
- validcount++;
- P_RecursiveSound(emmiter->subsector->sector, 0);
- }
- //----------------------------------------------------------------------------
- //
- // FUNC P_CheckMeleeRange
- //
- //----------------------------------------------------------------------------
- boolean P_CheckMeleeRange(mobj_t *actor)
- {
- mobj_t *mo;
- fixed_t dist;
- if(!actor->target)
- {
- return(false);
- }
- mo = actor->target;
- dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y);
- if(dist >= MELEERANGE)
- {
- return(false);
- }
- if(!P_CheckSight(actor, mo))
- {
- return(false);
- }
- if(mo->z > actor->z+actor->height)
- { // Target is higher than the attacker
- return(false);
- }
- else if(actor->z > mo->z+mo->height)
- { // Attacker is higher
- return(false);
- }
- return(true);
- }
- //----------------------------------------------------------------------------
- //
- // FUNC P_CheckMeleeRange2
- //
- //----------------------------------------------------------------------------
- boolean P_CheckMeleeRange2(mobj_t *actor)
- {
- mobj_t *mo;
- fixed_t dist;
- if(!actor->target)
- {
- return(false);
- }
- mo = actor->target;
- dist = P_AproxDistance(mo->x-actor->x, mo->y-actor->y);
- if(dist >= MELEERANGE*2 || dist < MELEERANGE)
- {
- return(false);
- }
- if(!P_CheckSight(actor, mo))
- {
- return(false);
- }
- if(mo->z > actor->z+actor->height)
- { // Target is higher than the attacker
- return(false);
- }
- else if(actor->z > mo->z+mo->height)
- { // Attacker is higher
- return(false);
- }
- return(true);
- }
- //----------------------------------------------------------------------------
- //
- // FUNC P_CheckMissileRange
- //
- //----------------------------------------------------------------------------
- boolean P_CheckMissileRange(mobj_t *actor)
- {
- fixed_t dist;
- if(!P_CheckSight(actor, actor->target))
- {
- return(false);
- }
- if(actor->flags&MF_JUSTHIT)
- { // The target just hit the enemy, so fight back!
- actor->flags &= ~MF_JUSTHIT;
- return(true);
- }
- if(actor->reactiontime)
- { // Don't attack yet
- return(false);
- }
- dist = (P_AproxDistance(actor->x-actor->target->x,
- actor->y-actor->target->y)>>FRACBITS)-64;
- if(!actor->info->meleestate)
- { // No melee attack, so fire more frequently
- dist -= 128;
- }
- if(dist > 200)
- {
- dist = 200;
- }
- if(P_Random() < dist)
- {
- return(false);
- }
- return(true);
- }
- /*
- ================
- =
- = P_Move
- =
- = Move in the current direction
- = returns false if the move is blocked
- ================
- */
- fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
- fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
- #define MAXSPECIALCROSS 8
- extern line_t *spechit[MAXSPECIALCROSS];
- extern int numspechit;
- boolean P_Move(mobj_t *actor)
- {
- fixed_t tryx, tryy;
- line_t *ld;
- boolean good;
- if(actor->flags2&MF2_BLASTED) return(true);
- if(actor->movedir == DI_NODIR)
- {
- return(false);
- }
- tryx = actor->x+actor->info->speed*xspeed[actor->movedir];
- tryy = actor->y+actor->info->speed*yspeed[actor->movedir];
- if(!P_TryMove(actor, tryx, tryy))
- { // open any specials
- if(actor->flags&MF_FLOAT && floatok)
- { // must adjust height
- if(actor->z < tmfloorz)
- {
- actor->z += FLOATSPEED;
- }
- else
- {
- actor->z -= FLOATSPEED;
- }
- actor->flags |= MF_INFLOAT;
- return(true);
- }
- if(!numspechit)
- {
- return false;
- }
- actor->movedir = DI_NODIR;
- good = false;
- while(numspechit--)
- {
- ld = spechit[numspechit];
- // if the special isn't a door that can be opened, return false
- if(P_ActivateLine(ld, actor, 0, SPAC_USE))
- {
- good = true;
- }
- /* Old version before use/cross/impact specials were combined
- if(P_UseSpecialLine(actor, ld))
- {
- good = true;
- }
- */
- }
- return(good);
- }
- else
- {
- actor->flags &= ~MF_INFLOAT;
- }
- if(!(actor->flags&MF_FLOAT))
- {
- if(actor->z > actor->floorz)
- {
- P_HitFloor(actor);
- }
- actor->z = actor->floorz;
- }
- return(true);
- }
- //----------------------------------------------------------------------------
- //
- // FUNC P_TryWalk
- //
- // Attempts to move actor in its current (ob->moveangle) direction.
- // If blocked by either a wall or an actor returns FALSE.
- // If move is either clear of block only by a door, returns TRUE and sets.
- // If a door is in the way, an OpenDoor call is made to start it opening.
- //
- //----------------------------------------------------------------------------
- boolean P_TryWalk(mobj_t *actor)
- {
- if(!P_Move(actor))
- {
- return(false);
- }
- actor->movecount = P_Random()&15;
- return(true);
- }
- /*
- ================
- =
- = P_NewChaseDir
- =
- ================
- */
- dirtype_t opposite[] =
- {DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
- DI_NORTH, DI_NORTHWEST, DI_NODIR};
- dirtype_t diags[] = {DI_NORTHWEST,DI_NORTHEAST,DI_SOUTHWEST,DI_SOUTHEAST};
- void P_NewChaseDir (mobj_t *actor)
- {
- fixed_t deltax,deltay;
- dirtype_t d[3];
- dirtype_t tdir, olddir, turnaround;
- if (!actor->target)
- I_Error ("P_NewChaseDir: called with no target");
- olddir = actor->movedir;
- turnaround=opposite[olddir];
- deltax = actor->target->x - actor->x;
- deltay = actor->target->y - actor->y;
- if (deltax>10*FRACUNIT)
- d[1]= DI_EAST;
- else if (deltax<-10*FRACUNIT)
- d[1]= DI_WEST;
- else
- d[1]=DI_NODIR;
- if (deltay<-10*FRACUNIT)
- d[2]= DI_SOUTH;
- else if (deltay>10*FRACUNIT)
- d[2]= DI_NORTH;
- else
- d[2]=DI_NODIR;
- // try direct route
- if (d[1] != DI_NODIR && d[2] != DI_NODIR)
- {
- actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
- if (actor->movedir != turnaround && P_TryWalk(actor))
- return;
- }
- // try other directions
- if (P_Random() > 200 || abs(deltay)>abs(deltax))
- {
- tdir=d[1];
- d[1]=d[2];
- d[2]=tdir;
- }
- if (d[1]==turnaround)
- d[1]=DI_NODIR;
- if (d[2]==turnaround)
- d[2]=DI_NODIR;
- if (d[1]!=DI_NODIR)
- {
- actor->movedir = d[1];
- if (P_TryWalk(actor))
- return; /*either moved forward or attacked*/
- }
- if (d[2]!=DI_NODIR)
- {
- actor->movedir =d[2];
- if (P_TryWalk(actor))
- return;
- }
- /* there is no direct path to the player, so pick another direction */
- if (olddir!=DI_NODIR)
- {
- actor->movedir =olddir;
- if (P_TryWalk(actor))
- return;
- }
- if (P_Random()&1) /*randomly determine direction of search*/
- {
- for (tdir=DI_EAST ; tdir<=DI_SOUTHEAST ; tdir++)
- {
- if (tdir!=turnaround)
- {
- actor->movedir =tdir;
- if ( P_TryWalk(actor) )
- return;
- }
- }
- }
- else
- {
- for (tdir=DI_SOUTHEAST ; tdir >= DI_EAST;tdir--)
- {
- if (tdir!=turnaround)
- {
- actor->movedir =tdir;
- if ( P_TryWalk(actor) )
- return;
- }
- }
- }
- if (turnaround != DI_NODIR)
- {
- actor->movedir =turnaround;
- if ( P_TryWalk(actor) )
- return;
- }
- actor->movedir = DI_NODIR; // can't move
- }
- //---------------------------------------------------------------------------
- //
- // FUNC P_LookForMonsters
- //
- //---------------------------------------------------------------------------
- #define MONS_LOOK_RANGE (16*64*FRACUNIT)
- #define MONS_LOOK_LIMIT 64
- boolean P_LookForMonsters(mobj_t *actor)
- {
- int count;
- mobj_t *mo;
- thinker_t *think;
- if(!P_CheckSight(players[0].mo, actor))
- { // Player can't see monster
- return(false);
- }
- count = 0;
- for(think = thinkercap.next; think != &thinkercap; think = think->next)
- {
- if(think->function != P_MobjThinker)
- { // Not a mobj thinker
- continue;
- }
- mo = (mobj_t *)think;
- if(!(mo->flags&MF_COUNTKILL) || (mo == actor) || (mo->health <= 0))
- { // Not a valid monster
- continue;
- }
- if(P_AproxDistance(actor->x-mo->x, actor->y-mo->y)
- > MONS_LOOK_RANGE)
- { // Out of range
- continue;
- }
- if(P_Random() < 16)
- { // Skip
- continue;
- }
- if(count++ > MONS_LOOK_LIMIT)
- { // Stop searching
- return(false);
- }
- if(!P_CheckSight(actor, mo))
- { // Out of sight
- continue;
- }
- if (actor->type == MT_MINOTAUR)
- {
- if ((mo->type == MT_MINOTAUR) &&
- (mo->target != ((player_t *)actor->special1)->mo))
- {
- continue;
- }
- }
- // Found a target monster
- actor->target = mo;
- return(true);
- }
- return(false);
- }
- /*
- ================
- =
- = P_LookForPlayers
- =
- = If allaround is false, only look 180 degrees in front
- = returns true if a player is targeted
- ================
- */
- boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
- {
- int c;
- int stop;
- player_t *player;
- sector_t *sector;
- angle_t an;
- fixed_t dist;
- if(!netgame && players[0].health <= 0)
- { // Single player game and player is dead, look for monsters
- return(P_LookForMonsters(actor));
- }
- sector = actor->subsector->sector;
- c = 0;
- stop = (actor->lastlook-1)&3;
- for( ; ; actor->lastlook = (actor->lastlook+1)&3 )
- {
- if (!playeringame[actor->lastlook])
- continue;
- if (c++ == 2 || actor->lastlook == stop)
- return false; // done looking
- player = &players[actor->lastlook];
- if (player->health <= 0)
- continue; // dead
- if (!P_CheckSight (actor, player->mo))
- continue; // out of sight
- if (!allaround)
- {
- an = R_PointToAngle2 (actor->x, actor->y,
- player->mo->x, player->mo->y) - actor->angle;
- if (an > ANG90 && an < ANG270)
- {
- dist = P_AproxDistance (player->mo->x - actor->x,
- player->mo->y - actor->y);
- // if real close, react anyway
- if (dist > MELEERANGE)
- continue; // behind back
- }
- }
- if(player->mo->flags&MF_SHADOW)
- { // Player is invisible
- if((P_AproxDistance(player->mo->x-actor->x,
- player->mo->y-actor->y) > 2*MELEERANGE)
- && P_AproxDistance(player->mo->momx, player->mo->momy)
- < 5*FRACUNIT)
- { // Player is sneaking - can't detect
- return(false);
- }
- if(P_Random() < 225)
- { // Player isn't sneaking, but still didn't detect
- return(false);
- }
- }
- if (actor->type == MT_MINOTAUR)
- {
- if(((player_t *)(actor->special1)) == player)
- {
- continue; // Don't target master
- }
- }
- actor->target = player->mo;
- return(true);
- }
- return(false);
- }
- /*
- ===============================================================================
- ACTION ROUTINES
- ===============================================================================
- */
- /*
- ==============
- =
- = A_Look
- =
- = Stay in state until a player is sighted
- =
- ==============
- */
- void A_Look (mobj_t *actor)
- {
- mobj_t *targ;
- actor->threshold = 0; // any shot will wake up
- targ = actor->subsector->sector->soundtarget;
- if (targ && (targ->flags & MF_SHOOTABLE) )
- {
- actor->target = targ;
- if ( actor->flags & MF_AMBUSH )
- {
- if (P_CheckSight (actor, actor->target))
- goto seeyou;
- }
- else
- goto seeyou;
- }
- if (!P_LookForPlayers (actor, false) )
- return;
- // go into chase state
- seeyou:
- if (actor->info->seesound)
- {
- int sound;
- sound = actor->info->seesound;
- if(actor->flags2&MF2_BOSS)
- { // Full volume
- S_StartSound(NULL, sound);
- }
- else
- {
- S_StartSound(actor, sound);
- }
- }
- P_SetMobjState(actor, actor->info->seestate);
- }
- /*
- ==============
- =
- = A_Chase
- =
- = Actor has a melee attack, so it tries to close as fast as possible
- =
- ==============
- */
- void A_Chase(mobj_t *actor)
- {
- int delta;
- if(actor->reactiontime)
- {
- actor->reactiontime--;
- }
- // Modify target threshold
- if(actor->threshold)
- {
- actor->threshold--;
- }
- if(gameskill == sk_nightmare)
- { // Monsters move faster in nightmare mode
- actor->tics -= actor->tics/2;
- if(actor->tics < 3)
- {
- actor->tics = 3;
- }
- }
- //
- // turn towards movement direction if not there yet
- //
- if(actor->movedir < 8)
- {
- actor->angle &= (7<<29);
- delta = actor->angle-(actor->movedir << 29);
- if(delta > 0)
- {
- actor->angle -= ANG90/2;
- }
- else if(delta < 0)
- {
- actor->angle += ANG90/2;
- }
- }
- if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- { // look for a new target
- if(P_LookForPlayers(actor, true))
- { // got a new target
- return;
- }
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
- //
- // don't attack twice in a row
- //
- if(actor->flags & MF_JUSTATTACKED)
- {
- actor->flags &= ~MF_JUSTATTACKED;
- if (gameskill != sk_nightmare)
- P_NewChaseDir (actor);
- return;
- }
- //
- // check for melee attack
- //
- if (actor->info->meleestate && P_CheckMeleeRange (actor))
- {
- if(actor->info->attacksound)
- {
- S_StartSound (actor, actor->info->attacksound);
- }
- P_SetMobjState (actor, actor->info->meleestate);
- return;
- }
- //
- // check for missile attack
- //
- if (actor->info->missilestate)
- {
- if (gameskill < sk_nightmare && actor->movecount)
- goto nomissile;
- if (!P_CheckMissileRange (actor))
- goto nomissile;
- P_SetMobjState (actor, actor->info->missilestate);
- actor->flags |= MF_JUSTATTACKED;
- return;
- }
- nomissile:
- //
- // possibly choose another target
- //
- if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
- {
- if (P_LookForPlayers(actor,true))
- return; // got a new target
- }
- //
- // chase towards player
- //
- if (--actor->movecount<0 || !P_Move (actor))
- {
- P_NewChaseDir (actor);
- }
- //
- // make active sound
- //
- if(actor->info->activesound && P_Random() < 3)
- {
- if(actor->type == MT_BISHOP && P_Random() < 128)
- {
- S_StartSound(actor, actor->info->seesound);
- }
- else if(actor->type == MT_PIG)
- {
- S_StartSound(actor, SFX_PIG_ACTIVE1+(P_Random()&1));
- }
- else if(actor->flags2&MF2_BOSS)
- {
- S_StartSound(NULL, actor->info->activesound);
- }
- else
- {
- S_StartSound(actor, actor->info->activesound);
- }
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_FaceTarget
- //
- //----------------------------------------------------------------------------
- void A_FaceTarget(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- actor->flags &= ~MF_AMBUSH;
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
- actor->target->y);
- if(actor->target->flags&MF_SHADOW)
- { // Target is a ghost
- actor->angle += (P_Random()-P_Random())<<21;
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_Pain
- //
- //----------------------------------------------------------------------------
- void A_Pain(mobj_t *actor)
- {
- if(actor->info->painsound)
- {
- S_StartSound(actor, actor->info->painsound);
- }
- }
- //============================================================================
- //
- // A_SetInvulnerable
- //
- //============================================================================
- void A_SetInvulnerable(mobj_t *actor)
- {
- actor->flags2 |= MF2_INVULNERABLE;
- }
- //============================================================================
- //
- // A_UnSetInvulnerable
- //
- //============================================================================
- void A_UnSetInvulnerable(mobj_t *actor)
- {
- actor->flags2 &= ~MF2_INVULNERABLE;
- }
- //============================================================================
- //
- // A_SetReflective
- //
- //============================================================================
- void A_SetReflective(mobj_t *actor)
- {
- actor->flags2 |= MF2_REFLECTIVE;
- if ((actor->type == MT_CENTAUR) ||
- (actor->type == MT_CENTAURLEADER))
- {
- A_SetInvulnerable(actor);
- }
- }
- //============================================================================
- //
- // A_UnSetReflective
- //
- //============================================================================
- void A_UnSetReflective(mobj_t *actor)
- {
- actor->flags2 &= ~MF2_REFLECTIVE;
- if ((actor->type == MT_CENTAUR) ||
- (actor->type == MT_CENTAURLEADER))
- {
- A_UnSetInvulnerable(actor);
- }
- }
- //----------------------------------------------------------------------------
- //
- // FUNC P_UpdateMorphedMonster
- //
- // Returns true if the pig morphs.
- //
- //----------------------------------------------------------------------------
- boolean P_UpdateMorphedMonster(mobj_t *actor, int tics)
- {
- mobj_t *fog;
- fixed_t x;
- fixed_t y;
- fixed_t z;
- mobjtype_t moType;
- mobj_t *mo;
- mobj_t oldMonster;
- actor->special1 -= tics;
- if(actor->special1 > 0)
- {
- return(false);
- }
- moType = actor->special2;
- switch(moType)
- {
- case MT_WRAITHB: // These must remain morphed
- case MT_SERPENT:
- case MT_SERPENTLEADER:
- case MT_MINOTAUR:
- return(false);
- default:
- break;
- }
- x = actor->x;
- y = actor->y;
- z = actor->z;
- oldMonster = *actor; // Save pig vars
- P_RemoveMobjFromTIDList(actor);
- P_SetMobjState(actor, S_FREETARGMOBJ);
- mo = P_SpawnMobj(x, y, z, moType);
- if(P_TestMobjLocation(mo) == false)
- { // Didn't fit
- P_RemoveMobj(mo);
- mo = P_SpawnMobj(x, y, z, oldMonster.type);
- mo->angle = oldMonster.angle;
- mo->flags = oldMonster.flags;
- mo->health = oldMonster.health;
- mo->target = oldMonster.target;
- mo->special = oldMonster.special;
- mo->special1 = 5*35; // Next try in 5 seconds
- mo->special2 = moType;
- mo->tid = oldMonster.tid;
- memcpy(mo->args, oldMonster.args, 5);
- P_InsertMobjIntoTIDList(mo, oldMonster.tid);
- return(false);
- }
- mo->angle = oldMonster.angle;
- mo->target = oldMonster.target;
- mo->tid = oldMonster.tid;
- mo->special = oldMonster.special;
- memcpy(mo->args, oldMonster.args, 5);
- P_InsertMobjIntoTIDList(mo, oldMonster.tid);
- fog = P_SpawnMobj(x, y, z+TELEFOGHEIGHT, MT_TFOG);
- S_StartSound(fog, SFX_TELEPORT);
- return(true);
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_PigLook
- //
- //----------------------------------------------------------------------------
- void A_PigLook(mobj_t *actor)
- {
- if(P_UpdateMorphedMonster(actor, 10))
- {
- return;
- }
- A_Look(actor);
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_PigChase
- //
- //----------------------------------------------------------------------------
- void A_PigChase(mobj_t *actor)
- {
- if(P_UpdateMorphedMonster(actor, 3))
- {
- return;
- }
- A_Chase(actor);
- }
- //============================================================================
- //
- // A_PigAttack
- //
- //============================================================================
- void A_PigAttack(mobj_t *actor)
- {
- if(P_UpdateMorphedMonster(actor, 18))
- {
- return;
- }
- if(!actor->target)
- {
- return;
- }
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, 2+(P_Random()&1));
- S_StartSound(actor, SFX_PIG_ATTACK);
- }
- }
- //============================================================================
- //
- // A_PigPain
- //
- //============================================================================
- void A_PigPain(mobj_t *actor)
- {
- A_Pain(actor);
- if(actor->z <= actor->floorz)
- {
- actor->momz = 3.5*FRACUNIT;
- }
- }
- void FaceMovementDirection(mobj_t *actor)
- {
- switch(actor->movedir)
- {
- case DI_EAST:
- actor->angle = 0<<24;
- break;
- case DI_NORTHEAST:
- actor->angle = 32<<24;
- break;
- case DI_NORTH:
- actor->angle = 64<<24;
- break;
- case DI_NORTHWEST:
- actor->angle = 96<<24;
- break;
- case DI_WEST:
- actor->angle = 128<<24;
- break;
- case DI_SOUTHWEST:
- actor->angle = 160<<24;
- break;
- case DI_SOUTH:
- actor->angle = 192<<24;
- break;
- case DI_SOUTHEAST:
- actor->angle = 224<<24;
- break;
- }
- }
- //----------------------------------------------------------------------------
- //
- // Minotaur variables
- //
- // special1 pointer to player that spawned it (mobj_t)
- // special2 internal to minotaur AI
- // args[0] args[0]-args[3] together make up minotaur start time
- // args[1] |
- // args[2] |
- // args[3] V
- // args[4] charge duration countdown
- //----------------------------------------------------------------------------
- void A_MinotaurFade0(mobj_t *actor)
- {
- actor->flags &= ~MF_ALTSHADOW;
- actor->flags |= MF_SHADOW;
- }
- void A_MinotaurFade1(mobj_t *actor)
- {
- // Second level of transparency
- actor->flags &= ~MF_SHADOW;
- actor->flags |= MF_ALTSHADOW;
- }
- void A_MinotaurFade2(mobj_t *actor)
- {
- // Make fully visible
- actor->flags &= ~MF_SHADOW;
- actor->flags &= ~MF_ALTSHADOW;
- }
- //----------------------------------------------------------------------------
- //
- // A_MinotaurRoam -
- //
- //
- //----------------------------------------------------------------------------
- void A_MinotaurLook(mobj_t *actor);
- void A_MinotaurRoam(mobj_t *actor)
- {
- unsigned int *starttime = (unsigned int *)actor->args;
- actor->flags &= ~MF_SHADOW; // In case pain caused him to
- actor->flags &= ~MF_ALTSHADOW; // skip his fade in.
- if ((leveltime - *starttime) >= MAULATORTICS)
- {
- P_DamageMobj(actor,NULL,NULL,10000);
- return;
- }
- if (P_Random()<30)
- A_MinotaurLook(actor); // adjust to closest target
- if (P_Random()<6)
- {
- //Choose new direction
- actor->movedir = P_Random() % 8;
- FaceMovementDirection(actor);
- }
- if (!P_Move(actor))
- {
- // Turn
- if (P_Random() & 1)
- actor->movedir = (++actor->movedir)%8;
- else
- actor->movedir = (actor->movedir+7)%8;
- FaceMovementDirection(actor);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurLook
- //
- // Look for enemy of player
- //----------------------------------------------------------------------------
- #define MINOTAUR_LOOK_DIST (16*54*FRACUNIT)
- void A_MinotaurLook(mobj_t *actor)
- {
- mobj_t *mo=NULL;
- player_t *player;
- thinker_t *think;
- fixed_t dist;
- int i;
- mobj_t *master = (mobj_t *)(actor->special1);
- actor->target = NULL;
- if (deathmatch) // Quick search for players
- {
- for (i=0; i<MAXPLAYERS; i++)
- {
- if (!playeringame[i]) continue;
- player = &players[i];
- mo = player->mo;
- if (mo == master) continue;
- if (mo->health <= 0) continue;
- dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
- if (dist > MINOTAUR_LOOK_DIST) continue;
- actor->target = mo;
- break;
- }
- }
- if (!actor->target) // Near player monster search
- {
- if (master && (master->health>0) && (master->player))
- mo = P_RoughMonsterSearch(master, 20);
- else
- mo = P_RoughMonsterSearch(actor, 20);
- actor->target = mo;
- }
- if (!actor->target) // Normal monster search
- {
- for(think = thinkercap.next; think != &thinkercap; think = think->next)
- {
- if(think->function != P_MobjThinker) continue;
- mo = (mobj_t *)think;
- if (!(mo->flags&MF_COUNTKILL)) continue;
- if (mo->health <= 0) continue;
- if (!(mo->flags&MF_SHOOTABLE)) continue;
- dist = P_AproxDistance(actor->x - mo->x, actor->y - mo->y);
- if (dist > MINOTAUR_LOOK_DIST) continue;
- if ((mo == master) || (mo == actor)) continue;
- if ((mo->type == MT_MINOTAUR) &&
- (mo->special1 == actor->special1)) continue;
- actor->target = mo;
- break; // Found mobj to attack
- }
- }
- if (actor->target)
- {
- P_SetMobjStateNF(actor, S_MNTR_WALK1);
- }
- else
- {
- P_SetMobjStateNF(actor, S_MNTR_ROAM1);
- }
- }
- void A_MinotaurChase(mobj_t *actor)
- {
- unsigned int *starttime = (unsigned int *)actor->args;
- actor->flags &= ~MF_SHADOW; // In case pain caused him to
- actor->flags &= ~MF_ALTSHADOW; // skip his fade in.
- if ((leveltime - *starttime) >= MAULATORTICS)
- {
- P_DamageMobj(actor,NULL,NULL,10000);
- return;
- }
- if (P_Random()<30)
- A_MinotaurLook(actor); // adjust to closest target
- if (!actor->target || (actor->target->health <= 0) ||
- !(actor->target->flags&MF_SHOOTABLE))
- { // look for a new target
- P_SetMobjState(actor, S_MNTR_LOOK1);
- return;
- }
- FaceMovementDirection(actor);
- actor->reactiontime=0;
- // Melee attack
- if (actor->info->meleestate && P_CheckMeleeRange(actor))
- {
- if(actor->info->attacksound)
- {
- S_StartSound (actor, actor->info->attacksound);
- }
- P_SetMobjState (actor, actor->info->meleestate);
- return;
- }
- // Missile attack
- if (actor->info->missilestate && P_CheckMissileRange(actor))
- {
- P_SetMobjState (actor, actor->info->missilestate);
- return;
- }
- // chase towards target
- if (!P_Move(actor))
- {
- P_NewChaseDir(actor);
- }
- // Active sound
- if(actor->info->activesound && P_Random() < 6)
- {
- S_StartSound(actor, actor->info->activesound);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurAtk1
- //
- // Melee attack.
- //
- //----------------------------------------------------------------------------
- void A_MinotaurAtk1(mobj_t *actor)
- {
- if (!actor->target) return;
- S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(4));
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurDecide
- //
- // Choose a missile attack.
- //
- //----------------------------------------------------------------------------
- #define MNTR_CHARGE_SPEED (23*FRACUNIT)
- void A_MinotaurDecide(mobj_t *actor)
- {
- angle_t angle;
- mobj_t *target = actor->target;
- int dist;
- if (!target) return;
- dist = P_AproxDistance(actor->x-target->x, actor->y-target->y);
- if(target->z+target->height > actor->z
- && target->z+target->height < actor->z+actor->height
- && dist < 16*64*FRACUNIT
- && dist > 1*64*FRACUNIT
- && P_Random() < 230)
- { // Charge attack
- // Don't call the state function right away
- P_SetMobjStateNF(actor, S_MNTR_ATK4_1);
- actor->flags |= MF_SKULLFLY;
- A_FaceTarget(actor);
- angle = actor->angle>>ANGLETOFINESHIFT;
- actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]);
- actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]);
- actor->args[4] = 35/2; // Charge duration
- }
- else if(target->z == target->floorz
- && dist < 9*64*FRACUNIT
- && P_Random() < 100)
- { // Floor fire attack
- P_SetMobjState(actor, S_MNTR_ATK3_1);
- actor->special2 = 0;
- }
- else
- { // Swing attack
- A_FaceTarget(actor);
- // Don't need to call P_SetMobjState because the current state
- // falls through to the swing attack
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurCharge
- //
- //----------------------------------------------------------------------------
- void A_MinotaurCharge(mobj_t *actor)
- {
- mobj_t *puff;
- if (!actor->target) return;
- if(actor->args[4] > 0)
- {
- puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PUNCHPUFF);
- puff->momz = 2*FRACUNIT;
- actor->args[4]--;
- }
- else
- {
- actor->flags &= ~MF_SKULLFLY;
- P_SetMobjState(actor, actor->info->seestate);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurAtk2
- //
- // Swing attack.
- //
- //----------------------------------------------------------------------------
- void A_MinotaurAtk2(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t angle;
- fixed_t momz;
- if(!actor->target) return;
- S_StartSound(actor, SFX_MAULATOR_HAMMER_SWING);
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(3));
- return;
- }
- mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1);
- if(mo)
- {
- //S_StartSound(mo, sfx_minat2);
- momz = mo->momz;
- angle = mo->angle;
- P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/8), momz);
- P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/8), momz);
- P_SpawnMissileAngle(actor, MT_MNTRFX1, angle-(ANG45/16), momz);
- P_SpawnMissileAngle(actor, MT_MNTRFX1, angle+(ANG45/16), momz);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MinotaurAtk3
- //
- // Floor fire attack.
- //
- //----------------------------------------------------------------------------
- void A_MinotaurAtk3(mobj_t *actor)
- {
- mobj_t *mo;
- player_t *player;
- if(!actor->target)
- {
- return;
- }
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(3));
- if((player = actor->target->player) != NULL)
- { // Squish the player
- player->deltaviewheight = -16*FRACUNIT;
- }
- }
- else
- {
- mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2);
- if(mo != NULL)
- {
- S_StartSound(mo, SFX_MAULATOR_HAMMER_HIT);
- }
- }
- if(P_Random() < 192 && actor->special2 == 0)
- {
- P_SetMobjState(actor, S_MNTR_ATK3_4);
- actor->special2 = 1;
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_MntrFloorFire
- //
- //----------------------------------------------------------------------------
- void A_MntrFloorFire(mobj_t *actor)
- {
- mobj_t *mo;
- actor->z = actor->floorz;
- mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<10),
- actor->y+((P_Random()-P_Random())<<10), ONFLOORZ, MT_MNTRFX3);
- mo->target = actor->target;
- mo->momx = 1; // Force block checking
- P_CheckMissileSpawn(mo);
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_Scream
- //
- //----------------------------------------------------------------------------
- void A_Scream(mobj_t *actor)
- {
- int sound;
- S_StopSound(actor);
- if(actor->player)
- {
- if(actor->player->morphTics)
- {
- S_StartSound(actor, actor->info->deathsound);
- }
- else
- {
- // Handle the different player death screams
- if(actor->momz <= -39*FRACUNIT)
- { // Falling splat
- sound = SFX_PLAYER_FALLING_SPLAT;
- }
- else if(actor->health > -50)
- { // Normal death sound
- switch(actor->player->class)
- {
- case PCLASS_FIGHTER:
- sound = SFX_PLAYER_FIGHTER_NORMAL_DEATH;
- break;
- case PCLASS_CLERIC:
- sound = SFX_PLAYER_CLERIC_NORMAL_DEATH;
- break;
- case PCLASS_MAGE:
- sound = SFX_PLAYER_MAGE_NORMAL_DEATH;
- break;
- default:
- sound = SFX_NONE;
- break;
- }
- }
- else if(actor->health > -100)
- { // Crazy death sound
- switch(actor->player->class)
- {
- case PCLASS_FIGHTER:
- sound = SFX_PLAYER_FIGHTER_CRAZY_DEATH;
- break;
- case PCLASS_CLERIC:
- sound = SFX_PLAYER_CLERIC_CRAZY_DEATH;
- break;
- case PCLASS_MAGE:
- sound = SFX_PLAYER_MAGE_CRAZY_DEATH;
- break;
- default:
- sound = SFX_NONE;
- break;
- }
- }
- else
- { // Extreme death sound
- switch(actor->player->class)
- {
- case PCLASS_FIGHTER:
- sound = SFX_PLAYER_FIGHTER_EXTREME1_DEATH;
- break;
- case PCLASS_CLERIC:
- sound = SFX_PLAYER_CLERIC_EXTREME1_DEATH;
- break;
- case PCLASS_MAGE:
- sound = SFX_PLAYER_MAGE_EXTREME1_DEATH;
- break;
- default:
- sound = SFX_NONE;
- break;
- }
- sound += P_Random()%3; // Three different extreme deaths
- }
- S_StartSound(actor, sound);
- }
- }
- else
- {
- S_StartSound(actor, actor->info->deathsound);
- }
- }
- //---------------------------------------------------------------------------
- //
- // PROC P_DropItem
- //
- //---------------------------------------------------------------------------
- /*
- void P_DropItem(mobj_t *source, mobjtype_t type, int special, int chance)
- {
- mobj_t *mo;
- if(P_Random() > chance)
- {
- return;
- }
- mo = P_SpawnMobj(source->x, source->y,
- source->z+(source->height>>1), type);
- mo->momx = (P_Random()-P_Random())<<8;
- mo->momy = (P_Random()-P_Random())<<8;
- mo->momz = FRACUNIT*5+(P_Random()<<10);
- mo->flags2 |= MF2_DROPPED;
- mo->health = special;
- }
- */
- //----------------------------------------------------------------------------
- //
- // PROC A_NoBlocking
- //
- //----------------------------------------------------------------------------
- void A_NoBlocking(mobj_t *actor)
- {
- actor->flags &= ~MF_SOLID;
- // Check for monsters dropping things
- /* switch(actor->type)
- {
- // Add the monster dropped items here
- case MT_MUMMYLEADERGHOST:
- P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84);
- break;
- default:
- break;
- }
- */
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_Explode
- //
- // Handles a bunch of exploding things.
- //
- //----------------------------------------------------------------------------
- void A_Explode(mobj_t *actor)
- {
- int damage;
- int distance;
- boolean damageSelf;
- damage = 128;
- distance = 128;
- damageSelf = true;
- switch(actor->type)
- {
- case MT_FIREBOMB: // Time Bombs
- actor->z += 32*FRACUNIT;
- actor->flags &= ~MF_SHADOW;
- break;
- case MT_MNTRFX2: // Minotaur floor fire
- damage = 24;
- break;
- case MT_BISHOP: // Bishop radius death
- damage = 25+(P_Random()&15);
- break;
- case MT_HAMMER_MISSILE: // Fighter Hammer
- damage = 128;
- damageSelf = false;
- break;
- case MT_FSWORD_MISSILE: // Fighter Runesword
- damage = 64;
- damageSelf = false;
- break;
- case MT_CIRCLEFLAME: // Cleric Flame secondary flames
- damage = 20;
- damageSelf = false;
- break;
- case MT_SORCBALL1: // Sorcerer balls
- case MT_SORCBALL2:
- case MT_SORCBALL3:
- distance = 255;
- damage = 255;
- actor->args[0] = 1; // don't play bounce
- break;
- case MT_SORCFX1: // Sorcerer spell 1
- damage = 30;
- break;
- case MT_SORCFX4: // Sorcerer spell 4
- damage = 20;
- break;
- case MT_TREEDESTRUCTIBLE:
- damage = 10;
- break;
- case MT_DRAGON_FX2:
- damage = 80;
- damageSelf = false;
- break;
- case MT_MSTAFF_FX:
- damage = 64;
- distance = 192;
- damageSelf = false;
- break;
- case MT_MSTAFF_FX2:
- damage = 80;
- distance = 192;
- damageSelf = false;
- break;
- case MT_POISONCLOUD:
- damage = 4;
- distance = 40;
- break;
- case MT_ZXMAS_TREE:
- case MT_ZSHRUB2:
- damage = 30;
- distance = 64;
- break;
- default:
- break;
- }
- P_RadiusAttack(actor, actor->target, damage, distance, damageSelf);
- if(actor->z <= actor->floorz+(distance<<FRACBITS)
- && actor->type != MT_POISONCLOUD)
- {
- P_HitFloor(actor);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC P_Massacre
- //
- // Kills all monsters.
- //
- //----------------------------------------------------------------------------
- int P_Massacre(void)
- {
- int count;
- mobj_t *mo;
- thinker_t *think;
- count = 0;
- for(think = thinkercap.next; think != &thinkercap;
- think = think->next)
- {
- if(think->function != P_MobjThinker)
- { // Not a mobj thinker
- continue;
- }
- mo = (mobj_t *)think;
- if((mo->flags&MF_COUNTKILL) && (mo->health > 0))
- {
- mo->flags2 &= ~(MF2_NONSHOOTABLE+MF2_INVULNERABLE);
- mo->flags |= MF_SHOOTABLE;
- P_DamageMobj(mo, NULL, NULL, 10000);
- count++;
- }
- }
- return count;
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_SkullPop
- //
- //----------------------------------------------------------------------------
- void A_SkullPop(mobj_t *actor)
- {
- mobj_t *mo;
- player_t *player;
- if(!actor->player)
- {
- return;
- }
- actor->flags &= ~MF_SOLID;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+48*FRACUNIT,
- MT_BLOODYSKULL);
- //mo->target = actor;
- mo->momx = (P_Random()-P_Random())<<9;
- mo->momy = (P_Random()-P_Random())<<9;
- mo->momz = FRACUNIT*2+(P_Random()<<6);
- // Attach player mobj to bloody skull
- player = actor->player;
- actor->player = NULL;
- actor->special1 = player->class;
- mo->player = player;
- mo->health = actor->health;
- mo->angle = actor->angle;
- player->mo = mo;
- player->lookdir = 0;
- player->damagecount = 32;
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_CheckSkullFloor
- //
- //----------------------------------------------------------------------------
- void A_CheckSkullFloor(mobj_t *actor)
- {
- if(actor->z <= actor->floorz)
- {
- P_SetMobjState(actor, S_BLOODYSKULLX1);
- S_StartSound(actor, SFX_DRIP);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_CheckSkullDone
- //
- //----------------------------------------------------------------------------
- void A_CheckSkullDone(mobj_t *actor)
- {
- if(actor->special2 == 666)
- {
- P_SetMobjState(actor, S_BLOODYSKULLX2);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_CheckBurnGone
- //
- //----------------------------------------------------------------------------
- void A_CheckBurnGone(mobj_t *actor)
- {
- if(actor->special2 == 666)
- {
- P_SetMobjState(actor, S_PLAY_FDTH20);
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_FreeTargMobj
- //
- //----------------------------------------------------------------------------
- void A_FreeTargMobj(mobj_t *mo)
- {
- mo->momx = mo->momy = mo->momz = 0;
- mo->z = mo->ceilingz+4*FRACUNIT;
- mo->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY|MF_SOLID|MF_COUNTKILL);
- mo->flags |= MF_CORPSE|MF_DROPOFF|MF_NOGRAVITY;
- mo->flags2 &= ~(MF2_PASSMOBJ|MF2_LOGRAV);
- mo->flags2 |= MF2_DONTDRAW;
- mo->player = NULL;
- mo->health = -1000; // Don't resurrect
- }
- //----------------------------------------------------------------------------
- //
- // CorpseQueue Routines
- //
- //----------------------------------------------------------------------------
- // Corpse queue for monsters - this should be saved out
- #define CORPSEQUEUESIZE 64
- mobj_t *corpseQueue[CORPSEQUEUESIZE];
- int corpseQueueSlot;
- // throw another corpse on the queue
- void A_QueueCorpse(mobj_t *actor)
- {
- mobj_t *corpse;
- if(corpseQueueSlot >= CORPSEQUEUESIZE)
- { // Too many corpses - remove an old one
- corpse = corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE];
- if (corpse) P_RemoveMobj(corpse);
- }
- corpseQueue[corpseQueueSlot%CORPSEQUEUESIZE] = actor;
- corpseQueueSlot++;
- }
- // Remove a mobj from the queue (for resurrection)
- void A_DeQueueCorpse(mobj_t *actor)
- {
- int slot;
- for (slot=0; slot<CORPSEQUEUESIZE; slot++)
- {
- if (corpseQueue[slot] == actor)
- {
- corpseQueue[slot] = NULL;
- break;
- }
- }
- }
- void P_InitCreatureCorpseQueue(boolean corpseScan)
- {
- thinker_t *think;
- mobj_t *mo;
- // Initialize queue
- corpseQueueSlot=0;
- memset(corpseQueue, 0, sizeof(mobj_t *)*CORPSEQUEUESIZE);
- if (!corpseScan) return;
- // Search mobj list for corpses and place them in this queue
- for(think = thinkercap.next; think != &thinkercap; think = think->next)
- {
- if(think->function != P_MobjThinker) continue;
- mo = (mobj_t *)think;
- if (!(mo->flags&MF_CORPSE)) continue; // Must be a corpse
- if (mo->flags&MF_ICECORPSE) continue; // Not ice corpses
- // Only corpses that call A_QueueCorpse from death routine
- switch(mo->type)
- {
- case MT_CENTAUR:
- case MT_CENTAURLEADER:
- case MT_DEMON:
- case MT_DEMON2:
- case MT_WRAITH:
- case MT_WRAITHB:
- case MT_BISHOP:
- case MT_ETTIN:
- case MT_PIG:
- case MT_CENTAUR_SHIELD:
- case MT_CENTAUR_SWORD:
- case MT_DEMONCHUNK1:
- case MT_DEMONCHUNK2:
- case MT_DEMONCHUNK3:
- case MT_DEMONCHUNK4:
- case MT_DEMONCHUNK5:
- case MT_DEMON2CHUNK1:
- case MT_DEMON2CHUNK2:
- case MT_DEMON2CHUNK3:
- case MT_DEMON2CHUNK4:
- case MT_DEMON2CHUNK5:
- case MT_FIREDEMON_SPLOTCH1:
- case MT_FIREDEMON_SPLOTCH2:
- A_QueueCorpse(mo); // Add corpse to queue
- break;
- default:
- break;
- }
- }
- }
- //----------------------------------------------------------------------------
- //
- // PROC A_AddPlayerCorpse
- //
- //----------------------------------------------------------------------------
- #define BODYQUESIZE 32
- mobj_t *bodyque[BODYQUESIZE];
- int bodyqueslot;
- void A_AddPlayerCorpse(mobj_t *actor)
- {
- if(bodyqueslot >= BODYQUESIZE)
- { // Too many player corpses - remove an old one
- P_RemoveMobj(bodyque[bodyqueslot%BODYQUESIZE]);
- }
- bodyque[bodyqueslot%BODYQUESIZE] = actor;
- bodyqueslot++;
- }
- //============================================================================
- //
- // A_SerpentUnHide
- //
- //============================================================================
- void A_SerpentUnHide(mobj_t *actor)
- {
- actor->flags2 &= ~MF2_DONTDRAW;
- actor->floorclip = 24*FRACUNIT;
- }
- //============================================================================
- //
- // A_SerpentHide
- //
- //============================================================================
- void A_SerpentHide(mobj_t *actor)
- {
- actor->flags2 |= MF2_DONTDRAW;
- actor->floorclip = 0;
- }
- //============================================================================
- //
- // A_SerpentChase
- //
- //============================================================================
- void A_SerpentChase(mobj_t *actor)
- {
- int delta;
- int oldX, oldY, oldFloor;
- if(actor->reactiontime)
- {
- actor->reactiontime--;
- }
- // Modify target threshold
- if(actor->threshold)
- {
- actor->threshold--;
- }
- if(gameskill == sk_nightmare)
- { // Monsters move faster in nightmare mode
- actor->tics -= actor->tics/2;
- if(actor->tics < 3)
- {
- actor->tics = 3;
- }
- }
- //
- // turn towards movement direction if not there yet
- //
- if(actor->movedir < 8)
- {
- actor->angle &= (7<<29);
- delta = actor->angle-(actor->movedir << 29);
- if(delta > 0)
- {
- actor->angle -= ANG90/2;
- }
- else if(delta < 0)
- {
- actor->angle += ANG90/2;
- }
- }
- if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- { // look for a new target
- if(P_LookForPlayers(actor, true))
- { // got a new target
- return;
- }
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
- //
- // don't attack twice in a row
- //
- if(actor->flags & MF_JUSTATTACKED)
- {
- actor->flags &= ~MF_JUSTATTACKED;
- if (gameskill != sk_nightmare)
- P_NewChaseDir (actor);
- return;
- }
- //
- // check for melee attack
- //
- if (actor->info->meleestate && P_CheckMeleeRange (actor))
- {
- if(actor->info->attacksound)
- {
- S_StartSound (actor, actor->info->attacksound);
- }
- P_SetMobjState (actor, actor->info->meleestate);
- return;
- }
- //
- // possibly choose another target
- //
- if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
- {
- if (P_LookForPlayers(actor,true))
- return; // got a new target
- }
- //
- // chase towards player
- //
- oldX = actor->x;
- oldY = actor->y;
- oldFloor = actor->subsector->sector->floorpic;
- if (--actor->movecount<0 || !P_Move (actor))
- {
- P_NewChaseDir (actor);
- }
- if(actor->subsector->sector->floorpic != oldFloor)
- {
- P_TryMove(actor, oldX, oldY);
- P_NewChaseDir (actor);
- }
- //
- // make active sound
- //
- if(actor->info->activesound && P_Random() < 3)
- {
- S_StartSound(actor, actor->info->activesound);
- }
- }
- //============================================================================
- //
- // A_SerpentRaiseHump
- //
- // Raises the hump above the surface by raising the floorclip level
- //============================================================================
- void A_SerpentRaiseHump(mobj_t *actor)
- {
- actor->floorclip -= 4*FRACUNIT;
- }
- //============================================================================
- //
- // A_SerpentLowerHump
- //
- //============================================================================
- void A_SerpentLowerHump(mobj_t *actor)
- {
- actor->floorclip += 4*FRACUNIT;
- }
- //============================================================================
- //
- // A_SerpentHumpDecide
- //
- // Decided whether to hump up, or if the mobj is a serpent leader,
- // to missile attack
- //============================================================================
- void A_SerpentHumpDecide(mobj_t *actor)
- {
- if(actor->type == MT_SERPENTLEADER)
- {
- if(P_Random() > 30)
- {
- return;
- }
- else if(P_Random() < 40)
- { // Missile attack
- P_SetMobjState(actor, S_SERPENT_SURFACE1);
- return;
- }
- }
- else if(P_Random() > 3)
- {
- return;
- }
- if(!P_CheckMeleeRange(actor))
- { // The hump shouldn't occur when within melee range
- if(actor->type == MT_SERPENTLEADER && P_Random() < 128)
- {
- P_SetMobjState(actor, S_SERPENT_SURFACE1);
- }
- else
- {
- P_SetMobjState(actor, S_SERPENT_HUMP1);
- S_StartSound(actor, SFX_SERPENT_ACTIVE);
- }
- }
- }
- //============================================================================
- //
- // A_SerpentBirthScream
- //
- //============================================================================
- void A_SerpentBirthScream(mobj_t *actor)
- {
- S_StartSound(actor, SFX_SERPENT_BIRTH);
- }
- //============================================================================
- //
- // A_SerpentDiveSound
- //
- //============================================================================
- void A_SerpentDiveSound(mobj_t *actor)
- {
- S_StartSound(actor, SFX_SERPENT_ACTIVE);
- }
- //============================================================================
- //
- // A_SerpentWalk
- //
- // Similar to A_Chase, only has a hardcoded entering of meleestate
- //============================================================================
- void A_SerpentWalk(mobj_t *actor)
- {
- int delta;
- if(actor->reactiontime)
- {
- actor->reactiontime--;
- }
- // Modify target threshold
- if(actor->threshold)
- {
- actor->threshold--;
- }
- if(gameskill == sk_nightmare)
- { // Monsters move faster in nightmare mode
- actor->tics -= actor->tics/2;
- if(actor->tics < 3)
- {
- actor->tics = 3;
- }
- }
- //
- // turn towards movement direction if not there yet
- //
- if(actor->movedir < 8)
- {
- actor->angle &= (7<<29);
- delta = actor->angle-(actor->movedir << 29);
- if(delta > 0)
- {
- actor->angle -= ANG90/2;
- }
- else if(delta < 0)
- {
- actor->angle += ANG90/2;
- }
- }
- if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- { // look for a new target
- if(P_LookForPlayers(actor, true))
- { // got a new target
- return;
- }
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
- //
- // don't attack twice in a row
- //
- if(actor->flags & MF_JUSTATTACKED)
- {
- actor->flags &= ~MF_JUSTATTACKED;
- if (gameskill != sk_nightmare)
- P_NewChaseDir (actor);
- return;
- }
- //
- // check for melee attack
- //
- if (actor->info->meleestate && P_CheckMeleeRange (actor))
- {
- if (actor->info->attacksound)
- {
- S_StartSound (actor, actor->info->attacksound);
- }
- P_SetMobjState(actor, S_SERPENT_ATK1);
- return;
- }
- //
- // possibly choose another target
- //
- if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
- {
- if (P_LookForPlayers(actor,true))
- return; // got a new target
- }
- //
- // chase towards player
- //
- if (--actor->movecount<0 || !P_Move (actor))
- {
- P_NewChaseDir (actor);
- }
- }
- //============================================================================
- //
- // A_SerpentCheckForAttack
- //
- //============================================================================
- void A_SerpentCheckForAttack(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- if(actor->type == MT_SERPENTLEADER)
- {
- if(!P_CheckMeleeRange(actor))
- {
- P_SetMobjState(actor, S_SERPENT_ATK1);
- return;
- }
- }
- if(P_CheckMeleeRange2(actor))
- {
- P_SetMobjState(actor, S_SERPENT_WALK1);
- }
- else if(P_CheckMeleeRange(actor))
- {
- if(P_Random() < 32)
- {
- P_SetMobjState(actor, S_SERPENT_WALK1);
- }
- else
- {
- P_SetMobjState(actor, S_SERPENT_ATK1);
- }
- }
- }
- //============================================================================
- //
- // A_SerpentChooseAttack
- //
- //============================================================================
- void A_SerpentChooseAttack(mobj_t *actor)
- {
- if(!actor->target || P_CheckMeleeRange(actor))
- {
- return;
- }
- if(actor->type == MT_SERPENTLEADER)
- {
- P_SetMobjState(actor, S_SERPENT_MISSILE1);
- }
- }
-
- //============================================================================
- //
- // A_SerpentMeleeAttack
- //
- //============================================================================
- void A_SerpentMeleeAttack(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(5));
- S_StartSound(actor, SFX_SERPENT_MELEEHIT);
- }
- if(P_Random() < 96)
- {
- A_SerpentCheckForAttack(actor);
- }
- }
- //============================================================================
- //
- // A_SerpentMissileAttack
- //
- //============================================================================
-
- void A_SerpentMissileAttack(mobj_t *actor)
- {
- mobj_t *mo;
- if(!actor->target)
- {
- return;
- }
- mo = P_SpawnMissile(actor, actor->target, MT_SERPENTFX);
- }
- //============================================================================
- //
- // A_SerpentHeadPop
- //
- //============================================================================
- void A_SerpentHeadPop(mobj_t *actor)
- {
- P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT, MT_SERPENT_HEAD);
- }
- //============================================================================
- //
- // A_SerpentSpawnGibs
- //
- //============================================================================
- void A_SerpentSpawnGibs(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
- actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
- MT_SERPENT_GIB1);
- if(mo)
- {
- mo->momx = (P_Random()-128)<<6;
- mo->momy = (P_Random()-128)<<6;
- mo->floorclip = 6*FRACUNIT;
- }
- mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
- actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
- MT_SERPENT_GIB2);
- if(mo)
- {
- mo->momx = (P_Random()-128)<<6;
- mo->momy = (P_Random()-128)<<6;
- mo->floorclip = 6*FRACUNIT;
- }
- mo = P_SpawnMobj(actor->x+((P_Random()-128)<<12),
- actor->y+((P_Random()-128)<<12), actor->floorz+FRACUNIT,
- MT_SERPENT_GIB3);
- if(mo)
- {
- mo->momx = (P_Random()-128)<<6;
- mo->momy = (P_Random()-128)<<6;
- mo->floorclip = 6*FRACUNIT;
- }
- }
- //============================================================================
- //
- // A_FloatGib
- //
- //============================================================================
- void A_FloatGib(mobj_t *actor)
- {
- actor->floorclip -= FRACUNIT;
- }
- //============================================================================
- //
- // A_SinkGib
- //
- //============================================================================
- void A_SinkGib(mobj_t *actor)
- {
- actor->floorclip += FRACUNIT;
- }
- //============================================================================
- //
- // A_DelayGib
- //
- //============================================================================
- void A_DelayGib(mobj_t *actor)
- {
- actor->tics -= P_Random()>>2;
- }
- //============================================================================
- //
- // A_SerpentHeadCheck
- //
- //============================================================================
- void A_SerpentHeadCheck(mobj_t *actor)
- {
- if(actor->z <= actor->floorz)
- {
- if(P_GetThingFloorType(actor) >= FLOOR_LIQUID)
- {
- P_HitFloor(actor);
- P_SetMobjState(actor, S_NULL);
- }
- else
- {
- P_SetMobjState(actor, S_SERPENT_HEAD_X1);
- }
- }
- }
- //============================================================================
- //
- // A_CentaurAttack
- //
- //============================================================================
- void A_CentaurAttack(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, P_Random()%7+3);
- }
- }
- //============================================================================
- //
- // A_CentaurAttack2
- //
- //============================================================================
- void A_CentaurAttack2(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- P_SpawnMissile(actor, actor->target, MT_CENTAUR_FX);
- S_StartSound(actor, SFX_CENTAURLEADER_ATTACK);
- }
- //============================================================================
- //
- // A_CentaurDropStuff
- //
- // Spawn shield/sword sprites when the centaur pulps //============================================================================
- void A_CentaurDropStuff(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t angle;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_CENTAUR_SHIELD);
- if(mo)
- {
- angle = actor->angle+ANG90;
- mo->momz = FRACUNIT*8+(P_Random()<<10);
- mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_CENTAUR_SWORD);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = FRACUNIT*8+(P_Random()<<10);
- mo->momx = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul(((P_Random()-128)<<11)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- }
- //============================================================================
- //
- // A_CentaurDefend
- //
- //============================================================================
- void A_CentaurDefend(mobj_t *actor)
- {
- A_FaceTarget(actor);
- if(P_CheckMeleeRange(actor) && P_Random() < 32)
- {
- A_UnSetInvulnerable(actor);
- P_SetMobjState(actor, actor->info->meleestate);
- }
- }
- //============================================================================
- //
- // A_BishopAttack
- //
- //============================================================================
- void A_BishopAttack(mobj_t *actor)
- {
- if(!actor->target)
- {
- return;
- }
- S_StartSound(actor, actor->info->attacksound);
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(4));
- return;
- }
- actor->special1 = (P_Random()&3)+5;
- }
- //============================================================================
- //
- // A_BishopAttack2
- //
- // Spawns one of a string of bishop missiles
- //============================================================================
- void A_BishopAttack2(mobj_t *actor)
- {
- mobj_t *mo;
- if(!actor->target || !actor->special1)
- {
- actor->special1 = 0;
- P_SetMobjState(actor, S_BISHOP_WALK1);
- return;
- }
- mo = P_SpawnMissile(actor, actor->target, MT_BISH_FX);
- if(mo)
- {
- mo->special1 = (int)actor->target;
- mo->special2 = 16; // High word == x/y, Low word == z
- }
- actor->special1--;
- }
- //============================================================================
- //
- // A_BishopMissileWeave
- //
- //============================================================================
- void A_BishopMissileWeave(mobj_t *actor)
- {
- fixed_t newX, newY;
- int weaveXY, weaveZ;
- int angle;
- weaveXY = actor->special2>>16;
- weaveZ = actor->special2&0xFFFF;
- angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
- newX = actor->x-FixedMul(finecosine[angle],
- FloatBobOffsets[weaveXY]<<1);
- newY = actor->y-FixedMul(finesine[angle],
- FloatBobOffsets[weaveXY]<<1);
- weaveXY = (weaveXY+2)&63;
- newX += FixedMul(finecosine[angle],
- FloatBobOffsets[weaveXY]<<1);
- newY += FixedMul(finesine[angle],
- FloatBobOffsets[weaveXY]<<1);
- P_TryMove(actor, newX, newY);
- actor->z -= FloatBobOffsets[weaveZ];
- weaveZ = (weaveZ+2)&63;
- actor->z += FloatBobOffsets[weaveZ];
- actor->special2 = weaveZ+(weaveXY<<16);
- }
- //============================================================================
- //
- // A_BishopMissileSeek
- //
- //============================================================================
- void A_BishopMissileSeek(mobj_t *actor)
- {
- P_SeekerMissile(actor, ANGLE_1*2, ANGLE_1*3);
- }
- //============================================================================
- //
- // A_BishopDecide
- //
- //============================================================================
- void A_BishopDecide(mobj_t *actor)
- {
- if(P_Random() < 220)
- {
- return;
- }
- else
- {
- P_SetMobjState(actor, S_BISHOP_BLUR1);
- }
- }
- //============================================================================
- //
- // A_BishopDoBlur
- //
- //============================================================================
- void A_BishopDoBlur(mobj_t *actor)
- {
- actor->special1 = (P_Random()&3)+3; // Random number of blurs
- if(P_Random() < 120)
- {
- P_ThrustMobj(actor, actor->angle+ANG90, 11*FRACUNIT);
- }
- else if(P_Random() > 125)
- {
- P_ThrustMobj(actor, actor->angle-ANG90, 11*FRACUNIT);
- }
- else
- { // Thrust forward
- P_ThrustMobj(actor, actor->angle, 11*FRACUNIT);
- }
- S_StartSound(actor, SFX_BISHOP_BLUR);
- }
- //============================================================================
- //
- // A_BishopSpawnBlur
- //
- //============================================================================
- void A_BishopSpawnBlur(mobj_t *actor)
- {
- mobj_t *mo;
- if(!--actor->special1)
- {
- actor->momx = 0;
- actor->momy = 0;
- if(P_Random() > 96)
- {
- P_SetMobjState(actor, S_BISHOP_WALK1);
- }
- else
- {
- P_SetMobjState(actor, S_BISHOP_ATK1);
- }
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOPBLUR);
- if(mo)
- {
- mo->angle = actor->angle;
- }
- }
- //============================================================================
- //
- // A_BishopChase
- //
- //============================================================================
- void A_BishopChase(mobj_t *actor)
- {
- actor->z -= FloatBobOffsets[actor->special2]>>1;
- actor->special2 = (actor->special2+4)&63;
- actor->z += FloatBobOffsets[actor->special2]>>1;
- }
- //============================================================================
- //
- // A_BishopPuff
- //
- //============================================================================
- void A_BishopPuff(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+40*FRACUNIT,
- MT_BISHOP_PUFF);
- if(mo)
- {
- mo->momz = FRACUNIT/2;
- }
- }
- //============================================================================
- //
- // A_BishopPainBlur
- //
- //============================================================================
- void A_BishopPainBlur(mobj_t *actor)
- {
- mobj_t *mo;
- if(P_Random() < 64)
- {
- P_SetMobjState(actor, S_BISHOP_BLUR1);
- return;
- }
- mo = P_SpawnMobj(actor->x+((P_Random()-P_Random())<<12), actor->y
- +((P_Random()-P_Random())<<12), actor->z+((P_Random()-P_Random())<<11),
- MT_BISHOPPAINBLUR);
- if(mo)
- {
- mo->angle = actor->angle;
- }
- }
- //============================================================================
- //
- // DragonSeek
- //
- //============================================================================
- static void DragonSeek(mobj_t *actor, angle_t thresh, angle_t turnMax)
- {
- int dir;
- int dist;
- angle_t delta;
- angle_t angle;
- mobj_t *target;
- int search;
- int i;
- int bestArg;
- angle_t bestAngle;
- angle_t angleToSpot, angleToTarget;
- mobj_t *mo;
- target = (mobj_t *)actor->special1;
- if(target == NULL)
- {
- return;
- }
- dir = P_FaceMobj(actor, target, &delta);
- if(delta > thresh)
- {
- delta >>= 1;
- if(delta > turnMax)
- {
- delta = turnMax;
- }
- }
- if(dir)
- { // Turn clockwise
- actor->angle += delta;
- }
- else
- { // Turn counter clockwise
- actor->angle -= delta;
- }
- angle = actor->angle>>ANGLETOFINESHIFT;
- actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
- actor->momy = FixedMul(actor->info->speed, finesine[angle]);
- if(actor->z+actor->height < target->z
- || target->z+target->height < actor->z)
- {
- dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
- dist = dist/actor->info->speed;
- if(dist < 1)
- {
- dist = 1;
- }
- actor->momz = (target->z-actor->z)/dist;
- }
- else
- {
- dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
- dist = dist/actor->info->speed;
- }
- if(target->flags&MF_SHOOTABLE && P_Random() < 64)
- { // attack the destination mobj if it's attackable
- mobj_t *oldTarget;
-
- if(abs(actor->angle-R_PointToAngle2(actor->x, actor->y,
- target->x, target->y)) < ANGLE_45/2)
- {
- oldTarget = actor->target;
- actor->target = target;
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(10));
- S_StartSound(actor, SFX_DRAGON_ATTACK);
- }
- else if(P_Random() < 128 && P_CheckMissileRange(actor))
- {
- P_SpawnMissile(actor, target, MT_DRAGON_FX);
- S_StartSound(actor, SFX_DRAGON_ATTACK);
- }
- actor->target = oldTarget;
- }
- }
- if(dist < 4)
- { // Hit the target thing
- if(actor->target && P_Random() < 200)
- {
- bestArg = -1;
- bestAngle = ANGLE_MAX;
- angleToTarget = R_PointToAngle2(actor->x, actor->y,
- actor->target->x, actor->target->y);
- for(i = 0; i < 5; i++)
- {
- if(!target->args[i])
- {
- continue;
- }
- search = -1;
- mo = P_FindMobjFromTID(target->args[i], &search);
- angleToSpot = R_PointToAngle2(actor->x, actor->y,
- mo->x, mo->y);
- if(abs(angleToSpot-angleToTarget) < bestAngle)
- {
- bestAngle = abs(angleToSpot-angleToTarget);
- bestArg = i;
- }
- }
- if(bestArg != -1)
- {
- search = -1;
- actor->special1 = (int)P_FindMobjFromTID(target->args[bestArg],
- &search);
- }
- }
- else
- {
- do
- {
- i = (P_Random()>>2)%5;
- } while(!target->args[i]);
- search = -1;
- actor->special1 = (int)P_FindMobjFromTID(target->args[i], &search);
- }
- }
- }
- //============================================================================
- //
- // A_DragonInitFlight
- //
- //============================================================================
- void A_DragonInitFlight(mobj_t *actor)
- {
- int search;
- search = -1;
- do
- { // find the first tid identical to the dragon's tid
- actor->special1 = (int)P_FindMobjFromTID(actor->tid, &search);
- if(search == -1)
- {
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
- } while(actor->special1 == (int)actor);
- P_RemoveMobjFromTIDList(actor);
- }
- //============================================================================
- //
- // A_DragonFlight
- //
- //============================================================================
- void A_DragonFlight(mobj_t *actor)
- {
- angle_t angle;
- DragonSeek(actor, 4*ANGLE_1, 8*ANGLE_1);
- if(actor->target)
- {
- if(!(actor->target->flags&MF_SHOOTABLE))
- { // target died
- actor->target = NULL;
- return;
- }
- angle = R_PointToAngle2(actor->x, actor->y, actor->target->x,
- actor->target->y);
- if(abs(actor->angle-angle) < ANGLE_45/2 && P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(8));
- S_StartSound(actor, SFX_DRAGON_ATTACK);
- }
- else if(abs(actor->angle-angle) <= ANGLE_1*20)
- {
- P_SetMobjState(actor, actor->info->missilestate);
- S_StartSound(actor, SFX_DRAGON_ATTACK);
- }
- }
- else
- {
- P_LookForPlayers(actor, true);
- }
- }
- //============================================================================
- //
- // A_DragonFlap
- //
- //============================================================================
- void A_DragonFlap(mobj_t *actor)
- {
- A_DragonFlight(actor);
- if(P_Random() < 240)
- {
- S_StartSound(actor, SFX_DRAGON_WINGFLAP);
- }
- else
- {
- S_StartSound(actor, actor->info->activesound);
- }
- }
- //============================================================================
- //
- // A_DragonAttack
- //
- //============================================================================
- void A_DragonAttack(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMissile(actor, actor->target, MT_DRAGON_FX);
- }
- //============================================================================
- //
- // A_DragonFX2
- //
- //============================================================================
- void A_DragonFX2(mobj_t *actor)
- {
- mobj_t *mo;
- int i;
- int delay;
- delay = 16+(P_Random()>>3);
- for(i = 1+(P_Random()&3); i; i--)
- {
- mo = P_SpawnMobj(actor->x+((P_Random()-128)<<14),
- actor->y+((P_Random()-128)<<14), actor->z+((P_Random()-128)<<12),
- MT_DRAGON_FX2);
- if(mo)
- {
- mo->tics = delay+(P_Random()&3)*i*2;
- mo->target = actor->target;
- }
- }
- }
- //============================================================================
- //
- // A_DragonPain
- //
- //============================================================================
- void A_DragonPain(mobj_t *actor)
- {
- A_Pain(actor);
- if(!actor->special1)
- { // no destination spot yet
- P_SetMobjState(actor, S_DRAGON_INIT);
- }
- }
- //============================================================================
- //
- // A_DragonCheckCrash
- //
- //============================================================================
- void A_DragonCheckCrash(mobj_t *actor)
- {
- if(actor->z <= actor->floorz)
- {
- P_SetMobjState(actor, S_DRAGON_CRASH1);
- }
- }
- //============================================================================
- // Demon AI
- //============================================================================
- //
- // A_DemonAttack1 (melee)
- //
- void A_DemonAttack1(mobj_t *actor)
- {
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(2));
- }
- }
- //
- // A_DemonAttack2 (missile)
- //
- void A_DemonAttack2(mobj_t *actor)
- {
- mobj_t *mo;
- int fireBall;
- if(actor->type == MT_DEMON)
- {
- fireBall = MT_DEMONFX1;
- }
- else
- {
- fireBall = MT_DEMON2FX1;
- }
- mo = P_SpawnMissile(actor, actor->target, fireBall);
- if (mo)
- {
- mo->z += 30*FRACUNIT;
- S_StartSound(actor, SFX_DEMON_MISSILE_FIRE);
- }
- }
- //
- // A_DemonDeath
- //
- void A_DemonDeath(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t angle;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMONCHUNK1);
- if(mo)
- {
- angle = actor->angle+ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMONCHUNK2);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMONCHUNK3);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMONCHUNK4);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMONCHUNK5);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- }
- //===========================================================================
- //
- // A_Demon2Death
- //
- //===========================================================================
- void A_Demon2Death(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t angle;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMON2CHUNK1);
- if(mo)
- {
- angle = actor->angle+ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMON2CHUNK2);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMON2CHUNK3);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMON2CHUNK4);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+45*FRACUNIT,
- MT_DEMON2CHUNK5);
- if(mo)
- {
- angle = actor->angle-ANG90;
- mo->momz = 8*FRACUNIT;
- mo->momx = FixedMul((P_Random()<<10)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<10)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- }
- }
- //
- // A_SinkMobj
- // Sink a mobj incrementally into the floor
- //
- boolean A_SinkMobj(mobj_t *actor)
- {
- if (actor->floorclip < actor->info->height)
- {
- switch(actor->type)
- {
- case MT_THRUSTFLOOR_DOWN:
- case MT_THRUSTFLOOR_UP:
- actor->floorclip += 6*FRACUNIT;
- break;
- default:
- actor->floorclip += FRACUNIT;
- break;
- }
- return false;
- }
- return true;
- }
- //
- // A_RaiseMobj
- // Raise a mobj incrementally from the floor to
- //
- boolean A_RaiseMobj(mobj_t *actor)
- {
- int done = true;
- // Raise a mobj from the ground
- if (actor->floorclip > 0)
- {
- switch(actor->type)
- {
- case MT_WRAITHB:
- actor->floorclip -= 2*FRACUNIT;
- break;
- case MT_THRUSTFLOOR_DOWN:
- case MT_THRUSTFLOOR_UP:
- actor->floorclip -= actor->special2*FRACUNIT;
- break;
- default:
- actor->floorclip -= 2*FRACUNIT;
- break;
- }
- if (actor->floorclip <= 0)
- {
- actor->floorclip = 0;
- done=true;
- }
- else
- {
- done = false;
- }
- }
- return done; // Reached target height
- }
- //============================================================================
- // Wraith Variables
- //
- // special1 Internal index into floatbob
- // special2
- //============================================================================
- //
- // A_WraithInit
- //
- void A_WraithInit(mobj_t *actor)
- {
- actor->z += 48<<FRACBITS;
- actor->special1 = 0; // index into floatbob
- }
- void A_WraithRaiseInit(mobj_t *actor)
- {
- actor->flags2 &= ~MF2_DONTDRAW;
- actor->flags2 &= ~MF2_NONSHOOTABLE;
- actor->flags |= MF_SHOOTABLE|MF_SOLID;
- actor->floorclip = actor->info->height;
- }
- void A_WraithRaise(mobj_t *actor)
- {
- if (A_RaiseMobj(actor))
- {
- // Reached it's target height
- P_SetMobjState(actor,S_WRAITH_CHASE1);
- }
- P_SpawnDirt(actor, actor->radius);
- }
- void A_WraithMelee(mobj_t *actor)
- {
- int amount;
- // Steal health from target and give to player
- if(P_CheckMeleeRange(actor) && (P_Random()<220))
- {
- amount = HITDICE(2);
- P_DamageMobj(actor->target, actor, actor, amount);
- actor->health += amount;
- }
- }
- void A_WraithMissile(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMissile(actor, actor->target, MT_WRAITHFX1);
- if (mo)
- {
- S_StartSound(actor, SFX_WRAITH_MISSILE_FIRE);
- }
- }
- //
- // A_WraithFX2 - spawns sparkle tail of missile
- //
- void A_WraithFX2(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t angle;
- int i;
- for (i=0; i<2; i++)
- {
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX2);
- if(mo)
- {
- if (P_Random()<128)
- {
- angle = actor->angle+(P_Random()<<22);
- }
- else
- {
- angle = actor->angle-(P_Random()<<22);
- }
- mo->momz = 0;
- mo->momx = FixedMul((P_Random()<<7)+FRACUNIT,
- finecosine[angle>>ANGLETOFINESHIFT]);
- mo->momy = FixedMul((P_Random()<<7)+FRACUNIT,
- finesine[angle>>ANGLETOFINESHIFT]);
- mo->target = actor;
- mo->floorclip = 10*FRACUNIT;
- }
- }
- }
- // Spawn an FX3 around the actor during attacks
- void A_WraithFX3(mobj_t *actor)
- {
- mobj_t *mo;
- int numdropped=P_Random()%15;
- int i;
- for (i=0; i<numdropped; i++)
- {
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX3);
- if(mo)
- {
- mo->x += (P_Random()-128)<<11;
- mo->y += (P_Random()-128)<<11;
- mo->z += (P_Random()<<10);
- mo->target = actor;
- }
- }
- }
- // Spawn an FX4 during movement
- void A_WraithFX4(mobj_t *actor)
- {
- mobj_t *mo;
- int chance = P_Random();
- int spawn4,spawn5;
- if (chance < 10)
- {
- spawn4 = true;
- spawn5 = false;
- }
- else if (chance < 20)
- {
- spawn4 = false;
- spawn5 = true;
- }
- else if (chance < 25)
- {
- spawn4 = true;
- spawn5 = true;
- }
- else
- {
- spawn4 = false;
- spawn5 = false;
- }
- if (spawn4)
- {
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX4);
- if(mo)
- {
- mo->x += (P_Random()-128)<<12;
- mo->y += (P_Random()-128)<<12;
- mo->z += (P_Random()<<10);
- mo->target = actor;
- }
- }
- if (spawn5)
- {
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_WRAITHFX5);
- if(mo)
- {
- mo->x += (P_Random()-128)<<11;
- mo->y += (P_Random()-128)<<11;
- mo->z += (P_Random()<<10);
- mo->target = actor;
- }
- }
- }
- void A_WraithLook(mobj_t *actor)
- {
- // A_WraithFX4(actor); // too expensive
- A_Look(actor);
- }
- void A_WraithChase(mobj_t *actor)
- {
- int weaveindex = actor->special1;
- actor->z += FloatBobOffsets[weaveindex];
- actor->special1 = (weaveindex+2)&63;
- // if (actor->floorclip > 0)
- // {
- // P_SetMobjState(actor, S_WRAITH_RAISE2);
- // return;
- // }
- A_Chase(actor);
- A_WraithFX4(actor);
- }
- //============================================================================
- // Ettin AI
- //============================================================================
- void A_EttinAttack(mobj_t *actor)
- {
- if(P_CheckMeleeRange(actor))
- {
- P_DamageMobj(actor->target, actor, actor, HITDICE(2));
- }
- }
- void A_DropMace(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x, actor->y,
- actor->z+(actor->height>>1), MT_ETTIN_MACE);
- if (mo)
- {
- mo->momx = (P_Random()-128)<<11;
- mo->momy = (P_Random()-128)<<11;
- mo->momz = FRACUNIT*10+(P_Random()<<10);
- mo->target = actor;
- }
- }
- //============================================================================
- // Fire Demon AI
- //
- // special1 index into floatbob
- // special2 whether strafing or not
- //============================================================================
- void A_FiredSpawnRock(mobj_t *actor)
- {
- mobj_t *mo;
- int x,y,z;
- int rtype;
- switch(P_Random()%5)
- {
- case 0:
- rtype = MT_FIREDEMON_FX1;
- break;
- case 1:
- rtype = MT_FIREDEMON_FX2;
- break;
- case 2:
- rtype = MT_FIREDEMON_FX3;
- break;
- case 3:
- rtype = MT_FIREDEMON_FX4;
- break;
- case 4:
- rtype = MT_FIREDEMON_FX5;
- break;
- }
- x = actor->x + ((P_Random()-128) << 12);
- y = actor->y + ((P_Random()-128) << 12);
- z = actor->z + ((P_Random()) << 11);
- mo = P_SpawnMobj(x,y,z,rtype);
- if (mo)
- {
- mo->target = actor;
- mo->momx = (P_Random()-128)<<10;
- mo->momy = (P_Random()-128)<<10;
- mo->momz = (P_Random()<<10);
- mo->special1 = 2; // Number bounces
- }
- // Initialize fire demon
- actor->special2 = 0;
- actor->flags &= ~MF_JUSTATTACKED;
- }
- void A_FiredRocks(mobj_t *actor)
- {
- A_FiredSpawnRock(actor);
- A_FiredSpawnRock(actor);
- A_FiredSpawnRock(actor);
- A_FiredSpawnRock(actor);
- A_FiredSpawnRock(actor);
- }
- void A_FiredAttack(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMissile(actor, actor->target, MT_FIREDEMON_FX6);
- if (mo) S_StartSound(actor, SFX_FIRED_ATTACK);
- }
- void A_SmBounce(mobj_t *actor)
- {
- // give some more momentum (x,y,&z)
- actor->z = actor->floorz + FRACUNIT;
- actor->momz = (2*FRACUNIT) + (P_Random()<<10);
- actor->momx = P_Random()%3<<FRACBITS;
- actor->momy = P_Random()%3<<FRACBITS;
- }
- #define FIREDEMON_ATTACK_RANGE 64*8*FRACUNIT
- void A_FiredChase(mobj_t *actor)
- {
- int weaveindex = actor->special1;
- mobj_t *target = actor->target;
- angle_t ang;
- fixed_t dist;
- if(actor->reactiontime) actor->reactiontime--;
- if(actor->threshold) actor->threshold--;
- // Float up and down
- actor->z += FloatBobOffsets[weaveindex];
- actor->special1 = (weaveindex+2)&63;
- // Insure it stays above certain height
- if (actor->z < actor->floorz + (64*FRACUNIT))
- {
- actor->z += 2*FRACUNIT;
- }
- if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- { // Invalid target
- P_LookForPlayers(actor,true);
- return;
- }
- // Strafe
- if (actor->special2 > 0)
- {
- actor->special2--;
- }
- else
- {
- actor->special2 = 0;
- actor->momx = actor->momy = 0;
- dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
- if (dist < FIREDEMON_ATTACK_RANGE)
- {
- if (P_Random()<30)
- {
- ang = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
- if (P_Random()<128)
- ang += ANGLE_90;
- else
- ang -= ANGLE_90;
- ang>>=ANGLETOFINESHIFT;
- actor->momx = FixedMul(8*FRACUNIT, finecosine[ang]);
- actor->momy = FixedMul(8*FRACUNIT, finesine[ang]);
- actor->special2 = 3; // strafe time
- }
- }
- }
- FaceMovementDirection(actor);
- // Normal movement
- if (!actor->special2)
- {
- if (--actor->movecount<0 || !P_Move (actor))
- {
- P_NewChaseDir (actor);
- }
- }
- // Do missile attack
- if (!(actor->flags&MF_JUSTATTACKED))
- {
- if (P_CheckMissileRange(actor) && (P_Random()<20))
- {
- P_SetMobjState (actor, actor->info->missilestate);
- actor->flags |= MF_JUSTATTACKED;
- return;
- }
- }
- else
- {
- actor->flags &= ~MF_JUSTATTACKED;
- }
- // make active sound
- if(actor->info->activesound && P_Random() < 3)
- {
- S_StartSound(actor, actor->info->activesound);
- }
- }
- void A_FiredSplotch(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH1);
- if (mo)
- {
- mo->momx = (P_Random()-128)<<11;
- mo->momy = (P_Random()-128)<<11;
- mo->momz = FRACUNIT*3 + (P_Random()<<10);
- }
- mo = P_SpawnMobj(actor->x,actor->y,actor->z, MT_FIREDEMON_SPLOTCH2);
- if (mo)
- {
- mo->momx = (P_Random()-128)<<11;
- mo->momy = (P_Random()-128)<<11;
- mo->momz = FRACUNIT*3 + (P_Random()<<10);
- }
- }
- //============================================================================
- //
- // A_IceGuyLook
- //
- //============================================================================
- void A_IceGuyLook(mobj_t *actor)
- {
- fixed_t dist;
- fixed_t an;
- A_Look(actor);
- if(P_Random() < 64)
- {
- dist = ((P_Random()-128)*actor->radius)>>7;
- an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
- P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]),
- actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT,
- MT_ICEGUY_WISP1+(P_Random()&1));
- }
- }
- //============================================================================
- //
- // A_IceGuyChase
- //
- //============================================================================
- void A_IceGuyChase(mobj_t *actor)
- {
- fixed_t dist;
- fixed_t an;
- mobj_t *mo;
- A_Chase(actor);
- if(P_Random() < 128)
- {
- dist = ((P_Random()-128)*actor->radius)>>7;
- an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
- mo = P_SpawnMobj(actor->x+FixedMul(dist, finecosine[an]),
- actor->y+FixedMul(dist, finesine[an]), actor->z+60*FRACUNIT,
- MT_ICEGUY_WISP1+(P_Random()&1));
- if(mo)
- {
- mo->momx = actor->momx;
- mo->momy = actor->momy;
- mo->momz = actor->momz;
- mo->target = actor;
- }
- }
- }
- //============================================================================
- //
- // A_IceGuyAttack
- //
- //============================================================================
- void A_IceGuyAttack(mobj_t *actor)
- {
- fixed_t an;
- if(!actor->target)
- {
- return;
- }
- an = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
- P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1,
- finecosine[an]), actor->y+FixedMul(actor->radius>>1,
- finesine[an]), actor->z+40*FRACUNIT, actor, actor->target,
- MT_ICEGUY_FX);
- an = (actor->angle-ANG90)>>ANGLETOFINESHIFT;
- P_SpawnMissileXYZ(actor->x+FixedMul(actor->radius>>1,
- finecosine[an]), actor->y+FixedMul(actor->radius>>1,
- finesine[an]), actor->z+40*FRACUNIT, actor, actor->target,
- MT_ICEGUY_FX);
- S_StartSound(actor, actor->info->attacksound);
- }
- //============================================================================
- //
- // A_IceGuyMissilePuff
- //
- //============================================================================
- void A_IceGuyMissilePuff(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+2*FRACUNIT, MT_ICEFX_PUFF);
- }
- //============================================================================
- //
- // A_IceGuyDie
- //
- //============================================================================
- void A_IceGuyDie(mobj_t *actor)
- {
- void A_FreezeDeathChunks(mobj_t *actor);
- actor->momx = 0;
- actor->momy = 0;
- actor->momz = 0;
- actor->height <<= 2;
- A_FreezeDeathChunks(actor);
- }
- //============================================================================
- //
- // A_IceGuyMissileExplode
- //
- //============================================================================
- void A_IceGuyMissileExplode(mobj_t *actor)
- {
- mobj_t *mo;
- int i;
- for(i = 0; i < 8; i++)
- {
- mo = P_SpawnMissileAngle(actor, MT_ICEGUY_FX2, i*ANG45, -0.3*FRACUNIT);
- if(mo)
- {
- mo->target = actor->target;
- }
- }
- }
- //============================================================================
- //
- // Sorcerer stuff
- //
- // Sorcerer Variables
- // special1 Angle of ball 1 (all others relative to that)
- // special2 which ball to stop at in stop mode (MT_???)
- // args[0] Denfense time
- // args[1] Number of full rotations since stopping mode
- // args[2] Target orbit speed for acceleration/deceleration
- // args[3] Movement mode (see SORC_ macros)
- // args[4] Current ball orbit speed
- // Sorcerer Ball Variables
- // special1 Previous angle of ball (for woosh)
- // special2 Countdown of rapid fire (FX4)
- // args[0] If set, don't play the bounce sound when bouncing
- //============================================================================
- #define SORCBALL_INITIAL_SPEED 7
- #define SORCBALL_TERMINAL_SPEED 25
- #define SORCBALL_SPEED_ROTATIONS 5
- #define SORC_DEFENSE_TIME 255
- #define SORC_DEFENSE_HEIGHT 45
- #define BOUNCE_TIME_UNIT (35/2)
- #define SORCFX4_RAPIDFIRE_TIME (6*3) // 3 seconds
- #define SORCFX4_SPREAD_ANGLE 20
- #define SORC_DECELERATE 0
- #define SORC_ACCELERATE 1
- #define SORC_STOPPING 2
- #define SORC_FIRESPELL 3
- #define SORC_STOPPED 4
- #define SORC_NORMAL 5
- #define SORC_FIRING_SPELL 6
- #define BALL1_ANGLEOFFSET 0
- #define BALL2_ANGLEOFFSET (ANGLE_MAX/3)
- #define BALL3_ANGLEOFFSET ((ANGLE_MAX/3)*2)
- void A_SorcBallOrbit(mobj_t *actor);
- void A_SorcSpinBalls(mobj_t *actor);
- void A_SpeedBalls(mobj_t *actor);
- void A_SlowBalls(mobj_t *actor);
- void A_StopBalls(mobj_t *actor);
- void A_AccelBalls(mobj_t *actor);
- void A_DecelBalls(mobj_t *actor);
- void A_SorcBossAttack(mobj_t *actor);
- void A_SpawnFizzle(mobj_t *actor);
- void A_CastSorcererSpell(mobj_t *actor);
- void A_SorcUpdateBallAngle(mobj_t *actor);
- void A_BounceCheck(mobj_t *actor);
- void A_SorcFX1Seek(mobj_t *actor);
- void A_SorcOffense1(mobj_t *actor);
- void A_SorcOffense2(mobj_t *actor);
- // Spawn spinning balls above head - actor is sorcerer
- void A_SorcSpinBalls(mobj_t *actor)
- {
- mobj_t *mo;
- fixed_t z;
- A_SlowBalls(actor);
- actor->args[0] = 0; // Currently no defense
- actor->args[3] = SORC_NORMAL;
- actor->args[4] = SORCBALL_INITIAL_SPEED; // Initial orbit speed
- actor->special1 = ANGLE_1;
- z = actor->z - actor->floorclip + actor->info->height;
-
- mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL1);
- if (mo)
- {
- mo->target = actor;
- mo->special2 = SORCFX4_RAPIDFIRE_TIME;
- }
- mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL2);
- if (mo) mo->target = actor;
- mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCBALL3);
- if (mo) mo->target = actor;
- }
- //
- // A_SorcBallOrbit() ==========================================
- //
- void A_SorcBallOrbit(mobj_t *actor)
- {
- int x,y;
- angle_t angle, baseangle;
- int mode = actor->target->args[3];
- mobj_t *parent = (mobj_t *)actor->target;
- int dist = parent->radius - (actor->radius<<1);
- angle_t prevangle = actor->special1;
-
- if (actor->target->health <= 0)
- P_SetMobjState(actor, actor->info->painstate);
- baseangle = (angle_t)parent->special1;
- switch(actor->type)
- {
- case MT_SORCBALL1:
- angle = baseangle + BALL1_ANGLEOFFSET;
- break;
- case MT_SORCBALL2:
- angle = baseangle + BALL2_ANGLEOFFSET;
- break;
- case MT_SORCBALL3:
- angle = baseangle + BALL3_ANGLEOFFSET;
- break;
- default:
- I_Error("corrupted sorcerer");
- break;
- }
- actor->angle = angle;
- angle >>= ANGLETOFINESHIFT;
- switch(mode)
- {
- case SORC_NORMAL: // Balls rotating normally
- A_SorcUpdateBallAngle(actor);
- break;
- case SORC_DECELERATE: // Balls decelerating
- A_DecelBalls(actor);
- A_SorcUpdateBallAngle(actor);
- break;
- case SORC_ACCELERATE: // Balls accelerating
- A_AccelBalls(actor);
- A_SorcUpdateBallAngle(actor);
- break;
- case SORC_STOPPING: // Balls stopping
- if ((parent->special2 == actor->type) &&
- (parent->args[1] > SORCBALL_SPEED_ROTATIONS) &&
- (abs(angle - (parent->angle>>ANGLETOFINESHIFT)) < (30<<5)))
- {
- // Can stop now
- actor->target->args[3] = SORC_FIRESPELL;
- actor->target->args[4] = 0;
- // Set angle so ball angle == sorcerer angle
- switch(actor->type)
- {
- case MT_SORCBALL1:
- parent->special1 = (int)(parent->angle -
- BALL1_ANGLEOFFSET);
- break;
- case MT_SORCBALL2:
- parent->special1 = (int)(parent->angle -
- BALL2_ANGLEOFFSET);
- break;
- case MT_SORCBALL3:
- parent->special1 = (int)(parent->angle -
- BALL3_ANGLEOFFSET);
- break;
- default:
- break;
- }
- }
- else
- {
- A_SorcUpdateBallAngle(actor);
- }
- break;
- case SORC_FIRESPELL: // Casting spell
- if (parent->special2 == actor->type)
- {
- // Put sorcerer into special throw spell anim
- if (parent->health > 0)
- P_SetMobjStateNF(parent, S_SORC_ATTACK1);
- if (actor->type==MT_SORCBALL1 && P_Random()<200)
- {
- S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
- actor->special2 = SORCFX4_RAPIDFIRE_TIME;
- actor->args[4] = 128;
- parent->args[3] = SORC_FIRING_SPELL;
- }
- else
- {
- A_CastSorcererSpell(actor);
- parent->args[3] = SORC_STOPPED;
- }
- }
- break;
- case SORC_FIRING_SPELL:
- if (parent->special2 == actor->type)
- {
- if (actor->special2-- <= 0)
- {
- // Done rapid firing
- parent->args[3] = SORC_STOPPED;
- // Back to orbit balls
- if (parent->health > 0)
- P_SetMobjStateNF(parent, S_SORC_ATTACK4);
- }
- else
- {
- // Do rapid fire spell
- A_SorcOffense2(actor);
- }
- }
- break;
- case SORC_STOPPED: // Balls stopped
- default:
- break;
- }
- if ((angle < prevangle) && (parent->args[4]==SORCBALL_TERMINAL_SPEED))
- {
- parent->args[1]++; // Bump rotation counter
- // Completed full rotation - make woosh sound
- S_StartSound(actor, SFX_SORCERER_BALLWOOSH);
- }
- actor->special1 = angle; // Set previous angle
- x = parent->x + FixedMul(dist, finecosine[angle]);
- y = parent->y + FixedMul(dist, finesine[angle]);
- actor->x = x;
- actor->y = y;
- actor->z = parent->z - parent->floorclip + parent->info->height;
- }
- //
- // Set balls to speed mode - actor is sorcerer
- //
- void A_SpeedBalls(mobj_t *actor)
- {
- actor->args[3] = SORC_ACCELERATE; // speed mode
- actor->args[2] = SORCBALL_TERMINAL_SPEED; // target speed
- }
- //
- // Set balls to slow mode - actor is sorcerer
- //
- void A_SlowBalls(mobj_t *actor)
- {
- actor->args[3] = SORC_DECELERATE; // slow mode
- actor->args[2] = SORCBALL_INITIAL_SPEED; // target speed
- }
- //
- // Instant stop when rotation gets to ball in special2
- // actor is sorcerer
- //
- void A_StopBalls(mobj_t *actor)
- {
- int chance = P_Random();
- actor->args[3] = SORC_STOPPING; // stopping mode
- actor->args[1] = 0; // Reset rotation counter
- if ((actor->args[0] <= 0) && (chance < 200))
- {
- actor->special2 = MT_SORCBALL2; // Blue
- }
- else if((actor->health < (actor->info->spawnhealth >> 1)) &&
- (chance < 200))
- {
- actor->special2 = MT_SORCBALL3; // Green
- }
- else
- {
- actor->special2 = MT_SORCBALL1; // Yellow
- }
- }
- //
- // Increase ball orbit speed - actor is ball
- //
- void A_AccelBalls(mobj_t *actor)
- {
- mobj_t *sorc = actor->target;
- if (sorc->args[4] < sorc->args[2])
- {
- sorc->args[4]++;
- }
- else
- {
- sorc->args[3] = SORC_NORMAL;
- if (sorc->args[4] >= SORCBALL_TERMINAL_SPEED)
- {
- // Reached terminal velocity - stop balls
- A_StopBalls(sorc);
- }
- }
- }
- // Decrease ball orbit speed - actor is ball
- void A_DecelBalls(mobj_t *actor)
- {
- mobj_t *sorc = actor->target;
- if (sorc->args[4] > sorc->args[2])
- {
- sorc->args[4]--;
- }
- else
- {
- sorc->args[3] = SORC_NORMAL;
- }
- }
- // Update angle if first ball - actor is ball
- void A_SorcUpdateBallAngle(mobj_t *actor)
- {
- if (actor->type == MT_SORCBALL1)
- {
- actor->target->special1 += ANGLE_1*actor->target->args[4];
- }
- }
- // actor is ball
- void A_CastSorcererSpell(mobj_t *actor)
- {
- mobj_t *mo;
- int spell = actor->type;
- angle_t ang1,ang2;
- fixed_t z;
- mobj_t *parent = actor->target;
- S_StartSound(NULL, SFX_SORCERER_SPELLCAST);
- // Put sorcerer into throw spell animation
- if (parent->health > 0) P_SetMobjStateNF(parent, S_SORC_ATTACK4);
- switch(spell)
- {
- case MT_SORCBALL1: // Offensive
- A_SorcOffense1(actor);
- break;
- case MT_SORCBALL2: // Defensive
- z = parent->z - parent->floorclip +
- SORC_DEFENSE_HEIGHT*FRACUNIT;
- mo = P_SpawnMobj(actor->x, actor->y, z, MT_SORCFX2);
- parent->flags2 |= MF2_REFLECTIVE|MF2_INVULNERABLE;
- parent->args[0] = SORC_DEFENSE_TIME;
- if (mo) mo->target = parent;
- break;
- case MT_SORCBALL3: // Reinforcements
- ang1 = actor->angle - ANGLE_45;
- ang2 = actor->angle + ANGLE_45;
- if(actor->health < (actor->info->spawnhealth/3))
- { // Spawn 2 at a time
- mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT);
- if (mo) mo->target = parent;
- mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang2, 4*FRACUNIT);
- if (mo) mo->target = parent;
- }
- else
- {
- if (P_Random() < 128)
- ang1 = ang2;
- mo = P_SpawnMissileAngle(parent, MT_SORCFX3, ang1, 4*FRACUNIT);
- if (mo) mo->target = parent;
- }
- break;
- default:
- break;
- }
- }
- /*
- void A_SpawnReinforcements(mobj_t *actor)
- {
- mobj_t *parent = actor->target;
- mobj_t *mo;
- angle_t ang;
- ang = ANGLE_1 * P_Random();
- mo = P_SpawnMissileAngle(actor, MT_SORCFX3, ang, 5*FRACUNIT);
- if (mo) mo->target = parent;
- }
- */
- // actor is ball
- void A_SorcOffense1(mobj_t *actor)
- {
- mobj_t *mo;
- angle_t ang1,ang2;
- mobj_t *parent=(mobj_t *)actor->target;
- ang1 = actor->angle + ANGLE_1*70;
- ang2 = actor->angle - ANGLE_1*70;
- mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang1, 0);
- if (mo)
- {
- mo->target = parent;
- mo->special1 = (int)parent->target;
- mo->args[4] = BOUNCE_TIME_UNIT;
- mo->args[3] = 15; // Bounce time in seconds
- }
- mo = P_SpawnMissileAngle(parent, MT_SORCFX1, ang2, 0);
- if (mo)
- {
- mo->target = parent;
- mo->special1 = (int)parent->target;
- mo->args[4] = BOUNCE_TIME_UNIT;
- mo->args[3] = 15; // Bounce time in seconds
- }
- }
- // Actor is ball
- void A_SorcOffense2(mobj_t *actor)
- {
- angle_t ang1;
- mobj_t *mo;
- int delta, index;
- mobj_t *parent = actor->target;
- mobj_t *dest = parent->target;
- int dist;
- index = actor->args[4] << 5;
- actor->args[4] += 15;
- delta = (finesine[index])*SORCFX4_SPREAD_ANGLE;
- delta = (delta>>FRACBITS)*ANGLE_1;
- ang1 = actor->angle + delta;
- mo = P_SpawnMissileAngle(parent, MT_SORCFX4, ang1, 0);
- if (mo)
- {
- mo->special2 = 35*5/2; // 5 seconds
- dist = P_AproxDistance(dest->x - mo->x, dest->y - mo->y);
- dist = dist/mo->info->speed;
- if(dist < 1) dist = 1;
- mo->momz = (dest->z-mo->z)/dist;
- }
- }
- // Resume ball spinning
- void A_SorcBossAttack(mobj_t *actor)
- {
- actor->args[3] = SORC_ACCELERATE;
- actor->args[2] = SORCBALL_INITIAL_SPEED;
- }
- // spell cast magic fizzle
- void A_SpawnFizzle(mobj_t *actor)
- {
- fixed_t x,y,z;
- fixed_t dist = 5*FRACUNIT;
- angle_t angle = actor->angle >> ANGLETOFINESHIFT;
- fixed_t speed = actor->info->speed;
- angle_t rangle;
- mobj_t *mo;
- int ix;
- x = actor->x + FixedMul(dist,finecosine[angle]);
- y = actor->y + FixedMul(dist,finesine[angle]);
- z = actor->z - actor->floorclip + (actor->height>>1);
- for (ix=0; ix<5; ix++)
- {
- mo = P_SpawnMobj(x,y,z,MT_SORCSPARK1);
- if (mo)
- {
- rangle = angle + ((P_Random()%5) << 1);
- mo->momx = FixedMul(P_Random()%speed,finecosine[rangle]);
- mo->momy = FixedMul(P_Random()%speed,finesine[rangle]);
- mo->momz = FRACUNIT*2;
- }
- }
- }
- //============================================================================
- // Yellow spell - offense
- //============================================================================
- void A_SorcFX1Seek(mobj_t *actor)
- {
- A_BounceCheck(actor);
- P_SeekerMissile(actor,ANGLE_1*2,ANGLE_1*6);
- }
- //============================================================================
- // Blue spell - defense
- //============================================================================
- //
- // FX2 Variables
- // special1 current angle
- // special2
- // args[0] 0 = CW, 1 = CCW
- // args[1]
- //============================================================================
- // Split ball in two
- void A_SorcFX2Split(mobj_t *actor)
- {
- mobj_t *mo;
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
- if (mo)
- {
- mo->target = actor->target;
- mo->args[0] = 0; // CW
- mo->special1 = actor->angle; // Set angle
- P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
- }
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX2);
- if (mo)
- {
- mo->target = actor->target;
- mo->args[0] = 1; // CCW
- mo->special1 = actor->angle; // Set angle
- P_SetMobjStateNF(mo, S_SORCFX2_ORBIT1);
- }
- P_SetMobjStateNF(actor, S_NULL);
- }
- // Orbit FX2 about sorcerer
- void A_SorcFX2Orbit(mobj_t *actor)
- {
- angle_t angle;
- fixed_t x,y,z;
- mobj_t *parent = actor->target;
- fixed_t dist = parent->info->radius;
- if ((parent->health <= 0) || // Sorcerer is dead
- (!parent->args[0])) // Time expired
- {
- P_SetMobjStateNF(actor, actor->info->deathstate);
- parent->args[0] = 0;
- parent->flags2 &= ~MF2_REFLECTIVE;
- parent->flags2 &= ~MF2_INVULNERABLE;
- }
- if (actor->args[0] && (parent->args[0]-- <= 0)) // Time expired
- {
- P_SetMobjStateNF(actor, actor->info->deathstate);
- parent->args[0] = 0;
- parent->flags2 &= ~MF2_REFLECTIVE;
- }
- // Move to new position based on angle
- if (actor->args[0]) // Counter clock-wise
- {
- actor->special1 += ANGLE_1*10;
- angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT;
- x = parent->x + FixedMul(dist, finecosine[angle]);
- y = parent->y + FixedMul(dist, finesine[angle]);
- z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
- z += FixedMul(15*FRACUNIT,finecosine[angle]);
- // Spawn trailer
- P_SpawnMobj(x,y,z, MT_SORCFX2_T1);
- }
- else // Clock wise
- {
- actor->special1 -= ANGLE_1*10;
- angle = ((angle_t)actor->special1) >> ANGLETOFINESHIFT;
- x = parent->x + FixedMul(dist, finecosine[angle]);
- y = parent->y + FixedMul(dist, finesine[angle]);
- z = parent->z - parent->floorclip + SORC_DEFENSE_HEIGHT*FRACUNIT;
- z += FixedMul(20*FRACUNIT,finesine[angle]);
- // Spawn trailer
- P_SpawnMobj(x,y,z, MT_SORCFX2_T1);
- }
- actor->x = x;
- actor->y = y;
- actor->z = z;
- }
- //============================================================================
- // Green spell - spawn bishops
- //============================================================================
- void A_SpawnBishop(mobj_t *actor)
- {
- mobj_t *mo;
- mo=P_SpawnMobj(actor->x, actor->y, actor->z, MT_BISHOP);
- if(mo)
- {
- if(!P_TestMobjLocation(mo))
- {
- P_SetMobjState(mo, S_NULL);
- }
- }
- P_SetMobjState(actor, S_NULL);
- }
- /*
- void A_SmokePuffEntry(mobj_t *actor)
- {
- P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKE);
- }
- */
- void A_SmokePuffExit(mobj_t *actor)
- {
- P_SpawnMobj(actor->x, actor->y, actor->z, MT_MNTRSMOKEEXIT);
- }
- void A_SorcererBishopEntry(mobj_t *actor)
- {
- P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCFX3_EXPLOSION);
- S_StartSound(actor, actor->info->seesound);
- }
- //============================================================================
- // FX4 - rapid fire balls
- //============================================================================
- void A_SorcFX4Check(mobj_t *actor)
- {
- if (actor->special2-- <= 0)
- {
- P_SetMobjStateNF(actor, actor->info->deathstate);
- }
- }
- //============================================================================
- // Ball death - spawn stuff
- //============================================================================
- void A_SorcBallPop(mobj_t *actor)
- {
- S_StartSound(NULL, SFX_SORCERER_BALLPOP);
- actor->flags &= ~MF_NOGRAVITY;
- actor->flags2 |= MF2_LOGRAV;
- actor->momx = ((P_Random()%10)-5) << FRACBITS;
- actor->momy = ((P_Random()%10)-5) << FRACBITS;
- actor->momz = (2+(P_Random()%3)) << FRACBITS;
- actor->special2 = 4*FRACUNIT; // Initial bounce factor
- actor->args[4] = BOUNCE_TIME_UNIT; // Bounce time unit
- actor->args[3] = 5; // Bounce time in seconds
- }
- void A_BounceCheck(mobj_t *actor)
- {
- if (actor->args[4]-- <= 0)
- {
- if (actor->args[3]-- <= 0)
- {
- P_SetMobjState(actor, actor->info->deathstate);
- switch(actor->type)
- {
- case MT_SORCBALL1:
- case MT_SORCBALL2:
- case MT_SORCBALL3:
- S_StartSound(NULL, SFX_SORCERER_BIGBALLEXPLODE);
- break;
- case MT_SORCFX1:
- S_StartSound(NULL, SFX_SORCERER_HEADSCREAM);
- break;
- default:
- break;
- }
- }
- else
- {
- actor->args[4] = BOUNCE_TIME_UNIT;
- }
- }
- }
- //============================================================================
- // Class Bosses
- //============================================================================
- #define CLASS_BOSS_STRAFE_RANGE 64*10*FRACUNIT
- void A_FastChase(mobj_t *actor)
- {
- int delta;
- fixed_t dist;
- angle_t ang;
- mobj_t *target;
- if(actor->reactiontime)
- {
- actor->reactiontime--;
- }
- // Modify target threshold
- if(actor->threshold)
- {
- actor->threshold--;
- }
- if(gameskill == sk_nightmare)
- { // Monsters move faster in nightmare mode
- actor->tics -= actor->tics/2;
- if(actor->tics < 3)
- {
- actor->tics = 3;
- }
- }
- //
- // turn towards movement direction if not there yet
- //
- if(actor->movedir < 8)
- {
- actor->angle &= (7<<29);
- delta = actor->angle-(actor->movedir << 29);
- if(delta > 0)
- {
- actor->angle -= ANG90/2;
- }
- else if(delta < 0)
- {
- actor->angle += ANG90/2;
- }
- }
- if(!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- { // look for a new target
- if(P_LookForPlayers(actor, true))
- { // got a new target
- return;
- }
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
- //
- // don't attack twice in a row
- //
- if(actor->flags & MF_JUSTATTACKED)
- {
- actor->flags &= ~MF_JUSTATTACKED;
- if (gameskill != sk_nightmare)
- P_NewChaseDir (actor);
- return;
- }
- // Strafe
- if (actor->special2 > 0)
- {
- actor->special2--;
- }
- else
- {
- target = actor->target;
- actor->special2 = 0;
- actor->momx = actor->momy = 0;
- dist=P_AproxDistance(actor->x - target->x,
- actor->y - target->y);
- if (dist < CLASS_BOSS_STRAFE_RANGE)
- {
- if (P_Random()<100)
- {
- ang = R_PointToAngle2(actor->x, actor->y,
- target->x, target->y);
- if (P_Random()<128)
- ang += ANGLE_90;
- else
- ang -= ANGLE_90;
- ang>>=ANGLETOFINESHIFT;
- actor->momx = FixedMul(13*FRACUNIT, finecosine[ang]);
- actor->momy = FixedMul(13*FRACUNIT, finesine[ang]);
- actor->special2 = 3; // strafe time
- }
- }
- }
- //
- // check for missile attack
- //
- if (actor->info->missilestate)
- {
- if (gameskill < sk_nightmare && actor->movecount)
- goto nomissile;
- if (!P_CheckMissileRange (actor))
- goto nomissile;
- P_SetMobjState (actor, actor->info->missilestate);
- actor->flags |= MF_JUSTATTACKED;
- return;
- }
- nomissile:
- //
- // possibly choose another target
- //
- if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
- {
- if (P_LookForPlayers(actor,true))
- return; // got a new target
- }
- //
- // chase towards player
- //
- if (!actor->special2)
- {
- if (--actor->movecount<0 || !P_Move (actor))
- {
- P_NewChaseDir (actor);
- }
- }
- }
- void A_FighterAttack(mobj_t *actor)
- {
- extern void A_FSwordAttack2(mobj_t *actor);
- if(!actor->target) return;
- A_FSwordAttack2(actor);
- }
- void A_ClericAttack(mobj_t *actor)
- {
- extern void A_CHolyAttack3(mobj_t *actor);
- if(!actor->target) return;
- A_CHolyAttack3(actor);
- }
- void A_MageAttack(mobj_t *actor)
- {
- extern void A_MStaffAttack2(mobj_t *actor);
- if(!actor->target) return;
- A_MStaffAttack2(actor);
- }
- void A_ClassBossHealth(mobj_t *actor)
- {
- if (netgame && !deathmatch) // co-op only
- {
- if (!actor->special1)
- {
- actor->health *= 5;
- actor->special1 = true; // has been initialized
- }
- }
- }
- //===========================================================================
- //
- // A_CheckFloor - Checks if an object hit the floor
- //
- //===========================================================================
- void A_CheckFloor(mobj_t *actor)
- {
- if(actor->z <= actor->floorz)
- {
- actor->z = actor->floorz;
- actor->flags2 &= ~MF2_LOGRAV;
- P_SetMobjState(actor, actor->info->deathstate);
- }
- }
- //============================================================================
- //
- // A_FreezeDeath
- //
- //============================================================================
- void A_FreezeDeath(mobj_t *actor)
- {
- actor->tics = 75+P_Random()+P_Random();
- actor->flags |= MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD;
- actor->flags2 |= MF2_PUSHABLE|MF2_TELESTOMP|MF2_PASSMOBJ|MF2_SLIDE;
- actor->height <<= 2;
- S_StartSound(actor, SFX_FREEZE_DEATH);
- if(actor->player)
- {
- actor->player->damagecount = 0;
- actor->player->poisoncount = 0;
- actor->player->bonuscount = 0;
- if(actor->player == &players[consoleplayer])
- {
- SB_PaletteFlash(false);
- }
- }
- else if(actor->flags&MF_COUNTKILL && actor->special)
- { // Initiate monster death actions
- P_ExecuteLineSpecial(actor->special, actor->args, NULL, 0, actor);
- }
- }
- //============================================================================
- //
- // A_IceSetTics
- //
- //============================================================================
- void A_IceSetTics(mobj_t *actor)
- {
- int floor;
- actor->tics = 70+(P_Random()&63);
- floor = P_GetThingFloorType(actor);
- if(floor == FLOOR_LAVA)
- {
- actor->tics >>= 2;
- }
- else if(floor == FLOOR_ICE)
- {
- actor->tics <<= 1;
- }
- }
- //============================================================================
- //
- // A_IceCheckHeadDone
- //
- //============================================================================
- void A_IceCheckHeadDone(mobj_t *actor)
- {
- if(actor->special2 == 666)
- {
- P_SetMobjState(actor, S_ICECHUNK_HEAD2);
- }
- }
- //============================================================================
- //
- // A_FreezeDeathChunks
- //
- //============================================================================
- void A_FreezeDeathChunks(mobj_t *actor)
- {
- int i;
- mobj_t *mo;
-
- if(actor->momx || actor->momy || actor->momz)
- {
- actor->tics = 105;
- return;
- }
- S_StartSound(actor, SFX_FREEZE_SHATTER);
- for(i = 12+(P_Random()&15); i >= 0; i--)
- {
- mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7),
- actor->y+(((P_Random()-128)*actor->radius)>>7),
- actor->z+(P_Random()*actor->height/255), MT_ICECHUNK);
- P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3));
- if(mo)
- {
- mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
- mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
- mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
- A_IceSetTics(mo); // set a random tic wait
- }
- }
- for(i = 12+(P_Random()&15); i >= 0; i--)
- {
- mo = P_SpawnMobj(actor->x+(((P_Random()-128)*actor->radius)>>7),
- actor->y+(((P_Random()-128)*actor->radius)>>7),
- actor->z+(P_Random()*actor->height/255), MT_ICECHUNK);
- P_SetMobjState(mo, mo->info->spawnstate+(P_Random()%3));
- if(mo)
- {
- mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
- mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
- mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
- A_IceSetTics(mo); // set a random tic wait
- }
- }
- if(actor->player)
- { // attach the player's view to a chunk of ice
- mo = P_SpawnMobj(actor->x, actor->y, actor->z+VIEWHEIGHT, MT_ICECHUNK);
- P_SetMobjState(mo, S_ICECHUNK_HEAD);
- mo->momz = FixedDiv(mo->z-actor->z, actor->height)<<2;
- mo->momx = (P_Random()-P_Random())<<(FRACBITS-7);
- mo->momy = (P_Random()-P_Random())<<(FRACBITS-7);
- mo->flags2 |= MF2_ICEDAMAGE; // used to force blue palette
- mo->flags2 &= ~MF2_FLOORCLIP;
- mo->player = actor->player;
- actor->player = NULL;
- mo->health = actor->health;
- mo->angle = actor->angle;
- mo->player->mo = mo;
- mo->player->lookdir = 0;
- }
- P_RemoveMobjFromTIDList(actor);
- P_SetMobjState(actor, S_FREETARGMOBJ);
- actor->flags2 |= MF2_DONTDRAW;
- }
- //===========================================================================
- // Korax Variables
- // special1 last teleport destination
- // special2 set if "below half" script not yet run
- //
- // Korax Scripts (reserved)
- // 249 Tell scripts that we are below half health
- // 250-254 Control scripts
- // 255 Death script
- //
- // Korax TIDs (reserved)
- // 245 Reserved for Korax himself
- // 248 Initial teleport destination
- // 249 Teleport destination
- // 250-254 For use in respective control scripts
- // 255 For use in death script (spawn spots)
- //===========================================================================
- #define KORAX_SPIRIT_LIFETIME (5*(35/5)) // 5 seconds
- #define KORAX_COMMAND_HEIGHT (120*FRACUNIT)
- #define KORAX_COMMAND_OFFSET (27*FRACUNIT)
- void KoraxFire1(mobj_t *actor, int type);
- void KoraxFire2(mobj_t *actor, int type);
- void KoraxFire3(mobj_t *actor, int type);
- void KoraxFire4(mobj_t *actor, int type);
- void KoraxFire5(mobj_t *actor, int type);
- void KoraxFire6(mobj_t *actor, int type);
- void KSpiritInit(mobj_t *spirit, mobj_t *korax);
- #define KORAX_TID (245)
- #define KORAX_FIRST_TELEPORT_TID (248)
- #define KORAX_TELEPORT_TID (249)
- void A_KoraxChase(mobj_t *actor)
- {
- mobj_t *spot;
- int lastfound;
- byte args[3]={0,0,0};
- if ((!actor->special2) &&
- (actor->health <= (actor->info->spawnhealth/2)))
- {
- lastfound = 0;
- spot = P_FindMobjFromTID(KORAX_FIRST_TELEPORT_TID, &lastfound);
- if (spot)
- {
- P_Teleport(actor, spot->x, spot->y, spot->angle, true);
- }
- P_StartACS(249, 0, args, actor, NULL, 0);
- actor->special2 = 1; // Don't run again
- return;
- }
- if (!actor->target) return;
- if (P_Random()<30)
- {
- P_SetMobjState(actor, actor->info->missilestate);
- }
- else if (P_Random()<30)
- {
- S_StartSound(NULL, SFX_KORAX_ACTIVE);
- }
- // Teleport away
- if (actor->health < (actor->info->spawnhealth>>1))
- {
- if (P_Random()<10)
- {
- lastfound = actor->special1;
- spot = P_FindMobjFromTID(KORAX_TELEPORT_TID, &lastfound);
- actor->special1 = lastfound;
- if (spot)
- {
- P_Teleport(actor, spot->x, spot->y, spot->angle, true);
- }
- }
- }
- }
- void A_KoraxStep(mobj_t *actor)
- {
- A_Chase(actor);
- }
- void A_KoraxStep2(mobj_t *actor)
- {
- S_StartSound(NULL, SFX_KORAX_STEP);
- A_Chase(actor);
- }
- void A_KoraxBonePop(mobj_t *actor)
- {
- fixed_t x,y,z;
- mobj_t *mo;
- byte args[5];
- args[0]=args[1]=args[2]=args[3]=args[4]=0;
- x=actor->x, y=actor->y, z=actor->z;
- // Spawn 6 spirits equalangularly
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT1, ANGLE_60*0, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT2, ANGLE_60*1, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT3, ANGLE_60*2, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT4, ANGLE_60*3, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT5, ANGLE_60*4, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- mo = P_SpawnMissileAngle(actor,MT_KORAX_SPIRIT6, ANGLE_60*5, 5*FRACUNIT);
- if (mo) KSpiritInit(mo,actor);
- P_StartACS(255, 0, args, actor, NULL, 0); // Death script
- }
- void KSpiritInit(mobj_t *spirit, mobj_t *korax)
- {
- int i;
- mobj_t *tail, *next;
- spirit->health = KORAX_SPIRIT_LIFETIME;
- spirit->special1 = (int)korax; // Swarm around korax
- spirit->special2 = 32+(P_Random()&7); // Float bob index
- spirit->args[0] = 10; // initial turn value
- spirit->args[1] = 0; // initial look angle
- // Spawn a tail for spirit
- tail = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
- tail->special2 = (int)spirit; // parent
- for(i = 1; i < 3; i++)
- {
- next = P_SpawnMobj(spirit->x, spirit->y, spirit->z, MT_HOLY_TAIL);
- P_SetMobjState(next, next->info->spawnstate+1);
- tail->special1 = (int)next;
- tail = next;
- }
- tail->special1 = 0; // last tail bit
- }
- void A_KoraxDecide(mobj_t *actor)
- {
- if (P_Random()<220)
- {
- P_SetMobjState(actor, S_KORAX_MISSILE1);
- }
- else
- {
- P_SetMobjState(actor, S_KORAX_COMMAND1);
- }
- }
- void A_KoraxMissile(mobj_t *actor)
- {
- int type = P_Random()%6;
- int sound;
- S_StartSound(actor, SFX_KORAX_ATTACK);
- switch(type)
- {
- case 0:
- type = MT_WRAITHFX1;
- sound = SFX_WRAITH_MISSILE_FIRE;
- break;
- case 1:
- type = MT_DEMONFX1;
- sound = SFX_DEMON_MISSILE_FIRE;
- break;
- case 2:
- type = MT_DEMON2FX1;
- sound = SFX_DEMON_MISSILE_FIRE;
- break;
- case 3:
- type = MT_FIREDEMON_FX6;
- sound = SFX_FIRED_ATTACK;
- break;
- case 4:
- type = MT_CENTAUR_FX;
- sound = SFX_CENTAURLEADER_ATTACK;
- break;
- case 5:
- type = MT_SERPENTFX;
- sound = SFX_CENTAURLEADER_ATTACK;
- break;
- }
- // Fire all 6 missiles at once
- S_StartSound(NULL, sound);
- KoraxFire1(actor, type);
- KoraxFire2(actor, type);
- KoraxFire3(actor, type);
- KoraxFire4(actor, type);
- KoraxFire5(actor, type);
- KoraxFire6(actor, type);
- }
- // Call action code scripts (250-254)
- void A_KoraxCommand(mobj_t *actor)
- {
- byte args[5];
- fixed_t x,y,z;
- angle_t ang;
- int numcommands;
- S_StartSound(actor, SFX_KORAX_COMMAND);
- // Shoot stream of lightning to ceiling
- ang = (actor->angle - ANGLE_90) >> ANGLETOFINESHIFT;
- x=actor->x + FixedMul(KORAX_COMMAND_OFFSET,finecosine[ang]);
- y=actor->y + FixedMul(KORAX_COMMAND_OFFSET,finesine[ang]);
- z=actor->z + KORAX_COMMAND_HEIGHT;
- P_SpawnMobj(x,y,z, MT_KORAX_BOLT);
- args[0]=args[1]=args[2]=args[3]=args[4]=0;
- if (actor->health <= (actor->info->spawnhealth >> 1))
- {
- numcommands = 5;
- }
- else
- {
- numcommands = 4;
- }
- switch(P_Random() % numcommands)
- {
- case 0:
- P_StartACS(250, 0, args, actor, NULL, 0);
- break;
- case 1:
- P_StartACS(251, 0, args, actor, NULL, 0);
- break;
- case 2:
- P_StartACS(252, 0, args, actor, NULL, 0);
- break;
- case 3:
- P_StartACS(253, 0, args, actor, NULL, 0);
- break;
- case 4:
- P_StartACS(254, 0, args, actor, NULL, 0);
- break;
- }
- }
- #define KORAX_DELTAANGLE (85*ANGLE_1)
- #define KORAX_ARM_EXTENSION_SHORT (40*FRACUNIT)
- #define KORAX_ARM_EXTENSION_LONG (55*FRACUNIT)
- #define KORAX_ARM1_HEIGHT (108*FRACUNIT)
- #define KORAX_ARM2_HEIGHT (82*FRACUNIT)
- #define KORAX_ARM3_HEIGHT (54*FRACUNIT)
- #define KORAX_ARM4_HEIGHT (104*FRACUNIT)
- #define KORAX_ARM5_HEIGHT (86*FRACUNIT)
- #define KORAX_ARM6_HEIGHT (53*FRACUNIT)
- // Arm projectiles
- // arm positions numbered:
- // 1 top left
- // 2 middle left
- // 3 lower left
- // 4 top right
- // 5 middle right
- // 6 lower right
- // Arm 1 projectile
- void KoraxFire1(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM1_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- // Arm 2 projectile
- void KoraxFire2(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM2_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- // Arm 3 projectile
- void KoraxFire3(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle - KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM3_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- // Arm 4 projectile
- void KoraxFire4(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_SHORT, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_SHORT, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM4_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- // Arm 5 projectile
- void KoraxFire5(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM5_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- // Arm 6 projectile
- void KoraxFire6(mobj_t *actor, int type)
- {
- mobj_t *mo;
- angle_t ang;
- fixed_t x,y,z;
- ang = (actor->angle + KORAX_DELTAANGLE) >> ANGLETOFINESHIFT;
- x = actor->x + FixedMul(KORAX_ARM_EXTENSION_LONG, finecosine[ang]);
- y = actor->y + FixedMul(KORAX_ARM_EXTENSION_LONG, finesine[ang]);
- z = actor->z - actor->floorclip + KORAX_ARM6_HEIGHT;
- mo = P_SpawnKoraxMissile(x,y,z,actor, actor->target, type);
- }
- void A_KSpiritWeave(mobj_t *actor)
- {
- fixed_t newX, newY;
- int weaveXY, weaveZ;
- int angle;
- weaveXY = actor->special2>>16;
- weaveZ = actor->special2&0xFFFF;
- angle = (actor->angle+ANG90)>>ANGLETOFINESHIFT;
- newX = actor->x-FixedMul(finecosine[angle],
- FloatBobOffsets[weaveXY]<<2);
- newY = actor->y-FixedMul(finesine[angle],
- FloatBobOffsets[weaveXY]<<2);
- weaveXY = (weaveXY+(P_Random()%5))&63;
- newX += FixedMul(finecosine[angle],
- FloatBobOffsets[weaveXY]<<2);
- newY += FixedMul(finesine[angle],
- FloatBobOffsets[weaveXY]<<2);
- P_TryMove(actor, newX, newY);
- actor->z -= FloatBobOffsets[weaveZ]<<1;
- weaveZ = (weaveZ+(P_Random()%5))&63;
- actor->z += FloatBobOffsets[weaveZ]<<1;
- actor->special2 = weaveZ+(weaveXY<<16);
- }
- void A_KSpiritSeeker(mobj_t *actor, angle_t thresh, angle_t turnMax)
- {
- int dir;
- int dist;
- angle_t delta;
- angle_t angle;
- mobj_t *target;
- fixed_t newZ;
- fixed_t deltaZ;
- target = (mobj_t *)actor->special1;
- if(target == NULL)
- {
- return;
- }
- dir = P_FaceMobj(actor, target, &delta);
- if(delta > thresh)
- {
- delta >>= 1;
- if(delta > turnMax)
- {
- delta = turnMax;
- }
- }
- if(dir)
- { // Turn clockwise
- actor->angle += delta;
- }
- else
- { // Turn counter clockwise
- actor->angle -= delta;
- }
- angle = actor->angle>>ANGLETOFINESHIFT;
- actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
- actor->momy = FixedMul(actor->info->speed, finesine[angle]);
- if(!(leveltime&15)
- || actor->z > target->z+(target->info->height)
- || actor->z+actor->height < target->z)
- {
- newZ = target->z+((P_Random()*target->info->height)>>8);
- deltaZ = newZ-actor->z;
- if(abs(deltaZ) > 15*FRACUNIT)
- {
- if(deltaZ > 0)
- {
- deltaZ = 15*FRACUNIT;
- }
- else
- {
- deltaZ = -15*FRACUNIT;
- }
- }
- dist = P_AproxDistance(target->x-actor->x, target->y-actor->y);
- dist = dist/actor->info->speed;
- if(dist < 1)
- {
- dist = 1;
- }
- actor->momz = deltaZ/dist;
- }
- return;
- }
- void A_KSpiritRoam(mobj_t *actor)
- {
- if (actor->health-- <= 0)
- {
- S_StartSound(actor, SFX_SPIRIT_DIE);
- P_SetMobjState(actor, S_KSPIRIT_DEATH1);
- }
- else
- {
- if (actor->special1)
- {
- A_KSpiritSeeker(actor, actor->args[0]*ANGLE_1,
- actor->args[0]*ANGLE_1*2);
- }
- A_KSpiritWeave(actor);
- if (P_Random()<50)
- {
- S_StartSound(NULL, SFX_SPIRIT_ACTIVE);
- }
- }
- }
- void A_KBolt(mobj_t *actor)
- {
- // Countdown lifetime
- if (actor->special1-- <= 0)
- {
- P_SetMobjState(actor, S_NULL);
- }
- }
- #define KORAX_BOLT_HEIGHT 48*FRACUNIT
- #define KORAX_BOLT_LIFETIME 3
- void A_KBoltRaise(mobj_t *actor)
- {
- mobj_t *mo;
- fixed_t z;
- // Spawn a child upward
- z = actor->z + KORAX_BOLT_HEIGHT;
- if ((z + KORAX_BOLT_HEIGHT) < actor->ceilingz)
- {
- mo = P_SpawnMobj(actor->x, actor->y, z, MT_KORAX_BOLT);
- if (mo)
- {
- mo->special1 = KORAX_BOLT_LIFETIME;
- }
- }
- else
- {
- // Maybe cap it off here
- }
- }
|