Inside3D!
     

Set a monster on fire, but for a set time....

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming
View previous topic :: View next topic  
Author Message
ajay



Joined: 29 Oct 2004
Posts: 295
Location: Swindon, UK

PostPosted: Sun May 30, 2010 4:19 pm    Post subject: Set a monster on fire, but for a set time.... Reply with quote

I have the following code for a molotov type bomb-thing. It works beautifully in as much as it is thrown, it explodes and it sets enemies on fire, and, they die. But, I don't want them to! Well I do, but I want them to only die if their health runs out before the flame goes out. Anyway, first the full code:
Code:

//===================================================================================
//=  M O L O T O V  start                                                           =
//===================================================================================

void() Molotov_Explosion = // adapted/renamed from standard Quake Become Explosion
{
   self.movetype = MOVETYPE_NONE;
   self.velocity = '0 0 0';
   self.touch = SUB_Null;
   setmodel (self, "progs/s_explod.spr");
   self.solid = SOLID_NOT;
   s_explode1 ();
};

void() Molotov_Explode = // adapted/renamed from standard Quake Grenade Explode
{
   T_RadiusDamage (self, self.owner, 60, world);

   WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
   WriteByte (MSG_BROADCAST, TE_EXPLOSION);
   WriteCoord (MSG_BROADCAST, self.origin_x);
   WriteCoord (MSG_BROADCAST, self.origin_y);
   WriteCoord (MSG_BROADCAST, self.origin_z);

   Molotov_Explosion ();
};   

void() Molotov_burn_Think = // part copeid from inside3d flamethrower tutorial
{
        self.origin = self.enemy.origin + '0 0 25';

          T_Damage (self.enemy, self, self.owner, 1);// how much it burns...   

   if (self.enemy.health <= 0)
      remove (self); // get rid of the flame once enemy dies, otherwise it burns in mid-air
   
   self.nextthink = time + 0.01;// NOTE: you must keep the .nextthink fast, otherwise the flame won't keepup with player.
};

void(vector org, entity e) Molotov_burn =
{
        local entity flame;
   float enemy_health_check;

        flame = spawn ();
        flame.owner = self.owner;
        flame.enemy = e;
        flame.classname = "fire";
        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 = Molotov_burn_Think;
        flame.effects = flame.effects | EF_DIMLIGHT;
};

void() Molotov_Touch = // adapted from standard Quake GrenadeTouch and insided3d flamethrower tutorial
{

   local vector org;

   if (other == self.owner)
      return;      // don't explode on owner
   if (other.takedamage == DAMAGE_AIM)
   {
      org = other.origin;
                org_z = self.origin_z;
      T_Damage (other, self, self.owner, 8);
                remove (self);
      Molotov_Explode();
      Molotov_burn(org, other);
      return;
   }
   sound (self, CHAN_WEAPON, "weapons/bottle.wav", 1, ATTN_NORM);   // bounce sound
   if (self.velocity == '0 0 0')
      self.avelocity = '0 0 0';
};

void() W_Throw_Molotov = // adapted/renamed from standard Quake Fire_grenade
{
   local   entity missile, mpuff;
   
   self.currentammo = self.ammo_nails = self.ammo_nails - 1; //update
   
   //sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM); // change for more appropriate

   self.punchangle_x = -2;

   missile = spawn ();
   missile.owner = self;
   missile.movetype = MOVETYPE_BOUNCE;
   missile.solid = SOLID_BBOX;
   missile.classname = "grenade"; // change
   missile.effects = missile.effects | EF_DIMLIGHT;// so its flame has light
      
// set missile speed   

   makevectors (self.v_angle);

   if (self.v_angle_x)
      missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
   else
   {
      missile.velocity = aim(self, 10000);
      missile.velocity = missile.velocity * 600;
      missile.velocity_z = 200;
   }

   missile.avelocity = '300 300 300';

   missile.angles = vectoangles(missile.velocity);
   
   missile.touch = Molotov_Touch;
   
// set missile duration
   missile.nextthink = time + 2.5;
   missile.think = Molotov_Explode;

   setmodel (missile, "progs/molotov.mdl");
   setsize (missile, '0 0 0', '0 0 0');   
      
   setorigin (missile, self.origin);
   Gyro_Object_Activate(missile, 800);
};

//===================================================================================
//=  M O L O T O V  end                                                             =
//===================================================================================


Now what I want is for the the flame to remove itself after, say, 5 seconds. My inclination is to ammend Molotov_burn_Think, this bit:
Code:

void() Molotov_burn_Think = // part copeid from inside3d flamethrower tutorial
{
        self.origin = self.enemy.origin + '0 0 25';

          T_Damage (self.enemy, self, self.owner, 1);// how much it burns...   

   if (self.enemy.health <= 0)
      remove (self); // get rid of the flame once enemy dies, otherwise it burns in mid-air
   
   self.nextthink = time + 0.01;// NOTE: you must keep the .nextthink fast, otherwise the flame won't keepup with player.
};

I would like to set it's nextthink to 5 seconds then remove it, but it's nextthink is used to retain it's position relative to the enemy its burning, so I'm at a loss how to get it to burn out after a set time. Help gratefully received, cheers.
_________________
my site
Back to top
View user's profile Send private message MSN Messenger
Sajt



Joined: 16 Oct 2004
Posts: 1026

PostPosted: Sun May 30, 2010 5:36 pm    Post subject: Reply with quote

Method 1: specify how many times the flame should "hit" the player before disappearing. This, coupled with the .nextthink interval in the think function will determine how long the flame lasts.
Code:
// When you spawn the flame:

flame.count = 25; // (or however many hits it should do).

// In the flame's think function, right around where T_Damage is called:

self.count = self.count - 1;
if (self.count <= 0)
{
    remove(self);
    return;
}


Method 2: specify how long the flame will last, in seconds. Might be harder to tweak exactly how much damage the player will take, if you care about that.
Code:
// When you spawn the flame:

flame.attack_finished = time + 5.0; // here the flame will last 5 seconds

// In the flame's think function:

if (self.attack_finished <= time)
{
    remove(self);
    return;
}


Note I arbitrarily chose to use the "count" and "attack_finished" fields. You could just add new fields instead with more specific names.
_________________
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Back to top
View user's profile Send private message
ajay



Joined: 29 Oct 2004
Posts: 295
Location: Swindon, UK

PostPosted: Sun May 30, 2010 6:55 pm    Post subject: Reply with quote

I went with Method 2, works likes a charm, thank you very much Smile

I tried so many ways, and I'm a little gutted I completely missed such a simple and elegant solution - you wouldn't believe the over-complicated nonsense I tried!
_________________
my site
Back to top
View user's profile Send private message MSN Messenger
Sajt



Joined: 16 Oct 2004
Posts: 1026

PostPosted: Mon May 31, 2010 10:19 am    Post subject: Reply with quote

*Gulp* Yes, you could have done something crazy like spawn a new entity, which acts as the timer. Blehh. More code, and you would have to worry about deleting that thing.

By the way, it's not very nice that your code uses the think function for both damage and re-locating to the monster's location. You can't (IIRC) go lower than 1 damage so if you wanted the fire to damage more slowly, you'd have to compromise how often it re-locates. I imagine if a player were set on fire, being spammed with 1 damage 100 times per second wouldn't be so fun. Also, depending how armour code works in T_Damage, 1 damage could only end up as 0 or 1 damage to health, no in-between, whereas doing more damage per T_Damage call (5, 10, 20...) you leave more room for fractions.

You could use another timer field to say when next to do damage. So here we do 20 damage, 5 times a second (which matches the per-second damage rate of your old code, which by the way is a lot of damage!):
Code:
// When you spawn the flame:

flame.next_attack = time + 0.2;

// In the flame's think function, this replaces the old T_Damage call:

if (self.next_attack <= time)
{
    T_Damage(self.enemy, self, self.owner, 20);

    self.next_attack = time + 0.2;
}


Now in the think function you can just set "self.nextthink = time;" and have it update every frame. (Since the dynamic molotov entity should be after map-placed monsters in the list, there won't be a one-frame delay.)

Another think you could play with is resetting the enemy's pain_finished after T_Damage, if you want this thing to really mess with the monsters. That would probably put them in an infinite flinch-juggle, making them unable to attack until the flame runs out. If the flame did not so much damage but had that effect, it might be interesting...

As a side note, I'm not sure what will happen if the framerate is worse than 5 fps. (I doubt vanilla Quake runs multiple thinks per frame.) You could change the "if (self.next_attack <= time)" to a while loop, but that's well, playing with fire. (What if next_attack is molested by some other bad code? You could end up with a runaway loop crash.) The proper solution is some kind of complex arithmetic, but ehh... okay, who cares if you take a lot less damage from fire when the game runs at 1 fps.
_________________
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> QuakeC Programming All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group