Directions, please! (Part 1)

Hello everyone… whoever might be visiting this site. I have been messing with a few things in QuakeC particularly the ability to seamlessly transfer the player to new locations without special effects. This has been done in a few doom maps and is very prevalent in horror games.

Examples:

  • A player looks up to see something on the ceiling but when they look back down the room around them is totally different.
  • Looking out of a window a continuous swarm of troops pass by (in reality it is only 3-4 enemies looping continuously)
  • When the player walks down the hall in one direction the hall is endless but when he walks back he can return the way that he came

I showed the following gif on the Quake Mapping discord:

This technique will be shared in a future article

It was met with a lot of excitement but a question was raised, “How can we make sure the player is looking the right direction?” and that is how a new series of experiments involving directions were created.

What is the problem?

As always I am going to state the problem I am trying to solve first then what logic I employed to find a solution, the difficulties I faced and then the final solution I came up with.

So what was I trying to solve? This time. Multiple things. I wanted to understand if I can determine with a player is looking in a direction before triggering another entity. There are multiple ways to do this:

  1. Create a brush with a mangle and determine if the player is looking towards the mangle while touching the brush.
  2. Create a brush with flags that have directions (up, down, left, right, forward, back) and see if that player is looking in the selected directions.
  3. Create a brush that has a target. Determine if the player is looking at the target.

Each one of these has a potential problem. Let’s look at the pros and cons to each.

1. The brush with a mangle to look at

Pros:

  • Super easy to set up for mappers
  • Mappers can define their own tolerance
  • One line of code for horizontal detection

Cons

  • Looking up or down is difficult to get right due to the fact the pitch values change according to if the player is facing “left” or “right”
  • Might be confusing if the mapper doesn’t direct the players to look in the expected direction

2. The brush with flags

Pros:

  • This logic is fairly straightforward and provided by the makevectors function
  • If configured correctly in a .fgd then this would be even easier for mappers to use
  • Mappers can set the tolerance

Cons:

  • Easier to set, harder to understand. Left, right, forward, and backward are relative terms and might be confusing without experience with the trigger
  • Not as accurate. This trigger provides 24 (8*3) possible directions though
  • Might have the same issue as #1. The mapper needs to direct a player to look in a direction regardless of where they are standing in the brush

3. The brush with a target

Pros:

  • The mapper can make anything a target
  • Might make more sense for the player to look at a symbol or object of importance to trigger an event
  • The mapper can set distance and tolerance values

Cons:

  • More steps to implement correctly
  • More required settings for mappers
  • Not compatible with vanilla QuakeC sources since it requires target2 (one to trigger and one for the direction detection)

To be continued…

This article is long enough. We have taken a look at the ideas behind the solutions I would like to investigate. In Part 2 I will go into what I did to solve the first of these trigger types and the issues I ran into while testing it.

Wall Spikes – Fixed

I tried a few minor tweaking tutorials for rapid-fire shotguns, flashlight, and just a coding standards tutorial for QuakeC that were fairly bland. From there I wanted to do something I fondly remember from the first time I tried coding for Quake… nails that stick to walls. Granted, the first time I did this I made the nails explode after a short period and that might have side-stepped the errors that I ran into but this time I just wanted to replicate what I thought the tutorial wanted to do… make nails stick to walls.

The Tutorial

I looked up the tutorial on Inside3D here: http://www.insideqc.com/qctut/qctut-1.shtml

void() spike_touch =
{
        local float rand;
        if (other == self.owner)
                return;
        if (other.solid == SOLID_TRIGGER)
                return; // trigger field, do nothing
        if (pointcontents(self.origin) == CONTENT_SKY) {
                remove(self);
                return;
        }
        // hit something that bleeds
        if (other.takedamage) {
                spawn_touchblood (9);
                T_Damage (other, self, self.owner, 9)
                remove(self);   //so they don't float...
                return;         //to avoid errors
        } else {
                WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
                
                if (self.classname == "wizspike")
                        WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
                else if (self.classname == "knightspike")
                        WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
                else
                        WriteByte (MSG_BROADCAST, TE_SPIKE);
                WriteCoord (MSG_BROADCAST, self.origin_x);
                WriteCoord (MSG_BROADCAST, self.origin_y);
                WriteCoord (MSG_BROADCAST, self.origin_z);
        }
        self.velocity = '0 0 0';        //makes the nail stop
        self.nextthink = time + 10;     //to prevent packet overflows
        self.think = SUB_Remove;        //same reason as above

};

The Problem(s)

I immediately ran into problems. The issue revolves around this logic:

        // hit something that bleeds
        if (other.takedamage) {
when an enemy dies they enter a solid dying state

Every time I fired more arrows than required to kill an enemy or shot a brush with health nails would float in the air.

BUT WAIT! The tutorial removed the spikes on entities that took damage! Enemies, doors, and buttons all can take damage so shouldn’t the spikes be removed?, I can hear you say.

The issue here is that once these entities hit zero health their state changes from an entity that can be damaged to one that is dying. Enemies briefly go into a dying state and doors will become solid and begin moving into an open position.

The Solution

My knowledge of QuakeC ended over 16 years ago. So fixing this tutorial simply required me to figure out one thing: how in the world do I remove spikes from things that had or have health?
To fix this we need to only stop and float spikes that hit solids that don’t have health values defined. I modified the code about a dozen times before I realized that, in QuakeC, I can check if a value was set by logically checking the value. In layman’s terms, QuakeC evaluates everything above zero as true. With that knowledge, we need to replace this:

        self.velocity = '0 0 0';        //makes the nail stop
        self.nextthink = time + 10;     //to prevent packet overflows
        self.think = SUB_Remove;        //same reason as above

Which will fire for all things that don’t take damage. Instead, we will check for if the thing the spike hits has health. If it does, we simply remove the spike but if it doesn’t then we halt the spike and set a time to remove the spike in the future.

if(!other.health) {
	self.velocity = '0 0 0';    //make the nail stop
	self.nextthink = time + 5;  //to prevent packet overflows
	self.think = SUB_Remove;
} else {
	remove(self);  // removed to prevent nails from vanishing
}

Again, this was done on day 1 of my return to QuakeC. I am more than willing to fess up to this fix having problems.

Next up I will be taking a look at a low effort stealth mod tutorial and adding sneak attacks in order to flesh out the tutorial.

QuakeC Tutorials, Updated

Hello to those interested in what I have been up to. Well, after the last Quake map jam I was in I realized that I just don’t have the time to get anything done in a short time frame unless that thing happens to be a short project. Recently I have been itching to do some coding since I do a ton of software development but rarely with code involved.

This begins my new journey. I am going to relearn QuakeC and go through some older tutorials. I will be going through them, fixing them, completing the code, and enhancing them as I feel interested in doing so.

I have no schedule for updating this site… I just will when I have the time.

So, let the stumbling through QuakeC begin!