Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

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

| Inside3d Forums Forum Index -> QuakeC Programming

View previous topic :: View next topic
Author | Message
--- | ---
ajay Joined: 29 Oct 2004 Posts: 295 Location: Swindon, UK |

Posted: Sun May 30, 2010 4:19 pm Post subject: Set a monster on fire, but for a set time.... |

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 |

Sajt Joined: 16 Oct 2004 Posts: 1026 |

Posted: Sun May 30, 2010 5:36 pm Post subject: |

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 |

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

Posted: Sun May 30, 2010 6:55 pm Post subject: |

I went with Method 2, works likes a charm, thank you very much 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 |

Sajt Joined: 16 Oct 2004 Posts: 1026 |

Posted: Mon May 31, 2010 10:19 am Post subject: |

*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 |

Display posts from previous:

| 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