Forum

Death messages

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

Death messages

Postby redrum » Mon Dec 10, 2007 3:08 am

Guys, got a problem that I need help with.
I changed the lightning gun to a lava launcher.
You can catch on fire from the lavaball and then die well after the shots have been fired.
If I am using a different weapon when the target dies, the death message displays for the current weapon and not the lava launcher :(
Here is my death message code:
Code: Select all
            else if (rnum == IT_LIGHTNING)
            {
               deathstring = " gets torched ";
               deathstring2 = "'s lava launcher!\n";
            }

So, I tried to do this instead:
Code: Select all
            else if (targ.deathtype == "lava")
                  {
                     bprint (PRINT_MEDIUM, attacker.netname);
                     bprint (PRINT_MEDIUM, " torches ");
                     bprint (PRINT_MEDIUM, targ.netname);
                     bprint (PRINT_MEDIUM, " with the lava launcher!\n");
                     return;
                  }

Problem is that I can't get the compiler to recognize "lava".
How can I associate "lava" so that it works correctly?
Here is the rest of the code:
Code: Select all
void(vector org) Flame_Burn =
{
        flame = spawn ();
        flame.owner = self;
        flame.classname = "lava";
        flame.movetype = MOVETYPE_FLYMISSILE;
        flame.solid = SOLID_NOT;
        flame.origin = org;
        flame.velocity = '0 0 75';
        setmodel (flame, "progs/flame2.mdl");
        setsize (flame, '0 0 0', '0 0 0');
        flame.nextthink = time + .01;
        flame.think = s_explode1;
};

void() Flame_Think =
{
        local float r;

        if (self.enemy.waterlevel == 3 || self.enemy.health <= 0)
        {
                BecomeExplosion ();
                return;
        }
        self.origin = self.enemy.origin + '0 0 25';
        if (r < 0.5)                                                       
                  T_Damage (self.enemy, self, self.owner, 1);               
        self.nextthink = time + 0.01;
        self.classname = "lava";                         
};

void(vector org, entity e) Flame_Onfire =
{
        flame = spawn ();
        flame.owner = self.owner;
        flame.enemy = e;
        flame.classname = "lava";
        flame.movetype = MOVETYPE_NONE;
        flame.solid = SOLID_NOT;
        flame.origin = org;
        flame.velocity = '0 0 0';
        setmodel (flame, "progs/flame2.mdl");             
        setsize (flame, '0 0 -15', '0 0 0');
        flame.nextthink = time + 0.01;
        flame.think = Flame_Think;
        flame.effects = flame.effects | EF_DIMLIGHT;
};

void() Flame_Touch =
{
        local float r;
        local vector org;

        if (other.takedamage)
        {
                org = other.origin;
                org_z = self.origin_z;
                Flame_Burn (org);
                T_Damage (other, self, self.owner, 8);                                                 
                remove (self);
                r = random ();
                if (r < 0.2)                                                                             
                {
                        if ((other.classname == "player"))
                        {
                                centerprint (other,"You are on fire!\n\nFind WATER fast");
                                stuffcmd (other,"bf\n");
                        }
                        Flame_Onfire (org, other);
                }
        }
};

void() W_FireLightning =                                                                         
{

   if (self.ammo_cells < 1)
   {
      self.weapon = W_BestWeapon ();
      W_SetCurrentAmmo ();
      return;
   }
   
   if (deathmatch != 4)
      self.currentammo = self.ammo_cells = self.ammo_cells - 1;
   
   sound (self, CHAN_WEAPON, "hknight/hit.wav", .8, ATTN_NORM);

        makevectors (self.angles);
        self.velocity = self.velocity - v_forward * 50;                                           
         
        if (self.health >= 1 && self.health <= 25)
        {
        makevectors (self.angles);
        self.velocity = self.velocity - v_forward * 100;                                               
        } 

   msg_entity = self;

   flame = spawn ();
   flame.voided=0;
   flame.owner = self;
   flame.movetype = MOVETYPE_BOUNCE;
   flame.solid = SOLID_BBOX;
   flame.classname = "lava";   

   makevectors (self.v_angle);

   if (self.v_angle_x)
      flame.velocity = v_forward*500 + v_up * 225 + crandom()*v_right*25 + crandom()*v_up*25;
   else
   {
      flame.velocity = aim(self, 10000);
      flame.velocity = flame.velocity * 400;
      flame.velocity_z = 100;
   }

   flame.avelocity = '300 300 300';
   flame.angles = vectoangles(newmis.velocity);
   
   flame.touch = Flame_Touch;
   flame.nextthink = time + 1.0;
   flame.think = s_explode1;
        setmodel (flame, "progs/lavaball.mdl");
   setsize (flame, '0 0 0', '0 0 0');             
   setorigin (flame, self.origin);
};
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Dr. Shadowborg » Mon Dec 10, 2007 5:05 pm

Open combat.qc, then go into the T_Damage function.

Look for where the code looks like this:
Code: Select all
if(targ.health <= 0)
{
  Killed(targ, attacker);
  return;
}


Put inside the same if check just before Killed() is called so that the above looks like this:

Code: Select all
if(targ.health <= 0)
{
  if(inflictor.classname == "lava")
   targ.deathtype = "lava";

  Killed(targ, attacker);
  return;
}


That should do the trick.
User avatar
Dr. Shadowborg
InsideQC Staff
 
Posts: 1110
Joined: Sat Oct 16, 2004 3:34 pm

Postby Orion » Mon Dec 10, 2007 11:30 pm

An easier way to do that is:

Go to Flame_Touch(), and before T_Damage(), add:

Code: Select all
other.deathtype = "lava";


And in Flame_Think(), before T_Damage() too, add:

Code: Select all
self.enemy.deathtype = "lava";


I did that with the code that contained my bots.
Don't forget to put brackets between (if (r < 0.5)) and the 2 lines of code.
You can use an if() without brackets only if there's only 1 line of code. otherwise you should put brackets between the lines.

BUT, if you use more than 1 if(), you don't need to use brackets. Here's an example:

Code: Select all
if()
if()
if()
{
    code
    code
    code
}


This won't have any problems, if you use:

Code: Select all
if()
{
   if()
   {
       if()
       {
           code
           code
           code
       }
   }
}


It will work exactly the same, but will have an increased size of code.

But the better of bests ways is using AND (&&) and/or OR (||) inside an if().

Example:

Code: Select all
if(something && something && something)
{
   code
   code
   code
}


This might work exactly the same as the 2 codes above.

Now I'll show you a better example. It's a code that searches for damageable entities, and I'll show the if() differences.

Code: Select all
local entity ent;
ent = findradius(self.origin, 1000);
while (ent)
{
   if (ent != self)
   if (ent.health > 0)
   if (ent.takedamage == DAMAGE_AIM)
   if (visible(ent))
      self.enemy = ent;
   ent = ent.chain;
}


Notice that if you don't use brackets between a while(), it will cause problems too.

Here's another way:

Code: Select all
local entity ent;
ent = findradius(self.origin, 1000);
while (ent)
{
   if (ent != self && ent.takedamage == DAMAGE_AIM && ent.health > 0 && visible(ent))
      self.enemy = ent;
   ent = ent.chain;
}


This is a shorter way, better use and (&&) and/or or (||) instead of creating many ifs.

This gone a bit off-topic, but it's ok.
User avatar
Orion
 
Posts: 476
Joined: Fri Jan 12, 2007 6:32 pm
Location: Brazil

Postby redrum » Tue Dec 11, 2007 12:11 am

Awesome! Thanks guys! :D
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Spike » Tue Dec 11, 2007 2:22 am

if (blah)
if (blah)
if (blah)
blah();

results in more efficient code than

if (blah && blah && blah)
blah();


Its a pain to read, but multiple conditions is more efficient. This is especially important when you're itterating over a lot of entities.
QuakeC does not typically early-out like C does. The entire statement is evallulated (including function calls!) before the result is compared.
Both FrikQCC and FTEQCC can do it, but doing so is not always advised, and can break some code (admittedly such code is arguably poorly written).

in the above example, the line:

if (ent != self && ent.takedamage == DAMAGE_AIM && ent.health > 0 && visible(ent))

is exactly identical to (except that it would break else statements):
temp = (ent != self);
temp2 = ent.takedamage;
temp2 = (temp2 == DAMAGE_AIM);
temp = (temp && temp2);
temp2 = ent.health;
temp2 = temp2 > 0;
temp = temp && temp2;
arg0 = ent;
temp2 = visible(arg0);
temp = temp && temp2;
if (temp)
{
...
}
Yes, the entire thing is executed for every ent within those 1000 units.
Note that earlier qc compilers will use new temps for each line, instead of reusing them.

The multiple-if form goes:

temp = (ent != self);
if (temp)
{
temp = ent.takedamage;
temp = (temp == DAMAGE_AIM);
if (temp)
{
temp = ent.health;
temp = temp > 0;
if (temp)
{
arg0 = ent;
temp = visible(arg0);
if (temp)
{
...
}
}
}
}

In both examples, each line (baring braces) is a single qc instruction.
Due to the comparisons instead of logic operators, the second form will run more efficiently. The function call can be particuarly nasty, depending on the length of the function - ranging from mearly inefficient to downright nasty.
Consider that visible is a qc function containing 18 instructions, one of them a call to a builtin which recurses down the bsp tree, using floating point maths, followed by a quick run through all the entities in the block and a recursion down any bsp trees within the region, and you'll realise how important that seperating your conditions into seperate if statements can be in quakec.

But yes, the logical 'and' (&&) and 'or' (||) operators are more readable.

I suspect I might be overcomplicating the issue, and going slightly off topic.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby redrum » Tue Dec 11, 2007 12:30 pm

Didn't grasp all of that but I get the jist of it!
I have a lot to learn.
Where/how did you guys acquire all of this knowledge?
Welcome to the Overlook Hotel: The-Overlook-Hotel.game-server.cc
User avatar
redrum
 
Posts: 410
Joined: Wed Mar 28, 2007 11:35 pm
Location: Long Island, New York

Postby Urre » Tue Dec 11, 2007 3:31 pm

No lives?
I was once a Quake modder
User avatar
Urre
 
Posts: 1109
Joined: Fri Nov 05, 2004 2:36 am
Location: Moon

Postby Spike » Tue Dec 11, 2007 5:07 pm

redrum wrote:Where/how did you guys acquire all of this knowledge?


As Urre said. I need to get out more.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Dr. Shadowborg » Tue Dec 11, 2007 6:49 pm

Off and on, endless amounts of tinkering, abandoning, restarting, recoding mods.

(What Urre said.)

The more you work with quakec the better you will become over time.
User avatar
Dr. Shadowborg
InsideQC Staff
 
Posts: 1110
Joined: Sat Oct 16, 2004 3:34 pm


Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest