1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298 |
- // g_misc.c
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include "g_local.h"
- #include "g_functions.h"
- #include "g_nav.h"
- #include "g_items.h"
- extern gentity_t *G_FindDoorTrigger( gentity_t *door );
- extern void G_SetEnemy( gentity_t *self, gentity_t *enemy );
- extern void SetMiscModelDefaults( gentity_t *ent, useFunc_t use_func, char *material, int solid_mask,int animFlag,
- qboolean take_damage, qboolean damage_model);
- #define MAX_AMMO_GIVE 4
- /*QUAKED func_group (0 0 0) ?
- Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
- q3map_onlyvertexlighting 1 = brush only gets vertex lighting (reduces bsp size!)
- */
- /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) LIGHT
- Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
- LIGHT - If this info_null is only targeted by a non-switchable light (a light without a targetname), it does NOT spawn in at all and doesn't count towards the # of entities on the map, even at map spawn/load
- */
- void SP_info_null( gentity_t *self ) {
- if ( (self->spawnflags&1) )
- {//only used as a light target, so bugger off
- G_FreeEntity( self );
- return;
- }
- //FIXME: store targetname and vector (origin) in a list for further reference... remove after 1st second of game?
- G_SetOrigin( self, self->s.origin );
- self->e_ThinkFunc = thinkF_G_FreeEntity;
- //Give other ents time to link
- self->nextthink = level.time + START_TIME_REMOVE_ENTS;
- }
- /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
- Used as a positional target for in-game calculation, like jumppad targets.
- target_position does the same thing
- */
- void SP_info_notnull( gentity_t *self ){
- //FIXME: store in ref_tag system?
- G_SetOrigin( self, self->s.origin );
- }
- /*QUAKED lightJunior (0 0.7 0.3) (-8 -8 -8) (8 8 8) nonlinear angle negative_spot negative_point
- Non-displayed light that only affects dynamic game models, but does not contribute to lightmaps
- "light" overrides the default 300 intensity.
- Nonlinear checkbox gives inverse square falloff instead of linear
- Angle adds light:surface angle calculations (only valid for "Linear" lights) (wolf)
- Lights pointed at a target will be spotlights.
- "radius" overrides the default 64 unit radius of a spotlight at the target point.
- "fade" falloff/radius adjustment value. multiply the run of the slope by "fade" (1.0f default) (only valid for "Linear" lights) (wolf)
- */
- /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear noIncidence START_OFF
- Non-displayed light.
- "light" overrides the default 300 intensity. - affects size
- a negative "light" will subtract the light's color
- 'Linear' checkbox gives linear falloff instead of inverse square
- 'noIncidence' checkbox makes lighting smoother
- Lights pointed at a target will be spotlights.
- "radius" overrides the default 64 unit radius of a spotlight at the target point.
- "scale" multiplier for the light intensity - does not affect size (default 1)
- greater than 1 is brighter, between 0 and 1 is dimmer.
- "color" sets the light's color
- "targetname" to indicate a switchable light - NOTE that all lights with the same targetname will be grouped together and act as one light (ie: don't mix colors, styles or start_off flag)
- "style" to specify a specify light style, even for switchable lights!
- "style_off" light style to use when switched off (Only for switchable lights)
- 1 FLICKER (first variety)
- 2 SLOW STRONG PULSE
- 3 CANDLE (first variety)
- 4 FAST STROBE
- 5 GENTLE PULSE 1
- 6 FLICKER (second variety)
- 7 CANDLE (second variety)
- 8 CANDLE (third variety)
- 9 SLOW STROBE (fourth variety)
- 10 FLUORESCENT FLICKER
- 11 SLOW PULSE NOT FADE TO BLACK
- 12 FAST PULSE FOR JEREMY
- 13 Test Blending
- */
- static void misc_lightstyle_set ( gentity_t *ent)
- {
- const int mLightStyle = ent->count;
- const int mLightSwitchStyle = ent->bounceCount;
- const int mLightOffStyle = ent->fly_sound_debounce_time;
- if (!ent->misc_dlight_active)
- { //turn off
- if (mLightOffStyle) //i have a light style i'd like to use when off
- {
- char lightstyle[32];
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+0, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+1, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+2, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
- }else
- {
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "a");
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "a");
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "a");
- }
- }
- else
- { //Turn myself on now
- if (mLightSwitchStyle) //i have a light style i'd like to use when on
- {
- char lightstyle[32];
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+0, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+1, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
- gi.GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+2, lightstyle, 32);
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
- }
- else
- {
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "z");
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "z");
- gi.SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "z");
- }
- }
- }
- void SP_light( gentity_t *self ) {
- if (!self->targetname )
- {//if i don't have a light style switch, the i go away
- G_FreeEntity( self );
- return;
- }
- G_SpawnInt( "style", "0", &self->count );
- G_SpawnInt( "switch_style", "0", &self->bounceCount );
- G_SpawnInt( "style_off", "0", &self->fly_sound_debounce_time );
- G_SetOrigin( self, self->s.origin );
- gi.linkentity( self );
- self->e_UseFunc = useF_misc_dlight_use;
- self->e_clThinkFunc = clThinkF_NULL;
- self->s.eType = ET_GENERAL;
- self->misc_dlight_active = qfalse;
- self->svFlags |= SVF_NOCLIENT;
- if ( !(self->spawnflags & 4) )
- { //turn myself on now
- self->misc_dlight_active = qtrue;
- }
- misc_lightstyle_set (self);
- }
- void misc_dlight_use ( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- G_ActivateBehavior(ent,BSET_USE);
- ent->misc_dlight_active = !ent->misc_dlight_active; //toggle
- misc_lightstyle_set (ent);
- }
- /*
- =================================================================================
- TELEPORTERS
- =================================================================================
- */
- void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles )
- {
- if ( player->NPC && ( player->NPC->aiFlags&NPCAI_FORM_TELE_NAV ) )
- {
- //My leader teleported, I was trying to catch up, take this off
- player->NPC->aiFlags &= ~NPCAI_FORM_TELE_NAV;
-
- }
- // unlink to make sure it can't possibly interfere with G_KillBox
- gi.unlinkentity (player);
- VectorCopy ( origin, player->client->ps.origin );
- player->client->ps.origin[2] += 1;
- VectorCopy ( player->client->ps.origin, player->currentOrigin );
- // spit the player out
- AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
- VectorScale( player->client->ps.velocity, 0, player->client->ps.velocity );
- //player->client->ps.pm_time = 160; // hold time
- //player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- // toggle the teleport bit so the client knows to not lerp
- player->client->ps.eFlags ^= EF_TELEPORT_BIT;
- // set angles
- SetClientViewAngle( player, angles );
- // kill anything at the destination
- G_KillBox (player);
- // save results of pmove
- PlayerStateToEntityState( &player->client->ps, &player->s );
- gi.linkentity (player);
- }
- void TeleportMover( gentity_t *mover, vec3_t origin, vec3_t diffAngles, qboolean snapAngle )
- {//FIXME: need an effect
- vec3_t oldAngle, newAngle;
- float speed;
- // unlink to make sure it can't possibly interfere with G_KillBox
- gi.unlinkentity (mover);
- //reposition it
- VectorCopy( origin, mover->s.pos.trBase );
- VectorCopy( origin, mover->currentOrigin );
- //Maintain their previous speed, but adjusted for new direction
- if ( snapAngle )
- {//not a diffAngle, actually an absolute angle
- vec3_t dir;
- VectorCopy( diffAngles, newAngle );
- AngleVectors( newAngle, dir, NULL, NULL );
- VectorNormalize( dir );//necessary?
- speed = VectorLength( mover->s.pos.trDelta );
- VectorScale( dir, speed, mover->s.pos.trDelta );
- mover->s.pos.trTime = level.time;
- VectorSubtract( newAngle, mover->s.apos.trBase, diffAngles );
- VectorCopy( newAngle, mover->s.apos.trBase );
- }
- else
- {
- speed = VectorNormalize( mover->s.pos.trDelta );
- vectoangles( mover->s.pos.trDelta, oldAngle );
- VectorAdd( oldAngle, diffAngles, newAngle );
- AngleVectors( newAngle, mover->s.pos.trDelta, NULL, NULL );
- VectorNormalize( mover->s.pos.trDelta );
- VectorScale( mover->s.pos.trDelta, speed, mover->s.pos.trDelta );
- mover->s.pos.trTime = level.time;
- //Maintain their previous angles, but adjusted to new orientation
- VectorAdd( mover->s.apos.trBase, diffAngles, mover->s.apos.trBase );
- }
- //Maintain their previous anglespeed, but adjusted to new orientation
- speed = VectorNormalize( mover->s.apos.trDelta );
- VectorAdd( mover->s.apos.trDelta, diffAngles, mover->s.apos.trDelta );
- VectorNormalize( mover->s.apos.trDelta );
- VectorScale( mover->s.apos.trDelta, speed, mover->s.apos.trDelta );
- mover->s.apos.trTime = level.time;
-
- //Tell them it was teleported this move
- mover->s.eFlags |= EF_TELEPORT_BIT;
- // kill anything at the destination
- //G_KillBox (mover);
- //FIXME: call touch func instead of killbox?
- gi.linkentity (mover);
- }
- void teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace)
- {
- gentity_t *dest;
- if (!other->client)
- return;
- dest = G_PickTarget( self->target );
- if (!dest) {
- gi.Printf ("Couldn't find teleporter destination\n");
- return;
- }
- TeleportPlayer( other, dest->s.origin, dest->s.angles );
- }
- /*QUAK-D misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
- Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
- */
- void SP_misc_teleporter (gentity_t *ent)
- {
- gentity_t *trig;
- if (!ent->target)
- {
- gi.Printf ("teleporter without a target.\n");
- G_FreeEntity( ent );
- return;
- }
- ent->s.modelindex = G_ModelIndex( "models/objects/dmspot.md3" );
- ent->s.clientNum = 1;
- // ent->s.loopSound = G_SoundIndex("sound/world/amb10.wav");
- ent->contents = CONTENTS_SOLID;
- G_SetOrigin( ent, ent->s.origin );
- VectorSet (ent->mins, -32, -32, -24);
- VectorSet (ent->maxs, 32, 32, -16);
- gi.linkentity (ent);
- trig = G_Spawn ();
- trig->e_TouchFunc = touchF_teleporter_touch;
- trig->contents = CONTENTS_TRIGGER;
- trig->target = ent->target;
- trig->owner = ent;
- G_SetOrigin( trig, ent->s.origin );
- VectorSet (trig->mins, -8, -8, 8);
- VectorSet (trig->maxs, 8, 8, 24);
- gi.linkentity (trig);
-
- }
- /*QUAK-D misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) - - NODRAW
- Point teleporters at these.
- */
- void SP_misc_teleporter_dest( gentity_t *ent ) {
- if ( ent->spawnflags & 4 ){
- return;
- }
- G_SetOrigin( ent, ent->s.origin );
- gi.linkentity (ent);
- }
- //===========================================================
- /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16) RMG SOLID
- "model" arbitrary .md3 or .ase file to display
- "_frame" "x" which frame from an animated md3
- "modelscale" "x" uniform scale
- "modelscale_vec" "x y z" scale model in each axis
- "_remap" "from to" remap a shader in this model
- turns into BSP triangles - not solid by default (click SOLID or use _clipmodel shader)
- */
- void SP_misc_model( gentity_t *ent ) {
- G_FreeEntity( ent );
- }
- /*QUAKED misc_model_static (1 0 0) (-16 -16 0) (16 16 16)
- "model" arbitrary .md3 file to display
- "_frame" "x" which frame from an animated md3
- "modelscale" "x" uniform scale
- "modelscale_vec" "x y z" scale model in each axis
- "zoffset" units to offset vertical culling position by, can be
- negative or positive. This does not affect the actual
- position of the model, only the culling position. Use
- it for models with stupid origins that go below the
- ground and whatnot.
- loaded as a model in the renderer - does not take up precious bsp space!
- */
- extern void CG_CreateMiscEntFromGent(gentity_t *ent, const vec3_t scale, float zOff); //cg_main.cpp
- void SP_misc_model_static(gentity_t *ent)
- {
- char *value;
- float temp;
- float zOff;
- vec3_t scale;
- G_SpawnString("modelscale_vec", "1 1 1", &value);
- sscanf( value, "%f %f %f", &scale[ 0 ], &scale[ 1 ], &scale[ 2 ] );
- G_SpawnFloat( "modelscale", "0", &temp);
- if (temp != 0.0f)
- {
- scale[ 0 ] = scale[ 1 ] = scale[ 2 ] = temp;
- }
- G_SpawnFloat( "zoffset", "0", &zOff);
- if (!ent->model)
- {
- Com_Error( ERR_DROP,"misc_model_static at %s with out a MODEL!\n", vtos(ent->s.origin) );
- }
- //we can be horrible and cheat since this is SP!
- CG_CreateMiscEntFromGent(ent, scale, zOff);
- G_FreeEntity( ent );
- }
- //===========================================================
- void setCamera ( gentity_t *ent )
- {
- vec3_t dir;
- gentity_t *target = 0;
- // frame holds the rotate speed
- if ( ent->owner->spawnflags & 1 )
- {
- ent->s.frame = 25;
- }
- else if ( ent->owner->spawnflags & 2 )
- {
- ent->s.frame = 75;
- }
- // clientNum holds the rotate offset
- ent->s.clientNum = ent->owner->s.clientNum;
- VectorCopy( ent->owner->s.origin, ent->s.origin2 );
- // see if the portal_camera has a target
- if (ent->owner->target) {
- target = G_PickTarget( ent->owner->target );
- }
- if ( target )
- {
- VectorSubtract( target->s.origin, ent->owner->s.origin, dir );
- VectorNormalize( dir );
- }
- else
- {
- G_SetMovedir( ent->owner->s.angles, dir );
- }
- ent->s.eventParm = DirToByte( dir );
- }
- void cycleCamera( gentity_t *self )
- {
- self->owner = G_Find( self->owner, FOFS(targetname), self->target );
- if ( self->owner == NULL )
- {
- //Uh oh! Not targeted at any ents! Or reached end of list? Which is it?
- //for now assume reached end of list and are cycling
- self->owner = G_Find( self->owner, FOFS(targetname), self->target );
- if ( self->owner == NULL )
- {//still didn't find one
- gi.Printf( "Couldn't find target for misc_portal_surface\n" );
- G_FreeEntity( self );
- return;
- }
- }
- setCamera( self );
- if ( self->e_ThinkFunc == thinkF_cycleCamera )
- {
- if ( self->owner->wait > 0 )
- {
- self->nextthink = level.time + self->owner->wait;
- }
- else
- {
- self->nextthink = level.time + self->wait;
- }
- }
- }
- void misc_portal_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- cycleCamera( self );
- }
- void locateCamera( gentity_t *ent )
- {//FIXME: make this fadeout with distance from misc_camera_portal
- ent->owner = G_Find(NULL, FOFS(targetname), ent->target);
- if ( !ent->owner )
- {
- gi.Printf( "Couldn't find target for misc_portal_surface\n" );
- G_FreeEntity( ent );
- return;
- }
- setCamera( ent );
- if ( !ent->targetname )
- {//not targetted, so auto-cycle
- if ( G_Find(ent->owner, FOFS(targetname), ent->target) != NULL )
- {//targeted at more than one thing
- ent->e_ThinkFunc = thinkF_cycleCamera;
- if ( ent->owner->wait > 0 )
- {
- ent->nextthink = level.time + ent->owner->wait;
- }
- else
- {
- ent->nextthink = level.time + ent->wait;
- }
- }
- }
- }
- /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
- The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
- This must be within 64 world units of the surface!
- targetname - When used, cycles to the next misc_portal_camera it's targeted
- wait - makes it auto-cycle between all cameras it's pointed at at intevervals of specified number of seconds.
- cameras will be cycled through in the order they were created on the map.
- */
- void SP_misc_portal_surface(gentity_t *ent)
- {
- VectorClear( ent->mins );
- VectorClear( ent->maxs );
- gi.linkentity (ent);
- ent->svFlags = SVF_PORTAL;
- ent->s.eType = ET_PORTAL;
- ent->wait *= 1000;
- if ( !ent->target )
- {//mirror?
- VectorCopy( ent->s.origin, ent->s.origin2 );
- }
- else
- {
- ent->e_ThinkFunc = thinkF_locateCamera;
- ent->nextthink = level.time + 100;
- if ( ent->targetname )
- {
- ent->e_UseFunc = useF_misc_portal_use;
- }
- }
- }
- /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate
- The target for a misc_portal_surface. You can set either angles or target another entity (NOT an info_null) to determine the direction of view.
- "roll" an angle modifier to orient the camera around the target vector;
- */
- void SP_misc_portal_camera(gentity_t *ent) {
- float roll;
- VectorClear( ent->mins );
- VectorClear( ent->maxs );
- gi.linkentity (ent);
- G_SpawnFloat( "roll", "0", &roll );
- ent->s.clientNum = roll/360.0 * 256;
- ent->wait *= 1000;
- }
- void G_SubBSPSpawnEntitiesFromString(const char *entityString, vec3_t posOffset, vec3_t angOffset);
- /*QUAKED misc_bsp (1 0 0) (-16 -16 -16) (16 16 16)
- "bspmodel" arbitrary .bsp file to display
- */
- void SP_misc_bsp(gentity_t *ent)
- {
- char temp[MAX_QPATH];
- char *out;
- float newAngle;
- int tempint;
- G_SpawnFloat( "angle", "0", &newAngle );
- if (newAngle != 0.0)
- {
- ent->s.angles[1] = newAngle;
- }
- // don't support rotation any other way
- ent->s.angles[0] = 0.0;
- ent->s.angles[2] = 0.0;
-
- G_SpawnString("bspmodel", "", &out);
- ent->s.eFlags = EF_PERMANENT;
- // Mainly for debugging
- G_SpawnInt( "spacing", "0", &tempint);
- ent->s.time2 = tempint;
- G_SpawnInt( "flatten", "0", &tempint);
- ent->s.time = tempint;
- Com_sprintf(temp, MAX_QPATH, "#%s", out);
- gi.SetBrushModel( ent, temp ); // SV_SetBrushModel -- sets mins and maxs
- G_BSPIndex(temp);
- level.mNumBSPInstances++;
- Com_sprintf(temp, MAX_QPATH, "%d-", level.mNumBSPInstances);
- VectorCopy(ent->s.origin, level.mOriginAdjust);
- level.mRotationAdjust = ent->s.angles[1];
- level.mTargetAdjust = temp;
- level.hasBspInstances = qtrue;
- level.mBSPInstanceDepth++;
- VectorCopy( ent->s.origin, ent->s.pos.trBase );
- VectorCopy( ent->s.origin, ent->currentOrigin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- VectorCopy( ent->s.angles, ent->currentAngles );
- ent->s.eType = ET_MOVER;
- gi.linkentity (ent);
- const char *ents = gi.SetActiveSubBSP(ent->s.modelindex);
- if (ents)
- {
- G_SubBSPSpawnEntitiesFromString(ents, ent->s.origin, ent->s.angles);
- }
- gi.SetActiveSubBSP(-1);
- level.mBSPInstanceDepth--;
- }
- #define MAX_INSTANCE_TYPES 16
- void AddSpawnField(char *field, char *value);
- /*QUAKED terrain (1.0 1.0 1.0) ? NOVEHDMG
- NOVEHDMG - don't damage vehicles upon impact with this terrain
- Terrain entity
- It will stretch to the full height of the brush
- numPatches - integer number of patches to split the terrain brush into (default 200)
- terxels - integer number of terxels on a patch side (default 4) (2 <= count <= 8)
- seed - integer seed for random terrain generation (default 0)
- textureScale - float scale of texture (default 0.005)
- heightmap - name of heightmap data image to use, located in heightmaps/*.png. (must be PNG format)
- terrainDef - defines how the game textures the terrain (file is base/ext_data/rmg/*.terrain - default is grassyhills)
- instanceDef - defines which bsp instances appear
- miscentDef - defines which client models spawn on the terrain (file is base/ext_data/rmg/*.miscents)
- densityMap - how dense the client models are packed
- */
- void SP_terrain(gentity_t *ent)
- {
- #ifdef _XBOX
- assert(0);
- #else
- char temp[MAX_INFO_STRING];
- char final[MAX_QPATH];
- // char seed[MAX_QPATH];
- // char missionType[MAX_QPATH];
- // char soundSet[MAX_QPATH];
- int shaderNum, i;
- char *value;
- int terrainID;
- //k, found a terrain, just set rmg to 1.
- //This should always get set before RE_LoadWorldMap and all that is
- //called which is all that matters.
- //gi.cvar_set("RMG", "1");
- VectorClear (ent->s.angles);
- gi.SetBrushModel( ent, ent->model );
- // Get the shader from the top of the brush
- // shaderNum = gi.CM_GetShaderNum(s.modelindex);
- shaderNum = 0;
- //rww - Why not do this all the time? Not like terrain entities are used when you don't want them to be terrain.
- /* if (g_RMG->integer)
- {
- gi.Cvar_VariableStringBuffer("RMG_seed", seed, MAX_QPATH);
- gi.Cvar_VariableStringBuffer("RMG_mission", missionType, MAX_QPATH);
- // gi.Cvar_VariableStringBuffer("RMG_soundset", soundSet, MAX_QPATH);
- // gi.SetConfigstring(CS_AMBIENT_SOUNDSETS, soundSet );
- }
- */
- // Arbitrary (but sane) limits to the number of terxels
- // if((mTerxels < MIN_TERXELS) || (mTerxels > MAX_TERXELS))
- {
- // Com_printf("G_Terrain: terxels out of range - defaulting to 4\n");
- // mTerxels = 4;
- }
- // Get info required for the common init
- temp[0] = 0;
- G_SpawnString("heightmap", "", &value);
- Info_SetValueForKey(temp, "heightMap", value);
- G_SpawnString("numpatches", "400", &value);
- Info_SetValueForKey(temp, "numPatches", va("%d", atoi(value)));
- G_SpawnString("terxels", "4", &value);
- Info_SetValueForKey(temp, "terxels", va("%d", atoi(value)));
- //Info_SetValueForKey(temp, "seed", seed);
- Info_SetValueForKey(temp, "minx", va("%f", ent->mins[0]));
- Info_SetValueForKey(temp, "miny", va("%f", ent->mins[1]));
- Info_SetValueForKey(temp, "minz", va("%f", ent->mins[2]));
- Info_SetValueForKey(temp, "maxx", va("%f", ent->maxs[0]));
- Info_SetValueForKey(temp, "maxy", va("%f", ent->maxs[1]));
- Info_SetValueForKey(temp, "maxz", va("%f", ent->maxs[2]));
- Info_SetValueForKey(temp, "modelIndex", va("%d", ent->s.modelindex));
- G_SpawnString("terraindef", "grassyhills", &value);
- Info_SetValueForKey(temp, "terrainDef", value);
- G_SpawnString("instancedef", "", &value);
- Info_SetValueForKey(temp, "instanceDef", value);
- G_SpawnString("miscentdef", "", &value);
- Info_SetValueForKey(temp, "miscentDef", value);
- //Info_SetValueForKey(temp, "missionType", missionType);
-
- for(i = 0; i < MAX_INSTANCE_TYPES; i++)
- {
- gi.Cvar_VariableStringBuffer(va("RMG_instance%d", i), final, MAX_QPATH);
- if(strlen(final))
- {
- Info_SetValueForKey(temp, va("inst%d", i), final);
- }
- }
- // Set additional data required on the client only
- G_SpawnString("densitymap", "", &value);
- Info_SetValueForKey(temp, "densityMap", value);
- Info_SetValueForKey(temp, "shader", va("%d", shaderNum));
- G_SpawnString("texturescale", "0.005", &value);
- Info_SetValueForKey(temp, "texturescale", va("%f", atof(value)));
- // Initialise the common aspects of the terrain
- terrainID = gi.CM_RegisterTerrain(temp);
- // SetCommon(common);
- Info_SetValueForKey(temp, "terrainId", va("%d", terrainID));
- // Let the entity know if it is random generated or not
- // SetIsRandom(common->GetIsRandom());
- // Let the game remember everything
- // level.landScapes[terrainID] = ent;
- //rww - I'm not doing this. Because it didn't even appear to be used. Is it?
- // Send all the data down to the client
- gi.SetConfigstring(CS_TERRAINS + terrainID, temp);
- // Make sure the contents are properly set
- ent->contents = CONTENTS_TERRAIN;
- ent->svFlags = SVF_NOCLIENT;
- ent->s.eFlags = EF_PERMANENT;
- ent->s.eType = ET_TERRAIN;
- // Hook into the world so physics will work
- gi.linkentity(ent);
- // If running RMG then initialize the terrain and handle team skins
- //rww - Why not do this all the time? Not like terrain entities are used when you don't want them to be terrain.
- /* not using RMG
- if ( g_RMG->integer )
- {
- gi.RMG_Init(terrainID);
- }
- */
- #endif // _XBOX
- }
- //rww - Called by skyportal entities. This will check through entities and flag them
- //as portal ents if they are in the same pvs as a skyportal entity and pass
- //a direct point trace check between origins. I really wanted to use an eFlag for
- //flagging portal entities, but too many entities like to reset their eFlags.
- //Note that this was not part of the original wolf sky portal stuff.
- void G_PortalifyEntities(gentity_t *ent)
- {
- int i = 0;
- gentity_t *scan = NULL;
- while (i < MAX_GENTITIES)
- {
- scan = &g_entities[i];
- if (scan && scan->inuse && scan->s.number != ent->s.number && gi.inPVS(ent->s.origin, scan->currentOrigin))
- {
- trace_t tr;
- gi.trace(&tr, ent->s.origin, vec3_origin, vec3_origin, scan->currentOrigin, ent->s.number, CONTENTS_SOLID, G2_NOCOLLIDE, 0);
- if (tr.fraction == 1.0 || (tr.entityNum == scan->s.number && tr.entityNum != ENTITYNUM_NONE && tr.entityNum != ENTITYNUM_WORLD))
- {
- scan->s.isPortalEnt = qtrue; //he's flagged now
- }
- }
- i++;
- }
- ent->e_ThinkFunc = thinkF_G_FreeEntity; //the portal entity is no longer needed because its information is stored in a config string.
- ent->nextthink = level.time;
- }
- /*QUAKED misc_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
- To have the portal sky fogged, enter any of the following values:
- "fogcolor" (r g b) (values 0.0-1.0)
- "fognear" distance from entity to start fogging
- "fogfar" distance from entity that fog is opaque
- rww - NOTE: fog doesn't work with these currently (at least not in this way).
- Use a fog brush instead.
- */
- void SP_misc_skyportal (gentity_t *ent)
- {
- vec3_t fogv; //----(SA)
- int fogn; //----(SA)
- int fogf; //----(SA)
- int isfog = 0; // (SA)
- isfog += G_SpawnVector ("fogcolor", "0 0 0", fogv);
- isfog += G_SpawnInt ("fognear", "0", &fogn);
- isfog += G_SpawnInt ("fogfar", "300", &fogf);
- gi.SetConfigstring( CS_SKYBOXORG, va("%.2f %.2f %.2f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
- ent->e_ThinkFunc = thinkF_G_PortalifyEntities;
- ent->nextthink = level.time + 1050; //give it some time first so that all other entities are spawned.
- }
- extern qboolean G_ClearViewEntity( gentity_t *ent );
- extern void G_SetViewEntity( gentity_t *self, gentity_t *viewEntity );
- extern void SP_fx_runner( gentity_t *ent );
- void camera_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
- {
- if ( player && player->client && player->client->ps.viewEntity == self->s.number )
- {
- G_UseTargets2( self, player, self->target4 );
- G_ClearViewEntity( player );
- G_Sound( player, self->soundPos2 );
- }
- G_UseTargets2( self, player, self->closetarget );
- //FIXME: explosion fx/sound
- //leave sparks at origin- where base's pole is still at?
- gentity_t *sparks = G_Spawn();
- if ( sparks )
- {
- sparks->fxFile = "sparks/spark";
- sparks->delay = 100;
- sparks->random = 500;
- sparks->s.angles[0] = 180;//point down
- VectorCopy( self->s.origin, sparks->s.origin );
- SP_fx_runner( sparks );
- }
- //bye!
- self->takedamage = qfalse;
- self->contents = 0;
- self->s.eFlags |= EF_NODRAW;
- self->s.modelindex = 0;
- }
- void camera_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if ( !activator || !activator->client || activator->s.number )
- {//really only usable by the player
- return;
- }
- self->painDebounceTime = level.time + (self->wait*1000);//FRAMETIME*5;//don't check for player buttons for 500 ms
- // FIXME: I guess we are allowing them to switch to a dead camera. Maybe we should conditionally do this though?
- if ( /*self->health <= 0 ||*/ (player && player->client && player->client->ps.viewEntity == self->s.number) )
- {//I'm already viewEntity, or I'm destroyed, find next
- gentity_t *next = NULL;
- if ( self->target2 != NULL )
- {
- next = G_Find( NULL, FOFS(targetname), self->target2 );
- }
- if ( next )
- {//found another one
- if ( !Q_stricmp( "misc_camera", next->classname ) )
- {//make sure it's another camera
- camera_use( next, other, activator );
- }
- }
- else //if ( self->health > 0 )
- {//I was the last (only?) one, clear out the viewentity
- G_UseTargets2( self, activator, self->target4 );
- G_ClearViewEntity( activator );
- G_Sound( activator, self->soundPos2 );
- }
- }
- else
- {//set me as view entity
- G_UseTargets2( self, activator, self->target3 );
- self->s.eFlags |= EF_NODRAW;
- self->s.modelindex = 0;
- G_SetViewEntity( activator, self );
- G_Sound( activator, self->soundPos1 );
- }
- }
- void camera_aim( gentity_t *self )
- {
- self->nextthink = level.time + FRAMETIME;
- if ( player && player->client && player->client->ps.viewEntity == self->s.number )
- {//I am the viewEntity
- if ( player->client->usercmd.forwardmove || player->client->usercmd.rightmove || player->client->usercmd.upmove )
- {//player wants to back out of camera
- G_UseTargets2( self, player, self->target4 );
- G_ClearViewEntity( player );
- G_Sound( player, self->soundPos2 );
- self->painDebounceTime = level.time + (self->wait*1000);//FRAMETIME*5;//don't check for player buttons for 500 ms
- if ( player->client->usercmd.upmove > 0 )
- {//stop player from doing anything for a half second after
- player->aimDebounceTime = level.time + 500;
- }
- }
- else if ( self->painDebounceTime < level.time )
- {//check for use button
- if ( (player->client->usercmd.buttons&BUTTON_USE) )
- {//player pressed use button, wants to cycle to next
- camera_use( self, player, player );
- }
- }
- else
- {//don't draw me when being looked through
- self->s.eFlags |= EF_NODRAW;
- self->s.modelindex = 0;
- }
- }
- else if ( self->health > 0 )
- {//still alive, can draw me again
- self->s.eFlags &= ~EF_NODRAW;
- self->s.modelindex = self->s.modelindex3;
- }
- //update my aim
- if ( self->target )
- {
- gentity_t *targ = G_Find( NULL, FOFS(targetname), self->target );
- if ( targ )
- {
- vec3_t angles, dir;
- VectorSubtract( targ->currentOrigin, self->currentOrigin, dir );
- vectoangles( dir, angles );
- //FIXME: if a G2 model, do a bone override..???
- VectorCopy( self->currentAngles, self->s.apos.trBase );
- for( int i = 0; i < 3; i++ )
- {
- angles[i] = AngleNormalize180( angles[i] );
- self->s.apos.trDelta[i] = AngleNormalize180( (angles[i]-self->currentAngles[i])*10 );
- }
- //VectorSubtract( angles, self->currentAngles, self->s.apos.trDelta );
- //VectorScale( self->s.apos.trDelta, 10, self->s.apos.trDelta );
- self->s.apos.trTime = level.time;
- self->s.apos.trDuration = FRAMETIME;
- VectorCopy( angles, self->currentAngles );
- if ( DistanceSquared( self->currentAngles, self->lastAngles ) > 0.01f ) // if it moved at all, start a loop sound? not exactly the "bestest" solution
- {
- self->s.loopSound = G_SoundIndex( "sound/movers/objects/cameramove_lp2" );
- }
- else
- {
- self->s.loopSound = 0; // not moving so don't bother
- }
- VectorCopy( self->currentAngles, self->lastAngles );
- //G_SetAngles( self, angles );
- }
- }
- }
- /*QUAKED misc_camera (0 0 1) (-8 -8 -12) (8 8 16) VULNERABLE
- A model in the world that can be used by the player to look through it's viewpoint
- There will be a video overlay instead of the regular HUD and the FOV will be wider
- VULNERABLE - allow camera to be destroyed
- "target" - camera will remain pointed at this entity (if it's a train or some other moving object, it will keep following it)
- "target2" - when player is in this camera and hits the use button, it will cycle to this next camera (if no target2, returns to normal view )
- "target3" - thing to use when player enters this camera view
- "target4" - thing to use when player leaves this camera view
- "closetarget" - (sigh...) yet another target, fired this when it's destroyed
- "wait" - how long to wait between being used (default 0.5)
- */
- void SP_misc_camera( gentity_t *self )
- {
- G_SpawnFloat( "wait", "0.5", &self->wait );
- //FIXME: spawn base, too
- gentity_t *base = G_Spawn();
- if ( base )
- {
- base->s.modelindex = G_ModelIndex( "models/map_objects/kejim/impcam_base.md3" );
- VectorCopy( self->s.origin, base->s.origin );
- base->s.origin[2] += 16;
- G_SetOrigin( base, base->s.origin );
- G_SetAngles( base, self->s.angles );
- gi.linkentity( base );
- }
- self->s.modelindex3 = self->s.modelindex = G_ModelIndex( "models/map_objects/kejim/impcam.md3" );
- self->soundPos1 = G_SoundIndex( "sound/movers/camera_on.mp3" );
- self->soundPos2 = G_SoundIndex( "sound/movers/camera_off.mp3" );
- G_SoundIndex( "sound/movers/objects/cameramove_lp2" );
- G_SetOrigin( self, self->s.origin );
- G_SetAngles( self, self->s.angles );
- self->s.apos.trType = TR_LINEAR_STOP;//TR_INTERPOLATE;//
- self->alt_fire = qtrue;
- VectorSet( self->mins, -8, -8, -12 );
- VectorSet( self->maxs, 8, 8, 0 );
- self->contents = CONTENTS_SOLID;
- gi.linkentity( self );
- self->fxID = G_EffectIndex( "sparks/spark" );
- if ( self->spawnflags & 1 ) // VULNERABLE
- {
- self->takedamage = qtrue;
- }
- self->health = 10;
- self->e_DieFunc = dieF_camera_die;
-
- self->e_UseFunc = useF_camera_use;
- self->e_ThinkFunc = thinkF_camera_aim;
- self->nextthink = level.time + START_TIME_LINK_ENTS;
- }
- /*
- ======================================================================
- SHOOTERS
- ======================================================================
- */
- void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- /* vec3_t dir;
- float deg;
- vec3_t up, right;
- */
- G_ActivateBehavior(ent,BSET_USE);
- /*
- // see if we have a target
- if ( ent->enemy ) {
- VectorSubtract( ent->enemy->currentOrigin, ent->s.origin, dir );
- VectorNormalize( dir );
- } else {
- VectorCopy( ent->movedir, dir );
- }
- // randomize a bit
- PerpendicularVector( up, dir );
- CrossProduct( up, dir, right );
- deg = crandom() * ent->random;
- VectorMA( dir, deg, up, dir );
- deg = crandom() * ent->random;
- VectorMA( dir, deg, right, dir );
- VectorNormalize( dir );
- switch ( ent->s.weapon )
- {
- case WP_GRENADE_LAUNCHER:
- fire_grenade( ent, ent->s.origin, dir );
- break;
- case WP_ROCKET_LAUNCHER:
- fire_rocket( ent, ent->s.origin, dir );
- break;
- case WP_PLASMAGUN:
- fire_plasma( ent, ent->s.origin, dir );
- break;
- }
- G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
- */
- }
- void InitShooter( gentity_t *ent, int weapon ) {
- ent->e_UseFunc = useF_Use_Shooter;
- ent->s.weapon = weapon;
- RegisterItem( FindItemForWeapon( (weapon_t) weapon ) );
- G_SetMovedir( ent->s.angles, ent->movedir );
- if ( !ent->random ) {
- ent->random = 1.0;
- }
- ent->random = sin( M_PI * ent->random / 180 );
- // target might be a moving object, so we can't set movedir for it
- if ( ent->target ) {
- G_SetEnemy(ent, G_PickTarget( ent->target ));
- }
- gi.linkentity( ent );
- }
- /*QUAK-ED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16)
- Fires at either the target or the current direction.
- "random" the number of degrees of deviance from the taget. (1.0 default)
- */
- void SP_shooter_rocket( gentity_t *ent )
- {
- // InitShooter( ent, WP_TETRION_DISRUPTOR );
- }
- /*QUAK-ED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16)
- Fires at either the target or the current direction.
- "random" is the number of degrees of deviance from the taget. (1.0 default)
- */
- void SP_shooter_plasma( gentity_t *ent )
- {
- InitShooter( ent, WP_BRYAR_PISTOL);
- }
- /*QUAK-ED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
- Fires at either the target or the current direction.
- "random" is the number of degrees of deviance from the taget. (1.0 default)
- */
- void SP_shooter_grenade( gentity_t *ent )
- {
- // InitShooter( ent, WP_GRENADE_LAUNCHER);
- }
- /*QUAKED object_cargo_barrel1 (1 0 0) (-16 -16 -16) (16 16 29) SMALLER KLINGON NO_SMOKE POWDERKEG
- Cargo Barrel
- if given a targetname, using it makes it explode
- SMALLER - (-8, -8, -16) (8, 8, 8)
- KLINGON - klingon style barrel
- NO_SMOKE - will not leave lingering smoke cloud when killed
- POWDERKEG - wooden explosive barrel
- health default = 20
- splashDamage default = 100
- splashRadius default = 200
- */
- void SP_object_cargo_barrel1(gentity_t *ent)
- {
- if(ent->spawnflags & 8)
- {
- ent->s.modelindex = G_ModelIndex( "/models/mapobjects/cargo/barrel_wood2.md3" );
- // ent->sounds = G_SoundIndex("sound/weapons/explosions/explode3.wav");
- }
- else if(ent->spawnflags & 2)
- {
- ent->s.modelindex = G_ModelIndex( "/models/mapobjects/scavenger/k_barrel.md3" );
- // ent->sounds = G_SoundIndex("sound/weapons/explosions/explode4.wav");
- }
- else
- {
- ent->s.modelindex = G_ModelIndex( va("/models/mapobjects/cargo/barrel%i.md3", Q_irand( 0, 2 )) );
- // ent->sounds = G_SoundIndex("sound/weapons/explosions/explode1.wav");
- }
- ent->contents = CONTENTS_SOLID|CONTENTS_OPAQUE;
-
- if ( ent->spawnflags & 1 )
- {
- VectorSet (ent->mins, -8, -8, -16);
- VectorSet (ent->maxs, 8, 8, 8);
- }
- else
- {
- VectorSet (ent->mins, -16, -16, -16);
- VectorSet (ent->maxs, 16, 16, 29);
- }
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- if(!ent->health)
- ent->health = 20;
-
- if(!ent->splashDamage)
- ent->splashDamage = 100;
- if(!ent->splashRadius)
- ent->splashRadius = 200;
- ent->takedamage = qtrue;
- ent->e_DieFunc = dieF_ExplodeDeath_Wait;
-
- if(ent->targetname)
- ent->e_UseFunc = useF_GoExplodeDeath;
- gi.linkentity (ent);
- }
- /*QUAKED misc_dlight (0.2 0.8 0.2) (-4 -4 -4) (4 4 4) STARTOFF FADEON FADEOFF PULSE
- Dynamic light, toggles on and off when used
-
- STARTOFF - Starts off
- FADEON - Fades from 0 Radius to start Radius
- FADEOFF - Fades from current Radius to 0 Radius before turning off
- PULSE - This flag must be checked if you want it to fade/switch between start and final RGBA, otherwise it will just sit at startRGBA
- ownername - Will display the light at the origin of the entity with this targetname
- startRGBA - Red Green Blue Radius to start with - This MUST be set or your light won't do anything
- These next values are used only if you want to fade/switch between 2 values (PULSE flag on)
- finalRGBA - Red Green Blue Radius to end with
- speed - how long to take to fade from start to final and final to start. Also how long to fade on and off if appropriate flags are checked (seconds)
- finaltime - how long to hold at final (seconds)
- starttime - how long to hold at start (seconds)
- TODO: Add random to speed/radius?
- */
- void SP_misc_dlight(gentity_t *ent)
- {
- G_SetOrigin( ent, ent->s.origin );
- gi.linkentity( ent );
- ent->speed *= 1000;
- ent->wait *= 1000;
- ent->radius *= 1000;
- //FIXME: attach self to a train or something?
- ent->e_UseFunc = useF_misc_dlight_use;
-
- ent->misc_dlight_active = qfalse;
- ent->e_clThinkFunc = clThinkF_NULL;
- ent->s.eType = ET_GENERAL;
- //Delay first think so we can find owner
- if ( ent->ownername )
- {
- ent->e_ThinkFunc = thinkF_misc_dlight_think;
- ent->nextthink = level.time + START_TIME_LINK_ENTS;
- }
- if ( !(ent->spawnflags & 1) )
- {//Turn myself on now
- GEntity_UseFunc( ent, ent, ent );
- }
- }
- void misc_dlight_use_old ( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- G_ActivateBehavior(ent,BSET_USE);
- if ( ent->misc_dlight_active )
- {//We're on, turn off
- if ( ent->spawnflags & 4 )
- {//fade off
- ent->pushDebounceTime = 3;
- }
- else
- {
- ent->misc_dlight_active = qfalse;
- ent->e_clThinkFunc = clThinkF_NULL;
- ent->s.eType = ET_GENERAL;
- ent->svFlags &= ~SVF_BROADCAST;
- }
- }
- else
- {
- //Start at start regardless of when we were turned off
- if ( ent->spawnflags & 4 )
- {//fade on
- ent->pushDebounceTime = 2;
- }
- else
- {//Just start on
- ent->pushDebounceTime = 0;
- }
- ent->painDebounceTime = level.time;
- ent->misc_dlight_active = qtrue;
- ent->e_ThinkFunc = thinkF_misc_dlight_think;
- ent->nextthink = level.time + FRAMETIME;
- ent->e_clThinkFunc = clThinkF_CG_DLightThink;
- ent->s.eType = ET_THINKER;
- ent->svFlags |= SVF_BROADCAST;// Broadcast to all clients
- }
- }
- void misc_dlight_think ( gentity_t *ent )
- {
- //Stay Attached to owner
- if ( ent->owner )
- {
- G_SetOrigin( ent, ent->owner->currentOrigin );
- gi.linkentity( ent );
- }
- else if ( ent->ownername )
- {
- ent->owner = G_Find( NULL, FOFS(targetname), ent->ownername );
- ent->ownername = NULL;
- }
- ent->nextthink = level.time + FRAMETIME;
- }
- void station_pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
- {
- // self->s.modelindex = G_ModelIndex("/models/mapobjects/stasis/plugin2_in.md3");
- // self->s.eFlags &= ~ EF_ANIM_ALLFAST;
- // self->s.eFlags |= EF_ANIM_ONCE;
- // gi.linkentity (self);
- self->s.modelindex = self->s.modelindex2;
- gi.linkentity (self);
- }
- // --------------------------------------------------------------------
- //
- // HEALTH/ARMOR plugin functions
- //
- // --------------------------------------------------------------------
- void health_use( gentity_t *self, gentity_t *other, gentity_t *activator);
- int ITM_AddArmor (gentity_t *ent, int count);
- int ITM_AddHealth (gentity_t *ent, int count);
- void health_shutdown( gentity_t *self )
- {
- if (!(self->s.eFlags & EF_ANIM_ONCE))
- {
- self->s.eFlags &= ~ EF_ANIM_ALLFAST;
- self->s.eFlags |= EF_ANIM_ONCE;
- // Switch to and animate its used up model.
- if (!Q_stricmp(self->model,"models/mapobjects/stasis/plugin2.md3"))
- {
- self->s.modelindex = self->s.modelindex2;
- }
- else if (!Q_stricmp(self->model,"models/mapobjects/borg/plugin2.md3"))
- {
- self->s.modelindex = self->s.modelindex2;
- }
- else if (!Q_stricmp(self->model,"models/mapobjects/stasis/plugin2_floor.md3"))
- {
- self->s.modelindex = self->s.modelindex2;
- // G_Sound(self, G_SoundIndex("sound/ambience/stasis/shrinkage1.wav") );
- }
- else if (!Q_stricmp(self->model,"models/mapobjects/forge/panels.md3"))
- {
- self->s.modelindex = self->s.modelindex2;
- }
- gi.linkentity (self);
- }
- }
- void health_think( gentity_t *ent )
- {
- int dif;
- // He's dead, Jim. Don't give him health
- if (ent->enemy->health<1)
- {
- ent->count = 0;
- ent->e_ThinkFunc = thinkF_NULL;
- }
- // Still has power to give
- if (ent->count > 0)
- {
- // For every 3 points of health, you get 1 point of armor
- // BUT!!! after health is filled up, you get the full energy going to armor
- dif = ent->enemy->client->ps.stats[STAT_MAX_HEALTH] - ent->enemy->health;
- if (dif > 3 )
- {
- dif= 3;
- }
- else if (dif < 0)
- {
- dif= 0;
- }
- if (dif > ent->count) // Can't give more than count
- {
- dif = ent->count;
- }
- if ((ITM_AddHealth (ent->enemy,dif)) && (dif>0))
- {
- ITM_AddArmor (ent->enemy,1); // 1 armor for every 3 health
- ent->count-=dif;
- ent->nextthink = level.time + 10;
- }
- else // User has taken all health he can hold, see about giving it all to armor
- {
- dif = ent->enemy->client->ps.stats[STAT_MAX_HEALTH] -
- ent->enemy->client->ps.stats[STAT_ARMOR];
- if (dif > 3)
- {
- dif = 3;
- }
- else if (dif < 0)
- {
- dif= 0;
- }
- if (ent->count < dif) // Can't give more than count
- {
- dif = ent->count;
- }
- if ((!ITM_AddArmor(ent->enemy,dif)) || (dif<=0))
- {
- ent->e_UseFunc = useF_health_use;
- ent->e_ThinkFunc = thinkF_NULL;
- }
- else
- {
- ent->count-=dif;
- ent->nextthink = level.time + 10;
- }
- }
- }
- if (ent->count < 1)
- {
- health_shutdown(ent);
- }
- }
- void misc_model_useup( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- G_ActivateBehavior(self,BSET_USE);
- self->s.eFlags &= ~ EF_ANIM_ALLFAST;
- self->s.eFlags |= EF_ANIM_ONCE;
- // Switch to and animate its used up model.
- self->s.modelindex = self->s.modelindex2;
- gi.linkentity (self);
- // Use target when used
- if (self->spawnflags & 8)
- {
- G_UseTargets( self, activator );
- }
- self->e_UseFunc = useF_NULL;
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = -1;
- }
- void health_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {//FIXME: Heal entire team? Or only those that are undying...?
- int dif;
- int dif2;
- int hold;
- G_ActivateBehavior(self,BSET_USE);
- if (self->e_ThinkFunc != thinkF_NULL)
- {
- self->e_ThinkFunc = thinkF_NULL;
- }
- else
- {
- if (other->client)
- {
- // He's dead, Jim. Don't give him health
- if (other->client->ps.stats[STAT_HEALTH]<1)
- {
- dif = 1;
- self->count = 0;
- }
- else
- { // Health
- dif = other->client->ps.stats[STAT_MAX_HEALTH] - other->client->ps.stats[STAT_HEALTH];
- // Armor
- dif2 = other->client->ps.stats[STAT_MAX_HEALTH] - other->client->ps.stats[STAT_ARMOR];
- hold = (dif2 - dif);
- // For every 3 points of health, you get 1 point of armor
- // BUT!!! after health is filled up, you get the full energy going to armor
- if (hold>0) // Need more armor than health
- {
- // Calculate total amount of station energy needed.
-
- hold = dif / 3; // For every 3 points of health, you get 1 point of armor
- dif2 -= hold;
- dif2 += dif;
-
- dif = dif2;
- }
- }
- }
- else
- { // Being triggered to be used up
- dif = 1;
- self->count = 0;
- }
-
- // Does player already have full health and full armor?
- if (dif > 0)
- {
- // G_Sound(self, G_SoundIndex("sound/player/suithealth.wav") );
- if ((dif >= self->count) || (self->count<1)) // use it all up?
- {
- health_shutdown(self);
- }
- // Use target when used
- if (self->spawnflags & 8)
- {
- G_UseTargets( self, activator );
- }
- self->e_UseFunc = useF_NULL;
- self->enemy = other;
- self->e_ThinkFunc = thinkF_health_think;
- self->nextthink = level.time + 50;
- }
- else
- {
- // G_Sound(self, G_SoundIndex("sound/weapons/noammo.wav") );
- }
- }
- }
- // --------------------------------------------------------------------
- //
- // AMMO plugin functions
- //
- // --------------------------------------------------------------------
- void ammo_use( gentity_t *self, gentity_t *other, gentity_t *activator);
- int Add_Ammo2 (gentity_t *ent, int ammoType, int count);
- void ammo_shutdown( gentity_t *self )
- {
- if (!(self->s.eFlags & EF_ANIM_ONCE))
- {
- self->s.eFlags &= ~ EF_ANIM_ALLFAST;
- self->s.eFlags |= EF_ANIM_ONCE;
- gi.linkentity (self);
- }
- }
- void ammo_think( gentity_t *ent )
- {
- int dif;
- // Still has ammo to give
- if (ent->count > 0 && ent->enemy )
- {
- dif = ammoData[AMMO_BLASTER].max - ent->enemy->client->ps.ammo[AMMO_BLASTER];
- if (dif > 2 )
- {
- dif= 2;
- }
- else if (dif < 0)
- {
- dif= 0;
- }
- if (ent->count < dif) // Can't give more than count
- {
- dif = ent->count;
- }
- // Give player ammo
- if (Add_Ammo2(ent->enemy,AMMO_BLASTER,dif) && (dif!=0))
- {
- ent->count-=dif;
- ent->nextthink = level.time + 10;
- }
- else // User has taken all ammo he can hold
- {
- ent->e_UseFunc = useF_ammo_use;
- ent->e_ThinkFunc = thinkF_NULL;
- }
- }
- if (ent->count < 1)
- {
- ammo_shutdown(ent);
- }
- }
- //------------------------------------------------------------
- void ammo_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int dif;
- G_ActivateBehavior(self,BSET_USE);
- if (self->e_ThinkFunc != thinkF_NULL)
- {
- if (self->e_UseFunc != useF_NULL)
- {
- self->e_ThinkFunc = thinkF_NULL;
- }
- }
- else
- {
- if (other->client)
- {
- dif = ammoData[AMMO_BLASTER].max - other->client->ps.ammo[AMMO_BLASTER];
- }
- else
- { // Being triggered to be used up
- dif = 1;
- self->count = 0;
- }
- // Does player already have full ammo?
- if (dif > 0)
- {
- // G_Sound(self, G_SoundIndex("sound/player/suitenergy.wav") );
- if ((dif >= self->count) || (self->count<1)) // use it all up?
- {
- ammo_shutdown(self);
- }
- }
- else
- {
- // G_Sound(self, G_SoundIndex("sound/weapons/noammo.wav") );
- }
- // Use target when used
- if (self->spawnflags & 8)
- {
- G_UseTargets( self, activator );
- }
- self->e_UseFunc = useF_NULL;
- G_SetEnemy( self, other );
- self->e_ThinkFunc = thinkF_ammo_think;
- self->nextthink = level.time + 50;
- }
- }
- //------------------------------------------------------------
- void mega_ammo_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- G_ActivateBehavior( self, BSET_USE );
- // Use target when used
- G_UseTargets( self, activator );
- // first use, adjust the max ammo a person can hold for each type of ammo
- ammoData[AMMO_BLASTER].max = 999;
- ammoData[AMMO_POWERCELL].max = 999;
- // Set up our count with whatever the max difference will be
- if ( other->client->ps.ammo[AMMO_POWERCELL] > other->client->ps.ammo[AMMO_BLASTER] )
- self->count = ammoData[AMMO_BLASTER].max - other->client->ps.ammo[AMMO_BLASTER];
- else
- self->count = ammoData[AMMO_POWERCELL].max - other->client->ps.ammo[AMMO_POWERCELL];
- // G_Sound( self, G_SoundIndex("sound/player/superenergy.wav") );
- // Clear our usefunc, then think until our ammo is full
- self->e_UseFunc = useF_NULL;
- G_SetEnemy( self, other );
- self->e_ThinkFunc = thinkF_mega_ammo_think;
- self->nextthink = level.time + 50;
- self->s.frame = 0;
- self->s.eFlags |= EF_ANIM_ONCE;
- }
- //------------------------------------------------------------
- void mega_ammo_think( gentity_t *self )
- {
- int ammo_add = 5;
- // If the middle model is done animating, and we haven't switched to the last model yet...
- // chuck up the last model.
- if (!Q_stricmp(self->model,"models/mapobjects/forge/power_up_boss.md3")) // Because the normal forge_ammo model can use this too
- {
- if ( self->s.frame > 16 && self->s.modelindex != self->s.modelindex2 )
- self->s.modelindex = self->s.modelindex2;
- }
-
- if ( self->enemy && self->count > 0 )
- {
- // Add an equal ammount of ammo to each type
- self->enemy->client->ps.ammo[AMMO_BLASTER] += ammo_add;
- self->enemy->client->ps.ammo[AMMO_POWERCELL] += ammo_add;
- // Now cap to prevent overflows
- if ( self->enemy->client->ps.ammo[AMMO_BLASTER] > ammoData[AMMO_BLASTER].max )
- self->enemy->client->ps.ammo[AMMO_BLASTER] = ammoData[AMMO_BLASTER].max;
- if ( self->enemy->client->ps.ammo[AMMO_POWERCELL] > ammoData[AMMO_POWERCELL].max )
- self->enemy->client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
- // Decrement the count given counter
- self->count -= ammo_add;
- // If we've given all we should, prevent giving any more, even if they player is no longer full
- if ( self->count <= 0 )
- {
- self->count = 0;
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = -1;
- }
- else
- self->nextthink = 20;
- }
- }
- //------------------------------------------------------------
- void switch_models( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- // FIXME: need a sound here!!
- if ( self->s.modelindex2 )
- self->s.modelindex = self->s.modelindex2;
- }
- //------------------------------------------------------------
- void touch_ammo_crystal_tigger( gentity_t *self, gentity_t *other, trace_t *trace )
- {
- if ( !other->client )
- return;
- // dead people can't pick things up
- if ( other->health < 1 )
- return;
- // Only player can pick it up
- if ( !other->s.number == 0 )
- {
- return;
- }
- if ( other->client->ps.ammo[ AMMO_POWERCELL ] >= ammoData[AMMO_POWERCELL].max )
- {
- return; // can't hold any more
- }
- // Add the ammo
- other->client->ps.ammo[AMMO_POWERCELL] += self->owner->count;
- if ( other->client->ps.ammo[AMMO_POWERCELL] > ammoData[AMMO_POWERCELL].max )
- {
- other->client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
- }
- // Trigger once only
- self->e_TouchFunc = touchF_NULL;
- // swap the model to the version without the crystal and ditch the infostring
- self->owner->s.modelindex = self->owner->s.modelindex2;
- // play the normal pickup sound
- // G_AddEvent( other, EV_ITEM_PICKUP, ITM_AMMO_CRYSTAL_BORG );
- // fire item targets
- G_UseTargets( self->owner, other );
- }
- //------------------------------------------------------------
- void spawn_ammo_crystal_trigger( gentity_t *ent )
- {
- gentity_t *other;
- vec3_t mins, maxs;
- // Set the base bounds
- VectorCopy( ent->s.origin, mins );
- VectorCopy( ent->s.origin, maxs );
- // Now add an area of influence around the thing
- for ( int i = 0; i < 3; i++ )
- {
- maxs[i] += 48;
- mins[i] -= 48;
- }
- // create a trigger with this size
- other = G_Spawn( );
- VectorCopy( mins, other->mins );
- VectorCopy( maxs, other->maxs );
- // set up the other bits that the engine needs to know
- other->owner = ent;
- other->contents = CONTENTS_TRIGGER;
- other->e_TouchFunc = touchF_touch_ammo_crystal_tigger;
- gi.linkentity( other );
- }
- void misc_replicator_item_remove ( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- self->s.eFlags |= EF_NODRAW;
- //self->contents = 0;
- self->s.modelindex = 0;
- self->e_UseFunc = useF_misc_replicator_item_spawn;
- //FIXME: pickup sound?
- if ( activator->client )
- {
- activator->health += 5;
- if ( activator->health > activator->client->ps.stats[STAT_MAX_HEALTH] ) // Past max health
- {
- activator->health = activator->client->ps.stats[STAT_MAX_HEALTH];
- }
- }
- }
- void misc_replicator_item_finish_spawn( gentity_t *self )
- {
- //self->contents = CONTENTS_ITEM;
- //FIXME: blinks out for a couple frames when transporter effect is done?
- self->e_UseFunc = useF_misc_replicator_item_remove;
- }
- void misc_replicator_item_spawn ( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- switch ( Q_irand( 1, self->count ) )
- {
- case 1:
- self->s.modelindex = self->bounceCount;
- break;
- case 2:
- self->s.modelindex = self->fly_sound_debounce_time;
- break;
- case 3:
- self->s.modelindex = self->painDebounceTime;
- break;
- case 4:
- self->s.modelindex = self->disconnectDebounceTime;
- break;
- case 5:
- self->s.modelindex = self->attackDebounceTime;
- break;
- case 6://max
- self->s.modelindex = self->pushDebounceTime;
- break;
- }
- self->s.eFlags &= ~EF_NODRAW;
- self->e_ThinkFunc = thinkF_misc_replicator_item_finish_spawn;
- self->nextthink = level.time + 4000;//shorter?
- self->e_UseFunc = useF_NULL;
- gentity_t *tent = G_TempEntity( self->currentOrigin, EV_REPLICATOR );
- tent->owner = self;
- }
- /*QUAK-ED misc_replicator_item (0.2 0.8 0.2) (-4 -4 0) (4 4 8)
- When used. this will "spawn" an entity with a random model from the ones provided below...
- Using it again removes the item as if it were picked up.
- model - first random model key
- model2 - second random model key
- model3 - third random model key
- model4 - fourth random model key
- model5 - fifth random model key
- model6 - sixth random model key
- NOTE: do not skip one of these model names, start with the lowest and fill in each next highest one with a value. A gap will cause the item to not work correctly.
- NOTE: if you use an invalid model, it will still try to use it and show the NULL axis model (or nothing at all)
- targetname - how you refer to it for using it
- */
- void SP_misc_replicator_item ( gentity_t *self )
- {
- if ( self->model )
- {
- self->bounceCount = G_ModelIndex( self->model );
- self->count++;
- if ( self->model2 )
- {
- self->fly_sound_debounce_time = G_ModelIndex( self->model2 );
- self->count++;
- if ( self->target )
- {
- self->painDebounceTime = G_ModelIndex( self->target );
- self->count++;
- if ( self->target2 )
- {
- self->disconnectDebounceTime = G_ModelIndex( self->target2 );
- self->count++;
- if ( self->target3 )
- {
- self->attackDebounceTime = G_ModelIndex( self->target3 );
- self->count++;
- if ( self->target4 )
- {
- self->pushDebounceTime = G_ModelIndex( self->target4 );
- self->count++;
- }
- }
- }
- }
- }
- }
- // G_SoundIndex( "sound/movers/switches/replicator.wav" );
- self->e_UseFunc = useF_misc_replicator_item_spawn;
- self->s.eFlags |= EF_NODRAW;
- //self->contents = 0;
- VectorSet( self->mins, -4, -4, 0 );
- VectorSet( self->maxs, 4, 4, 8 );
- G_SetOrigin( self, self->s.origin );
- G_SetAngles( self, self->s.angles );
- gi.linkentity( self );
- }
- /*QUAKED misc_trip_mine (0.2 0.8 0.2) (-4 -4 -4) (4 4 4) START_ON BROADCAST START_OFF
- Place in a map and point the angles at whatever surface you want it to attach to.
- START_ON - If you give it a targetname to make it toggle-able, but want it to start on, set this flag
- BROADCAST - ONLY USE THIS IF YOU HAVE TO! causes the trip wire and loop sound to always happen, use this if the beam drops out in certain situations
- START_OFF - If you give it a targetname, it will start completely off (laser AND base unit) until used.
- The trip mine will attach to that surface and fire it's beam away from the surface at an angle perpendicular to it.
- targetname - starts off, when used, turns on (toggles)
- FIXME: sometimes we want these to not be shootable... maybe just put them behind a force field?
- */
- extern void touchLaserTrap( gentity_t *ent, gentity_t *other, trace_t *trace );
- extern void CreateLaserTrap( gentity_t *laserTrap, vec3_t start, gentity_t *owner );
- void misc_trip_mine_activate( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if ( self->e_ThinkFunc == thinkF_laserTrapThink )
- {
- self->s.eFlags &= ~EF_FIRING;
- self->s.loopSound = 0;
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = -1;
- }
- else
- {
- self->e_ThinkFunc = thinkF_laserTrapThink;
- self->nextthink = level.time + FRAMETIME;
- self->s.eFlags &= ~EF_NODRAW;
- self->contents = CONTENTS_SHOTCLIP;//CAN'T USE CONTENTS_SOLID because only ARCHITECTURE is contents_solid!!!
- self->takedamage = qtrue;
- }
- }
- void SP_misc_trip_mine( gentity_t *self )
- {
- vec3_t forward, end;
- trace_t trace;
- AngleVectors( self->s.angles, forward, NULL, NULL );
- VectorMA( self->s.origin, 128, forward, end );
- gi.trace( &trace, self->s.origin, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
- if ( trace.allsolid || trace.startsolid )
- {
- Com_Error( ERR_DROP,"misc_trip_mine at %s in solid\n", vtos(self->s.origin) );
- G_FreeEntity( self );
- return;
- }
- if ( trace.fraction == 1.0 )
- {
- Com_Error( ERR_DROP,"misc_trip_mine at %s pointed at no surface\n", vtos(self->s.origin) );
- G_FreeEntity( self );
- return;
- }
- RegisterItem( FindItemForWeapon( WP_TRIP_MINE )); //precache the weapon
- self->count = 2/*TRIPWIRE_STYLE*/;
- vectoangles( trace.plane.normal, end );
- G_SetOrigin( self, trace.endpos );
- G_SetAngles( self, end );
- CreateLaserTrap( self, trace.endpos, self );
- touchLaserTrap( self, self, &trace );
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = -1;
- if ( !self->targetname || (self->spawnflags&1) )
- {//starts on
- misc_trip_mine_activate( self, self, self );
- }
- if ( self->targetname )
- {
- self->e_UseFunc = useF_misc_trip_mine_activate;
- }
- if (( self->spawnflags & 2 )) // broadcast...should only be used in very rare cases. could fix networking, perhaps, but james suggested this because it's easier
- {
- self->svFlags |= SVF_BROADCAST;
- }
- // Whether to start completelly off or not.
- if ( self->targetname && self->spawnflags & 4 )
- {
- self->s.eFlags = EF_NODRAW;
- self->contents = 0;
- self->takedamage = qfalse;
- }
- gi.linkentity( self );
- }
- /*QUAKED misc_maglock (0 .5 .8) (-8 -8 -8) (8 8 8) x x x x x x x x
- Place facing a door (using the angle, not a targetname) and it will lock that door. Can only be destroyed by lightsaber and will automatically unlock the door it's attached to
- NOTE: place these half-way in the door to make it flush with the door's surface.
- "target" thing to use when destoryed (not doors - it automatically unlocks the door it was angled at)
- "health" default is 10
- */
- void maglock_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc )
- {
- //unlock our door if we're the last lock pointed at the door
- if ( self->activator )
- {
- self->activator->lockCount--;
- if ( !self->activator->lockCount )
- {
- self->activator->svFlags &= ~SVF_INACTIVE;
- }
- }
-
- //use targets
- G_UseTargets( self, attacker );
- //die
- WP_Explode( self );
- }
- void SP_misc_maglock ( gentity_t *self )
- {
- //NOTE: May have to make these only work on doors that are either untargeted
- // or are targeted by a trigger, not doors fired off by scripts, counters
- // or other such things?
- self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" );
- self->fxID = G_EffectIndex( "maglock/explosion" );
- G_SetOrigin( self, self->s.origin );
- self->e_ThinkFunc = thinkF_maglock_link;
- //FIXME: for some reason, when you re-load a level, these fail to find their doors...? Random? Testing an additional 200ms after the START_TIME_FIND_LINKS
- self->nextthink = level.time + START_TIME_FIND_LINKS+200;//START_TIME_FIND_LINKS;//because we need to let the doors link up and spawn their triggers first!
- }
- void maglock_link( gentity_t *self )
- {
- //find what we're supposed to be attached to
- vec3_t forward, start, end;
- trace_t trace;
- AngleVectors( self->s.angles, forward, NULL, NULL );
- VectorMA( self->s.origin, 128, forward, end );
- VectorMA( self->s.origin, -4, forward, start );
- gi.trace( &trace, start, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
- if ( trace.allsolid || trace.startsolid )
- {
- Com_Error( ERR_DROP,"misc_maglock at %s in solid\n", vtos(self->s.origin) );
- G_FreeEntity( self );
- return;
- }
- if ( trace.fraction == 1.0 )
- {
- self->e_ThinkFunc = thinkF_maglock_link;
- self->nextthink = level.time + 100;
- /*
- Com_Error( ERR_DROP,"misc_maglock at %s pointed at no surface\n", vtos(self->s.origin) );
- G_FreeEntity( self );
- */
- return;
- }
- gentity_t *traceEnt = &g_entities[trace.entityNum];
- if ( trace.entityNum >= ENTITYNUM_WORLD || !traceEnt || Q_stricmp( "func_door", traceEnt->classname ) )
- {
- self->e_ThinkFunc = thinkF_maglock_link;
- self->nextthink = level.time + 100;
- //Com_Error( ERR_DROP,"misc_maglock at %s not pointed at a door\n", vtos(self->s.origin) );
- //G_FreeEntity( self );
- return;
- }
- //check the traceEnt, make sure it's a door and give it a lockCount and deactivate it
- //find the trigger for the door
- self->activator = G_FindDoorTrigger( traceEnt );
- if ( !self->activator )
- {
- self->activator = traceEnt;
- }
- self->activator->lockCount++;
- self->activator->svFlags |= SVF_INACTIVE;
- //now position and orient it
- vectoangles( trace.plane.normal, end );
- G_SetOrigin( self, trace.endpos );
- G_SetAngles( self, end );
- //make it hittable
- //FIXME: if rotated/inclined this bbox may be off... but okay if we're a ghoul model?
- //self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" );
- VectorSet( self->mins, -8, -8, -8 );
- VectorSet( self->maxs, 8, 8, 8 );
- self->contents = CONTENTS_CORPSE;
- //make it destroyable
- self->flags |= FL_SHIELDED;//only damagable by lightsabers
- self->takedamage = qtrue;
- self->health = 10;
- self->e_DieFunc = dieF_maglock_die;
- //self->fxID = G_EffectIndex( "maglock/explosion" );
- gi.linkentity( self );
- }
- /*
- ================
- EnergyShieldStationSettings
- ================
- */
- void EnergyShieldStationSettings(gentity_t *ent)
- {
- G_SpawnInt( "count", "0", &ent->count );
- if (!ent->count)
- {
- switch (g_spskill->integer)
- {
- case 0: // EASY
- ent->count = 100;
- break;
- case 1: // MEDIUM
- ent->count = 75;
- break;
- default :
- case 2: // HARD
- ent->count = 50;
- break;
- }
- }
- }
- /*
- ================
- shield_power_converter_use
- ================
- */
- void shield_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int dif,add;
- if ( !activator || activator->s.number != 0 )
- {
- //only the player gets to use these
- return;
- }
- G_ActivateBehavior( self,BSET_USE );
- if ( self->setTime < level.time )
- {
- self->setTime = level.time + 100;
- dif = 100 - activator->client->ps.stats[STAT_ARMOR]; // FIXME: define for max armor?
- if ( dif > 0 && self->count ) // Already at full armor?..and do I even have anything to give
- {
- if ( dif > MAX_AMMO_GIVE )
- {
- add = MAX_AMMO_GIVE;
- }
- else
- {
- add = dif;
- }
- if ( self->count < add )
- {
- add = self->count;
- }
- self->count -= add;
- activator->client->ps.stats[STAT_ARMOR] += add;
- self->s.loopSound = G_SoundIndex( "sound/interface/shieldcon_run.wav" );
- }
-
- if ( self->count <= 0 )
- {
- // play empty sound
- self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much
- G_Sound( self, G_SoundIndex( "sound/interface/shieldcon_empty.mp3" ));
- self->s.loopSound = 0;
- if ( self->s.eFlags & EF_SHADER_ANIM )
- {
- self->s.frame = 1;
- }
- }
- else if ( activator->client->ps.stats[STAT_ARMOR] >= 100 ) // FIXME: define for max
- {
- // play full sound
- G_Sound( self, G_SoundIndex( "sound/interface/shieldcon_done.mp3" ));
- self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much
- self->s.loopSound = 0;
- }
- }
- if ( self->s.loopSound )
- {
- // we will have to shut of the loop sound, so I guess try and do it intelligently...NOTE: this could get completely stomped every time through the loop
- // this is fine, since it just controls shutting off the sound when there are situations that could start the sound but not shut it off
- self->e_ThinkFunc = thinkF_poll_converter;
- self->nextthink = level.time + 500;
- }
- else
- {
- // sound is already off, so we don't need to "think" about it.
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = 0;
- }
-
- if ( activator->client->ps.stats[STAT_ARMOR] > 0 )
- {
- activator->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE;
- }
- }
- /*QUAKED misc_model_shield_power_converter (1 0 0) (-16 -16 -16) (16 16 16) x x x USETARGET
- model="models/items/psd_big.md3"
- Gives shield energy when used.
- USETARGET - when used it fires off target
- "health" - how much health the model has - default 60 (zero makes non-breakable)
- "targetname" - dies and displays damagemodel when used, if any (if not, removes itself)
- "target" - what to use when it dies
- "paintarget" - target to fire when hit (but not destroyed)
- "count" - the amount of ammo given when used (default 100)
- */
- //------------------------------------------------------------
- void SP_misc_model_shield_power_converter( gentity_t *ent )
- {
- SetMiscModelDefaults( ent, useF_shield_power_converter_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- EnergyShieldStationSettings(ent);
- G_SoundIndex("sound/interface/shieldcon_run.wav");
- G_SoundIndex("sound/interface/shieldcon_done.mp3");
- G_SoundIndex("sound/interface/shieldcon_empty.mp3");
- ent->s.modelindex = G_ModelIndex("models/items/psd_big.md3"); // Precache model
- ent->s.modelindex2 = G_ModelIndex("models/items/psd_big.md3"); // Precache model
- }
- void bomb_planted_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- if ( self->count == 2 )
- {
- self->s.eFlags &= ~EF_NODRAW;
- self->contents = CONTENTS_SOLID;
- self->count = 1;
- self->s.loopSound = self->noise_index;
- }
- else if ( self->count == 1 )
- {
- self->count = 0;
- // play disarm sound
- self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much
- G_Sound( self, G_SoundIndex("sound/weapons/overchargeend"));
- self->s.loopSound = 0;
- // this pauses the shader on one frame (more or less)
- self->s.eFlags |= EF_DISABLE_SHADER_ANIM;
- // this starts the animation for the model
- self->s.eFlags |= EF_ANIM_ONCE;
- self->s.frame = 0;
- //use targets
- G_UseTargets( self, activator );
- }
- }
- /*QUAKED misc_model_bomb_planted (1 0 0) (-16 -16 0) (16 16 70) x x x USETARGET
- model="models/map_objects/factory/bomb_new_deact.md3"
- Planted by evil men for evil purposes.
- "health" - please don't shoot the thermonuclear device
- "forcevisible" - When you turn on force sight (any level), you can see these draw through the entire level...
- */
- //------------------------------------------------------------
- void SP_misc_model_bomb_planted( gentity_t *ent )
- {
- VectorSet( ent->mins, -16, -16, 0 );
- VectorSet( ent->maxs, 16, 16, 70 );
- SetMiscModelDefaults( ent, useF_bomb_planted_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- G_SoundIndex("sound/weapons/overchargeend");
- ent->s.modelindex = G_ModelIndex("models/map_objects/factory/bomb_new_deact.md3"); // Precache model
- ent->s.modelindex2 = G_ModelIndex("models/map_objects/factory/bomb_new_deact.md3"); // Precache model
- ent->noise_index = G_SoundIndex("sound/interface/ammocon_run");
- ent->s.loopSound = ent->noise_index;
- //ent->s.eFlags |= EF_SHADER_ANIM;
- //ent->s.frame = ent->startFrame = 0;
- ent->count = 1;
- // If we have a targetname, we're are invisible until we are spawned in by being used.
- if ( ent->targetname )
- {
- ent->s.eFlags = EF_NODRAW;
- ent->contents = 0;
- ent->count = 2;
- ent->s.loopSound = 0;
- }
- int forceVisible = 0;
- G_SpawnInt( "forcevisible", "0", &forceVisible );
- if ( forceVisible )
- {//can see these through walls with force sight, so must be broadcast
- //ent->svFlags |= SVF_BROADCAST;
- ent->s.eFlags |= EF_FORCE_VISIBLE;
- }
- }
- void beacon_deploy( gentity_t *ent )
- {
- ent->e_ThinkFunc = thinkF_beacon_think;
- ent->nextthink = level.time + FRAMETIME * 0.5f;
- ent->s.frame = 0;
- ent->startFrame = 0;
- ent->endFrame = 30;
- ent->loopAnim = qfalse;
- }
- void beacon_think( gentity_t *ent )
- {
- ent->nextthink = level.time + FRAMETIME * 0.5f;
- // Deploy animation complete? Stop thinking and just animate signal forever.
- if ( ent->s.frame == 30 )
- {
- ent->e_ThinkFunc = thinkF_NULL;
- ent->nextthink = -1;
- ent->startFrame = 31;
- ent->endFrame = 60;
- ent->loopAnim = qtrue;
- ent->s.loopSound = ent->noise_index;
- }
- }
- void beacon_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- // Every time it's used it will be toggled on or off.
- if ( self->count == 0 )
- {
- self->s.eFlags &= ~EF_NODRAW;
- self->contents = CONTENTS_SOLID;
- self->count = 1;
- self->svFlags = SVF_ANIMATING;
- beacon_deploy( self );
- }
- else
- {
- self->s.eFlags = EF_NODRAW;
- self->contents = 0;
- self->count = 0;
- self->s.loopSound = 0;
- self->svFlags = 0;
- }
- }
- /*QUAKED misc_model_beacon (1 0 0) (-16 -16 0) (16 16 24) x x x
- model="models/map_objects/wedge/beacon.md3"
- An animating beacon model.
- "forcevisible" - When you turn on force sight (any level), you can see these draw through the entire level...
- */
- //------------------------------------------------------------
- void SP_misc_model_beacon( gentity_t *ent )
- {
- VectorSet( ent->mins, -16, -16, 0 );
- VectorSet( ent->maxs, 16, 16, 24 );
- SetMiscModelDefaults( ent, useF_beacon_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- //G_SoundIndex("sound/weapons/overchargeend");
- ent->s.modelindex = G_ModelIndex("models/map_objects/wedge/beacon.md3"); // Precache model
- ent->s.modelindex2 = G_ModelIndex("models/map_objects/wedge/beacon.md3"); // Precache model
- ent->noise_index = G_SoundIndex("sound/interface/ammocon_run");
- // If we have a targetname, we're are invisible until we are spawned in by being used.
- if ( ent->targetname )
- {
- ent->s.eFlags = EF_NODRAW;
- ent->contents = 0;
- ent->s.loopSound = 0;
- ent->count = 0;
- }
- else
- {
- ent->count = 1;
- beacon_deploy( ent );
- }
- int forceVisible = 0;
- G_SpawnInt( "forcevisible", "0", &forceVisible );
- if ( forceVisible )
- {//can see these through walls with force sight, so must be broadcast
- //ent->svFlags |= SVF_BROADCAST;
- ent->s.eFlags |= EF_FORCE_VISIBLE;
- }
- }
- /*QUAKED misc_shield_floor_unit (1 0 0) (-16 -16 0) (16 16 32) x x x USETARGET
- model="models/items/a_shield_converter.md3"
- Gives shield energy when used.
- USETARGET - when used it fires off target
- "health" - how much health the model has - default 60 (zero makes non-breakable)
- "targetname" - dies and displays damagemodel when used, if any (if not, removes itself)
- "target" - what to use when it dies
- "paintarget" - target to fire when hit (but not destroyed)
- "count" - the amount of ammo given when used (default 100)
- */
- //------------------------------------------------------------
- void SP_misc_shield_floor_unit( gentity_t *ent )
- {
- VectorSet( ent->mins, -16, -16, 0 );
- VectorSet( ent->maxs, 16, 16, 32 );
- SetMiscModelDefaults( ent, useF_shield_power_converter_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- EnergyShieldStationSettings(ent);
- G_SoundIndex("sound/interface/shieldcon_run.wav");
- G_SoundIndex("sound/interface/shieldcon_done.mp3");
- G_SoundIndex("sound/interface/shieldcon_empty.mp3");
- ent->s.modelindex = G_ModelIndex( "models/items/a_shield_converter.md3" ); // Precache model
- ent->s.eFlags |= EF_SHADER_ANIM;
- }
- /*
- ================
- EnergyAmmoShieldStationSettings
- ================
- */
- void EnergyAmmoStationSettings(gentity_t *ent)
- {
- G_SpawnInt( "count", "0", &ent->count );
- if (!ent->count)
- {
- switch (g_spskill->integer)
- {
- case 0: // EASY
- ent->count = 100;
- break;
- case 1: // MEDIUM
- ent->count = 75;
- break;
- default :
- case 2: // HARD
- ent->count = 50;
- break;
- }
- }
- }
- // There has to be an easier way to turn off the looping sound...but
- // it's the night before beta and my brain is fried
- //------------------------------------------------------------------
- void poll_converter( gentity_t *self )
- {
- self->s.loopSound = 0;
- self->nextthink = 0;
- self->e_ThinkFunc = thinkF_NULL;
- }
- /*
- ================
- ammo_power_converter_use
- ================
- */
- void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int add;
- int difBlaster,difPowerCell,difMetalBolts;
- playerState_t *ps;
- if ( !activator || activator->s.number != 0 )
- {//only the player gets to use these
- return;
- }
- G_ActivateBehavior( self, BSET_USE );
- ps = &activator->client->ps;
- if ( self->setTime < level.time )
- {
- difBlaster = ammoData[AMMO_BLASTER].max - ps->ammo[AMMO_BLASTER];
- difPowerCell = ammoData[AMMO_POWERCELL].max - ps->ammo[AMMO_POWERCELL];
- difMetalBolts = ammoData[AMMO_METAL_BOLTS].max - ps->ammo[AMMO_METAL_BOLTS];
- // Has it got any power left...and can we even use any of it?
- if ( self->count && ( difBlaster > 0 || difPowerCell > 0 || difMetalBolts > 0 ))
- {
- // at least one of the ammo types could stand to take on a bit more ammo
- self->setTime = level.time + 100;
- self->s.loopSound = G_SoundIndex( "sound/interface/ammocon_run.wav" );
- // dole out ammo in little packets
- if ( self->count > MAX_AMMO_GIVE )
- {
- add = MAX_AMMO_GIVE;
- }
- else if ( self->count < 0 )
- {
- add = 0;
- }
- else
- {
- add = self->count;
- }
- // all weapons fill at same rate...
- ps->ammo[AMMO_BLASTER] += add;
- ps->ammo[AMMO_POWERCELL] += add;
- ps->ammo[AMMO_METAL_BOLTS] += add;
- // ...then get clamped to max
- if ( ps->ammo[AMMO_BLASTER] > ammoData[AMMO_BLASTER].max )
- {
- ps->ammo[AMMO_BLASTER] = ammoData[AMMO_BLASTER].max;
- }
- if ( ps->ammo[AMMO_POWERCELL] > ammoData[AMMO_POWERCELL].max )
- {
- ps->ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
- }
- if ( ps->ammo[AMMO_METAL_BOLTS] > ammoData[AMMO_METAL_BOLTS].max )
- {
- ps->ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max;
- }
- self->count -= add;
- }
- if ( self->count <= 0 )
- {
- // play empty sound
- self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much
- G_Sound( self, G_SoundIndex( "sound/interface/ammocon_empty.mp3" ));
- self->s.loopSound = 0;
- if ( self->s.eFlags & EF_SHADER_ANIM )
- {
- self->s.frame = 1;
- }
- }
- else if ( ps->ammo[AMMO_BLASTER] >= ammoData[AMMO_BLASTER].max
- && ps->ammo[AMMO_POWERCELL] >= ammoData[AMMO_POWERCELL].max
- && ps->ammo[AMMO_METAL_BOLTS] >= ammoData[AMMO_METAL_BOLTS].max )
- {
- // play full sound
- G_Sound( self, G_SoundIndex( "sound/interface/ammocon_done.wav" ));
- self->setTime = level.time + 1000; // extra debounce so that the sounds don't overlap too much
- self->s.loopSound = 0;
- }
- }
-
- if ( self->s.loopSound )
- {
- // we will have to shut of the loop sound, so I guess try and do it intelligently...NOTE: this could get completely stomped every time through the loop
- // this is fine, since it just controls shutting off the sound when there are situations that could start the sound but not shut it off
- self->e_ThinkFunc = thinkF_poll_converter;
- self->nextthink = level.time + 500;
- }
- else
- {
- // sound is already off, so we don't need to "think" about it.
- self->e_ThinkFunc = thinkF_NULL;
- self->nextthink = 0;
- }
- }
- /*QUAKED misc_model_ammo_power_converter (1 0 0) (-16 -16 -16) (16 16 16) x x x USETARGET
- model="models/items/power_converter.md3"
- Gives ammo energy when used.
- USETARGET - when used it fires off target
- "health" - how much health the model has - default 60 (zero makes non-breakable)
- "targetname" - dies and displays damagemodel when used, if any (if not, removes itself)
- "target" - what to use when it dies
- "paintarget" - target to fire when hit (but not destroyed)
- "count" - the amount of ammo given when used (default 100)
- */
- //------------------------------------------------------------
- void SP_misc_model_ammo_power_converter( gentity_t *ent )
- {
- SetMiscModelDefaults( ent, useF_ammo_power_converter_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- EnergyAmmoStationSettings(ent);
- G_SoundIndex("sound/interface/ammocon_run.wav");
- G_SoundIndex("sound/interface/ammocon_done.mp3");
- G_SoundIndex("sound/interface/ammocon_empty.mp3");
- ent->s.modelindex = G_ModelIndex("models/items/power_converter.md3"); // Precache model
- ent->s.modelindex2 = G_ModelIndex("models/items/power_converter.md3"); // Precache model
- }
- /*QUAKED misc_ammo_floor_unit (1 0 0) (-16 -16 0) (16 16 32) x x x USETARGET
- model="models/items/a_pwr_converter.md3"
- Gives ammo energy when used.
- USETARGET - when used it fires off target
- "health" - how much health the model has - default 60 (zero makes non-breakable)
- "targetname" - dies and displays damagemodel when used, if any (if not, removes itself)
- "target" - what to use when it dies
- "paintarget" - target to fire when hit (but not destroyed)
- "count" - the amount of ammo given when used (default 100)
- */
- //------------------------------------------------------------
- void SP_misc_ammo_floor_unit( gentity_t *ent )
- {
- VectorSet( ent->mins, -16, -16, 0 );
- VectorSet( ent->maxs, 16, 16, 32 );
- SetMiscModelDefaults( ent, useF_ammo_power_converter_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- EnergyAmmoStationSettings(ent);
- G_SoundIndex("sound/interface/ammocon_run.wav");
- G_SoundIndex("sound/interface/ammocon_done.mp3");
- G_SoundIndex("sound/interface/ammocon_empty.mp3");
- ent->s.modelindex = G_ModelIndex("models/items/a_pwr_converter.md3"); // Precache model
- ent->s.eFlags |= EF_SHADER_ANIM;
- }
- /*
- ================
- welder_think
- ================
- */
- void welder_think( gentity_t *self )
- {
- self->nextthink = level.time + 200;
- vec3_t org,
- dir;
- mdxaBone_t boltMatrix;
- if( self->svFlags & SVF_INACTIVE )
- {
- return;
- }
- int newBolt;
-
- // could alternate between the two... or make it random... ?
- newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*flash" );
- // newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*flash01" );
- if ( newBolt != -1 )
- {
- G_Sound( self, self->noise_index );
- // G_PlayEffect( "blueWeldSparks", self->playerModel, newBolt, self->s.number);
- // The welder gets rotated around a lot, and since the origin is offset by 352 I have to make this super expensive call to position the hurt...
- gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, newBolt,
- &boltMatrix, self->currentAngles, self->currentOrigin, (cg.time?cg.time:level.time),
- NULL, self->s.modelScale );
- gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org );
- VectorSubtract( self->currentOrigin, org, dir );
- VectorNormalize( dir );
- // we want the welder effect to face inwards....
- G_PlayEffect( "sparks/blueWeldSparks", org, dir );
- G_RadiusDamage( org, self, 10, 45, self, MOD_UNKNOWN );
- }
- }
- /*
- ================
- welder_use
- ================
- */
- void welder_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- // Toggle on and off
- if( self->spawnflags & 1 )
- {
- self->nextthink = level.time + FRAMETIME;
- }
- else
- {
- self->nextthink = -1;
- }
- self->spawnflags = (self->spawnflags ^ 1);
-
- }
- /*QUAKED misc_model_welder (1 0 0) (-16 -16 -16) (16 16 16) START_OFF
- model="models/map_objects/cairn/welder.md3"
- When 'on' emits sparks from it's welding points
- START_OFF - welder starts off, using it toggles it on/off
- */
- //------------------------------------------------------------
- void SP_misc_model_welder( gentity_t *ent )
- {
- VectorSet( ent->mins, 336, -16, 0 );
- VectorSet( ent->maxs, 368, 16, 32 );
- SetMiscModelDefaults( ent, useF_welder_use, "4", CONTENTS_SOLID, NULL, qfalse, NULL );
- ent->takedamage = qfalse;
- ent->contents = 0;
- G_EffectIndex( "sparks/blueWeldSparks" );
- ent->noise_index = G_SoundIndex( "sound/movers/objects/welding.wav" );
- ent->s.modelindex = G_ModelIndex( "models/map_objects/cairn/welder.glm" );
- // ent->s.modelindex2 = G_ModelIndex( "models/map_objects/cairn/welder.md3" );
- ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/map_objects/cairn/welder.glm", ent->s.modelindex );
- ent->s.radius = 400.0f;// the origin of the welder is offset by approximately 352, so we need the big radius
- ent->e_ThinkFunc = thinkF_welder_think;
- ent->nextthink = level.time + 1000;
- if( ent->spawnflags & 1 )
- {
- ent->nextthink = -1;
- }
- }
- /*
- ================
- jabba_cam_use
- ================
- */
- void jabba_cam_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if( self->spawnflags & 1 )
- {
- self->spawnflags &= ~1;
- gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, 15, 0, BONE_ANIM_OVERRIDE_FREEZE, -1.5f, (cg.time?cg.time:level.time), -1, 0 );
- }
- else
- {
- self->spawnflags |= 1;
- gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, 0, 15, BONE_ANIM_OVERRIDE_FREEZE, 1.5f, (cg.time?cg.time:level.time), -1, 0 );
- }
- }
- /*QUAKED misc_model_jabba_cam (1 0 0) ( 0 -8 0) (60 8 16) EXTENDED
- model="models/map_objects/nar_shaddar/jabacam.md3"
- The eye camera that popped out of Jabba's front door
- EXTENDED - Starts in the extended position
- targetname - Toggles it on/off
-
- */
- //-----------------------------------------------------
- void SP_misc_model_jabba_cam( gentity_t *ent )
- //-----------------------------------------------------
- {
- VectorSet( ent->mins, -60.0f, -8.0f, 0.0f );
- VectorSet( ent->maxs, 60.0f, 8.0f, 16.0f );
-
- SetMiscModelDefaults( ent, useF_jabba_cam_use, "4", 0, NULL, qfalse, NULL );
- G_SetAngles( ent, ent->s.angles );
- ent->s.modelindex = G_ModelIndex( "models/map_objects/nar_shaddar/jabacam/jabacam.glm" );
- ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/map_objects/nar_shaddar/jabacam/jabacam.glm", ent->s.modelindex );
- ent->s.radius = 150.0f; //......
- VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );
- ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
-
- ent->e_UseFunc = useF_jabba_cam_use;
- ent->takedamage = qfalse;
- // start extended..
- if ( ent->spawnflags & 1 )
- {
- gi.G2API_SetBoneAnimIndex( &ent->ghoul2[ent->playerModel], ent->rootBone, 0, 15, BONE_ANIM_OVERRIDE_FREEZE, 0.6f, cg.time );
- }
- gi.linkentity( ent );
- }
- //------------------------------------------------------------------------
- void misc_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- misc_model_breakable_die( self, other, activator, 100, MOD_UNKNOWN );
- }
- /*QUAKED misc_exploding_crate (1 0 0.25) (-24 -24 0) (24 24 64)
- model="models/map_objects/nar_shaddar/crate_xplode.md3"
- Basic exploding crate
- "health" - how much health the model has - default 40 (zero makes non-breakable)
- "splashRadius" - radius to do damage in - default 128
- "splashDamage" - amount of damage to do when it explodes - default 50
- "targetname" - auto-explodes
- "target" - what to use when it dies
- */
- //------------------------------------------------------------
- void SP_misc_exploding_crate( gentity_t *ent )
- {
- G_SpawnInt( "health", "40", &ent->health );
- G_SpawnInt( "splashRadius", "128", &ent->splashRadius );
- G_SpawnInt( "splashDamage", "50", &ent->splashDamage );
- ent->s.modelindex = G_ModelIndex( "models/map_objects/nar_shaddar/crate_xplode.md3" );
- G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
- G_EffectIndex( "chunks/metalexplode" );
-
- VectorSet( ent->mins, -24, -24, 0 );
- VectorSet( ent->maxs, 24, 24, 64 );
- ent->contents = CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//CONTENTS_SOLID;
- ent->takedamage = qtrue;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- gi.linkentity (ent);
- if ( ent->targetname )
- {
- ent->e_UseFunc = useF_misc_use;
- }
- ent->material = MAT_CRATE1;
- ent->e_DieFunc = dieF_misc_model_breakable_die;//ExplodeDeath;
- }
- /*QUAKED misc_gas_tank (1 0 0.25) (-4 -4 0) (4 4 40)
- model="models/map_objects/imp_mine/tank.md3"
- Basic exploding oxygen tank
- "health" - how much health the model has - default 20 (zero makes non-breakable)
- "splashRadius" - radius to do damage in - default 48
- "splashDamage" - amount of damage to do when it explodes - default 32
- "targetname" - auto-explodes
- "target" - what to use when it dies
- */
- void gas_random_jet( gentity_t *self )
- {
- vec3_t pt;
- VectorCopy( self->currentOrigin, pt );
- pt[2] += 50;
- G_PlayEffect( "env/mini_gasjet", pt );
- self->nextthink = level.time + random() * 16000 + 12000; // do this rarely
- }
- //------------------------------------------------------------
- void GasBurst( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod, int hitLoc )
- {
- vec3_t pt;
- VectorCopy( self->currentOrigin, pt );
- pt[2] += 46;
- G_PlayEffect( "env/mini_flamejet", pt );
- // do some damage to anything that may be standing on top of it when it bursts into flame
- pt[2] += 32;
- G_RadiusDamage( pt, self, 32, 32, self, MOD_UNKNOWN );
- // only get one burst
- self->e_PainFunc = painF_NULL;
- }
- //------------------------------------------------------------
- void SP_misc_gas_tank( gentity_t *ent )
- {
- G_SpawnInt( "health", "20", &ent->health );
- G_SpawnInt( "splashRadius", "48", &ent->splashRadius );
- G_SpawnInt( "splashDamage", "32", &ent->splashDamage );
- ent->s.modelindex = G_ModelIndex( "models/map_objects/imp_mine/tank.md3" );
- G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
- G_EffectIndex( "chunks/metalexplode" );
- G_EffectIndex( "env/mini_flamejet" );
- G_EffectIndex( "env/mini_gasjet" );
- VectorSet( ent->mins, -4, -4, 0 );
- VectorSet( ent->maxs, 4, 4, 40 );
- ent->contents = CONTENTS_SOLID;
- ent->takedamage = qtrue;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- gi.linkentity (ent);
- ent->e_PainFunc = painF_GasBurst;
- if ( ent->targetname )
- {
- ent->e_UseFunc = useF_misc_use;
- }
- ent->material = MAT_METAL3;
- ent->e_DieFunc = dieF_misc_model_breakable_die;
- ent->e_ThinkFunc = thinkF_gas_random_jet;
- ent->nextthink = level.time + random() * 12000 + 6000; // do this rarely
- }
- /*QUAKED misc_crystal_crate (1 0 0.25) (-34 -34 0) (34 34 44) NON_SOLID
- model="models/map_objects/imp_mine/crate_open.md3"
- Open crate of crystals that explode when shot
- NON_SOLID - can only be shot
- "health" - how much health the crate has, default 80
- "splashRadius" - radius to do damage in, default 80
- "splashDamage" - amount of damage to do when it explodes, default 40
- "targetname" - auto-explodes
- "target" - what to use when it dies
- */
- //------------------------------------------------------------
- void CrystalCratePain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod, int hitLoc )
- {
- vec3_t pt;
- VectorCopy( self->currentOrigin, pt );
- pt[2] += 36;
- G_PlayEffect( "env/crystal_crate", pt );
- // do some damage, heh
- pt[2] += 32;
- G_RadiusDamage( pt, self, 16, 32, self, MOD_UNKNOWN );
- }
- //------------------------------------------------------------
- void SP_misc_crystal_crate( gentity_t *ent )
- {
- G_SpawnInt( "health", "80", &ent->health );
- G_SpawnInt( "splashRadius", "80", &ent->splashRadius );
- G_SpawnInt( "splashDamage", "40", &ent->splashDamage );
- ent->s.modelindex = G_ModelIndex( "models/map_objects/imp_mine/crate_open.md3" );
- ent->fxID = G_EffectIndex( "thermal/explosion" ); // FIXME: temp
- G_EffectIndex( "env/crystal_crate" );
- G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
- VectorSet( ent->mins, -34, -34, 0 );
- VectorSet( ent->maxs, 34, 34, 44 );
-
- //Blocks movement
- ent->contents = CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//Was CONTENTS_SOLID, but only architecture should be this
- if ( ent->spawnflags & 1 ) // non-solid
- { // Override earlier contents flag...
- //Can only be shot
- ent->contents = CONTENTS_SHOTCLIP;
- }
-
- ent->takedamage = qtrue;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- gi.linkentity (ent);
- ent->e_PainFunc = painF_CrystalCratePain;
- if ( ent->targetname )
- {
- ent->e_UseFunc = useF_misc_use;
- }
- ent->material = MAT_CRATE2;
- ent->e_DieFunc = dieF_misc_model_breakable_die;
- }
- /*QUAKED misc_atst_drivable (1 0 0.25) (-40 -40 -24) (40 40 248)
- model="models/players/atst/model.glm"
- Drivable ATST, when used by player, they become the ATST. When the player hits use again, they get out.
- "health" - how much health the atst has - default 800
- "target" - what to use when it dies
- */
- void misc_atst_setanim( gentity_t *self, int bone, int anim )
- {
- if ( bone < 0 || anim < 0 )
- {
- return;
- }
- int firstFrame = -1;
- int lastFrame = -1;
- float animSpeed = 0;
- //try to get anim ranges from the animation.cfg for an AT-ST
- for ( int i = 0; i < level.numKnownAnimFileSets; i++ )
- {
- if ( !Q_stricmp( "atst", level.knownAnimFileSets[i].filename ) )
- {
- firstFrame = level.knownAnimFileSets[i].animations[anim].firstFrame;
- lastFrame = firstFrame+level.knownAnimFileSets[i].animations[anim].numFrames;
- animSpeed = 50.0f / level.knownAnimFileSets[i].animations[anim].frameLerp;
- break;
- }
- }
- if ( firstFrame != -1 && lastFrame != -1 && animSpeed != 0 )
- {
- if (!gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], bone, firstFrame,
- lastFrame, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, animSpeed,
- (cg.time?cg.time:level.time), -1, 150 ))
- {
- gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], bone, firstFrame,
- lastFrame, BONE_ANIM_OVERRIDE_FREEZE, animSpeed,
- (cg.time?cg.time:level.time), -1, 150 );
- }
- }
- }
- void misc_atst_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc )
- {//ATST was destroyed while you weren't in it
- //can't be used anymore
- self->e_UseFunc = useF_NULL;
- //sigh... remove contents so we don't block the player's path...
- self->contents = CONTENTS_CORPSE;
- self->takedamage = qfalse;
- self->maxs[2] = 48;
- //FIXME: match to slope
- vec3_t effectPos;
- VectorCopy( self->currentOrigin, effectPos );
- effectPos[2] -= 15;
- G_PlayEffect( "explosions/droidexplosion1", effectPos );
- // G_PlayEffect( "small_chunks", effectPos );
- //set these to defaults that work in a worst-case scenario (according to current animation.cfg)
- gi.G2API_StopBoneAnimIndex( &self->ghoul2[self->playerModel], self->craniumBone );
- misc_atst_setanim( self, self->rootBone, BOTH_DEATH1 );
- }
- extern void G_DriveATST( gentity_t *ent, gentity_t *atst );
- extern void SetClientViewAngle( gentity_t *ent, vec3_t angle );
- extern qboolean PM_InSlopeAnim( int anim );
- void misc_atst_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if ( !activator || activator->s.number )
- {//only player can do this
- return;
- }
- int tempLocDmg[HL_MAX];
- int hl, tempHealth;
- if ( activator->client->NPC_class != CLASS_ATST )
- {//get in the ATST
- if ( activator->client->ps.groundEntityNum != self->s.number )
- {//can only get in if on top of me...
- //we *could* even check for the hatch surf...?
- return;
- }
- //copy origin
- G_SetOrigin( activator, self->currentOrigin );
- //copy angles
- VectorCopy( self->s.angles2, self->currentAngles );
- G_SetAngles( activator, self->currentAngles );
- SetClientViewAngle( activator, self->currentAngles );
- //set player to my g2 instance
- gi.G2API_StopBoneAnimIndex( &self->ghoul2[self->playerModel], self->craniumBone );
- G_DriveATST( activator, self );
- activator->activator = self;
- self->s.eFlags |= EF_NODRAW;
- self->svFlags |= SVF_NOCLIENT;
- self->contents = 0;
- self->takedamage = qfalse;
- //transfer armor
- tempHealth = self->health;
- self->health = activator->client->ps.stats[STAT_ARMOR];
- activator->client->ps.stats[STAT_ARMOR] = tempHealth;
- //transfer locationDamage
- for ( hl = HL_NONE; hl < HL_MAX; hl++ )
- {
- tempLocDmg[hl] = activator->locationDamage[hl];
- activator->locationDamage[hl] = self->locationDamage[hl];
- self->locationDamage[hl] = tempLocDmg[hl];
- }
- if ( !self->s.number )
- {
- CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.87 );
- }
- }
- else
- {//get out of ATST
- int legsAnim = activator->client->ps.legsAnim;
- if ( legsAnim != BOTH_STAND1
- && !PM_InSlopeAnim( legsAnim )
- && legsAnim != BOTH_TURN_RIGHT1 && legsAnim != BOTH_TURN_LEFT1 )
- {//can't get out of it while it's still moving
- return;
- }
- //FIXME: after a load/save, this crashes, BAD... somewhere in G2
- G_SetOrigin( self, activator->currentOrigin );
- VectorSet( self->currentAngles, 0, activator->client->ps.legsYaw, 0 );
- //self->currentAngles[PITCH] = activator->currentAngles[ROLL] = 0;
- G_SetAngles( self, self->currentAngles );
- VectorCopy( activator->currentAngles, self->s.angles2 );
- //remove my G2
- if ( self->playerModel >= 0 )
- {
- gi.G2API_RemoveGhoul2Model( self->ghoul2, self->playerModel );
- self->playerModel = -1;
- }
- //copy player's
- gi.G2API_CopyGhoul2Instance( activator->ghoul2, self->ghoul2 );
- self->playerModel = 0;//assumption
- //reset player to kyle
- G_DriveATST( activator, NULL );
- activator->activator = NULL;
- self->s.eFlags &= ~EF_NODRAW;
- self->svFlags &= ~SVF_NOCLIENT;
- self->contents = CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
- self->takedamage = qtrue;
- //transfer armor
- tempHealth = self->health;
- self->health = activator->client->ps.stats[STAT_ARMOR];
- activator->client->ps.stats[STAT_ARMOR] = tempHealth;
- //transfer locationDamage
- for ( hl = HL_NONE; hl < HL_MAX; hl++ )
- {
- tempLocDmg[hl] = self->locationDamage[hl];
- self->locationDamage[hl] = activator->locationDamage[hl];
- activator->locationDamage[hl] = tempLocDmg[hl];
- }
- //link me back in
- gi.linkentity ( self );
- //put activator on top of me?
- vec3_t newOrg = {activator->currentOrigin[0], activator->currentOrigin[1], activator->currentOrigin[2] + (self->maxs[2]-self->mins[2]) + 1 };
- G_SetOrigin( activator, newOrg );
- //open the hatch
- misc_atst_setanim( self, self->craniumBone, BOTH_STAND2 );
- gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "head_hatchcover", 0 );
- G_Sound( self, G_SoundIndex( "sound/chars/atst/atst_hatch_open" ));
- }
- }
- void SP_misc_atst_drivable( gentity_t *ent )
- {
- extern void NPC_ATST_Precache(void);
- extern void NPC_PrecacheAnimationCFG( const char *NPC_type );
- ent->s.modelindex = G_ModelIndex( "models/players/atst/model.glm" );
- ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, "models/players/atst/model.glm", ent->s.modelindex );
- ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
- ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue ); //FIXME: need to somehow set the anim/frame to the equivalent of BOTH_STAND1... use to be that BOTH_STAND1 was the first frame of the glm, but not anymore
- ent->s.radius = 320;
- VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );
- //register my weapons, sounds and model
- RegisterItem( FindItemForWeapon( WP_ATST_MAIN )); //precache the weapon
- RegisterItem( FindItemForWeapon( WP_ATST_SIDE )); //precache the weapon
- //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
- RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); //precache the weapon
- // RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER )); //precache the weapon
- // RegisterItem( FindItemForWeapon( WP_BOWCASTER )); //precache the weapon
- //HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
- G_SoundIndex( "sound/chars/atst/atst_hatch_open" );
- G_SoundIndex( "sound/chars/atst/atst_hatch_close" );
- NPC_ATST_Precache();
- ent->NPC_type = "atst";
- NPC_PrecacheAnimationCFG( ent->NPC_type );
- //open the hatch
- misc_atst_setanim( ent, ent->rootBone, BOTH_STAND2 );
- gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head_hatchcover", 0 );
- VectorSet( ent->mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
- VectorSet( ent->maxs, ATST_MAXS0, ATST_MAXS1, ATST_MAXS2 );
- ent->contents = CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
- ent->flags |= FL_SHIELDED;
- ent->takedamage = qtrue;
- if ( !ent->health )
- {
- ent->health = 800;
- }
- ent->s.radius = 320;
- ent->max_health = ent->health; // cg_draw needs this
- G_SetOrigin( ent, ent->s.origin );
- G_SetAngles( ent, ent->s.angles );
- VectorCopy( ent->currentAngles, ent->s.angles2 );
- gi.linkentity ( ent );
- //FIXME: test the origin to make sure I'm clear?
- ent->e_UseFunc = useF_misc_atst_use;
- ent->svFlags |= SVF_PLAYER_USABLE;
- //make it able to take damage and die when you're not in it...
- //do an explosion and play the death anim, remove use func.
- ent->e_DieFunc = dieF_misc_atst_die;
- }
- extern int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create );
- /*QUAKED misc_weather_zone (0 .5 .8) ?
- Determines a region to check for weather contents - (optional, used to reduce load times)
- Place surrounding your inside/outside brushes. It will not check for weather contents outside of these zones.
- */
- void SP_misc_weather_zone( gentity_t *ent )
- {
- gi.SetBrushModel(ent, ent->model);
- char temp[256];
- sprintf( temp, "zone ( %f %f %f ) ( %f %f %f )",
- ent->mins[0], ent->mins[1], ent->mins[2],
- ent->maxs[0], ent->maxs[1], ent->maxs[2] );
- G_FindConfigstringIndex(temp, CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- // gi.WE_AddWeatherZone(ent->mins, ent->maxs);
- G_FreeEntity(ent);
- }
|