Showing with 159 additions and 32 deletions.
  1. +18 −1 ai.qc
  2. +1 −1 boss.qc
  3. +1 −1 combat.qc
  4. +1 −1 demon.qc
  5. +1 −1 dog.qc
  6. +1 −1 enforcer.qc
  7. +1 −1 hknight.qc
  8. +1 −1 items.qc
  9. +1 −1 knight.qc
  10. +1 −1 misc.qc
  11. +80 −11 monsters.qc
  12. +1 −1 ogre.qc
  13. +41 −0 readme.txt
  14. +1 −1 shambler.qc
  15. +1 −1 soldier.qc
  16. +5 −5 weapons.qc
  17. +2 −2 wizard.qc
  18. +1 −1 zombie.qc
19 changes: 18 additions & 1 deletion ai.qc
Expand Up @@ -280,6 +280,14 @@ void() SightSound =
{
local float rsnd;

// Tourism
// We let triggered monsters get to this point (rather than cutting them
// off in monster_use) so that they will start moving rather than just
// standing still. However we don't want to play their sight roar sound.
// See also comments in ai_run.
if (cvar("showturtle") != 0)
return;

if (self.classname == "monster_ogre")
sound (self, CHAN_VOICE, "ogre/ogwake.wav", 1, ATTN_NORM);
else if (self.classname == "monster_knight")
Expand Down Expand Up @@ -354,6 +362,10 @@ float() FindTarget =
local entity client;
local float r;

// Tourism: always notarget
if (cvar("showturtle") != 0)
return FALSE;

// if the first spawnflag bit is set, the monster will only wake up on
// really seeing the player, not another monster getting angry

Expand Down Expand Up @@ -682,7 +694,12 @@ void(float dist) ai_run =

movedist = dist;
// see if the enemy is dead
if (self.enemy.health <= 0)
// Tourism
// We let triggered monsters get to this point (rather than cutting them
// off in monster_use) so that they will start moving rather than just
// standing still. However we don't want them to actually attack the
// player. See also comments in SightSound.
if (self.enemy.health <= 0 || cvar("showturtle") != 0)
{
self.enemy = world;
// FIXME: look all around for other targets
Expand Down
2 changes: 1 addition & 1 deletion boss.qc
Expand Up @@ -282,7 +282,7 @@ void() monster_boss =
total_monsters = total_monsters + 1;

// JPL ghosts mod: chthon auto-dies
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
self.use = boss_death1;
else
self.use = boss_awake;
Expand Down
2 changes: 1 addition & 1 deletion combat.qc
Expand Up @@ -110,7 +110,7 @@ void(entity targ, entity inflictor, entity attacker, float damage) T_Damage=
return;

// JPL ghosts mod: no damage, ever
if (targ.classname == "player" && cvar("showturtle") == 1)
if (targ.classname == "player" && cvar("showturtle") != 0)
return;

// used by buttons and triggers to set activator for target firing
Expand Down
2 changes: 1 addition & 1 deletion demon.qc
Expand Up @@ -140,7 +140,7 @@ void(entity attacker, float damage) demon1_pain =

void() demon1_die1 =[ $death1, demon1_die2 ] {
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "demon/ddeath.wav", 1, ATTN_NORM);
};
void() demon1_die2 =[ $death2, demon1_die3 ] {};
Expand Down
2 changes: 1 addition & 1 deletion dog.qc
Expand Up @@ -241,7 +241,7 @@ void() dog_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "dog/ddeath.wav", 1, ATTN_NORM);
self.solid = SOLID_NOT;

Expand Down
2 changes: 1 addition & 1 deletion enforcer.qc
Expand Up @@ -300,7 +300,7 @@ void() enf_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "enforcer/death1.wav", 1, ATTN_NORM);
if (random() > 0.5)
enf_die1 ();
Expand Down
2 changes: 1 addition & 1 deletion hknight.qc
Expand Up @@ -212,7 +212,7 @@ void() hknight_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "hknight/death1.wav", 1, ATTN_NORM);
if (random() > 0.5)
hknight_die1 ();
Expand Down
2 changes: 1 addition & 1 deletion items.qc
Expand Up @@ -64,7 +64,7 @@ Sets the clipping size and plants the object on the floor
void() StartItem =
{
// JPL ghosts mod: remove all items except keys
if (cvar("showturtle") == 1 && self.classname != "item_key1" &&
if (cvar("showturtle") != 0 && self.classname != "item_key1" &&
self.classname != "item_key2" && self.classname != "item_sigil")
{
remove(self);
Expand Down
2 changes: 1 addition & 1 deletion knight.qc
Expand Up @@ -225,7 +225,7 @@ void() knight_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "knight/kdeath.wav", 1, ATTN_NORM);
if (random() < 0.5)
knight_die1 ();
Expand Down
2 changes: 1 addition & 1 deletion misc.qc
Expand Up @@ -328,7 +328,7 @@ Laser is only for REGISTERED.
void() trap_spikeshooter =
{
// JPL ghosts mod: no shooters
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
{
remove (self);
return;
Expand Down
91 changes: 80 additions & 11 deletions monsters.qc
Expand Up @@ -13,8 +13,75 @@
void() monster_kill_hide =
{
// Tourism: kill and hide on spawn
T_Damage(self, world, world, self.health + 10);
self.origin_z = self.origin_z - 100000;
T_Damage(self, world, world, self.health + 10);
self.origin_z = self.origin_z - 100000;
};

void() monster_trigger_targets =
{
// Tourism: explicitly trigger targets that normally wait for monster death
// First restore the "normal" think routine.
self.think = self.think1;
self.think1 = SUB_Null;
// Now trigger stuff.
if (self.target)
{
activator = world;
SUB_UseTargets ();
}
// Now handoff to normal behavior.
self.think();
};

void() monster_tourismify =
{
// Tourism: affect monsters according to showturtle and skill settings
// (note that in all cases a permanent "notarget" is in effect; see
// the FindTarget function in ai.qc)

if (cvar("showturtle") == 1)
{
// "normal" Tourism settings
if (skill < 3)
{
// On Easy/Normal/Hard, monsters are present but not touchable or
// shootable.
self.solid = SOLID_NOT;
// Go ahead and trigger anything that would normally be triggered
// on monster death.
self.think1 = self.think;
self.think = monster_trigger_targets;
}
else
{
// On Nightmare, remove all monsters.
self.think = monster_kill_hide;
}
}
else if (cvar("showturtle") == 2)
{
// Tourism w/ shootable monsters
if (skill < 3)
{
// On Easy/Normal/Hard, monsters are present and have one health
// in case the player needs/wants to pop them. In this case the
// monster death triggers (if any) will only fire when the
// monster is killed.
self.health = 1;
}
else
{
// On Nightmare we still remove all monsters.
self.think = monster_kill_hide;
}
}
else if (cvar("showturtle") == 3)
{
// No monsters on any skill setting. This is equivalent to picking the
// Nightmare skill in previous modes; also same as the original
// Tourism mod behavior.
self.think = monster_kill_hide;
}
};

/*
Expand Down Expand Up @@ -119,12 +186,12 @@ local entity etemp;
self.pausetime = 99999999;
self.th_stand ();
}
// spread think times so they don't all happen at same time
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;
// Tourism
if (cvar("showturtle") == 1)
self.think = monster_kill_hide;
// Tourism
if (cvar("showturtle") != 0)
monster_tourismify();
};

void() walkmonster_start =
Expand Down Expand Up @@ -179,8 +246,9 @@ void() flymonster_start_go =
self.pausetime = 99999999;
self.th_stand ();
}
if (cvar("showturtle") == 1)
self.think = monster_kill_hide;
// Tourism
if (cvar("showturtle") != 0)
monster_tourismify();
};

void() flymonster_start =
Expand Down Expand Up @@ -234,8 +302,9 @@ void() swimmonster_start_go =
// spread think times so they don't all happen at same time
self.nextthink = self.nextthink + random()*0.5;

if (cvar("showturtle") == 1)
self.think = monster_kill_hide;
// Tourism
if (cvar("showturtle") != 0)
monster_tourismify();
};

void() swimmonster_start =
Expand Down
2 changes: 1 addition & 1 deletion ogre.qc
Expand Up @@ -395,7 +395,7 @@ void() ogre_die =
}

// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "ogre/ogdth.wav", 1, ATTN_NORM);

if (random() < 0.5)
Expand Down
41 changes: 41 additions & 0 deletions readme.txt
@@ -0,0 +1,41 @@
This is a tweak of JP LeBreton's "Ghosts" mod for Quake. You can get the Ghosts mod here: https://jp.itch.io/quake-ghosts

The Ghosts mod is a "tourism" mod that makes several changes to allow safely wandering through the levels of Quake. It also changes the soundtrack to use the Nine Inch Nails album "Ghosts I-IV". See the mod's webpage for more details.

This tweaked version of "Ghosts" restores the monsters into the levels, although they will not attack the player. You might like it if you feel that the presence of the Quake monsters is part of the ambience of the levels.


INSTALLATION

First download and install the Ghosts mod according to its own instructions.

Then take the progs.dat file from this package and use it to replace the progs.dat file in the "ghosts" directory of that installed mod. (If you want to save that original progs.dat file you can first move or copy it somewhere else for safekeeping.)


USE

Run the Ghosts mod according to its own instructions.

On Easy/Normal/Hard skills, monsters will now be present in the levels. (Different monsters may appear at different skill levels.) However these monsters will not notice the player and they cannot be touched or shot.

On Nightmare skill, no monsters are present. (Same as the original Ghosts mod behavior.)


ALTERNATE CONFIGURATIONS

You can modify the autoexec.cfg file in the "ghosts" directory to change the mod's behavior. You will see that normally that file sets "showturtle" to a value of 1, which will give the behavior described above. If you set showturtle to other values then you can get other behaviors:

If showturtle is 2: On Easy/Normal/Hard skills, monsters are present and do not notice the player, but they can be shot. They only have 1 health so they will die in one shot. It may be necessary to shoot some monsters to progress. On Nightmare skill, no monsters are present.

If showturtle is 3: No monsters are present regardless of skill setting. This is equivalent to picking the Nightmare skill in previous modes; also same as the original Ghosts mod behavior.


TECHNICAL NOTES

If you downloaded this in an archive that only contains this readme file and the progs.dat, note that the QuakeC source can be found here:
https://github.com/neogeographica/quakec/tree/ghosts_modified

To see the changes made compared to JP's mod, see:
https://github.com/neogeographica/quakec/compare/ghosts...ghosts_modified

There's a couple of decisions I had to make on how to handle monsters that are explicitly triggered when the player does something. Obviously they still shouldn't try to attack the player... but should they play their "wake up" announcement sound? (I chose "no".) Should they start to move around or stand still? (I chose "move around".) See the code comments in ai.qc for details.
2 changes: 1 addition & 1 deletion shambler.qc
Expand Up @@ -318,7 +318,7 @@ void() sham_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "shambler/sdeath.wav", 1, ATTN_NORM);
sham_death1 ();
};
Expand Down
2 changes: 1 addition & 1 deletion soldier.qc
Expand Up @@ -233,7 +233,7 @@ void() army_die =

// regular death
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "soldier/death1.wav", 1, ATTN_NORM);
if (random() < 0.5)
army_die1 ();
Expand Down
10 changes: 5 additions & 5 deletions weapons.qc
Expand Up @@ -271,7 +271,7 @@ void() W_FireShotgun =
self.punchangle_x = -2;

// JPL ghosts mod: infinite boomstick ammo
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
self.currentammo = self.ammo_shells = self.ammo_shells - 1;
dir = aim (self, 100000);
FireBullets (6, dir, '0.04 0.04 0');
Expand Down Expand Up @@ -843,7 +843,7 @@ float() W_BestWeapon =
if(self.ammo_shells >= 1 && (it & IT_SHOTGUN) )
return IT_SHOTGUN;
// JPL tourism: ignore axe, shotgun will always have ammo
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
return IT_SHOTGUN;
else
return IT_AXE;
Expand Down Expand Up @@ -961,7 +961,7 @@ void() W_ChangeWeapon =
if (self.impulse == 1)
{
// tourism: don't use axe
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
return;
fl = IT_AXE;
}
Expand Down Expand Up @@ -1081,7 +1081,7 @@ void() CycleWeaponCommand =
if (self.weapon == IT_LIGHTNING)
{
// JPL tourism: ignore axe
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
self.weapon = IT_SHOTGUN;
else
self.weapon = IT_AXE;
Expand Down Expand Up @@ -1194,7 +1194,7 @@ void() CycleWeaponReverseCommand =
}
else if (self.weapon == IT_SHOTGUN)
{
if (cvar("showturtle") == 1)
if (cvar("showturtle") != 0)
return;
else
self.weapon = IT_AXE;
Expand Down
4 changes: 2 additions & 2 deletions wizard.qc
Expand Up @@ -329,7 +329,7 @@ self.velocity_y = -200 + 400*random();
self.velocity_z = 100 + 100*random();
self.flags = self.flags - (self.flags & FL_ONGROUND);
// Tourism: no death sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "wizard/wdeath.wav", 1, ATTN_NORM);
};
void() wiz_death2 =[ $death2, wiz_death3 ] {};
Expand Down Expand Up @@ -360,7 +360,7 @@ void() wiz_die =
void(entity attacker, float damage) Wiz_Pain =
{
// Tourism: no pain sound
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
sound (self, CHAN_VOICE, "wizard/wpain.wav", 1, ATTN_NORM);
if (random()*70 > damage)
return; // didn't flinch
Expand Down
2 changes: 1 addition & 1 deletion zombie.qc
Expand Up @@ -387,7 +387,7 @@ void() zombie_paine30 =[ $paine30, zombie_run1 ] {};
void() zombie_die =
{
// Tourism: no death sound OR gibs
if (cvar("showturtle") != 1)
if (cvar("showturtle") == 0)
{
sound (self, CHAN_VOICE, "zombie/z_gib.wav", 1, ATTN_NORM);
ThrowHead ("progs/h_zombie.mdl", self.health);
Expand Down