
FlameThrower: Proposed Modification
Moderator: Moderators
Ok, just about done. Fixed the problems at the start and end conditions- it was a just a simple logic error in the end.
Now I just have to clean up and make sure it's timing about right, and I'm going to be super-duper nice to everybody, and set up defaults for FlameThrowers, so that old weapons won't be too badly broken in 0.73b... and then submit the patch. Whew!
Now I just have to clean up and make sure it's timing about right, and I'm going to be super-duper nice to everybody, and set up defaults for FlameThrowers, so that old weapons won't be too badly broken in 0.73b... and then submit the patch. Whew!
The size, texture and dispersion amount of the sprites is entirely under a modder's control, FYI. As for turning into "smoke" at the end of their lifetime, setting GroundBounce=1 achieves that goal, visually speaking. I'd like to take a whack at making a FlameThrower that's physically accurate, and obeys gravity, at some point, but not today- I need to get back to work on NanoBlobs to get it prepared for release-day.
And... here we go! I've even put in backwards-compatibility code, that will make it so that current flamethrowers won't have to change a thing. The example screenshot below is using only default values!

So... here's my sourcecode. I have taken the liberty of chopping out the irrelevant sections of WeaponDefHandler.cpp, because otherwise it runs for pages.
WeaponDefHandler.cpp:
FlameThrower.h:
FlameThrower.cpp:
FlameProjectile.h:
And, of course, what you actually want to read, FlameProjectile.cpp. Man, this was a lot've work for such a tiny amount of new code 

So... here's my sourcecode. I have taken the liberty of chopping out the irrelevant sections of WeaponDefHandler.cpp, because otherwise it runs for pages.
WeaponDefHandler.cpp:
Code: Select all
} else if(weaponDefs[id].type=="flame"){
//CFlameProjectile
weaponDefs[id].visuals.texture1 = &ph->explotex;
sunparser->GetTDef(weaponDefs[id].size, tempsize , weaponname + "\\size");
sunparser->GetDef(weaponDefs[id].sizeGrowth, "0.1", weaponname + "\\sizeGrowth");
sunparser->GetDef(weaponDefs[id].intensity, "0.9", weaponname + "\\intensity");
weaponDefs[id].visuals.color=sunparser->GetFloat3(float3(1.0,0.8,0.3),weaponname + "\\rgbcolor");
weaponDefs[id].visuals.color2=sunparser->GetFloat3(float3(0.05,0.0,0.0),weaponname + "\\rgbcolor2");
Code: Select all
#ifndef __FLAME_THROWER_H__
#define __FLAME_THROWER_H__
#include "Weapon.h"
class CFlameThrower :
public CWeapon
{
public:
CFlameThrower(CUnit* owner);
~CFlameThrower(void);
void Fire(void);
bool TryTarget(const float3 &pos,bool userTarget,CUnit* unit);
void Update(void);
float3 color;
float3 color2;
};
#endif // __FLAME_THROWER_H__
Code: Select all
#include "StdAfx.h"
#include "FlameThrower.h"
#include "Sim/Units/Unit.h"
#include "Sound.h"
#include "Map/Ground.h"
#include "Game/GameHelper.h"
#include "Sim/Projectiles/FlameProjectile.h"
#include "WeaponDefHandler.h"
#include "mmgr.h"
CFlameThrower::CFlameThrower(CUnit* owner)
: CWeapon(owner)
{
}
CFlameThrower::~CFlameThrower(void)
{
}
void CFlameThrower::Fire(void)
{
float3 dir=targetPos-weaponPos;
dir.Normalize();
float3 spread=(gs->randVector()*sprayangle+salvoError)*0.2;
spread-=dir*0.001;
new CFlameProjectile(weaponDef->visuals.color,weaponDef->visuals.color2,weaponDef->intensity,weaponPos,dir*projectileSpeed,spread,owner,damages,weaponDef,(int)(range/projectileSpeed*1.2));
if(fireSoundId && (!weaponDef->soundTrigger || salvoLeft==salvoSize-1))
sound->PlaySound(fireSoundId,owner,fireSoundVolume);
}
bool CFlameThrower::TryTarget(const float3 &pos,bool userTarget,CUnit* unit)
{
if(!CWeapon::TryTarget(pos,userTarget,unit))
return false;
if(unit){
if(unit->isUnderWater)
return false;
} else {
if(pos.y<0)
return false;
}
float3 dir=pos-weaponPos;
float length=dir.Length();
if(length==0)
return true;
dir/=length;
float g=ground->LineGroundCol(weaponPos,pos);
if(g>0 && g<length*0.9)
return false;
if(helper->LineFeatureCol(weaponPos,dir,length))
return false;
if(avoidFriendly && helper->TestCone(weaponPos,dir,length,(accuracy+sprayangle),owner->allyteam,owner)){
return false;
}
return true;
}
void CFlameThrower::Update(void)
{
if(targetType!=Target_None){
weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
wantedDir=targetPos-weaponPos;
wantedDir.Normalize();
}
CWeapon::Update();
}
Code: Select all
#ifndef __FLAME_PROJECTILE_H__
#define __FLAME_PROJECTILE_H__
#include "WeaponProjectile.h"
#include "Sim/Misc/DamageArray.h"
class CFlameProjectile :
public CWeaponProjectile
{
public:
CFlameProjectile(const float3& color,const float3& color2, float intensity,const float3& pos,const float3& speed,const float3& spread,CUnit* owner,const DamageArray& damages, WeaponDef *weaponDef, int ttl=50);
~CFlameProjectile(void);
float3 color;
float3 color2;
float intensity;
float3 spread;
float curTime;
float invttl;
void Update(void);
void Draw(void);
void Collision(CUnit* unit);
void Collision(void);
int ShieldRepulse(CPlasmaRepulser* shield,float3 shieldPos, float shieldForce, float shieldMaxSpeed);
};
#endif // __FLAME_PROJECTILE_H__

Code: Select all
#include "StdAfx.h"
#include "FlameProjectile.h"
#include "Rendering/GL/VertexArray.h"
#include "Game/Camera.h"
#include "Map/Ground.h"
#include "mmgr.h"
#include "ProjectileHandler.h"
#include "Sim/Weapons/WeaponDefHandler.h"
CFlameProjectile::CFlameProjectile(const float3& color, const float3& color2, float intensity, const float3& pos, const float3& speed, const float3& spread, CUnit* owner, const DamageArray& damages, WeaponDef *weaponDef, int ttl)
: CWeaponProjectile(pos,speed,owner,0,ZeroVector,weaponDef,damages,0),
color(color),
color2(color2),
intensity(intensity),
spread(spread),
curTime(0)
{
invttl=1.0/ttl;
SetRadius(weaponDef->size);
drawRadius=weaponDef->size;
}
CFlameProjectile::~CFlameProjectile(void)
{
}
void CFlameProjectile::Collision(void)
{
float3 norm=ground->GetNormal(pos.x,pos.z);
float ns=speed.dot(norm);
speed-=norm*ns*1;
pos.y+=0.05;
curTime+=0.05;
}
void CFlameProjectile::Collision(CUnit* unit)
{
CWeaponProjectile::Collision(unit);
}
void CFlameProjectile::Update(void)
{
pos+=speed;
speed+=spread;
SetRadius(radius+weaponDef->sizeGrowth);
drawRadius=(radius+weaponDef->sizeGrowth);
curTime+=invttl;
if(curTime>1){
curTime=1;
deleteMe=true;
}
}
void CFlameProjectile::Draw(void)
{
float ColorA;
float ColorB;
float ColorC;
inArray=true;
unsigned char col[4];
if(curTime>=0.000000)
{
if(color.x-(curTime*2)<0)
{
ColorA=0;
}else{
ColorA=color.x-(curTime*2);
}
if(color.y-(curTime*2)<0)
{
ColorB=0;
}else{
ColorB=color.y-(curTime*2);
}
if(color.z-(curTime*2)<0)
{
ColorC=0;
}else{
ColorC=color.z-(curTime*2);
}
col[0]=(unsigned char) ((ColorA+(color2.x*(curTime*2)))*intensity*255);
col[1]=(unsigned char) ((ColorB+(color2.y*(curTime*2)))*intensity*255);
col[2]=(unsigned char) ((ColorC+(color2.z*(curTime*2)))*intensity*255);
col[3]=3;
}
float3 interPos=pos+speed*gu->timeOffset;
va->AddVertexTC(interPos-camera->right*radius-camera->up*radius,weaponDef->visuals.texture1->xstart ,weaponDef->visuals.texture1->ystart ,col);
va->AddVertexTC(interPos+camera->right*radius-camera->up*radius,weaponDef->visuals.texture1->xend ,weaponDef->visuals.texture1->ystart ,col);
va->AddVertexTC(interPos+camera->right*radius+camera->up*radius,weaponDef->visuals.texture1->xend ,weaponDef->visuals.texture1->yend ,col);
va->AddVertexTC(interPos-camera->right*radius+camera->up*radius,weaponDef->visuals.texture1->xstart ,weaponDef->visuals.texture1->yend ,col);
}
int CFlameProjectile::ShieldRepulse(CPlasmaRepulser* shield,float3 shieldPos, float shieldForce, float shieldMaxSpeed)
{
float3 dir=pos-shieldPos;
dir.Normalize();
if(dir.dot(speed)<shieldMaxSpeed){
speed+=dir*shieldForce;
return 2;
}
return 0;
}
Arcing flames, not yet.
I will want to try combining gravity-effected weapons with the flamethrower code at some point, because the way that flamethrowers work right now has always really bothered me, but not today- I have about 4 hours left to work on NanoBlobs before I go to work
Zerg spray... well, how's this (this is a quickie, obviously):

I will want to try combining gravity-effected weapons with the flamethrower code at some point, because the way that flamethrowers work right now has always really bothered me, but not today- I have about 4 hours left to work on NanoBlobs before I go to work

Zerg spray... well, how's this (this is a quickie, obviously):

@Fiz:
Y'know, it changes purely based on the amount of time it's alive. I could see some very nice PPC FX come from this
Also, I should mention that this works with TGAs with an alpha, so if you give it a starting value of "1 1 1" for RGBColor, then it'll use the starting values of the bitmap, and color-shift it from there. The possibilities are, shall we say... fairly open
@AF:
I think it just has to do with the source bitmap. If we do an apples-vs-apples test, you can see that it's no more intense. This is the current released dev. preview:

<comes back from testing>
Hmm. No, AF's right. Even a pure alpha-block isn't a pure tone. I'll have to change my sourcecode then.
Y'know, it changes purely based on the amount of time it's alive. I could see some very nice PPC FX come from this

Also, I should mention that this works with TGAs with an alpha, so if you give it a starting value of "1 1 1" for RGBColor, then it'll use the starting values of the bitmap, and color-shift it from there. The possibilities are, shall we say... fairly open

@AF:
I think it just has to do with the source bitmap. If we do an apples-vs-apples test, you can see that it's no more intense. This is the current released dev. preview:

<comes back from testing>
Hmm. No, AF's right. Even a pure alpha-block isn't a pure tone. I'll have to change my sourcecode then.
Okie doke, problem solved. Minor issues, really. However, it did point out something to me: the default TGA files for certain FX don't have proper alpha channels! Big problem!

Here is the changed source. Note that intensity no longer has any effect on colors (there's really no need, I'm not sure what I was thinking there) and intensity falls off with time, taking the opacity of the texture down with time. When the particle is 2 seconds old, it becomes invisible, unless intensity is higher than 1.0.

Here is the changed source. Note that intensity no longer has any effect on colors (there's really no need, I'm not sure what I was thinking there) and intensity falls off with time, taking the opacity of the texture down with time. When the particle is 2 seconds old, it becomes invisible, unless intensity is higher than 1.0.
Code: Select all
#include "StdAfx.h"
#include "FlameProjectile.h"
#include "Rendering/GL/VertexArray.h"
#include "Game/Camera.h"
#include "Map/Ground.h"
#include "mmgr.h"
#include "ProjectileHandler.h"
#include "Sim/Weapons/WeaponDefHandler.h"
CFlameProjectile::CFlameProjectile(const float3& color, const float3& color2, float intensity, const float3& pos, const float3& speed, const float3& spread, CUnit* owner, const
DamageArray& damages, WeaponDef *weaponDef, int ttl)
: CWeaponProjectile(pos,speed,owner,0,ZeroVector,weaponDef,damages,0),
color(color),
color2(color2),
intensity(intensity),
spread(spread),
curTime(0)
{
invttl=1.0/ttl;
SetRadius(weaponDef->size);
drawRadius=weaponDef->size;
}
CFlameProjectile::~CFlameProjectile(void)
{
}
void CFlameProjectile::Collision(void)
{
float3 norm=ground->GetNormal(pos.x,pos.z);
float ns=speed.dot(norm);
speed-=norm*ns*1;
pos.y+=0.05;
curTime+=0.05;
}
void CFlameProjectile::Collision(CUnit* unit)
{
CWeaponProjectile::Collision(unit);
}
void CFlameProjectile::Update(void)
{
pos+=speed;
speed+=spread;
SetRadius(radius+weaponDef->sizeGrowth);
drawRadius=(radius+weaponDef->sizeGrowth);
curTime+=invttl;
if(curTime>1){
curTime=1;
deleteMe=true;
}
}
void CFlameProjectile::Draw(void)
{
float ColorA;
float ColorB;
float ColorC;
inArray=true;
unsigned char col[4];
if(curTime>=0.000000)
{
if(color.x-(curTime*2)<0)
{
ColorA=0;
}else{
ColorA=color.x-(curTime*2);
}
if(color.y-(curTime*2)<0)
{
ColorB=0;
}else{
ColorB=color.y-(curTime*2);
}
if(color.z-(curTime*2)<0)
{
ColorC=0;
}else{
ColorC=color.z-(curTime*2);
}
col[0]=(unsigned char) ((ColorA+(color2.x*(curTime*2)))*255);
col[1]=(unsigned char) ((ColorB+(color2.y*(curTime*2)))*255);
col[2]=(unsigned char) ((ColorC+(color2.z*(curTime*2)))*255);
col[3]=(intensity*(curTime/2))*255;
}
float3 interPos=pos+speed*gu->timeOffset;
va->AddVertexTC(interPos-camera->right*radius-camera->up*radius,weaponDef->visuals.texture1->xstart ,weaponDef->visuals.texture1->ystart ,col);
va->AddVertexTC(interPos+camera->right*radius-camera->up*radius,weaponDef->visuals.texture1->xend ,weaponDef->visuals.texture1->ystart ,col);
va->AddVertexTC(interPos+camera->right*radius+camera->up*radius,weaponDef->visuals.texture1->xend ,weaponDef->visuals.texture1->yend ,col);
va->AddVertexTC(interPos-camera->right*radius+camera->up*radius,weaponDef->visuals.texture1->xstart ,weaponDef->visuals.texture1->yend ,col);
}
int CFlameProjectile::ShieldRepulse(CPlasmaRepulser* shield,float3 shieldPos, float shieldForce, float shieldMaxSpeed)
{
float3 dir=pos-shieldPos;
dir.Normalize();
if(dir.dot(speed)<shieldMaxSpeed){
speed+=dir*shieldForce;
return 2;
}
return 0;
}
- TechnoTone
- Posts: 165
- Joined: 23 Aug 2005, 22:02
Wow - that looks awesome.
I'd like to suggest another possible improvement though. Notice how there are descrete positions for the flames when they are quite small:

Perhaps if the start position of each one was to have a random element to it - along the line of the firing axis - this could be eliminated too? I hope that makes sense.
I'd like to suggest another possible improvement though. Notice how there are descrete positions for the flames when they are quite small:

Perhaps if the start position of each one was to have a random element to it - along the line of the firing axis - this could be eliminated too? I hope that makes sense.