|
I'm reposting the Torture Chamber tutorial because I made some fixes to the
shell and I changed the way it free's itself, modified the way the bfg
fireball starts, and I fixed a bug which was causing it to crash when the
pipe took damage.
Okay, if you are having a
problem with the game crashing when the pipe takes a direct hit with a
rocket, (or something that causes T_Damage to run), make sure that both your
CheckArmor() and CheckPowerArmor() functions have a test at the very to to
make sure that ent is a client. So, if (!ent->client) return; out of both
of these functions!
So.. EVERYTHING WORKS
JUST FINE now..
Here is the rest of the
source:
#include "g_local.h"
//======================================================
//================= TORTURE CHAMBER ====================
//======================================================
//======================================================
void Dummy_Touch(edict_t *a,edict_t *b,cplane_t *c,csurface_t *d)
{}
//======================================================
void Chamber_Explode(edict_t *torturer) {
vec3_t start;
VectorCopy(torturer->s.origin,start);
start[2] += 64;
// Display huge bfg fireball at this location
fire_bfg(torturer,start,tv(0,0,-1),200,100,300);
// Restart autolauncher in 10 seconds
torturer->think=Init_Chamber_Launcher;
torturer->nextthink=level.time + 10.0;
}
//======================================================
void Chamber_Free(edict_t *torturer) {
edict_t *botpad;
//============================================
// Ent retrieval done ONLY to avoid confusion
//============================================
botpad=torturer->activator; // retrieve botpad entity
// Make crosslinked ents disappear in reverse order.
G_FreeEdict(botpad->mynoise2); // free Pipe Entity
G_FreeEdict(botpad->mynoise); // free Shell Entity
G_FreeEdict(botpad->movetarget);// free Top Pad
G_FreeEdict(botpad); // now free Bottom Pad
// Explode Chamber on next frame
torturer->think=Chamber_Explode;
torturer->nextthink=level.time + FRAMETIME;
}
void Search_For_Player(edict_t *);
//======================================================
void Torture_The_Bastard(edict_t *torturer) {
float damage=10; // ADJUSTABLE - torture duration
edict_t *victim;
victim=torturer->target_ent; // retrieve victim entity
// Victim died or left the game?
if (!G_ClientInGame(victim)) {
torturer->target_ent=NULL;
torturer->think=Search_For_Player; // Start searching again
torturer->nextthink=level.time + FRAMETIME;
return; }
// Test if this is last cycle for victim?
if (victim->health-damage<=0)
damage=50; // make victim explode! - see player_die()
else {
if (rand()&1) // make victim scream loudly
gi.sound(victim,2,gi.soundindex("player/burn1.wav"),1,1,0);
else
gi.sound(victim,2,gi.soundindex("player/burn2.wav"),1,1,0); }
// Damage victim a total of 'damage' units - Nobody gets the frags
T_Damage(victim,torturer,torturer,zvec,torturer->s.origin,zvec,damage,0,0,MOD_CHAMBER);
torturer->think=Torture_The_Bastard;
torturer->nextthink=level.time + 1.5; // Allow time for gi.sound() to finish
}
//======================================================
void Beam_Player_Into_Chamber(edict_t *torturer) {
edict_t *botpad;
edict_t *pipe;
edict_t *victim;
//============================================
// Ent retrieval done ONLY to avoid confusion
//============================================
victim=torturer->target_ent;// retrieve victim entity
botpad=torturer->activator; // retrieve botpad entity
pipe=botpad->mynoise2; // retrieve pipe entity
pipe->noise_index2=1; // start spurting blood - see Pipe_Think()
// Show laserbeam from top of pipe to victim's origin
G_Spawn_Trails(TE_BFG_LASER,pipe->pos1,victim->s.origin);
// Do teleport effect and play teleport sound at victim's origin
G_MuzzleFlash((short)(victim-g_edicts),victim->s.origin,MZ_LOGIN);
gi.sound(victim,2,gi.soundindex("world/amb10.wav"),1,1,0);
// Physically relocate victim to Chamber's origin
VectorCopy(torturer->s.origin,victim->s.origin);
// Start torturing on very next frame
torturer->think=Torture_The_Bastard;
torturer->nextthink=level.time + FRAMETIME;
}
//======================================================
void Search_For_Player(edict_t *torturer) {
edict_t *victim=NULL;
edict_t *pipe,*botpad;
vec3_t start;
botpad=torturer->activator; // retrieve botpad entity
pipe=botpad->mynoise2; // retrieve pipe entity
pipe->noise_index2=0; // Turn OFF blood spurts - see Pipe_Think()
VectorCopy(torturer->s.origin,start); // search from center of chamber
// Beam FIRST victim found into center of Chamber
while ((victim=findradius(victim,start,1000))!=NULL) { // search radius - ADJUSTABLE
if (!G_ClientInGame(victim)) continue; // Skip dead,respawners,spectators
if (!G_ClearPath(start,victim->s.origin)) continue; // straight line to victim?
//-------- Okay! Found one within radius! ----------
torturer->target_ent=victim; // player becomes victim
torturer->think=Beam_Player_Into_Chamber; // relocate player into chamber
torturer->nextthink=level.time + FRAMETIME; // do this on next frame
//--------------------------------------------------
return; }
torturer->nextthink=level.time + 1.0; // search again in 1 second
}
//======================================================
void Pipe_Die(edict_t *pipe,edict_t *inflictor,edict_t *attacker,int damage,vec3_t point) {
edict_t *botpad;
edict_t *torturer;
// Valid Attacker gets frags for Killing Pipe Entity!
if (G_ClientInGame(attacker)) {
gi_centerprintf(attacker,"5 Frags for destroying Torture Chamber!\n");
attacker->client->resp.score += 5; } // frag award - ADJUSTABLE
botpad=pipe->activator; // retrieve botpad entity
torturer=botpad->goalentity; // retrieve torturer entity
// Free chamber on next frame
torturer->think=Chamber_Free;
torturer->nextthink=level.time + FRAMETIME;
}
//======================================================
void Pipe_Think(edict_t *pipe) {
// Make Blood spurt out top of Pipe!
if (pipe->noise_index2==1) {
G_Spawn_Splash(TE_LASER_SPARKS,12,0xf2f2f0f0,pipe->pos1,zvec);
pipe->nextthink=level.time + 0.5; // spurt every 1/2 sec
return; }
pipe->nextthink=level.time + FRAMETIME; // check again next frame
}
//======================================================
// Place the 'stove pipe' on top of Torture Chamber.
// ONLY way to kill Chamber is to 'smoke' its stack!
//======================================================
void Create_Pipe(edict_t *botpad) {
edict_t *pipe;
gi.dprintf("CHAMBER SPAWNED\n");
pipe=G_Spawn();
pipe->owner=world;
pipe->activator=botpad; // crosslink pipe to bottom pad
botpad->mynoise2=pipe; // crosslink bottom pad to pipe
gi.setmodel(pipe,"models/objects/minelite/light1/tris.md2");
VectorSet(pipe->mins,-2,-2,-8);
VectorSet(pipe->maxs,+2,+2,+8);
//---- Make Pipe Is Killable --------
pipe->movetype=MOVETYPE_NONE;
pipe->solid=SOLID_BBOX; // touchable
pipe->clipmask=MASK_SHOT; // shootable
pipe->takedamage=DAMAGE_YES; // damageable
pipe->health=200; // 200 health
pipe->max_health=pipe->health;
//-----------------------------------
pipe->noise_index2=0; // No blood spurting yet
VectorCopy(botpad->s.origin,pipe->s.origin);
pipe->s.origin[2] += 65;
VectorCopy(pipe->s.origin,pipe->pos1);
pipe->pos1[2] += 12; // pos1 is top of pipe
pipe->touch=Dummy_Touch;
pipe->die=Pipe_Die; // do this when Killed()
pipe->think=Pipe_Think; // handle blood spurting
pipe->nextthink=level.time + FRAMETIME;
gi.linkentity(pipe);
}
//======================================================
void Create_Torturer_Entity(edict_t *botpad) {
edict_t *torturer;
torturer=G_Spawn();
torturer->owner=world;
torturer->activator=botpad; // crosslink Torturer to bottom pad
botpad->goalentity=torturer; // crosslink bottom pad to Torturer
torturer->svflags=SVF_NOCLIENT; // completely invisible in game
VectorCopy(botpad->s.origin,torturer->s.origin);
torturer->s.origin[2] += 7; // approx center of chamber
torturer->think=Search_For_Player; // Start looking for players
torturer->nextthink=level.time + 5.0; // Activate search in 5 secs
gi.linkentity(torturer);
}
//======================================================
void Shell_Think(edict_t *shell) {
vec3_t end;
trace_t tr;
// Must repeatedly be updated else shell flashes
VectorCopy(shell->s.origin,end);
end[2] -= 63;
tr=gi.trace(shell->s.origin,shell->mins,shell->maxs,end,NULL,0);
VectorCopy(tr.endpos,shell->s.old_origin);
shell->nextthink=level.time + FRAMETIME;
gi.linkentity(shell); // update engine
}
//======================================================
void Create_Chamber_Shell(edict_t *botpad) {
edict_t *shell;
shell=G_Spawn();
shell->owner=world;
botpad->mynoise=shell; // crosslink bottom pad to Shell
VectorSet(shell->mins,-20,-20,-60);
VectorSet(shell->maxs,+20,+20,+60);
shell->s.renderfx |= (RF_BEAM|RF_TRANSLUCENT|RF_GLOW);
shell->movetype=MOVETYPE_NONE;
shell->solid=SOLID_BBOX; // touchable by player
shell->clipmask=MASK_SHOT; // shootable by weapons
shell->takedamage=DAMAGE_NO; // but not damageable
shell->s.modelindex=1; // must==1 - world
shell->s.frame=58; // width of beam
shell->s.skinnum=0xd0d1d2d3; // beam color is Green
VectorCopy(botpad->s.origin,shell->s.origin);
shell->s.origin[2] += 48;
VectorCopy(shell->s.origin,shell->s.old_origin);
shell->touch=Dummy_Touch;
shell->think=Shell_Think; // Beam must be constantly refreshed
shell->nextthink=level.time + FRAMETIME;
gi.linkentity(shell);
}
//======================================================
void Create_Toppad(edict_t *botpad) {
edict_t *toppad;
toppad=G_Spawn();
toppad->owner=world;
botpad->movetarget=toppad; // crosslink bottom pad to top pad
gi.setmodel(toppad,"models/objects/dmspot/tris.md2");
VectorSet(toppad->mins,-20,-20,-5);
VectorSet(toppad->maxs,+20,+20,+5);
toppad->movetype=MOVETYPE_NONE;
toppad->clipmask=MASK_SHOT; // shootable
toppad->solid=SOLID_BBOX; // touchable
toppad->takedamage=DAMAGE_NO; // but not damageable
toppad->s.angles[2]+=180; // flip upside down..
VectorCopy(botpad->s.origin,toppad->s.origin);
toppad->s.origin[2] += 32;
toppad->touch=Dummy_Touch;
// no thinking required.
gi.linkentity(toppad);
}
//======================================================
// Keep checking if bottom spawnpad stopped bouncing yet
//======================================================
void botpad_Think(edict_t *botpad) {
// If bottom spawnpad bounced into Lava/Slime,then we need to do this.
if (gi.pointcontents(botpad->s.origin) & (CONTENTS_SLIME|CONTENTS_LAVA)) {
botpad->svflags=SVF_NOCLIENT; // Make pad invisible
botpad->think=Init_Chamber_Launcher;// Make Pad initialize launcher
botpad->nextthink=level.time + 10.0;// Start launcher in 10 seconds
return; }
// Pad stopped moving! Now make full chamber appear!
if (VectorLengthSqr(botpad->velocity)<=1.0) {
botpad->think=NULL; // no more thinking needed
botpad->nextthink=level.time; // reset thinking timer
//---- Create Torture Chamber ----
Create_Toppad(botpad); // Create top spawn pad
Create_Pipe(botpad); // Put Pipe model on top
Create_Chamber_Shell(botpad); // Activate Laser Shell.
Create_Torturer_Entity(botpad);// Create Torturer Entity
//--------------------------------
return; }
botpad->think=botpad_Think; // Check again on next frame
botpad->nextthink=level.time + FRAMETIME;
}
//======================================================
void Create_Botpad(edict_t *launcher) {
edict_t *botpad;
botpad=G_Spawn(); // botpad is chamber's primary entity
botpad->owner=world;
gi.setmodel(botpad,"models/objects/dmspot/tris.md2");
VectorSet(botpad->mins,-20,-20,-30);
VectorSet(botpad->maxs,+20,+20,+10);
botpad->clipmask=MASK_SHOT; // shootable
botpad->solid=SOLID_BBOX; // touchable
botpad->takedamage=DAMAGE_NO; // but not damageable - see T_Damage()
botpad->movetype=MOVETYPE_BOUNCE;// pad bounces around - see G_RunEntity()
VectorCopy(launcher->pos1,botpad->s.origin); // start bouncing from this location
botpad->s.origin[2] += 40;
botpad->touch=Dummy_Touch;
botpad->think=botpad_Think; // check when stop moving
botpad->nextthink=level.time + FRAMETIME;
gi.linkentity(botpad);
G_FreeEdict(launcher); // free launcher entity
}
//======================================================
edict_t *GetValidOrigin(edict_t *launcher) {
// Grab origin of first player we find
for (int i=1; i<=game.maxclients; i++)
if (G_ClientInGame(&g_edicts[i]))
return &g_edicts[i]; // Got one!
return NULL; // Nobody in game yet.
}
//======================================================
void Launcher_Think(edict_t *launcher) {
edict_t *ent;
if ((ent=GetValidOrigin(launcher))!=NULL) {// found a valid origin to put chamber?
VectorCopy(ent->s.origin,launcher->pos1);// locate chamber at this ent's origin
launcher->think=Create_Botpad; // delay creating chamber so ent can move
launcher->nextthink=level.time + 1.0; // 10 sec delay should be enough time..
return; }
launcher->nextthink=level.time + 10.0; // try again in 10 secs
}
//======================================================
void Init_Chamber_Launcher(edict_t *prev_ent) {
edict_t *launcher;
launcher=G_Spawn();
launcher->owner=world;
launcher->svflags=SVF_NOCLIENT; // completely invisible in game
launcher->think=Launcher_Think; // Find a location to spawn Chamber
launcher->nextthink=level.time + 30.0; // Give time for players to connect
gi.linkentity(launcher);
if (prev_ent)
G_FreeEdict(prev_ent); // free previous entity
}
Just cut and paste this
overtop the code in your g_chamber.c file and re-compile..
Have Fun!
Maj.Bitch
|