1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055 |
- #include <stdlib.h>
- #include <string.h>
- #include "actor.h"
- #include "debug4g.h"
- #include "engine.h"
- #include "trig.h"
- #include "gameutil.h"
- #include "misc.h"
- #include "db.h"
- #include "multi.h"
- #include "names.h"
- #include "screen.h"
- #include "sectorfx.h"
- #include "triggers.h"
- #include "error.h"
- #include "globals.h"
- #include "seq.h"
- #include "eventq.h"
- #include "dude.h"
- #include "ai.h"
- #include "view.h"
- #include "warp.h"
- #include "tile.h"
- #include "player.h"
- #include "options.h"
- #include "sfx.h"
- #include "sound.h" // can be removed once sfx complete
- #define kMaxSpareSprites 50
- SPRITEHIT gSpriteHit[kMaxXSprites];
- static void MakeSplash( SPRITE *pSprite, XSPRITE *pXSprite );
- static void FireballCallback( int /* type */, int nXIndex );
- //static void FlareCallback( int /* type */, int nXIndex );
- struct VECTORDATA
- {
- DAMAGE_TYPE damageType;
- int damageValue;
- int maxDist;
- struct
- {
- int nEffect;
- int nSoundId;
- } impact[kSurfMax];
- };
- static VECTORDATA gVectorData[kVectorMax] =
- {
- // kVectorTine
- { kDamageStab, 4, M2X(2.25),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorShell
- { kDamageBullet, 4, 0,
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, -1 }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, -1 }, // kSurfWood
- { ET_Squib1, -1 }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, -1 }, // kSurfDirt
- { ET_Ricochet2, -1 }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, -1 }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorBullet
- { kDamageBullet, 4, 0,
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorBulletAP
- { kDamageBullet, 4, 0,
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorAxe
- { kDamageStab, 20, M2X(2.0),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorCleaver
- { kDamageStab, 10, M2X(2.0),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorClaw
- { kDamageStab, 20, M2X(2.0),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorHoundBite
- { kDamageStab, 10, M2X(1.2),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorRatBite
- { kDamageStab, 4, M2X(1.8),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- // kVectorSpiderBite
- { kDamageStab, 8, M2X(1.2),
- {
- { -1, -1 }, // kSurfNone
- { ET_Ricochet1, kSfxForkStone }, // kSurfStone
- { ET_Ricochet1, kSfxForkMetal }, // kSurfMetal
- { ET_Ricochet2, kSfxForkWood }, // kSurfWood
- { ET_Squib1, kSfxForkFlesh }, // kSurfFlesh
- { ET_Splash2, -1 }, // kSurfWater
- { ET_Ricochet2, kSfxForkWood }, // kSurfDirt
- { ET_Ricochet2, kSfxForkWood }, // kSurfClay
- { -1, -1 }, // kSurfSnow
- { -1, -1 }, // kSurfIce
- { -1, -1 }, // kSurfLeaves
- { -1, -1 }, // kSurfCloth
- { -1, kSfxForkWood }, // kSurfPlant
- { -1, -1 }, // kSurfGoo
- { -1, -1 }, // kSurfLava
- }
- },
- };
- ITEMDATA gItemData[ kMaxItemTypes ] =
- {
- /*
- {cstat, picnum, sh, pal,xr, yr, stat, type, flags},
- */
- {0, kPicKey1, -8, 0, 32, 32, kStatItem, kItemKey1, 0},
- {0, kPicKey2, -8, 0, 32, 32, kStatItem, kItemKey2, 0},
- {0, kPicKey3, -8, 0, 32, 32, kStatItem, kItemKey3, 0},
- {0, kPicKey4, -8, 0, 32, 32, kStatItem, kItemKey4, 0},
- {0, kPicKey5, -8, 0, 32, 32, kStatItem, kItemKey5, 0},
- {0, kPicKey6, -8, 0, 32, 32, kStatItem, kItemKey6, 0},
- {0, -1, -8, 0, -1, -1, kStatItem, kItemKey7, 0},
- {0, kPicDocBag, -8, 0, 48, 48, kStatItem, kItemDoctorBag, 0},
- {0, kPicMedPouch, -8, 0, 40, 40, kStatItem, kItemMedPouch, 0},
- {0, kPicEssence, -8, 0, 40, 40, kStatItem, kItemLifeEssence, 0},
- {0, kAnmLifeSeed, -8, 0, 40, 40, kStatItem, kItemLifeSeed, 0},
- {0, kPicPotion, -8, 0, 40, 40, kStatItem, kItemPotion1, 0},
- {0, kAnmFeather, -8, 0, 40, 40, kStatItem, kItemFeatherFall, 0},
- {0, kAnmInviso, -8, 0, 40, 40, kStatItem, kItemLtdInvisibility, 0},
- {0, kPicInvulnerable, -8, 0, 40, 40, kStatItem, kItemInvulnerability, 0},
- {0, kPicJumpBoots, -8, 0, 40, 40, kStatItem, kItemJumpBoots, 0},
- {0, kPicRavenFlight, -8, 0, 40, 40, kStatItem, kItemRavenFlight, 0},
- {0, kPicGunsAkimbo, -8, 0, 40, 40, kStatItem, kItemGunsAkimbo, 0},
- {0, kPicDivingSuit, -8, 0, 80, 64, kStatItem, kItemDivingSuit, 0},
- {0, kPicGasMask, -8, 0, 40, 40, kStatItem, kItemGasMask, 0},
- {0, kAnmClone, -8, 0, 40, 40, kStatItem, kItemClone, 0},
- {0, kPicCrystalBall, -8, 0, 40, 40, kStatItem, kItemCrystalBall, 0},
- {0, kPicDecoy, -8, 0, 40, 40, kStatItem, kItemDecoy, 0},
- {0, kAnmDoppleganger, -8, 0, 40, 40, kStatItem, kItemDoppleganger, 0},
- {0, kAnmReflectShots, -8, 0, 40, 40, kStatItem, kItemReflectiveShots, 0},
- {0, kPicRoseGlasses, -8, 0, 40, 40, kStatItem, kItemRoseGlasses, 0},
- {0, kAnmCloakNDagger, -8, 0, 64, 64, kStatItem, kItemShadowCloak, 0},
- {0, kPicShroom1, -8, 0, 48, 48, kStatItem, kItemShroomRage, 0},
- {0, kPicShroom2, -8, 0, 48, 48, kStatItem, kItemShroomDelirium, 0},
- {0, kPicShroom3, -8, 0, 48, 48, kStatItem, kItemShroomGrow, 0},
- {0, kPicShroom4, -8, 0, 48, 48, kStatItem, kItemShroomShrink, 0},
- {0, kPicDeathMask, -8, 0, 40, 40, kStatItem, kItemDeathMask, 0},
- {0, kPicGoblet, -8, 0, 40, 40, kStatItem, kItemWineGoblet, 0},
- {0, kPicBottle1, -8, 0, 40, 40, kStatItem, kItemWineBottle, 0},
- {0, kPicSkullGrail, -8, 0, 40, 40, kStatItem, kItemSkullGrail, 0},
- {0, kPicSilverGrail, -8, 0, 40, 40, kStatItem, kItemSilverGrail, 0},
- {0, kPicTome1, -8, 0, 40, 40, kStatItem, kItemTome, 0},
- {0, kPicBlackChest, -8, 0, 40, 40, kStatItem, kItemBlackChest, 0},
- {0, kPicWoodChest, -8, 0, 40, 40, kStatItem, kItemWoodenChest, 0},
- {0, kPicAsbestosSuit, -8, 0, 80, 64, kStatItem, kItemAsbestosArmor, 0},
- };
- AMMOITEMDATA gAmmoItemData[kAmmoItemMax - kAmmoItemBase] =
- {
- /*
- {cstat, picnum, sh, pal,xr, yr, flags, count, ammoType, weaponType},
- */
- {0, kPicSprayCan, -8, 0, 40, 40, 0, kTimerRate*8, kAmmoSprayCan, kWeaponSprayCan},
- {0, kPicTNTStick, -8, 0, 48, 48, 0, 1, kAmmoTNTStick, kWeaponTNT},
- {0, kPicTNTPak, -8, 0, 48, 48, 0, 7, kAmmoTNTStick, kWeaponTNT},
- {0, kPicTNTBox, -8, 0, 48, 48, 0, 14, kAmmoTNTStick, kWeaponTNT},
- {0, kPicTNTProx, -8, 0, 48, 48, 0, 1, kAmmoTNTProximity, kWeaponTNT},
- {0, kPicTNTRemote, -8, 0, 48, 48, 0, 1, kAmmoTNTRemote, kWeaponTNT},
- {0, kPicTNTTimer, -8, 0, 48, 48, 0, 1, kAmmoTNTStick, kWeaponTNT},
- {0, kPicShotShells, -8, 0, 48, 48, 0, 4, kAmmoShell},
- {0, kPicShellBox, -8, 0, 48, 48, 0, 16, kAmmoShell},
- {0, kPicBullets, -8, 0, 48, 48, 0, 8, kAmmoBullet},
- {0, kPicBulletBox, -8, 0, 48, 48, 0, 50, kAmmoBullet},
- {0, kPicBulletBoxAP, -8, 0, 48, 48, 0, 50, kAmmoBulletAP},
- {0, kPicTommyDrum, -8, 0, 48, 48, 0, 100, kAmmoBullet},
- {0, kPicSpear, -8, 0, 48, 48, 0, 1, kAmmoSpear},
- {0, kPicSpears, -8, 0, 48, 48, 0, 6, kAmmoSpear},
- {0, kPicSpearExplode, -8, 0, 48, 48, 0, 6, kAmmoSpearXP},
- {0, kPicFlares, -8, 0, 48, 48, 0, 8, kAmmoFlare},
- {0, kPicFlareHE, -8, 0, 48, 48, 0, 8, kAmmoFlare},
- {0, kPicFlareBurst, -8, 0, 48, 48, 0, 8, kAmmoFlareSB},
- };
- WEAPONITEMDATA gWeaponItemData[ kWeaponItemMax - kWeaponItemBase ] =
- {
- /*
- {cstat, picnum, sh, pal,xr, yr, flags, weaponType, ammoType, count },
- */
- { 0, -1, 0, 0, 0, 0, 0, kWeaponNone, kAmmoNone, 0 },
- { 0, kPicShotgun, -8, 0, 48, 48, 0, kWeaponShotgun, kAmmoShell, 8 },
- { 0, kPicTommyGun, -8, 0, 48, 48, 0, kWeaponTommy, kAmmoBullet, 50 },
- { 0, kPicFlareGun, -8, 0, 48, 48, 0, kWeaponFlare, kAmmoFlare, 8 },
- { 0, kPicVoodooDoll, -8, 0, 48, 48, 0, kWeaponVoodoo, kAmmoVoodoo, 1 },
- { 0, kPicSpearGun, -8, 0, 48, 48, 0, kWeaponSpear, kAmmoSpear, 6 },
- { 0, kPicShadowGun, -8, 0, 48, 48, 0, kWeaponShadow, kAmmoNone, 0 },
- { 0, -1, 0, 0, 0, 0, 0, kWeaponPitchfork, kAmmoNone, 0 }, // don't actually pick this up
- { 0, kPicSprayCan, -8, 0, 48, 48, 0, kWeaponSprayCan, kAmmoSprayCan, kTimerRate*8},
- { 0, kPicTNTStick, -8, 0, 48, 48, 0, kWeaponTNT, kAmmoTNTStick, 1},
- };
- struct MissileType {
- short picnum;
- int velocity;
- int angleOfs;
- uchar xrepeat;
- uchar yrepeat;
- char shade;
- } missileInfo[] = {
- { kAnmButcherKnife, (M2X(14.0) << 4) / kTimerRate, kAngle90, 40, 40, -16 }, // kMissileButcherKnife
- { kAnmFlare, (M2X(20.0) << 4) / kTimerRate, 0, 32, 32, -128 }, // kMissileFlare
- { kAnmFlare, (M2X(20.0) << 4) / kTimerRate, 0, 32, 32, -128 }, // kMissileExplodingFlare
- { kAnmFlare, (M2X(20.0) << 4) / kTimerRate, 0, 32, 32, -128 }, // kMissileStarburstFlare
- { 0, (M2X(4.0) << 4) / kTimerRate, 0, 24, 24, -128 }, // kMissileSprayFlame
- { 0, (M2X(16.0) << 4) / kTimerRate, 0, 32, 32, -128 }, // kMissileFireball
- { kAnmSpear, (M2X(16.0) << 4) / kTimerRate, kAngle270, 64, 64, -8 }, // kMissileSpear // 18.0
- { kAnmEctoSkull, (M2X(16.0) << 4) / kTimerRate, 0, 32, 32, -24 }, // kMissileEctoSkull
- { 0, (M2X(6.0) << 4) / kTimerRate, 0, 24, 24, -128 }, // kMissileHoundFire
- { 0, (M2X(12.0) << 4) / kTimerRate, 0, 8, 8, 0 }, // kMissileGreenPuke
- { 0, (M2X(12.0) << 4) / kTimerRate, 0, 8, 8, 0 }, // kMissileRedPuke
- };
- static int dragTable[] =
- {
- 0,
- 0x0C00, // kDepthTread
- 0x1A00, // kDepthWade
- 0x1A00, // kDepthSwim
- };
- // miscellaneous effects
- enum
- {
- kSeqSprayFlame1 = 0,
- kSeqSprayFlame2,
- kSeqSkull,
- kSeqExplodeC1L, // large concussion on ground
- kSeqExplodeC1M, // medium concussion on ground
- kSeqExplodeC1S, // small concussion on ground
- kSeqExplodeC2L, // large concussion in air
- kSeqExplodeC2M, // medium concussion in air
- kSeqExplodeC2S, // small concussion in air
- kSeqExplodeC2T, // tiny concussion in air
- kSeqSplash1,
- kSeqSplash2,
- kSeqSplash3, // blood spash
- kSeqRicochet1,
- kSeqGoreWing,
- kSeqGoreHead,
- kSeqBarrel,
- kSeqBloodPool,
- kSeqRespawn,
- kSeqFlareSmoke,
- kSeqSquib1,
- kSeqFluorescentLight,
- kSeqClearGlass,
- kSeqStainedGlass,
- kSeqWeb,
- kSeqBeam,
- kSeqVase1,
- kSeqVase2,
- kSeqZombieBones,
- kSeqSkullExplode,
- kSeqMetalGrate1,
- kSeqFireball,
- kSeqBoneBreak,
- kSeqBurningTree1,
- kSeqBurningTree2,
- kSeqHoundFire,
- kSeqMGunDead,
- kSeqRicochet2,
- kSeqEffectMax,
- };
- struct THINGINFO
- {
- short startHealth;
- short mass; // in KG
- char clipdist;
- ushort flags;
- int damageShift[kDamageMax]; // use to indicate resistance to damage types
- };
- static THINGINFO thingInfo[kThingMax - kThingBase] =
- {
- { // kThingTNTBarrel
- 40,
- 150,
- 32,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- 0, // kDamageFall
- 0, // kDamageBurn
- 0, // kDamageBullet
- 1, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingTNTProxArmed
- 5,
- 5,
- 16,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- 0, // kDamageBullet
- kNoDamage, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingTNTRemArmed
- 5,
- 5,
- 16,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- 0, // kDamageBullet
- kNoDamage, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBlueVase
- 1,
- 20,
- 32,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- 0, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBrownVase
- 1,
- 150,
- 32,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- 0, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingCrateFace
- 10,
- 0,
- 0,
- 0,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingClearGlass
- 1,
- 0,
- 0,
- 0,
- {
- 0, // kDamagePummel
- 0, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingFluorescent
- 1,
- 0,
- 0,
- 0,
- {
- 0, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingWallCrack
- 8,
- 0,
- 0,
- 0,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingWoodBeam
- 8,
- 0,
- 0,
- 0,
- {
- 0, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingWeb
- 4,
- 0,
- 0,
- 0,
- {
- 0, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- 1, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingMetalGrate1
- 20,
- 0,
- 0,
- 0,
- {
- 3, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- 2, // kDamageBullet
- 4, // kDamageStab
- 1, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBurnableTree
- 1,
- 0,
- 0,
- 0,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- 0, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingMachineGun
- 50,
- 0,
- 0,
- 0,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- 2, // kDamageBullet
- 2, // kDamageStab
- 1, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingTNTStick
- 5,
- 2,
- 16,
- kAttrMove | kAttrGravity,
- {
- 0, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingTNTBundle
- 5,
- 14,
- 16,
- kAttrMove | kAttrGravity,
- {
- 0, // kDamagePummel
- kNoDamage, // kDamageFall
- 0, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBoneClub
- 5,
- 6,
- 16,
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingZombieBones
- 8,
- 3,
- 16,
- kAttrMove | kAttrGravity,
- {
- 0, // kDamagePummel
- 0, // kDamageFall
- kNoDamage, // kDamageBurn
- 0, // kDamageBullet
- 0, // kDamageStab
- 0, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingWaterDrip
- 0, // startHealth
- 1, // mass
- 1, // clipDist
- kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBloodDrip
- 0, // startHealth
- 1, // mass
- 1, // clipDist
- kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBubble
- 0, // startHealth
- -1, // mass
- 1, // clipDist
- kAttrMove,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingBubbles
- 0, // startHealth
- -1, // mass
- 1, // clipDist
- kAttrMove,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- { // kThingGibSmall
- 0, // startHealth
- 2, // mass
- 4, // clipDist
- kAttrMove | kAttrGravity,
- {
- kNoDamage, // kDamagePummel
- kNoDamage, // kDamageFall
- kNoDamage, // kDamageBurn
- kNoDamage, // kDamageBullet
- kNoDamage, // kDamageStab
- kNoDamage, // kDamageExplode
- kNoDamage, // kDamageGas
- kNoDamage, // kDamageDrown
- kNoDamage, // kDamageSpirit
- kNoDamage, // kDamageVoodoo
- },
- },
- };
- void actAllocateSpares( void )
- {
- /*
- dprintf("Creating spare sprites\n");
- for (int i = 0; i < kMaxSpareSprites; i++)
- {
- int nSprite = insertsprite( 0, kStatSpares );
- dassert(nSprite != -1);
- dbInsertXSprite(nSprite);
- sprite[nSprite].cstat |= kSpriteInvisible;
- }
- */
- }
- /*******************************************************************************
- FUNCTION: actInit()
- DESCRIPTION: Initialize the actor subsystem. Locks all sequences, and
- preloads all tiles for sequences used in the map.
- NOTES:
- *******************************************************************************/
- void actInit( void )
- {
- int nSprite;
- BOOL used[kDudeMax - kDudeBase];
- memset(used, FALSE, sizeof(used));
- // allocate sprites to use for effects
- actAllocateSpares();
- // see which dudes are present
- for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->type < kDudeBase || pSprite->type >= kDudeMax )
- {
- dprintf("ERROR IN SPRITE %i\n", nSprite);
- dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
- }
- used[pSprite->type - kDudeBase] = TRUE;
- }
- // preload tiles for dude sequences
- dprintf("Preload dude sequence tiles\n");
- for (int i = 0; i < kDudeMax - kDudeBase; i++)
- {
- if ( used[i] )
- {
- // only preload art for idle sequence
- // if ( dudeInfo[i].pSeq[0] != NULL )
- // dudeInfo[i].pSeq[0]->Preload();
- }
- }
- // initialize all dudes
- for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- int dudeIndex = pSprite->type - kDudeBase;
- pSprite->cstat |= kSpriteBlocking | kSpriteHitscan;
- pSprite->clipdist = dudeInfo[dudeIndex].clipdist;
- pSprite->flags = kAttrMove | kAttrGravity | kAttrFalling;
- pSprite->xvel = pSprite->yvel = pSprite->zvel = 0;
- pXSprite->health = dudeInfo[dudeIndex].startHealth << 4;
- if ( gSysRes.Lookup( dudeInfo[dudeIndex].seqStartID + kSeqDudeIdle, ".SEQ") != NULL )
- seqSpawn(dudeInfo[dudeIndex].seqStartID + kSeqDudeIdle, SS_SPRITE, nXSprite);
- }
- // initialize all things
- for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- int thingIndex = pSprite->type - kThingBase;
- pSprite->cstat |= kSpriteBlocking; // | kSpriteHitscan should be handled in BSTUB by hitbit
- pSprite->clipdist = thingInfo[thingIndex].clipdist;
- pSprite->flags = thingInfo[thingIndex].flags;
- pSprite->xvel = pSprite->yvel = pSprite->zvel = 0;
- pXSprite->health = thingInfo[thingIndex].startHealth << 4;
- switch(pSprite->type)
- {
- case kThingMachineGun:
- pXSprite->state = 0; // these must start off
- break;
- default:
- pXSprite->state = 1;
- break;
- }
- }
- aiInit();
- }
- #define kGlobalForceShift 26 // arbitrary scale for all concussion
- static void ConcussSprite( int nSource, int nSprite, int x, int y, int z, int ticks )
- {
- SPRITE *pSprite = &sprite[nSprite];
- int dx = pSprite->x - x;
- int dy = pSprite->y - y;
- int dz = (pSprite->z - z) >> 4;
- int dist2 = ClipLow(dx * dx + dy * dy + dz * dz, 32 << 4);
- int nTile = pSprite->picnum;
- int area = tilesizx[nTile] * pSprite->xrepeat * tilesizy[nTile] * pSprite->yrepeat >> 12;
- int force = divscale(ticks, dist2, kGlobalForceShift);
- if ( pSprite->flags & kAttrMove )
- {
- int mass = 0;
- if ( pSprite->type >= kDudeBase && pSprite->type < kDudeMax )
- mass = dudeInfo[pSprite->type - kDudeBase].mass;
- else if ( pSprite->type >= kThingBase && pSprite->type < kThingMax )
- mass = thingInfo[pSprite->type - kThingBase].mass;
- else
- ThrowError("Unexpected type encountered in ConcussSprite()", ES_ERROR);
- dassert(mass != 0);
- int impulse = muldiv(force, area, qabs(mass));
- dx = mulscale16(impulse, dx);
- dy = mulscale16(impulse, dy);
- dz = mulscale16(impulse, dz);
- pSprite->xvel += dx;
- pSprite->yvel += dy;
- pSprite->zvel += dz;
- }
- actDamageSprite(nSource, nSprite, kDamageExplode, force);
- }
- /*******************************************************************************
- FUNCTION: ReflectVector()
- DESCRIPTION: Reflects a vector off a wall
- PARAMETERS: nFraction is elasticity (0x10000 == perfectly elastic)
- *******************************************************************************/
- static void ReflectVector( short *dx, short *dy, int nWall, int nFraction )
- {
- // calculate normal for wall
- int nx = -(wall[wall[nWall].point2].y - wall[nWall].y) >> 4;
- int ny = (wall[wall[nWall].point2].x - wall[nWall].x) >> 4;
- int dotProduct = *dx * nx + *dy * ny;
- int length2 = nx * nx + ny * ny;
- dassert(length2 > 0);
- int dot2 = dotProduct + mulscale16r(dotProduct, nFraction);
- int sd = divscale16(dot2, length2);
- *dx -= mulscale16r(nx, sd);
- *dy -= mulscale16r(ny, sd);
- }
- static void DropPickupObject( int nActor, int nObject )
- {
- dassert( nActor >= 0 && nActor < kMaxSprites && sprite[nActor].statnum < kMaxStatus );
- dassert( (nObject >= kItemBase && nObject < kItemMax)
- || (nObject >= kAmmoItemBase && nObject < kAmmoItemMax)
- || (nObject >= kWeaponItemBase && nObject < kWeaponItemMax) );
- // create a sprite for the dropped ammo
- SPRITE *pActor = &sprite[nActor];
- int nSprite = actSpawnSprite( pActor->sectnum, pActor->x, pActor->y, sector[pActor->sectnum].floorz, kStatItem, FALSE );
- SPRITE *pSprite = &sprite[ nSprite ];
- if ( nObject >= kItemBase && nObject < kItemMax )
- {
- int nItemIndex = nObject - kItemBase;
- pSprite->type = (short)nObject;
- pSprite->picnum = gItemData[nItemIndex].picnum;
- pSprite->shade = gItemData[nItemIndex].shade ;
- pSprite->xrepeat = gItemData[nItemIndex].xrepeat;
- pSprite->yrepeat = gItemData[nItemIndex].yrepeat;
- if (nObject >= kItemKey1 && nObject <= kItemKey7)
- {
- // PF/NN: should this be in bloodbath too?
- if ( gNetMode == kNetModeCoop ) // force permanent keys in Coop mode
- {
- dbInsertXSprite( nSprite );
- XSPRITE *pXSprite = &xsprite[ pSprite->extra ];
- pXSprite->respawn = kRespawnPermanent;
- pXSprite->respawnTime = 0;
- }
- }
- }
- else if ( nObject >= kAmmoItemBase && nObject < kAmmoItemMax )
- {
- int nAmmoIndex = nObject - kAmmoItemBase;
- pSprite->type = (short)nObject;
- pSprite->picnum = gAmmoItemData[nAmmoIndex].picnum;
- pSprite->shade = gAmmoItemData[nAmmoIndex].shade ;
- pSprite->xrepeat = gAmmoItemData[nAmmoIndex].xrepeat;
- pSprite->yrepeat = gAmmoItemData[nAmmoIndex].yrepeat;
- }
- else if ( nObject >= kWeaponItemBase && nObject < kWeaponItemMax )
- {
- int nWeaponIndex = nObject - kWeaponItemBase;
- pSprite->type = (short)nObject;
- pSprite->picnum = gWeaponItemData[nWeaponIndex].picnum;
- pSprite->shade = gWeaponItemData[nWeaponIndex].shade ;
- pSprite->xrepeat = gWeaponItemData[nWeaponIndex].xrepeat;
- pSprite->yrepeat = gWeaponItemData[nWeaponIndex].yrepeat;
- }
- else
- ThrowError("Unhandled nObject passed to DropPickupObject()", ES_ERROR);
- }
- BOOL actHealDude( XSPRITE *pXDude, int healValue, int maxHealthClip)
- {
- dassert(pXDude != NULL);
- healValue <<= 4; // fix this later in the calling code
- maxHealthClip <<= 4;
- if ( pXDude->health < maxHealthClip )
- {
- pXDude->health = ClipHigh(pXDude->health + healValue, maxHealthClip);
- dprintf("Health=%d\n", pXDude->health >> 4);
- return TRUE;
- }
- return FALSE;
- }
- void actKillSprite( int nSource, int nSprite, DAMAGE_TYPE damageType )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- SPRITE *pSource = &sprite[nSource];
- dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
- int dudeIndex = pSprite->type - kDudeBase;
- DUDEINFO *pDudeInfo = &dudeInfo[dudeIndex];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0);
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- pXSprite->moveState = kMoveStill;
- // handle first cerberus head death
- if (pSprite->type == kDudeCerberus)
- {
- seqSpawn(dudeInfo[dudeIndex].seqStartID + kSeqDudeDeath1, SS_SPRITE, nXSprite);
- return;
- }
- actPostSprite( nSprite, kStatThing );
- trTriggerSprite( nSprite, pXSprite, kCommandOff ); // trigger death message
- pSprite->flags |= kAttrMove | kAttrGravity | kAttrFalling;
- if ( IsPlayerSprite(nSprite) )
- {
- PLAYER *pPlayer = &gPlayer[pSprite->type - kDudePlayer1];
- powerupClear( pPlayer );
- dprintf("health = %i\n",pXSprite->health);
- if (pXSprite->health == 0)
- pPlayer->deathTime = 0;
- if ( IsPlayerSprite(nSource) )
- {
- int nKilledIndex = pSprite->type - kDudePlayer1;
- int nKillerIndex = pSource->type - kDudePlayer1;
- PLAYER *pFragger = &gPlayer[nKillerIndex];
- if (nSource == nSprite) // fragged yourself, eh?
- {
- pPlayer->fragCount--;
- pPlayer->fragInfo[nKillerIndex]--; // frags against self is negative
- }
- else
- {
- pFragger->fragCount++;
- pFragger->fragInfo[nKilledIndex]++; // frags against others are positive
- }
- }
- // drop pickup items
- for (int i = 1; i < 8; i++)
- if ( pPlayer->hasKey[i] )
- DropPickupObject(nSprite, kItemKey1 + i - 1);
- if ( pPlayer->hasWeapon[kWeaponShotgun] )
- DropPickupObject(nSprite, kWeaponItemShotgun);
- if ( pPlayer->hasWeapon[kWeaponTommy] )
- DropPickupObject(nSprite, kWeaponItemTommyGun);
- if ( pPlayer->hasWeapon[kWeaponFlare] )
- DropPickupObject(nSprite, kWeaponItemFlareGun);
- if ( pPlayer->hasWeapon[kWeaponSpear] )
- DropPickupObject(nSprite, kWeaponItemSpearGun);
- if ( pPlayer->hasWeapon[kWeaponShadow] )
- DropPickupObject(nSprite, kWeaponItemShadowGun);
- }
- if ( pXSprite->key > 0 )
- DropPickupObject( nSprite, kItemKey1 + pXSprite->key - 1 );
- int deathType;
- switch (damageType)
- {
- case kDamageFall:
- case kDamageExplode:
- deathType = kSeqDudeDeath2;
- // sfxStart3DSound(nXSprite, kSfxThingSplat);
- break;
- case kDamageBurn:
- deathType = kSeqDudeDeath3;
- // sfxStart3DSound(nXSprite, kSfxThingBurn);
- break;
- default:
- deathType = kSeqDudeDeath1;
- break;
- }
- // are we missing this sequence? if so, just delete it
- if ( gSysRes.Lookup( dudeInfo[dudeIndex].seqStartID + deathType, ".SEQ") == NULL )
- {
- dprintf("sprite missing death sequence: deleted\n");
- seqKill(SS_SPRITE, nXSprite); // make sure we remove any active sequence
- actPostSprite( nSprite, kStatFree );
- return;
- }
- switch (pSprite->type)
- {
- case kDudeAxeZombie:
- if (pSprite->owner >= 0)
- {
- PLAYER *pPlayer = &gPlayer[sprite[pSprite->owner].type - kDudePlayer1];
- playerDeleteLackey( pPlayer, nSprite );
- }
- seqSpawn(dudeInfo[dudeIndex].seqStartID + deathType, SS_SPRITE, nXSprite);
- break;
- case kDudeFatZombie:
- // if ( damageType == kDamageBurn )
- // {
- // int thingIndex = kThingZombieBones - kThingBase;
- //
- // pSprite->type = kThingZombieBones;
- // pSprite->clipdist = thingInfo[thingIndex].clipdist;
- // pSprite->flags = thingInfo[thingIndex].flags;
- // pSprite->xvel = pSprite->yvel = pSprite->zvel = 0;
- // pXSprite->health = thingInfo[thingIndex].startHealth << 4;
- // }
- seqSpawn(dudeInfo[dudeIndex].seqStartID + deathType, SS_SPRITE, nXSprite);
- break;
- default:
- dprintf("spawning sprite %i death sequence %x\n", nSprite, dudeInfo[dudeIndex].seqStartID + deathType);
- seqSpawn(dudeInfo[dudeIndex].seqStartID + deathType, SS_SPRITE, nXSprite);
- break;
- }
- // drop any items or weapons
- if (pSprite->type == kDudeTommyCultist)
- {
- int nDropCheck = Random(100);
- // constants? table?
- if (nDropCheck < 80)
- DropPickupObject( nSprite, kAmmoItemBullets );
- else if (nDropCheck < 95)
- DropPickupObject( nSprite, kAmmoItemBulletBox );
- else
- DropPickupObject( nSprite, kWeaponItemTommyGun );
- }
- else if (pSprite->type == kDudeShotgunCultist)
- {
- int nDropCheck = Random(100);
- if (nDropCheck < 40)
- DropPickupObject( nSprite, kAmmoItemShells );
- else if (nDropCheck < 75)
- DropPickupObject( nSprite, kAmmoItemShellBox );
- else
- DropPickupObject( nSprite, kWeaponItemShotgun );
- }
- // SpawnGibs( nSprite, pXSprite );
- // gib generator
- if ( damageType == kDamageExplode )
- {
- int angle, velocity = 120;
- // thing gibs
- for (int i = 0; i < kGibMax && pDudeInfo->gib[i].chance > 0; i++)
- {
- if ( Random(256) < pDudeInfo->gib[i].chance )
- {
- int nGib = actCloneSprite(pSprite);
- changespritestat( (short)nGib, kStatThing );
- SPRITE *pGib = &sprite[nGib];
- angle = Random(kAngle360);
- pGib->type = kThingGibSmall;
- pGib->picnum = pDudeInfo->gib[i].tile;
- pGib->xvel += mulscale30(velocity, Cos(angle));
- pGib->yvel += mulscale30(velocity, Sin(angle));
- pGib->zvel -= 128; // toss it in the air a bit
- pGib->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
- pGib->flags = kAttrMove | kAttrGravity | kAttrFalling;
- pGib->pal = kPLUNormal;
- }
- }
- // debris gibs
- for (i = 0; i < 50; i++)
- {
- int nGib = actCloneSprite(pSprite);
- changespritestat( (short)nGib, kStatDebris);
- SPRITE *pGib = &sprite[nGib];
- pGib->picnum = 2053; // "worm" giblet
- pGib->xvel += BiRandom(500);
- pGib->yvel += BiRandom(500);
- pGib->zvel += BiRandom(500);
- pGib->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
- pGib->flags = kAttrMove | kAttrGravity | kAttrFalling;
- pGib->pal = kPLUNormal;
- }
- }
- }
- void actDamageSprite( int nSource, int nSprite, DAMAGE_TYPE nDamageType, int nDamage )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- if (nXSprite < 0)
- return;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if (pXSprite->health == 0) // it's already toast
- return;
- switch ( pSprite->statnum )
- {
- case kStatDude:
- {
- dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
- // calculate and apply damage to the dude or player sprite
- int nShift = dudeInfo[pSprite->type - kDudeBase].damageShift[nDamageType];
- nDamage = (nShift >= kNoDamage) ? 0 : nDamage >> nShift;
- pXSprite->health = ClipLow(pXSprite->health - nDamage, 0);
- // process results and effects of damage
- if ( IsPlayerSprite(pSprite) )
- playerDamageSprite( &gPlayer[pSprite->type - kDudePlayer1], nSource, nDamage );
- else
- aiDamageSprite( pSprite, pXSprite, nSource, nDamageType, nDamage );
- // kill dudes lacking health to sustain life
- if (pXSprite->health == 0)
- {
- // prevent dudes from exploding from weak explosion damage
- if ( nDamageType == kDamageExplode && nDamage < (5 << 4) )
- nDamageType = kDamagePummel;
- if ( IsPlayerSprite(pSprite) )
- sfxStart3DSound(nXSprite, kSfxPlayDie);
- actKillSprite(nSource, nSprite, nDamageType);
- }
- break;
- }
- case kStatThing:
- case kStatProximity:
- {
- dassert(pSprite->type >= kThingBase && pSprite->type < kThingMax);
- int thingIndex = pSprite->type - kThingBase;
- nDamage >>= thingInfo[thingIndex].damageShift[nDamageType];
- pXSprite->health = ClipLow(pXSprite->health - nDamage, 0);
- if (pXSprite->health == 0)
- {
- pSprite->owner = (short)nSource;
- trTriggerSprite(nSprite, pXSprite, kCommandOff);
- switch ( pSprite->type )
- {
- case kThingMachineGun:
- seqSpawn(kSeqMGunDead, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(nXSprite, kSfxMGDie);
- break;
- case kThingBlueVase:
- seqSpawn(kSeqVase1, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(nXSprite, kSfxPotteryHit);
- if (pXSprite->data1 > 0)
- DropPickupObject( nSprite, pXSprite->data1 );
- if (pXSprite->data2 > 0)
- DropPickupObject( nSprite, pXSprite->data2 );
- break;
- case kThingBrownVase:
- seqSpawn(kSeqVase2, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(nXSprite, kSfxPotteryHit);
- if (pXSprite->data1 > 0)
- DropPickupObject( nSprite, pXSprite->data1 );
- if (pXSprite->data2 > 0)
- DropPickupObject( nSprite, pXSprite->data2 );
- break;
- case kThingClearGlass:
- pSprite->yrepeat >>= 1;
- seqSpawn(kSeqClearGlass, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(nXSprite, kSfxGlassHit);
- break;
- case kThingFluorescent:
- seqSpawn(kSeqFluorescentLight, SS_SPRITE, pSprite->extra);
- break;
- case kThingWoodBeam:
- seqSpawn(kSeqBeam, SS_SPRITE, pSprite->extra);
- break;
- case kThingWeb:
- seqSpawn(kSeqWeb, SS_SPRITE, pSprite->extra);
- break;
- case kThingMetalGrate1:
- seqSpawn(kSeqMetalGrate1, SS_SPRITE, pSprite->extra);
- break;
- case kThingFlammableTree:
- if (pXSprite->data1 == 0)
- seqSpawn(kSeqBurningTree1, SS_SPRITE, pSprite->extra);
- else if (pXSprite->data1 == 1)
- seqSpawn(kSeqBurningTree2, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(nXSprite, kSfxBurn);
- break;
- case kThingZombieBones:
- dprintf("damaging zombie bones\n");
- if ( seqGetStatus(SS_SPRITE, nXSprite) < 0 ) // body finished burning
- seqSpawn(kSeqZombieBones, SS_SPRITE, pSprite->extra);
- break;
- }
- }
- break;
- }
- }
- }
- void actImpactMissile( int nSprite, int hitInfo )
- {
- SPRITE *pMissile = &sprite[nSprite];
- int hitType = hitInfo & kHitTypeMask;
- int hitObject = hitInfo & kHitIndexMask;
- int nXMissile = pMissile->extra;
- switch (pMissile->type)
- {
- case kMissileFireball:
- actExplodeSprite( nSprite );
- break;
- case kMissileFlare:
- case kMissileExplodingFlare:
- case kMissileStarburstFlare:
- {
- XSPRITE *pXMissile = &xsprite[pMissile->extra];
- if (pMissile->type == kMissileExplodingFlare || pMissile->type == kMissileStarburstFlare)
- {
- actExplodeSprite( nSprite );
- break;
- }
- if ( hitType == kHitSprite && sprite[hitObject].extra > 0 )
- {
- SPRITE *pObject = &sprite[hitObject];
- XSPRITE *pXObject = &xsprite[pObject->extra];
- pMissile->picnum = kAnmFlareBurn;
- pXMissile->target = hitObject;
- pXMissile->targetZ = pMissile->z - pObject->z;
- pXMissile->goalAng = getangle( pMissile->x - pObject->x, pMissile->y - pObject->y ) - pObject->ang;
- pXMissile->state = 1;
- actAddBurnTime(pMissile->owner, pXObject, 8 * kTimerRate);
- actDamageSprite(pMissile->owner, hitObject, kDamageStab, 10 << 4);
- actPostSprite( nSprite, kStatFlare );
- evPost(nSprite, SS_SPRITE, 8 * kTimerRate); // callback to flare
- }
- else
- {
- // ADD: spawn an explosion later
- actPostSprite( nSprite, kStatFree );
- }
- break;
- }
- case kMissileSprayFlame:
- case kMissileHoundFire:
- // seqKill(SS_SPRITE, nXMissile);
- // deletesprite((short)nSprite);
- if ( hitType == kHitSprite && sprite[hitObject].extra > 0 )
- {
- XSPRITE *pXObject = &xsprite[sprite[hitObject].extra];
- actAddBurnTime( pMissile->owner, pXObject, kFrameTicks );
- }
- break;
- case kMissileEctoSkull:
- actPostSprite( nSprite, kStatEffect );
- seqSpawn(kSeqSkullExplode, SS_SPRITE, pMissile->extra);
- if ( hitType == kHitSprite && sprite[hitObject].statnum == kStatDude )
- {
- actDamageSprite(pMissile->owner, hitObject, kDamageSpirit, 50 << 4);
- SPRITE *pDude = &sprite[pMissile->owner];
- XSPRITE *pXDude = &xsprite[pDude->extra];
- if ( pXDude->health > 0 )
- actHealDude(pXDude, 25);
- }
- break;
- case kMissileButcherKnife:
- actPostSprite( nSprite, kStatEffect );
- pMissile->cstat &= ~kSpriteWall;
- pMissile->type = kNothing;
- seqSpawn(kSeqSkullExplode, SS_SPRITE, pMissile->extra);
- if ( hitType == kHitSprite && sprite[hitObject].statnum == kStatDude )
- {
- actDamageSprite(pMissile->owner, hitObject, kDamageSpirit, 15 << 4);
- SPRITE *pDude = &sprite[pMissile->owner];
- XSPRITE *pXDude = &xsprite[pDude->extra];
- int dudeIndex = pDude->type - kDudeBase;
- if ( pXDude->health > 0 )
- actHealDude(pXDude, 10, dudeInfo[dudeIndex].startHealth);
- }
- break;
- default:
- seqKill(SS_SPRITE, nXMissile);
- actPostSprite( nSprite, kStatFree );
- if (hitType == kHitSprite)
- actDamageSprite(pMissile->owner, hitObject, kDamagePummel, 5 << 4);
- break;
- }
- }
- void ProcessTouchObjects( int nSprite, int nXSprite )
- {
- SPRITE *pSprite = &sprite[nSprite];
- XSPRITE *pXSprite = &xsprite[nXSprite];
- SPRITEHIT *pSpriteHit = &gSpriteHit[nXSprite];
- int nHitObject = pSpriteHit->ceilHit & kHitIndexMask;
- switch( pSpriteHit->ceilHit & kHitTypeMask )
- {
- case kHitSprite:
- if ( sprite[nHitObject].extra > 0 )
- {
- SPRITE *pHit = &sprite[nHitObject];
- XSPRITE *pXHit = &xsprite[pHit->extra];
- switch ( pHit->type )
- {
- case kTrapSawBlade:
- if ( pXHit->state )
- {
- pXHit->data1 = 1;
- pXHit->data2 = ClipHigh(pXHit->data2 + 2 * kFrameTicks, kTimerRate * 5);
- actDamageSprite(nSprite, nSprite, kDamageStab, 4);
- }
- else
- actDamageSprite(nSprite, nSprite, kDamageStab, 1);
- break;
- }
- }
- break;
- case kHitWall:
- case kHitSector:
- break;
- }
- nHitObject = pSpriteHit->moveHit & kHitIndexMask;
- switch( pSpriteHit->moveHit & kHitTypeMask )
- {
- case kHitSprite:
- break;
- case kHitWall:
- case kHitSector:
- break;
- }
- nHitObject = pSpriteHit->floorHit & kHitIndexMask;
- switch( pSpriteHit->floorHit & kHitTypeMask )
- {
- case kHitSprite:
- if ( sprite[nHitObject].extra > 0 )
- {
- SPRITE *pHit = &sprite[nHitObject];
- XSPRITE *pXHit = &xsprite[pHit->extra];
- switch ( pHit->type )
- {
- case kTrapSawBlade:
- if ( pXHit->state )
- {
- pXHit->data1 = 1;
- pXHit->data2 = ClipHigh(pXHit->data2 + 2 * kFrameTicks, kTimerRate * 5);
- actDamageSprite(nSprite, nSprite, kDamageStab, 4);
- }
- else
- actDamageSprite(nSprite, nSprite, kDamageStab, 1);
- break;
- }
- }
- break;
- case kHitWall:
- case kHitSector:
- break;
- }
- }
- #define kMinZVel (6 << 4)
- #define kAirDrag 0x0100
- static int MoveThing( int nSprite, char cliptype )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if ( !(pSprite->flags & kAttrFalling) && !pSprite->xvel && !pSprite->yvel && !pSprite->zvel )
- return 0;
- short nSector = pSprite->sectnum;
- dassert(nSector >= 0 && nSector < kMaxSectors);
- gSpriteHit[nXSprite].ceilHit = 0;
- gSpriteHit[nXSprite].floorHit = 0;
- gSpriteHit[nXSprite].moveHit = 0;
- int zTop, zBot;
- GetSpriteExtents(pSprite, &zTop, &zBot);
- if ( pSprite->xvel || pSprite->yvel )
- {
- short oldcstat = pSprite->cstat;
- pSprite->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
- gSpriteHit[nXSprite].moveHit = ClipMove(&pSprite->x, &pSprite->y, &pSprite->z, &nSector,
- pSprite->xvel * kFrameTicks >> 4, pSprite->yvel * kFrameTicks >> 4, pSprite->clipdist << 2,
- (pSprite->z - zTop) / 4, (zBot - pSprite->z) / 4, cliptype);
- pSprite->cstat = oldcstat;
- if ((nSector != pSprite->sectnum) && (nSector >= 0))
- changespritesect((short)nSprite, nSector);
- switch (gSpriteHit[nXSprite].moveHit & kHitTypeMask)
- {
- case kHitWall:
- {
- int nWall = gSpriteHit[nXSprite].moveHit & kHitIndexMask;
- ReflectVector(&pSprite->xvel, &pSprite->yvel, nWall, 0x4000);
- pSprite->zvel = (short)mulscale16(pSprite->zvel, 0xC000);
- break;
- }
- // need to handle sprite collisions here....
- }
- }
- long ceilz, ceilhit, floorz, floorhit;
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, pSprite->clipdist << 2, cliptype);
- if ( pSprite->zvel )
- {
- pSprite->z += pSprite->zvel * kFrameTicks;
- pSprite->zvel -= mulscale16r(pSprite->zvel, kAirDrag);
- }
- if ( (pSprite->flags & kAttrGravity) && zBot < floorz )
- {
- pSprite->z += kGravity * kFrameTicks * kFrameTicks / 2;
- pSprite->zvel += kGravity * kFrameTicks;
- pSprite->flags |= kAttrFalling;
- }
- // check for warping in linked sectors
- int nUpper = gUpperLink[nSector], nLower = gLowerLink[nSector];
- if ( nUpper >= 0 && pSprite->z < sprite[nUpper].z )
- {
- nLower = sprite[nUpper].owner;
- changespritesect((short)nSprite, sprite[nLower].sectnum);
- pSprite->x += sprite[nLower].x - sprite[nUpper].x;
- pSprite->y += sprite[nLower].y - sprite[nUpper].y;
- pSprite->z += sprite[nLower].z - sprite[nUpper].z;
- viewBackupSpriteLoc(nSprite, pSprite); // prevent interpolation
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, pSprite->clipdist << 2, cliptype);
- }
- else if ( nLower >= 0 && pSprite->z > sprite[nLower].z )
- {
- nUpper = sprite[nLower].owner;
- changespritesect((short)nSprite, sprite[nUpper].sectnum);
- pSprite->x += sprite[nUpper].x - sprite[nLower].x;
- pSprite->y += sprite[nUpper].y - sprite[nLower].y;
- pSprite->z += sprite[nUpper].z - sprite[nLower].z;
- viewBackupSpriteLoc(nSprite, pSprite); // prevent interpolation
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, pSprite->clipdist << 2, cliptype);
- }
- GetSpriteExtents(pSprite, &zTop, &zBot);
- // hit floor?
- if ( zBot >= floorz )
- {
- gSpriteHit[nXSprite].floorHit = floorhit;
- pSprite->z += floorz - zBot;
- if ( pSprite->flags & kAttrFalling )
- {
- pSprite->xvel = (short)mulscale16(pSprite->xvel, 0xC000);
- pSprite->yvel = (short)mulscale16(pSprite->yvel, 0xC000);
- pSprite->zvel = (short)mulscale16(-pSprite->zvel, 0x4000);
- if ( qabs(pSprite->zvel) < kMinZVel)
- {
- pSprite->zvel = 0;
- pSprite->flags &= ~kAttrFalling;
- }
- return kHitFloor | nSector;
- }
- pSprite->zvel = 0;
- }
- // hit ceiling
- if ( zTop < ceilz && ((ceilhit & kHitTypeMask) != kHitSector || !(sector[nSector].ceilingstat & kSectorParallax)) )
- {
- gSpriteHit[nXSprite].ceilHit = ceilhit;
- pSprite->z += ClipLow(ceilz - zTop, 0);
- pSprite->xvel = (short)mulscale16(pSprite->xvel, 0xC000);
- pSprite->yvel = (short)mulscale16(pSprite->yvel, 0xC000);
- pSprite->zvel = (short)mulscale16(-pSprite->zvel, 0x4000);
- // return kHitCeiling | nSector;
- }
- // drag and friction
- if ( pSprite->xvel || pSprite->yvel )
- {
- // air drag
- pSprite->xvel -= mulscale16r(pSprite->xvel, kAirDrag);
- pSprite->yvel -= mulscale16r(pSprite->yvel, kAirDrag);
- if ( !(pSprite->flags & kAttrFalling) )
- {
- // sliding
- int vel = qdist(pSprite->xvel, pSprite->yvel);
- int nFrict = ClipHigh(kFrameTicks * kGroundFriction, vel);
- if ( (floorhit & kHitTypeMask) == kHitSprite )
- {
- int nUnderSprite = floorhit & kHitIndexMask;
- if ( (sprite[nUnderSprite].cstat & kSpriteRMask) == kSpriteFace )
- {
- // push it off the face sprite
- pSprite->xvel += mulscale(kFrameTicks, pSprite->x - sprite[nUnderSprite].x, 6);
- pSprite->yvel += mulscale(kFrameTicks, pSprite->y - sprite[nUnderSprite].y, 6);
- return gSpriteHit[nXSprite].moveHit;
- }
- }
- if (vel > 0)
- {
- nFrict = divscale16(nFrict, vel);
- pSprite->xvel -= mulscale16(nFrict, pSprite->xvel);
- pSprite->yvel -= mulscale16(nFrict, pSprite->yvel);
- }
- }
- }
- return gSpriteHit[nXSprite].moveHit;
- }
- static void MoveDebris( int nSprite )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- short nSector = pSprite->sectnum;
- dassert(nSector >= 0 && nSector < kMaxSectors);
- if ( pSprite->xvel || pSprite->yvel )
- {
- pSprite->x += pSprite->xvel * kFrameTicks >> 4;
- pSprite->y += pSprite->yvel * kFrameTicks >> 4;
- // air drag
- pSprite->xvel -= mulscale16r(pSprite->xvel, kAirDrag);
- pSprite->yvel -= mulscale16r(pSprite->yvel, kAirDrag);
- }
- if ( pSprite->zvel )
- {
- pSprite->z += pSprite->zvel * kFrameTicks;
- pSprite->zvel -= mulscale16r(pSprite->zvel, kAirDrag);
- }
- if ( pSprite->flags & kAttrGravity )
- {
- pSprite->z += kGravity * kFrameTicks * kFrameTicks / 2;
- pSprite->zvel += kGravity * kFrameTicks;
- }
- if ( !FindSector(pSprite->x, pSprite->y, pSprite->z, &nSector) )
- {
- actPostSprite(nSprite, kStatFree);
- return;
- }
- if ( nSector != pSprite->sectnum )
- changespritesect((short)nSprite, nSector);
- }
- #define kScreamVel 1200 // zvel at which player screams
- #define kGruntVel 700
- #define kDudeDrag 0x2800
- #define kMinDudeVel (2 << 4)
- static void MoveDude( int nSprite )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if ( !(pSprite->flags & kAttrFalling) && !pSprite->xvel && !pSprite->yvel && !pSprite->zvel )
- return;
- PLAYER *pPlayer = NULL;
- if ( IsPlayerSprite(pSprite) )
- pPlayer = &gPlayer[pSprite->type - kDudePlayer1];
- dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
- DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type - kDudeBase];
- int zTop, zBot;
- GetSpriteExtents(pSprite, &zTop, &zBot);
- int floorDist = (zBot - pSprite->z) / 4;
- int ceilDist = (pSprite->z - zTop) / 4;
- int clipDist = pSprite->clipdist << 2;
- short nSector = pSprite->sectnum;
- dassert(nSector >= 0 && nSector < kMaxSectors);
- // if ( !(pSprite->xvel || pSprite->yvel || pSprite->zvel) && fWasOnFloor )
- // return;
- gSpriteHit[nXSprite].ceilHit = 0;
- gSpriteHit[nXSprite].floorHit = 0;
- gSpriteHit[nXSprite].moveHit = 0;
- if ( pSprite->xvel || pSprite->yvel )
- {
- int oldX = pSprite->x;
- int oldY = pSprite->y;
- if ( pPlayer && gNoClip )
- {
- pSprite->x += pSprite->xvel * kFrameTicks >> 4;
- pSprite->y += pSprite->yvel * kFrameTicks >> 4;
- updatesector( pSprite->x, pSprite->y, &nSector);
- if (nSector == -1)
- {
- pSprite->x = oldX;
- pSprite->y = oldY;
- nSector = pSprite->sectnum;
- }
- }
- else
- {
- short oldcstat = pSprite->cstat;
- pSprite->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
- gSpriteHit[nXSprite].moveHit = ClipMove(
- &pSprite->x, &pSprite->y, &pSprite->z, &nSector,
- pSprite->xvel * kFrameTicks >> 4, pSprite->yvel * kFrameTicks >> 4, clipDist,
- ceilDist, floorDist, 0);
- pSprite->cstat = oldcstat;
- }
- if ( pPlayer )
- {
- if ( nSector != pSprite->sectnum && nSector >= 0 )
- {
- // process sector exit/enter triggers
- int nXSector;
- nXSector = sector[pSprite->sectnum].extra;
- if ( nXSector > 0 && xsector[nXSector].triggerExit )
- trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCommandSectorExit);
- nXSector = sector[nSector].extra;
- if ( nXSector > 0 && xsector[nXSector].triggerEnter )
- trTriggerSector(nSector, &xsector[nXSector], kCommandSectorEnter);
- }
- }
- if ( nSector != pSprite->sectnum )
- changespritesect((short)nSprite, nSector);
- switch (gSpriteHit[nXSprite].moveHit & kHitTypeMask)
- {
- case kHitWall:
- {
- int nWall = gSpriteHit[nXSprite].moveHit & kHitIndexMask;
- WALL *pWall = &wall[nWall];
- // don't bounce off wall if not fully blocking
- if ( pWall->nextsector != -1 )
- {
- SECTOR *pSector = §or[pWall->nextsector];
- if ( pSector->floorz > zTop || pSector->ceilingz < zBot )
- {
- ReflectVector(&pSprite->xvel, &pSprite->yvel, nWall, 0);
- break;
- }
- }
- ReflectVector(&pSprite->xvel, &pSprite->yvel, nWall, 0x4000);
- pSprite->zvel = (short)mulscale16(pSprite->zvel, 0xC000);
- break;
- }
- // need to handle sprite collisions here....
- }
- }
- long ceilz, ceilhit, floorz, floorhit;
- if ( pPlayer )
- clipDist += 16; // increase clipdist to allow jumping onto ledges
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, clipDist, 0);
- if ( pSprite->zvel )
- {
- pSprite->z += pSprite->zvel * kFrameTicks;
- pSprite->zvel -= mulscale16r(pSprite->zvel, kAirDrag);
- }
- if ( pPlayer && pSprite->zvel > kScreamVel && !pPlayer->fScreamed )
- {
- pPlayer->fScreamed = TRUE;
- sfxStart3DSound(nXSprite, kSfxPlayFall);
- }
- if ( (pSprite->flags & kAttrGravity) && zBot < floorz )
- {
- pSprite->z += kGravity * kFrameTicks * kFrameTicks / 2;
- pSprite->zvel += kGravity * kFrameTicks;
- pSprite->flags |= kAttrFalling;
- }
- // check for warping in linked sectors
- int nUpper = gUpperLink[nSector], nLower = gLowerLink[nSector];
- if ( nUpper >= 0 && pSprite->z < sprite[nUpper].z )
- {
- nLower = sprite[nUpper].owner;
- changespritesect((short)nSprite, sprite[nLower].sectnum);
- pSprite->x += sprite[nLower].x - sprite[nUpper].x;
- pSprite->y += sprite[nLower].y - sprite[nUpper].y;
- pSprite->z += sprite[nLower].z - sprite[nUpper].z;
- viewBackupSpriteLoc(nSprite, pSprite); // prevent interpolation
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, clipDist, 0);
- }
- else if ( nLower >= 0 && pSprite->z > sprite[nLower].z )
- {
- nUpper = sprite[nLower].owner;
- changespritesect((short)nSprite, sprite[nUpper].sectnum);
- pSprite->x += sprite[nUpper].x - sprite[nLower].x;
- pSprite->y += sprite[nUpper].y - sprite[nLower].y;
- pSprite->z += sprite[nUpper].z - sprite[nLower].z;
- viewBackupSpriteLoc(nSprite, pSprite); // prevent interpolation
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, clipDist, 0);
- }
- GetSpriteExtents(pSprite, &zTop, &zBot);
- if ( pPlayer && zBot >= floorz)
- {
- long floorz2 = floorz, floorhit2 = floorhit;
- GetZRange(pSprite, &ceilz, &ceilhit, &floorz, &floorhit, pSprite->clipdist << 2, 0);
- if ( zBot <= floorz && pSprite->z - floorz2 < floorDist )
- {
- floorz = floorz2;
- floorhit2 = floorhit;
- }
- }
- // hit floor?
- if ( zBot >= floorz )
- {
- gSpriteHit[nXSprite].floorHit = floorhit;
- pSprite->z += floorz - zBot;
- if ( pSprite->flags & kAttrFalling )
- {
- // need some way of converting fall damge to a non-linear curve
- int fallDamage = mulscale16(pSprite->zvel, pSprite->zvel);
- fallDamage = mulscale(fallDamage, fallDamage, 1);
- if ( fallDamage > (20 << 4) )
- actDamageSprite(nSprite, nSprite, kDamageFall, fallDamage);
- if ( pPlayer && pXSprite->health > 0 )
- {
- pPlayer->fScreamed = FALSE;
- if ( pSprite->zvel > kGruntVel )
- sfxStart3DSound(nXSprite, kSfxPlayLand);
- }
- pSprite->xvel = (short)mulscale16(pSprite->xvel, 0xC000);
- pSprite->yvel = (short)mulscale16(pSprite->yvel, 0xC000);
- pSprite->zvel = (short)mulscale16(-pSprite->zvel, 0x2000);
- if ( pSprite->zvel > -kMinZVel )
- {
- pSprite->zvel = 0;
- pSprite->flags &= ~kAttrFalling;
- }
- int nSurfType = tileGetSurfType(floorhit);
- if ( nSurfType == kSurfWater )
- {
- actSpawnEffect(pSprite->sectnum, pSprite->x, pSprite->y, floorz, ET_Splash1);
- }
- return;
- }
- pSprite->zvel = 0;
- }
- // hit ceiling
- if ( zTop < ceilz && ((ceilhit & kHitTypeMask) != kHitSector || !(sector[nSector].ceilingstat & kSectorParallax)) )
- {
- pSprite->z += ClipLow(ceilz - zTop, 0);
- pSprite->xvel = (short)mulscale16(pSprite->xvel, 0xC000);
- pSprite->yvel = (short)mulscale16(pSprite->yvel, 0xC000);
- pSprite->zvel = (short)mulscale16(-pSprite->zvel, 0x2000);
- }
- if ( zTop <= ceilz )
- gSpriteHit[nXSprite].ceilHit = ceilhit;
- // drag and friction
- if ( pSprite->xvel || pSprite->yvel )
- {
- // air drag
- pSprite->xvel -= mulscale16(pSprite->xvel, kAirDrag);
- pSprite->yvel -= mulscale16(pSprite->yvel, kAirDrag);
- if ( !(pSprite->flags & kAttrFalling) )
- {
- if ( (floorhit & kHitTypeMask) == kHitSprite )
- {
- int nUnderSprite = floorhit & kHitIndexMask;
- if ( (sprite[nUnderSprite].cstat & kSpriteRMask) == kSpriteFace )
- {
- // push it off the face sprite
- pSprite->xvel += mulscale(kFrameTicks, pSprite->x - sprite[nUnderSprite].x, 6);
- pSprite->yvel += mulscale(kFrameTicks, pSprite->y - sprite[nUnderSprite].y, 6);
- return;
- }
- }
- // movement drag
- pSprite->xvel -= (sshort)mulscale16r(pSprite->xvel, kDudeDrag);
- pSprite->yvel -= (sshort)mulscale16r(pSprite->yvel, kDudeDrag);
- if ( qdist(pSprite->xvel, pSprite->yvel) < kMinDudeVel )
- pSprite->xvel = pSprite->yvel = 0;
- }
- }
- ProcessTouchObjects( nSprite, nXSprite );
- }
- // missiles are self-propelled and are unaffected by gravity
- static int MoveMissile( int nSprite )
- {
- dassert(nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- return movesprite((short)nSprite, pSprite->xvel, pSprite->yvel, pSprite->zvel,
- 4 << 8, 4 << 8, 1, kFrameTicks);
- }
- void actExplodeSprite( int nSprite )
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- // already exploding?
- if (pSprite->statnum == kStatExplosion)
- return;
- switch ( pSprite->type )
- {
- case kMissileExplodingFlare:
- case kMissileStarburstFlare:
- seqSpawn(kSeqExplodeC2T, SS_SPRITE, nXSprite);
- break;
- case kThingTNTStick:
- if ( gSpriteHit[nXSprite].floorHit == 0 )
- seqSpawn(kSeqExplodeC2S, SS_SPRITE, nXSprite);
- else
- seqSpawn(kSeqExplodeC1S, SS_SPRITE, nXSprite);
- break;
- case kThingTNTProxArmed:
- case kThingTNTRemArmed:
- case kThingTNTBundle:
- // if ( gSpriteHit[nXSprite].floorHit == 0 )
- seqSpawn(kSeqExplodeC2M, SS_SPRITE, nXSprite);
- // else
- // seqSpawn(kSeqExplodeC1M, SS_SPRITE, nXSprite);
- break;
- case kThingTNTBarrel:
- {
- // spawn an explosion effect
- int nEffect = actSpawnSprite( pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatExplosion, TRUE );
- sprite[nEffect].owner = pSprite->owner; // set owner for frag/targeting
- // place barrel on the respawn list or just delete it
- if ( actCheckRespawn( nSprite ) )
- {
- XSPRITE *pXSprite = &xsprite[nXSprite];
- pXSprite->state = 0;
- pXSprite->health = thingInfo[kThingTNTBarrel - kThingBase].startHealth << 4;
- }
- else
- actPostSprite( nSprite, kStatFree );
- // reset locals to point at the effect, not the barrel
- nSprite = nEffect;
- pSprite = &sprite[nEffect];
- nXSprite = pSprite->extra;
- seqSpawn(kSeqExplodeC2L, SS_SPRITE, nXSprite);
- break;
- }
- default:
- seqSpawn(kSeqExplodeC2M, SS_SPRITE, nXSprite);
- break;
- }
- pSprite->xvel = pSprite->yvel = pSprite->zvel = 0;
- actPostSprite( nSprite, kStatExplosion );
- pSprite->flags &= ~(kAttrMove | kAttrGravity);
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z, kSfxExplodeCS);
- }
- void actProcessSprites(void)
- {
- int nSprite;
- int nDude, nNextDude;
- // process proximity triggered sprites
- for (nSprite = headspritestat[kStatProximity]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- for (nDude = headspritestat[kStatDude]; nDude >= 0; nDude = nNextDude)
- {
- nNextDude = nextspritestat[nDude];
- if ( CheckProximity(&sprite[nDude], pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, 64) )
- trTriggerSprite(nSprite, pXSprite, kCommandSpriteProximity);
- }
- }
- // process things for effects
- for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->extra > 0 )
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- if ( actGetBurnTime(pXSprite) > 0 )
- {
- pXSprite->burnTime = ClipLow(pXSprite->burnTime - kFrameTicks, 0);
- actDamageSprite( pXSprite->burnSource, nSprite, kDamageBurn, 4 * kFrameTicks );
- }
- }
- }
- // process things for movement
- for (nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->flags & (kAttrMove | kAttrGravity) )
- {
- viewBackupSpriteLoc(nSprite, pSprite);
- int hitInfo = MoveThing(nSprite, 1);
- if (hitInfo != 0)
- {
- int nXSprite = pSprite->extra;
- if (nXSprite > 0)
- {
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if ( pXSprite->triggerProximity )
- trTriggerSprite(nSprite, pXSprite, kCommandOff);
- switch( pSprite->type )
- {
- case kThingWaterDrip:
- case kThingBloodDrip:
- MakeSplash(pSprite, pXSprite);
- break;
- case kThingBoneClub:
- seqSpawn(kSeqBoneBreak, SS_SPRITE, nXSprite);
- if ( (hitInfo & kHitTypeMask) == kHitSprite )
- actDamageSprite( pSprite->owner, hitInfo & kHitIndexMask, kDamagePummel, 12 );
- break;
- }
- }
- }
- }
- }
- // process debris sprites
- for (nSprite = headspritestat[kStatDebris]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- viewBackupSpriteLoc(nSprite, pSprite);
- MoveDebris(nSprite);
- }
- // process missile sprites
- for (nSprite = headspritestat[kStatMissile]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- viewBackupSpriteLoc(nSprite, pSprite);
- int hitInfo = MoveMissile(nSprite);
- // process impacts
- if (hitInfo != 0)
- actImpactMissile( nSprite, hitInfo );
- }
- // process explosions
- for (nSprite = headspritestat[kStatExplosion]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int x = pSprite->x, y = pSprite->y, z = pSprite->z, nSector = pSprite->sectnum;
- int nAffected;
- int radius = tilesizx[pSprite->picnum] * pSprite->xrepeat >> 6;
- for (nAffected = headspritestat[kStatDude]; nAffected >= 0; nAffected = nextspritestat[nAffected])
- {
- if ( CheckProximity(&sprite[nAffected], x, y, z, nSector, radius) )
- ConcussSprite(pSprite->owner, nAffected, x, y, z, kFrameTicks);
- }
- for (nAffected = headspritestat[kStatThing]; nAffected >= 0; nAffected = nextspritestat[nAffected])
- {
- if ( CheckProximity(&sprite[nAffected], x, y, z, nSector, radius) )
- ConcussSprite(pSprite->owner, nAffected, x, y, z, kFrameTicks);
- }
- for (nAffected = headspritestat[kStatProximity]; nAffected >= 0; nAffected = nextspritestat[nAffected])
- {
- if ( CheckProximity(&sprite[nAffected], x, y, z, nSector, radius) )
- ConcussSprite(pSprite->owner, nAffected, x, y, z, kFrameTicks);
- }
- }
- // process traps for effects
- for (nSprite = headspritestat[kStatTraps]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->extra > 0 )
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- switch( pSprite->type )
- {
- case kTrapSawBlade:
- pXSprite->data2 = ClipLow(pXSprite->data2 - kFrameTicks, 0);
- break;
- }
- }
- }
- // process dudes for effects
- for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->extra > 0 )
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- if ( actGetBurnTime(pXSprite) > 0 )
- {
- pXSprite->burnTime = ClipLow(pXSprite->burnTime - kFrameTicks, 0);
- actDamageSprite( pXSprite->burnSource, nSprite, kDamageBurn, 2 * kFrameTicks );
- }
- }
- }
- // process dudes for movement
- for (nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nSector = pSprite->sectnum;
- viewBackupSpriteLoc(nSprite, pSprite);
- // special sector processing
- if ( sector[nSector].extra > 0 )
- {
- int nXSector = sector[ nSector ]. extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- dassert(xsector[nXSector].reference == nSector);
- XSECTOR *pXSector = &xsector[nXSector];
- if ( pXSector->panVel && (pXSector->panAlways || pXSector->busy))
- {
- int windDrag = 0x0200; // 16:16 fixed point fraction
- int panVel = pXSector->panVel;
- int panAngle = pXSector->panAngle;
- if ( !pXSector->panAlways && pXSector->busy )
- panVel = mulscale16(panVel, pXSector->busy);
- if (sector[nSector].floorstat & kSectorRelAlign)
- {
- panAngle += GetWallAngle(sector[nSector].wallptr) + kAngle90;
- panAngle &= kAngleMask;
- }
- if (pXSector->wind)
- {
- int windX = mulscale30(panVel, Cos(panAngle)) - pSprite->xvel;
- int windY = mulscale30(panVel, Sin(panAngle)) - pSprite->yvel;
- pSprite->xvel += mulscale16(kFrameTicks * windX, windDrag);
- pSprite->yvel += mulscale16(kFrameTicks * windY, windDrag);
- }
- }
- // handle water dragging
- if ( pXSector->depth > 0 )
- {
- if ( pSprite->z >= sector[nSector].floorz )
- {
- int pushDrag = dragTable[pXSector->depth];
- int panVel = 0;
- int panAngle = pXSector->panAngle;
- if (pXSector->panAlways || pXSector->state || pXSector->busy)
- {
- panVel = pXSector->panVel;
- if ( !pXSector->panAlways && pXSector->busy )
- panVel = mulscale16(panVel, pXSector->busy);
- }
- if (sector[nSector].floorstat & kSectorRelAlign)
- {
- panAngle += GetWallAngle(sector[nSector].wallptr) + kAngle90;
- panAngle &= kAngleMask;
- }
- int pushX = mulscale30(panVel, Cos(panAngle)) - pSprite->xvel;
- int pushY = mulscale30(panVel, Sin(panAngle)) - pSprite->yvel;
- int nDrag = ClipHigh(kFrameTicks * pushDrag, 0x10000);
- pSprite->xvel += mulscale16(pushX, nDrag);
- pSprite->yvel += mulscale16(pushY, nDrag);
- }
- } else if ( pXSector->underwater ) // handle underwater dragging
- {
- int pushDrag = dragTable[pXSector->depth];
- int panVel = 0;
- int panAngle = pXSector->panAngle;
- if (pXSector->panAlways || pXSector->state || pXSector->busy)
- {
- panVel = pXSector->panVel;
- if ( !pXSector->panAlways && pXSector->busy )
- panVel = mulscale16(panVel, pXSector->busy);
- }
- if (sector[nSector].floorstat & kSectorRelAlign)
- {
- panAngle += GetWallAngle(sector[nSector].wallptr) + kAngle90;
- panAngle &= kAngleMask;
- }
- int pushX = mulscale30(panVel, Cos(panAngle)) - pSprite->xvel;
- int pushY = mulscale30(panVel, Sin(panAngle)) - pSprite->yvel;
- pSprite->xvel += mulscale16(kFrameTicks * pushX, pushDrag);
- pSprite->yvel += mulscale16(kFrameTicks * pushY, pushDrag);
- }
- }
- MoveDude(nSprite);
- }
- // process flares to keep them burning on dudes
- for (nSprite = headspritestat[kStatFlare]; nSprite >= 0; nSprite = nextspritestat[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- SPRITE *pTarget = &sprite[pXSprite->target];
- viewBackupSpriteLoc(nSprite, pSprite);
- int nAngle = pTarget->ang + pXSprite->goalAng;
- int x = pTarget->x + mulscale30r( Cos(nAngle), pTarget->clipdist << 1 ); // halfway into clipdist
- int y = pTarget->y + mulscale30r( Sin(nAngle), pTarget->clipdist << 1 );
- int z = pTarget->z + pXSprite->targetZ;
- setsprite((short)pXSprite->reference, x, y, z);
- }
- aiProcessDudes();
- }
- /***********************************************************************
- * actSpawnSprite()
- *
- * Spawns a new sprite at the specified world coordinates.
- **********************************************************************/
- int actSpawnSprite( short nSector, int x, int y, int z, short nStatus, BOOL bAddXSprite )
- {
- int nSprite = insertsprite( nSector, nStatus );
- if (nSprite >= 0)
- sprite[nSprite].extra = -1;
- else
- {
- dprintf("Out of sprites -- reclaiming sprite from purge list\n");
- nSprite = headspritestat[kStatPurge];
- dassert(nSprite >= 0);
- changespritesect((short)nSprite, nSector);
- changespritestat((short)nSprite, nStatus);
- }
- setsprite( (short)nSprite, x, y, z );
- SPRITE *pSprite = &sprite[nSprite];
- pSprite->type = 0;
- viewBackupSpriteLoc(nSprite, pSprite);
- // optionally create an xsprite
- if ( bAddXSprite && pSprite->extra == -1 )
- {
- int nXSprite = dbInsertXSprite(nSprite);
- gSpriteHit[nXSprite].floorHit = 0;
- gSpriteHit[nXSprite].ceilHit = 0;
- }
- return nSprite;
- }
- int actCloneSprite( SPRITE *pSourceSprite )
- {
- int nSprite = insertsprite( pSourceSprite->sectnum, pSourceSprite->statnum);
- if ( nSprite < 0 )
- {
- dprintf("Out of sprites -- reclaiming sprite from purge list\n");
- nSprite = headspritestat[kStatPurge];
- dassert(nSprite >= 0);
- changespritesect((short)nSprite, pSourceSprite->sectnum);
- changespritestat((short)nSprite, pSourceSprite->statnum);
- }
- SPRITE *pSprite = &sprite[nSprite];
- *pSprite = *pSourceSprite;
- viewBackupSpriteLoc(nSprite, pSprite);
- // don't copy xsprite
- pSprite->extra = -1;
- return nSprite;
- }
- int actSpawnThing( short nSector, int x, int y, int z, int thingType )
- {
- dassert( thingType >= kThingBase && thingType < kThingMax );
- int nThing = actSpawnSprite(nSector, x, y, z, kStatThing, TRUE );
- SPRITE *pThing = &sprite[nThing];
- pThing->type = (short)thingType;
- int thingIndex = thingType - kThingBase;
- int nXThing = pThing->extra;
- dassert(nXThing > 0 && nXThing < kMaxXSprites);
- XSPRITE *pXThing = &xsprite[nXThing];
- pThing->clipdist = thingInfo[thingIndex].clipdist;
- pThing->flags = thingInfo[thingIndex].flags;
- if ( pThing->flags & kAttrGravity )
- pThing->flags |= kAttrFalling;
- pThing->pal = 0;
- pXThing->health = thingInfo[thingIndex].startHealth << 4;
- SetBitString(show2dsprite, nThing);
- switch (thingType)
- {
- case kThingTNTStick:
- pThing->shade = -32;
- pThing->picnum = 2169;
- pThing->cstat |= kSpriteHitscan;
- pThing->xrepeat = pThing->yrepeat = 32;
- break;
- case kThingTNTBundle:
- pThing->shade = -32;
- pThing->picnum = 2172;
- pThing->cstat |= kSpriteHitscan;
- pThing->xrepeat = pThing->yrepeat = 32;
- break;
- case kThingTNTProxArmed:
- pThing->shade = -16;
- pThing->picnum = kAnmTNTProxArmed;
- pThing->cstat |= kSpriteHitscan;
- pThing->xrepeat = pThing->yrepeat = 32;
- break;
- case kThingTNTRemArmed:
- pThing->shade = -16;
- pThing->picnum = kAnmTNTRemArmed;
- pThing->cstat |= kSpriteHitscan;
- pThing->xrepeat = pThing->yrepeat = 32;
- break;
- case kThingBoneClub:
- pThing->shade = 0;
- pThing->picnum = kAnmBoneClub;
- pThing->cstat |= kSpriteHitscan;
- pThing->xrepeat = pThing->yrepeat = 32;
- break;
- case kThingWaterDrip:
- pThing->picnum = kAnmDrip;
- pThing->pal = kPLUCold;
- //pThing->cstat |= kSpriteTranslucent | kSpriteTranslucentR;
- pThing->shade = 0; // -12;
- break;
- case kThingBloodDrip:
- pThing->picnum = kAnmDrip;
- pThing->pal = kPLURed;
- //pThing->cstat |= kSpriteTranslucent | kSpriteTranslucentR;
- pThing->shade = 0; // -12;
- break;
- }
- return nThing;
- }
- int actFireThing( int nActor, int z, int nSlope, int thingType, int velocity )
- {
- dassert( thingType >= kThingBase && thingType < kThingMax );
- SPRITE *pActor = &sprite[nActor];
- int nThing = actSpawnThing(pActor->sectnum,
- pActor->x + mulscale30(pActor->clipdist << 2, Cos(pActor->ang)),
- pActor->y + mulscale30(pActor->clipdist << 2, Sin(pActor->ang)),
- z, thingType);
- SPRITE *pThing = &sprite[nThing];
- pThing->owner = (short)nActor;
- pThing->ang = pActor->ang;
- pThing->xvel = (short)mulscale30(velocity, Cos(pActor->ang));
- pThing->yvel = (short)mulscale30(velocity, Sin(pActor->ang));
- pThing->zvel = (short)mulscale(velocity, nSlope, 14);
- pThing->xvel += pActor->xvel / 2;
- pThing->yvel += pActor->yvel / 2;
- pThing->zvel += pActor->zvel / 2;
- return nThing;
- }
- void actFireMissile( int nActor, int z, int dx, int dy, int dz, int missileType )
- {
- dassert( missileType >= kMissileBase && missileType < kMissileMax );
- SPRITE *pActor = &sprite[nActor];
- int nSprite = actSpawnSprite(pActor->sectnum,
- pActor->x + mulscale30(pActor->clipdist << 1, Cos(pActor->ang)),
- pActor->y + mulscale30(pActor->clipdist << 1, Sin(pActor->ang)),
- z, kStatMissile, TRUE );
- SPRITE *pSprite = &sprite[nSprite];
- pSprite->type = (short)missileType;
- MissileType *pMissType = &missileInfo[ missileType - kMissileBase ];
- int velocity = pMissType->velocity;
- pSprite->shade = pMissType->shade;
- pSprite->pal = 0;
- pSprite->clipdist = 16;
- pSprite->flags = kAttrMove;
- pSprite->xrepeat = pMissType->xrepeat;
- pSprite->yrepeat = pMissType->yrepeat;
- pSprite->picnum = pMissType->picnum;
- pSprite->ang = (short)((pActor->ang +
- missileInfo[missileType - kMissileBase].angleOfs) & kAngleMask);
- pSprite->xvel = (short)mulscale(velocity, dx, 14);
- pSprite->yvel = (short)mulscale(velocity, dy, 14);
- pSprite->zvel = (short)mulscale(velocity, dz, 14);
- pSprite->owner = (short)nActor;
- SetBitString(show2dsprite, nSprite);
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- switch( missileType )
- {
- case kMissileButcherKnife:
- pSprite->cstat |= kSpriteWall;
- break;
- case kMissileSpear:
- pSprite->cstat |= kSpriteWall;
- break;
- case kMissileEctoSkull:
- seqSpawn(kSeqSkull, SS_SPRITE, nXSprite);
- break;
- case kMissileFireball:
- seqSpawn(kSeqFireball, SS_SPRITE, nXSprite, &FireballCallback);
- break;
- case kMissileHoundFire:
- seqSpawn(kSeqHoundFire, SS_SPRITE, nXSprite);
- pSprite->xvel += (pActor->xvel / 2) + BiRandom(50);
- pSprite->yvel += (pActor->yvel / 2) + BiRandom(50);
- pSprite->zvel += (pActor->zvel / 2) + BiRandom(50);
- break;
- case kMissileSprayFlame:
- if ( Chance(0x8000) )
- seqSpawn(kSeqSprayFlame1, SS_SPRITE, nXSprite);
- else
- seqSpawn(kSeqSprayFlame2, SS_SPRITE, nXSprite);
- pSprite->xvel += (pActor->xvel / 2) + BiRandom(50);
- pSprite->yvel += (pActor->yvel / 2) + BiRandom(50);
- pSprite->zvel += (pActor->zvel / 2) + BiRandom(50);
- break;
- case kMissileStarburstFlare:
- evPost(nSprite, SS_SPRITE, kTimerRate/6 ); // callback to flare
- break;
- case kMissileFlare:
- case kMissileExplodingFlare:
- //seqSpawn(kSeqFlare, SS_SPRITE, nXSprite, &FlareCallback);
- //pSprite->pal = kPLURed;
- break;
- default:
- break;
- }
- }
- /***********************************************************************
- * actSpawnEffect()
- *
- * Spawns an effect sprite at the specified world coordinates.
- **********************************************************************/
- SPRITE *actSpawnEffect( short nSector, int x, int y, int z, int nEffect )
- {
- int nSprite = actSpawnSprite(nSector, x, y, z, kStatEffect, TRUE);
- SPRITE *pSprite = &sprite[nSprite];
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
- switch( nEffect )
- {
- case ET_Splash1: // splash from dude landing in water
- seqSpawn(kSeqSplash1, SS_SPRITE, nXSprite);
- break;
- case ET_Splash2: // splash from bullet hitting water
- seqSpawn(kSeqSplash2, SS_SPRITE, nXSprite);
- break;
- case ET_Ricochet1: // bullet ricochet off stone/metal
- if ( Chance(0x8000) )
- pSprite->cstat |= kSpriteFlipX;
- seqSpawn(kSeqRicochet1, SS_SPRITE, nXSprite);
- break;
- case ET_Ricochet2: // bullet ricochet off wood/dirt/clay
- if ( Chance(0x8000) )
- pSprite->cstat |= kSpriteFlipX;
- seqSpawn(kSeqRicochet2, SS_SPRITE, nXSprite);
- break;
- case ET_Squib1:
- if ( Chance(0x8000) )
- pSprite->cstat |= kSpriteFlipX;
- seqSpawn(kSeqSquib1, SS_SPRITE, nXSprite);
- break;
- case ET_SmokeTrail:
- if ( Chance(0x8000) )
- pSprite->cstat |= kSpriteFlipX;
- if ( Chance(0x8000) )
- pSprite->cstat |= kSpriteFlipY;
- seqSpawn(kSeqFlareSmoke, SS_SPRITE, nXSprite);
- break;
- default:
- // don't have a sequence yet, so just delete it for now
- dprintf("actSpawnEffect: missing sequence\n");
- actPostSprite( nSprite, kStatFree );
- return NULL;
- }
- return pSprite;
- }
- BOOL actCheckRespawn( int nSprite )
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->extra > 0 )
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- if (pXSprite->respawn == kRespawnPermanent)
- return TRUE;
- // if (sprite respawn is forced OR (sprite has optional respawning set
- // AND (sprite is a dude AND global dude respawning is set)
- // OR (sprite is a thing AND global thing respawning is set)))
- if (pXSprite->respawn == kRespawnAlways || (pXSprite->respawn == kRespawnOptional
- && (pSprite->type >= kDudeBase && pSprite->type < kDudeMax && gRespawnEnemies)
- || ((pSprite->type < kDudeBase || pSprite->type > kDudeMax) && gRespawnItems)))
- {
- if (pXSprite->respawnTime > 0)
- {
- dprintf("Respawn statnum: %d\n",pSprite->statnum);
- pSprite->owner = pSprite->statnum; // store the sprite's status list for respawning
- actPostSprite( nSprite, kStatRespawn );
- pSprite->cstat &= ~kSpriteBlocking & ~kSpriteHitscan;
- pSprite->cstat |= kSpriteInvisible;
- evPost(nSprite, SS_SPRITE, pXSprite->respawnTime * kTimerRate / 10, kCommandRespawn);
- return TRUE;
- }
- }
- }
- return FALSE; // indicate sprite will not respawn, and should be deleted, exploded, etc.
- }
- void actRespawnDude( int nSprite )
- {
- SPRITE *pSprite = &sprite[nSprite];
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- // probably need more dude-specific initialization
- pXSprite->moveState = 0;
- pXSprite->aiState = 0;
- pXSprite->health = 0;
- pXSprite->target = 0;
- pXSprite->targetX = 0;
- pXSprite->targetY = 0;
- pXSprite->targetZ = 0;
- pXSprite->key = 0; // respawned dudes don't drop additional permanent keys
- }
- void actRespawnSprite( int nSprite )
- {
- SPRITE *pSprite = &sprite[nSprite];
- actPostSprite( nSprite, pSprite->owner );
- pSprite->owner = -1;
- pSprite->cstat &= ~kSpriteInvisible;
- if ( pSprite->extra > 0)
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- pXSprite->isTriggered = 0;
- if (pSprite->type >= kDudeBase && pSprite->type < kDudeMax)
- actRespawnDude( nSprite );
- }
- // spawn the respawn special effect
- int nRespawn = actSpawnSprite( pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, kStatDefault, TRUE );
- seqSpawn(kSeqRespawn, SS_SPRITE, sprite[nRespawn].extra);
- }
- void actFireVector( int nActor, int z, int dx, int dy, int dz, VECTOR_TYPE vectorType )
- {
- SPRITE *pActor = &sprite[nActor];
- HITINFO hitInfo;
- dassert(vectorType >= 0 && vectorType < kVectorMax);
- VECTORDATA *pVectorData = &gVectorData[vectorType];
- int maxDist = pVectorData->maxDist;
- int hitCode = VectorScan( pActor, z, dx, dy, dz, &hitInfo );
- // determine a point just a bit back from the intersection to place the ricochet
- int rx = hitInfo.hitx - mulscale(dx, 1 << 4, 14);
- int ry = hitInfo.hity - mulscale(dy, 1 << 4, 14);
- int rz = hitInfo.hitz - mulscale(dz, 1 << 4, 14);
- BYTE nSurf = kSurfNone;
- if ( maxDist == 0 || qdist(hitInfo.hitx - pActor->x, hitInfo.hity - pActor->y) < maxDist )
- {
- switch ( hitCode )
- {
- case SS_CEILING:
- if ( sector[hitInfo.hitsect].ceilingstat & kSectorParallax )
- nSurf = kSurfNone;
- else
- nSurf = surfType[sector[hitInfo.hitsect].ceilingpicnum];
- break;
- case SS_FLOOR:
- if ( sector[hitInfo.hitsect].floorstat & kSectorParallax )
- nSurf = kSurfNone;
- else
- nSurf = surfType[sector[hitInfo.hitsect].floorpicnum];
- break;
- case SS_WALL:
- {
- int nWall = hitInfo.hitwall;
- dassert( nWall >= 0 && nWall < kMaxWalls );
- nSurf = surfType[wall[nWall].picnum];
- WALL *pWall = &wall[nWall];
- int nXWall = wall[nWall].extra;
- if ( nXWall > 0 )
- {
- XWALL *pXWall = &xwall[nXWall];
- if ( pXWall->triggerImpact )
- trTriggerWall(nWall, pXWall, kCommandWallImpact);
- }
- break;
- }
- case SS_MASKED:
- {
- int nWall = hitInfo.hitwall;
- dassert( nWall >= 0 && nWall < kMaxWalls );
- nSurf = surfType[wall[nWall].overpicnum];
- WALL *pWall = &wall[nWall];
- int nXWall = wall[nWall].extra;
- if ( nXWall > 0 )
- {
- XWALL *pXWall = &xwall[nXWall];
- if ( pXWall->triggerImpact )
- trTriggerWall(nWall, pXWall, kCommandWallImpact);
- }
- break;
- }
- case SS_SPRITE:
- {
- int nSprite = hitInfo.hitsprite;
- nSurf = surfType[sprite[nSprite].picnum];
- dassert( nSprite >= 0 && nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- // back off ricochet (squibs) even more
- rx -= mulscale(dx, 7 << 4, 14);
- ry -= mulscale(dy, 7 << 4, 14);
- rz -= mulscale(dz, 7 << 4, 14);
- actDamageSprite(nActor, nSprite, pVectorData->damageType,
- pVectorData->damageValue << 4);
- int nXSprite = pSprite->extra;
- if ( nXSprite > 0 )
- {
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if ( pXSprite->triggerImpact )
- trTriggerSprite(nSprite, pXSprite, kCommandSpriteImpact);
- }
- break;
- }
- }
- }
- if ( pVectorData->impact[nSurf].nEffect >= 0 )
- actSpawnEffect(hitInfo.hitsect, rx, ry, rz, pVectorData->impact[nSurf].nEffect);
- if ( pVectorData->impact[nSurf].nSoundId >= 0 )
- sfxCreate3DSound(rx, ry, rz, pVectorData->impact[nSurf].nSoundId);
- }
- static void FireballCallback( int /* type */, int nXIndex )
- {
- XSPRITE *pXSprite = &xsprite[nXIndex];
- int nSprite = pXSprite->reference;
- SPRITE *pSprite = &sprite[nSprite];
- SPRITE *pSmoke = actSpawnEffect( pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, ET_SmokeTrail );
- if (gDetail < kDetailLevelMax)
- pSmoke->cstat |= kSpriteInvisible;
- }
- //static void FlareCallback( int /* type */, int nXIndex )
- //{
- // XSPRITE *pXSprite = &xsprite[nXIndex];
- // int nSprite = pXSprite->reference;
- // SPRITE *pSprite = &sprite[nSprite];
- //
- // actSpawnEffect( pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, ET_SmokeTrail );
- //}
- struct POSTPONE
- {
- short nSprite;
- short nStatus;
- };
- int gPostCount = 0;
- POSTPONE gPost[ kMaxSprites ];
- /***********************************************************************
- * actPostSprite()
- *
- * Postpones deletion or status list change for a sprite.
- * An nStatus value of kStatFree passed to this function will
- * postpone deletion of the sprite until the gPostpone list is
- * next processed.
- **********************************************************************/
- void actPostSprite( int nSprite, int nStatus )
- {
- dassert( gPostCount < kMaxSprites );
- dassert( nSprite < kMaxSprites && sprite[nSprite].statnum < kMaxStatus );
- dassert( nStatus >= 0 && nStatus <= kStatFree );
- // see if it is already in the list (we may want to semaphore with an attr bit for speed)
- for (int n = 0; n < gPostCount; n++)
- if ( gPost[n].nSprite == nSprite )
- break;
- gPost[n].nSprite = (short)nSprite;
- gPost[n].nStatus = (short)nStatus;
- if ( n == gPostCount )
- gPostCount++;
- }
- /***********************************************************************
- * actPostProcess()
- *
- * Processes postponed sprite events to ensure that sprite list
- * processing functions normally when sprites are deleted or change
- * status.
- *
- **********************************************************************/
- void actPostProcess( void )
- {
- if (gPostCount)
- {
- for ( int i = 0; i < gPostCount; i++ )
- {
- POSTPONE *pPost = &gPost[i];
- if ( pPost->nStatus == kStatFree )
- deletesprite( pPost->nSprite );
- else
- changespritestat( pPost->nSprite, pPost->nStatus );
- }
- gPostCount = 0;
- }
- }
- static void MakeSplash( SPRITE *pSprite, XSPRITE *pXSprite )
- {
- int nXSprite = pSprite->extra;
- int nSprite = pXSprite->reference;
- pSprite->flags &= ~kAttrGravity; // no bouncing...
- pSprite->z -= 4 << 8; // up one pixel
- viewBackupSpriteLoc(nSprite, pSprite); // prevent interpolation
- int nSurfType = tileGetSurfType(gSpriteHit[nXSprite].floorHit);
- switch ( pSprite->type )
- {
- case kThingWaterDrip:
- switch (nSurfType)
- {
- case kSurfWater:
- seqSpawn(kSeqSplash1, SS_SPRITE, nXSprite);
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z,
- kSfxDrip3, 0x2000);
- // add a check for depth that uses either kSfxDrip2 (deep) or kSfxDrip3 (not deep)
- break;
- default:
- seqSpawn(kSeqSplash2, SS_SPRITE, nXSprite);
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z,
- kSfxDrip1, 0x2000);
- break;
- }
- break;
- case kThingBloodDrip:
- seqSpawn(kSeqSplash3, SS_SPRITE, nXSprite);
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z,
- kSfxDrip1, 0x2000);
- break;
- }
- }
|